From da66de104344391bb1e3990c2f8a5608ca5818d9 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Jan 2023 10:45:11 +0100 Subject: [PATCH] [IMP] coupon_limit, sale_coupon_limit split Allow coupon_limit settings to be used by other loyalty modules --- coupon_limit/README.rst | 122 +++++ coupon_limit/__init__.py | 1 + coupon_limit/__manifest__.py | 15 + coupon_limit/i18n/coupon_limit.pot | 220 +++++++++ coupon_limit/models/__init__.py | 1 + coupon_limit/models/coupon_rule.py | 83 ++++ coupon_limit/readme/CONTRIBUTORS.rst | 8 + coupon_limit/readme/DESCRIPTION.rst | 2 + coupon_limit/security/ir.model.access.csv | 2 + coupon_limit/static/description/icon.png | Bin 0 -> 6204 bytes coupon_limit/static/description/icon.svg | 141 ++++++ coupon_limit/static/description/index.html | 463 ++++++++++++++++++ coupon_limit/tests/__init__.py | 1 + coupon_limit/tests/test_coupon_limit.py | 64 +++ .../views/coupon_program_views.xml | 0 sale_coupon_limit/README.rst | 24 +- sale_coupon_limit/__manifest__.py | 6 +- sale_coupon_limit/models/__init__.py | 6 +- .../{sale_coupon.py => coupon_coupon.py} | 9 +- ...le_coupon_program.py => coupon_program.py} | 0 .../{sale_coupon_rule.py => coupon_rule.py} | 74 +-- sale_coupon_limit/readme/CONTRIBUTORS.rst | 4 + sale_coupon_limit/static/description/icon.svg | 2 +- .../static/description/index.html | 18 +- .../tests/test_sale_coupon_limit.py | 63 +-- setup/coupon_limit/odoo/addons/coupon_limit | 1 + setup/coupon_limit/setup.py | 6 + 27 files changed, 1180 insertions(+), 156 deletions(-) create mode 100644 coupon_limit/README.rst create mode 100644 coupon_limit/__init__.py create mode 100644 coupon_limit/__manifest__.py create mode 100644 coupon_limit/i18n/coupon_limit.pot create mode 100644 coupon_limit/models/__init__.py create mode 100644 coupon_limit/models/coupon_rule.py create mode 100644 coupon_limit/readme/CONTRIBUTORS.rst create mode 100644 coupon_limit/readme/DESCRIPTION.rst create mode 100644 coupon_limit/security/ir.model.access.csv create mode 100644 coupon_limit/static/description/icon.png create mode 100644 coupon_limit/static/description/icon.svg create mode 100644 coupon_limit/static/description/index.html create mode 100644 coupon_limit/tests/__init__.py create mode 100644 coupon_limit/tests/test_coupon_limit.py rename {sale_coupon_limit => coupon_limit}/views/coupon_program_views.xml (100%) rename sale_coupon_limit/models/{sale_coupon.py => coupon_coupon.py} (88%) rename sale_coupon_limit/models/{sale_coupon_program.py => coupon_program.py} (100%) rename sale_coupon_limit/models/{sale_coupon_rule.py => coupon_rule.py} (57%) create mode 120000 setup/coupon_limit/odoo/addons/coupon_limit create mode 100644 setup/coupon_limit/setup.py diff --git a/coupon_limit/README.rst b/coupon_limit/README.rst new file mode 100644 index 000000000..67c195ddd --- /dev/null +++ b/coupon_limit/README.rst @@ -0,0 +1,122 @@ +============ +Coupon Limit +============ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |badge2| image:: https://img.shields.io/badge/licence-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--promotion-lightgray.png?logo=github + :target: https://github.com/OCA/sale-promotion/tree/15.0/coupon_limit + :alt: OCA/sale-promotion +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-promotion-15-0/sale-promotion-15-0-coupon_limit + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/296/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to configure a limit on the times a promotion can be applied. Two +limits can be configured: customer and salesman. Those limits apply to either programs +or coupons. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure customer limits: + +#. Go to *Sales > Catalog > Coupon Programs* and select or create a new one. +#. Set the *Maximum Customer Applications* to the number of times a coupon can be used + by a customer. + +NOTE: The customer limit is applied at commercial entity level, not for each contact +inside the entity. + +To configure salesmen limits: + +#. Go to *Sales > Catalog > Coupon Programs* and select or create a new one. +#. Add salesmen to the *Salesmen Limits* list and their maximum number of applications. +#. You can add different limits to different salesmen groups. +#. If you want to constrain the use of the promotion to the salesmen list, set the + option *Strict limit* on, so any other salesman won't be able to apply the promotion. + +Usage +===== + +Once the program limits are configured, apply the programs as usual in your sale orders. + +Once the limit for a customer or a salesman is reached, if we try to apply a promotion: + +- A code promotion will raise an error. +- A program with no code won't be applied. +- A coupon belonging to a limited program will raise an error. +- A promotion applied on the next order won't generate the coupon. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Pedro M. Baeza + * David Vidal + +* `Akretion `_: + + * Florian Mounier + +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. + +.. |maintainer-chienandalu| image:: https://github.com/chienandalu.png?size=40px + :target: https://github.com/chienandalu + :alt: chienandalu + +Current `maintainer `__: + +|maintainer-chienandalu| + +This module is part of the `OCA/sale-promotion `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/coupon_limit/__init__.py b/coupon_limit/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/coupon_limit/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/coupon_limit/__manifest__.py b/coupon_limit/__manifest__.py new file mode 100644 index 000000000..da24626bc --- /dev/null +++ b/coupon_limit/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Coupon Limit", + "summary": "Restrict number of promotions per customer or salesman", + "version": "15.0.1.0.0", + "development_status": "Production/Stable", + "category": "Sale", + "website": "https://github.com/OCA/sale-promotion", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["chienandalu"], + "license": "AGPL-3", + "depends": ["coupon"], + "data": ["views/coupon_program_views.xml", "security/ir.model.access.csv"], +} diff --git a/coupon_limit/i18n/coupon_limit.pot b/coupon_limit/i18n/coupon_limit.pot new file mode 100644 index 000000000..1e0686ea8 --- /dev/null +++ b/coupon_limit/i18n/coupon_limit.pot @@ -0,0 +1,220 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * coupon_limit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.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: coupon_limit +#: model:ir.model,name:coupon_limit.model_sale_coupon_rule_salesmen_limit +msgid "Coupon Rule Salesmen limits" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__create_uid +msgid "Created by" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__create_date +msgid "Created on" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__display_name +msgid "Display Name" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__id +msgid "ID" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_program__rule_salesmen_strict_limit +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_rule__rule_salesmen_strict_limit +msgid "" +"If marked, promotion will only be allowed for the list of salesmen with " +"their quantities" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit____last_update +msgid "Last Modified on" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__write_date +msgid "Last Updated on" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "Max" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "Max. Customer Applications" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "Max. Salesmen Applications" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_program__rule_max_customer_application +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule__rule_max_customer_application +msgid "Maximum Customer Applications" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__rule_max_salesman_application +msgid "Maximum Salesman Applications" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_rule_salesmen_limit__rule_max_salesman_application +msgid "Maximum times a salesman can apply a program. 0 for no limit." +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_program__rule_salesmen_limit_ids +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_rule__rule_salesmen_limit_ids +msgid "Maximum times salesmen can apply a program. Empty for no limit." +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_program__rule_max_customer_application +#: model:ir.model.fields,help:coupon_limit.field_sale_coupon_rule__rule_max_customer_application +msgid "" +"Maximum times that a program can be applied to a customer. 0 for no limit." +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__rule_id +msgid "Rule" +msgstr "" + +#. module: coupon_limit +#: model:ir.model,name:coupon_limit.model_sale_coupon +msgid "Sales Coupon" +msgstr "" + +#. module: coupon_limit +#: model:ir.model,name:coupon_limit.model_sale_coupon_program +msgid "Sales Coupon Program" +msgstr "" + +#. module: coupon_limit +#: model:ir.model,name:coupon_limit.model_sale_coupon_rule +msgid "Sales Coupon Rule" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__rule_user_id +msgid "Salesman" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_program__rule_salesmen_limit_ids +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule__rule_salesmen_limit_ids +msgid "Salesmen Limits" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_program__rule_salesmen_limit_count +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule__rule_salesmen_limit_count +msgid "Salesmen maximum promotions" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_program__rule_salesmen_limit_used_count +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule__rule_salesmen_limit_used_count +msgid "Salesmen promotions used" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_program__rule_salesmen_strict_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule__rule_salesmen_strict_limit +msgid "Strict limit" +msgstr "" + +#. module: coupon_limit +#: code:addons/coupon_limit/models/sale_coupon_program.py:0 +#, python-format +msgid "" +"This promo code was already applied %s times for this customer and there's " +"an stablished limit of %s for this promotion." +msgstr "" + +#. module: coupon_limit +#: code:addons/coupon_limit/models/sale_coupon_program.py:0 +#, python-format +msgid "" +"This promo code was already applied %s times for this salesman and there's " +"an stablished limit of %s for this promotion." +msgstr "" + +#. module: coupon_limit +#: code:addons/coupon_limit/models/sale_coupon.py:0 +#: code:addons/coupon_limit/models/sale_coupon_program.py:0 +#, python-format +msgid "This promotion is restricted to the listed salesmen." +msgstr "" + +#. module: coupon_limit +#: code:addons/coupon_limit/models/sale_coupon.py:0 +#: code:addons/coupon_limit/models/sale_coupon_program.py:0 +#, python-format +msgid "" +"This promotion was already applied %s times for this customer and there's an" +" stablished limit of %s." +msgstr "" + +#. module: coupon_limit +#: code:addons/coupon_limit/models/sale_coupon.py:0 +#, python-format +msgid "" +"This promotion was already applied %s times for this salesman and there's an" +" stablished limit of %s." +msgstr "" + +#. module: coupon_limit +#: model:ir.model.constraint,message:coupon_limit.constraint_sale_coupon_rule_salesmen_limit_user_id_uniq +msgid "This salesman limit is already configured" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "Total limit" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "Total used" +msgstr "" + +#. module: coupon_limit +#: model:ir.model.fields,field_description:coupon_limit.field_sale_coupon_rule_salesmen_limit__rule_times_used +msgid "Uses" +msgstr "" + +#. module: coupon_limit +#: model_terms:ir.ui.view,arch_db:coupon_limit.sale_coupon_program_view_form_common +msgid "used already)" +msgstr "" diff --git a/coupon_limit/models/__init__.py b/coupon_limit/models/__init__.py new file mode 100644 index 000000000..b6fc8de3e --- /dev/null +++ b/coupon_limit/models/__init__.py @@ -0,0 +1 @@ +from . import coupon_rule diff --git a/coupon_limit/models/coupon_rule.py b/coupon_limit/models/coupon_rule.py new file mode 100644 index 000000000..e47d9f97a --- /dev/null +++ b/coupon_limit/models/coupon_rule.py @@ -0,0 +1,83 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class CouponRule(models.Model): + _inherit = "coupon.rule" + + rule_max_customer_application = fields.Integer( + string="Maximum Customer Applications", + default=0, + help="Maximum times that a program can be applied to a customer. " + "0 for no limit.", + ) + rule_salesmen_limit_ids = fields.One2many( + string="Salesmen Limits", + comodel_name="coupon.rule.salesmen.limit", + inverse_name="rule_id", + help="Maximum times salesmen can apply a program. Empty for no limit.", + ) + rule_salesmen_strict_limit = fields.Boolean( + default=False, + string="Strict limit", + help="If marked, promotion will only be allowed for the list of salesmen with " + "their quantities", + ) + rule_salesmen_limit_count = fields.Integer( + string="Salesmen maximum promotions", + compute="_compute_rule_salesmen_limit_count", + ) + rule_salesmen_limit_used_count = fields.Integer( + string="Salesmen promotions used", + compute="_compute_rule_salesmen_limit_count", + ) + + @api.depends( + "rule_salesmen_limit_ids.rule_max_salesman_application", + "rule_salesmen_limit_ids.rule_times_used", + ) + def _compute_rule_salesmen_limit_count(self): + """To be overriden""" + self.rule_salesmen_limit_count = 0 + self.rule_salesmen_limit_used_count = 0 + + +class CouponRuleSalesmenLimit(models.Model): + _name = "coupon.rule.salesmen.limit" + _description = "Coupon Rule Salesmen limits" + + rule_id = fields.Many2one( + comodel_name="coupon.rule", + auto_join=True, + required=True, + ondelete="cascade", + ) + rule_user_id = fields.Many2one( + comodel_name="res.users", + string="Salesman", + required=True, + ondelete="cascade", + ) + rule_max_salesman_application = fields.Integer( + string="Maximum Salesman Applications", + default=0, + help="Maximum times a salesman can apply a program. 0 for no limit.", + ) + rule_times_used = fields.Integer( + string="Uses", + compute="_compute_rule_times_used", + ) + + _sql_constraints = [ + ( + "user_id_uniq", + "unique(rule_id, rule_user_id)", + "This salesman limit is already configured", + ), + ] + + @api.depends("rule_user_id", "rule_max_salesman_application") + def _compute_rule_times_used(self): + """To be overriden""" + self.rule_times_used = 0 diff --git a/coupon_limit/readme/CONTRIBUTORS.rst b/coupon_limit/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..a4f45715c --- /dev/null +++ b/coupon_limit/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* `Tecnativa `_: + + * Pedro M. Baeza + * David Vidal + +* `Akretion `_: + + * Florian Mounier diff --git a/coupon_limit/readme/DESCRIPTION.rst b/coupon_limit/readme/DESCRIPTION.rst new file mode 100644 index 000000000..17bd50980 --- /dev/null +++ b/coupon_limit/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows to configure a limit on the times a promotion can be applied. It's a +technical base to be extended with top level functionality (sales, pos). diff --git a/coupon_limit/security/ir.model.access.csv b/coupon_limit/security/ir.model.access.csv new file mode 100644 index 000000000..27f4ab888 --- /dev/null +++ b/coupon_limit/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_salesmen_limit_manager,salesmen_limit manager,model_coupon_rule_salesmen_limit,base.group_system,1,1,1,1 diff --git a/coupon_limit/static/description/icon.png b/coupon_limit/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..07f27584aa27ae5718a8b7aa3d407de238f6323e GIT binary patch literal 6204 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coupon_limit/static/description/index.html b/coupon_limit/static/description/index.html new file mode 100644 index 000000000..963051032 --- /dev/null +++ b/coupon_limit/static/description/index.html @@ -0,0 +1,463 @@ + + + + + + +Coupon Limit + + + +
+

