<?php
declare(strict_types=1);
namespace VendorModuleModelQuoteTotal;
use MagentoQuoteApiDataShippingAssignmentInterface;
use MagentoQuoteModelQuote;
use MagentoQuoteModelQuoteAddressTotal;
use MagentoQuoteModelQuoteAddressTotalAbstractTotal;
use MagentoQuoteModelQuoteItem as QuoteItem;
use MagentoQuoteModelQuoteAddress;
class CustomDiscount extends AbstractTotal
{
private const CODE = 'custom_discount';
private const LABEL = 'Points Redeemed';
public function __construct(
) {
$this->setCode(self::CODE);
}
/**
* Collect totals for the quote.
*
* @param Quote $quote
* @param ShippingAssignmentInterface $shippingAssignment
* @param Total $total
* @return self
*/
public function collect(
Quote $quote,
ShippingAssignmentInterface $shippingAssignment,
Total $total
): self {
parent::collect($quote, $shippingAssignment, $total);
$address = $shippingAssignment->getShipping()->getAddress();
$items = $shippingAssignment->getItems();
$subtotal = $total->getSubtotal();
if (!$items || $subtotal <= 0.0) {
return $this;
}
$dynamicDiscount = 0.0;
$myPoints = 0;
$storeId = (int) $quote->getStoreId();
foreach ($items as $item) {
[$discount, $points] = $this->calculateDiscountAndPoints($item, $storeId);
$dynamicDiscount += $discount;
$myPoints += $points;
$item->setDiscountAmount($item->getDiscountAmount() + abs($discount));
$item->setBaseDiscountAmount($item->getBaseDiscountAmount() + abs($discount));
}
$cartDiscount = $total->getDiscountAmount() ?: 0.0;
$totalDiscount = $dynamicDiscount + $cartDiscount;
$totalDiscount = max(-$subtotal, $totalDiscount);
if (abs($totalDiscount) < 0.0001) {
return $this;
}
$this->applyTotals(
$quote,
$total,
$shippingAssignment,
$dynamicDiscount,
$totalDiscount,
$myPoints,
$cartDiscount,
$address
);
return $this;
}
/**
* Fetch totals for the quote.
*
* @param Quote $quote
* @param Total $total
* @return array
*/
public function fetch(Quote $quote, Total $total): array
{
$discountAmount = (float)$total->getDiscountAmount() ?: 0.0;
$dynamicDiscount = 0.0;
$myPoints = 0;
$storeId = (int) $quote->getStoreId();
foreach ($quote->getAllItems() as $item) {
[$dynamicDiscount, $points] = $this->calculateDiscountAndPoints($item, $storeId);
$myPoints += $points;
$dynamicDiscount += $dynamicDiscount;
}
if (abs($dynamicDiscount) < 0.0001) {
return [];
}
return [
'code' => self::CODE,
'title' => $myPoints > 0 ? __($myPoints . ' ' . self::LABEL) : __('Discount'),
'value' => $dynamicDiscount,
];
}
/**
* Calculate the discount and points for a given item.
*
* @param QuoteItem $item
* @param int $storeId
* @return array
*/
private function calculateDiscountAndPoints($item, int $storeId): array
{
// my custom logic to calculate discount and points
return [$totalDiscount, $myPoints];
}
/**
* Apply the calculated totals to the quote.
*
* @param Quote $quote
* @param Total $total
*
* @param float $dynamicDiscount
* @param float $totalDiscount
* @param int $myPoints
* @param float $cartDiscount
* @param Address $address
* @return void
*/
private function applyTotals(
Quote $quote,
Total $total,
$shippingAssignment,
float $dynamicDiscount,
float $totalDiscount,
int $myPoints,
float $cartDiscount,
Address $address
): void {
$descriptions = [];
if ($total->getDiscountDescription()) {
$descriptions[] = (string) $total->getDiscountDescription();
}
if ($myPoints > 0) {
$descriptions[] = $myPoints . ' ' . self::LABEL;
}
$combinedDesc = implode(', ', $descriptions);
$total->setDiscountDescription($combinedDesc);
$total->addTotalAmount(self::CODE, $dynamicDiscount);
$total->addBaseTotalAmount(self::CODE, $dynamicDiscount);
$total->setDiscountAmount($total->getDiscountAmount() + $dynamicDiscount);
$total->setBaseDiscountAmount($total->getBaseDiscountAmount() + $dynamicDiscount);
$total->setCustomDiscountAmount($dynamicDiscount);
$total->setBaseCustomDiscountAmount($dynamicDiscount);
// Adjust subtotal with discount
$total->setSubtotalWithDiscount($total->getSubtotal() + $total->getDiscountAmount());
$total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $total->getBaseDiscountAmount());
$quote->setDiscountAmount($total->getDiscountAmount());
$quote->setBaseDiscountAmount($total->getBaseDiscountAmount());
// $address->setDiscountDescription($combinedDesc);
$address->setDiscountAmount($total->getDiscountAmount());
$address->setBaseDiscountAmount($total->getBaseDiscountAmount());
$address->setCustomDiscountAmount($total->getCustomDiscountAmount());
$address->setBaseCustomDiscountAmount($total->getBaseCustomDiscountAmount());
$address->setSubtotalWithDiscount($total->getSubtotalWithDiscount());
$address->setBaseSubtotalWithDiscount($total->getBaseSubtotalWithDiscount());
}
}
<?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="custom_discount"
instance="VendorModuleModelQuoteTotalCustomDiscount"
sort_order="310"/>
</group>
</section>
</config>
With this code i have an issue, when a redeemed product is in the cart, the shipping price is always 0, even though the subtotal_with_discount is below the free shipping threshold (using table rates). All other data, like subtotal and base_* fields, are saved correctly.
If I try setting the shipping amount manually, it displays correctly up to the payment page but does not appear in the order receipt after the order is placed and .
My table rate condition is set to package_value_with_discount.
Can anyone help clarify and resolve this issue?