From 0ca8a4376ae63128db79cab150a98601b328bc4f Mon Sep 17 00:00:00 2001 From: David Wulliamoz Date: Fri, 11 Oct 2024 10:10:01 +0200 Subject: [PATCH 1/2] migrate reconcile 1015 wizard --- account_reconcile_checkout/README.rst | 75 +++++++ account_reconcile_checkout/__init__.py | 11 + account_reconcile_checkout/__manifest__.py | 50 +++++ .../readme/CONFIGURE.rst | 3 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 6 + account_reconcile_checkout/readme/USAGE.rst | 3 + .../security/ir.model.access.csv | 2 + .../reconcile_outstanding_wizard_view.xml | 29 +++ .../wizards/__init__.py | 10 + .../wizards/reconcile_outstanding_wizard.py | 206 ++++++++++++++++++ 11 files changed, 396 insertions(+) create mode 100755 account_reconcile_checkout/README.rst create mode 100644 account_reconcile_checkout/__init__.py create mode 100644 account_reconcile_checkout/__manifest__.py create mode 100644 account_reconcile_checkout/readme/CONFIGURE.rst create mode 100644 account_reconcile_checkout/readme/CONTRIBUTORS.rst create mode 100644 account_reconcile_checkout/readme/DESCRIPTION.rst create mode 100644 account_reconcile_checkout/readme/USAGE.rst create mode 100644 account_reconcile_checkout/security/ir.model.access.csv create mode 100644 account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml create mode 100644 account_reconcile_checkout/wizards/__init__.py create mode 100644 account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py diff --git a/account_reconcile_checkout/README.rst b/account_reconcile_checkout/README.rst new file mode 100755 index 000000000..f563ce6a2 --- /dev/null +++ b/account_reconcile_checkout/README.rst @@ -0,0 +1,75 @@ +========================================== +Reconcile tools for Compassion CH +========================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-CompassionCH%2Fcompassion--switzerland-lightgray.png?logo=github + :target: https://github.com/CompassionCH/compassion-switzerland/tree/11.0/account_reconcile_compassion + :alt: CompassionCH/compassion-switzerland + +|badge1| |badge2| |badge3| + + +It finds a matching invoice for the move_line and reconciles only if the amount of the payment corresponds or if it is a multiple of the invoice amount. If many invoices are found, the first reconciled invoice is the current invoice (last invoice that is not in future). Then it reconciles the other invoices from last invoice to first. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + + +Usage +===== + + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Compassion CH + +Contributors +~~~~~~~~~~~~ + +* Emanuel Cino + +Maintainers +~~~~~~~~~~~ + +This module is maintained by Compassion Switzerland. + +.. image:: https://upload.wikimedia.org/wikipedia/en/8/83/CompassionInternationalLogo.png + :alt: Compassion Switzerland + :target: https://www.compassion.ch + +Compassion Switzerland is a nonprofit organization whose +mission is to release children from extreme poverty in Jesus name. + +This module is part of the `CompassionCH/compassion-switzerland `_ project on GitHub. + +You are welcome to contribute. diff --git a/account_reconcile_checkout/__init__.py b/account_reconcile_checkout/__init__.py new file mode 100644 index 000000000..838ba1c54 --- /dev/null +++ b/account_reconcile_checkout/__init__.py @@ -0,0 +1,11 @@ +############################################################################## +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## + +from . import wizards diff --git a/account_reconcile_checkout/__manifest__.py b/account_reconcile_checkout/__manifest__.py new file mode 100644 index 000000000..d7feca100 --- /dev/null +++ b/account_reconcile_checkout/__manifest__.py @@ -0,0 +1,50 @@ +############################################################################## +# +# ______ Releasing children from poverty _ +# / ____/___ ____ ___ ____ ____ ___________(_)___ ____ +# / / / __ \/ __ `__ \/ __ \/ __ `/ ___/ ___/ / __ \/ __ \ +# / /___/ /_/ / / / / / / /_/ / /_/ (__ |__ ) / /_/ / / / / +# \____/\____/_/ /_/ /_/ .___/\__,_/____/____/_/\____/_/ /_/ +# /_/ +# in Jesus' name +# +# Copyright (C) 2014-2017 Compassion CH (http://www.compassion.ch) +# @author: Emanuel Cino +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +# pylint: disable=C8101 +{ + "name": "Reconcile tools for Compassion CH", + "version": "14.0.1.0.0", + "author": "Compassion CH", + "license": "AGPL-3", + "category": "Finance", + "website": "http://www.compassion.ch", + "depends": [ + "account", + "payment_postfinance_flex" # paid-addons + ], + "external_dependencies": {"python": [ + "postfinancecheckout" + ]}, + "data": [ + "security/ir.model.access.csv", + "views/reconcile_outstanding_wizard_view.xml", + ], + "auto_install": False, + "installable": True, +} diff --git a/account_reconcile_checkout/readme/CONFIGURE.rst b/account_reconcile_checkout/readme/CONFIGURE.rst new file mode 100644 index 000000000..604b1a2d8 --- /dev/null +++ b/account_reconcile_checkout/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +You can add the following system parameter to enable an analytic account to be set on exchange rate move lines: + +* account_reconcile_compassion.currency_exchange_analytic_account diff --git a/account_reconcile_checkout/readme/CONTRIBUTORS.rst b/account_reconcile_checkout/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..6a9ca3183 --- /dev/null +++ b/account_reconcile_checkout/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Emanuel Cino diff --git a/account_reconcile_checkout/readme/DESCRIPTION.rst b/account_reconcile_checkout/readme/DESCRIPTION.rst new file mode 100644 index 000000000..7d8927bb0 --- /dev/null +++ b/account_reconcile_checkout/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +Reconcile rules with bvr_ref of invoice for Compassion CH. + +This will add a Product field in the bank statement reconcile view that will allow to create an invoice from a received payment. When choosing a product, an invoice will be created and will be reconciled with the given payment. + +It finds a matching invoice for the move_line and reconciles only if the amount of the payment corresponds or if it is a multiple of the invoice amount. +If many invoices are found, the first reconciled invoice is the current invoice (last invoice that is not in future). Then it reconciles the other invoices from last invoice to first. diff --git a/account_reconcile_checkout/readme/USAGE.rst b/account_reconcile_checkout/readme/USAGE.rst new file mode 100644 index 000000000..b3e512b2b --- /dev/null +++ b/account_reconcile_checkout/readme/USAGE.rst @@ -0,0 +1,3 @@ +To use this module, you need to: + +* Go to Accounting -> Bank Statement -> Reconcile diff --git a/account_reconcile_checkout/security/ir.model.access.csv b/account_reconcile_checkout/security/ir.model.access.csv new file mode 100644 index 000000000..43ddbc2de --- /dev/null +++ b/account_reconcile_checkout/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_reconcile_outstanding_view,Access reconcile outstanding receipts Wizard,model_reconcile_outstanding_wizard,account.group_account_invoice,1,1,1,1 diff --git a/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml b/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml new file mode 100644 index 000000000..e13d05451 --- /dev/null +++ b/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml @@ -0,0 +1,29 @@ + + + + checkout.reconcile.outstanding.wizard + reconcile.outstanding.wizard + +
+ + + + + +
+
+
+
+
+ + + + Reconcile outstanding receipts + reconcile.outstanding.wizard + form + new + + + +
diff --git a/account_reconcile_checkout/wizards/__init__.py b/account_reconcile_checkout/wizards/__init__.py new file mode 100644 index 000000000..5859791c5 --- /dev/null +++ b/account_reconcile_checkout/wizards/__init__.py @@ -0,0 +1,10 @@ +############################################################################## +# +# Copyright (C) 2014 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## +from . import reconcile_outstanding_wizard diff --git a/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py b/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py new file mode 100644 index 000000000..bcec5d1f2 --- /dev/null +++ b/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py @@ -0,0 +1,206 @@ +############################################################################## +# +# Copyright (C) 2022 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# @author: Emanuel Cino +# +# The licence is in the file __manifest__.py +# +############################################################################## +from datetime import datetime, timedelta +from enum import Enum +from postfinancecheckout import Configuration +from postfinancecheckout.api.transaction_service_api import TransactionServiceApi + +from odoo import api, models, fields, _ +from odoo.tools import ormcache + + +class Provider(Enum): + WORLDLINE = "WORLDLINE SCHWEIZ AG" + PF_CARD = "POSTFINANCE CARD" + TWINT = "TWINT" + E_FINANCE = "POSTFINANCE E-FINANCE" + + +PF_MAPPING = { + Provider.WORLDLINE: "SIX Acquiring", + Provider.PF_CARD: "PostFinance Acquiring - PostFinance Card", + Provider.E_FINANCE: "PostFinance Acquiring - PostFinance E-Finance", + Provider.TWINT: "TWINT - TWINT Connector" +} + + +class ReconcileOutstandingWizard(models.TransientModel): + + _name = "reconcile.outstanding.wizard" + _description = "Wizard reconcile outstanding payments account" + + account_id = fields.Many2one( + "account.account", "Reconcile account", + default=lambda s: s.env["account.account"].search([ + ("code", "=", "44")], limit=1) + ) + full_reconcile_line_ids = fields.Many2many( + "account.move.line", "reconcile_outstanding_reconciled", string="Reconciled lines", + readonly=True + ) + partial_reconcile_line_ids = fields.Many2many( + "account.move.line", "reconcile_outstanding_partial_reconciled", + string="Partial reconciled lines", readonly=True + ) + missing_donation_line_ids = fields.Many2many( + "account.move.line", "reconcile_outstanding_missing_invoice", + string="Leftover donations", + readonly=True + ) + + def reconcile_outstanding(self): + mvl_obj = self.env["account.move.line"] + credit_lines = mvl_obj.search([ + ("account_id", "=", self.account_id.id), + ("full_reconcile_id", "=", False), + ("credit", ">", 0), + ("date", ">=", "2022-02-09") # Date of activation of pf_checkout + ]) + for cl in credit_lines: + self.reconcile_using_pf_checkout(cl) + + # Compute results + if self.partial_reconcile_line_ids: + oldest_credit = min(self.partial_reconcile_line_ids.mapped("date")) + self.missing_donation_line_ids = mvl_obj.search([ + ("account_id", "=", self.account_id.id), + ("full_reconcile_id", "=", False), + ("debit", ">", 0), + ("date", "<=", fields.Date.to_string(oldest_credit)), + ("date", ">=", "2022-02-09") # Date of activation of pf_checkout + ]).filtered(lambda m: not m.matched_credit_ids) + if self.full_reconcile_line_ids: + self.env.user.notify_success( + message=_("Successfully reconciled %s entries") + % len(self.full_reconcile_line_ids), + sticky=True + ) + else: + if not credit_lines: + self.env.user.notify_success( + message=_("Every credit entry is reconciled")) + else: + self.env.user.notify_warning( + message=_("0 credit entry could be fully reconciled")) + return { + "type": "ir.actions.act_window", + "view_mode": "tree,form", + "view_type": "form", + "res_model": "account.move.line", + "target": "current", + "context": self.env.context, + "domain": [("id", "in", (self.partial_reconcile_line_ids + + self.missing_donation_line_ids).ids)] + } + + def reconcile_using_pf_checkout(self, move_line): + # Transactions are grouped by dates + date_position = -1 + date_length = 8 + search_days_delta = 0 + if Provider.WORLDLINE.value in move_line.name: + date_position = self._search_in_credit_string(move_line, "REFERENCES: ") + search_days_delta = -9 + provider = Provider.WORLDLINE + elif Provider.TWINT.value in move_line.name: + date_position = self._search_in_credit_string(move_line, "Payout ") + search_days_delta = -1 + provider = Provider.TWINT + elif Provider.PF_CARD.value in move_line.name: + date_position = self._search_in_credit_string(move_line, " TRAITEMENT DU ") + date_length = 10 + provider = Provider.PF_CARD + elif Provider.E_FINANCE.value in move_line.name: + date_position = self._search_in_credit_string(move_line, " TRAITEMENT DU ") + date_length = 10 + provider = Provider.E_FINANCE + if date_position == -1: + self.missing_donation_line_ids += move_line + return False + date_transactions = datetime.strptime( + move_line.name[date_position:date_position + date_length], + "%Y%m%d" if date_length == 8 else "%d.%m.%Y" + ) + timedelta(days=search_days_delta) + pf_service, space_id = self.get_pf_service() + debit_match = self.env["account.move.line"] + pf_filter = self.get_pf_filter(date_transactions, provider) + for transaction in pf_service.search(space_id, pf_filter): + # Some references have this TEMPTR- prefix that should be ignored + ref = transaction.merchant_reference.replace("TEMPTR-", "").split("-")[0] + debit_match += self.env["account.move.line"].search([ + ("ref", "like", ref), + ("debit", ">", 0), + ("full_reconcile_id", "=", False), + ("account_id", "=", self.account_id.id) + ]).filtered(lambda m: not m.matched_credit_ids) + # Perform a partial or full reconcile + (move_line + debit_match).reconcile() + if sum(debit_match.mapped("debit")) == move_line.credit: + self.full_reconcile_line_ids += move_line + else: + self.partial_reconcile_line_ids += move_line + return True + + @api.model + def _search_in_credit_string(self, move_line, search_string): + string_position = move_line.name.find(search_string) + if string_position > -1: + string_position += len(search_string) + return string_position + + @ormcache() + def get_pf_service(self): + pf_acquirer = self.env.ref( + "payment_postfinance_flex.payment_acquirer_postfinance") + config = Configuration( + user_id=pf_acquirer.postfinance_api_userid, + api_secret=pf_acquirer.postfinance_api_application_key) + return TransactionServiceApi(configuration=config),\ + pf_acquirer.postfinance_api_spaceid + + @api.model + def get_pf_filter(self, date_search=None, provider=None, state="FULFILL"): + if date_search is None: + date_search = datetime.today() + stop = date_search.replace(hour=23, minute=0, second=0, microsecond=0) + start = stop + timedelta(days=-1) + domain = { + "filter": { + "children": [ + { + "fieldName": "createdOn", + "operator": "GREATER_THAN", + "type": "LEAF", + "value": start.isoformat(), + }, + { + "fieldName": "createdOn", + "operator": "LESS_THAN_OR_EQUAL", + "type": "LEAF", + "value": stop.isoformat(), + }, + { + "fieldName": "state", + "operator": "EQUALS", + "type": "LEAF", + "value": state, + } + ], + "type": "AND", + } + } + if provider is not None: + domain["filter"]["children"].append({ + "fieldName": "paymentConnectorConfiguration.name", + "operator": "CONTAINS", + "type": "LEAF", + "value": PF_MAPPING.get(provider), + }) + return domain From b70b77aa55e5d645bb329b499b2c6f75a356a30d Mon Sep 17 00:00:00 2001 From: David Wulliamoz Date: Fri, 11 Oct 2024 10:35:29 +0200 Subject: [PATCH 2/2] fix precommit pre-commit fixes --- account_reconcile_checkout/README.rst | 47 +- account_reconcile_checkout/__manifest__.py | 8 +- .../readme/CONFIGURE.md | 0 .../readme/CONFIGURE.rst | 3 - .../readme/CONTRIBUTORS.md | 1 + .../readme/CONTRIBUTORS.rst | 1 - .../readme/DESCRIPTION.md | 7 + .../readme/DESCRIPTION.rst | 6 - account_reconcile_checkout/readme/USAGE.md | 3 + account_reconcile_checkout/readme/USAGE.rst | 3 - .../static/description/index.html | 432 ++++++++++++++++++ .../reconcile_outstanding_wizard_view.xml | 23 +- .../wizards/reconcile_outstanding_wizard.py | 131 ++++-- requirements.txt | 1 + 14 files changed, 571 insertions(+), 95 deletions(-) mode change 100755 => 100644 account_reconcile_checkout/README.rst create mode 100644 account_reconcile_checkout/readme/CONFIGURE.md delete mode 100644 account_reconcile_checkout/readme/CONFIGURE.rst create mode 100644 account_reconcile_checkout/readme/CONTRIBUTORS.md delete mode 100644 account_reconcile_checkout/readme/CONTRIBUTORS.rst create mode 100644 account_reconcile_checkout/readme/DESCRIPTION.md delete mode 100644 account_reconcile_checkout/readme/DESCRIPTION.rst create mode 100644 account_reconcile_checkout/readme/USAGE.md delete mode 100644 account_reconcile_checkout/readme/USAGE.rst create mode 100644 account_reconcile_checkout/static/description/index.html diff --git a/account_reconcile_checkout/README.rst b/account_reconcile_checkout/README.rst old mode 100755 new mode 100644 index f563ce6a2..5f510066e --- a/account_reconcile_checkout/README.rst +++ b/account_reconcile_checkout/README.rst @@ -1,11 +1,14 @@ -========================================== +================================= Reconcile tools for Compassion CH -========================================== +================================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:0e57ca6729267a49293f58a3dac4b4e90a7138fe7da8249cea5670b84ff4c64a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,13 +17,18 @@ Reconcile tools for Compassion CH :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-CompassionCH%2Fcompassion--switzerland-lightgray.png?logo=github - :target: https://github.com/CompassionCH/compassion-switzerland/tree/11.0/account_reconcile_compassion + :target: https://github.com/CompassionCH/compassion-switzerland/tree/14.0/account_reconcile_checkout :alt: CompassionCH/compassion-switzerland -|badge1| |badge2| |badge3| +|badge1| |badge2| |badge3| +Reconcile rules with bvr_ref of invoice for Compassion CH. -It finds a matching invoice for the move_line and reconciles only if the amount of the payment corresponds or if it is a multiple of the invoice amount. If many invoices are found, the first reconciled invoice is the current invoice (last invoice that is not in future). Then it reconciles the other invoices from last invoice to first. +It finds a matching invoice for the move_line and reconciles only if the +amount of the payment corresponds or if it is a multiple of the invoice +amount. If many invoices are found, the first reconciled invoice is the +current invoice (last invoice that is not in future). Then it reconciles +the other invoices from last invoice to first. **Table of contents** @@ -31,17 +39,21 @@ Configuration ============= + Usage ===== +To use this module, you need to: + +- Go to Accounting -> Action -> Reconcile outstanding 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 smashing it by providing a detailed and welcomed -`feedback `_. +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. @@ -49,27 +61,18 @@ Credits ======= Authors -~~~~~~~ +------- * Compassion CH Contributors -~~~~~~~~~~~~ +------------ -* Emanuel Cino +- Emanuel Cino Maintainers -~~~~~~~~~~~ - -This module is maintained by Compassion Switzerland. - -.. image:: https://upload.wikimedia.org/wikipedia/en/8/83/CompassionInternationalLogo.png - :alt: Compassion Switzerland - :target: https://www.compassion.ch - -Compassion Switzerland is a nonprofit organization whose -mission is to release children from extreme poverty in Jesus name. +----------- -This module is part of the `CompassionCH/compassion-switzerland `_ project on GitHub. +This module is part of the `CompassionCH/compassion-switzerland `_ project on GitHub. You are welcome to contribute. diff --git a/account_reconcile_checkout/__manifest__.py b/account_reconcile_checkout/__manifest__.py index d7feca100..ffd2985f8 100644 --- a/account_reconcile_checkout/__manifest__.py +++ b/account_reconcile_checkout/__manifest__.py @@ -33,14 +33,12 @@ "author": "Compassion CH", "license": "AGPL-3", "category": "Finance", - "website": "http://www.compassion.ch", + "website": "https://github.com/CompassionCH/compassion-switzerland", "depends": [ "account", - "payment_postfinance_flex" # paid-addons + "payment_postfinance_flex", # paid-addons ], - "external_dependencies": {"python": [ - "postfinancecheckout" - ]}, + "external_dependencies": {"python": ["postfinancecheckout"]}, "data": [ "security/ir.model.access.csv", "views/reconcile_outstanding_wizard_view.xml", diff --git a/account_reconcile_checkout/readme/CONFIGURE.md b/account_reconcile_checkout/readme/CONFIGURE.md new file mode 100644 index 000000000..e69de29bb diff --git a/account_reconcile_checkout/readme/CONFIGURE.rst b/account_reconcile_checkout/readme/CONFIGURE.rst deleted file mode 100644 index 604b1a2d8..000000000 --- a/account_reconcile_checkout/readme/CONFIGURE.rst +++ /dev/null @@ -1,3 +0,0 @@ -You can add the following system parameter to enable an analytic account to be set on exchange rate move lines: - -* account_reconcile_compassion.currency_exchange_analytic_account diff --git a/account_reconcile_checkout/readme/CONTRIBUTORS.md b/account_reconcile_checkout/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..651645fe2 --- /dev/null +++ b/account_reconcile_checkout/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Emanuel Cino \<\> diff --git a/account_reconcile_checkout/readme/CONTRIBUTORS.rst b/account_reconcile_checkout/readme/CONTRIBUTORS.rst deleted file mode 100644 index 6a9ca3183..000000000 --- a/account_reconcile_checkout/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1 +0,0 @@ -* Emanuel Cino diff --git a/account_reconcile_checkout/readme/DESCRIPTION.md b/account_reconcile_checkout/readme/DESCRIPTION.md new file mode 100644 index 000000000..36a377e7c --- /dev/null +++ b/account_reconcile_checkout/readme/DESCRIPTION.md @@ -0,0 +1,7 @@ +Reconcile rules with bvr_ref of invoice for Compassion CH. + +It finds a matching invoice for the move_line and reconciles only if the +amount of the payment corresponds or if it is a multiple of the invoice +amount. If many invoices are found, the first reconciled invoice is the +current invoice (last invoice that is not in future). Then it reconciles +the other invoices from last invoice to first. diff --git a/account_reconcile_checkout/readme/DESCRIPTION.rst b/account_reconcile_checkout/readme/DESCRIPTION.rst deleted file mode 100644 index 7d8927bb0..000000000 --- a/account_reconcile_checkout/readme/DESCRIPTION.rst +++ /dev/null @@ -1,6 +0,0 @@ -Reconcile rules with bvr_ref of invoice for Compassion CH. - -This will add a Product field in the bank statement reconcile view that will allow to create an invoice from a received payment. When choosing a product, an invoice will be created and will be reconciled with the given payment. - -It finds a matching invoice for the move_line and reconciles only if the amount of the payment corresponds or if it is a multiple of the invoice amount. -If many invoices are found, the first reconciled invoice is the current invoice (last invoice that is not in future). Then it reconciles the other invoices from last invoice to first. diff --git a/account_reconcile_checkout/readme/USAGE.md b/account_reconcile_checkout/readme/USAGE.md new file mode 100644 index 000000000..af6463b7b --- /dev/null +++ b/account_reconcile_checkout/readme/USAGE.md @@ -0,0 +1,3 @@ +To use this module, you need to: + +- Go to Accounting -\> Action -\> Reconcile outstanding diff --git a/account_reconcile_checkout/readme/USAGE.rst b/account_reconcile_checkout/readme/USAGE.rst deleted file mode 100644 index b3e512b2b..000000000 --- a/account_reconcile_checkout/readme/USAGE.rst +++ /dev/null @@ -1,3 +0,0 @@ -To use this module, you need to: - -* Go to Accounting -> Bank Statement -> Reconcile diff --git a/account_reconcile_checkout/static/description/index.html b/account_reconcile_checkout/static/description/index.html new file mode 100644 index 000000000..9eb220ed1 --- /dev/null +++ b/account_reconcile_checkout/static/description/index.html @@ -0,0 +1,432 @@ + + + + + +Reconcile tools for Compassion CH + + + +
+

