Home > OS >  Magento 2.4.4 - Add a Tare weight for packaging for shipping calculation
Magento 2.4.4 - Add a Tare weight for packaging for shipping calculation

Time:01-17

The issue is that we have lots of items that are very small 2-50g and a box packaging weighs 100-200g

The package is often the heaviest part

If we set the item weight (in the product) as the actual weight of the item then when people order lots of things the box weight can make the total weight go over the chosen postage method.

If we ad the box weight to the item then it makes the cost of postage massively expensive and means people don't make the purchase.

How can I set a box weight for each each package type.

is there an extension for this or is there a way to add some logic to add a tare weight for the box before Magento calculates the weight of a package. It seems odd that this isn't a feature of Magento as this seems like something everyone that sells items online would have to do.

I guess i would need some thing that checks order items weight

IF items total weight is less than 1KG THEN add (entered weight for small box) IF items total weight is less the 2GK but Greater than 1KG then add (entered weight for Medium box) IF items total is greater than 2KG then add (entered weight for Large Box)

expecting to be able to enter a packaging base weight for parcels

CodePudding user response:

This flow is set on Assumption that the Parcel weight and type information is for the admin And shipping charge is fixed.

Step 1:- create etc/db_schema.xml

<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="sales_order" resource="default" engine="innodb" comment="sales_order">
        <column xsi:type="int" name="box_weight" default="0" comment="Box Weight" />
        <column xsi:type="varchar" name="box_type" nullable="false" length="255" comment="Box Type" />
    </table>
    <table name="sales_order_grid" resource="default" engine="innodb" comment="sales_order">
        <column xsi:type="int" name="box_weight" default="0" comment="Box Weight" />
        <column xsi:type="varchar" name="box_type" nullable="false" length="255" comment="Box Type" />
    </table>
</schema>

Step 2: Create Model/ResourceModel/Order/Grid/Collection.php file

<?php

namespace Sunarc\Custom\Model\ResourceModel\Order\Grid;
 
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as CoreFetchStrategy;
use Magento\Framework\Data\Collection\EntityFactoryInterface as CoreEntityFactory;
use Magento\Framework\Event\ManagerInterface as CoreEventManager;
use Magento\Sales\Model\ResourceModel\Order\Grid\Collection as CoreSalesGrid;
use Psr\Log\LoggerInterface as Logger;
 

class Collection extends CoreSalesGrid
{
    /**
     * @param CoreEntityFactory $entityFactory
     * @param Logger            $logger       
     * @param CoreFetchStrategy $fetchStrategy
     * @param CoreEventManager  $eventManager 
     * @param string            $mainTable    
     * @param string            $resourceModel
     */
    public function __construct(
        CoreEntityFactory $entityFactory,
        Logger $logger,
        CoreFetchStrategy $fetchStrategy,
        CoreEventManager $eventManager,
        $mainTable = 'sales_order_grid',
        $resourceModel = \Magento\Sales\Model\ResourceModel\Order::class
    ) {
        parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel);
    }
 
    protected function _renderFiltersBefore()
    {
        $joinTable = $this->getTable('sales_order');
        $this->getSelect()->joinLeft(
            $joinTable,
            'main_table.entity_id = sales_order.entity_id',
            ['box_weight','box_type']
        );
        parent::_renderFiltersBefore();
    }
}

Step 3: Now, Create etc/di.xml file to add custom column data into order grid.

<?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\Controller\Order\History" type="Sunarc\Custom\Controller\Order\History" />

    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="sales_order_grid_data_source" xsi:type="string">Sunarc\Custom\Model\ResourceModel\Order\Grid\Collection</item>
            </argument>
        </arguments>
    </type>
    <type name="Sunarc\Custom\Model\ResourceModel\Order\Grid\Collection">
        <arguments>
            <argument name="mainTable" xsi:type="string">sales_order_grid</argument>
            <argument name="resourceModel" xsi:type="string">Magento\Sales\Model\ResourceModel\Order</argument>
        </arguments>
    </type>

</config>

