src/Ecommerce/PriceSystem/Event/PriceSystem.php line 49

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Ecommerce\PriceSystem\Event;
  7. use App\Ecommerce\PriceSystem\Ticket\PriceSystem as TicketPriceSystem;
  8. use App\Ecommerce\PricingManager\Environment;
  9. use App\Model\Shop\Event\EventProduct;
  10. use App\Model\Shop\Event\ShopDynamicPrice;
  11. use Carbon\Carbon;
  12. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory;
  13. use Elements\Bundle\TicketShopFrameworkBundle\Service\CartService;
  14. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  15. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\CheckoutableInterface;
  16. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\CachingPriceSystem;
  17. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\Price;
  18. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceSystemInterface;
  19. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\TaxManagement\TaxCalculationService;
  20. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\TaxManagement\TaxEntry;
  21. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PriceInfoInterface;
  22. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManagerLocatorInterface;
  23. use Pimcore\Bundle\EcommerceFrameworkBundle\Type\Decimal;
  24. use Pimcore\Model\DataObject\AbstractObject;
  25. use Pimcore\Model\DataObject\OnlineShopTaxClass;
  26. use Pimcore\Model\Exception\UnsupportedException;
  27. use Psr\Log\LoggerInterface;
  28. class PriceSystem extends CachingPriceSystem implements PriceSystemInterface
  29. {
  30.     public function __construct(
  31.         PricingManagerLocatorInterface $pricingManagers,
  32.         private CartService $cartService,
  33.         private TicketPriceSystem $ticketPriceSystem,
  34.         private LoggerInterface $logger
  35.     ) {
  36.         parent::__construct($pricingManagers);
  37.     }
  38.     protected function initPriceInfoInstance(mixed $quantityScaleCheckoutableInterface $product$products nullCarbon $date null): \Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface
  39.     {
  40.         $priceInfo $this->createPriceInfoInstance($quantityScale$product$products$date);
  41.         if ($quantityScale !== \Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface::MIN_PRICE) {
  42.             $priceInfo->setQuantity($quantityScale);
  43.         }
  44.         $priceInfo->setProduct($product);
  45.         $priceInfo->setProducts($products);
  46.         $priceInfo->setPriceSystem($this);
  47.         $priceInfo->setBaseAmount($priceInfo->getPrice()->getAmount()->asNumeric());
  48.         // apply pricing rules
  49.         /** @var \Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\PricingManager\PriceInfo $priceInfoWithRules */
  50.         $priceInfoWithRules $this->getPricingManager()->applyProductRules($priceInfo);
  51.         $priceInfoWithRules->setBaseAmount($priceInfo->getPrice()->getAmount());
  52.         /** @var Environment $env */
  53.         $env $priceInfoWithRules->getEnvironment();
  54.         $env->setCartForProduct($this->cartService->getCart());
  55.         if ($product instanceof EventProduct) {
  56.             $orderDate $product->getProductStartDate();
  57.             if ($orderDate) {
  58.                 $env->setStartDate($orderDate);
  59.             }
  60.         }
  61.         return $priceInfoWithRules;
  62.     }
  63.     /**
  64.      * @param mixed $quantityScale
  65.      * @param CheckoutableInterface $product
  66.      * @param CheckoutableInterface[] $products
  67.      * @param Carbon|null $date
  68.      *
  69.      * @return PriceInfo
  70.      */
  71.     public function createPriceInfoInstance(
  72.         mixed $quantityScale,
  73.         CheckoutableInterface $product,
  74.         mixed $products,
  75.         ?Carbon $date null
  76.     ): PriceInfo {
  77.         if (!$product instanceof EventProduct) {
  78.             throw new UnsupportedException('Object must be of type EventProduct');
  79.         }
  80.         $priceInfo = new PriceInfo($product);
  81.         $priceInfo->setQuantity($quantityScale);
  82.         $priceInfo->setPrices($this->getPrices($product$date));
  83.         $priceInfo->setPrice($this->calculatePrice($product$date));
  84.         $priceInfo->setDate($date);
  85.         $this->applyTaxes($product$priceInfo);
  86.         return $priceInfo;
  87.     }
  88.     /**
  89.      * @param EventProduct $product
  90.      * @param null|int $quantityScale
  91.      * @param array<mixed>|null $products
  92.      * @param Carbon|null $date
  93.      *
  94.      * @return PriceInfoInterface
  95.      */
  96.     public function getPriceInfo(CheckoutableInterface $product$quantityScale null$products nullCarbon $date null): PriceInfoInterface
  97.     {
  98.         $pId $product->getId();
  99.         if (empty($date)) {
  100.             $date $product->getEventStartDate();
  101.         }
  102.         if ($date) {
  103.             $pId .= $date->getTimestamp();
  104.         }
  105.         if (!array_key_exists($pId$this->priceInfos)) {
  106.             $this->priceInfos[$pId] = [];
  107.         }
  108.         $quantityScaleKey = (string)$quantityScale;
  109.         if (empty($this->priceInfos[$pId][$quantityScaleKey])) {
  110.             $priceInfo $this->initPriceInfoInstance($quantityScale$product$products$date);
  111.             $this->priceInfos[$pId][$quantityScaleKey] = $priceInfo;
  112.         }
  113.         /** @phpstan-ignore-next-line  */
  114.         return $this->priceInfos[$pId][$quantityScaleKey];
  115.     }
  116.     /**
  117.      * Calculate price for order item configuration
  118.      *
  119.      * @param EventProduct $product
  120.      * @param Carbon|null $date
  121.      *
  122.      * @return Price
  123.      */
  124.     protected function calculatePrice(EventProduct $productCarbon $date null): Price
  125.     {
  126.         $priceObj null;
  127.         if ($product->getType() == AbstractObject::OBJECT_TYPE_VARIANT) {
  128.             /** @var ShopDynamicPrice $priceObj */
  129.             $priceObj $product->getOrderedPriceItem();
  130.         } elseif ($priceObjects $product->getPriceObjects((bool)$product->getSkidataProduct())) {
  131.             $priceObj $priceObjects[0];
  132.         }
  133.         $voucherPrice $product->hasVouchers() ? $priceObj?->getPrice($date) : 0;
  134.         $ticketPrice Decimal::zero();
  135.         if ($product->getSkidataProduct($priceObj) && ($consumerCategory $priceObj?->getConsumerCategory()) && $consumerCategory instanceof TicketConsumerCategory) {
  136.             try {
  137.                 $ticketPrice $this->ticketPriceSystem->fetchSkidataPrice($product$consumerCategory$date)->getAmount();
  138.             } catch (\Exception $e) {
  139.                 if (\Pimcore::inDebugMode()) {
  140.                     p_r($e->getMessage());
  141.                 }
  142.                 $this->logger->error('Failed to get skidata price for ' $product->getSkidataProduct($priceObj)->getId() . ' (for Event ' $product->getId() . ': ' $e->getMessage() . ' -> using fallback price...');
  143.             }
  144.         }
  145.         $additionalPrice $priceObj instanceof ShopDynamicPrice $priceObj->getAdditionalPrice() : 0;
  146.         $totalPrice $ticketPrice->add($voucherPrice $additionalPrice);
  147.         return new Price($totalPriceFactory::getInstance()->getEnvironment()->getDefaultCurrency(), true);
  148.     }
  149.     /**
  150.      * Get prices from shopPrice descriptions
  151.      *
  152.      * @param EventProduct $product
  153.      * @param ?Carbon $date
  154.      *
  155.      * @return Price[]
  156.      */
  157.     public function getPrices(EventProduct $productCarbon $date null): array
  158.     {
  159.         $prices = [];
  160.         foreach ($product->getPrices() as $priceRelation) {
  161.             /** @var ShopDynamicPrice $dynamicPriceObject */
  162.             $dynamicPriceObject $priceRelation->getObject();
  163.             $prices[$dynamicPriceObject->getId()] = new Price(
  164.                 Decimal::fromNumeric($dynamicPriceObject->getPrice($product->getProductStartDate() ?: $date) ?: 0),
  165.                 Factory::getInstance()->getEnvironment()->getDefaultCurrency(), true
  166.             );
  167.         }
  168.         return $prices;
  169.     }
  170.     /**
  171.      * @param EventProduct $product
  172.      *
  173.      * @return OnlineShopTaxClass
  174.      */
  175.     public function getTaxClassForProduct(CheckoutableInterface $product): OnlineShopTaxClass
  176.     {
  177.         $taxRate $this->getDefaultTaxClass();
  178.         $startDate $product->getEventStartDate() ?? Carbon::today();
  179.         if ($startDate->year == 2024 && $taxRate->getNewTaxRate()) {
  180.             $taxRate $taxRate->getNewTaxRate();
  181.         }
  182.         return $taxRate;
  183.     }
  184.     /**
  185.      * @param EventProduct $product
  186.      * @param \Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface $priceInfo
  187.      *
  188.      * @return void
  189.      *
  190.      * @throws \Pimcore\Bundle\EcommerceFrameworkBundle\Exception\UnsupportedException
  191.      */
  192.     protected function applyTaxes(CheckoutableInterface $product\Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface $priceInfo): void
  193.     {
  194.         $price $priceInfo->getPrice();
  195.         $taxClass $this->getTaxClassForProduct($product);
  196.         $totalPrice $priceInfo->getTotalPrice();
  197.         if ($taxClass->getTaxEntryCombinationType()) {
  198.             $price->setTaxEntryCombinationMode($taxClass->getTaxEntryCombinationType());
  199.             $price->setTaxEntries(TaxEntry::convertTaxEntries($taxClass));
  200.             $totalPrice->setTaxEntryCombinationMode($taxClass->getTaxEntryCombinationType());
  201.             $totalPrice->setTaxEntries(TaxEntry::convertTaxEntries($taxClass));
  202.         }
  203.         $taxCalculationService $this->getTaxCalculationService();
  204.         $taxCalculationService->updateTaxes($priceTaxCalculationService::CALCULATION_FROM_GROSS);
  205.         $taxCalculationService->updateTaxes($totalPriceTaxCalculationService::CALCULATION_FROM_GROSS);
  206.     }
  207. }