Coupon Limit

+ + +

Production/Stable License: AGPL-3 OCA/sale-promotion Translate me on Weblate Try me on Runbot

+

This module allows to configure a limit on the times a promotion can be applied. Two +limits can be configured: customer and salesman. Those limits apply to either programs +or coupons.

+

Table of contents

+ +
+

Configuration

+

To configure customer limits:

+
    +
  1. Go to Sales > Catalog > Coupon Programs and select or create a new one.
  2. +
  3. Set the Maximum Customer Applications to the number of times a coupon can be used +by a customer.
  4. +
+

NOTE: The customer limit is applied at commercial entity level, not for each contact +inside the entity.

+

To configure salesmen limits:

+
    +
  1. Go to Sales > Catalog > Coupon Programs and select or create a new one.
  2. +
  3. Add salesmen to the Salesmen Limits list and their maximum number of applications.
  4. +
  5. You can add different limits to different salesmen groups.
  6. +
  7. If you want to constrain the use of the promotion to the salesmen list, set the +option Strict limit on, so any other salesman won’t be able to apply the promotion.
  8. +
+
+
+

Usage

+

Once the program limits are configured, apply the programs as usual in your sale orders.

+

Once the limit for a customer or a salesman is reached, if we try to apply a promotion:

+
    +
  • A code promotion will raise an error.
  • +
  • A program with no code won’t be applied.
  • +
  • A coupon belonging to a limited program will raise an error.
  • +
  • A promotion applied on the next order won’t generate the coupon.
  • +
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

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.