Step 4 : create view/adminhtml/ui_component/sales_order_grid.xml

<?xml version="1.0" encoding="UTF-8"?>

<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">

        <column name="box_weight">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Parcel Weight</item>
                </item>
            </argument>
        </column>

        <column name="box_type">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Box Type</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

Step 5:- Create etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_save_before">
        <observer name="weight" instance="Sunarc\Custom\Observer\Weight" />
    </event>
</config>

step 6: Create Observer/Weight.php and write your custom logic

<?php

namespace Sunarc\Custom\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Quote\Model\QuoteFactory;

class Weight implements ObserverInterface
{

    public function __construct(QuoteFactory $quoteFactory)
    {
        $this->quoteFactory = $quoteFactory;
    }
    /**
     * Below is the method that will fire whenever the event runs!
     *
     * @param Observer $observer
     */
    public function execute(Observer $observer)
    {

        //WRITE YOUR CUSTOM LOGIC HERE
        $order = $observer->getEvent()->getOrder();

        if ($order->getData('weight') == 1) {
            $order->setData('box_weight', 5); // Assumed wight of small parcel
            $order->setData('box_type', 'Small');
        } elseif ($order->getData('weight') > 1 && $order->getData('weight') <= 2) {
            $order->setData('box_weight', 7); // Assumed wight of Medium parcel
            $order->setData('box_type', 'Medium');
        } else {
            $order->setData('box_weight', 10);  // Assumed wight of Large parcel
            $order->setData('box_type', 'Large');
        }
    }
}

CodePudding user response:

This is the CSV

Step 1:- Create di.xml

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <type name='Magento\OfflineShipping\Model\Carrier\Tablerate'>         
        <plugin name='tablerate_plugin' type='Sunarc\Custom\Plugin\TableratePlugin' sortOrder='10' disabled='false'  />     
    </type>

</config>

Step 2:- Create a plugin to add your custom code

<?php

namespace Sunarc\Custom\Plugin;

use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Model\Quote\Address\RateRequest;

class TableratePlugin
{


    /**
     * @var \Magento\Catalog\Api\ProductRepositoryInterface
     */
    private $productRepository;

    /**
     * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory
     */
    private $rateErrorFactory;

    /**
     * @var string
     */
    protected $_code = 'tablerate';
    /**
     * @var string
     */
    protected $_defaultConditionName = 'package_weight';

    /**
     * @var \Magento\Shipping\Model\Rate\ResultFactory
     */
    protected $_rateResultFactory;

    /**
     * @var \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory
     */
    protected $_resultMethodFactory;

    public function __construct(
        \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
        \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $resultMethodFactory,
        \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
    ) {
        $this->_rateResultFactory = $rateResultFactory;
        $this->_resultMethodFactory = $resultMethodFactory;
        $this->rateErrorFactory = $rateErrorFactory;
        $this->productRepository = $productRepository;
    }
    public function aroundCollectRates(
        \Magento\OfflineShipping\Model\Carrier\Tablerate $subject,
        \Closure $proceed,
        RateRequest $request
    ) {

        if (!$subject->getConfigFlag('active')) {
            return false;
        }
        // exclude Virtual products price from Package value if pre-configured
        if (!$subject->getConfigFlag('include_virtual_price') && $request->getAllItems()) {
            foreach ($request->getAllItems() as $item) {
                if ($item->getParentItem()) {
                    continue;
                }
                if ($item->getHasChildren() && $item->isShipSeparately()) {
                    foreach ($item->getChildren() as $child) {
                        if ($child->getProduct()->isVirtual()) {
                            $request->setPackageValue($request->getPackageValue() - $child->getBaseRowTotal());
                        }
                    }
                } elseif ($item->getProduct()->isVirtual()) {
                    $request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal());
                }
            }
        }

        // Free shipping by qty
        $freeQty = 0;
        $freePackageValue = 0;

