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\QueryHandler;
28
29use Address;
30use Carrier;
31use Cart;
32use ConnectionsSource;
33use Context;
34use Country;
35use Currency;
36use Customer;
37use DateTimeImmutable;
38use Gender;
39use Module;
40use Order;
41use OrderInvoice;
42use OrderPayment;
43use OrderSlip;
44use OrderState;
45use PrestaShop\Decimal\DecimalNumber;
46use PrestaShop\PrestaShop\Adapter\Address\AddressFormatter;
47use PrestaShop\PrestaShop\Adapter\Configuration;
48use PrestaShop\PrestaShop\Adapter\Customer\CustomerDataProvider;
49use PrestaShop\PrestaShop\Adapter\Order\AbstractOrderHandler;
50use PrestaShop\PrestaShop\Core\Address\AddressFormatterInterface;
51use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
52use PrestaShop\PrestaShop\Core\Domain\Exception\InvalidSortingException;
53use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderException;
54use PrestaShop\PrestaShop\Core\Domain\Order\OrderDocumentType;
55use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderForViewing;
56use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderProductsForViewing;
57use PrestaShop\PrestaShop\Core\Domain\Order\QueryHandler\GetOrderForViewingHandlerInterface;
58use PrestaShop\PrestaShop\Core\Domain\Order\QueryHandler\GetOrderProductsForViewingHandlerInterface;
59use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\LinkedOrderForViewing;
60use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\LinkedOrdersForViewing;
61use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderCarrierForViewing;
62use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderCustomerForViewing;
63use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderDiscountForViewing;
64use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderDiscountsForViewing;
65use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderDocumentForViewing;
66use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderDocumentsForViewing;
67use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderForViewing;
68use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderHistoryForViewing;
69use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderInvoiceAddressForViewing;
70use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderMessageDateForViewing;
71use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderMessageForViewing;
72use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderMessagesForViewing;
73use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPaymentForViewing;
74use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPaymentsForViewing;
75use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPricesForViewing;
76use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderProductsForViewing;
77use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderReturnForViewing;
78use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderReturnsForViewing;
79use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderShippingAddressForViewing;
80use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderShippingForViewing;
81use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderSourceForViewing;
82use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderSourcesForViewing;
83use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderStatusForViewing;
84use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId;
85use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
86use PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException;
87use PrestaShop\PrestaShop\Core\Localization\Locale;
88use State;
89use Symfony\Component\Translation\TranslatorInterface;
90use Tools;
91use Validate;
92
93/**
94 * Handle getting order for viewing
95 *
96 * @internal
97 */
98final class GetOrderForViewingHandler extends AbstractOrderHandler implements GetOrderForViewingHandlerInterface
99{
100    /**
101     * @var Locale
102     */
103    private $locale;
104
105    /**
106     * @var TranslatorInterface
107     */
108    private $translator;
109
110    /**
111     * @var int
112     */
113    private $contextLanguageId;
114
115    /**
116     * @var CustomerDataProvider
117     */
118    private $customerDataProvider;
119
120    /**
121     * @var Configuration
122     */
123    private $configuration;
124
125    /**
126     * @var Context
127     */
128    private $context;
129
130    /**
131     * @var GetOrderProductsForViewingHandlerInterface
132     */
133    private $getOrderProductsForViewingHandler;
134
135    /**
136     * @var AddressFormatterInterface
137     */
138    private $addressFormatter;
139
140    /**
141     * @param TranslatorInterface $translator
142     * @param int $contextLanguageId
143     * @param Locale $locale
144     * @param Context $context
145     * @param CustomerDataProvider $customerDataProvider
146     * @param GetOrderProductsForViewingHandlerInterface $getOrderProductsForViewingHandler
147     */
148    public function __construct(
149        TranslatorInterface $translator,
150        int $contextLanguageId,
151        Locale $locale,
152        Context $context,
153        CustomerDataProvider $customerDataProvider,
154        GetOrderProductsForViewingHandlerInterface $getOrderProductsForViewingHandler,
155        Configuration $configuration,
156        AddressFormatterInterface $addressFormatter = null
157    ) {
158        $this->translator = $translator;
159        $this->contextLanguageId = $contextLanguageId;
160        $this->locale = $locale;
161        $this->translator = $translator;
162        $this->context = $context;
163        $this->customerDataProvider = $customerDataProvider;
164        $this->getOrderProductsForViewingHandler = $getOrderProductsForViewingHandler;
165        $this->configuration = $configuration;
166        $this->addressFormatter = $addressFormatter ?? new AddressFormatter();
167    }
168
169    /**
170     * {@inheritdoc}
171     */
172    public function handle(GetOrderForViewing $query): OrderForViewing
173    {
174        $order = $this->getOrder($query->getOrderId());
175        $orderCarrier = new Carrier($order->id_carrier);
176        $taxCalculationMethod = $this->getOrderTaxCalculationMethod($order);
177
178        $isTaxIncluded = ($taxCalculationMethod == PS_TAX_INC);
179
180        $taxMethod = $isTaxIncluded ?
181            $this->translator->trans('Tax included', [], 'Admin.Global') :
182            $this->translator->trans('Tax excluded', [], 'Admin.Global');
183
184        $invoiceManagementIsEnabled = (bool) $this->configuration->get(
185            'PS_INVOICE',
186            null,
187            ShopConstraint::shop((int) $order->id_shop)
188        );
189
190        $orderInvoiceAddress = $this->getOrderInvoiceAddress($order);
191
192        return new OrderForViewing(
193            (int) $order->id,
194            (int) $order->id_currency,
195            (int) $order->id_carrier,
196            (string) $orderCarrier->name,
197            (int) $order->id_shop,
198            $order->reference,
199            (bool) $order->isVirtual(),
200            $taxMethod,
201            $isTaxIncluded,
202            (bool) $order->valid,
203            $order->hasBeenPaid(),
204            $order->hasInvoice(),
205            $order->hasBeenDelivered(),
206            $order->hasBeenShipped(),
207            $invoiceManagementIsEnabled,
208            new DateTimeImmutable($order->date_add),
209            $this->getOrderCustomer($order, $orderInvoiceAddress),
210            $this->getOrderShippingAddress($order),
211            $orderInvoiceAddress,
212            $this->getOrderProducts($query->getOrderId(), $query->getProductsSorting()->getValue()),
213            $this->getOrderHistory($order),
214            $this->getOrderDocuments($order),
215            $this->getOrderShipping($order),
216            $this->getOrderReturns($order),
217            $this->getOrderPayments($order),
218            $this->getOrderMessages($order),
219            $this->getOrderPrices($order),
220            $this->getOrderDiscounts($order),
221            $this->getOrderSources($order),
222            $this->getLinkedOrders($order),
223            $this->addressFormatter->format(new AddressId((int) $order->id_address_delivery)),
224            $this->addressFormatter->format(new AddressId((int) $order->id_address_invoice)),
225            (string) $order->note
226        );
227    }
228
229    /**
230     * @param Order $order
231     *
232     * @return OrderCustomerForViewing
233     */
234    private function getOrderCustomer(Order $order, OrderInvoiceAddressForViewing $invoiceAddress): OrderCustomerForViewing
235    {
236        $currency = new Currency($order->id_currency);
237        $customer = new Customer($order->id_customer);
238        $genderName = '';
239        $totalSpentSinceRegistration = null;
240
241        if (!Validate::isLoadedObject($customer)) {
242            $customer = $this->buildFakeCustomerObject($order, $invoiceAddress);
243            $customerStats = ['nb_orders' => 1]; // Count this current order as loaded
244        } else {
245            $gender = new Gender($customer->id_gender);
246            if (Validate::isLoadedObject($gender)) {
247                $genderName = $gender->name[(int) $order->getAssociatedLanguage()->getId()];
248            }
249
250            $customerStats = $customer->getStats();
251            $totalSpentSinceRegistration = Tools::convertPrice($customerStats['total_orders'], $order->id_currency);
252        }
253
254        $isB2BEnabled = $this->configuration->getBoolean('PS_B2B_ENABLE');
255
256        return new OrderCustomerForViewing(
257            (int) $customer->id,
258            $customer->firstname,
259            $customer->lastname,
260            $genderName,
261            $customer->email,
262            new DateTimeImmutable($customer->date_add),
263            $totalSpentSinceRegistration !== null ? $this->locale->formatPrice($totalSpentSinceRegistration, $currency->iso_code) : '',
264            $customerStats['nb_orders'],
265            $customer->note,
266            (bool) $customer->is_guest,
267            (int) $order->getAssociatedLanguage()->getId(),
268            $isB2BEnabled ? ($customer->ape ?: '') : '',
269            $isB2BEnabled ? ($customer->siret ?: '') : ''
270        );
271    }
272
273    /**
274     * @param Order $order
275     *
276     * @return OrderShippingAddressForViewing
277     */
278    public function getOrderShippingAddress(Order $order): OrderShippingAddressForViewing
279    {
280        $address = new Address($order->id_address_delivery);
281        $country = new Country($address->id_country);
282        $stateName = '';
283
284        if ($address->id_state) {
285            $state = new State($address->id_state);
286
287            $stateName = $state->name;
288        }
289
290        $dni = Address::dniRequired($address->id_country) ? $address->dni : null;
291
292        return new OrderShippingAddressForViewing(
293            $address->id,
294            $address->firstname,
295            $address->lastname,
296            $address->company,
297            $address->address1,
298            $address->address2,
299            $stateName,
300            $address->city,
301            $country->name[(int) $order->getAssociatedLanguage()->getId()],
302            $address->postcode,
303            $address->phone,
304            $address->phone_mobile,
305            $address->vat_number,
306            $dni
307        );
308    }
309
310    /**
311     * @param Order $order
312     *
313     * @return OrderInvoiceAddressForViewing
314     */
315    private function getOrderInvoiceAddress(Order $order): OrderInvoiceAddressForViewing
316    {
317        $address = new Address($order->id_address_invoice);
318        $country = new Country($address->id_country);
319        $stateName = '';
320
321        if ($address->id_state) {
322            $state = new State($address->id_state);
323
324            $stateName = $state->name;
325        }
326
327        $dni = Address::dniRequired($address->id_country) ? $address->dni : null;
328
329        return new OrderInvoiceAddressForViewing(
330            $address->id,
331            $address->firstname,
332            $address->lastname,
333            $address->company,
334            $address->address1,
335            $address->address2,
336            $stateName,
337            $address->city,
338            $country->name[(int) $order->getAssociatedLanguage()->getId()],
339            $address->postcode,
340            $address->phone,
341            $address->phone_mobile,
342            $address->vat_number,
343            $dni
344        );
345    }
346
347    /**
348     * @param Order $order
349     *
350     * @return OrderHistoryForViewing
351     */
352    private function getOrderHistory(Order $order): OrderHistoryForViewing
353    {
354        $history = $order->getHistory($this->contextLanguageId);
355
356        $statuses = [];
357
358        foreach ($history as $item) {
359            $statuses[] = new OrderStatusForViewing(
360                (int) $item['id_order_history'],
361                (int) $item['id_order_state'],
362                $item['ostate_name'],
363                $item['color'],
364                new DateTimeImmutable($item['date_add']),
365                (bool) $item['send_email'],
366                $item['employee_firstname'],
367                $item['employee_lastname']
368            );
369        }
370
371        return new OrderHistoryForViewing(
372            $order->current_state,
373            $statuses
374        );
375    }
376
377    /**
378     * @param Order $order
379     *
380     * @return OrderDocumentsForViewing
381     *
382     * @throws LocalizationException
383     */
384    private function getOrderDocuments(Order $order): OrderDocumentsForViewing
385    {
386        $currency = new Currency($order->id_currency);
387        $documents = $order->getDocuments();
388
389        $documentsForViewing = [];
390
391        /** @var OrderInvoice|OrderSlip $document */
392        foreach ($documents as $document) {
393            $type = null;
394            $number = null;
395            $amount = null;
396            $numericAmount = null;
397            $amountMismatch = null;
398            $isAddPaymentAllowed = false;
399
400            if ($document instanceof OrderInvoice) {
401                $type = isset($document->is_delivery) ? OrderDocumentType::DELIVERY_SLIP : OrderDocumentType::INVOICE;
402            } elseif ($document instanceof OrderSlip) {
403                $type = OrderDocumentType::CREDIT_SLIP;
404            }
405
406            if (OrderDocumentType::INVOICE === $type) {
407                $number = $document->getInvoiceNumberFormatted(
408                    $this->contextLanguageId,
409                    $order->id_shop
410                );
411
412                if ($document->getRestPaid()) {
413                    $isAddPaymentAllowed = true;
414                }
415                $amount = $this->locale->formatPrice($document->total_paid_tax_incl, $currency->iso_code);
416                $numericAmount = $document->total_paid_tax_incl;
417
418                if ($document->getTotalPaid()) {
419                    if ($document->getRestPaid() > 0) {
420                        $amountMismatch = sprintf(
421                            '%s %s',
422                            $this->locale->formatPrice($document->getRestPaid(), $currency->iso_code),
423                            $this->translator->trans('not paid', [], 'Admin.Orderscustomers.Feature')
424                        );
425                    } elseif ($document->getRestPaid() < 0) {
426                        $amountMismatch = sprintf(
427                            '%s %s',
428                            $this->locale->formatPrice($document->getRestPaid(), $currency->iso_code),
429                            $this->translator->trans('overpaid', [], 'Admin.Orderscustomers.Feature')
430                        );
431                    }
432                }
433            } elseif (OrderDocumentType::DELIVERY_SLIP === $type) {
434                $conf = $this->configuration->get(
435                    'PS_DELIVERY_PREFIX',
436                    null,
437                    ShopConstraint::shop((int) $order->id_shop)
438                );
439                $number = sprintf(
440                    '%s%06d',
441                    $conf[$this->contextLanguageId] ?? '',
442                    $document->delivery_number
443                );
444                $amount = $this->locale->formatPrice(
445                    $document->total_paid_tax_incl,
446                    $currency->iso_code
447                );
448                $numericAmount = $document->total_paid_tax_incl;
449            } elseif (OrderDocumentType::CREDIT_SLIP === $type) {
450                $conf = $this->configuration->get('PS_CREDIT_SLIP_PREFIX');
451                $number = sprintf(
452                    '%s%06d',
453                    $conf[$this->contextLanguageId] ?? '',
454                    $document->id
455                );
456                $amount = $this->locale->formatPrice(
457                    $document->total_products_tax_incl + $document->total_shipping_tax_incl,
458                    $currency->iso_code
459                );
460                $numericAmount = $document->total_products_tax_incl + $document->total_shipping_tax_incl;
461            }
462
463            $documentsForViewing[] = new OrderDocumentForViewing(
464                $document->id,
465                $type,
466                new DateTimeImmutable($document->date_add),
467                $number,
468                $numericAmount,
469                $amount,
470                $amountMismatch,
471                $document instanceof OrderInvoice ? $document->note : null,
472                $isAddPaymentAllowed
473            );
474        }
475
476        $canGenerateInvoice = $this->configuration->get('PS_INVOICE') &&
477            count($order->getInvoicesCollection()) &&
478            $order->invoice_number;
479
480        $canGenerateDeliverySlip = (bool) $order->delivery_number;
481
482        return new OrderDocumentsForViewing(
483            $canGenerateInvoice,
484            $canGenerateDeliverySlip,
485            $documentsForViewing
486        );
487    }
488
489    /**
490     * @param Order $order
491     *
492     * @return OrderShippingForViewing
493     *
494     * @throws LocalizationException
495     */
496    private function getOrderShipping(Order $order): OrderShippingForViewing
497    {
498        $taxCalculationMethod = $this->getOrderTaxCalculationMethod($order);
499
500        $shipping = $order->getShipping();
501        $carriers = [];
502        $carrierModuleInfo = null;
503
504        $currency = new Currency($order->id_currency);
505        $carrier = new Carrier($order->id_carrier);
506        $carrierModuleInfo = null;
507
508        if ($carrier->is_module) {
509            $module = Module::getInstanceByName($carrier->external_module_name);
510            if (method_exists($module, 'displayInfoByCart')) {
511                $carrierModuleInfo = $module->displayInfoByCart($order->id_cart);
512            }
513        }
514
515        if (!$order->isVirtual()) {
516            foreach ($shipping as $item) {
517                if ($taxCalculationMethod == PS_TAX_INC) {
518                    $price = Tools::displayPrice($item['shipping_cost_tax_incl'], $currency);
519                } else {
520                    $price = Tools::displayPrice($item['shipping_cost_tax_excl'], $currency);
521                }
522
523                $trackingUrl = null;
524                $trackingNumber = $item['tracking_number'];
525
526                if ($item['url'] && $trackingNumber) {
527                    $trackingUrl = str_replace('@', $trackingNumber, $item['url']);
528                }
529
530                $weight = sprintf('%.3f %s', $item['weight'], $this->configuration->get('PS_WEIGHT_UNIT'));
531
532                $carriers[] = new OrderCarrierForViewing(
533                    (int) $item['id_order_carrier'],
534                    new DateTimeImmutable($item['date_add']),
535                    $item['carrier_name'],
536                    $weight,
537                    (int) $item['id_carrier'],
538                    $price,
539                    $trackingUrl,
540                    $trackingNumber,
541                    $item['can_edit']
542                );
543            }
544        }
545
546        return new OrderShippingForViewing(
547            $carriers,
548            (bool) $order->recyclable,
549            (bool) $order->gift,
550            $order->gift_message,
551            $carrierModuleInfo
552        );
553    }
554
555    /**
556     * @param Order $order
557     *
558     * @return OrderReturnsForViewing
559     */
560    private function getOrderReturns(Order $order): OrderReturnsForViewing
561    {
562        $returns = $order->getReturn();
563
564        if ($order->isVirtual()) {
565            return new OrderReturnsForViewing();
566        }
567
568        $orderReturns = [];
569
570        foreach ($returns as $orderReturn) {
571            $trackingUrl = null;
572            $trackingNumber = null;
573
574            if (isset($orderReturn['url'], $orderReturn['tracking_number'])) {
575                $trackingUrl = $orderReturn['url'];
576                $trackingNumber = $orderReturn['tracking_number'];
577            } elseif (isset($orderReturn['tracking_number'])) {
578                $trackingNumber = $orderReturn['tracking_number'];
579            }
580
581            $orderReturns[] = new OrderReturnForViewing(
582                (int) $orderReturn['id_order_return'],
583                isset($orderReturn['id_order_invoice']) ? (int) $orderReturn['id_order_invoice'] : 0,
584                isset($orderReturn['id_carrier']) ? (int) $orderReturn['id_carrier'] : 0,
585                new DateTimeImmutable($orderReturn['date_add']),
586                $orderReturn['type'],
587                $orderReturn['state_name'] ?? '',
588                $trackingUrl,
589                $trackingNumber
590            );
591        }
592
593        return new OrderReturnsForViewing($orderReturns);
594    }
595
596    /**
597     * @param Order $order
598     *
599     * @return OrderPaymentsForViewing
600     *
601     * @throws LocalizationException
602     */
603    private function getOrderPayments(Order $order): OrderPaymentsForViewing
604    {
605        $currency = new Currency($order->id_currency);
606        $payments = $order->getOrderPayments();
607
608        $currentState = $order->getCurrentOrderState();
609
610        $orderAmountToPay = null;
611        $orderAmountPaid = null;
612        $paymentMismatchOrders = [];
613
614        if (count($payments) > 0) {
615            $noPaymentMismatch = round($order->getOrdersTotalPaid(), 2) == round($order->getTotalPaid(), 2)
616                || ($currentState && $currentState->id == 6);
617
618            if (!$noPaymentMismatch) {
619                $orderAmountToPay = $this->locale->formatPrice($order->getOrdersTotalPaid(), $currency->iso_code);
620                $orderAmountPaid = $this->locale->formatPrice($order->getTotalPaid(), $currency->iso_code);
621
622                foreach ($order->getBrother() as $relatedOrder) {
623                    $paymentMismatchOrders[] = $relatedOrder->id;
624                }
625            }
626        }
627
628        $orderPayments = [];
629
630        /** @var OrderPayment $payment */
631        foreach ($order->getOrderPaymentCollection() as $payment) {
632            $currency = new Currency($payment->id_currency);
633            $invoice = $payment->getOrderInvoice($order->id);
634            $invoiceNumber = $invoice ?
635                $invoice->getInvoiceNumberFormatted($this->contextLanguageId, $order->id_shop) :
636                null;
637
638            $orderPayments[] = new OrderPaymentForViewing(
639                $payment->id,
640                new DateTimeImmutable($payment->date_add),
641                $payment->payment_method,
642                $payment->transaction_id,
643                $this->locale->formatPrice($payment->amount, $currency->iso_code),
644                $invoiceNumber,
645                $payment->card_number,
646                $payment->card_brand,
647                $payment->card_expiration,
648                $payment->card_holder
649            );
650        }
651
652        return new OrderPaymentsForViewing(
653            $orderPayments,
654            $orderAmountToPay,
655            $orderAmountPaid,
656            $paymentMismatchOrders
657        );
658    }
659
660    /**
661     * @param Order $order
662     *
663     * @return OrderMessagesForViewing
664     */
665    private function getOrderMessages(Order $order): OrderMessagesForViewing
666    {
667        $orderMessagesForOrderPage = $this->customerDataProvider->getCustomerMessages(
668            (int) $order->id_customer,
669            (int) $order->id
670        );
671
672        $messages = [];
673
674        foreach ($orderMessagesForOrderPage['messages'] as $orderMessage) {
675            $messageEmployeeId = (int) $orderMessage['id_employee'];
676            $isCurrentEmployeesMessage = (int) $this->context->employee->id === $messageEmployeeId;
677
678            $messages[] = new OrderMessageForViewing(
679                (int) $orderMessage['id_customer_message'],
680                $orderMessage['message'],
681                new OrderMessageDateForViewing(
682                    new DateTimeImmutable($orderMessage['date_add']),
683                    $this->context->language->date_format_full
684                ),
685                $messageEmployeeId,
686                $isCurrentEmployeesMessage,
687                $orderMessage['efirstname'],
688                $orderMessage['elastname'],
689                $orderMessage['cfirstname'],
690                $orderMessage['clastname'],
691                (bool) $orderMessage['private']
692            );
693        }
694
695        return new OrderMessagesForViewing($messages, $orderMessagesForOrderPage['total']);
696    }
697
698    /**
699     * @param Order $order
700     *
701     * @return OrderPricesForViewing
702     *
703     * @throws LocalizationException
704     */
705    private function getOrderPrices(Order $order): OrderPricesForViewing
706    {
707        $currency = new Currency($order->id_currency);
708        $customer = $order->getCustomer();
709
710        $isTaxExcluded = !$this->isTaxIncludedInOrder($order);
711
712        $shipping_refundable_tax_excl = $order->total_shipping_tax_excl;
713        $shipping_refundable_tax_incl = $order->total_shipping_tax_incl;
714
715        $slips = OrderSlip::getOrdersSlip($customer->id, $order->id);
716        foreach ($slips as $slip) {
717            $shipping_refundable_tax_excl -= $slip['total_shipping_tax_excl'];
718            $shipping_refundable_tax_incl -= $slip['total_shipping_tax_incl'];
719        }
720
721        if ($isTaxExcluded) {
722            $productsPrice = (float) $order->total_products;
723            $discountsAmount = (float) $order->total_discounts_tax_excl;
724            $wrappingPrice = (float) $order->total_wrapping_tax_excl;
725            $shippingPrice = (float) $order->total_shipping_tax_excl;
726            $shippingRefundable = max(0, $shipping_refundable_tax_excl);
727        } else {
728            $productsPrice = (float) $order->total_products_wt;
729            $discountsAmount = (float) $order->total_discounts_tax_incl;
730            $wrappingPrice = (float) $order->total_wrapping_tax_incl;
731            $shippingPrice = (float) $order->total_shipping_tax_incl;
732            $shippingRefundable = max(0, $shipping_refundable_tax_incl);
733        }
734        $totalAmount = (float) $order->total_paid_tax_incl;
735
736        $taxesAmount = $order->total_paid_tax_incl - $order->total_paid_tax_excl;
737
738        return new OrderPricesForViewing(
739            new DecimalNumber((string) $productsPrice),
740            new DecimalNumber((string) $discountsAmount),
741            new DecimalNumber((string) $wrappingPrice),
742            new DecimalNumber((string) $shippingPrice),
743            new DecimalNumber((string) $shippingRefundable),
744            new DecimalNumber((string) $taxesAmount),
745            new DecimalNumber((string) $totalAmount),
746            Tools::displayPrice($productsPrice, $currency),
747            Tools::displayPrice($discountsAmount, $currency),
748            Tools::displayPrice($wrappingPrice, $currency),
749            Tools::displayPrice($shippingPrice, $currency),
750            Tools::displayPrice($shippingRefundable, $currency),
751            Tools::displayPrice($taxesAmount, $currency),
752            Tools::displayPrice($totalAmount, $currency)
753        );
754    }
755
756    /**
757     * @param Order $order
758     *
759     * @return OrderDiscountsForViewing
760     *
761     * @throws LocalizationException
762     */
763    private function getOrderDiscounts(Order $order): OrderDiscountsForViewing
764    {
765        $isTaxIncluded = $this->isTaxIncludedInOrder($order);
766        $currency = new Currency($order->id_currency);
767        $discounts = $order->getCartRules();
768        $discountsForViewing = [];
769
770        foreach ($discounts as $discount) {
771            $discountAmount = $isTaxIncluded ? $discount['value'] : $discount['value_tax_excl'];
772            $discountsForViewing[] = new OrderDiscountForViewing(
773                (int) $discount['id_order_cart_rule'],
774                $discount['name'],
775                new DecimalNumber((string) $discountAmount),
776                Tools::displayPrice($discountAmount, $currency)
777            );
778        }
779
780        return new OrderDiscountsForViewing($discountsForViewing);
781    }
782
783    /**
784     * @param Order $order
785     *
786     * @return OrderSourcesForViewing
787     */
788    private function getOrderSources(Order $order): OrderSourcesForViewing
789    {
790        $sourcesData = ConnectionsSource::getOrderSources($order->id);
791        $sources = [];
792
793        foreach ($sourcesData as $sourceItem) {
794            $sources[] = new OrderSourceForViewing(
795                $sourceItem['http_referer'],
796                $sourceItem['request_uri'],
797                new DateTimeImmutable($sourceItem['date_add']),
798                $sourceItem['keywords']
799            );
800        }
801
802        return new OrderSourcesForViewing($sources);
803    }
804
805    /**
806     * @return LinkedOrdersForViewing
807     */
808    private function getLinkedOrders(Order $order): LinkedOrdersForViewing
809    {
810        $brothersData = $order->getBrother();
811        $brothers = [];
812        /** @var Order $brotherItem */
813        foreach ($brothersData as $brotherItem) {
814            $isTaxExcluded = !$this->isTaxIncludedInOrder($brotherItem);
815
816            $currency = new Currency($brotherItem->id_currency);
817
818            if ($isTaxExcluded) {
819                $totalAmount = $this->locale->formatPrice($brotherItem->total_paid_tax_excl, $currency->iso_code);
820            } else {
821                $totalAmount = $this->locale->formatPrice($brotherItem->total_paid_tax_incl, $currency->iso_code);
822            }
823
824            $orderState = new OrderState($brotherItem->current_state);
825
826            $brothers[] = new LinkedOrderForViewing(
827                $brotherItem->id,
828                $orderState->name[$this->context->language->getId()],
829                $totalAmount
830            );
831        }
832
833        return new LinkedOrdersForViewing($brothers);
834    }
835
836    /**
837     * @param OrderId $orderId
838     * @param string $productsOrder
839     *
840     * @return OrderProductsForViewing
841     *
842     * @throws OrderException
843     * @throws InvalidSortingException
844     */
845    private function getOrderProducts(OrderId $orderId, string $productsOrder): OrderProductsForViewing
846    {
847        return $this->getOrderProductsForViewingHandler->handle(
848            GetOrderProductsForViewing::all($orderId->getValue(), $productsOrder)
849        );
850    }
851
852    /**
853     * If there is no valid customer attached to the order, the customer must have been deleted
854     * from the database. We then create a fake customer object, using the invoice address data
855     * and cart language.
856     *
857     * @param Order $order Order object
858     * @param OrderInvoiceAddressForViewing $invoiceAddress Invoice address information
859     *
860     * @return Customer The created customer
861     */
862    private function buildFakeCustomerObject(Order $order, OrderInvoiceAddressForViewing $invoiceAddress): Customer
863    {
864        $cart = new Cart($order->id_cart);
865
866        $customer = new Customer();
867        $customer->firstname = $invoiceAddress->getFirstName();
868        $customer->lastname = $invoiceAddress->getLastName();
869        $customer->email = '';
870        $customer->id_lang = $cart->getAssociatedLanguage()->getId();
871        $customer->is_guest = true;
872
873        return $customer;
874    }
875}
876