diff --git a/setup/website_sale_loyalty_suggestion_wizard/odoo/addons/website_sale_loyalty_suggestion_wizard b/setup/website_sale_loyalty_suggestion_wizard/odoo/addons/website_sale_loyalty_suggestion_wizard new file mode 120000 index 000000000..b59ab4009 --- /dev/null +++ b/setup/website_sale_loyalty_suggestion_wizard/odoo/addons/website_sale_loyalty_suggestion_wizard @@ -0,0 +1 @@ +../../../../website_sale_loyalty_suggestion_wizard \ No newline at end of file diff --git a/setup/website_sale_loyalty_suggestion_wizard/setup.py b/setup/website_sale_loyalty_suggestion_wizard/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/website_sale_loyalty_suggestion_wizard/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/website_sale_loyalty_suggestion_wizard/README.rst b/website_sale_loyalty_suggestion_wizard/README.rst new file mode 100644 index 000000000..e59ba1075 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/README.rst @@ -0,0 +1,111 @@ +====================================== +Website Sale Loyalty Suggestion Wizard +====================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:8c11a0d3f2c9fac24b5e452a5da349a41d86d34df86fa10b103b47acd0babf25 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/website_sale_loyalty_suggestion_wizard + :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-website_sale_loyalty_suggestion_wizard + :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 makes suggestions for promotions whose rules include a product added to the +cart. It also allows you to configure and apply these promotions with an elegant eCommerce +wizard. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To apply promotions: + +Option 1: + +#. Go to /promotions and choose the one apply. +#. You'll be prompted to check the promotion configuration. +#. Apply after that. + +Option 2: + +#. Add a product in the promotion rules. +#. A cart suggestion will appear. +#. Add the promotion and configure the options. + +Option 3: + +#. Select a suggested promotion in the shopping cart from a list of "Suggested promotions". +#. Add the promotion and configure the options. + +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 + +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/website_sale_loyalty_suggestion_wizard/__init__.py b/website_sale_loyalty_suggestion_wizard/__init__.py new file mode 100644 index 000000000..e046e49fb --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/website_sale_loyalty_suggestion_wizard/__manifest__.py b/website_sale_loyalty_suggestion_wizard/__manifest__.py new file mode 100644 index 000000000..a074315e8 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Website Sale Loyalty Suggestion Wizard", + "summary": "Suggests promotions and allows you to configure and apply these " + "promotions directly from the website", + "version": "16.0.1.0.0", + "development_status": "Beta", + "category": "eCommerce", + "website": "https://github.com/OCA/sale-promotion", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["chienandalu"], + "license": "AGPL-3", + "depends": [ + "sale_loyalty", + "sale_loyalty_order_suggestion", + "website_sale_loyalty_page", + ], + "data": ["templates/promotion_templates.xml", "templates/wizard_templates.xml"], + "assets": { + "web.assets_frontend": [ + "/website_sale_loyalty_suggestion_wizard/static/src/scss/" + "website_sale_loyalty_suggestion_wizard.scss", + "/website_sale_loyalty_suggestion_wizard/static/src/js/*", + ] + }, +} diff --git a/website_sale_loyalty_suggestion_wizard/controllers/__init__.py b/website_sale_loyalty_suggestion_wizard/controllers/__init__.py new file mode 100644 index 000000000..4316008bd --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/controllers/__init__.py @@ -0,0 +1,3 @@ +from . import promotion_page +from . import main +from . import promotion_wizard diff --git a/website_sale_loyalty_suggestion_wizard/controllers/main.py b/website_sale_loyalty_suggestion_wizard/controllers/main.py new file mode 100644 index 000000000..57395616c --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/controllers/main.py @@ -0,0 +1,66 @@ +# Copyright 2021 Tecnativa - David Vidal +# Copyright 2024 Tecnativa - Pilar Vargas +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.http import request, route + +from odoo.addons.website_sale.controllers.main import WebsiteSale + + +class WebsiteSaleLoyaltySuggestionWizard(WebsiteSale): + def _get_sale_loyalty_reward_wizard(self, order, program): + wizard = ( + request.env["sale.loyalty.reward.wizard"] + .with_context(active_id=order.id) + .sudo() + .create({"selected_reward_id": program.reward_ids[:1].id}) + ) + return wizard + + @route( + ["/promotions//apply"], type="http", auth="public", website=True + ) + def promotion_program_apply(self, program_id, **kwargs): + program = request.env["loyalty.program"].sudo().browse(program_id).exists() + request.session.pop("wizard_id", None) + if not program or not program.active or not program.is_published: + return request.redirect("/shop/cart") + # Prevent to apply a promotion to a processed order + order = request.website.sale_get_order() + if order and order.state != "draft": + request.session["sale_order_id"] = None + order = request.website.sale_get_order() + # We won't apply it twice + if program in order._get_reward_programs(): + return request.redirect("/shop/cart") + # Let's inject some context into the view + request.session["promotion_id"] = program.id + request.session["order_id"] = order.id + return request.redirect("/shop/cart") + + @route() + def cart(self, **post): + error = request.session.get("error_promo_code") + response = super().cart(**post) + promotion = request.session.get("promotion_id") + order = request.session.get("sale_order_id") + if promotion: + program_id = request.env["loyalty.program"].sudo().browse(promotion) + order_id = request.env["sale.order"].browse(order) + wizard_id = self._get_sale_loyalty_reward_wizard(order_id, program_id) + mandatory_program_options = ( + response.qcontext.get("mandatory_program_options") + or wizard_id.loyalty_rule_line_ids + ) + response.qcontext["promotion_id"] = program_id + response.qcontext["order_id"] = order_id + response.qcontext["mandatory_program_options"] = mandatory_program_options + if error: + request.session["error_promo_code"] = error + return response + + @route(["/promotions/dismiss"], type="http", auth="public", website=True) + def promotion_in_cart_dismiss(self, **kw): + request.session.pop("promotion_id", None) + request.session.pop("error_promo_code", None) + request.session.pop("wizard_id", None) + return request.redirect("/shop/cart") diff --git a/website_sale_loyalty_suggestion_wizard/controllers/promotion_page.py b/website_sale_loyalty_suggestion_wizard/controllers/promotion_page.py new file mode 100644 index 000000000..3e385fd6c --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/controllers/promotion_page.py @@ -0,0 +1,23 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.http import request, route + +from odoo.addons.website_sale_loyalty_page.controllers.main import WebsiteSale + + +class LoyaltyPage(WebsiteSale): + @route() + def promotion(self, **post): + """Rules to render the 'Apply promotion' button""" + response = super().promotion(**post) + order = request.website.sale_get_order(force_create=True) + if not order: + return response + promo_values = response.qcontext.get("promos", []) + for promo_dict in promo_values: + promo_dict["applicable"] = False + promo = request.env["loyalty.program"].sudo().browse(promo_dict["id"]) + if promo not in order.sudo()._filter_programs_by_rules_with_products(): + continue + promo_dict["applicable"] = True + return response diff --git a/website_sale_loyalty_suggestion_wizard/controllers/promotion_wizard.py b/website_sale_loyalty_suggestion_wizard/controllers/promotion_wizard.py new file mode 100644 index 000000000..2d622910b --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/controllers/promotion_wizard.py @@ -0,0 +1,79 @@ +# Copyright 2021 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.exceptions import ValidationError +from odoo.http import request, route + +from odoo.addons.website_sale_loyalty.controllers.main import WebsiteSale + + +class WebsiteSaleLoyaltySuggestionWizardController(WebsiteSale): + def _process_promotion_lines(self, wizard_id, promotion_lines): + for product, qty in promotion_lines.items(): + line = wizard_id.loyalty_rule_line_ids.filtered( + lambda x: x.product_id.id == int(product) + ) + if len(promotion_lines) == 1: + qty = line.units_required - line.units_included + if not qty: + continue + line.units_to_include = qty + + def _process_reward_line_options(self, wizard_id, reward_line_options): + reward_id = wizard_id.selected_reward_id + if reward_id.reward_type == "product": + reward_products = reward_id.reward_product_ids + if len(reward_products) == 1: + wizard_id.selected_product_id = reward_products.id + else: + wizard_id.selected_product_id = ( + int(reward_line_options.get("selected_product_ids", False)[0]) + or wizard_id.selected_product_id.id + ) + + @route( + "/website_sale_loyalty_suggestion_wizard/get_defaults", + type="json", + auth="public", + methods=["POST"], + ) + def get_default_products(self): + program_id = ( + request.env["loyalty.program"] + .sudo() + .browse(request.session.get("promotion_id")) + ) + order_id = request.env["sale.order"].browse( + request.session.get("sale_order_id") + ) + wiz = self._get_sale_loyalty_reward_wizard(order_id, program_id) + return wiz.selected_product_id.ids + + @route( + "/website_sale_loyalty_suggestion_wizard/apply", + type="json", + auth="public", + methods=["POST"], + ) + def apply_promotion_public( + self, program_id, promotion_lines, reward_line_options, **kw + ): + """Frontend controller that wraps common methods and handles errors properly""" + order_id = request.env["sale.order"].browse( + request.session.get("sale_order_id") + ) + program_id = request.env["loyalty.program"].sudo().browse(program_id) + wiz = self._get_sale_loyalty_reward_wizard(order_id, program_id) + reward_id = reward_line_options.get("reward_id", False) + wiz.selected_reward_id = int(reward_id) or ( + program_id.reward_ids.id if len(program_id.reward_ids) == 1 else False + ) + if wiz.selected_reward_id: + self._process_promotion_lines(wiz, promotion_lines) + self._process_reward_line_options(wiz, reward_line_options) + try: + wiz.action_apply() + except ValidationError as e: + request.session["error_promo_code"] = str(e) + return + request.session.pop("promotion_id", None) + request.session.pop("error_promo_code", None) diff --git a/website_sale_loyalty_suggestion_wizard/i18n/es.po b/website_sale_loyalty_suggestion_wizard/i18n/es.po new file mode 100644 index 000000000..bb79e36d1 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/i18n/es.po @@ -0,0 +1,68 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_sale_loyalty_suggestion_wizard +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-18 16:05+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: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid " Configure" +msgstr " Configurar" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "×" +msgstr "×" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Add" +msgstr "Añadir" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.promotion_item +msgid "Apply" +msgstr "Aplicar" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "Close" +msgstr "Cerrar" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Configurable promotions suggested" +msgstr "Promociones configurables sugeridas" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "In order to apply this promotion you need to configure some options." +msgstr "Para aplicar esta promoción necesita configurar algunas opciones." + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Suggested Promotions:" +msgstr "Promociones Sugeridas:" + +#. module: website_sale_loyalty_suggestion_wizard +#: code:addons/website_sale_loyalty_suggestion_wizard/controllers/promotion_wizard.py:0 +#, python-format +msgid "This promotion can't be applied to this order" +msgstr "Esta promoción no puede ser aplicada en este pedido" + +#~ msgid "Sales Order" +#~ msgstr "Pedido de Venta" diff --git a/website_sale_loyalty_suggestion_wizard/i18n/website_sale_coupon_selection_wizard.pot b/website_sale_loyalty_suggestion_wizard/i18n/website_sale_coupon_selection_wizard.pot new file mode 100644 index 000000000..c0427e5a6 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/i18n/website_sale_coupon_selection_wizard.pot @@ -0,0 +1,61 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_sale_loyalty_suggestion_wizard +# +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: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid " Configure" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "×" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Add" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.promotion_item +msgid "Apply" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "Close" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Configurable promotions suggested" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.configure_promotion +msgid "In order to apply this promotion you need to configure some options." +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: model_terms:ir.ui.view,arch_db:website_sale_loyalty_suggestion_wizard.website_sale_loyalty_suggestion +msgid "Suggested Promotions:" +msgstr "" + +#. module: website_sale_loyalty_suggestion_wizard +#: code:addons/website_sale_loyalty_suggestion_wizard/controllers/promotion_wizard.py:0 +#, python-format +msgid "This promotion can't be applied to this order" +msgstr "" diff --git a/website_sale_loyalty_suggestion_wizard/readme/CONTRIBUTORS.rst b/website_sale_loyalty_suggestion_wizard/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..c17621ec1 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Tecnativa `_: + + * David Vidal + * Pilar Vargas diff --git a/website_sale_loyalty_suggestion_wizard/readme/DESCRIPTION.rst b/website_sale_loyalty_suggestion_wizard/readme/DESCRIPTION.rst new file mode 100644 index 000000000..39772c61a --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module makes suggestions for promotions whose rules include a product added to the +cart. It also allows you to configure and apply these promotions with an elegant eCommerce +wizard. diff --git a/website_sale_loyalty_suggestion_wizard/readme/USAGE.rst b/website_sale_loyalty_suggestion_wizard/readme/USAGE.rst new file mode 100644 index 000000000..7d4e7eb75 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/readme/USAGE.rst @@ -0,0 +1,18 @@ +To apply promotions: + +Option 1: + +#. Go to /promotions and choose the one apply. +#. You'll be prompted to check the promotion configuration. +#. Apply after that. + +Option 2: + +#. Add a product in the promotion rules. +#. A cart suggestion will appear. +#. Add the promotion and configure the options. + +Option 3: + +#. Select a suggested promotion in the shopping cart from a list of "Suggested promotions". +#. Add the promotion and configure the options. diff --git a/website_sale_loyalty_suggestion_wizard/static/description/icon.png b/website_sale_loyalty_suggestion_wizard/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/website_sale_loyalty_suggestion_wizard/static/description/icon.png differ diff --git a/website_sale_loyalty_suggestion_wizard/static/description/index.html b/website_sale_loyalty_suggestion_wizard/static/description/index.html new file mode 100644 index 000000000..ccf05bf8b --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/static/description/index.html @@ -0,0 +1,451 @@ + + + + + + +Website Sale Loyalty Suggestion Wizard + + + +
+

