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

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|null
  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.      * @throws InvalidConfigException
  288.      */
  289.     public function getItemAmount(string $countSubItems self::COUNT_MAIN_ITEMS_ONLY)
  290.     {
  291.         switch ($countSubItems) {
  292.             case self::COUNT_MAIN_OR_SUB_ITEMS:
  293.                 if ($this->subItemAmount == null) {
  294.                     $count 0;
  295.                     $items $this->getItems();
  296.                     if (!empty($items)) {
  297.                         foreach ($items as $item) {
  298.                             $subItems $item->getSubItems();
  299.                             if ($subItems) {
  300.                                 foreach ($subItems as $subItem) {
  301.                                     $count += ($subItem->getCount() * $item->getCount());
  302.                                 }
  303.                             } else {
  304.                                 $count += $item->getCount();
  305.                             }
  306.                         }
  307.                     }
  308.                     $this->subItemAmount $count;
  309.                 }
  310.                 return $this->subItemAmount;
  311.             case self::COUNT_MAIN_AND_SUB_ITEMS:
  312.                 if ($this->mainAndSubItemAmount == null) {
  313.                     $count 0;
  314.                     $items $this->getItems();
  315.                     if (!empty($items)) {
  316.                         foreach ($items as $item) {
  317.                             $subItems $item->getSubItems();
  318.                             if ($subItems) {
  319.                                 foreach ($subItems as $subItem) {
  320.                                     $count += ($subItem->getCount() * $item->getCount());
  321.                                 }
  322.                             }
  323.                             $count += $item->getCount();
  324.                         }
  325.                     }
  326.                     $this->mainAndSubItemAmount $count;
  327.                 }
  328.                 return $this->mainAndSubItemAmount;
  329.             case self::COUNT_MAIN_ITEMS_ONLY:
  330.                 if ($this->itemAmount == null) {
  331.                     $count 0;
  332.                     $items $this->getItems();
  333.                     if (!empty($items)) {
  334.                         foreach ($items as $item) {
  335.                             $count += $item->getCount();
  336.                         }
  337.                     }
  338.                     $this->itemAmount $count;
  339.                 }
  340.                 return $this->itemAmount;
  341.             default:
  342.                 throw new InvalidConfigException('Invalid value for $countSubItems: ' $countSubItems);
  343.         }
  344.     }
  345.     /**
  346.      * @param string $countSubItems - use one of COUNT_MAIN_ITEMS_ONLY, COUNT_MAIN_OR_SUB_ITEMS, COUNT_MAIN_AND_SUB_ITEMS
  347.      *
  348.      * @return int
  349.      */
  350.     public function getItemCount(string $countSubItems self::COUNT_MAIN_ITEMS_ONLY)
  351.     {
  352.         switch ($countSubItems) {
  353.             case self::COUNT_MAIN_OR_SUB_ITEMS:
  354.                 if ($this->subItemCount == null) {
  355.                     $items $this->getItems();
  356.                     $count 0;
  357.                     if (!empty($items)) {
  358.                         foreach ($items as $item) {
  359.                             $subItems $item->getSubItems();
  360.                             if (!empty($subItems)) {
  361.                                 $count += count($subItems);
  362.                             } else {
  363.                                 $count++;
  364.                             }
  365.                         }
  366.                     }
  367.                     $this->subItemCount $count;
  368.                 }
  369.                 return $this->subItemCount;
  370.             case self::COUNT_MAIN_AND_SUB_ITEMS:
  371.                 if ($this->mainAndSubItemCount == null) {
  372.                     $items $this->getItems();
  373.                     $count count($items);
  374.                     if (!empty($items)) {
  375.                         foreach ($items as $item) {
  376.                             $subItems $item->getSubItems();
  377.                             $count += count($subItems);
  378.                         }
  379.                     }
  380.                     $this->mainAndSubItemCount $count;
  381.                 }
  382.                 return $this->mainAndSubItemCount;
  383.             case self::COUNT_MAIN_ITEMS_ONLY:
  384.                 if ($this->itemCount == null) {
  385.                     $items $this->getItems();
  386.                     $this->itemCount count($items);
  387.                 }
  388.                 return $this->itemCount;
  389.             default:
  390.                 throw new InvalidConfigException('Invalid value for $countSubItems: ' $countSubItems);
  391.         }
  392.     }
  393.     /**
  394.      * @return CartItemInterface[]
  395.      */
  396.     public function getItems()
  397.     {
  398.         $this->items $this->items $this->items : [];
  399.         return $this->items;
  400.     }
  401.     /**
  402.      * @param string $itemKey
  403.      *
  404.      * @return CartItemInterface|null
  405.      */
  406.     public function getItem($itemKey)
  407.     {
  408.         //load items first in order to lazyload items (if they are lazy loaded)
  409.         $this->getItems();
  410.         return array_key_exists($itemKey$this->items) ? $this->items[$itemKey] : null;
  411.     }
  412.     /**
  413.      * @return bool
  414.      */
  415.     public function isEmpty()
  416.     {
  417.         return count($this->getItems()) === 0;
  418.     }
  419.     /**
  420.      * @return CartItemInterface[]
  421.      */
  422.     public function getGiftItems()
  423.     {
  424.         //make sure that cart is calculated
  425.         if (!$this->getPriceCalculator()->isCalculated()) {
  426.             $this->getPriceCalculator()->calculate();
  427.         }
  428.         return $this->giftItems;
  429.     }
  430.     /**
  431.      * @param string $itemKey
  432.      *
  433.      * @return CartItemInterface|null
  434.      */
  435.     public function getGiftItem($itemKey)
  436.     {
  437.         //make sure that cart is calculated
  438.         if (!$this->getPriceCalculator()->isCalculated()) {
  439.             $this->getPriceCalculator()->calculate();
  440.         }
  441.         return array_key_exists($itemKey$this->giftItems) ? $this->giftItems[$itemKey] : null;
  442.     }
  443.     /**
  444.      * @param CartItemInterface[]|null $items
  445.      */
  446.     public function setItems($items)
  447.     {
  448.         $this->items $items;
  449.         // trigger cart has been modified
  450.         $this->modified();
  451.     }
  452.     /**
  453.      * @param string $itemKey
  454.      */
  455.     public function removeItem($itemKey)
  456.     {
  457.         //load items first in order to lazyload items (if they are lazy loaded)
  458.         $this->getItems();
  459.         unset($this->items[$itemKey]);
  460.         // trigger cart has been modified
  461.         $this->modified();
  462.     }
  463.     /**
  464.      * @param string $name
  465.      */
  466.     public function setName($name)
  467.     {
  468.         $this->name $name;
  469.     }
  470.     /**
  471.      * @return string
  472.      */
  473.     public function getName()
  474.     {
  475.         return $this->name;
  476.     }
  477.     /**
  478.      * @return bool
  479.      */
  480.     public function getIsBookable()
  481.     {
  482.         foreach ($this->getItems() as $item) {
  483.             if (!$item->getProduct()->getOSIsBookable($item->getCount())) {
  484.                 return false;
  485.             }
  486.         }
  487.         return true;
  488.     }
  489.     /**
  490.      * @param string|int $id
  491.      */
  492.     public function setId($id)
  493.     {
  494.         $this->id $id;
  495.     }
  496.     /**
  497.      * @return string|int|null
  498.      */
  499.     public function getId()
  500.     {
  501.         return $this->id;
  502.     }
  503.     /**
  504.      * @return \DateTime
  505.      */
  506.     public function getCreationDate()
  507.     {
  508.         if (empty($this->creationDate) && $this->creationDateTimestamp) {
  509.             $this->creationDate = new \DateTime();
  510.             $this->creationDate->setTimestamp($this->creationDateTimestamp);
  511.         }
  512.         return $this->creationDate;
  513.     }
  514.     /**
  515.      * @param \DateTime|null $creationDate
  516.      */
  517.     public function setCreationDate(\DateTime $creationDate null)
  518.     {
  519.         $this->creationDate $creationDate;
  520.         if ($creationDate) {
  521.             $this->creationDateTimestamp $creationDate->getTimestamp();
  522.         } else {
  523.             $this->creationDateTimestamp null;
  524.         }
  525.     }
  526.     /**
  527.      * @param int $creationDateTimestamp
  528.      */
  529.     public function setCreationDateTimestamp($creationDateTimestamp)
  530.     {
  531.         $this->creationDateTimestamp $creationDateTimestamp;
  532.         $this->creationDate null;
  533.     }
  534.     /**
  535.      * @return int|null
  536.      */
  537.     public function getCreationDateTimestamp()
  538.     {
  539.         return $this->creationDateTimestamp;
  540.     }
  541.     /**
  542.      * @return \DateTime|null
  543.      */
  544.     public function getModificationDate()
  545.     {
  546.         if (empty($this->modificationDate) && $this->modificationDateTimestamp) {
  547.             $this->modificationDate = new \DateTime();
  548.             $this->modificationDate->setTimestamp($this->modificationDateTimestamp);
  549.         }
  550.         return $this->modificationDate;
  551.     }
  552.     /**
  553.      * @param \DateTime|null $modificationDate
  554.      */
  555.     public function setModificationDate(\DateTime $modificationDate null)
  556.     {
  557.         $this->modificationDate $modificationDate;
  558.         if ($modificationDate) {
  559.             $this->modificationDateTimestamp $modificationDate->getTimestamp();
  560.         } else {
  561.             $this->modificationDateTimestamp null;
  562.         }
  563.     }
  564.     /**
  565.      * @param int $modificationDateTimestamp
  566.      */
  567.     public function setModificationDateTimestamp($modificationDateTimestamp)
  568.     {
  569.         $this->modificationDateTimestamp $modificationDateTimestamp;
  570.         $this->modificationDate null;
  571.     }
  572.     /**
  573.      * @return int|null
  574.      */
  575.     public function getModificationDateTimestamp()
  576.     {
  577.         return $this->modificationDateTimestamp;
  578.     }
  579.     /**
  580.      * @return int
  581.      */
  582.     public function getUserId()
  583.     {
  584.         return $this->userId ?: Factory::getInstance()->getEnvironment()->getCurrentUserId();
  585.     }
  586.     /**
  587.      * @param int $userId
  588.      */
  589.     public function setUserId($userId)
  590.     {
  591.         $this->userId = (int)$userId;
  592.     }
  593.     /**
  594.      * @return void
  595.      */
  596.     abstract public function save();
  597.     /**
  598.      * @return void
  599.      */
  600.     abstract public function delete();
  601.     /**
  602.      * @param string $key
  603.      *
  604.      * @return string|null
  605.      */
  606.     public function getCheckoutData($key)
  607.     {
  608.         $entry $this->checkoutData[$key] ?? null;
  609.         if ($entry) {
  610.             return $this->checkoutData[$key]->getData();
  611.         }
  612.         return null;
  613.     }
  614.     /**
  615.      * @param string $key
  616.      * @param string $data
  617.      */
  618.     public function setCheckoutData($key$data)
  619.     {
  620.         $className $this->getCartCheckoutDataClassName();
  621.         $value = new $className();
  622.         $value->setCart($this);
  623.         $value->setKey($key);
  624.         $value->setData($data);
  625.         $this->checkoutData[$key] = $value;
  626.     }
  627.     /**
  628.      * @return CartPriceCalculatorInterface
  629.      */
  630.     public function getPriceCalculator()
  631.     {
  632.         if (empty($this->priceCalculator)) {
  633.             $this->priceCalculator Factory::getInstance()->getCartManager()->getCartPriceCalculator($this);
  634.         }
  635.         return $this->priceCalculator;
  636.     }
  637.     /**
  638.      * @param CartPriceCalculatorInterface $priceCalculator
  639.      */
  640.     public function setPriceCalculator(CartPriceCalculatorInterface $priceCalculator)
  641.     {
  642.         $this->priceCalculator $priceCalculator;
  643.     }
  644.     /**
  645.      * @return $this
  646.      */
  647.     public function modified()
  648.     {
  649.         $this->setModificationDateTimestamp(time());
  650.         $this->itemAmount null;
  651.         $this->subItemAmount null;
  652.         $this->mainAndSubItemAmount null;
  653.         $this->itemCount null;
  654.         $this->subItemCount null;
  655.         $this->mainAndSubItemCount null;
  656.         //don't use getter here because reset is only necessary if price calculator is already there
  657.         if ($this->priceCalculator) {
  658.             $this->priceCalculator->reset();
  659.         }
  660.         $this->validateVoucherTokenReservations();
  661.         $this->giftItems = [];
  662.         return $this;
  663.     }
  664.     /**
  665.      * @param int $count
  666.      *
  667.      * @return CartItemInterface[]
  668.      */
  669.     public function getRecentlyAddedItems($count)
  670.     {
  671.         // get last items
  672.         $index = [];
  673.         foreach ($this->getItems() as $item) {
  674.             $index[$item->getAddedDate()->getTimestamp()] = $item;
  675.         }
  676.         krsort($index);
  677.         return array_slice($index0$count);
  678.     }
  679.     /**
  680.      * sorts all items in cart according to a given callback function
  681.      *
  682.      * @param callable $value_compare_func
  683.      *
  684.      * @return $this
  685.      */
  686.     public function sortItems(callable $value_compare_func)
  687.     {
  688.         return $this;
  689.     }
  690.     /**
  691.      * Adds a voucher token to the cart's checkout data and reserves it.
  692.      *
  693.      * @param string $code
  694.      *
  695.      * @return bool
  696.      *
  697.      * @throws \Exception
  698.      */
  699.     public function addVoucherToken($code)
  700.     {
  701.         $service Factory::getInstance()->getVoucherService();
  702.         if ($service->checkToken($code$this)) {
  703.             if ($service->reserveToken($code$this)) {
  704.                 $index 'voucher_' $code;
  705.                 $this->setCheckoutData($index$code);
  706.                 $this->save();
  707.                 $this->modified();
  708.                 return true;
  709.             }
  710.         }
  711.         return false;
  712.     }
  713.     /**
  714.      * Checks if an error code is a defined Voucher Error Code.
  715.      *
  716.      * @param int $errorCode
  717.      *
  718.      * @return bool
  719.      */
  720.     public function isVoucherErrorCode($errorCode)
  721.     {
  722.         return $errorCode && $errorCode 10;
  723.     }
  724.     /**
  725.      * Removes all tokens form cart and releases the token reservations.
  726.      *
  727.      * @throws InvalidConfigException
  728.      */
  729.     public function removeAllVoucherTokens()
  730.     {
  731.         foreach ($this->getVoucherTokenCodes() as $code) {
  732.             $this->removeVoucherToken($code);
  733.         }
  734.     }
  735.     /**
  736.      * Removes a token from cart and releases token reservation.
  737.      *
  738.      * @param string $code
  739.      *
  740.      * @throws \Exception
  741.      *
  742.      * @return bool
  743.      */
  744.     public function removeVoucherToken($code)
  745.     {
  746.         $service Factory::getInstance()->getVoucherService();
  747.         $key array_search($code$this->getVoucherTokenCodes());
  748.         if ($key !== false) {
  749.             if ($service->releaseToken($code$this)) {
  750.                 unset($this->checkoutData['voucher_' $code]);
  751.                 $this->save();
  752.                 $this->modified();
  753.                 return true;
  754.             }
  755.         } else {
  756.             throw new VoucherServiceException('No Token with code ' $code ' in this cart.'VoucherServiceException::ERROR_CODE_NOT_FOUND_IN_CART);
  757.         }
  758.         return false;
  759.     }
  760.     /**
  761.      * Filters checkout data and returns an array of strings with the assigns tokens.
  762.      *
  763.      * @return string[]
  764.      */
  765.     public function getVoucherTokenCodes()
  766.     {
  767.         $tokens = [];
  768.         foreach ($this->checkoutData as $key => $value) {
  769.             $exp_key explode('_'$key);
  770.             if ($exp_key[0] == 'voucher') {
  771.                 $tokens[] = $value->getData();
  772.             }
  773.         }
  774.         return $tokens;
  775.     }
  776.     /**
  777.      * @return PricingManagerTokenInformation[]
  778.      */
  779.     public function getPricingManagerTokenInformationDetails(): array
  780.     {
  781.         $voucherService Factory::getInstance()->getVoucherService();
  782.         return $voucherService->getPricingManagerTokenInformationDetails($this);
  783.     }
  784.     /**
  785.      * Checks if checkout data voucher tokens are valid reservations
  786.      */
  787.     protected function validateVoucherTokenReservations()
  788.     {
  789.         if ($this->getVoucherTokenCodes()) {
  790.             $order Factory::getInstance()->getOrderManager()->getOrderFromCart($this);
  791.             $appliedVoucherCodes = [];
  792.             if ($order) {
  793.                 foreach ($order->getVoucherTokens() as $voucherToken) {
  794.                     $appliedVoucherCodes[$voucherToken->getToken()] = $voucherToken->getToken();
  795.                 }
  796.             }
  797.             //check for each voucher token if reservation is valid or it is already applied to order
  798.             foreach ($this->getVoucherTokenCodes() as $code) {
  799.                 $reservation Reservation::get($code$this);
  800.                 if (!$reservation && !array_key_exists($code$appliedVoucherCodes)) {
  801.                     unset($this->checkoutData['voucher_'.$code]);
  802.                 }
  803.             }
  804.         }
  805.     }
  806.     /**
  807.      * Should be added to the cart
  808.      *
  809.      * @param CartItemInterface $item
  810.      *
  811.      * @return bool
  812.      */
  813.     protected static function isValidCartItem(CartItemInterface $item)
  814.     {
  815.         $product $item->getProduct();
  816.         if ($product instanceof CheckoutableInterface && !$product instanceof MockProduct) {
  817.             return true;
  818.         }
  819.         Logger::warn('Product ' $item->getProduct()->getId() . ' not found');
  820.         return false;
  821.     }
  822. }