2

i have a 10% Discount for more than 6 items. With several items i get discounts like 12.48.

How can i round this discount amount, so that this amount is also applied for the cart total amount?

The amount should rounded to either half or full number. The example above should give 12.50

another option would be to round the cart total. i know how to round the amounts for the frontend, but they wont be passed along for the checkout process. I the payment gateway, the not rounded price is always used.

Here is code for my custom adjuster, to round the cart Total Price, together with the given answer to round the price. But its not working, the total price is not rounded:

use craft\commerce\base\AdjusterInterface;
use craft\commerce\elements\Order;
use craft\commerce\models\OrderAdjustment;

class RoundOrderAdjuster implements AdjusterInterface
{

  const ADJUSTMENT_TYPE = 'discount';

  public function adjust(Order $order): array
  {
    $adjustments = [];
    $adjustment = new OrderAdjustment;

    $basePrice = $order->getTotalPrice();
    $roundAmount = round($basePrice, 1, PHP_ROUND_HALF_UP);

    $adjustment->amount = $roundAmount;

    $adjustments[] = $adjustment;

    return $adjustments;
  }
}

1 Answer 1

3

Are you using a custom adjuster for the discount? If yes, then just use round() to round up.

...
$num = $adjustment->amount; // 12.48
$adjustment->amount = round($num, 1, PHP_ROUND_HALF_UP); // 12.5(or same as 12.50)
...

UPD: It looks like I understand what you want. Change the price of the entire order, for example, the total of the current cart (active order)? In my field of view, I have a bad news because it is not fully implementable. The main problem is that you cannot simply change the value of the total, for example:

$myOrder->setTotal(1337.37);
// Or
$myOrder->total = 1337.37;
$myOrder->save();

This is because most of the properties of the order (element) are only readable.

However, you can change values directly via Records. For example, on an event after updating/saving an order.

use craft\commerce\elements\Order;
use craft\events\ModelEvent;
use yii\base\Event;
use craft\commerce\records\Order as OrderRecord;

...

Event::on(
    Order::class,
    Order::EVENT_AFTER_SAVE,
    function (ModelEvent $e) {
        $order = $e->sender;
        if (!$order->id) {
            return;
        }
        $orderRecord = OrderRecord::findOne($order->id);
        if (!$orderRecord) {
            throw new Exception('Invalid order ID: ' . $this->id);
        }

        $orderRecord->total = '1337.37';
        $lineItems = $orderRecord->lineItems;
        foreach ($lineItems as $item) {
            $item->total = 1337.37;
            $item->subtotal = 1337.37;
            // Change whatever you need via $item->.... = ...;
            $item->save();
        }
    }
);

Unfortunately, for example, a different price will be shown in the admin panel, since it will calculate through the getTotal() method, i.e. salePrice * qty + discounts/taxes That is, not everywhere the value is taken directly from the database, however, I think it can still be used in invoices / emails.

And as another option, you can change the output of the cart before the controller sends the data in JSON format.

Event::on(
    BaseFrontEndController::class,
    BaseFrontEndController::EVENT_MODIFY_CART_INFO,
    function (Event $e) {
        // $e->cart mostly useless here since it's Order Element(property-read issue)
        $cartInfo = $e->cartInfo;
        // Change whatever you need depends on what you get from the cart JSON response 
        $cartInfo['itemTotal'] = 1337.37;
        $cartInfo['storedItemTotal'] = 1337.37;
        // $cartInfo['....'] = ...
        // ...
        $e->cartInfo = $cartInfo;

        return $e;
    }
);

In general, I recommend that you look at the available events for craft commerce. Unfortunately, craft commerce is extremely inconvenient at some points, for example, like here, since you cannot easily change the total price of the order.

UPD2:

use craft\commerce\elements\Order;
use craft\events\ModelEvent;
use yii\base\Event;
use craft\commerce\records\OrderAdjustment as OrderAdjustmentRecorder; 

Event::on(
    Order::class,
    Order::EVENT_AFTER_SAVE,
    function (ModelEvent $e): void {
        $order = $e->sender;
        if (!$order->id) {
            return;
        }

        $lineItems = $order->lineItems;
        foreach ($lineItems as $item) {
            $adjustments = $item->adjustments;
            foreach($adjustments as $adjustment) {
                // !!! Change your DISCOUNT NAME here
                if ($adjustment['name'] !== 'MY PRODUCTS DISCOUNT NAME') {
                    continue;
                }

                $adjustmentRecord = OrderAdjustmentRecorder::findOne($adjustment->id);
                if (!$adjustmentRecord) {
                    throw new Exception('Invalid adjustment ID: ' . $adjustment->id);
                }

                $adjustmentRecord->amount = round($adjustment->amount, 1, PHP_ROUND_HALF_UP);
                $adjustmentRecord->save();
            }
        }
    }
);
8
  • thanks, this looks good, but i dont have a custom adjuster. the discount is something custom somebody else made, and i dont see really where the amount i being output. i add code to my question, where i try to adjust the totalCartPrice, together with your answer. Unfortunatly it is not working (my adjuster), the Total Price is not rounded.
    – Henrik
    Commented Jun 1, 2022 at 11:38
  • @Henrik i updated my answer for you.
    – RomanAvr
    Commented Jun 2, 2022 at 7:32
  • thanks for your profound answer! it sounds like it would be best to alter the discount price. Before i will try your second option with the event. I guess this Event::on would go into a module?
    – Henrik
    Commented Jun 2, 2022 at 7:44
  • Exactly. The event may have been implemented through your own module/plugin.
    – RomanAvr
    Commented Jun 2, 2022 at 7:45
  • Having difficulties getting your events running. ...Besides that: you wrote its not possible to change the value of the total. but can i change the itemSubTotal? Meaning: item cost 10€, gets discount of 1.02€, itemSubTotal is now 8.98€, desired rounded itemSubTotal should be 8.95€
    – Henrik
    Commented Jun 2, 2022 at 8:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.