forked from OCA/l10n-switzerland
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '14.0-camt54' into 14.0-all
- Loading branch information
Showing
17 changed files
with
913 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
================================== | ||
l10n_ch_bank_statement_import_camt | ||
================================== | ||
|
||
.. | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! This file is generated by oca-gen-addon-readme !! | ||
!! changes will be overwritten. !! | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! source digest: sha256:9b0edf16915f6c32eb8a8559d23ee530ed493c2f02dff4e954fef5b166bb137b | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
.. |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%2Fl10n--switzerland-lightgray.png?logo=github | ||
:target: https://github.com/OCA/l10n-switzerland/tree/14.0/l10n_ch_bank_statement_import_camt | ||
:alt: OCA/l10n-switzerland | ||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png | ||
:target: https://translation.odoo-community.org/projects/l10n-switzerland-14-0/l10n-switzerland-14-0-l10n_ch_bank_statement_import_camt | ||
: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/l10n-switzerland&target_branch=14.0 | ||
:alt: Try me on Runboat | ||
|
||
|badge1| |badge2| |badge3| |badge4| |badge5| | ||
|
||
This addon extend the features of the camt parser in bank-statement-import from OCA by responding to specific need for switzerland (like QRR account). | ||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Usage | ||
===== | ||
|
||
Journals can be defined as "QR IBAN for import" which modify the way the import will behave. | ||
If this is marked it will search for a bank account with a l10n_ch_qr_iban corresponding to the second IBAN available in the camt (Ntry/NtryRef xml tag). | ||
This allow to have a bank account set on two different journals and use the QRR swiss functionality to import on his own specific journal the referenced transactions. | ||
|
||
Known issues / Roadmap | ||
====================== | ||
|
||
Known issues: | ||
|
||
* Currently this module has a problem with the manual import (Specific import button on each journal) when two journals are set with the same bank account. This is because the match journal check that the context journal is equal to the one matched, it matches the first journal with the IBAN found in the file. The general import button does work. | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/l10n-switzerland/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 <https://github.com/OCA/l10n-switzerland/issues/new?body=module:%20l10n_ch_bank_statement_import_camt%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. | ||
|
||
Do not contact contributors directly about support or help with technical issues. | ||
|
||
Credits | ||
======= | ||
|
||
Authors | ||
~~~~~~~ | ||
|
||
* Compassion CH | ||
|
||
Contributors | ||
~~~~~~~~~~~~ | ||
|
||
* Simon Gonzalez (Compassion CH) | ||
|
||
Other credits | ||
~~~~~~~~~~~~~ | ||
|
||
The development of this module has been financially supported by: | ||
|
||
* Compassion CH | ||
|
||
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. | ||
|
||
This module is part of the `OCA/l10n-switzerland <https://github.com/OCA/l10n-switzerland/tree/14.0/l10n_ch_bank_statement_import_camt>`_ project on GitHub. | ||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import models | ||
from . import wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright 2023 Compassion CH | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
{ | ||
"name": "l10n_ch_bank_statement_import_camt", | ||
"version": "14.0.1.0.0", | ||
"category": "Account", | ||
"website": "https://github.com/OCA/l10n-switzerland", | ||
"author": "Compassion CH, " "Odoo Community Association (OCA)", | ||
"license": "AGPL-3", | ||
"installable": True, | ||
"data": ["views/account_journal_view.xml"], | ||
"depends": ["account_statement_import_camt54", "l10n_ch_qriban"], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import account_journal | ||
from . import custom_parser |
27 changes: 27 additions & 0 deletions
27
l10n_ch_bank_statement_import_camt/models/account_journal.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Copyright 2023 Compassion CH <https://compassion.ch> | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
from odoo import api, fields, models | ||
|
||
|
||
class AccountJournal(models.Model): | ||
_inherit = "account.journal" | ||
|
||
should_qr_parsing = fields.Boolean( | ||
string="QR IBAN for import", | ||
help="Parse the account QR iban field for CAMT54\n" | ||
"This field can't accept three journals with the same account number.", | ||
) | ||
|
||
@api.constrains("should_qr_parsing") | ||
def _qr_iban_defined_qr_parsing(self): | ||
for journal in self: | ||
if ( | ||
journal.should_qr_parsing | ||
and not journal.bank_account_id.l10n_ch_qr_iban | ||
): | ||
raise ValueError( | ||
"The QR IBAN should be defined on the bank account " | ||
"if you use QR IBAN for import on the journal" | ||
) | ||
return True |
215 changes: 215 additions & 0 deletions
215
l10n_ch_bank_statement_import_camt/models/custom_parser.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
import re | ||
import string | ||
|
||
from odoo import _, models | ||
|
||
|
||
class CustomParser(models.AbstractModel): | ||
_inherit = "account.statement.import.camt.parser" | ||
|
||
def parse_entry(self, ns, node): | ||
"""Parse an Ntry node and yield transactions""" | ||
|
||
# Get some basic infos about the transaction in the XML. | ||
transaction = { | ||
"payment_ref": "/", | ||
"amount": 0, | ||
"narration": {}, | ||
"transaction_type": {}, | ||
} | ||
|
||
self.add_value_from_node( | ||
ns, node, "./ns:BkTxCd/ns:Prtry/ns:Cd", transaction, "transaction_type" | ||
) | ||
self.add_value_from_node(ns, node, "./ns:BookgDt/ns:Dt", transaction, "date") | ||
amount = self.parse_amount(ns, node) | ||
if amount != 0.0: | ||
transaction["amount"] = amount | ||
self.add_value_from_node( | ||
ns, node, "./ns:AddtlNtryInf", transaction, "payment_ref" | ||
) | ||
self.add_value_from_node( | ||
ns, | ||
node, | ||
[ | ||
"./ns:NtryDtls/ns:RmtInf/ns:Strd/ns:CdtrRefInf/ns:Ref", | ||
"./ns:NtryDtls/ns:Btch/ns:PmtInfId", | ||
"./ns:NtryDtls/ns:TxDtls/ns:Refs/ns:AcctSvcrRef", | ||
], | ||
transaction["narration"], | ||
"%s (NtryDtls/Btch/PmtInfId)" % _("Account Servicer Reference"), | ||
) | ||
|
||
# save AddtlNtryInf for after to add it to name of transaction | ||
addtl_ntry_inf = node.xpath("./ns:AddtlNtryInf", namespaces={"ns": ns}) | ||
|
||
node_charge_amount = node.xpath( | ||
"./ns:Chrgs/ns:Rcrd/ns:Amt", namespaces={"ns": ns} | ||
) | ||
node_charge_included = node.xpath( | ||
"./ns:Chrgs/ns:Rcrd/ns:ChrgInclInd", namespaces={"ns": ns} | ||
) | ||
|
||
# has a charge and is not included | ||
if len(node_charge_included) > 0 and node_charge_included[0].text == "true": | ||
if len(node_charge_amount) > 0: | ||
charge_amount = -float(node_charge_amount[0].text) | ||
tr = transaction.copy() | ||
tr["amount"] = charge_amount | ||
tr["payment_ref"] += " (bank charge)" | ||
yield tr | ||
|
||
# If there is a 'TxDtls' node in the XML we get the value of | ||
# 'AcctSvcrRef' in it. | ||
details_nodes = node.xpath("./ns:NtryDtls/ns:TxDtls", namespaces={"ns": ns}) | ||
if len(details_nodes) == 0: | ||
yield transaction | ||
self.add_value_from_node( | ||
ns, | ||
node, | ||
"./ns:AcctSvcrRef", | ||
transaction["narration"], | ||
"%s (AcctSvcrRef)" % _("Account Servicer Reference"), | ||
) | ||
return | ||
|
||
self.add_value_from_node( | ||
ns, | ||
node, | ||
"./ns:BkTxCd/ns:Domn/ns:Fmly/ns:SubFmlyCd", | ||
transaction["narration"], | ||
"%s (BkTxCd/Domn/Fmly/SubFmlyCd)" % _("Sub Family Code"), | ||
) | ||
|
||
self.add_value_from_node( | ||
ns, | ||
node, | ||
"./ns:NtryDtls/ns:TxDtls/ns:Refs/ns:EndToEndId", | ||
transaction["narration"], | ||
"%s (NtryDtls/TxDtls/Refs/EndToEndId)" % _("End To End Identification"), | ||
) | ||
|
||
transaction_base = transaction | ||
for node in details_nodes: | ||
transaction = transaction_base.copy() | ||
self.parse_transaction_details(ns, node, transaction) | ||
|
||
# If there is a AddtlNtryInf, then we do the concatenate | ||
if addtl_ntry_inf: | ||
transaction["payment_ref"] += f" - [{addtl_ntry_inf[0].text}]" | ||
yield transaction | ||
self.generate_narration(transaction) | ||
|
||
def parse_transaction_details(self, ns, node, transaction): | ||
super().parse_transaction_details(ns, node, transaction) | ||
# Check if a global AcctSvcrRef exist | ||
found_node = node.xpath("../../ns:AcctSvcrRef", namespaces={"ns": ns}) | ||
if len(found_node) != 0: | ||
self.add_value_from_node( | ||
ns, | ||
node, | ||
"../../ns:AcctSvcrRef", | ||
transaction["narration"], | ||
"%s (../../AcctSvcrRef)" % _("Account Servicer Reference"), | ||
) | ||
else: | ||
self.add_value_from_node( | ||
ns, | ||
node, | ||
"./ns:Refs/ns:AcctSvcrRef", | ||
transaction["narration"], | ||
"%s (./Refs/AcctSvcrRef)" % _("Account Servicer Reference"), | ||
) | ||
# Add transaction note for QR statements | ||
self.add_value_from_node( | ||
ns, | ||
node, | ||
[ | ||
"./ns:RmtInf/ns:Strd/ns:AddtlRmtInf", | ||
], | ||
transaction, | ||
"note", | ||
join_str="\n", | ||
) | ||
|
||
def parse_statement(self, ns, node): | ||
result = {} | ||
|
||
iban = node.xpath("./ns:Acct/ns:Id/ns:IBAN", namespaces={"ns": ns}) | ||
if len(iban) > 0: | ||
# find the journal related to this account | ||
journals = self.env["account.journal"].search([]) | ||
trans_table = str.maketrans("", "", string.whitespace) | ||
account_number_ = iban[0].text.translate(trans_table) | ||
journal = journals.filtered( | ||
lambda x: x.bank_acc_number | ||
and x.bank_acc_number.translate(trans_table) == account_number_ | ||
and not x.should_qr_parsing | ||
) | ||
if journal: | ||
self = self.with_context(journal_id=journal.id) | ||
|
||
entry_nodes = node.xpath("./ns:Ntry", namespaces={"ns": ns}) | ||
if len(entry_nodes) > 0: | ||
result = super().parse_statement(ns, node) | ||
|
||
entry_ref = node.xpath("./ns:Ntry/ns:NtryRef", namespaces={"ns": ns}) | ||
if len(entry_ref) > 1 and "054" in ns: | ||
first_entry = entry_ref[0].text | ||
# Parse all entry ref node to check if they're all the same. | ||
for entry in entry_ref: | ||
if first_entry != entry.text: | ||
raise ValueError( | ||
"Different entry ref in same file " "not supported !" | ||
) | ||
self.add_value_from_node( | ||
ns, node, "./ns:Ntry/ns:NtryRef", result, "ntryRef" | ||
) | ||
result["camt_headers"] = ns | ||
# In case of an empty camt file | ||
else: | ||
result["transactions"] = "" | ||
result["is_empty"] = True | ||
return result | ||
|
||
def parse(self, data): | ||
result = super().parse(data) | ||
currency = result[0] | ||
account_number = result[1] | ||
statements = result[2] | ||
if len(statements) > 0: | ||
if "camt_headers" in statements[0]: | ||
if "camt.054" not in statements[0]["camt_headers"]: | ||
statements[0].pop("camt_headers") | ||
if "ntryRef" in statements[0]: | ||
statements[0].pop("ntryRef") | ||
return currency, account_number, statements | ||
|
||
def get_balance_amounts(self, ns, node): | ||
result = super().get_balance_amounts(ns, node) | ||
start_balance_node = result[0] | ||
end_balance_node = result[0] | ||
|
||
details_nodes = node.xpath("./ns:Bal/ns:Amt", namespaces={"ns": ns}) | ||
|
||
if start_balance_node == 0.0 and not len(details_nodes): | ||
start_balance_node = node.xpath("./ns:Ntry", namespaces={"ns": ns}) | ||
amount_tot = 0 | ||
for node in start_balance_node: | ||
amount_tot -= self.parse_amount(ns, node) | ||
return (amount_tot, end_balance_node) | ||
return result | ||
|
||
def check_version(self, ns, root): | ||
try: | ||
super().check_version(ns, root) | ||
except ValueError: | ||
re_camt_version = re.compile( | ||
r"(^urn:iso:std:iso:20022:tech:xsd:camt.054." r"|^ISO:camt.054.)" | ||
) | ||
if not re_camt_version.search(ns): | ||
raise ValueError("no camt 052 or 053 or 054: " + ns) | ||
# Check GrpHdr element: | ||
root_0_0 = root[0][0].tag[len(ns) + 2 :] # strip namespace | ||
if root_0_0 != "GrpHdr": | ||
raise ValueError("expected GrpHdr, got: " + root_0_0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Simon Gonzalez (Compassion CH) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
The development of this module has been financially supported by: | ||
|
||
* Compassion CH |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This addon extend the features of the camt parser in bank-statement-import from OCA by responding to specific need for switzerland (like QRR account). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Known issues: | ||
|
||
* Currently this module has a problem with the manual import (Specific import button on each journal) when two journals are set with the same bank account. This is because the match journal check that the context journal is equal to the one matched, it matches the first journal with the IBAN found in the file. The general import button does work. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Journals can be defined as "QR IBAN for import" which modify the way the import will behave. | ||
If this is marked it will search for a bank account with a l10n_ch_qr_iban corresponding to the second IBAN available in the camt (Ntry/NtryRef xml tag). | ||
This allow to have a bank account set on two different journals and use the QRR swiss functionality to import on his own specific journal the referenced transactions. |
Oops, something went wrong.