diff --git a/pos_order_to_sale_order_sale_financial_risk/README.rst b/pos_order_to_sale_order_sale_financial_risk/README.rst new file mode 100644 index 0000000000..ffb9e194eb --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/README.rst @@ -0,0 +1,83 @@ +========================== +Sale Financial Risk in POS +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:39ff0f3d524db12a936d64030ab41c029b0f118112ea6c365464ceee95e247c8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fpos-lightgray.png?logo=github + :target: https://github.com/OCA/pos/tree/16.0/pos_order_to_sale_order_sale_financial_risk + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-16-0/pos-16-0-pos_order_to_sale_order_sale_financial_risk + :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/pos&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is a bridging module between **sale_financial_risk** and **pos_order_to_sale_order**. It implements control for the Sale Orders created from POS. +Same warning or blocking message will be displayed in POS as if an order was created from the backend. + +**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 +~~~~~~~ + +* Cetmix + +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-geomer198| image:: https://github.com/geomer198.png?size=40px + :target: https://github.com/geomer198 + :alt: geomer198 +.. |maintainer-CetmixGitDrone| image:: https://github.com/CetmixGitDrone.png?size=40px + :target: https://github.com/CetmixGitDrone + :alt: CetmixGitDrone + +Current `maintainers `__: + +|maintainer-geomer198| |maintainer-CetmixGitDrone| + +This module is part of the `OCA/pos `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pos_order_to_sale_order_sale_financial_risk/__init__.py b/pos_order_to_sale_order_sale_financial_risk/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_order_to_sale_order_sale_financial_risk/__manifest__.py b/pos_order_to_sale_order_sale_financial_risk/__manifest__.py new file mode 100644 index 0000000000..a2e251bcbe --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/__manifest__.py @@ -0,0 +1,21 @@ +{ + "name": "Sale Financial Risk in POS", + "version": "16.0.1.0.0", + "category": "Sales/Point of Sale", + "summary": "Sale Financial Risk control for Sales Orders created from POS", + "depends": ["sale_financial_risk", "pos_order_to_sale_order_delivery"], + "website": "https://github.com/OCA/pos", + "author": "Cetmix,Odoo Community Association (OCA)", + "maintainers": ["geomer198", "CetmixGitDrone"], + "data": ["data/demo.xml"], + "installable": True, + "assets": { + "point_of_sale.assets": [ + "pos_order_to_sale_order_sale_financial_risk/static/src/js/*.esm.js", + ], + "web.assets_tests": [ + "pos_order_to_sale_order_sale_financial_risk/static/src/tests/tours/SaleFinancialRiskPosCompatibility.tour.esm.js", # noqa + ], + }, + "license": "AGPL-3", +} diff --git a/pos_order_to_sale_order_sale_financial_risk/data/demo.xml b/pos_order_to_sale_order_sale_financial_risk/data/demo.xml new file mode 100644 index 0000000000..9e0b7f9462 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/data/demo.xml @@ -0,0 +1,10 @@ + + + + + Test Partner + 1.0 + 1.0 + + + diff --git a/pos_order_to_sale_order_sale_financial_risk/models/__init__.py b/pos_order_to_sale_order_sale_financial_risk/models/__init__.py new file mode 100644 index 0000000000..77ec692e71 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/models/__init__.py @@ -0,0 +1,2 @@ +from . import pos_session +from . import sale_order diff --git a/pos_order_to_sale_order_sale_financial_risk/models/pos_session.py b/pos_order_to_sale_order_sale_financial_risk/models/pos_session.py new file mode 100644 index 0000000000..6d56e5828b --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/models/pos_session.py @@ -0,0 +1,17 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class PosSession(models.Model): + _inherit = "pos.session" + + def _get_pos_ui_res_users(self, params): + data = super()._get_pos_ui_res_users(params) + # Adding key that checks user has group 'risk exception' + user = self.env["res.users"].browse(data["id"]) + data["has_role_risk_manager"] = user.has_group( + "account_financial_risk.group_overpass_partner_risk_exception", + ) + return data diff --git a/pos_order_to_sale_order_sale_financial_risk/models/sale_order.py b/pos_order_to_sale_order_sale_financial_risk/models/sale_order.py new file mode 100644 index 0000000000..2b5924199a --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/models/sale_order.py @@ -0,0 +1,16 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + @api.model + def create_order_from_pos(self, order_data, action): + if "bypass_risk" in order_data: + # Adding does not exist field in context for checking value on UI + bypass_risk = order_data.pop("bypass_risk") + self = self.with_context(bypass_risk=bypass_risk) + return super(SaleOrder, self).create_order_from_pos(order_data, action) diff --git a/pos_order_to_sale_order_sale_financial_risk/readme/DESCRIPTION.rst b/pos_order_to_sale_order_sale_financial_risk/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..4b92abe679 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module is a bridging module between **sale_financial_risk** and **pos_order_to_sale_order**. It implements control for the Sale Orders created from POS. +Same warning or blocking message will be displayed in POS as if an order was created from the backend. diff --git a/pos_order_to_sale_order_sale_financial_risk/static/description/index.html b/pos_order_to_sale_order_sale_financial_risk/static/description/index.html new file mode 100644 index 0000000000..aff7b0b7e2 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/static/description/index.html @@ -0,0 +1,417 @@ + + + + + + +Sale Financial Risk in POS + + + +
+

