|
@ -2,8 +2,9 @@ |
|
|
"""Class to parse camt files.""" |
|
|
"""Class to parse camt files.""" |
|
|
############################################################################## |
|
|
############################################################################## |
|
|
# |
|
|
# |
|
|
# Copyright (C) 2013-2015 Therp BV <http://therp.nl> |
|
|
|
|
|
|
|
|
# Copyright (C) 2013-2018 Therp BV <http://therp.nl> |
|
|
# Copyright 2017 Open Net Sàrl |
|
|
# Copyright 2017 Open Net Sàrl |
|
|
|
|
|
# (C) 2015 1200wd.com |
|
|
# |
|
|
# |
|
|
# This program is free software: you can redistribute it and/or modify |
|
|
# 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 |
|
|
# it under the terms of the GNU Affero General Public License as published |
|
@ -19,14 +20,13 @@ |
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
# |
|
|
# |
|
|
############################################################################## |
|
|
############################################################################## |
|
|
|
|
|
|
|
|
|
|
|
import logging |
|
|
import re |
|
|
import re |
|
|
from copy import copy |
|
|
from copy import copy |
|
|
from datetime import datetime |
|
|
from datetime import datetime |
|
|
from lxml import etree |
|
|
from lxml import etree |
|
|
|
|
|
|
|
|
from openerp import _ |
|
|
from openerp import _ |
|
|
|
|
|
|
|
|
from openerp.addons.account_bank_statement_import.parserlib import ( |
|
|
from openerp.addons.account_bank_statement_import.parserlib import ( |
|
|
BankStatement) |
|
|
BankStatement) |
|
|
|
|
|
|
|
@ -195,38 +195,53 @@ class CamtParser(models.AbstractModel): |
|
|
transaction['data'] = etree.tostring(data) |
|
|
transaction['data'] = etree.tostring(data) |
|
|
yield transaction |
|
|
yield transaction |
|
|
|
|
|
|
|
|
def get_balance_amounts(self, ns, node): |
|
|
|
|
|
"""Return opening and closing balance. |
|
|
|
|
|
|
|
|
def get_balance_type_node(self, node, balance_type): |
|
|
|
|
|
""" |
|
|
|
|
|
:param node: BkToCstmrStmt/Stmt/Bal node |
|
|
|
|
|
:param balance type: one of 'OPBD', 'PRCD', 'ITBD', 'CLBD' |
|
|
|
|
|
""" |
|
|
|
|
|
code_expr = ( |
|
|
|
|
|
'./ns:Bal/ns:Tp/ns:CdOrPrtry/ns:Cd[text()="%s"]/../../..' % |
|
|
|
|
|
balance_type |
|
|
|
|
|
) |
|
|
|
|
|
return self.xpath(node, code_expr) |
|
|
|
|
|
|
|
|
Depending on kind of balance and statement, the balance might be in a |
|
|
|
|
|
different kind of node: |
|
|
|
|
|
OPBD = OpeningBalance |
|
|
|
|
|
PRCD = PreviousClosingBalance |
|
|
|
|
|
ITBD = InterimBalance (first ITBD is start-, second is end-balance) |
|
|
|
|
|
CLBD = ClosingBalance |
|
|
|
|
|
|
|
|
def get_start_balance(self, node): |
|
|
""" |
|
|
""" |
|
|
start_balance_node = None |
|
|
|
|
|
end_balance_node = None |
|
|
|
|
|
for node_name in ['OPBD', 'PRCD', 'CLBD', 'ITBD']: |
|
|
|
|
|
code_expr = ( |
|
|
|
|
|
'./ns:Bal/ns:Tp/ns:CdOrPrtry/ns:Cd[text()="%s"]/../../..' % |
|
|
|
|
|
node_name |
|
|
|
|
|
) |
|
|
|
|
|
balance_node = node.xpath(code_expr, namespaces={'ns': ns}) |
|
|
|
|
|
if balance_node: |
|
|
|
|
|
if node_name in ['OPBD', 'PRCD']: |
|
|
|
|
|
start_balance_node = balance_node[0] |
|
|
|
|
|
elif node_name == 'CLBD': |
|
|
|
|
|
end_balance_node = balance_node[0] |
|
|
|
|
|
else: |
|
|
|
|
|
if not start_balance_node: |
|
|
|
|
|
start_balance_node = balance_node[0] |
|
|
|
|
|
if not end_balance_node: |
|
|
|
|
|
end_balance_node = balance_node[-1] |
|
|
|
|
|
return ( |
|
|
|
|
|
self.parse_amount(ns, start_balance_node), |
|
|
|
|
|
self.parse_amount(ns, end_balance_node) |
|
|
|
|
|
|
|
|
Find the (only) balance node with code OpeningBalance, or |
|
|
|
|
|
the only one with code 'PreviousClosingBalance' |
|
|
|
|
|
or the first balance node with code InterimBalance in |
|
|
|
|
|
the case of preceeding pagination. |
|
|
|
|
|
|
|
|
|
|
|
:param node: BkToCstmrStmt/Stmt/Bal node |
|
|
|
|
|
""" |
|
|
|
|
|
balance = 0 |
|
|
|
|
|
nodes = ( |
|
|
|
|
|
self.get_balance_type_node(node, 'OPBD') or |
|
|
|
|
|
self.get_balance_type_node(node, 'PRCD') or |
|
|
|
|
|
self.get_balance_type_node(node, 'ITBD') |
|
|
) |
|
|
) |
|
|
|
|
|
if nodes: |
|
|
|
|
|
balance = self.parse_amount(nodes[0]) |
|
|
|
|
|
return balance |
|
|
|
|
|
|
|
|
|
|
|
def get_end_balance(self, node): |
|
|
|
|
|
""" |
|
|
|
|
|
Find the (only) balance node with code ClosingBalance, or |
|
|
|
|
|
the second (and last) balance node with code InterimBalance in |
|
|
|
|
|
the case of continued pagination. |
|
|
|
|
|
|
|
|
|
|
|
:param node: BkToCstmrStmt/Stmt/Bal node |
|
|
|
|
|
""" |
|
|
|
|
|
balance = 0 |
|
|
|
|
|
nodes = ( |
|
|
|
|
|
self.get_balance_type_node(node, 'CLAV') or |
|
|
|
|
|
self.get_balance_type_node(node, 'CLBD') or |
|
|
|
|
|
self.get_balance_type_node(node, 'ITBD') |
|
|
|
|
|
) |
|
|
|
|
|
if nodes: |
|
|
|
|
|
balance = self.parse_amount(nodes[-1]) |
|
|
|
|
|
return balance |
|
|
|
|
|
|
|
|
def parse_statement(self, ns, node): |
|
|
def parse_statement(self, ns, node): |
|
|
"""Parse a single Stmt node.""" |
|
|
"""Parse a single Stmt node.""" |
|
@ -241,14 +256,14 @@ class CamtParser(models.AbstractModel): |
|
|
ns, node, './ns:Id', statement, 'statement_id') |
|
|
ns, node, './ns:Id', statement, 'statement_id') |
|
|
self.add_value_from_node( |
|
|
self.add_value_from_node( |
|
|
ns, node, './ns:Acct/ns:Ccy', statement, 'local_currency') |
|
|
ns, node, './ns:Acct/ns:Ccy', statement, 'local_currency') |
|
|
(statement.start_balance, statement.end_balance) = ( |
|
|
|
|
|
self.get_balance_amounts(ns, node)) |
|
|
|
|
|
entry_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns}) |
|
|
|
|
|
transactions = [] |
|
|
|
|
|
for entry_node in entry_nodes: |
|
|
|
|
|
|
|
|
statement.start_balance = self.get_start_balance(node) |
|
|
|
|
|
statement.end_balance = self.get_end_balance(node) |
|
|
|
|
|
transaction_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns}) |
|
|
|
|
|
total_amount = 0 |
|
|
|
|
|
for entry_node in transaction_nodes: |
|
|
transaction = statement.create_transaction() |
|
|
transaction = statement.create_transaction() |
|
|
transactions.extend(self.parse_entry(ns, entry_node, transaction)) |
|
|
|
|
|
statement['transactions'] = transactions |
|
|
|
|
|
|
|
|
total_amount += transaction['transferred_amount'] |
|
|
|
|
|
self.parse_transaction(ns, entry_node, transaction) |
|
|
if statement['transactions']: |
|
|
if statement['transactions']: |
|
|
execution_date = statement['transactions'][0].execution_date[:10] |
|
|
execution_date = statement['transactions'][0].execution_date[:10] |
|
|
statement.date = datetime.strptime(execution_date, "%Y-%m-%d") |
|
|
statement.date = datetime.strptime(execution_date, "%Y-%m-%d") |
|
@ -256,6 +271,15 @@ class CamtParser(models.AbstractModel): |
|
|
if execution_date not in statement.statement_id: |
|
|
if execution_date not in statement.statement_id: |
|
|
statement.statement_id = "%s-%s" % ( |
|
|
statement.statement_id = "%s-%s" % ( |
|
|
execution_date, statement.statement_id) |
|
|
execution_date, statement.statement_id) |
|
|
|
|
|
if statement.start_balance == 0 and statement.end_balance != 0: |
|
|
|
|
|
statement.start_balance = statement.end_balance - total_amount |
|
|
|
|
|
_logger.debug( |
|
|
|
|
|
_("Start balance %s calculated from end balance %s and" |
|
|
|
|
|
" Total amount %s."), |
|
|
|
|
|
statement.start_balance, |
|
|
|
|
|
statement.end_balance, |
|
|
|
|
|
total_amount |
|
|
|
|
|
) |
|
|
return statement |
|
|
return statement |
|
|
|
|
|
|
|
|
def check_version(self, ns, root): |
|
|
def check_version(self, ns, root): |
|
|