Apply Discount in Checkout Total Amount?

How to configure and adapt Aimeos based shops as developer
Forum rules
Always add your Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
User avatar
PaoloLegaspi
Posts: 27
Joined: 07 Nov 2024, 15:02

Apply Discount in Checkout Total Amount?

Post by PaoloLegaspi » 03 Dec 2024, 10:39

Hi,
In the project I’m working on, we want to give customers a 5% discount if they add their national ID during registration or in their profile. I’ve already added the necessary attribute to the users table.

Now, I’m looking to implement this functionality in our checkout process using Aimeos. The goal is for the 5% discount to automatically apply to the order total/order.price for users who have provided their national ID.

Is there a specific part in the documentation or a guide that covers this type of customization? Any help or pointers would be greatly appreciated!

Code: Select all

Aimeos version: Aimeos Headless 2024.07.*
PHP version: 8.2
Environment: Mac
//Frontend view
Attachments
Screenshot 2024-12-03 at 8.28.27 PM.png
Screenshot 2024-12-03 at 8.28.27 PM.png (53.85 KiB) Viewed 26957 times

User avatar
PaoloLegaspi
Posts: 27
Joined: 07 Nov 2024, 15:02

Re: Apply Discount in Checkout Total Amount?

Post by PaoloLegaspi » 04 Dec 2024, 05:38

I’m thinking of creating a Basket Plugin Provider to handle this. My idea is to dynamically set the customer’s discount percentage, check the authenticated user, and apply the calculated discount to the total product price in the basket.

Does this approach make sense? Is it possible to achieve this with a custom basket plugin in Aimeos?

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: Apply Discount in Checkout Total Amount?

Post by aimeos » 04 Dec 2024, 08:45

Yes, makes sense and you need a basket plugin for that: https://aimeos.org/docs/latest/provider ... t-plugins/
The alternative would be a coupon provider but it's discount would be only applied if the customer enters a coupon code.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

User avatar
PaoloLegaspi
Posts: 27
Joined: 07 Nov 2024, 15:02

Re: Apply Discount in Checkout Total Amount?

Post by PaoloLegaspi » 04 Dec 2024, 09:36

Thanks for getting back to me!

Do you know of any examples or references I could check out? Especially something that shows how to recalculate the basket total with the discount.

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: Apply Discount in Checkout Total Amount?

Post by aimeos » 05 Dec 2024, 17:19

There's no basket plugin yet that suits as an example for what you need to do but use these steps:
1.) Create a discount product like done here: https://github.com/aimeos/aimeos-core/b ... #L224-L238
2.) Add the discount product to the order using addProduct()
3.) Check if the event occurs again and update the discount product in that case
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

User avatar
PaoloLegaspi
Posts: 27
Joined: 07 Nov 2024, 15:02

Re: Apply Discount in Checkout Total Amount?

Post by PaoloLegaspi » 15 Dec 2024, 13:40

Thank you for the guides! I followed the steps you provided and used the percentage rebate from the coupon provider to set up percentage discounts. It works great and adds the discount product to the basket.

However, I ran into an issue: when a product in the basket is about to go out of stock, and the customer adds more quantity (by clicking the plus button), it shows a "product out of stock" message but still adds the discount product and calculates its price. Ideally, this shouldn’t happen. Also, could you let me know if my implementation is correct and follows Aimeos standards?

//ClientDiscount Basket Plugin

Code: Select all

public function register(\Aimeos\MShop\Order\Item\Iface $p): \Aimeos\MShop\Plugin\Provider\Iface
    {
        $plugin = $this->object();

        $p->attach($plugin, 'addProduct.after');
        $p->attach($plugin, 'deleteProduct.after');
        $p->attach($plugin, 'setProducts.after');
        $p->attach($plugin, 'check.before');

        return $plugin;
    }
    
    public function update(\Aimeos\MShop\Order\Item\Iface $order, string $action, $value = null)
    {
        $percent = (float) $this->getConfigValue('rebate.percent');
        $prodcode = $this->getConfigValue('rebate.productcode');
        $user = $this->context()->user();

        if ($percent == 0 || $prodcode === null || empty($user) || $user->getPersonalCode() === null) {
            return $this;
        }

        $products = $order->getProducts();

        if (count($products) === 0) {
            $this->removeDiscountProduct($order, $prodcode);
            return $this;
        }

        $price = $this->calcPrice($order);
        $rebate = $this->round(($price->getValue() + $price->getCosts() + $price->getRebate()) * $percent / 100);

        $this->removeDiscountProduct($order, $prodcode);

        if ($rebate > 0) {
            $discountProduct = $this->createDiscountProduct($prodcode, 1, 0, $rebate);
            $order->addProduct($discountProduct);
        }

        return $this;
    }
    
    public function createDiscountProduct(string $prodcode, float $quantity = 1, float $price = 0, float $rebate = 0): \Aimeos\MShop\Order\Item\Product\Iface
    {
        $productManager = \Aimeos\MShop::create($this->context(), 'product');
        $product = $productManager->find($prodcode, ['price']);

        $priceManager = \Aimeos\MShop::create($this->context(), 'price');
        $priceItem = $priceManager->create()->setValue(-$rebate)->setRebate($rebate);

        return \Aimeos\MShop::create($this->context(), 'order/product')->create()
            ->copyFrom($product)
            ->setQuantity($quantity)
            ->setStockType( 'default' )
            ->setPrice($priceItem)
            ->setFlags(\Aimeos\MShop\Order\Item\Product\Base::FLAG_IMMUTABLE);
    }
    
    protected function calcPrice(\Aimeos\MShop\Order\Item\Iface $base): \Aimeos\MShop\Price\Item\Iface
    {
        $price = \Aimeos\MShop::create($this->context(), 'price')->create();

        foreach ($base->getProducts() as $product) {
            $price = $price->addItem($product->getPrice(), $product->getQuantity());
        }

        return $price;
    }
    protected function round(float $number): float
    {
        $prec = $this->getConfigValue('percent.precision', 2);
        $value = $this->getConfigValue('percent.roundvalue', 0);

        if ($value == 0) {
            return round($number, $prec);
        }

        return round(round($number / $value) * $value, $prec);
    }
    protected function removeDiscountProduct(\Aimeos\MShop\Order\Item\Iface $order, string $prodcode): void
    {
        $products = $order->getProducts();

        foreach ($products as $position => $product) {
            if ($product->getProductCode() === $prodcode) {
                $order->deleteProduct($position);
            }
        }
    }
    
I am using Aimeos with frontend to show the issue screenshot
Attachments
Screenshot 2024-12-15 at 9.22.53 PM.png
Screenshot 2024-12-15 at 9.22.53 PM.png (161.28 KiB) Viewed 15552 times

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: Apply Discount in Checkout Total Amount?

Post by aimeos » 16 Dec 2024, 13:47

Seems like your calculation in your code is wrong. Check the value of the price, costs and rebate you sum up
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

User avatar
PaoloLegaspi
Posts: 27
Joined: 07 Nov 2024, 15:02

Re: Apply Discount in Checkout Total Amount?

Post by PaoloLegaspi » 16 Dec 2024, 15:08

Hello
I checked out the code, and it’s calculating the price correctly when a product and quantity are added. The issue seems to show up only when the product is down to its last stock. If the user clicks the "Add Quantity" (+) button at that point, it incorrectly adds up a price to the rebate product price. Interestingly, this only happens on the first click—if the button is clicked multiple times after that, it no longer affects the price. I also tested this with coupon discount products, and it looks like the rebate price calculation stops entirely once the product is out of stock.

Post Reply