Skip to content

Commit

Permalink
Merge branch '14.0-camt54' into 14.0-all
Browse files Browse the repository at this point in the history
  • Loading branch information
ecino committed Feb 27, 2024
2 parents 2d97078 + 554bdbb commit 241e2ee
Show file tree
Hide file tree
Showing 17 changed files with 913 additions and 0 deletions.
97 changes: 97 additions & 0 deletions l10n_ch_bank_statement_import_camt/README.rst
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.
2 changes: 2 additions & 0 deletions l10n_ch_bank_statement_import_camt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
13 changes: 13 additions & 0 deletions l10n_ch_bank_statement_import_camt/__manifest__.py
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"],
}
2 changes: 2 additions & 0 deletions l10n_ch_bank_statement_import_camt/models/__init__.py
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 l10n_ch_bank_statement_import_camt/models/account_journal.py
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 l10n_ch_bank_statement_import_camt/models/custom_parser.py
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)
1 change: 1 addition & 0 deletions l10n_ch_bank_statement_import_camt/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Simon Gonzalez (Compassion CH)
3 changes: 3 additions & 0 deletions l10n_ch_bank_statement_import_camt/readme/CREDITS.rst
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
1 change: 1 addition & 0 deletions l10n_ch_bank_statement_import_camt/readme/DESCRIPTION.rst
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).
3 changes: 3 additions & 0 deletions l10n_ch_bank_statement_import_camt/readme/ROADMAP.rst
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.
3 changes: 3 additions & 0 deletions l10n_ch_bank_statement_import_camt/readme/USAGE.rst
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.
Loading

0 comments on commit 241e2ee

Please sign in to comment.