classes/XLite/Model/Order.php line 80

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 ApiPlatform\Core\Annotation as ApiPlatform;
  8. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
  9. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
  10. use Doctrine\Common\Collections\ArrayCollection;
  11. use Doctrine\DBAL\LockMode;
  12. use Doctrine\ORM\EntityManager;
  13. use Doctrine\ORM\Mapping as ORM;
  14. use XCart\Domain\GmvTrackerDomain;
  15. use XCart\Framework\ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\IntegerDateFilter;
  16. use XLite\API\Endpoint\Order\DTO\OrderOutput as Output;
  17. use XLite\Core\Database;
  18. use XLite\Model\Payment\Transaction;
  19. /**
  20.  * Class represents an order
  21.  *
  22.  * @ORM\Entity
  23.  * @ORM\Table  (name="orders",
  24.  *      indexes={
  25.  *          @ORM\Index (name="date", columns={"date"}),
  26.  *          @ORM\Index (name="total", columns={"total"}),
  27.  *          @ORM\Index (name="subtotal", columns={"subtotal"}),
  28.  *          @ORM\Index (name="tracking", columns={"tracking"}),
  29.  *          @ORM\Index (name="payment_status", columns={"payment_status_id"}),
  30.  *          @ORM\Index (name="shipping_status", columns={"shipping_status_id"}),
  31.  *          @ORM\Index (name="shipping_id", columns={"shipping_id"}),
  32.  *          @ORM\Index (name="lastRenewDate", columns={"lastRenewDate"}),
  33.  *          @ORM\Index (name="orderNumber", columns={"orderNumber"}),
  34.  *          @ORM\Index (name="is_order", columns={"is_order"}),
  35.  *          @ORM\Index (name="xcPendingExport", columns={"xcPendingExport"})
  36.  *      }
  37.  * )
  38.  *
  39.  * @ ClearDiscriminatorCondition
  40.  * @ORM\InheritanceType       ("SINGLE_TABLE")
  41.  * @ORM\DiscriminatorColumn   (name="is_order", type="integer", length=1)
  42.  * @ORM\DiscriminatorMap      ({1 = "XLite\Model\Order", 0 = "XLite\Model\Cart"})
  43.  * @ApiPlatform\ApiResource(
  44.  *     output=Output::class,
  45.  *     itemOperations={
  46.  *          "get"={
  47.  *              "method"="GET",
  48.  *              "path"="/orders/{id}.{_format}",
  49.  *              "identifiers"={"id"},
  50.  *              "requirements"={"id"="\d+"},
  51.  *              "openapi_context"={
  52.  *                  "summary"="Retrieve an order by order id",
  53.  *                  "parameters"={
  54.  *                      {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  55.  *                  }
  56.  *             },
  57.  *          },
  58.  *          "get_by_number"={
  59.  *              "method"="GET",
  60.  *              "path"="/orders/by_number/{orderNumber}.{_format}",
  61.  *              "requirements"={"orderNumber"="\w+"},
  62.  *              "identifiers"={"orderNumber"},
  63.  *              "openapi_context"={
  64.  *                  "summary"="Retrieve an order by order number",
  65.  *                  "parameters"={
  66.  *                      {"name"="orderNumber", "in"="path", "required"=true, "schema"={"type"="string"}}
  67.  *                  }
  68.  *             },
  69.  *          }
  70.  *     },
  71.  *     collectionOperations={
  72.  *          "get"={
  73.  *              "method"="GET",
  74.  *              "path"="/orders.{_format}",
  75.  *              "identifiers"={"id"}
  76.  *          }
  77.  *     }
  78.  * )
  79.  * @ApiPlatform\ApiFilter(IntegerDateFilter::class, properties={"date", "lastRenewDate"})
  80.  * @ApiPlatform\ApiFilter(OrderFilter::class, properties={"date","lastRenewDate"}, arguments={"orderParameterName"="order"})
  81.  * @ApiPlatform\ApiFilter(SearchFilter::class, properties={"paymentStatus.code"="exact", "shippingStatus.code"="exact", "profile.login"="partial"})
  82.  */
  83. class Order extends \XLite\Model\Base\SurchargeOwner
  84. {
  85.     use \XLite\Core\Cache\ExecuteCachedTrait;
  86.     /**
  87.      * Order total that is financially declared as zero (null)
  88.      */
  89.     public const ORDER_ZERO 0.00001;
  90.     /**
  91.      * Add item error codes
  92.      */
  93.     public const NOT_VALID_ERROR 'notValid';
  94.     /**
  95.      * Order unique id
  96.      *
  97.      * @var mixed
  98.      *
  99.      * @ORM\Id
  100.      * @ORM\GeneratedValue (strategy="AUTO")
  101.      * @ORM\Column         (type="integer")
  102.      */
  103.     protected $order_id;
  104.     /**
  105.      * Order unique id
  106.      *
  107.      * @var string|null
  108.      *
  109.      * @ORM\Column (type="string", length=36, nullable=true, options={"fixed": true})
  110.      */
  111.     protected ?string $public_id null;
  112.     /**
  113.      * Order profile
  114.      *
  115.      * @var \XLite\Model\Profile
  116.      *
  117.      * @ORM\OneToOne   (targetEntity="XLite\Model\Profile", cascade={"merge","detach","persist"})
  118.      * @ORM\JoinColumn (name="profile_id", referencedColumnName="profile_id", onDelete="CASCADE")
  119.      */
  120.     protected $profile;
  121.     /**
  122.      * Original profile
  123.      *
  124.      * @var \XLite\Model\Profile
  125.      *
  126.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Profile", cascade={"merge","detach","persist"})
  127.      * @ORM\JoinColumn (name="orig_profile_id", referencedColumnName="profile_id", onDelete="SET NULL")
  128.      */
  129.     protected $orig_profile;
  130.     /**
  131.      * Shipping method unique id
  132.      *
  133.      * @var integer
  134.      *
  135.      * @ORM\Column (type="integer")
  136.      */
  137.     protected $shipping_id 0;
  138.     /**
  139.      * Shipping method name
  140.      *
  141.      * @var integer
  142.      *
  143.      * @ORM\Column (type="string", nullable=true)
  144.      */
  145.     protected $shipping_method_name '';
  146.     /**
  147.      * Payment method name
  148.      *
  149.      * @var integer
  150.      *
  151.      * @ORM\Column (type="string", nullable=true)
  152.      */
  153.     protected $payment_method_name '';
  154.     /**
  155.      * Shipping tracking code
  156.      *
  157.      * @var string
  158.      *
  159.      * @ORM\Column (type="string", length=32)
  160.      */
  161.     protected $tracking '';
  162.     /**
  163.      * Order creation timestamp
  164.      *
  165.      * @var integer
  166.      *
  167.      * @ORM\Column (type="integer")
  168.      */
  169.     protected $date;
  170.     /**
  171.      * Last order renew date
  172.      *
  173.      * @var integer
  174.      *
  175.      * @ORM\Column (type="integer")
  176.      */
  177.     protected $lastRenewDate 0;
  178.     /**
  179.      * Payment status
  180.      *
  181.      * @var \XLite\Model\Order\Status\Payment
  182.      *
  183.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Order\Status\Payment")
  184.      * @ORM\JoinColumn (name="payment_status_id", referencedColumnName="id", onDelete="SET NULL")
  185.      */
  186.     protected $paymentStatus;
  187.     /**
  188.      * Shipping status
  189.      *
  190.      * @var \XLite\Model\Order\Status\Shipping
  191.      *
  192.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Order\Status\Shipping")
  193.      * @ORM\JoinColumn (name="shipping_status_id", referencedColumnName="id", onDelete="SET NULL")
  194.      */
  195.     protected $shippingStatus;
  196.     /**
  197.      * Customer notes
  198.      *
  199.      * @var string
  200.      *
  201.      * @ORM\Column (type="text")
  202.      */
  203.     protected $notes '';
  204.     /**
  205.      * Admin notes
  206.      *
  207.      * @var string
  208.      *
  209.      * @ORM\Column (type="text")
  210.      */
  211.     protected $adminNotes '';
  212.     /**
  213.      * Order details
  214.      *
  215.      * @var \Doctrine\Common\Collections\Collection
  216.      *
  217.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderDetail", mappedBy="order", cascade={"all"})
  218.      * @ORM\OrderBy   ({"name" = "ASC"})
  219.      */
  220.     protected $details;
  221.     /**
  222.      * Order tracking numbers
  223.      *
  224.      * @var \Doctrine\Common\Collections\Collection
  225.      *
  226.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderTrackingNumber", mappedBy="order", cascade={"all"})
  227.      */
  228.     protected $trackingNumbers;
  229.     /**
  230.      * Order events queue
  231.      *
  232.      * @var \Doctrine\Common\Collections\Collection
  233.      *
  234.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderHistoryEvents", mappedBy="order", cascade={"all"})
  235.      */
  236.     protected $events;
  237.     /**
  238.      * Order items
  239.      *
  240.      * @var \Doctrine\Common\Collections\Collection|\XLite\Model\OrderItem[]
  241.      *
  242.      * @ORM\OneToMany (targetEntity="XLite\Model\OrderItem", mappedBy="order", cascade={"all"})
  243.      */
  244.     protected $items;
  245.     /**
  246.      * Order surcharges
  247.      *
  248.      * @var \Doctrine\Common\Collections\Collection
  249.      *
  250.      * @ORM\OneToMany (targetEntity="XLite\Model\Order\Surcharge", mappedBy="owner", cascade={"all"})
  251.      * @ORM\OrderBy   ({"weight" = "ASC", "id" = "ASC"})
  252.      */
  253.     protected $surcharges;
  254.     /**
  255.      * Payment transactions
  256.      *
  257.      * @var \Doctrine\Common\Collections\Collection
  258.      *
  259.      * @ORM\OneToMany (targetEntity="XLite\Model\Payment\Transaction", mappedBy="order", cascade={"all"})
  260.      */
  261.     protected $payment_transactions;
  262.     /**
  263.      * Currency
  264.      *
  265.      * @var \XLite\Model\Currency
  266.      *
  267.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Currency", inversedBy="orders", cascade={"merge","detach"})
  268.      * @ORM\JoinColumn (name="currency_id", referencedColumnName="currency_id", onDelete="CASCADE")
  269.      */
  270.     protected $currency;
  271.     /**
  272.      * Unique order identificator (it is working for orders only, not for cart entities)
  273.      *
  274.      * @var integer
  275.      *
  276.      * @ORM\Column (type="string", length=255, nullable=true)
  277.      */
  278.     protected $orderNumber;
  279.     /**
  280.      * Recent flag: true if order's statuses were not changed manually by an administrator, otherwise - false
  281.      *
  282.      * @var boolean
  283.      *
  284.      * @ORM\Column (type="boolean")
  285.      */
  286.     protected $recent true;
  287.     /**
  288.      * @var string
  289.      *
  290.      * @ORM\Column (type="string", length=255, nullable=true)
  291.      */
  292.     protected $stockStatus '';
  293.     /**
  294.      * 'Add item' error code
  295.      *
  296.      * @var string
  297.      */
  298.     protected $addItemError;
  299.     /**
  300.      * Order previous payment status
  301.      *
  302.      * @var \XLite\Model\Order\Status\Payment
  303.      */
  304.     protected $oldPaymentStatus;
  305.     /**
  306.      * Flag: true - order is prepared for removing
  307.      *
  308.      * @var boolean
  309.      */
  310.     protected $isRemoving false;
  311.     /**
  312.      * Order previous shipping status
  313.      *
  314.      * @var \XLite\Model\Order\Status\Shipping
  315.      */
  316.     protected $oldShippingStatus;
  317.     /**
  318.      * Modifiers (cache)
  319.      *
  320.      * @var \XLite\DataSet\Collection\OrderModifier
  321.      */
  322.     protected $modifiers;
  323.     /**
  324.      * Shipping carrier object cache.
  325.      * Use $this->getShippingProcessor() method to retrieve.
  326.      *
  327.      * @var \XLite\Model\Shipping\Processor\AProcessor
  328.      */
  329.     protected $shippingProcessor;
  330.     /**
  331.      * Check if notification sent by any status handler to avoid extra 'changed' notification
  332.      *
  333.      * @var boolean
  334.      */
  335.     protected $isNotificationSent false;
  336.     /**
  337.      * Flag: Ignore sending notifications to a customer if true
  338.      *
  339.      * @var boolean
  340.      */
  341.     protected $ignoreCustomerNotifications false;
  342.     /**
  343.      * Flag: is email notifications are allowed for the order
  344.      *
  345.      * @var boolean
  346.      */
  347.     protected $isNotificationsAllowedFlag true;
  348.     /**
  349.      * Check status is set or not
  350.      *
  351.      * @var boolean
  352.      */
  353.     protected $statusIsSet false;
  354.     /**
  355.      * Payment transaction sums
  356.      *
  357.      * @var array
  358.      */
  359.     protected $paymentTransactionSums;
  360.     /**
  361.      * Source address
  362.      *
  363.      * @var \XLite\Model\Address
  364.      */
  365.     protected $sourceAddress;
  366.     /**
  367.      * Flag to exporting entities
  368.      *
  369.      * @var boolean
  370.      *
  371.      * @ORM\Column (type="boolean")
  372.      */
  373.     protected $xcPendingExport false;
  374.     /**
  375.      * Profiles
  376.      *
  377.      * @var ArrayCollection
  378.      *
  379.      * @ORM\ManyToMany (targetEntity="XLite\Model\Order")
  380.      * @ORM\JoinTable  (
  381.      *      name="order_backorder_competitors",
  382.      *      joinColumns={@ORM\JoinColumn(name="id", referencedColumnName="order_id", onDelete="CASCADE")},
  383.      *      inverseJoinColumns={@ORM\JoinColumn(name="competitor_id", referencedColumnName="order_id", onDelete="CASCADE")}
  384.      * )
  385.      */
  386.     protected $backorderCompetitors;
  387.     protected $backorderProductAmounts = [];
  388.     /**
  389.      * If entity temporary
  390.      *
  391.      * @var bool
  392.      */
  393.     protected $temporary false;
  394.     /**
  395.      * @return boolean
  396.      */
  397.     public function isTemporary()
  398.     {
  399.         return $this->temporary;
  400.     }
  401.     /**
  402.      * @param boolean $temporary
  403.      */
  404.     public function setTemporary($temporary)
  405.     {
  406.         $this->temporary $temporary;
  407.     }
  408.     /**
  409.      * Flag if cart become order in current runtime
  410.      *
  411.      * @var bool
  412.      */
  413.     protected $justClosed false;
  414.     /**
  415.      * @return bool
  416.      */
  417.     public function isJustClosed()
  418.     {
  419.         return $this->justClosed;
  420.     }
  421.     /**
  422.      * @param bool $justClosed
  423.      */
  424.     protected function setJustClosed($justClosed)
  425.     {
  426.         $this->justClosed $justClosed;
  427.     }
  428.     /**
  429.      * Add item to order
  430.      *
  431.      * @param \XLite\Model\OrderItem $newItem Item to add
  432.      *
  433.      * @return boolean
  434.      */
  435.     public function addItem(\XLite\Model\OrderItem $newItem)
  436.     {
  437.         $result false;
  438.         if ($newItem->isValid() && $newItem->isConfigured()) {
  439.             $this->addItemError null;
  440.             $newItem->setOrder($this);
  441.             $item $this->getItemByItem($newItem);
  442.             $warningText '';
  443.             if ($item) {
  444.                 $warningText $item->getAmountWarning(
  445.                     $item->getAmount() + $newItem->getAmount()
  446.                 );
  447.                 $item->setAmount($item->getAmount() + $newItem->getAmount());
  448.                 $item->refreshUpdateDate();
  449.             } else {
  450.                 $newItem->refreshUpdateDate();
  451.                 $this->addItems($newItem);
  452.             }
  453.             $result $warningText === '';
  454.             if ($warningText !== '') {
  455.                 \XLite\Core\TopMessage::addWarning($warningText);
  456.             }
  457.         } else {
  458.             $this->addItemError = static::NOT_VALID_ERROR;
  459.         }
  460.         return $result;
  461.     }
  462.     /**
  463.      * Remove item from order
  464.      *
  465.      * @param \XLite\Model\OrderItem $item
  466.      */
  467.     public function removeItem(\XLite\Model\OrderItem $item)
  468.     {
  469.         if ($item->getAmount() > 1) {
  470.             $item->setAmount($item->getAmount() - 1);
  471.         } else {
  472.             $this->getItems()->removeElement($item);
  473.             \XLite\Core\Database::getEM()->remove($item);
  474.         }
  475.     }
  476.     /**
  477.      * Get 'Add item' error code
  478.      *
  479.      * @return string|void
  480.      */
  481.     public function getAddItemError()
  482.     {
  483.         return $this->addItemError;
  484.     }
  485.     /**
  486.      * Get item from order by another item
  487.      *
  488.      * @param \XLite\Model\OrderItem $item Another item
  489.      *
  490.      * @return \XLite\Model\OrderItem|void
  491.      */
  492.     public function getItemByItem(\XLite\Model\OrderItem $item)
  493.     {
  494.         return $this->getItemByItemKey($item->getKey());
  495.     }
  496.     /**
  497.      * Get item from order by item key
  498.      *
  499.      * @param string $key Item key
  500.      *
  501.      * @return \XLite\Model\OrderItem|void
  502.      */
  503.     public function getItemByItemKey($key)
  504.     {
  505.         $items $this->getItems();
  506.         return \Includes\Utils\ArrayManager::findValue(
  507.             $items,
  508.             [$this'checkItemKeyEqual'],
  509.             $key
  510.         );
  511.     }
  512.     /**
  513.      * Get item from order by item  id
  514.      *
  515.      * @param integer $itemId Item id
  516.      *
  517.      * @return \XLite\Model\OrderItem|void
  518.      */
  519.     public function getItemByItemId($itemId)
  520.     {
  521.         $items $this->getItems();
  522.         return \Includes\Utils\ArrayManager::findValue(
  523.             $items,
  524.             [$this'checkItemIdEqual'],
  525.             $itemId
  526.         );
  527.     }
  528.     /**
  529.      * Find items by product ID
  530.      *
  531.      * @param integer $productId Product ID to use
  532.      *
  533.      * @return array
  534.      */
  535.     public function getItemsByProductId($productId)
  536.     {
  537.         $items $this->getItems();
  538.         return \Includes\Utils\ArrayManager::filter(
  539.             $items,
  540.             [$this'isItemProductIdEqual'],
  541.             $productId
  542.         );
  543.     }
  544.     /**
  545.      * Normalize items
  546.      *
  547.      * @throws \Doctrine\ORM\ORMInvalidArgumentException
  548.      *
  549.      * @return void
  550.      */
  551.     public function normalizeItems()
  552.     {
  553.         // Normalize by key
  554.         $keys = [];
  555.         foreach ($this->getItems() as $item) {
  556.             $key $item->getKey();
  557.             if (isset($keys[$key])) {
  558.                 $keys[$key]->setAmount($keys[$key]->getAmount() + $item->getAmount());
  559.                 $this->getItems()->removeElement($item);
  560.                 if (\XLite\Core\Database::getEM()->contains($item)) {
  561.                     \XLite\Core\Database::getEM()->remove($item);
  562.                 }
  563.             } else {
  564.                 $keys[$key] = $item;
  565.             }
  566.         }
  567.         unset($keys);
  568.         // Remove invalid items
  569.         foreach ($this->getItems() as $item) {
  570.             if (!$item->isValid()) {
  571.                 $this->getItems()->removeElement($item);
  572.                 if (\XLite\Core\Database::getEM()->contains($item)) {
  573.                     \XLite\Core\Database::getEM()->remove($item);
  574.                 }
  575.             }
  576.         }
  577.     }
  578.     /**
  579.      * Return items number
  580.      *
  581.      * @return integer
  582.      */
  583.     public function countItems()
  584.     {
  585.         return count($this->getItems());
  586.     }
  587.     /**
  588.      * Return order items total quantity
  589.      *
  590.      * @return integer
  591.      */
  592.     public function countQuantity()
  593.     {
  594.         $quantity 0;
  595.         foreach ($this->getItems() as $item) {
  596.             $quantity += $item->getAmount();
  597.         }
  598.         return $quantity;
  599.     }
  600.     /**
  601.      * Get failure reason
  602.      *
  603.      * @return string
  604.      */
  605.     public function getFailureReason()
  606.     {
  607.         $transactions $this->getPaymentTransactions()->getValues();
  608.         /** @var \XLite\Model\Payment\Transaction $transaction */
  609.         foreach (array_reverse($transactions) as $transaction) {
  610.             if ($transaction->isFailed()) {
  611.                 if ($transaction->getNote() && $transaction->getNote() !== Transaction::getDefaultFailedReason()) {
  612.                     return $transaction->getNote();
  613.                 }
  614.                 $reason $transaction->getDataCell('status');
  615.                 if ($reason && $reason->getValue()) {
  616.                     return $reason->getValue();
  617.                 }
  618.             }
  619.         }
  620.         return null;
  621.     }
  622.     /**
  623.      * Checks whether the shopping cart/order is empty
  624.      *
  625.      * @return boolean
  626.      */
  627.     public function isEmpty()
  628.     {
  629.         return >= $this->countItems();
  630.     }
  631.     /**
  632.      * Check order subtotal
  633.      *
  634.      * @return boolean
  635.      */
  636.     public function isMinOrderAmountError()
  637.     {
  638.         return $this->getSubtotal() < \XLite\Core\Config::getInstance()->General->minimal_order_amount;
  639.     }
  640.     /**
  641.      * Check order subtotal
  642.      *
  643.      * @return boolean
  644.      */
  645.     public function isMaxOrderAmountError()
  646.     {
  647.         return $this->getSubtotal() > \XLite\Core\Config::getInstance()->General->maximal_order_amount;
  648.     }
  649.     /**
  650.      * Return printable order number
  651.      *
  652.      * @return string
  653.      */
  654.     public function getPrintableOrderNumber()
  655.     {
  656.         $orderNumber = (string) $this->getOrderNumber();
  657.         return '#' str_repeat('0'min(5strlen($orderNumber))) . $orderNumber;
  658.     }
  659.     /**
  660.      * Check - is order processed or not
  661.      *
  662.      * @return boolean
  663.      */
  664.     public function isProcessed()
  665.     {
  666.         return in_array(
  667.             $this->getPaymentStatusCode(),
  668.             [
  669.                 \XLite\Model\Order\Status\Payment::STATUS_PART_PAID,
  670.                 \XLite\Model\Order\Status\Payment::STATUS_PAID,
  671.             ],
  672.             true
  673.         );
  674.     }
  675.     /**
  676.      * Check - is order queued or not
  677.      *
  678.      * @return boolean
  679.      */
  680.     public function isQueued()
  681.     {
  682.         return $this->getPaymentStatusCode() === \XLite\Model\Order\Status\Payment::STATUS_QUEUED;
  683.     }
  684.     /**
  685.      * Check item amounts
  686.      *
  687.      * @return array
  688.      */
  689.     public function getItemsWithWrongAmounts()
  690.     {
  691.         $items = [];
  692.         foreach ($this->getItems() as $item) {
  693.             if ($item->hasWrongAmount()) {
  694.                 $items[] = $item;
  695.             }
  696.         }
  697.         return $items;
  698.     }
  699.     /**
  700.      * Set profile
  701.      *
  702.      * @param \XLite\Model\Profile $profile Profile OPTIONAL
  703.      *
  704.      * @throws \Doctrine\ORM\ORMInvalidArgumentException
  705.      *
  706.      * @return void
  707.      */
  708.     public function setProfile(\XLite\Model\Profile $profile null)
  709.     {
  710.         if (
  711.             $this->getProfile()
  712.             && $this->getProfile()->getOrder()
  713.             && (!$profile || $this->getProfile()->getProfileId() != $profile->getProfileId())
  714.         ) {
  715.             $this->getProfile()->setOrder(null);
  716.             if ($this->getProfile()->getAnonymous() && $profile && !$profile->getAnonymous()) {
  717.                 \XLite\Core\Database::getEM()->remove($this->getProfile());
  718.             }
  719.         }
  720.         $this->profile $profile;
  721.     }
  722.     /**
  723.      * Set original profile
  724.      * FIXME: is it really needed?
  725.      *
  726.      * @param \XLite\Model\Profile $profile Profile OPTIONAL
  727.      *
  728.      * @return void
  729.      */
  730.     public function setOrigProfile(\XLite\Model\Profile $profile null)
  731.     {
  732.         if (
  733.             $this->getOrigProfile()
  734.             && $this->getOrigProfile()->getOrder()
  735.             && (!$profile || $this->getOrigProfile()->getProfileId() != $profile->getProfileId())
  736.             && (!$this->getProfile() || $this->getOrigProfile()->getProfileId() != $this->getProfile()->getProfileId())
  737.         ) {
  738.             $this->getOrigProfile()->setOrder(null);
  739.         }
  740.         $this->orig_profile $profile;
  741.     }
  742.     /**
  743.      * Set profile copy
  744.      *
  745.      * @param \XLite\Model\Profile $profile Profile
  746.      *
  747.      * @return void
  748.      */
  749.     public function setProfileCopy(\XLite\Model\Profile $profile)
  750.     {
  751.         // Set profile as original profile
  752.         $this->setOrigProfile($profile);
  753.         // Clone profile and set as order profile
  754.         $clonedProfile $profile->cloneEntity();
  755.         $this->setProfile($clonedProfile);
  756.         $clonedProfile->setOrder($this);
  757.     }
  758.     // {{{ Clone
  759.     /**
  760.      * clone Order as temporary
  761.      *
  762.      * @return \XLite\Model\Order
  763.      */
  764.     public function cloneOrderAsTemporary()
  765.     {
  766.         $isTemporary $this->isTemporary();
  767.         $this->setTemporary(true);
  768.         $result $this->cloneEntity();
  769.         $result->setOrderNumber(null);
  770.         $result->setIsNotificationsAllowedFlag(null);
  771.         $result->setTemporary(true);
  772.         $this->setTemporary($isTemporary);
  773.         return $result;
  774.     }
  775.     /**
  776.      * Clone order and all related data
  777.      *
  778.      * @return \XLite\Model\Order
  779.      */
  780.     public function cloneEntity()
  781.     {
  782.         // Clone order
  783.         $newOrder parent::cloneEntity();
  784.         $this->cloneEntityAssignOrderNumber($newOrder);
  785.         $this->cloneEntityProfile($newOrder);
  786.         $this->cloneEntityCurrency($newOrder);
  787.         $this->cloneEntityOrderStatuses($newOrder);
  788.         $this->cloneOrderItems($newOrder);
  789.         $this->cloneEntityOrderDetails($newOrder);
  790.         $this->cloneEntityTrackingNumbers($newOrder);
  791.         $this->cloneEntityEvents($newOrder);
  792.         $this->cloneEntitySurcharges($newOrder);
  793.         $this->cloneEntityPaymentTransactions($newOrder);
  794.         return $newOrder;
  795.     }
  796.     /**
  797.      * @return string|null
  798.      */
  799.     public function getStockStatus()
  800.     {
  801.         return $this->stockStatus;
  802.     }
  803.     /**
  804.      * @param string|null $stockStatus
  805.      */
  806.     public function setStockStatus($stockStatus)
  807.     {
  808.         $this->stockStatus $stockStatus;
  809.     }
  810.     /**
  811.      * Assign order number for cloned entity
  812.      *
  813.      * @param \XLite\Model\Order $newOrder
  814.      * @throws \Doctrine\ORM\ORMException
  815.      */
  816.     protected function cloneEntityAssignOrderNumber($newOrder)
  817.     {
  818.         if ($this->getOrderNumber() && !$this->isTemporary()) {
  819.             $newOrder->setOrderNumber(
  820.                 \XLite\Core\Database::getRepo('XLite\Model\Order')->findNextOrderNumber()
  821.             );
  822.         }
  823.     }
  824.     /**
  825.      * Assign profiles for cloned entity
  826.      *
  827.      * @param \XLite\Model\Order $newOrder
  828.      */
  829.     protected function cloneEntityProfile($newOrder)
  830.     {
  831.         $newOrder->setOrigProfile($this->getOrigProfile());
  832.         if ($this->getProfile()) {
  833.             $clonedProfile $this->getProfile()->cloneEntity();
  834.             $newOrder->setProfile($clonedProfile);
  835.             $clonedProfile->setOrder($newOrder);
  836.         }
  837.     }
  838.     /**
  839.      * Assign currency for cloned entity
  840.      *
  841.      * @param \XLite\Model\Order $newOrder
  842.      */
  843.     protected function cloneEntityCurrency($newOrder)
  844.     {
  845.         $newOrder->setCurrency($this->getCurrency());
  846.     }
  847.     /**
  848.      * Assign order statuses for cloned entity
  849.      *
  850.      * @param \XLite\Model\Order $newOrder
  851.      */
  852.     protected function cloneEntityOrderStatuses($newOrder)
  853.     {
  854.         $newOrder->setPaymentStatus($this->getPaymentStatus());
  855.         $newOrder->setShippingStatus($this->getShippingStatus());
  856.     }
  857.     /**
  858.      * Assign order details for cloned entity
  859.      *
  860.      * @param \XLite\Model\Order $newOrder
  861.      */
  862.     protected function cloneEntityOrderDetails($newOrder)
  863.     {
  864.         foreach ($this->getDetails() as $detail) {
  865.             $clonedDetails $detail->cloneEntity();
  866.             $newOrder->addDetails($clonedDetails);
  867.             $clonedDetails->setOrder($newOrder);
  868.         }
  869.     }
  870.     /**
  871.      * Assign tracking numbers for cloned entity
  872.      *
  873.      * @param \XLite\Model\Order $newOrder
  874.      */
  875.     protected function cloneEntityTrackingNumbers($newOrder)
  876.     {
  877.         foreach ($this->getTrackingNumbers() as $tn) {
  878.             $clonedTN $tn->cloneEntity();
  879.             $newOrder->addTrackingNumbers($clonedTN);
  880.             $clonedTN->setOrder($newOrder);
  881.         }
  882.     }
  883.     /**
  884.      * Assign events for cloned entity
  885.      *
  886.      * @param \XLite\Model\Order $newOrder
  887.      */
  888.     protected function cloneEntityEvents($newOrder)
  889.     {
  890.         foreach ($this->getEvents() as $event) {
  891.             $cloned $event->cloneEntity();
  892.             $newOrder->addEvents($cloned);
  893.             $cloned->setOrder($newOrder);
  894.             $cloned->setAuthor($event->getAuthor());
  895.         }
  896.     }
  897.     /**
  898.      * Assign surcharges for cloned entity
  899.      *
  900.      * @param \XLite\Model\Order $newOrder
  901.      */
  902.     protected function cloneEntitySurcharges($newOrder)
  903.     {
  904.         foreach ($this->getSurcharges() as $surcharge) {
  905.             $cloned $surcharge->cloneEntity();
  906.             $newOrder->addSurcharges($cloned);
  907.             $cloned->setOwner($newOrder);
  908.         }
  909.     }
  910.     /**
  911.      * Assign payment transactions for cloned entity
  912.      *
  913.      * @param \XLite\Model\Order $newOrder
  914.      */
  915.     protected function cloneEntityPaymentTransactions($newOrder)
  916.     {
  917.         foreach ($this->getPaymentTransactions() as $pt) {
  918.             $cloned $pt->cloneEntity();
  919.             $newOrder->addPaymentTransactions($cloned);
  920.             $cloned->setOrder($newOrder);
  921.         }
  922.     }
  923.     /**
  924.      * Clone order items
  925.      *
  926.      * @param \XLite\Model\Order $newOrder New Order
  927.      *
  928.      * @return void
  929.      */
  930.     protected function cloneOrderItems($newOrder)
  931.     {
  932.         // Clone order items
  933.         foreach ($this->getItems() as $item) {
  934.             $clonedItem $item->cloneEntity();
  935.             $clonedItem->setOrder($newOrder);
  936.             $newOrder->addItems($clonedItem);
  937.         }
  938.     }
  939.     // }}}
  940.     /**
  941.      * Get shipping method name
  942.      *
  943.      * @return string
  944.      */
  945.     public function getShippingMethodName()
  946.     {
  947.         $shipping \XLite\Core\Database::getRepo('XLite\Model\Shipping\Method')->find($this->getShippingId());
  948.         return $shipping $shipping->getName() : $this->shipping_method_name;
  949.     }
  950.     /**
  951.      * Get payment method name
  952.      *
  953.      * @return string
  954.      */
  955.     public function getPaymentMethodName()
  956.     {
  957.         $paymentMethod $this->getPaymentMethod();
  958.         return $paymentMethod $paymentMethod->getName() : $this->payment_method_name;
  959.     }
  960.     /**
  961.      * Get payment method name
  962.      *
  963.      * @return string
  964.      */
  965.     public function getPaymentMethodId()
  966.     {
  967.         $paymentMethod $this->getPaymentMethod();
  968.         return $paymentMethod $paymentMethod->getMethodId() : null;
  969.     }
  970.     /**
  971.      * Set old payment status of the order (not stored in the DB)
  972.      *
  973.      * @param string $paymentStatus Payment status
  974.      *
  975.      * @return void
  976.      */
  977.     public function setOldPaymentStatus($paymentStatus)
  978.     {
  979.         $this->oldPaymentStatus $paymentStatus;
  980.     }
  981.     /**
  982.      * Set old shipping status of the order (not stored in the DB)
  983.      *
  984.      * @param string $shippingStatus Shipping status
  985.      *
  986.      * @return void
  987.      */
  988.     public function setOldShippingStatus($shippingStatus)
  989.     {
  990.         $this->oldShippingStatus $shippingStatus;
  991.     }
  992.     /**
  993.      * Get items list fingerprint by amount and key
  994.      *
  995.      * @return string
  996.      */
  997.     public function getItemsAmountKeyFingerprint()
  998.     {
  999.         $result false;
  1000.         if (!$this->isEmpty()) {
  1001.             $items = [];
  1002.             foreach ($this->getItems() as $item) {
  1003.                 $items[] = [
  1004.                     $item->getKey(),
  1005.                     $item->getAmount()
  1006.                 ];
  1007.             }
  1008.             $result md5(serialize($items));
  1009.         }
  1010.         return $result;
  1011.     }
  1012.     /**
  1013.      * Get items list fingerprint
  1014.      *
  1015.      * @return string
  1016.      */
  1017.     public function getItemsFingerprint()
  1018.     {
  1019.         $result false;
  1020.         if (!$this->isEmpty()) {
  1021.             $items = [];
  1022.             foreach ($this->getItems() as $item) {
  1023.                 $items[] = [
  1024.                     $item->getItemId(),
  1025.                     $item->getKey(),
  1026.                     $item->getAmount()
  1027.                 ];
  1028.             }
  1029.             $result md5(serialize($items));
  1030.         }
  1031.         return $result;
  1032.     }
  1033.     /**
  1034.      * Get fingerprint of cart items corresponding to specific product id
  1035.      *
  1036.      * @param integer $productId
  1037.      *
  1038.      * @return string
  1039.      */
  1040.     public function getItemsFingerprintByProductId($productId)
  1041.     {
  1042.         $result false;
  1043.         if (!$this->isEmpty()) {
  1044.             $items = [];
  1045.             foreach ($this->getItems() as $item) {
  1046.                 if ($item->getObject() && $item->getObject()->getId() == $productId) {
  1047.                     $items[] = [
  1048.                         $item->getItemId(),
  1049.                         $item->getKey(),
  1050.                         $item->getAmount(),
  1051.                     ];
  1052.                 }
  1053.             }
  1054.             $result md5(serialize($items));
  1055.         }
  1056.         return $result;
  1057.     }
  1058.     /**
  1059.      * Generate a string representation of the order
  1060.      * to send to a payment service
  1061.      *
  1062.      * @return string
  1063.      */
  1064.     public function getDescription()
  1065.     {
  1066.         $result = [];
  1067.         foreach ($this->getItems() as $item) {
  1068.             $result[] = $item->getDescription();
  1069.         }
  1070.         return implode("\n"$result);
  1071.     }
  1072.     /**
  1073.      * Get order fingerprint for event subsystem
  1074.      *
  1075.      * @param array $exclude Exclude kes OPTIONAL
  1076.      *
  1077.      * @return array
  1078.      */
  1079.     public function getEventFingerprint(array $exclude = [])
  1080.     {
  1081.         $keys array_diff($this->defineFingerprintKeys(), $exclude);
  1082.         $hash = [];
  1083.         foreach ($keys as $key) {
  1084.             $method 'getFingerprintBy' ucfirst($key);
  1085.             $hash[$key] = $this->$method();
  1086.         }
  1087.         return $hash;
  1088.     }
  1089.     /**
  1090.      * Returns delivery source address
  1091.      *
  1092.      * @return \XLite\Model\Address
  1093.      */
  1094.     public function getSourceAddress()
  1095.     {
  1096.         if ($this->sourceAddress === null) {
  1097.             $address = new \XLite\Model\Address();
  1098.             $config $this->getSourceAddressConfiguration();
  1099.             $address->setAddress1($config->origin_address1);
  1100.             $address->setAddress2($config->origin_address2);
  1101.             $address->setAddress3($config->origin_address3);
  1102.             $address->setCity($config->origin_city);
  1103.             $address->setCountryCode($config->origin_country);
  1104.             $address->setName($config->company_name);
  1105.             if ($config->origin_state) {
  1106.                 $address->setStateId($config->origin_state);
  1107.             }
  1108.             if ($config->origin_custom_state) {
  1109.                 $address->setCustomState($config->origin_custom_state);
  1110.             }
  1111.             $address->setZipcode($config->origin_zipcode);
  1112.             $address->setPhone($config->company_phone);
  1113.             $this->sourceAddress $address;
  1114.         }
  1115.         return $this->sourceAddress;
  1116.     }
  1117.     /**
  1118.      * Returns company configuration for getSourceAddress()
  1119.      *
  1120.      * @return \XLite\Core\ConfigCell
  1121.      */
  1122.     protected function getSourceAddressConfiguration()
  1123.     {
  1124.         return \XLite\Core\Config::getInstance()->Company;
  1125.     }
  1126.     /**
  1127.      * Returns company configuration
  1128.      *
  1129.      * @return \XLite\Core\ConfigCell
  1130.      */
  1131.     public function getCompanyConfiguration()
  1132.     {
  1133.         return \XLite\Core\Config::getInstance()->Company;
  1134.     }
  1135.     /**
  1136.      * Define fingerprint keys
  1137.      *
  1138.      * @return array
  1139.      */
  1140.     protected function defineFingerprintKeys()
  1141.     {
  1142.         return [
  1143.             'items',
  1144.             'itemsCount',
  1145.             'shippingTotal',
  1146.             'total',
  1147.             'shippingMethodId',
  1148.             'paymentMethodId',
  1149.             'shippingMethodsHash',
  1150.             'paymentMethodsHash',
  1151.             'shippingAddressId',
  1152.             'billingAddressId',
  1153.             'shippingAddressFields',
  1154.             'billingAddressFields',
  1155.             'sameAddress',
  1156.         ];
  1157.     }
  1158.     /**
  1159.      * Get fingerprint by 'items' key
  1160.      *
  1161.      * @return array
  1162.      */
  1163.     protected function getFingerprintByItems()
  1164.     {
  1165.         $list = [];
  1166.         foreach ($this->getItems() as $item) {
  1167.             $event $item->getEventCell();
  1168.             // float is intended here to adopt fractional quantities via modules
  1169.             $event['quantity'] = (float) $item->getAmount();
  1170.             if ($this instanceof \XLite\Model\Cart && $this->isItemLimitReached($item)) {
  1171.                 $event['is_limit'] = 1;
  1172.             }
  1173.             $list[] = $event;
  1174.         }
  1175.         return $list;
  1176.     }
  1177.     /**
  1178.      * Return true if item amount limit is reached
  1179.      *
  1180.      * @param \XLite\Model\OrderItem $item Order item
  1181.      *
  1182.      * @return boolean
  1183.      */
  1184.     protected function isItemLimitReached($item)
  1185.     {
  1186.         $result false;
  1187.         $object $item->getObject();
  1188.         if ($object) {
  1189.             $result $object->getInventoryEnabled() && $object->getPublicAmount() <= $item->getAmount();
  1190.         }
  1191.         return $result;
  1192.     }
  1193.     /**
  1194.      * Get fingerprint by 'itemsCount' key
  1195.      *
  1196.      * @return int
  1197.      */
  1198.     protected function getFingerprintByItemsCount()
  1199.     {
  1200.         return (int) $this->countQuantity();
  1201.     }
  1202.     /**
  1203.      * Get fingerprint by 'shippingTotal' key
  1204.      *
  1205.      * @return float
  1206.      */
  1207.     protected function getFingerprintByShippingTotal()
  1208.     {
  1209.         /** @var \XLite\Logic\Order\Modifier\Shipping $shippingModifier */
  1210.         $shippingModifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  1211.         if (!$shippingModifier) {
  1212.             return 0;
  1213.         }
  1214.         $rate $shippingModifier->getSelectedRate();
  1215.         return $rate ? (float)$rate->getTotalRate() : 0;
  1216.     }
  1217.     /**
  1218.      * Get fingerprint by 'total' key
  1219.      *
  1220.      * @return float
  1221.      */
  1222.     protected function getFingerprintByTotal()
  1223.     {
  1224.         return (float) $this->getTotal();
  1225.     }
  1226.     /**
  1227.      * Get fingerprint by 'shippingMethodId' key
  1228.      *
  1229.      * @return integer
  1230.      */
  1231.     protected function getFingerprintByShippingMethodId()
  1232.     {
  1233.         /** @var \XLite\Logic\Order\Modifier\Shipping $shippingModifier */
  1234.         $shippingModifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  1235.         if (!$shippingModifier) {
  1236.             return 0;
  1237.         }
  1238.         $rate $shippingModifier->getSelectedRate();
  1239.         return $rate $rate->getMethod()->getMethodId() : 0;
  1240.     }
  1241.     /**
  1242.      * Get fingerprint by 'paymentMethodId' key
  1243.      *
  1244.      * @return integer
  1245.      */
  1246.     protected function getFingerprintByPaymentMethodId()
  1247.     {
  1248.         return $this->getPaymentMethod()
  1249.                 ? $this->getPaymentMethod()->getMethodId()
  1250.                 : 0;
  1251.     }
  1252.     /**
  1253.      * Get fingerprint by 'shippingMethodsHash' key
  1254.      *
  1255.      * @return string
  1256.      */
  1257.     protected function getFingerprintByShippingMethodsHash()
  1258.     {
  1259.         /** @var \XLite\Logic\Order\Modifier\Shipping $shippingModifier */
  1260.         $shippingModifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  1261.         $shippingMethodsHash = [];
  1262.         if ($shippingModifier) {
  1263.             $rates $shippingModifier->getRates();
  1264.             foreach ($rates as $rate) {
  1265.                 $methodId $rate->getMethod()->getMethodId();
  1266.                 $totalRate $rate->getTotalRate();
  1267.                 $shippingMethodsHash[] = "$methodId:$totalRate";
  1268.             }
  1269.         }
  1270.         return implode(';'$shippingMethodsHash);
  1271.     }
  1272.     /**
  1273.      * Get fingerprint by 'paymentMethodsHash' key
  1274.      *
  1275.      * @return string
  1276.      */
  1277.     protected function getFingerprintByPaymentMethodsHash()
  1278.     {
  1279.         $paymentMethodsHash = [];
  1280.         foreach ($this->getPaymentMethods() as $method) {
  1281.             $paymentMethodsHash[] = $method->getMethodId();
  1282.         }
  1283.         return implode(';'$paymentMethodsHash);
  1284.     }
  1285.     /**
  1286.      * Get fingerprint by 'shippingAddressId' key
  1287.      *
  1288.      * @return integer
  1289.      */
  1290.     protected function getFingerprintByShippingAddressId()
  1291.     {
  1292.         return $this->getProfile() && $this->getProfile()->getShippingAddress()
  1293.             ? $this->getProfile()->getShippingAddress()->getAddressId()
  1294.             : 0;
  1295.     }
  1296.     /**
  1297.      * Get fingerprint by 'billingAddressId' key
  1298.      *
  1299.      * @return integer
  1300.      */
  1301.     protected function getFingerprintByBillingAddressId()
  1302.     {
  1303.         return $this->getProfile() && $this->getProfile()->getBillingAddress()
  1304.             ? $this->getProfile()->getBillingAddress()->getAddressId()
  1305.             : 0;
  1306.     }
  1307.     /**
  1308.      * Get fingerprint by 'shippingAddressId' key
  1309.      *
  1310.      * @return integer
  1311.      */
  1312.     protected function getFingerprintByShippingAddressFields()
  1313.     {
  1314.         if ($this->getProfile() && $this->getProfile()->getShippingAddress()) {
  1315.             return $this->getProfile()->getShippingAddress()->serialize();
  1316.         }
  1317.         return '';
  1318.     }
  1319.     /**
  1320.      * Get fingerprint by 'billingAddressId' key
  1321.      *
  1322.      * @return integer
  1323.      */
  1324.     protected function getFingerprintByBillingAddressFields()
  1325.     {
  1326.         if ($this->getProfile() && $this->getProfile()->getBillingAddress()) {
  1327.             return $this->getProfile()->getBillingAddress()->serialize();
  1328.         }
  1329.         return '';
  1330.     }
  1331.     /**
  1332.      * Get fingerprint by 'sameAddress' key
  1333.      *
  1334.      * @return boolean
  1335.      */
  1336.     protected function getFingerprintBySameAddress()
  1337.     {
  1338.         return $this->getProfile() && $this->getProfile()->isSameAddress();
  1339.     }
  1340.     /**
  1341.      * Get detail
  1342.      *
  1343.      * @param string $name Details cell name
  1344.      *
  1345.      * @return mixed
  1346.      */
  1347.     public function getDetail($name)
  1348.     {
  1349.         $details $this->getDetails();
  1350.         return \Includes\Utils\ArrayManager::findValue(
  1351.             $details,
  1352.             [$this'checkDetailName'],
  1353.             $name
  1354.         );
  1355.     }
  1356.     /**
  1357.      * Get detail value
  1358.      *
  1359.      * @param string $name Details cell name
  1360.      *
  1361.      * @return mixed
  1362.      */
  1363.     public function getDetailValue($name)
  1364.     {
  1365.         $detail $this->getDetail($name);
  1366.         return $detail $detail->getValue() : null;
  1367.     }
  1368.     /**
  1369.      * Set detail cell
  1370.      * To unset detail cell the value should be null
  1371.      *
  1372.      * @param string $name  Cell code
  1373.      * @param mixed  $value Cell value
  1374.      * @param string $label Cell label OPTIONAL
  1375.      *
  1376.      * @return void
  1377.      */
  1378.     public function setDetail($name$value$label null)
  1379.     {
  1380.         $detail $this->getDetail($name);
  1381.         if ($value === null) {
  1382.             if ($detail) {
  1383.                 $this->getDetails()->removeElement($detail);
  1384.                 \XLite\Core\Database::getEM()->remove($detail);
  1385.             }
  1386.         } else {
  1387.             if (!$detail) {
  1388.                 $detail = new \XLite\Model\OrderDetail();
  1389.                 $detail->setOrder($this);
  1390.                 $this->addDetails($detail);
  1391.                 $detail->setName($name);
  1392.             }
  1393.             $detail->setValue($value);
  1394.             $detail->setLabel($label);
  1395.         }
  1396.     }
  1397.     /**
  1398.      * Get meaning order details
  1399.      *
  1400.      * @return array
  1401.      */
  1402.     public function getMeaningDetails()
  1403.     {
  1404.         $result = [];
  1405.         foreach ($this->getDetails() as $detail) {
  1406.             if ($detail->getLabel()) {
  1407.                 $result[] = $detail;
  1408.             }
  1409.         }
  1410.         return $result;
  1411.     }
  1412.     /**
  1413.      * Called when an order successfully placed by a client
  1414.      *
  1415.      * @throws \Doctrine\ORM\NoResultException
  1416.      * @throws \Doctrine\ORM\NonUniqueResultException
  1417.      */
  1418.     public function processSucceed()
  1419.     {
  1420.         $this->markAsOrder();
  1421.         if ($this->canChangeStatusOnSucceed()) {
  1422.             $this->setShippingStatus(\XLite\Model\Order\Status\Shipping::STATUS_NEW);
  1423.         }
  1424.         $property \XLite\Core\Database::getRepo('XLite\Model\Order\Status\Property')->findOneBy([
  1425.             'paymentStatus'  => $this->getPaymentStatus(),
  1426.             'shippingStatus' => $this->getShippingStatus(),
  1427.         ]);
  1428.         $incStock $property $property->getIncStock() : null;
  1429.         if ($incStock === false) {
  1430.             $this->processBackorderedItems();
  1431.             $this->decreaseInventory();
  1432.             $this->setStockStatus('reduced');
  1433.         }
  1434.         // Register 'Place order' event in the order history
  1435.         $this->registerPlaceOrder();
  1436.         // Transform attributes
  1437.         $this->transformItemsAttributes();
  1438.         $this->sendOrderCreatedIfNeeded();
  1439.         $this->sendOrderWaitingForApproveIfNeeded();
  1440.     }
  1441.     /**
  1442.      * @return int
  1443.      * @throws \Doctrine\ORM\NoResultException
  1444.      * @throws \Doctrine\ORM\NonUniqueResultException
  1445.      */
  1446.     protected function processBackorderedItems()
  1447.     {
  1448.         $backstockCount 0;
  1449.         $items $this->getItems();
  1450.         foreach ($items as $item) {
  1451.             if ($item->isDeleted() || !$this->isInventoryEnabledForItem($item)) {
  1452.                 continue;
  1453.             }
  1454.             \XLite\Core\Database::getEM()->transactional(function (EntityManager $em) use ($item, &$backstockCount) {
  1455.                 /* @var OrderItem $ite */
  1456.                 $product $item->getProduct();
  1457.                 $em->lock($item->getProduct(), LockMode::PESSIMISTIC_READ);
  1458.                 $em->refresh($product);
  1459.                 $pa $this->getProductAmountForItem($item);
  1460.                 $this->reduceProductAmountForItem($item);
  1461.                 $ia $item->getAmount()+$item->getquantity1();
  1462.                 if ($pa $ia) {
  1463.                     $backstockCount += $ia $pa;
  1464.                     $item->setBackorderedAmount($ia $pa);
  1465.                 }
  1466.             });
  1467.         }
  1468.         if ($backstockCount) {
  1469.             \XLite\Core\Mailer::sendBackorderCreatedAdmin($this);
  1470.             $this->setShippingStatus(\XLite\Model\Order\Status\Shipping::STATUS_NEW_BACKORDERED);
  1471.             //Set oldShippingStatus to NBA
  1472.             $this->setShippingStatus(\XLite\Model\Order\Status\Shipping::STATUS_NEW_BACKORDERED);
  1473.             $this->assignBackorderCompetitors();
  1474.         }
  1475.         return $backstockCount;
  1476.     }
  1477.     /**
  1478.      * @throws \Doctrine\ORM\NoResultException
  1479.      * @throws \Doctrine\ORM\NonUniqueResultException
  1480.      */
  1481.     protected function assignBackorderCompetitors()
  1482.     {
  1483.         $repo Database::getRepo('XLite\Model\Order');
  1484.         $competitors $this->getBackorderCompetitors();
  1485.         foreach ($this->getItems() as $item) {
  1486.             if (
  1487.                 $item->getBackorderedAmount()
  1488.                 && $competitor $repo->getBackorderCompetitorByItem($item)
  1489.             ) {
  1490.                 if (!$competitors->contains($competitor)) {
  1491.                     $this->getBackorderCompetitors()->add($competitor);
  1492.                 }
  1493.             }
  1494.         }
  1495.     }
  1496.     /**
  1497.      * @return bool
  1498.      */
  1499.     public function isBackordered()
  1500.     {
  1501.         return $this->getShippingStatusCode() === \XLite\Model\Order\Status\Shipping::STATUS_NEW_BACKORDERED;
  1502.     }
  1503.     /**
  1504.      * @param OrderItem $item
  1505.      *
  1506.      * @return int
  1507.      */
  1508.     protected function getProductAmountForItem(OrderItem $item)
  1509.     {
  1510.         $key $this->getProductKeyForItem($item);
  1511.         if (!isset($this->backorderProductAmounts[$key])) {
  1512.             $this->backorderProductAmounts[$key] = $this->calculateProductAmountForItem($item);
  1513.         }
  1514.         return $this->backorderProductAmounts[$key];
  1515.     }
  1516.     /**
  1517.      * @param OrderItem $item
  1518.      *
  1519.      * @return int
  1520.      */
  1521.     protected function calculateProductAmountForItem(OrderItem $item)
  1522.     {
  1523.         return $item->getProduct()->getAmount();
  1524.     }
  1525.     /**
  1526.      * @param OrderItem $item
  1527.      *
  1528.      * @return static
  1529.      */
  1530.     protected function reduceProductAmountForItem(OrderItem $item)
  1531.     {
  1532.         $this->backorderProductAmounts[$this->getProductKeyForItem($item)]
  1533.             = max($this->getProductAmountForItem($item) - $item->getAmount(), 0);
  1534.         return $this;
  1535.     }
  1536.     /**
  1537.      * @param OrderItem $item
  1538.      *
  1539.      * @return boolean
  1540.      */
  1541.     protected function isInventoryEnabledForItem(OrderItem $item)
  1542.     {
  1543.         return $item->getProduct()->getInventoryEnabled();
  1544.     }
  1545.     /**
  1546.      * @param OrderItem $item
  1547.      *
  1548.      * @return string
  1549.      */
  1550.     protected function getProductKeyForItem(OrderItem $item)
  1551.     {
  1552.         return $item->getProduct()->getId();
  1553.     }
  1554.     protected function registerPlaceOrder()
  1555.     {
  1556.         \XLite\Core\OrderHistory::getInstance()->registerPlaceOrder($this->getOrderId());
  1557.     }
  1558.     /**
  1559.      * Send order created notification if needed
  1560.      */
  1561.     public function sendOrderCreatedIfNeeded()
  1562.     {
  1563.         if (
  1564.             $this->getPaymentStatus()
  1565.             && in_array(
  1566.                 $this->getPaymentStatus()->getCode(),
  1567.                 $this->getValidStatusesForCreateNotification(),
  1568.                 true
  1569.             )
  1570.         ) {
  1571.             \XLite\Core\Mailer::sendOrderCreated($this);
  1572.         }
  1573.     }
  1574.     /**
  1575.      * Send order awaiting approval notification if needed
  1576.      */
  1577.     public function sendOrderWaitingForApproveIfNeeded()
  1578.     {
  1579.         if (
  1580.             $this->getShippingStatus()
  1581.             && $this->getShippingStatus()->getCode() === \XLite\Model\Order\Status\Shipping::STATUS_WAITING_FOR_APPROVE
  1582.         ) {
  1583.             \XLite\Core\Mailer::sendOrderWaitingForApprove($this);
  1584.         }
  1585.     }
  1586.     /**
  1587.      * Valid not paid statuses
  1588.      *
  1589.      * @return array
  1590.      */
  1591.     public function getValidStatusesForCreateNotification()
  1592.     {
  1593.         return [
  1594.             \XLite\Model\Order\Status\Payment::STATUS_QUEUED,
  1595.             \XLite\Model\Order\Status\Payment::STATUS_AUTHORIZED,
  1596.         ];
  1597.     }
  1598.     /**
  1599.      * Mark cart as order
  1600.      *
  1601.      * @return void
  1602.      */
  1603.     public function markAsOrder()
  1604.     {
  1605.     }
  1606.     /**
  1607.      * Mark order as cart
  1608.      *
  1609.      * @return void
  1610.      */
  1611.     public function markAsCart()
  1612.     {
  1613.         $this->getRepository()->markAsCart($this->getOrderId());
  1614.     }
  1615.     /**
  1616.      * Refresh order items
  1617.      * TODO - rework after tax subsystem rework
  1618.      *
  1619.      * @return void
  1620.      */
  1621.     public function refreshItems()
  1622.     {
  1623.     }
  1624.     /**
  1625.      * Return removing status
  1626.      *
  1627.      * @return boolean
  1628.      */
  1629.     public function isRemoving()
  1630.     {
  1631.         return $this->isRemoving;
  1632.     }
  1633.     /**
  1634.      * Constructor
  1635.      *
  1636.      * @param array $data Entity properties OPTIONAL
  1637.      */
  1638.     public function __construct(array $data = [])
  1639.     {
  1640.         $this->details              = new ArrayCollection();
  1641.         $this->items                = new ArrayCollection();
  1642.         $this->surcharges           = new ArrayCollection();
  1643.         $this->payment_transactions = new ArrayCollection();
  1644.         $this->events               = new ArrayCollection();
  1645.         $this->trackingNumbers      = new ArrayCollection();
  1646.         $this->backorderCompetitors = new ArrayCollection();
  1647.         parent::__construct($data);
  1648.     }
  1649.     /**
  1650.      * Return list of available payment methods
  1651.      *
  1652.      * @return array
  1653.      */
  1654.     public function getPaymentMethods()
  1655.     {
  1656.         if (>= $this->getOpenTotal()) {
  1657.             return [];
  1658.         }
  1659.         $list \XLite\Core\Database::getRepo('XLite\Model\Payment\Method')
  1660.             ->findAllActive();
  1661.         foreach ($list as $i => $method) {
  1662.             if (!$method->isEnabled() || !$method->getProcessor()->isApplicable($this$method)) {
  1663.                 unset($list[$i]);
  1664.             }
  1665.         }
  1666.         if (\XLite\Core\Auth::getInstance()->isOperatingAsUserMode()) {
  1667.             $fakeMethods \XLite\Core\Auth::getInstance()->getOperateAsUserPaymentMethods();
  1668.             $filteredFakeMethods array_filter(
  1669.                 $fakeMethods,
  1670.                 static function ($fakeMethod) use ($list) {
  1671.                     $fakeServiceName $fakeMethod->getServiceName();
  1672.                     // Check if $list already contains fake method
  1673.                     return !array_reduce($list, static function ($carry$method) use ($fakeServiceName) {
  1674.                         return $carry ?: $method->getServiceName() === $fakeServiceName;
  1675.                     }, false);
  1676.                 }
  1677.             );
  1678.             $list array_merge(
  1679.                 $list,
  1680.                 $filteredFakeMethods
  1681.             );
  1682.         }
  1683.         return $list;
  1684.     }
  1685.     /**
  1686.      * Renew payment method
  1687.      *
  1688.      * @return void
  1689.      */
  1690.     public function renewPaymentMethod()
  1691.     {
  1692.         if ($this->isPaymentMethodRequired()) {
  1693.             $method $this->getPaymentMethod();
  1694.             if ($this->isPaymentMethodIsApplicable($method)) {
  1695.                 $this->setPaymentMethod($method);
  1696.             } else {
  1697.                 $first $this->getFirstPaymentMethod();
  1698.                 if ($first) {
  1699.                     if ($this->getProfile()) {
  1700.                         $this->getProfile()->setLastPaymentId($first->getMethodId());
  1701.                     }
  1702.                     $this->setPaymentMethod($first);
  1703.                 } else {
  1704.                     $this->unsetPaymentMethod();
  1705.                 }
  1706.             }
  1707.         } else {
  1708.             $this->unsetPaymentMethod();
  1709.         }
  1710.     }
  1711.     /**
  1712.      * Returns true if order is allowed to change status on succeed
  1713.      *
  1714.      * @return boolean
  1715.      */
  1716.     protected function canChangeStatusOnSucceed()
  1717.     {
  1718.         return $this->getShippingStatus() === null;
  1719.     }
  1720.     /**
  1721.      * Return true if payment method is required for the order
  1722.      *
  1723.      * @return boolean
  1724.      */
  1725.     protected function isPaymentMethodRequired()
  1726.     {
  1727.         return $this->getOpenTotal()
  1728.             && ($this instanceof \XLite\Model\Cart || $this->getOrderNumber() === null);
  1729.     }
  1730.     /**
  1731.      * Check if given payment method can be applied to the order
  1732.      *
  1733.      * @param  \XLite\Model\Payment\Method  $method
  1734.      * @return boolean
  1735.      */
  1736.     protected function isPaymentMethodIsApplicable($method)
  1737.     {
  1738.         return $method
  1739.             && $method->getProcessor()
  1740.             && $method->getProcessor()->isApplicable($this$method);
  1741.     }
  1742.     /**
  1743.      * Get payment method
  1744.      *
  1745.      * @return \XLite\Model\Payment\Method|null
  1746.      */
  1747.     public function getPaymentMethod()
  1748.     {
  1749.         $transaction $this->getFirstOpenPaymentTransaction();
  1750.         if (!$transaction) {
  1751.             $transaction $this->hasUnpaidTotal() || count($this->getPaymentTransactions()) === 0
  1752.                 $this->assignLastPaymentMethod()
  1753.                 : $this->getPaymentTransactions()->last();
  1754.         }
  1755.         return $transaction $transaction->getPaymentMethod() : null;
  1756.     }
  1757.     /**
  1758.      * Check item key equal
  1759.      *
  1760.      * @param \XLite\Model\OrderItem $item Item
  1761.      * @param string                 $key  Key
  1762.      *
  1763.      * @return boolean
  1764.      */
  1765.     public function checkItemKeyEqual(\XLite\Model\OrderItem $item$key)
  1766.     {
  1767.         return $item->getKey() == $key;
  1768.     }
  1769.     /**
  1770.      * Check item id equal
  1771.      *
  1772.      * @param \XLite\Model\OrderItem $item   Item
  1773.      * @param integer                $itemId Item id
  1774.      *
  1775.      * @return boolean
  1776.      */
  1777.     public function checkItemIdEqual(\XLite\Model\OrderItem $item$itemId)
  1778.     {
  1779.         return $item->getItemId() == $itemId;
  1780.     }
  1781.     /**
  1782.      * Check order detail name
  1783.      *
  1784.      * @param \XLite\Model\OrderDetail $detail Detail
  1785.      * @param string                   $name   Name
  1786.      *
  1787.      * @return boolean
  1788.      */
  1789.     public function checkDetailName(\XLite\Model\OrderDetail $detail$name)
  1790.     {
  1791.         return $detail->getName() === $name;
  1792.     }
  1793.     /**
  1794.      * Check payment transaction status
  1795.      *
  1796.      * @param \XLite\Model\Payment\Transaction $transaction Transaction
  1797.      * @param mixed                            $status      Status
  1798.      *
  1799.      * @return boolean
  1800.      */
  1801.     public function checkPaymentTransactionStatusEqual(\XLite\Model\Payment\Transaction $transaction$status)
  1802.     {
  1803.         return is_array($status)
  1804.             ? in_array($transaction->getStatus(), $statustrue)
  1805.             : $transaction->getStatus() === $status;
  1806.     }
  1807.     /**
  1808.      * Check payment transaction - open or not
  1809.      *
  1810.      * @param \XLite\Model\Payment\Transaction $transaction Payment transaction
  1811.      *
  1812.      * @return boolean
  1813.      */
  1814.     public function checkPaymentTransactionOpen(\XLite\Model\Payment\Transaction $transaction)
  1815.     {
  1816.         return $transaction->isOpen() || $transaction->isInProgress();
  1817.     }
  1818.     /**
  1819.      * Check - is item product id equal specified product id
  1820.      *
  1821.      * @param \XLite\Model\OrderItem $item      Item
  1822.      * @param integer                $productId Product id
  1823.      *
  1824.      * @return boolean
  1825.      */
  1826.     public function isItemProductIdEqual(\XLite\Model\OrderItem $item$productId)
  1827.     {
  1828.         return $item->getProductId() == $productId;
  1829.     }
  1830.     /**
  1831.      * Check last payment method
  1832.      *
  1833.      * @param \XLite\Model\Payment\Method $pmethod       Payment method
  1834.      * @param integer                     $lastPaymentId Last selected payment method id
  1835.      *
  1836.      * @return boolean
  1837.      */
  1838.     public function checkLastPaymentMethod(\XLite\Model\Payment\Method $pmethod$lastPaymentId)
  1839.     {
  1840.         $result $pmethod->getMethodId() == $lastPaymentId;
  1841.         if ($result) {
  1842.             $this->setPaymentMethod($pmethod);
  1843.             \XLite\Core\Database::getEM()->flush();
  1844.         }
  1845.         return $result;
  1846.     }
  1847.     // {{{ Payment method and transactions
  1848.     /**
  1849.      * Set payment method
  1850.      *
  1851.      * @param \XLite\Model\Payment\Method|null $paymentMethod Payment method
  1852.      * @param float                            $value         Payment transaction value OPTIONAL
  1853.      *
  1854.      * @return void
  1855.      */
  1856.     public function setPaymentMethod($paymentMethod$value null)
  1857.     {
  1858.         if ($paymentMethod && !($paymentMethod instanceof \XLite\Model\Payment\Method)) {
  1859.             $paymentMethod null;
  1860.         }
  1861.         $sameMethod false;
  1862.         if ($paymentMethod === null || $this->getFirstOpenPaymentTransaction()) {
  1863.             $transaction $this->getFirstOpenPaymentTransaction();
  1864.             if ($transaction) {
  1865.                 if ($paymentMethod === null) {
  1866.                     $this->unsetPaymentMethod();
  1867.                 } elseif ($transaction->isSameMethod($paymentMethod)) {
  1868.                     $transaction->updateValue($this);
  1869.                     $sameMethod true;
  1870.                 }
  1871.             }
  1872.         }
  1873.         if ($paymentMethod === null) {
  1874.             $this->setPaymentMethodName('');
  1875.         } elseif (!$sameMethod) {
  1876.             $this->unsetPaymentMethod();
  1877.             $this->addPaymentTransaction($paymentMethod$value);
  1878.             $this->setPaymentMethodName($paymentMethod->getName());
  1879.         }
  1880.     }
  1881.     /**
  1882.      * Unset payment method
  1883.      *
  1884.      * @throws \Doctrine\ORM\ORMInvalidArgumentException
  1885.      *
  1886.      * @return void
  1887.      */
  1888.     public function unsetPaymentMethod()
  1889.     {
  1890.         $transaction $this->getFirstOpenPaymentTransaction();
  1891.         $this->setPaymentMethodName('');
  1892.         if ($transaction) {
  1893.             $this->payment_transactions->removeElement($transaction);
  1894.             \XLite\Core\Database::getEM()->remove($transaction);
  1895.         }
  1896.     }
  1897.     /**
  1898.      * Get active payment transactions
  1899.      *
  1900.      * @return array
  1901.      */
  1902.     public function getActivePaymentTransactions()
  1903.     {
  1904.         $result = [];
  1905.         foreach ($this->getPaymentTransactions() as $t) {
  1906.             if ($t->isCompleted() || $t->isPending()) {
  1907.                 $result[] = $t;
  1908.             }
  1909.         }
  1910.         return $result;
  1911.     }
  1912.     /**
  1913.      * Get visible payment methods
  1914.      *
  1915.      * @return array
  1916.      */
  1917.     public function getVisiblePaymentMethods()
  1918.     {
  1919.         $result = [];
  1920.         foreach ($this->getActivePaymentTransactions() as $t) {
  1921.             if ($t->getPaymentMethod()) {
  1922.                 $result[] = $t->getPaymentMethod();
  1923.             }
  1924.         }
  1925.         if (count($result) === && count($this->getPaymentTransactions())) {
  1926.             $method $this->getPaymentTransactions()->last()->getPaymentMethod();
  1927.             if ($method) {
  1928.                 $result[] = $method;
  1929.             }
  1930.         }
  1931.         return $result;
  1932.     }
  1933.     /**
  1934.      * Get first open (not payed) payment transaction
  1935.      *
  1936.      * @return \XLite\Model\Payment\Transaction|null
  1937.      */
  1938.     public function getFirstOpenPaymentTransaction()
  1939.     {
  1940.         $transactions $this->getPaymentTransactions();
  1941.         return \Includes\Utils\ArrayManager::findValue(
  1942.             $transactions,
  1943.             [$this'checkPaymentTransactionOpen']
  1944.         );
  1945.     }
  1946.     /**
  1947.      * Get open (not-payed) total
  1948.      *
  1949.      * @return float
  1950.      */
  1951.     public function getOpenTotal()
  1952.     {
  1953.         $total $this->getCurrency()->roundValue($this->getTotal());
  1954.         $totalAsSurcharges $this->getCurrency()->roundValue($this->getSurchargesTotal());
  1955.         $totalsPaid $this->getPaidTotals();
  1956.         return max(
  1957.             $total $totalsPaid['total'],
  1958.             $totalAsSurcharges $totalsPaid['totalAsSurcharges']
  1959.         );
  1960.     }
  1961.     public function getPaidTotals()
  1962.     {
  1963.         $total $totalAsSurcharges 0;
  1964.         foreach ($this->getPaymentTransactions() as $t) {
  1965.             $total += $this->getCurrency()->roundValue($t->getChargeValueModifier());
  1966.             $totalAsSurcharges += $this->getCurrency()->roundValue($t->getChargeValueModifier());
  1967.         }
  1968.         return [
  1969.             'total'             => $total,
  1970.             'totalAsSurcharges' => $totalAsSurcharges,
  1971.         ];
  1972.     }
  1973.     /**
  1974.      * Get paid total
  1975.      *
  1976.      * @return float
  1977.      */
  1978.     public function getPaidTotal()
  1979.     {
  1980.         $totals $this->getPaidTotals();
  1981.         return max(
  1982.             $totals['total'],
  1983.             $totals['totalAsSurcharges']
  1984.         );
  1985.     }
  1986.     /**
  1987.      * Check - order is open (has initialized transactions and has open total) or not
  1988.      *
  1989.      * @return boolean
  1990.      */
  1991.     public function isOpen()
  1992.     {
  1993.         return $this->getFirstOpenPaymentTransaction() && $this->hasUnpaidTotal();
  1994.     }
  1995.     /**
  1996.      * Has unpaid total?
  1997.      *
  1998.      * @return boolean
  1999.      */
  2000.     public function hasUnpaidTotal()
  2001.     {
  2002.         return $this->getCurrency()->getMinimumValue() <= $this->getCurrency()->roundValue(abs($this->getOpenTotal()));
  2003.     }
  2004.     /**
  2005.      * Get totally payed total
  2006.      *
  2007.      * @return float
  2008.      */
  2009.     public function getPayedTotal()
  2010.     {
  2011.         $total $this->getCurrency()->roundValue($this->getTotal());
  2012.         foreach ($this->getPaymentTransactions() as $t) {
  2013.             if ($t->isCompleted() && ($t->isAuthorized() || $t->isCaptured())) {
  2014.                 $total -= $this->getCurrency()->roundValue($t->getChargeValueModifier());
  2015.             }
  2016.         }
  2017.         $sums $this->getRawPaymentTransactionSums();
  2018.         $total += $sums['refunded'];
  2019.         return $total;
  2020.     }
  2021.     /**
  2022.      * Check - order is payed or not
  2023.      * Payed - order has not open total and all payment transactions are failed or completed
  2024.      *
  2025.      * @return boolean
  2026.      */
  2027.     public function isPayed()
  2028.     {
  2029.         return >= $this->getPayedTotal();
  2030.     }
  2031.     /**
  2032.      * Check - order has in-progress payments or not
  2033.      *
  2034.      * @return boolean
  2035.      */
  2036.     public function hasInprogressPayments()
  2037.     {
  2038.         $result false;
  2039.         foreach ($this->getPaymentTransactions() as $t) {
  2040.             if ($t->isInProgress()) {
  2041.                 $result true;
  2042.                 break;
  2043.             }
  2044.         }
  2045.         return $result;
  2046.     }
  2047.     /**
  2048.      * Assign last used payment method
  2049.      *
  2050.      * @return \XLite\Model\Payment\Transaction|void
  2051.      */
  2052.     protected function assignLastPaymentMethod()
  2053.     {
  2054.         $found null;
  2055.         if ($this->isPaymentMethodRequired() && $this->getProfile() && $this->getProfile()->getLastPaymentId()) {
  2056.             $paymentMethods $this->getPaymentMethods();
  2057.             $found \Includes\Utils\ArrayManager::findValue(
  2058.                 $paymentMethods,
  2059.                 [$this'checkLastPaymentMethod'],
  2060.                 $this->getProfile()->getLastPaymentId()
  2061.             );
  2062.         }
  2063.         return $found $this->getFirstOpenPaymentTransaction() : null;
  2064.     }
  2065.     /**
  2066.      * Get first applicable payment method
  2067.      *
  2068.      * @return \XLite\Model\Payment\Method
  2069.      */
  2070.     protected function getFirstPaymentMethod()
  2071.     {
  2072.         $list $this->getPaymentMethods();
  2073.         return $list array_shift($list) : null;
  2074.     }
  2075.     /**
  2076.      * Add payment transaction
  2077.      * FIXME: move logic into \XLite\Model\Payment\Transaction
  2078.      *
  2079.      * @param \XLite\Model\Payment\Method $method Payment method
  2080.      * @param float                       $value  Value OPTIONAL
  2081.      *
  2082.      * @throws \Doctrine\ORM\ORMInvalidArgumentException
  2083.      *
  2084.      * @return void
  2085.      */
  2086.     protected function addPaymentTransaction(\XLite\Model\Payment\Method $method$value null)
  2087.     {
  2088.         if ($value === null || >= $value) {
  2089.             $value $this->getOpenTotal();
  2090.         } else {
  2091.             $value min($value$this->getOpenTotal());
  2092.         }
  2093.         // Do not add 0 or <0 transactions. This is for a "Payment not required" case.
  2094.         if ($value 0) {
  2095.             $transaction = new \XLite\Model\Payment\Transaction();
  2096.             $this->addPaymentTransactions($transaction);
  2097.             $transaction->setOrder($this);
  2098.             $transaction->setPaymentMethod($method);
  2099.             \XLite\Core\Database::getEM()->persist($method);
  2100.             $transaction->setCurrency($this->getCurrency());
  2101.             $transaction->setStatus($transaction::STATUS_INITIALIZED);
  2102.             $transaction->setValue($value);
  2103.             $transaction->setType($method->getProcessor()->getInitialTransactionType($method));
  2104.             if ($method->getProcessor()->isTestMode($method)) {
  2105.                 $transaction->setDataCell(
  2106.                     'test_mode',
  2107.                     true,
  2108.                     'Test mode'
  2109.                 );
  2110.             }
  2111.             \XLite\Core\Database::getEM()->persist($transaction);
  2112.         }
  2113.     }
  2114.     // }}}
  2115.     // {{{ Shipping
  2116.     /**
  2117.      * Returns last shipping method id
  2118.      *
  2119.      * @return integer|null
  2120.      */
  2121.     public function getLastShippingId()
  2122.     {
  2123.         /** @var \XLite\Model\Profile $profile */
  2124.         $profile $this->getProfile();
  2125.         return $profile $profile->getLastShippingId() : null;
  2126.     }
  2127.     /**
  2128.      * Sets last shipping method id used
  2129.      *
  2130.      * @param integer $value Method id
  2131.      *
  2132.      * @return void
  2133.      */
  2134.     public function setLastShippingId($value)
  2135.     {
  2136.         /** @var \XLite\Model\Profile $profile */
  2137.         $profile $this->getProfile();
  2138.         if ($profile !== null) {
  2139.             $profile->setLastShippingId($value);
  2140.         }
  2141.     }
  2142.     /**
  2143.      * Renew shipping method
  2144.      *
  2145.      * @return void
  2146.      */
  2147.     public function renewShippingMethod()
  2148.     {
  2149.         if (!\XLite\Model\Shipping::getInstance()->shouldAllowLongCalculations()) {
  2150.             return;
  2151.         }
  2152.         $this->updateEmptyShippingID();
  2153.         /** @var \XLite\Logic\Order\Modifier\Shipping $modifier */
  2154.         $modifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  2155.         if ($modifier && $modifier->getSelectedRate() === null) {
  2156.             $method $this->getFirstShippingMethod();
  2157.             if ($method) {
  2158.                 $methodId $method->getMethodId();
  2159.                 $this->setLastShippingId($methodId);
  2160.                 $this->setShippingId($methodId);
  2161.             } else {
  2162.                 $this->setShippingId(0);
  2163.             }
  2164.         }
  2165.     }
  2166.     /**
  2167.      * Get the link for the detailed tracking information
  2168.      *
  2169.      * @return boolean|\XLite\Model\Shipping\Processor\AProcessor False if the shipping is not set or
  2170.      *                                                            shipping processor is absent
  2171.      */
  2172.     public function getShippingProcessor()
  2173.     {
  2174.         if ($this->shippingProcessor === null) {
  2175.             $shipping \XLite\Core\Database::getRepo('XLite\Model\Shipping\Method')->find($this->getShippingId());
  2176.             $this->shippingProcessor $shipping && $shipping->getProcessorObject()
  2177.                 ? $shipping->getProcessorObject()
  2178.                 : false;
  2179.         }
  2180.         return $this->shippingProcessor;
  2181.     }
  2182.     /**
  2183.      * Defines whether the form must be used for tracking information.
  2184.      * The 'getTrackingInformationURL' result will be used as tracking link instead
  2185.      *
  2186.      * @param string $trackingNumber Tracking number value
  2187.      *
  2188.      * @return boolean
  2189.      */
  2190.     public function isTrackingInformationForm($trackingNumber)
  2191.     {
  2192.         return $this->getShippingProcessor()
  2193.             ? $this->getShippingProcessor()->isTrackingInformationForm($trackingNumber)
  2194.             : null;
  2195.     }
  2196.     /**
  2197.      * Get the link for the detailed tracking information
  2198.      *
  2199.      * @param string $trackingNumber Tracking number value
  2200.      *
  2201.      * @return null|string
  2202.      */
  2203.     public function getTrackingInformationURL($trackingNumber)
  2204.     {
  2205.         return $this->getShippingProcessor()
  2206.             ? $this->getShippingProcessor()->getTrackingInformationURL($trackingNumber)
  2207.             : null;
  2208.     }
  2209.     /**
  2210.      * Get the form parameters for the detailed tracking information
  2211.      *
  2212.      * @param string $trackingNumber Tracking number value
  2213.      *
  2214.      * @return null|array
  2215.      */
  2216.     public function getTrackingInformationParams($trackingNumber)
  2217.     {
  2218.         return $this->getShippingProcessor()
  2219.             ? $this->getShippingProcessor()->getTrackingInformationParams($trackingNumber)
  2220.             : null;
  2221.     }
  2222.     /**
  2223.      * Get the form method for the detailed tracking information
  2224.      *
  2225.      * @param string $trackingNumber Tracking number value
  2226.      *
  2227.      * @return null|string
  2228.      */
  2229.     public function getTrackingInformationMethod($trackingNumber)
  2230.     {
  2231.         return $this->getShippingProcessor()
  2232.             ? $this->getShippingProcessor()->getTrackingInformationMethod($trackingNumber)
  2233.             : null;
  2234.     }
  2235.     /**
  2236.      * Get first shipping method
  2237.      *
  2238.      * @return \XLite\Model\Shipping\Method
  2239.      */
  2240.     protected function getFirstShippingMethod()
  2241.     {
  2242.         $method null;
  2243.         $modifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  2244.         if ($modifier) {
  2245.             $rates $modifier->getRates();
  2246.             $rate array_shift($rates);
  2247.             if ($rate) {
  2248.                 $method $rate->getMethod();
  2249.             }
  2250.         }
  2251.         return $method;
  2252.     }
  2253.     // }}}
  2254.     // {{{ Mail notification
  2255.     /**
  2256.      * Set value of isNotificationSent flag and return old value
  2257.      *
  2258.      * @param boolean $value New value
  2259.      *
  2260.      * @return boolean
  2261.      */
  2262.     public function setIsNotificationSent($value)
  2263.     {
  2264.         $oldValue $this->isNotificationSent;
  2265.         $this->isNotificationSent $value;
  2266.         return $oldValue;
  2267.     }
  2268.     /**
  2269.      * Get value of isNotificationSent flag
  2270.      *
  2271.      * @return boolean
  2272.      */
  2273.     public function isNotificationSent()
  2274.     {
  2275.         return $this->isNotificationSent;
  2276.     }
  2277.     /**
  2278.      * Set value of isNotificationsAllowedFlag flag and return old value
  2279.      *
  2280.      * @param boolean $value New value
  2281.      *
  2282.      * @return boolean
  2283.      */
  2284.     public function setIsNotificationsAllowedFlag($value)
  2285.     {
  2286.         $oldValue $this->isNotificationsAllowedFlag;
  2287.         $this->isNotificationsAllowedFlag = (bool)$value;
  2288.         return $oldValue;
  2289.     }
  2290.     /**
  2291.      * Set value of ignoreCustomerNotifications flag and return old value
  2292.      *
  2293.      * @param boolean $value New value
  2294.      *
  2295.      * @return boolean
  2296.      */
  2297.     public function setIgnoreCustomerNotifications($value)
  2298.     {
  2299.         $oldValue $this->ignoreCustomerNotifications;
  2300.         $this->ignoreCustomerNotifications = (bool)$value;
  2301.         return $oldValue;
  2302.     }
  2303.     /**
  2304.      * Get value of isNotificationsAllowedFlag flag
  2305.      *
  2306.      * @return boolean
  2307.      */
  2308.     protected function isNotificationsAllowed()
  2309.     {
  2310.         return $this->isNotificationsAllowedFlag;
  2311.     }
  2312.     /**
  2313.      * Get value of ignoreCustomerNotifications flag
  2314.      *
  2315.      * @return boolean
  2316.      */
  2317.     protected function isIgnoreCustomerNotifications()
  2318.     {
  2319.         return $this->ignoreCustomerNotifications;
  2320.     }
  2321.     // }}}
  2322.     // {{{ Calculation
  2323.     /**
  2324.      * Get modifiers
  2325.      *
  2326.      * @return \XLite\DataSet\Collection\OrderModifier
  2327.      */
  2328.     public function getModifiers()
  2329.     {
  2330.         if ($this->modifiers === null) {
  2331.             $this->modifiers \XLite\Core\Database::getRepo('XLite\Model\Order\Modifier')->findActive();
  2332.             // Initialize
  2333.             foreach ($this->modifiers as $modifier) {
  2334.                 $modifier->initialize($this$this->modifiers);
  2335.             }
  2336.             // Preprocess modifiers
  2337.             foreach ($this->modifiers as $modifier) {
  2338.                 $modifier->preprocess();
  2339.             }
  2340.         }
  2341.         return $this->modifiers;
  2342.     }
  2343.     /**
  2344.      * Get modifier
  2345.      *
  2346.      * @param string $type Modifier type
  2347.      * @param string $code Modifier code
  2348.      *
  2349.      * @return \XLite\Model\Order\Modifier
  2350.      */
  2351.     public function getModifier($type$code)
  2352.     {
  2353.         $result null;
  2354.         foreach ($this->getModifiers() as $modifier) {
  2355.             if ($modifier->getType() === $type && $modifier->getCode() === $code) {
  2356.                 $result $modifier;
  2357.                 break;
  2358.             }
  2359.         }
  2360.         return $result;
  2361.     }
  2362.     /**
  2363.      * Check - modifier is exists or not (by type)
  2364.      *
  2365.      * @param string $type Type
  2366.      *
  2367.      * @return boolean
  2368.      */
  2369.     public function isModifierByType($type)
  2370.     {
  2371.         $result false;
  2372.         foreach ($this->getModifiers() as $modifier) {
  2373.             if ($modifier->getType() === $type) {
  2374.                 $result true;
  2375.                 break;
  2376.             }
  2377.         }
  2378.         return $result;
  2379.     }
  2380.     /**
  2381.      * Get modifiers by type
  2382.      *
  2383.      * @param string $type Modifier type
  2384.      *
  2385.      * @return array
  2386.      */
  2387.     public function getModifiersByType($type)
  2388.     {
  2389.         $list = [];
  2390.         foreach ($this->getModifiers() as $modifier) {
  2391.             if ($modifier->getType() === $type) {
  2392.                 $list[] = $modifier;
  2393.             }
  2394.         }
  2395.         return $list;
  2396.     }
  2397.     /**
  2398.      * Get items exclude surcharges info
  2399.      *
  2400.      * @return array
  2401.      */
  2402.     public function getItemsExcludeSurcharges()
  2403.     {
  2404.         $list = [];
  2405.         foreach ($this->getItems() as $item) {
  2406.             foreach ($item->getExcludeSurcharges() as $surcharge) {
  2407.                 if (!isset($list[$surcharge->getKey()])) {
  2408.                     $list[$surcharge->getKey()] = $surcharge->getName();
  2409.                 }
  2410.             }
  2411.         }
  2412.         return $list;
  2413.     }
  2414.     /**
  2415.      * Get items included surcharges totals
  2416.      *
  2417.      * @param boolean $forCartItems Flag: true - return values for cart items only, false - for cart totals and items
  2418.      *
  2419.      * @return array
  2420.      */
  2421.     public function getItemsIncludeSurchargesTotals($forCartItems false)
  2422.     {
  2423.         $list = [];
  2424.         if ($forCartItems) {
  2425.             // Add surcharges for cart items
  2426.             foreach ($this->getItems() as $item) {
  2427.                 foreach ($item->getIncludeSurcharges() as $surcharge) {
  2428.                     if (!isset($list[$surcharge->getKey()])) {
  2429.                         $list[$surcharge->getKey()] = [
  2430.                             'surcharge' => $surcharge,
  2431.                             'cost'      => 0,
  2432.                         ];
  2433.                     }
  2434.                     $list[$surcharge->getKey()]['cost'] += $surcharge->getValue();
  2435.                 }
  2436.             }
  2437.         } else {
  2438.             // Get global cart surcharges
  2439.             foreach ($this->getIncludeSurcharges() as $surcharge) {
  2440.                 if (!isset($list[$surcharge->getKey()])) {
  2441.                     $list[$surcharge->getKey()] = [
  2442.                         'surcharge' => $surcharge,
  2443.                         'cost'      => 0,
  2444.                     ];
  2445.                 }
  2446.                 $list[$surcharge->getKey()]['cost'] += $surcharge->getValue();
  2447.             }
  2448.         }
  2449.         return $list;
  2450.     }
  2451.     /**
  2452.      * Common method to update cart/order
  2453.      *
  2454.      * @return void
  2455.      */
  2456.     public function updateOrder()
  2457.     {
  2458.         $this->normalizeItems();
  2459.         $this->reinitializeCurrency();
  2460.         // If shipping method is not selected or shipping conditions is changed (remove order items or changed address)
  2461.         $this->renewShippingMethod();
  2462.         $this->calculateInitialValues();
  2463.         $this->calculate();
  2464.         $this->renewPaymentMethod();
  2465.     }
  2466.     /**
  2467.      * Calculate order
  2468.      *
  2469.      * @return void
  2470.      */
  2471.     public function calculate()
  2472.     {
  2473.         $oldSurcharges $this->resetSurcharges();
  2474.         $this->reinitializeCurrency();
  2475.         $this->calculateInitialValues();
  2476.         foreach ($this->getModifiers() as $modifier) {
  2477.             if ($modifier->canApply()) {
  2478.                 $modifier->calculate();
  2479.             }
  2480.         }
  2481.         $this->mergeSurcharges($oldSurcharges);
  2482.         $this->finalizeItemsCalculation();
  2483.         $this->setTotal(max($this->getSurchargesTotal(), 0));
  2484.     }
  2485.     /**
  2486.      * Recalculate edited order
  2487.      *
  2488.      * @return void
  2489.      */
  2490.     public function recalculate()
  2491.     {
  2492.         $this->reinitializeCurrency();
  2493.         $this->finalizeItemsCalculation();
  2494.         $this->setTotal($this->getSurchargesTotal());
  2495.     }
  2496.     /**
  2497.      * Renew order
  2498.      *
  2499.      * @return void
  2500.      */
  2501.     public function renew()
  2502.     {
  2503.         foreach ($this->getItems() as $item) {
  2504.             if (!$item->renew()) {
  2505.                 $this->getItems()->removeElement($item);
  2506.                 \XLite\Core\Database::getRepo('XLite\Model\OrderItem')->delete($item);
  2507.             }
  2508.         }
  2509.         $this->calculate();
  2510.     }
  2511.     /**
  2512.      * Soft renew
  2513.      *
  2514.      * @return void
  2515.      */
  2516.     public function renewSoft()
  2517.     {
  2518.         $this->reinitializeCurrency();
  2519.     }
  2520.     /**
  2521.      * Reinitialize currency
  2522.      *
  2523.      * @return void
  2524.      */
  2525.     protected function reinitializeCurrency()
  2526.     {
  2527.         $new $this->defineCurrency();
  2528.         $old $this->getCurrency();
  2529.         if (empty($old) || (!empty($new) && $old->getCode() !== $new->getCode())) {
  2530.             $this->setCurrency($new);
  2531.         }
  2532.     }
  2533.     /**
  2534.      * Define order currency
  2535.      *
  2536.      * @return \XLite\Model\Currency
  2537.      */
  2538.     protected function defineCurrency()
  2539.     {
  2540.         return \XLite::getInstance()->getCurrency();
  2541.     }
  2542.     /**
  2543.      * Reset surcharges list
  2544.      *
  2545.      * @return array
  2546.      */
  2547.     public function resetSurcharges()
  2548.     {
  2549.         $result = [
  2550.             'items' => [],
  2551.         ];
  2552.         $result['items'] = $this->resetItemsSurcharges($this->getItems()->toArray());
  2553.         $result['surcharges'] = parent::resetSurcharges();
  2554.         return $result;
  2555.     }
  2556.     /**
  2557.      * Reset order items surcharge
  2558.      *
  2559.      * @param \XLite\Model\OrderItem[] $items Order items
  2560.      *
  2561.      * @return array
  2562.      */
  2563.     protected function resetSelfSurcharges($items)
  2564.     {
  2565.         $result = [];
  2566.         foreach ($items as $item) {
  2567.             $result[$item->getItemId()] = $item->resetSurcharges();
  2568.         }
  2569.         return $result;
  2570.     }
  2571.     /**
  2572.      * Reset order items surcharge
  2573.      *
  2574.      * @param \XLite\Model\OrderItem[] $items Order items
  2575.      *
  2576.      * @return array
  2577.      */
  2578.     protected function resetItemsSurcharges($items)
  2579.     {
  2580.         $result = [];
  2581.         foreach ($items as $item) {
  2582.             $result[$item->getItemId() ?: spl_object_hash($item)] = $item->resetSurcharges();
  2583.         }
  2584.         return $result;
  2585.     }
  2586.     /**
  2587.      * Merge surcharges
  2588.      *
  2589.      * @param array $oldSurcharges Old surcharges
  2590.      *
  2591.      * @return void
  2592.      */
  2593.     protected function mergeSurcharges(array $oldSurcharges)
  2594.     {
  2595.         foreach ($this->getItems() as $item) {
  2596.             if (!empty($oldSurcharges['items'][$item->getItemId() ?: spl_object_hash($item)])) {
  2597.                 $item->compareSurcharges($oldSurcharges['items'][$item->getItemId() ?: spl_object_hash($item)]);
  2598.             }
  2599.         }
  2600.         $this->compareSurcharges($oldSurcharges['surcharges']);
  2601.     }
  2602.     /**
  2603.      * Calculate initial order values
  2604.      *
  2605.      * @return void
  2606.      */
  2607.     public function calculateInitialValues()
  2608.     {
  2609.         $subtotal 0;
  2610.         foreach ($this->getItems() as $item) {
  2611.             $item->calculate();
  2612.             $subtotal += $item->getSubtotal();
  2613.         }
  2614.         $subtotal $this->getCurrency()->roundValue($subtotal);
  2615.         $this->setSubtotal($subtotal);
  2616.         $this->setTotal($subtotal);
  2617.     }
  2618.     /**
  2619.      * Finalize items calculation
  2620.      *
  2621.      * @return void
  2622.      */
  2623.     protected function finalizeItemsCalculation()
  2624.     {
  2625.         $subtotal 0;
  2626.         foreach ($this->getItems() as $item) {
  2627.             $itemTotal $item->calculateTotal();
  2628.             $subtotal += $itemTotal;
  2629.             $item->setTotal($itemTotal);
  2630.         }
  2631.         $this->setSubtotal($subtotal);
  2632.         $this->setTotal($subtotal);
  2633.     }
  2634.     // }}}
  2635.     // {{{ Surcharges
  2636.     /**
  2637.      * Get surcharges by type
  2638.      *
  2639.      * @param string $type Surcharge type
  2640.      *
  2641.      * @return array
  2642.      */
  2643.     public function getSurchargesByType($type null)
  2644.     {
  2645.         if ($type) {
  2646.             $list = [];
  2647.             foreach ($this->getSurcharges() as $surcharge) {
  2648.                 if ($surcharge->getType() === $type) {
  2649.                     $list[] = $surcharge;
  2650.                 }
  2651.             }
  2652.         } else {
  2653.             $list $this->getSurcharges();
  2654.         }
  2655.         return $list;
  2656.     }
  2657.     /**
  2658.      * Get surcharges subtotal with specified type
  2659.      *
  2660.      * @param string  $type    Surcharge type OPTIONAL
  2661.      * @param boolean $include Surcharge include flag OPTIONAL
  2662.      *
  2663.      * @return float
  2664.      */
  2665.     public function getSurchargesSubtotal($type null$include null)
  2666.     {
  2667.         $subtotal 0;
  2668.         $surcharges $this->getSurchargesByType($type);
  2669.         foreach ($surcharges as $surcharge) {
  2670.             if ($surcharge->getAvailable() && ($include === null || $surcharge->getInclude() === $include)) {
  2671.                 $subtotal += $this->getCurrency()->roundValue($surcharge->getValue());
  2672.             }
  2673.         }
  2674.         return $subtotal;
  2675.     }
  2676.     /**
  2677.      * Get subtotal for displaying
  2678.      *
  2679.      * @return float
  2680.      */
  2681.     public function getDisplaySubtotal()
  2682.     {
  2683.         return $this->getSubtotal();
  2684.     }
  2685.     /**
  2686.      * Get surcharges total with specified type
  2687.      *
  2688.      * @param string $type Surcharge type OPTIONAL
  2689.      *
  2690.      * @return float
  2691.      */
  2692.     public function getSurchargesTotal($type null)
  2693.     {
  2694.         return $this->getSubtotal() + $this->getSurchargesSubtotal($typefalse);
  2695.     }
  2696.     // }}}
  2697.     // {{{ Lifecycle callbacks
  2698.     /**
  2699.      * Prepare order before remove operation
  2700.      *
  2701.      * @return void
  2702.      */
  2703.     public function prepareBeforeRemove()
  2704.     {
  2705.         $profile $this->getProfile();
  2706.         $origProfile $this->getOrigProfile();
  2707.         try {
  2708.             if ($profile && (!$origProfile || $profile->getProfileId() !== $origProfile->getProfileId())) {
  2709.                 \XLite\Core\Database::getRepo('XLite\Model\Profile')->delete($profilefalse);
  2710.             }
  2711.         } catch (\Doctrine\ORM\EntityNotFoundException $e) {
  2712.         }
  2713.     }
  2714.     /**
  2715.      * Since Doctrine lifecycle callbacks do not allow to modify associations, we've added this method
  2716.      *
  2717.      * @param string $type Type of current operation
  2718.      *
  2719.      * @return void
  2720.      */
  2721.     public function prepareEntityBeforeCommit($type)
  2722.     {
  2723.         if ($type === static::ACTION_DELETE) {
  2724.             $this->setOldPaymentStatus(null);
  2725.             $this->setOldShippingStatus(null);
  2726.             $this->updateItemsSales(-1);
  2727.             $this->prepareBeforeRemove();
  2728.         }
  2729.     }
  2730.     // }}}
  2731.     // {{{ Change status routine
  2732.     /**
  2733.      * Get payment status code
  2734.      *
  2735.      * @return string
  2736.      */
  2737.     public function getPaymentStatusCode()
  2738.     {
  2739.         return $this->getPaymentStatus() && $this->getPaymentStatus()->getCode()
  2740.             ? $this->getPaymentStatus()->getCode()
  2741.             : '';
  2742.     }
  2743.     /**
  2744.      * Get shipping status code
  2745.      *
  2746.      * @return string
  2747.      */
  2748.     public function getShippingStatusCode()
  2749.     {
  2750.         return $this->getShippingStatus() && $this->getShippingStatus()->getCode()
  2751.             ? $this->getShippingStatus()->getCode()
  2752.             : '';
  2753.     }
  2754.     /**
  2755.      * Set payment status
  2756.      *
  2757.      * @param mixed $paymentStatus Payment status
  2758.      *
  2759.      * @return void
  2760.      */
  2761.     public function setPaymentStatus($paymentStatus null)
  2762.     {
  2763.         $this->processStatus($paymentStatus'payment');
  2764.     }
  2765.     /**
  2766.      * Set shipping status
  2767.      *
  2768.      * @param mixed $shippingStatus Shipping status
  2769.      *
  2770.      * @return void
  2771.      */
  2772.     public function setShippingStatus($shippingStatus null)
  2773.     {
  2774.         $this->processStatus($shippingStatus'shipping');
  2775.     }
  2776.     /**
  2777.      * Process order status
  2778.      *
  2779.      * @param mixed  $status Status
  2780.      * @param string $type   Type
  2781.      *
  2782.      * @return void
  2783.      */
  2784.     public function processStatus($status$type)
  2785.     {
  2786.         static $cache = [];
  2787.         if (is_scalar($status)) {
  2788.             if (!isset($cache[$type][$status])) {
  2789.                 $requestedStatus $status;
  2790.                 if (
  2791.                     is_int($status)
  2792.                     || (is_string($status)
  2793.                         && preg_match('/^[\d]+$/'$status)
  2794.                     )
  2795.                 ) {
  2796.                     $status \XLite\Core\Database::getRepo('XLite\Model\Order\Status\\' ucfirst($type))
  2797.                         ->find($status);
  2798.                 } elseif (is_string($status)) {
  2799.                     $status \XLite\Core\Database::getRepo('XLite\Model\Order\Status\\' ucfirst($type))
  2800.                         ->findOneByCode($status);
  2801.                 }
  2802.                 $cache[$type][$requestedStatus] = $status;
  2803.             } else {
  2804.                 $status $cache[$type][$status];
  2805.             }
  2806.         }
  2807.         $this->statusIsSet true;
  2808.         $this->{'old' ucfirst($type) . 'Status'} = $this->{$type 'Status'};
  2809.         $this->{$type 'Status'} = $status;
  2810.     }
  2811.     /**
  2812.      * Check order statuses
  2813.      *
  2814.      * @return boolean
  2815.      */
  2816.     public function checkStatuses()
  2817.     {
  2818.         $changed false;
  2819.         $em Database::getEM();
  2820.         if ($this->statusIsSet) {
  2821.             $this->statusIsSet false;
  2822.             $statusHandlers = [];
  2823.             foreach (['payment''shipping'] as $type) {
  2824.                 $status $this->{$type 'Status'};
  2825.                 $oldStatus $this->{'old' ucfirst($type) . 'Status'};
  2826.                 if (
  2827.                     $status
  2828.                     && $oldStatus
  2829.                     && $status->getId() !== $oldStatus->getId()
  2830.                 ) {
  2831.                     $em->addAfterFlushCallback(function () use ($oldStatus$status$type) {
  2832.                         \XLite\Core\OrderHistory::getInstance()->registerOrderChangeStatus(
  2833.                             $this->getOrderId(),
  2834.                             [
  2835.                                 'old'  => $oldStatus,
  2836.                                 'new'  => $status,
  2837.                                 'type' => $type,
  2838.                             ]
  2839.                         );
  2840.                         $this->processStatusAnyToAny($oldStatus$status$type);
  2841.                     });
  2842.                     $statusHandlers array_merge(
  2843.                         $this->getStatusHandlers($oldStatus$status$type),
  2844.                         $statusHandlers
  2845.                     );
  2846.                     $changed true;
  2847.                 } elseif (!$oldStatus) {
  2848.                     $this->{'old' ucfirst($type) . 'Status'} = $this->{$type 'Status'};
  2849.                 }
  2850.             }
  2851.             $em->addAfterFlushCallback(function () use ($changed$statusHandlers) {
  2852.                 if ($changed) {
  2853.                     $this->checkInventory();
  2854.                 }
  2855.                 if ($statusHandlers) {
  2856.                     foreach (array_unique($statusHandlers) as $handler) {
  2857.                         $this->{'process' ucfirst($handler)}();
  2858.                     }
  2859.                 }
  2860.                 foreach (['payment''shipping'] as $type) {
  2861.                     $this->{'old' ucfirst($type) . 'Status'} = null;
  2862.                 }
  2863.                 if ($changed) {
  2864.                     if (
  2865.                         !$this->isNotificationSent
  2866.                         && $this->isNotificationsAllowed()
  2867.                         && $this->getOrderNumber()
  2868.                     ) {
  2869.                         \XLite\Core\Mailer::sendOrderChanged($this$this->isIgnoreCustomerNotifications());
  2870.                     }
  2871.                 }
  2872.             });
  2873.         }
  2874.         return $changed;
  2875.     }
  2876.     /**
  2877.      * Check inventory
  2878.      *
  2879.      * @throws \Doctrine\ORM\OptimisticLockException
  2880.      */
  2881.     public function checkInventory()
  2882.     {
  2883.         $property \XLite\Core\Database::getRepo('XLite\Model\Order\Status\Property')->findOneBy(
  2884.             [
  2885.                 'paymentStatus'  => $this->paymentStatus,
  2886.                 'shippingStatus' => $this->shippingStatus,
  2887.             ]
  2888.         );
  2889.         $incStock $property $property->getIncStock() : null;
  2890.         if ($incStock !== null) {
  2891.             $stokStatus $this->getStockStatus();
  2892.             $oldIncStock null;
  2893.             if ($stokStatus === 'increased') {
  2894.                 $oldIncStock true;
  2895.             } elseif ($stokStatus === 'reduced') {
  2896.                 $oldIncStock false;
  2897.             }
  2898.             if ($oldIncStock === null) {
  2899.                 $property    \XLite\Core\Database::getRepo('XLite\Model\Order\Status\Property')->findOneBy(
  2900.                     [
  2901.                         'paymentStatus'  => $this->oldPaymentStatus,
  2902.                         'shippingStatus' => $this->oldShippingStatus,
  2903.                     ]
  2904.                 );
  2905.                 $oldIncStock $property $property->getIncStock() : null;
  2906.             }
  2907.             if ($oldIncStock !== null && $incStock !== $oldIncStock) {
  2908.                 if ($incStock) {
  2909.                     $this->processIncrease();
  2910.                     $this->setStockStatus('increased');
  2911.                 } else {
  2912.                     $this->processDecrease();
  2913.                     $this->setStockStatus('reduced');
  2914.                 }
  2915.             }
  2916.         }
  2917.         $this->updateSales();
  2918.         \XLite\Core\Database::getEM()->flush();
  2919.     }
  2920.     /**
  2921.      * Transform attributes: remove relations between order item attributes and product attribute values
  2922.      * to avoid data lost after product attribute values modification
  2923.      *
  2924.      * @return void
  2925.      */
  2926.     public function transformItemsAttributes()
  2927.     {
  2928.         foreach ($this->getItems() as $item) {
  2929.             if ($item->hasAttributeValues()) {
  2930.                 foreach ($item->getAttributeValues() as $av) {
  2931.                     if ($av->getAttributeValue()) {
  2932.                         $attributeValue $av->getAttributeValue();
  2933.                         $av->setName($attributeValue->getAttribute()->getName());
  2934.                         if (!($attributeValue instanceof \XLite\Model\AttributeValue\AttributeValueText)) {
  2935.                             $av->setValue($attributeValue->asString());
  2936.                         }
  2937.                         $av->setAttributeId($attributeValue->getAttribute()->getId());
  2938.                     }
  2939.                 }
  2940.             }
  2941.         }
  2942.     }
  2943.     /**
  2944.      * These handlers will be used in systemStatus1->...customStatus(es)..->systemStatus2 transition
  2945.      * by default, don't try to cast old statuses
  2946.      * @param string $type Type
  2947.      *
  2948.      * @return array
  2949.      */
  2950.     protected function getStatusHandlersForCast($type)
  2951.     {
  2952.         return [];
  2953.     }
  2954.     /**
  2955.      * Return base part of the certain "change status" handler name
  2956.      *
  2957.      * @param mixed  $oldStatus  Old order status
  2958.      * @param mixed  $newStatus  New order status
  2959.      * @param string $type Type
  2960.      *
  2961.      * @return string|array
  2962.      */
  2963.     protected function getStatusHandlers($oldStatus$newStatus$type)
  2964.     {
  2965.         $class '\XLite\Model\Order\Status\\' ucfirst($type);
  2966.         $oldCode $oldStatus->getCode();
  2967.         $newCode $newStatus->getCode();
  2968.         $statusHandlers $class::getStatusHandlers();
  2969.         $result = [];
  2970.         if ($oldCode && $newCode && isset($statusHandlers[$oldCode][$newCode])) {
  2971.             $result is_array($statusHandlers[$oldCode][$newCode])
  2972.                 ? array_unique($statusHandlers[$oldCode][$newCode])
  2973.                 : $statusHandlers[$oldCode][$newCode];
  2974.         }
  2975.         return $result;
  2976.     }
  2977.     /**
  2978.      * @param array $inStatusHandlers
  2979.      *
  2980.      * @return array
  2981.      */
  2982.     protected function getFlatStatuses($inStatusHandlers = [])
  2983.     {
  2984.         $flatStatuses array_reduce($inStatusHandlers, static function ($a$b) {
  2985.             return array_merge($a, (array) $b);
  2986.         }, []);
  2987.         return array_unique(array_merge(array_keys($flatStatuses), array_keys($inStatusHandlers)));
  2988.     }
  2989.     /**
  2990.      * A "change status" handler
  2991.      *
  2992.      * @return void
  2993.      */
  2994.     protected function processCheckout()
  2995.     {
  2996.     }
  2997.     /**
  2998.      * A "change status" handler
  2999.      *
  3000.      * @return void
  3001.      */
  3002.     protected function processDecrease()
  3003.     {
  3004.         $this->decreaseInventory();
  3005.     }
  3006.     /**
  3007.      * A "change status" handler
  3008.      *
  3009.      * @return void
  3010.      */
  3011.     protected function processUncheckout()
  3012.     {
  3013.     }
  3014.     /**
  3015.      * A "change status" handler
  3016.      *
  3017.      * @return void
  3018.      */
  3019.     protected function processQueue()
  3020.     {
  3021.     }
  3022.     /**
  3023.      * A "change status" handler
  3024.      *
  3025.      * @return void
  3026.      */
  3027.     protected function processAuthorize()
  3028.     {
  3029.         if ($this instanceof Cart) {
  3030.             $this->setIsNotificationsAllowedFlag(false);
  3031.         }
  3032.     }
  3033.     /**
  3034.      * A "change status" handler
  3035.      *
  3036.      * @return void
  3037.      * @throws \JsonException
  3038.      */
  3039.     protected function processProcess()
  3040.     {
  3041.         /** @var GmvTrackerDomain $gmvTracker */
  3042.         $gmvTracker \XCart\Container::getContainer()->get(GmvTrackerDomain::class);
  3043.         $gmvOrderData $gmvTracker->prepareOrderGmvData($this);
  3044.         $gmvTracker->saveOrderGmvData($gmvOrderData);
  3045.         if ($this->isNotificationsAllowed()) {
  3046.             \XLite\Core\Mailer::sendOrderProcessed($this$this->isIgnoreCustomerNotifications());
  3047.         }
  3048.         $this->setIsNotificationSent(true);
  3049.     }
  3050.     /**
  3051.      * A "change status" handler
  3052.      *
  3053.      * @return void
  3054.      */
  3055.     protected function processShip()
  3056.     {
  3057.         if ($this->isNotificationsAllowed() && !$this->isIgnoreCustomerNotifications()) {
  3058.             \XLite\Core\Mailer::sendOrderShipped($this);
  3059.         }
  3060.         $this->setIsNotificationSent(true);
  3061.     }
  3062.     /**
  3063.      * A "change status" handler
  3064.      *
  3065.      * @return void
  3066.      */
  3067.     protected function processWaitingForApprove()
  3068.     {
  3069.         if ($this->isNotificationsAllowed() && !$this->isIgnoreCustomerNotifications()) {
  3070.             \XLite\Core\Mailer::sendOrderWaitingForApprove($this);
  3071.         }
  3072.         $this->setIsNotificationSent(true);
  3073.     }
  3074.     /**
  3075.      * A "change status" handler
  3076.      *
  3077.      * @return void
  3078.      */
  3079.     protected function processReleaseBackorder()
  3080.     {
  3081.         foreach ($this->getItems() as $item) {
  3082.             $item->releaseBackorder();
  3083.         }
  3084.     }
  3085.     /**
  3086.      * A "change status" handler
  3087.      *
  3088.      * @return void
  3089.      */
  3090.     protected function processIncrease()
  3091.     {
  3092.         $this->increaseInventory();
  3093.     }
  3094.     /**
  3095.      * A "change status" handler
  3096.      *
  3097.      * @return void
  3098.      */
  3099.     protected function processDecline()
  3100.     {
  3101.     }
  3102.     /**
  3103.      * A "change status" handler
  3104.      *
  3105.      * @return void
  3106.      */
  3107.     protected function processFail()
  3108.     {
  3109.         if ($this->isNotificationsAllowed() && $this->getOrderNumber()) {
  3110.             \XLite\Core\Mailer::sendOrderFailed($this$this->isIgnoreCustomerNotifications());
  3111.         }
  3112.         $this->setIsNotificationSent(true);
  3113.     }
  3114.     /**
  3115.      * A "change status" handler
  3116.      *
  3117.      * @return void
  3118.      */
  3119.     protected function processCancel()
  3120.     {
  3121.         if ($this->isNotificationsAllowed()) {
  3122.             \XLite\Core\Mailer::sendOrderCanceled($this$this->isIgnoreCustomerNotifications());
  3123.         }
  3124.         $this->setIsNotificationSent(true);
  3125.     }
  3126.     /**
  3127.      * Status change handler for the "any to any" event.
  3128.      * 1)Allows to run handlers on systemStatus1->...customStatus(es)..->systemStatus2 transition
  3129.      *   Attention! handlers must include internal checking to avoid double execution
  3130.      *
  3131.      * @param $oldStatus
  3132.      * @param $newStatus
  3133.      * @param $type
  3134.      */
  3135.     protected function processStatusAnyToAny($oldStatus$newStatus$type)
  3136.     {
  3137.         // here is the key point to decide if we try to find an old system status in order history
  3138.         $statusHandlers2Cast $this->getStatusHandlersForCast($type);
  3139.         if (!$statusHandlers2Cast) {
  3140.             return;
  3141.         }
  3142.         // Transitions between these statuses are valued only
  3143.         $businessLogicStatuses $this->getFlatStatuses($statusHandlers2Cast);
  3144.         $inOldCode $oldStatus->getCode();
  3145.         $inNewCode $newStatus->getCode();
  3146.         // cycle is used for performance only, 'continue' below avoids unnecessary code execution
  3147.         foreach ($statusHandlers2Cast as $oldCode2Deal => $newCodes2Deal) {
  3148.             if (
  3149.                 $oldCode2Deal === $inOldCode // exact handler will be returned/launched by getStatusHandlers, do nothing
  3150.                 || !in_array($inNewCodearray_keys($newCodes2Deal)) // nothing to deal with here
  3151.                 || in_array($inOldCode$businessLogicStatuses// take into account transition from custom statuses to business statuses only
  3152.             ) {
  3153.                 continue;
  3154.             }
  3155.             // find the nearest business(system) status code from order history
  3156.             $oldSystemBusinessCode $this->getNearestOldBusinessCode($inOldCode$businessLogicStatuses$type);
  3157.             if (
  3158.                 $oldSystemBusinessCode !== $inOldCode
  3159.                 && isset($statusHandlers2Cast[$oldSystemBusinessCode][$inNewCode])
  3160.             ) {
  3161.                 // We have found an old business code before a bunch of meaningless ones
  3162.                 $castHandlers is_array($statusHandlers2Cast[$oldSystemBusinessCode][$inNewCode])
  3163.                     ? array_unique($statusHandlers2Cast[$oldSystemBusinessCode][$inNewCode])
  3164.                     : [$statusHandlers2Cast[$oldSystemBusinessCode][$inNewCode]];
  3165.                 foreach ($castHandlers as $castHandler) {
  3166.                     // here we run a handler for a cast oldBusinessStatus->...someStatuses...->newBusinessStatus transition
  3167.                     $methodOnStatusChange 'process' \Includes\Utils\Converter::convertToUpperCamelCase($castHandler);
  3168.                     if (method_exists($this$methodOnStatusChange)) {
  3169.                         $this->$methodOnStatusChange();
  3170.                     }
  3171.                 }
  3172.                 // typecast old code only once
  3173.                 break;
  3174.             }
  3175.         }
  3176.     }
  3177.     /**
  3178.      * Find backwards the nearest old business(system) code before a bunch of non-business logic codes
  3179.      *
  3180.      * @param $meaninglessOldCode
  3181.      * @param $businessLogicStatuses
  3182.      * @param $type
  3183.      *
  3184.      * @return string
  3185.      */
  3186.     protected function getNearestOldBusinessCode($meaninglessOldCode$businessLogicStatuses$type)
  3187.     {
  3188.         // this shared static cache is supposed to be used in other functions in the future. use the key ['OrderHistoryEvents', $this->getOrderId()]
  3189.         $historyList $this->executeCachedRuntime(function () {
  3190.             return \XLite\Core\Database::getRepo('XLite\Model\OrderHistoryEvents')->findAllByOrder($this) ?: [];
  3191.         }, ['OrderHistoryEvents'$this->getOrderId()]) ?: [];
  3192.         $eventCodeName $type === 'payment' \XLite\Core\OrderHistory::CODE_CHANGE_PAYMENT_STATUS_ORDER \XLite\Core\OrderHistory::CODE_CHANGE_SHIPPING_STATUS_ORDER;
  3193.         $historyList array_filter($historyList, static function ($event) use ($eventCodeName) {
  3194.             return $event->getCode() === $eventCodeName;
  3195.         });
  3196.         foreach ($historyList as $event) {
  3197.             $oldOrderStatus $event->getData()['oldStatusCode'];
  3198.             if (in_array($oldOrderStatus$businessLogicStatuses)) {
  3199.                 return $oldOrderStatus;
  3200.             }
  3201.         }
  3202.         return $meaninglessOldCode;
  3203.     }
  3204.     // }}}
  3205.     // {{{ Inventory tracking
  3206.     /**
  3207.      * Increase / decrease item products inventory
  3208.      *
  3209.      * @param integer $sign Flag; "1" or "-1"
  3210.      *
  3211.      * @return void
  3212.      */
  3213.     protected function changeItemsInventory($sign)
  3214.     {
  3215.         $registerAsGroup count($this->getItems());
  3216.         $data = [];
  3217.         foreach ($this->getItems() as $item) {
  3218.             $data[] = $this->getGroupedDataItem($item$sign $item->getAmount());
  3219.         }
  3220.         if ($registerAsGroup) {
  3221.             \XLite\Core\OrderHistory::getInstance()->registerChangeAmountGrouped(
  3222.                 $this->getOrderId(),
  3223.                 $data
  3224.             );
  3225.         }
  3226.         foreach ($this->getItems() as $item) {
  3227.             $this->changeItemInventory($item$sign, !$registerAsGroup);
  3228.         }
  3229.     }
  3230.     /**
  3231.      * Get grouped data item
  3232.      *
  3233.      * @param \XLite\Model\OrderItem    $item       Order item
  3234.      * @param integer                   $amount     Amount
  3235.      *
  3236.      * @return array
  3237.      */
  3238.     protected function getGroupedDataItem($item$amount)
  3239.     {
  3240.         return [
  3241.             'item'      => $item,
  3242.             'amount'    => $item->getProduct()->getPublicAmount(),
  3243.             'delta'     => $amount,
  3244.         ];
  3245.     }
  3246.     /**
  3247.      * Increase / decrease item product inventory
  3248.      *
  3249.      * @param \XLite\Model\OrderItem $item      Order item
  3250.      * @param integer                $sign      Flag; "1" or "-1"
  3251.      * @param boolean                $register  Register in order history OPTIONAL
  3252.      *
  3253.      * @return integer
  3254.      */
  3255.     protected function changeItemInventory($item$sign$register true)
  3256.     {
  3257.         $delta $sign $item->getAmount();
  3258.         $realDelta $delta && $item->getBackorderedAmount() > 0
  3259.             $delta $item->getBackorderedAmount()
  3260.             : $delta;
  3261.         if ($realDelta !== 0) {
  3262.             if ($register) {
  3263.                 $this->registerHistoryChangeItemAmount($item$realDelta);
  3264.             }
  3265.             $item->changeAmount($realDelta);
  3266.         }
  3267.         return $realDelta;
  3268.     }
  3269.     /**
  3270.      * @param \XLite\Model\OrderItem $item
  3271.      * @param integer                $delta
  3272.      */
  3273.     protected function registerHistoryChangeItemAmount($item$delta)
  3274.     {
  3275.         $history \XLite\Core\OrderHistory::getInstance();
  3276.         $history->registerChangeAmount($this->getOrderId(), $item->getProduct(), $delta);
  3277.     }
  3278.     /**
  3279.      * Order processed: decrease products inventory
  3280.      *
  3281.      * @return void
  3282.      */
  3283.     protected function decreaseInventory()
  3284.     {
  3285.         $this->changeItemsInventory(-1);
  3286.     }
  3287.     /**
  3288.      * Order declined: increase products inventory
  3289.      *
  3290.      * @return void
  3291.      */
  3292.     protected function increaseInventory()
  3293.     {
  3294.         $this->changeItemsInventory(1);
  3295.     }
  3296.     // }}}
  3297.     // {{{ Order actions
  3298.     /**
  3299.      * Get allowed actions
  3300.      *
  3301.      * @return array
  3302.      */
  3303.     public function getAllowedActions()
  3304.     {
  3305.         return [];
  3306.     }
  3307.     /**
  3308.      * Get allowed payment actions
  3309.      *
  3310.      * @return array
  3311.      */
  3312.     public function getAllowedPaymentActions()
  3313.     {
  3314.         $actions = [];
  3315.         $transactions $this->getPaymentTransactions();
  3316.         if ($transactions) {
  3317.             foreach ($transactions as $transaction) {
  3318.                 $processor $transaction->getPaymentMethod()
  3319.                     ? $transaction->getPaymentMethod()->getProcessor()
  3320.                     : null;
  3321.                 if ($processor) {
  3322.                     $allowedTransactions $processor->getAllowedTransactions();
  3323.                     foreach ($allowedTransactions as $transactionType) {
  3324.                         if ($processor->isTransactionAllowed($transaction$transactionType)) {
  3325.                             $actions[$transactionType] = $transaction->getTransactionId();
  3326.                         }
  3327.                     }
  3328.                 }
  3329.             }
  3330.         }
  3331.         return $actions;
  3332.     }
  3333.     /**
  3334.      * Get array of payment transaction sums (how much is authorized, captured and refunded)
  3335.      *
  3336.      * @return array
  3337.      */
  3338.     public function getPaymentTransactionSums()
  3339.     {
  3340.         $paymentTransactionSums $this->getRawPaymentTransactionSums();
  3341.         $lblAuth = (string) static::t('Authorized amount');
  3342.         $lblCapture = (string) static::t('Captured amount');
  3343.         $lblRefunded = (string) static::t('Refunded amount');
  3344.         $paymentTransactionSums = [
  3345.             $lblAuth     => $paymentTransactionSums['authorized'],
  3346.             $lblCapture  => $paymentTransactionSums['captured'],
  3347.             $lblRefunded => $paymentTransactionSums['refunded'],
  3348.         ];
  3349.         // Remove from array all zero sums
  3350.         foreach ($paymentTransactionSums as $k => $v) {
  3351.             if (0.01 >= $v) {
  3352.                 unset($paymentTransactionSums[$k]);
  3353.             }
  3354.         }
  3355.         return $paymentTransactionSums;
  3356.     }
  3357.     /**
  3358.      * Get array of raw payment transaction sums
  3359.      *
  3360.      * @param boolean $override Override cache OPTIONAL
  3361.      *
  3362.      * @return array
  3363.      */
  3364.     public function getRawPaymentTransactionSums($override false)
  3365.     {
  3366.         if ($this->paymentTransactionSums === null || $override) {
  3367.             $transactions $this->getPaymentTransactions();
  3368.             $this->paymentTransactionSums = [
  3369.                 'authorized' => 0,
  3370.                 'captured'   => 0,
  3371.                 'refunded'   => 0,
  3372.                 'sale'       => 0,
  3373.                 'blocked'    => 0,
  3374.             ];
  3375.             foreach ($transactions as $t) {
  3376.                 if ($t->isPersistent()) {
  3377.                     \XLite\Core\Database::getEM()->refresh($t);
  3378.                 }
  3379.                 $backendTransactions $t->getBackendTransactions();
  3380.                 $authorized 0;
  3381.                 if ($backendTransactions && count($backendTransactions) > 0) {
  3382.                     // By backend transactions
  3383.                     foreach ($backendTransactions as $bt) {
  3384.                         if ($bt->isCompleted()) {
  3385.                             switch ($bt->getType()) {
  3386.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_AUTH:
  3387.                                     $authorized += $bt->getValue();
  3388.                                     $this->paymentTransactionSums['blocked'] += $bt->getValue();
  3389.                                     break;
  3390.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_SALE:
  3391.                                     $this->paymentTransactionSums['blocked'] += $bt->getValue();
  3392.                                     $this->paymentTransactionSums['sale'] += $bt->getValue();
  3393.                                     break;
  3394.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_CAPTURE:
  3395.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_CAPTURE_PART:
  3396.                                     $this->paymentTransactionSums['captured'] += $bt->getValue();
  3397.                                     $authorized -= $bt->getValue();
  3398.                                     $this->paymentTransactionSums['sale'] += $bt->getValue();
  3399.                                     break;
  3400.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND:
  3401.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND_PART:
  3402.                                     $this->paymentTransactionSums['refunded'] += $bt->getValue();
  3403.                                     $this->paymentTransactionSums['blocked'] -= $bt->getValue();
  3404.                                     $this->paymentTransactionSums['sale'] -= $bt->getValue();
  3405.                                     break;
  3406.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND_MULTI:
  3407.                                     $this->paymentTransactionSums['refunded'] += $bt->getValue();
  3408.                                     $authorized -= $bt->getValue();
  3409.                                     $this->paymentTransactionSums['blocked'] -= $bt->getValue();
  3410.                                     $this->paymentTransactionSums['sale'] -= $bt->getValue();
  3411.                                     break;
  3412.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_VOID:
  3413.                                 case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_VOID_PART:
  3414.                                     $authorized -= $bt->getValue();
  3415.                                     $this->paymentTransactionSums['blocked'] -= $bt->getValue();
  3416.                                     break;
  3417.                                 default:
  3418.                             }
  3419.                         }
  3420.                     }
  3421.                 } else {
  3422.                     // By transaction
  3423.                     if ($t->isCompleted()) {
  3424.                         switch ($t->getType()) {
  3425.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_AUTH:
  3426.                                 $authorized += $t->getValue();
  3427.                                 $this->paymentTransactionSums['blocked'] += $t->getValue();
  3428.                                 break;
  3429.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_SALE:
  3430.                                 $this->paymentTransactionSums['blocked'] += $t->getValue();
  3431.                                 $this->paymentTransactionSums['sale'] += $t->getValue();
  3432.                                 break;
  3433.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_CAPTURE:
  3434.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_CAPTURE_PART:
  3435.                                 $this->paymentTransactionSums['captured'] += $t->getValue();
  3436.                                 $authorized -= $t->getValue();
  3437.                                 $this->paymentTransactionSums['sale'] += $t->getValue();
  3438.                                 break;
  3439.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND:
  3440.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND_PART:
  3441.                                 $this->paymentTransactionSums['refunded'] += $t->getValue();
  3442.                                 $this->paymentTransactionSums['blocked'] -= $t->getValue();
  3443.                                 $this->paymentTransactionSums['sale'] -= $t->getValue();
  3444.                                 break;
  3445.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_REFUND_MULTI:
  3446.                                 $this->paymentTransactionSums['refunded'] += $t->getValue();
  3447.                                 $authorized -= $t->getValue();
  3448.                                 $this->paymentTransactionSums['blocked'] -= $t->getValue();
  3449.                                 $this->paymentTransactionSums['sale'] -= $t->getValue();
  3450.                                 break;
  3451.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_VOID:
  3452.                             case \XLite\Model\Payment\BackendTransaction::TRAN_TYPE_VOID_PART:
  3453.                                 $authorized -= $t->getValue();
  3454.                                 $this->paymentTransactionSums['blocked'] -= $t->getValue();
  3455.                                 break;
  3456.                             default:
  3457.                         }
  3458.                     }
  3459.                 }
  3460.                 if (
  3461.                     $authorized
  3462.                     && $this->paymentTransactionSums['captured']
  3463.                     && !$t->getPaymentMethod()->getProcessor()->isTransactionAllowed($t\XLite\Model\Payment\BackendTransaction::TRAN_TYPE_CAPTURE_MULTI)
  3464.                 ) {
  3465.                     // Do not take in consideration an authorized sum after capture
  3466.                     // if payment processor does not support multiple partial capture transactions
  3467.                     $authorized 0;
  3468.                 }
  3469.                 $this->paymentTransactionSums['authorized'] += $authorized;
  3470.             } // foreach
  3471.             $this->paymentTransactionSums array_map(function ($item) {
  3472.                 return $this->getCurrency()->roundValue($item);
  3473.             }, $this->paymentTransactionSums);
  3474.         }
  3475.         return $this->paymentTransactionSums;
  3476.     }
  3477.     // }}}
  3478.     // {{{ Common for several pages method to use in invoice templates
  3479.     /**
  3480.      * Return true if shipping section should be visible on the invoice
  3481.      *
  3482.      * @return boolean
  3483.      */
  3484.     public function isShippingSectionVisible()
  3485.     {
  3486.         $modifier $this->getModifier(\XLite\Model\Base\Surcharge::TYPE_SHIPPING'SHIPPING');
  3487.         return $modifier && $modifier->canApply();
  3488.     }
  3489.     /**
  3490.      * Return true if payment section should be visible on the invoice
  3491.      * (this section is always visible by the default)
  3492.      *
  3493.      * @return boolean
  3494.      */
  3495.     public function isPaymentSectionVisible()
  3496.     {
  3497.         return true;
  3498.     }
  3499.     /**
  3500.      * Return true if payment and/or shipping sections should be visible on the invoice
  3501.      *
  3502.      * @return boolean
  3503.      */
  3504.     public function isPaymentShippingSectionVisible()
  3505.     {
  3506.         return $this->isShippingSectionVisible() || $this->isPaymentSectionVisible();
  3507.     }
  3508.     // }}}
  3509.     /**
  3510.      * Renew payment status.
  3511.      * Return true if payment status has been changed
  3512.      *
  3513.      * @return boolean
  3514.      */
  3515.     public function renewPaymentStatus()
  3516.     {
  3517.         $result false;
  3518.         $status $this->getCalculatedPaymentStatus(true);
  3519.         if ($this->getPaymentStatusCode() !== $status) {
  3520.             $this->setPaymentStatus($status);
  3521.             $result true;
  3522.         }
  3523.         return $result;
  3524.     }
  3525.     /**
  3526.      * Get calculated payment status
  3527.      *
  3528.      * @param boolean $override Override calculation cache OPTIONAL
  3529.      *
  3530.      * @return string
  3531.      */
  3532.     public function getCalculatedPaymentStatus($override false)
  3533.     {
  3534.         $result \XLite\Model\Order\Status\Payment::STATUS_QUEUED;
  3535.         $sums $this->getRawPaymentTransactionSums($override);
  3536.         $total $this->getCurrency()->roundValue($this->getTotal());
  3537.         if ($total == 0.0) {
  3538.             $result \XLite\Model\Order\Status\Payment::STATUS_PAID;
  3539.         } elseif ($sums['authorized'] > && $sums['sale'] == && $sums['captured'] == 0) {
  3540.             $result \XLite\Model\Order\Status\Payment::STATUS_AUTHORIZED;
  3541.         } elseif ($sums['sale'] < $total) {
  3542.             if ($sums['sale'] > 0) {
  3543.                 $result \XLite\Model\Order\Status\Payment::STATUS_PART_PAID;
  3544.             } elseif ($sums['refunded'] > 0) {
  3545.                 $result \XLite\Model\Order\Status\Payment::STATUS_REFUNDED;
  3546.             }
  3547.         } else {
  3548.             if ($sums['sale'] > || $sums['captured'] > 0) {
  3549.                 $result \XLite\Model\Order\Status\Payment::STATUS_PAID;
  3550.             } elseif ($sums['refunded'] > 0) {
  3551.                 $result \XLite\Model\Order\Status\Payment::STATUS_REFUNDED;
  3552.             }
  3553.         }
  3554.         $lastTransaction $this->getPaymentTransactions()->last();
  3555.         if ($lastTransaction && $lastTransaction->isVoid()) {
  3556.             $result \XLite\Model\Order\Status\Payment::STATUS_CANCELED;
  3557.         }
  3558.         if ($result === \XLite\Model\Order\Status\Payment::STATUS_QUEUED) {
  3559.             if ($lastTransaction) {
  3560.                 if ($lastTransaction->isFailed() || $lastTransaction->isVoid()) {
  3561.                     $result \XLite\Model\Order\Status\Payment::STATUS_DECLINED;
  3562.                 }
  3563.                 if ($lastTransaction->isCanceled()) {
  3564.                     $result \XLite\Model\Order\Status\Payment::STATUS_CANCELED;
  3565.                 }
  3566.             }
  3567.         }
  3568.         return $result;
  3569.     }
  3570.     /**
  3571.      * Set order status by transaction
  3572.      *
  3573.      * @param \XLite\Model\Payment\Transaction $transaction Transaction which changes status
  3574.      *
  3575.      * @return void
  3576.      */
  3577.     public function setPaymentStatusByTransaction(\XLite\Model\Payment\Transaction $transaction)
  3578.     {
  3579.         if ($this->isPayed()) {
  3580.             $status $transaction->isCaptured()
  3581.                 ? \XLite\Model\Order\Status\Payment::STATUS_PAID
  3582.                 \XLite\Model\Order\Status\Payment::STATUS_AUTHORIZED;
  3583.         } else {
  3584.             if ($transaction->isRefunded()) {
  3585.                 $paymentTransactionSums $this->getRawPaymentTransactionSums(true);
  3586.                 $refunded $paymentTransactionSums['refunded'];
  3587.                 // Check if the whole refunded sum (along with the previous refunded transactions for the order)
  3588.                 // covers the whole total for order
  3589.                 $status $this->getCurrency()->roundValue($refunded) < $this->getCurrency()->roundValue($this->getTotal())
  3590.                     ? \XLite\Model\Order\Status\Payment::STATUS_PART_PAID
  3591.                     \XLite\Model\Order\Status\Payment::STATUS_REFUNDED;
  3592.             } elseif ($transaction->isFailed()) {
  3593.                 $status \XLite\Model\Order\Status\Payment::STATUS_DECLINED;
  3594.             } elseif ($transaction->isVoid()) {
  3595.                 $status \XLite\Model\Order\Status\Payment::STATUS_CANCELED;
  3596.             } elseif ($transaction->isCaptured()) {
  3597.                 $status \XLite\Model\Order\Status\Payment::STATUS_PART_PAID;
  3598.             } else {
  3599.                 $status \XLite\Model\Order\Status\Payment::STATUS_QUEUED;
  3600.             }
  3601.         }
  3602.         $this->setPaymentStatus($status);
  3603.     }
  3604.     /**
  3605.      * Checks whether order is shippable or not
  3606.      *
  3607.      * @return boolean
  3608.      */
  3609.     public function isShippable()
  3610.     {
  3611.         $result false;
  3612.         foreach ($this->getItems() as $item) {
  3613.             if ($item->isShippable()) {
  3614.                 $result true;
  3615.                 break;
  3616.             }
  3617.         }
  3618.         return $result;
  3619.     }
  3620.     /**
  3621.      * Get addresses list
  3622.      *
  3623.      * @return array
  3624.      */
  3625.     public function getAddresses()
  3626.     {
  3627.         $list $this->getProfile()->getAddresses()->toArray();
  3628.         if ($this->getOrigProfile()) {
  3629.             foreach ($this->getOrigProfile()->getAddresses() as $address) {
  3630.                 $equal false;
  3631.                 foreach ($list as $address2) {
  3632.                     if (!$equal && $address->isEqualAddress($address2)) {
  3633.                         $equal true;
  3634.                         break;
  3635.                     }
  3636.                 }
  3637.                 if (!$equal) {
  3638.                     $list[] = $address;
  3639.                 }
  3640.             }
  3641.         }
  3642.         return $list;
  3643.     }
  3644.     /**
  3645.      * Check if all order items in order is valid
  3646.      *
  3647.      * @return boolean
  3648.      */
  3649.     protected function isAllItemsValid()
  3650.     {
  3651.         $result true;
  3652.         foreach ($this->getItems() as $item) {
  3653.             if (!$item->isValid()) {
  3654.                 $result false;
  3655.                 break;
  3656.             }
  3657.         }
  3658.         return $result;
  3659.     }
  3660.     /**
  3661.      * Check if all order items in order is configured
  3662.      *
  3663.      * @return boolean
  3664.      */
  3665.     protected function isConfigured()
  3666.     {
  3667.         $isConfigured true;
  3668.         foreach ($this->getItems() as $item) {
  3669.             if (!$item->isConfigured()) {
  3670.                 $isConfigured false;
  3671.                 break;
  3672.             }
  3673.         }
  3674.         return $isConfigured;
  3675.     }
  3676.     /**
  3677.      * Get payment transaction data.
  3678.      * These data are displayed on the order page, invoice and packing slip
  3679.      *
  3680.      * @param boolean $isPrimary Flag: true - return only data fields marked by processor as 'primary', false - all fields OPTIONAL
  3681.      *
  3682.      * @return array
  3683.      */
  3684.     public function getPaymentTransactionData($isPrimary false)
  3685.     {
  3686.         $result = [];
  3687.         $transaction $this->getPaymentTransactions()
  3688.             ? $this->getPaymentTransactions()->last()
  3689.             : null;
  3690.         if ($transaction) {
  3691.             $method $transaction->getPaymentMethod() ?: $this->getPaymentMethod();
  3692.             $processor $method $method->getProcessor() : null;
  3693.             if ($processor) {
  3694.                 $result $processor->getTransactionData($transaction$isPrimary);
  3695.             }
  3696.         }
  3697.         return $result;
  3698.     }
  3699.     /**
  3700.      * Get last payment transaction ID.
  3701.      * This data is displayed on the order page, invoice and packing slip
  3702.      *
  3703.      * @return string|null
  3704.      */
  3705.     public function getPaymentTransactionId()
  3706.     {
  3707.         $transaction $this->getPaymentTransactions()
  3708.             ? $this->getPaymentTransactions()->last()
  3709.             : null;
  3710.         return $transaction
  3711.             $transaction->getPublicId()
  3712.             : null;
  3713.     }
  3714.     /**
  3715.      * @return bool
  3716.      */
  3717.     public function isOfflineProcessorUsed()
  3718.     {
  3719.         $processor $this->getPaymentProcessor();
  3720.         return $processor instanceof \XLite\Model\Payment\Processor\Offline;
  3721.     }
  3722.     /**
  3723.      * @return \XLite\Model\Payment\Base\Processor
  3724.      */
  3725.     public function getPaymentProcessor()
  3726.     {
  3727.         $processor null;
  3728.         $transaction $this->getPaymentTransactions()
  3729.             ? $this->getPaymentTransactions()->last()
  3730.             : null;
  3731.         if ($transaction) {
  3732.             $method $transaction->getPaymentMethod() ?: $this->getPaymentMethod();
  3733.             $processor $method $method->getProcessor() : null;
  3734.         }
  3735.         return $processor;
  3736.     }
  3737.     // {{{ Sales statistic
  3738.     /**
  3739.      * Returns old payment status code
  3740.      *
  3741.      * @return string
  3742.      */
  3743.     protected function getOldPaymentStatusCode()
  3744.     {
  3745.         $oldPaymentStatus $this->oldPaymentStatus;
  3746.         return $oldPaymentStatus && $oldPaymentStatus->getCode()
  3747.             ? $oldPaymentStatus->getCode()
  3748.             : '';
  3749.     }
  3750.     /**
  3751.      * Calculate sales delta
  3752.      *
  3753.      * @return integer|null
  3754.      */
  3755.     protected function getSalesDelta()
  3756.     {
  3757.         $result null;
  3758.         $newStatusCode $this->getPaymentStatusCode();
  3759.         if ($newStatusCode) {
  3760.             $oldStatusCode $this->getOldPaymentStatusCode();
  3761.             $paidStatuses \XLite\Model\Order\Status\Payment::getPaidStatuses();
  3762.             if (
  3763.                 (!$oldStatusCode || !in_array($oldStatusCode$paidStatusestrue))
  3764.                 && in_array($newStatusCode$paidStatusestrue)
  3765.             ) {
  3766.                 $result 1;
  3767.             } elseif (
  3768.                 $oldStatusCode
  3769.                 && in_array($oldStatusCode$paidStatusestrue)
  3770.                 && !in_array($newStatusCode$paidStatusestrue)
  3771.             ) {
  3772.                 $result = -1;
  3773.             }
  3774.         }
  3775.         return $result;
  3776.     }
  3777.     /**
  3778.      * Update sales statistics
  3779.      *
  3780.      * @param integer $delta Delta
  3781.      */
  3782.     protected function updateItemsSales($delta)
  3783.     {
  3784.         if ($delta === null) {
  3785.             return;
  3786.         }
  3787.         foreach ($this->getItems() as $item) {
  3788.             $product $item->getObject();
  3789.             if ($product === null) {
  3790.                 continue;
  3791.             }
  3792.             $product->setSales(
  3793.                 $product->getSales() + $delta $item->getAmount()
  3794.             );
  3795.         }
  3796.     }
  3797.     /**
  3798.      * Update sales
  3799.      */
  3800.     protected function updateSales()
  3801.     {
  3802.         $this->updateItemsSales($this->getSalesDelta());
  3803.     }
  3804.     // }}}
  3805.     /**
  3806.      * Get order_id
  3807.      *
  3808.      * @return integer
  3809.      */
  3810.     public function getOrderId()
  3811.     {
  3812.         return $this->order_id;
  3813.     }
  3814.     public function getPublicId(): ?string
  3815.     {
  3816.         return $this->public_id;
  3817.     }
  3818.     /**
  3819.      * Cannot be strict `string` type as there are 'public_id=null' orders after upgrade. #XCB-2685
  3820.      */
  3821.     public function setPublicId(?string $public_id): void
  3822.     {
  3823.         $this->public_id $public_id;
  3824.     }
  3825.     /**
  3826.      * Set shipping_id
  3827.      *
  3828.      * @param integer $shippingId
  3829.      * @return Order
  3830.      */
  3831.     public function setShippingId($shippingId)
  3832.     {
  3833.         $this->shipping_id $shippingId;
  3834.         return $this;
  3835.     }
  3836.     /**
  3837.      * Get shipping_id
  3838.      *
  3839.      * @return integer
  3840.      */
  3841.     public function getShippingId()
  3842.     {
  3843.         return $this->shipping_id;
  3844.     }
  3845.     /**
  3846.      * Set shipping_method_name
  3847.      *
  3848.      * @param string $shippingMethodName
  3849.      * @return Order
  3850.      */
  3851.     public function setShippingMethodName($shippingMethodName)
  3852.     {
  3853.         $this->shipping_method_name $shippingMethodName;
  3854.         return $this;
  3855.     }
  3856.     /**
  3857.      * Set payment_method_name
  3858.      *
  3859.      * @param string $paymentMethodName
  3860.      * @return Order
  3861.      */
  3862.     public function setPaymentMethodName($paymentMethodName)
  3863.     {
  3864.         $this->payment_method_name $paymentMethodName;
  3865.         return $this;
  3866.     }
  3867.     /**
  3868.      * Set tracking
  3869.      *
  3870.      * @param string $tracking
  3871.      * @return Order
  3872.      */
  3873.     public function setTracking($tracking)
  3874.     {
  3875.         $this->tracking $tracking;
  3876.         return $this;
  3877.     }
  3878.     /**
  3879.      * Get tracking
  3880.      *
  3881.      * @return string
  3882.      */
  3883.     public function getTracking()
  3884.     {
  3885.         return $this->tracking;
  3886.     }
  3887.     /**
  3888.      * Set date
  3889.      *
  3890.      * @param integer $date
  3891.      * @return Order
  3892.      */
  3893.     public function setDate($date)
  3894.     {
  3895.         $this->date $date;
  3896.         return $this;
  3897.     }
  3898.     /**
  3899.      * Get date
  3900.      *
  3901.      * @return integer
  3902.      */
  3903.     public function getDate()
  3904.     {
  3905.         return $this->date;
  3906.     }
  3907.     /**
  3908.      * Set lastRenewDate
  3909.      *
  3910.      * @param integer $lastRenewDate
  3911.      * @return Order
  3912.      */
  3913.     public function setLastRenewDate($lastRenewDate)
  3914.     {
  3915.         $this->lastRenewDate $lastRenewDate;
  3916.         return $this;
  3917.     }
  3918.     /**
  3919.      * Get lastRenewDate
  3920.      *
  3921.      * @return integer
  3922.      */
  3923.     public function getLastRenewDate()
  3924.     {
  3925.         return $this->lastRenewDate;
  3926.     }
  3927.     /**
  3928.      * Set notes
  3929.      *
  3930.      * @param string $notes
  3931.      *
  3932.      * @return Order
  3933.      */
  3934.     public function setNotes($notes)
  3935.     {
  3936.         $this->notes $notes;
  3937.         return $this;
  3938.     }
  3939.     /**
  3940.      * Get notes
  3941.      *
  3942.      * @return string
  3943.      */
  3944.     public function getNotes()
  3945.     {
  3946.         return $this->notes;
  3947.     }
  3948.     /**
  3949.      * Set adminNotes
  3950.      *
  3951.      * @param string $adminNotes
  3952.      *
  3953.      * @return Order
  3954.      */
  3955.     public function setAdminNotes($adminNotes)
  3956.     {
  3957.         $this->adminNotes $adminNotes;
  3958.         return $this;
  3959.     }
  3960.     /**
  3961.      * Get adminNotes
  3962.      *
  3963.      * @return string
  3964.      */
  3965.     public function getAdminNotes()
  3966.     {
  3967.         return $this->adminNotes;
  3968.     }
  3969.     /**
  3970.      * Set orderNumber
  3971.      *
  3972.      * @param string $orderNumber
  3973.      * @return Order
  3974.      */
  3975.     public function setOrderNumber($orderNumber)
  3976.     {
  3977.         $this->orderNumber $orderNumber;
  3978.         return $this;
  3979.     }
  3980.     /**
  3981.      * Get orderNumber
  3982.      *
  3983.      * @return string
  3984.      */
  3985.     public function getOrderNumber()
  3986.     {
  3987.         return $this->orderNumber;
  3988.     }
  3989.     /**
  3990.      * Set recent
  3991.      *
  3992.      * @param boolean $recent
  3993.      * @return Order
  3994.      */
  3995.     public function setRecent($recent)
  3996.     {
  3997.         $this->recent $recent;
  3998.         return $this;
  3999.     }
  4000.     /**
  4001.      * Get recent
  4002.      *
  4003.      * @return boolean
  4004.      */
  4005.     public function getRecent()
  4006.     {
  4007.         return $this->recent;
  4008.     }
  4009.     /**
  4010.      * Set xcPendingExport
  4011.      *
  4012.      * @param boolean $xcPendingExport
  4013.      * @return Order
  4014.      */
  4015.     public function setXcPendingExport($xcPendingExport)
  4016.     {
  4017.         $this->xcPendingExport $xcPendingExport;
  4018.         return $this;
  4019.     }
  4020.     /**
  4021.      * Get xcPendingExport
  4022.      *
  4023.      * @return boolean
  4024.      */
  4025.     public function getXcPendingExport()
  4026.     {
  4027.         return $this->xcPendingExport;
  4028.     }
  4029.     /**
  4030.      * Return BackorderCompetitors
  4031.      *
  4032.      * @return ArrayCollection
  4033.      */
  4034.     public function getBackorderCompetitors()
  4035.     {
  4036.         return $this->backorderCompetitors;
  4037.     }
  4038.     /**
  4039.      * Set BackorderCompetitors
  4040.      *
  4041.      * @param Order[] $backorderCompetitors
  4042.      *
  4043.      * @return $this
  4044.      */
  4045.     public function setBackorderCompetitors($backorderCompetitors)
  4046.     {
  4047.         $this->backorderCompetitors $backorderCompetitors;
  4048.         return $this;
  4049.     }
  4050.     /**
  4051.      * Get total
  4052.      *
  4053.      * @return float
  4054.      */
  4055.     public function getTotal()
  4056.     {
  4057.         return $this->total;
  4058.     }
  4059.     /**
  4060.      * Get subtotal
  4061.      *
  4062.      * @return float
  4063.      */
  4064.     public function getSubtotal()
  4065.     {
  4066.         return $this->subtotal;
  4067.     }
  4068.     /**
  4069.      * Get profile
  4070.      *
  4071.      * @return \XLite\Model\Profile
  4072.      */
  4073.     public function getProfile()
  4074.     {
  4075.         return $this->profile;
  4076.     }
  4077.     /**
  4078.      * Get orig_profile
  4079.      *
  4080.      * @return \XLite\Model\Profile
  4081.      */
  4082.     public function getOrigProfile()
  4083.     {
  4084.         return $this->orig_profile;
  4085.     }
  4086.     /**
  4087.      * Get paymentStatus
  4088.      *
  4089.      * @return \XLite\Model\Order\Status\Payment
  4090.      */
  4091.     public function getPaymentStatus()
  4092.     {
  4093.         return $this->paymentStatus;
  4094.     }
  4095.     /**
  4096.      * Get shippingStatus
  4097.      *
  4098.      * @return \XLite\Model\Order\Status\Shipping
  4099.      */
  4100.     public function getShippingStatus()
  4101.     {
  4102.         return $this->shippingStatus;
  4103.     }
  4104.     /**
  4105.      * Add details
  4106.      *
  4107.      * @param \XLite\Model\OrderDetail $details
  4108.      * @return Order
  4109.      */
  4110.     public function addDetails(\XLite\Model\OrderDetail $details)
  4111.     {
  4112.         $this->details[] = $details;
  4113.         return $this;
  4114.     }
  4115.     /**
  4116.      * Get details
  4117.      *
  4118.      * @return \Doctrine\Common\Collections\Collection|\XLite\Model\OrderDetail[]
  4119.      */
  4120.     public function getDetails()
  4121.     {
  4122.         return $this->details;
  4123.     }
  4124.     /**
  4125.      * Add trackingNumbers
  4126.      *
  4127.      * @param \XLite\Model\OrderTrackingNumber $trackingNumbers
  4128.      * @return Order
  4129.      */
  4130.     public function addTrackingNumbers(\XLite\Model\OrderTrackingNumber $trackingNumbers)
  4131.     {
  4132.         $this->trackingNumbers[] = $trackingNumbers;
  4133.         return $this;
  4134.     }
  4135.     /**
  4136.      * Get trackingNumbers
  4137.      *
  4138.      * @return \Doctrine\Common\Collections\Collection
  4139.      */
  4140.     public function getTrackingNumbers()
  4141.     {
  4142.         return $this->trackingNumbers;
  4143.     }
  4144.     /**
  4145.      * Add events
  4146.      *
  4147.      * @param \XLite\Model\OrderHistoryEvents $events
  4148.      * @return Order
  4149.      */
  4150.     public function addEvents(\XLite\Model\OrderHistoryEvents $events)
  4151.     {
  4152.         $this->events[] = $events;
  4153.         return $this;
  4154.     }
  4155.     /**
  4156.      * Get events
  4157.      *
  4158.      * @return \Doctrine\Common\Collections\Collection
  4159.      */
  4160.     public function getEvents()
  4161.     {
  4162.         return $this->events;
  4163.     }
  4164.     /**
  4165.      * Add item
  4166.      *
  4167.      * @param \XLite\Model\OrderItem $item
  4168.      * @return Order
  4169.      */
  4170.     public function addItems(\XLite\Model\OrderItem $item)
  4171.     {
  4172.         $this->items[] = $item;
  4173.         return $this;
  4174.     }
  4175.     /**
  4176.      * Get items
  4177.      *
  4178.      * @return \XLite\Model\OrderItem[]
  4179.      */
  4180.     public function getItems()
  4181.     {
  4182.         return $this->items;
  4183.     }
  4184.     /**
  4185.      * Add surcharges
  4186.      *
  4187.      * @param \XLite\Model\Order\Surcharge $surcharges
  4188.      * @return Order
  4189.      */
  4190.     public function addSurcharges(\XLite\Model\Order\Surcharge $surcharges)
  4191.     {
  4192.         $this->surcharges[] = $surcharges;
  4193.         return $this;
  4194.     }
  4195.     /**
  4196.      * Get surcharges
  4197.      *
  4198.      * @return \Doctrine\Common\Collections\Collection
  4199.      */
  4200.     public function getSurcharges()
  4201.     {
  4202.         return $this->surcharges;
  4203.     }
  4204.     /**
  4205.      * Add payment_transactions
  4206.      *
  4207.      * @param \XLite\Model\Payment\Transaction $paymentTransactions
  4208.      * @return Order
  4209.      */
  4210.     public function addPaymentTransactions(\XLite\Model\Payment\Transaction $paymentTransactions)
  4211.     {
  4212.         $this->payment_transactions[] = $paymentTransactions;
  4213.         return $this;
  4214.     }
  4215.     /**
  4216.      * Get payment_transactions
  4217.      *
  4218.      * @return \Doctrine\Common\Collections\Collection|\XLite\Model\Payment\Transaction[]
  4219.      */
  4220.     public function getPaymentTransactions()
  4221.     {
  4222.         $result $this->payment_transactions;
  4223.         $compare = static function ($a$b) {
  4224.             /** @var \XLite\Model\Payment\Transaction $a */
  4225.             /** @var \XLite\Model\Payment\Transaction $b */
  4226.             return ($a->getTransactionId() < $b->getTransactionId()) ? -1;
  4227.         };
  4228.         if ($result instanceof \Doctrine\Common\Collections\Collection) {
  4229.             $iterator $result->getIterator();
  4230.             $iterator->uasort($compare);
  4231.             foreach ($iterator as $key => $item) {
  4232.                 $result->set($key$item);
  4233.             }
  4234.         } elseif (is_array($result)) {
  4235.             uasort($result$compare);
  4236.         }
  4237.         return $result;
  4238.     }
  4239.     /**
  4240.      * Set currency
  4241.      *
  4242.      * @param \XLite\Model\Currency $currency
  4243.      * @return Order
  4244.      */
  4245.     public function setCurrency(\XLite\Model\Currency $currency null)
  4246.     {
  4247.         $this->currency $currency;
  4248.         return $this;
  4249.     }
  4250.     /**
  4251.      * Get currency
  4252.      *
  4253.      * @return \XLite\Model\Currency
  4254.      */
  4255.     public function getCurrency()
  4256.     {
  4257.         return $this->currency;
  4258.     }
  4259.     /**
  4260.      * Check - block Paid is visible or not
  4261.      *
  4262.      * @return boolean
  4263.      */
  4264.     protected function blockPaidIsVisible()
  4265.     {
  4266.         return $this->getPaidTotal() > 0
  4267.             && $this->getOpenTotal() >= 0;
  4268.     }
  4269.     /**
  4270.      * Remember last shipping id
  4271.      *
  4272.      * @return bool
  4273.      */
  4274.     public function updateEmptyShippingID()
  4275.     {
  4276.         if (!$this->getShippingId() && $this->getProfile() && $this->getLastShippingId()) {
  4277.             $this->setShippingId($this->getLastShippingId());
  4278.             return true;
  4279.         }
  4280.         return false;
  4281.     }
  4282. }