classes/XLite/Model/OrderItem.php line 25

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (c) 2011-present Qualiteam software Ltd. All rights reserved.
  4.  * See https://www.x-cart.com/license-agreement.html for license details.
  5.  */
  6. namespace XLite\Model;
  7. use Doctrine\ORM\Mapping as ORM;
  8. use XLite\Model\Base\IMark;
  9. use XLite\Model\OrderItem\AttributeValue;
  10. use XLite\Model\OrderItem\Surcharge;
  11. /**
  12.  * Something customer can put into his cart
  13.  *
  14.  * @ORM\Entity
  15.  * @ORM\Table  (name="order_items",
  16.  *          indexes={
  17.  *               @ORM\Index (name="ooo", columns={"order_id","object_type","object_id"}),
  18.  *               @ORM\Index (name="object_id", columns={"object_id"}),
  19.  *               @ORM\Index (name="price", columns={"price"}),
  20.  *               @ORM\Index (name="amount", columns={"amount"})
  21.  *          }
  22.  * )
  23.  *
  24.  * @ORM\InheritanceType       ("SINGLE_TABLE")
  25.  * @ORM\DiscriminatorColumn   (name="object_type", type="string", length=16)
  26.  * @ORM\DiscriminatorMap      ({"product" = "XLite\Model\OrderItem"})
  27.  */
  28. class OrderItem extends \XLite\Model\Base\SurchargeOwner
  29. {
  30.     public const PRODUCT_TYPE 'product';
  31.     /**
  32.      * Primary key
  33.      *
  34.      * @var integer
  35.      *
  36.      * @ORM\Id
  37.      * @ORM\GeneratedValue (strategy="AUTO")
  38.      * @ORM\Column         (type="integer")
  39.      */
  40.     protected $item_id;
  41.     /**
  42.      * Object (product)
  43.      *
  44.      * @var \XLite\Model\Product
  45.      *
  46.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Product", inversedBy="order_items", cascade={"merge","detach"})
  47.      * @ORM\JoinColumn (name="object_id", referencedColumnName="product_id", onDelete="SET NULL")
  48.      */
  49.     protected $object;
  50.     /**
  51.      * Item name
  52.      *
  53.      * @var string
  54.      *
  55.      * @ORM\Column (type="string", length=255)
  56.      */
  57.     protected $name;
  58.     /**
  59.      * Item SKU
  60.      *
  61.      * @var string
  62.      *
  63.      * @ORM\Column (type="string", length=32)
  64.      */
  65.     protected $sku '';
  66.     /**
  67.      * Item price
  68.      *
  69.      * @var float
  70.      *
  71.      * @ORM\Column (type="decimal", precision=14, scale=4)
  72.      */
  73.     protected $price;
  74.     /**
  75.      * Item net price
  76.      *
  77.      * @var float
  78.      *
  79.      * @ORM\Column (type="decimal", precision=14, scale=4)
  80.      */
  81.     protected $itemNetPrice;
  82.     /**
  83.      * Item discounted subtotal
  84.      *
  85.      * @var float
  86.      *
  87.      * @ORM\Column (type="decimal", precision=14, scale=4)
  88.      */
  89.     protected $discountedSubtotal 0;
  90.     /**
  91.      * Item quantity
  92.      *
  93.      * @var integer
  94.      *
  95.      * @ORM\Column (type="integer")
  96.      */
  97.     protected $amount 1;
  98.     /**
  99.      * Item quantity
  100.      *
  101.      * @var integer
  102.      *
  103.      * @ORM\Column (type="integer")
  104.      */
  105.     protected $backorderedAmount 0;
  106.     /**
  107.      * Item order
  108.      *
  109.      * @var \XLite\Model\Order
  110.      *
  111.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Order", inversedBy="items")
  112.      * @ORM\JoinColumn (name="order_id", referencedColumnName="order_id", onDelete="CASCADE")
  113.      */
  114.     protected $order;
  115.     /**
  116.      * Order item surcharges
  117.      *
  118.      * @var \Doctrine\Common\Collections\Collection
  119.      *
  120.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderItem\Surcharge", mappedBy="owner", cascade={"all"})
  121.      * @ORM\OrderBy   ({"weight" = "ASC", "id" = "ASC"})
  122.      */
  123.     protected $surcharges;
  124.     /**
  125.      * Dump product (deleted)
  126.      *
  127.      * @var \XLite\Model\Product
  128.      */
  129.     protected $dumpProduct;
  130.     /**
  131.      * Attribute values
  132.      *
  133.      * @var \Doctrine\Common\Collections\Collection
  134.      *
  135.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderItem\AttributeValue", mappedBy="orderItem", cascade={"all"})
  136.      */
  137.     protected $attributeValues;
  138.     /**
  139.      * Update date (UNIX timestamp)
  140.      *
  141.      * @var integer
  142.      *
  143.      * @ORM\Column (type="integer")
  144.      */
  145.     protected $updateDate 0;
  146.     /**
  147.      * Constructor
  148.      *
  149.      * @param array $data Entity properties OPTIONAL
  150.      */
  151.     public function __construct(array $data = [])
  152.     {
  153.         $this->surcharges = new \Doctrine\Common\Collections\ArrayCollection();
  154.         $this->attributeValues = new \Doctrine\Common\Collections\ArrayCollection();
  155.         parent::__construct($data);
  156.     }
  157.     /**
  158.      * Get name
  159.      *
  160.      * @return string
  161.      */
  162.     public function getName()
  163.     {
  164.         return $this->getObject() ? $this->getObject()->getName() : $this->name;
  165.     }
  166.     /**
  167.      * Set order
  168.      *
  169.      * @param \XLite\Model\Order $order Order OPTIONAL
  170.      */
  171.     public function setOrder(\XLite\Model\Order $order null)
  172.     {
  173.         $this->order $order;
  174.     }
  175.     /**
  176.      * Clone order item object. The product only is set additionally
  177.      * since the order could be different and should be set manually
  178.      *
  179.      * @return \XLite\Model\AEntity
  180.      */
  181.     public function cloneEntity()
  182.     {
  183.         $newItem parent::cloneEntity();
  184.         if ($this->getObject()) {
  185.             $newItem->setObject($this->getObject());
  186.         }
  187.         foreach ($this->getSurcharges() as $surchrg) {
  188.             $cloned $surchrg->cloneEntity();
  189.             $cloned->setOwner($newItem);
  190.             $newItem->addSurcharges($cloned);
  191.         }
  192.         if ($this->hasAttributeValues()) {
  193.             foreach ($this->getAttributeValues() as $av) {
  194.                 $cloned $av->cloneEntity();
  195.                 $cloned->setOrderItem($newItem);
  196.                 $newItem->addAttributeValues($cloned);
  197.             }
  198.         }
  199.         return $newItem;
  200.     }
  201.     /**
  202.      * Get item clear price. This value is used as a base item price for calculation of netPrice
  203.      *
  204.      * @return float
  205.      */
  206.     public function getClearPrice()
  207.     {
  208.         return $this->getProduct()->getClearPrice();
  209.     }
  210.     /**
  211.      * Get net Price
  212.      *
  213.      * @return float
  214.      */
  215.     public function getNetPrice()
  216.     {
  217.         return \XLite\Logic\Price::getInstance()->apply($this'getClearPrice', ['taxable'], 'net');
  218.     }
  219.     /**
  220.      * Get display Price
  221.      *
  222.      * @return float
  223.      */
  224.     public function getDisplayPrice()
  225.     {
  226.         return \XLite\Logic\Price::getInstance()->apply($this'getNetPrice', ['taxable'], 'display');
  227.     }
  228.     /**
  229.      * Get item price
  230.      *
  231.      * @return float
  232.      */
  233.     public function getItemPrice()
  234.     {
  235.         return $this->isOrderOpen() ? $this->getClearPrice() : $this->getPrice();
  236.     }
  237.     /**
  238.      * Get item net price
  239.      *
  240.      * @return float
  241.      */
  242.     public function getItemNetPrice()
  243.     {
  244.         return $this->isOrderOpen() ? $this->getNetPrice() : $this->itemNetPrice;
  245.     }
  246.     /**
  247.      * Return false if order is fixed in the database (i.e. order is placed) and true if order is still used as "cart"
  248.      *
  249.      * @return boolean
  250.      */
  251.     public function isOrderOpen()
  252.     {
  253.         $order $this->getOrder();
  254.         return $order && method_exists($order'hasCartStatus') && $order->hasCartStatus();
  255.     }
  256.     /**
  257.      * Get through exclude surcharges
  258.      *
  259.      * @return array
  260.      */
  261.     public function getThroughExcludeSurcharges()
  262.     {
  263.         $list $this->getOrder()->getItemsExcludeSurcharges();
  264.         foreach ($list as $key => $value) {
  265.             $list[$key] = null;
  266.             foreach ($this->getExcludeSurcharges() as $surcharge) {
  267.                 if ($surcharge->getKey() == $key) {
  268.                     $list[$key] = $surcharge;
  269.                     break;
  270.                 }
  271.             }
  272.         }
  273.         return $list;
  274.     }
  275.     /**
  276.      * Wrapper. If the product was deleted,
  277.      * item will use save product name and SKU
  278.      * TODO - switch to getObject() and remove
  279.      *
  280.      * @return \XLite\Model\Product
  281.      */
  282.     public function getProduct()
  283.     {
  284.         if ($this->isDeleted()) {
  285.             $result $this->getDeletedProduct();
  286.         } else {
  287.             $result $this->getObject();
  288.             $result->setAttrValues($this->getAttributeValuesIds());
  289.         }
  290.         return $result;
  291.     }
  292.     /**
  293.      * Get item product id if it exists
  294.      *
  295.      * @return int|null
  296.      */
  297.     public function getProductId()
  298.     {
  299.         return $this->isDeleted() ? null $this->getObject()->getProductId();
  300.     }
  301.     /**
  302.      * Save some fields from product
  303.      *
  304.      * @param \XLite\Model\Product $product Product to set OPTIONAL
  305.      *
  306.      * @return void
  307.      */
  308.     public function setProduct(\XLite\Model\Product $product null)
  309.     {
  310.         $this->setObject($product);
  311.     }
  312.     /**
  313.      * Set object
  314.      *
  315.      * @param \XLite\Model\Base\IOrderItem $item Order item related object OPTIONAL
  316.      *
  317.      * @return void
  318.      */
  319.     public function setObject(\XLite\Model\Base\IOrderItem $item null)
  320.     {
  321.         $this->object $item;
  322.         if ($item) {
  323.             $this->saveItemState($item);
  324.         } else {
  325.             $this->resetItemState();
  326.         }
  327.     }
  328.     /**
  329.      * Define the warning if amount is less or more than purchase limits
  330.      *
  331.      * @param integer $amount
  332.      *
  333.      * @return string
  334.      */
  335.     public function getAmountWarning($amount)
  336.     {
  337.         $result '';
  338.         if ($this->getObject() === null) {
  339.             return $result;
  340.         }
  341.         $minQuantity $this->getObject()->getMinPurchaseLimit();
  342.         $maxQuantity $this->getObject()->getMaxPurchaseLimit();
  343.         if ($amount $minQuantity) {
  344.             //There's a minimum purchase limit of MinQuantity. The number of units of the product ProductName in cart has been adjusted to reach this limit.
  345.             $result \XLite\Core\Translation::lbl('There is a minimum purchase limit of MinQuantity', [
  346.                 'minQuantity' => $minQuantity,
  347.                 'productName' => $this->getName(),
  348.             ]);
  349.         } elseif ($amount $maxQuantity) {
  350.             $result \XLite\Core\Translation::lbl('There is a maximum purchase limit of MaxQuantity', [
  351.                 'maxQuantity' => $maxQuantity,
  352.                 'productName' => $this->getName(),
  353.             ]);
  354.         }
  355.         return $result;
  356.     }
  357.     /**
  358.      * Modified setter
  359.      *
  360.      * @param integer $amount Value to set
  361.      *
  362.      * @return void
  363.      */
  364.     public function setAmount($amount)
  365.     {
  366.         $correctedAmount $amount;
  367.         if (
  368.             ($amount !== $this->getAmount() && !\XLite::isAdminZone())
  369.             || !$this->getOrder()
  370.             || $this->isOrderOpen()
  371.         ) {
  372.             $correctedAmount $this->processAmount($amount);
  373.             if ($warningText $this->getAmountWarning($amount)) {
  374.                 \XLite\Core\TopMessage::addWarning($warningText);
  375.             }
  376.         }
  377.         $this->amount $correctedAmount;
  378.     }
  379.     /**
  380.      * Set Backordered Amount
  381.      *
  382.      * @param int $backorderedAmount
  383.      *
  384.      * @return $this
  385.      */
  386.     public function setBackorderedAmount($backorderedAmount)
  387.     {
  388.         $this->backorderedAmount $backorderedAmount;
  389.         return $this;
  390.     }
  391.     /**
  392.      * Process amount value before set
  393.      *
  394.      * @param $amount
  395.      *
  396.      * @return mixed
  397.      */
  398.     public function processAmount($amount)
  399.     {
  400.         if ($this->getObject()) {
  401.             $amount max($amount$this->getObject()->getMinPurchaseLimit());
  402.             $amount min($amount$this->getObject()->getMaxPurchaseLimit());
  403.         }
  404.         return $amount;
  405.     }
  406.     /**
  407.      * Get item weight
  408.      *
  409.      * @return float
  410.      */
  411.     public function getWeight()
  412.     {
  413.         $result $this->getClearWeight();
  414.         foreach ($this->getAttributeValues() as $attributeValue) {
  415.             $av $attributeValue->getAttributeValue();
  416.             if (is_object($av)) {
  417.                 $result += $av->getAbsoluteValue('weight');
  418.             }
  419.         }
  420.         return $result
  421.             $result $this->getAmount()
  422.             : 0;
  423.     }
  424.     /**
  425.      * Get clear weight
  426.      *
  427.      * @return float
  428.      */
  429.     public function getClearWeight()
  430.     {
  431.         return $this->getObject() ? $this->getObject()->getClearWeight() : 0;
  432.     }
  433.     /**
  434.      * Check if item has a image
  435.      *
  436.      * @return boolean
  437.      */
  438.     public function hasImage()
  439.     {
  440.         return $this->getImage() !== null && (bool) $this->getImage()->getId();
  441.     }
  442.     /**
  443.      * Check if item has a wrong amount
  444.      *
  445.      * @return boolean
  446.      */
  447.     public function hasWrongAmount()
  448.     {
  449.         return $this->getProduct()->getInventoryEnabled()
  450.             && ($this->getProduct()->getPublicAmount() < $this->getAmount());
  451.     }
  452.     /**
  453.      * Get item image URL
  454.      *
  455.      * @return string|null
  456.      */
  457.     public function getImageURL()
  458.     {
  459.         return $this->getImage()?->getURL();
  460.     }
  461.     /**
  462.      * Get item image relative URL
  463.      *
  464.      * @return string
  465.      */
  466.     public function getImageRelativeURL()
  467.     {
  468.         return \Includes\Utils\FileManager::getRelativePath($this->getImage()->getStoragePath(), LC_DIR_ROOT);
  469.     }
  470.     /**
  471.      * Get item resized image relative URL
  472.      *
  473.      * @return string
  474.      */
  475.     public function getResizedImageURL($width$height)
  476.     {
  477.         $img $this->getImage();
  478.         $img->doResize($width$height);
  479.         return $img->getResizedURL($width$height)[2];
  480.     }
  481.     /**
  482.      * Get item image
  483.      *
  484.      * @return \XLite\Model\Base\Image
  485.      */
  486.     public function getImage()
  487.     {
  488.         return $this->getProduct()->getImage();
  489.     }
  490.     /**
  491.      * Get minicart image width
  492.      *
  493.      * @return string
  494.      */
  495.     public function getMiniCartImageWidth()
  496.     {
  497.         return 60;
  498.     }
  499.     /**
  500.      * Get minicart image height
  501.      *
  502.      * @return string
  503.      */
  504.     public function getMiniCartImageHeight()
  505.     {
  506.         return 60;
  507.     }
  508.     /**
  509.      * Get item description
  510.      *
  511.      * @return string
  512.      */
  513.     public function getDescription()
  514.     {
  515.         return $this->getProduct()->getName() . ' (' $this->getAmount() . ')';
  516.     }
  517.     /**
  518.      * Get extended item description
  519.      *
  520.      * @return string
  521.      */
  522.     public function getExtendedDescription()
  523.     {
  524.         return '';
  525.     }
  526.     /**
  527.      * Get available amount for the product
  528.      *
  529.      * @return integer
  530.      */
  531.     public function getProductAvailableAmount()
  532.     {
  533.         return $this->getProduct()->getInventoryEnabled()
  534.             ? $this->getProduct()->getPublicAmount()
  535.             : $this->getProduct()->getMaxPurchaseLimit();
  536.     }
  537.     /**
  538.      * Get item URL
  539.      *
  540.      * @return string
  541.      */
  542.     public function getURL()
  543.     {
  544.         return $this->getProduct()->getURL();
  545.     }
  546.     /**
  547.      * Flag; is this item needs to be shipped
  548.      *
  549.      * @return boolean
  550.      */
  551.     public function isShippable()
  552.     {
  553.         return !$this->getProduct()->getFreeShipping();
  554.     }
  555.     /**
  556.      * This key is used when checking if item is unique in the cart
  557.      *
  558.      * @return string
  559.      */
  560.     public function getKey()
  561.     {
  562.         $result = static::PRODUCT_TYPE '.' . ($this->getObject() ? $this->getObject()->getId() : null);
  563.         foreach ($this->getAttributeValues() as $attributeValue) {
  564.             $result .= '||'
  565.                 $attributeValue->getActualName()
  566.                 . '::'
  567.                 $attributeValue->getActualValue();
  568.         }
  569.         return $result;
  570.     }
  571.     /**
  572.      * Return attribute values ids
  573.      *
  574.      * @return array
  575.      */
  576.     public function getAttributeValuesIds()
  577.     {
  578.         $result = [];
  579.         foreach ($this->getAttributeValues() as $itemValue) {
  580.             $attributeValue $itemValue->getAttributeValue();
  581.             if ($attributeValue) {
  582.                 if ($attributeValue instanceof \XLite\Model\AttributeValue\AttributeValueText) {
  583.                     $result[$attributeValue->getAttribute()->getId()] = $itemValue->getValue();
  584.                 } else {
  585.                     $result[$attributeValue->getAttribute()->getId()] = $attributeValue->getId();
  586.                 }
  587.             }
  588.         }
  589.         ksort($result);
  590.         return $result;
  591.     }
  592.     /**
  593.      * Get attribute values as plain values
  594.      *
  595.      * @return array
  596.      */
  597.     public function getAttributeValuesPlain()
  598.     {
  599.         $result = [];
  600.         foreach ($this->getAttributeValues() as $attributeValue) {
  601.             $actualAttributeValue $attributeValue->getAttributeValue();
  602.             if ($actualAttributeValue) {
  603.                 if ($actualAttributeValue instanceof \XLite\Model\AttributeValue\AttributeValueText) {
  604.                     $value $attributeValue->getValue();
  605.                 } else {
  606.                     $value $actualAttributeValue->getId();
  607.                 }
  608.                 $result[$actualAttributeValue->getAttribute()->getId()] = $value;
  609.             }
  610.         }
  611.         ksort($result);
  612.         return $result;
  613.     }
  614.     /**
  615.      * Get attribute values string
  616.      *
  617.      * @return string
  618.      */
  619.     public function getAttributeValuesAsString()
  620.     {
  621.         $result = [];
  622.         foreach ($this->getAttributeValues() as $value) {
  623.             $result[] = $value->getValue();
  624.         }
  625.         return $result implode(' / '$result) : '';
  626.     }
  627.     /**
  628.      * Check - item has product attribute values or not
  629.      *
  630.      * @return boolean
  631.      */
  632.     public function getAttributeValuesCount()
  633.     {
  634.         return $this->getAttributeValues()->count();
  635.     }
  636.     /**
  637.      * Return attribute values ids
  638.      *
  639.      * @param integer|null $limit Limit length for returned array OPTIONAL
  640.      *
  641.      * @return array
  642.      */
  643.     public function getSortedAttributeValues($limit null)
  644.     {
  645.         $result $this->getAttributeValues()->toArray();
  646.         if ($this->getProduct()) {
  647.              usort($result, [$this'sortAttributeValues']);
  648.         }
  649.         if ($limit !== null) {
  650.             $result array_slice($result0$limit);
  651.         }
  652.         return $result;
  653.     }
  654.     /**
  655.      * Sort attribute values
  656.      *
  657.      * @param $a
  658.      * @param $b
  659.      *
  660.      * @return int
  661.      */
  662.     protected function sortAttributeValues($a$b)
  663.     {
  664.         $myA = ($a->getAttributeValue() && $a->getAttributeValue()->getAttribute()) ? $a->getAttributeValue()->getAttribute()->getPosition($this->getProduct()) : 0;
  665.         $myB = ($b->getAttributeValue() && $b->getAttributeValue()->getAttribute()) ? $b->getAttributeValue()->getAttribute()->getPosition($this->getProduct()) : 0;
  666.         return $myA <=> $myB;
  667.     }
  668.     /**
  669.      * Check if item is valid
  670.      *
  671.      * @return boolean
  672.      */
  673.     public function isValid()
  674.     {
  675.         $result $this->getProduct()->getEnabled() && $this->getAmount();
  676.         if ($result && $this->getProduct()->isUpcomingProduct()) {
  677.             $result $this->getProduct()->isAllowedUpcomingProduct();
  678.         }
  679.         if (
  680.             $result
  681.             && (
  682.                 $this->hasAttributeValues()
  683.                 || $this->getProduct()->hasEditableAttributes()
  684.             )
  685.         ) {
  686.             $result array_keys($this->getAttributeValuesIds()) == $this->getProduct()->getEditableAttributesIds();
  687.         }
  688.         return $result;
  689.     }
  690.     /**
  691.      * Check if item is allowed to add to cart
  692.      *
  693.      * @return boolean
  694.      */
  695.     public function isConfigured()
  696.     {
  697.         return true;
  698.     }
  699.     /**
  700.      * Check - can change item's amount or not
  701.      *
  702.      * @return boolean
  703.      */
  704.     public function canChangeAmount()
  705.     {
  706.         $product $this->getProduct();
  707.         return !$product
  708.             || !$product->getInventoryEnabled()
  709.             || $product->getPublicAmount();
  710.     }
  711.     /**
  712.      * Check - item has valid amount or not
  713.      *
  714.      * @return boolean
  715.      */
  716.     public function isValidAmount()
  717.     {
  718.         return $this->checkAmount();
  719.     }
  720.     /**
  721.      * Check if the item is valid to clone through the Re-order functionality
  722.      *
  723.      * @return boolean
  724.      */
  725.     public function isValidToClone()
  726.     {
  727.         $result = !$this->isDeleted() && $this->isValid() && $this->getProduct()->isAvailable();
  728.         if ($result && !$this->isActualAttributes()) {
  729.             $result false;
  730.         }
  731.         return $result;
  732.     }
  733.     /**
  734.      * Return true if order item's attribute values are all up-to-date
  735.      *
  736.      * @return boolean
  737.      */
  738.     public function isActualAttributes()
  739.     {
  740.         $result true;
  741.         if ($this->hasAttributeValues()) {
  742.             foreach ($this->getAttributeValues() as $av) {
  743.                 if (!$av->getAttributeValue()) {
  744.                     $result false;
  745.                     break;
  746.                 }
  747.             }
  748.         } elseif ($this->getObject() && $this->getObject()->hasEditableAttributes()) {
  749.             $result false;
  750.         }
  751.         return $result;
  752.     }
  753.     /**
  754.      * Set price
  755.      *
  756.      * @param float $price Price
  757.      *
  758.      * @return void
  759.      */
  760.     public function setPrice($price)
  761.     {
  762.         $this->price $price;
  763.         if ($this->itemNetPrice === null) {
  764.             $this->setItemNetPrice($price);
  765.         }
  766.     }
  767.     /**
  768.      * Set attribute values
  769.      *
  770.      * @param array $attributeValues Attribute values (prepared, from request)
  771.      *
  772.      * @return void
  773.      */
  774.     public function setAttributeValues(array $attributeValues)
  775.     {
  776.         foreach ($this->getAttributeValues() as $av) {
  777.             \XLite\Core\Database::getEM()->remove($av);
  778.         }
  779.         $this->getAttributeValues()->clear();
  780.         foreach ($attributeValues as $av) {
  781.             if (is_array($av)) {
  782.                 $value $av['value'];
  783.                 $av $av['attributeValue'];
  784.             } else {
  785.                 $value $av->asString();
  786.             }
  787.             $newValue = new AttributeValue();
  788.             $newValue->setName($av->getAttribute()->getName());
  789.             $newValue->setValue($value);
  790.             $newValue->setAttributeId($av->getAttribute()->getId());
  791.             $newValue->setOrderItem($this);
  792.             $this->addAttributeValues($newValue);
  793.             $newValue->setAttributeValue($av);
  794.         }
  795.     }
  796.     /**
  797.      * Check - item has product attribute values or not
  798.      *
  799.      * @return boolean
  800.      */
  801.     public function hasAttributeValues()
  802.     {
  803.         return $this->getAttributeValues()->count();
  804.     }
  805.     /**
  806.      * Initial calculate order item
  807.      *
  808.      * @return void
  809.      */
  810.     public function calculate()
  811.     {
  812.         $subtotal $this->calculateNetSubtotal();
  813.         $this->setSubtotal($subtotal);
  814.         $this->setDiscountedSubtotal($subtotal);
  815.         $this->setTotal($subtotal);
  816.     }
  817.     /**
  818.      * Renew order item
  819.      *
  820.      * @return boolean
  821.      */
  822.     public function renew()
  823.     {
  824.         $available true;
  825.         $product $this->getProduct();
  826.         if ($product) {
  827.             if (!$product->getId()) {
  828.                 $available false;
  829.             } else {
  830.                 $this->setPrice($product->getDisplayPrice());
  831.                 $this->setName($product->getName());
  832.                 $this->setSku($product->getSku());
  833.             }
  834.         }
  835.         return $available;
  836.     }
  837.     /**
  838.      * Return true if ordered item is a valid product and is taxable
  839.      *
  840.      * @return boolean
  841.      */
  842.     public function getTaxable()
  843.     {
  844.         $product $this->getProduct();
  845.         return $product && $product->getTaxable();
  846.     }
  847.     /**
  848.      * Get item taxable basis
  849.      *
  850.      * @return float
  851.      */
  852.     public function getTaxableBasis()
  853.     {
  854.         $product $this->getProduct();
  855.         return $product?->getTaxableBasis();
  856.     }
  857.     /**
  858.      * Get product classes
  859.      *
  860.      * @return array
  861.      */
  862.     public function getProductClass()
  863.     {
  864.         $product $this->getProduct();
  865.         return $product?->getClass();
  866.     }
  867.     /**
  868.      * Get event cell base information
  869.      *
  870.      * @return array
  871.      */
  872.     public function getEventCell()
  873.     {
  874.         return [
  875.             'item_id'     => $this->getItemId(),
  876.             'key'         => $this->getKey(),
  877.             'object_type' => static::PRODUCT_TYPE,
  878.             'object_id'   => $this->getProductId(),
  879.         ];
  880.     }
  881.     /**
  882.      * 'IsDeleted' flag
  883.      *
  884.      * @return boolean
  885.      */
  886.     public function isDeleted()
  887.     {
  888.         return !$this->getObject();
  889.     }
  890.     /**
  891.      * Calculate item total
  892.      *
  893.      * @return float
  894.      */
  895.     public function calculateTotal()
  896.     {
  897.         $total $this->getSubtotal();
  898.         /** @var Surcharge $surcharge */
  899.         foreach ($this->getExcludeSurcharges() as $surcharge) {
  900.             if ($surcharge->getAvailable()) {
  901.                 $total += $surcharge->getValue();
  902.             }
  903.         }
  904.         return $total;
  905.     }
  906.     /**
  907.      * Get total with VAT
  908.      */
  909.     public function getDisplayTotal()
  910.     {
  911.         return $this->getTotal();
  912.     }
  913.     /**
  914.      * Calculate net subtotal
  915.      *
  916.      * @return float
  917.      */
  918.     public function calculateNetSubtotal()
  919.     {
  920.         if ($this->isOrderOpen() || $this->getItemNetPrice() === null) {
  921.             $this->setItemNetPrice($this->defineNetPrice());
  922.         }
  923.         return $this->getItemNetPrice() * $this->getAmount();
  924.     }
  925.     /**
  926.      * Get net subtotal without round net price
  927.      *
  928.      * @return float
  929.      */
  930.     public function getNetSubtotal()
  931.     {
  932.         return $this->calculateNetSubtotal();
  933.     }
  934.     /**
  935.      * Get inventory amount of this item
  936.      *
  937.      * @return int
  938.      */
  939.     public function getInventoryAmount()
  940.     {
  941.         return $this->getProduct()->getAmount();
  942.     }
  943.     /**
  944.      * Increase / decrease product inventory amount
  945.      *
  946.      * @param integer $delta Amount delta
  947.      *
  948.      * @return void
  949.      */
  950.     public function changeAmount($delta)
  951.     {
  952.         $this->getProduct()->changeAmount($delta);
  953.     }
  954.     /**
  955.      * Check - item price is controlled by server or not
  956.      *
  957.      * @return boolean
  958.      */
  959.     public function isPriceControlledServer()
  960.     {
  961.         return false;
  962.     }
  963.     /**
  964.      * Define net price
  965.      *
  966.      * @return float
  967.      */
  968.     protected function defineNetPrice()
  969.     {
  970.         return $this->getNetPrice();
  971.     }
  972.     /**
  973.      * Get deleted product
  974.      *
  975.      * @return \XLite\Model\Product|void
  976.      */
  977.     protected function getDeletedProduct()
  978.     {
  979.         if ($this->dumpProduct === null) {
  980.             $this->dumpProduct = new \XLite\Model\Product();
  981.             $this->dumpProduct->setPrice($this->getItemPrice());
  982.             $this->dumpProduct->setName($this->getName());
  983.             $this->dumpProduct->setSku($this->getSku());
  984.         }
  985.         return $this->dumpProduct;
  986.     }
  987.     /**
  988.      * Check item amount
  989.      *
  990.      * @return boolean
  991.      */
  992.     protected function checkAmount()
  993.     {
  994.         $result true;
  995.         $product $this->getProduct();
  996.         if ($product && $product->getId()) {
  997.             $result = !$product->getInventoryEnabled()
  998.                 || $product->getAvailableAmount() >= 0;
  999.         }
  1000.         return $result;
  1001.     }
  1002.     /**
  1003.      * Save item state
  1004.      *
  1005.      * @param \XLite\Model\Base\IOrderItem $item Item object
  1006.      *
  1007.      * @return void
  1008.      */
  1009.     protected function saveItemState(\XLite\Model\Base\IOrderItem $item)
  1010.     {
  1011.         $price $item->getPrice();
  1012.         $this->setPrice(\Includes\Utils\Converter::formatPrice($price));
  1013.         $this->setName($item->getName());
  1014.         $this->setSku($item->getSku());
  1015.     }
  1016.     /**
  1017.      * Reset item state
  1018.      *
  1019.      * @return void
  1020.      */
  1021.     protected function resetItemState()
  1022.     {
  1023.         $this->price 0;
  1024.         $this->itemNetPrice 0;
  1025.         $this->name '';
  1026.         $this->sku '';
  1027.     }
  1028.     /**
  1029.      * Get item_id
  1030.      *
  1031.      * @return integer
  1032.      */
  1033.     public function getItemId()
  1034.     {
  1035.         return $this->item_id;
  1036.     }
  1037.     /**
  1038.      * Get item_id
  1039.      *
  1040.      * @return integer
  1041.      */
  1042.     public function getId()
  1043.     {
  1044.         return $this->getItemId();
  1045.     }
  1046.     /**
  1047.      * Set name
  1048.      *
  1049.      * @param string $name
  1050.      * @return OrderItem
  1051.      */
  1052.     public function setName($name)
  1053.     {
  1054.         $this->name $name;
  1055.         return $this;
  1056.     }
  1057.     /**
  1058.      * Set sku
  1059.      *
  1060.      * @param string $sku
  1061.      * @return OrderItem
  1062.      */
  1063.     public function setSku($sku)
  1064.     {
  1065.         $this->sku $sku;
  1066.         return $this;
  1067.     }
  1068.     /**
  1069.      * Get sku
  1070.      *
  1071.      * @return string
  1072.      */
  1073.     public function getSku()
  1074.     {
  1075.         return $this->sku;
  1076.     }
  1077.     /**
  1078.      * Get price
  1079.      *
  1080.      * @return float
  1081.      */
  1082.     public function getPrice()
  1083.     {
  1084.         return $this->price;
  1085.     }
  1086.     /**
  1087.      * Set itemNetPrice
  1088.      *
  1089.      * @param float $itemNetPrice
  1090.      * @return OrderItem
  1091.      */
  1092.     public function setItemNetPrice($itemNetPrice)
  1093.     {
  1094.         $this->itemNetPrice $itemNetPrice;
  1095.         return $this;
  1096.     }
  1097.     /**
  1098.      * Set discountedSubtotal
  1099.      *
  1100.      * @param float $discountedSubtotal
  1101.      * @return OrderItem
  1102.      */
  1103.     public function setDiscountedSubtotal($discountedSubtotal)
  1104.     {
  1105.         $this->discountedSubtotal $discountedSubtotal;
  1106.         return $this;
  1107.     }
  1108.     /**
  1109.      * Get discountedSubtotal
  1110.      *
  1111.      * @return float
  1112.      */
  1113.     public function getDiscountedSubtotal()
  1114.     {
  1115.         return $this->discountedSubtotal;
  1116.     }
  1117.     /**
  1118.      * Get amount
  1119.      *
  1120.      * @return integer
  1121.      */
  1122.     public function getAmount()
  1123.     {
  1124.         return $this->amount;
  1125.     }
  1126.     /**
  1127.      * Return BackorderedAmount
  1128.      *
  1129.      * @return int
  1130.      */
  1131.     public function getBackorderedAmount()
  1132.     {
  1133.         return $this->backorderedAmount;
  1134.     }
  1135.     /**
  1136.      * Get total
  1137.      *
  1138.      * @return float
  1139.      */
  1140.     public function getTotal()
  1141.     {
  1142.         return $this->total;
  1143.     }
  1144.     /**
  1145.      * Get subtotal
  1146.      *
  1147.      * @return float
  1148.      */
  1149.     public function getSubtotal()
  1150.     {
  1151.         return $this->subtotal;
  1152.     }
  1153.     /**
  1154.      * Get object
  1155.      *
  1156.      * @return \XLite\Model\Product
  1157.      */
  1158.     public function getObject()
  1159.     {
  1160.         return $this->object;
  1161.     }
  1162.     /**
  1163.      * Get order
  1164.      *
  1165.      * @return \XLite\Model\Order
  1166.      */
  1167.     public function getOrder()
  1168.     {
  1169.         return $this->order;
  1170.     }
  1171.     /**
  1172.      * Add surcharges
  1173.      *
  1174.      * @param \XLite\Model\OrderItem\Surcharge $surcharges
  1175.      * @return OrderItem
  1176.      */
  1177.     public function addSurcharges(\XLite\Model\OrderItem\Surcharge $surcharges)
  1178.     {
  1179.         $this->surcharges[] = $surcharges;
  1180.         return $this;
  1181.     }
  1182.     /**
  1183.      * Get surcharges
  1184.      *
  1185.      * @return \Doctrine\Common\Collections\Collection
  1186.      */
  1187.     public function getSurcharges()
  1188.     {
  1189.         return $this->surcharges;
  1190.     }
  1191.     /**
  1192.      * Add attributeValues
  1193.      *
  1194.      * @param AttributeValue $attributeValues
  1195.      *
  1196.      * @return OrderItem
  1197.      */
  1198.     public function addAttributeValues(AttributeValue $attributeValues)
  1199.     {
  1200.         $this->attributeValues[] = $attributeValues;
  1201.         return $this;
  1202.     }
  1203.     /**
  1204.      * Get attributeValues
  1205.      *
  1206.      * @return \Doctrine\Common\Collections\Collection|AttributeValue[]
  1207.      */
  1208.     public function getAttributeValues()
  1209.     {
  1210.         return $this->attributeValues;
  1211.     }
  1212.     /**
  1213.      * Release backorder
  1214.      */
  1215.     public function releaseBackorder()
  1216.     {
  1217.         $this->setBackorderedAmount(0);
  1218.     }
  1219.     /**
  1220.      * @return bool
  1221.      */
  1222.     protected function isBackordered()
  1223.     {
  1224.         return $this->getOrder()
  1225.             && $this->getOrder()->isBackordered()
  1226.             && $this->getBackorderedAmount();
  1227.     }
  1228.     /**
  1229.      * Refresh update date
  1230.      */
  1231.     public function refreshUpdateDate()
  1232.     {
  1233.         $this->updateDate \XLite\Core\Converter::time();
  1234.     }
  1235.     /**
  1236.      * Get update date
  1237.      *
  1238.      * @return integer
  1239.      */
  1240.     public function getUpdateDate()
  1241.     {
  1242.         return $this->updateDate;
  1243.     }
  1244.     /**
  1245.      * Item marks
  1246.      *
  1247.      * @return IMark[]
  1248.      */
  1249.     public function getMarks(): array
  1250.     {
  1251.         return [];
  1252.     }
  1253. }