+

Current maintainer:

+

chienandalu

+

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

+

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

+
+
+
+ + diff --git a/coupon_limit/tests/__init__.py b/coupon_limit/tests/__init__.py new file mode 100644 index 000000000..6566525d0 --- /dev/null +++ b/coupon_limit/tests/__init__.py @@ -0,0 +1 @@ +from . import test_coupon_limit diff --git a/coupon_limit/tests/test_coupon_limit.py b/coupon_limit/tests/test_coupon_limit.py new file mode 100644 index 000000000..8453bf723 --- /dev/null +++ b/coupon_limit/tests/test_coupon_limit.py @@ -0,0 +1,64 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests import Form, TransactionCase + + +class CouponLimitCase(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + product_obj = cls.env["product.product"] + cls.pricelist = cls.env["product.pricelist"].create( + { + "name": "Test pricelist", + "item_ids": [ + ( + 0, + 0, + { + "applied_on": "3_global", + "compute_price": "formula", + "base": "list_price", + }, + ) + ], + } + ) + cls.partner_1 = cls.env["res.partner"].create( + {"name": "Mr. Odoo", "property_product_pricelist": cls.pricelist.id} + ) + cls.partner_2 = cls.env["res.partner"].create( + {"name": "Mrs. Odoo", "property_product_pricelist": cls.pricelist.id} + ) + cls.salesman_1 = cls.env["res.users"].create( + {"name": "Salesman 1", "login": "test_salesman_1"} + ) + cls.salesman_2 = cls.env["res.users"].create( + {"name": "Salesman 2", "login": "test_salesman_2"} + ) + cls.product_a = product_obj.create({"name": "Product A", "list_price": 50}) + coupon_program_form = Form( + cls.env["coupon.program"], + view="sale_coupon.sale_coupon_program_view_promo_program_form", + ) + coupon_program_form.name = "Test Coupon Limit" + # We don't want demo programs spoiling our tests + coupon_program_form.rule_products_domain = "[('id', '=', %s)]" % ( + cls.product_a.id + ) + coupon_program_form.promo_code_usage = "no_code_needed" + coupon_program_form.reward_type = "discount" + coupon_program_form.discount_apply_on = "on_order" + coupon_program_form.discount_type = "percentage" + coupon_program_form.discount_percentage = 10 + # Customer limits preceed salesmen limits + coupon_program_form.rule_max_customer_application = 2 + with coupon_program_form.rule_salesmen_limit_ids.new() as salesman_limit: + salesman_limit.rule_user_id = cls.salesman_1 + salesman_limit.rule_max_salesman_application = 2 + with coupon_program_form.rule_salesmen_limit_ids.new() as salesman_limit: + salesman_limit.rule_user_id = cls.salesman_2 + salesman_limit.rule_max_salesman_application = 2 + # With any other salesman, the limits won't apply + coupon_program_form.rule_salesmen_strict_limit = False + cls.coupon_program = coupon_program_form.save() diff --git a/sale_coupon_limit/views/coupon_program_views.xml b/coupon_limit/views/coupon_program_views.xml similarity index 100% rename from sale_coupon_limit/views/coupon_program_views.xml rename to coupon_limit/views/coupon_program_views.xml diff --git a/sale_coupon_limit/README.rst b/sale_coupon_limit/README.rst index a0afcbf79..963edd6dd 100644 --- a/sale_coupon_limit/README.rst +++ b/sale_coupon_limit/README.rst @@ -1,26 +1,26 @@ -================= -Sale Coupon Limit -================= +============ +Coupon Limit +============ .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png :target: https://odoo-community.org/page/development-status - :alt: Beta + :alt: Production/Stable .. |badge2| image:: https://img.shields.io/badge/licence-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--promotion-lightgray.png?logo=github - :target: https://github.com/OCA/sale-promotion/tree/13.0/sale_coupon_limit + :target: https://github.com/OCA/sale-promotion/tree/15.0/sale_coupon_limit :alt: OCA/sale-promotion .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/sale-promotion-13-0/sale-promotion-13-0-sale_coupon_limit + :target: https://translation.odoo-community.org/projects/sale-promotion-15-0/sale-promotion-15-0-sale_coupon_limit :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/296/13.0 + :target: https://runbot.odoo-community.org/runbot/296/15.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -72,7 +72,7 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -92,6 +92,10 @@ Contributors * Pedro M. Baeza * David Vidal +* `Akretion `_: + + * Florian Mounier + Maintainers ~~~~~~~~~~~ @@ -113,6 +117,6 @@ Current `maintainer `__: |maintainer-chienandalu| -This module is part of the `OCA/sale-promotion `_ project on GitHub. +This module is part of the `OCA/sale-promotion `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_coupon_limit/__manifest__.py b/sale_coupon_limit/__manifest__.py index 9c260e3f1..0721ae22a 100644 --- a/sale_coupon_limit/__manifest__.py +++ b/sale_coupon_limit/__manifest__.py @@ -1,7 +1,7 @@ # Copyright 2021 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - "name": "Sale Coupon Limit", + "name": "Coupon Limit", "summary": "Restrict number of promotions per customer or salesman", "version": "15.0.1.0.0", "development_status": "Production/Stable", @@ -11,9 +11,9 @@ "maintainers": ["chienandalu"], "license": "AGPL-3", "depends": [ - "sale_coupon", + "coupon_limit", "sale_commercial_partner", "sale_coupon_order_line_link", ], - "data": ["views/coupon_program_views.xml", "security/ir.model.access.csv"], + "data": ["security/ir.model.access.csv"], } diff --git a/sale_coupon_limit/models/__init__.py b/sale_coupon_limit/models/__init__.py index ec403ad49..894c2d2fc 100644 --- a/sale_coupon_limit/models/__init__.py +++ b/sale_coupon_limit/models/__init__.py @@ -1,3 +1,3 @@ -from . import sale_coupon_rule -from . import sale_coupon_program -from . import sale_coupon +from . import coupon_coupon +from . import coupon_program +from . import coupon_rule diff --git a/sale_coupon_limit/models/sale_coupon.py b/sale_coupon_limit/models/coupon_coupon.py similarity index 88% rename from sale_coupon_limit/models/sale_coupon.py rename to sale_coupon_limit/models/coupon_coupon.py index fe3719ee0..94657e0a8 100644 --- a/sale_coupon_limit/models/sale_coupon.py +++ b/sale_coupon_limit/models/coupon_coupon.py @@ -6,13 +6,14 @@ class SaleCoupon(models.Model): _inherit = "coupon.coupon" - def _check_coupon_code(self, order): + def _check_coupon_code(self, order_date, partner_id, **kwargs): """Add customer and salesmen limit to program coupons. Check the error strings for a detailed case detail.""" - message = super()._check_coupon_code(order) - if message: + message = super()._check_coupon_code(order_date, partner_id, **kwargs) + order = kwargs.get("order") + if message or not order: return message - # The module sale_couopon_selection_wizard works with new records to probe + # The module sale_coupon_selection_wizard works with new records to probe # if a promotion is applicable before apply it for sure. Thus we need to ensure # the right id in the domain. domain = [ diff --git a/sale_coupon_limit/models/sale_coupon_program.py b/sale_coupon_limit/models/coupon_program.py similarity index 100% rename from sale_coupon_limit/models/sale_coupon_program.py rename to sale_coupon_limit/models/coupon_program.py diff --git a/sale_coupon_limit/models/sale_coupon_rule.py b/sale_coupon_limit/models/coupon_rule.py similarity index 57% rename from sale_coupon_limit/models/sale_coupon_rule.py rename to sale_coupon_limit/models/coupon_rule.py index d53db5f6b..6596fa727 100644 --- a/sale_coupon_limit/models/sale_coupon_rule.py +++ b/sale_coupon_limit/models/coupon_rule.py @@ -1,46 +1,14 @@ # Copyright 2021 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import models class CouponRule(models.Model): _inherit = "coupon.rule" - rule_max_customer_application = fields.Integer( - string="Maximum Customer Applications", - default=0, - help="Maximum times that a program can be applied to a customer. " - "0 for no limit.", - ) - rule_salesmen_limit_ids = fields.One2many( - string="Salesmen Limits", - comodel_name="coupon.rule.salesmen.limit", - inverse_name="rule_id", - help="Maximum times salesmen can apply a program. Empty for no limit.", - ) - rule_salesmen_strict_limit = fields.Boolean( - default=False, - string="Strict limit", - help="If marked, promotion will only be allowed for the list of salesmen with " - "their quantities", - ) - rule_salesmen_limit_count = fields.Integer( - string="Salesmen maximum promotions", - compute="_compute_rule_salesmen_limit_count", - ) - rule_salesmen_limit_used_count = fields.Integer( - string="Salesmen promotions used", - compute="_compute_rule_salesmen_limit_count", - ) - - @api.depends( - "rule_salesmen_limit_ids.rule_max_salesman_application", - "rule_salesmen_limit_ids.rule_times_used", - ) def _compute_rule_salesmen_limit_count(self): """This count is merely informative""" - self.rule_salesmen_limit_count = 0 - self.rule_salesmen_limit_used_count = 0 + res = super()._compute_rule_salesmen_limit_count() for rule in self: rule.rule_salesmen_limit_count = sum( rule.rule_salesmen_limit_ids.mapped("rule_max_salesman_application") @@ -48,47 +16,16 @@ def _compute_rule_salesmen_limit_count(self): rule.rule_salesmen_limit_used_count = sum( rule.rule_salesmen_limit_ids.mapped("rule_times_used") ) + return res class CouponRuleSalesmenLimit(models.Model): - _name = "coupon.rule.salesmen.limit" - _description = "Coupon Rule Salesmen limits" - - rule_id = fields.Many2one( - comodel_name="coupon.rule", - auto_join=True, - required=True, - ondelete="cascade", - ) - rule_user_id = fields.Many2one( - comodel_name="res.users", - string="Salesman", - required=True, - ondelete="cascade", - ) - rule_max_salesman_application = fields.Integer( - string="Maximum Salesman Applications", - default=0, - help="Maximum times a salesman can apply a program. 0 for no limit.", - ) - rule_times_used = fields.Integer( - string="Uses", - compute="_compute_rule_times_used", - ) - - _sql_constraints = [ - ( - "user_id_uniq", - "unique(rule_id, rule_user_id)", - "This salesman limit is already configured", - ), - ] + _inherit = "coupon.rule.salesmen.limit" - @api.depends("rule_user_id", "rule_max_salesman_application") def _compute_rule_times_used(self): """This count is also used in the check methods to avoid applying the rule above the salesmen limits.""" - self.rule_times_used = 0 + res = super()._compute_rule_times_used() programs = self.env["coupon.program"].search_read( [("rule_id", "in", self.mapped("rule_id").ids)], ["id", "rule_id", "program_type", "coupon_ids", "rule_salesmen_limit_ids"], @@ -138,3 +75,4 @@ def _compute_rule_times_used(self): ["order_id"], ) ) + return res diff --git a/sale_coupon_limit/readme/CONTRIBUTORS.rst b/sale_coupon_limit/readme/CONTRIBUTORS.rst index 39af65cd5..a4f45715c 100644 --- a/sale_coupon_limit/readme/CONTRIBUTORS.rst +++ b/sale_coupon_limit/readme/CONTRIBUTORS.rst @@ -2,3 +2,7 @@ * Pedro M. Baeza * David Vidal + +* `Akretion `_: + + * Florian Mounier diff --git a/sale_coupon_limit/static/description/icon.svg b/sale_coupon_limit/static/description/icon.svg index 82c53341c..2f5df826c 100644 --- a/sale_coupon_limit/static/description/icon.svg +++ b/sale_coupon_limit/static/description/icon.svg @@ -11,7 +11,7 @@ version="1.1" id="svg33" sodipodi:docname="icon.svg" - inkscape:export-filename="/home/david/odoo/oca13/odoo/custom/src/sale-promotion/sale_coupon_limit/static/description/icon.png" + inkscape:export-filename="/home/david/odoo/oca13/odoo/custom/src/sale-promotion/coupon_limit/static/description/icon.png" inkscape:export-xdpi="192" inkscape:export-ydpi="192" inkscape:version="1.1-dev (1:1.0+devel+202007152027+7044d6c)"> diff --git a/sale_coupon_limit/static/description/index.html b/sale_coupon_limit/static/description/index.html index 7d034dfe9..247f27bac 100644 --- a/sale_coupon_limit/static/description/index.html +++ b/sale_coupon_limit/static/description/index.html @@ -3,8 +3,8 @@ - -Sale Coupon Limit + +Coupon Limit -
-

Sale Coupon Limit

+
+

Coupon Limit

-

Beta License: AGPL-3 OCA/sale-promotion Translate me on Weblate Try me on Runbot

+

Production/Stable License: AGPL-3 OCA/sale-promotion Translate me on Weblate Try me on Runbot

This module allows to configure a limit on the times a promotion can be applied. Two limits can be configured: customer and salesman. Those limits apply to either programs or coupons.

@@ -420,7 +420,7 @@

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 smashing it by providing a detailed and welcomed -feedback.

+feedback.

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

@@ -439,6 +439,10 @@

Contributors

  • David Vidal
  • +
  • Akretion:
      +
    • Florian Mounier
    • +
    +
  • @@ -450,7 +454,7 @@

    Maintainers

    promote its widespread use.

    Current maintainer:

    chienandalu

    -

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

    +

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

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

    diff --git a/sale_coupon_limit/tests/test_sale_coupon_limit.py b/sale_coupon_limit/tests/test_sale_coupon_limit.py index 497c20f16..7b0b7e10d 100644 --- a/sale_coupon_limit/tests/test_sale_coupon_limit.py +++ b/sale_coupon_limit/tests/test_sale_coupon_limit.py @@ -1,69 +1,12 @@ # Copyright 2021 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo.exceptions import UserError -from odoo.tests import Form, common +from odoo.tests import Form +from odoo.addons.coupon_limit.tests.test_coupon_limit import CouponLimitCase -class TestSaleCouponLimit(common.SavepointCase): - @classmethod - def setUpClass(cls): - super().setUpClass() - product_obj = cls.env["product.product"] - cls.pricelist = cls.env["product.pricelist"].create( - { - "name": "Test pricelist", - "item_ids": [ - ( - 0, - 0, - { - "applied_on": "3_global", - "compute_price": "formula", - "base": "list_price", - }, - ) - ], - } - ) - cls.partner_1 = cls.env["res.partner"].create( - {"name": "Mr. Odoo", "property_product_pricelist": cls.pricelist.id} - ) - cls.partner_2 = cls.env["res.partner"].create( - {"name": "Mrs. Odoo", "property_product_pricelist": cls.pricelist.id} - ) - cls.salesman_1 = cls.env["res.users"].create( - {"name": "Salesman 1", "login": "test_salesman_1"} - ) - cls.salesman_2 = cls.env["res.users"].create( - {"name": "Salesman 2", "login": "test_salesman_2"} - ) - cls.product_a = product_obj.create({"name": "Product A", "list_price": 50}) - coupon_program_form = Form( - cls.env["coupon.program"], - view="sale_coupon.sale_coupon_program_view_promo_program_form", - ) - coupon_program_form.name = "Test Coupon Limit" - # We don't want demo programs spoiling our tests - coupon_program_form.rule_products_domain = "[('id', '=', %s)]" % ( - cls.product_a.id - ) - coupon_program_form.promo_code_usage = "no_code_needed" - coupon_program_form.reward_type = "discount" - coupon_program_form.discount_apply_on = "on_order" - coupon_program_form.discount_type = "percentage" - coupon_program_form.discount_percentage = 10 - # Customer limits preceed salesmen limits - coupon_program_form.rule_max_customer_application = 2 - with coupon_program_form.rule_salesmen_limit_ids.new() as salesman_limit: - salesman_limit.rule_user_id = cls.salesman_1 - salesman_limit.rule_max_salesman_application = 2 - with coupon_program_form.rule_salesmen_limit_ids.new() as salesman_limit: - salesman_limit.rule_user_id = cls.salesman_2 - salesman_limit.rule_max_salesman_application = 2 - # With any other salesman, the limits won't apply - coupon_program_form.rule_salesmen_strict_limit = False - cls.coupon_program = coupon_program_form.save() +class TestSaleCouponLimit(CouponLimitCase): def _create_sale(self, partner, salesman=False): """Helper method to create sales in the test cases""" sale_form = Form(self.env["sale.order"]) diff --git a/setup/coupon_limit/odoo/addons/coupon_limit b/setup/coupon_limit/odoo/addons/coupon_limit new file mode 120000 index 000000000..28653a123 --- /dev/null +++ b/setup/coupon_limit/odoo/addons/coupon_limit @@ -0,0 +1 @@ +../../../../coupon_limit \ No newline at end of file diff --git a/setup/coupon_limit/setup.py b/setup/coupon_limit/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/coupon_limit/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)