mirror of
https://github.com/OCA/bank-statement-import.git
synced 2025-01-20 12:37:43 +02:00
[10.0] Better parse camt groupped transactions (#131)
* correctly parse entries with multiple transactions Our bank does it. Care has been taken not to break cases where Ntry/NtryDtls/TxDtls isn't used: if any piece of information isn't found on these nodes, the matching information from the Ntry is used. Most often the entry has a sum or a summary and the transaction details are more precise and specific, so it makes sense to use their contents rather than its. * more complete unit tests for camt parser This checks the whole output data of the parser against its expected output. * add a test case that checks TxDtls decoding * special case for Ntrys without TxDtls * CO-12 - Date Import BVR * FIX tests * CO-1232 Fixed issues with CAMT parser tests
This commit is contained in:
committed by
Moises Lopez - https://www.vauxoo.com/
parent
7db5b1e54e
commit
19c1256ee3
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Class to parse camt files."""
|
"""Class to parse camt files."""
|
||||||
# © 2013-2016 Therp BV <http://therp.nl>
|
# © 2013-2016 Therp BV <http://therp.nl>
|
||||||
|
# Copyright 2017 Open Net Sàrl
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
import re
|
import re
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
@@ -46,7 +47,7 @@ class CamtParser(models.AbstractModel):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def parse_transaction_details(self, ns, node, transaction):
|
def parse_transaction_details(self, ns, node, transaction):
|
||||||
"""Parse transaction details (message, party, account...)."""
|
"""Parse TxDtls node."""
|
||||||
# message
|
# message
|
||||||
self.add_value_from_node(
|
self.add_value_from_node(
|
||||||
ns, node, [
|
ns, node, [
|
||||||
@@ -64,9 +65,13 @@ class CamtParser(models.AbstractModel):
|
|||||||
ns, node, [
|
ns, node, [
|
||||||
'./ns:RmtInf/ns:Strd/ns:CdtrRefInf/ns:Ref',
|
'./ns:RmtInf/ns:Strd/ns:CdtrRefInf/ns:Ref',
|
||||||
'./ns:Refs/ns:EndToEndId',
|
'./ns:Refs/ns:EndToEndId',
|
||||||
|
'./ns:Ntry/ns:AcctSvcrRef'
|
||||||
],
|
],
|
||||||
transaction, 'ref'
|
transaction, 'ref'
|
||||||
)
|
)
|
||||||
|
amount = self.parse_amount(ns, node)
|
||||||
|
if amount != 0.0:
|
||||||
|
transaction['amount'] = amount
|
||||||
# remote party values
|
# remote party values
|
||||||
party_type = 'Dbtr'
|
party_type = 'Dbtr'
|
||||||
party_type_node = node.xpath(
|
party_type_node = node.xpath(
|
||||||
@@ -107,10 +112,11 @@ class CamtParser(models.AbstractModel):
|
|||||||
ns, account_node[0], './ns:Othr/ns:Id', transaction,
|
ns, account_node[0], './ns:Othr/ns:Id', transaction,
|
||||||
'account_number'
|
'account_number'
|
||||||
)
|
)
|
||||||
|
transaction['data'] = etree.tostring(node)
|
||||||
|
|
||||||
def parse_transaction(self, ns, node):
|
def parse_entry(self, ns, node):
|
||||||
"""Parse transaction (entry) node."""
|
"""Parse an Ntry node and yield transactions"""
|
||||||
transaction = {}
|
transaction = {'name': '/', 'amount': 0} # fallback defaults
|
||||||
self.add_value_from_node(
|
self.add_value_from_node(
|
||||||
ns, node, './ns:BkTxCd/ns:Prtry/ns:Cd', transaction,
|
ns, node, './ns:BkTxCd/ns:Prtry/ns:Cd', transaction,
|
||||||
'transfer_type'
|
'transfer_type'
|
||||||
@@ -121,27 +127,30 @@ class CamtParser(models.AbstractModel):
|
|||||||
ns, node, './ns:BookgDt/ns:Dt', transaction, 'execution_date')
|
ns, node, './ns:BookgDt/ns:Dt', transaction, 'execution_date')
|
||||||
self.add_value_from_node(
|
self.add_value_from_node(
|
||||||
ns, node, './ns:ValDt/ns:Dt', transaction, 'value_date')
|
ns, node, './ns:ValDt/ns:Dt', transaction, 'value_date')
|
||||||
|
amount = self.parse_amount(ns, node)
|
||||||
|
if amount != 0.0:
|
||||||
|
transaction['amount'] = amount
|
||||||
|
self.add_value_from_node(
|
||||||
|
ns, node, './ns:AddtlNtryInf', transaction, 'name')
|
||||||
|
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, 'ref'
|
||||||
|
)
|
||||||
|
|
||||||
transaction['amount'] = self.parse_amount(ns, node)
|
details_nodes = node.xpath(
|
||||||
|
|
||||||
details_node = node.xpath(
|
|
||||||
'./ns:NtryDtls/ns:TxDtls', namespaces={'ns': ns})
|
'./ns:NtryDtls/ns:TxDtls', namespaces={'ns': ns})
|
||||||
if details_node:
|
if len(details_nodes) == 0:
|
||||||
self.parse_transaction_details(ns, details_node[0], transaction)
|
yield transaction
|
||||||
if not transaction.get('name'):
|
return
|
||||||
self.add_value_from_node(
|
transaction_base = transaction
|
||||||
ns, node, './ns:AddtlNtryInf', transaction, 'name')
|
for node in details_nodes:
|
||||||
if not transaction.get('name'):
|
transaction = transaction_base.copy()
|
||||||
transaction['name'] = '/'
|
self.parse_transaction_details(ns, node, transaction)
|
||||||
if not transaction.get('ref'):
|
yield transaction
|
||||||
self.add_value_from_node(
|
|
||||||
ns, node, [
|
|
||||||
'./ns:NtryDtls/ns:Btch/ns:PmtInfId',
|
|
||||||
],
|
|
||||||
transaction, 'ref'
|
|
||||||
)
|
|
||||||
transaction['data'] = etree.tostring(node)
|
|
||||||
return transaction
|
|
||||||
|
|
||||||
def get_balance_amounts(self, ns, node):
|
def get_balance_amounts(self, ns, node):
|
||||||
"""Return opening and closing balance.
|
"""Return opening and closing balance.
|
||||||
@@ -193,12 +202,15 @@ class CamtParser(models.AbstractModel):
|
|||||||
ns, node, './ns:Acct/ns:Ccy', result, 'currency')
|
ns, node, './ns:Acct/ns:Ccy', result, 'currency')
|
||||||
result['balance_start'], result['balance_end_real'] = (
|
result['balance_start'], result['balance_end_real'] = (
|
||||||
self.get_balance_amounts(ns, node))
|
self.get_balance_amounts(ns, node))
|
||||||
transaction_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns})
|
entry_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns})
|
||||||
result['transactions'] = []
|
transactions = []
|
||||||
for entry_node in transaction_nodes:
|
for entry_node in entry_nodes:
|
||||||
transaction = self.parse_transaction(ns, entry_node)
|
transactions.extend(self.parse_entry(ns, entry_node))
|
||||||
if transaction:
|
result['transactions'] = transactions
|
||||||
result['transactions'].append(transaction)
|
result['date'] = sorted(transactions,
|
||||||
|
key=lambda x: x['date'],
|
||||||
|
reverse=True
|
||||||
|
)[0]['date']
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def check_version(self, ns, root):
|
def check_version(self, ns, root):
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
(None,
|
||||||
|
'CH1111000000123456789',
|
||||||
|
[{'balance_end_real': 79443.15,
|
||||||
|
'balance_start': 75960.15,
|
||||||
|
'date': '2017-03-22',
|
||||||
|
'name': '20170323123456789012345',
|
||||||
|
'transactions': [{'account_number': 'CH2222000000123456789',
|
||||||
|
'amount': 2187.0,
|
||||||
|
'data': '<TxDtls xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n <Refs>\n <AcctSvcrRef>123456CHCAFEBABE</AcctSvcrRef>\n <Prtry>\n <Tp>01</Tp>\n <Ref>123456CHCAFEBABE</Ref>\n </Prtry>\n </Refs>\n <Amt Ccy="CHF">2187.00</Amt>\n <CdtDbtInd>CRDT</CdtDbtInd>\n <BkTxCd>\n <Domn>\n <Cd>PMNT</Cd>\n <Fmly>\n <Cd>RCDT</Cd>\n <SubFmlyCd>AUTT</SubFmlyCd>\n </Fmly>\n </Domn>\n </BkTxCd>\n <RltdPties>\n <Dbtr>\n <Nm>Banque Cantonale Vaudoise</Nm>\n <PstlAdr>\n <StrtNm>Place Saint-François</StrtNm>\n <BldgNb>14</BldgNb>\n <PstCd>1003</PstCd>\n <TwnNm>Lausanne</TwnNm>\n <Ctry>CH1</Ctry>\n </PstlAdr>\n </Dbtr>\n <DbtrAcct>\n <Id>\n <IBAN>CH2222000000123456789</IBAN>\n </Id>\n </DbtrAcct>\n </RltdPties>\n <RltdAgts>\n <DbtrAgt>\n <FinInstnId>\n <BICFI>POFICHBEXXX</BICFI>\n <Nm>POSTFINANCE AG</Nm>\n <PstlAdr>\n <AdrLine>MINGERSTRASSE 20</AdrLine>\n <AdrLine>3030 BERNE</AdrLine>\n </PstlAdr>\n </FinInstnId>\n </DbtrAgt>\n </RltdAgts>\n <RmtInf>\n <Strd>\n <CdtrRefInf>\n <Tp>\n <CdOrPrtry>\n <Prtry>ISR Reference</Prtry>\n </CdOrPrtry>\n </Tp>\n <Ref>302388292000011111111111111</Ref>\n </CdtrRefInf>\n <AddtlRmtInf>?REJECT?0</AddtlRmtInf>\n </Strd>\n </RmtInf>\n <RltdDts>\n <AccptncDtTm>2017-03-22T20:00:00</AccptncDtTm>\n </RltdDts>\n </TxDtls>\n ',
|
||||||
|
'date': '2017-03-22',
|
||||||
|
'execution_date': '2017-03-22',
|
||||||
|
'name': u'CR\xc9DIT GROUP\xc9 BVR TRAITEMENT DU 22.03.2017 NUM\xc9RO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE',
|
||||||
|
'partner_country': 'CH1',
|
||||||
|
'partner_name': 'Banque Cantonale Vaudoise',
|
||||||
|
'ref': '302388292000011111111111111',
|
||||||
|
'value_date': '2017-03-23'},
|
||||||
|
{'account_number': 'CH3333000000123456789',
|
||||||
|
'amount': 1296.0,
|
||||||
|
'data': '<TxDtls xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n <Refs>\n <AcctSvcrRef>123456CHCAFEBABE</AcctSvcrRef>\n <Prtry>\n <Tp>01</Tp>\n <Ref>123456CHCAFEBABE</Ref>\n </Prtry>\n </Refs>\n <Amt Ccy="CHF">1296.00</Amt>\n <CdtDbtInd>CRDT</CdtDbtInd>\n <BkTxCd>\n <Domn>\n <Cd>PMNT</Cd>\n <Fmly>\n <Cd>RCDT</Cd>\n <SubFmlyCd>AUTT</SubFmlyCd>\n </Fmly>\n </Domn>\n </BkTxCd>\n <RltdPties>\n <Dbtr>\n <Nm>Banque Cantonale Vaudoise</Nm>\n <PstlAdr>\n <StrtNm>Place Saint-François</StrtNm>\n <BldgNb>14</BldgNb>\n <PstCd>1003</PstCd>\n <TwnNm>Lausanne</TwnNm>\n <Ctry>CH2</Ctry>\n </PstlAdr>\n </Dbtr>\n <DbtrAcct>\n <Id>\n <IBAN>CH3333000000123456789</IBAN>\n </Id>\n </DbtrAcct>\n </RltdPties>\n <RltdAgts>\n <DbtrAgt>\n <FinInstnId>\n <BICFI>POFICHBEYYY</BICFI>\n <Nm>POSTFINANCE AG</Nm>\n <PstlAdr>\n <AdrLine>MINGERSTRASSE 20</AdrLine>\n <AdrLine>3030 BERNE</AdrLine>\n </PstlAdr>\n </FinInstnId>\n </DbtrAgt>\n </RltdAgts>\n <RmtInf>\n <Strd>\n <CdtrRefInf>\n <Tp>\n <CdOrPrtry>\n <Prtry>ISR Reference</Prtry>\n </CdOrPrtry>\n </Tp>\n <Ref>302388292000022222222222222</Ref>\n </CdtrRefInf>\n <AddtlRmtInf>?REJECT?0</AddtlRmtInf>\n </Strd>\n </RmtInf>\n <RltdDts>\n <AccptncDtTm>2017-03-22T20:00:00</AccptncDtTm>\n </RltdDts>\n </TxDtls>\n ',
|
||||||
|
'date': '2017-03-22',
|
||||||
|
'execution_date': '2017-03-22',
|
||||||
|
'name': u'CR\xc9DIT GROUP\xc9 BVR TRAITEMENT DU 22.03.2017 NUM\xc9RO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE',
|
||||||
|
'partner_country': 'CH2',
|
||||||
|
'partner_name': 'Banque Cantonale Vaudoise',
|
||||||
|
'ref': '302388292000022222222222222',
|
||||||
|
'value_date': '2017-03-23'}]}])
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
(None,
|
||||||
|
'NL77ABNA0574908765',
|
||||||
|
[{'balance_end_real': 15121.12,
|
||||||
|
'balance_start': 15568.27,
|
||||||
|
'date': '2014-01-05',
|
||||||
|
'name': '1234Test/1',
|
||||||
|
'transactions': [{'account_bic': 'ABNANL2A',
|
||||||
|
'account_number': 'NL46ABNA0499998748',
|
||||||
|
'amount': -754.25,
|
||||||
|
'data': '<TxDtls xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">\n <Refs>\n <InstrId>INNDNL2U20141231000142300002844</InstrId>\n <EndToEndId>435005714488-ABNO33052620</EndToEndId>\n <MndtId>1880000341866</MndtId>\n </Refs>\n <AmtDtls>\n <TxAmt>\n <Amt Ccy="EUR">754.25</Amt>\n </TxAmt>\n </AmtDtls>\n <RltdPties>\n <Cdtr>\n <Nm>INSURANCE COMPANY TESTX</Nm>\n <PstlAdr>\n <StrtNm>TEST STREET 20</StrtNm>\n <TwnNm>1234 AB TESTCITY</TwnNm>\n <Ctry>NL</Ctry>\n </PstlAdr>\n </Cdtr>\n <CdtrAcct>\n <Id>\n <IBAN>NL46ABNA0499998748</IBAN>\n </Id>\n </CdtrAcct>\n </RltdPties>\n <RltdAgts>\n <CdtrAgt>\n <FinInstnId>\n <BIC>ABNANL2A</BIC>\n </FinInstnId>\n </CdtrAgt>\n </RltdAgts>\n <RmtInf>\n <Ustrd>Insurance policy 857239PERIOD 01.01.2014 - 31.12.2014</Ustrd>\n </RmtInf>\n <AddtlTxInf>MKB Insurance 859239PERIOD 01.01.2014 - 31.12.2014</AddtlTxInf>\n </TxDtls>\n ',
|
||||||
|
'date': '2014-01-05',
|
||||||
|
'execution_date': '2014-01-05',
|
||||||
|
'name': 'MKB Insurance 859239PERIOD 01.01.2014 - 31.12.2014',
|
||||||
|
'note': 'Insurance policy 857239PERIOD 01.01.2014 - 31.12.2014',
|
||||||
|
'partner_country': 'NL',
|
||||||
|
'partner_name': 'INSURANCE COMPANY TESTX',
|
||||||
|
'ref': '435005714488-ABNO33052620',
|
||||||
|
'transfer_type': 'EI',
|
||||||
|
'value_date': '2014-01-05'},
|
||||||
|
{'account_bic': 'ABNANL2A',
|
||||||
|
'account_number': 'NL46ABNA0499998748',
|
||||||
|
'amount': -594.05,
|
||||||
|
'data': '<TxDtls xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">\n <Refs>\n <InstrId>TESTBANK/NL/20141229/01206408</InstrId>\n <EndToEndId>TESTBANK/NL/20141229/01206408</EndToEndId>\n <MndtId>NL22ZZZ524885430000-C0125.1</MndtId>\n </Refs>\n <AmtDtls>\n <TxAmt>\n <Amt Ccy="EUR">564.05</Amt>\n </TxAmt>\n </AmtDtls>\n <RltdPties>\n <Cdtr>\n <Nm>Test Customer</Nm>\n <PstlAdr>\n <Ctry>NL</Ctry>\n </PstlAdr>\n </Cdtr>\n <CdtrAcct>\n <Id>\n <IBAN>NL46ABNA0499998748</IBAN>\n </Id>\n </CdtrAcct>\n </RltdPties>\n <RltdAgts>\n <CdtrAgt>\n <FinInstnId>\n <BIC>ABNANL2A</BIC>\n </FinInstnId>\n </CdtrAgt>\n </RltdAgts>\n <RmtInf>\n <Ustrd>Direct Debit S14 0410</Ustrd>\n </RmtInf>\n <RtrInf>\n <Rsn>\n <Cd>AC06</Cd>\n </Rsn>\n </RtrInf>\n <AddtlTxInf>Direct debit S14 0410 AC07 Rek.nummer blokkade TESTBANK/NL/20141229/01206408</AddtlTxInf>\n </TxDtls>\n ',
|
||||||
|
'date': '2014-01-05',
|
||||||
|
'execution_date': '2014-01-05',
|
||||||
|
'name': 'Direct debit S14 0410 AC07 Rek.nummer blokkade TESTBANK/NL/20141229/01206408',
|
||||||
|
'note': 'Direct Debit S14 0410',
|
||||||
|
'partner_country': 'NL',
|
||||||
|
'partner_name': 'Test Customer',
|
||||||
|
'ref': 'TESTBANK/NL/20141229/01206408',
|
||||||
|
'transfer_type': 'EIST',
|
||||||
|
'value_date': '2014-01-05'},
|
||||||
|
{'account_bic': 'ABNANL2A',
|
||||||
|
'account_number': 'NL69ABNA0522123643',
|
||||||
|
'amount': 1405.31,
|
||||||
|
'data': '<TxDtls xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">\n <Refs>\n <InstrId>INNDNL2U20140105000217200000708</InstrId>\n <EndToEndId>115</EndToEndId>\n </Refs>\n <AmtDtls>\n <TxAmt>\n <Amt Ccy="EUR">1405.31</Amt>\n </TxAmt>\n </AmtDtls>\n <RltdPties>\n <Dbtr>\n <Nm>3rd party Media</Nm>\n <PstlAdr>\n <StrtNm>SOMESTREET 570-A</StrtNm>\n <TwnNm>1276 ML HOUSCITY</TwnNm>\n <Ctry>NL</Ctry>\n </PstlAdr>\n </Dbtr>\n <DbtrAcct>\n <Id>\n <IBAN>NL69ABNA0522123643</IBAN>\n </Id>\n </DbtrAcct>\n </RltdPties>\n <RltdAgts>\n <DbtrAgt>\n <FinInstnId>\n <BIC>ABNANL2A</BIC>\n </FinInstnId>\n </DbtrAgt>\n </RltdAgts>\n <AddtlTxInf>#RD PARTY MEDIA CUSNO 90782 4210773</AddtlTxInf>\n </TxDtls>\n ',
|
||||||
|
'date': '2014-01-05',
|
||||||
|
'execution_date': '2014-01-05',
|
||||||
|
'name': '#RD PARTY MEDIA CUSNO 90782 4210773',
|
||||||
|
'note': 'INNDNL2U20140105000217200000708',
|
||||||
|
'partner_country': 'NL',
|
||||||
|
'partner_name': '3rd party Media',
|
||||||
|
'ref': '115',
|
||||||
|
'transfer_type': 'ET',
|
||||||
|
'value_date': '2014-01-05'}]}])
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04 camt.053.001.04.xsd">
|
||||||
|
<BkToCstmrStmt>
|
||||||
|
<GrpHdr>
|
||||||
|
<MsgId>20170323312345678900000</MsgId>
|
||||||
|
<CreDtTm>2017-03-23T14:47:00</CreDtTm>
|
||||||
|
<MsgPgntn>
|
||||||
|
<PgNb>1</PgNb>
|
||||||
|
<LastPgInd>true</LastPgInd>
|
||||||
|
</MsgPgntn>
|
||||||
|
<AddtlInf>Test</AddtlInf>
|
||||||
|
</GrpHdr>
|
||||||
|
<Stmt>
|
||||||
|
<Id>20170323123456789012345</Id>
|
||||||
|
<ElctrncSeqNb>58</ElctrncSeqNb>
|
||||||
|
<CreDtTm>2017-03-23T14:47:00</CreDtTm>
|
||||||
|
<FrToDt>
|
||||||
|
<FrDtTm>2017-03-23T00:00:00</FrDtTm>
|
||||||
|
<ToDtTm>2017-03-23T23:59:59</ToDtTm>
|
||||||
|
</FrToDt>
|
||||||
|
<Acct>
|
||||||
|
<Id>
|
||||||
|
<IBAN>CH1111000000123456789</IBAN>
|
||||||
|
</Id>
|
||||||
|
<Ownr>
|
||||||
|
<Nm>Open Net S. à r.l. Prilly</Nm>
|
||||||
|
</Ownr>
|
||||||
|
</Acct>
|
||||||
|
<Bal>
|
||||||
|
<Tp>
|
||||||
|
<CdOrPrtry>
|
||||||
|
<Cd>OPBD</Cd>
|
||||||
|
</CdOrPrtry>
|
||||||
|
</Tp>
|
||||||
|
<Amt Ccy="CHF">75960.15</Amt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<Dt>
|
||||||
|
<Dt>2017-03-22</Dt>
|
||||||
|
</Dt>
|
||||||
|
</Bal>
|
||||||
|
<Bal>
|
||||||
|
<Tp>
|
||||||
|
<CdOrPrtry>
|
||||||
|
<Cd>CLBD</Cd>
|
||||||
|
</CdOrPrtry>
|
||||||
|
</Tp>
|
||||||
|
<Amt Ccy="CHF">79443.15</Amt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<Dt>
|
||||||
|
<Dt>2017-03-23</Dt>
|
||||||
|
</Dt>
|
||||||
|
</Bal>
|
||||||
|
<Ntry>
|
||||||
|
<NtryRef>012345678</NtryRef>
|
||||||
|
<Amt Ccy="CHF">3483.00</Amt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<RvslInd>false</RvslInd>
|
||||||
|
<Sts>BOOK</Sts>
|
||||||
|
<BookgDt>
|
||||||
|
<Dt>2017-03-22</Dt>
|
||||||
|
</BookgDt>
|
||||||
|
<ValDt>
|
||||||
|
<Dt>2017-03-23</Dt>
|
||||||
|
</ValDt>
|
||||||
|
<AcctSvcrRef>20170323001234567891234567891234</AcctSvcrRef>
|
||||||
|
<BkTxCd>
|
||||||
|
<Domn>
|
||||||
|
<Cd>PMNT</Cd>
|
||||||
|
<Fmly>
|
||||||
|
<Cd>RCDT</Cd>
|
||||||
|
<SubFmlyCd>VCOM</SubFmlyCd>
|
||||||
|
</Fmly>
|
||||||
|
</Domn>
|
||||||
|
</BkTxCd>
|
||||||
|
<NtryDtls>
|
||||||
|
<Btch>
|
||||||
|
<NbOfTxs>2</NbOfTxs>
|
||||||
|
</Btch>
|
||||||
|
<TxDtls>
|
||||||
|
<Refs>
|
||||||
|
<AcctSvcrRef>123456CHCAFEBABE</AcctSvcrRef>
|
||||||
|
<Prtry>
|
||||||
|
<Tp>01</Tp>
|
||||||
|
<Ref>123456CHCAFEBABE</Ref>
|
||||||
|
</Prtry>
|
||||||
|
</Refs>
|
||||||
|
<Amt Ccy="CHF">2187.00</Amt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<BkTxCd>
|
||||||
|
<Domn>
|
||||||
|
<Cd>PMNT</Cd>
|
||||||
|
<Fmly>
|
||||||
|
<Cd>RCDT</Cd>
|
||||||
|
<SubFmlyCd>AUTT</SubFmlyCd>
|
||||||
|
</Fmly>
|
||||||
|
</Domn>
|
||||||
|
</BkTxCd>
|
||||||
|
<RltdPties>
|
||||||
|
<Dbtr>
|
||||||
|
<Nm>Banque Cantonale Vaudoise</Nm>
|
||||||
|
<PstlAdr>
|
||||||
|
<StrtNm>Place Saint-François</StrtNm>
|
||||||
|
<BldgNb>14</BldgNb>
|
||||||
|
<PstCd>1003</PstCd>
|
||||||
|
<TwnNm>Lausanne</TwnNm>
|
||||||
|
<Ctry>CH1</Ctry>
|
||||||
|
</PstlAdr>
|
||||||
|
</Dbtr>
|
||||||
|
<DbtrAcct>
|
||||||
|
<Id>
|
||||||
|
<IBAN>CH2222000000123456789</IBAN>
|
||||||
|
</Id>
|
||||||
|
</DbtrAcct>
|
||||||
|
</RltdPties>
|
||||||
|
<RltdAgts>
|
||||||
|
<DbtrAgt>
|
||||||
|
<FinInstnId>
|
||||||
|
<BICFI>POFICHBEXXX</BICFI>
|
||||||
|
<Nm>POSTFINANCE AG</Nm>
|
||||||
|
<PstlAdr>
|
||||||
|
<AdrLine>MINGERSTRASSE 20</AdrLine>
|
||||||
|
<AdrLine>3030 BERNE</AdrLine>
|
||||||
|
</PstlAdr>
|
||||||
|
</FinInstnId>
|
||||||
|
</DbtrAgt>
|
||||||
|
</RltdAgts>
|
||||||
|
<RmtInf>
|
||||||
|
<Strd>
|
||||||
|
<CdtrRefInf>
|
||||||
|
<Tp>
|
||||||
|
<CdOrPrtry>
|
||||||
|
<Prtry>ISR Reference</Prtry>
|
||||||
|
</CdOrPrtry>
|
||||||
|
</Tp>
|
||||||
|
<Ref>302388292000011111111111111</Ref>
|
||||||
|
</CdtrRefInf>
|
||||||
|
<AddtlRmtInf>?REJECT?0</AddtlRmtInf>
|
||||||
|
</Strd>
|
||||||
|
</RmtInf>
|
||||||
|
<RltdDts>
|
||||||
|
<AccptncDtTm>2017-03-22T20:00:00</AccptncDtTm>
|
||||||
|
</RltdDts>
|
||||||
|
</TxDtls>
|
||||||
|
<TxDtls>
|
||||||
|
<Refs>
|
||||||
|
<AcctSvcrRef>123456CHCAFEBABE</AcctSvcrRef>
|
||||||
|
<Prtry>
|
||||||
|
<Tp>01</Tp>
|
||||||
|
<Ref>123456CHCAFEBABE</Ref>
|
||||||
|
</Prtry>
|
||||||
|
</Refs>
|
||||||
|
<Amt Ccy="CHF">1296.00</Amt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<BkTxCd>
|
||||||
|
<Domn>
|
||||||
|
<Cd>PMNT</Cd>
|
||||||
|
<Fmly>
|
||||||
|
<Cd>RCDT</Cd>
|
||||||
|
<SubFmlyCd>AUTT</SubFmlyCd>
|
||||||
|
</Fmly>
|
||||||
|
</Domn>
|
||||||
|
</BkTxCd>
|
||||||
|
<RltdPties>
|
||||||
|
<Dbtr>
|
||||||
|
<Nm>Banque Cantonale Vaudoise</Nm>
|
||||||
|
<PstlAdr>
|
||||||
|
<StrtNm>Place Saint-François</StrtNm>
|
||||||
|
<BldgNb>14</BldgNb>
|
||||||
|
<PstCd>1003</PstCd>
|
||||||
|
<TwnNm>Lausanne</TwnNm>
|
||||||
|
<Ctry>CH2</Ctry>
|
||||||
|
</PstlAdr>
|
||||||
|
</Dbtr>
|
||||||
|
<DbtrAcct>
|
||||||
|
<Id>
|
||||||
|
<IBAN>CH3333000000123456789</IBAN>
|
||||||
|
</Id>
|
||||||
|
</DbtrAcct>
|
||||||
|
</RltdPties>
|
||||||
|
<RltdAgts>
|
||||||
|
<DbtrAgt>
|
||||||
|
<FinInstnId>
|
||||||
|
<BICFI>POFICHBEYYY</BICFI>
|
||||||
|
<Nm>POSTFINANCE AG</Nm>
|
||||||
|
<PstlAdr>
|
||||||
|
<AdrLine>MINGERSTRASSE 20</AdrLine>
|
||||||
|
<AdrLine>3030 BERNE</AdrLine>
|
||||||
|
</PstlAdr>
|
||||||
|
</FinInstnId>
|
||||||
|
</DbtrAgt>
|
||||||
|
</RltdAgts>
|
||||||
|
<RmtInf>
|
||||||
|
<Strd>
|
||||||
|
<CdtrRefInf>
|
||||||
|
<Tp>
|
||||||
|
<CdOrPrtry>
|
||||||
|
<Prtry>ISR Reference</Prtry>
|
||||||
|
</CdOrPrtry>
|
||||||
|
</Tp>
|
||||||
|
<Ref>302388292000022222222222222</Ref>
|
||||||
|
</CdtrRefInf>
|
||||||
|
<AddtlRmtInf>?REJECT?0</AddtlRmtInf>
|
||||||
|
</Strd>
|
||||||
|
</RmtInf>
|
||||||
|
<RltdDts>
|
||||||
|
<AccptncDtTm>2017-03-22T20:00:00</AccptncDtTm>
|
||||||
|
</RltdDts>
|
||||||
|
</TxDtls>
|
||||||
|
</NtryDtls>
|
||||||
|
<AddtlNtryInf>CRÉDIT GROUPÉ BVR TRAITEMENT DU 22.03.2017 NUMÉRO CLIENT 01-70884-3 PAQUET ID: 123456CHCAFEBABE</AddtlNtryInf>
|
||||||
|
</Ntry>
|
||||||
|
</Stmt>
|
||||||
|
</BkToCstmrStmt>
|
||||||
|
</Document>
|
||||||
@@ -1,12 +1,53 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Run test to import camt.053 import."""
|
"""Run test to import camt.053 import."""
|
||||||
# © 2013-2016 Therp BV <http://therp.nl>
|
# © 2013-2016 Therp BV <http://therp.nl>
|
||||||
|
# Copyright 2017 Open Net Sàrl
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
import base64
|
import base64
|
||||||
|
import difflib
|
||||||
|
import pprint
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase
|
||||||
from odoo.tools.misc import file_open
|
from odoo.tools.misc import file_open
|
||||||
|
|
||||||
|
|
||||||
|
DATA_DIR = 'account_bank_statement_import_camt/test_files/'
|
||||||
|
|
||||||
|
|
||||||
|
class TestParser(TransactionCase):
|
||||||
|
"""Tests for the camt parser itself."""
|
||||||
|
def setUp(self):
|
||||||
|
super(TestParser, self).setUp()
|
||||||
|
self.parser = self.env['account.bank.statement.import.camt.parser']
|
||||||
|
|
||||||
|
def _do_parse_test(self, inputfile, goldenfile):
|
||||||
|
with file_open(inputfile) as testfile:
|
||||||
|
data = testfile.read()
|
||||||
|
res = self.parser.parse(data)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.pydata') as temp:
|
||||||
|
pprint.pprint(res, temp)
|
||||||
|
with file_open(goldenfile) as golden:
|
||||||
|
temp.seek(0)
|
||||||
|
diff = list(
|
||||||
|
difflib.unified_diff(golden.readlines(), temp.readlines(),
|
||||||
|
golden.name, temp.name))
|
||||||
|
if len(diff) > 2:
|
||||||
|
self.fail(
|
||||||
|
"actual output doesn't match exptected output:\n%s" %
|
||||||
|
"".join(diff))
|
||||||
|
|
||||||
|
def test_parse(self):
|
||||||
|
self._do_parse_test(
|
||||||
|
DATA_DIR + 'test-camt053',
|
||||||
|
DATA_DIR + 'golden-camt053.pydata')
|
||||||
|
|
||||||
|
def test_parse_txdtls(self):
|
||||||
|
self._do_parse_test(
|
||||||
|
DATA_DIR + 'test-camt053-txdtls',
|
||||||
|
DATA_DIR + 'golden-camt053-txdtls.pydata')
|
||||||
|
|
||||||
|
|
||||||
class TestImport(TransactionCase):
|
class TestImport(TransactionCase):
|
||||||
"""Run test to import camt import."""
|
"""Run test to import camt import."""
|
||||||
transactions = [
|
transactions = [
|
||||||
@@ -36,9 +77,7 @@ class TestImport(TransactionCase):
|
|||||||
def test_statement_import(self):
|
def test_statement_import(self):
|
||||||
"""Test correct creation of single statement."""
|
"""Test correct creation of single statement."""
|
||||||
action = {}
|
action = {}
|
||||||
with file_open(
|
with file_open(DATA_DIR + 'test-camt053') as testfile:
|
||||||
'account_bank_statement_import_camt/test_files/test-camt053'
|
|
||||||
) as testfile:
|
|
||||||
action = self.env['account.bank.statement.import'].create({
|
action = self.env['account.bank.statement.import'].create({
|
||||||
'data_file': base64.b64encode(testfile.read()),
|
'data_file': base64.b64encode(testfile.read()),
|
||||||
}).import_file()
|
}).import_file()
|
||||||
|
|||||||
Reference in New Issue
Block a user