1<?php
2/**
3 * Copyright since 2007 PrestaShop SA and Contributors
4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5 *
6 * NOTICE OF LICENSE
7 *
8 * This source file is subject to the Open Software License (OSL 3.0)
9 * that is bundled with this package in the file LICENSE.md.
10 * It is also available through the world-wide-web at this URL:
11 * https://opensource.org/licenses/OSL-3.0
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@prestashop.com so we can send you a copy immediately.
15 *
16 * DISCLAIMER
17 *
18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
19 * versions in the future. If you wish to customize PrestaShop for your
20 * needs please refer to https://devdocs.prestashop.com/ for more information.
21 *
22 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
23 * @copyright Since 2007 PrestaShop SA and Contributors
24 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
25 */
26
27namespace PrestaShop\PrestaShop\Adapter\Order\CommandHandler;
28
29use Address;
30use Cart;
31use Order;
32use OrderDetail;
33use OrderInvoice;
34use PrestaShop\PrestaShop\Adapter\Cart\Comparator\CartProductsComparator;
35use PrestaShop\PrestaShop\Adapter\Cart\Comparator\CartProductUpdate;
36use PrestaShop\PrestaShop\Adapter\ContextStateManager;
37use PrestaShop\PrestaShop\Adapter\Order\OrderAmountUpdater;
38use PrestaShop\PrestaShop\Adapter\Order\OrderDetailUpdater;
39use PrestaShop\PrestaShop\Adapter\Order\OrderProductQuantityUpdater;
40use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderDeliveryAddressCommand;
41use PrestaShop\PrestaShop\Core\Domain\Order\CommandHandler\ChangeOrderDeliveryAddressHandlerInterface;
42use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderException;
43use Validate;
44
45/**
46 * @internal
47 */
48final class ChangeOrderDeliveryAddressHandler extends AbstractOrderCommandHandler implements ChangeOrderDeliveryAddressHandlerInterface
49{
50    /**
51     * @var OrderAmountUpdater
52     */
53    private $orderAmountUpdater;
54
55    /**
56     * @var OrderDetailUpdater
57     */
58    private $orderDetailTaxUpdater;
59
60    /**
61     * @var ContextStateManager
62     */
63    private $contextStateManager;
64
65    /**
66     * @var OrderProductQuantityUpdater
67     */
68    private $orderProductQuantityUpdater;
69
70    /**
71     * @param OrderAmountUpdater $orderAmountUpdater
72     * @param OrderDetailUpdater $orderDetailTaxUpdater
73     * @param ContextStateManager $contextStateManager
74     * @param OrderProductQuantityUpdater $orderProductQuantityUpdater
75     */
76    public function __construct(
77        OrderAmountUpdater $orderAmountUpdater,
78        OrderDetailUpdater $orderDetailTaxUpdater,
79        ContextStateManager $contextStateManager,
80        OrderProductQuantityUpdater $orderProductQuantityUpdater
81    ) {
82        $this->orderAmountUpdater = $orderAmountUpdater;
83        $this->orderDetailTaxUpdater = $orderDetailTaxUpdater;
84        $this->contextStateManager = $contextStateManager;
85        $this->orderProductQuantityUpdater = $orderProductQuantityUpdater;
86    }
87
88    /**
89     * {@inheritdoc}
90     */
91    public function handle(ChangeOrderDeliveryAddressCommand $command)
92    {
93        $order = $this->getOrder($command->getOrderId());
94        $address = new Address($command->getNewDeliveryAddressId()->getValue());
95
96        $cart = Cart::getCartByOrderId($order->id);
97
98        if (!Validate::isLoadedObject($address)) {
99            throw new OrderException('New delivery address is not valid');
100        }
101
102        $this->setCartContext($this->contextStateManager, $cart);
103
104        try {
105            $comparator = new CartProductsComparator($cart);
106
107            $cart->updateDeliveryAddressId((int) $cart->id_address_delivery, (int) $address->id);
108            $cart->setDeliveryOption([
109                (int) $cart->id_address_delivery => $this->formatLegacyDeliveryOptionFromCarrierId($order->id_carrier),
110            ]);
111            $cart->update();
112
113            // gift could have been added/deleted when changing delivery address
114            $this->synchronizeOrderWithCart($order, $cart, $comparator);
115
116            $order->id_address_delivery = $address->id;
117            $this->orderDetailTaxUpdater->updateOrderDetailsTaxes($order);
118            $this->orderAmountUpdater->update($order, $cart);
119        } finally {
120            $this->contextStateManager->restorePreviousContext();
121        }
122    }
123
124    /**
125     * @param Order $order
126     * @param Cart $cart
127     * @param CartProductsComparator $productsComparator
128     */
129    private function synchronizeOrderWithCart(
130        Order $order,
131        Cart $cart,
132        CartProductsComparator $productsComparator
133    ): void {
134        $modified = $productsComparator->getModifiedProducts();
135        foreach ($modified as $productUpdate) {
136            $orderDetail = $this->getOrderDetail($productUpdate, $order, $cart);
137            if (null === $orderDetail) {
138                continue;
139            }
140            $quantity = $productUpdate->isCreated()
141                ? $productUpdate->getDeltaQuantity()
142                : $orderDetail->product_quantity + $productUpdate->getDeltaQuantity();
143            $orderInvoice = $orderDetail->id_order_invoice != 0 ? new OrderInvoice($orderDetail->id_order_invoice) : null;
144
145            $this->orderProductQuantityUpdater->update(
146                $order,
147                $orderDetail,
148                $quantity,
149                $orderInvoice,
150                false
151            );
152        }
153    }
154
155    /**
156     * @param CartProductUpdate $productUpdate
157     * @param Order $order
158     * @param Cart $cart
159     *
160     * @return OrderDetail|null
161     */
162    private function getOrderDetail(CartProductUpdate $productUpdate, Order $order, Cart $cart): ?OrderDetail
163    {
164        $combinationId = $productUpdate->getCombinationId() ? $productUpdate->getCombinationId()->getValue() : 0;
165        foreach ($order->getProducts() as $product) {
166            if (
167                (int) $product['product_id'] === $productUpdate->getProductId()->getValue()
168                && (int) $product['product_attribute_id'] === $combinationId
169            ) {
170                return new OrderDetail($product['id_order_detail']);
171            }
172        }
173
174        foreach ($cart->getProducts() as $product) {
175            if (
176                (int) $product['id_product'] === $productUpdate->getProductId()->getValue()
177                && (int) $product['id_product_attribute'] === $combinationId
178            ) {
179                $orderDetail = new OrderDetail();
180                $orderDetail->createList($order, $cart, $order->getCurrentOrderState(), [$product]);
181
182                return $orderDetail;
183            }
184        }
185
186        return null;
187    }
188}
189