mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[FIX] account_banking: fixed error handling for Belgian IBAN convertor [FIX] account_banking: fixed straight copy for early IBAN adopters [IMP] account_banking: improved matching algorithm, handles split and combined payments as well [IMP] account_banking: better partner selection - now handles shared bank accounts [FIX] account_banking: default accounts can be overruled now [IMP] account_banking: extensions on memory model for bank transactions [IMP] account_banking: optional automatic creation of invoices for bank accounts [IMP] account_banking_xxx: translations update [IMP] account_banking_nl_clieop: better coding style [IMP] account_banking_nl_multibank: extended for memory model changes
250 lines
9.6 KiB
Python
250 lines
9.6 KiB
Python
# -*- encoding: utf-8 -*-
|
|
##############################################################################
|
|
#
|
|
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
|
# All Rights Reserved
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
##############################################################################
|
|
|
|
from tools.translate import _
|
|
|
|
class mem_bank_statement(object):
|
|
'''
|
|
A mem_bank_statement is a real life projection of a bank statement paper
|
|
containing a report of one or more transactions done. As these reports can
|
|
contain payments that originate in several accounting periods, period is an
|
|
attribute of mem_bank_transaction, not of mem_bank_statement.
|
|
Also note that the statement_id is copied from the bank statement, and not
|
|
generated from any sequence. This enables us to skip old data in new
|
|
statement files.
|
|
'''
|
|
# Lock attributes to enable parsers to trigger non-conformity faults
|
|
__slots__ = [
|
|
'start_balance','end_balance', 'date', 'local_account',
|
|
'local_currency', 'id', 'statements'
|
|
]
|
|
def __init__(self, *args, **kwargs):
|
|
super(mem_bank_statement, self).__init__(*args, **kwargs)
|
|
self.id = ''
|
|
self.local_account = ''
|
|
self.local_currency = ''
|
|
self.start_balance = 0.0
|
|
self.end_balance = 0.0
|
|
self.date = ''
|
|
self.transactions = []
|
|
|
|
def is_valid(self):
|
|
'''
|
|
Final check: ok if calculated end_balance and parsed end_balance are
|
|
identical and perform a heuristic check on the transactions.
|
|
'''
|
|
if any([x for x in self.transactions if not x.is_valid()]):
|
|
return False
|
|
check = float(self.start_balance)
|
|
for transaction in self.transactions:
|
|
check += float(transaction.transferred_amount)
|
|
return abs(check - float(self.end_balance)) < 0.0001
|
|
|
|
class mem_bank_transaction(object):
|
|
'''
|
|
A mem_bank_transaction is a real life copy of a bank transfer. Mapping to
|
|
OpenERP moves and linking to invoices and the like is done afterwards.
|
|
'''
|
|
# Lock attributes to enable parsers to trigger non-conformity faults
|
|
__slots__ = [
|
|
'id', 'local_account', 'local_currency', 'execution_date',
|
|
'effective_date', 'remote_owner', 'remote_account',
|
|
'remote_currency', 'transferred_amount', 'transfer_type',
|
|
'reference', 'message', 'statement_id',
|
|
]
|
|
|
|
# transfer_type's to be used by the business logic.
|
|
# Depending on the type your parser gives a transaction, different
|
|
# behavior can be triggered in the business logic.
|
|
#
|
|
# BANK_COSTS Automated credited costs by the bank.
|
|
# Used to generate an automated invoice from the bank
|
|
# Will be excluded from matching.
|
|
# BANK_TERMINAL A cash withdrawal from a bank terminal.
|
|
# Will be excluded from matching.
|
|
# CHECK A delayed payment. Can be used to trigger extra
|
|
# moves from temporary accounts. (Money away).
|
|
# TODO: No special treatment yet.
|
|
# Will be selected for matching.
|
|
# DIRECT_DEBIT Speaks for itself. When outgoing (credit) and
|
|
# matched, can signal the matched invoice triaged.
|
|
# Will be selected for matching.
|
|
# ORDER Order to the bank. Can work both ways.
|
|
# Will be selected for matching.
|
|
# PAYMENT_BATCH A payment batch. Can work in both directions.
|
|
# Incoming payment batch transactions can't be
|
|
# matched with payments, outgoing can.
|
|
# Will be selected for matching.
|
|
# PAYMENT_TERMINAL A payment with debit/credit card in a (web)shop
|
|
# Invoice numbers and other hard criteria are most
|
|
# likely missing.
|
|
# Will be selected for matching
|
|
# PERIODIC_ORDER An automated payment by the bank on your behalf.
|
|
# Always outgoing.
|
|
# Will be selected for matching.
|
|
#
|
|
# Perhaps more will follow.
|
|
#
|
|
# When writing parsers, map other types with similar meaning to these to
|
|
# prevent cluttering the API. For example: the Dutch ING Bank has a
|
|
# transfer type Post Office, meaning a cash withdrawal from one of their
|
|
# agencies. This can be mapped to BANK_TERMINAL without losing any
|
|
# significance for OpenERP.
|
|
|
|
BANK_COSTS = 'BC'
|
|
BANK_TERMINAL = 'BT'
|
|
CHECK = 'CK'
|
|
DIRECT_DEBIT = 'DD'
|
|
ORDER = 'DO'
|
|
PAYMENT_BATCH = 'PB'
|
|
PAYMENT_TERMINAL = 'PT'
|
|
PERIODIC_ORDER = 'PO'
|
|
|
|
types = [
|
|
BANK_COSTS, BANK_TERMINAL, CHECK, DIRECT_DEBIT, ORDER,
|
|
PAYMENT_BATCH, PAYMENT_TERMINAL, PERIODIC_ORDER,
|
|
]
|
|
type_map = {
|
|
# This can be a translation map of type versus bank type. Each key is
|
|
# the real transfer_type, each value is the mem_bank_transaction.type
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(mem_bank_transaction, self).__init__(*args, **kwargs)
|
|
self.id = ''
|
|
self.local_account = ''
|
|
self.local_currency = ''
|
|
self.execution_date = ''
|
|
self.effective_date = ''
|
|
self.remote_account = ''
|
|
self.remote_owner = ''
|
|
self.remote_currency = ''
|
|
self.transferred_amount = ''
|
|
self.transfer_type = ''
|
|
self.reference = ''
|
|
self.message = ''
|
|
self.statement_id = ''
|
|
|
|
def copy(self):
|
|
'''
|
|
Return a copy of self
|
|
'''
|
|
retval = mem_bank_transaction()
|
|
for attr in self.__slots__:
|
|
setattr(retval, attr, getattr(self, attr))
|
|
return retval
|
|
|
|
def _get_type(self):
|
|
if self.transfer_type in self.type_map:
|
|
return self.type_map[self.transfer_type]
|
|
return self.transfer_type
|
|
|
|
def _set_type(self, value):
|
|
if value in self.types:
|
|
self.transfer_type = value
|
|
else:
|
|
raise ValueError, _('Invalid value for transfer_type')
|
|
|
|
type = property(_get_type, _set_type)
|
|
|
|
def is_valid(self):
|
|
'''
|
|
Heuristic check: at least id, execution_date, remote_account and
|
|
transferred_amount should be filled to create a valid transfer.
|
|
'''
|
|
return (self.execution_date and self.remote_account
|
|
and self.transferred_amount and True) or False
|
|
|
|
class parser_type(type):
|
|
'''
|
|
Meta annex factory class for house keeping and collecting parsers.
|
|
'''
|
|
parsers = []
|
|
parser_by_name = {}
|
|
parser_by_code = {}
|
|
parser_by_classname = {}
|
|
|
|
def __new__(metacls, clsname, bases, clsdict):
|
|
newcls = type.__new__(metacls, clsname, bases, clsdict)
|
|
if 'name' in clsdict and newcls.name:
|
|
metacls.parsers.append(newcls)
|
|
metacls.parser_by_name[newcls.name] = newcls
|
|
metacls.parser_by_code[newcls.code] = newcls
|
|
metacls.parser_by_classname[clsname] = newcls
|
|
return newcls
|
|
|
|
@classmethod
|
|
def get_parser_types(cls, sort='name'):
|
|
'''
|
|
Return the parser class names, optional in sort order.
|
|
'''
|
|
if sort == 'name':
|
|
keys = cls.parser_by_name.keys()
|
|
parsers = cls.parser_by_name
|
|
else:
|
|
keys = cls.parser_by_code.itervalues()
|
|
parsers = cls.parser_by_code
|
|
keys.sort()
|
|
return [(parsers[x].code, parsers[x].name) for x in keys]
|
|
|
|
def create_parser(code):
|
|
if code in parser_type.parser_by_code:
|
|
return parser_type.parser_by_code[code]()
|
|
return None
|
|
|
|
class parser(object):
|
|
'''
|
|
A parser delivers the interface for any parser object. Inherit from
|
|
it to implement your own.
|
|
You should at least implement the following at the class level:
|
|
name -> the name of the parser, shown to the user and
|
|
translatable.
|
|
code -> the identifier you care to give it. Not translatable
|
|
doc -> the description of the identifier. Shown to the user.
|
|
Translatable.
|
|
|
|
parse -> the method for the actual parsing.
|
|
'''
|
|
__metaclass__ = parser_type
|
|
name = None
|
|
code = None
|
|
doc = __doc__
|
|
|
|
def parse(self, data):
|
|
'''
|
|
Parse data.
|
|
|
|
data is a raw in memory file object. You have to split it in
|
|
whatever chunks you see fit for parsing. It should return a list
|
|
of mem_bank_statement objects. Every mem_bank_statement object
|
|
should contain a list of mem_bank_transaction objects.
|
|
|
|
For identification purposes, don't invent numbering of the transaction
|
|
numbers or bank statements ids on your own - stick with those provided
|
|
by your bank. Doing so enables the users to re-load old transaction
|
|
files without creating multiple identical bank statements.
|
|
'''
|
|
raise NotImplementedError(
|
|
_('This is a stub. Please implement your own.')
|
|
)
|
|
|
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|