|
@ -22,9 +22,6 @@ import re |
|
|
import logging |
|
|
import logging |
|
|
from datetime import datetime |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
from openerp.addons.account_bank_statement_import.parserlib import ( |
|
|
|
|
|
BankStatement) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def str2amount(sign, amount_str): |
|
|
def str2amount(sign, amount_str): |
|
|
"""Convert sign (C or D) and amount in string to signed amount (float).""" |
|
|
"""Convert sign (C or D) and amount in string to signed amount (float).""" |
|
@ -68,13 +65,11 @@ def get_counterpart(transaction, subfield): |
|
|
if not subfield: |
|
|
if not subfield: |
|
|
return # subfield is empty |
|
|
return # subfield is empty |
|
|
if len(subfield) >= 1 and subfield[0]: |
|
|
if len(subfield) >= 1 and subfield[0]: |
|
|
transaction.remote_account = subfield[0] |
|
|
|
|
|
|
|
|
transaction.update({'account_number': subfield[0]}) |
|
|
if len(subfield) >= 2 and subfield[1]: |
|
|
if len(subfield) >= 2 and subfield[1]: |
|
|
transaction.remote_bank_bic = subfield[1] |
|
|
|
|
|
|
|
|
transaction.update({'account_bic': subfield[1]}) |
|
|
if len(subfield) >= 3 and subfield[2]: |
|
|
if len(subfield) >= 3 and subfield[2]: |
|
|
transaction.remote_owner = subfield[2] |
|
|
|
|
|
if len(subfield) >= 4 and subfield[3]: |
|
|
|
|
|
transaction.remote_owner_city = subfield[3] |
|
|
|
|
|
|
|
|
transaction.update({'partner_name': subfield[2]}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handle_common_subfields(transaction, subfields): |
|
|
def handle_common_subfields(transaction, subfields): |
|
@ -83,11 +78,11 @@ def handle_common_subfields(transaction, subfields): |
|
|
for counterpart_field in ['CNTP', 'BENM', 'ORDP']: |
|
|
for counterpart_field in ['CNTP', 'BENM', 'ORDP']: |
|
|
if counterpart_field in subfields: |
|
|
if counterpart_field in subfields: |
|
|
get_counterpart(transaction, subfields[counterpart_field]) |
|
|
get_counterpart(transaction, subfields[counterpart_field]) |
|
|
if not transaction.message: |
|
|
|
|
|
transaction.message = '' |
|
|
|
|
|
|
|
|
if not transaction.get('name'): |
|
|
|
|
|
transaction['name'] = '' |
|
|
# REMI: Remitter information (text entered by other party on trans.): |
|
|
# REMI: Remitter information (text entered by other party on trans.): |
|
|
if 'REMI' in subfields: |
|
|
if 'REMI' in subfields: |
|
|
transaction.message += ( |
|
|
|
|
|
|
|
|
transaction['name'] += ( |
|
|
subfields['REMI'][2] |
|
|
subfields['REMI'][2] |
|
|
# this might look like |
|
|
# this might look like |
|
|
# /REMI/USTD//<remittance info>/ |
|
|
# /REMI/USTD//<remittance info>/ |
|
@ -101,10 +96,10 @@ def handle_common_subfields(transaction, subfields): |
|
|
) |
|
|
) |
|
|
# EREF: End-to-end reference |
|
|
# EREF: End-to-end reference |
|
|
if 'EREF' in subfields: |
|
|
if 'EREF' in subfields: |
|
|
transaction.message += '/'.join(filter(bool, subfields['EREF'])) |
|
|
|
|
|
|
|
|
transaction['name'] += '/'.join(filter(bool, subfields['EREF'])) |
|
|
# Get transaction reference subfield (might vary): |
|
|
# Get transaction reference subfield (might vary): |
|
|
if transaction.eref in subfields: |
|
|
|
|
|
transaction.eref = ''.join(subfields[transaction.eref]) |
|
|
|
|
|
|
|
|
if transaction.get('ref') in subfields: |
|
|
|
|
|
transaction['ref'] = ''.join(subfields[transaction['ref']]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MT940(object): |
|
|
class MT940(object): |
|
@ -131,6 +126,8 @@ class MT940(object): |
|
|
self.current_statement = None |
|
|
self.current_statement = None |
|
|
self.current_transaction = None |
|
|
self.current_transaction = None |
|
|
self.statements = [] |
|
|
self.statements = [] |
|
|
|
|
|
self.currency_code = None |
|
|
|
|
|
self.account_number = None |
|
|
|
|
|
|
|
|
def is_mt940(self, line): |
|
|
def is_mt940(self, line): |
|
|
"""determine if a line is the header of a statement""" |
|
|
"""determine if a line is the header of a statement""" |
|
@ -170,7 +167,7 @@ class MT940(object): |
|
|
record_line = '' |
|
|
record_line = '' |
|
|
self.statements.append(self.current_statement) |
|
|
self.statements.append(self.current_statement) |
|
|
self.current_statement = None |
|
|
self.current_statement = None |
|
|
return self.statements |
|
|
|
|
|
|
|
|
return self.currency_code, self.account_number, self.statements |
|
|
|
|
|
|
|
|
def add_record_line(self, line, record_line): |
|
|
def add_record_line(self, line, record_line): |
|
|
record_line += line |
|
|
record_line += line |
|
@ -188,7 +185,13 @@ class MT940(object): |
|
|
"""skip header lines, create current statement""" |
|
|
"""skip header lines, create current statement""" |
|
|
for dummy_i in range(self.header_lines): |
|
|
for dummy_i in range(self.header_lines): |
|
|
iterator.next() |
|
|
iterator.next() |
|
|
self.current_statement = BankStatement() |
|
|
|
|
|
|
|
|
self.current_statement = { |
|
|
|
|
|
'name': None, |
|
|
|
|
|
'date': None, |
|
|
|
|
|
'balance_start': 0.0, |
|
|
|
|
|
'balance_end_real': 0.0, |
|
|
|
|
|
'transactions': [] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
def handle_footer(self, dummy_line, dummy_iterator): |
|
|
def handle_footer(self, dummy_line, dummy_iterator): |
|
|
"""add current statement to list, reset state""" |
|
|
"""add current statement to list, reset state""" |
|
@ -213,7 +216,7 @@ class MT940(object): |
|
|
def handle_tag_25(self, data): |
|
|
def handle_tag_25(self, data): |
|
|
"""Handle tag 25: local bank account information.""" |
|
|
"""Handle tag 25: local bank account information.""" |
|
|
data = data.replace('EUR', '').replace('.', '').strip() |
|
|
data = data.replace('EUR', '').replace('.', '').strip() |
|
|
self.current_statement.local_account = data |
|
|
|
|
|
|
|
|
self.account_number = data |
|
|
|
|
|
|
|
|
def handle_tag_28C(self, data): |
|
|
def handle_tag_28C(self, data): |
|
|
"""Sequence number within batch - normally only zeroes.""" |
|
|
"""Sequence number within batch - normally only zeroes.""" |
|
@ -224,18 +227,21 @@ class MT940(object): |
|
|
# For the moment only first 60F record |
|
|
# For the moment only first 60F record |
|
|
# The alternative would be to split the file and start a new |
|
|
# The alternative would be to split the file and start a new |
|
|
# statement for each 20: tag encountered. |
|
|
# statement for each 20: tag encountered. |
|
|
stmt = self.current_statement |
|
|
|
|
|
if not stmt.local_currency: |
|
|
|
|
|
stmt.local_currency = data[7:10] |
|
|
|
|
|
stmt.start_balance = str2amount(data[0], data[10:]) |
|
|
|
|
|
|
|
|
if not self.currency_code: |
|
|
|
|
|
self.currency_code = data[7:10] |
|
|
|
|
|
self.current_statement['balance_start'] = str2amount( |
|
|
|
|
|
data[0], |
|
|
|
|
|
data[10:] |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def handle_tag_61(self, data): |
|
|
def handle_tag_61(self, data): |
|
|
"""get transaction values""" |
|
|
"""get transaction values""" |
|
|
transaction = self.current_statement.create_transaction() |
|
|
|
|
|
self.current_transaction = transaction |
|
|
|
|
|
transaction.execution_date = datetime.strptime(data[:6], '%y%m%d') |
|
|
|
|
|
transaction.value_date = datetime.strptime(data[:6], '%y%m%d') |
|
|
|
|
|
# ...and the rest already is highly bank dependent |
|
|
|
|
|
|
|
|
self.current_statement['transactions'].append({}) |
|
|
|
|
|
self.current_transaction = self.current_statement['transactions'][-1] |
|
|
|
|
|
self.current_transaction['date'] = datetime.strptime( |
|
|
|
|
|
data[:6], |
|
|
|
|
|
'%y%m%d' |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def handle_tag_62F(self, data): |
|
|
def handle_tag_62F(self, data): |
|
|
"""Get ending balance, statement date and id. |
|
|
"""Get ending balance, statement date and id. |
|
@ -251,19 +257,24 @@ class MT940(object): |
|
|
Depending on the bank, there might be multiple 62F tags in the import |
|
|
Depending on the bank, there might be multiple 62F tags in the import |
|
|
file. The last one counts. |
|
|
file. The last one counts. |
|
|
""" |
|
|
""" |
|
|
stmt = self.current_statement |
|
|
|
|
|
stmt.end_balance = str2amount(data[0], data[10:]) |
|
|
|
|
|
stmt.date = datetime.strptime(data[1:7], '%y%m%d') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.current_statement['balance_end_real'] = str2amount( |
|
|
|
|
|
data[0], |
|
|
|
|
|
data[10:] |
|
|
|
|
|
) |
|
|
|
|
|
self.current_statement['date'] = datetime.strptime(data[1:7], '%y%m%d') |
|
|
|
|
|
|
|
|
# Only replace logically empty (only whitespace or zeroes) id's: |
|
|
# Only replace logically empty (only whitespace or zeroes) id's: |
|
|
# But do replace statement_id's added before (therefore starting |
|
|
# But do replace statement_id's added before (therefore starting |
|
|
# with local_account), because we need the date on the last 62F |
|
|
# with local_account), because we need the date on the last 62F |
|
|
# record. |
|
|
# record. |
|
|
test_empty_id = re.sub(r'[\s0]', '', stmt.statement_id) |
|
|
|
|
|
if ((not test_empty_id) or |
|
|
|
|
|
(stmt.statement_id.startswith(stmt.local_account))): |
|
|
|
|
|
stmt.statement_id = '%s-%s' % ( |
|
|
|
|
|
stmt.local_account, |
|
|
|
|
|
stmt.date.strftime('%Y-%m-%d'), |
|
|
|
|
|
|
|
|
statement_name = self.current_statement['name'] or '' |
|
|
|
|
|
test_empty_id = re.sub(r'[\s0]', '', statement_name) |
|
|
|
|
|
is_account_number = statement_name.startswith(self.account_number) |
|
|
|
|
|
if ((not test_empty_id) or is_account_number): |
|
|
|
|
|
self.current_statement['name'] = '%s-%s' % ( |
|
|
|
|
|
self.account_number, |
|
|
|
|
|
self.current_statement['date'].strftime('%Y-%m-%d'), |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
def handle_tag_64(self, data): |
|
|
def handle_tag_64(self, data): |
|
|