Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions purchase_discount/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import wizard
16 changes: 16 additions & 0 deletions purchase_discount/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "purchase discount",
"version": "1.0.0",
"depends": ["base", "purchase"],
"author": "Mehul Kotak",
"category": "Task-1",
"description": "This perfrom global discount in purchase",
"license": "LGPL-3",
"auto_install": True,
"installable": True,
"data": [
"security/ir.model.access.csv",
"wizard/purchase_order_discount.xml",
"views/purchase_order_view.xml",
],
}
2 changes: 2 additions & 0 deletions purchase_discount/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_purchase_order_discount,access_purchase_order_discount,model_purchase_order_discount,purchase.group_purchase_user,1,1,1,1
16 changes: 16 additions & 0 deletions purchase_discount/views/purchase_order_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_purchase_order_form" model="ir.ui.view">
<field name="name">purchase.order.view.form.discount</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']" position="after">
<div class="d-flex justify-content-end mt-2 mb-2">
<button string="Discount" name="%(purchase_discount.action_purchase_order_discount)d" type="action" class="btn btn-secondary"/>
</div>
</xpath>
</field>
</record>
</odoo>

1 change: 1 addition & 0 deletions purchase_discount/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import purchase_order_discount
66 changes: 66 additions & 0 deletions purchase_discount/wizard/purchase_order_discount.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError


class PurchaseOrderDiscount(models.TransientModel):
_name = "purchase.order.discount"
_description = "Apply Global Discount on Purchase Order"

discount = fields.Float(string="discount", required=True)
discount_type = fields.Selection(
[("amount", "$"), ("percent", "%")],
default="percent",
string="Discount Type",
required=True,
)
discount_percent = fields.Float(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't discount_type also be required?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right it should be required. i will do it.

compute="_calculate_percentage", string="Calculated Percentage"
)
order_id = fields.Many2one(
"purchase.order",
string="Purchase Order",
required=True,
)

@api.constrains("discount", "discount_type")
def _check_discount_limit(self):
if (
self.discount < 0 or self.discount > 100
) and self.discount_type == "percent":
raise ValidationError("Discount value is invalid")

def _get_base_untaxed_amount(self, order):
total = 0.0
for line in order.order_line:
total += line.price_unit * line.product_qty
return total

@api.depends("discount", "discount_type")
def _calculate_percentage(self):
base_amount = self._get_base_untaxed_amount(self.order_id)
if self.discount_type == "percent":
self.discount_percent = self.discount
elif base_amount > 0:
self.discount_percent = (self.discount * 100) / base_amount
else:
self.discount_percent = 0

def action_apply_discount(self):
self.ensure_one()
if not self.order_id.order_line:
raise UserError("There are no lines on this order to discount.")

base_amount = self._get_base_untaxed_amount(self.order_id)

if self.discount_type == "percent":
target_discount = self.discount
else:
if base_amount <= 0:
raise UserError("Cannot apply an amount discount to a $0 order.")
target_discount = (self.discount * 100) / base_amount
Comment on lines +57 to +60
Copy link

@kcv-odoo kcv-odoo Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right? can you explain what is done and why it should be like this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically, if discount_type is 'amount', it first checks whether the base price is greater than zero to avoid a division-by-zero error. If the base amount is valid, then it calculates the percentage from the given discount amount.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it give desire result?

IMO If I say I am giving 100 rupee fixed discount on the order then it should deduct 100 rupees from total order amount regardless of number of order line, regardless of total amount

so I didn't understand how you trying to achieve it and is it working properly?

(PS. I would be better to add UI efferct instead raising User error on every step of the flow like here we could hide Discount button if order does not have amount by computing base_amount somewhere)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it give desire result?

IMO If I say I am giving 100 rupee fixed discount on the order then it should deduct 100 rupees from total order amount regardless of number of order line, regardless of total amount

so I didn't understand how you trying to achieve it and is it working properly?

(PS. I would be better to add UI efferct instead raising User error on every step of the flow like here we could hide Discount button if order does not have amount by computing base_amount somewhere)

Yes sir you are right but in this task https://www.odoo.com/odoo/project/18468/tasks/5926008 they are mention to perform global discount and global discount can be (% or amount) and what you tell me is fixed amount discount which is different type of discount which we can see in SO ,but you can also check mock-up of the task in which they clearly give us that if we global discount provide as amount then we have to first convert it into percentage and then have to apply to each products in order line. This is the thing which i understood from the task mock-up .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess, I did see that but does it mean I get total amount convert it to % and then apply same percentage on each line, will it give desire result?

I am not able to test it because of constrain (which should only apply on % type)
image

and percentage is also not computing properly, I don't think 419.75 should be 52% of 36500 🤔
image

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what you tell me is fixed amount discount which is different type of discount which we can see in SO

It should kind of work same as fix discount AFAIK using discount field on order line, like if I apply 100 discount then it should deduct 100 from order total


# Final check to ensure calculated amount doesn't exceed 100%
if target_discount > 100:
raise UserError("The discount amount exceeds the total value of the order.")

self.order_id.order_line.write({"discount": target_discount})
44 changes: 44 additions & 0 deletions purchase_discount/wizard/purchase_order_discount.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>

<record id="action_purchase_order_discount" model="ir.actions.act_window">
<field name="name">Discount</field>
<field name="res_model">purchase.order.discount</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'default_order_id': active_id}</field>
</record>

<record id="purchase_order_line_wizard_form" model="ir.ui.view">
<field name="name">purchase.order.discount.form</field>
<field name="model">purchase.order.discount</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<div class="d-flex align-items-center gap-2">
<label for="discount" string="Discount"/>
<div class="col-1">
<field name="discount"/>
</div>
<div class="col-1">
<field name="discount_type" nolabel="1"/>
</div>
<span class="text-muted" invisible="discount_type =='percent'">
(
<field name="discount_percent" nolabel="1" class="w-auto d-inline"/>
%
)
</span>
</div>
</group>
<footer>
<button name="action_apply_discount" string="Apply" type="object" class="btn btn-primary"/>
<button special="cancel" string="Discard" class="btn btn-secondary" data-hotkey="x"/>
</footer>
</sheet>
</form>
Comment on lines 16 to 40

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it more user friendly currently is seems very ugly 🙁

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Now i made exactly as they are asking

</field>
</record>

</odoo>