diff --git a/sale_loyalty_multi_gift/README.rst b/sale_loyalty_multi_gift/README.rst new file mode 100644 index 000000000..9f9396728 --- /dev/null +++ b/sale_loyalty_multi_gift/README.rst @@ -0,0 +1,106 @@ +======================= +Sale Loyalty Multi Gift +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:214d59fbe6811aa1a74e43630c640fc5230b22769cefa87b7a3bba3eee355706 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/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/16.0/sale_loyalty_multi_gift + :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-16-0/sale-promotion-16-0-sale_loyalty_multi_gift + :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-promotion&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to define multiple reward products on promotions on sale orders. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Configure the promotion as told in ``coupon_multi_gift`` readme. + +Usage +===== + +Once configured, apply the programs as usual to get the expected rewards. + +Known issues / Roadmap +====================== + +* Gifts are applied just once independently from the min quantity set. + +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 +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * David Vidal + * Pilar Vargas + +* `Domatix `_: + + * Carolina Ferrer + +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/sale_loyalty_multi_gift/__init__.py b/sale_loyalty_multi_gift/__init__.py new file mode 100644 index 000000000..9b4296142 --- /dev/null +++ b/sale_loyalty_multi_gift/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/sale_loyalty_multi_gift/__manifest__.py b/sale_loyalty_multi_gift/__manifest__.py new file mode 100644 index 000000000..b57669d7d --- /dev/null +++ b/sale_loyalty_multi_gift/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Sale Loyalty Multi Gift", + "summary": "Allows to configure multiple gift rewards per promotion in sales", + "version": "16.0.1.0.0", + "development_status": "Beta", + "category": "Sale", + "website": "https://github.com/OCA/sale-promotion", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["chienandalu"], + "license": "AGPL-3", + "depends": ["sale_loyalty_order_line_link", "loyalty_multi_gift"], + "data": [ + "security/ir.model.access.csv", + "wizard/sale_loyalty_reward_wizard_views.xml", + ], +} diff --git a/sale_loyalty_multi_gift/i18n/es.po b/sale_loyalty_multi_gift/i18n/es.po new file mode 100644 index 000000000..fa5fc2ced --- /dev/null +++ b/sale_loyalty_multi_gift/i18n/es.po @@ -0,0 +1,49 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_coupon_multi_gift +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-02-17 10:02+0000\n" +"PO-Revision-Date: 2023-11-08 13:38+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: sale_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_coupon_program +msgid "Coupon display on a website" +msgstr "Visualización de cupones en un sitio web" + +#. module: sale_coupon_multi_gift +#: code:addons/sale_coupon_multi_gift/models/sale_order.py:0 +#, python-format +msgid "Free Product" +msgstr "Producto gratis" + +#. module: sale_coupon_multi_gift +#: model:ir.model.fields,field_description:sale_coupon_multi_gift.field_sale_order_line__multi_gift_reward_line_id +msgid "Multi Gift Reward Line" +msgstr "Línea de recompensa multi regalo" + +#. module: sale_coupon_multi_gift +#: model:ir.model.fields,field_description:sale_coupon_multi_gift.field_sale_order_line__multi_gift_reward_line_id_option_product_id +msgid "Multi Gift Reward Line Id Option Product" +msgstr "Producto opcional de la línea de recompensa multi regalo" + +#. module: sale_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_sale_order +msgid "Sales Order" +msgstr "Pedido de venta" + +#. module: sale_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_sale_order_line +msgid "Sales Order Line" +msgstr "Línea de pedido de venta" diff --git a/sale_loyalty_multi_gift/i18n/sale_coupon_multi_gift.pot b/sale_loyalty_multi_gift/i18n/sale_coupon_multi_gift.pot new file mode 100644 index 000000000..9366b7e3e --- /dev/null +++ b/sale_loyalty_multi_gift/i18n/sale_coupon_multi_gift.pot @@ -0,0 +1,45 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_coupon_multi_gift +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_coupon_program +msgid "Coupon display on a website" +msgstr "" + +#. module: sale_coupon_multi_gift +#: code:addons/sale_coupon_multi_gift/models/sale_order.py:0 +#, python-format +msgid "Free Product" +msgstr "" + +#. module: sale_coupon_multi_gift +#: model:ir.model.fields,field_description:sale_coupon_multi_gift.field_sale_order_line__multi_gift_reward_line_id +msgid "Multi Gift Reward Line" +msgstr "" + +#. module: sale_coupon_multi_gift +#: model:ir.model.fields,field_description:sale_coupon_multi_gift.field_sale_order_line__multi_gift_reward_line_id_option_product_id +msgid "Multi Gift Reward Line Id Option Product" +msgstr "" + +#. module: sale_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_coupon_multi_gift +#: model:ir.model,name:sale_coupon_multi_gift.model_sale_order_line +msgid "Sales Order Line" +msgstr "" diff --git a/sale_loyalty_multi_gift/models/__init__.py b/sale_loyalty_multi_gift/models/__init__.py new file mode 100644 index 000000000..7578fe454 --- /dev/null +++ b/sale_loyalty_multi_gift/models/__init__.py @@ -0,0 +1,2 @@ +# from . import coupon_program +from . import sale_order diff --git a/sale_loyalty_multi_gift/models/sale_order.py b/sale_loyalty_multi_gift/models/sale_order.py new file mode 100644 index 000000000..150c5de75 --- /dev/null +++ b/sale_loyalty_multi_gift/models/sale_order.py @@ -0,0 +1,163 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import random + +from odoo import _, fields, models +from odoo.exceptions import UserError +from odoo.fields import Command, first +from odoo.tools.float_utils import float_round + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def action_open_reward_wizard(self): + self.ensure_one() + self._update_programs_and_rewards() + claimable_rewards = self._get_claimable_rewards() + if ( + len(claimable_rewards) == 1 + and claimable_rewards.get(next(iter(claimable_rewards))).reward_type + == "multi_gift" + and any( + len(record.reward_product_ids) > 1 + for record in claimable_rewards.get( + next(iter(claimable_rewards)) + ).loyalty_multi_gift_ids + ) + ): + ctx = { + "default_selected_reward_id": claimable_rewards.get( + next(iter(claimable_rewards)) + ).id, + } + return { + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "sale.loyalty.reward.wizard", + "target": "new", + "context": ctx, + } + else: + return super().action_open_reward_wizard() + + def _get_reward_values_multi_gift_line( + self, reward, coupon, cost, reward_line=False, product=False + ): + """Multi Gift reward rules. For every gift reward rule, we'll create a new + sale order line flagged as reward line with a 100% discount""" + self.ensure_one() + assert reward.reward_type == "multi_gift" + selected_product = ( + self.env["product.product"].browse( + self.env.context.get("reward_line_options", {}).get(reward_line.id) + ) + & reward_line.reward_product_ids + ) + reward_product_id = ( + selected_product or product or first(reward_line.reward_product_ids) + ) + if ( + not reward_product_id + or reward_product_id not in reward.loyalty_multi_gift_ids.reward_product_ids + ): + raise UserError(_("Invalid product to claim.")) + taxes = self.fiscal_position_id.map_tax( + reward_product_id.taxes_id.filtered( + lambda t: t.company_id == self.company_id + ) + ) + vals = { + "order_id": self.id, + "is_reward_line": True, + "name": _( + "Free Product - %(product)s", + product=reward_product_id.with_context( + display_default_code=False + ).display_name, + ), + "product_id": reward_product_id.id, + "price_unit": reward_product_id.list_price, + "discount": 100, + "product_uom_qty": reward_line.reward_product_quantity, + "reward_id": reward.id, + "coupon_id": coupon.id, + "points_cost": cost, + "reward_identifier_code": str(random.getrandbits(32)), + "product_uom": reward_product_id.uom_id.id, + "sequence": max( + self.order_line.filtered(lambda x: not x.is_reward_line).mapped( + "sequence" + ), + default=10, + ) + + 1, + "tax_id": [(Command.CLEAR, 0, 0)] + + [(Command.LINK, tax.id, False) for tax in taxes], + "loyalty_program_id": reward.program_id.id, + "multi_gift_reward_line_id": reward_line.id, + "multi_gift_reward_line_id_option_product_id": reward_product_id.id, + } + return vals + + def _get_reward_values_multi_gift(self, reward, coupon, **kwargs): + """Wrapper to create the reward lines for a multi gift promotion""" + points = self._get_real_points_for_coupon(coupon) + claimable_count = ( + float_round( + points / reward.required_points, + precision_rounding=1, + rounding_method="DOWN", + ) + if not reward.clear_wallet + else 1 + ) + total_cost = ( + points if reward.clear_wallet else claimable_count * reward.required_points + ) + cost = total_cost / len(reward.loyalty_multi_gift_ids) + order_lines = self.order_line.filtered( + lambda x: x.is_reward_line + and x.reward_id.reward_type == "multi_gift" + and x.coupon_id == coupon + ) + if order_lines: + return [ + self._get_reward_values_multi_gift_line( + reward, + coupon, + cost, + reward_line=reward_line.multi_gift_reward_line_id, + product=reward_line.multi_gift_reward_line_id_option_product_id, + ) + for reward_line in order_lines + ] + else: + return [ + self._get_reward_values_multi_gift_line( + reward, coupon, cost, reward_line=reward_line + ) + for reward_line in reward.loyalty_multi_gift_ids + ] + + def _get_reward_line_values(self, reward, coupon, **kwargs): + """Hook into the core method considering multi gift rewards""" + self.ensure_one() + self = self.with_context(lang=self.partner_id.lang) + reward = reward.with_context(lang=self.partner_id.lang) + if reward.reward_type == "multi_gift": + return self._get_reward_values_multi_gift(reward, coupon, **kwargs) + return super()._get_reward_line_values(reward, coupon, **kwargs) + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + multi_gift_reward_line_id = fields.Many2one( + comodel_name="loyalty.reward.product_line", + readonly=True, + ) + multi_gift_reward_line_id_option_product_id = fields.Many2one( + comodel_name="product.product", + readonly=True, + ) diff --git a/sale_loyalty_multi_gift/readme/CONFIGURE.rst b/sale_loyalty_multi_gift/readme/CONFIGURE.rst new file mode 100644 index 000000000..a1458fa81 --- /dev/null +++ b/sale_loyalty_multi_gift/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Configure the promotion as told in ``coupon_multi_gift`` readme. diff --git a/sale_loyalty_multi_gift/readme/CONTRIBUTORS.rst b/sale_loyalty_multi_gift/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..06c2608cd --- /dev/null +++ b/sale_loyalty_multi_gift/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* `Tecnativa `_: + + * David Vidal + * Pilar Vargas + +* `Domatix `_: + + * Carolina Ferrer diff --git a/sale_loyalty_multi_gift/readme/DESCRIPTION.rst b/sale_loyalty_multi_gift/readme/DESCRIPTION.rst new file mode 100644 index 000000000..9be058fbd --- /dev/null +++ b/sale_loyalty_multi_gift/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows to define multiple reward products on promotions on sale orders. diff --git a/sale_loyalty_multi_gift/readme/ROADMAP.rst b/sale_loyalty_multi_gift/readme/ROADMAP.rst new file mode 100644 index 000000000..7e488e4f9 --- /dev/null +++ b/sale_loyalty_multi_gift/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Gifts are applied just once independently from the min quantity set. diff --git a/sale_loyalty_multi_gift/readme/USAGE.rst b/sale_loyalty_multi_gift/readme/USAGE.rst new file mode 100644 index 000000000..ef5b79731 --- /dev/null +++ b/sale_loyalty_multi_gift/readme/USAGE.rst @@ -0,0 +1 @@ +Once configured, apply the programs as usual to get the expected rewards. diff --git a/sale_loyalty_multi_gift/security/ir.model.access.csv b/sale_loyalty_multi_gift/security/ir.model.access.csv new file mode 100644 index 000000000..24817d088 --- /dev/null +++ b/sale_loyalty_multi_gift/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_multi_gift_salesman,salesman,loyalty_multi_gift.model_loyalty_reward_product_line,sales_team.group_sale_salesman,1,0,0,0 +access_multi_gift_manager,multi_gift manager,loyalty_multi_gift.model_loyalty_reward_product_line,sales_team.group_sale_manager,1,1,1,1 +sale_loyalty_multi_gift.access_sale_loyalty_reward_product_line_wizard,access_sale_loyalty_reward_product_line_wizard,sale_loyalty_multi_gift.model_sale_loyalty_reward_product_line_wizard,base.group_user,1,1,1,1 diff --git a/sale_loyalty_multi_gift/static/description/icon.png b/sale_loyalty_multi_gift/static/description/icon.png new file mode 100644 index 000000000..e6c546b35 Binary files /dev/null and b/sale_loyalty_multi_gift/static/description/icon.png differ diff --git a/sale_loyalty_multi_gift/static/description/index.html b/sale_loyalty_multi_gift/static/description/index.html new file mode 100644 index 000000000..4c17d5e01 --- /dev/null +++ b/sale_loyalty_multi_gift/static/description/index.html @@ -0,0 +1,448 @@ + + + + + + +Sale Loyalty Multi Gift + + + +
+

