-
Notifications
You must be signed in to change notification settings - Fork 3k
[IMP] purchase: add global discount support on RFQ lines #1160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 19.0
Are you sure you want to change the base?
Changes from all commits
ae14337
d962228
92609ab
dfd1dc2
3ea2c8f
45d0c72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import wizard |
| 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", | ||
| ], | ||
| } |
| 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 |
| 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> | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import purchase_order_discount |
| 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( | ||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 . There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) and percentage is also not computing properly, I don't think 419.75 should be 52% of 36500 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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}) | ||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make it more user friendly currently is seems very ugly 🙁
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, Now i made exactly as they are asking |
||
| </field> | ||
| </record> | ||
|
|
||
| </odoo> | ||


There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.