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\Presenter\Order;
28
29use Address;
30use AddressFormat;
31use Carrier;
32use Cart;
33use Configuration;
34use Context;
35use Currency;
36use CustomerMessage;
37use Doctrine\Common\Annotations\AnnotationException;
38use Order;
39use OrderReturn;
40use PrestaShop\PrestaShop\Adapter\Presenter\AbstractLazyArray;
41use PrestaShop\PrestaShop\Adapter\Presenter\Cart\CartPresenter;
42use PrestaShop\PrestaShop\Adapter\Presenter\Object\ObjectPresenter;
43use PrestaShop\PrestaShop\Adapter\Product\PriceFormatter;
44use PrestaShopBundle\Translation\TranslatorComponent;
45use PrestaShopException;
46use ProductDownload;
47use ReflectionException;
48use TaxConfiguration;
49use Tools;
50
51class OrderLazyArray extends AbstractLazyArray
52{
53    /** @var CartPresenter */
54    private $cartPresenter;
55
56    /** @var ObjectPresenter */
57    private $objectPresenter;
58
59    /** @var PriceFormatter */
60    private $priceFormatter;
61
62    /** @var TranslatorComponent */
63    private $translator;
64
65    /** @var TaxConfiguration */
66    private $taxConfiguration;
67
68    /** @var Order */
69    private $order;
70
71    /** @var OrderSubtotalLazyArray */
72    private $subTotals;
73
74    /**
75     * OrderArray constructor.
76     *
77     * @throws AnnotationException
78     * @throws ReflectionException
79     */
80    public function __construct(Order $order)
81    {
82        $this->order = $order;
83        $this->cartPresenter = new CartPresenter();
84        $this->objectPresenter = new ObjectPresenter();
85        $this->priceFormatter = new PriceFormatter();
86        $this->translator = Context::getContext()->getTranslator();
87        $this->taxConfiguration = new TaxConfiguration();
88        $this->subTotals = new OrderSubtotalLazyArray($this->order);
89        parent::__construct();
90    }
91
92    /**
93     * @arrayAccess
94     *
95     * @return mixed
96     */
97    public function getTotals()
98    {
99        $amounts = $this->getAmounts();
100
101        return $amounts['totals'];
102    }
103
104    /**
105     * @arrayAccess
106     *
107     * @return int
108     */
109    public function getIdAddressInvoice()
110    {
111        return $this->order->id_address_invoice;
112    }
113
114    /**
115     * @arrayAccess
116     *
117     * @return int
118     */
119    public function getIdAddressDelivery()
120    {
121        return $this->order->id_address_delivery;
122    }
123
124    /**
125     * @arrayAccess
126     *
127     * @return mixed
128     */
129    public function getSubtotals()
130    {
131        return $this->subTotals;
132    }
133
134    /**
135     * @arrayAccess
136     *
137     * @return int
138     */
139    public function getProductsCount()
140    {
141        return count($this->getProducts());
142    }
143
144    /**
145     * @arrayAccess
146     *
147     * @return mixed
148     *
149     * @throws PrestaShopException
150     */
151    public function getShipping()
152    {
153        $details = $this->getDetails();
154
155        return $details['shipping'];
156    }
157
158    /**
159     * @arrayAccess
160     *
161     * @return array
162     */
163    public function getProducts()
164    {
165        $order = $this->order;
166        $cart = new Cart($order->id_cart);
167
168        $orderProducts = $order->getProducts();
169        $cartProducts = $this->cartPresenter->present($cart);
170        $orderPaid = $order->getCurrentOrderState() && $order->getCurrentOrderState()->paid;
171
172        $includeTaxes = $this->includeTaxes();
173        foreach ($orderProducts as &$orderProduct) {
174            // Use data from OrderDetail in case that the Product has been deleted
175            $orderProduct['name'] = $orderProduct['product_name'];
176            $orderProduct['quantity'] = $orderProduct['product_quantity'];
177            $orderProduct['id_product'] = $orderProduct['product_id'];
178            $orderProduct['id_product_attribute'] = $orderProduct['product_attribute_id'];
179
180            $productPrice = $includeTaxes ? 'product_price_wt' : 'product_price';
181            $totalPrice = $includeTaxes ? 'total_wt' : 'total_price';
182
183            $orderProduct['price'] = $this->priceFormatter->format(
184                $orderProduct[$productPrice],
185                Currency::getCurrencyInstance((int) $order->id_currency)
186            );
187            $orderProduct['total'] = $this->priceFormatter->format(
188                $orderProduct[$totalPrice],
189                Currency::getCurrencyInstance((int) $order->id_currency)
190            );
191
192            if ($orderPaid && $orderProduct['is_virtual']) {
193                $id_product_download = ProductDownload::getIdFromIdProduct($orderProduct['product_id']);
194                $product_download = new ProductDownload($id_product_download);
195                if ($product_download->display_filename != '') {
196                    $orderProduct['download_link'] =
197                        $product_download->getTextLink(false, $orderProduct['download_hash'])
198                        . '&id_order=' . (int) $order->id
199                        . '&secure_key=' . $order->secure_key;
200                }
201            }
202
203            foreach ($cartProducts['products'] as $cartProduct) {
204                if (($cartProduct['id_product'] === $orderProduct['id_product'])
205                    && ($cartProduct['id_product_attribute'] === $orderProduct['id_product_attribute'])
206                ) {
207                    if (isset($cartProduct['attributes'])) {
208                        $orderProduct['attributes'] = $cartProduct['attributes'];
209                    } else {
210                        $orderProduct['attributes'] = [];
211                    }
212                    $orderProduct['cover'] = $cartProduct['cover'];
213                    $orderProduct['default_image'] = $cartProduct['default_image'];
214                    $orderProduct['unit_price_full'] = $cartProduct['unit_price_full'];
215                    break;
216                }
217            }
218
219            OrderReturn::addReturnedQuantity($orderProducts, $order->id);
220        }
221
222        $orderProducts = $this->cartPresenter->addCustomizedData($orderProducts, $cart);
223
224        return $orderProducts;
225    }
226
227    /**
228     * @arrayAccess
229     *
230     * @return array
231     */
232    public function getAmounts()
233    {
234        $order = $this->order;
235
236        $amounts['subtotals'] = $this->subTotals;
237
238        $amounts['totals'] = [];
239        $amount = $this->includeTaxes() ? $order->total_paid : $order->total_paid_tax_excl;
240        $amounts['totals']['total'] = [
241            'type' => 'total',
242            'label' => $this->translator->trans('Total', [], 'Shop.Theme.Checkout'),
243            'amount' => $amount,
244            'value' => $this->priceFormatter->format($amount, Currency::getCurrencyInstance((int) $order->id_currency)),
245        ];
246
247        $amounts['totals']['total_paid'] = [
248            'type' => 'total_paid',
249            'label' => $this->translator->trans('Total paid', [], 'Shop.Theme.Checkout'),
250            'amount' => $order->total_paid_real,
251            'value' => $this->priceFormatter->format(
252                $order->total_paid_real,
253                Currency::getCurrencyInstance((int) $order->id_currency)
254            ),
255        ];
256
257        $amounts['totals']['total_including_tax'] = [
258            'type' => 'total_including_tax',
259            'label' => $this->translator->trans('Total (tax incl.)', [], 'Shop.Theme.Checkout'),
260            'amount' => $order->total_paid_tax_incl,
261            'value' => $this->priceFormatter->format(
262                $order->total_paid_tax_incl,
263                Currency::getCurrencyInstance((int) $order->id_currency)
264            ),
265        ];
266
267        $amounts['totals']['total_excluding_tax'] = [
268            'type' => 'total_excluding_tax',
269            'label' => $this->translator->trans('Total (tax excl.)', [], 'Shop.Theme.Checkout'),
270            'amount' => $order->total_paid_tax_excl,
271            'value' => $this->priceFormatter->format(
272                $order->total_paid_tax_excl,
273                Currency::getCurrencyInstance((int) $order->id_currency)
274            ),
275        ];
276
277        return $amounts;
278    }
279
280    /**
281     * @arrayAccess
282     *
283     * @return OrderDetailLazyArray
284     */
285    public function getDetails()
286    {
287        return new OrderDetailLazyArray($this->order);
288    }
289
290    /**
291     * @arrayAccess
292     *
293     * @return array
294     */
295    public function getHistory()
296    {
297        $order = $this->order;
298
299        $orderHistory = [];
300        $context = Context::getContext();
301        $historyList = $order->getHistory($context->language->id, false, true);
302
303        foreach ($historyList as $historyId => $history) {
304            // HistoryList only contains order states that are not hidden to customers, the last visible order state,
305            // that is to say the one we get in the first iteration
306            if ($historyId === array_key_first($historyList)) {
307                $historyId = 'current';
308            }
309            $orderHistory[$historyId] = $history;
310            $orderHistory[$historyId]['history_date'] = Tools::displayDate($history['date_add'], null, false);
311            $orderHistory[$historyId]['contrast'] = (Tools::getBrightness($history['color']) > 128) ? 'dark' : 'bright';
312        }
313
314        if (!isset($orderHistory['current'])) {
315            $orderHistory['current'] = $this->getDefaultHistory();
316        }
317
318        return $orderHistory;
319    }
320
321    /**
322     * @arrayAccess
323     *
324     * @return array
325     */
326    public function getMessages()
327    {
328        $order = $this->order;
329
330        $messages = [];
331        $customerMessages = CustomerMessage::getMessagesByOrderId((int) $order->id, false);
332
333        foreach ($customerMessages as $cmId => $customerMessage) {
334            $messages[$cmId] = $customerMessage;
335            $messages[$cmId]['message'] = nl2br($customerMessage['message']);
336            $messages[$cmId]['message_date'] = Tools::displayDate($customerMessage['date_add'], null, true);
337            if (isset($customerMessage['elastname']) && $customerMessage['elastname']) {
338                $messages[$cmId]['name'] = $customerMessage['efirstname'] . ' ' . $customerMessage['elastname'];
339            } elseif ($customerMessage['clastname']) {
340                $messages[$cmId]['name'] = $customerMessage['cfirstname'] . ' ' . $customerMessage['clastname'];
341            } else {
342                $messages[$cmId]['name'] = Configuration::get('PS_SHOP_NAME');
343            }
344        }
345
346        return $messages;
347    }
348
349    /**
350     * @arrayAccess
351     *
352     * @return array
353     */
354    public function getCarrier()
355    {
356        $order = $this->order;
357
358        $carrier = new Carrier((int) $order->id_carrier, (int) $order->getAssociatedLanguage()->getId());
359        $orderCarrier = $this->objectPresenter->present($carrier);
360        $orderCarrier['name'] = ($carrier->name == '0') ? Configuration::get('PS_SHOP_NAME') : $carrier->name;
361        $orderCarrier['delay'] = $carrier->delay;
362
363        return $orderCarrier;
364    }
365
366    /**
367     * @arrayAccess
368     *
369     * @return array
370     */
371    public function getAddresses()
372    {
373        $order = $this->order;
374
375        $orderAddresses = [
376            'delivery' => [],
377            'invoice' => [],
378        ];
379
380        $addressDelivery = new Address((int) $order->id_address_delivery);
381        $addressInvoice = new Address((int) $order->id_address_invoice);
382
383        if (!$order->isVirtual()) {
384            $orderAddresses['delivery'] = $this->objectPresenter->present($addressDelivery);
385            $orderAddresses['delivery']['formatted'] =
386                AddressFormat::generateAddress($addressDelivery, [], '<br />');
387        }
388
389        $orderAddresses['invoice'] = $this->objectPresenter->present($addressInvoice);
390        $orderAddresses['invoice']['formatted'] = AddressFormat::generateAddress($addressInvoice, [], '<br />');
391
392        return $orderAddresses;
393    }
394
395    /**
396     * @arrayAccess
397     *
398     * @return string
399     */
400    public function getFollowUp()
401    {
402        $order = $this->order;
403
404        $carrier = $this->getCarrier();
405        if (!empty($carrier['url']) && !empty($order->shipping_number)) {
406            return str_replace('@', $order->shipping_number, $carrier['url']);
407        }
408
409        return '';
410    }
411
412    /**
413     * @arrayAccess
414     *
415     * @return array
416     */
417    public function getLabels()
418    {
419        return [
420            'tax_short' => ($this->includeTaxes())
421                ? $this->translator->trans('(tax incl.)', [], 'Shop.Theme.Global')
422                : $this->translator->trans('(tax excl.)', [], 'Shop.Theme.Global'),
423            'tax_long' => ($this->includeTaxes())
424                ? $this->translator->trans('(tax included)', [], 'Shop.Theme.Global')
425                : $this->translator->trans('(tax excluded)', [], 'Shop.Theme.Global'),
426        ];
427    }
428
429    /**
430     * @return bool|mixed
431     */
432    private function includeTaxes()
433    {
434        return $this->taxConfiguration->includeTaxes();
435    }
436
437    /**
438     * @return array
439     */
440    private function getDefaultHistory()
441    {
442        return [
443            'id_order_state' => '',
444            'invoice' => '',
445            'send_email' => '',
446            'module_name' => '',
447            'color' => '',
448            'unremovable' => '',
449            'hidden' => '',
450            'loggable' => '',
451            'delivery' => '',
452            'shipped' => '',
453            'paid' => '',
454            'pdf_invoice' => '',
455            'pdf_delivery' => '',
456            'deleted' => '',
457            'id_order_history' => '',
458            'id_employee' => '',
459            'id_order' => '',
460            'date_add' => '',
461            'employee_firstname' => '',
462            'employee_lastname' => '',
463            'ostate_name' => '',
464            'history_date' => '',
465            'contrast' => '',
466        ];
467    }
468}
469