diff --git a/account_customer_wallet/README.rst b/account_customer_wallet/README.rst index abae389a1..94dc9e0b9 100644 --- a/account_customer_wallet/README.rst +++ b/account_customer_wallet/README.rst @@ -44,7 +44,8 @@ Setting this up requires a few careful steps: - In the Invoicing settings, set the Customer Wallet Account to the previously created account. - (Optional) Create a product (Wallet Product), and enable the Wallet Product - toggle. Set the income and expense account to the previously created account. + toggle. Set the income and expense account will be automatically the Customer + wallet account previously created. Changelog ========= diff --git a/account_customer_wallet/__init__.py b/account_customer_wallet/__init__.py index 0650744f6..aee8895e7 100644 --- a/account_customer_wallet/__init__.py +++ b/account_customer_wallet/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/account_customer_wallet/__manifest__.py b/account_customer_wallet/__manifest__.py index 7820c8ff2..842b3d4f1 100644 --- a/account_customer_wallet/__manifest__.py +++ b/account_customer_wallet/__manifest__.py @@ -21,6 +21,7 @@ "views/product_template_views.xml", "views/res_config_settings_views.xml", "views/res_partner_views.xml", + "wizards/account_payment_register_views.xml", ], "demo": [ "demo/account_account_demo.xml", diff --git a/account_customer_wallet/models/product_template.py b/account_customer_wallet/models/product_template.py index d5ca78606..768138d37 100644 --- a/account_customer_wallet/models/product_template.py +++ b/account_customer_wallet/models/product_template.py @@ -10,7 +10,14 @@ class ProductTemplate(models.Model): is_customer_wallet_product = fields.Boolean( string="Wallet Product", - help="Check this box if this product is used to credit" - " customer wallets. Important note : you should set the" - " the same income and expense account as the journal wallet.", + help="Check this box if this product is used to credit" " customer wallets.", ) + + def _get_product_accounts(self): + if self.is_customer_wallet_product: + company = self.company_id or self.env.company + return { + "income": company.customer_wallet_account_id, + "expense": company.customer_wallet_account_id, + } + return super()._get_product_accounts() diff --git a/account_customer_wallet/readme/CONFIGURE.rst b/account_customer_wallet/readme/CONFIGURE.rst index 72b20eb09..bf8263955 100644 --- a/account_customer_wallet/readme/CONFIGURE.rst +++ b/account_customer_wallet/readme/CONFIGURE.rst @@ -10,4 +10,5 @@ Setting this up requires a few careful steps: - In the Invoicing settings, set the Customer Wallet Account to the previously created account. - (Optional) Create a product (Wallet Product), and enable the Wallet Product - toggle. Set the income and expense account to the previously created account. + toggle. Set the income and expense account will be automatically the Customer + wallet account previously created. diff --git a/account_customer_wallet/static/description/index.html b/account_customer_wallet/static/description/index.html index c171c730f..f6767a214 100644 --- a/account_customer_wallet/static/description/index.html +++ b/account_customer_wallet/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -366,14 +367,14 @@

Account Customer Wallet

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:0f9255d796d4f4aa7441b7b0351bf245ac3ab6ecc12f17eb5a3fd56aac0cfbb3 +!! source digest: sha256:b7bc4c73dc40a6013af0515a28f25abe38fba21bf357c12a98693a84750f4059 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 coopiteasy/addons

Allow customers to pay using a wallet which is tracked by the company.

Table of contents

-
-

Usage

+
+

Configuration

Setting this up requires a few careful steps:

diff --git a/account_customer_wallet/tests/common.py b/account_customer_wallet/tests/common.py index fd6e6a064..71cb1d83a 100644 --- a/account_customer_wallet/tests/common.py +++ b/account_customer_wallet/tests/common.py @@ -43,6 +43,7 @@ def setUpClass(cls, *args, **kwargs): [("type", "=", "sale")], limit=1 ) cls.payment_method = cls.env.ref("account.account_payment_method_manual_in") + cls.wallet_product = cls.env.ref("account_customer_wallet.product_wallet_demo") cls.cash_account = cls.env["account.account"].search( [("account_type", "=", "asset_cash")], limit=1 ) diff --git a/account_customer_wallet/views/account_payment_views.xml b/account_customer_wallet/views/account_payment_views.xml index 979de9858..791d6356f 100644 --- a/account_customer_wallet/views/account_payment_views.xml +++ b/account_customer_wallet/views/account_payment_views.xml @@ -7,6 +7,15 @@ account.payment + + + + + + + account.payment.register + + + + + + + + + + + + diff --git a/pos_customer_wallet/README.rst b/pos_customer_wallet/README.rst new file mode 100644 index 000000000..d585f1eb3 --- /dev/null +++ b/pos_customer_wallet/README.rst @@ -0,0 +1,81 @@ +============================= +Point of Sale Customer Wallet +============================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:8a76da9293cd9fdde70423bcf873f2325a232d6fb5629ee8fc39dc46f570bc4b + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Faddons-lightgray.png?logo=github + :target: https://github.com/coopiteasy/addons/tree/16.0/pos_customer_wallet + :alt: coopiteasy/addons + +|badge1| |badge2| |badge3| + + +**Table of contents** + +.. contents:: + :local: + +Changelog +========= + +12.0.1.1.0 (2022-07-08) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Features** + +- Customer wallet balance is now displayed on tickets. (`#237 `_) + +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 +* GRAP + +Contributors +~~~~~~~~~~~~ + +* `Coop IT Easy SC `_: + + * Carmen Bianca Bakker + * Rémy Taymans + +Maintainers +~~~~~~~~~~~ + +.. |maintainer-carmenbianca| image:: https://github.com/carmenbianca.png?size=40px + :target: https://github.com/carmenbianca + :alt: carmenbianca + +Current maintainer: + +|maintainer-carmenbianca| + +This module is part of the `coopiteasy/addons `_ project on GitHub. + +You are welcome to contribute. diff --git a/pos_customer_wallet/__init__.py b/pos_customer_wallet/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/pos_customer_wallet/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_customer_wallet/__manifest__.py b/pos_customer_wallet/__manifest__.py new file mode 100644 index 000000000..24c990854 --- /dev/null +++ b/pos_customer_wallet/__manifest__.py @@ -0,0 +1,33 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Point of Sale Customer Wallet", + "summary": """ + Enable usage of the Customer Wallet in the Point of Sale.""", + "version": "16.0.1.0.0", + "category": "Point of Sale", + "website": "https://github.com/coopiteasy/addons", + "author": "Coop IT Easy SC,GRAP", + "maintainers": ["carmenbianca"], + "license": "AGPL-3", + "application": False, + "depends": [ + "point_of_sale", + "account_customer_wallet", + # "base_suspend_security", + ], + "excludes": [], + "assets": { + "point_of_sale.assets": [ + "pos_customer_wallet/static/src/css/pos.css", + "pos_customer_wallet/static/src/js/**/*.js", + "pos_customer_wallet/static/src/xml/**/*.xml", + ], + }, + "data": [], + "demo": [ + "demo/pos_payment_method_demo.xml", + "demo/product_product_demo.xml", + ], +} diff --git a/pos_customer_wallet/demo/pos_payment_method_demo.xml b/pos_customer_wallet/demo/pos_payment_method_demo.xml new file mode 100644 index 000000000..ec22ced57 --- /dev/null +++ b/pos_customer_wallet/demo/pos_payment_method_demo.xml @@ -0,0 +1,16 @@ + + + + + + Customer Wallet + + + + diff --git a/pos_customer_wallet/demo/product_product_demo.xml b/pos_customer_wallet/demo/product_product_demo.xml new file mode 100644 index 000000000..02e7bf5b9 --- /dev/null +++ b/pos_customer_wallet/demo/product_product_demo.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/pos_customer_wallet/i18n/fr.po b/pos_customer_wallet/i18n/fr.po new file mode 100644 index 000000000..8a8c8ca7d --- /dev/null +++ b/pos_customer_wallet/i18n/fr.po @@ -0,0 +1,113 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_customer_wallet +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-02-28 23:07+0000\n" +"PO-Revision-Date: 2023-02-28 23:07+0000\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: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:61 +#, python-format +msgid "' without selecting a customer. Please select a customer or remove the order line(s)." +msgstr "' sans sélectionner un client. Veuillez sélectionner un client ou retirer la ou les lignes de ventes." + +#. module: pos_customer_wallet +#: model_terms:ir.ui.view,arch_db:pos_customer_wallet.view_pos_config_form +msgid "Minimum Wallet Amount" +msgstr "Montant minimum du compte client" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:59 +#, python-format +msgid "Cannot sell the product '" +msgstr "Vous ne pouvez pas vendre le produit '" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:44 +#, python-format +msgid "Cannot use customer wallet payment method without selecting a customer.\n" +"\n" +" Please select a customer or use a different payment method." +msgstr "Vous ne pouvez pas utiliser un moyen de paiement de type compte client sans sélectionner un client.\n" +"\n" +" Veuillez sélectionner un client ou changer de moyen de paiement." + +#. module: pos_customer_wallet +#: model:ir.model,name:pos_customer_wallet.model_res_partner +msgid "Contact" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:123 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:10 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:71 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:85 +#, python-format +msgid "Customer Wallet Balance" +msgstr "Solde de compte client" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:43 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:57 +#, python-format +msgid "Customer Wallet Balance:" +msgstr "Solde de compte client :" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:73 +#, python-format +msgid "Customer wallet balance not sufficient" +msgstr "Le solde du compte client n'est pas suffisant" + +#. module: pos_customer_wallet +#: model:ir.model.fields,field_description:pos_customer_wallet.field_pos_config__is_enabled_customer_wallet +msgid "Is Customer Wallet Enabled" +msgstr "Compte client activé" + +#. module: pos_customer_wallet +#: model:ir.model.fields,field_description:pos_customer_wallet.field_pos_config__minimum_wallet_amount +msgid "Minimum Wallet Amount" +msgstr "Montant minimum du compte client" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:43 +#: code:addons/pos_customer_wallet/static/src/js/screens.js:57 +#, python-format +msgid "No customer selected" +msgstr "Aucun client sélectionné" + +#. module: pos_customer_wallet +#: model:ir.model,name:pos_customer_wallet.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Paramétrage du point de vente" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:74 +#, python-format +msgid "There is not enough balance in the customer's wallet to perform this order." +msgstr "Le solde du compte client est insuffisant pour réaliser cette vente." + +#. module: pos_customer_wallet +#: model:ir.model.fields,help:pos_customer_wallet.field_pos_config__minimum_wallet_amount +#: model_terms:ir.ui.view,arch_db:pos_customer_wallet.view_pos_config_form +msgid "Usually 0. You can enter a negative value, if you want to accept that the customer wallet is negative. Maybe useful if the sale amount is slightly higher than the wallet amount, to avoid charging the customer a small amount." +msgstr "Habituellement 0. vous pouvez entrer une valeur négative, si vous souhaitez accepter que le customer wallet soit négatif. Peut-être utile si le montant de la vente, est légèrement supérieur au montant de la cagnotte, pour éviter de faire payer" + diff --git a/pos_customer_wallet/i18n/pos_customer_wallet.pot b/pos_customer_wallet/i18n/pos_customer_wallet.pot new file mode 100644 index 000000000..37211db76 --- /dev/null +++ b/pos_customer_wallet/i18n/pos_customer_wallet.pot @@ -0,0 +1,98 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_customer_wallet +# +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: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:65 +#, python-format +msgid "' without selecting a customer. Please select a customer or remove the order line(s)." +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:63 +#, python-format +msgid "Cannot sell the product '" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:48 +#, python-format +msgid "Cannot use customer wallet payment method without selecting a customer.\n" +"\n" +" Please select a customer or use a different payment method." +msgstr "" + +#. module: pos_customer_wallet +#: model:ir.model,name:pos_customer_wallet.model_res_partner +msgid "Contact" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:135 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:10 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:71 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:85 +#, python-format +msgid "Customer Wallet Balance" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:43 +#: code:addons/pos_customer_wallet/static/src/xml/pos.xml:57 +#, python-format +msgid "Customer Wallet Balance:" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:74 +#, python-format +msgid "Customer wallet balance not sufficient" +msgstr "" + +#. module: pos_customer_wallet +#: model:ir.model.fields,field_description:pos_customer_wallet.field_pos_config__is_enabled_customer_wallet +msgid "Is Customer Wallet Enabled" +msgstr "" + +#. module: pos_customer_wallet +#: model:ir.model.fields,field_description:pos_customer_wallet.field_pos_config__minimum_wallet_amount +msgid "Minimum Wallet Amount" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:47 +#: code:addons/pos_customer_wallet/static/src/js/screens.js:61 +#, python-format +msgid "No customer selected" +msgstr "" + +#. module: pos_customer_wallet +#: model:ir.model,name:pos_customer_wallet.model_pos_config +msgid "Point of Sale Configuration" +msgstr "" + +#. module: pos_customer_wallet +#. openerp-web +#: code:addons/pos_customer_wallet/static/src/js/screens.js:75 +#, python-format +msgid "There is not enough balance in the customer's wallet to perform this order." +msgstr "" + diff --git a/pos_customer_wallet/models/__init__.py b/pos_customer_wallet/models/__init__.py new file mode 100644 index 000000000..4a3b8c3b4 --- /dev/null +++ b/pos_customer_wallet/models/__init__.py @@ -0,0 +1,4 @@ +from . import pos_config +from . import pos_payment_method +from . import pos_session +from . import res_partner diff --git a/pos_customer_wallet/models/pos_config.py b/pos_customer_wallet/models/pos_config.py new file mode 100644 index 000000000..2f6565ac0 --- /dev/null +++ b/pos_customer_wallet/models/pos_config.py @@ -0,0 +1,33 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class PosConfig(models.Model): + _inherit = "pos.config" + + is_enabled_customer_wallet = fields.Boolean( + related="company_id.is_enabled_customer_wallet", + string="Is Customer Wallet Enabled", + ) + + minimum_wallet_amount = fields.Monetary( + compute="_compute_minimum_wallet_amount", + ) + + @api.depends( + "payment_method_ids.journal_id.is_customer_wallet_journal", + "payment_method_ids.journal_id.minimum_wallet_amount", + ) + def _compute_minimum_wallet_amount(self): + for config in self: + wallet_method = config.payment_method_ids.filtered( + lambda method: method.journal_id.is_customer_wallet_journal + ) + if wallet_method: + config.minimum_wallet_amount = min( + wallet_method.mapped("journal_id.minimum_wallet_amount") + ) + else: + config.minimum_wallet_amount = False diff --git a/pos_customer_wallet/models/pos_payment_method.py b/pos_customer_wallet/models/pos_payment_method.py new file mode 100644 index 000000000..6031b291d --- /dev/null +++ b/pos_customer_wallet/models/pos_payment_method.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2023 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class PosPaymentMethod(models.Model): + _inherit = "pos.payment.method" + + is_customer_wallet_method = fields.Boolean( + related="journal_id.is_customer_wallet_journal", + store=True, + ) diff --git a/pos_customer_wallet/models/pos_session.py b/pos_customer_wallet/models/pos_session.py new file mode 100644 index 000000000..b84c76155 --- /dev/null +++ b/pos_customer_wallet/models/pos_session.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2023 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import models + + +class PosSession(models.Model): + _inherit = "pos.session" + + def _loader_params_pos_payment_method(self): + result = super()._loader_params_pos_payment_method() + result["search_params"]["fields"].append("is_customer_wallet_method") + return result + + def _loader_params_product_product(self): + result = super()._loader_params_product_product() + result["search_params"]["fields"].append("is_customer_wallet_product") + return result + + def _loader_params_res_partner(self): + result = super()._loader_params_res_partner() + result["search_params"]["fields"].append("customer_wallet_balance") + return result diff --git a/pos_customer_wallet/models/res_partner.py b/pos_customer_wallet/models/res_partner.py new file mode 100644 index 000000000..1ce465dcf --- /dev/null +++ b/pos_customer_wallet/models/res_partner.py @@ -0,0 +1,58 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from collections import defaultdict + +from odoo import api, models + + +class Partner(models.Model): + _inherit = "res.partner" + + @api.model + def get_wallet_balance_pos_payment(self, all_partner_ids): + pre_result = defaultdict(float) + for line in ( + self.env["pos.payment"] + .sudo() + .search( + [ + ("partner_id", "in", list(all_partner_ids)), + ("session_id.state", "=", "opened"), + ("pos_order_id.state", "=", "paid"), + ("payment_method_id.is_customer_wallet_method", "=", True), + ] + ) + ): + pre_result[line.partner_id.id] += line.amount + return [ + {"partner_id": partner_id, "total": total} + for partner_id, total in pre_result.items() + ] + + @api.model + def get_wallet_balance_pos_order_line(self, all_partner_ids): + pre_result = defaultdict(float) + for line in ( + self.env["pos.order.line"] + # FIXME: This used to be suspend_security(). Verify that this still + # works. + .sudo().search( + [ + ("order_id.state", "=", "paid"), + ("order_id.partner_id", "in", list(all_partner_ids)), + ("product_id.is_customer_wallet_product", "=", True), + ] + ) + ): + pre_result[line.order_id.partner_id.id] -= line.price_subtotal + return [ + {"partner_id": partner_id, "total": total} + for partner_id, total in pre_result.items() + ] + + def get_wallet_balance_all(self, all_partner_ids, all_account_ids): + res = super().get_wallet_balance_all(all_partner_ids, all_account_ids) + res.append(self.get_wallet_balance_pos_order_line(all_partner_ids)) + res.append(self.get_wallet_balance_pos_payment(all_partner_ids)) + return res diff --git a/pos_customer_wallet/readme/CONTRIBUTORS.rst b/pos_customer_wallet/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..14bc43514 --- /dev/null +++ b/pos_customer_wallet/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Coop IT Easy SC `_: + + * Carmen Bianca Bakker + * Rémy Taymans diff --git a/pos_customer_wallet/readme/DESCRIPTION.rst b/pos_customer_wallet/readme/DESCRIPTION.rst new file mode 100644 index 000000000..e69de29bb diff --git a/pos_customer_wallet/readme/HISTORY.rst b/pos_customer_wallet/readme/HISTORY.rst new file mode 100644 index 000000000..83e30a215 --- /dev/null +++ b/pos_customer_wallet/readme/HISTORY.rst @@ -0,0 +1,6 @@ +12.0.1.1.0 (2022-07-08) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Features** + +- Customer wallet balance is now displayed on tickets. (`#237 `_) diff --git a/pos_customer_wallet/static/description/index.html b/pos_customer_wallet/static/description/index.html new file mode 100644 index 000000000..e8cf5b5bc --- /dev/null +++ b/pos_customer_wallet/static/description/index.html @@ -0,0 +1,436 @@ + + + + + +Point of Sale Customer Wallet + + + +
+