Sale Loyalty Multi Gift

+ + +

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

+

This module allows to define multiple reward products on promotions on sale orders.

+

Table of contents

+ +
+

Configuration

+

Configure the promotion as told in coupon_multi_gift readme.

+
+
+

Usage

+

Once configured, apply the programs as usual to get the expected rewards.

+
+
+

Known issues / Roadmap

+
    +
  • Gifts are applied just once independently from the min quantity set.
  • +
+
+
+

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

+
    +
  • 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/sale_loyalty_multi_gift/tests/__init__.py b/sale_loyalty_multi_gift/tests/__init__.py new file mode 100644 index 000000000..34744a87c --- /dev/null +++ b/sale_loyalty_multi_gift/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_loyalty_multi_gift diff --git a/sale_loyalty_multi_gift/tests/test_sale_loyalty_multi_gift.py b/sale_loyalty_multi_gift/tests/test_sale_loyalty_multi_gift.py new file mode 100644 index 000000000..8be3e05b4 --- /dev/null +++ b/sale_loyalty_multi_gift/tests/test_sale_loyalty_multi_gift.py @@ -0,0 +1,106 @@ +# Copyright 2021 Tecnativa - David Vidal +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests import Form + +from odoo.addons.loyalty_multi_gift.tests.test_loyalty_multi_gift_case import ( + LoyaltyMultiGiftCase, +) + + +class TestSaleLoyaltyMultiGift(LoyaltyMultiGiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + sale_form = Form(cls.env["sale.order"]) + sale_form.partner_id = cls.partner + with sale_form.order_line.new() as line_form: + line_form.product_id = cls.product_1 + line_form.product_uom_qty = 2 + cls.sale = sale_form.save() + + def _action_apply_program(self, sale, program, reward_line_options=False): + sale._update_programs_and_rewards() + wizard = ( + self.env["sale.loyalty.reward.wizard"] + .with_context(active_id=sale) + .create({"selected_reward_id": program.reward_ids[0].id}) + ) + if reward_line_options: + for line in wizard.loyalty_gift_line_ids: + line.selected_gift_id = reward_line_options.get(line.line_id.id) + wizard.action_apply() + + def test_01_sale_coupon_test_multi_gift(self): + """As we fulfill the proper product qties, we get the proper free product""" + self._action_apply_program(self.sale, self.loyalty_program_form) + # As set up, we should one discount line for every reward line in the promotion + discount_line_product_2 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_2 and x.is_reward_line + ) + discount_line_product_3 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_3 and x.is_reward_line + ) + self.assertEqual(2, discount_line_product_2.product_uom_qty) + self.assertEqual(3, discount_line_product_3.product_uom_qty) + self.assertEqual(0, discount_line_product_2.price_reduce) + self.assertEqual(0, discount_line_product_3.price_reduce) + self.assertEqual(60, discount_line_product_2.price_unit) + self.assertEqual(70, discount_line_product_3.price_unit) + + def test_02_test_sale_coupon_test_multi_gift(self): + line = self.sale.order_line + line_form = Form(line, view="sale.view_order_line_tree") + line_form.product_uom_qty = 7 + line_form.save() + self._action_apply_program(self.sale, self.loyalty_program_form) + discount_line_product_2 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_2 and x.is_reward_line + ) + discount_line_product_3 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_3 and x.is_reward_line + ) + # The promotion will only be applied once whatever the min quantity + self.assertEqual(2, discount_line_product_2.product_uom_qty) + self.assertEqual(3, discount_line_product_3.product_uom_qty) + self.assertEqual(0, discount_line_product_2.price_reduce) + self.assertEqual(0, discount_line_product_3.price_reduce) + + def test_03_test_sale_coupon_test_multi_gift(self): + line = self.sale.order_line + line_form = Form(line, view="sale.view_order_line_tree") + # Now it can't be applied anymore so the discount lines will dissapear + line_form.product_uom_qty = 1 + line_form.save() + self.sale._update_programs_and_rewards() + discount_line = self.sale.order_line.filtered("is_reward_line") + # The discount goes away + self.assertFalse(bool(discount_line)) + + def test_04_test_sale_coupon_test_multi_gift(self): + # Optional rewards + reward_line_options = { + self.loyalty_program_form.reward_ids[0] + .loyalty_multi_gift_ids[0] + .id: self.product_2.id, + self.loyalty_program_form.reward_ids[0] + .loyalty_multi_gift_ids[1] + .id: self.product_4.id, + } + self._action_apply_program( + self.sale, + self.loyalty_program_form, + reward_line_options=reward_line_options, + ) + discount_line_product_2 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_2 and x.is_reward_line + ) + discount_line_product_3 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_3 and x.is_reward_line + ) + discount_line_product_4 = self.sale.order_line.filtered( + lambda x: x.product_id == self.product_4 and x.is_reward_line + ) + self.assertEqual(2, discount_line_product_2.product_uom_qty) + self.assertEqual(3, discount_line_product_4.product_uom_qty) + self.assertFalse(discount_line_product_3) diff --git a/sale_loyalty_multi_gift/wizard/__init__.py b/sale_loyalty_multi_gift/wizard/__init__.py new file mode 100644 index 000000000..1ca11a9d4 --- /dev/null +++ b/sale_loyalty_multi_gift/wizard/__init__.py @@ -0,0 +1 @@ +from . import sale_loyalty_reward_wizard diff --git a/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard.py b/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard.py new file mode 100644 index 000000000..ee3637a7e --- /dev/null +++ b/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard.py @@ -0,0 +1,97 @@ +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class SaleLoyaltyRewardWizard(models.TransientModel): + _inherit = "sale.loyalty.reward.wizard" + + # In case of multi_gift reward + multi_gift_reward = fields.Boolean(related="selected_reward_id.multi_gift") + loyalty_multi_gift_ids = fields.One2many( + related="selected_reward_id.loyalty_multi_gift_ids" + ) + loyalty_gift_line_ids = fields.One2many( + comodel_name="sale.loyalty.reward.product_line.wizard", + inverse_name="wizard_id", + compute="_compute_loyalty_gift_line_ids", + store=True, + readonly=False, + string="Loyalty Gift Lines", + ) + + @api.depends("selected_reward_id") + def _compute_loyalty_gift_line_ids(self): + self.loyalty_gift_line_ids = None + lines_vals = [] + if self.selected_reward_id.reward_type == "multi_gift": + for line in self.loyalty_multi_gift_ids: + lines_vals.append( + ( + 0, + 0, + { + "wizard_id": self.id, + "reward_id": self.selected_reward_id.id, + "multi_gift_ids": [(6, 0, self.loyalty_multi_gift_ids.ids)], + "line_id": line.id, + }, + ) + ) + self.loyalty_gift_line_ids = lines_vals + + def action_apply(self): + self.ensure_one() + if not self.selected_reward_id: + raise ValidationError(_("No reward selected.")) + claimable_rewards = self.order_id._get_claimable_rewards() + selected_coupon = False + for coupon, rewards in claimable_rewards.items(): + if self.selected_reward_id in rewards: + selected_coupon = coupon + break + if not selected_coupon: + raise ValidationError( + _( + "Coupon not found while trying to add the following reward: %s", + self.selected_reward_id.description, + ) + ) + if self.selected_reward_id.reward_type == "multi_gift": + reward_line_options = {} + for line in self.loyalty_gift_line_ids: + reward_line_options.update({line.line_id.id: line.selected_gift_id.id}) + order = self.env["sale.order"].browse(self.order_id.id) + order.with_context( + reward_line_options=reward_line_options + )._apply_program_reward(self.selected_reward_id, coupon) + order.with_context( + reward_line_options=reward_line_options + )._update_programs_and_rewards() + else: + return super().action_apply() + return True + + +class SaleLoyaltyRewardProductLineWizard(models.TransientModel): + _name = "sale.loyalty.reward.product_line.wizard" + + wizard_id = fields.Many2one(comodel_name="sale.loyalty.reward.wizard") + reward_id = fields.Many2one(related="wizard_id.selected_reward_id", store=True) + multi_gift_ids = fields.One2many(related="wizard_id.loyalty_multi_gift_ids") + line_id = fields.Many2one(comodel_name="loyalty.reward.product_line") + gift_ids = fields.Many2many(related="line_id.reward_product_ids") + selected_gift_id = fields.Many2one( + comodel_name="product.product", + domain="[('id', 'in', gift_ids)]", + compute="_compute_selected_gift_id", + readonly=False, + store=True, + ) + + @api.depends("gift_ids", "reward_id") + def _compute_selected_gift_id(self): + for wizard in self: + if not wizard.wizard_id.selected_reward_id.reward_type == "multi_gift": + wizard.selected_gift_id = None + else: + wizard.selected_gift_id = wizard.gift_ids[:1] diff --git a/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard_views.xml b/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard_views.xml new file mode 100644 index 000000000..b8e0ec708 --- /dev/null +++ b/sale_loyalty_multi_gift/wizard/sale_loyalty_reward_wizard_views.xml @@ -0,0 +1,31 @@ + + + + sale.loyalty.reward.wizard + + + + + + + + + + + + + + + + diff --git a/setup/sale_loyalty_multi_gift/odoo/addons/sale_loyalty_multi_gift b/setup/sale_loyalty_multi_gift/odoo/addons/sale_loyalty_multi_gift new file mode 120000 index 000000000..7a23f9b7f --- /dev/null +++ b/setup/sale_loyalty_multi_gift/odoo/addons/sale_loyalty_multi_gift @@ -0,0 +1 @@ +../../../../sale_loyalty_multi_gift \ No newline at end of file diff --git a/setup/sale_loyalty_multi_gift/setup.py b/setup/sale_loyalty_multi_gift/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/sale_loyalty_multi_gift/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)