cdmanager / magento-min-order-fee
Magento 2 module that displays a minimum order fee as a separate total row on orders, invoices and credit memos, including admin, storefront, emails and PDFs. The fee itself is calculated and persisted by an external system.
Package info
github.com/cdmanager/magento-min-order-fee
Type:magento2-module
pkg:composer/cdmanager/magento-min-order-fee
Requires
- php: >=7.4
- magento/framework: ^102.0 || ^103.0 || ^104.0
- magento/module-sales: ^102.0 || ^103.0 || ^104.0
README
A Magento 2 module that shows a "Small Order Fee" as a separate total row on orders, invoices and credit memos — in the admin panel, on the storefront, in sales emails and in invoice/credit memo PDFs.
The module is designed for setups where orders are created outside the Magento checkout (e.g. written directly to the Magento database by an external backend). It therefore does not calculate the fee; it only:
- Schema — adds fee columns to
sales_order,sales_invoiceandsales_creditmemo. - Display — renders the fee row between Subtotal and Grand Total on admin order/invoice/credit memo screens, storefront order view/print, order emails and PDFs.
- Consistency — registers invoice and credit memo total collectors so that invoices/credit memos created in the admin include the fee in their
grand_total. - API — exposes the fee via extension attributes on the order, invoice and credit memo REST/SOAP APIs.
Requirements
- Magento 2.3.x / 2.4.x (tested on 2.4.7)
- PHP 7.4+
Installation
Via Composer (Packagist)
In your Magento root directory:
composer require cdmanager/magento-min-order-fee
Via Composer (directly from GitHub)
If the package is not (yet) available on Packagist, add the repository to your Magento project's composer.json first:
composer config repositories.magento-min-order-fee vcs https://github.com/cdmanager/magento-min-order-fee composer require cdmanager/magento-min-order-fee
Enable the module
bin/magento module:enable Chefsdeal_MinOrderFee
bin/magento setup:upgrade
bin/magento setup:di:compile # production mode
bin/magento cache:flush
setup:upgrade applies the declarative schema (etc/db_schema.xml) and creates the fee columns.
Data contract
The external system that creates orders must populate the following columns on sales_order:
| Column | Type | Description |
|---|---|---|
min_order_fee |
decimal(20,4) | Total fee in order currency |
base_min_order_fee |
decimal(20,4) | Total fee in base currency |
min_order_fee_details |
text (JSON) | Optional per-brand breakdown, e.g. [{"brand_id":1,"label":"Small Order Fee for Winco","amount":25.0}] |
Rules:
-
The fee is not an order item.
sales_order.subtotalcontains products only;grand_totalincludes the fee:grand_total = subtotal + shipping + tax − discount + min_order_fee -
The fee is tax-free — it must not affect
tax_amount. -
In a single-currency setup,
min_order_fee == base_min_order_fee.
Invoices and credit memos
The module's total collectors only run inside Magento's collectTotals() flow (admin "Invoice"/"Credit Memo" buttons, REST API creation, payment-capture auto-invoicing). In that flow the collector derives the fee from sales_order, writes the sales_invoice / sales_creditmemo columns and adds the fee to the document's grand_total — do not pre-fill the columns there.
If the external system writes sales_invoice / sales_creditmemo rows directly to the database, collectTotals() never runs, so it must populate the columns itself:
- Invoice: copy
min_order_fee/base_min_order_feefrom the order. Do not add the fee to the invoicegrand_totalagain — the order-levelgrand_totalbeing copied already includes it. Missing columns only break the display (no fee row on invoice view/PDF); the totals stay correct. - Credit memo: depends on the refund policy. If the fee is refundable, copy the remaining fee (
order fee − fee already refunded on other credit memos) and include it in the credit memogrand_total. If the fee is non-refundable, write an explicit0so the intent is visible instead of a silentNULL. - ⚠️ Never combine both paths: writing the columns directly and letting a collector run on the same document double-counts the fee in
grand_total.
The breakdown tooltip falls back to the order's min_order_fee_details, so invoices/credit memos don't need a details column.
Verification
After deploying:
- Check that the columns exist:
SHOW COLUMNS FROM sales_order LIKE 'min\_order\_fee%'. - Create an order with a fee via the external backend; verify that the "Small Order Fee" row appears between Subtotal and Grand Total on the admin order view and that
grand_totalreconciles. - Create an invoice in the admin → the fee row appears in the invoice totals and
grand_totalmatches the order. - Create a full/partial credit memo → the fee appears in the credit memo totals and is not double-counted (order fee − already refunded).
- Verify the fee row in the order confirmation email and the invoice PDF.
- For multi-brand orders, hover over the row label → the tooltip shows the per-brand breakdown from
min_order_fee_details.
File map
├── registration.php
├── etc/
│ ├── module.xml
│ ├── db_schema.xml # fee columns
│ ├── db_schema_whitelist.json
│ ├── extension_attributes.xml # order/invoice/creditmemo API fields
│ ├── sales.xml # invoice & creditmemo total collectors
│ └── config.xml # PDF (invoice/creditmemo) total row
├── Model/Total/
│ ├── Invoice/MinOrderFee.php # adds the fee to invoice grand_total
│ └── Creditmemo/MinOrderFee.php # adds the fee to creditmemo grand_total
├── Block/
│ ├── Adminhtml/Totals/MinOrderFee.php # admin order/invoice/creditmemo totals
│ └── Order/Totals/MinOrderFee.php # storefront + email totals
└── view/
├── adminhtml/
│ ├── layout/ # sales_order_view, invoice view/new, creditmemo view/new
│ └── templates/order/totals/min_order_fee.phtml
└── frontend/
├── layout/ # sales_order_view, sales_order_print, sales_email_order_items
└── templates/order/totals/min_order_fee.phtml
Notes
-
Email/PDF block names can be theme-dependent. The
order_totalslayout reference works with the standard themes; a customized theme may need extra layout handles for invoice/credit memo emails (sales_email_order_invoice_items,sales_email_order_creditmemo_items). -
Tooltip is currently a plain
titleattribute (native browser tooltip). A rich tooltip requires a theme-side JS/CSS component. -
If you add new columns, regenerate the whitelist with:
bin/magento setup:db-declaration:generate-whitelist --module-name=Chefsdeal_MinOrderFee
License
GPL-3.0-only — see LICENSE.