Magento – Magento 2 Override Magento sales order totals block

magento2sales-order

I am trying to override Magento 2 sales order totals block. I am setting preference for the same in my custom module as the function to be overridden is protected function, please find code below:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for='Magento\Sales\Block\Order\Totals' type='Company\Module\Block\Order\Totals'/>
</config> 

The custom block file being used to override the same is as below:

<?php

namespace Company\Module\Block\Order;

use Magento\Sales\Model\Order;

class Totals extends \Magento\Sales\Block\Order\Totals
{
    /**
     * Associated array of totals
     * array(
     *  $totalCode => $totalObject
     * )
     *
     * @var array
     */
    protected $_totals;

    /**
     * @var Order|null
     */
    protected $_order = null;

    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;

    /**
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Framework\Registry $registry,
        array $data = []
    ) {
        parent::__construct($context, $registry, $data);
    }

    /**
     * Initialize self totals and children blocks totals before html building
     *
     * @return $this
     */
    protected function _beforeToHtml()
    {
        $this->_initTotals();
        foreach ($this->getLayout()->getChildBlocks($this->getNameInLayout()) as $child) {
            if (method_exists($child, 'initTotals') && is_callable([$child, 'initTotals'])) {
                $child->initTotals();
            }
        }
        return parent::_beforeToHtml();
    }

    /**
     * Initialize order totals array
     *
     * @return $this
     */
    protected function _initTotals()
    {
        $source = $this->getSource();

        $this->_totals = [];
        $this->_totals['subtotal'] = new \Magento\Framework\DataObject(
            ['code' => 'subtotal', 'value' => $source->getSubtotal(), 'label' => __('Subtotal')]
        );

        /**
         * Add shipping
         */
        if (!$source->getIsVirtual() && ((double)$source->getShippingAmount() || $source->getShippingDescription())) {
            $this->_totals['shipping'] = new \Magento\Framework\DataObject(
                [
                    'code' => 'shipping',
                    'field' => 'shipping_amount',
                    'value' => $this->getSource()->getShippingAmount(),
                    'label' => __('Shipping & Handling'),
                ]
            );
        }

        /**
         * Add discount
         */
        if ((double)$this->getSource()->getDiscountAmount() != 0) {
            if ($this->getSource()->getDiscountDescription()) {
                $discountLabel = __('Discount (%1)', $source->getDiscountDescription());
            } else {
                $discountLabel = __('Discount');
            }
            $this->_totals['discount'] = new \Magento\Framework\DataObject(
                [
                    'code' => 'discount',
                    'field' => 'discount_amount',
                    'value' => $source->getDiscountAmount(),
                    'label' => $discountLabel,
                ]
            );
        }

        $this->_totals['grand_total'] = new \Magento\Framework\DataObject(
            [
                'code' => 'grand_total',
                'field' => 'grand_total',
                'strong' => true,
                'value' => $source->getGrandTotal(),
                'label' => __('Grand Total'),
            ]
        );

        /**
         * Base grandtotal
         */
        if ($this->getOrder()->isCurrencyDifferent()) {
            $this->_totals['base_grandtotal'] = new \Magento\Framework\DataObject(
                [
                    'code' => 'base_grandtotal',
                    'value' => $this->getOrder()->formatBasePrice($source->getBaseGrandTotal()),
                    'label' => __('Grand Total to be Charged'),
                    'is_formated' => true,
                ]
            );
        }
        return $this;
    }
}

I can see the function is overwritten and called via my custom block but it is not being rendered at orders total section. Order total section is gone blank, not sure why it is happening so. Please suggest me if anyone have idea on it.

Best Answer

You have to create layout sales_order_view and create block in container reference of order_totals.

 <referenceContainer name="order_totals">   

<block class="[company]\[module]\Block\Sales\Totals\custom" name="custom" as="custom"/>
  </referenceContainer>

Block for order_totals

class custom extends \Magento\Framework\View\Element\Template {

protected $_order;
protected $_source;

public function __construct(\Magento\Framework\View\Element\Template\Context $context,
    array $data = []
    ) {
    parent :: __construct($context, $data);
} 

public function getSource() {
    return $this -> _source;
} 

public function getStore() {
    return $this -> _order -> getStore();
} 

public function getOrder() {
    return $this -> _order;
} 


public function initTotals() {
    $parent = $this -> getParentBlock();
    $this -> _order = $parent -> getOrder();
    $this -> _source = $parent -> getSource();

    $custom = new \Magento\Framework\DataObject([
        'code' => 'custom',
        'strong' => false,
        'value' => [value],
        'label' =>[label],
        ]
        );

    $parent -> addTotal($custum, 'custom');

    return $this;
} 
} 

Here i add custom field in order_totals reference block.