src/Model/Shop/Event/EventProduct.php line 87

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Model\Shop\Event;
  7. use App\Ecommerce\AvailabilitySystem\Event\Availability;
  8. use App\Ecommerce\PriceSystem\Event\PriceSystem;
  9. use App\Model\Shop\CancelableInterface;
  10. use App\Model\Shop\OrderItem;
  11. use App\Model\Shop\TeaserInfoInterface;
  12. use Carbon\Carbon;
  13. use Elements\Bundle\RecurringDatesTypeBundle\Templating\RecurringDatesHelper;
  14. use Elements\Bundle\SkidataTicketingSwebBundle\Model\Constant\CheckoutableType;
  15. use Elements\Bundle\SkidataTicketingSwebBundle\Model\Constant\ValidityUnit;
  16. use Elements\Bundle\SkidataTicketingSwebBundle\Model\SkidataTicketProductInterface;
  17. use Elements\Bundle\SkidataTicketingSwebBundle\Skidata\Sweb\Client\SalesChannel\Enum\PermissionStatusEnum;
  18. use Elements\Bundle\SkidataTicketingSwebBundle\Skidata\Sweb\Client\SalesChannel\Enum\SalesStatusEnum;
  19. use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\PricingManager\PriceInfo;
  20. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\ProductDateInterface;
  21. use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Product\CheckoutableInterface;
  22. use Elements\Bundle\TicketShopFrameworkBundle\Model\Traits\ProductTrait;
  23. use Pimcore\Bundle\EcommerceFrameworkBundle\AvailabilitySystem\AvailabilityInterface;
  24. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\ProductInterface;
  25. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface;
  26. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInterface;
  27. use Pimcore\Model\Asset\Image;
  28. use Pimcore\Model\DataObject;
  29. use Pimcore\Model\DataObject\AbstractObject;
  30. use Pimcore\Model\DataObject\ShopEvent;
  31. use Pimcore\Model\DataObject\SkidataConsumerCategory;
  32. use Pimcore\Model\DataObject\SkidataProduct;
  33. /**
  34.  * Class EventProduct
  35.  *
  36.  * @method null|ShopDynamicPrice getOrderedPriceItem()
  37.  */
  38. class EventProduct extends ShopEvent implements
  39.     ProductInterface,
  40.     CheckoutableInterface,
  41.     ProductDateInterface,
  42.     TeaserInfoInterface,
  43.     CancelableInterface,
  44.     SkidataTicketProductInterface
  45. {
  46.     use ProductTrait;
  47.     const PRODUCT_TYPE 'event';
  48.     public function getPriceSystemName(): ?string
  49.     {
  50.         return $this->getShopSystemName();
  51.     }
  52.     public function getAvailabilitySystemName(): ?string
  53.     {
  54.         return $this->getShopSystemName();
  55.     }
  56.     protected function getShopSystemName(): ?string
  57.     {
  58.         return self::PRODUCT_TYPE;
  59.     }
  60.     public function needsDelivery(): bool
  61.     {
  62.         return false;
  63.     }
  64.     public function isBookable(int $quantityScale 1bool $useCheckoutable false): bool
  65.     {
  66.         /** @var Availability|null $availability */
  67.         $availability $this->getOSAvailabilityInfo($quantityScale);
  68.         if ($useCheckoutable) {
  69.             return $this->isPublished() && $availability?->isCheckoutable();
  70.         } else {
  71.             return $this->isPublished() && $availability?->getAvailable();
  72.         }
  73.     }
  74.     public function getOSPrice($quantityScale 1): ?PriceInterface
  75.     {
  76.         return $this->getOSPriceInfo($quantityScale)->getPrice();
  77.     }
  78.     public function getOSIsBookable($quantityScale 1): bool
  79.     {
  80.         $price $this->getOSPrice($quantityScale);
  81.         return $this->isBookable() && !empty($price->getGrossAmount()->asNumeric());
  82.     }
  83.     /** @phpstan-ignore-next-line  */
  84.     public function getOSPriceInfo($quantityScale 1$products nullCarbon $date null): ?PriceInfoInterface
  85.     {
  86.         /** @var PriceSystem $priceSystem */
  87.         $priceSystem $this->getPriceSystemImplementation();
  88.         return $priceSystem->getPriceInfo($this$quantityScale$products$date);
  89.     }
  90.     public function getOSAvailabilityInfo($quantity null): ?AvailabilityInterface
  91.     {
  92.         return $this->getAvailabilitySystemImplementation()->getAvailabilityInfo($this$quantity);
  93.     }
  94.     public function getProductStartDate(): ?Carbon
  95.     {
  96.         return $this->getEventStartDate();
  97.     }
  98.     public function getTeaserPrice(): ?PriceInterface
  99.     {
  100.         /** @var PriceInfo $priceInfo */
  101.         $priceInfo $this->getOSPriceInfo();
  102.         return $priceInfo->getBasePrice();
  103.     }
  104.     public function getTeaserTopTitle(): ?string
  105.     {
  106.         if ($category $this->getProductCategory()) {
  107.             return $category->getName();
  108.         }
  109.         return '';
  110.     }
  111.     public function getTeaserDescription(): ?string
  112.     {
  113.         return $this->getShortDescription();
  114.     }
  115.     public function getTeaserImage(): ?Image
  116.     {
  117.         return $this->getMainImage();
  118.     }
  119.     /**
  120.      * Events got no validity date on the teaser
  121.      *
  122.      * @return array<mixed>
  123.      */
  124.     public function getTeaserValidity(): array
  125.     {
  126.         return [];
  127.     }
  128.     /**
  129.      * @return ShopDynamicPrice[]
  130.      */
  131.     public function getPriceObjects(?bool $includeFreePrices false): array
  132.     {
  133.         $priceObjs = [];
  134.         if ($items $this->getPrices()) {
  135.             foreach ($items as $item) {
  136.                 /** @var ShopDynamicPrice $priceObj */
  137.                 $priceObj $item->getObject();
  138.                 if ($includeFreePrices || $priceObj->getPrice()) {
  139.                     $priceObjs[] = $priceObj;
  140.                 }
  141.             }
  142.         }
  143.         return $priceObjs;
  144.     }
  145.     /**
  146.      * @return array<mixed>
  147.      */
  148.     public function getPriceObjectGrouped(): array
  149.     {
  150.         $groupedObjs = [];
  151.         if ($items $this->getPrices()) {
  152.             foreach ($items as $item) {
  153.                 /** @var ShopDynamicPrice $priceObj */
  154.                 $priceObj $item->getObject();
  155.                 if ($priceObj->getPrice()) {
  156.                     $groupedObjs[$item->getGroupname()][] = $priceObj;
  157.                 }
  158.             }
  159.         }
  160.         return $groupedObjs;
  161.     }
  162.     /**
  163.      * Get price group containing the given price description
  164.      *
  165.      * @param ShopDynamicPrice $priceDescription
  166.      *
  167.      * @return string
  168.      */
  169.     public function getPriceGroupForPriceDescription(?ShopDynamicPrice $priceDescription): string
  170.     {
  171.         if ($priceDescription) {
  172.             /** @phpstan-ignore-next-line  */
  173.             foreach ($this->getReference()->getPrices() as $price) {
  174.                 if ($price->getObject()->getId() == $priceDescription->getId()) {
  175.                     return $price->getGroupname();
  176.                 }
  177.             }
  178.         }
  179.         return '';
  180.     }
  181.     /**
  182.      * @param Carbon|null $startDate
  183.      * @param ShopDynamicPrice|null $priceItem
  184.      *
  185.      * @return EventProduct
  186.      *
  187.      * @throws \Exception
  188.      */
  189.     public function getOrCreateOrderableObject(Carbon $startDate nullShopDynamicPrice $priceItem null): self
  190.     {
  191.         if ($this->getBookableOnDayOfEvent()) {
  192.             $startDate->endOfDay();
  193.         }
  194.         $key sprintf('%s-%s-%s',
  195.             $this->getKey(),
  196.             $startDate->toDateTimeString(),
  197.             $priceItem $priceItem->getId() : ''
  198.         );
  199.         $list = new ShopEvent\Listing();
  200.         $list->setObjectTypes([AbstractObject::OBJECT_TYPE_VARIANT]);
  201.         $list->addConditionParam('o_parentId = ?'$this->getId());
  202.         $list->addConditionParam('ifnull(o_key,0) = ?'$key);
  203.         $list->addConditionParam('eventStartDate >= ?'$startDate->timestamp);
  204.         if ($list->count() == 0) {
  205.             // create variant for booking
  206.             $orderObject = new self();
  207.             $orderObject->setPublished(true);
  208.             $orderObject->setType(AbstractObject::OBJECT_TYPE_VARIANT);
  209.             $orderObject->setParent($this);
  210.             $orderObject->setKey($key);
  211.             $orderObject->setEventStartDate($startDate);
  212.             $orderObject->setOrderedPriceItem($priceItem);
  213.             $orderObject->save();
  214.         } else {
  215.             /** @var EventProduct $orderObject */
  216.             $orderObject $list->current();
  217.         }
  218.         return $orderObject;
  219.     }
  220.     public function isPersonsTimeList(): bool
  221.     {
  222.         if ($prices $this->getPrices()) {
  223.             $price array_pop($prices);
  224.             return !empty($price->getGroupname());
  225.         }
  226.         return false;
  227.     }
  228.     public function isCancelable(OrderItem $orderItem null): bool
  229.     {
  230.         return true;
  231.     }
  232.     public function getFirstValidValidityDate(Carbon $date): ?Carbon
  233.     {
  234.         $validityDate null;
  235.         $firstValidityDateRange $this->getFirstValidDateRange($date);
  236.         if (isset($firstValidityDateRange['from'])) {
  237.             /** @var Carbon $fromDateValidity */
  238.             $fromDateValidity $firstValidityDateRange['from'];
  239.             $validityDate $fromDateValidity->lt($date) ? $date $fromDateValidity;
  240.         }
  241.         return $validityDate;
  242.     }
  243.     /**
  244.      * @param Carbon $date
  245.      *
  246.      * @return  array<mixed>
  247.      */
  248.     public function getFirstValidDateRange(Carbon $date): array
  249.     {
  250.         $recurringDatesHelper = new RecurringDatesHelper();
  251.         if ($validityDates $recurringDatesHelper->getCalculatedDates($this'getValidityDates')) {
  252.             foreach ($validityDates as $validityDate) {
  253.                 $fromDate Carbon::parse($validityDate['fromDate']);
  254.                 $toDate Carbon::parse($validityDate['toDate']);
  255.                 if (($validityDate['type'] == 'RecurringDate' || $validityDate['type'] == 'RecurringDateTime')
  256.                     && ($date->between($fromDate$toDate) || $fromDate->gte($date))
  257.                 ) {
  258.                     return [
  259.                         'from' => $fromDate,
  260.                         'to' => $toDate,
  261.                     ];
  262.                 }
  263.             }
  264.         }
  265.         return [];
  266.     }
  267.     /**
  268.      *
  269.      * returns all valid date ranges now and in the future, if no dateformat is given, dats are returned as timestamp
  270.      *
  271.      * @param Carbon $date
  272.      *
  273.      * @return  array<mixed>
  274.      */
  275.     public function getAllValidDateRanges(?Carbon $date nullstring $dateFormat null): array
  276.     {
  277.         $ranges = [];
  278.         if(!$date) {
  279.             $date Carbon::now()->startOfDay();
  280.         }
  281.         if ($validDates $this->getValidityDates()) {
  282.             if ($definitions $validDates->getDefinitionArray()) {
  283.                 foreach ($definitions as $definition) {
  284.                     if(!isset($definition['values']['fromDate']) && isset($definition['values']['date'])) {
  285.                         $fromDate Carbon::parse($definition['values']['date']);
  286.                         $toDate = clone $fromDate;
  287.                     } else {
  288.                         $fromDate Carbon::parse($definition['values']['fromDate']);
  289.                         $toDate Carbon::parse($definition['values']['toDate']);
  290.                     }
  291.                     if (($definition['type'] == 'FixedDate' || $definition['type'] == 'RecurringDate' || $definition['type'] == 'RecurringDateTime')
  292.                         && ($date->between($fromDate$toDate) || $fromDate->gte($date) || $fromDate->getTimestamp()==$date->getTimestamp())
  293.                     ) {
  294.                         $ranges[] = [
  295.                             'from' => $fromDate,
  296.                             'to' => $toDate,
  297.                         ];
  298.                     }
  299.                 }
  300.             }
  301.         }
  302.         return $ranges;
  303.     }
  304.     public function getCheckoutableType(): ?string
  305.     {
  306.         if ($this->getSkidataProduct()) {
  307.             return CheckoutableType::TICKET;
  308.         }
  309.         return null;
  310.     }
  311.     public function getSkidataConsumerCategory(): ?SkidataConsumerCategory
  312.     {
  313.         if ($orderedPrice $this->getOrderedPriceItem()) {
  314.             $consumerCategory $orderedPrice->getConsumerCategory();
  315.             $skidataProduct $this->getSkidataProduct($orderedPrice);
  316.             if ($skidataProduct instanceof SkidataProduct && $consumerCategory instanceof \Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory) {
  317.                 return $consumerCategory->getSkidataConsumerForSkidataProduct($skidataProduct);
  318.             }
  319.         }
  320.         return null;
  321.     }
  322.     public function getIsDepotTicket(): ?bool
  323.     {
  324.         return false;
  325.     }
  326.     public function isSeasonTicket(): ?bool
  327.     {
  328.         return false;
  329.     }
  330.     public function getKeycardProduct(): void
  331.     {
  332.     }
  333.     public function getTicketDate(): ?Carbon
  334.     {
  335.         return $this->getProductStartDate();
  336.     }
  337.     public function isCancelableInSkidata(OrderItem $orderItem): bool
  338.     {
  339.         if (($ticket $this->getSkidataProduct()) && $ticket instanceof SkidataProduct && ($skidataBrick $orderItem->getSkidataBrick())) {
  340.             // copy from TicketProduct.php
  341.             $salesStatus $skidataBrick->getSkiDataSalesStatus();
  342.             $cancelableSalesStates array_merge(SalesStatusEnum::getBookedSalesStates(), SalesStatusEnum::getCanceledSalesStates());
  343.             $cancelableSalesStates[] = SalesStatusEnum::Reserved;
  344.             if (!in_array($salesStatus$cancelableSalesStates)) {
  345.                 return false;
  346.             }
  347.             $permissionStatus $skidataBrick->getSkiDataPermissionStatus();
  348.             // not cancelable anymore if they are on this  states !
  349.             if (in_array($permissionStatus, [PermissionStatusEnum::ConsumedPermissionStatusEnum::Blocked])) {
  350.                 return false;
  351.             }
  352.             if ($skidataBrick->getSkiDataPermissionConsumed()) {
  353.                 return false;
  354.             }
  355.             return true;
  356.         }
  357.         return false;
  358.     }
  359.     /**
  360.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  361.      */
  362.     public function getValidityDate(): ?Carbon
  363.     {
  364.         return null;
  365.     }
  366.     /**
  367.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  368.      */
  369.     public function getValidityFrom(): ?Carbon
  370.     {
  371.         return null;
  372.     }
  373.     /**
  374.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  375.      */
  376.     public function getValidityTo(): ?Carbon
  377.     {
  378.         return null;
  379.     }
  380.     /**
  381.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  382.      */
  383.     public function getReloadValidityFrom(): ?Carbon
  384.     {
  385.         return null;
  386.     }
  387.     /**
  388.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  389.      */
  390.     public function getValidityValue(): float|int|string
  391.     {
  392.         if ($this->getSkidataProduct() === null) {
  393.             return 0;
  394.         }
  395.         try {
  396.             /** @phpstan-ignore-next-line  */
  397.             return $this->getSkidataProduct()?->getValidityCategory()?->getValidityValue();
  398.         } catch (\Throwable) {
  399.             return 0;
  400.         }
  401.     }
  402.     /**
  403.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  404.      */
  405.     public function getValidity(): float|int|string
  406.     {
  407.         return $this->getValidityValue();
  408.     }
  409.     /**
  410.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  411.      */
  412.     public function getIsValidDayBefore(): ?bool
  413.     {
  414.         return false;
  415.     }
  416.     /**
  417.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  418.      */
  419.     public function getValidityValueInDays(): int
  420.     {
  421.         if ($this->getValidityUnit() === ValidityUnit::DAY) {
  422.             return (int)$this->getValidityValue();
  423.         }
  424.         if ($this->getValidityUnit() === ValidityUnit::POINT) {
  425.             return (int)round((int)$this->getValidityValue() / 2);
  426.         }
  427.         return 1;
  428.     }
  429.     /**
  430.      * Copied from TicketProduct. Method necessary for correct skidata transmission
  431.      */
  432.     public function getValidityUnit(): string
  433.     {
  434.         if ($this->getSkidataProduct() === null) {
  435.             return ValidityUnit::DAY// default
  436.         }
  437.         try {
  438.             return $this->getSkidataProduct()->getValidityCategory()?->getValueUnit();
  439.         } catch (\Throwable) {
  440.             return ValidityUnit::DAY;
  441.         }
  442.     }
  443.     public function getTicketEndDate(): ?Carbon
  444.     {
  445.         return $this->getTicketStartDate()?->endOfDay();
  446.     }
  447.     public function getTicketStartDate(): ?Carbon
  448.     {
  449.         return $this->getProductStartDate();
  450.     }
  451.     public function isVoucher(): bool
  452.     {
  453.         return !$this->getSkidataProduct();
  454.     }
  455.     public function hasVouchers(): bool
  456.     {
  457.         // fieldcollection "vouchers" shows how many vouchers the event product has
  458.         // if empty and a skidata product is available -> event is a ticket
  459.         // if emtpy and no skidata product -> event has one voucher (= event before multiple voucher feature)
  460.         return !$this->getSkidataProduct() || $this->getVouchersFieldcollection();
  461.     }
  462.     public function getVouchersFieldcollection(): ?\Pimcore\Model\DataObject\Fieldcollection
  463.     {
  464.         $parentProduct $this->getType() === DataObject::OBJECT_TYPE_VARIANT $this->getParent() : $this;
  465.         if ($parentProduct instanceof EventProduct) {
  466.             return $parentProduct->getVouchers();
  467.         }
  468.         return null;
  469.     }
  470.     public function getSkidataProduct(?ShopDynamicPrice $priceItem null): ?\Pimcore\Model\Element\AbstractElement
  471.     {
  472.         if ($priceItem && $priceItem->getSkidataProduct()) {
  473.             return $priceItem->getSkidataProduct();
  474.         }
  475.         if ($this->getOrderedPriceItem() && $this->getOrderedPriceItem()->getSkidataProduct()) {
  476.             return $this->getOrderedPriceItem()->getSkidataProduct();
  477.         }
  478.         if ($this->getParent() instanceof EventProduct && $this->getParent()->getSkidataProduct()) {
  479.             return $this->getParent()->getSkidataProduct();
  480.         }
  481.         return parent::getSkidataProduct();
  482.     }
  483.     public function getAcquisitionType(?ShopDynamicPrice $priceItem null): ?string
  484.     {
  485.         if ($priceItem && $priceItem->getAcquisitionType()) {
  486.             return $priceItem->getAcquisitionType();
  487.         }
  488.         if ($this->getOrderedPriceItem() && $this->getOrderedPriceItem()->getAcquisitionType()) {
  489.             return $this->getOrderedPriceItem()->getAcquisitionType();
  490.         }
  491.         return parent::getAcquisitionType();
  492.     }
  493. }