diff --git a/rotordc_optional_product/README.rst b/rotordc_optional_product/README.rst new file mode 100644 index 00000000..b0bee3f4 --- /dev/null +++ b/rotordc_optional_product/README.rst @@ -0,0 +1,71 @@ +======================== +RotorDC Optional Product +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:dabfd73311994804673378c159347d6e662683dea95768701302679c9b064f04 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-coopiteasy%2Fcie--custom-lightgray.png?logo=github + :target: https://github.com/coopiteasy/cie-custom/tree/16.0/rotordc_optional_product + :alt: coopiteasy/cie-custom + +|badge1| |badge2| |badge3| + +This module has two main behaviours: + +- When selecting optional products in the 'add to cart' modal, the user is + prevented from adding more than one optional product of the same internal + category. +- When checking out, the user is redirected to an informative error page when + they did not select all available optional products for their order. e.g. if a + lamp has optional products with the internal categories 'light bulb' and + 'cable', then the user **must** select one optional product of both categories + when ordering a lamp. + +**Table of contents** + +.. contents:: + :local: + +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 +~~~~~~~ + +* Coop IT Easy SC + +Contributors +~~~~~~~~~~~~ + +* `Coop IT Easy SC `_: + + * Carmen Bianca Bakker + +Maintainers +~~~~~~~~~~~ + +This module is part of the `coopiteasy/cie-custom `_ project on GitHub. + +You are welcome to contribute. diff --git a/rotordc_optional_product/__init__.py b/rotordc_optional_product/__init__.py new file mode 100644 index 00000000..e046e49f --- /dev/null +++ b/rotordc_optional_product/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/rotordc_optional_product/__manifest__.py b/rotordc_optional_product/__manifest__.py new file mode 100644 index 00000000..2f534bcf --- /dev/null +++ b/rotordc_optional_product/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "RotorDC Optional Product", + "summary": """ + Custom modifications regarding RotorDC's use of optional products.""", + "version": "16.0.1.0.0", + "category": "Sales", + "website": "https://coopiteasy.be", + "author": "Coop IT Easy SC", + "license": "AGPL-3", + "depends": [ + "sale_product_configurator", + "web", + "website_sale", + ], + "data": [ + "views/sale_product_configurator_templates.xml", + "views/templates.xml", + ], + "assets": { + "web.assets_frontend": [ + "rotordc_optional_product/static/src/js/product_configurator_modal.esm.js", + ], + }, +} diff --git a/rotordc_optional_product/controllers/__init__.py b/rotordc_optional_product/controllers/__init__.py new file mode 100644 index 00000000..12a7e529 --- /dev/null +++ b/rotordc_optional_product/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/rotordc_optional_product/controllers/main.py b/rotordc_optional_product/controllers/main.py new file mode 100644 index 00000000..c7354386 --- /dev/null +++ b/rotordc_optional_product/controllers/main.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import http +from odoo.http import request + +from odoo.addons.website_sale.controllers.main import WebsiteSale + + +class MissingCategoryException(Exception): + def __init__(self, *args, **kwargs): + super().__init__(*args) + self.unused_categs = kwargs.get("unused_categs") + + +def _verify_products_have_all_optional_products(env, order): + """On an order, verify that each product has chosen one optional product of + each available category to that product. + + Return a Dict[sale.order.line, product.category] with all order lines that + have missing categories. + """ + order_line_unused_map = {} + for order_line in order.order_line: + try: + _verify_line(order_line) + except MissingCategoryException as err: + order_line_unused_map[order_line] = err.unused_categs + return order_line_unused_map + + +def _verify_line(order_line): + # FIXME: Consider also raising an exception if there is a duplicate encounter. + categ_ids = { + template_id.categ_id + for template_id in order_line.product_id.optional_product_ids + } + encountered = {line.product_id.categ_id for line in order_line.option_line_ids} + # unused_categs is all elements in categ_ids that are not in encountered, + # NOT the other way around. The other way around is not tested, assuming + # that Odoo works soundly. + unused_categs = categ_ids.difference(encountered) + if unused_categs: + raise MissingCategoryException(unused_categs=unused_categs) + + +class WebsiteSaleDelivery(WebsiteSale): + @http.route() + def checkout(self, **post): + order = request.website.sale_get_order() + result = _verify_products_have_all_optional_products(request.env, order) + + if result: + return request.render( + "rotordc_optional_product.missing_categories", + { + "order_line_unused_map": result, + }, + ) + + return super().checkout(**post) diff --git a/rotordc_optional_product/i18n/rotordc_optional_product.pot b/rotordc_optional_product/i18n/rotordc_optional_product.pot new file mode 100644 index 00000000..9f131620 --- /dev/null +++ b/rotordc_optional_product/i18n/rotordc_optional_product.pot @@ -0,0 +1,66 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rotordc_optional_product +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.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: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "
\n" +" Missing categories:" +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "Go to" +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "If this problem persists, please contact us." +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "In order to rectify this problem, take the following steps:" +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "Re-add the products. When you do, make sure to also add any available 'Optional Products'." +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "The order checkout was interrupted because your order is not complete. You added one or more products to your cart that are not completely configured. The products are listed below together with the configurations that are missing." +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "Try checking out again." +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "Your order is incomplete" +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "and remove the above listed products." +msgstr "" + +#. module: rotordc_optional_product +#: model_terms:ir.ui.view,arch_db:rotordc_optional_product.missing_categories +msgid "your cart" +msgstr "" + diff --git a/rotordc_optional_product/readme/CONTRIBUTORS.rst b/rotordc_optional_product/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..d64451ed --- /dev/null +++ b/rotordc_optional_product/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Coop IT Easy SC `_: + + * Carmen Bianca Bakker diff --git a/rotordc_optional_product/readme/DESCRIPTION.rst b/rotordc_optional_product/readme/DESCRIPTION.rst new file mode 100644 index 00000000..45bb5074 --- /dev/null +++ b/rotordc_optional_product/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +This module has two main behaviours: + +- When selecting optional products in the 'add to cart' modal, the user is + prevented from adding more than one optional product of the same internal + category. +- When checking out, the user is redirected to an informative error page when + they did not select all available optional products for their order. e.g. if a + lamp has optional products with the internal categories 'light bulb' and + 'cable', then the user **must** select one optional product of both categories + when ordering a lamp. diff --git a/rotordc_optional_product/static/description/index.html b/rotordc_optional_product/static/description/index.html new file mode 100644 index 00000000..d20f2add --- /dev/null +++ b/rotordc_optional_product/static/description/index.html @@ -0,0 +1,428 @@ + + + + + +RotorDC Optional Product + + + +
+

