[FIX] account_banking_nl_clieop: description records are not being used

[FIX] account_banking_nl_clieop: wrong position of beneficiary in payments
[FIX] account_banking_nl_clieop: wrong alignment of truncates messages
[IMP] account_banking_nl_clieop: Allow long messages to wrap over multiple
                                 records
[IMP] account_banking: Improved matching of partial payments
[IMP] account_banking: Dutch ING accounts no longer require online IBAN
                       conversion
This commit is contained in:
Pieter J. Kersten
2011-02-09 20:54:03 +01:00
parent 5a5b00699b
commit e70e7a70ea
4 changed files with 80 additions and 25 deletions

View File

@@ -51,7 +51,7 @@ class Field(object):
def format(self, value):
value = str(value)
if len(value) > self.length:
return value[len(value) - self.length:]
return value[:self.length]
return value.ljust(self.length, self.fillchar)
def take(self, buffer):

View File

@@ -49,11 +49,12 @@ def get_iban_bic_NL(bank_acc):
# calculates accounts, so no need to consult it - calculate our own
number = bank_acc.lstrip('0')
if len(number) <= 7:
iban = IBAN.create(BBAN='INGB' + number.rjust(10, '0'),
countrycode='NL'
)
return struct(
iban = IBAN(BBAN='INGB' + number.rjust(10, '0'),
countrycode='NL'
).replace(' ',''),
account = iban,
iban = iban.replace(' ',''),
account = iban.BBAN[4:],
bic = 'INGBNL2A',
code = 'INGBNL',
bank = 'ING Bank N.V.',

View File

@@ -33,6 +33,7 @@ import time
import wizard
import netsvc
import base64
import datetime
from tools import config
from tools.translate import _
from account_banking.parsers import models
@@ -43,6 +44,10 @@ from banktools import *
bt = models.mem_bank_transaction
# This variable is used to match supplier invoices with an invoice date after
# the real payment date. This can occur with online transactions (web shops).
payment_window = datetime.timedelta(days=10)
def parser_types(*args, **kwargs):
'''Delay evaluation of parser types until start of wizard, to allow
depending modules to initialize and add their parsers to the list
@@ -106,6 +111,7 @@ class banking_import(wizard.interface):
self.__state = ''
self.__linked_invoices = {}
self.__linked_payments = {}
self.__multiple_matches = []
def _fill_results(self, *args, **kwargs):
return {'log': self._log}
@@ -135,7 +141,9 @@ class banking_import(wizard.interface):
)
else:
if move_line.reconcile_partial_id:
partial_ids = [x.id for x in move_line.reconcile_partial_id]
partial_ids = [x.id for x in
move_line.reconcile_partial_id.line_partial_ids
]
else:
partial_ids = []
move_line.reconcile_id = reconcile_obj.create(
@@ -284,7 +292,7 @@ class banking_import(wizard.interface):
if partner_ids:
candidates = [x for x in move_lines
if x.partner_id.id in partner_ids and
str2date(x.date, '%Y-%m-%d') <= trans.execution_date
str2date(x.date, '%Y-%m-%d') <= (trans.execution_date + payment_window)
and (not _cached(x) or _remaining(x))
]
else:
@@ -303,8 +311,9 @@ class banking_import(wizard.interface):
# reporting this.
candidates = [x for x in candidates or move_lines
if x.invoice and has_id_match(x.invoice, ref, msg)
and str2date(x.invoice.date_invoice, '%Y-%m-%d') <= trans.execution_date
and (not _cached(x) or _remaining(x))
and str2date(x.invoice.date_invoice, '%Y-%m-%d')
<= (trans.execution_date + payment_window)
and (not _cached(x) or _remaining(x))
]
# Match on amount expected. Limit this kind of search to known
@@ -312,9 +321,10 @@ class banking_import(wizard.interface):
if not candidates and partner_ids:
candidates = [x for x in move_lines
if round(abs(x.credit or x.debit), digits) ==
round(abs(trans.transferred_amount), digits) and
str2date(x.date, '%Y-%m-%d') <= trans.execution_date and
(not _cached(x) or _remaining(x))
round(abs(trans.transferred_amount), digits)
and str2date(x.date, '%Y-%m-%d') <=
(trans.execution_date + payment_window)
and (not _cached(x) or _remaining(x))
]
move_line = False
@@ -325,8 +335,9 @@ class banking_import(wizard.interface):
# TODO: currency coercing
best = [x for x in candidates
if round(abs(x.credit or x.debit), digits) ==
round(abs(trans.transferred_amount), digits) and
str2date(x.date, '%Y-%m-%d') <= trans.execution_date
round(abs(trans.transferred_amount), digits)
and str2date(x.date, '%Y-%m-%d') <=
(trans.execution_date + payment_window)
]
if len(best) == 1:
# Exact match
@@ -343,8 +354,9 @@ class banking_import(wizard.interface):
# transfers first
paid = [x for x in move_lines
if x.invoice and has_id_match(x.invoice, ref, msg)
and str2date(x.invoice.date_invoice, '%Y-%m-%d') <= trans.execution_date
and (_cached(x) and not _remaining(x))
and str2date(x.invoice.date_invoice, '%Y-%m-%d')
<= trans.execution_date
and (_cached(x) and not _remaining(x))
]
if paid:
log.append(
@@ -364,6 +376,14 @@ class banking_import(wizard.interface):
'ref': trans.reference,
'no_candidates': len(best) or len(candidates)
})
log.append(' ' +
_('Candidates: %(candidates)s') % {
'candidates': ', '.join([x.invoice.number
for x in best or candidates
])
})
self.__multiple_matches.append((trans, best or
candidates))
move_line = False
partial = False
@@ -403,6 +423,15 @@ class banking_import(wizard.interface):
x.id for x in bank_account_ids
if x.partner_id.id == move_line.partner_id.id
]
# Re-check the cases with multiple candidates again:
# later matches may have removed possible candidates.
for trans, candidates in self.__multiple_matches:
best = [x for x in candidates if not self._cached(x)]
if len(best) == 1:
# Now an exact match can be made
pass
return (
self._get_move_info(cursor, uid, move_line,
account_ids and account_ids[0] or False,
@@ -411,6 +440,7 @@ class banking_import(wizard.interface):
trans2
)
return (False, False)
def _link_canceled_debit(self, cursor, uid, trans, payment_lines,
@@ -877,7 +907,7 @@ class banking_import(wizard.interface):
i += 1
results.stat_loaded_cnt += 1
if payment_lines:
# As payments lines are treated as individual transactions, the
# batch as a whole is only marked as 'done' when all payment lines

View File

@@ -37,6 +37,14 @@ def eleven_test(s):
r += (l-i) * int(c)
return (r % 11) == 0
def chunk(str, length):
'''
Split a string in equal sized substrings of length <length>
'''
while str:
yield str[:length]
str = str[length:]
class HeaderRecord(record.Record): #{{{
'''ClieOp3 header record'''
_fields = [
@@ -145,7 +153,7 @@ class DescriptionRecord(record.Record):
'''Description'''
_fields = [
record.Filler('recordcode', 4, '0160'),
record.Filler('variantcode', 1, 'B'),
record.Filler('variantcode', 1, 'A'),
record.Field('description', 32),
record.Filler('filler', 13),
]
@@ -209,6 +217,17 @@ class Optional(object):
setattr(newitem, attr, value)
self._guts.append(newitem)
def __len__(self):
'''Return actual contents'''
return len(self._guts)
def length(self, attr):
'''Return length of optional record'''
res = [x for x in self._klass._fields if x.name == attr]
if res:
return res[0].length
raise AttributeError(attr)
def __getattr__(self, attr):
'''Only return if used'''
if attr[0] == '_':
@@ -246,10 +265,15 @@ class Transaction(object):
self.transaction.amount = int(amount * 100)
if reference:
self.paymentreference.paymentreference = reference
for msg in messages:
# Allow long message lines to redistribute over multiple message
# records
for msg in chunk(''.join(messages),
self.description.length('description')
)[:4]:
self.description.description = msg
self.name.name = name
class DirectDebit(Transaction):
'''Direct Debit Payment transaction'''
def __init__(self, *args, **kwargs):
@@ -266,8 +290,8 @@ class DirectDebit(Transaction):
items = [str(self.transaction)]
if self.name:
items.append(str(self.name))
for kenmerk in self.paymentreference:
items.append(str(kenmerk))
for reference in self.paymentreference:
items.append(str(reference))
for description in self.description:
items.append(str(description))
return '\r\n'.join(items)
@@ -289,12 +313,12 @@ class Payment(Transaction):
Return self as writeable file content object
'''
items = [str(self.transaction)]
for kenmerk in self.paymentreference:
items.append(str(kenmerk))
if self.name:
items.append(str(self.name))
for reference in self.paymentreference:
items.append(str(reference))
for description in self.description:
items.append(str(description))
if self.name:
items.append(str(self.name))
return '\r\n'.join(items)
class SalaryPayment(Payment):