vendor/pimcore/pimcore/bundles/EcommerceFrameworkBundle/CartManager/AbstractCart.php line 735

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\EcommerceFrameworkBundle\CartManager;
  15. use Pimcore\Bundle\EcommerceFrameworkBundle\Exception\InvalidConfigException;
  16. use Pimcore\Bundle\EcommerceFrameworkBundle\Exception\VoucherServiceException;
  17. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  18. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\AbstractSetProductEntry;
  19. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\CheckoutableInterface;
  20. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\MockProduct;
  21. use Pimcore\Bundle\EcommerceFrameworkBundle\VoucherService\PricingManagerTokenInformation;
  22. use Pimcore\Bundle\EcommerceFrameworkBundle\VoucherService\Reservation;
  23. use Pimcore\Logger;
  24. use Pimcore\Model\AbstractModel;
  25. use Pimcore\Model\DataObject\Concrete;
  26. abstract class AbstractCart extends AbstractModel implements CartInterface
  27. {
  28.     /**
  29.      * @var int
  30.      */
  31.     protected $userId;
  32.     /**
  33.      * @var CartItemInterface[]|null
  34.      */
  35.     protected $items;
  36.     /**
  37.      * @var array
  38.      */
  39.     public $checkoutData = [];
  40.     /**
  41.      * @var string
  42.      */
  43.     protected $name;
  44.     /**
  45.      * @var \DateTime|null
  46.      */
  47.     protected $creationDate;
  48.     /**
  49.      * @var int|null
  50.      */
  51.     protected $creationDateTimestamp;
  52.     /**
  53.      * @var \DateTime|null
  54.      */
  55.     protected $modificationDate;
  56.     /**
  57.      * @var int|null
  58.      */
  59.     protected $modificationDateTimestamp;
  60.     /**
  61.      * @var string|int|null
  62.      */
  63.     protected $id;
  64.     /**
  65.      * @var CartItemInterface[]
  66.      */
  67.     protected $giftItems = [];
  68.     /**
  69.      * @var CartPriceCalculatorInterface|null
  70.      */
  71.     protected $priceCalculator;
  72.     /**
  73.      * @var int|null
  74.      */
  75.     protected $itemAmount;
  76.     /**
  77.      * @var int|null
  78.      */
  79.     protected $subItemAmount;
  80.     /**
  81.      * @var int|null
  82.      */
  83.     protected $mainAndSubItemAmount;
  84.     /**
  85.      * @var int|null
  86.      */
  87.     protected $itemCount;
  88.     /**
  89.      * @var int|null
  90.      */
  91.     protected $subItemCount;
  92.     /**
  93.      * @var int|null
  94.      */
  95.     protected $mainAndSubItemCount;
  96.     public function __construct()
  97.     {
  98.         $this->setCreationDate(new \DateTime());
  99.     }
  100.     /**
  101.      * @return string
  102.      */
  103.     abstract protected function getCartItemClassName();
  104.     /**
  105.      * @return string
  106.      */
  107.     abstract protected function getCartCheckoutDataClassName();
  108.     /**
  109.      * @param CheckoutableInterface&Concrete $product
  110.      * @param int $count
  111.      * @param string|null $itemKey
  112.      * @param bool $replace
  113.      * @param array $params
  114.      * @param AbstractSetProductEntry[] $subProducts
  115.      * @param string|null $comment
  116.      *
  117.      * @return string
  118.      */
  119.     public function addItem(CheckoutableInterface $product$count$itemKey null$replace false$params = [], $subProducts = [], $comment null)
  120.     {
  121.         if (empty($itemKey)) {
  122.             $itemKey $product->getId();
  123.             if (!empty($subProducts)) {
  124.                 $itemKey $itemKey '_' uniqid();
  125.             }
  126.         }
  127.         return $this->updateItem($itemKey$product$count$replace$params$subProducts$comment);
  128.     }
  129.     /**
  130.      * @param string $itemKey
  131.      * @param CheckoutableInterface&Concrete $product
  132.      * @param int $count
  133.      * @param bool $replace
  134.      * @param array $params
  135.      * @param AbstractSetProductEntry[] $subProducts
  136.      * @param string|null $comment
  137.      *
  138.      * @return string
  139.      */
  140.     public function updateItem($itemKeyCheckoutableInterface $product$count$replace false$params = [], $subProducts = [], $comment null)
  141.     {
  142.         //load items first in order to lazyload items (if they are lazy loaded)
  143.         $this->getItems();
  144.         if (!array_key_exists($itemKey$this->items)) {
  145.             $className $this->getCartItemClassName();
  146.             $item = new $className();
  147.             $item->setCart($this);
  148.         } else {
  149.             $item $this->items[$itemKey];
  150.         }
  151.         $item->setProduct($product);
  152.         $item->setItemKey($itemKey);
  153.         if ($comment !== null) {
  154.             $item->setComment($comment);
  155.         }
  156.         if ($replace) {
  157.             $item->setCount($count);
  158.         } else {
  159.             $item->setCount($item->getCount() + $count);
  160.         }
  161.         if (!empty($subProducts)) {
  162.             $subItems = [];
  163.             foreach ($subProducts as $subProduct) {
  164.                 if (array_key_exists($subProduct->getProduct()->getId(), $subItems)) {
  165.                     $subItem $subItems[$subProduct->getProduct()->getId()];
  166.                     $subItem->setCount($subItem->getCount() + $subProduct->getQuantity());
  167.                 } else {
  168.                     $className $this->getCartItemClassName();
  169.                     $subItem = new $className();
  170.                     $subItem->setCart($this);
  171.                     $subItem->setItemKey($subProduct->getProduct()->getId());
  172.                     $subItem->setProduct($subProduct->getProduct());
  173.                     $subItem->setCount($subProduct->getQuantity());
  174.                     $subItems[$subProduct->getProduct()->getId()] = $subItem;
  175.                 }
  176.             }
  177.             $item->setSubItems($subItems);
  178.         }
  179.         $this->items[$itemKey] = $item;
  180.         // trigger cart has been modified
  181.         $this->modified();
  182.         return $itemKey;
  183.     }
  184.     /**
  185.      * updates count of specific cart item
  186.      *
  187.      * @param string $itemKey
  188.      * @param int $count
  189.      *
  190.      * @return CartItemInterface
  191.      */
  192.     public function updateItemCount($itemKey$count)
  193.     {
  194.         //load items first in order to lazyload items (if they are lazy loaded)
  195.         $this->getItems();
  196.         if (!empty($this->items[$itemKey])) {
  197.             $this->items[$itemKey]->setCount($count);
  198.         }
  199.         return $this->items[$itemKey];
  200.     }
  201.     /**
  202.      * @param CheckoutableInterface&Concrete $product
  203.      * @param int $count
  204.      * @param string|null $itemKey
  205.      * @param bool $replace
  206.      * @param array $params
  207.      * @param array $subProducts
  208.      * @param string|null $comment
  209.      *
  210.      * @return string
  211.      */
  212.     public function addGiftItem(CheckoutableInterface $product$count$itemKey null$replace false$params = [], $subProducts = [], $comment null)
  213.     {
  214.         if (empty($itemKey)) {
  215.             $itemKey $product->getId();
  216.             if (!empty($subProducts)) {
  217.                 $itemKey $itemKey '_' uniqid();
  218.             }
  219.         }
  220.         return $this->updateGiftItem($itemKey$product$count$replace$params$subProducts$comment);
  221.     }
  222.     /**
  223.      * @param string $itemKey
  224.      * @param CheckoutableInterface&Concrete $product
  225.      * @param int $count
  226.      * @param bool $replace
  227.      * @param array $params
  228.      * @param array $subProducts
  229.      * @param string|null $comment
  230.      *
  231.      * @return string
  232.      */
  233.     public function updateGiftItem($itemKeyCheckoutableInterface $product$count$replace false$params = [], $subProducts = [], $comment null)
  234.     {
  235.         // item already exists?
  236.         if (!array_key_exists($itemKey$this->giftItems)) {
  237.             $className $this->getCartItemClassName();
  238.             $item = new $className();
  239.             $item->setCart($this);
  240.         } else {
  241.             $item $this->giftItems[$itemKey];
  242.         }
  243.         // update item
  244.         $item->setProduct($productfalse);
  245.         $item->setItemKey($itemKey);
  246.         $item->setComment($comment);
  247.         if ($replace) {
  248.             $item->setCount($countfalse);
  249.         } else {
  250.             $item->setCount($item->getCount() + $countfalse);
  251.         }
  252.         // handle sub products
  253.         if (!empty($subProducts)) {
  254.             $subItems = [];
  255.             foreach ($subProducts as $subProduct) {
  256.                 if (isset($subItems[$subProduct->getProduct()->getId()])) {
  257.                     $subItem $subItems[$subProduct->getProduct()->getId()];
  258.                     $subItem->setCount($subItem->getCount() + $subProduct->getQuantity());
  259.                 } else {
  260.                     $className $this->getCartItemClassName();
  261.                     $subItem = new $className();
  262.                     $subItem->setCart($this);
  263.                     $subItem->setItemKey($subProduct->getProduct()->getId());
  264.                     $subItem->setProduct($subProduct->getProduct());
  265.                     $subItem->setCount($subProduct->getQuantity());
  266.                     $subItems[$subProduct->getProduct()->getId()] = $subItem;
  267.                 }
  268.             }
  269.             $item->setSubItems($subItems);
  270.         }
  271.         $this->giftItems[$itemKey] = $item;
  272.         return $itemKey;
  273.     }
  274.     public function clear()
  275.     {
  276.         $this->items = [];
  277.         $this->giftItems = [];
  278.         $this->removeAllVoucherTokens();
  279.         // trigger cart has been modified
  280.         $this->modified();
  281.     }
  282.     /**
  283.      * @param string $countSubItems - use one of COUNT_MAIN_ITEMS_ONLY, COUNT_MAIN_OR_SUB_ITEMS, COUNT_MAIN_AND_SUB_ITEMS
  284.      *
  285.      * @return int
  286.      */
  287.     public function getItemAmount(string $countSubItems self::COUNT_MAIN_ITEMS_ONLY)
  288.     {
  289.         switch ($countSubItems) {
  290.             case self::COUNT_MAIN_OR_SUB_ITEMS:
  291.                 if ($this->subItemAmount == null) {
  292.                     $count 0;
  293.                     $items $this->getItems();
  294.                     if (!empty($items)) {
  295.                         foreach ($items as $item) {
  296.                             $subItems $item->getSubItems();
  297.                             if ($subItems) {
  298.                                 foreach ($subItems as $subItem) {
  299.                                     $count += ($subItem->getCount() * $item->getCount());
  300.                                 }
  301.                             } else {
  302.                                 $count += $item->getCount();
  303.                             }
  304.                         }
  305.                     }
  306.                     $this->subItemAmount $count;
  307.                 }
  308.                 return $this->subItemAmount;
  309.             case self::COUNT_MAIN_AND_SUB_ITEMS:
  310.                 if ($this->mainAndSubItemAmount == null) {
  311.                     $count 0;
  312.                     $items $this->getItems();
  313.                     if (!empty($items)) {
  314.                         foreach ($items as $item) {
  315.                             $subItems $item->getSubItems();
  316.                             if ($subItems) {
  317.                                 foreach ($subItems as $subItem) {
  318.                                     $count += ($subItem->getCount() * $item->getCount());
  319.                                 }
  320.                             }
  321.                             $count += $item->getCount();
  322.                         }
  323.                     }
  324.                     $this->mainAndSubItemAmount $count;
  325.                 }
  326.                 return $this->mainAndSubItemAmount;
  327.             case self::COUNT_MAIN_ITEMS_ONLY:
  328.                 if ($this->itemAmount == null) {
  329.                     $count 0;
  330.                     $items $this->getItems();
  331.                     if (!empty($items)) {
  332.                         foreach ($items as $item) {
  333.                             $count += $item->getCount();
  334.                         }
  335.                     }
  336.                     $this->itemAmount $count;
  337.                 }
  338.                 return $this->itemAmount;
  339.             default:
  340.                 throw new InvalidConfigException('Invalid value for $countSubItems: ' $countSubItems);
  341.         }
  342.     }
  343.     /**
  344.      * @param string $countSubItems - use one of COUNT_MAIN_ITEMS_ONLY, COUNT_MAIN_OR_SUB_ITEMS, COUNT_MAIN_AND_SUB_ITEMS
  345.      *
  346.      * @return int
  347.      */
  348.     public function getItemCount(string $countSubItems self::COUNT_MAIN_ITEMS_ONLY)
  349.     {
  350.         switch ($countSubItems) {
  351.             case self::COUNT_MAIN_OR_SUB_ITEMS:
  352.                 if ($this->subItemCount == null) {
  353.                     $items $this->getItems();
  354.                     $count 0;
  355.                     if (!empty($items)) {
  356.                         foreach ($items as $item) {
  357.                             $subItems $item->getSubItems();
  358.                             if (!empty($subItems)) {
  359.                                 $count += count($subItems);
  360.                             } else {
  361.                                 $count++;
  362.                             }
  363.                         }
  364.                     }
  365.                     $this->subItemCount $count;
  366.                 }
  367.                 return $this->subItemCount;
  368.             case self::COUNT_MAIN_AND_SUB_ITEMS:
  369.                 if ($this->mainAndSubItemCount == null) {
  370.                     $items $this->getItems();
  371.                     $count count($items);
  372.                     if (!empty($items)) {
  373.                         foreach ($items as $item) {
  374.                             $subItems $item->getSubItems();
  375.                             $count += count($subItems);
  376.                         }
  377.                     }
  378.                     $this->mainAndSubItemCount $count;
  379.                 }
  380.                 return $this->mainAndSubItemCount;
  381.             case self::COUNT_MAIN_ITEMS_ONLY:
  382.                 if ($this->itemCount == null) {
  383.                     $items $this->getItems();
  384.                     $this->itemCount count($items);
  385.                 }
  386.                 return $this->itemCount;
  387.             default:
  388.                 throw new InvalidConfigException('Invalid value for $countSubItems: ' $countSubItems);
  389.         }
  390.     }
  391.     /**
  392.      * @return CartItemInterface[]
  393.      */
  394.     public function getItems()
  395.     {
  396.         $this->items $this->items $this->items : [];
  397.         return $this->items;
  398.     }
  399.     /**
  400.      * @param string $itemKey
  401.      *
  402.      * @return CartItemInterface|null
  403.      */
  404.     public function getItem($itemKey)
  405.     {
  406.         //load items first in order to lazyload items (if they are lazy loaded)
  407.         $this->getItems();
  408.         return array_key_exists($itemKey$this->items) ? $this->items[$itemKey] : null;
  409.     }
  410.     /**
  411.      * @return bool
  412.      */
  413.     public function isEmpty()
  414.     {
  415.         return count($this->getItems()) === 0;
  416.     }
  417.     /**
  418.      * @return CartItemInterface[]
  419.      */
  420.     public function getGiftItems()
  421.     {
  422.         //make sure that cart is calculated
  423.         if (!$this->getPriceCalculator()->isCalculated()) {
  424.             $this->getPriceCalculator()->calculate();
  425.         }
  426.         return $this->giftItems;
  427.     }
  428.     /**
  429.      * @param string $itemKey
  430.      *
  431.      * @return CartItemInterface|null
  432.      */
  433.     public function getGiftItem($itemKey)
  434.     {
  435.         //make sure that cart is calculated
  436.         if (!$this->getPriceCalculator()->isCalculated()) {
  437.             $this->getPriceCalculator()->calculate();
  438.         }
  439.         return array_key_exists($itemKey$this->giftItems) ? $this->giftItems[$itemKey] : null;
  440.     }
  441.     /**
  442.      * @param CartItemInterface[]|null $items
  443.      */
  444.     public function setItems($items)
  445.     {
  446.         $this->items $items;
  447.         // trigger cart has been modified
  448.         $this->modified();
  449.     }
  450.     /**
  451.      * @param string $itemKey
  452.      */
  453.     public function removeItem($itemKey)
  454.     {
  455.         //load items first in order to lazyload items (if they are lazy loaded)
  456.         $this->getItems();
  457.         unset($this->items[$itemKey]);
  458.         // trigger cart has been modified
  459.         $this->modified();
  460.     }
  461.     /**
  462.      * @param string $name
  463.      */
  464.     public function setName($name)
  465.     {
  466.         $this->name $name;
  467.     }
  468.     /**
  469.      * @return string
  470.      */
  471.     public function getName()
  472.     {
  473.         return $this->name;
  474.     }
  475.     /**
  476.      * @return bool
  477.      */
  478.     public function getIsBookable()
  479.     {
  480.         foreach ($this->getItems() as $item) {
  481.             if (!$item->getProduct()->getOSIsBookable($item->getCount())) {
  482.                 return false;
  483.             }
  484.         }
  485.         return true;
  486.     }
  487.     /**
  488.      * @param string|int $id
  489.      */
  490.     public function setId($id)
  491.     {
  492.         $this->id $id;
  493.     }
  494.     /**
  495.      * @return string|int|null
  496.      */
  497.     public function getId()
  498.     {
  499.         return $this->id;
  500.     }
  501.     /**
  502.      * @return \DateTime
  503.      */
  504.     public function getCreationDate()
  505.     {
  506.         if (empty($this->creationDate) && $this->creationDateTimestamp) {
  507.             $this->creationDate = new \DateTime();
  508.             $this->creationDate->setTimestamp($this->creationDateTimestamp);
  509.         }
  510.         return $this->creationDate;
  511.     }
  512.     /**
  513.      * @param \DateTime|null $creationDate
  514.      */
  515.     public function setCreationDate(\DateTime $creationDate null)
  516.     {
  517.         $this->creationDate $creationDate;
  518.         if ($creationDate) {
  519.             $this->creationDateTimestamp $creationDate->getTimestamp();
  520.         } else {
  521.             $this->creationDateTimestamp null;
  522.         }
  523.     }
  524.     /**
  525.      * @param int $creationDateTimestamp
  526.      */
  527.     public function setCreationDateTimestamp($creationDateTimestamp)
  528.     {
  529.         $this->creationDateTimestamp $creationDateTimestamp;
  530.         $this->creationDate null;
  531.     }
  532.     /**
  533.      * @return int
  534.      */
  535.     public function getCreationDateTimestamp()
  536.     {
  537.         return $this->creationDateTimestamp;
  538.     }
  539.     /**
  540.      * @return \DateTime|null
  541.      */
  542.     public function getModificationDate()
  543.     {
  544.         if (empty($this->modificationDate) && $this->modificationDateTimestamp) {
  545.             $this->modificationDate = new \DateTime();
  546.             $this->modificationDate->setTimestamp($this->modificationDateTimestamp);
  547.         }
  548.         return $this->modificationDate;
  549.     }
  550.     /**
  551.      * @param \DateTime|null $modificationDate
  552.      */
  553.     public function setModificationDate(\DateTime $modificationDate null)
  554.     {
  555.         $this->modificationDate $modificationDate;
  556.         if ($modificationDate) {
  557.             $this->modificationDateTimestamp $modificationDate->getTimestamp();
  558.         } else {
  559.             $this->modificationDateTimestamp null;
  560.         }
  561.     }
  562.     /**
  563.      * @param int $modificationDateTimestamp
  564.      */
  565.     public function setModificationDateTimestamp($modificationDateTimestamp)
  566.     {
  567.         $this->modificationDateTimestamp $modificationDateTimestamp;
  568.         $this->modificationDate null;
  569.     }
  570.     /**
  571.      * @return int|null
  572.      */
  573.     public function getModificationDateTimestamp()
  574.     {
  575.         return $this->modificationDateTimestamp;
  576.     }
  577.     /**
  578.      * @return int
  579.      */
  580.     public function getUserId()
  581.     {
  582.         return $this->userId ?: Factory::getInstance()->getEnvironment()->getCurrentUserId();
  583.     }
  584.     /**
  585.      * @param int $userId
  586.      */
  587.     public function setUserId($userId)
  588.     {
  589.         $this->userId = (int)$userId;
  590.     }
  591.     /**
  592.      * @return void
  593.      */
  594.     abstract public function save();
  595.     /**
  596.      * @return void
  597.      */
  598.     abstract public function delete();
  599.     /**
  600.      * @param string $key
  601.      *
  602.      * @return string|null
  603.      */
  604.     public function getCheckoutData($key)
  605.     {
  606.         $entry $this->checkoutData[$key] ?? null;
  607.         if ($entry) {
  608.             return $this->checkoutData[$key]->getData();
  609.         }
  610.         return null;
  611.     }
  612.     /**
  613.      * @param string $key
  614.      * @param string $data
  615.      */
  616.     public function setCheckoutData($key$data)
  617.     {
  618.         $className $this->getCartCheckoutDataClassName();
  619.         $value = new $className();
  620.         $value->setCart($this);
  621.         $value->setKey($key);
  622.         $value->setData($data);
  623.         $this->checkoutData[$key] = $value;
  624.     }
  625.     /**
  626.      * @return CartPriceCalculatorInterface
  627.      */
  628.     public function getPriceCalculator()
  629.     {
  630.         if (empty($this->priceCalculator)) {
  631.             $this->priceCalculator Factory::getInstance()->getCartManager()->getCartPriceCalculator($this);
  632.         }
  633.         return $this->priceCalculator;
  634.     }
  635.     /**
  636.      * @param CartPriceCalculatorInterface $priceCalculator
  637.      */
  638.     public function setPriceCalculator(CartPriceCalculatorInterface $priceCalculator)
  639.     {
  640.         $this->priceCalculator $priceCalculator;
  641.     }
  642.     /**
  643.      * @return $this
  644.      */
  645.     public function modified()
  646.     {
  647.         $this->setModificationDateTimestamp(time());
  648.         $this->itemAmount null;
  649.         $this->subItemAmount null;
  650.         $this->mainAndSubItemAmount null;
  651.         $this->itemCount null;
  652.         $this->subItemCount null;
  653.         $this->mainAndSubItemCount null;
  654.         //don't use getter here because reset is only necessary if price calculator is already there
  655.         if ($this->priceCalculator) {
  656.             $this->priceCalculator->reset();
  657.         }
  658.         $this->validateVoucherTokenReservations();
  659.         $this->giftItems = [];
  660.         return $this;
  661.     }
  662.     /**
  663.      * @param int $count
  664.      *
  665.      * @return array<int, CartItemInterface>
  666.      */
  667.     public function getRecentlyAddedItems($count)
  668.     {
  669.         // get last items
  670.         $index = [];
  671.         foreach ($this->getItems() as $item) {
  672.             $index[$item->getAddedDate()->getTimestamp()] = $item;
  673.         }
  674.         krsort($index);
  675.         return array_slice($index0$count);
  676.     }
  677.     /**
  678.      * sorts all items in cart according to a given callback function
  679.      *
  680.      * @param callable $value_compare_func
  681.      *
  682.      * @return $this
  683.      */
  684.     public function sortItems(callable $value_compare_func)
  685.     {
  686.         return $this;
  687.     }
  688.     /**
  689.      * Adds a voucher token to the cart's checkout data and reserves it.
  690.      *
  691.      * @param string $code
  692.      *
  693.      * @return bool
  694.      *
  695.      * @throws \Exception
  696.      */
  697.     public function addVoucherToken($code)
  698.     {
  699.         $service Factory::getInstance()->getVoucherService();
  700.         if ($service->checkToken($code$this)) {
  701.             if ($service->reserveToken($code$this)) {
  702.                 $index 'voucher_' $code;
  703.                 $this->setCheckoutData($index$code);
  704.                 $this->save();
  705.                 $this->modified();
  706.                 return true;
  707.             }
  708.         }
  709.         return false;
  710.     }
  711.     /**
  712.      * Checks if an error code is a defined Voucher Error Code.
  713.      *
  714.      * @param int $errorCode
  715.      *
  716.      * @return bool
  717.      */
  718.     public function isVoucherErrorCode($errorCode)
  719.     {
  720.         return $errorCode && $errorCode 10;
  721.     }
  722.     /**
  723.      * Removes all tokens form cart and releases the token reservations.
  724.      *
  725.      * @throws InvalidConfigException
  726.      */
  727.     public function removeAllVoucherTokens()
  728.     {
  729.         foreach ($this->getVoucherTokenCodes() as $code) {
  730.             $this->removeVoucherToken($code);
  731.         }
  732.     }
  733.     /**
  734.      * Removes a token from cart and releases token reservation.
  735.      *
  736.      * @param string $code
  737.      *
  738.      * @throws \Exception
  739.      *
  740.      * @return bool
  741.      */
  742.     public function removeVoucherToken($code)
  743.     {
  744.         $service Factory::getInstance()->getVoucherService();
  745.         $key array_search($code$this->getVoucherTokenCodes());
  746.         if ($key !== false) {
  747.             if ($service->releaseToken($code$this)) {
  748.                 unset($this->checkoutData['voucher_' $code]);
  749.                 $this->save();
  750.                 $this->modified();
  751.                 return true;
  752.             }
  753.         } else {
  754.             throw new VoucherServiceException('No Token with code ' $code ' in this cart.'VoucherServiceException::ERROR_CODE_NOT_FOUND_IN_CART);
  755.         }
  756.         return false;
  757.     }
  758.     /**
  759.      * Filters checkout data and returns an array of strings with the assigns tokens.
  760.      *
  761.      * @return string[]
  762.      */
  763.     public function getVoucherTokenCodes()
  764.     {
  765.         $tokens = [];
  766.         foreach ($this->checkoutData as $key => $value) {
  767.             $exp_key explode('_'$key);
  768.             if ($exp_key[0] == 'voucher') {
  769.                 $tokens[] = $value->getData();
  770.             }
  771.         }
  772.         return $tokens;
  773.     }
  774.     /**
  775.      * @return PricingManagerTokenInformation[]
  776.      */
  777.     public function getPricingManagerTokenInformationDetails(): array
  778.     {
  779.         $voucherService Factory::getInstance()->getVoucherService();
  780.         return $voucherService->getPricingManagerTokenInformationDetails($this);
  781.     }
  782.     /**
  783.      * Checks if checkout data voucher tokens are valid reservations
  784.      */
  785.     protected function validateVoucherTokenReservations()
  786.     {
  787.         if ($this->getVoucherTokenCodes()) {
  788.             $order Factory::getInstance()->getOrderManager()->getOrderFromCart($this);
  789.             $appliedVoucherCodes = [];
  790.             if ($order) {
  791.                 foreach ($order->getVoucherTokens() as $voucherToken) {
  792.                     $appliedVoucherCodes[$voucherToken->getToken()] = $voucherToken->getToken();
  793.                 }
  794.             }
  795.             //check for each voucher token if reservation is valid or it is already applied to order
  796.             foreach ($this->getVoucherTokenCodes() as $code) {
  797.                 $reservation Reservation::get($code$this);
  798.                 if (!$reservation && !array_key_exists($code$appliedVoucherCodes)) {
  799.                     unset($this->checkoutData['voucher_'.$code]);
  800.                 }
  801.             }
  802.         }
  803.     }
  804.     /**
  805.      * Should be added to the cart
  806.      *
  807.      * @param CartItemInterface $item
  808.      *
  809.      * @return bool
  810.      */
  811.     protected static function isValidCartItem(CartItemInterface $item)
  812.     {
  813.         $product $item->getProduct();
  814.         if ($product instanceof CheckoutableInterface && !$product instanceof MockProduct) {
  815.             return true;
  816.         }
  817.         Logger::warn('Product ' $item->getProduct()->getId() . ' not found');
  818.         return false;
  819.     }
  820. }