Sale Financial Risk in POS

+ + +

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

+

This module is a bridging module between sale_financial_risk and pos_order_to_sale_order. It implements control for the Sale Orders created from POS. +Same warning or blocking message will be displayed in POS as if an order was created from the backend.

+

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

+
    +
  • Cetmix
  • +
+
+
+

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 maintainers:

+

geomer198 CetmixGitDrone

+

This module is part of the OCA/pos project on GitHub.

+

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

+
+
+
+ + diff --git a/pos_order_to_sale_order_sale_financial_risk/static/img/desktop_table.jpg b/pos_order_to_sale_order_sale_financial_risk/static/img/desktop_table.jpg new file mode 100644 index 0000000000..7c8b939694 Binary files /dev/null and b/pos_order_to_sale_order_sale_financial_risk/static/img/desktop_table.jpg differ diff --git a/pos_order_to_sale_order_sale_financial_risk/static/src/js/CreateOrderPopup.esm.js b/pos_order_to_sale_order_sale_financial_risk/static/src/js/CreateOrderPopup.esm.js new file mode 100644 index 0000000000..224d179a69 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/static/src/js/CreateOrderPopup.esm.js @@ -0,0 +1,123 @@ +/** @odoo-module **/ + +import CreateOrderPopup from "point_of_sale.CreateOrderPopup"; +import Registries from "point_of_sale.Registries"; + +export const CreateOrderPopupRisk = (CreateOrderPopup) => + class CreateOrderPopupRisk extends CreateOrderPopup { + /** + * Calculate risk amount by order + * @returns {Promise<*>} order total + * @private + */ + async _calculateRiskAmount() { + const order = this.env.pos.get_order(); + return order.get_total_with_tax() + order.get_rounding_applied(); + } + + /** + * Get risk exception message for partner by risk data and order amount + * @param {Object} partner - partner risk fields {...} + * @param {String} riskAmount - risk order amount + * @returns {*|String} error message or empty string + * @private + */ + _getRiskExceptionMessage(partner, riskAmount) { + if (partner.risk_exception) { + return this.env._t("Financial risk exceeded.\n"); + } + if ( + partner.risk_sale_order_limit && + partner.risk_sale_order + riskAmount > partner.risk_sale_order_limit + ) { + return this.env._t("This sale order exceeds the sales orders risk.\n"); + } + if ( + partner.risk_sale_order_include && + partner.risk_total + riskAmount > partner.credit_limit + ) { + return this.env._t("This sale order exceeds the financial risk.\n"); + } + return ""; + } + + /** + * Get Partners field list + * @returns {String[]} + * @private + */ + _getPartnerFields() { + return [ + "risk_exception", + "risk_sale_order_limit", + "risk_sale_order", + "risk_sale_order_include", + "risk_total", + "credit_limit", + ]; + } + + /** + * Get risk fields values for partner of order + * @returns {Promise<*>} partner fields Object + * @private + */ + async _getOrderPartnerRiskValue() { + const order = this.env.pos.get_order(); + const [partnerRiskValues] = await this.rpc({ + model: "res.partner", + method: "read", + args: [order.partner.id, this._getPartnerFields()], + }); + return partnerRiskValues; + } + + /** + * Check Risk for partner by order + * @param {String} exception_msg Exception message string + * @param {String} order_state order state + * @returns {Promise<*>} + * @private + */ + async _handleRiskException(exception_msg, order_state) { + const order = this.env.pos.get_order(); + if (this.env.pos.user.has_role_risk_manager) { + const {confirmed} = await this.showPopup("ConfirmPopup", { + title: this.env._t("Partner risk exceeded"), + body: exception_msg, + }); + if (confirmed) { + order.set_bypass_risk(true); + const result = await super._actionCreateSaleOrder(order_state); + order.set_bypass_risk(false); + return result; + } + } else { + await this.showPopup("ErrorPopup", { + title: this.env._t("Partner risk exceeded"), + body: exception_msg, + }); + } + return await this.cancel(); + } + + async _actionCreateSaleOrder(order_state) { + if (order_state === "draft") { + // Default behavior + return await super._actionCreateSaleOrder(order_state); + } + this.extraContext = false; + const riskAmount = await this._calculateRiskAmount(); + const partnerRiskValue = await this._getOrderPartnerRiskValue(); + const exceptionMsg = this._getRiskExceptionMessage( + partnerRiskValue, + riskAmount + ); + if (!exceptionMsg) { + return await super._actionCreateSaleOrder(order_state); + } + return await this._handleRiskException(exceptionMsg, order_state); + } + }; + +Registries.Component.extend(CreateOrderPopup, CreateOrderPopupRisk); diff --git a/pos_order_to_sale_order_sale_financial_risk/static/src/js/models.esm.js b/pos_order_to_sale_order_sale_financial_risk/static/src/js/models.esm.js new file mode 100644 index 0000000000..62b51ddf5b --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/static/src/js/models.esm.js @@ -0,0 +1,28 @@ +/** @odoo-module **/ + +import {Order} from "point_of_sale.models"; +import Registries from "point_of_sale.Registries"; + +const PosSaleFinancialRiskOrder = (Order) => + class PosSaleFinancialRiskOrder extends Order { + constructor() { + super(...arguments); + this.bypass_risk = false; + } + set_bypass_risk(bypass_risk) { + this.bypass_risk = bypass_risk; + } + export_as_JSON() { + const result = super.export_as_JSON(...arguments); + result.bypass_risk = this.bypass_risk; + return result; + } + init_from_JSON(json) { + super.init_from_JSON(...arguments); + this.bypass_risk = json.bypass_risk; + } + }; + +Registries.Model.extend(Order, PosSaleFinancialRiskOrder); + +export default PosSaleFinancialRiskOrder; diff --git a/pos_order_to_sale_order_sale_financial_risk/static/src/tests/tours/SaleFinancialRiskPosCompatibility.tour.esm.js b/pos_order_to_sale_order_sale_financial_risk/static/src/tests/tours/SaleFinancialRiskPosCompatibility.tour.esm.js new file mode 100644 index 0000000000..7c32d23948 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/static/src/tests/tours/SaleFinancialRiskPosCompatibility.tour.esm.js @@ -0,0 +1,80 @@ +odoo.define( + "pos_order_to_sale_order_sale_financial_risk.SaleFinancialRiskPosCompatibility", + function (require) { + const Tour = require("web_tour.tour"); + + const steps = [ + { + content: "Test pos_order_to_sale_order: Waiting for loading to finish", + trigger: "body:not(:has(.loader))", + // eslint-disable-next-line no-empty-function + run: () => {}, + }, + { + content: "Test pos_order_to_sale_order: Close Opening cashbox popup", + trigger: "div.opening-cash-control .button:contains('Open session')", + }, + { + content: + "Test pos_order_to_sale_order: Leave category displayed by default", + trigger: ".breadcrumb-home", + // eslint-disable-next-line no-empty-function + run: () => {}, + }, + { + content: + "Test pos_order_to_sale_order: Order a 'Whiteboard Pen' (price 3.20)", + trigger: ".product-list .product-name:contains('Whiteboard Pen')", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Click on 'Customer' Button", + trigger: "button.set-partner", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Select a customer 'Test Partner'", + trigger: "tr.partner-line td div:contains('Abdulah')", + }, + { + content: "Test pos_order_to_sale_order: Click on More...", + trigger: "div.control-button:contains('More...')", + skip_trigger: "span.control-button span:contains('Create Order')", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Click on 'Create Order' Button", + trigger: "span.control-button span:contains('Create Order')", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Click on 'Create Confirmed Sale Order' Button", + trigger: + "div.button-sale-order span:contains('Create Confirmed Sale Order')", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Confirm popup click on 'Cancel' Button", + trigger: ".modal-dialog .cancel", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Close the Point of Sale frontend", + trigger: ".header-button", + }, + { + content: + "Test pos_order_to_sale_order_sale_financial_risk: Confirm closing the frontend", + trigger: ".header-button", + // eslint-disable-next-line no-empty-function + run: () => {}, + }, + ]; + + Tour.register( + "SaleOrderConfirmFinancialRiskPosCompatibility", + {test: true, url: "/pos/ui"}, + steps + ); + } +); diff --git a/pos_order_to_sale_order_sale_financial_risk/tests/__init__.py b/pos_order_to_sale_order_sale_financial_risk/tests/__init__.py new file mode 100644 index 0000000000..3c930e73c1 --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/tests/__init__.py @@ -0,0 +1 @@ +from . import test_pos_ui diff --git a/pos_order_to_sale_order_sale_financial_risk/tests/test_pos_ui.py b/pos_order_to_sale_order_sale_financial_risk/tests/test_pos_ui.py new file mode 100644 index 0000000000..9d6fe1c60b --- /dev/null +++ b/pos_order_to_sale_order_sale_financial_risk/tests/test_pos_ui.py @@ -0,0 +1,23 @@ +from odoo.tests import tagged + +from odoo.addons.point_of_sale.tests.test_frontend import TestPointOfSaleHttpCommon + + +@tagged("post_install", "-at_install") +class TestUi(TestPointOfSaleHttpCommon): + def test_pos_order_to_sale_order_financial_risk(self): + self.main_pos_config.open_ui() + self.env["res.partner"].create( + { + "name": "Abdulah", + "company_type": "company", + "risk_sale_order_include": True, + "risk_sale_order_limit": 2, + "credit_limit": 2, + } + ) + self.start_tour( + f"/pos/ui?config_id={self.main_pos_config.id}", + "SaleOrderConfirmFinancialRiskPosCompatibility", + login="accountman", + ) diff --git a/setup/pos_order_to_sale_order_sale_financial_risk/odoo/addons/pos_order_to_sale_order_sale_financial_risk b/setup/pos_order_to_sale_order_sale_financial_risk/odoo/addons/pos_order_to_sale_order_sale_financial_risk new file mode 120000 index 0000000000..904643cdba --- /dev/null +++ b/setup/pos_order_to_sale_order_sale_financial_risk/odoo/addons/pos_order_to_sale_order_sale_financial_risk @@ -0,0 +1 @@ +../../../../pos_order_to_sale_order_sale_financial_risk \ No newline at end of file diff --git a/setup/pos_order_to_sale_order_sale_financial_risk/setup.py b/setup/pos_order_to_sale_order_sale_financial_risk/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/pos_order_to_sale_order_sale_financial_risk/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)