* @date 11/03/2016 */ namespace AppBundle\Entity; use AppBundle\Util\DepartureUtil; use Doctrine\Common\Collections\ArrayCollection; /** * TravelDate is a wrapper for TravelPeriod + start date and end date. This entity doesn't represent a database * table. It was introduced because of requirement to keep the bad original database design in which a TravelPeriod * serves two purposes: * - Representing a season * - Representing a single travel date (by being linked to a TravelPeriodDate-instance) * Also TravelPeriodDate has two purposes: * - Representing a time period of a season. From this time period, multiple travel dates are derived by taking * the possible weekdays into account * - Holding the start and end date for a TravelPeriod that is not a season (see above) * * To avoid further confusion and to slowly move away from this design, "TravelDate" has been invented, which has single * clearly defined purpose: * Representing a single travel date, i.e. * - time period a traveller starts and ends his/her journey * - prices * - availability * - departure locations and their prices * * "TravelPeriod" would be a more fitting name, but this name is already in use. * * @package AppBundle\Entity */ final class TravelDate { /** @var String $key */ private $key; /** @var TravelPeriod $travelPeriod */ private $travelPeriod; /** @var \DateTime $start */ private $start; /** @var \DateTime $end */ private $end; private $index; /** @var FlightPeriod $flightPeriod */ private $flightPeriod; private $currencyFactor; private $travelProgram; /** @var TravelDeparturePoint[]|null $departures Departures cache */ private $departures = null; /** @var TravelPeriodPrice[]|null $prices Prices cache */ private $prices = null; private $calculatedEffectivePrices = false; /** * TravelDate constructor. * * @param String $key * @param TravelPeriod $travelPeriod * @param FlightPeriod|null $flightPeriod * @param float $currencyFactor * @param \DateTime $start * @param \DateTime $end Optional. Computed from travel duration, if not specified. * @param null $index * * @todo Make this object immutable */ public function __construct($key, TravelPeriod $travelPeriod, FlightPeriod $flightPeriod = null, $currencyFactor, \DateTime $start = null, \DateTime $end = null, $index = null) { if ($travelPeriod->getIsSeason()) { if ($index === null) { throw new \InvalidArgumentException('Expected numeric value for argument $index due to virtual travel date. Got null.'); } if ($start === null) { throw new \InvalidArgumentException('Expected DateTime value for argument $start due to virtual travel date. Got null.'); } $this->start = $start; $this->index = $index; $this->prices = []; foreach ($travelPeriod->getPrices() as $price) { $this->prices[$price->getPriceTypeId()] = clone $price; } } else { $this->start = $travelPeriod->getStartDate(); $this->prices = $travelPeriod->getPrices(); } $this->flightPeriod = $flightPeriod; $this->travelProgram = $travelPeriod->getProgram(); $this->key = $key; $this->travelPeriod = $travelPeriod; if ($end === null) { $this->end = clone $this->start; $this->end->modify('+'. $this->travelProgram->getProgramDuration() .' day'); } else { $this->end = $end; } $this->currencyFactor = $currencyFactor; } public static function createForNonSeasonTravelPeriod($key, TravelPeriod $travelPeriod, FlightPeriod $flightPeriod = null, $currencyFactor) { if ($travelPeriod->getIsSeason()) { throw new \Exception('Expected non-season TravelPeriod instance'); } return new TravelDate($key, $travelPeriod, $flightPeriod, $currencyFactor); } public static function createForSeasonTravelPeriod($key, TravelPeriod $travelPeriod, $index, \DateTime $start, \DateTime $end = null, FlightPeriod $flightPeriod = null, $currencyFactor) { if (!$travelPeriod->getIsSeason()) { throw new \Exception('Expected season TravelPeriod instance'); } return new TravelDate($key, $travelPeriod, $flightPeriod, $currencyFactor, $start, $end, $index); } /** * @return String */ public function getKey() { return $this->key; } /** * @return \DateTime */ public function getStart() { return $this->start; } /** * @return \DateTime */ public function getStartWeekday() { return $this->start->format('w'); } public function getFinalPaymentDate() { $pDate = strtotime('-4 week', $this->getStart()->getTimestamp()); if($pDate <= time()){ $pDate = time(); } return date('d.m.Y',$pDate); } public function getFinalPaymentDateStr() { $pDate = strtotime('-4 week', $this->getStart()->getTimestamp()); if($pDate <= time()){ return "ist sofort fällig"; } return "bis zum ".date('d.m.Y',$pDate); } /** * @return \DateTime */ public function getEnd() { return $this->end; } public function getName() { return $this->travelPeriod->getIsSeason() ? (trim($this->travelProgram->getProgramCode()) . $this->index) : $this->travelPeriod->getName() ; } public function getStatus() { return $this->travelPeriod->getStatus(); } public function getEffectiveStatus() { if ($this->getStatus() == 2 && $this->getStart()->getTimestamp() < time() + 2419200) { return 1; } return $this->getStatus(); } public function getDepartures() { if ($this->departures === null) { if ($this->travelProgram->getIsMediated()) { $defaultDepartures = $this->travelProgram->getDepartures(); $departures = $this->travelPeriod->getDepartures(); } else { $defaultDepartures = $this->travelProgram->getTravelArrivalPoint()->getDepartures(); $departures = $this->flightPeriod === null ? [] : $this->flightPeriod->getDepartures(); } $defaultDepartures = DepartureUtil::filterDeparturesByPeriod($defaultDepartures, $this->start, $this->end, true); $this->departures = DepartureUtil::mergeDeparturesWithDefaults($departures, $defaultDepartures, true); } return $this->departures; } public function getFlightPrice() { if ($this->travelProgram->getIsMediated()) { return 0; } $flightPrice = null; if ($this->flightPeriod !== null) { $flightPrice = $this->flightPeriod->getPrice(); } if ($flightPrice === null) { $flightPrice = $this->travelProgram->getDefaultFlightPrice(); } return $flightPrice; } public function getFlightCalcPrice() { $flightPrice = $this->getFlightPrice(); if ($this->travelProgram->getIsMediated()) { $profitMargin = 1; } else { $profitMargin = $this->travelProgram->getProfitMargin() / 100 + 1; } $currencyFactor = $this->travelProgram->getNettoPricesInEuro() ? 1 : $this->currencyFactor; return round(($flightPrice * $currencyFactor) * $profitMargin); } /** * @return TravelPeriodPrice[]|\Doctrine\Common\Collections\Collection */ public function getPrices() { if (!$this->calculatedEffectivePrices) { $this->calculatedEffectivePrices = true; $flightPrice = $this->getFlightPrice(); if ($this->travelProgram->getIsMediated()) { $profitMargin = 1; } else { $profitMargin = $this->travelProgram->getProfitMargin() / 100 + 1; } $currencyFactor = $this->travelProgram->getNettoPricesInEuro() ? 1 : $this->currencyFactor; foreach ($this->prices as &$price) { $price->setEffectivePrice(round(($flightPrice + $price->getPrice() * $currencyFactor) * $profitMargin)); $price->setEffectiveComfortPrice(round($price->getPriceComfort() * $currencyFactor * $profitMargin)); $price->setEffectiveChildPrice(round(($flightPrice + $price->getPriceChildren() * $currencyFactor) * $profitMargin)); } } return $this->prices; } public function getLowestPrice() { $lowest = -1; foreach ($this->getPrices() as $price) { if ($price->getPriceTypeId() == 3) { // Use double room if available (#1076) return $price->getEffectiveDiscountPrice() ?? $price->getEffectivePrice(); } if ($lowest < 0 || $price->getEffectivePrice() < 0) { $lowest = $price->getEffectivePrice(); } } return $lowest == -1 ? null : $lowest; } public function hasComfortCategory() { foreach ($this->getPrices() as $price) { if ($price->getPriceComfort() > 0) { return true; } } return false; } /** * @return TravelProgram */ public function getTravelProgram(): TravelProgram { return $this->travelProgram; } /** * @return TravelPeriod * @internal */ public function __getTravelPeriod() { return $this->travelPeriod; } }