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

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