        if ($request->getAllItems()) {
            foreach ($request->getAllItems() as $item) {
                if ($item->getProduct()->isVirtual() || $item->getParentItem()) {
                    continue;
                }

                if ($item->getHasChildren() && $item->isShipSeparately()) {
                    foreach ($item->getChildren() as $child) {
                        if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
                            $freeShipping = is_numeric($child->getFreeShipping()) ? $child->getFreeShipping() : 0;
                            $freeQty  = $item->getQty() * ($child->getQty() - $freeShipping);
                        }
                    }
                } elseif ($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) {
                    $freeShipping = $item->getFreeShipping() ?
                        $item->getFreeShipping() : $item->getAddress()->getFreeShipping();
                    $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0;
                    $freeQty  = $item->getQty() - $freeShipping;
                    $freePackageValue  = $item->getBaseRowTotal();
                }
            }
            $oldValue = $request->getPackageValue();
            $request->setPackageValue($oldValue - $freePackageValue);
        }

        if (!$request->getConditionName()) {
            $conditionName = $subject->getConfigData('condition_name');
            $request->setConditionName($conditionName ? $conditionName : $this->_defaultConditionName);
        }


        // Package weight and qty free shipping
        $oldWeight = $request->getPackageWeight();
        $oldQty = $request->getPackageQty();


        // ADD YOUR CUSTOM CODE HERE //

        if ($request->getFreeMethodWeight() == 1) {
            $request->setPackageWeight(50);
        } elseif ($request->getFreeMethodWeight() > 1 && $request->getFreeMethodWeight() <= 2) {
            $request->setPackageWeight(40);
        } else {
            $request->setPackageWeight(40);
        }

        // $request->setPackageWeight($request->getFreeMethodWeight());
        $request->setPackageQty($oldQty - $freeQty);

        /** @var \Magento\Shipping\Model\Rate\Result $result */
        $result = $this->_rateResultFactory->create();
        $rate = $subject->getRate($request);

        $request->setPackageWeight($oldWeight);
        $request->setPackageQty($oldQty);

        if (!empty($rate) && $rate['price'] >= 0) {
            if ($request->getPackageQty() == $freeQty) {
                $shippingPrice = 0;
            } else {
                $shippingPrice = $subject->getFinalPriceWithHandlingFee($rate['price']);
            }
            $method = $this->createShippingMethod($shippingPrice, $rate['cost'], $subject, $request);
            $result->append($method);
        } elseif ($request->getPackageQty() == $freeQty) {

            /**
             * Promotion rule was applied for the whole cart.
             *  In this case all other shipping methods could be omitted
             * Table rate shipping method with 0$ price must be shown if grand total is more than minimal value.
             * Free package weight has been already taken into account.
             */
            $request->setPackageValue($freePackageValue);
            $request->setPackageQty($freeQty);
            $rate = $subject->getRate($request);
            if (!empty($rate) && $rate['price'] >= 0) {
                $method = $this->createShippingMethod(0, 0, $subject, $request);
                $result->append($method);
            }
        } else {
            /** @var \Magento\Quote\Model\Quote\Address\RateResult\Error $error */
            $error = $this->rateErrorFactory->create(
                [
                    'data' => [
                        'carrier' => $this->_code,
                        'carrier_title' => $subject->getConfigData('title'),
                        'error_message' => $subject->getConfigData('specificerrmsg'),
                    ],
                ]
            );
            $result->append($error);
        }
        return $result;
    }

    /**
     * Get the method object based on the shipping price and cost
     *
     * @param float $shippingPrice
     * @param float $cost
     * @return \Magento\Quote\Model\Quote\Address\RateResult\Method
     */
    private function createShippingMethod($shippingPrice, $cost, $subject, $request)
    {

        /** @var  \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
        $method = $this->_resultMethodFactory->create();

        $method->setCarrier('tablerate');
        $method->setCarrierTitle($subject->getConfigData('title'));

        $method->setMethod('bestway');
        $method->setMethodTitle($subject->getConfigData('name'));

        $method->setPrice($shippingPrice);
        $method->setCost($cost);
        return $method;
    }
}

You can calculate the weight according to your logic code and price will get updated accordingly.

  • Related