1
# -*- encoding: utf-8 -*-
2
##############################################################################
4
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
22
from tools.translate import _
24
class mem_bank_statement(object):
26
A mem_bank_statement is a real life projection of a bank statement paper
27
containing a report of one or more transactions done. As these reports can
28
contain payments that originate in several accounting periods, period is an
29
attribute of mem_bank_transaction, not of mem_bank_statement.
30
Also note that the statement_id is copied from the bank statement, and not
31
generated from any sequence. This enables us to skip old data in new
34
# Lock attributes to enable parsers to trigger non-conformity faults
36
'start_balance','end_balance', 'date', 'local_account',
37
'local_currency', 'id', 'statements'
39
def __init__(self, *args, **kwargs):
40
super(mem_bank_statement, self).__init__(*args, **kwargs)
42
self.local_account = ''
43
self.local_currency = ''
44
self.start_balance = 0.0
45
self.end_balance = 0.0
47
self.transactions = []
51
Final check: ok if calculated end_balance and parsed end_balance are
52
identical and perform a heuristic check on the transactions.
54
if any([x for x in self.transactions if not x.is_valid()]):
56
check = float(self.start_balance)
57
for transaction in self.transactions:
58
check += float(transaction.transferred_amount)
59
return abs(check - float(self.end_balance)) < 0.0001
61
class mem_bank_transaction(object):
63
A mem_bank_transaction is a real life copy of a bank transfer. Mapping to
64
OpenERP moves and linking to invoices and the like is done afterwards.
66
# Lock attributes to enable parsers to trigger non-conformity faults
68
'id', 'local_account', 'local_currency', 'execution_date',
69
'effective_date', 'remote_owner', 'remote_account',
70
'remote_currency', 'transferred_amount', 'transfer_type',
71
'reference', 'message', 'statement_id',
74
# transfer_type's to be used by the business logic.
75
# Depending on the type your parser gives a transaction, different
76
# behavior can be triggered in the business logic.
78
# BANK_COSTS Automated credited costs by the bank.
79
# Used to generate an automated invoice from the bank
80
# Will be excluded from matching.
81
# BANK_TERMINAL A cash withdrawal from a bank terminal.
82
# Will be excluded from matching.
83
# CHECK A delayed payment. Can be used to trigger extra
84
# moves from temporary accounts. (Money away).
85
# TODO: No special treatment yet.
86
# Will be selected for matching.
87
# DIRECT_DEBIT Speaks for itself. When outgoing (credit) and
88
# matched, can signal the matched invoice triaged.
89
# Will be selected for matching.
90
# ORDER Order to the bank. Can work both ways.
91
# Will be selected for matching.
92
# PAYMENT_BATCH A payment batch. Can work in both directions.
93
# Incoming payment batch transactions can't be
94
# matched with payments, outgoing can.
95
# Will be selected for matching.
96
# PAYMENT_TERMINAL A payment with debit/credit card in a (web)shop
97
# Invoice numbers and other hard criteria are most
99
# Will be selected for matching
100
# PERIODIC_ORDER An automated payment by the bank on your behalf.
102
# Will be selected for matching.
104
# Perhaps more will follow.
106
# When writing parsers, map other types with similar meaning to these to
107
# prevent cluttering the API. For example: the Dutch ING Bank has a
108
# transfer type Post Office, meaning a cash withdrawal from one of their
109
# agencies. This can be mapped to BANK_TERMINAL without losing any
110
# significance for OpenERP.
118
PAYMENT_TERMINAL = 'PT'
119
PERIODIC_ORDER = 'PO'
122
BANK_COSTS, BANK_TERMINAL, CHECK, DIRECT_DEBIT, ORDER,
123
PAYMENT_BATCH, PAYMENT_TERMINAL, PERIODIC_ORDER,
126
# This can be a translation map of type versus bank type. Each key is
127
# the real transfer_type, each value is the mem_bank_transaction.type
130
def __init__(self, *args, **kwargs):
131
super(mem_bank_transaction, self).__init__(*args, **kwargs)
133
self.local_account = ''
134
self.local_currency = ''
135
self.execution_date = ''
136
self.effective_date = ''
137
self.remote_account = ''
138
self.remote_owner = ''
139
self.remote_currency = ''
140
self.transferred_amount = ''
141
self.transfer_type = ''
144
self.statement_id = ''
148
Return a copy of self
150
retval = mem_bank_transaction()
151
for attr in self.__slots__:
152
setattr(retval, attr, getattr(self, attr))
156
if self.transfer_type in self.type_map:
157
return self.type_map[self.transfer_type]
158
return self.transfer_type
160
def _set_type(self, value):
161
if value in self.types:
162
self.transfer_type = value
164
raise ValueError, _('Invalid value for transfer_type')
166
type = property(_get_type, _set_type)
170
Heuristic check: at least id, execution_date, remote_account and
171
transferred_amount should be filled to create a valid transfer.
173
return (self.execution_date and self.remote_account
174
and self.transferred_amount and True) or False
176
class parser_type(type):
178
Meta annex factory class for house keeping and collecting parsers.
183
parser_by_classname = {}
185
def __new__(metacls, clsname, bases, clsdict):
186
newcls = type.__new__(metacls, clsname, bases, clsdict)
187
if 'name' in clsdict and newcls.name:
188
metacls.parsers.append(newcls)
189
metacls.parser_by_name[newcls.name] = newcls
190
metacls.parser_by_code[newcls.code] = newcls
191
metacls.parser_by_classname[clsname] = newcls
195
def get_parser_types(cls, sort='name'):
197
Return the parser class names, optional in sort order.
200
keys = cls.parser_by_name.keys()
201
parsers = cls.parser_by_name
203
keys = cls.parser_by_code.itervalues()
204
parsers = cls.parser_by_code
206
return [(parsers[x].code, parsers[x].name) for x in keys]
208
def create_parser(code):
209
if code in parser_type.parser_by_code:
210
return parser_type.parser_by_code[code]()
213
class parser(object):
215
A parser delivers the interface for any parser object. Inherit from
216
it to implement your own.
217
You should at least implement the following at the class level:
218
name -> the name of the parser, shown to the user and
220
code -> the identifier you care to give it. Not translatable
221
doc -> the description of the identifier. Shown to the user.
224
parse -> the method for the actual parsing.
226
__metaclass__ = parser_type
231
def parse(self, data):
235
data is a raw in memory file object. You have to split it in
236
whatever chunks you see fit for parsing. It should return a list
237
of mem_bank_statement objects. Every mem_bank_statement object
238
should contain a list of mem_bank_transaction objects.
240
For identification purposes, don't invent numbering of the transaction
241
numbers or bank statements ids on your own - stick with those provided
242
by your bank. Doing so enables the users to re-load old transaction
243
files without creating multiple identical bank statements.
245
raise NotImplementedError(
246
_('This is a stub. Please implement your own.')
249
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: