-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Description
Preconditions and environment
- Magento version: 2.4.5-p1 (Vanilla Magento 2 with Sample Data)
- PHP Version: 8.1.2
- Maria DB: 10.4
- Apache: 2.4.52
- Composer: 2.2.6
- ElasticSearch: 7.17.8
Steps to reproduce
In app/code/Magento/Sales/Model/Order/Invoice.php, three invoice states are defined.
/**#@+
* Invoice states
*/
const STATE_OPEN = 1;
const STATE_PAID = 2;
const STATE_CANCELED = 3;
And, in app/code/Magento/Sales/Model/Order/Invoice/Total/Discount.php, the Discount::collect()
function checks if the shipping discount has already been invoiced with the private function Discount::isShippingDiscount()
/**
* Checking if shipping discount was added in previous invoices.
* So basically if we have invoice with positive discount and it
* was not canceled we don't add shipping discount to this one.
*/
if ($this->isShippingDiscount($invoice)) {
$totalDiscountAmount = $totalDiscountAmount + $invoice->getOrder()->getShippingDiscountAmount();
$baseTotalDiscountAmount = $baseTotalDiscountAmount +
$invoice->getOrder()->getBaseShippingDiscountAmount();
}
Then, the function loops the invoice collection if the shipping discount has been applied to earlier invoices.
private function isShippingDiscount(Invoice $invoice): bool
{
$addShippingDiscount = true;
foreach ($invoice->getOrder()->getInvoiceCollection() as $previousInvoice) {
if ($previousInvoice->getDiscountAmount()) {
$addShippingDiscount = false;
}
}
return $addShippingDiscount;
}
The condition to test the discount amount should also test if the invoice is not Canceled
in case the invoice, for some reason, has been removed because the new invoice needs to add the shipping discount which has been canceled.
So, it should be...
private function isShippingDiscount(Invoice $invoice): bool
{
$addShippingDiscount = true;
foreach ($invoice->getOrder()->getInvoiceCollection() as $previousInvoice) {
if ($previousInvoice->getState() != \Magento\Sales\Model\Order\Invoice::STATE_CANCELED && $previousInvoice->getDiscountAmount()) {
$addShippingDiscount = false;
}
}
return $addShippingDiscount;
}
Steps to produce
- Deploy the sample data
- Navigate MARKETING > Cart Price Rules
- Create a discount that applies to the shipping amount. For example, this is a discount on Shipping Fee when the order has more than 10 items:
- Under Conditions: Total Items Quantity equals or greater than 10,
- Under Actions:
- Apply: Percent of product price discount
- Discount Amount: 30
- Apply to Shipping Amount: Yes <--- this is important
- Create an order to get the shipping discount
- Make a partial invoice (this will create an invoice with shipping & handling with the discount)
- Somehow change the state of the invoice to "STATE_CANCEL" (the easier way is by directly changing the state from DB)
UPDATE sales_invoice SET state = 3 WHERE entity_id = {entity id of the invoice}; UPDATE sales_invoice_grid SET state = 3 WHERE entity_id = {entity id of the invoice};
- Make the invoice for the remaining (this will create an invoice without the discount on the shipping & handling fee)
NOTE: the above steps use "partial invoice" only to make the reproduction of the issue easier. It is possible to reproduce with a standard full invoice but you must somehow cancel the invoice and also revert the invoiced quantity of each product line in the order so that the order can be invoiced again.
Expected result
The invoice should include the discount on shipping if the earlier invoices with the discount have been canceled.
Actual result
The shipping discount does not get included in the subsequent invoices if the earlier "Cancelled" invoice includes the shipping discount.
Additional information
Although it is not possible to have an invoice with the state, STATE_CANCELED, in the vanilla Magento setup, the state is defined in the code and some payment gateways make use of the state to indicate the issue in the payment capture.
In such cases, Magento Core should check for the state of the invoice and ignore those canceled invoices, so that the shipping discount is correctly included when the invoice for the order is re-issued. (The comment in the code says that "So basically if we have invoice with positive discount and it was not canceled we don't add shipping discount to this one." )
Release note
Additional validation of the invoice state so that a new invoice includes the shipping discount if the earlier invoices with the discount are canceled.
Triage and priority
- Severity: S0 - Affects critical data or functionality and leaves users without workaround.
- Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
- Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
- Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
- Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.