RotorDC Optional Product

+ + +

Beta License: AGPL-3 coopiteasy/cie-custom

+

This module has two main behaviours:

+
    +
  • When selecting optional products in the ‘add to cart’ modal, the user is +prevented from adding more than one optional product of the same internal +category.
  • +
  • When checking out, the user is redirected to an informative error page when +they did not select all available optional products for their order. e.g. if a +lamp has optional products with the internal categories ‘light bulb’ and +‘cable’, then the user must select one optional product of both categories +when ordering a lamp.
  • +
+

Table of contents

+ +
+

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

+
    +
  • Coop IT Easy SC
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the coopiteasy/cie-custom project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/rotordc_optional_product/static/src/js/product_configurator_modal.esm.js b/rotordc_optional_product/static/src/js/product_configurator_modal.esm.js new file mode 100644 index 00000000..76038411 --- /dev/null +++ b/rotordc_optional_product/static/src/js/product_configurator_modal.esm.js @@ -0,0 +1,45 @@ +/** @odoo-module **/ + +import {OptionalProductsModal} from "@sale_product_configurator/js/product_configurator_modal"; +import {patch} from "@web/core/utils/patch"; + +patch(OptionalProductsModal.prototype, "RotorDC customization", { + _getCategId: function ($element) { + return $element.find("input.product_categ_id").val(); + }, + _onAddOption: function ($modal, $parent, productTemplateId) { + var categ_id = this._getCategId($parent); + var self = this; + + // Disable all 'add to cart' buttons of products of the same + // internal category. + $modal.find(".js_product:not(.in_cart):not(.main_product)").each(function () { + var $item = $(this); + var item_product_id = $item.find(".product_id").val(); + if ( + item_product_id !== $parent.find(".product_id").val() && + self._getCategId($item) === categ_id + ) { + var $button = $item.find("a.js_add"); + $button.addClass("disabled"); + } + }); + + this._super($modal, $parent, productTemplateId); + }, + _onRemoveOption: function ($modal, $parent, productTemplateId) { + var categ_id = this._getCategId($parent); + var self = this; + + // Re-enable all buttons. + $modal.find(".js_product:not(.main_product)").each(function () { + var $item = $(this); + if (self._getCategId($item) === categ_id) { + var $button = $item.find("a.js_add"); + $button.removeClass("disabled"); + } + }); + + this._super($modal, $parent, productTemplateId); + }, +}); diff --git a/rotordc_optional_product/views/sale_product_configurator_templates.xml b/rotordc_optional_product/views/sale_product_configurator_templates.xml new file mode 100644 index 00000000..0238c76a --- /dev/null +++ b/rotordc_optional_product/views/sale_product_configurator_templates.xml @@ -0,0 +1,15 @@ + + + + diff --git a/rotordc_optional_product/views/templates.xml b/rotordc_optional_product/views/templates.xml new file mode 100644 index 00000000..ae576215 --- /dev/null +++ b/rotordc_optional_product/views/templates.xml @@ -0,0 +1,41 @@ + + + + + diff --git a/setup/rotordc_optional_product/odoo/addons/rotordc_optional_product b/setup/rotordc_optional_product/odoo/addons/rotordc_optional_product new file mode 120000 index 00000000..d7620108 --- /dev/null +++ b/setup/rotordc_optional_product/odoo/addons/rotordc_optional_product @@ -0,0 +1 @@ +../../../../rotordc_optional_product \ No newline at end of file diff --git a/setup/rotordc_optional_product/setup.py b/setup/rotordc_optional_product/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/rotordc_optional_product/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)