Tutorial for Prestashop to decorate a controller – How to add paid and outstanding amount in order page

Tutorial for Prestashop to decorate a controller – How to add paid and outstanding amount in order page

Tutorial for Prestashop to decorate a controller

  • A better way to modify the controller is to decorate it. With such a method, we can extend the module with most of the original behaviour and customise the specific parts of the controller.

  • In this tutorial we are going to build up a module which adds paid and outstanding amounts in the Order page without modifying any src files directly. Shown as following:

  • Step 1, create a composer.json file in your module with the following code:

{

  "name"     : "yourname/yourmodule",  

  "autoload" : {

     "psr-4" : {"Namespace\\" : "src/"},

     "classmap": ["yourmodule.php"],

     "config": {"prepend-autoloader" : false},

     "type"  : "prestashop-module"

  }

}

Our example (modules/ordertotal/composer.json):

{

   "name": "genkiware/ordertotal",

   "license": "AFL-3.0",

   "autoload": {

       "psr-4": { "ordertotal\\": "src/" },

       "classmap": ["ordertotal.php"],

       "config": { "prepend-autoloader": false },

       "type": "prestashop-module"

   }

}

  • Step 2, we need to create a basic module first, which you may generate by this link https://validator.prestashop.com/generator and install it to your prestashop module. (just choose a random hook at the last step, you may delete the related code after installing it.)

  • Step 3, create a services.yml in modules/your-module/config/ file, with the following code:

  • Services:
  •  custom_controller:
  •    class: Namespace\Controller\Admin\DemoController
  •    decorates:
  • PrestaShopBundle\Controller\Admin\Improve\Design\CmsPageController
  •    arguments: ['@custom_controller.inner']

        

        Our example ( modules/ordertotal/config/services.yml ):

services:

 custom_controller:

   class: ordertotal\Controller\Admin\DemoController

   decorates: PrestaShopBundle\Controller\Admin\Sell\Order\OrderController

   arguments: ['@custom_controller.inner']

  • Step 4, add the following code to yourmodule.php

declare(strict_types=1);

// Needed for installing process

require_once __DIR__ . '/vendor/autoload.php';

class YourModule extends Module

{

Our example (modules/ordertotal/ordertotal.php):

declare(strict_types=1);

// Needed for installing process

require_once __DIR__ . '/vendor/autoload.php';

if (!defined('_PS_VERSION_')) {

   exit;

}

class Ordertotal extends Module

{

After creating those files, then run

$ composer dumpautoload

in your module’s terminal.

  • Step 5, create a demo controller in src/Controller/Admin/DemoController.php, which extends the original controller. Here is the “basic” version of code:

declare(strict_types=1);

namespace Namespace\Controller\Admin;

// The path that you are going to use

use ………………

use ………………

use ………………

...

use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;

use Symfony\Component\HttpFoundation\Request;

class DemoController extends FrameworkBundleAdminController

{

   private $decoratedController;

   public function __construct(TheOriginalController $decoratedController)

   {

       $this->decoratedController = $decoratedController;

   }

   public function indexAction(ABC $temp1, DEF $temp2,.....)

   {

       return $this->decoratedController->indexAction($temp1, $temp2, ………………);

   }

}

So the parameters will be passed to the function in the new controller instead of the original one. We can call and return the original function by using $this->decoratedController->. It means that nothing has changed. If we want modification, just add the code to the function in the new controller (with or without calling $this-decoratedController, depending on your objective).

        

        Our example:

<?php

declare(strict_types=1);

namespace ordertotal\Controller\Admin;

use PrestaShopLogger;

use PrestaShopBundle\Controller\Admin\Sell\Order\OrderController;

use PrestaShopBundle\Controller\Admin\Sell\Order\ActionsBarButtonsCollection;

use Currency;

use Exception;

use InvalidArgumentException;

use PrestaShop\Decimal\Number;

use PrestaShop\PrestaShop\Core\Domain\Cart\Query\GetCartForOrderCreation;

use PrestaShop\PrestaShop\Core\Domain\CartRule\Exception\InvalidCartRuleDiscountValueException;

use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Command\AddOrderCustomerMessageCommand;

use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Exception\CannotSendEmailException;

use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Exception\CustomerMessageConstraintException;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\AddCartRuleToOrderCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\AddOrderFromBackOfficeCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\BulkChangeOrderStatusCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderCurrencyCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderDeliveryAddressCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderInvoiceAddressCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\DeleteCartRuleFromOrderCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\DuplicateOrderCartCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\ResendOrderEmailCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\SendProcessOrderEmailCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\UpdateOrderShippingDetailsCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Command\UpdateOrderStatusCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\CannotEditDeliveredOrderProductException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\CannotFindProductInOrderException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\ChangeOrderStatusException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\DuplicateProductInOrderException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\DuplicateProductInOrderInvoiceException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\InvalidAmountException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\InvalidCancelProductException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\InvalidOrderStateException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\InvalidProductQuantityException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\NegativePaymentAmountException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderConstraintException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderEmailSendException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderNotFoundException;

use PrestaShop\PrestaShop\Core\Domain\Order\Exception\TransistEmailSendingException;

use PrestaShop\PrestaShop\Core\Domain\Order\Invoice\Command\GenerateInvoiceCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Invoice\Command\UpdateInvoiceNoteCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\OrderConstraints;

use PrestaShop\PrestaShop\Core\Domain\Order\Payment\Command\AddPaymentCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Product\Command\AddProductToOrderCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Product\Command\DeleteProductFromOrderCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Product\Command\UpdateProductInOrderCommand;

use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderForViewing;

use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderPreview;

use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderForViewing;

use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreview;

use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderProductForViewing;

use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId;

use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductOutOfStockException;

use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductSearchEmptyPhraseException;

use PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts;

use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\FoundProduct;

use PrestaShop\PrestaShop\Core\Domain\ValueObject\QuerySorting;

use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface;

use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\OrderGridDefinitionFactory;

use PrestaShop\PrestaShop\Core\Multistore\MultistoreContextCheckerInterface;

use PrestaShop\PrestaShop\Core\Order\OrderSiblingProviderInterface;

use PrestaShop\PrestaShop\Core\Search\Filters\OrderFilters;

use PrestaShopBundle\Component\CsvResponse;

use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;

use PrestaShopBundle\Exception\InvalidModuleException;

use PrestaShopBundle\Form\Admin\Sell\Customer\PrivateNoteType;

use PrestaShopBundle\Form\Admin\Sell\Order\AddOrderCartRuleType;

use PrestaShopBundle\Form\Admin\Sell\Order\AddProductRowType;

use PrestaShopBundle\Form\Admin\Sell\Order\CartSummaryType;

use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrderAddressType;

use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrderCurrencyType;

use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrdersStatusType;

use PrestaShopBundle\Form\Admin\Sell\Order\EditProductRowType;

use PrestaShopBundle\Form\Admin\Sell\Order\OrderMessageType;

use PrestaShopBundle\Form\Admin\Sell\Order\OrderPaymentType;

use PrestaShopBundle\Form\Admin\Sell\Order\UpdateOrderShippingType;

use PrestaShopBundle\Form\Admin\Sell\Order\UpdateOrderStatusType;

use PrestaShopBundle\Security\Annotation\AdminSecurity;

use PrestaShopBundle\Security\Annotation\DemoRestricted;

use PrestaShopBundle\Service\Grid\ResponseBuilder;

use Symfony\Component\Filesystem\Filesystem;

use Symfony\Component\HttpFoundation\BinaryFileResponse;

use Symfony\Component\HttpFoundation\File\File;

use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Component\HttpFoundation\RedirectResponse;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

class DemoController extends FrameworkBundleAdminController

{

