diff --git a/sale_pricelist_triple_discount/README.rst b/sale_pricelist_triple_discount/README.rst new file mode 100644 index 00000000000..2fa65ca36a3 --- /dev/null +++ b/sale_pricelist_triple_discount/README.rst @@ -0,0 +1,109 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============================== +Sale Pricelist Triple Discount +============================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:62dd305432c77c119a1d7e92442b020fb62c3e7e62fbc1c34c99605f141becff + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/19.0/sale_pricelist_triple_discount + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-19-0/sale-workflow-19-0-sale_pricelist_triple_discount + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows the user to specify all three discounts in the +pricelist. The specified discounts will be then present in the quotation +and sale order lines. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Add discounts percentage value to pricelist rules. The additional +discounts are only visible for Discount and Formula Computation +pricelist rules. + +Usage +===== + +1. Configure a pricelist with Discount Policy = Show public price & + discount to the customer +2. Configure a pricelist rule with Discount or Formula Computation and + the three discounts +3. Create a sale order and select the configured pricelist +4. Add a product in the sale order, matching a rule of the configured + pricelist +5. Check that the three discounts defined on the pricelist item will + populate the three discounts in the sale order line + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Agile Business Group + +Contributors +------------ + +- Simone Rubino +- `Aion Tech `__: + + - Simone Rubino + +- `Open Source Integrators `__ + + - Daniel Reis + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_pricelist_triple_discount/__init__.py b/sale_pricelist_triple_discount/__init__.py new file mode 100644 index 00000000000..7beca06c84b --- /dev/null +++ b/sale_pricelist_triple_discount/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import model diff --git a/sale_pricelist_triple_discount/__manifest__.py b/sale_pricelist_triple_discount/__manifest__.py new file mode 100644 index 00000000000..5647cbd537d --- /dev/null +++ b/sale_pricelist_triple_discount/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015 credativ ltd () +# Copyright (c) 2020 Simone Rubino - Agile Business Group +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Sale Pricelist Triple Discount", + "version": "19.0.1.0.0", + "category": "Sale", + "author": "Agile Business Group, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/sale-workflow", + "license": "AGPL-3", + "depends": [ + "sale_triple_discount", + ], + "data": [ + "view/product_pricelist_item_views.xml", + ], + "auto_install": False, + "installable": True, +} diff --git a/sale_pricelist_triple_discount/i18n/it.po b/sale_pricelist_triple_discount/i18n/it.po new file mode 100644 index 00000000000..1293c20c24d --- /dev/null +++ b/sale_pricelist_triple_discount/i18n/it.po @@ -0,0 +1,73 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_pricelist_triple_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-13 16:51+0000\n" +"PO-Revision-Date: 2024-11-08 11:06+0000\n" +"Last-Translator: mymage \n" +"Language-Team: \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "%" +msgstr "%" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_sale_order_line__discount2 +msgid "Disc. 2 (%)" +msgstr "Sc. 2 (%)" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_sale_order_line__discount3 +msgid "Disc. 3 (%)" +msgstr "Sc. 3 (%)" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "Discount 2" +msgstr "Sconto 2" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_product_pricelist_item__discount2 +msgid "Discount 2 (%)" +msgstr "Sconto 2 (%)" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "Discount 3" +msgstr "Sconto 3" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_product_pricelist_item__discount3 +msgid "Discount 3 (%)" +msgstr "Sconto 3 (%)" + +#. module: sale_pricelist_triple_discount +#: model:ir.model,name:sale_pricelist_triple_discount.model_product_pricelist_item +msgid "Pricelist Rule" +msgstr "Regola listino prezzi" + +#. module: sale_pricelist_triple_discount +#: model:ir.model,name:sale_pricelist_triple_discount.model_sale_order_line +msgid "Sales Order Line" +msgstr "Riga ordine di vendita" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,help:sale_pricelist_triple_discount.field_product_pricelist_item__discount2 +msgid "Second discount applied on a sale order line." +msgstr "Secondo sconto applicato alla riga dell'ordine di vendita." + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,help:sale_pricelist_triple_discount.field_product_pricelist_item__discount3 +msgid "Third discount applied on a sale order line." +msgstr "Terzo sconto applicato alla riga dell'ordine di vendita." diff --git a/sale_pricelist_triple_discount/i18n/sale_pricelist_triple_discount.pot b/sale_pricelist_triple_discount/i18n/sale_pricelist_triple_discount.pot new file mode 100644 index 00000000000..dd8d57620c4 --- /dev/null +++ b/sale_pricelist_triple_discount/i18n/sale_pricelist_triple_discount.pot @@ -0,0 +1,69 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_pricelist_triple_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "%" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_sale_order_line__discount2 +msgid "Disc. 2 (%)" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_sale_order_line__discount3 +msgid "Disc. 3 (%)" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "Discount 2" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_product_pricelist_item__discount2 +msgid "Discount 2 (%)" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model_terms:ir.ui.view,arch_db:sale_pricelist_triple_discount.product_pricelist_view_form_discount +msgid "Discount 3" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,field_description:sale_pricelist_triple_discount.field_product_pricelist_item__discount3 +msgid "Discount 3 (%)" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model,name:sale_pricelist_triple_discount.model_product_pricelist_item +msgid "Pricelist Rule" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model,name:sale_pricelist_triple_discount.model_sale_order_line +msgid "Sales Order Line" +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,help:sale_pricelist_triple_discount.field_product_pricelist_item__discount2 +msgid "Second discount applied on a sale order line." +msgstr "" + +#. module: sale_pricelist_triple_discount +#: model:ir.model.fields,help:sale_pricelist_triple_discount.field_product_pricelist_item__discount3 +msgid "Third discount applied on a sale order line." +msgstr "" diff --git a/sale_pricelist_triple_discount/model/__init__.py b/sale_pricelist_triple_discount/model/__init__.py new file mode 100644 index 00000000000..2a3287eb07b --- /dev/null +++ b/sale_pricelist_triple_discount/model/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import pricelist +from . import sale diff --git a/sale_pricelist_triple_discount/model/pricelist.py b/sale_pricelist_triple_discount/model/pricelist.py new file mode 100644 index 00000000000..f15a64428ed --- /dev/null +++ b/sale_pricelist_triple_discount/model/pricelist.py @@ -0,0 +1,124 @@ +# Copyright 2019 Simone Rubino - Agile Business Group +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import contextlib +import operator +from functools import reduce + +from odoo import api, fields, models +from odoo.tools import float_is_zero + +# Map the rule's computation method to the name of the field used for the discount. +COMPUTE_PRICE_TO_DISCOUNT_FIELD = { + "percentage": "percent_price", + "formula": "price_discount", +} + + +class ProductPricelistItem(models.Model): + _inherit = "product.pricelist.item" + + discount2 = fields.Float( + string="Discount 2 (%)", + help="Second discount applied on a sale order line.", + digits="Discount", + ) + + discount3 = fields.Float( + string="Discount 3 (%)", + help="Third discount applied on a sale order line.", + digits="Discount", + ) + + def _get_triple_discounts_perc(self): + """Return the list of discounts percentages to be applied.""" + self.ensure_one() + discount_fields = self.env["sale.order.line"]._discount_fields() + discount_fields += ["discount"] + item_discount_field = COMPUTE_PRICE_TO_DISCOUNT_FIELD.get(self.compute_price) + if item_discount_field is not None: + discount_index = discount_fields.index("discount1") + discount_fields[discount_index] = item_discount_field + + # Exclude discounts that are 0 according to their precision + discount_fields_digits = self.fields_get( + allfields=discount_fields, + attributes=[ + "digits", + ], + ) + discounts_perc = [] + for discount_field, attributes in discount_fields_digits.items(): + discount_perc = self[discount_field] or 0 + discount_digits = attributes.get("digits") + if discount_digits is not None: + all_digits, precision_digits = discount_digits + is_zero = float_is_zero( + discount_perc, + precision_digits=precision_digits, + ) + else: + is_zero = discount_perc == 0 + + if not is_zero: + discounts_perc.append(discount_perc) + return discounts_perc + + def _get_triple_discount(self): + """Return the discount percentage represented by all the discounts.""" + self.ensure_one() + discounts_perc = self._get_triple_discounts_perc() + discounts = [1 - discount_perc / 100 for discount_perc in discounts_perc] + triple_discount = reduce(operator.mul, discounts, 1) + triple_discount = 100 - triple_discount * 100 + return triple_discount + + @contextlib.contextmanager + def _patch_triple_discount(self): + """Use the triple discount instead of the standard discount.""" + # Save the original discount of each rule + item_to_original_discount = {} + for item in self: + item_discount_field = COMPUTE_PRICE_TO_DISCOUNT_FIELD.get( + item.compute_price + ) + if item_discount_field is not None: + original_discount = item[item_discount_field] + item_to_original_discount[item] = original_discount + item.sudo()[item_discount_field] = item._get_triple_discount() + + yield + + # Restore the original discount in each updated rule + for item, original_discount in item_to_original_discount.items(): + item_discount_field = COMPUTE_PRICE_TO_DISCOUNT_FIELD.get( + item.compute_price + ) + item.sudo()[item_discount_field] = original_discount + + def _compute_price(self, product, quantity, uom, date, currency=None): + with self._patch_triple_discount(): + return super()._compute_price( + product, + quantity, + uom, + date, + currency=currency, + ) + + @api.depends( + "discount2", + "discount3", + ) + def _compute_rule_tip(self): + with self._patch_triple_discount(): + return super()._compute_rule_tip() + + @api.depends( + "discount2", + "discount3", + ) + def _compute_name_and_price(self): + with self._patch_triple_discount(): + return super()._compute_name_and_price() diff --git a/sale_pricelist_triple_discount/model/sale.py b/sale_pricelist_triple_discount/model/sale.py new file mode 100644 index 00000000000..d4f9fab290d --- /dev/null +++ b/sale_pricelist_triple_discount/model/sale.py @@ -0,0 +1,36 @@ +# Copyright 2019 Simone Rubino - Agile Business Group +# Copyright 2023 Simone Rubino - Aion Tech +# Copyright 2025 Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, models + +from .pricelist import COMPUTE_PRICE_TO_DISCOUNT_FIELD + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def _recompute_prices(self): + res = super()._recompute_prices() + lines_to_recompute = self._get_update_prices_lines() + lines_to_recompute._compute_discounts() + return res + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + @api.depends("product_id", "product_uom_id", "product_uom_qty", "pricelist_item_id") + def _compute_discounts(self): + res = super()._compute_discounts() + for line in self: + price_rule = line.pricelist_item_id + item_discount_field = COMPUTE_PRICE_TO_DISCOUNT_FIELD.get( + price_rule.compute_price + ) + if item_discount_field is not None: + line.discount1 = price_rule.percent_price + line.discount2 = price_rule.discount2 + line.discount3 = price_rule.discount3 + return res diff --git a/sale_pricelist_triple_discount/pyproject.toml b/sale_pricelist_triple_discount/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sale_pricelist_triple_discount/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_pricelist_triple_discount/readme/CONFIGURE.md b/sale_pricelist_triple_discount/readme/CONFIGURE.md new file mode 100644 index 00000000000..1869f4822d5 --- /dev/null +++ b/sale_pricelist_triple_discount/readme/CONFIGURE.md @@ -0,0 +1,3 @@ +Add discounts percentage value to pricelist rules. The additional +discounts are only visible for Discount and Formula Computation +pricelist rules. diff --git a/sale_pricelist_triple_discount/readme/CONTRIBUTORS.md b/sale_pricelist_triple_discount/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..51e69a6cb1f --- /dev/null +++ b/sale_pricelist_triple_discount/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Simone Rubino \<\> +- [Aion Tech](https://aiontech.company/): + - Simone Rubino \<\> +- [Open Source Integrators](https://www.opensourceintegrators.com/) + - Daniel Reis \<\> \ No newline at end of file diff --git a/sale_pricelist_triple_discount/readme/DESCRIPTION.md b/sale_pricelist_triple_discount/readme/DESCRIPTION.md new file mode 100644 index 00000000000..6813bd1f141 --- /dev/null +++ b/sale_pricelist_triple_discount/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module allows the user to specify all three discounts in the +pricelist. The specified discounts will be then present in the quotation +and sale order lines. diff --git a/sale_pricelist_triple_discount/readme/USAGE.md b/sale_pricelist_triple_discount/readme/USAGE.md new file mode 100644 index 00000000000..0d91ce7c747 --- /dev/null +++ b/sale_pricelist_triple_discount/readme/USAGE.md @@ -0,0 +1,9 @@ +1. Configure a pricelist with Discount Policy = Show public price & + discount to the customer +2. Configure a pricelist rule with Discount or Formula Computation and + the three discounts +3. Create a sale order and select the configured pricelist +4. Add a product in the sale order, matching a rule of the configured + pricelist +5. Check that the three discounts defined on the pricelist item will + populate the three discounts in the sale order line diff --git a/sale_pricelist_triple_discount/static/description/icon.png b/sale_pricelist_triple_discount/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/sale_pricelist_triple_discount/static/description/icon.png differ diff --git a/sale_pricelist_triple_discount/static/description/index.html b/sale_pricelist_triple_discount/static/description/index.html new file mode 100644 index 00000000000..94acc038ad8 --- /dev/null +++ b/sale_pricelist_triple_discount/static/description/index.html @@ -0,0 +1,461 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Sale Pricelist Triple Discount

