Skip to content

Commit 4613856

Browse files
committed
[ADD] purchase_discount: add global discount functionality in purchase order
Purpose: When you have a big RFQ (with a lot of lines) and you receive a Quote from the vendor with a global discount (% or amount), it's very time consuming to apply it to all lines. Technical changes: Add discount_type, discount_in_value and computed discount_in_percentage fields on purchase.order. Add constraints for validate discount percentage. Add discount button in purchase order view form. Add action_discount to open a popup and apply_discount to apply the global discount on all order lines. Discount can be entered either as a percentage or as a fixed amount. task-5366796
1 parent b68a192 commit 4613856

File tree

5 files changed

+116
-0
lines changed

5 files changed

+116
-0
lines changed

purchase_discount/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

purchase_discount/__manifest__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
'author': 'Odoo S.A.',
3+
'name': 'Purchase Discount',
4+
"description": """
5+
This module introduces a global discount feature for Purchase Orders,
6+
allowing users to apply either value-based or percentage-based discounts.
7+
""",
8+
'depends': ['purchase'],
9+
'license': 'LGPL-3',
10+
'data': [
11+
'views/purchase_order_views.xml'
12+
],
13+
'application': True,
14+
'installable': True
15+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import purchase_order
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from odoo import models, fields, api
2+
from odoo.exceptions import ValidationError
3+
from odoo.tools import _
4+
5+
6+
class PurchaseOrder(models.Model):
7+
_inherit = "purchase.order"
8+
9+
discount_type = fields.Selection(
10+
selection=[
11+
("value", "$"),
12+
("percentage", "%"),
13+
],
14+
default="percentage",
15+
)
16+
discount_in_value = fields.Float(string="Discount")
17+
discount_in_percentage = fields.Float(compute="_compute_discount_percentage", store=True, readonly=True)
18+
19+
@api.depends("discount_type", "discount_in_value")
20+
def _compute_discount_percentage(self):
21+
for record in self:
22+
if record.discount_type == "value" and record.amount_total > 0:
23+
record.discount_in_percentage = (record.discount_in_value * 100) / (record.amount_total)
24+
elif record.discount_type == "percentage":
25+
record.discount_in_percentage = record.discount_in_value
26+
else:
27+
record.discount_in_percentage = 0.0
28+
29+
@api.constrains("discount_in_value", "discount_type", "amount_total")
30+
def _check_discount_price(self):
31+
for record in self:
32+
if record.discount_type == "percentage":
33+
if record.discount_in_value < 0 or record.discount_in_value > 100:
34+
raise ValidationError("discount value is not valid please check again")
35+
if record.discount_type == "value":
36+
if record.discount_in_value < 0 or record.discount_in_value > record.amount_total:
37+
raise ValidationError("discount value is not valid please check again")
38+
39+
def apply_discount(self):
40+
self.order_line.discount = self.discount_in_percentage
41+
42+
def action_discount(self):
43+
return {
44+
"type": "ir.actions.act_window",
45+
"name": _("Discount"),
46+
"res_model": "purchase.order",
47+
"view_mode": "form",
48+
"view_id": self.env.ref(
49+
"purchase_discount.view_purchase_order_discount_form"
50+
).id,
51+
"res_id": self.id,
52+
"target": "new",
53+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0"?>
2+
<odoo>
3+
<record id="view_purchase_order_discount_form" model="ir.ui.view">
4+
<field name="name">purchase.order.discount.form</field>
5+
<field name="model">purchase.order</field>
6+
<field name="priority">17</field>
7+
<field name="arch" type="xml">
8+
<form>
9+
<sheet>
10+
<div class="d-flex flex-row gap-2">
11+
<label for="discount_in_value">Discount</label>
12+
<div style="max-width:150px;">
13+
<field name="discount_in_value"/>
14+
</div>
15+
<div style="max-width:50px;">
16+
<field name="discount_type"/>
17+
</div>
18+
<span class="text-muted" invisible="discount_type == 'percentage'">
19+
(<field name="discount_in_percentage"/>%)
20+
</span>
21+
</div>
22+
</sheet>
23+
<footer>
24+
<button name="apply_discount" type="object" string="Apply" class="oe_highlight"/>
25+
<button string="Cancel" class="btn-secondary" special="cancel"/>
26+
</footer>
27+
</form>
28+
</field>
29+
</record>
30+
31+
<record id="purchase_order_form_inherit" model="ir.ui.view">
32+
<field name="name">purchase.order.view.form.inherit</field>
33+
<field name="model">purchase.order</field>
34+
<field name="inherit_id" ref="purchase.purchase_order_form"/>
35+
<field name="arch" type="xml">
36+
<xpath expr="//*[hasclass('oe_subtotal_footer')]" position="replace">
37+
<div class="d-flex flex-column align-items-end">
38+
<button name="action_discount" type="object" class="oe_highlight" string="Discount" invisible="state in ('purchase', 'cancel')"/>
39+
<group class="oe_subtotal_footer">
40+
<field name="tax_totals" widget="account-tax-totals-field" nolabel="1" readonly="1"/>
41+
</group>
42+
</div>
43+
</xpath>
44+
</field>
45+
</record>
46+
</odoo>

0 commit comments

Comments
 (0)