   const DEFAULT_PRODUCTS_NUMBER = 8;

   const PRODUCTS_PAGINATION_OPTIONS = [8, 20, 50, 100];

   // used for decorating controller

   private $decoratedController;

   public function __construct(OrderController $decoratedController)

   {

       $this->decoratedController = $decoratedController;

   }

   public function indexAction(Request $request, OrderFilters $filters)

   {

       return $this->decoratedController->indexAction($request, $filters);

   }

   public function placeAction(Request $request)

   {

       return $this->decoratedController->placeAction($request);

   }

   public function createAction(Request $request)

   {

       return $this->decoratedController->createAction($request);

   }

   public function searchAction(Request $request)

   {

       return $this->decoratedController->searchAction($request);

   }

   public function generateInvoicePdfAction($orderId)

   {

       return $this->decoratedController->generateInvoicePdfAction($orderId);

   }

   public function generateDeliverySlipPdfAction($orderId)

   {

       return $this->decoratedController->generateDeliverySlipPdfAction($orderId);

   }

   public function changeOrdersStatusAction(Request $request)

   {

       return $this->decoratedController->changeOrdersStatusAction($request);

   }

   public function exportAction(OrderFilters $filters)

   {

       return $this->decoratedController->exportAction($filters);

   }

   public function viewAction(int $orderId, Request $request): Response

   {

       try {

           /** @var OrderForViewing $orderForViewing */

           $orderForViewing = $this->getQueryBus()->handle(new GetOrderForViewing($orderId, QuerySorting::DESC));

       } catch (OrderException $e) {

           $this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages($e)));

           return $this->redirectToRoute('admin_orders_index');

       }

       $formFactory = $this->get('form.factory');

       $updateOrderStatusForm = $formFactory->createNamed(

           'update_order_status',

           UpdateOrderStatusType::class, [

               'new_order_status_id' => $orderForViewing->getHistory()->getCurrentOrderStatusId(),

           ]

       );

       $updateOrderStatusActionBarForm = $formFactory->createNamed(

           'update_order_status_action_bar',

           UpdateOrderStatusType::class, [

               'new_order_status_id' => $orderForViewing->getHistory()->getCurrentOrderStatusId(),

           ]

       );

       $addOrderCartRuleForm = $this->createForm(AddOrderCartRuleType::class, [], [

           'order_id' => $orderId,

       ]);

       $addOrderPaymentForm = $this->createForm(OrderPaymentType::class, [

           'id_currency' => $orderForViewing->getCurrencyId(),

       ], [

           'id_order' => $orderId,

       ]);

       $orderMessageForm = $this->createForm(OrderMessageType::class, [

           'lang_id' => $orderForViewing->getCustomer()->getLanguageId(),

       ], [

           'action' => $this->generateUrl('admin_orders_send_message', ['orderId' => $orderId]),

       ]);

       $orderMessageForm->handleRequest($request);

       $changeOrderCurrencyForm = $this->createForm(ChangeOrderCurrencyType::class, [], [

           'current_currency_id' => $orderForViewing->getCurrencyId(),

       ]);

       $changeOrderAddressForm = null;

       $privateNoteForm = null;

       if (null !== $orderForViewing->getCustomer() && $orderForViewing->getCustomer()->getId() !== 0) {

           $changeOrderAddressForm = $this->createForm(ChangeOrderAddressType::class, [], [

               'customer_id' => $orderForViewing->getCustomer()->getId(),

           ]);

           $privateNoteForm = $this->createForm(PrivateNoteType::class, [

               'note' => $orderForViewing->getCustomer()->getPrivateNote(),

           ]);

       }

       $updateOrderShippingForm = $this->createForm(UpdateOrderShippingType::class, [

           'new_carrier_id' => $orderForViewing->getCarrierId(),

       ], [

           'order_id' => $orderId,

       ]);

       $currencyDataProvider = $this->container->get('prestashop.adapter.data_provider.currency');

       $orderCurrency = $currencyDataProvider->getCurrencyById($orderForViewing->getCurrencyId());

       $addProductRowForm = $this->createForm(AddProductRowType::class, [], [

           'order_id' => $orderId,

           'currency_id' => $orderForViewing->getCurrencyId(),

           'symbol' => $orderCurrency->symbol,

       ]);

       $editProductRowForm = $this->createForm(EditProductRowType::class, [], [

           'order_id' => $orderId,

           'symbol' => $orderCurrency->symbol,

       ]);

