[ 2 => 120€, 3 => 180€ ] * * @return array Contiene los precios en función del número de personas. */ public function getFinalPricesForSearch(RoomCategory $category, \DateTimeInterface $startDate, \DateTimeInterface $endDate): array { $daysDiff = $startDate->diff($endDate)->days; if (!$daysDiff) { $this->logger->warning("PricesService.getFinalPricesForSearch - Invalid search period"); return []; } $fareTablesForPeriod = $this->repoFareTable->findFareTable($category->getId(), $startDate, $endDate); //Obtenemos el número de personas permitido por todos los tarifarios activos. $allowedGuestNumbers = $this->getAllowedGuestNumberFromFaretableList($fareTablesForPeriod); $rs = []; //Calculamos los precios para cada GuestNumber permitido. foreach ($allowedGuestNumbers as $currentGuestNumber) { //Calculamos el precio de cada día. // e.g. --> [ "2024-04-30" => 20, "2024-05-01" => 28, "2024-05-02" => 20, ...]. $dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $fareTablesForPeriod); $rs[$currentGuestNumber] = $dailyPrices; } //Aplicamos la estrategia de precios configurada para calcular el precio final de la estancia. // TODOME: Crear y configurar diversas estrategias de precios -> Por el momento utilizamos "Suma del precio de cada día". $finalResult = []; foreach ($rs as $guestNum => $priceArray) { $finalAmount = \array_reduce($priceArray, function ($acc, $element) { return $acc + $element; }); $finalResult[$guestNum] = $finalAmount; } return $finalResult; } /** * Calculate stay total price on each roomCategory for the given time period * Returns a list with all roomCategories, containing the total price by guestnumber. * * e. g. --> [ 2 => 120€, 3 => 180€ ] * * @return SearchPricesResponse[] Total prices for each guestnumber of each roomCategory. */ public function getRoomSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate): array { $daysDiff = $startDate->diff($endDate)->days; if (!$daysDiff) { $this->logger->warning("PricesService.getFinalPricesForSearch - Invalid search period"); return []; } $rs = []; $roomCategoryList = $this->repoRoomCategory->findAll(); $fareTableList = $this->repoFareTable->getFareTableListForPeriod($startDate, $endDate); $faretablesByRoomCategory = []; foreach ($fareTableList as $faretable) { $faretablesByRoomCategory[$faretable->getRoomCategory()->getId()][] = $faretable; } foreach ($roomCategoryList as $roomCat) { //Get Faretables for RoomCategory. $roomFaretables = []; if (array_key_exists($roomCat->getId(), $faretablesByRoomCategory)) { $roomFaretables = $faretablesByRoomCategory[$roomCat->getId()]; } //Calculate distinct GuestNumber for RoomCategory by it's faretables. $allowedGuestNumberList = $this->getAllowedGuestNumberFromFaretableList($roomFaretables); //Calculate stay price for each allowed GuestNumber. $roomPricesByGuestNum = []; foreach ($allowedGuestNumberList as $currentGuestNumber) { //Calculate the price for each day. $dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $roomFaretables); $stayPrice = $this->getTotalPriceForStay($dailyPrices); $roomPricesByGuestNum[$currentGuestNumber] = $stayPrice; } $roomPrices = []; foreach ($roomPricesByGuestNum as $guestNum => $price) { $item = new SearchPricesResponsePriceItem(); $item->guestNumber = $guestNum; $item->price = $price; $roomPrices[] = $item; } $roomItem = new SearchPricesResponse(); $roomItem->roomCategoryId = $roomCat->getId(); $roomItem->prices = $roomPrices; $rs[] = $roomItem; } return $rs; } /** * Dado un número de huéspedes, calcula el precio de cada día del periodo indicado. * @param \DateTimeInterface $startDate Fecha de inicio del periodo. * @param \DateTimeInterface $endDate Fecha fin del periodo. * @param int $guestNumber Número de huéspedes para los que calcular los precios. * @param array $fareTableList Array con los tarifarios que aplican en el periodo indicado. * @return array Devuelve un array con los precios: e.g. --> [ "2024-04-30" => 2500, "2024-05-01" => 3500, "2024-05-02" => 3000, ...]. */ private function getDailyPricesForSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate, int $guestNumber, array $fareTableList): array { $daysDiff = $startDate->diff($endDate)->days; $dailyPrices = []; $currentDate = new \DateTime($startDate->format("Y-m-d")); //Copy start date to operate. for ($i = 0; $i < $daysDiff; $i++) { $strCurrentDate = $currentDate->format("Y-m-d"); //Obtenemos el tarifario que aplica --> TODOME: Buscar si se puede cachear esto, para no recarlcularlo para cada GuestNumber. $currentFareTableList = \array_filter($fareTableList, function (FareTable $element) use ($currentDate) { return $element->isActiveOnDate($currentDate); }); if (count($currentFareTableList) != 1) { $this->logger->error( "PricesService.getDailyPricesForSearch: Can't calculate prices: Invalid number of FareTable found: " . count($currentFareTableList), [ "startDate" => $startDate, "endDate" => $endDate, "fareTableList" => $fareTableList, "currentFareTableList" => $currentFareTableList ] ); return []; } $currentFareTable = array_values($currentFareTableList)[0]; //TODOME: -> Obtener precio específico (RoomPrices). //Si no hay precio, calcular mediante tarifarios. $roomFares = $currentFareTable->getRoomFaresByGuestNumber($guestNumber); if (count($roomFares) < 1) { $this->logger->error( "PricesService.getDailyPricesForSearch: Can't calculate prices: Found faretable without fares", [ "currentFareTable" => $currentFareTable, "guestNumber" => $guestNumber, "roomFaresForGuestNumber" => $roomFares ] ); return []; } //Si sólo hay 1 tarifa, aplico esa, sino calculo la apropiada según condiciones. $fareAmount = null; if (count($roomFares) == 1) { $fareAmount = $roomFares->first()->getFareAmount(); } else { $fareAmount = $this->calculateFareApplyingConditions($roomFares, $currentDate); } $dailyPrices[$strCurrentDate] = $fareAmount; //Add 1 day to current date. $currentDate->add(new \DateInterval("P1D")); } return $dailyPrices; } /** * Calcula el precio a aplicar según las condiciones de las tarifas. * @param Collection $roomFareList Lista con las posibles tarifas. * @param DateTimeInterface $fareCondDate Fecha utilizada para las condiciones. * */ private function calculateFareApplyingConditions(Collection $roomFareList, \DateTimeInterface $fareCondDate): int { $currentDayOfWeek = $fareCondDate->format("w"); if (count($roomFareList) == 1) { return $roomFareList->first()->getFareAmount(); } //Comprobamos si a alguna de las tarifas se la aplica la condición de "DayType" y coincide con el actual. // Si es así, devolvemos su valor. foreach ($roomFareList as $roomFare) { $dayType = $roomFare->getDayType(); if ($dayType == null || $dayType == "") { continue; } if ($dayType == $currentDayOfWeek) { return $roomFare->getFareAmount(); } } //TODOME: Si existen múltiples tarifas posibles, debería haber una estrategia para elegir una u otra. Variable de configuración para esto. $faresWithoutConditions = $roomFareList->filter(function ($element) { return $element->getDayType() == null; }); return $faresWithoutConditions->first()->getFareAmount(); } /** * Gets a list of allowed GuestNumbers from a list of FareTables. * @param FareTable[] $faretableList List of Faretables * @return int[] Array of allowed guestNumbers from faretable list. */ private function getAllowedGuestNumberFromFaretableList(array $faretableList): array { if (count($faretableList) == 0) { return []; } $guestSet = new Set(); foreach ($faretableList as $table) { $fares = $table->getRoomFares(); foreach ($fares as $roomFare) { $guestSet->add($roomFare->getGuestNumber()); } } return $guestSet->toArray(); } /** * Calculate total stay price given a list of prices. * @param array $dailyPrices Array containing the price for each day. * @return int Total stay price. */ private function getTotalPriceForStay(array $dailyPrices): int { // TODOME: Crear y configurar diversas estrategias de precios -> Por el momento utilizamos "Suma del precio de cada día". $finalAmount = \array_reduce($dailyPrices, function ($acc, $element) { return $acc + $element; }); return $finalAmount; } }