diff --git a/account_statement_import_camt/models/parser.py b/account_statement_import_camt/models/parser.py index ecbd8d86..ac700289 100644 --- a/account_statement_import_camt/models/parser.py +++ b/account_statement_import_camt/models/parser.py @@ -6,7 +6,7 @@ import re from lxml import etree -from odoo import models +from odoo import _, models class CamtParser(models.AbstractModel): @@ -68,7 +68,103 @@ class CamtParser(models.AbstractModel): "payment_ref", join_str="\n", ) - # name + + self.add_value_from_node( + ns, + node, + ["./ns:RmtInf/ns:Ustrd"], + transaction["narration"], + "%s (RmtInf/Ustrd)" % _("Unstructured Reference"), + join_str=" ", + ) + self.add_value_from_node( + ns, + node, + ["./ns:RmtInf/ns:Strd/ns:CdtrRefInf/ns:Ref"], + transaction["narration"], + "%s (RmtInf/Strd/CdtrRefInf/Ref)" % _("Structured Reference"), + join_str=" ", + ) + self.add_value_from_node( + ns, + node, + ["./ns:AddtlTxInf"], + transaction["narration"], + "%s (AddtlTxInf)" % _("Additional Transaction Information"), + join_str=" ", + ) + self.add_value_from_node( + ns, + node, + ["./ns:RtrInf/ns:Rsn/ns:Cd"], + transaction["narration"], + "%s (RtrInf/Rsn/Cd)" % _("Return Reason Code"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:RtrInf/ns:Rsn/ns:Cd"], + transaction["narration"], + "%s (RtrInf/Rsn/Prtry)" % _("Return Reason Code (Proprietary)"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:RtrInf/ns:AddtlInf"], + transaction["narration"], + "%s (RtrInf/AddtlInf)" % _("Return Reason Additional Information"), + join_str=" ", + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:MsgId"], + transaction["narration"], + "%s (Refs/MsgId)" % _("Msg Id"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:AcctSvcrRef"], + transaction["narration"], + "%s (Refs/AcctSvcrRef)" % _("Account Servicer Reference"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:EndToEndId"], + transaction["narration"], + "%s (Refs/EndToEndId)" % _("End To End Id"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:InstrId"], + transaction["narration"], + "%s (Refs/InstrId)" % _("Instructed Id"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:TxId"], + transaction["narration"], + "%s (Refs/TxId)" % _("Transaction Identification"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:MntId"], + transaction["narration"], + "%s (Refs/MntId)" % _("Mandate Id"), + ) + self.add_value_from_node( + ns, + node, + ["./ns:Refs/ns:ChqNb"], + transaction["narration"], + "%s (Refs/ChqNb)" % _("Cheque Number"), + ) + self.add_value_from_node( ns, node, ["./ns:AddtlTxInf"], transaction, "payment_ref", join_str="\n" ) @@ -111,6 +207,24 @@ class CamtParser(models.AbstractModel): transaction, "partner_name", ) + self.add_value_from_node( + ns, + party_node[0], + "./ns:PstlAdr/ns:StrtNm|" + "./ns:PstlAdr/ns:BldgNb|" + "./ns:PstlAdr/ns:BldgNm|" + "./ns:PstlAdr/ns:PstBx|" + "./ns:PstlAdr/ns:PstCd|" + "./ns:PstlAdr/ns:TwnNm|" + "./ns:PstlAdr/ns:TwnLctnNm|" + "./ns:PstlAdr/ns:DstrctNm|" + "./ns:PstlAdr/ns:CtrySubDvsn|" + "./ns:PstlAdr/ns:Ctry|" + "./ns:PstlAdr/ns:AdrLine", + transaction["narration"], + "%s (PstlAdr)" % _("Postal Address"), + join_str=" | ", + ) # Get remote_account from iban or from domestic account: account_node = node.xpath( "./ns:RltdPties/ns:%sAcct/ns:Id" % party_type, namespaces={"ns": ns} @@ -128,16 +242,37 @@ class CamtParser(models.AbstractModel): "account_number", ) + def generate_narration(self, transaction): + # this block ensure compatibility with v13 + transaction["narration"] = { + "%s (RltdPties/Nm)" + % _("Partner Name"): transaction.get("partner_name", ""), + "%s (RltdPties/Acct)" + % _("Partner Account Number"): transaction.get("partner_name", ""), + "%s (BookgDt)" % _("Transaction Date"): transaction.get("date", ""), + _("Reference"): transaction.get("ref", ""), + _("Communication"): transaction.get("name", ""), + "%s (BkTxCd)" + % _("Transaction Type"): transaction.get("transaction_type", ""), + **transaction["narration"], + } + + transaction["narration"] = "\n".join( + ["%s: %s" % (key, val) for key, val in transaction["narration"].items()] + ) + def parse_entry(self, ns, node): """Parse an Ntry node and yield transactions""" - transaction = {"payment_ref": "/", "amount": 0} # fallback defaults + transaction = { + "payment_ref": "/", + "amount": 0, + "narration": {}, + "transaction_type": {}, + } # fallback defaults 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, "narration" - ) self.add_value_from_node( ns, node, @@ -150,14 +285,57 @@ class CamtParser(models.AbstractModel): "ref", ) + # enrich the notes with some more infos when they are available + self.add_value_from_node( + ns, + node, + "./ns:AddtlNtryInf", + transaction["narration"], + "%s (AddtlNtryInf)" % _("Additional Entry Information"), + ) + self.add_value_from_node( + ns, + node, + "./ns:RvslInd", + transaction["narration"], + "%s (RvslInd)" % _("Reversal Indicator"), + ) + + self.add_value_from_node( + ns, + node, + "./ns:BkTxCd/ns:Domn/ns:Cd", + transaction["transaction_type"], + "Code", + ) + self.add_value_from_node( + ns, + node, + "./ns:BkTxCd/ns:Domn/ns:Fmly/ns:Cd", + transaction["transaction_type"], + "FmlyCd", + ) + self.add_value_from_node( + ns, + node, + "./ns:BkTxCd/ns:Domn/ns:Fmly/ns:SubFmlyCd", + transaction["transaction_type"], + "SubFmlyCd", + ) + transaction["transaction_type"] = ( + "-".join(transaction["transaction_type"].values()) or "" + ) + details_nodes = node.xpath("./ns:NtryDtls/ns:TxDtls", namespaces={"ns": ns}) if len(details_nodes) == 0: + self.generate_narration(transaction) yield transaction return transaction_base = transaction for node in details_nodes: transaction = transaction_base.copy() self.parse_transaction_details(ns, node, transaction) + self.generate_narration(transaction) yield transaction def get_balance_amounts(self, ns, node): diff --git a/account_statement_import_camt/readme/newsfragment/1_verbose_narration.feature b/account_statement_import_camt/readme/newsfragment/1_verbose_narration.feature new file mode 100644 index 00000000..792d6b00 --- /dev/null +++ b/account_statement_import_camt/readme/newsfragment/1_verbose_narration.feature @@ -0,0 +1,15 @@ +The narration field now contains a lot of information as key: value pairs. See code for complete list of parsed values. + + Partner Name (RltdPties/Nm): Banque Cantonale Vaudoise + Partner Account Number (RltdPties/Acct): Banque Cantonale Vaudoise + Transaction Date (BookgDt): 2017-03-22 + Reference: 302388292000022222222222222 + Communication: + Transaction Type (BkTxCd): PMNT-RCDT-VCOM + Additional Entry Information (AddtlNtryInf): CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE + Reversal Indicator (RvslInd): false + Structured Reference (RmtInf/Strd/CdtrRefInf/Ref): 302388292000022222222222222 + Account Servicer Reference (Refs/AcctSvcrRef): 123456CHCAFEBABE + Postal Address (PstlAdr): Place Saint-François | 14 | 1003 | Lausanne | CH2 + +The previous value is available at `Additional Entry Information (AddtlNtryInf):` diff --git a/account_statement_import_camt/test_files/golden-camt053-txdtls.pydata b/account_statement_import_camt/test_files/golden-camt053-txdtls.pydata index 44418034..a993ba3a 100644 --- a/account_statement_import_camt/test_files/golden-camt053-txdtls.pydata +++ b/account_statement_import_camt/test_files/golden-camt053-txdtls.pydata @@ -7,14 +7,38 @@ 'transactions': [{'account_number': 'CH2222000000123456789', 'amount': 2187.0, 'date': '2017-03-22', - 'narration': 'CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE', + 'narration': 'Partner Name (RltdPties/Nm): Banque Cantonale Vaudoise\n' + 'Partner Account Number (RltdPties/Acct): Banque Cantonale Vaudoise\n' + 'Transaction Date (BookgDt): 2017-03-22\n' + 'Reference: 302388292000011111111111111\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-RCDT-VCOM\n' + 'Additional Entry Information (AddtlNtryInf): CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: ' + '123456CHCAFEBABE\n' + 'Reversal Indicator (RvslInd): false\n' + 'Structured Reference (RmtInf/Strd/CdtrRefInf/Ref): 302388292000011111111111111\n' + 'Account Servicer Reference (Refs/AcctSvcrRef): 123456CHCAFEBABE\n' + 'Postal Address (PstlAdr): Place Saint-François | 14 | 1003 | Lausanne | CH1', 'partner_name': 'Banque Cantonale Vaudoise', 'payment_ref': '/', - 'ref': '302388292000011111111111111'}, + 'ref': '302388292000011111111111111', + 'transaction_type': 'PMNT-RCDT-VCOM'}, {'account_number': 'CH3333000000123456789', 'amount': 1296.0, 'date': '2017-03-22', - 'narration': 'CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE', + 'narration': 'Partner Name (RltdPties/Nm): Banque Cantonale Vaudoise\n' + 'Partner Account Number (RltdPties/Acct): Banque Cantonale Vaudoise\n' + 'Transaction Date (BookgDt): 2017-03-22\n' + 'Reference: 302388292000022222222222222\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-RCDT-VCOM\n' + 'Additional Entry Information (AddtlNtryInf): CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: ' + '123456CHCAFEBABE\n' + 'Reversal Indicator (RvslInd): false\n' + 'Structured Reference (RmtInf/Strd/CdtrRefInf/Ref): 302388292000022222222222222\n' + 'Account Servicer Reference (Refs/AcctSvcrRef): 123456CHCAFEBABE\n' + 'Postal Address (PstlAdr): Place Saint-François | 14 | 1003 | Lausanne | CH2', 'partner_name': 'Banque Cantonale Vaudoise', 'payment_ref': '/', - 'ref': '302388292000022222222222222'}]}]) + 'ref': '302388292000022222222222222', + 'transaction_type': 'PMNT-RCDT-VCOM'}]}]) diff --git a/account_statement_import_camt/test_files/golden-camt053.pydata b/account_statement_import_camt/test_files/golden-camt053.pydata index 4c116025..73172a6c 100644 --- a/account_statement_import_camt/test_files/golden-camt053.pydata +++ b/account_statement_import_camt/test_files/golden-camt053.pydata @@ -7,24 +7,79 @@ 'transactions': [{'account_number': 'NL46ABNA0499998748', 'amount': -754.25, 'date': '2014-01-05', + 'narration': 'Partner Name (RltdPties/Nm): INSURANCE COMPANY TESTX\n' + 'Partner Account Number (RltdPties/Acct): INSURANCE COMPANY TESTX\n' + 'Transaction Date (BookgDt): 2014-01-05\n' + 'Reference: 435005714488-ABNO33052620\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-RDDT-ESDD\n' + 'Unstructured Reference (RmtInf/Ustrd): Insurance policy 857239PERIOD 01.01.2014 - 31.12.2014\n' + 'Additional Transaction Information (AddtlTxInf): MKB Insurance 859239PERIOD 01.01.2014 - 31.12.2014\n' + 'End To End Id (Refs/EndToEndId): 435005714488-ABNO33052620\n' + 'Instructed Id (Refs/InstrId): INNDNL2U20141231000142300002844\n' + 'Postal Address (PstlAdr): TEST STREET 20 | 1234 AB TESTCITY | NL', 'partner_name': 'INSURANCE COMPANY TESTX', 'payment_ref': 'MKB Insurance 859239PERIOD 01.01.2014 - 31.12.2014', - 'ref': '435005714488-ABNO33052620'}, + 'ref': '435005714488-ABNO33052620', + 'transaction_type': 'PMNT-RDDT-ESDD'}, {'account_number': 'NL46ABNA0499998748', 'amount': -564.05, 'date': '2014-01-05', + 'narration': 'Partner Name (RltdPties/Nm): Test Customer\n' + 'Partner Account Number (RltdPties/Acct): Test Customer\n' + 'Transaction Date (BookgDt): 2014-01-05\n' + 'Reference: TESTBANK/NL/20141229/01206408\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-IDDT-UPDD\n' + 'Reversal Indicator (RvslInd): true\n' + 'Unstructured Reference (RmtInf/Ustrd): Direct Debit S14 0410\n' + 'Additional Transaction Information (AddtlTxInf): Direct debit S14 0410 AC07 Rek.nummer blokkade ' + 'TESTBANK/NL/20141229/01206408\n' + 'Return Reason Code (RtrInf/Rsn/Cd): AC06\n' + 'Return Reason Code (Proprietary) (RtrInf/Rsn/Prtry): AC06\n' + 'End To End Id (Refs/EndToEndId): TESTBANK/NL/20141229/01206408\n' + 'Instructed Id (Refs/InstrId): TESTBANK/NL/20141229/01206408\n' + 'Postal Address (PstlAdr): NL', 'partner_name': 'Test Customer', 'payment_ref': 'Direct debit S14 0410 AC07 Rek.nummer blokkade TESTBANK/NL/20141229/01206408', - 'ref': 'TESTBANK/NL/20141229/01206408'}, + 'ref': 'TESTBANK/NL/20141229/01206408', + 'transaction_type': 'PMNT-IDDT-UPDD'}, {'account_number': 'NL46ABNA0499998748', 'amount': -100.0, 'date': '2014-01-05', + 'narration': 'Partner Name (RltdPties/Nm): Test Customer\n' + 'Partner Account Number (RltdPties/Acct): Test Customer\n' + 'Transaction Date (BookgDt): 2014-01-05\n' + 'Reference: TESTBANK/NL/20141229/01206407\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-IDDT-UPDD\n' + 'Reversal Indicator (RvslInd): true\n' + 'Unstructured Reference (RmtInf/Ustrd): Direct Debit S14 0410\n' + 'Additional Transaction Information (AddtlTxInf): Direct debit S14 0410 AC07 Rek.nummer blokkade ' + 'TESTBANK/NL/20141229/01206408\n' + 'Return Reason Code (RtrInf/Rsn/Cd): AC06\n' + 'Return Reason Code (Proprietary) (RtrInf/Rsn/Prtry): AC06\n' + 'End To End Id (Refs/EndToEndId): TESTBANK/NL/20141229/01206407\n' + 'Instructed Id (Refs/InstrId): TESTBANK/NL/20141229/01206407\n' + 'Postal Address (PstlAdr): NL', 'partner_name': 'Test Customer', 'payment_ref': 'Direct debit S14 0410 AC07 Rek.nummer blokkade TESTBANK/NL/20141229/01206408', - 'ref': 'TESTBANK/NL/20141229/01206407'}, + 'ref': 'TESTBANK/NL/20141229/01206407', + 'transaction_type': 'PMNT-IDDT-UPDD'}, {'account_number': 'NL69ABNA0522123643', 'amount': 1405.31, 'date': '2014-01-05', + 'narration': 'Partner Name (RltdPties/Nm): 3rd party Media\n' + 'Partner Account Number (RltdPties/Acct): 3rd party Media\n' + 'Transaction Date (BookgDt): 2014-01-05\n' + 'Reference: 115\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-RCDT-ESCT\n' + 'Additional Transaction Information (AddtlTxInf): #RD PARTY MEDIA CUSNO 90782 4210773\n' + 'End To End Id (Refs/EndToEndId): 115\n' + 'Instructed Id (Refs/InstrId): INNDNL2U20140105000217200000708\n' + 'Postal Address (PstlAdr): SOMESTREET 570-A | 1276 ML HOUSCITY | NL', 'partner_name': '3rd party Media', 'payment_ref': '#RD PARTY MEDIA CUSNO 90782 4210773', - 'ref': '115'}]}]) + 'ref': '115', + 'transaction_type': 'PMNT-RCDT-ESCT'}]}]) diff --git a/account_statement_import_camt/test_files/golden-camt054.pydata b/account_statement_import_camt/test_files/golden-camt054.pydata index 6a00cb5a..ac60bdb1 100644 --- a/account_statement_import_camt/test_files/golden-camt054.pydata +++ b/account_statement_import_camt/test_files/golden-camt054.pydata @@ -7,7 +7,20 @@ 'transactions': [{'account_number': 'NL46ABNA0499998748', 'amount': 5.0, 'date': '2022-01-26', - 'narration': 'Additional entry info', + 'narration': 'Partner Name (RltdPties/Nm): Test Customer\n' + 'Partner Account Number (RltdPties/Acct): Test Customer\n' + 'Transaction Date (BookgDt): 2022-01-26\n' + 'Reference: 000000000000000000000000003\n' + 'Communication: \n' + 'Transaction Type (BkTxCd): PMNT-RCDT-VCOM\n' + 'Additional Entry Information (AddtlNtryInf): Additional entry info\n' + 'Reversal Indicator (RvslInd): false\n' + 'Structured Reference (RmtInf/Strd/CdtrRefInf/Ref): 000000000000000000000000003\n' + 'Account Servicer Reference (Refs/AcctSvcrRef): 15180015077602405\n' + 'End To End Id (Refs/EndToEndId): ENDTOENDID-01\n' + 'Instructed Id (Refs/InstrId): MIB InstrId305-312MM20211231v1\n' + 'Postal Address (PstlAdr): Test street 1 | 1234 AB Test city', 'partner_name': 'Test Customer', 'payment_ref': 'MIB InstrId305-312MM20211231v1', - 'ref': '000000000000000000000000003'}]}]) + 'ref': '000000000000000000000000003', + 'transaction_type': 'PMNT-RCDT-VCOM'}]}])