       $formBuilder = $this->get('prestashop.core.form.identifiable_object.builder.cancel_product_form_builder');

       $backOfficeOrderButtons = new ActionsBarButtonsCollection();

       try {

           $this->dispatchHook(

               'actionGetAdminOrderButtons', [

               'controller' => $this,

               'id_order' => $orderId,

               'actions_bar_buttons_collection' => $backOfficeOrderButtons,

           ]);

           $cancelProductForm = $formBuilder->getFormFor($orderId);

       } catch (Exception $e) {

           $this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages($e)));

           return $this->redirectToRoute('admin_orders_index');

       }

       $this->handleOutOfStockProduct($orderForViewing);

       $merchandiseReturnEnabled = (bool) $this->decoratedController->configuration->get('PS_ORDER_RETURN');

       /** @var OrderSiblingProviderInterface $orderSiblingProvider */

       $orderSiblingProvider = $this->get('prestashop.adapter.order.order_sibling_provider');

       $paginationNum = (int) $this->decoratedController->configuration->get('PS_ORDER_PRODUCTS_NB_PER_PAGE', self::DEFAULT_PRODUCTS_NUMBER);

       $paginationNumOptions = self::PRODUCTS_PAGINATION_OPTIONS;

       if (!in_array($paginationNum, $paginationNumOptions)) {

           $paginationNumOptions[] = $paginationNum;

       }

       sort($paginationNumOptions);

       $metatitle = sprintf(

           '%s %s %s',

           $this->trans('Orders', 'Admin.Orderscustomers.Feature'),

           $this->decoratedController->configuration->get('PS_NAVIGATION_PIPE', '>'),

           $this->trans(

               'Order %reference% from %firstname% %lastname%',

               'Admin.Orderscustomers.Feature',

               [

                   '%reference%' => $orderForViewing->getReference(),

                   '%firstname%' => $orderForViewing->getCustomer()->getFirstName(),

                   '%lastname%' => $orderForViewing->getCustomer()->getLastName(),

               ]

           )

       );

       // start - edited

       $paidAmount = 0;

       $balanceAmount = 0;

       $amountToPay = $orderForViewing->getPrices()->getTotalAmountFormatted();

       $amountToPay = floatval(preg_replace('/[^\d\.]+/', '', $amountToPay));

       $paymentArrayAmt = array_values((array)$orderForViewing->getPayments());

 

       foreach($paymentArrayAmt[0] as $key){

           $originvalue = $key->getAmount();

           // is negative number

           $neg = strpos((string)$originvalue, '-') !== false;

           // remove everything except numbers and dot "."

           $originvalue = preg_replace("/[^0-9\.]/", "", $originvalue);

           // Set negative number

           if( $neg ) {

               $originvalue = '-' . $originvalue;

           }

           //convert string to float

           $originvalue = (float) $originvalue;

           // sum all order payment amount

           $paidAmount += $originvalue;              

       }

       // calculate from Total Amount substract Paid Amount

       $balanceAmount = $amountToPay - $paidAmount;

       // verify null value if true store default value 0 and set the price format

       if (empty($balanceAmount)) {

           $balanceAmount = $this->getContextLocale()->formatPrice(0, $orderCurrency->iso_code);

       }else{

           $balanceAmount = $this->getContextLocale()->formatPrice($balanceAmount, $orderCurrency->iso_code);

       }

       if (empty($paidAmount)) {

           $paidAmount = $this->getContextLocale()->formatPrice(0, $orderCurrency->iso_code);

       } else {

           $paidAmount = $this->getContextLocale()->formatPrice($paidAmount, $orderCurrency->iso_code);

       }

       // end - edited

     

       // start - edited - new render

        return $this->render('@Modules/ordertotal/views/templates/admin/view.html.twig', [

       // end - edited

         

           // start - edited

           'outstandingAmt' => $balanceAmount,

           'orderPaymentPaidAmt' => $paidAmount,

           // end - edited

           'showContentHeader' => true,

           'enableSidebar' => true,

           'orderCurrency' => $orderCurrency,

           'meta_title' => $metatitle,

           'help_link' => $this->generateSidebarLink($request->attributes->get('_legacy_controller')),

           'orderForViewing' => $orderForViewing,

           'addOrderCartRuleForm' => $addOrderCartRuleForm->createView(),

           'updateOrderStatusForm' => $updateOrderStatusForm->createView(),

           'updateOrderStatusActionBarForm' => $updateOrderStatusActionBarForm->createView(),

           'addOrderPaymentForm' => $addOrderPaymentForm->createView(),

           'changeOrderCurrencyForm' => $changeOrderCurrencyForm->createView(),

           'privateNoteForm' => $privateNoteForm ? $privateNoteForm->createView() : null,

           'updateOrderShippingForm' => $updateOrderShippingForm->createView(),

           'cancelProductForm' => $cancelProductForm->createView(),

           'invoiceManagementIsEnabled' => $orderForViewing->isInvoiceManagementIsEnabled(),

           'changeOrderAddressForm' => $changeOrderAddressForm ? $changeOrderAddressForm->createView() : null,

           'orderMessageForm' => $orderMessageForm->createView(),

           'addProductRowForm' => $addProductRowForm->createView(),

           'editProductRowForm' => $editProductRowForm->createView(),

           'backOfficeOrderButtons' => $backOfficeOrderButtons,

           'merchandiseReturnEnabled' => $merchandiseReturnEnabled,

           'priceSpecification' => $this->getContextLocale()->getPriceSpecification($orderCurrency->iso_code)->toArray(),

           'previousOrderId' => $orderSiblingProvider->getPreviousOrderId($orderId),

           'nextOrderId' => $orderSiblingProvider->getNextOrderId($orderId),

           'paginationNum' => $paginationNum,

           'paginationNumOptions' => $paginationNumOptions,

           'isAvailableQuantityDisplayed' => $this->decoratedController->configuration->getBoolean('PS_STOCK_MANAGEMENT'),

       ]);

   }

   public function partialRefundAction(int $orderId, Request $request)

   {

       return $this->decoratedController->partialRefundAction($orderId, $request);

   }

   public function standardRefundAction(int $orderId, Request $request)

   {

       return $this->decoratedController->standardRefundAction($orderId, $request);

   }

   public function returnProductAction(int $orderId, Request $request)

   {

       return $this->decoratedController->returnProductAction($orderId, $request);

   }

   // since a private function called by function viewaction() and we need to modify the viewaciton().

   private function handleOutOfStockProduct(OrderForViewing $orderForViewing)

   {

       $isStockManagementEnabled = $this->decoratedController->configuration->getBoolean('PS_STOCK_MANAGEMENT');

       if (!$isStockManagementEnabled || $orderForViewing->isDelivered() || $orderForViewing->isShipped()) {

           return;

       }

       foreach ($orderForViewing->getProducts()->getProducts() as $product) {

           if ($product->getAvailableQuantity() <= 0) {

               $this->addFlash(

                   'warning',

                   $this->trans('This product is out of stock:', 'Admin.Orderscustomers.Notification') . ' ' . $product->getName()

               );

           }

       }

   }

   public function addProductAction(int $orderId, Request $request): Response

   {

       return $this->decoratedController->addProductAction($orderId, $request);

   }

   public function getProductPricesAction(int $orderId): Response

   {

       return $this->decoratedController->getProductPricesAction($orderId);

   }

   public function getInvoicesAction(int $orderId)

   {

       return $this->decoratedController->getInvoicesAction($orderId);

   }

   public function getDocumentsAction(int $orderId)

   {

       return $this->decoratedController->getDocumentsAction($orderId);

   }

   public function getShippingAction(int $orderId)

   {

       return $this->decoratedController->getShippingAction($orderId);

   }

   public function updateShippingAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->updateShippingAction($orderId, $request);

   }

   public function removeCartRuleAction(int $orderId, int $orderCartRuleId): RedirectResponse

   {

       return $this->decoratedController->removeCartRuleAction($orderId, $orderCartRuleId);

   }

   public function updateInvoiceNoteAction(int $orderId, int $orderInvoiceId, Request $request): RedirectResponse

   {

       return $this->decoratedController->updateInvoiceNoteAction($orderId, $orderInvoiceId, $request);

   }

   public function updateProductAction(int $orderId, int $orderDetailId, Request $request): Response

   {

       return $this->decoratedController->updateProductAction($orderId, $orderDetailId, $request);

   }

   public function addCartRuleAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->addCartRuleAction($orderId, $request);

   }

   public function updateStatusAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->updateStatusAction($orderId, $request);

   }

   public function updateStatusFromListAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->updateStatusFromListAction($orderId, $request);

   }

   public function addPaymentAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->addPaymentAction($orderId, $request);

   }

   public function previewAction(int $orderId): JsonResponse

   {

       return $this->decoratedController->previewAction($orderId);

   }

   public function duplicateOrderCartAction(int $orderId)

   {

       return $this->decoratedController->duplicateOrderCartAction($orderId);

   }

   public function sendMessageAction(Request $request, int $orderId): Response

   {

       return $this->decoratedController->sendMessageAction($request, $orderId);

   }

   public function changeCustomerAddressAction(Request $request): RedirectResponse

   {

       return $this->decoratedController->changeCustomerAddressAction($request);

   }

   public function changeCurrencyAction(int $orderId, Request $request): RedirectResponse

   {

       return $this->decoratedController->changeCurrencyAction($orderId, $request);

   }

   public function resendEmailAction(int $orderId, int $orderStatusId, int $orderHistoryId): RedirectResponse

   {

       return $this->decoratedController->resendEmailAction($orderId, $orderStatusId, $orderHistoryId);

   }

   public function deleteProductAction(int $orderId, int $orderDetailId): JsonResponse

   {

       return $this->decoratedController->deleteProductAction($orderId, $orderDetailId);

   }

   public function getDiscountsAction(int $orderId): Response

   {

       return $this->decoratedController->getDiscountsAction($orderId);

   }

   public function getPricesAction(int $orderId): JsonResponse

   {

       return $this->decoratedController->getPricesAction($orderId);

   }

   public function getPaymentsAction(int $orderId): Response

   {

       return $this->decoratedController->getPaymentsAction($orderId);

   }

   public function getProductsListAction(int $orderId): Response

   {

       return $this->decoratedController->getProductsListAction($orderId);

   }

   public function generateInvoiceAction(int $orderId): RedirectResponse

   {

       return $this->decoratedController->generateInvoiceAction($orderId);

   }

   public function sendProcessOrderEmailAction(Request $request): JsonResponse

   {

       return $this->decoratedController->sendProcessOrderEmailAction($request);

   }

   public function cancellationAction(int $orderId, Request $request)

   {

       return $this->decoratedController->cancellationAction($orderId, $request);

   }

   public function configureProductPaginationAction(Request $request): JsonResponse

   {

       return $this->decoratedController->configureProductPaginationAction($request);

   }

   public function displayCustomizationImageAction(int $orderId, string $value)

   {

       return $this->decoratedController->displayCustomizationImageAction($orderId, $value);

   }

   public function searchProductsAction(Request $request): JsonResponse

   {

       return $this->decoratedController->searchProductsAction($request);

   }

   private function handleOrderStatusUpdate(int $orderId, int $orderStatusId): void

   {

       $this->decoratedController->handleOrderStatusUpdate($orderId, $orderStatusId);

   }

   // private funciton

   private function getErrorMessages(Exception $e)

   {

       $refundableQuantity = 0;

       if ($e instanceof InvalidCancelProductException) {

           $refundableQuantity = $e->getRefundableQuantity();

       }

       $orderInvoiceNumber = '#unknown';

       if ($e instanceof DuplicateProductInOrderInvoiceException) {

           $orderInvoiceNumber = $e->getOrderInvoiceNumber();

       }

       return [

           CannotEditDeliveredOrderProductException::class => $this->trans('You cannot edit the cart once the order delivered.', 'Admin.Orderscustomers.Notification'),

           OrderNotFoundException::class => $e instanceof OrderNotFoundException ?

               $this->trans(

                   'Order #%d cannot be loaded.',

                   'Admin.Orderscustomers.Notification',

                   ['#%d' => $e->getOrderId()->getValue()]

               ) : '',

           OrderEmailSendException::class => $this->trans(

               'An error occurred while sending the e-mail to the customer.',

               'Admin.Orderscustomers.Notification'

           ),

           OrderException::class => $this->trans(

               $e->getMessage(),

               'Admin.Orderscustomers.Notification'

           ),

           InvalidAmountException::class => $this->trans(

               'Only numbers and decimal points (".") are allowed in the amount fields, e.g. 10.50 or 1050.',

               'Admin.Orderscustomers.Notification'

           ),

           InvalidCartRuleDiscountValueException::class => [

               InvalidCartRuleDiscountValueException::INVALID_MIN_PERCENT => $this->trans(

                   'Percent value must be greater than 0.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCartRuleDiscountValueException::INVALID_MAX_PERCENT => $this->trans(

                   'Percent value cannot exceed 100.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCartRuleDiscountValueException::INVALID_MIN_AMOUNT => $this->trans(

                   'Amount value must be greater than 0.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCartRuleDiscountValueException::INVALID_MAX_AMOUNT => $this->trans(

                   'Discount value cannot exceed the total price of this order.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCartRuleDiscountValueException::INVALID_FREE_SHIPPING => $this->trans(

                   'Shipping discount value cannot exceed the total price of this order.',

                   'Admin.Orderscustomers.Notification'

               ),

           ],

           InvalidCancelProductException::class => [

               InvalidCancelProductException::INVALID_QUANTITY => $this->trans(

                   'Positive product quantity is required.',

                   'Admin.Notifications.Error'

               ),

               InvalidCancelProductException::QUANTITY_TOO_HIGH => $this->trans(

                   'Please enter a maximum quantity of [1].',

                   'Admin.Orderscustomers.Notification',

                   ['[1]' => $refundableQuantity]

               ),

               InvalidCancelProductException::NO_REFUNDS => $this->trans(

                   'Please select at least one product.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCancelProductException::INVALID_AMOUNT => $this->trans(

                   'Please enter a positive amount.',

                   'Admin.Orderscustomers.Notification'

               ),

               InvalidCancelProductException::NO_GENERATION => $this->trans(

                   'Please generate at least one credit slip or voucher.',

                   'Admin.Orderscustomers.Notification'

               ),

           ],

           InvalidModuleException::class => $this->trans(

               'You must choose a payment module to create the order.',

               'Admin.Orderscustomers.Notification'

           ),

           ProductOutOfStockException::class => $this->trans(

               'There are not enough products in stock.',

               'Admin.Catalog.Notification'

           ),

           NegativePaymentAmountException::class => $this->trans(

               'Invalid value: the payment must be a positive amount.',

               'Admin.Notifications.Error'

           ),

           InvalidOrderStateException::class => [

               InvalidOrderStateException::ALREADY_PAID => $this->trans(

                   'Invalid action: this order has already been paid.',

                   'Admin.Notifications.Error'

               ),

               InvalidOrderStateException::DELIVERY_NOT_FOUND => $this->trans(

                   'Invalid action: this order has not been delivered.',

                   'Admin.Notifications.Error'

               ),

               InvalidOrderStateException::UNEXPECTED_DELIVERY => $this->trans(

                   'Invalid action: this order has already been delivered.',

                   'Admin.Notifications.Error'

               ),

               InvalidOrderStateException::NOT_PAID => $this->trans(

                   'Invalid action: this order has not been paid.',

                   'Admin.Notifications.Error'

               ),

               InvalidOrderStateException::INVALID_ID => $this->trans(

                   'You must choose an order status to create the order.',

                   'Admin.Orderscustomers.Notification'

               ),

           ],

           OrderConstraintException::class => [

               OrderConstraintException::INVALID_CUSTOMER_MESSAGE => $this->trans(

                   'The order message given is invalid.',

                   'Admin.Orderscustomers.Notification'

               ),

           ],

           InvalidProductQuantityException::class => $this->trans(

               'Positive product quantity is required.',

               'Admin.Notifications.Error'

           ),

           DuplicateProductInOrderException::class => $this->trans(

               'This product is already in your order, please edit the quantity instead.',

               'Admin.Notifications.Error'

           ),

           DuplicateProductInOrderInvoiceException::class => $this->trans(

               'This product is already in the invoice [1], please edit the quantity instead.',

               'Admin.Notifications.Error',

               ['[1]' => $orderInvoiceNumber]

           ),

           CannotFindProductInOrderException::class => $this->trans(

               'You cannot edit the price of a product that no longer exists in your catalog.',

               'Admin.Notifications.Error'

           ),

       ];

   }

   // private function

   private function getPaymentErrorMessages(Exception $e)

   {

       return array_merge($this->getErrorMessages($e), [

           InvalidArgumentException::class => $this->trans(

               'Only numbers and decimal points (".") are allowed in the amount fields of the payment block, e.g. 10.50 or 1050.',

               'Admin.Orderscustomers.Notification'

           ),

           OrderConstraintException::class => [

               OrderConstraintException::INVALID_PAYMENT_METHOD => sprintf(

                   '%s %s %s',

                   $this->trans(

                       'The selected payment method is invalid.',

                       'Admin.Orderscustomers.Notification'

                   ),

                   $this->trans(

                       'Invalid characters:',

                       'Admin.Notifications.Info'

                   ),

                   AddPaymentCommand::INVALID_CHARACTERS_NAME

               ),

           ],

       ]);

   }

   // private function

   private function handleChangeOrderStatusException(ChangeOrderStatusException $e)

   {

       $orderIds = array_merge(

           $e->getOrdersWithFailedToUpdateStatus(),

           $e->getOrdersWithFailedToSendEmail()

       );

       /** @var OrderId $orderId */

       foreach ($orderIds as $orderId) {

           $this->addFlash(

               'error',

               $this->trans(

                   'An error occurred while changing the status for order #%d, or we were unable to send an email to the customer.',

                   'Admin.Orderscustomers.Notification',

                   ['#%d' => $orderId->getValue()]

               )

           );

       }

       foreach ($e->getOrdersWithAssignedStatus() as $orderId) {

           $this->addFlash(

               'error',

               $this->trans(

                   'Order #%d has already been assigned this status.',

                   'Admin.Orderscustomers.Notification',

                   ['#%d' => $orderId->getValue()]

               )

           );

       }

   }

   // private function

   private function getCustomerMessageErrorMapping(Exception $exception): array

   {

       return [

           OrderNotFoundException::class => $exception instanceof OrderNotFoundException ?

               $this->trans(

                   'Order #%d cannot be loaded.',

                   'Admin.Orderscustomers.Notification',

                   ['#%d' => $exception->getOrderId()->getValue()]

               ) : '',

           CustomerMessageConstraintException::class => [

               CustomerMessageConstraintException::MISSING_MESSAGE => $this->trans(

                       'The %s field is not valid',

                       'Admin.Notifications.Error',

                       [

                           sprintf('"%s"', $this->trans('Message', 'Admin.Global')),

                       ]

                   ),

               CustomerMessageConstraintException::INVALID_MESSAGE => $this->trans(

                       'The %s field is not valid',

                       'Admin.Notifications.Error',

                       [

                           sprintf('"%s"', $this->trans('Message', 'Admin.Global')),

                       ]

                   ),

           ],

       ];

   }

}

  • Step 6, since we need to modify the order page layout, we need to render new template files.

