classes/XLite/Model/OrderItem.php line 24

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