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\Cart\CommandHandler;
28
29use Cart;
30use CartRule;
31use PrestaShop\PrestaShop\Adapter\Cart\AbstractCartHandler;
32use PrestaShop\PrestaShop\Core\ConfigurationInterface;
33use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateCartDeliverySettingsCommand;
34use PrestaShop\PrestaShop\Core\Domain\Cart\CommandHandler\UpdateCartDeliverySettingsHandlerInterface;
35use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartException;
36use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\InvalidGiftMessageException;
37use PrestaShop\PrestaShop\Core\Domain\CartRule\Exception\CannotDeleteCartRuleException;
38use PrestaShop\PrestaShop\Core\Domain\CartRule\Exception\CartRuleException;
39use PrestaShopException;
40use Symfony\Component\Translation\TranslatorInterface;
41use Validate;
42
43/**
44 * @internal
45 */
46final class UpdateCartDeliverySettingsHandler extends AbstractCartHandler implements UpdateCartDeliverySettingsHandlerInterface
47{
48    /**
49     * @var TranslatorInterface
50     */
51    private $translator;
52
53    /**
54     * @var ConfigurationInterface
55     */
56    private $configuration;
57
58    /**
59     * @param TranslatorInterface $translator
60     * @param ConfigurationInterface $configuration
61     */
62    public function __construct(TranslatorInterface $translator, ConfigurationInterface $configuration)
63    {
64        $this->translator = $translator;
65        $this->configuration = $configuration;
66    }
67
68    /**
69     * {@inheritdoc}
70     */
71    public function handle(UpdateCartDeliverySettingsCommand $command): void
72    {
73        $cart = $this->getCart($command->getCartId());
74
75        if (($command->getGiftMessage() !== null) && (!Validate::isMessage($command->getGiftMessage()))) {
76            throw new InvalidGiftMessageException();
77        }
78
79        $this->handleFreeShippingOption($cart, $command);
80        $shouldSaveCartAfterGiftOption = $this->handleGiftOption($cart, $command);
81        $shouldSaveCartAfterWrappingOption = $this->handleRecycledWrappingOption($cart, $command);
82        $shouldSaveCartAfterGiftMessageOption = $this->handleGiftMessageOption($cart, $command);
83
84        $shouldSaveCart = ($shouldSaveCartAfterGiftOption
85            || $shouldSaveCartAfterWrappingOption
86            || $shouldSaveCartAfterGiftMessageOption);
87
88        if ($shouldSaveCart) {
89            try {
90                if (false === $cart->update()) {
91                    throw new CartException('Failed to update cart delivery settings');
92                }
93            } catch (PrestaShopException $e) {
94                throw new CartException(sprintf('An error occurred while trying to update delivery settings for cart with id "%d"', $cart->id));
95            }
96        }
97    }
98
99    /**
100     * Sometimes, the cart rule to enable 'free shipping' exists
101     * but is not linked to the cart. We look for this cart rule
102     * to avoid creating duplicates.
103     *
104     * @param string $code
105     *
106     * @return CartRule|null
107     *
108     * @throws PrestaShopException
109     */
110    private function getCartRuleForBackOfficeFreeShipping($code): ?CartRule
111    {
112        $cartRuleId = CartRule::getIdByCode($code);
113
114        if (!$cartRuleId) {
115            return null;
116        }
117
118        return new CartRule((int) $cartRuleId);
119    }
120
121    /**
122     * @param Cart $cart
123     * @param string $backOfficeOrderCode
124     *
125     * @return CartRule
126     */
127    private function createCartRule(Cart $cart, string $backOfficeOrderCode): CartRule
128    {
129        $freeShippingCartRule = new CartRule();
130        $freeShippingCartRule->code = $backOfficeOrderCode;
131        $freeShippingCartRule->name = [
132            $this->configuration->get('PS_LANG_DEFAULT') => $this->translator->trans(
133                'Free Shipping',
134                [],
135                'Admin.Orderscustomers.Feature'
136            ),
137        ];
138        $freeShippingCartRule->id_customer = (int) $cart->id_customer;
139        $freeShippingCartRule->free_shipping = true;
140        $freeShippingCartRule->quantity = 1;
141        $freeShippingCartRule->quantity_per_user = 1;
142        $freeShippingCartRule->minimum_amount_currency = (int) $cart->id_currency;
143        $freeShippingCartRule->reduction_currency = (int) $cart->id_currency;
144        $freeShippingCartRule->date_from = date('Y-m-d H:i:s');
145        $freeShippingCartRule->date_to = date('Y-m-d H:i:s', time() + 24 * 36000);
146        $freeShippingCartRule->active = true;
147        $freeShippingCartRule->add();
148
149        return $freeShippingCartRule;
150    }
151
152    /**
153     * This method works as follows:
154     * 1. if free shipping should be enabled, enable it
155     * 2. if free shipping should not be enabled and cart already does not have free shipping, do nothing
156     * 3.if free shipping should not be enabled and cart has free shipping, disable it
157     *
158     * @param Cart $cart
159     * @param UpdateCartDeliverySettingsCommand $command
160     *
161     * @throws CannotDeleteCartRuleException
162     */
163    protected function handleFreeShippingOption(Cart $cart, UpdateCartDeliverySettingsCommand $command): void
164    {
165        $backOfficeOrderCode = sprintf('%s%s', CartRule::BO_ORDER_CODE_PREFIX, $cart->id);
166
167        $freeShippingCartRule = $this->getCartRuleForBackOfficeFreeShipping($backOfficeOrderCode);
168
169        $freeShippingShouldBeEnabled = $command->allowFreeShipping();
170
171        // Step 1
172        if ($freeShippingShouldBeEnabled) {
173            if (null === $freeShippingCartRule) {
174                // there is not yet a 'free shipping' cart rule available in the system so we create it
175                $freeShippingCartRule = $this->createCartRule($cart, $backOfficeOrderCode);
176            }
177            $cart->addCartRule((int) $freeShippingCartRule->id);
178
179            return;
180        }
181
182        if (null === $freeShippingCartRule) {
183            return;
184        }
185
186        $cart->removeCartRule((int) $freeShippingCartRule->id);
187
188        try {
189            if (false === $freeShippingCartRule->delete()) {
190                throw new CannotDeleteCartRuleException(sprintf('Failed deleting cart rule #%s', $freeShippingCartRule->id));
191            }
192        } catch (PrestaShopException $e) {
193            throw new CartRuleException(sprintf('An error occurred when trying to delete cart rule #%s', $freeShippingCartRule->id));
194        }
195    }
196
197    /**
198     * @param Cart $cart
199     * @param UpdateCartDeliverySettingsCommand $command
200     *
201     * @return bool should save the cart or not
202     *
203     * @throws CartException
204     * @throws PrestaShopException
205     */
206    private function handleGiftOption(Cart $cart, UpdateCartDeliverySettingsCommand $command): bool
207    {
208        if ($command->isAGift() === null) {
209            return false;
210        }
211
212        $cart->gift = $command->isAGift();
213
214        return true;
215    }
216
217    /**
218     * @param Cart $cart
219     * @param UpdateCartDeliverySettingsCommand $command
220     *
221     * @return bool should save the cart or not
222     */
223    private function handleRecycledWrappingOption(Cart $cart, UpdateCartDeliverySettingsCommand $command): bool
224    {
225        if ($command->useRecycledPackaging() === null) {
226            return false;
227        }
228
229        $cart->recyclable = $command->useRecycledPackaging();
230
231        return true;
232    }
233
234    /**
235     * @param Cart $cart
236     * @param UpdateCartDeliverySettingsCommand $command
237     *
238     * @return bool should save the cart or not
239     */
240    private function handleGiftMessageOption(Cart $cart, UpdateCartDeliverySettingsCommand $command): bool
241    {
242        if ($command->getGiftMessage() === null) {
243            return false;
244        }
245
246        $cart->gift_message = $command->getGiftMessage();
247
248        return true;
249    }
250}
251