In this post, we will check how to add an extra Fee to customer for each order based on fixed amount with or without tax at Checkout Page Magento2.
Let's start it by creating custom module.
You can find complete module on Github at Magelearn_CustomFee
Create folder inside app/code/Magelearn/CustomFee
1 2 3 4 5 6 | <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Magelearn_CustomFee' , __DIR__ ); |
Add composer.json file in it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | { "name" : "magelearn/module-custom-fee" , "description" : "A Magento 2 module that adds custom extra fee to customer for each order based on fixed amount with or without tax at checkout" , "type" : "magento2-module" , "license" : "proprietary" , "authors" : [ { "name" : "vijay rami" , "email" : "vijaymrami@gmail.com" } ], "minimum-stability" : "dev" , "require" : {}, "autoload" : { "files" : [ "registration.php" ], "psr-4" : { "Magelearn\\CustomFee\\" : "" } } } |
Add etc/module.xml file in it:
1 2 3 4 | <? xml version = "1.0" ?> < config xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:Module/etc/module.xsd" > < module name = "Magelearn_CustomFee" setup_version = "1.0.0" ></ module > </ config > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <? xml version = "1.0" ?> < 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 = "sales" engine = "innodb" comment = "Sales Order Fee and Base Fee Attribute" > < column xsi:type = "decimal" name = "fee" nullable = "true" default = "0.00" comment = "Fee" /> < column xsi:type = "float" name = "fee_tax" nullable = "true" default = "0.00" comment = "Fee Tax" /> < column xsi:type = "int" name = "gift_fee_check" padding = "5" unsigned = "true" nullable = "false" identity = "false" default = "0" comment = "Gift Fee Check" /> </ table > < table name = "quote" resource = "checkout" engine = "innodb" comment = "Quote Fee and Base Fee Attribute" > < column xsi:type = "decimal" name = "fee" nullable = "true" default = "0.00" comment = "Fee" /> < column xsi:type = "float" name = "fee_tax" nullable = "true" default = "0.00" comment = "Fee Tax" /> < column xsi:type = "int" name = "gift_fee_check" padding = "5" unsigned = "true" nullable = "false" identity = "false" default = "0" comment = "Gift Fee Check" /> </ table > < table name = "quote_address" resource = "checkout" engine = "innodb" comment = "Quote Address Fee and Base Fee Attribute" > < column xsi:type = "decimal" name = "fee" nullable = "true" default = "0.00" comment = "Fee" /> < column xsi:type = "float" name = "fee_tax" nullable = "true" default = "0.00" comment = "Fee Tax" /> < column xsi:type = "int" name = "gift_fee_check" padding = "5" unsigned = "true" nullable = "false" identity = "false" default = "0" comment = "Gift Fee Check" /> </ table > < table name = "sales_invoice" resource = "checkout" engine = "innodb" comment = "Sales Invoice Fee and Base Fee Attribute" > < column xsi:type = "decimal" name = "fee" nullable = "true" default = "0.00" comment = "Fee" /> < column xsi:type = "float" name = "fee_tax" nullable = "true" default = "0.00" comment = "Fee Tax" /> </ table > < table name = "sales_creditmemo" resource = "checkout" engine = "innodb" comment = "Sales Credit memo Fee and Base Fee Attribute" > < column xsi:type = "decimal" name = "fee" nullable = "true" default = "0.00" comment = "Fee" /> < column xsi:type = "float" name = "fee_tax" nullable = "true" default = "0.00" comment = "Fee Tax" /> </ table > </ schema > |
To validate this db_schema.xml file we will first run below command:
php bin/magento setup:db-declaration:generate-whitelist --module-name=Magelearn_CustomFee
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <? xml version = "1.0" ?> xsi:noNamespaceSchemaLocation = "urn:magento:module:Magento_Config:etc/system_file.xsd" > < system > < section id = "customfee" translate = "label" type = "text" sortOrder = "40" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < class >separator-top</ class > < label >Custom Fee</ label > < tab >sales</ tab > < resource >Magelearn_CustomFee::config_customfee</ resource > < group id = "customfee" translate = "label" type = "text" sortOrder = "500" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Custom Fee Configuration</ label > < field id = "status" translate = "label comment" type = "select" sortOrder = "1" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Status</ label > < source_model >Magento\Config\Model\Config\Source\Enabledisable</ source_model > </ field > < field id = "name" translate = "label comment" type = "text" sortOrder = "2" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Fee Name</ label > < depends > < field id = "customfee/customfee/status" >1</ field > </ depends > </ field > < field id = "customfee_amount" translate = "label comment" type = "text" sortOrder = "3" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Custom Fee Amount</ label > < validate >validate-number validate-zero-or-greater required</ validate > < comment > <![CDATA[Applies as Fixed Amount]]> </ comment > < depends > < field id = "customfee/customfee/status" >1</ field > </ depends > </ field > < field id = "minimum_order_amount" translate = "label comment" type = "text" sortOrder = "4" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Minimum Order Amount To Display Custom Fee</ label > < comment > <![CDATA[Greaterthan or equal to]]> </ comment > < validate >validate-number validate-zero-or-greater</ validate > < depends > < field id = "customfee/customfee/status" >1</ field > </ depends > </ field > </ group > < group id = "tax" translate = "label" type = "text" sortOrder = "600" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Custom Fee Tax</ label > < field id = "enable" translate = "label comment" type = "select" sortOrder = "10" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Calculate Tax On Custom Fee</ label > < source_model >Magento\Config\Model\Config\Source\Yesno</ source_model > </ field > < field id = "tax_class" translate = "label comment" type = "select" sortOrder = "20" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Tax Class</ label > < source_model >Magento\Tax\Model\TaxClass\Source\Product</ source_model > < depends >< field id = "customfee/tax/enable" >1</ field ></ depends > </ field > < field id = "display" translate = "label comment" type = "select" sortOrder = "30" showInDefault = "1" showInWebsite = "1" showInStore = "1" > < label >Custom Fee</ label > < source_model >Magento\Tax\Model\System\Config\Source\Tax\Display\Type</ source_model > < depends >< field id = "customfee/tax/enable" >1</ field ></ depends > </ field > </ group > </ section > </ system > </ config > |
And will set these options default values in etc/config.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <? xml version = "1.0" ?> < config xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:module:Magento_Store:etc/config.xsd" > < default > < customfee > < customfee > < status >1</ status > < name >Custom Fee</ name > < customfee_amount >10</ customfee_amount > < minimum_order_amount >30</ minimum_order_amount > </ customfee > </ customfee > </ default > </ config > |
Add etc/acl.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <? xml version = "1.0" ?> < config xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:Acl/etc/acl.xsd" > < acl > < resources > < resource id = "Magento_Backend::admin" > < resource id = "Magento_Backend::stores" > < resource id = "Magento_Backend::stores_settings" > < resource id = "Magento_Config::config" > < resource id = "Magelearn_CustomFee::config_customfee" title = "Magelearn CustomFee" /> </ resource > </ resource > </ resource > </ resource > </ resources > </ acl > </ config > |
1 2 3 4 5 6 | <? 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\Checkout\Model\ShippingInformationManagement" > < plugin name = "magelearn_save_custom_fee_in_quote" type = "Magelearn\CustomFee\Plugin\Checkout\Model\ShippingInformationManagement" sortOrder = "1" /> </ type > </ config > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <?php namespace Magelearn\CustomFee\Plugin\Checkout\Model; class ShippingInformationManagement { /** * @var \Magento\Quote\Model\QuoteRepository */ protected $quoteRepository ; /** * @var \Magelearn\CustomFee\Helper\Data */ protected $dataHelper ; /** * @param \Magento\Quote\Model\QuoteRepository $quoteRepository * @param \Magelearn\CustomFee\Helper\Data $dataHelper */ public function __construct( \Magento\Quote\Model\QuoteRepository $quoteRepository , \Magelearn\CustomFee\Helper\Data $dataHelper ) { $this ->quoteRepository = $quoteRepository ; $this ->dataHelper = $dataHelper ; } /** * @param \Magento\Checkout\Model\ShippingInformationManagement $subject * @param $cartId * @param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation */ public function beforeSaveAddressInformation( \Magento\Checkout\Model\ShippingInformationManagement $subject , $cartId , \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation ) { $customFee = $addressInformation ->getExtensionAttributes()->getFee(); $Giftfeecheck = $addressInformation ->getExtensionAttributes()->getGiftFeeCheck(); $quote = $this ->quoteRepository->getActive( $cartId ); if ( $customFee ) { $fee = $this ->dataHelper->getCustomFee(); $quote ->setFee( $fee ); } else { $quote ->setFee(NULL); } if ( $Giftfeecheck ) { $quote ->setGiftFeeCheck( $Giftfeecheck ); } else { $quote ->setGiftFeeCheck(0); } } } |
As per highlighted code above, we will add Helper/Data.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <?php namespace Magelearn\CustomFee\Helper; use Magento\Framework\App\Helper\AbstractHelper; class Data extends AbstractHelper { /** * Custom fee config path */ const CONFIG_CUSTOM_IS_ENABLED = 'customfee/customfee/status' ; const CONFIG_CUSTOM_FEE = 'customfee/customfee/customfee_amount' ; const CONFIG_FEE_LABEL = 'customfee/customfee/name' ; const CONFIG_MINIMUM_ORDER_AMOUNT = 'customfee/customfee/minimum_order_amount' ; /** * @return mixed */ public function isModuleEnabled() { $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE; $isEnabled = $this ->scopeConfig->getValue(self::CONFIG_CUSTOM_IS_ENABLED, $storeScope ); return $isEnabled ; } /** * Get custom fee * * @return mixed */ public function getCustomFee() { $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE; $fee = $this ->scopeConfig->getValue(self::CONFIG_CUSTOM_FEE, $storeScope ); return $fee ; } /** * Get custom fee * * @return mixed */ public function getFeeLabel() { $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE; $feeLabel = $this ->scopeConfig->getValue(self::CONFIG_FEE_LABEL, $storeScope ); return $feeLabel ; } /** * @return mixed */ public function getMinimumOrderAmount() { $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE; $MinimumOrderAmount = $this ->scopeConfig->getValue(self::CONFIG_MINIMUM_ORDER_AMOUNT, $storeScope ); return $MinimumOrderAmount ; } } |
Now we will add our events.xml file in which sales_model_service_quote_submit_before event will be used to save order object to quote object and payment_cart_collect_items_and_amounts event will be used to add custom fee amount in order.
Add etc/events.xml file.
1 2 3 4 5 6 7 8 9 10 | <? xml version = "1.0" ?> xsi:noNamespaceSchemaLocation = "urn:magento:framework:Event/etc/events.xsd" > < event name = "sales_model_service_quote_submit_before" > < observer name = "paymentfee" instance = "Magelearn\CustomFee\Observer\AddFeeToOrderObserver" /> </ event > < event name = "payment_cart_collect_items_and_amounts" > < observer name = "magelearn_extrafee_payment_collect_total" instance = "Magelearn\CustomFee\Observer\AddCustomfeetototal" /> </ event > </ config > |
Now add Observer/AddFeeToOrderObserver.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?php namespace Magelearn\CustomFee\Observer; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; class AddFeeToOrderObserver implements ObserverInterface { /** * @var \Magento\Framework\DataObject\Copy */ protected $objectCopyService ; /** * AddFeeToOrderObserver constructor. */ /** * @param \Magento\Framework\DataObject\Copy $objectCopyService */ public function __construct( \Magento\Framework\DataObject\ Copy $objectCopyService ) { $this ->objectCopyService = $objectCopyService ; } /** * Set payment fee to order * * @param EventObserver $observer * @return $this */ public function execute(EventObserver $observer ) { /** @var \Magento\Sales\Model\Order $order */ $order = $observer ->getEvent()->getData( 'order' ); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $observer ->getEvent()->getData( 'quote' ); $CustomFeeFee = $quote ->getFee(); if (! $CustomFeeFee ) { return $this ; } $order ->setFee( $CustomFeeFee ); $this ->objectCopyService->copyFieldsetToTarget( 'sales_convert_quote' , 'to_order' , $quote , $order ); return $this ; } } |
Also add Observer/AddCustomfeetototal.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?php namespace Magelearn\CustomFee\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\StoreManagerInterface; use \Magento\Checkout\Model\Session; use Magelearn\CustomFee\Helper\Data; class AddCustomfeetototal implements ObserverInterface { protected $checkout ; protected $helper ; public function __construct( Session $checkout , Data $helper ) { $this ->checkout = $checkout ; $this ->helper = $helper ; } public function execute(Observer $observer ) { if (! $this ->helper->isModuleEnabled()){ return $this ; } $cart = $observer ->getEvent()->getCart(); $quote = $this ->checkout->getQuote(); $customAmount = $quote ->getFee(); $label = $this ->helper->getFeeLabel(); if ( $customAmount ) { $cart ->addCustomItem( $label , 1, $customAmount , $label ); } } } |
Here, addCustomItem() function is added your custom amount field in cart and then payment method will also manages the amount as well.
In this, first parameter is the field name, i.e. amount field name. $label
Second parameter is quantity for the field.
Third parameter is amount, which you want to add. $customAmount
Forth parameter is identifier, i.e. id of your custom amount field. $label
Now add etc/extension_attributes.xml file.
1 2 3 4 5 6 7 | <? xml version = "1.0" ?> < config xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:Api/etc/extension_attributes.xsd" > < extension_attributes for = "Magento\Checkout\Api\Data\ShippingInformationInterface" > < attribute code = "fee" type = "integer" /> < attribute code = "gift_fee_check" type = "integer" /> </ extension_attributes > </ config > |
Add Now add etc/fieldset.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <? xml version = "1.0" ?> xsi:noNamespaceSchemaLocation = "urn:magento:framework:DataObject/etc/fieldset.xsd" > < scope id = "global" > < fieldset id = "sales_convert_quote_address" > < field name = "fee" > < aspect name = "to_order" /> </ field > < field name = "gift_fee_check" > < aspect name = "to_order" /> </ field > </ fieldset > < fieldset id = "sales_convert_quote" > < field name = "fee" > < aspect name = "to_order" /> </ field > < field name = "gift_fee_check" > < aspect name = "to_order" /> </ field > </ fieldset > </ scope > </ config > |
Now add etc/frontend/di.xml file. Here we will add Configuration Provider array to be available at checkout page.
1 2 3 4 5 6 7 8 9 10 11 | <? xml version = "1.0" ?> xsi:noNamespaceSchemaLocation = "urn:magento:framework:ObjectManager/etc/config.xsd" > < type name = "Magento\Checkout\Model\CompositeConfigProvider" > < arguments > < argument name = "configProviders" xsi:type = "array" > < item name = "checkout_customfee_block" xsi:type = "object" >Magelearn\CustomFee\Model\CustomFeeConfigProvider</ item > </ argument > </ arguments > </ type > </ config > |
Now add Model/CustomFeeConfigProvider.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | <?php namespace Magelearn\CustomFee\Model; use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Quote\Model\Quote; class CustomFeeConfigProvider implements ConfigProviderInterface { /** * @var \Magelearn\CustomFee\Helper\Data */ protected $dataHelper ; /** * @var \Magento\Checkout\Model\Session */ protected $checkoutSession ; /** * @var \Psr\Log\LoggerInterface */ protected $logger ; protected $taxHelper ; /** * @param \Magelearn\CustomFee\Helper\Data $dataHelper * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Psr\Log\LoggerInterface $logger * @param \Magelearn\CustomFee\Helper\Tax $helperTax */ public function __construct( \Magelearn\CustomFee\Helper\Data $dataHelper , \Magento\Checkout\Model\Session $checkoutSession , \Psr\Log\LoggerInterface $logger , \Magelearn\CustomFee\Helper\Tax $helperTax ) { $this ->dataHelper = $dataHelper ; $this ->checkoutSession = $checkoutSession ; $this ->logger = $logger ; $this ->taxHelper = $helperTax ; } /** * @return array */ public function getConfig() { $customFeeConfig = []; $enabled = $this ->dataHelper->isModuleEnabled(); $minimumOrderAmount = $this ->dataHelper->getMinimumOrderAmount(); $customFeeConfig [ 'fee_label' ] = $this ->dataHelper->getFeeLabel(); $quote = $this ->checkoutSession->getQuote(); $subtotal = $quote ->getSubtotal(); $customFeeConfig [ 'custom_fee_amount' ] = $this ->dataHelper->getCustomFee(); if ( $this ->taxHelper->isTaxEnabled() && $this ->taxHelper->displayInclTax()) { $address = $this ->_getAddressFromQuote( $quote ); $customFeeConfig [ 'custom_fee_amount' ] = $this ->dataHelper->getCustomFee() + $address ->getFeeTax(); } if ( $this ->taxHelper->isTaxEnabled() && $this ->taxHelper->displayBothTax()) { $address = $this ->_getAddressFromQuote( $quote ); $customFeeConfig [ 'custom_fee_amount' ] = $this ->dataHelper->getCustomFee(); $customFeeConfig [ 'custom_fee_amount_inc' ] = $this ->dataHelper->getCustomFee() + $address ->getFeeTax(); } $customFeeConfig [ 'displayInclTax' ] = $this ->taxHelper->displayInclTax(); $customFeeConfig [ 'displayExclTax' ] = $this ->taxHelper->displayExclTax(); $customFeeConfig [ 'displayBoth' ] = $this ->taxHelper->displayBothTax(); $customFeeConfig [ 'exclTaxPostfix' ] = __( 'Excl. Tax' ); $customFeeConfig [ 'inclTaxPostfix' ] = __( 'Incl. Tax' ); $customFeeConfig [ 'TaxEnabled' ] = $this ->taxHelper->isTaxEnabled(); $customFeeConfig [ 'show_hide_customfee_block' ] = ( $enabled && ( $minimumOrderAmount <= $subtotal ) && $quote ->getFee()) ? true : false; $customFeeConfig [ 'show_hide_customfee_shipblock' ] = ( $enabled && ( $minimumOrderAmount <= $subtotal )) ? true : false; return $customFeeConfig ; } protected function _getAddressFromQuote(Quote $quote ) { return $quote ->isVirtual() ? $quote ->getBillingAddress() : $quote ->getShippingAddress(); } } |
Now we will add etc/sales.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <? xml version = "1.0" ?> < config xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:module:Magento_Sales:etc/sales.xsd" > < section name = "quote" > < group name = "totals" > < item name = "fee" instance = "Magelearn\CustomFee\Model\Quote\Total\Fee" sort_order = "150" /> </ group > </ section > < section name = "order_invoice" > < group name = "totals" > < item name = "fee" instance = "Magelearn\CustomFee\Model\Invoice\Total\Fee" sort_order = "160" /> </ group > </ section > < section name = "order_creditmemo" > < group name = "totals" > < item name = "fee" instance = "Magelearn\CustomFee\Model\Creditmemo\Total\Fee" sort_order = "160" /> </ group > </ section > </ config > |
Here we have added a total item for "quote", "order_invoice" and "order_creditmemo" sections.
And the instance class to handle the calculation and collection of this total item must extend the
Magento\Quote\Model\Quote\Address\Total\AbstractTotal and implement the collect and fetch methods.
The collect method is used to calculate the value of our total,
while the fetch method returns the result along with the total collector’s code and its name.
Now first add Magelearn/CustomFee/Model/Total/Fee.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | <?php namespace Magelearn\CustomFee\Model\Total; use Magento\Store\Model\ScopeInterface; class Fee extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { protected $helperData ; /** * Collect grand total address amount * * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this */ protected $quoteValidator = null; public function __construct( \Magento\Quote\Model\QuoteValidator $quoteValidator , \Magelearn\CustomFee\Helper\Data $helperData ) { $this ->quoteValidator = $quoteValidator ; $this ->helperData = $helperData ; } public function collect( \Magento\Quote\Model\Quote $quote , \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment , \Magento\Quote\Model\Quote\Address\Total $total ) { parent::collect( $quote , $shippingAssignment , $total ); if (! count ( $shippingAssignment ->getItems())) { return $this ; } $enabled = $this ->helperData->isModuleEnabled(); $minimumOrderAmount = $this ->helperData->getMinimumOrderAmount(); $subtotal = $total ->getTotalAmount( 'subtotal' ); if ( $enabled && $minimumOrderAmount <= $subtotal ) { $fee = $quote ->getFee(); $total ->setTotalAmount( 'fee' , $fee ); $total ->setBaseTotalAmount( 'fee' , $fee ); $total ->setFee( $fee ); $quote ->setFee( $fee ); $total ->setGrandTotal( $total ->getGrandTotal() + $fee ); $total ->setBaseGrandTotal( $total ->getBaseGrandTotal() + $fee ); } return $this ; } /** * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Model\Quote\Address\Total $total * @return array */ public function fetch( \Magento\Quote\Model\Quote $quote , \Magento\Quote\Model\Quote\Address\Total $total ) { $enabled = $this ->helperData->isModuleEnabled(); $minimumOrderAmount = $this ->helperData->getMinimumOrderAmount(); $subtotal = $quote ->getSubtotal(); $fee = $quote ->getFee(); if ( $enabled && $minimumOrderAmount <= $subtotal && $fee ) { return [ 'code' => 'fee' , 'title' => 'Custom Fee' , 'value' => $fee ]; } else { return array (); } } /** * Get Subtotal label * * @return \Magento\Framework\Phrase */ public function getLabel() { return __( 'Custom Fee' ); } /** * @param \Magento\Quote\Model\Quote\Address\Total $total */ protected function clearValues(\Magento\Quote\Model\Quote\Address\Total $total ) { $total ->setTotalAmount( 'subtotal' , 0); $total ->setBaseTotalAmount( 'subtotal' , 0); $total ->setTotalAmount( 'tax' , 0); $total ->setBaseTotalAmount( 'tax' , 0); $total ->setTotalAmount( 'discount_tax_compensation' , 0); $total ->setBaseTotalAmount( 'discount_tax_compensation' , 0); $total ->setTotalAmount( 'shipping_discount_tax_compensation' , 0); $total ->setBaseTotalAmount( 'shipping_discount_tax_compensation' , 0); $total ->setSubtotalInclTax(0); $total ->setBaseSubtotalInclTax(0); } } |
Now add Magelearn/CustomFee/Model/Quote/Total/Fee.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | <?php namespace Magelearn\CustomFee\Model\Quote\Total; use Magento\Store\Model\ScopeInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Address\Total; class Fee extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { protected $quoteValidator = null; protected $_priceCurrency ; protected $helperData ; protected $taxHelper ; private $taxCalculator ; private $productMetadata ; /** * Collect grand total address amount * * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this */ public function __construct( \Magento\Quote\Model\QuoteValidator $quoteValidator , \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency , \Magelearn\CustomFee\Helper\Data $helperData , \Magelearn\CustomFee\Helper\Tax $helperTax , \Magento\Tax\Model\Calculation $taxCalculator , \Magento\Framework\App\ProductMetadataInterface $productMetadata ) { $this ->quoteValidator = $quoteValidator ; $this ->_priceCurrency = $priceCurrency ; $this ->helperData = $helperData ; $this ->taxHelper = $helperTax ; $this ->taxCalculator = $taxCalculator ; $this ->productMetadata = $productMetadata ; } public function collect( \Magento\Quote\Model\Quote $quote , \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment , \Magento\Quote\Model\Quote\Address\Total $total ) { parent::collect( $quote , $shippingAssignment , $total ); if (! count ( $shippingAssignment ->getItems())) { return $this ; } $enabled = $this ->helperData->isModuleEnabled(); $minimumOrderAmount = $this ->helperData->getMinimumOrderAmount(); $subtotal = $total ->getTotalAmount( 'subtotal' ); if ( $enabled && $minimumOrderAmount <= $subtotal ) { $fee = $this ->helperData->getCustomFee(); //Try to test with sample value //$fee=50; $total ->setTotalAmount( 'fee' , $fee ); $total ->setBaseTotalAmount( 'fee' , $fee ); $total ->setFee( $fee ); $quote ->setFee( $fee ); $version = (float) $this ->getMagentoVersion(); if ( $version > 2.1) { //$total->setGrandTotal($total->getGrandTotal() + $fee); } else { $total ->setGrandTotal( $total ->getGrandTotal() + $fee ); } if ( $this ->taxHelper->isTaxEnabled()) { $address = $this ->_getAddressFromQuote( $quote ); $this ->_calculateTax( $address , $total ); $extraTaxables = $address ->getAssociatedTaxables(); $extraTaxables [] = [ 'code' => 'fee' , 'type' => 'fee' , 'quantity' => 1, 'tax_class_id' => $this ->taxHelper->getTaxClassId(), 'unit_price' => $fee , 'base_unit_price' => $fee , 'price_includes_tax' => false, 'associated_item_code' => false ]; $address ->setAssociatedTaxables( $extraTaxables ); } } return $this ; } public function getMagentoVersion() { return $this ->productMetadata->getVersion(); } /** * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Model\Quote\Address\Total $total * @return array */ public function fetch( \Magento\Quote\Model\Quote $quote , \Magento\Quote\Model\Quote\Address\Total $total ) { $enabled = $this ->helperData->isModuleEnabled(); $minimumOrderAmount = $this ->helperData->getMinimumOrderAmount(); $subtotal = $quote ->getSubtotal(); $fee = $quote ->getFee(); $address = $this ->_getAddressFromQuote( $quote ); $result = []; if ( $enabled && ( $minimumOrderAmount <= $subtotal ) && $fee ) { $result = [ 'code' => 'fee' , 'title' => $this ->helperData->getFeeLabel(), 'value' => $fee ]; if ( $this ->taxHelper->isTaxEnabled() && $this ->taxHelper->displayInclTax()) { $result [] = [ 'code' => 'fee' , 'value' => $fee + $address ->getFeeTax(), 'title' => __( $this ->helperData->getFeeLabel()), ]; } if ( $this ->taxHelper->isTaxEnabled() && $this ->taxHelper->displayBothTax()) { $result [] = [ 'code' => 'fee' , 'value' => $fee + $address ->getFeeTax(), 'title' => __( $this ->helperData->getFeeLabel()), ]; } } return $result ; } /** * Get Subtotal label * * @return \Magento\Framework\Phrase */ public function getLabel() { return __( 'Custom Fee' ); } /** * @param \Magento\Quote\Model\Quote\Address\Total $total */ protected function clearValues(\Magento\Quote\Model\Quote\Address\Total $total ) { $total ->setTotalAmount( 'subtotal' , 0); $total ->setBaseTotalAmount( 'subtotal' , 0); $total ->setTotalAmount( 'tax' , 0); $total ->setBaseTotalAmount( 'tax' , 0); $total ->setTotalAmount( 'discount_tax_compensation' , 0); $total ->setBaseTotalAmount( 'discount_tax_compensation' , 0); $total ->setTotalAmount( 'shipping_discount_tax_compensation' , 0); $total ->setBaseTotalAmount( 'shipping_discount_tax_compensation' , 0); $total ->setSubtotalInclTax(0); $total ->setBaseSubtotalInclTax(0); } protected function _getAddressFromQuote(Quote $quote ) { return $quote ->isVirtual() ? $quote ->getBillingAddress() : $quote ->getShippingAddress(); } protected function _calculateTax(Address $address , Total $total ) { $taxClassId = $this ->taxHelper->getTaxClassId(); if (! $taxClassId ) { return $this ; } $taxRateRequest = $this ->_getAddressTaxRequest( $address ); $taxRateRequest ->setProductClassId( $taxClassId ); $rate = $this ->taxCalculator->getRate( $taxRateRequest ); $baseTax = $this ->taxCalculator->calcTaxAmount( $total ->getBaseTotalAmount( 'fee' ), $rate , false, true ); $tax = $this ->taxCalculator->calcTaxAmount( $total ->getTotalAmount( 'fee' ), $rate , false, true ); //$total->setBaseMcPaymentfeeTaxAmount($baseTax); $total ->setFeeTax( $tax ); $appliedRates = $this ->taxCalculator->getAppliedRates( $taxRateRequest ); $this ->_saveAppliedTaxes( $address , $appliedRates , $tax , $baseTax , $rate ); $total ->addBaseTotalAmount( 'tax' , $baseTax ); $total ->addTotalAmount( 'tax' , $tax ); return $this ; } protected function _getAddressTaxRequest( $address ) { $addressTaxRequest = $this ->taxCalculator->getRateRequest( $address , $address ->getQuote()->getBillingAddress(), $address ->getQuote()->getCustomerTaxClassId(), $address ->getQuote()->getStore() ); return $addressTaxRequest ; } protected function _saveAppliedTaxes( Address $address , $applied , $amount , $baseAmount , $rate ) { $previouslyAppliedTaxes = $address ->getAppliedTaxes(); $process = 0; if ( is_array ( $previouslyAppliedTaxes )) { $process = count ( $previouslyAppliedTaxes ); } foreach ( $applied as $row ) { if ( $row [ 'percent' ] == 0) { continue ; } if (!isset( $previouslyAppliedTaxes [ $row [ 'id' ]])) { $row [ 'process' ] = $process ; $row [ 'amount' ] = 0; $row [ 'base_amount' ] = 0; $previouslyAppliedTaxes [ $row [ 'id' ]] = $row ; } if ( $row [ 'percent' ] !== null) { $row [ 'percent' ] = $row [ 'percent' ] ? $row [ 'percent' ] : 1; $rate = $rate ? $rate : 1; $appliedAmount = $amount / $rate * $row [ 'percent' ]; $baseAppliedAmount = $baseAmount / $rate * $row [ 'percent' ]; } else { $appliedAmount = 0; $baseAppliedAmount = 0; foreach ( $row [ 'rates' ] as $rate ) { $appliedAmount += $rate [ 'amount' ]; $baseAppliedAmount += $rate [ 'base_amount' ]; } } if ( $appliedAmount || $previouslyAppliedTaxes [ $row [ 'id' ]][ 'amount' ]) { $previouslyAppliedTaxes [ $row [ 'id' ]][ 'amount' ] += $appliedAmount ; $previouslyAppliedTaxes [ $row [ 'id' ]][ 'base_amount' ] += $baseAppliedAmount ; } else { unset( $previouslyAppliedTaxes [ $row [ 'id' ]]); } } $address ->setAppliedTaxes( $previouslyAppliedTaxes ); } } |
Now as per highlighted code above, we will create Helper/Tax.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <?php namespace Magelearn\CustomFee\Helper; use Magento\Framework\App\Helper\AbstractHelper; class Tax extends AbstractHelper { const XML_PATH_TAX_ENABLED = 'customfee/tax/enable' ; const XML_PATH_TAX_CLASS = 'customfee/tax/tax_class' ; const XML_PATH_TAX_DISPLAY = 'customfee/tax/display' ; public function isTaxEnabled() { return $this ->scopeConfig->isSetFlag(self::XML_PATH_TAX_ENABLED, 'store' ); } public function getTaxClassId() { return $this ->scopeConfig->getValue(self::XML_PATH_TAX_CLASS, 'store' ); } public function getTaxDisplay() { return $this ->scopeConfig->getValue(self::XML_PATH_TAX_DISPLAY, 'store' ); } public function displayInclTax() { return in_array( $this ->getTaxDisplay(), [2]); } public function displayExclTax() { return in_array( $this ->getTaxDisplay(), [1]); } public function displayBothTax() { return in_array( $this ->getTaxDisplay(), [3]); } public function displaySuffix() { return ( $this ->getTaxDisplay() == 3); } } |
Now add Model/Invoice/Total/Fee.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php namespace Magelearn\CustomFee\Model\Invoice\Total; use Magento\Sales\Model\Order\Invoice\Total\AbstractTotal; class Fee extends AbstractTotal { /** * @param \Magento\Sales\Model\Order\Invoice $invoice * @return $this */ public function collect(\Magento\Sales\Model\Order\Invoice $invoice ) { $invoice ->setFee(0); $amount = $invoice ->getOrder()->getFee(); $invoice ->setFee( $amount ); $invoice ->setGrandTotal( $invoice ->getGrandTotal() + $invoice ->getFee()); $invoice ->setBaseGrandTotal( $invoice ->getBaseGrandTotal() + $invoice ->getFee()); return $this ; } } |
Now add Model/Creditmemo/Total/Fee.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php namespace Magelearn\CustomFee\Model\Creditmemo\Total; use Magento\Sales\Model\Order\Creditmemo\Total\AbstractTotal; class Fee extends AbstractTotal { /** * @param \Magento\Sales\Model\Order\Creditmemo $creditmemo * @return $this */ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo ) { $creditmemo ->setFee(0); $amount = $creditmemo ->getOrder()->getFee(); $creditmemo ->setFee( $amount ); $creditmemo ->setGrandTotal( $creditmemo ->getGrandTotal() + $creditmemo ->getFee()); $creditmemo ->setBaseGrandTotal( $creditmemo ->getBaseGrandTotal() + $creditmemo ->getFee()); return $this ; } } |
Now we will move on front end part.
First we will modify checkout_cart_index.xml file and display custom fee on checkout/cart page.
Add view/frontend/layout/checkout_cart_index.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceBlock name = "checkout.cart.totals" > < arguments > < argument name = "jsLayout" xsi:type = "array" > < item name = "components" xsi:type = "array" > < item name = "block-totals" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "fee" xsi:type = "array" > < item name = "component" xsi:type = "string" >Magelearn_CustomFee/js/view/checkout/cart/totals/fee</ item > < item name = "sortOrder" xsi:type = "string" >20</ item > < item name = "config" xsi:type = "array" > < item name = "template" xsi:type = "string" >Magelearn_CustomFee/checkout/cart/totals/fee</ item > < item name = "title" xsi:type = "string" translate = "true" >Custom Fee</ item > </ item > </ item > </ item > </ item > </ item > </ argument > </ arguments > </ referenceBlock > </ body > </ page > |
Now, as per the highlighted code above, we will add our JS component file and template file.
Add view/frontend/web/js/view/checkout/cart/totals/fee.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | define([ 'ko' , 'uiComponent' , 'Magento_Checkout/js/model/quote' , 'Magento_Catalog/js/price-utils' , 'Magento_Checkout/js/model/totals' ], function (ko, Component, quote, priceUtils, totals) { 'use strict' ; var show_hide_customfee_blockConfig = window.checkoutConfig.show_hide_customfee_block; var fee_label = window.checkoutConfig.fee_label; var custom_fee_amount = window.checkoutConfig.custom_fee_amount; var custom_in_fee_amount = window.checkoutConfig.custom_fee_amount_inc; return Component.extend({ totals: quote.getTotals(), canVisibleCustomFeeBlock: show_hide_customfee_blockConfig, getFormattedPrice: ko.observable(priceUtils.formatPrice(custom_fee_amount, quote.getPriceFormat())), getFeeLabel:ko.observable(fee_label), getInFeeLabel:ko.observable(window.checkoutConfig.inclTaxPostfix), getExFeeLabel:ko.observable(window.checkoutConfig.exclTaxPostfix), isDisplayed: function () { return this.getValue() != 0; }, isDisplayBoth: function () { return window.checkoutConfig.displayBoth; }, displayExclTax: function () { return window.checkoutConfig.displayExclTax; }, displayInclTax: function () { return window.checkoutConfig.displayInclTax; }, isTaxEnabled: function () { return window.checkoutConfig.TaxEnabled; }, getValue: function () { var price = 0; if (this.totals() && totals.getSegment( 'fee' )) { price = totals.getSegment( 'fee' ).value; } return price; }, getInFormattedPrice: function () { var price = 0; if (this.totals() && totals.getSegment( 'fee' )) { price = totals.getSegment( 'fee' ).value; } return priceUtils.formatPrice(price, quote.getPriceFormat()); }, }); }); |
And add view/frontend/web/template/checkout/cart/totals/fee.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <!-- ko if : isDisplayed() --> <!-- ko if : isTaxEnabled() --> <!-- ko if : isDisplayBoth() --> <tr class = "totals fee excl" > <th class = "mark" colspan= "1" scope= "row" data-bind= "text: getFeeLabel() + ' ' + getExFeeLabel()" ></th> <td class = "amount" > <span class = "price" data-bind= "text: getFormattedPrice()" ></span> </td> </tr> <tr class = "totals fee incl" > <th class = "mark" colspan= "1" scope= "row" data-bind= "text: getFeeLabel() + ' ' + getInFeeLabel()" ></th> <td class = "amount" > <span class = "price" data-bind= "text: getInFormattedPrice()" ></span> </td> </tr> <!-- /ko --> <!-- ko ifnot: isDisplayBoth() --> <!-- ko if : displayExclTax() --> <tr class = "totals fee excl" > <th class = "mark" colspan= "1" scope= "row" data-bind= "text: getFeeLabel()" ></th> <td class = "amount" > <span class = "price" data-bind= "text: getFormattedPrice()" ></span> </td> </tr> <!-- /ko --> <!-- ko if : displayInclTax() --> <tr class = "totals fee incl" > <th class = "mark" colspan= "1" scope= "row" data-bind= "text: getFeeLabel()" ></th> <td class = "amount" > <span class = "price" data-bind= "text: getInFormattedPrice()" ></span> </td> </tr> <!-- /ko --> <!-- /ko --> <!-- /ko --> <!-- ko ifnot: isTaxEnabled() --> <tr class = "totals fee excl" > <th class = "mark" colspan= "1" scope= "row" data-bind= "text: getFeeLabel()" ></th> <td class = "amount" > <span class = "price" data-bind= "text: getFormattedPrice()" ></span> </td> </tr> <!-- /ko --> <!-- /ko --> |
Now, To display totals data properly on checkout summary, we will also create same files.
Add Magelearn/CustomFee/view/frontend/web/js/view/checkout/summary/fee.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | define( [ 'Magento_Checkout/js/view/summary/abstract-total' , 'Magento_Checkout/js/model/quote' , 'Magento_Catalog/js/price-utils' , 'Magento_Checkout/js/model/totals' ], function (Component, quote, priceUtils, totals) { "use strict" ; return Component.extend({ defaults: { isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, template: 'Magelearn_CustomFee/checkout/summary/fee' }, totals: quote.getTotals(), isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, isDisplayed: function () { return this.getValue() != 0; }, getValue: function () { var price = 0; if (this.totals()) { price = totals.getSegment( 'fee' ).value; } return this.getFormattedPrice(price); } }); } ); |
Add Magelearn/CustomFee/view/frontend/web/template/checkout/summary/fee.html
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- ko --> <tr class = "totals fee excl" > <th class = "mark" scope= "row" > <span class = "label" data-bind= "text: title" ></span> <span class = "value" data-bind= "text: getValue()" ></span> </th> <td class = "amount" > <span class = "price" data-bind= "text: getValue(), attr: {'data-th': title}" ></span> </td> </tr> <!-- /ko --> |
Now, we will modify Magelearn/CustomFee/view/frontend/layout/checkout_index_index.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" layout = "1column" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceBlock name = "checkout.root" > < arguments > < argument name = "jsLayout" xsi:type = "array" > < item name = "components" xsi:type = "array" > < item name = "checkout" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "steps" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "shipping-step" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "shippingAddress" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "shippingAdditional" xsi:type = "array" > < item name = "component" xsi:type = "string" >uiComponent</ item > < item name = "displayArea" xsi:type = "string" >shippingAdditional</ item > < item name = "children" xsi:type = "array" > < item name = "gift_fee" xsi:type = "array" > < item name = "component" xsi:type = "string" >Magelearn_CustomFee/js/view/checkout/shipping/custom-fee</ item > </ item > < item name = "gift_fee_error_text" xsi:type = "array" > < item name = "sortOrder" xsi:type = "string" >35</ item > < item name = "component" xsi:type = "string" >uiComponent</ item > < item name = "config" xsi:type = "array" > < item name = "template" xsi:type = "string" >Magelearn_CustomFee/error_text</ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > < item name = "sidebar" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "summary" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "totals" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "fee" xsi:type = "array" > < item name = "component" xsi:type = "string" >Magelearn_CustomFee/js/view/checkout/cart/totals/fee</ item > < item name = "sortOrder" xsi:type = "string" >20</ item > < item name = "config" xsi:type = "array" > < item name = "template" xsi:type = "string" >Magelearn_CustomFee/checkout/cart/totals/fee</ item > <!-- <item name="title" xsi:type="string" translate="true">Custom Fee</item>--> </ item > </ item > </ item > </ item > < item name = "cart_items" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "details" xsi:type = "array" > < item name = "children" xsi:type = "array" > < item name = "subtotal" xsi:type = "array" > < item name = "component" xsi:type = "string" >Magento_Tax/js/view/checkout/summary/item/details/subtotal</ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ item > </ argument > </ arguments > </ referenceBlock > </ body > </ page > |
Now, as per the highlighted code above, we will add our JS Component file.
Add view/frontend/web/js/view/checkout/shipping/custom-fee.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | define([ 'jquery' , 'ko' , 'uiComponent' , 'Magento_Checkout/js/model/quote' , 'Magento_Catalog/js/price-utils' , 'Magento_Ui/js/modal/modal' ], function ($, ko, Component, quote, priceUtils,modal) { 'use strict' ; var fee_label = window.checkoutConfig.fee_label; var custom_fee_amount = window.checkoutConfig.custom_fee_amount; return Component.extend({ defaults: { template: 'Magelearn_CustomFee/checkout/shipping/custom-fee' , allowGiftFee: ko.observable(true), canVisibleCustomfeeBlock: window.checkoutConfig.show_hide_customfee_shipblock, getFormattedPrice: ko.observable(priceUtils.formatPrice(custom_fee_amount, quote.getPriceFormat())), getFeeLabel:ko.observable(fee_label) }, initialize: function () { this._super(); var self = this; $(document).on( 'change' , 'input[name="gift_fee"]' , function () { if ($(this).prop( 'checked' ) == true) { $( ".gift-fee-error" ).hide(); } }); } }); }); |
Now as per highlighted above code, we will add template file at view/frontend/web/template/checkout/shipping/custom-fee.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <div data-bind= "visible: canVisibleCustomfeeBlock" data-trigger= "trigger" > <input type= "checkbox" name= "gift_fee_check" data-bind= "checked: allowGiftFee" /> <span data-bind= "text: getFormattedPrice()" class = "price" style= "font-weight: 600" ></span> <span data-bind= "text: getFeeLabel()" ></span> </div> <div data-bind="mageInit: { 'Magento_Ui/js/modal/modal' :{ 'type' : 'popup' , 'title' : 'Custom Fee' , 'trigger' : '[data-trigger=trigger]' , 'responsive' : true, 'buttons' : [{ text: 'OK' , class : 'action' }] }}"> <div class = "content" >We are taking custom gift wrapping fee for this order. Please accept Custom Fee Checkbox before proceed.</div> </div> |
We will modify requirejs-config.js file and add some validation when going from next step from Shipping.
We will also add extension_attributes in payloadExtender to save its value further in database.
Add Magelearn/CustomFee/view/frontend/requirejs-config.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var config = { map: { "*" : { 'Magento_Checkout/js/model/shipping-save-processor/default' : 'Magelearn_CustomFee/js/model/shipping-save-processor/default' }, }, config: { mixins: { 'Magento_Checkout/js/model/shipping-save-processor/payload-extender' : { 'Magelearn_CustomFee/js/model/shipping-save-processor/payload-extender-mixin' : true } } } }; |
Now add Magelearn/CustomFee/view/frontend/web/js/model/shipping-save-processor/default.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ define([ 'ko' , 'Magento_Checkout/js/model/quote' , 'Magento_Checkout/js/model/resource-url-manager' , 'mage/storage' , 'Magento_Checkout/js/model/payment-service' , 'Magento_Checkout/js/model/payment/method-converter' , 'Magento_Checkout/js/model/error-processor' , 'Magento_Checkout/js/model/full-screen-loader' , 'Magento_Checkout/js/action/select-billing-address' , 'Magento_Checkout/js/model/shipping-save-processor/payload-extender' , 'jquery' , 'Magento_Ui/js/modal/alert' ], function ( ko, quote, resourceUrlManager, storage, paymentService, methodConverter, errorProcessor, fullScreenLoader, selectBillingAddressAction, payloadExtender, $, alert ) { 'use strict' ; return { /** * @return {jQuery.Deferred} */ saveShippingInformation: function () { var payload; var gift_fee = $( '[name="gift_fee_check"]' ).prop( "checked" ); if (gift_fee != true ){ alert({ title: $.mage.__( 'Error' ), content: $.mage.__( 'Please accept custom gift price for this order.' ) }); $( ".gift-fee-error" ).show(); // show hidden message $( '[name="gift_fee_check"]' ).focus(); return false ; } else { $( ".gift-fee-error" ).hide(); } if (!quote.billingAddress() && quote.shippingAddress().canUseForBilling()) { selectBillingAddressAction(quote.shippingAddress()); } payload = { addressInformation: { 'shipping_address' : quote.shippingAddress(), 'billing_address' : quote.billingAddress(), 'shipping_method_code' : quote.shippingMethod()[ 'method_code' ], 'shipping_carrier_code' : quote.shippingMethod()[ 'carrier_code' ] } }; payloadExtender(payload); fullScreenLoader.startLoader(); return storage.post( resourceUrlManager.getUrlForSetShippingInformation(quote), JSON.stringify(payload) ).done( function (response) { quote.setTotals(response.totals); paymentService.setPaymentMethods(methodConverter(response[ 'payment_methods' ])); fullScreenLoader.stopLoader(); } ).fail( function (response) { errorProcessor.process(response); fullScreenLoader.stopLoader(); } ); } }; }); |
Add Magelearn/CustomFee/view/frontend/web/js/model/shipping-save-processor/payload-extender-mixin.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | define([ 'jquery' , 'underscore' , 'mage/utils/wrapper' ], function ($, _, wrapper) { 'use strict' ; return function (payloadExtender) { return wrapper.wrap(payloadExtender, function (originalPayloadExtender, payload) { var gift_fee_val = $( '[name="gift_fee_check"]' ).prop( "checked" ) == true ? 1 : 0; payload = originalPayloadExtender(payload); _.extend(payload.addressInformation,{ extension_attributes: { 'gift_fee_check' : gift_fee_val } }); return payload }); }; }); |
Now as per defined in checkout_index_index.xml file, we will also add our error_text html file.
Add Magelearn/CustomFee/view/frontend/web/template/error_text.html file.
1 2 3 | <div class = "gift-fee-error message error" role= "alert" style= "display:none;" > <span class = "gift-fee-error-message" >Please accept gift fee custom amount for this order to further proceed.</span> </div> |
Now to display this custom fee at different places on frontend, we will modify different layout xml files.
Add Magelearn/CustomFee/view/frontend/layout/sales_order_view.xml file.
1 2 3 4 5 6 7 8 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceBlock name = "order_totals" > < block class = "Magelearn\CustomFee\Block\Sales\Totals\Fee" name = "fee" /> </ referenceBlock > </ body > </ page > |
As per highlighted code above, we will add Magelearn/CustomFee/Block/Sales/Totals/Fee.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | <?php namespace Magelearn\CustomFee\Block\Sales\Totals; class Fee extends \Magento\Framework\View\Element\Template { /** * @var \Magelearn\CustomFee\Helper\Data */ protected $_dataHelper ; /** * @var Order */ protected $_order ; /** * @var \Magento\Framework\DataObject */ protected $_source ; /** * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data */ public function __construct( \Magento\Framework\View\Element\Template\Context $context , \Magelearn\CustomFee\Helper\Data $dataHelper , array $data = [] ) { $this ->_dataHelper = $dataHelper ; parent::__construct( $context , $data ); } /** * Check if we nedd display full tax total info * * @return bool */ public function displayFullSummary() { return true; } /** * Get data (totals) source model * * @return \Magento\Framework\DataObject */ public function getSource() { return $this ->_source; } public function getStore() { return $this ->_order->getStore(); } /** * @return Order */ public function getOrder() { return $this ->_order; } /** * @return array */ public function getLabelProperties() { return $this ->getParentBlock()->getLabelProperties(); } /** * @return array */ public function getValueProperties() { return $this ->getParentBlock()->getValueProperties(); } /** * @return $this */ public function initTotals() { $parent = $this ->getParentBlock(); $this ->_order = $parent ->getOrder(); $this ->_source = $parent ->getSource(); // $store = $this->getStore(); $fee = new \Magento\Framework\DataObject( [ 'code' => 'fee' , 'strong' => false, 'value' => $this ->_source->getFee(), 'label' => $this ->_dataHelper->getFeeLabel(), ] ); $parent ->addTotal( $fee , 'fee' ); return $this ; } } |
Now we will define same XML nodes in different xml files.
We will add xml files to display custom totals inside Magelearn/CustomFee/view/frontend/layout
sales_email_order_creditmemo_items.xml
sales_email_order_invoice_items.xml
Now to display this custom fee at different places on adminhtml, we will modify different layout xml files.
Add Magelearn/CustomFee/view/adminhtml/layout/sales_order_view.xml file.
1 2 3 4 5 6 7 8 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceContainer name = "order_totals" > < block class = "Magelearn\CustomFee\Block\Adminhtml\Sales\Totals" name = "fee" /> </ referenceContainer > </ body > </ page > |
As per highlighted code above, we will add Magelearn/CustomFee/Block/Adminhtml/Sales/Totals.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | <?php namespace Magelearn\CustomFee\Block\Adminhtml\Sales; class Totals extends \Magento\Framework\View\Element\Template { /** * @var \Magelearn\CustomFee\Helper\Data */ protected $_dataHelper ; /** * @var \Magento\Directory\Model\Currency */ protected $_currency ; public function __construct( \Magento\Framework\View\Element\Template\Context $context , \Magelearn\CustomFee\Helper\Data $dataHelper , \Magento\Directory\Model\Currency $currency , array $data = [] ) { parent::__construct( $context , $data ); $this ->_dataHelper = $dataHelper ; $this ->_currency = $currency ; } /** * Retrieve current order model instance * * @return \Magento\Sales\Model\Order */ public function getOrder() { return $this ->getParentBlock()->getOrder(); } /** * @return mixed */ public function getSource() { return $this ->getParentBlock()->getSource(); } /** * @return string */ public function getCurrencySymbol() { return $this ->_currency->getCurrencySymbol(); } /** * * * @return $this */ public function initTotals() { $this ->getParentBlock(); $this ->getOrder(); $this ->getSource(); if (! $this ->getSource()->getFee()) { return $this ; } $total = new \Magento\Framework\DataObject( [ 'code' => 'fee' , 'value' => $this ->getSource()->getFee(), 'label' => $this ->_dataHelper->getFeeLabel(), ] ); $this ->getParentBlock()->addTotalBefore( $total , 'grand_total' ); return $this ; } } |
Add Magelearn/CustomFee/view/adminhtml/layout/sales_order_invoice_view.xml file.
1 2 3 4 5 6 7 8 9 10 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceBlock name = "invoice_totals" > < block class = "Magelearn\CustomFee\Block\Adminhtml\Sales\Order\Invoice\Totals" name = "fee" as = "fee" > </ block > </ referenceBlock > </ body > </ page > |
As per highlighted code above, we will add Magelearn/CustomFee/Block/Adminhtml/Sales/Order/Invoice/Totals.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?php namespace Magelearn\CustomFee\Block\Adminhtml\Sales\Order\Invoice; class Totals extends \Magento\Framework\View\Element\Template { /** * @var \Magelearn\CustomFee\Helper\Data */ protected $_dataHelper ; /** * Order invoice * * @var \Magento\Sales\Model\Order\Invoice|null */ protected $_invoice = null; /** * @var \Magento\Framework\DataObject */ protected $_source ; /** * OrderFee constructor. * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data */ public function __construct( \Magento\Framework\View\Element\Template\Context $context , \Magelearn\CustomFee\Helper\Data $dataHelper , array $data = [] ) { $this ->_dataHelper = $dataHelper ; parent::__construct( $context , $data ); } /** * Get data (totals) source model * * @return \Magento\Framework\DataObject */ public function getSource() { return $this ->getParentBlock()->getSource(); } public function getInvoice() { return $this ->getParentBlock()->getInvoice(); } /** * Initialize payment fee totals * * @return $this */ public function initTotals() { $this ->getParentBlock(); $this ->getInvoice(); $this ->getSource(); if (! $this ->getSource()->getFee()) { return $this ; } $total = new \Magento\Framework\DataObject( [ 'code' => 'fee' , 'value' => $this ->getSource()->getFee(), 'label' => $this ->_dataHelper->getFeeLabel(), ] ); $this ->getParentBlock()->addTotalBefore( $total , 'grand_total' ); return $this ; } } |
Add Magelearn/CustomFee/view/adminhtml/layout/sales_order_creditmemo_view.xml file.
1 2 3 4 5 6 7 8 9 | <? xml version = "1.0" ?> < page xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "urn:magento:framework:View/Layout/etc/page_configuration.xsd" > < body > < referenceBlock name = "creditmemo_totals" > < block class = "Magelearn\CustomFee\Block\Adminhtml\Sales\Order\Creditmemo\Totals" name = "fee" as = "fee" /> </ referenceBlock > </ body > </ page > |
As per highlighted code above, we will add Magelearn/CustomFee/Block/Adminhtml/Sales/Order/Creditmemo/Totals.php file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <?php namespace Magelearn\CustomFee\Block\Adminhtml\Sales\Order\Creditmemo; class Totals extends \Magento\Framework\View\Element\Template { /** * Order invoice * * @var \Magento\Sales\Model\Order\Creditmemo|null */ protected $_creditmemo = null; /** * @var \Magento\Framework\DataObject */ protected $_source ; /** * @var \Magelearn\CustomFee\Helper\Data */ protected $_dataHelper ; /** * OrderFee constructor. * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data */ public function __construct( \Magento\Framework\View\Element\Template\Context $context , \Magelearn\CustomFee\Helper\Data $dataHelper , array $data = [] ) { $this ->_dataHelper = $dataHelper ; parent::__construct( $context , $data ); } /** * Get data (totals) source model * * @return \Magento\Framework\DataObject */ public function getSource() { return $this ->getParentBlock()->getSource(); } public function getCreditmemo() { return $this ->getParentBlock()->getCreditmemo(); } /** * Initialize payment fee totals * * @return $this */ public function initTotals() { $this ->getParentBlock(); $this ->getCreditmemo(); $this ->getSource(); if (! $this ->getSource()->getFee()) { return $this ; } $fee = new \Magento\Framework\DataObject( [ 'code' => 'fee' , 'strong' => false, 'value' => $this ->getSource()->getFee(), 'label' => $this ->_dataHelper->getFeeLabel(), ] ); $this ->getParentBlock()->addTotalBefore( $fee , 'grand_total' ); return $this ; } } |
We will also add different layout file for admin at Magelearn/CustomFee/view/adminhtml/layout
sales_order_creditmemo_new.xml
0 Comments On "Add Extra Fee to customer for each order based on fixed amount with or Without tax at Checkout Magento2"