src/Model/Shop/Ticket/TicketProduct.php line 36

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Model\Shop\Ticket;
  7. use App\Model\Shop\CancelableInterface;
  8. use App\Model\Shop\OrderItem;
  9. use App\Model\Traits\SkidataTrait;
  10. use App\Model\Type\ForwardingStateType;
  11. use App\Model\Type\PoolType;
  12. use Carbon\Carbon;
  13. use Elements\Bundle\SkidataTicketingSwebBundle\Model\Constant\ValidityUnit;
  14. use Elements\Bundle\SkidataTicketingSwebBundle\Skidata\Sweb\Client\SalesChannel\Enum\PermissionStatusEnum;
  15. use Elements\Bundle\SkidataTicketingSwebBundle\Skidata\Sweb\Client\SalesChannel\Enum\SalesStatusEnum;
  16. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\PriceGroupInterface;
  17. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory;
  18. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct as TSF_TicketProduct;
  19. use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Product\PriceGroupAwareProduct;
  20. use Pimcore;
  21. use Pimcore\Model\DataObject\TicketMetaProduct;
  22. use Pimcore\Model\DataObject\TicketshopTicketAdditional;
  23. /**
  24.  * Class TicketProduct
  25.  *
  26.  * @package App\Model\Shop\Ticketing
  27.  *
  28.  * @method TicketConsumerCategory|null getTicketConsumerCategory()
  29.  * @method ShopTicketCatalog|null getCatalogObject()
  30.  * @method TicketProduct getReference()
  31.  */
  32. class TicketProduct extends TSF_TicketProduct implements CancelableInterface
  33. {
  34.     use SkidataTrait;
  35.     const PRODUCT_TYPE 'ticket';
  36.     const SHOP_SYSTEM_NAME 'ticket';
  37.     protected function getShopSystemName(): string
  38.     {
  39.         return TicketProduct::SHOP_SYSTEM_NAME;
  40.     }
  41.     /**
  42.      * defines the name of the price system for this product.
  43.      * there should either be a attribute in pro product object or
  44.      * it should be overwritten in mapped sub classes of product classes
  45.      *
  46.      * @return string
  47.      */
  48.     public function getPriceSystemName(): ?string
  49.     {
  50.         return $this->getShopSystemName();
  51.     }
  52.     /**
  53.      * defines the name of the availability system for this product.
  54.      * there should either be a attribute in pro product object or
  55.      * it should be overwritten in mapped sub classes of product classes
  56.      *
  57.      * @return string
  58.      */
  59.     public function getAvailabilitySystemName(): ?string
  60.     {
  61.         return 'default';
  62.     }
  63.     public function getOrderCatalog(): ?ShopTicketCatalog
  64.     {
  65.         // load unpublished objects too to display catalog infos in backend attask #419238 - storno-thema
  66.         Pimcore\Model\DataObject\AbstractObject::setHideUnpublished(false);
  67.         $orderCatalog $this->getCatalogObject();
  68.         Pimcore\Model\DataObject\AbstractObject::setHideUnpublished(true);
  69.         return $orderCatalog;
  70.     }
  71.     /**
  72.      * Checks if TicketProduct is bookable
  73.      */
  74.     public function isBookable(int $quantity 1): bool
  75.     {
  76.         if (!\Pimcore::inDebugMode() && $this->deactivateSkidataRequests()) {
  77.             return false;
  78.         }
  79.         $skidataProduct $this->getSkidataProduct();
  80.         if (null === $skidataProduct) {
  81.             return false;
  82.         }
  83.         if ($this->getOnlyToday()) {
  84.             if (Carbon::now()->lt(Carbon::now()->setTime(121000))) {
  85.                 return false;
  86.             }
  87.         }
  88.         return $skidataProduct->getActive() && $this->getReference()->isPublished();
  89.     }
  90.     public function isSingleRide(): bool
  91.     {
  92.         return (bool)$this->getSingleRideTicket();
  93.     }
  94.     public function isCancelable(OrderItem $orderItem null): bool
  95.     {
  96.         if ($orderItem) {
  97.             $currentAdminUser \Pimcore\Tool\Admin::getCurrentUser();
  98.             $skidataBrick $orderItem->getSkidataBrick();
  99.             // can not cancel if cancelled in Skidata - except for special users
  100.             if ($orderItem->isCanceledInSkidata() && !$currentAdminUser?->isAllowed('shop_backend_cancel_cancelled_in_skidata')) {
  101.                 return false;
  102.             }
  103.             // can cancel always if not submitted !
  104.             if (!$skidataBrick || $skidataBrick->getTransmisssionState() == ForwardingStateType::ERROR || $skidataBrick->getTransmisssionState() == null) {
  105.                 return true;
  106.             }
  107.             if (!$this->isCancelableInSkidata($orderItem)) {
  108.                 return false;
  109.             }
  110.             if (!$this->isSeasonTicket()) {
  111.                 if ($validityBrick $orderItem->getCustomized()->getOrderItemCustomizedValidityRange()) {
  112.                     // do not allow cancellation in between validity -> enhance to allow until 7 o clock if needed
  113.                     // this logic shouldn't be applied for seasonal tickets (attask #414766)
  114.                     // Root Cause for this: the Consumed State is only synced once a day ( at around 23:50)  so we can not rely on that -> hence  during validity cancellation is locked -> if not consumed it can be cancelled after validity again.
  115.                     if (Carbon::now()->between($validityBrick->getStartDate(), $validityBrick->getEndDate())) {
  116.                         return false;
  117.                     }
  118.                 }
  119.             }
  120.         }
  121.         return true;
  122.     }
  123.     public function isCancelableInSkidata(OrderItem $orderItem): bool
  124.     {
  125.         $skidataBrick $orderItem->getSkidataBrick();
  126.         if ($skidataBrick) {
  127.             /**
  128.              * Ein "TicketOrderItem" ist stornierbar falls sämtliche TicketItems:
  129.              * 1.) die Permission noch nicht "consumed" ist
  130.              * 2.) das TicketItem selbst stornierbar ist, entspricht folgendener Abfrage
  131.              * Booked ( BOOKED oder BOOKED_AND_REJECTED oder  BOOKED_AND_TRANSFERRED )
  132.              * ! reserved => wieder stornierbar ( #642504 / #624984)
  133.              *
  134.              * wenn bereits in Skidata storniert => cancel im Pimcore erlaubt (Pimcore-Storno, RĂĽckerstattung datatrans)
  135.              */
  136.             $salesStatus $skidataBrick->getSkiDataSalesStatus();
  137.             $cancelableSalesStates array_merge(SalesStatusEnum::getBookedSalesStates(), SalesStatusEnum::getCanceledSalesStates());
  138.             $cancelableSalesStates[] = SalesStatusEnum::Reserved;
  139.             if (!in_array($salesStatus$cancelableSalesStates)) {
  140.                 return false;
  141.             }
  142.             $permissionStatus $skidataBrick->getSkiDataPermissionStatus();
  143.             // not cancelable anymore if they are on this  states !
  144.             if (in_array($permissionStatus, [PermissionStatusEnum::ConsumedPermissionStatusEnum::Blocked])) {
  145.                 return false;
  146.             }
  147.             if ($skidataBrick->getSkiDataPermissionConsumed()) {
  148.                 return false;
  149.             }
  150.         }
  151.         return true;
  152.     }
  153.     public function getMetaProduct(): TicketMetaProduct|false
  154.     {
  155.         $listing = new TicketMetaProduct\Listing();
  156.         $listing->addConditionParam("find_in_set({$this->getId()}, relatedTo)");
  157.         return $listing->current();
  158.     }
  159.     public function getInsuranceMetaProduct(): TicketshopTicketAdditional|false
  160.     {
  161.         $listing = new TicketshopTicketAdditional\Listing();
  162.         $listing->addConditionParam("find_in_set({$this->getId()}, products)");
  163.         return $listing->current();
  164.     }
  165.     /**
  166.      * @param Carbon|null $startDate
  167.      *
  168.      * @return Carbon|null
  169.      */
  170.     public function getTicketEndDate(Carbon $startDate null): ?Carbon
  171.     {
  172.         $duration null;
  173.         $catalog $this->getCatalogObject();
  174.         if ($startDate == null) {
  175.             $startDate $this->getTicketStartDate();
  176.         }
  177.         if ($catalog && $catalog->getIsAnnualCatalog()) {
  178.             $yearlyDate = clone $startDate;
  179.             return $yearlyDate->addYear();
  180.         } elseif ($this->isSeasonTicket() && $catalog) {
  181.             return $catalog->getCurrentDateRangeEndDate();
  182.         }
  183.         if ($this->getValidityUnit() == ValidityUnit::DAY) {
  184.             $duration $this->getValidityValue() - 1// substract 1 as we use StartOfDay and EndOfDay hence 1 Day is always on the same days
  185.         } else {
  186.             //todo - Saisonkarten, Punktekarten? Stundenkarten?
  187.         }
  188.         //  sanity checks that it is at least 0 !! no negative numbers
  189.         if ($duration 0) {
  190.             $duration 0;
  191.         }
  192.         return $startDate $startDate->copy()->addDays($duration)->endOfDay() : null;
  193.     }
  194.     public function isSkidataBookableFor(TicketConsumerCategory $consumerCategory): bool
  195.     {
  196.         if ($this->getSkidataProduct() &&  !empty($this->getSkidataProduct()->getConsumerCategories())) {
  197.             $skidataConsumer $consumerCategory->getSkidataConsumerForSkidataProduct($this->getSkidataProduct());
  198.             if ($skidataConsumer) {
  199.                 return true;
  200.             }
  201.         }
  202.         return false;
  203.     }
  204.     public function getTicketPool(): string
  205.     {
  206.         $skiDataProduct $this->getReference()->getSkidataProduct();
  207.         if($skiDataProduct == null) {
  208.             return PoolType::UNASSIGNED;
  209.         }
  210.         $pool $skiDataProduct->getValidPoolName() ??  PoolType::UNASSIGNED;
  211.         if (!PoolType::isValid($pool)) {
  212.             $pool PoolType::UNKNOWN;
  213.         }
  214.         return $pool;
  215.     }
  216.     public function getOrCreateCloneWithPriceGroup(PriceGroupInterface $priceGroup): PriceGroupAwareProduct
  217.     {
  218.         if (!$priceGroup instanceof TicketConsumerCategory) {
  219.             throw new \InvalidArgumentException('PriceGroup must be instance of TicketConsumerCategory');
  220.         }
  221.         /** @phpstan-ignore-next-line  */
  222.         return $this->getReference()->getOrCreateOrderableObject($priceGroup,
  223.             $this->getTicketStartDate(),
  224.             $this->getAcquisitionType(),
  225.             $this->getCatalogObject());
  226.     }
  227. }