Reconcile tools for Compassion CH

+ + +

Beta License: AGPL-3 CompassionCH/compassion-switzerland

+

Reconcile rules with bvr_ref of invoice for Compassion CH.

+

It finds a matching invoice for the move_line and reconciles only if the +amount of the payment corresponds or if it is a multiple of the invoice +amount. If many invoices are found, the first reconciled invoice is the +current invoice (last invoice that is not in future). Then it reconciles +the other invoices from last invoice to first.

+

Table of contents

+ + +
+

Usage

+

To use this module, you need to:

+
    +
  • Go to Accounting -> Action -> Reconcile outstanding
  • +
+
+
+

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

+
    +
  • Compassion CH
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the CompassionCH/compassion-switzerland project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml b/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml index e13d05451..37c0f0330 100644 --- a/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml +++ b/account_reconcile_checkout/views/reconcile_outstanding_wizard_view.xml @@ -1,4 +1,4 @@ - + checkout.reconcile.outstanding.wizard @@ -7,23 +7,36 @@
- +
-
- + Reconcile outstanding receipts reconcile.outstanding.wizard form new - +
diff --git a/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py b/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py index bcec5d1f2..949f30466 100644 --- a/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py +++ b/account_reconcile_checkout/wizards/reconcile_outstanding_wizard.py @@ -1,4 +1,4 @@ -############################################################################## +############################################################################## # # Copyright (C) 2022 Compassion CH (http://www.compassion.ch) # Releasing children from poverty in Jesus' name @@ -9,10 +9,11 @@ ############################################################################## from datetime import datetime, timedelta from enum import Enum + from postfinancecheckout import Configuration from postfinancecheckout.api.transaction_service_api import TransactionServiceApi -from odoo import api, models, fields, _ +from odoo import _, api, fields, models from odoo.tools import ormcache @@ -27,68 +28,80 @@ class Provider(Enum): Provider.WORLDLINE: "SIX Acquiring", Provider.PF_CARD: "PostFinance Acquiring - PostFinance Card", Provider.E_FINANCE: "PostFinance Acquiring - PostFinance E-Finance", - Provider.TWINT: "TWINT - TWINT Connector" + Provider.TWINT: "TWINT - TWINT Connector", } class ReconcileOutstandingWizard(models.TransientModel): - _name = "reconcile.outstanding.wizard" _description = "Wizard reconcile outstanding payments account" account_id = fields.Many2one( - "account.account", "Reconcile account", - default=lambda s: s.env["account.account"].search([ - ("code", "=", "44")], limit=1) + "account.account", + "Reconcile account", + default=lambda s: s.env["account.account"].search( + [("code", "=", "44")], limit=1 + ), ) full_reconcile_line_ids = fields.Many2many( - "account.move.line", "reconcile_outstanding_reconciled", string="Reconciled lines", - readonly=True + "account.move.line", + "reconcile_outstanding_reconciled", + string="Reconciled lines", + readonly=True, ) partial_reconcile_line_ids = fields.Many2many( - "account.move.line", "reconcile_outstanding_partial_reconciled", - string="Partial reconciled lines", readonly=True + "account.move.line", + "reconcile_outstanding_partial_reconciled", + string="Partial reconciled lines", + readonly=True, ) missing_donation_line_ids = fields.Many2many( - "account.move.line", "reconcile_outstanding_missing_invoice", + "account.move.line", + "reconcile_outstanding_missing_invoice", string="Leftover donations", - readonly=True + readonly=True, ) def reconcile_outstanding(self): mvl_obj = self.env["account.move.line"] - credit_lines = mvl_obj.search([ - ("account_id", "=", self.account_id.id), - ("full_reconcile_id", "=", False), - ("credit", ">", 0), - ("date", ">=", "2022-02-09") # Date of activation of pf_checkout - ]) + credit_lines = mvl_obj.search( + [ + ("account_id", "=", self.account_id.id), + ("full_reconcile_id", "=", False), + ("credit", ">", 0), + ("date", ">=", "2022-02-09"), # Date of activation of pf_checkout + ] + ) for cl in credit_lines: self.reconcile_using_pf_checkout(cl) # Compute results if self.partial_reconcile_line_ids: oldest_credit = min(self.partial_reconcile_line_ids.mapped("date")) - self.missing_donation_line_ids = mvl_obj.search([ - ("account_id", "=", self.account_id.id), - ("full_reconcile_id", "=", False), - ("debit", ">", 0), - ("date", "<=", fields.Date.to_string(oldest_credit)), - ("date", ">=", "2022-02-09") # Date of activation of pf_checkout - ]).filtered(lambda m: not m.matched_credit_ids) + self.missing_donation_line_ids = mvl_obj.search( + [ + ("account_id", "=", self.account_id.id), + ("full_reconcile_id", "=", False), + ("debit", ">", 0), + ("date", "<=", fields.Date.to_string(oldest_credit)), + ("date", ">=", "2022-02-09"), # Date of activation of pf_checkout + ] + ).filtered(lambda m: not m.matched_credit_ids) if self.full_reconcile_line_ids: self.env.user.notify_success( message=_("Successfully reconciled %s entries") % len(self.full_reconcile_line_ids), - sticky=True + sticky=True, ) else: if not credit_lines: self.env.user.notify_success( - message=_("Every credit entry is reconciled")) + message=_("Every credit entry is reconciled") + ) else: self.env.user.notify_warning( - message=_("0 credit entry could be fully reconciled")) + message=_("0 credit entry could be fully reconciled") + ) return { "type": "ir.actions.act_window", "view_mode": "tree,form", @@ -96,8 +109,15 @@ def reconcile_outstanding(self): "res_model": "account.move.line", "target": "current", "context": self.env.context, - "domain": [("id", "in", (self.partial_reconcile_line_ids + - self.missing_donation_line_ids).ids)] + "domain": [ + ( + "id", + "in", + ( + self.partial_reconcile_line_ids + self.missing_donation_line_ids + ).ids, + ) + ], } def reconcile_using_pf_checkout(self, move_line): @@ -125,8 +145,8 @@ def reconcile_using_pf_checkout(self, move_line): self.missing_donation_line_ids += move_line return False date_transactions = datetime.strptime( - move_line.name[date_position:date_position + date_length], - "%Y%m%d" if date_length == 8 else "%d.%m.%Y" + move_line.name[date_position : date_position + date_length], + "%Y%m%d" if date_length == 8 else "%d.%m.%Y", ) + timedelta(days=search_days_delta) pf_service, space_id = self.get_pf_service() debit_match = self.env["account.move.line"] @@ -134,12 +154,18 @@ def reconcile_using_pf_checkout(self, move_line): for transaction in pf_service.search(space_id, pf_filter): # Some references have this TEMPTR- prefix that should be ignored ref = transaction.merchant_reference.replace("TEMPTR-", "").split("-")[0] - debit_match += self.env["account.move.line"].search([ - ("ref", "like", ref), - ("debit", ">", 0), - ("full_reconcile_id", "=", False), - ("account_id", "=", self.account_id.id) - ]).filtered(lambda m: not m.matched_credit_ids) + debit_match += ( + self.env["account.move.line"] + .search( + [ + ("ref", "like", ref), + ("debit", ">", 0), + ("full_reconcile_id", "=", False), + ("account_id", "=", self.account_id.id), + ] + ) + .filtered(lambda m: not m.matched_credit_ids) + ) # Perform a partial or full reconcile (move_line + debit_match).reconcile() if sum(debit_match.mapped("debit")) == move_line.credit: @@ -158,12 +184,15 @@ def _search_in_credit_string(self, move_line, search_string): @ormcache() def get_pf_service(self): pf_acquirer = self.env.ref( - "payment_postfinance_flex.payment_acquirer_postfinance") + "payment_postfinance_flex.payment_acquirer_postfinance" + ) config = Configuration( user_id=pf_acquirer.postfinance_api_userid, - api_secret=pf_acquirer.postfinance_api_application_key) - return TransactionServiceApi(configuration=config),\ - pf_acquirer.postfinance_api_spaceid + api_secret=pf_acquirer.postfinance_api_application_key, + ) + return TransactionServiceApi( + configuration=config + ), pf_acquirer.postfinance_api_spaceid @api.model def get_pf_filter(self, date_search=None, provider=None, state="FULFILL"): @@ -191,16 +220,18 @@ def get_pf_filter(self, date_search=None, provider=None, state="FULFILL"): "operator": "EQUALS", "type": "LEAF", "value": state, - } + }, ], "type": "AND", } } if provider is not None: - domain["filter"]["children"].append({ - "fieldName": "paymentConnectorConfiguration.name", - "operator": "CONTAINS", - "type": "LEAF", - "value": PF_MAPPING.get(provider), - }) + domain["filter"]["children"].append( + { + "fieldName": "paymentConnectorConfiguration.name", + "operator": "CONTAINS", + "type": "LEAF", + "value": PF_MAPPING.get(provider), + } + ) return domain diff --git a/requirements.txt b/requirements.txt index 4e30a6a00..022f7876f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ bs4 pandas pandas>=1.5.3 pdf2image +postfinancecheckout pyminizip PyPDF2 pypng