Website Sale Loyalty Suggestion Wizard

+ + +

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

+

This module makes suggestions for promotions whose rules include a product added to the +cart. It also allows you to configure and apply these promotions with an elegant eCommerce +wizard.

+

Table of contents

+ +
+

Usage

+

To apply promotions:

+

Option 1:

+
    +
  1. Go to /promotions and choose the one apply.
  2. +
  3. You’ll be prompted to check the promotion configuration.
  4. +
  5. Apply after that.
  6. +
+

Option 2:

+
    +
  1. Add a product in the promotion rules.
  2. +
  3. A cart suggestion will appear.
  4. +
  5. Add the promotion and configure the options.
  6. +
+

Option 3:

+
    +
  1. Select a suggested promotion in the shopping cart from a list of “Suggested promotions”.
  2. +
  3. Add the promotion and configure the options.
  4. +
+
+
+

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
    • +
    +
  • +
+
+
+

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/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard.js b/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard.js new file mode 100644 index 000000000..d18c39e39 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard.js @@ -0,0 +1,89 @@ +/* Copyright 2021 Tecnativa - David Vidal + * License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ +odoo.define("website_sale_loyalty_suggestion_wizard", function (require) { + "use strict"; + + const CouponSelectionMixin = require("website_sale_loyalty_suggestion_wizard.CouponSelectionMixin"); + const publicWidget = require("web.public.widget"); + const websiteSale = require("website_sale.website_sale"); + + publicWidget.registry.WebsiteSaleLoyaltySuggestionWizard = + publicWidget.Widget.extend(CouponSelectionMixin, { + selector: "#o_promo_configure", + events: Object.assign({}, CouponSelectionMixin.events || {}, { + "click .o_coupon_selection_wizard_apply": "apply_promotion", + }), + /** + * @override + */ + start: function () { + const def = this._super.apply(this, arguments); + this.program_id = $("span.js_promotion_change") + .first() + .data().promotionId; + this.website_sale_order = $("span.website_sale_order_id") + .first() + .data().orderId; + $("span.js_promotion_change").trigger("change"); + return def; + }, + /** + * Communicate the form options to the controller. An object with product id + * key and quantity as value is passed to try to apply them to the order and + * check if the selected coupon can be applied. + * + * @returns {Promise} + */ + apply_promotion: async function () { + const $modal = this.$el; + const $wizard_inputs = $modal.find("input.js_promotion_item_quantity"); + const $reward_options = $modal.find( + "input.reward_optional_input:checked" + ); + const $cardBodyContainer = $reward_options.closest( + "div.csw_optional_reward" + ); + const $reward_selected_product = + $cardBodyContainer.find(".bg-info input"); + var promotion_values = {}; + $wizard_inputs.each(function () { + const product_id = this.dataset.product_id; + promotion_values[product_id] = + (promotion_values[product_id] || 0) + + (parseInt(this.value, 10) || 0); + }); + var reward_line_options = { + reward_id: $reward_options.val(), + selected_product_ids: $reward_selected_product + .map(function () { + return this.value; + }) + .get(), + }; + await this._rpc({ + route: "/website_sale_loyalty_suggestion_wizard/apply", + params: { + program_id: this.program_id, + sale_order_id: this.website_sale_order, + promotion_lines: promotion_values, + reward_line_options: reward_line_options, + }, + }); + $("#o_promo_configure_modal").modal("hide"); + window.location = "/shop/cart"; + }, + }); + + websiteSale.websiteSaleCart.include({ + /** + * Opens the promotion modal by default when the cart is reloaded + * @override + */ + start: function () { + const def = this._super.apply(this, arguments); + return def.then(() => { + $("#o_promo_configure_modal").modal("show"); + }); + }, + }); +}); diff --git a/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard_mixin.js b/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard_mixin.js new file mode 100644 index 000000000..b12a044ae --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/static/src/js/website_sale_loyalty_suggestion_wizard_mixin.js @@ -0,0 +1,131 @@ +odoo.define("website_sale_loyalty_suggestion_wizard.CouponSelectionMixin", function () { + "use strict"; + + var CouponSelectionMixin = { + events: { + "change .js_promotion_item_quantity": "_onchange_quantity", + "click button.csw_add_quantity, button.csw_remove_quantity": + "_onclick_add_or_remove", + "click div.csw_optional_reward": "_onclick_choose_reward", + "click div.csw_optional_product": "_onclick_choose_product", + }, + /** + * When the quantity changes, apply some logic to help the user checking if + * the promotion can be applied or not. + * + * @param {InputEvent} ev + */ + _onchange_quantity: function (ev) { + var $row = $(ev.currentTarget).closest(".row.pl-3.pr-3"); + var $needed_qty_span = $row.find(".csw_criteria_needed_qty"); + var $criteria_icon = $row.find(".csw_criteria_icon"); + var $row_add_buttons = $row.find(".csw_add_quantity"); + var $inputs = $row.find("input"); + var needed_qty = parseInt($needed_qty_span.data("qty"), 10); + var current_row_qty = 0; + _.each($inputs, function (inp) { + current_row_qty += parseInt(inp.value, 10); + }); + needed_qty = Math.max(needed_qty - current_row_qty, 0); + if (needed_qty) { + $needed_qty_span.parent().removeClass("d-none"); + $row_add_buttons.removeAttr("disabled"); + $criteria_icon.removeClass(["fa-certificate", "text-success"]); + $criteria_icon.addClass(["fa-sun-o", "text-warning"]); + $inputs.closest(".card").removeClass("border-success"); + $needed_qty_span.text(needed_qty); + } else { + $needed_qty_span.parent().addClass("d-none"); + $row_add_buttons.attr("disabled", "disabled"); + $criteria_icon.removeClass(["fa-sun-o", "text-warning"]); + $criteria_icon.addClass(["fa-certificate", "text-success"]); + $inputs + .filter(function () { + return this.value !== "0"; + }) + .closest(".card") + .addClass("border-success"); + } + }, + /** + * Buttons circuitry + * + * @param {InputEvent} ev + */ + _onclick_add_or_remove: function (ev) { + ev.preventDefault(); + var $button = $(ev.currentTarget); + var $input = $button.closest(".input-group").find("input"); + var min = parseFloat($input.attr("min") || 0); + var max = parseFloat($input.attr("max") || Infinity); + var previousQty = parseFloat($input.val() || 0, 10); + var quantity = ($button.has(".fa-minus").length ? -1 : 1) + previousQty; + var newQty = quantity > min ? (quantity < max ? quantity : max) : min; + if (newQty !== previousQty) { + $input.val(newQty).trigger("change"); + } + }, + /** + * Reward card click circuitry + * + * @param {InputEvent} ev + */ + _onclick_choose_reward: function (ev) { + ev.preventDefault(); + var $input = $(ev.currentTarget).find("input[name='reward']"); + var $input_siblings = $(ev.currentTarget.closest(".row")).find( + "input[name='reward']" + ); + _.each($input_siblings, function ($sibling) { + $($sibling) + .closest(".csw_optional_reward") + .not(ev.currentTarget) + .find(".bg-info") + .removeClass("bg-info"); + $($sibling).closest(".csw_optional_reward").removeClass("bg-success"); + }); + $input.prop("checked", true); + $(ev.currentTarget).addClass("bg-success"); + this._choose_default_products($(ev.currentTarget)); + }, + + _choose_default_products: async function ($target) { + const defaults = await this._rpc({ + route: "/website_sale_loyalty_suggestion_wizard/get_defaults", + }); + for (var def of defaults) { + const $input = $target.find( + "input.reward_product_input[value='" + def + "']" + ); + var group = $input.attr("name"); + var $input_siblings = $target.find( + "input[name='" + group + "']:checked" + ); + if (!$input_siblings.length) { + $input.prop("checked", true); + $input.closest(".csw_optional_product").addClass("bg-info"); + } + } + console.log(defaults); + }, + /** + * Reward product card click circuitry + * + * @param {InputEvent} ev + */ + _onclick_choose_product: function (ev) { + ev.preventDefault(); + var $input = $(ev.currentTarget).find("input.reward_product_input"); + var group = $input.attr("name"); + var $input_siblings = $(ev.currentTarget.closest(".card-body")).find( + "input[name='" + group + "']" + ); + _.each($input_siblings, function ($sibling) { + $($sibling).closest(".card").removeClass("bg-info"); + }); + $input.prop("checked", true); + $(ev.currentTarget).addClass("bg-info"); + }, + }; + return CouponSelectionMixin; +}); diff --git a/website_sale_loyalty_suggestion_wizard/static/src/scss/website_sale_loyalty_suggestion_wizard.scss b/website_sale_loyalty_suggestion_wizard/static/src/scss/website_sale_loyalty_suggestion_wizard.scss new file mode 100644 index 000000000..7750d2607 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/static/src/scss/website_sale_loyalty_suggestion_wizard.scss @@ -0,0 +1,33 @@ +.oe_promotion_wizard_item_infos { + font-size: 0.9rem; +} +.oe_scw_criteria_condition, +.oe_scw_reward_retribution { + font-weight: bold; + font-size: 3rem; + -webkit-text-stroke: 1px white; +} +.oe_scw_reward_retribution { + font-size: 2rem; +} +.js_promotion_item_quantity { + max-width: 4rem; + padding: 0.7rem; + text-align: center; + border-color: transparent; +} +.csw_optional_reward { + cursor: pointer; +} +.csw_reward_img { + height: 2rem; + width: 2rem; + object-fit: cover; + border: 1px solid; +} +.js_promotion { + h6.card-title { + margin: 0; + padding: 10px; + } +} diff --git a/website_sale_loyalty_suggestion_wizard/templates/promotion_templates.xml b/website_sale_loyalty_suggestion_wizard/templates/promotion_templates.xml new file mode 100644 index 000000000..bba0548c1 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/templates/promotion_templates.xml @@ -0,0 +1,190 @@ + + + + + + + + + diff --git a/website_sale_loyalty_suggestion_wizard/templates/wizard_templates.xml b/website_sale_loyalty_suggestion_wizard/templates/wizard_templates.xml new file mode 100644 index 000000000..e2288ef54 --- /dev/null +++ b/website_sale_loyalty_suggestion_wizard/templates/wizard_templates.xml @@ -0,0 +1,275 @@ + + + + + + + + + + +