+ +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

This module allows the user to specify all three discounts in the +pricelist. The specified discounts will be then present in the quotation +and sale order lines.

+

Table of contents

+ +
+

Configuration

+

Add discounts percentage value to pricelist rules. The additional +discounts are only visible for Discount and Formula Computation +pricelist rules.

+
+
+

Usage

+
    +
  1. Configure a pricelist with Discount Policy = Show public price & +discount to the customer
  2. +
  3. Configure a pricelist rule with Discount or Formula Computation and +the three discounts
  4. +
  5. Create a sale order and select the configured pricelist
  6. +
  7. Add a product in the sale order, matching a rule of the configured +pricelist
  8. +
  9. Check that the three discounts defined on the pricelist item will +populate the three discounts in the sale order line
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Agile Business Group
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/sale-workflow project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/sale_pricelist_triple_discount/tests/__init__.py b/sale_pricelist_triple_discount/tests/__init__.py new file mode 100644 index 00000000000..9e84affa1fc --- /dev/null +++ b/sale_pricelist_triple_discount/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_sale_order diff --git a/sale_pricelist_triple_discount/tests/test_sale_order.py b/sale_pricelist_triple_discount/tests/test_sale_order.py new file mode 100644 index 00000000000..4c4f67415c3 --- /dev/null +++ b/sale_pricelist_triple_discount/tests/test_sale_order.py @@ -0,0 +1,210 @@ +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.fields import first +from odoo.tests import Form, tagged + +from odoo.addons.sale.tests.common import SaleCommon + + +@tagged("post_install", "-at_install") +class TestSalePrices(SaleCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Enable user groups to be able to set advanced fields using Form + pricelist_groups = [ + # for product.pricelist.discount_policy + "product.group_discount_per_so_line", + # for product.pricelist.item.compute_price + "product.group_sale_pricelist", + ] + user = cls.env.user + for pricelist_group in pricelist_groups: + user.groups_id += cls.env.ref(pricelist_group) + + def test_percentage_with_discount(self): + """A percentage Discount Rule in a "with_discount" Pricelist + does not propagate the discounts to the sale order line. + """ + # Arrange + order = self.empty_order + pricelist_form = Form(self.pricelist) + pricelist_form.discount_policy = "with_discount" + with pricelist_form.item_ids.new() as item: + item.compute_price = "percentage" + item.percent_price = 10 + item.discount2 = 20 + item.discount3 = 30 + pricelist = pricelist_form.save() + rule = first(pricelist.item_ids) + product = self.product + # pre-condition + self.assertEqual(pricelist.discount_policy, "with_discount") + self.assertEqual(rule.compute_price, "percentage") + self.assertEqual(rule.percent_price, 10) + self.assertEqual(rule.discount2, 20) + self.assertEqual(rule.discount3, 30) + self.assertEqual(product.list_price, 20) + + # Act + order_form = Form(order) + with order_form.order_line.new() as line: + line.product_id = product + order = order_form.save() + + # Assert + rule_explanation = "49.6 % discount" + self.assertIn(rule_explanation, rule.price) + line = order.order_line + self.assertFalse(line.discount1) + self.assertFalse(line.discount2) + self.assertFalse(line.discount3) + self.assertEqual(line.price_unit, 10.08) + self.assertEqual(line.price_subtotal, 10.08) + + def test_percentage_without_discount(self): + """A percentage Discount Rule in a "without_discount" Pricelist + propagates the discounts to the sale order line. + """ + # Arrange + order = self.empty_order + pricelist_form = Form(self.pricelist) + pricelist_form.discount_policy = "without_discount" + with pricelist_form.item_ids.new() as item: + item.compute_price = "percentage" + item.percent_price = 10 + item.discount2 = 20 + item.discount3 = 30 + pricelist = pricelist_form.save() + rule = first(pricelist.item_ids) + product = self.product + # pre-condition + self.assertEqual(pricelist.discount_policy, "without_discount") + self.assertEqual(rule.compute_price, "percentage") + self.assertEqual(rule.percent_price, 10) + self.assertEqual(rule.discount2, 20) + self.assertEqual(rule.discount3, 30) + self.assertEqual(product.list_price, 20) + + # Act + order_form = Form(order) + with order_form.order_line.new() as line: + line.product_id = product + order = order_form.save() + + # Assert + rule_explanation = "49.6 % discount" + self.assertIn(rule_explanation, rule.price) + line = order.order_line + self.assertEqual(line.discount1, 10) + self.assertEqual(line.discount2, 20) + self.assertEqual(line.discount3, 30) + self.assertEqual(line.price_unit, 20) + self.assertEqual(line.price_subtotal, 10.08) + + def test_formula_with_discount(self): + """A formula Discount Rule in a "with_discount" Pricelist + does not propagate the discounts to the sale order line. + """ + # Arrange + order = self.empty_order + pricelist_form = Form(self.pricelist) + pricelist_form.discount_policy = "with_discount" + with pricelist_form.item_ids.new() as item: + item.compute_price = "formula" + item.price_discount = 10 + item.discount2 = 20 + item.discount3 = 30 + pricelist = pricelist_form.save() + rule = first(pricelist.item_ids) + product = self.product + # pre-condition + self.assertEqual(pricelist.discount_policy, "with_discount") + self.assertEqual(rule.compute_price, "formula") + self.assertEqual(rule.price_discount, 10) + self.assertEqual(rule.discount2, 20) + self.assertEqual(rule.discount3, 30) + self.assertEqual(product.list_price, 20) + + # Act + order_form = Form(order) + with order_form.order_line.new() as line: + line.product_id = product + order = order_form.save() + + # Assert + rule_explanation = "49.6 % discount" + self.assertIn(rule_explanation, rule.price) + self.assertIn(rule_explanation, rule.rule_tip) + line = order.order_line + self.assertFalse(line.discount1) + self.assertFalse(line.discount2) + self.assertFalse(line.discount3) + self.assertEqual(line.price_unit, 10.08) + self.assertEqual(line.price_subtotal, 10.08) + + def test_formula_without_discount(self): + """A formula Discount Rule in a "without_discount" Pricelist + propagates the discounts to the sale order line. + """ + # Arrange + order = self.empty_order + pricelist_form = Form(self.pricelist) + pricelist_form.discount_policy = "without_discount" + with pricelist_form.item_ids.new() as item: + item.compute_price = "formula" + item.price_discount = 10 + item.discount2 = 20 + item.discount3 = 30 + pricelist = pricelist_form.save() + rule = first(pricelist.item_ids) + product = self.product + # pre-condition + self.assertEqual(pricelist.discount_policy, "without_discount") + self.assertEqual(rule.compute_price, "formula") + self.assertEqual(rule.price_discount, 10) + self.assertEqual(rule.discount2, 20) + self.assertEqual(rule.discount3, 30) + self.assertEqual(product.list_price, 20) + + # Act + order_form = Form(order) + with order_form.order_line.new() as line: + line.product_id = product + order = order_form.save() + + # Assert + rule_explanation = "49.6 % discount" + self.assertIn(rule_explanation, rule.price) + self.assertIn(rule_explanation, rule.rule_tip) + line = order.order_line + self.assertEqual(line.discount1, 10) + self.assertEqual(line.discount2, 20) + self.assertEqual(line.discount3, 30) + self.assertEqual(line.price_unit, 20) + self.assertEqual(line.price_subtotal, 10.08) + + def test_pricelist_readonly(self): + """A user with readonly access on pricelists can read them and their items.""" + # Arrange + pricelist_form = Form(self.pricelist) + pricelist_form.discount_policy = "with_discount" + with pricelist_form.item_ids.new() as item: + item.compute_price = "percentage" + item.percent_price = 10 + item.discount2 = 20 + item.discount3 = 30 + pricelist = pricelist_form.save() + readonly_pricelist = pricelist.with_user(self.sale_user) + # pre-condition + self.assertTrue( + readonly_pricelist.check_access_rights("read", raise_exception=False) + ) + self.assertFalse( + readonly_pricelist.check_access_rights("write", raise_exception=False) + ) + + # Assert + readonly_pricelist.read() + readonly_pricelist.item_ids.read() diff --git a/sale_pricelist_triple_discount/view/product_pricelist_item_views.xml b/sale_pricelist_triple_discount/view/product_pricelist_item_views.xml new file mode 100644 index 00000000000..33a90fdc77f --- /dev/null +++ b/sale_pricelist_triple_discount/view/product_pricelist_item_views.xml @@ -0,0 +1,54 @@ + + + + + Add triple discounts to Pricelist Rule form view + product.pricelist.item + + + + + + + + +