Our example:

(modules/ordertotal/views/templates/admin/view.html.twig)

{#**

* Copy from PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/view.html.twig

*#}

{% set use_regular_h1_structure = false %}

{% set layoutTitle %}

 {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/header.html.twig' %}

{% endset %}

{% extends '@PrestaShop/Admin/layout.html.twig' %}

{% block content %}

 <div id="order-view-page"

      data-order-title="{{ 'Order'|trans({}, 'Admin.Global') }} #{{ orderForViewing.id }} {{ orderForViewing.reference }}">

   <div class="row d-print-none">

     {% set displayAdminOrderTopHookContent = renderhook('displayAdminOrderTop', {'id_order': orderForViewing.id}) %}

     {% if displayAdminOrderTopHookContent != '' %}

       <div class="col-md-12">

         {{ displayAdminOrderTopHookContent|raw }}

       </div>

     {% endif %}

     <div class="order-actions col-md-12">

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/order_actions.html.twig' %}

     </div>

   </div>

   <div class="row d-none d-print-block mb-4">

     <div class="col-md-12">

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/print_order_statistics.html.twig' %}

     </div>

   </div>

   <div class="row d-none mb-4">

     <div class="col-12" id="orderProductsModificationPosition"></div>

   </div>

   <div class="row d-none d-print-block mb-4">

     <div class="col-md-12">

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/print_title.html.twig' %}

     </div>

   </div>

   <div class="product-row row">

     <div class="col-md-4 left-column">

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/customer.html.twig' %}

       {{ renderhook('displayAdminOrderSide', {'id_order': orderForViewing.id}) }}

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/messages.html.twig' %}

       {{ renderhook('displayAdminOrderSideBottom', {'id_order': orderForViewing.id}) }}

     </div>

     <div class="col-md-8 d-print-block right-column">

       <div id="orderProductsOriginalPosition">

       {# start - edit - rending a different products.html.twig in this module for outstanding and unpaid amt#}

         {# {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/products.html.twig' %} #}

         {% include '@Modules/ordertotal/views/templates/admin/products.html.twig' %}

       

       {# end - edit #}

       </div>

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/details.html.twig' %}

       {{ renderhook('displayAdminOrderMain', {'id_order': orderForViewing.id}) }}

       {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/payments.html.twig' %}

       {{ renderhook('displayAdminOrderMainBottom', {'id_order': orderForViewing.id}) }}

     </div>

   </div>

   {% if orderForViewing.sources.sources is not empty %}

     <div class="product-row row">

       <div class="col-md-12 left-column">

         {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/sources.html.twig' %}

       </div>

     </div>

   {% endif %}

   {% if orderForViewing.linkedOrders.linkedOrders is not empty %}

     {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/linked_orders.html.twig' %}

   {% endif %}

   {{ renderhook('displayAdminOrder', {'id_order': orderForViewing.id}) }}

   {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/Modal/add_order_discount_modal.html.twig' %}

   {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/Modal/update_shipping_modal.html.twig' %}

   {% if orderForViewing.customer is not null and orderForViewing.customer.id != 0 %}

     {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/Modal/update_customer_address_modal.html.twig' %}

   {% endif %}

   {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/Modal/view_all_messages_modal.html.twig' %}

   {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/Modal/view_product_pack_modal.html.twig' %}

 </div>

{% endblock %}

{% block javascripts %}

 {{ parent() }}

 <script src="{{ asset('themes/new-theme/public/order_view.bundle.js') }}"></script>

{% endblock %}

{% set js_translatable = {

 "The product was successfully added.": 'The product was successfully added.'|trans({}, 'Admin.Notifications.Success'),

 "The product was successfully removed.": 'The product was successfully removed.'|trans({}, 'Admin.Notifications.Success'),

 "[1] products were successfully added.": '[1] products were successfully added.'|trans({}, 'Admin.Notifications.Success'),

 "[1] products were successfully removed.": '[1] products were successfully removed.'|trans({}, 'Admin.Notifications.Success'),

}|merge(js_translatable)

%}

( modules/ordertotal/views/templates/admin/products.html.twig )

{#**

* Copy of PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig

*#}

{% set isColumnLocationDisplayed = false %}

{% set isColumnRefundedDisplayed = false %}

{% for product in orderForViewing.products.products|slice(0, paginationNum) %}

 {% if product.location is not empty %}

   {% set isColumnLocationDisplayed = true %}

 {% endif %}

 {% if product.quantityRefunded > 0 %}

   {% set isColumnRefundedDisplayed = true %}

 {% endif %}

{% endfor %}

<div class="card" id="orderProductsPanel">

 <div class="card-header">

   <h3 class="card-header-title">

     {{ 'Products'|trans({}, 'Admin.Global') }} (<span id="orderProductsPanelCount">{{ orderForViewing.products.products|length }}</span>)

   </h3>

 </div>

 <div class="card-body">

   <div class="spinner-order-products-container" id="orderProductsLoading">

     <div class="spinner spinner-primary"></div>

   </div>

   {% set formOptions = {

     'attr': {

       'data-order-id': orderForViewing.id,

       'data-is-delivered': orderForViewing.isDelivered,

       'data-is-tax-included': orderForViewing.isTaxIncluded,

       'data-discounts-amount': orderForViewing.prices.discountsAmountRaw,

       'data-price-specification': priceSpecification|json_encode|replace("'", "&#39;")|raw

     }

   } %}

   {{ form_start(cancelProductForm, formOptions) }}

   <table class="table" id="orderProductsTable" data-currency-precision="{{ orderCurrency.precision }}">

     <thead>

     <tr>

       <th>

         <p>{{ 'Product'|trans({}, 'Admin.Global') }}</p>

       </th>

       <th></th>

       <th>

         <p class="mb-0">{{ 'Price per unit'|trans({}, 'Admin.Advparameters.Feature') }}</p>

         <small class="text-muted">{{ orderForViewing.taxMethod }}</small>

       </th>

       <th>

         <p>{{ 'Quantity'|trans({}, 'Admin.Global') }}</p>

       </th>

       <th class="cellProductLocation{% if not isColumnLocationDisplayed %} d-none{% endif %}">

         <p>{{ 'Stock location'|trans({}, 'Admin.Orderscustomers.Feature') }}</p>

       </th>

       <th class="cellProductRefunded{% if not isColumnRefundedDisplayed %} d-none{% endif %}">

         <p>{{ 'Refunded'|trans({}, 'Admin.Orderscustomers.Feature') }}</p>

       </th>

       <th {% if not isAvailableQuantityDisplayed %}class="d-none"{% endif %}>

         <p>{{ 'Available'|trans({}, 'Admin.Global') }}</p>

       </th>

       <th>

         <p class="mb-0">{{ 'Total'|trans({}, 'Admin.Global') }}</p>

         <small class="text-muted">{{ orderForViewing.taxMethod }}</small>

       </th>

       {% if orderForViewing.hasInvoice() %}

         <th>

           <p>{{ 'Invoice'|trans({}, 'Admin.Global') }}</p>

         </th>

       {% endif %}

       {% if not orderForViewing.delivered %}

         <th class="text-right product_actions d-print-none">

           <p>{{ 'Actions'|trans({}, 'Admin.Global') }}</p>

         </th>

       {% endif %}

       <th class="text-center cancel-product-element">

         <p>{{ 'Partial refund'|trans({}, 'Admin.Orderscustomers.Feature') }}</p>

       </th>

     </tr>

     </thead>

     <tbody>

     {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/product_list.html.twig' %}

     {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/add_product_row.html.twig' %}

     {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/edit_product_row.html.twig' %}

     </tbody>

   </table>

   <div class="row mb-3">

     <div class="col-md-6 text-left d-print-none order-product-pagination">

       <div class="form-group row">

         <label for="paginator_select_page_limit" class="col-form-label ml-3">{{ "Items per page:"|trans({}, 'Admin.Catalog.Feature') }}</label>

         <div class="col">

           <select id="orderProductsTablePaginationNumberSelector" class="pagination-link custom-select">

             {% for numPageOption in paginationNumOptions %}

               <option value="{{ numPageOption }}"{% if numPageOption == paginationNum %} selected{% endif %}>{{ numPageOption }}</option>

             {% endfor %}

           </select>

         </div>

       </div>

       {% set numPages = max(orderForViewing.products.products|length / paginationNum, 1)|round(0, 'ceil') %}

       <nav aria-label="Products Navigation"{% if orderForViewing.products.products|length <= paginationNum %} class="d-none"{% endif %} id="orderProductsNavPagination">

         <ul class="pagination" id="orderProductsTablePagination" data-num-per-page="{{ paginationNum }}" data-num-pages="{{ numPages }}">

           <li class="page-item disabled" id="orderProductsTablePaginationPrev">

             <a class="page-link" href="javascript:void(0);" aria-label="Previous">

               <span aria-hidden="true">&laquo;</span>

               <span class="sr-only">Previous</span>

             </a>

           </li>

           {% for numPage in 1..numPages %}

             <li class="page-item{% if numPage==1 %} active{% endif %}"><span class="page-link" data-order-id="{{ orderForViewing.id }}" data-page="{{ numPage }}">{{ numPage }}</span></li>

           {% endfor %}

           <li class="page-item d-none"><span class="page-link" data-order-id="{{ orderForViewing.id }}" data-page=""></span></li>

           <li class="page-item" id="orderProductsTablePaginationNext">

             <a class="page-link" href="javascript:void(0);" aria-label="Next">

               <span aria-hidden="true">&raquo;</span>

               <span class="sr-only">Next</span>

             </a>

           </li>

         </ul>

       </nav>

     </div>

     <div class="col-md-6 text-right discount-action">

       {% if not orderForViewing.delivered %}

         <button type="button" class="btn btn-outline-secondary js-product-action-btn mr-3" id="addProductBtn">

           <i class="material-icons">add_circle_outline</i>

           {{ 'Add a product'|trans({}, 'Admin.Orderscustomers.Feature') }}

         </button>

       {% endif %}

       <button type="button" class="btn btn-outline-secondary js-product-action-btn" data-toggle="modal" data-target="#addOrderDiscountModal">

         <i class="material-icons">confirmation_number</i>

         {{ 'Add a discount'|trans({}, 'Admin.Orderscustomers.Feature') }}

       </button>

     </div>

   </div>

   {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/discount_list.html.twig' with {

     'discounts': orderForViewing.discounts.discounts,

     'orderId': orderForViewing.id

   } %}

   <div class="row">

     <div class="col-md-12">

       <div class="info-block">

         <div class="row">

           <div class="col-sm text-center">

             <p class="text-muted mb-0"><strong>{{ 'Products'|trans({}, 'Admin.Global') }}</strong></p>

             <strong id="orderProductsTotal">{{ orderForViewing.prices.productsPriceFormatted }}</strong>

           </div>

           <div id="order-discounts-total-container" class="col-sm text-center{% if not orderForViewing.prices.discountsAmountRaw.greaterThan((number(0))) %} d-none{% endif %}">

             <p class="text-muted mb-0"><strong>{{ 'Discounts'|trans({}, 'Admin.Global') }}</strong></p>

             <strong id="orderDiscountsTotal">-{{ orderForViewing.prices.discountsAmountFormatted }}</strong>

           </div>

           {% if orderForViewing.prices.wrappingPriceRaw.greaterThan(number(0)) %}

             <div class="col-sm text-center">

               <p class="text-muted mb-0"><strong>{{ 'Wrapping'|trans({}, 'Admin.Orderscustomers.Feature') }}</strong></p>

               <strong id="orderWrappingTotal">{{ orderForViewing.prices.wrappingPriceFormatted }}</strong>

             </div>

           {% endif %}

           <div id="order-shipping-total-container" class="col-sm text-center{% if not orderForViewing.prices.shippingPriceRaw.greaterThan((number(0))) %} d-none{% endif %}">

             <p class="text-muted mb-0"><strong>{{ 'Shipping'|trans({}, 'Admin.Catalog.Feature') }}</strong></p>

             <div class="shipping-price">

               <strong id="orderShippingTotal">{{ orderForViewing.prices.shippingPriceFormatted }}</strong>

               <div class="cancel-product-element shipping-refund-amount{% if orderForViewing.prices.shippingRefundableAmountRaw.lowerOrEqualThan(number(0)) %} hidden{% endif %}">

                 <div class="input-group">

                   {{ form_widget(cancelProductForm.shipping_amount) }}

                   <div class="input-group-append">

                     <div class="input-group-text">{{ orderCurrency.symbol }}</div>

                   </div>

                 </div>

                 <p class="text-center">(max {{ orderForViewing.prices.shippingRefundableAmountFormatted }} tax included)</p>

               </div>

             </div>

           </div>

           {% if not orderForViewing.taxIncluded %}

             <div class="col-sm text-center">

               <p class="text-muted mb-0"><strong>{{ 'Taxes'|trans({}, 'Admin.Global') }}</strong></p>

               <strong id="orderTaxesTotal">{{ orderForViewing.prices.taxesAmountFormatted }}</strong>

             </div>

           {% endif %}

           {#**  edited - adding Balance amount **#}

           <div class="col-sm text-center">

             <p class="text-muted mb-0"><strong>{{ 'Outstanding'|trans({}, 'Admin.Global') }}</strong></p>

             <strong id="outstandingTotal">{{ outstandingAmt }}</strong>

           </div>

           {#**  edited - adding Balance amount **#}

           <div class="col-sm text-center">

             <p class="text-muted mb-0"><strong>{{ 'Paid Amt'|trans({}, 'Admin.Global') }}</strong></p>

             <strong id="orderPaymentPaid">{{ orderPaymentPaidAmt }}</strong>

           </div>

           <div class="col-sm text-center">

             <p class="text-muted mb-0"><strong>{{ 'Total'|trans({}, 'Admin.Global') }}</strong></p>

             <span class="badge rounded badge-dark font-size-100" id="orderTotal">{{ orderForViewing.prices.totalAmountFormatted }}</span>

           </div>

         </div>

       </div>

     </div>

     <div class="col-md-12">

       <p class="mb-0 mt-1 text-center text-muted">

         <small>

           {{ 'For this customer group, prices are displayed as: [1]%tax_method%[/1]'|trans({

             '%tax_method%': orderForViewing.taxMethod,

             '[1]': '<strong>',

             '[/1]': '</strong>'

           }, 'Admin.Orderscustomers.Notification')|raw }}.

           {% if not 'PS_ORDER_RETURN'|configuration %}

             <strong>{{ 'Merchandise returns are disabled'|trans({}, 'Admin.Orderscustomers.Notification') }}</strong>

           {% endif %}

         </small>

       </p>

       <div class="cancel-product-element refund-checkboxes-container">

         <div class="cancel-product-element form-group restock-products">

           {{ form_widget(cancelProductForm.restock) }}

         </div>

         <div class="cancel-product-element form-group refund-credit-slip">

           {{ form_widget(cancelProductForm.credit_slip) }}

         </div>

         <div class="cancel-product-element form-group refund-voucher">

           {{ form_widget(cancelProductForm.voucher) }}

         </div>

         <div class="cancel-product-element shipping-refund{% if orderForViewing.prices.shippingRefundableAmountRaw.lowerOrEqualThan(number(0)) %} hidden{% endif %}">

           <div class="form-group">

             {{ form_widget(cancelProductForm.shipping) }}

             <small class="shipping-refund-amount">({{ orderForViewing.prices.shippingRefundableAmountFormatted }})</small>

           </div>

         </div>

         <div class="cancel-product-element form-group voucher-refund-type{% if orderForViewing.prices.discountsAmountRaw.lowerOrEqualThan(number(0)) %} hidden{% endif %}">

           {{ 'This order has been partially paid by voucher. Choose the amount you want to refund:'|trans({}, 'Admin.Orderscustomers.Feature') }}

           {{ form_widget(cancelProductForm.voucher_refund_type) }}

           <div class="voucher-refund-type-negative-error">

             {{ 'Error. You cannot refund a negative amount.'|trans({}, 'Admin.Orderscustomers.Notification') }}

           </div>

         </div>

       </div>

     </div>

     <div class="cancel-product-element form-submit text-right">

       {{ form_widget(cancelProductForm.cancel) }}

       {{ form_widget(cancelProductForm.save) }}

     </div>

   </div>

   {{ form_end(cancelProductForm) }}

 </div>

</div>

Limitation: Still working while disavbled the module.

Reference:

https://devdocs.prestashop.com/1.7/modules/concepts/controllers/admin-controllers/override-decorate-controller/



Start typing and press Enter to search