Point of Sale Customer Wallet

+ + +

Beta License: AGPL-3 coopiteasy/addons

+

Table of contents

+ +
+

Changelog

+
+

12.0.1.1.0 (2022-07-08)

+

Features

+
    +
  • Customer wallet balance is now displayed on tickets. (#237)
  • +
+
+
+
+

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

Contributors

+ +
+
+

Maintainers

+

Current maintainer:

+

carmenbianca

+

This module is part of the coopiteasy/addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/pos_customer_wallet/static/src/css/pos.css b/pos_customer_wallet/static/src/css/pos.css new file mode 100644 index 000000000..c314ce370 --- /dev/null +++ b/pos_customer_wallet/static/src/css/pos.css @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2022 Coop IT Easy SC + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +.wallet-balance-container { + display: inline-block; + width: 50%; + box-sizing: border-box; + padding: 1rem; + padding-top: 1rem; + padding-left: 1rem; + float: right; +} + +.wallet-balance { + text-align: center; + font-size: 28px; + color: #43996e; + text-shadow: 0px 2px white, 0px 2px 2px rgba(0, 0, 0, 0.27); +} +.new-wallet-balance { + font-size: 18px; + color: #aaa; +} + +.payment-name { + display: flex; + align-items: center; +} diff --git a/pos_customer_wallet/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/pos_customer_wallet/static/src/js/Screens/PaymentScreen/PaymentScreen.js new file mode 100644 index 000000000..f533c7a99 --- /dev/null +++ b/pos_customer_wallet/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2022 Coop IT Easy SC +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +odoo.define("pos_customer_wallet.PaymentScreen", function (require) { + "use strict"; + const PaymentScreen = require("point_of_sale.PaymentScreen"); + + const Registries = require("point_of_sale.Registries"); + + const WalletPaymentScreen = (PaymentScreen_) => + class extends PaymentScreen_ { + /* eslint-disable no-unused-vars */ + /** + * Overload function. + * + * - If wallet journal is selected, check if customer is selected. + * - if wallet journal is selected, check if wallet amount is sufficient. + * + * @param {Boolean} isForceValidate - Passed to super. + * @returns {Boolean} Whether the order is valid. + */ + async validateOrder(isForceValidate) { + /* eslint-enable no-unused-vars */ + var partner = this.currentOrder.get_partner(); + var [payment_wallet_amount, payment_lines_qty] = + this.get_amount_debit_with_customer_wallet_journal(); + var [product_wallet_amount, product_lines_qty] = + this.get_amount_credit_with_customer_wallet_product(); + + var wallet_amount = payment_wallet_amount - product_wallet_amount; + + if (!partner) { + if (payment_lines_qty > 0) { + this.showPopup("ErrorPopup", { + title: this.env._t("No customer selected"), + body: this.env._t( + "Cannot use customer wallet payment method without selecting a customer.\n\n Please select a customer or use a different payment method." + ), + }); + return; + } + if (product_lines_qty > 0) { + var wallet_product_names = []; + var wallet_products = this.find_customer_wallet_products(); + wallet_products.forEach(function (product) { + wallet_product_names.push(product.display_name); + }); + this.showPopup("ErrorPopup", { + title: this.env._t("No customer selected"), + body: + this.env._t("Cannot sell the product '") + + wallet_product_names.join(",") + + this.env._t( + "' without selecting a customer. Please select a customer or remove the order line(s)." + ), + }); + return; + } + } else if (this.is_balance_above_minimum(partner, wallet_amount)) { + this.showPopup("ErrorPopup", { + title: this.env._t("Customer wallet balance not sufficient"), + body: this.env._t( + "There is not enough balance in the customer's wallet to perform this order." + ), + }); + return; + } + + await super.validateOrder(...arguments); + } + + /** + * Overload function. + * + * Once the order is validated, update the wallet amount + * of the current customer, if defined. + */ + async _finalizeValidation() { + var partner = this.currentOrder.get_partner(); + if (partner) { + var payment_wallet_amount = + this.get_amount_debit_with_customer_wallet_journal()[0]; + var product_wallet_amount = + this.get_amount_credit_with_customer_wallet_product()[0]; + var wallet_amount = payment_wallet_amount - product_wallet_amount; + partner.customer_wallet_balance -= wallet_amount; + } + + await super._finalizeValidation(); + } + + is_balance_above_minimum(client, wallet_amount) { + return ( + client.customer_wallet_balance - wallet_amount <= + this.env.pos.config.minimum_wallet_amount - 0.00001 + ); + } + + /** + * Calculate the balance of the customer wallet after completing this + * order. + * + * @returns {Number} New balance. + */ + get new_wallet_amount() { + var partner = this.currentOrder.get_partner(); + if (partner) { + var payment_wallet_amount = + this.get_amount_debit_with_customer_wallet_journal()[0]; + var product_wallet_amount = + this.get_amount_credit_with_customer_wallet_product()[0]; + return ( + partner.customer_wallet_balance - + payment_wallet_amount + + product_wallet_amount + ); + } + return false; + } + + /** + * Return the payment method of the wallet journal, if exists. + * + * @returns A payment method which has a customer + * wallet journal. The first match is returned. + */ + find_customer_wallet_payment_method() { + // This is fairly naive. + for (var i = 0; i < this.payment_methods_from_config.length; i++) { + if (this.payment_methods_from_config[i].is_customer_wallet_method) { + return this.payment_methods_from_config[i]; + } + } + return null; + } + + /** + * Return the wallet products, if exist. + * + * @returns {list} A list of products which are marked as wallet + * products. + */ + find_customer_wallet_products() { + var wallet_products = []; + for (const value of Object.values(this.env.pos.db.product_by_id)) { + if (value.is_customer_wallet_product) { + wallet_products.push(value); + } + } + return wallet_products; + } + + /** + * Return the payment amount with wallet payment method. + * + * @returns {list} A list of two elements. The first element is the + * balance of payment done with wallet payment method. The second + * element is the number of payment lines. + */ + get_amount_debit_with_customer_wallet_journal() { + var order = this.currentOrder; + var method = this.find_customer_wallet_payment_method(); + var wallet_amount = 0; + var lines_qty = 0; + order.paymentlines.forEach((item) => { + if (item.payment_method === method) { + wallet_amount += item.amount; + lines_qty += 1; + } + }); + return [wallet_amount, lines_qty]; + } + + /** + * Return the amount credited by wallet products. + * + * @returns {list} A list of two elements. The first element is the + * balance of order lines done with wallet product. The second element + * is the number of order lines. + */ + get_amount_credit_with_customer_wallet_product() { + var order = this.currentOrder; + var wallet_product_ids = []; + var wallet_products = this.find_customer_wallet_products(); + wallet_products.forEach(function (product) { + wallet_product_ids.push(product.id); + }); + var wallet_amount = 0; + var lines_qty = 0; + + order.orderlines.forEach((orderline) => { + if (wallet_product_ids.includes(orderline.product.id)) { + wallet_amount += orderline.get_price_without_tax(); + lines_qty += 1; + } + }); + + return [wallet_amount, lines_qty]; + } + }; + + Registries.Component.extend(PaymentScreen, WalletPaymentScreen); + + return WalletPaymentScreen; +}); diff --git a/pos_customer_wallet/static/src/js/models.js b/pos_customer_wallet/static/src/js/models.js new file mode 100644 index 000000000..e968dd8b1 --- /dev/null +++ b/pos_customer_wallet/static/src/js/models.js @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2022 Coop IT Easy SC +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +odoo.define("pos_customer_wallet.models", function (require) { + "use strict"; + + const {Order} = require("point_of_sale.models"); + const Registries = require("point_of_sale.Registries"); + + const WalletOrder = (Order_) => + class extends Order_ { + export_for_printing() { + var json = super.export_for_printing(...arguments); + json.customer_wallet_balance = this.partner + ? this.partner.customer_wallet_balance + : 0; + return json; + } + }; + + Registries.Model.extend(Order, WalletOrder); +}); diff --git a/pos_customer_wallet/static/src/xml/Screens/PartnerListScreen/PartnerEditDetails.xml b/pos_customer_wallet/static/src/xml/Screens/PartnerListScreen/PartnerEditDetails.xml new file mode 100644 index 000000000..f5845dc5e --- /dev/null +++ b/pos_customer_wallet/static/src/xml/Screens/PartnerListScreen/PartnerEditDetails.xml @@ -0,0 +1,26 @@ + + + + + + +
+ Customer Wallet + +
+
+
+ +
diff --git a/pos_customer_wallet/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml b/pos_customer_wallet/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml new file mode 100644 index 000000000..491260f97 --- /dev/null +++ b/pos_customer_wallet/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml @@ -0,0 +1,36 @@ + + + + + + + +
+
+ + + + + + () + + +
+
+
+
+
+
diff --git a/pos_customer_wallet/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml b/pos_customer_wallet/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml new file mode 100644 index 000000000..59ca28360 --- /dev/null +++ b/pos_customer_wallet/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml @@ -0,0 +1,26 @@ + + + + + + +
+ Customer Wallet Balance + +
+
+
+ +
diff --git a/pos_customer_wallet/tests/__init__.py b/pos_customer_wallet/tests/__init__.py new file mode 100644 index 000000000..450e28a21 --- /dev/null +++ b/pos_customer_wallet/tests/__init__.py @@ -0,0 +1,2 @@ +from . import common +from . import test_balance diff --git a/pos_customer_wallet/tests/common.py b/pos_customer_wallet/tests/common.py new file mode 100644 index 000000000..811625612 --- /dev/null +++ b/pos_customer_wallet/tests/common.py @@ -0,0 +1,128 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from random import randint + +from odoo import fields + +from odoo.addons.account_customer_wallet.tests.common import TestBalance + + +class TestPosBalance(TestBalance): + @classmethod + def setUpClass(cls, *args, **kwargs): + super().setUpClass(*args, **kwargs) + + cls.customer_wallet_payment_method = cls.env.ref( + "pos_customer_wallet.customer_wallet_payment_method" + ) + + def _create_random_uid(self): + return "%05d-%03d-%04d" % (randint(1, 99999), randint(1, 999), randint(1, 9999)) + + # TODO: maybe also pass a pos_config or pos_session as parameter. not + # necessary yet + def _create_pos_order(self, product, payment_method, amount, partner): + pricelist = self.env["product.pricelist"].create( + { + "name": "Test pricelist", + "currency_id": self.env.company.currency_id.id, + "item_ids": [ + ( + 0, + 0, + { + "applied_on": "3_global", + "compute_price": "formula", + "base": "list_price", + }, + ) + ], + } + ) + uid = self._create_random_uid() + + # Create a new pos config and open it + pos_config = self.env.ref("point_of_sale.pos_config_main").copy( + { + "available_pricelist_ids": [(6, 0, pricelist.ids)], + "pricelist_id": pricelist.id, + } + ) + pos_config.payment_method_ids += self.customer_wallet_payment_method + pos_config.open_ui() + pos_session = pos_config.current_session_id + pos_session.action_pos_session_open() + # Bypass cash control + pos_session.state = "opened" + + order_data = { + "data": { + "name": "Order %s" % uid, + "amount_paid": amount, + "amount_total": amount, + "amount_tax": 0, + "amount_return": 0, + "lines": [ + [ + 0, + 0, + { + "qty": 1, + "price_unit": amount, + "price_subtotal": amount, + "price_subtotal_incl": amount, + "discount": 0, + "product_id": product.id, + "tax_ids": [[6, 0, []]], + # The randint seems rather strange to me, but I + # nicked this idea from tests/common.py in the pos + # module. + "id": randint(1000, 1000000), + "pack_lot_ids": [], + }, + ] + ], + "statement_ids": [ + [ + 0, + 0, + { + "name": fields.Datetime.to_string(fields.Datetime.now()), + "payment_method_id": payment_method.id, + "amount": amount, + }, + ] + ], + "pos_session_id": pos_session.id, + "pricelist_id": pricelist.id, + "partner_id": partner.id, + "user_id": self.env.user.id, + "uid": uid, + "sequence_number": 1, + "creation_date": fields.Datetime.to_string(fields.Datetime.now()), + "fiscal_position_id": False, + "to_invoice": False, + }, + "uid": uid, + "to_invoice": False, + } + + return self.env["pos.order"].create_from_ui([order_data]) + + def _create_wallet_pos_payment(self, amount=0, partner=None): + if partner is None: + partner = self.partner + self._create_pos_order( + self.env["product.product"].create( + { + "name": "Foo Product", + "available_in_pos": True, + "list_price": amount, + "taxes_id": False, + } + ), + self.customer_wallet_payment_method, + amount, + partner, + ) diff --git a/pos_customer_wallet/tests/test_balance.py b/pos_customer_wallet/tests/test_balance.py new file mode 100644 index 000000000..a0034b60c --- /dev/null +++ b/pos_customer_wallet/tests/test_balance.py @@ -0,0 +1,38 @@ +# Copyright 2022 Coop IT Easy SC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from .common import TestPosBalance as TestBalance + + +class TestPosBalance(TestBalance): + def test_with_pos_payment(self): + """Pos payments in open POS sessions affect balance.""" + self._create_move(credit=100) + self._create_wallet_pos_payment(amount=40) + + self.assertEqual(self.partner.customer_wallet_balance, 60) + + def test_with_pos_payment_different_partner(self): + """Payments for other partners do not affect the balances of all + clients. + """ + other_partner = self.env.ref("base.res_partner_address_31") + self._create_wallet_pos_payment(amount=100, partner=other_partner) + + self.assertEqual(self.partner.customer_wallet_balance, 0) + self.assertEqual(other_partner.customer_wallet_balance, -100) + + def test_credit_by_product(self): + # Intialize wallet with 500 + self._create_move(credit=500) + + self._create_pos_order( + self.wallet_product, + self.env["pos.payment.method"].search( + [("is_cash_count", "=", True)], limit=1 + ), + 1000, + self.partner, + ) + self.assertEqual(self.partner.customer_wallet_balance, 1500) diff --git a/setup/pos_customer_wallet/odoo/addons/pos_customer_wallet b/setup/pos_customer_wallet/odoo/addons/pos_customer_wallet new file mode 120000 index 000000000..c33bd2c59 --- /dev/null +++ b/setup/pos_customer_wallet/odoo/addons/pos_customer_wallet @@ -0,0 +1 @@ +../../../../pos_customer_wallet \ No newline at end of file diff --git a/setup/pos_customer_wallet/setup.py b/setup/pos_customer_wallet/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/pos_customer_wallet/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)