Ronald Portier (Therp BV)
10 years ago
92 changed files with 30403 additions and 0 deletions
-
36__unported__/account_banking/__init__.py
-
105__unported__/account_banking/__openerp__.py
-
683__unported__/account_banking/account_banking.py
-
32__unported__/account_banking/account_banking_demo.xml
-
17__unported__/account_banking/data/account_banking_data.xml
-
1626__unported__/account_banking/i18n/account_banking.pot
-
1677__unported__/account_banking/i18n/da.po
-
759__unported__/account_banking/i18n/da_DK.po
-
1695__unported__/account_banking/i18n/en.po
-
1677__unported__/account_banking/i18n/es_ES.po
-
1689__unported__/account_banking/i18n/hr.po
-
774__unported__/account_banking/i18n/hr_HR.po
-
1693__unported__/account_banking/i18n/hu.po
-
1679__unported__/account_banking/i18n/nb.po
-
1750__unported__/account_banking/i18n/nl.po
-
1677__unported__/account_banking/i18n/pt_BR.po
-
1686__unported__/account_banking/i18n/ro.po
-
773__unported__/account_banking/i18n/ro_RO.po
-
1678__unported__/account_banking/i18n/tr.po
-
766__unported__/account_banking/i18n/tr_TR.po
-
34__unported__/account_banking/migrations/6.1.0.1.81/post-set-statement-line-state.py
-
34__unported__/account_banking/migrations/7.0.0.1/pre-migration.py
-
32__unported__/account_banking/migrations/7.0.0.3/pre-migration.py
-
46__unported__/account_banking/migrations/7.0.0.4/pre-migration.py
-
209__unported__/account_banking/record.py
-
72__unported__/account_banking/res_partner.py
-
92__unported__/account_banking/res_partner_bank.py
-
23__unported__/account_banking/sepa/__init__.py
-
534__unported__/account_banking/sepa/iban.py
-
187__unported__/account_banking/sepa/postalcode.py
-
57__unported__/account_banking/struct.py
-
24__unported__/account_banking/wizard/__init__.py
-
338__unported__/account_banking/wizard/banktools.py
-
208__unported__/account_banking/wizard/link_partner.py
-
61__unported__/account_banking/wizard/link_partner.xml
-
18__unported__/account_banking/workflow/account_invoice.xml
-
29__unported__/account_banking_fi_patu/__init__.py
-
43__unported__/account_banking_fi_patu/__openerp__.py
-
31__unported__/account_banking_fi_patu/i18n/account_banking_fi_patu.pot
-
37__unported__/account_banking_fi_patu/i18n/nl.po
-
33__unported__/account_banking_fi_patu/i18n/pt_BR.po
-
250__unported__/account_banking_fi_patu/parser.py
-
132__unported__/account_banking_fi_patu/patu.py
-
2__unported__/account_banking_nl_abnamro/__init__.py
-
42__unported__/account_banking_nl_abnamro/__openerp__.py
-
394__unported__/account_banking_nl_abnamro/abnamro.py
-
89__unported__/account_banking_nl_abnamro/i18n/account_banking_nl_abnamro.pot
-
94__unported__/account_banking_nl_abnamro/i18n/en.po
-
98__unported__/account_banking_nl_abnamro/i18n/nl.po
-
28__unported__/account_banking_nl_girotel/__init__.py
-
36__unported__/account_banking_nl_girotel/__openerp__.py
-
399__unported__/account_banking_nl_girotel/girotel.py
-
36__unported__/account_banking_nl_girotel/i18n/account_banking_nl_girotel.pot
-
37__unported__/account_banking_nl_girotel/i18n/en.po
-
39__unported__/account_banking_nl_girotel/i18n/nl.po
-
38__unported__/account_banking_nl_girotel/i18n/pt_BR.po
-
2__unported__/account_banking_nl_ing/__init__.py
-
51__unported__/account_banking_nl_ing/__openerp__.py
-
38__unported__/account_banking_nl_ing/i18n/account_banking_nl_ing.pot
-
41__unported__/account_banking_nl_ing/i18n/nl.po
-
39__unported__/account_banking_nl_ing/i18n/pt_BR.po
-
288__unported__/account_banking_nl_ing/ing.py
-
28__unported__/account_banking_nl_multibank/__init__.py
-
36__unported__/account_banking_nl_multibank/__openerp__.py
-
39__unported__/account_banking_nl_multibank/i18n/account_banking_nl_multibank.pot
-
39__unported__/account_banking_nl_multibank/i18n/en.po
-
43__unported__/account_banking_nl_multibank/i18n/nl.po
-
40__unported__/account_banking_nl_multibank/i18n/pt_BR.po
-
333__unported__/account_banking_nl_multibank/multibank.py
-
30__unported__/account_banking_nl_triodos/__init__.py
-
45__unported__/account_banking_nl_triodos/__openerp__.py
-
38__unported__/account_banking_nl_triodos/i18n/account_banking_nl_triodos.pot
-
43__unported__/account_banking_nl_triodos/i18n/en.po
-
43__unported__/account_banking_nl_triodos/i18n/nl.po
-
40__unported__/account_banking_nl_triodos/i18n/pt_BR.po
-
229__unported__/account_banking_nl_triodos/triodos.py
-
26__unported__/account_banking_uk_hsbc/__init__.py
-
52__unported__/account_banking_uk_hsbc/__openerp__.py
-
141__unported__/account_banking_uk_hsbc/account_banking_uk_hsbc.py
-
85__unported__/account_banking_uk_hsbc/account_banking_uk_hsbc.xml
-
29__unported__/account_banking_uk_hsbc/data/banking_export_hsbc.xml
-
49__unported__/account_banking_uk_hsbc/hsbc_clientid.py
-
76__unported__/account_banking_uk_hsbc/hsbc_clientid_view.xml
-
202__unported__/account_banking_uk_hsbc/hsbc_mt940.py
-
287__unported__/account_banking_uk_hsbc/i18n/account_banking_uk_hsbc.pot
-
166__unported__/account_banking_uk_hsbc/mt940_parser.py
-
3__unported__/account_banking_uk_hsbc/security/ir.model.access.csv
-
23__unported__/account_banking_uk_hsbc/wizard/__init__.py
-
428__unported__/account_banking_uk_hsbc/wizard/export_hsbc.py
-
37__unported__/account_banking_uk_hsbc/wizard/export_hsbc_view.xml
-
686__unported__/account_banking_uk_hsbc/wizard/paymul.py
-
300__unported__/account_banking_uk_hsbc/wizard/paymul_test.py
@ -0,0 +1,36 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import sepa |
|||
import record |
|||
import banking_import_transaction |
|||
import account_banking |
|||
import parsers |
|||
import wizard |
|||
import res_partner |
|||
import res_bank |
|||
import res_partner_bank |
@ -0,0 +1,105 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# (C) 2011 Therp BV (<http://therp.nl>). |
|||
# (C) 2011 Smile (<http://smile.fr>). |
|||
# |
|||
# All other contributions are (C) by their respective contributors |
|||
# |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'Account Banking', |
|||
'version': '0.4', |
|||
'license': 'AGPL-3', |
|||
'author': 'Banking addons community', |
|||
'website': 'https://launchpad.net/banking-addons', |
|||
'category': 'Banking addons', |
|||
'depends': [ |
|||
'account_voucher', |
|||
], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'data/account_banking_data.xml', |
|||
'wizard/bank_import_view.xml', |
|||
'account_banking_view.xml', |
|||
'wizard/banking_transaction_wizard.xml', |
|||
'wizard/link_partner.xml', |
|||
'workflow/account_invoice.xml', |
|||
], |
|||
'js': [ |
|||
'static/src/js/account_banking.js', |
|||
], |
|||
'description': ''' |
|||
Module to do banking. |
|||
|
|||
This modules tries to combine all current banking import and export |
|||
schemes. Rationale for this is that it is quite common to have foreign |
|||
bank account numbers next to national bank account numbers. The current |
|||
approach, which hides the national banking interface schemes in the |
|||
l10n_xxx modules, makes it very difficult to use these simultanious. |
|||
A more banking oriented approach seems more logical and cleaner. |
|||
|
|||
Changes to default OpenERP: |
|||
|
|||
* Puts focus on the real life messaging with banks: |
|||
+ Bank statement lines upgraded to independent bank transactions. |
|||
+ Banking statements have no special accountancy meaning, they're just |
|||
message envelopes for a number of bank transactions. |
|||
+ Bank statements can be either encoded by hand to reflect the document |
|||
version of Bank Statements, or created as an optional side effect of |
|||
importing Bank Transactions. |
|||
|
|||
* Preparations for SEPA: |
|||
+ IBAN accounts are the standard in the SEPA countries |
|||
+ local accounts are derived from SEPA (excluding Turkey) but are |
|||
considered to be identical to the corresponding SEPA account. |
|||
+ Banks are identified with either Country + Bank code + Branch code or |
|||
BIC |
|||
+ Each bank can have its own pace in introducing SEPA into their |
|||
communication with their customers. |
|||
+ National online databases can be used to convert BBAN's to IBAN's. |
|||
+ The SWIFT database is consulted for bank information. |
|||
|
|||
* Adds dropin extensible import facility for bank communication in: |
|||
- Drop-in input parser development. |
|||
- MultiBank (NL) format transaction files available as |
|||
account_banking_nl_multibank, |
|||
|
|||
* Extends payments for digital banking: |
|||
+ Adapted workflow in payments to reflect banking operations |
|||
+ Relies on account_payment mechanics to extend with export generators. |
|||
- ClieOp3 (NL) payment and direct debit orders files available as |
|||
account_banking_nl_clieop |
|||
|
|||
* Additional features for the import/export mechanism: |
|||
+ Automatic matching and creation of bank accounts, banks and partners, |
|||
during import of statements. |
|||
+ Automatic matching with invoices and payments. |
|||
+ Sound import mechanism, allowing multiple imports of the same |
|||
transactions repeated over multiple files. |
|||
+ Journal configuration per bank account. |
|||
+ Business logic and format parsing strictly separated to ease the |
|||
development of new parsers. |
|||
+ No special configuration needed for the parsers, new parsers are |
|||
recognized and made available at server (re)start. |
|||
''', |
|||
'installable': False, |
|||
'auto_install': False, |
|||
} |
@ -0,0 +1,683 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>). |
|||
# |
|||
# All other contributions are (C) by their respective contributors |
|||
# |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This module shows resemblance to both account_bankimport/bankimport.py, |
|||
account/account_bank_statement.py and account_payment(_export). All hail to |
|||
the makers. account_bankimport is only referenced for their ideas and the |
|||
framework of the filters, which they in their turn seem to have derived |
|||
from account_coda. |
|||
|
|||
Modifications are extensive: |
|||
|
|||
1. In relation to account/account_bank_statement.py: |
|||
account.bank.statement is effectively stripped from its account.period |
|||
association, while account.bank.statement.line is extended with the same |
|||
association, thereby reflecting real world usage of bank.statement as a |
|||
list of bank transactions and bank.statement.line as a bank transaction. |
|||
|
|||
2. In relation to account/account_bankimport: |
|||
All filter objects and extensions to res.company are removed. Instead a |
|||
flexible auto-loading and auto-browsing plugin structure is created, |
|||
whereby business logic and encoding logic are strictly separated. |
|||
Both parsers and business logic are rewritten from scratch. |
|||
|
|||
The association of account.journal with res.company is replaced by an |
|||
association of account.journal with res.partner.bank, thereby allowing |
|||
multiple bank accounts per company and one journal per bank account. |
|||
|
|||
The imported bank statement file does not result in a single 'bank |
|||
statement', but in a list of bank statements by definition of whatever the |
|||
bank sees as a statement. Every imported bank statement contains at least |
|||
one bank transaction, which is a modded account.bank.statement.line. |
|||
|
|||
3. In relation to account_payment: |
|||
An additional state was inserted between 'open' and 'done', to reflect a |
|||
exported bank orders file which was not reported back through statements. |
|||
The import of statements matches the payments and reconciles them when |
|||
needed, flagging them 'done'. When no export wizards are found, the |
|||
default behavior is to flag the orders as 'sent', not as 'done'. |
|||
Rejected payments from the bank receive on import the status 'rejected'. |
|||
''' |
|||
|
|||
from openerp.osv import orm, fields |
|||
from openerp.osv.osv import except_osv |
|||
from openerp.tools.translate import _ |
|||
from openerp import netsvc |
|||
from openerp.addons.decimal_precision import decimal_precision as dp |
|||
|
|||
|
|||
class account_banking_account_settings(orm.Model): |
|||
'''Default Journal for Bank Account''' |
|||
_name = 'account.banking.account.settings' |
|||
_description = __doc__ |
|||
_columns = { |
|||
'company_id': fields.many2one('res.company', 'Company', select=True, |
|||
required=True), |
|||
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account', |
|||
select=True, required=True), |
|||
'journal_id': fields.many2one('account.journal', 'Journal', |
|||
required=True), |
|||
'partner_id': fields.related( |
|||
'company_id', 'partner_id', |
|||
type='many2one', relation='res.partner', |
|||
string='Partner'), |
|||
'default_credit_account_id': fields.many2one( |
|||
'account.account', 'Default credit account', select=True, |
|||
help=('The account to use when an unexpected payment was signaled.' |
|||
' This can happen when a direct debit payment is cancelled ' |
|||
'by a customer, or when no matching payment can be found. ' |
|||
'Mind that you can correct movements before confirming them.' |
|||
), |
|||
required=True |
|||
), |
|||
'default_debit_account_id': fields.many2one( |
|||
'account.account', 'Default debit account', |
|||
select=True, required=True, |
|||
help=('The account to use when an unexpected payment is received. ' |
|||
'This can be needed when a customer pays in advance or when ' |
|||
'no matching invoice can be found. Mind that you can ' |
|||
'correct movements before confirming them.' |
|||
), |
|||
), |
|||
'costs_account_id': fields.many2one( |
|||
'account.account', 'Bank Costs Account', select=True, |
|||
help=('The account to use when the bank invoices its own costs. ' |
|||
'Leave it blank to disable automatic invoice generation ' |
|||
'on bank costs.' |
|||
), |
|||
), |
|||
'invoice_journal_id': fields.many2one( |
|||
'account.journal', 'Costs Journal', |
|||
help=('This is the journal used to create invoices for bank costs.' |
|||
), |
|||
), |
|||
'bank_partner_id': fields.many2one( |
|||
'res.partner', 'Bank Partner', |
|||
help=('The partner to use for bank costs. Banks are not partners ' |
|||
'by default. You will most likely have to create one.' |
|||
), |
|||
), |
|||
|
|||
} |
|||
|
|||
def _default_company(self, cr, uid, context=None): |
|||
""" |
|||
Return the user's company or the first company found |
|||
in the database |
|||
""" |
|||
user = self.pool.get('res.users').read( |
|||
cr, uid, uid, ['company_id'], context=context) |
|||
if user['company_id']: |
|||
return user['company_id'][0] |
|||
return self.pool.get('res.company').search( |
|||
cr, uid, [('parent_id', '=', False)])[0] |
|||
|
|||
def _default_partner_id(self, cr, uid, context=None, company_id=False): |
|||
if not company_id: |
|||
company_id = self._default_company(cr, uid, context=context) |
|||
return self.pool.get('res.company').read( |
|||
cr, uid, company_id, ['partner_id'], |
|||
context=context)['partner_id'][0] |
|||
|
|||
def _default_journal(self, cr, uid, context=None, company_id=False): |
|||
if not company_id: |
|||
company_id = self._default_company(cr, uid, context=context) |
|||
journal_ids = self.pool.get('account.journal').search( |
|||
cr, uid, [('type', '=', 'bank'), ('company_id', '=', company_id)]) |
|||
return journal_ids and journal_ids[0] or False |
|||
|
|||
def _default_partner_bank_id( |
|||
self, cr, uid, context=None, company_id=False): |
|||
if not company_id: |
|||
company_id = self._default_company(cr, uid, context=context) |
|||
partner_id = self.pool.get('res.company').read( |
|||
cr, uid, company_id, ['partner_id'], |
|||
context=context)['partner_id'][0] |
|||
bank_ids = self.pool.get('res.partner.bank').search( |
|||
cr, uid, [('partner_id', '=', partner_id)], context=context) |
|||
return bank_ids and bank_ids[0] or False |
|||
|
|||
def _default_debit_account_id( |
|||
self, cr, uid, context=None, company_id=False): |
|||
localcontext = context and context.copy() or {} |
|||
localcontext['force_company'] = ( |
|||
company_id or self._default_company(cr, uid, context=context)) |
|||
account_def = self.pool.get('ir.property').get( |
|||
cr, uid, 'property_account_receivable', |
|||
'res.partner', context=localcontext) |
|||
return account_def and account_def.id or False |
|||
|
|||
def _default_credit_account_id( |
|||
self, cr, uid, context=None, company_id=False): |
|||
localcontext = context and context.copy() or {} |
|||
localcontext['force_company'] = ( |
|||
company_id or self._default_company(cr, uid, context=context)) |
|||
account_def = self.pool.get('ir.property').get( |
|||
cr, uid, 'property_account_payable', |
|||
'res.partner', context=localcontext) |
|||
return account_def and account_def.id or False |
|||
|
|||
def find(self, cr, uid, journal_id, partner_bank_id=False, context=None): |
|||
domain = [('journal_id', '=', journal_id)] |
|||
if partner_bank_id: |
|||
domain.append(('partner_bank_id', '=', partner_bank_id)) |
|||
return self.search(cr, uid, domain, context=context) |
|||
|
|||
def onchange_partner_bank_id( |
|||
self, cr, uid, ids, partner_bank_id, context=None): |
|||
values = {} |
|||
if partner_bank_id: |
|||
bank = self.pool.get('res.partner.bank').read( |
|||
cr, uid, partner_bank_id, ['journal_id'], context=context) |
|||
if bank['journal_id']: |
|||
values['journal_id'] = bank['journal_id'][0] |
|||
return {'value': values} |
|||
|
|||
def onchange_company_id( |
|||
self, cr, uid, ids, company_id=False, context=None): |
|||
if not company_id: |
|||
return {} |
|||
result = { |
|||
'partner_id': self._default_partner_id( |
|||
cr, uid, company_id=company_id, context=context), |
|||
'journal_id': self._default_journal( |
|||
cr, uid, company_id=company_id, context=context), |
|||
'default_debit_account_id': self._default_debit_account_id( |
|||
cr, uid, company_id=company_id, context=context), |
|||
'default_credit_account_id': self._default_credit_account_id( |
|||
cr, uid, company_id=company_id, context=context), |
|||
} |
|||
return {'value': result} |
|||
|
|||
_defaults = { |
|||
'company_id': _default_company, |
|||
'partner_id': _default_partner_id, |
|||
'journal_id': _default_journal, |
|||
'default_debit_account_id': _default_debit_account_id, |
|||
'default_credit_account_id': _default_credit_account_id, |
|||
'partner_bank_id': _default_partner_bank_id, |
|||
} |
|||
account_banking_account_settings() |
|||
|
|||
|
|||
class account_banking_imported_file(orm.Model): |
|||
'''Imported Bank Statements File''' |
|||
_name = 'account.banking.imported.file' |
|||
_description = __doc__ |
|||
_rec_name = 'date' |
|||
_columns = { |
|||
'company_id': fields.many2one( |
|||
'res.company', |
|||
'Company', |
|||
select=True, |
|||
readonly=True, |
|||
), |
|||
'date': fields.datetime( |
|||
'Import Date', |
|||
readonly=True, |
|||
select=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'format': fields.char( |
|||
'File Format', |
|||
size=20, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'file': fields.binary( |
|||
'Raw Data', |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'file_name': fields.char('File name', size=256), |
|||
'log': fields.text( |
|||
'Import Log', |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'user_id': fields.many2one( |
|||
'res.users', |
|||
'Responsible User', |
|||
readonly=True, |
|||
select=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'state': fields.selection( |
|||
[ |
|||
('unfinished', 'Unfinished'), |
|||
('error', 'Error'), |
|||
('review', 'Review'), |
|||
('ready', 'Finished'), |
|||
], |
|||
'State', |
|||
select=True, |
|||
readonly=True, |
|||
), |
|||
'statement_ids': fields.one2many( |
|||
'account.bank.statement', |
|||
'banking_id', |
|||
'Statements', |
|||
readonly=False, |
|||
), |
|||
} |
|||
_defaults = { |
|||
'date': fields.date.context_today, |
|||
'user_id': lambda self, cr, uid, context: uid, |
|||
} |
|||
account_banking_imported_file() |
|||
|
|||
|
|||
class account_bank_statement(orm.Model): |
|||
''' |
|||
Implement changes to this model for the following features: |
|||
|
|||
* bank statement lines have their own period_id, derived from |
|||
their effective date. The period and date are propagated to |
|||
the move lines created from each statement line |
|||
* bank statement lines have their own state. When a statement |
|||
is confirmed, all lines are confirmed too. When a statement |
|||
is reopened, lines remain confirmed until reopened individually. |
|||
* upon confirmation of a statement line, the move line is |
|||
created and reconciled according to the matched entry(/ies) |
|||
''' |
|||
_inherit = 'account.bank.statement' |
|||
|
|||
_columns = { |
|||
'period_id': fields.many2one( |
|||
'account.period', |
|||
'Period', |
|||
required=False, |
|||
readonly=True, |
|||
), |
|||
'banking_id': fields.many2one( |
|||
'account.banking.imported.file', |
|||
'Imported File', |
|||
readonly=True, |
|||
), |
|||
} |
|||
|
|||
_defaults = { |
|||
'period_id': False, |
|||
} |
|||
|
|||
def _check_company_id(self, cr, uid, ids, context=None): |
|||
""" |
|||
Adapt this constraint method from the account module to reflect the |
|||
move of period_id to the statement line: also check the periods of the |
|||
lines. Update the statement period if it does not have one yet. |
|||
Don't call super, because its check is integrated below and |
|||
it will break if a statement does not have any lines yet and |
|||
therefore may not have a period. |
|||
""" |
|||
for statement in self.browse(cr, uid, ids, context=context): |
|||
if (statement.period_id and |
|||
statement.company_id != statement.period_id.company_id): |
|||
return False |
|||
for line in statement.line_ids: |
|||
if (line.period_id and |
|||
statement.company_id != line.period_id.company_id): |
|||
return False |
|||
if not statement.period_id: |
|||
statement.write({'period_id': line.period_id.id}) |
|||
statement.refresh() |
|||
return True |
|||
|
|||
# Redefine the constraint, or it still refer to the original method |
|||
_constraints = [ |
|||
(_check_company_id, |
|||
'The journal and period chosen have to belong to the same company.', |
|||
['journal_id', 'period_id']), |
|||
] |
|||
|
|||
def _get_period(self, cr, uid, date=False, context=None): |
|||
""" |
|||
Used in statement line's _defaults, so it is always triggered |
|||
on installation or module upgrade even if there are no records |
|||
without a value. For that reason, we need |
|||
to be tolerant and allow for the situation in which no period |
|||
exists for the current date (i.e. when no date is specified). |
|||
|
|||
Cannot be used directly as a defaults method due to lp:1296229 |
|||
""" |
|||
local_ctx = dict(context or {}, account_period_prefer_normal=True) |
|||
try: |
|||
return self.pool.get('account.period').find( |
|||
cr, uid, dt=date, context=local_ctx)[0] |
|||
except except_osv: |
|||
if date: |
|||
raise |
|||
return False |
|||
|
|||
def _prepare_move( |
|||
self, cr, uid, st_line, st_line_number, context=None): |
|||
""" |
|||
Add the statement line's period to the move, overwriting |
|||
the period on the statement |
|||
""" |
|||
res = super(account_bank_statement, self)._prepare_move( |
|||
cr, uid, st_line, st_line_number, context=context) |
|||
if context and context.get('period_id'): |
|||
res['period_id'] = context['period_id'] |
|||
return res |
|||
|
|||
def _prepare_move_line_vals( |
|||
self, cr, uid, st_line, move_id, debit, credit, currency_id=False, |
|||
amount_currency=False, account_id=False, analytic_id=False, |
|||
partner_id=False, context=None): |
|||
""" |
|||
Add the statement line's period to the move lines, overwriting |
|||
the period on the statement |
|||
""" |
|||
res = super(account_bank_statement, self)._prepare_move_line_vals( |
|||
cr, uid, st_line, move_id, debit, credit, currency_id=currency_id, |
|||
amount_currency=amount_currency, account_id=account_id, |
|||
analytic_id=analytic_id, partner_id=partner_id, context=context) |
|||
if context and context.get('period_id'): |
|||
res['period_id'] = context['period_id'] |
|||
return res |
|||
|
|||
def create_move_from_st_line(self, cr, uid, st_line_id, |
|||
company_currency_id, st_line_number, |
|||
context=None): |
|||
if context is None: |
|||
context = {} |
|||
account_move_obj = self.pool.get('account.move') |
|||
account_move_line_obj = self.pool.get('account.move.line') |
|||
account_bank_statement_line_obj = self.pool.get( |
|||
'account.bank.statement.line') |
|||
st_line = account_bank_statement_line_obj.browse( |
|||
cr, uid, st_line_id, context=context) |
|||
|
|||
# Take period from statement line and write to context |
|||
# this will be picked up by the _prepare_move* methods |
|||
period_id = self._get_period( |
|||
cr, uid, date=st_line.date, context=context) |
|||
localctx = context.copy() |
|||
localctx['period_id'] = period_id |
|||
|
|||
# Write date & period on the voucher, delegate to account_voucher's |
|||
# override of this method. Then post the related move and return. |
|||
if st_line.voucher_id: |
|||
voucher_pool = self.pool.get('account.voucher') |
|||
voucher_pool.write( |
|||
cr, uid, [st_line.voucher_id.id], { |
|||
'date': st_line.date, |
|||
'period_id': period_id, |
|||
}, context=context) |
|||
|
|||
res = super(account_bank_statement, self).create_move_from_st_line( |
|||
cr, uid, st_line_id, company_currency_id, st_line_number, |
|||
context=localctx) |
|||
|
|||
st_line.refresh() |
|||
if st_line.voucher_id: |
|||
if not st_line.voucher_id.journal_id.entry_posted: |
|||
account_move_obj.post( |
|||
cr, uid, [st_line.voucher_id.move_id.id], context={}) |
|||
else: |
|||
# Write stored reconcile_id and pay invoices through workflow |
|||
if st_line.reconcile_id: |
|||
move_ids = [move.id for move in st_line.move_ids] |
|||
torec = account_move_line_obj.search( |
|||
cr, uid, [ |
|||
('move_id', 'in', move_ids), |
|||
('account_id', '=', st_line.account_id.id)], |
|||
context=context) |
|||
account_move_line_obj.write(cr, uid, torec, { |
|||
(st_line.reconcile_id.line_partial_ids |
|||
and 'reconcile_partial_id' |
|||
or 'reconcile_id'): st_line.reconcile_id.id |
|||
}, context=context) |
|||
for move_line in (st_line.reconcile_id.line_id or []) + ( |
|||
st_line.reconcile_id.line_partial_ids or []): |
|||
netsvc.LocalService("workflow").trg_trigger( |
|||
uid, 'account.move.line', move_line.id, cr) |
|||
return res |
|||
|
|||
def button_confirm_bank(self, cr, uid, ids, context=None): |
|||
""" |
|||
Assign journal sequence to statements without a name |
|||
""" |
|||
if context is None: |
|||
context = {} |
|||
obj_seq = self.pool.get('ir.sequence') |
|||
if ids and isinstance(ids, (int, long)): |
|||
ids = [ids] |
|||
noname_ids = self.search( |
|||
cr, uid, [('id', 'in', ids), ('name', '=', '/')], |
|||
context=context) |
|||
for st in self.browse(cr, uid, noname_ids, context=context): |
|||
if st.journal_id.sequence_id: |
|||
period_id = self._get_period( |
|||
cr, uid, date=st.date, context=context) |
|||
year = self.pool.get('account.period').browse( |
|||
cr, uid, period_id, context=context).fiscalyear_id.id |
|||
c = {'fiscalyear_id': year} |
|||
st_number = obj_seq.get_id( |
|||
cr, uid, st.journal_id.sequence_id.id, context=c) |
|||
self.write( |
|||
cr, uid, ids, {'name': st_number}, context=context) |
|||
|
|||
return super(account_bank_statement, self).button_confirm_bank( |
|||
cr, uid, ids, context) |
|||
|
|||
|
|||
class account_voucher(orm.Model): |
|||
_inherit = 'account.voucher' |
|||
|
|||
def _get_period(self, cr, uid, context=None): |
|||
if context is None: |
|||
context = {} |
|||
if not context.get('period_id') and context.get('move_line_ids'): |
|||
move_line = self.pool.get('account.move.line').browse( |
|||
cr, uid, context.get('move_line_ids')[0], context=context) |
|||
return move_line.period_id.id |
|||
return super(account_voucher, self)._get_period(cr, uid, context) |
|||
|
|||
|
|||
class account_bank_statement_line(orm.Model): |
|||
''' |
|||
Extension on basic class: |
|||
1. Extra links to account.period and res.partner.bank for tracing and |
|||
matching. |
|||
2. Extra 'trans' field to carry the transaction id of the bank. |
|||
3. Readonly states for most fields except when in draft. |
|||
''' |
|||
_inherit = 'account.bank.statement.line' |
|||
_description = 'Bank Transaction' |
|||
|
|||
def _get_period(self, cr, uid, date=False, context=None): |
|||
return self.pool['account.bank.statement']._get_period( |
|||
cr, uid, date=date, context=context) |
|||
|
|||
def _get_period_context(self, cr, uid, context=None): |
|||
""" |
|||
Workaround for lp:1296229, context is passed positionally |
|||
""" |
|||
return self._get_period(cr, uid, context=context) |
|||
|
|||
def _get_currency(self, cr, uid, context=None): |
|||
''' |
|||
Get the default currency (required to allow other modules to function, |
|||
which assume currency to be a calculated field and thus optional) |
|||
Remark: this is only a fallback as the real default is in the journal, |
|||
which is inaccessible from within this method. |
|||
''' |
|||
res_users_obj = self.pool.get('res.users') |
|||
return res_users_obj.browse( |
|||
cr, uid, uid, context=context).company_id.currency_id.id |
|||
|
|||
def _get_invoice_id(self, cr, uid, ids, name, args, context=None): |
|||
res = {} |
|||
for st_line in self.browse(cr, uid, ids, context): |
|||
res[st_line.id] = False |
|||
for move_line in ( |
|||
st_line.reconcile_id and |
|||
(st_line.reconcile_id.line_id or |
|||
st_line.reconcile_id.line_partial_ids) or |
|||
st_line.import_transaction_id and |
|||
st_line.import_transaction_id.move_line_id and |
|||
[st_line.import_transaction_id.move_line_id] or []): |
|||
if move_line.invoice: |
|||
res[st_line.id] = move_line.invoice.id |
|||
continue |
|||
return res |
|||
|
|||
_columns = { |
|||
# Redefines. Todo: refactor away to view attrs |
|||
'amount': fields.float( |
|||
'Amount', |
|||
readonly=True, |
|||
digits_compute=dp.get_precision('Account'), |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'ref': fields.char( |
|||
'Ref.', |
|||
size=32, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'name': fields.char( |
|||
'Name', |
|||
size=64, |
|||
required=False, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'date': fields.date( |
|||
'Date', |
|||
required=True, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
# New columns |
|||
'trans': fields.char( |
|||
'Bank Transaction ID', |
|||
size=15, |
|||
required=False, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'partner_bank_id': fields.many2one( |
|||
'res.partner.bank', |
|||
'Bank Account', |
|||
required=False, |
|||
readonly=True, |
|||
states={'draft': [('readonly', False)]}, |
|||
), |
|||
'period_id': fields.many2one( |
|||
'account.period', |
|||
'Period', |
|||
required=True, |
|||
states={'confirmed': [('readonly', True)]}, |
|||
), |
|||
'currency': fields.many2one( |
|||
'res.currency', |
|||
'Currency', |
|||
required=True, |
|||
states={'confirmed': [('readonly', True)]}, |
|||
), |
|||
'reconcile_id': fields.many2one( |
|||
'account.move.reconcile', |
|||
'Reconciliation', |
|||
readonly=True, |
|||
), |
|||
'invoice_id': fields.function( |
|||
_get_invoice_id, |
|||
method=True, |
|||
string='Linked Invoice', |
|||
type='many2one', |
|||
relation='account.invoice', |
|||
), |
|||
} |
|||
|
|||
_defaults = { |
|||
'period_id': _get_period_context, |
|||
'currency': _get_currency, |
|||
} |
|||
|
|||
|
|||
class invoice(orm.Model): |
|||
''' |
|||
Create other reference types as well. |
|||
|
|||
Descendant classes can extend this function to add more reference |
|||
types, ie. |
|||
|
|||
def _get_reference_type(self, cr, uid, context=None): |
|||
return super(my_class, self)._get_reference_type(cr, uid, |
|||
context=context) + [('my_ref', _('My reference')] |
|||
|
|||
Don't forget to redefine the column "reference_type" as below or |
|||
your method will never be triggered. |
|||
|
|||
TODO: move 'structured' part to account_banking_payment module |
|||
where it belongs |
|||
''' |
|||
_inherit = 'account.invoice' |
|||
|
|||
def test_undo_paid(self, cr, uid, ids, context=None): |
|||
""" |
|||
Called from the workflow. Used to unset paid state on |
|||
invoices that were paid with bank transfers which are being cancelled |
|||
""" |
|||
for invoice in self.read(cr, uid, ids, ['reconciled'], context): |
|||
if invoice['reconciled']: |
|||
return False |
|||
return True |
|||
|
|||
def _get_reference_type(self, cr, uid, context=None): |
|||
''' |
|||
Return the list of reference types |
|||
''' |
|||
return [('none', _('Free Reference')), |
|||
('structured', _('Structured Reference')), |
|||
] |
|||
|
|||
_columns = { |
|||
'reference_type': fields.selection(_get_reference_type, |
|||
'Reference Type', required=True |
|||
) |
|||
} |
|||
|
|||
|
|||
class account_move_line(orm.Model): |
|||
_inherit = "account.move.line" |
|||
|
|||
def get_balance(self, cr, uid, ids, context=None): |
|||
""" |
|||
Return the balance of any set of move lines. |
|||
|
|||
Not to be confused with the 'balance' field on this model, which |
|||
returns the account balance that the move line applies to. |
|||
""" |
|||
total = 0.0 |
|||
if not ids: |
|||
return total |
|||
for line in self.read( |
|||
cr, uid, ids, ['debit', 'credit'], context=context): |
|||
total += (line['debit'] or 0.0) - (line['credit'] or 0.0) |
|||
return total |
@ -0,0 +1,32 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data noupdate="1"> |
|||
<record model="res.partner" id="partner_demo1"> |
|||
<field name="name">Tiny S.p.r.l</field> |
|||
</record> |
|||
<record model="res.partner.bank" id="partner_bank1"> |
|||
<field name="acc_number">301915554082</field> |
|||
<field name="state">bank</field> |
|||
<field name="partner_id" ref="partner_demo1"></field> |
|||
</record> |
|||
|
|||
<record model="res.partner" id="partner_demo2"> |
|||
<field name="name">The-design Company</field> |
|||
</record> |
|||
<record model="res.partner.bank" id="partner_bank2"> |
|||
<field name="acc_number">050000000017</field> |
|||
<field name="state">iban</field> |
|||
<field name="partner_id" ref="partner_demo2"></field> |
|||
</record> |
|||
|
|||
<!-- <record model="res.partner.bank" id="partner_agrolait"> |
|||
<field name="acc_number">301915554082</field> |
|||
<field name="partner_id" ref="base.res_partner_agrolait"/> |
|||
</record> |
|||
|
|||
<record model="res.partner.bank" id="res_partner_10"> |
|||
<field name="acc_number">050000000017</field> |
|||
<field name="partner_id" ref="base.res_partner_10"/> |
|||
</record>--> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<!-- Unset readonly state of country_id for ordinary account. |
|||
Leaving it will make it impossible to use bank accounts with |
|||
addresses outside the companies country. |
|||
Ratio: one can have bank accounts in foreign banks. Foreign |
|||
addresses not automatically involve international banking. |
|||
--> |
|||
<record id="base.bank_normal_field_contry" model="res.partner.bank.type.field"> |
|||
<field name="name">country_id</field> |
|||
<field name="bank_type_id" ref="base.bank_normal"/> |
|||
<field eval="False" name="required"/> |
|||
<field eval="False" name="readonly"/> |
|||
</record> |
|||
</data> |
|||
</openerp> |
1626
__unported__/account_banking/i18n/account_banking.pot
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1677
__unported__/account_banking/i18n/da.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,759 @@ |
|||
# Danish translation for account-banking |
|||
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 |
|||
# This file is distributed under the same license as the account-banking package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: account-banking\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2010-07-16 14:58:26+0000\n" |
|||
"PO-Revision-Date: 2010-02-06 14:36+0000\n" |
|||
"Last-Translator: nanker <Unknown>\n" |
|||
"Language-Team: Danish <da@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Number of bank costs invoices created" |
|||
msgstr "Totalt antal transaktioner" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Results:" |
|||
msgstr "Resultater:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of errors found" |
|||
msgstr "Antal fejl fundet" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
msgid "Select the processing details:" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s (ref: %(ref)s) to invoice: '\n" |
|||
" '%(no_candidates)s candidates found; can\'t choose." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid format" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.actions.act_window:0 |
|||
msgid "Invalid model name in the action definition." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,date_done:0 |
|||
msgid "Date Confirmed" |
|||
msgstr "Dato bekræftet" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_statements,open_statements:0 |
|||
msgid "_View Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,end:0 |
|||
#: wizard_button:account_banking.banking_import,view_statements,end:0 |
|||
msgid "_Close" |
|||
msgstr "_Luk" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "Bank Partner" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_account_settings |
|||
msgid "Default Journal for Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,init,file:0 |
|||
msgid "Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"More than one bank account was found with the same number %(account_no)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of transactions" |
|||
msgstr "Totalt antal transaktioner" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Account move line \"%s\" is not valid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment is received. This can be " |
|||
"needed when a customer pays in advance or when no matching invoice can be " |
|||
"found. Mind that you can correct movements before confirming them." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Bank account %(account_no)s was not found in the database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Generation of Bank Costs Invoices" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions skipped due to errors" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"The expected balance (%.2f) is different '\n" |
|||
" 'than the computed one. (%.2f)" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,invoice_journal_id:0 |
|||
#, fuzzy |
|||
msgid "Costs Journal" |
|||
msgstr "Journal" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements skipped due to errors" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid IBAN account number!" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Import Settings for Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment was signaled. This can happen " |
|||
"when a direct debit payment is cancelled by a customer, or when no matching " |
|||
"payment can be found. Mind that you can correct movements before confirming " |
|||
"them." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Unable to import parser %(parser)s. Parser class not found." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,file:0 |
|||
msgid "Raw Data" |
|||
msgstr "Rå data" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Cancelled" |
|||
msgstr "Annulleret" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Insufficient data to select online '\n" |
|||
" 'conversion database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,statement_ids:0 |
|||
msgid "Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "Default debit account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Unknown Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,end:0 |
|||
msgid "_Cancel" |
|||
msgstr "_Annuller" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Draft" |
|||
msgstr "Udkast" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,date:0 |
|||
msgid "Import Date" |
|||
msgstr "Importer dato" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Confirmed" |
|||
msgstr "Bekræftet" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_res_partner_banks |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_accounts |
|||
msgid "Bank Accounts" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Accounts for Unknown Movements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.bank.statement:0 |
|||
msgid "Confirm" |
|||
msgstr "Bekræft" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for account %(bank_account)s, '\n" |
|||
msgstr " 'but no default journal was defined." |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "Default credit account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,international:0 |
|||
msgid "International Transaction" |
|||
msgstr "International transaktion" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Please verify that an account is defined in the journal." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,trans:0 |
|||
msgid "Bank Transaction ID" |
|||
msgstr "Bank transaktion ID" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "This is the journal used to create invoices for bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statement %(id)s known - skipped" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Sent" |
|||
msgstr "Sendt" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,costs_account_id:0 |
|||
msgid "" |
|||
"The account to use when the bank invoices its own costs. Leave it blank to " |
|||
"disable automatic invoice generation on bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "Fejl !" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_normal_field_contry |
|||
msgid "country_id" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Rejected" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_journals |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_journals |
|||
msgid "Default Import Settings for Bank Accounts" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.wizard,name:account_banking.wizard_account_banking_import_file |
|||
#: model:ir.ui.menu,name:account_banking.menu_account_banking_import_wizard |
|||
msgid "Import Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account_banking.banking_import,init,file:0 |
|||
msgid "" |
|||
"The Transactions File to import. Please note that while it is perfectly safe " |
|||
"to reload the same file multiple times or to load in timeframe overlapping " |
|||
"statements files, there are formats that may introduce different sequencing, " |
|||
"which may create double entries.\n" |
|||
"\n" |
|||
"To stay on the safe side, always load bank statements files using the same " |
|||
"format." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/sepa/bbantoiban.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own code" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.ui.view:0 |
|||
msgid "Invalid XML for View Architecture!" |
|||
msgstr "Ugyldig XML for View Architecture!" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Imported Bank Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,description:account_banking.module_meta_information |
|||
msgid "" |
|||
"\n" |
|||
" Module to do banking.\n" |
|||
"\n" |
|||
" Note: This module is depending on BeautifulSoup.\n" |
|||
"\n" |
|||
" This modules tries to combine all current banking import and export\n" |
|||
" schemes. Rationale for this is that it is quite common to have foreign\n" |
|||
" bank account numbers next to national bank account numbers. The current\n" |
|||
" approach, which hides the national banking interface schemes in the\n" |
|||
" l10n_xxx modules, makes it very difficult to use these simultanious.\n" |
|||
" A more banking oriented approach seems more logical and cleaner.\n" |
|||
"\n" |
|||
" Changes to default OpenERP:\n" |
|||
"\n" |
|||
" * Puts focus on the real life messaging with banks:\n" |
|||
" + Bank statement lines upgraded to independent bank transactions.\n" |
|||
" + Banking statements have no special accountancy meaning, they're " |
|||
"just\n" |
|||
" message envelopes for a number of bank transactions.\n" |
|||
" + Bank statements can be either encoded by hand to reflect the " |
|||
"document\n" |
|||
" version of Bank Statements, or created as an optional side effect " |
|||
"of\n" |
|||
" importing Bank Transactions.\n" |
|||
"\n" |
|||
" * Preparations for SEPA:\n" |
|||
" + IBAN accounts are the standard in the SEPA countries\n" |
|||
" + local accounts are derived from SEPA (excluding Turkey) but are\n" |
|||
" considered to be identical to the corresponding SEPA account.\n" |
|||
" + Banks are identified with either Country + Bank code + Branch code " |
|||
"or BIC\n" |
|||
" + Each bank can have its own pace in introducing SEPA into their\n" |
|||
" communication with their customers.\n" |
|||
" + National online databases can be used to convert BBAN's to IBAN's.\n" |
|||
" + The SWIFT database is consulted for bank information.\n" |
|||
"\n" |
|||
" * Adds dropin extensible import facility for bank communication in:\n" |
|||
" - Drop-in input parser development.\n" |
|||
" - MultiBank (NL) format transaction files available as\n" |
|||
" account_banking_nl_multibank,\n" |
|||
"\n" |
|||
" * Extends payments for digital banking:\n" |
|||
" + Adapted workflow in payments to reflect banking operations\n" |
|||
" + Relies on account_payment mechanics to extend with export " |
|||
"generators.\n" |
|||
" - ClieOp3 (NL) payment and direct debit orders files available as\n" |
|||
" account_banking_nl_clieop\n" |
|||
"\n" |
|||
" * Additional features for the import/export mechanism:\n" |
|||
" + Automatic matching and creation of bank accounts, banks and " |
|||
"partners,\n" |
|||
" during import of statements.\n" |
|||
" + Automatic matching with invoices and payments.\n" |
|||
" + Sound import mechanism, allowing multiple imports of the same\n" |
|||
" transactions repeated over multiple files.\n" |
|||
" + Journal configuration per bank account.\n" |
|||
" + Business logic and format parsing strictly separated to ease the\n" |
|||
" development of new parsers.\n" |
|||
" + No special configuration needed for the parsers, new parsers are\n" |
|||
" recognized and made available at server (re)start.\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Import Bank Transactions File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Account %(account_no)s is not owned by %(partner)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,import:0 |
|||
msgid "_Ok" |
|||
msgstr "_Ok" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "More then one possible match found for partner with name %(name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,state:0 |
|||
#: field:payment.line,export_state:0 |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "ERROR!" |
|||
msgstr "FEJL!" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s '\n" |
|||
" '(ref: %(ref)s) to invoice: '\n" |
|||
" 'invoice %(invoice)s was already paid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number has the wrong format for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,currency:0 |
|||
msgid "Currency" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,msg:0 |
|||
msgid "Message" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,company_id:0 |
|||
#: field:account.banking.imported.file,company_id:0 |
|||
#: wizard_field:account_banking.banking_import,init,company:0 |
|||
msgid "Company" |
|||
msgstr "Virksomhed" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,view_error,log:0 |
|||
#: wizard_field:account_banking.banking_import,view_statements,log:0 |
|||
msgid "Log" |
|||
msgstr "Log" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "Invalid value for transfer_type" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Insufficient data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Configration Error !" |
|||
msgstr "Konfigurationsfejl !" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.act_account_payment_account_bank_statement |
|||
msgid "Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "No suitable period found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_imported_files |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_imported_files |
|||
msgid "Imported Bank Statements Files" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement,banking_id:0 |
|||
msgid "Imported File" |
|||
msgstr "Importerede filer" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,log:0 |
|||
msgid "Import Log" |
|||
msgstr "Importer log" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping periods for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "The imported statements appear to be invalid! Check your file." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements loaded" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_actions |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_settings |
|||
msgid "Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Error" |
|||
msgstr "Fejl" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Unable to reconcile entry \"%s\": %.2f" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"No suitable fiscal year found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Import Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,period_id:0 |
|||
msgid "Period" |
|||
msgstr "Periode" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Done" |
|||
msgstr "Udført" |
|||
|
|||
#. module: account_banking |
|||
#: view:payment.order:0 |
|||
msgid "Select Invoices to Pay" |
|||
msgstr "Vælg faktura til betaling" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,user_id:0 |
|||
msgid "Responsible User" |
|||
msgstr "Ansvarlig bruger" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The statement balance is incorrect !\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.model:0 |
|||
msgid "" |
|||
"The Object name must start with x_ and not contain any special character !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Unfinished" |
|||
msgstr "Ikke afsluttet" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for unknown account %(bank_account)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,shortdesc:account_banking.module_meta_information |
|||
msgid "Account Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,open_import:0 |
|||
msgid "_View Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The IBAN number doesn't seem to be correct" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,format:0 |
|||
#: wizard_field:account_banking.banking_import,init,parser:0 |
|||
msgid "File Format" |
|||
msgstr "Filformat" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping fiscal years found for date %(date)s and company %" |
|||
"(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,journal_id:0 |
|||
msgid "Journal" |
|||
msgstr "Journal" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,costs_account_id:0 |
|||
#, fuzzy |
|||
msgid "Bank Costs Account" |
|||
msgstr "Bankkonto" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Finished" |
|||
msgstr "Afsluttet" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Bank Account Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,partner_bank_id:0 |
|||
#: field:account.banking.account.settings,partner_bank_id:0 |
|||
msgid "Bank Account" |
|||
msgstr "Bankkonto" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions loaded" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number appears to be invalid for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_acc_number_field |
|||
msgid "acc_number" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Number of transactions matched" |
|||
msgstr "Totalt antal transaktioner" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "" |
|||
"The partner to use for bank costs. Banks are not partners by default. You " |
|||
"will most likely have to create one." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account entries lines are not in valid state." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_imported_file |
|||
msgid "Imported Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Statement %(statement_id)s for account %(bank_account)s' \n" |
|||
" ' uses different currency than the defined bank " |
|||
"journal." |
|||
msgstr "" |
1695
__unported__/account_banking/i18n/en.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1677
__unported__/account_banking/i18n/es_ES.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1689
__unported__/account_banking/i18n/hr.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,774 @@ |
|||
# Croatian translation for account-banking |
|||
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 |
|||
# This file is distributed under the same license as the account-banking package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: account-banking\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2010-07-16 14:58:26+0000\n" |
|||
"PO-Revision-Date: 2010-02-12 01:28+0000\n" |
|||
"Last-Translator: goranc <goranc@gmail.com>\n" |
|||
"Language-Team: Croatian <hr@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Number of bank costs invoices created" |
|||
msgstr "Broj učitanih transakcija" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Results:" |
|||
msgstr "Rezultati:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of errors found" |
|||
msgstr "Broj pronađenih grešaka" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
msgid "Select the processing details:" |
|||
msgstr "Odaberite detalje obrade:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s (ref: %(ref)s) to invoice: '\n" |
|||
" '%(no_candidates)s candidates found; can\'t choose." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid format" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.actions.act_window:0 |
|||
msgid "Invalid model name in the action definition." |
|||
msgstr "Pogrešno ime modela u definiciji akcije." |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,date_done:0 |
|||
msgid "Date Confirmed" |
|||
msgstr "Datum potvrđen" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_statements,open_statements:0 |
|||
msgid "_View Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,end:0 |
|||
#: wizard_button:account_banking.banking_import,view_statements,end:0 |
|||
msgid "_Close" |
|||
msgstr "_Zatvori" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "Bank Partner" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_account_settings |
|||
msgid "Default Journal for Bank Account" |
|||
msgstr "Predefinirana temeljnica za bankovni račun" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,init,file:0 |
|||
msgid "Statements File" |
|||
msgstr "Datoteka sa stavkama" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"More than one bank account was found with the same number %(account_no)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of transactions" |
|||
msgstr "Ukupan broj transakcija" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Account move line \"%s\" is not valid" |
|||
msgstr "Redak prijenosa računa \"%s\" nije ispravan" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment is received. This can be " |
|||
"needed when a customer pays in advance or when no matching invoice can be " |
|||
"found. Mind that you can correct movements before confirming them." |
|||
msgstr "" |
|||
"Račun za upis primljene neočekivane uplate. To može biti potrebno kada " |
|||
"klijent plaća unaprijed, ili ako nema podudarne fakture. Omogućeno je " |
|||
"ispravljanje upisa prije potvrde." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Bank account %(account_no)s was not found in the database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Generation of Bank Costs Invoices" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions skipped due to errors" |
|||
msgstr "Broj transakcija preskočenih zbog pogreški" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"The expected balance (%.2f) is different '\n" |
|||
" 'than the computed one. (%.2f)" |
|||
msgstr "" |
|||
"Očekivani saldo (%.2f) je različit '\n" |
|||
" 'od izračunatog. (%.2f)" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,invoice_journal_id:0 |
|||
#, fuzzy |
|||
msgid "Costs Journal" |
|||
msgstr "Temeljnica" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements skipped due to errors" |
|||
msgstr "Broj stavki preskočenih zbog pogreški" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid IBAN account number!" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Import Settings for Bank Account" |
|||
msgstr "Predefinirane postavke za učitavanje podataka za bankovni račun" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment was signaled. This can happen " |
|||
"when a direct debit payment is cancelled by a customer, or when no matching " |
|||
"payment can be found. Mind that you can correct movements before confirming " |
|||
"them." |
|||
msgstr "" |
|||
"Račun za upis signalizirane neočekivane uplate. To se može dogoditi kada je " |
|||
"otkazana uplata izravnim zaduženjem od strane kupca, ili kada se ne može " |
|||
"pronaći podudarna uplata. Omogućeno je ispravljanje upisa prije potvrde." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Unable to import parser %(parser)s. Parser class not found." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,file:0 |
|||
msgid "Raw Data" |
|||
msgstr "nepripremljeni podaci" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Cancelled" |
|||
msgstr "Poništeno" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Insufficient data to select online '\n" |
|||
" 'conversion database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,statement_ids:0 |
|||
msgid "Statements" |
|||
msgstr "Izvodi" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "Default debit account" |
|||
msgstr "Predefinirani račun terećenja" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Unknown Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,end:0 |
|||
msgid "_Cancel" |
|||
msgstr "_Odustani" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Draft" |
|||
msgstr "Nedovršeno" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,date:0 |
|||
msgid "Import Date" |
|||
msgstr "Datum učitavanja" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Confirmed" |
|||
msgstr "Potvrđeno" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_res_partner_banks |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_accounts |
|||
msgid "Bank Accounts" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Accounts for Unknown Movements" |
|||
msgstr "Predefinirani računi za nedefinirane promete" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.bank.statement:0 |
|||
msgid "Confirm" |
|||
msgstr "Potvrdi" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for account %(bank_account)s, '\n" |
|||
msgstr " 'but no default journal was defined." |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "Default credit account" |
|||
msgstr "Predefinirani račun odobrenja" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,international:0 |
|||
msgid "International Transaction" |
|||
msgstr "Međunaradni prijenos" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Please verify that an account is defined in the journal." |
|||
msgstr "Molimo provjerite da je račun definiran u temeljnici" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,trans:0 |
|||
msgid "Bank Transaction ID" |
|||
msgstr "Oznaka bankovne transakcije" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "This is the journal used to create invoices for bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statement %(id)s known - skipped" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Sent" |
|||
msgstr "Poslano" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,costs_account_id:0 |
|||
msgid "" |
|||
"The account to use when the bank invoices its own costs. Leave it blank to " |
|||
"disable automatic invoice generation on bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "Greška !" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_normal_field_contry |
|||
msgid "country_id" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Rejected" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_journals |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_journals |
|||
msgid "Default Import Settings for Bank Accounts" |
|||
msgstr "Predefinirane postavke učitavanja za bankovne račune" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.wizard,name:account_banking.wizard_account_banking_import_file |
|||
#: model:ir.ui.menu,name:account_banking.menu_account_banking_import_wizard |
|||
msgid "Import Bank Statements File" |
|||
msgstr "Učitaj datoteku izvoda banke" |
|||
|
|||
#. module: account_banking |
|||
#: help:account_banking.banking_import,init,file:0 |
|||
msgid "" |
|||
"The Transactions File to import. Please note that while it is perfectly safe " |
|||
"to reload the same file multiple times or to load in timeframe overlapping " |
|||
"statements files, there are formats that may introduce different sequencing, " |
|||
"which may create double entries.\n" |
|||
"\n" |
|||
"To stay on the safe side, always load bank statements files using the same " |
|||
"format." |
|||
msgstr "" |
|||
"Datoteka s transakcijama za učitavanje. Imajte na umu da je potpuno sigurno " |
|||
"ponovno učitavanje iste datoteke ili učitati datoteku izvoda koji se " |
|||
"vremenski preklapaju, ipak postoje formati datoteka koji mogu uzrokovati " |
|||
"različiti redoslijed, što može dovesti do dvostrukog unosa.\n" |
|||
"\n" |
|||
"Za sigurnost, uvijek učitati datoteke koristeći bankovni izvod istog formata." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/sepa/bbantoiban.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own code" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.ui.view:0 |
|||
msgid "Invalid XML for View Architecture!" |
|||
msgstr "Neispravan XML za arhitekturu prikaza!" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Imported Bank Statements" |
|||
msgstr "Učitani izvodi banke" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,description:account_banking.module_meta_information |
|||
msgid "" |
|||
"\n" |
|||
" Module to do banking.\n" |
|||
"\n" |
|||
" Note: This module is depending on BeautifulSoup.\n" |
|||
"\n" |
|||
" This modules tries to combine all current banking import and export\n" |
|||
" schemes. Rationale for this is that it is quite common to have foreign\n" |
|||
" bank account numbers next to national bank account numbers. The current\n" |
|||
" approach, which hides the national banking interface schemes in the\n" |
|||
" l10n_xxx modules, makes it very difficult to use these simultanious.\n" |
|||
" A more banking oriented approach seems more logical and cleaner.\n" |
|||
"\n" |
|||
" Changes to default OpenERP:\n" |
|||
"\n" |
|||
" * Puts focus on the real life messaging with banks:\n" |
|||
" + Bank statement lines upgraded to independent bank transactions.\n" |
|||
" + Banking statements have no special accountancy meaning, they're " |
|||
"just\n" |
|||
" message envelopes for a number of bank transactions.\n" |
|||
" + Bank statements can be either encoded by hand to reflect the " |
|||
"document\n" |
|||
" version of Bank Statements, or created as an optional side effect " |
|||
"of\n" |
|||
" importing Bank Transactions.\n" |
|||
"\n" |
|||
" * Preparations for SEPA:\n" |
|||
" + IBAN accounts are the standard in the SEPA countries\n" |
|||
" + local accounts are derived from SEPA (excluding Turkey) but are\n" |
|||
" considered to be identical to the corresponding SEPA account.\n" |
|||
" + Banks are identified with either Country + Bank code + Branch code " |
|||
"or BIC\n" |
|||
" + Each bank can have its own pace in introducing SEPA into their\n" |
|||
" communication with their customers.\n" |
|||
" + National online databases can be used to convert BBAN's to IBAN's.\n" |
|||
" + The SWIFT database is consulted for bank information.\n" |
|||
"\n" |
|||
" * Adds dropin extensible import facility for bank communication in:\n" |
|||
" - Drop-in input parser development.\n" |
|||
" - MultiBank (NL) format transaction files available as\n" |
|||
" account_banking_nl_multibank,\n" |
|||
"\n" |
|||
" * Extends payments for digital banking:\n" |
|||
" + Adapted workflow in payments to reflect banking operations\n" |
|||
" + Relies on account_payment mechanics to extend with export " |
|||
"generators.\n" |
|||
" - ClieOp3 (NL) payment and direct debit orders files available as\n" |
|||
" account_banking_nl_clieop\n" |
|||
"\n" |
|||
" * Additional features for the import/export mechanism:\n" |
|||
" + Automatic matching and creation of bank accounts, banks and " |
|||
"partners,\n" |
|||
" during import of statements.\n" |
|||
" + Automatic matching with invoices and payments.\n" |
|||
" + Sound import mechanism, allowing multiple imports of the same\n" |
|||
" transactions repeated over multiple files.\n" |
|||
" + Journal configuration per bank account.\n" |
|||
" + Business logic and format parsing strictly separated to ease the\n" |
|||
" development of new parsers.\n" |
|||
" + No special configuration needed for the parsers, new parsers are\n" |
|||
" recognized and made available at server (re)start.\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Import Bank Transactions File" |
|||
msgstr "Učitaj datoteku bankovnih transakcija" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Account %(account_no)s is not owned by %(partner)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,import:0 |
|||
msgid "_Ok" |
|||
msgstr "_U redu" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "More then one possible match found for partner with name %(name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,state:0 |
|||
#: field:payment.line,export_state:0 |
|||
msgid "State" |
|||
msgstr "Status" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "ERROR!" |
|||
msgstr "Greška!" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s '\n" |
|||
" '(ref: %(ref)s) to invoice: '\n" |
|||
" 'invoice %(invoice)s was already paid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number has the wrong format for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,currency:0 |
|||
msgid "Currency" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,msg:0 |
|||
msgid "Message" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,company_id:0 |
|||
#: field:account.banking.imported.file,company_id:0 |
|||
#: wizard_field:account_banking.banking_import,init,company:0 |
|||
msgid "Company" |
|||
msgstr "Tvrtka" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,view_error,log:0 |
|||
#: wizard_field:account_banking.banking_import,view_statements,log:0 |
|||
msgid "Log" |
|||
msgstr "Dnevnik izmjena" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "Invalid value for transfer_type" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Insufficient data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Configration Error !" |
|||
msgstr "Konfiguracijska pogreška !" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.act_account_payment_account_bank_statement |
|||
msgid "Bank Statements File" |
|||
msgstr "Datoteka bankovnog izvoda" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own." |
|||
msgstr "Ovo je okvir. Izvedbu provedite sami." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "No suitable period found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_imported_files |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_imported_files |
|||
msgid "Imported Bank Statements Files" |
|||
msgstr "Učitana datoteka bankovnog izvoda" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement,banking_id:0 |
|||
msgid "Imported File" |
|||
msgstr "Učitana datoteka" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,log:0 |
|||
msgid "Import Log" |
|||
msgstr "Dnevnik učitavanja" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping periods for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "The imported statements appear to be invalid! Check your file." |
|||
msgstr "Učitane stavke nisu korektne! Provjerite datoteku." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements loaded" |
|||
msgstr "Broj učitanih stavki" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_actions |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_settings |
|||
msgid "Banking" |
|||
msgstr "Bankarstvo" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Error" |
|||
msgstr "Greška" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of statements" |
|||
msgstr "Ukupan broj stavki" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Unable to reconcile entry \"%s\": %.2f" |
|||
msgstr "Nije moguće uskladiti stavku \"%s\".%.2f" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"No suitable fiscal year found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Import Details" |
|||
msgstr "Pregled učitanih stavki" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,period_id:0 |
|||
msgid "Period" |
|||
msgstr "Razdoblje" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Done" |
|||
msgstr "Završeno" |
|||
|
|||
#. module: account_banking |
|||
#: view:payment.order:0 |
|||
msgid "Select Invoices to Pay" |
|||
msgstr "Odaberite fakture za plaćanje" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,user_id:0 |
|||
msgid "Responsible User" |
|||
msgstr "Odgovorni korisnik" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The statement balance is incorrect !\n" |
|||
msgstr "Saldo izvoda je netočan !\n" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.model:0 |
|||
msgid "" |
|||
"The Object name must start with x_ and not contain any special character !" |
|||
msgstr "" |
|||
"Naziv objekta mora početi s x_ i ne smije sadržavati specijalne znakove !" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Unfinished" |
|||
msgstr "Nedovršeno" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for unknown account %(bank_account)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,shortdesc:account_banking.module_meta_information |
|||
msgid "Account Banking" |
|||
msgstr "Bankovni računi" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,open_import:0 |
|||
msgid "_View Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The IBAN number doesn't seem to be correct" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,format:0 |
|||
#: wizard_field:account_banking.banking_import,init,parser:0 |
|||
msgid "File Format" |
|||
msgstr "Format datoteke" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping fiscal years found for date %(date)s and company %" |
|||
"(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,journal_id:0 |
|||
msgid "Journal" |
|||
msgstr "Temeljnica" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,costs_account_id:0 |
|||
#, fuzzy |
|||
msgid "Bank Costs Account" |
|||
msgstr "Bankovni račun" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Finished" |
|||
msgstr "Završeno" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Bank Account Details" |
|||
msgstr "Stavke bankovnih računa" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,partner_bank_id:0 |
|||
#: field:account.banking.account.settings,partner_bank_id:0 |
|||
msgid "Bank Account" |
|||
msgstr "Bankovni račun" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions loaded" |
|||
msgstr "Broj učitanih transakcija" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number appears to be invalid for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_acc_number_field |
|||
msgid "acc_number" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Number of transactions matched" |
|||
msgstr "Broj učitanih transakcija" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "" |
|||
"The partner to use for bank costs. Banks are not partners by default. You " |
|||
"will most likely have to create one." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account entries lines are not in valid state." |
|||
msgstr "Stavke ovog računa nisu u valjanom stanju." |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_imported_file |
|||
msgid "Imported Bank Statements File" |
|||
msgstr "Učitane datoteke bankovnih izvoda" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Statement %(statement_id)s for account %(bank_account)s' \n" |
|||
" ' uses different currency than the defined bank " |
|||
"journal." |
|||
msgstr "" |
1693
__unported__/account_banking/i18n/hu.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1679
__unported__/account_banking/i18n/nb.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1750
__unported__/account_banking/i18n/nl.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1677
__unported__/account_banking/i18n/pt_BR.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1686
__unported__/account_banking/i18n/ro.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,773 @@ |
|||
# Romanian translation for account-banking |
|||
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 |
|||
# This file is distributed under the same license as the account-banking package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: account-banking\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2010-07-16 14:58:26+0000\n" |
|||
"PO-Revision-Date: 2010-05-24 22:26+0000\n" |
|||
"Last-Translator: Lavinia Loredana Bertia <Unknown>\n" |
|||
"Language-Team: Romanian <ro@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of bank costs invoices created" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Results:" |
|||
msgstr "Resultate:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of errors found" |
|||
msgstr "Număr de erori găsite" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
msgid "Select the processing details:" |
|||
msgstr "Selectați detaliile de procesare:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s (ref: %(ref)s) to invoice: '\n" |
|||
" '%(no_candidates)s candidates found; can\'t choose." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid format" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.actions.act_window:0 |
|||
msgid "Invalid model name in the action definition." |
|||
msgstr "Nume invalid de model în definirea acțiunii" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,date_done:0 |
|||
msgid "Date Confirmed" |
|||
msgstr "Dată confirmată" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_statements,open_statements:0 |
|||
msgid "_View Statements" |
|||
msgstr "_Vizualizează extrasurile de cont" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,end:0 |
|||
#: wizard_button:account_banking.banking_import,view_statements,end:0 |
|||
msgid "_Close" |
|||
msgstr "_Închide" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "Bank Partner" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_account_settings |
|||
msgid "Default Journal for Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,init,file:0 |
|||
msgid "Statements File" |
|||
msgstr "Fișier de extrasuri de cont" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"More than one bank account was found with the same number %(account_no)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of transactions" |
|||
msgstr "Număr total de tranzacții" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Account move line \"%s\" is not valid" |
|||
msgstr "înregistrarea contabilă \"%s\" nu este validă" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment is received. This can be " |
|||
"needed when a customer pays in advance or when no matching invoice can be " |
|||
"found. Mind that you can correct movements before confirming them." |
|||
msgstr "" |
|||
"Contul ce trebuie folosit când este primită o plată neașteptată. Acesta " |
|||
"poate fi necesitat când un client plătește în avans sau când nu este găsită " |
|||
"factura aferentă. Tranzacțiile pot fi corectate înainte de a fi confirmate." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Bank account %(account_no)s was not found in the database" |
|||
msgstr "Contul bancar %(account_no)s nu a fost găsit în baza de date." |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Generation of Bank Costs Invoices" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions skipped due to errors" |
|||
msgstr "S-a sărit peste o serie de tranzacții din cauza unor erori." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"The expected balance (%.2f) is different '\n" |
|||
" 'than the computed one. (%.2f)" |
|||
msgstr "" |
|||
"Soldul preconizat (%.2f) este diferit '\n" |
|||
" ' de cel calculat. (%.2f)" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "Costs Journal" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements skipped due to errors" |
|||
msgstr "S-a sărit peste o serie de extrasuri de cont din cauza unor erori" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid IBAN account number!" |
|||
msgstr "IBAN invalid!" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Import Settings for Bank Account" |
|||
msgstr "Setări implicite de importare pentru Contul Bancar" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment was signaled. This can happen " |
|||
"when a direct debit payment is cancelled by a customer, or when no matching " |
|||
"payment can be found. Mind that you can correct movements before confirming " |
|||
"them." |
|||
msgstr "" |
|||
"Contul ce trebuie folosit când este semnalată o plată neașteptată. Aceasta " |
|||
"se poate întâmpla când un client anulează un debit direct, sau când nu este " |
|||
"găsită plata aferentă. Tranzacțiile pot fi modificate înainte de a fi " |
|||
"confirmate." |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Unable to import parser %(parser)s. Parser class not found." |
|||
msgstr "" |
|||
"Nu se poate importa analizatorul %(parser)s. Categoria analizatorului nu a " |
|||
"fost găsită." |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,file:0 |
|||
msgid "Raw Data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Cancelled" |
|||
msgstr "Anulat" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Insufficient data to select online '\n" |
|||
" 'conversion database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,statement_ids:0 |
|||
msgid "Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "Default debit account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Unknown Bank" |
|||
msgstr "Bancă necunoscută" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,end:0 |
|||
msgid "_Cancel" |
|||
msgstr "_Anulează" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Draft" |
|||
msgstr "Schiță" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,date:0 |
|||
msgid "Import Date" |
|||
msgstr "Importă data" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Confirmed" |
|||
msgstr "Confirmat" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_res_partner_banks |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_accounts |
|||
msgid "Bank Accounts" |
|||
msgstr "Conturi bancare" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Accounts for Unknown Movements" |
|||
msgstr "Conturi Implicite pentru Tranzacții Necunoscute" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.bank.statement:0 |
|||
msgid "Confirm" |
|||
msgstr "Confirmă" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Statements found for account %(bank_account)s, '\n" |
|||
msgstr "" |
|||
"Extrasuri de cont găsite pentru contul %(bank_account)s, '\n" |
|||
" ' dar s-a setat nici un jurnal implicit." |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "Default credit account" |
|||
msgstr "Cont de credit implicit" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,international:0 |
|||
msgid "International Transaction" |
|||
msgstr "Tranzacție Internațională" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Please verify that an account is defined in the journal." |
|||
msgstr "Verificaţi dacă acest cont este definit în jurnal." |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,trans:0 |
|||
msgid "Bank Transaction ID" |
|||
msgstr "ID-ul Tranzacției Bancare" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "This is the journal used to create invoices for bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statement %(id)s known - skipped" |
|||
msgstr "Extrasul %(id)s este recunoscut - s-a sărit peste el" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Sent" |
|||
msgstr "S-a trimis" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,costs_account_id:0 |
|||
msgid "" |
|||
"The account to use when the bank invoices its own costs. Leave it blank to " |
|||
"disable automatic invoice generation on bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_normal_field_contry |
|||
msgid "country_id" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Rejected" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_journals |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_journals |
|||
msgid "Default Import Settings for Bank Accounts" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.wizard,name:account_banking.wizard_account_banking_import_file |
|||
#: model:ir.ui.menu,name:account_banking.menu_account_banking_import_wizard |
|||
msgid "Import Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account_banking.banking_import,init,file:0 |
|||
msgid "" |
|||
"The Transactions File to import. Please note that while it is perfectly safe " |
|||
"to reload the same file multiple times or to load in timeframe overlapping " |
|||
"statements files, there are formats that may introduce different sequencing, " |
|||
"which may create double entries.\n" |
|||
"\n" |
|||
"To stay on the safe side, always load bank statements files using the same " |
|||
"format." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/sepa/bbantoiban.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own code" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.ui.view:0 |
|||
msgid "Invalid XML for View Architecture!" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Imported Bank Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,description:account_banking.module_meta_information |
|||
msgid "" |
|||
"\n" |
|||
" Module to do banking.\n" |
|||
"\n" |
|||
" Note: This module is depending on BeautifulSoup.\n" |
|||
"\n" |
|||
" This modules tries to combine all current banking import and export\n" |
|||
" schemes. Rationale for this is that it is quite common to have foreign\n" |
|||
" bank account numbers next to national bank account numbers. The current\n" |
|||
" approach, which hides the national banking interface schemes in the\n" |
|||
" l10n_xxx modules, makes it very difficult to use these simultanious.\n" |
|||
" A more banking oriented approach seems more logical and cleaner.\n" |
|||
"\n" |
|||
" Changes to default OpenERP:\n" |
|||
"\n" |
|||
" * Puts focus on the real life messaging with banks:\n" |
|||
" + Bank statement lines upgraded to independent bank transactions.\n" |
|||
" + Banking statements have no special accountancy meaning, they're " |
|||
"just\n" |
|||
" message envelopes for a number of bank transactions.\n" |
|||
" + Bank statements can be either encoded by hand to reflect the " |
|||
"document\n" |
|||
" version of Bank Statements, or created as an optional side effect " |
|||
"of\n" |
|||
" importing Bank Transactions.\n" |
|||
"\n" |
|||
" * Preparations for SEPA:\n" |
|||
" + IBAN accounts are the standard in the SEPA countries\n" |
|||
" + local accounts are derived from SEPA (excluding Turkey) but are\n" |
|||
" considered to be identical to the corresponding SEPA account.\n" |
|||
" + Banks are identified with either Country + Bank code + Branch code " |
|||
"or BIC\n" |
|||
" + Each bank can have its own pace in introducing SEPA into their\n" |
|||
" communication with their customers.\n" |
|||
" + National online databases can be used to convert BBAN's to IBAN's.\n" |
|||
" + The SWIFT database is consulted for bank information.\n" |
|||
"\n" |
|||
" * Adds dropin extensible import facility for bank communication in:\n" |
|||
" - Drop-in input parser development.\n" |
|||
" - MultiBank (NL) format transaction files available as\n" |
|||
" account_banking_nl_multibank,\n" |
|||
"\n" |
|||
" * Extends payments for digital banking:\n" |
|||
" + Adapted workflow in payments to reflect banking operations\n" |
|||
" + Relies on account_payment mechanics to extend with export " |
|||
"generators.\n" |
|||
" - ClieOp3 (NL) payment and direct debit orders files available as\n" |
|||
" account_banking_nl_clieop\n" |
|||
"\n" |
|||
" * Additional features for the import/export mechanism:\n" |
|||
" + Automatic matching and creation of bank accounts, banks and " |
|||
"partners,\n" |
|||
" during import of statements.\n" |
|||
" + Automatic matching with invoices and payments.\n" |
|||
" + Sound import mechanism, allowing multiple imports of the same\n" |
|||
" transactions repeated over multiple files.\n" |
|||
" + Journal configuration per bank account.\n" |
|||
" + Business logic and format parsing strictly separated to ease the\n" |
|||
" development of new parsers.\n" |
|||
" + No special configuration needed for the parsers, new parsers are\n" |
|||
" recognized and made available at server (re)start.\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Import Bank Transactions File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Account %(account_no)s is not owned by %(partner)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,import:0 |
|||
msgid "_Ok" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "More then one possible match found for partner with name %(name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,state:0 |
|||
#: field:payment.line,export_state:0 |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "ERROR!" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s '\n" |
|||
" '(ref: %(ref)s) to invoice: '\n" |
|||
" 'invoice %(invoice)s was already paid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number has the wrong format for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,currency:0 |
|||
msgid "Currency" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,msg:0 |
|||
msgid "Message" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,company_id:0 |
|||
#: field:account.banking.imported.file,company_id:0 |
|||
#: wizard_field:account_banking.banking_import,init,company:0 |
|||
msgid "Company" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,view_error,log:0 |
|||
#: wizard_field:account_banking.banking_import,view_statements,log:0 |
|||
msgid "Log" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "Invalid value for transfer_type" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Insufficient data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Configration Error !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.act_account_payment_account_bank_statement |
|||
msgid "Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "No suitable period found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_imported_files |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_imported_files |
|||
msgid "Imported Bank Statements Files" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement,banking_id:0 |
|||
msgid "Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,log:0 |
|||
msgid "Import Log" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping periods for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "The imported statements appear to be invalid! Check your file." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements loaded" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_actions |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_settings |
|||
msgid "Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Error" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Unable to reconcile entry \"%s\": %.2f" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"No suitable fiscal year found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Import Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,period_id:0 |
|||
msgid "Period" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Done" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:payment.order:0 |
|||
msgid "Select Invoices to Pay" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,user_id:0 |
|||
msgid "Responsible User" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The statement balance is incorrect !\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.model:0 |
|||
msgid "" |
|||
"The Object name must start with x_ and not contain any special character !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Unfinished" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for unknown account %(bank_account)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,shortdesc:account_banking.module_meta_information |
|||
msgid "Account Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,open_import:0 |
|||
msgid "_View Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The IBAN number doesn't seem to be correct" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,format:0 |
|||
#: wizard_field:account_banking.banking_import,init,parser:0 |
|||
msgid "File Format" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping fiscal years found for date %(date)s and company %" |
|||
"(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,journal_id:0 |
|||
msgid "Journal" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,costs_account_id:0 |
|||
#, fuzzy |
|||
msgid "Bank Costs Account" |
|||
msgstr "Conturi bancare" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Finished" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Bank Account Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,partner_bank_id:0 |
|||
#: field:account.banking.account.settings,partner_bank_id:0 |
|||
msgid "Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions loaded" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number appears to be invalid for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_acc_number_field |
|||
msgid "acc_number" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "Number of transactions matched" |
|||
msgstr "Număr total de tranzacții" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "" |
|||
"The partner to use for bank costs. Banks are not partners by default. You " |
|||
"will most likely have to create one." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account entries lines are not in valid state." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_imported_file |
|||
msgid "Imported Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, fuzzy, python-format |
|||
msgid "" |
|||
"Statement %(statement_id)s for account %(bank_account)s' \n" |
|||
" ' uses different currency than the defined bank " |
|||
"journal." |
|||
msgstr "" |
|||
"Extrasuri de cont găsite pentru contul %(bank_account)s, '\n" |
|||
" ' dar s-a setat nici un jurnal implicit." |
1678
__unported__/account_banking/i18n/tr.po
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,766 @@ |
|||
# Turkish translation for account-banking |
|||
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 |
|||
# This file is distributed under the same license as the account-banking package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: account-banking\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2010-07-16 14:58+0000\n" |
|||
"PO-Revision-Date: 2010-10-10 11:17+0000\n" |
|||
"Last-Translator: Engin BAHADIR <Unknown>\n" |
|||
"Language-Team: Turkish <tr@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2010-10-11 12:32+0000\n" |
|||
"X-Generator: Launchpad (build Unknown)\n" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of bank costs invoices created" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Results:" |
|||
msgstr "Sonuçlar:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of errors found" |
|||
msgstr "Bulunan hataların sayısı" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
msgid "Select the processing details:" |
|||
msgstr "İşlem ayrıntılarını seçin:" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s (ref: %(ref)s) to invoice: '\n" |
|||
" '%(no_candidates)s candidates found; can't choose." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid format" |
|||
msgstr "Geçersiz biçim" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.actions.act_window:0 |
|||
msgid "Invalid model name in the action definition." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,date_done:0 |
|||
msgid "Date Confirmed" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_statements,open_statements:0 |
|||
msgid "_View Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,end:0 |
|||
#: wizard_button:account_banking.banking_import,view_statements,end:0 |
|||
msgid "_Close" |
|||
msgstr "_Kapat" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "Bank Partner" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_account_settings |
|||
msgid "Default Journal for Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,init,file:0 |
|||
msgid "Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"More than one bank account was found with the same number %(account_no)s" |
|||
msgstr "" |
|||
"%(account_no)s ile aynı numaraya sahip birden fazla banka hesabı bulundu" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of transactions" |
|||
msgstr "Toplam işlem sayısı" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Account move line \"%s\" is not valid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment is received. This can be " |
|||
"needed when a customer pays in advance or when no matching invoice can be " |
|||
"found. Mind that you can correct movements before confirming them." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Bank account %(account_no)s was not found in the database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Generation of Bank Costs Invoices" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions skipped due to errors" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"The expected balance (%.2f) is different '\n" |
|||
" 'than the computed one. (%.2f)" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "Costs Journal" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements skipped due to errors" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid IBAN account number!" |
|||
msgstr "Geçersiz IBAN hesap numarası" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Import Settings for Bank Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "" |
|||
"The account to use when an unexpected payment was signaled. This can happen " |
|||
"when a direct debit payment is cancelled by a customer, or when no matching " |
|||
"payment can be found. Mind that you can correct movements before confirming " |
|||
"them." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Unable to import parser %(parser)s. Parser class not found." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,file:0 |
|||
msgid "Raw Data" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Cancelled" |
|||
msgstr "İptal Edildi" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Insufficient data to select online '\n" |
|||
" 'conversion database" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,statement_ids:0 |
|||
msgid "Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_debit_account_id:0 |
|||
msgid "Default debit account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Unknown Bank" |
|||
msgstr "Bilinmeyen Banka" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,end:0 |
|||
msgid "_Cancel" |
|||
msgstr "_iptal" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Draft" |
|||
msgstr "Taslak" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,date:0 |
|||
msgid "Import Date" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Confirmed" |
|||
msgstr "Onaylandı" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_res_partner_banks |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_accounts |
|||
msgid "Bank Accounts" |
|||
msgstr "Banka Hesapları" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Default Accounts for Unknown Movements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.bank.statement:0 |
|||
msgid "Confirm" |
|||
msgstr "Onayla" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Statements found for account %(bank_account)s, '\n" |
|||
" 'but no default journal was defined." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,default_credit_account_id:0 |
|||
msgid "Default credit account" |
|||
msgstr "Varsayılan kredi hesabı" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,international:0 |
|||
msgid "International Transaction" |
|||
msgstr "Uluslararası İşlem" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Please verify that an account is defined in the journal." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,trans:0 |
|||
msgid "Bank Transaction ID" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,invoice_journal_id:0 |
|||
msgid "This is the journal used to create invoices for bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statement %(id)s known - skipped" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Sent" |
|||
msgstr "Gönderildi" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,costs_account_id:0 |
|||
msgid "" |
|||
"The account to use when the bank invoices its own costs. Leave it blank to " |
|||
"disable automatic invoice generation on bank costs." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "Hata !" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Invalid data" |
|||
msgstr "Geçersiz veri" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_normal_field_contry |
|||
msgid "country_id" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Rejected" |
|||
msgstr "Reddedildi" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_journals |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_bank_journals |
|||
msgid "Default Import Settings for Bank Accounts" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.wizard,name:account_banking.wizard_account_banking_import_file |
|||
#: model:ir.ui.menu,name:account_banking.menu_account_banking_import_wizard |
|||
msgid "Import Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: help:account_banking.banking_import,init,file:0 |
|||
msgid "" |
|||
"The Transactions File to import. Please note that while it is perfectly safe " |
|||
"to reload the same file multiple times or to load in timeframe overlapping " |
|||
"statements files, there are formats that may introduce different sequencing, " |
|||
"which may create double entries.\n" |
|||
"\n" |
|||
"To stay on the safe side, always load bank statements files using the same " |
|||
"format." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/sepa/bbantoiban.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own code" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.ui.view:0 |
|||
msgid "Invalid XML for View Architecture!" |
|||
msgstr "Görüntüleme Yapısı için Geçersiz XML!" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Imported Bank Statements" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,description:account_banking.module_meta_information |
|||
msgid "" |
|||
"\n" |
|||
" Module to do banking.\n" |
|||
"\n" |
|||
" Note: This module is depending on BeautifulSoup.\n" |
|||
"\n" |
|||
" This modules tries to combine all current banking import and export\n" |
|||
" schemes. Rationale for this is that it is quite common to have foreign\n" |
|||
" bank account numbers next to national bank account numbers. The current\n" |
|||
" approach, which hides the national banking interface schemes in the\n" |
|||
" l10n_xxx modules, makes it very difficult to use these simultanious.\n" |
|||
" A more banking oriented approach seems more logical and cleaner.\n" |
|||
"\n" |
|||
" Changes to default OpenERP:\n" |
|||
"\n" |
|||
" * Puts focus on the real life messaging with banks:\n" |
|||
" + Bank statement lines upgraded to independent bank transactions.\n" |
|||
" + Banking statements have no special accountancy meaning, they're " |
|||
"just\n" |
|||
" message envelopes for a number of bank transactions.\n" |
|||
" + Bank statements can be either encoded by hand to reflect the " |
|||
"document\n" |
|||
" version of Bank Statements, or created as an optional side effect " |
|||
"of\n" |
|||
" importing Bank Transactions.\n" |
|||
"\n" |
|||
" * Preparations for SEPA:\n" |
|||
" + IBAN accounts are the standard in the SEPA countries\n" |
|||
" + local accounts are derived from SEPA (excluding Turkey) but are\n" |
|||
" considered to be identical to the corresponding SEPA account.\n" |
|||
" + Banks are identified with either Country + Bank code + Branch code " |
|||
"or BIC\n" |
|||
" + Each bank can have its own pace in introducing SEPA into their\n" |
|||
" communication with their customers.\n" |
|||
" + National online databases can be used to convert BBAN's to IBAN's.\n" |
|||
" + The SWIFT database is consulted for bank information.\n" |
|||
"\n" |
|||
" * Adds dropin extensible import facility for bank communication in:\n" |
|||
" - Drop-in input parser development.\n" |
|||
" - MultiBank (NL) format transaction files available as\n" |
|||
" account_banking_nl_multibank,\n" |
|||
"\n" |
|||
" * Extends payments for digital banking:\n" |
|||
" + Adapted workflow in payments to reflect banking operations\n" |
|||
" + Relies on account_payment mechanics to extend with export " |
|||
"generators.\n" |
|||
" - ClieOp3 (NL) payment and direct debit orders files available as\n" |
|||
" account_banking_nl_clieop\n" |
|||
"\n" |
|||
" * Additional features for the import/export mechanism:\n" |
|||
" + Automatic matching and creation of bank accounts, banks and " |
|||
"partners,\n" |
|||
" during import of statements.\n" |
|||
" + Automatic matching with invoices and payments.\n" |
|||
" + Sound import mechanism, allowing multiple imports of the same\n" |
|||
" transactions repeated over multiple files.\n" |
|||
" + Journal configuration per bank account.\n" |
|||
" + Business logic and format parsing strictly separated to ease the\n" |
|||
" development of new parsers.\n" |
|||
" + No special configuration needed for the parsers, new parsers are\n" |
|||
" recognized and made available at server (re)start.\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_view:account_banking.banking_import,init:0 |
|||
#: wizard_view:account_banking.banking_import,view_error:0 |
|||
#: wizard_view:account_banking.banking_import,view_statements:0 |
|||
msgid "Import Bank Transactions File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "Account %(account_no)s is not owned by %(partner)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,init,import:0 |
|||
msgid "_Ok" |
|||
msgstr "_Tamam" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "More then one possible match found for partner with name %(name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,state:0 |
|||
#: field:payment.line,export_state:0 |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "ERROR!" |
|||
msgstr "HATA!" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Unable to link transaction id %(trans)s '\n" |
|||
" '(ref: %(ref)s) to invoice: '\n" |
|||
" 'invoice %(invoice)s was already paid" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number has the wrong format for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,currency:0 |
|||
msgid "Currency" |
|||
msgstr "Para Birimi" |
|||
|
|||
#. module: account_banking |
|||
#: field:payment.line,msg:0 |
|||
msgid "Message" |
|||
msgstr "İleti" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,company_id:0 |
|||
#: field:account.banking.imported.file,company_id:0 |
|||
#: wizard_field:account_banking.banking_import,init,company:0 |
|||
msgid "Company" |
|||
msgstr "Firma" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_field:account_banking.banking_import,view_error,log:0 |
|||
#: wizard_field:account_banking.banking_import,view_statements,log:0 |
|||
msgid "Log" |
|||
msgstr "Kayıt" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "Invalid value for transfer_type" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Insufficient data" |
|||
msgstr "Yetersiz veri" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Configration Error !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.actions.act_window,name:account_banking.act_account_payment_account_bank_statement |
|||
msgid "Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/parsers/models.py:0 |
|||
#, python-format |
|||
msgid "This is a stub. Please implement your own." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"No suitable period found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: model:ir.actions.act_window,name:account_banking.action_account_banking_imported_files |
|||
#: model:ir.ui.menu,name:account_banking.menu_action_account_banking_imported_files |
|||
msgid "Imported Bank Statements Files" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement,banking_id:0 |
|||
msgid "Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
#: field:account.banking.imported.file,log:0 |
|||
msgid "Import Log" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping periods for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "The imported statements appear to be invalid! Check your file." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of statements loaded" |
|||
msgstr "Yüklü beyanların sayısı" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_actions |
|||
#: model:ir.ui.menu,name:account_banking.menu_finance_banking_settings |
|||
msgid "Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Error" |
|||
msgstr "Hata" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Total number of statements" |
|||
msgstr "Beyanların toplam sayısı" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "Unable to reconcile entry \"%s\": %.2f" |
|||
msgstr "\"%s\": %.2f girdisi bağdaştırılamadı" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"No suitable fiscal year found for date %(date)s and company %(company_name)s" |
|||
msgstr "" |
|||
"%(company_name)s şirketi ve %(date)s tarihi için uygun mali yıl bulunamadı" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.imported.file:0 |
|||
msgid "Import Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,period_id:0 |
|||
msgid "Period" |
|||
msgstr "Dönem" |
|||
|
|||
#. module: account_banking |
|||
#: selection:payment.line,export_state:0 |
|||
msgid "Done" |
|||
msgstr "Tamamlandı" |
|||
|
|||
#. module: account_banking |
|||
#: view:payment.order:0 |
|||
msgid "Select Invoices to Pay" |
|||
msgstr "Ödeme için Faturaları Seç" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,user_id:0 |
|||
msgid "Responsible User" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The statement balance is incorrect !\n" |
|||
msgstr "Beyan dengesi doğru değil\n" |
|||
|
|||
#. module: account_banking |
|||
#: constraint:ir.model:0 |
|||
msgid "" |
|||
"The Object name must start with x_ and not contain any special character !" |
|||
msgstr "Nesne adı x_ ile başlamalı ve özel karakter içermemelidir!" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Unfinished" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Statements found for unknown account %(bank_account)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.module.module,shortdesc:account_banking.module_meta_information |
|||
msgid "Account Banking" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: wizard_button:account_banking.banking_import,view_error,open_import:0 |
|||
msgid "_View Imported File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The IBAN number doesn't seem to be correct" |
|||
msgstr "IBAN numarası yanlış gibi gözüküyor" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.imported.file,format:0 |
|||
#: wizard_field:account_banking.banking_import,init,parser:0 |
|||
msgid "File Format" |
|||
msgstr "Dosya Biçimi" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/banktools.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Multiple overlapping fiscal years found for date %(date)s and company " |
|||
"%(company_name)s" |
|||
msgstr "" |
|||
"%(company_name)s şireketi ve %(date)s tarihine ait eşleşen birçok mali yıl " |
|||
"bulundu" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,journal_id:0 |
|||
msgid "Journal" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.banking.account.settings,costs_account_id:0 |
|||
msgid "Bank Costs Account" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: selection:account.banking.imported.file,state:0 |
|||
msgid "Finished" |
|||
msgstr "Tamamlandı" |
|||
|
|||
#. module: account_banking |
|||
#: view:account.banking.account.settings:0 |
|||
msgid "Bank Account Details" |
|||
msgstr "Banka Hesabı Ayrıntıları" |
|||
|
|||
#. module: account_banking |
|||
#: field:account.bank.statement.line,partner_bank_id:0 |
|||
#: field:account.banking.account.settings,partner_bank_id:0 |
|||
msgid "Bank Account" |
|||
msgstr "Banka Hesabı" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions loaded" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account number appears to be invalid for %(country)s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:res.partner.bank.type.field,name:account_banking.bank_acc_number_field |
|||
msgid "acc_number" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "Number of transactions matched" |
|||
msgstr "Eşleşen işlem sayısı" |
|||
|
|||
#. module: account_banking |
|||
#: help:account.banking.account.settings,bank_partner_id:0 |
|||
msgid "" |
|||
"The partner to use for bank costs. Banks are not partners by default. You " |
|||
"will most likely have to create one." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/account_banking.py:0 |
|||
#, python-format |
|||
msgid "The account entries lines are not in valid state." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: model:ir.model,name:account_banking.model_account_banking_imported_file |
|||
msgid "Imported Bank Statements File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking |
|||
#: code:addons/account_banking/wizard/bank_import.py:0 |
|||
#, python-format |
|||
msgid "" |
|||
"Statement %(statement_id)s for account %(bank_account)s' \n" |
|||
" ' uses different currency than the defined bank " |
|||
"journal." |
|||
msgstr "" |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 Therp BV (<http://therp.nl>) |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
""" r81: introduction of bank statement line state |
|||
""" |
|||
__name__ = ("account.bank.statement.line:: set new field 'state' to " |
|||
"confirmed for all statement lines belonging to confirmed " |
|||
"statements") |
|||
|
|||
|
|||
def migrate(cr, version): |
|||
cr.execute("UPDATE account_bank_statement_line as sl " |
|||
" SET state = 'confirmed'" |
|||
" FROM account_bank_statement as s " |
|||
" WHERE sl.statement_id = s.id " |
|||
" AND s.state = 'confirm' " |
|||
) |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2013 Therp BV (<http://therp.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
def migrate(cr, version): |
|||
if not version: |
|||
return |
|||
|
|||
# workflow state moved to another, new module |
|||
cr.execute( |
|||
""" |
|||
UPDATE ir_model_data |
|||
SET module = 'account_banking_payment' |
|||
WHERE name = 'trans_done_sent' |
|||
AND module = 'account_direct_debit' |
|||
""") |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2014 Therp BV (<http://therp.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
def migrate(cr, version): |
|||
if not version: |
|||
return |
|||
|
|||
# Rename value date column |
|||
cr.execute( |
|||
""" |
|||
ALTER TABLE banking_import_transaction |
|||
RENAME COLUMN effective_date TO value_date |
|||
""") |
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2014 Akretion (http://www.akretion.com/) |
|||
# @author: Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
def table_exists(cr, table): |
|||
""" Check whether a certain table or view exists """ |
|||
cr.execute( |
|||
'SELECT count(relname) FROM pg_class WHERE relname = %s', |
|||
(table,)) |
|||
return cr.fetchone()[0] == 1 |
|||
|
|||
|
|||
def migrate(cr, version): |
|||
""" |
|||
Migration script for semantic changes in account_banking_payment_export. |
|||
Putting the same script in this module for users migrating from 6.1, |
|||
before the export module was refactored out. |
|||
""" |
|||
if not version or not table_exists(cr, 'payment_line'): |
|||
return |
|||
cr.execute( |
|||
"UPDATE payment_line SET communication = communication2, " |
|||
"communication2 = null " |
|||
"FROM payment_order " |
|||
"WHERE payment_line.order_id = payment_order.id " |
|||
"AND payment_order.state in ('draft', 'open') " |
|||
"AND payment_line.state = 'normal' " |
|||
"AND communication2 is not null") |
@ -0,0 +1,209 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
__all__ = [ |
|||
'Field', 'Filler', 'DateField', 'NumberField', 'RightAlignedField', |
|||
'RecordType', 'Record', 'asciify' |
|||
] |
|||
|
|||
__doc__ = '''Ease working with fixed length records in files''' |
|||
|
|||
from datetime import datetime, date |
|||
|
|||
# Correct python2.4 issues |
|||
try: |
|||
datetime.strptime |
|||
|
|||
def strpdate(str, format): |
|||
return datetime.strptime(str, format).date() |
|||
except AttributeError: |
|||
import time |
|||
|
|||
def strpdate(str, format): |
|||
tm = time.strptime(str, format) |
|||
return date(tm.tm_year, tm.tm_mon, tm.tm_mday) |
|||
|
|||
import unicodedata |
|||
|
|||
|
|||
class Field(object): |
|||
'''Base Field class - fixed length left aligned string field in a record''' |
|||
def __init__(self, name, length=1, fillchar=' ', cast=str): |
|||
self.name = name.replace(' ', '_') |
|||
self.length = length |
|||
self.fillchar = fillchar |
|||
self.cast = cast |
|||
|
|||
def format(self, value): |
|||
value = self.cast(value) |
|||
if len(value) > self.length: |
|||
return value[:self.length] |
|||
return value.ljust(self.length, self.fillchar) |
|||
|
|||
def take(self, buffer): |
|||
offset = hasattr(self, 'offset') and self.offset or 0 |
|||
return self.cast(buffer[offset:offset + self.length].rstrip( |
|||
self.fillchar) |
|||
) |
|||
|
|||
def __repr__(self): |
|||
return '%s "%s"' % (self.__class__.__name__, self.name) |
|||
|
|||
|
|||
class Filler(Field): |
|||
'''Constant value field''' |
|||
def __init__(self, name, length=1, value=' '): |
|||
super(Filler, self).__init__(name, length, cast=str) |
|||
self.value = str(value) |
|||
|
|||
def take(self, buffer): |
|||
return self.format(buffer) |
|||
|
|||
def format(self, value): |
|||
return super(Filler, self).format( |
|||
self.value * (self.length / len(self.value) + 1) |
|||
) |
|||
|
|||
|
|||
class DateField(Field): |
|||
'''Variable date field''' |
|||
def __init__(self, name, format='%Y-%m-%d', auto=False, cast=str): |
|||
length = len(date.today().strftime(format)) |
|||
super(DateField, self).__init__(name, length, cast=cast) |
|||
self.dateformat = format |
|||
self.auto = auto |
|||
|
|||
def format(self, value): |
|||
if isinstance(value, (str, unicode)) and \ |
|||
len(value.strip()) == self.length: |
|||
value = strpdate(value, self.dateformat) |
|||
elif not isinstance(value, (datetime, date)): |
|||
value = date.today() |
|||
return value.strftime(self.dateformat) |
|||
|
|||
def take(self, buffer): |
|||
value = super(DateField, self).take(buffer) |
|||
if value: |
|||
return strpdate(value, self.dateformat) |
|||
return self.auto and date.today() or None |
|||
|
|||
|
|||
class RightAlignedField(Field): |
|||
'''Deviation of Field: right aligned''' |
|||
def format(self, value): |
|||
if len(value) > self.length: |
|||
return value[-self.length:] |
|||
return value.rjust(self.length, self.fillchar) |
|||
|
|||
def take(self, buffer): |
|||
offset = hasattr(self, 'offset') and self.offset or 0 |
|||
return self.cast(buffer[offset:offset + self.length].lstrip( |
|||
self.fillchar) |
|||
) |
|||
|
|||
|
|||
class NumberField(RightAlignedField): |
|||
'''Deviation of Field: left zero filled''' |
|||
def __init__(self, *args, **kwargs): |
|||
kwargs['fillchar'] = '0' |
|||
super(NumberField, self).__init__(*args, **kwargs) |
|||
|
|||
def format(self, value): |
|||
return super(NumberField, self).format(self.cast(value or '')) |
|||
|
|||
|
|||
class RecordType(object): |
|||
fields = [] |
|||
|
|||
def __init__(self, fields=[]): |
|||
if fields: |
|||
self.fields = fields |
|||
offset = 0 |
|||
for field in self.fields: |
|||
field.offset = offset |
|||
offset += field.length |
|||
|
|||
def __len__(self): |
|||
return reduce(lambda x, y: x + y.length, self.fields, 0) |
|||
|
|||
def __contains__(self, key): |
|||
return any(lambda x, y=key: x.name == y, self.fields) |
|||
|
|||
def __getitem__(self, key): |
|||
for field in self.fields: |
|||
if field.name == key: |
|||
return field |
|||
raise KeyError('No such field: %s' % key) |
|||
|
|||
def format(self, buffer): |
|||
result = [] |
|||
for field in self.fields: |
|||
result.append(field.format(field.take(buffer))) |
|||
return ''.join(result) |
|||
|
|||
def take(self, buffer): |
|||
return dict(zip([x.name for x in self.fields], |
|||
[x.take(buffer) for x in self.fields] |
|||
)) |
|||
|
|||
|
|||
class Record(object): |
|||
_recordtype = None |
|||
|
|||
def __init__(self, recordtype=None, value=''): |
|||
if hasattr(self, '_fields') and self._fields: |
|||
self._recordtype = RecordType(self._fields) |
|||
if not self._recordtype and not recordtype: |
|||
raise ValueError('No recordtype specified') |
|||
if not self._recordtype: |
|||
self._recordtype = recordtype() |
|||
self._length = len(self._recordtype) |
|||
self._value = value.ljust(self._length)[:self._length] |
|||
|
|||
def __len__(self): |
|||
return self._length |
|||
|
|||
def __setattr__(self, attr, value): |
|||
if attr.startswith('_'): |
|||
super(Record, self).__setattr__(attr, value) |
|||
else: |
|||
field = self._recordtype[attr] |
|||
self._value = ( |
|||
self._value[:field.offset] + |
|||
field.format(value) + |
|||
self._value[field.offset + field.length:] |
|||
) |
|||
|
|||
def __getattr__(self, attr): |
|||
if attr.startswith('_'): |
|||
return super(Record, self).__getattr__(attr) |
|||
field = self._recordtype[attr] |
|||
return field.take(self._value) |
|||
|
|||
def __str__(self): |
|||
return self._recordtype.format(self._value) |
|||
|
|||
def __unicode__(self): |
|||
return unicode(self.cast(self)) |
|||
|
|||
|
|||
def asciify(str): |
|||
return unicodedata.normalize('NFKD', str).encode('ascii', 'ignore') |
@ -0,0 +1,72 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2013 Therp BV (<http://therp.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 openerp.osv import orm |
|||
|
|||
|
|||
class ResPartner(orm.Model): |
|||
_inherit = 'res.partner' |
|||
|
|||
def def_journal_account_bank( |
|||
self, cr, uid, ids, get_property_account, context=None): |
|||
""" |
|||
Returns the property journal account for the given partners ids. |
|||
|
|||
:param get_property_account: method of this object that takes |
|||
a partner browse record and returns a field name of type many2one. |
|||
""" |
|||
if not ids: |
|||
return {} |
|||
res = dict([(res_id, False) for res_id in ids]) |
|||
for partner in self.browse(cr, uid, ids, context=context): |
|||
property_account = get_property_account(partner) |
|||
if partner[property_account]: |
|||
res[partner.id] = partner[property_account].id |
|||
return res |
|||
|
|||
def get_property_account_decrease(self, partner): |
|||
if partner.customer and not partner.supplier: |
|||
return 'property_account_receivable' |
|||
return 'property_account_payable' |
|||
|
|||
def get_property_account_increase(self, partner): |
|||
if partner.supplier and not partner.customer: |
|||
return 'property_account_payable' |
|||
return 'property_account_receivable' |
|||
|
|||
def def_journal_account_bank_decr( |
|||
self, cr, uid, ids, context=None): |
|||
""" |
|||
Return the default journal account to be used for this partner |
|||
in the case of bank transactions that decrease the balance. |
|||
""" |
|||
return self.def_journal_account_bank( |
|||
cr, uid, ids, self.get_property_account_decrease, context=context) |
|||
|
|||
def def_journal_account_bank_incr( |
|||
self, cr, uid, ids, context=None): |
|||
""" |
|||
Return the default journal account to be used for this partner |
|||
in the case of bank transactions that increase the balance. |
|||
""" |
|||
return self.def_journal_account_bank( |
|||
cr, uid, ids, self.get_property_account_increase, context=context) |
@ -0,0 +1,92 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# (C) 2011 - 2014 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
from openerp.osv import orm |
|||
from openerp.addons.account_banking import sepa |
|||
|
|||
|
|||
class ResPartnerBank(orm.Model): |
|||
_inherit = 'res.partner.bank' |
|||
|
|||
def online_account_info( |
|||
self, cr, uid, country_code, acc_number, context=None): |
|||
""" |
|||
API hook for legacy online lookup of account info, |
|||
to be removed in OpenERP 8.0. |
|||
""" |
|||
return False |
|||
|
|||
def search(self, cr, uid, args, *rest, **kwargs): |
|||
""" |
|||
Disregard spaces when comparing IBANs. |
|||
""" |
|||
|
|||
def is_term(arg): |
|||
'''Flag an arg as term or otherwise''' |
|||
return isinstance(arg, (list, tuple)) and len(arg) == 3 |
|||
|
|||
def extended_filter_term(term): |
|||
''' |
|||
Extend the search criteria in term when appropriate. |
|||
''' |
|||
result = [term] |
|||
extra_terms = [] |
|||
if term[0].lower() == 'acc_number' and term[1] in ('=', '=='): |
|||
iban = sepa.IBAN(term[2]) |
|||
if iban.valid: |
|||
# Disregard spaces when comparing IBANs |
|||
cr.execute( |
|||
""" |
|||
SELECT id FROM res_partner_bank |
|||
WHERE replace(acc_number, ' ', '') = %s |
|||
""", (term[2].replace(' ', ''),)) |
|||
ids = [row[0] for row in cr.fetchall()] |
|||
result = [('id', 'in', ids)] |
|||
for extra_term in extra_terms: |
|||
result = ['|'] + result + [extra_term] |
|||
return result |
|||
|
|||
def extended_search_expression(args): |
|||
''' |
|||
Extend the search expression in args when appropriate. |
|||
The expression itself is in reverse polish notation, so recursion |
|||
is not needed. |
|||
''' |
|||
if not args: |
|||
return [] |
|||
|
|||
result = [] |
|||
if is_term(args[0]) and len(args) > 1: |
|||
# Classic filter, implicit '&' |
|||
result += ['&'] |
|||
|
|||
for arg in args: |
|||
if is_term(arg): |
|||
result += extended_filter_term(arg) |
|||
else: |
|||
result += arg |
|||
return result |
|||
|
|||
# Extend search filter |
|||
newargs = extended_search_expression(args) |
|||
|
|||
# Original search |
|||
return super(ResPartnerBank, self).search( |
|||
cr, uid, newargs, *rest, **kwargs) |
@ -0,0 +1,23 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
import iban |
|||
IBAN = iban.IBAN |
|||
BBAN = iban.BBAN |
@ -0,0 +1,534 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
# The information about SEPA account numbers in this module are collected |
|||
# from ISO 13616-1, which can be found at SWIFT's website: |
|||
# http://www.swift.com/solutions/messaging/information_products/bic_downloads_documents/pdfs/IBAN_Registry.pdf |
|||
# |
|||
# This module uses both SEPA and IBAN as seemingly interchangeble terms. |
|||
# However, a SEPA account is a bank account in the SEPA zone, which is |
|||
# represented by a IBAN number, which is build up from a ISO-693-1 two letter |
|||
# country code, two check digits and a BBAN number, representing the |
|||
# local/national accounting scheme. |
|||
# |
|||
# With the exception of Turkey, all countries use the full local adressing |
|||
# scheme in the IBAN numbers, making it possible to deduce the BBAN from the |
|||
# IBAN. As Turkey uses an additional code in the local scheme which is not |
|||
# part of the BBAN, for accounts located in Turkeys banks it is not possible |
|||
# to use the BBAN to reconstruct the local account. |
|||
# |
|||
# WARNING: |
|||
# This module contains seemingly enough info to create IBAN's from BBAN's. |
|||
# Although many BBAN/IBAN conversions seem algorithmic, there is enough |
|||
# deviation to take the warning from SEPA seriously: this is the domain of the |
|||
# account owning banks. Don't use it, unless you are prepared to loose your |
|||
# money. It is for heuristic validation purposes only. |
|||
|
|||
__all__ = ['IBAN', 'BBAN'] |
|||
|
|||
|
|||
def modulo_97_base10(abuffer): |
|||
''' |
|||
Calculate the modulo 97 value of a string in base10 |
|||
''' |
|||
checksum = int(abuffer[0]) |
|||
for digit in abuffer[1:]: |
|||
checksum *= 10 |
|||
checksum += int(digit) |
|||
checksum %= 97 |
|||
return checksum |
|||
|
|||
|
|||
def base36_to_base10str(abuffer): |
|||
''' |
|||
Convert a base36 string value to a string of base10 digits. |
|||
''' |
|||
result = '' |
|||
for digit in abuffer: |
|||
if digit.isalpha(): |
|||
result += str(ord(digit) - 55) |
|||
else: |
|||
result += digit |
|||
return result |
|||
|
|||
|
|||
class BBANFormat(object): |
|||
''' |
|||
A BBANFormat is an auxilliary class for IBAN. It represents the composition |
|||
of a BBAN number from the different elements in order to translate a |
|||
IBAN number to a localized number. The reverse route, transforming a local |
|||
account to a SEPA account, is the sole responsibility of the banks. |
|||
''' |
|||
|
|||
def __init__(self, ibanfmt, bbanfmt='%A', nolz=False): |
|||
''' |
|||
Specify the structure of the SEPA account in relation to the local |
|||
account. The XXZZ prefix that all SEPA accounts have is not part of |
|||
the structure in BBANFormat. |
|||
|
|||
ibanfmt: string of identifiers from position 5 (start = 1): |
|||
A = Account position |
|||
N = Account digit |
|||
B = Bank code digit |
|||
C = Branch code digit |
|||
V = Account check digit |
|||
W = Bank code check digit |
|||
X = Additional check digit (some countries check everything) |
|||
P = Account prefix digit |
|||
|
|||
The combination of N and A can be used to encode minimum length |
|||
leading-zero-stripped account numbers. |
|||
|
|||
Example: (NL) 'CCCCAAAAAAAAAA' |
|||
will convert 'INGB0001234567' into |
|||
bankcode 'INGB' and account '0001234567' |
|||
|
|||
bbanfmt: string of placeholders for the local bank account |
|||
%C: bank code |
|||
%B: branch code |
|||
%I: IBAN number (complete) |
|||
%T: account type |
|||
%P: account prefix |
|||
%A: account number. This will include the 'N' placeholder |
|||
positions in the ibanfmt. |
|||
%V, %W, %X: check digits (separate meanings) |
|||
%Z: IBAN check digits (only Poland uses these) |
|||
%%: % |
|||
anything else: literal copy |
|||
|
|||
Example: (AT): '%A BLZ %C' |
|||
|
|||
nolz: boolean indicating stripping of leading zeroes in the account |
|||
number. Defaults to False |
|||
''' |
|||
self._iban = ibanfmt |
|||
self._bban = bbanfmt |
|||
self._nolz = nolz |
|||
|
|||
def __extract__(self, spec, value): |
|||
'''Extract the value based on the spec''' |
|||
i = self._iban.find(spec) |
|||
if i < 0: |
|||
return '' |
|||
result = '' |
|||
j = len(self._iban) |
|||
while i < j and self._iban[i] == spec: |
|||
result += value[i+4] |
|||
i += 1 |
|||
return self._nolz and result.lstrip('0') or result |
|||
|
|||
def bankcode(self, iban): |
|||
'''Return the bankcode''' |
|||
return self.__extract__('B', iban) |
|||
|
|||
def branchcode(self, iban): |
|||
'''Return the branch code''' |
|||
return self.__extract__('C', iban) |
|||
|
|||
def account(self, iban): |
|||
'''Return the account number''' |
|||
if self._iban.find('N') >= 0: |
|||
prefix = self.__extract__('N', iban).lstrip('0') |
|||
else: |
|||
prefix = '' |
|||
return prefix + self.__extract__('A', iban) |
|||
|
|||
def BBAN(self, iban): |
|||
''' |
|||
Format the BBAN part of the IBAN in iban following the local |
|||
addressing scheme. We need the full IBAN in order to be able to use |
|||
the IBAN check digits in it, as Poland needs. |
|||
''' |
|||
res = '' |
|||
i = 0 |
|||
while i < len(self._bban): |
|||
if self._bban[i] == '%': |
|||
i += 1 |
|||
parm = self._bban[i] |
|||
if parm == 'I': |
|||
res += unicode(iban) |
|||
elif parm in 'BCDPTVWX': |
|||
res += self.__extract__(parm, iban) |
|||
elif parm == 'A': |
|||
res += self.account(iban) |
|||
elif parm == 'S': |
|||
res += iban |
|||
elif parm == 'Z': |
|||
# IBAN check digits (Poland) |
|||
res += iban[2:4] |
|||
elif parm == '%': |
|||
res += '%' |
|||
else: |
|||
res += self._bban[i] |
|||
i += 1 |
|||
return res |
|||
|
|||
|
|||
class IBAN(str): |
|||
''' |
|||
A IBAN string represents a SEPA bank account number. This class provides |
|||
the interpretation and some validation of such strings. |
|||
|
|||
Mind that, although there is sufficient reason to comment on the chosen |
|||
approach, we are talking about a transition period of at max. 1 year. Good |
|||
is good enough. |
|||
''' |
|||
BBAN_formats = { |
|||
'AL': BBANFormat('CCBBBBVAAAAAAAAAAAAAAAAAA', '%B%A'), |
|||
'AD': BBANFormat('CCCCBBBBAAAAAAAAAAAA', '%A'), |
|||
'AT': BBANFormat('BBBBBAAAAAAAAAAA', '%A BLZ %B'), |
|||
'BE': BBANFormat('CCCAAAAAAAVV', '%C-%A-%V'), |
|||
'BA': BBANFormat('BBBCCCAAAAAAAA', '%I'), |
|||
'BG': BBANFormat('BBBBCCCCAAAAAAAAAA', '%I'), |
|||
'CH': BBANFormat('CCCCCAAAAAAAAAAAAV', '%C %A', nolz=True), |
|||
'CS': BBANFormat('BBBAAAAAAAAAAAAAVV', '%B-%A-%V'), |
|||
'CY': BBANFormat('BBBCCCCCAAAAAAAAAAAAAAAA', '%B%C%A'), |
|||
'CZ': BBANFormat('BBBBPPPPPPAAAAAAAAAA', '%B-%P/%A'), |
|||
'DE': BBANFormat('BBBBBBBBAAAAAAAAAAV', '%A BLZ %B'), |
|||
'DK': BBANFormat('CCCCAAAAAAAAAV', '%C %A%V'), |
|||
'EE': BBANFormat('BBCCAAAAAAAAAAAV', '%A%V'), |
|||
'ES': BBANFormat('BBBBCCCCWVAAAAAAAAAA', '%B%C%W%V%A'), |
|||
'FI': BBANFormat('CCCCTTAAAAAAAV', '%C%T-%A%V', nolz=True), |
|||
'FR': BBANFormat('BBBBBCCCCCAAAAAAAAAAAVV', '%B %C %A %V'), |
|||
'FO': BBANFormat('BBBBAAAAAAAAAV', '%B %A%V'), |
|||
# Great Brittain uses a special display for the branch code, which we |
|||
# can't honor using the current system. If this appears to be a |
|||
# problem, we can come up with something later. |
|||
'GB': BBANFormat('BBBBCCCCCCAAAAAAAAV', '%C %A'), |
|||
'GI': BBANFormat('BBBBAAAAAAAAAAAAAAA', '%A'), |
|||
'GL': BBANFormat('CCCCAAAAAAAAAV', '%C %A%V'), |
|||
'GR': BBANFormat('BBBCCCCAAAAAAAAAAAAAAAA', '%B-%C-%A', nolz=True), |
|||
'HR': BBANFormat('BBBBBBBAAAAAAAAAA', '%B-%A'), |
|||
'HU': BBANFormat('BBBCCCCXAAAAAAAAAAAAAAAV', '%B%C%X %A%V'), |
|||
'IE': BBANFormat('BBBBCCCCCCAAAAAAAA', '%C %A'), |
|||
'IL': BBANFormat('BBBCCCAAAAAAAAAAAAA', '%C%A'), |
|||
# Iceland uses an extra identification number, split in two on |
|||
# display. Coded here as %P%V. |
|||
'IS': BBANFormat('CCCCTTAAAAAAPPPPPPVVVV', '%C-%T-%A-%P-%V'), |
|||
'IT': BBANFormat('WBBBBBCCCCCAAAAAAAAAAAA', '%W/%B/%C/%A'), |
|||
'LV': BBANFormat('BBBBAAAAAAAAAAAAA', '%I'), |
|||
'LI': BBANFormat('CCCCCAAAAAAAAAAAA', '%C %A', nolz=True), |
|||
'LT': BBANFormat('BBBBBAAAAAAAAAAA', '%I'), |
|||
'LU': BBANFormat('BBBAAAAAAAAAAAAA', '%I'), |
|||
'MC': BBANFormat('BBBBBCCCCCAAAAAAAAAAAVV', '%B %C %A %V'), |
|||
'ME': BBANFormat('CCCAAAAAAAAAAAAAVV', '%C-%A-%V'), |
|||
'MK': BBANFormat('BBBAAAAAAAAAAVV', '%B-%A-%V', nolz=True), |
|||
'MT': BBANFormat('BBBBCCCCCAAAAAAAAAAAAAAAAAA', '%A', nolz=True), |
|||
# Mauritius has an aditional bank identifier, a reserved part and the |
|||
# currency as part of the IBAN encoding. As there is no representation |
|||
# given for the local account in ISO 13616-1 we assume IBAN, which |
|||
# circumvents the BBAN display problem. |
|||
'MU': BBANFormat('BBBBBBCCAAAAAAAAAAAAVVVWWW', '%I'), |
|||
# Netherlands has two different local account schemes: one with and |
|||
# one without check digit (9-scheme and 7-scheme). Luckily most Dutch |
|||
# financial services can keep the two apart without telling, so leave |
|||
# that. Also leave the leading zero issue, as most banks are already |
|||
# converting their local account numbers to BBAN format. |
|||
'NL': BBANFormat('BBBBAAAAAAAAAA', '%A'), |
|||
# Norway seems to split the account number in two on display. For now |
|||
# we leave that. If this appears to be a problem, we can fix it later. |
|||
'NO': BBANFormat('CCCCAAAAAV', '%C.%A%V'), |
|||
'PL': BBANFormat('CCCCCCCCAAAAAAAAAAAAAAAA', '%Z%C %A'), |
|||
'PT': BBANFormat('BBBBCCCCAAAAAAAAAAAVV', '%B.%C.%A.%V'), |
|||
'RO': BBANFormat('BBBBAAAAAAAAAAAAAAAA', '%A'), |
|||
'SA': BBANFormat('BBAAAAAAAAAAAAAAAA', '%B%A'), |
|||
'SE': BBANFormat('CCCAAAAAAAAAAAAAAAAV', '%A'), |
|||
'SI': BBANFormat('CCCCCAAAAAAAAVV', '%C-%A%V', ), |
|||
# Slovakia uses two different format for local display. We stick with |
|||
# their formal BBAN specs |
|||
'SK': BBANFormat('BBBBPPPPPPAAAAAAAAAAAA', '%B%P%A'), |
|||
# San Marino: No information for display of BBAN, so stick with IBAN |
|||
'SM': BBANFormat('WBBBBBCCCCCCAAAAAAAAAAAAV', '%I'), |
|||
'TN': BBANFormat('BBCCCAAAAAAAAAAAAAVV', '%B %C %A %V'), |
|||
# Turkey has insufficient information in the IBAN number to regenerate |
|||
# the BBAN: the branch code for local addressing is missing (5n). |
|||
'TR': BBANFormat('BBBBBWAAAAAAAAAAAAAAAA', '%B%C%A'), |
|||
} |
|||
countries = BBAN_formats.keys() |
|||
unknown_BBAN_format = BBANFormat('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', '%I') |
|||
|
|||
def __new__(cls, arg, **kwargs): |
|||
''' |
|||
All letters should be uppercase and acceptable. As str is an |
|||
in 'C' implemented class, this can't be done in __init__. |
|||
''' |
|||
init = '' |
|||
if arg: |
|||
for item in arg.upper(): |
|||
if item.isalnum(): |
|||
init += item |
|||
elif item not in ' \t.-': |
|||
raise ValueError('Invalid chars found in IBAN number') |
|||
return str.__new__(cls, init) |
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
''' |
|||
Sanity check: don't offer extensions unless the base is sound. |
|||
''' |
|||
super(IBAN, self).__init__() |
|||
if self.countrycode not in self.countries: |
|||
self.BBAN_format = self.unknown_BBAN_format |
|||
else: |
|||
self.BBAN_format = self.BBAN_formats[self.countrycode] |
|||
|
|||
@classmethod |
|||
def create(cls, BIC=None, countrycode=None, BBAN=None, bankcode=None, |
|||
branchcode=None, account=None): |
|||
''' |
|||
Create a IBAN number from a BBAN and a country code. Optionaly create |
|||
a BBAN from BBAN components before generation. |
|||
|
|||
Incomplete: can only work with valid BBAN now. |
|||
''' |
|||
if BIC: |
|||
if not bankcode: |
|||
bankcode = BIC[:4] |
|||
if not countrycode: |
|||
countrycode = BIC[4:6] |
|||
else: |
|||
if countrycode: |
|||
countrycode = countrycode.upper() |
|||
else: |
|||
raise ValueError('Either BIC or countrycode is required') |
|||
|
|||
if countrycode not in cls.countries: |
|||
raise ValueError('%s is not a SEPA country' % countrycode) |
|||
format = cls.BBAN_formats[countrycode] |
|||
|
|||
if BBAN: |
|||
if len(BBAN) == len(format._iban): |
|||
ibanno = cls(countrycode + '00' + BBAN) |
|||
return cls(countrycode + ibanno.checksum + BBAN) |
|||
raise ValueError('Insufficient data to generate IBAN') |
|||
|
|||
@property |
|||
def valid(self): |
|||
''' |
|||
Check if the string + check digits deliver a valid checksum |
|||
''' |
|||
_buffer = self[4:] + self[:4] |
|||
return ( |
|||
self.countrycode in self.countries |
|||
and int(base36_to_base10str(_buffer)) % 97 == 1 |
|||
) |
|||
|
|||
def __repr__(self): |
|||
''' |
|||
Formal representation is in chops of four characters, devided by a |
|||
space. |
|||
''' |
|||
parts = [] |
|||
for i in range(0, len(self), 4): |
|||
parts.append(self[i:i+4]) |
|||
return ' '.join(parts) |
|||
|
|||
def __unicode__(self): |
|||
''' |
|||
Return unicode representation of self |
|||
''' |
|||
return u'%r' % self |
|||
|
|||
@property |
|||
def checksum(self): |
|||
''' |
|||
Generate a new checksum for an otherwise correct layed out BBAN in a |
|||
IBAN string. |
|||
NOTE: This is the responsability of the banks. No guaranties whatsoever |
|||
that this delivers usable IBAN accounts. Mind your money! |
|||
''' |
|||
_buffer = self[4:] + self[:2] + '00' |
|||
_buffer = base36_to_base10str(_buffer) |
|||
return '%.2d' % (98 - modulo_97_base10(_buffer)) |
|||
|
|||
@property |
|||
def checkdigits(self): |
|||
''' |
|||
Return the digits which form the checksum in the IBAN string |
|||
''' |
|||
return self[2:4] |
|||
|
|||
@property |
|||
def countrycode(self): |
|||
''' |
|||
Return the ISO country code |
|||
''' |
|||
return self[:2] |
|||
|
|||
@property |
|||
def bankcode(self): |
|||
''' |
|||
Return the bank code |
|||
''' |
|||
return self.BBAN_format.bankcode(self) |
|||
|
|||
@property |
|||
def BIC_searchkey(self): |
|||
''' |
|||
BIC's, or Bank Identification Numbers, are composed of the bank |
|||
code, followed by the country code, followed by the localization |
|||
code, followed by an optional department number. |
|||
|
|||
The bank code seems to be world wide unique. Knowing this, |
|||
one can use the country + bankcode info from BIC to narrow a |
|||
search for the bank itself. |
|||
|
|||
Note that some countries use one single localization code for |
|||
all bank transactions in that country, while others do not. This |
|||
makes it impossible to use an algorithmic approach for generating |
|||
the full BIC. |
|||
''' |
|||
return self.bankcode[:4] + self.countrycode |
|||
|
|||
@property |
|||
def branchcode(self): |
|||
''' |
|||
Return the branch code |
|||
''' |
|||
return self.BBAN_format.branchcode(self) |
|||
|
|||
@property |
|||
def localized_BBAN(self): |
|||
''' |
|||
Localized format of local or Basic Bank Account Number, aka BBAN |
|||
''' |
|||
if self.countrycode == 'TR': |
|||
# The Turkish BBAN requires information that is not in the |
|||
# IBAN number. |
|||
return False |
|||
return self.BBAN_format.BBAN(self) |
|||
|
|||
@property |
|||
def BBAN(self): |
|||
''' |
|||
Return full encoded BBAN, which is for all countries the IBAN string |
|||
after the ISO-639 code and the two check digits. |
|||
''' |
|||
return self[4:] |
|||
|
|||
|
|||
class BBAN(object): |
|||
''' |
|||
Class to reformat a local BBAN account number to IBAN specs. |
|||
Simple validation based on length of spec string elements and real data. |
|||
''' |
|||
|
|||
@staticmethod |
|||
def _get_length(fmt, element): |
|||
''' |
|||
Internal method to calculate the length of a parameter in a |
|||
formatted string |
|||
''' |
|||
i = 0 |
|||
max_i = len(fmt._iban) |
|||
while i < max_i: |
|||
if fmt._iban[i] == element: |
|||
next = i + 1 |
|||
while next < max_i and fmt._iban[next] == element: |
|||
next += 1 |
|||
return next - i |
|||
i += 1 |
|||
return 0 |
|||
|
|||
def __init__(self, bban, countrycode): |
|||
''' |
|||
Reformat and sanity check on BBAN format. |
|||
Note that this is not a fail safe check, it merely checks the format of |
|||
the BBAN following the IBAN specifications. |
|||
''' |
|||
self._bban = None |
|||
if countrycode.upper() in IBAN.countries: |
|||
self._fmt = IBAN.BBAN_formats[countrycode.upper()] |
|||
res = '' |
|||
i = 0 |
|||
j = 0 |
|||
max_i = len(self._fmt._bban) |
|||
max_j = len(bban) |
|||
while i < max_i and j < max_j: |
|||
while bban[j] in ' \t' and j < max_j: |
|||
j += 1 |
|||
if self._fmt._bban[i] == '%': |
|||
i += 1 |
|||
parm = self._fmt._bban[i] |
|||
if parm == 'I': |
|||
_bban = IBAN(bban) |
|||
if _bban.valid: |
|||
self._bban = str(_bban) |
|||
else: |
|||
self._bban = None |
|||
# Valid, so nothing else to do |
|||
return |
|||
elif parm in 'ABCDPSTVWXZ': |
|||
_len = self._get_length(self._fmt, parm) |
|||
addon = bban[j:j+_len] |
|||
if len(addon) != _len: |
|||
# Note that many accounts in the IBAN standard |
|||
# are allowed to have leading zeros, so zfill |
|||
# to full spec length for visual validation. |
|||
# |
|||
# Note 2: this may look funny to some, as most |
|||
# local schemes strip leading zeros. It allows |
|||
# us however to present the user a visual feedback |
|||
# in order to catch simple user mistakes as |
|||
# missing digits. |
|||
if parm == 'A': |
|||
res += addon.zfill(_len) |
|||
else: |
|||
# Invalid, just drop the work and leave |
|||
return |
|||
else: |
|||
res += addon |
|||
j += _len |
|||
elif self._fmt._bban[i] in [bban[j], ' ', '/', '-', '.']: |
|||
res += self._fmt._bban[i] |
|||
if self._fmt._bban[i] == bban[j]: |
|||
j += 1 |
|||
elif self._fmt._bban[i].isalpha(): |
|||
res += self._fmt._bban[i] |
|||
i += 1 |
|||
if i == max_i: |
|||
self._bban = res |
|||
|
|||
def __str__(self): |
|||
'''String representation''' |
|||
return self._bban |
|||
|
|||
def __unicode__(self): |
|||
'''Unicode representation''' |
|||
return unicode(self._bban) |
|||
|
|||
@property |
|||
def valid(self): |
|||
'''Simple check if BBAN is in the right format''' |
|||
return self._bban and True or False |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
import sys |
|||
for arg in sys.argv[1:]: |
|||
iban = IBAN(arg) |
|||
print('IBAN:', iban) |
|||
print('country code:', iban.countrycode) |
|||
print('bank code:', iban.bankcode) |
|||
print('branch code:', iban.branchcode) |
|||
print('BBAN:', iban.BBAN) |
|||
print('localized BBAN:', iban.localized_BBAN) |
|||
print('check digits:', iban.checkdigits) |
|||
print('checksum:', iban.checksum) |
@ -0,0 +1,187 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This module provides a utility class to extract postal codes from address |
|||
strings. |
|||
''' |
|||
import re |
|||
|
|||
__all__ = ['split', 'get', 'PostalCode'] |
|||
|
|||
|
|||
class PostalCode(object): |
|||
''' |
|||
The PostalCode class is a wrapper around PostCodeFormat and an internal |
|||
database of postalcode formats. It provides the class methods split() and |
|||
get(), both of which must be called with the two character iso country |
|||
code as first parameter. |
|||
''' |
|||
|
|||
class PostalCodeFormat(object): |
|||
''' |
|||
Utility class of PostalCode. |
|||
Allows finding and splitting of postalcode in strings |
|||
''' |
|||
def __init__(self, format): |
|||
''' |
|||
Create regexp patterns for matching |
|||
''' |
|||
# Sort formats on length, longest first |
|||
formats = [(len(x), x) for x in format.split('|')] |
|||
formats = [x[1] for x in sorted(formats, lambda x, y: -cmp(x, y))] |
|||
self.res = [re.compile(x.replace('#', '\\d').replace('@', '[A-Z]')) |
|||
for x in formats |
|||
] |
|||
|
|||
def get(self, str_): |
|||
''' |
|||
Return the postal code from the string str_ |
|||
''' |
|||
for re_ in self.res: |
|||
retval = re_.findall(str_) |
|||
if retval: |
|||
break |
|||
return retval and retval[0] or '' |
|||
|
|||
def split(self, str_): |
|||
''' |
|||
Split str_ into (postalcode, remainder) |
|||
''' |
|||
for re_ in self.res: |
|||
pos = re_.search(str_) |
|||
if pos: |
|||
break |
|||
if pos: |
|||
return (pos.group(), str_[pos.end():]) |
|||
return ('', str_) |
|||
|
|||
_formats = { |
|||
'AF': '', 'AX': '', 'AL': '', 'DZ': '#####', 'AS': '', 'AD': 'AD###', |
|||
'AO': '', 'AI': '', 'AQ': '', 'AG': '', 'AR': '@####@@@', |
|||
'AM': '######', 'AW': '', 'AU': '####', 'AT': '####', 'AZ': 'AZ ####', |
|||
'BS': '', 'BH': '####|###', 'BD': '####', 'BB': 'BB#####', |
|||
'BY': '######', 'BE': '####', 'BZ': '', 'BJ': '', 'BM': '@@ ##', |
|||
'BT': '', 'BO': '', 'BA': '#####', 'BW': '', 'BV': '', |
|||
'BR': '#####-###', 'IO': '', 'BN': '@@####', 'BG': '####', 'BF': '', |
|||
'BI': '', 'KH': '#####', 'CM': '', 'CA': '@#@ #@#', 'CV': '####', |
|||
'KY': '', 'CF': '', 'TD': '', 'CL': '#######', 'CN': '######', |
|||
'CX': '####', 'CC': '', 'CO': '', 'KM': '', 'CG': '', 'CD': '', |
|||
'CK': '', 'CR': '####', 'CI': '', 'HR': 'HR-#####', 'CU': 'CP #####', |
|||
'CY': '####', 'CZ': '### ##', 'DK': '####', 'DJ': '', 'DM': '', |
|||
'DO': '#####', 'EC': '@####@', 'EG': '#####', 'SV': 'CP ####', |
|||
'GQ': '', 'ER': '', 'EE': '#####', 'ET': '####', 'FK': '', |
|||
'FO': 'FO-###', 'FJ': '', 'FI': 'FI-#####', 'FR': '#####', |
|||
'GF': '#####', 'PF': '#####', 'TF': '', 'GA': '', 'GM': '', |
|||
'GE': '####', 'DE': '#####', 'GH': '', 'GI': '', 'GR': '### ##', |
|||
'GL': '####', 'GD': '', 'GP': '#####', 'GU': '969##', 'GT': '#####', |
|||
'GG': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA', |
|||
'GN': '', 'GW': '####', 'GY': '', 'HT': 'HT####', 'HM': '', 'VA': '', |
|||
'HN': '@@####', 'HK': '', 'HU': '####', 'IS': '###', 'IN': '######', |
|||
'ID': '#####', 'IR': '##########', 'IQ': '#####', 'IE': '', |
|||
'IM': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA', |
|||
'IL': '#####', 'IT': '####', 'JM': '', 'JP': '###-####', |
|||
'JE': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA', |
|||
'JO': '#####', 'KZ': '######', 'KE': '#####', 'KI': '', |
|||
'KP': '###-###', |
|||
'KR': 'SEOUL ###-###', 'KW': '#####', 'KG': '######', 'LA': '#####', |
|||
'LV': 'LV-####', 'LB': '#### ####|####', 'LS': '###', 'LR': '####', |
|||
'LY': '', 'LI': '####', 'LT': 'LT-#####', 'LU': '####', 'MO': '', |
|||
'MK': '####', 'MG': '###', 'MW': '', 'MY': '#####', 'MV': '#####', |
|||
'ML': '', 'MT': '@@@ ###|@@@ ##', 'MH': '', 'MQ': '#####', 'MR': '', |
|||
'MU': '', 'YT': '#####', 'MX': '#####', 'FM': '#####', 'MD': 'MD-####', |
|||
'MC': '#####', 'MN': '######', 'ME': '#####', 'MS': '', 'MA': '#####', |
|||
'MZ': '####', 'MM': '#####', 'NA': '', 'NR': '', 'NP': '#####', |
|||
'NL': '#### @@', 'AN': '', 'NC': '#####', 'NZ': '####', |
|||
'NI': '###-###-#', 'NE': '####', 'NG': '######', 'NU': '', 'NF': '', |
|||
'MP': '', 'NO': '####', 'OM': '###', 'PK': '#####', 'PW': '96940', |
|||
'PS': '', 'PA': '', 'PG': '###', 'PY': '####', 'PE': '', 'PH': '####', |
|||
'PN': '', 'PL': '##-###', 'PT': '####-###', 'PR': '#####-####', |
|||
'QA': '', 'RE': '#####', 'RO': '######', 'RU': '######', 'RW': '', |
|||
'BL': '### ###', 'SH': 'STHL 1ZZ', 'KN': '', 'LC': '', 'MF': '### ###', |
|||
'PM': '', 'VC': '', 'WS': '', 'SM': '4789#', 'ST': '', 'SA': '#####', |
|||
'SN': '#####', 'RS': '######', 'SC': '', 'SL': '', 'SG': '######', |
|||
'SK': '### ##', 'SI': 'SI- ####', 'SB': '', 'SO': '@@ #####', |
|||
'ZA': '####', 'GS': '', 'ES': '#####', 'LK': '#####', 'SD': '#####', |
|||
'SR': '', 'SJ': '', 'SZ': '@###', 'SE': 'SE-### ##', 'CH': '####', |
|||
'SY': '', 'TW': '#####', 'TJ': '######', 'TZ': '', 'TH': '#####', |
|||
'TL': '', 'TG': '', 'TK': '', 'TO': '', 'TT': '', 'TN': '####', |
|||
'TR': '#####', 'TM': '######', 'TC': 'TKCA 1ZZ', 'TV': '', 'UG': '', |
|||
'UA': '#####', 'AE': '', |
|||
'GB': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA', |
|||
'US': '#####-####', 'UM': '', 'UY': '#####', 'UZ': '######', 'VU': '', |
|||
'VE': '####', 'VN': '######', 'VG': '', 'VI': '', 'WF': '', 'EH': '', |
|||
'YE': '', 'ZM': '#####', 'ZW': '' |
|||
} |
|||
for iso, formatstr in _formats.iteritems(): |
|||
_formats[iso] = PostalCodeFormat(formatstr) |
|||
|
|||
@classmethod |
|||
def split(cls, str_, iso=''): |
|||
''' |
|||
Split string <str_> in (postalcode, remainder) following the specs of |
|||
country <iso>. |
|||
|
|||
Returns iso, postal code and the remaining part of <str_>. |
|||
|
|||
When iso is filled but postal code remains empty, no postal code could |
|||
be found according to the rules of iso. |
|||
|
|||
When iso is empty but postal code is not, a proximity match was |
|||
made where multiple hits gave similar results. A postal code is |
|||
likely, but a unique iso code could not be identified. |
|||
|
|||
When neither iso or postal code are filled, no proximity match could |
|||
be made. |
|||
''' |
|||
if iso in cls._formats: |
|||
return (iso,) + tuple(cls._formats[iso].split(str_)) |
|||
|
|||
# Find optimum (= max length postalcode) when iso code is unknown |
|||
all = {} |
|||
max_l = 0 |
|||
for key in cls._formats.iterkeys(): |
|||
i, p, c = cls.split(str_, key) |
|||
l = len(p) |
|||
if l > max_l: |
|||
max_l = l |
|||
if l in all: |
|||
all[l].append((i, p, c)) |
|||
else: |
|||
all[l] = [(i, p, c)] |
|||
if max_l > 0: |
|||
if len(all[max_l]) > 1: |
|||
return ('',) + all[max_l][0][1:] |
|||
return all[max_l][0] |
|||
return ('', '', str_) |
|||
|
|||
@classmethod |
|||
def get(cls, iso, str_): |
|||
''' |
|||
Extracts the postal code from str_ following the specs of country |
|||
<iso>. |
|||
''' |
|||
if iso in cls._formats: |
|||
return cls._formats[iso].get(str_) |
|||
return '' |
|||
|
|||
get = PostalCode.get |
|||
split = PostalCode.split |
@ -0,0 +1,57 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
Define a struct class which behaves like a dict, but allows using |
|||
object.attr alongside object['attr']. |
|||
''' |
|||
|
|||
__all__ = ['struct'] |
|||
|
|||
|
|||
class struct(dict): |
|||
''' |
|||
Ease working with dicts. Allow dict.key alongside dict['key'] |
|||
''' |
|||
def __setattr__(self, item, value): |
|||
self.__setitem__(item, value) |
|||
|
|||
def __getattr__(self, item): |
|||
return self.__getitem__(item) |
|||
|
|||
def show(self, indent=0, align=False, ralign=False): |
|||
''' |
|||
PrettyPrint method. Aligns keys right (ralign) or left (align). |
|||
''' |
|||
if align or ralign: |
|||
width = 0 |
|||
for key in self.iterkeys(): |
|||
width = max(width, len(key)) |
|||
alignment = '' |
|||
if not ralign: |
|||
alignment = '-' |
|||
fmt = '%*.*s%%%s%d.%ds: %%s' % ( |
|||
indent, indent, '', alignment, width, width |
|||
) |
|||
else: |
|||
fmt = '%*.*s%%s: %%s' % (indent, indent, '') |
|||
for item in self.iteritems(): |
|||
print(fmt % item) |
@ -0,0 +1,24 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import bank_import |
|||
from . import banking_transaction_wizard |
|||
from . import link_partner |
@ -0,0 +1,338 @@ |
|||
# -*- coding: 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from openerp.tools.translate import _ |
|||
from openerp.addons.account_banking import sepa |
|||
from openerp.addons.account_banking.struct import struct |
|||
|
|||
__all__ = [ |
|||
'get_period', |
|||
'get_bank_accounts', |
|||
'get_partner', |
|||
'get_country_id', |
|||
'get_company_bank_account', |
|||
'create_bank_account', |
|||
] |
|||
|
|||
|
|||
def get_period(pool, cr, uid, date, company, log=None): |
|||
''' |
|||
Wrapper over account_period.find() to log exceptions of |
|||
missing periods instead of raising. |
|||
''' |
|||
context = {'account_period_prefer_normal': True} |
|||
if company: |
|||
context['company_id'] = company.id |
|||
try: |
|||
period_ids = pool.get('account.period').find( |
|||
cr, uid, dt=date, context=context) |
|||
except Exception as e: |
|||
if log is None: |
|||
raise |
|||
else: |
|||
log.append(e) |
|||
return False |
|||
return period_ids[0] |
|||
|
|||
|
|||
def get_bank_accounts(pool, cr, uid, account_number, log, fail=False): |
|||
''' |
|||
Get the bank account with account number account_number |
|||
''' |
|||
# No need to search for nothing |
|||
if not account_number: |
|||
return [] |
|||
|
|||
partner_bank_obj = pool.get('res.partner.bank') |
|||
bank_account_ids = partner_bank_obj.search(cr, uid, [ |
|||
('acc_number', '=', account_number) |
|||
]) |
|||
if not bank_account_ids: |
|||
if not fail: |
|||
log.append( |
|||
_('Bank account %(account_no)s was not found in the database') |
|||
% dict(account_no=account_number) |
|||
) |
|||
return [] |
|||
return partner_bank_obj.browse(cr, uid, bank_account_ids) |
|||
|
|||
|
|||
def _has_attr(obj, attr): |
|||
# Needed for dangling addresses and a weird exception scheme in |
|||
# OpenERP's orm. |
|||
try: |
|||
return bool(getattr(obj, attr)) |
|||
except KeyError: |
|||
return False |
|||
|
|||
|
|||
def get_partner(pool, cr, uid, name, address, postal_code, city, |
|||
country_id, log, context=None): |
|||
''' |
|||
Get the partner belonging to the account holders name <name> |
|||
|
|||
If multiple partners are found with the same name, select the first and |
|||
add a warning to the import log. |
|||
|
|||
TODO: revive the search by lines from the address argument |
|||
''' |
|||
partner_obj = pool.get('res.partner') |
|||
partner_ids = partner_obj.search( |
|||
cr, uid, [ |
|||
'|', ('is_company', '=', True), ('parent_id', '=', False), |
|||
('name', 'ilike', name), |
|||
], context=context) |
|||
if not partner_ids: |
|||
# Try brute search on address and then match reverse |
|||
criteria = [] |
|||
if country_id: |
|||
criteria.append(('country_id', '=', country_id)) |
|||
if city: |
|||
criteria.append(('city', 'ilike', city)) |
|||
if postal_code: |
|||
criteria.append(('zip', 'ilike', postal_code)) |
|||
partner_search_ids = partner_obj.search( |
|||
cr, uid, criteria, context=context) |
|||
if (not partner_search_ids and country_id): |
|||
# Try again with country_id = False |
|||
criteria[0] = ('country_id', '=', False) |
|||
partner_search_ids = partner_obj.search( |
|||
cr, uid, criteria, context=context) |
|||
key = name.lower() |
|||
partners = [] |
|||
for partner in partner_obj.read( |
|||
cr, uid, partner_search_ids, ['name', 'commercial_partner_id'], |
|||
context=context): |
|||
if (len(partner['name']) > 3 and partner['name'].lower() in key): |
|||
partners.append(partner) |
|||
partners.sort(key=lambda x: len(x['name']), reverse=True) |
|||
partner_ids = [x['commercial_partner_id'][0] for x in partners] |
|||
if len(partner_ids) > 1: |
|||
log.append( |
|||
_('More than one possible match found for partner with ' |
|||
'name %(name)s') % {'name': name}) |
|||
return partner_ids and partner_ids[0] or False |
|||
|
|||
|
|||
def get_company_bank_account(pool, cr, uid, account_number, currency, |
|||
company, log): |
|||
''' |
|||
Get the matching bank account for this company. Currency is the ISO code |
|||
for the requested currency. |
|||
''' |
|||
results = struct() |
|||
bank_accounts = get_bank_accounts(pool, cr, uid, account_number, log, |
|||
fail=True) |
|||
if not bank_accounts: |
|||
return False |
|||
elif len(bank_accounts) != 1: |
|||
log.append( |
|||
_('More than one bank account was found with the same number ' |
|||
'%(account_no)s') % dict(account_no=account_number) |
|||
) |
|||
return False |
|||
if bank_accounts[0].partner_id.id != company.partner_id.id: |
|||
log.append( |
|||
_('Account %(account_no)s is not owned by %(partner)s') |
|||
% dict(account_no=account_number, |
|||
partner=company.partner_id.name, |
|||
)) |
|||
return False |
|||
results.account = bank_accounts[0] |
|||
bank_settings_obj = pool.get('account.banking.account.settings') |
|||
criteria = [('partner_bank_id', '=', bank_accounts[0].id)] |
|||
|
|||
# Find matching journal for currency |
|||
journal_obj = pool.get('account.journal') |
|||
journal_ids = journal_obj.search(cr, uid, [ |
|||
('type', '=', 'bank'), |
|||
('currency.name', '=', currency or company.currency_id.name) |
|||
]) |
|||
if currency == company.currency_id.name: |
|||
journal_ids_no_curr = journal_obj.search(cr, uid, [ |
|||
('type', '=', 'bank'), ('currency', '=', False) |
|||
]) |
|||
journal_ids.extend(journal_ids_no_curr) |
|||
if journal_ids: |
|||
criteria.append(('journal_id', 'in', journal_ids)) |
|||
|
|||
# Find bank account settings |
|||
bank_settings_ids = bank_settings_obj.search(cr, uid, criteria) |
|||
if bank_settings_ids: |
|||
settings = bank_settings_obj.browse(cr, uid, bank_settings_ids)[0] |
|||
results.company_id = company |
|||
results.journal_id = settings.journal_id |
|||
|
|||
# Take currency from settings or from company |
|||
if settings.journal_id.currency.id: |
|||
results.currency_id = settings.journal_id.currency |
|||
else: |
|||
results.currency_id = company.currency_id |
|||
# Rest just copy/paste from settings. Why am I doing this? |
|||
results.default_debit_account_id = settings.default_debit_account_id |
|||
results.default_credit_account_id = settings.default_credit_account_id |
|||
results.costs_account_id = settings.costs_account_id |
|||
results.invoice_journal_id = settings.invoice_journal_id |
|||
results.bank_partner_id = settings.bank_partner_id |
|||
|
|||
return results |
|||
|
|||
|
|||
def get_or_create_bank(pool, cr, uid, bic, online=False, code=None, |
|||
name=None, context=None): |
|||
''' |
|||
Find or create the bank with the provided BIC code. |
|||
When online, the SWIFT database will be consulted in order to |
|||
provide for missing information. |
|||
''' |
|||
# UPDATE: Free SWIFT databases are since 2/22/2010 hidden behind an |
|||
# image challenge/response interface. |
|||
|
|||
bank_obj = pool.get('res.bank') |
|||
|
|||
# Self generated key? |
|||
if len(bic) < 8: |
|||
# search key |
|||
bank_ids = bank_obj.search( |
|||
cr, uid, [ |
|||
('bic', '=', bic[:6]) |
|||
]) |
|||
if not bank_ids: |
|||
bank_ids = bank_obj.search( |
|||
cr, uid, [ |
|||
('bic', 'ilike', bic + '%') |
|||
]) |
|||
else: |
|||
bank_ids = bank_obj.search( |
|||
cr, uid, [ |
|||
('bic', '=', bic) |
|||
]) |
|||
|
|||
if bank_ids and len(bank_ids) == 1: |
|||
banks = bank_obj.browse(cr, uid, bank_ids) |
|||
return banks[0].id, banks[0].country.id |
|||
|
|||
country_obj = pool.get('res.country') |
|||
country_ids = country_obj.search( |
|||
cr, uid, [('code', '=', bic[4:6])] |
|||
) |
|||
country_id = country_ids and country_ids[0] or False |
|||
bank_id = False |
|||
|
|||
if online: |
|||
info, address = bank_obj.online_bank_info( |
|||
cr, uid, bic, context=context |
|||
) |
|||
if info: |
|||
bank_id = bank_obj.create(cr, uid, dict( |
|||
code=info.code, |
|||
name=info.name, |
|||
street=address.street, |
|||
street2=address.street2, |
|||
zip=address.zip, |
|||
city=address.city, |
|||
country=country_id, |
|||
bic=info.bic[:8], |
|||
)) |
|||
else: |
|||
info = struct(name=name, code=code) |
|||
|
|||
if not online or not bank_id: |
|||
bank_id = bank_obj.create(cr, uid, dict( |
|||
code=info.code or 'UNKNOW', # FIXME: Typo? |
|||
name=info.name or _('Unknown Bank'), |
|||
country=country_id, |
|||
bic=bic, |
|||
)) |
|||
return bank_id, country_id |
|||
|
|||
|
|||
def get_country_id(pool, cr, uid, transaction, context=None): |
|||
""" |
|||
Derive a country id from the info on the transaction. |
|||
|
|||
:param transaction: browse record of a transaction |
|||
:returns: res.country id or False |
|||
""" |
|||
|
|||
country_code = False |
|||
iban = sepa.IBAN(transaction.remote_account) |
|||
if iban.valid: |
|||
country_code = iban.countrycode |
|||
elif transaction.remote_owner_country_code: |
|||
country_code = transaction.remote_owner_country_code |
|||
# fallback on the import parsers country code |
|||
elif transaction.bank_country_code: |
|||
country_code = transaction.bank_country_code |
|||
if country_code: |
|||
country_ids = pool.get('res.country').search( |
|||
cr, uid, [('code', '=', country_code.upper())], |
|||
context=context) |
|||
country_id = country_ids and country_ids[0] or False |
|||
if not country_id: |
|||
company = transaction.statement_line_id.company_id |
|||
if company.partner_id.country: |
|||
country_id = company.partner_id.country.id |
|||
return country_id |
|||
|
|||
|
|||
def create_bank_account(pool, cr, uid, partner_id, |
|||
account_number, holder_name, address, city, |
|||
country_id, bic=False, |
|||
context=None): |
|||
''' |
|||
Create a matching bank account with this holder for this partner. |
|||
''' |
|||
values = struct( |
|||
partner_id=partner_id, |
|||
owner_name=holder_name, |
|||
country_id=country_id, |
|||
) |
|||
|
|||
# Are we dealing with IBAN? |
|||
iban = sepa.IBAN(account_number) |
|||
if iban.valid: |
|||
# Take as much info as possible from IBAN |
|||
values.state = 'iban' |
|||
values.acc_number = str(iban) |
|||
else: |
|||
# No, try to convert to IBAN |
|||
values.state = 'bank' |
|||
values.acc_number = account_number |
|||
|
|||
if country_id: |
|||
country_code = pool.get('res.country').read( |
|||
cr, uid, country_id, ['code'], context=context)['code'] |
|||
if country_code in sepa.IBAN.countries: |
|||
account_info = pool['res.partner.bank'].online_account_info( |
|||
cr, uid, country_code, values.acc_number, context=context) |
|||
if account_info: |
|||
values.acc_number = iban = account_info.iban |
|||
values.state = 'iban' |
|||
bic = account_info.bic |
|||
|
|||
if bic: |
|||
values.bank = get_or_create_bank(pool, cr, uid, bic)[0] |
|||
values.bank_bic = bic |
|||
|
|||
# Create bank account and return |
|||
return pool.get('res.partner.bank').create( |
|||
cr, uid, values, context=context) |
@ -0,0 +1,208 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2013 Therp BV (<http://therp.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 openerp.osv import orm, fields |
|||
from openerp.tools.translate import _ |
|||
from openerp.addons.account_banking.wizard import banktools |
|||
import ast |
|||
|
|||
|
|||
class link_partner(orm.TransientModel): |
|||
_name = 'banking.link_partner' |
|||
_description = 'Link partner' |
|||
|
|||
_columns = { |
|||
'name': fields.char( |
|||
'Create partner with name', size=128, required=True), |
|||
'supplier': fields.boolean('Supplier'), |
|||
'customer': fields.boolean('Customer'), |
|||
'partner_id': fields.many2one( |
|||
'res.partner', 'or link existing partner', |
|||
domain=['|', ('is_company', '=', True), |
|||
('parent_id', '=', False)], |
|||
), |
|||
'statement_line_id': fields.many2one( |
|||
'account.bank.statement.line', |
|||
'Statement line', required=True), |
|||
'amount': fields.related( |
|||
'statement_line_id', 'amount', type='float', |
|||
string="Amount", readonly=True), |
|||
'ref': fields.related( |
|||
'statement_line_id', 'ref', type='char', size=32, |
|||
string="Reference", readonly=True), |
|||
'message': fields.related( |
|||
'statement_line_id', 'import_transaction_id', 'message', |
|||
type='char', size=1024, |
|||
string="Message", readonly=True), |
|||
'remote_account': fields.char( |
|||
'Account number', size=24, readonly=True), |
|||
# Partner values |
|||
'street': fields.char('Street', size=128), |
|||
'street2': fields.char('Street2', size=128), |
|||
'zip': fields.char('Zip', change_default=True, size=24), |
|||
'city': fields.char('City', size=128), |
|||
'state_id': fields.many2one("res.country.state", 'State'), |
|||
'country_id': fields.many2one('res.country', 'Country'), |
|||
'email': fields.char('Email', size=240), |
|||
'phone': fields.char('Phone', size=64), |
|||
'fax': fields.char('Fax', size=64), |
|||
'mobile': fields.char('Mobile', size=64), |
|||
'is_company': fields.boolean('Is a Company'), |
|||
} |
|||
|
|||
_defaults = { |
|||
'is_company': True, |
|||
} |
|||
|
|||
def create(self, cr, uid, vals, context=None): |
|||
""" |
|||
Get default values from the transaction data |
|||
on the statement line |
|||
""" |
|||
if vals and vals.get('statement_line_id'): |
|||
statement_line_obj = self.pool.get('account.bank.statement.line') |
|||
statement_line = statement_line_obj.browse( |
|||
cr, uid, vals['statement_line_id'], context=context) |
|||
transaction = statement_line.import_transaction_id |
|||
|
|||
if statement_line.partner_bank_id: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('Statement line is already linked to a bank account ')) |
|||
|
|||
if not(transaction |
|||
and transaction.remote_account): |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('No transaction data on statement line')) |
|||
|
|||
if 'supplier' not in vals and statement_line.amount < 0: |
|||
vals['supplier'] = True |
|||
if 'customer' not in vals and statement_line.amount > 0: |
|||
vals['customer'] = True |
|||
|
|||
address_list = [] |
|||
try: |
|||
address_list = ast.literal_eval( |
|||
transaction.remote_owner_address or []) |
|||
except ValueError: |
|||
pass |
|||
if address_list and not vals.get('street'): |
|||
vals['street'] = address_list.pop(0) |
|||
if address_list and not vals.get('street2'): |
|||
vals['street2'] = address_list.pop(0) |
|||
if transaction.remote_owner_postalcode and not vals.get('zip'): |
|||
vals['zip'] = transaction.remote_owner_postalcode |
|||
if transaction.remote_owner_city and not vals.get('city'): |
|||
vals['city'] = transaction.remote_owner_city |
|||
if not vals.get('country_id'): |
|||
vals['country_id'] = banktools.get_country_id( |
|||
self.pool, cr, uid, transaction, context=context) |
|||
if not vals.get('name'): |
|||
vals['name'] = transaction.remote_owner |
|||
if not vals['name']: |
|||
vals['name'] = '/' |
|||
if not vals.get('remote_account'): |
|||
vals['remote_account'] = transaction.remote_account |
|||
|
|||
return super(link_partner, self).create( |
|||
cr, uid, vals, context=context) |
|||
|
|||
def update_partner_values(self, cr, uid, wizard, values, context=None): |
|||
""" |
|||
Updates the new partner values with the values from the wizard |
|||
|
|||
:param wizard: read record of wizard (with load='_classic_write') |
|||
:param values: the dictionary of partner values that will be updated |
|||
""" |
|||
for field in ['is_company', |
|||
'name', |
|||
'street', |
|||
'street2', |
|||
'zip', |
|||
'city', |
|||
'country_id', |
|||
'state_id', |
|||
'phone', |
|||
'fax', |
|||
'mobile', |
|||
'email' |
|||
]: |
|||
if wizard[field]: |
|||
values[field] = wizard[field] |
|||
return True |
|||
|
|||
def link_partner(self, cr, uid, ids, context=None): |
|||
statement_line_obj = self.pool.get( |
|||
'account.bank.statement.line') |
|||
wiz = self.browse(cr, uid, ids[0], context=context) |
|||
|
|||
if wiz.partner_id: |
|||
partner_id = wiz.partner_id.id |
|||
else: |
|||
wiz_read = self.read( |
|||
cr, uid, ids[0], context=context, load='_classic_write') |
|||
partner_vals = { |
|||
'type': 'default', |
|||
} |
|||
self.update_partner_values( |
|||
cr, uid, wiz_read, partner_vals, context=context) |
|||
partner_id = self.pool.get('res.partner').create( |
|||
cr, uid, partner_vals, context=context) |
|||
|
|||
partner_bank_id = banktools.create_bank_account( |
|||
self.pool, cr, uid, partner_id, |
|||
wiz.remote_account, wiz.name, |
|||
wiz.street, wiz.city, |
|||
wiz.country_id and wiz.country_id.id or False, |
|||
bic=wiz.statement_line_id.import_transaction_id.remote_bank_bic, |
|||
context=context) |
|||
|
|||
statement_line_ids = statement_line_obj.search( |
|||
cr, uid, |
|||
[('import_transaction_id.remote_account', '=', wiz.remote_account), |
|||
('partner_bank_id', '=', False), |
|||
('state', '=', 'draft')], context=context) |
|||
statement_line_obj.write( |
|||
cr, uid, statement_line_ids, |
|||
{'partner_bank_id': partner_bank_id, |
|||
'partner_id': partner_id}, context=context) |
|||
|
|||
return {'type': 'ir.actions.act_window_close'} |
|||
|
|||
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None): |
|||
""" |
|||
Return a popup window for this model |
|||
""" |
|||
if isinstance(ids, (int, long)): |
|||
ids = [ids] |
|||
return { |
|||
'name': self._description, |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': self._name, |
|||
'domain': [], |
|||
'context': context, |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'new', |
|||
'res_id': ids[0], |
|||
'nodestroy': nodestroy, |
|||
} |
@ -0,0 +1,61 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record model="ir.ui.view" id="link_partner_view"> |
|||
<field name="name">Link partner wizard view</field> |
|||
<field name="type">form</field> |
|||
<field name="model">banking.link_partner</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Link partner" version="7.0" > |
|||
<group colspan="4" col="6" string="Transaction data"> |
|||
<field name="ref" /> |
|||
<field name="amount" /> |
|||
<field name="remote_account" /> |
|||
<field name="message" colspan="6"/> |
|||
</group> |
|||
<group colspan="4" col="4" string="Create or link partner"> |
|||
<field name="name" |
|||
attrs="{'readonly': [('partner_id', '!=', False)]}" /> |
|||
<field name="partner_id"/> |
|||
</group> |
|||
<group colspan="2"> |
|||
<field name="is_company" /> |
|||
</group> |
|||
<group colspan="4" |
|||
string="Address" |
|||
attrs="{'invisible': [('partner_id', '!=', False)]}" |
|||
col="4"> |
|||
<group colspan="2" col="2"> |
|||
<field name="street"/> |
|||
<field name="street2"/> |
|||
<field name="zip"/> |
|||
<field name="city"/> |
|||
<field name="country_id"/> |
|||
<field name="state_id"/> |
|||
</group> |
|||
<group colspan="2" col="2"> |
|||
<field name="phone"/> |
|||
<field name="fax"/> |
|||
<field name="mobile"/> |
|||
<field name="email" widget="email"/> |
|||
</group> |
|||
</group> |
|||
<footer> |
|||
<button string="Create partner" |
|||
name="link_partner" type="object" |
|||
class="oe_highlight" |
|||
attrs="{'invisible': [('partner_id', '!=', False)]}" |
|||
/> |
|||
<button string="Link existing partner" |
|||
class="oe_highlight" |
|||
name="link_partner" type="object" |
|||
attrs="{'invisible': [('partner_id', '==', False)]}" |
|||
/> |
|||
or |
|||
<button class="oe_link" string="Cancel" special="cancel" /> |
|||
</footer> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,18 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<!-- |
|||
Transition to reopening the invoice, triggered by |
|||
cancelling a bank transaction with which the invoice |
|||
was paid. |
|||
the existing workflow contains a similar step |
|||
but without the test on being reconciled |
|||
--> |
|||
<record id="paid_to_open" model="workflow.transition"> |
|||
<field name="act_from" ref="account.act_paid"/> |
|||
<field name="act_to" ref="account.act_open_test"/> |
|||
<field name="condition">test_undo_paid()</field> |
|||
<field name="signal">undo_paid</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,29 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2010 Sami Haahtinen (<http://ressukka.net>). |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import patu |
@ -0,0 +1,43 @@ |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2010 Sami Haahtinen (<http://ressukka.net>). |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'Account Banking PATU module', |
|||
'version': '0.62', |
|||
'license': 'AGPL-3', |
|||
'author': 'Sami Haahtinen', |
|||
'website': 'http://ressukka.net', |
|||
'category': 'Account Banking', |
|||
'depends': ['account_banking'], |
|||
'description': ''' |
|||
Module to import Finnish PATU format transation files. |
|||
|
|||
This modules contains no logic, just an import filter for account_banking. |
|||
''', |
|||
'active': False, |
|||
'installable': False, |
|||
} |
@ -0,0 +1,31 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_fi_patu |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:53+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:115 |
|||
#, python-format |
|||
msgid "PATU statement sheet" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:116 |
|||
#, python-format |
|||
msgid "PATU statement format defines one or more statements in each file. This parser\n" |
|||
"will parse all statements in a file and import them to OpenERP\n" |
|||
"" |
|||
msgstr "" |
|||
|
@ -0,0 +1,37 @@ |
|||
# Dutch translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-12-03 11:08+0000\n" |
|||
"Last-Translator: Erwin van der Ploeg (BAS Solutions) <Unknown>\n" |
|||
"Language-Team: Dutch <nl@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:115 |
|||
#, python-format |
|||
msgid "PATU statement sheet" |
|||
msgstr "PATU bankafschrift" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:116 |
|||
#, python-format |
|||
msgid "" |
|||
"PATU statement format defines one or more statements in each file. This " |
|||
"parser\n" |
|||
"will parse all statements in a file and import them to OpenERP\n" |
|||
msgstr "" |
|||
"PATU afschrift formaat definieert een of meerdere afschriften in ieder " |
|||
"bestand. deze parser\n" |
|||
"zal alle afschriften verwerken in het bestand en deze importeren in " |
|||
"OpenERP.\n" |
@ -0,0 +1,33 @@ |
|||
# Brazilian Portuguese translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-12-25 12:32+0000\n" |
|||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:115 |
|||
#, python-format |
|||
msgid "PATU statement sheet" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_fi_patu |
|||
#: code:addons/account_banking_fi_patu/patu.py:116 |
|||
#, python-format |
|||
msgid "" |
|||
"PATU statement format defines one or more statements in each file. This " |
|||
"parser\n" |
|||
"will parse all statements in a file and import them to OpenERP\n" |
|||
msgstr "" |
@ -0,0 +1,250 @@ |
|||
#!/usr/bin/env python |
|||
# encoding: utf-8 |
|||
"""Parser for PATU format files""" |
|||
import re |
|||
import datetime |
|||
|
|||
|
|||
def fixchars(line): |
|||
"""Fix the characters mangled in the input |
|||
|
|||
:param line: Line to rewrite |
|||
|
|||
:returns: string, fixed line |
|||
""" |
|||
# Fix the umlauts int the input |
|||
line = line.replace("{", u"ä") |
|||
line = line.replace("}", u"ö") |
|||
# XXX: There are a whole bunch of these, adding them later |
|||
return line |
|||
|
|||
|
|||
class PatuParser(object): |
|||
"""Parse PATU lines in to structs""" |
|||
|
|||
def __init__(self): |
|||
""" Initialize PATU parser """ |
|||
|
|||
recparse = dict() |
|||
recparse["00"] = ( |
|||
"T(?P<recordid>00)(?P<record_len>\d{3})" |
|||
"(?P<version>\d{3})(?P<accountnr>\d{14})" |
|||
"(?P<statementnr>\d{3})(?P<startdate>\d{6})" |
|||
"(?P<enddate>\d{6})" |
|||
"(?P<creationdate>\d{6})(?P<creationtime>\d{4})" |
|||
"(?P<customerid>.{17})(?P<balancedate>\d{6})" |
|||
"(?P<startingbalance>.{19})" |
|||
"(?P<itemcount>\d{6})(?P<currency>.{3})" |
|||
"(?P<accountname>.{30})" |
|||
"(?P<accountlimit>\d{18})(?P<accountowner>.{35})" |
|||
"(?P<bankcontact1>.{40})(?P<bankcontact2>.{40})" |
|||
"(?P<bankcontact3>.{30})(?P<ibanswift>.{30})" |
|||
) |
|||
recparse["10"] = ( |
|||
"T(?P<recordid>[18]0)(?P<record_len>\d{3})" |
|||
"(?P<eventid>\d{6})" |
|||
"(?P<archivalnr>.{18})(?P<recorddate>\d{6})" |
|||
"(?P<valuedate>\d{6})" |
|||
"(?P<paymentdate>\d{6})(?P<eventtype>\d)" |
|||
"(?P<eventcode>.{3})(?P<eventdesc>.{35})" |
|||
"(?P<amount>.{19})(?P<receiptcode>.)(?P<creationmethod>.)" |
|||
"(?P<recipientname>.{35})(?P<recipientsource>.)" |
|||
"(?P<recipientaccount>.{14})(?P<recipientaccountchanged>.)" |
|||
"(?P<refnr>.{20})" |
|||
"(?P<formnr>.{8})(?P<eventlevel>.)" |
|||
) |
|||
recparse["11"] = ( |
|||
"T(?P<recordid>[18]1)(?P<record_len>\d{3})" |
|||
"(?P<infotype>.{2})" |
|||
"(?:(?# Match specific info)" |
|||
"(?<=00)(?P<message>.{35})+" |
|||
"|" |
|||
"(?<=01)(?P<transactioncount>\d{8})" |
|||
"|" |
|||
"(?<=02)(?P<customerid>.{10})\s(?P<invoicenr>.{15})\s" |
|||
"(?P<invoicedate>\d{6})" |
|||
"|" |
|||
"(?<=03)(?P<cardnumber>.{19})\s(?P<storereference>.{14})" |
|||
"|" |
|||
"(?<=04)(?P<origarchiveid>.{18})" |
|||
"|" |
|||
"(?<=05)(?P<destinationamount>.{19})\s(?P<currency>.{3})\s" |
|||
"(?P<exchangerate>.{11})(?P<rateref>.{6})" |
|||
"|" |
|||
"(?<=06)(?P<principalinfo1>.{35})(?P<principalinfo2>.{35})" |
|||
"|" |
|||
"(?<=07)(?P<bankinfo1>.{35})" |
|||
"(?P<bankinfo2>.{35})?" |
|||
"(?P<bankinfo3>.{35})?" |
|||
"(?P<bankinfo4>.{35})?" |
|||
"(?P<bankinfo5>.{35})?" |
|||
"(?P<bankinfo6>.{35})?" |
|||
"(?P<bankinfo7>.{35})?" |
|||
"(?P<bankinfo8>.{35})?" |
|||
"(?P<bankinfo9>.{35})?" |
|||
"(?P<bankinfo10>.{35})?" |
|||
"(?P<bankinfo11>.{35})?" |
|||
"(?P<bankinfo12>.{35})?" |
|||
"|" |
|||
"(?<=08)(?P<paymentcode>\d{3})\s(?P<paymentdesc>.{31})" |
|||
"|" |
|||
"(?<=09)(?P<recipientname2>.{35})" |
|||
"|" |
|||
"(?<=11)(?P<reference>.{35})(?P<recipientiban>.{35})" |
|||
"(?P<recipientbic>.{35})(?P<recipientnameiban>.{70})" |
|||
"(?P<sendername>.{70})(?P<senderid>.{35})" |
|||
"(?P<archivalid>.{70})" |
|||
")" |
|||
) |
|||
recparse["40"] = ( |
|||
"T(?P<recordid>40)(?P<record_len>\d{3})" |
|||
"(?P<recorddate>\d{6})(?P<balance>.{19})" |
|||
"(?P<availablefunds>.{19})" |
|||
) |
|||
recparse["50"] = ( |
|||
"T(?P<recordid>50)(?P<record_len>\d{3})" |
|||
"(?P<period>\d)(?P<perioddate>\d{6})" |
|||
"(?P<depositcount>\d{8})(?P<depositsum>.{19})" |
|||
"(?P<withdrawcount>\d{8})(?P<withdrawsum>.{19})" |
|||
) |
|||
recparse["60"] = ( |
|||
"T(?P<recordid>60)(?P<record_len>\d{3})" |
|||
"(?P<bankid>.{3})(?P<specialid>01)" |
|||
"(?P<interestperiodstart>\d{6})-" |
|||
"(?P<interestperiodend>\d{6})" |
|||
"(?P<avgbalanceinfo>.)(?P<avgbalance>.{19})" |
|||
"(?P<interestinfo>.)(?P<interestrate>\d{7})" |
|||
"(?P<limitbalanceinfo>.)(?P<avglimitbalance>.{19})" |
|||
"(?P<limitinterestinfo>.)(?P<limitinterestrate>\d{7})" |
|||
"(?P<limitusageinfo>.)(?P<limitusage>\d{7})" |
|||
"(?P<permanentbalanceinfo>.)(?P<permanentbalance>.{19})" |
|||
"(?P<refinterestinfo>.)(?P<refinterestname>.{35})" |
|||
"(?P<refinterestrate>\d{7})" |
|||
"(?P<refcreditinfo>.)(?P<refcreditname>.{35})" |
|||
"(?P<refcreditrate>\d{7})" |
|||
) |
|||
recparse["70"] = ( |
|||
"T(?P<recordid>70)(?P<record_len>\d{3})" |
|||
"(?P<bankid>\d{3})" |
|||
"(?P<infoline1>.{80})" |
|||
"(?P<infoline2>.{80})?" |
|||
"(?P<infoline3>.{80})?" |
|||
"(?P<infoline4>.{80})?" |
|||
"(?P<infoline5>.{80})?" |
|||
"(?P<infoline6>.{80})?" |
|||
) |
|||
for record in recparse: |
|||
recparse[record] = re.compile(recparse[record]) |
|||
self.recparse = recparse |
|||
|
|||
def parse_record(self, line): |
|||
"""Docstring for parse_perus |
|||
|
|||
:param line: description |
|||
|
|||
:returns: description |
|||
""" |
|||
line = fixchars(line) |
|||
for matcher in self.recparse: |
|||
matchobj = self.recparse[matcher].match(line) |
|||
if matchobj: |
|||
break |
|||
if not matchobj: |
|||
print(" **** failed to match line '%s'" % (line)) |
|||
return |
|||
# Strip strings |
|||
matchdict = matchobj.groupdict() |
|||
|
|||
# Remove members set to None |
|||
for field in matchdict.keys(): |
|||
if not matchdict[field]: |
|||
del matchdict[field] |
|||
|
|||
matchkeys = set(matchdict.keys()) |
|||
needstrip = set([ |
|||
"bankcontact1", "bankcontact2", "bankcontact3", |
|||
"customerid", "accountowner", "accountname", "refnr", "formnr", |
|||
"recipientname", "eventdesc", "recipientaccount", "message", |
|||
"principalinfo1", "bankinfo1", "bankinfo2", "bankinfo3", |
|||
"bankinfo4", "bankinfo5", "bankinfo6", "bankinfo7", "bankinfo8", |
|||
"bankinfo9", "bankinfo10", "bankinfo11", "bankinfo12", |
|||
"principalinfo2", "paymentdesc", "infoline1", "infoline2", |
|||
"infoline3", "infoline4", "infoline5", "infoline6", |
|||
"recipientname2", "recipientnameiban", "sendername"]) |
|||
for field in matchkeys & needstrip: |
|||
matchdict[field] = matchdict[field].strip() |
|||
# Convert to int |
|||
needsint = set([ |
|||
"itemcount", "eventid", "record_len", |
|||
"depositcount", "withdrawcount"]) |
|||
for field in matchkeys & needsint: |
|||
matchdict[field] = float(matchdict[field]) |
|||
# Convert to float |
|||
needsfloat = set([ |
|||
"startingbalance", "accountlimit", "amount", |
|||
"destinationamount", "balance", "availablefunds", "depositsum", |
|||
"withdrawsum", "avgbalance", "avglimitbalance", |
|||
"permanentbalance"]) |
|||
for field in matchkeys & needsfloat: |
|||
matchdict[field] = float(matchdict[field]) |
|||
# convert sents to euros |
|||
needseur = set([ |
|||
"startingbalance", "accountlimit", "amount", |
|||
"destinationamount", "balance", "availablefunds", "depositsum", |
|||
"withdrawsum", "avgbalance", "permanentbalance"]) |
|||
for field in matchkeys & needseur: |
|||
matchdict[field] = matchdict[field] / 100 |
|||
# convert ibanswift to separate fields |
|||
if "ibanswift" in matchdict: |
|||
matchdict["iban"], matchdict["swift"] = ( |
|||
matchdict["ibanswift"].strip().split() |
|||
) |
|||
|
|||
# Convert date fields |
|||
needdate = set([ |
|||
"startdate", "enddate", "creationdate", "balancedate", |
|||
"valuedate", "paymentdate", "recorddate", "perioddate"]) |
|||
for field in matchkeys & needdate: |
|||
# Base all dates on the year 2000, since it's unlikely that this |
|||
# starndard will survive to see 2020 due to SEPA |
|||
datestring = matchdict[field] |
|||
if datestring == '000000': |
|||
matchdict[field] = None |
|||
continue |
|||
|
|||
matchdict[field] = datetime.date( |
|||
int("20" + datestring[0:2]), |
|||
int(datestring[2:4]), int(datestring[4:6])) |
|||
# convert time fields |
|||
needtime = set(["creationtime"]) |
|||
for field in matchkeys & needtime: |
|||
timestring = matchdict[field] |
|||
matchdict[field] = datetime.time( |
|||
int(timestring[0:2]), |
|||
int(timestring[2:4])) |
|||
|
|||
return matchdict |
|||
|
|||
|
|||
def parse_file(filename): |
|||
"""Parse file with PATU format inside |
|||
|
|||
:param filename: description |
|||
|
|||
:returns: description |
|||
""" |
|||
patufile = open(filename, "r") |
|||
parser = PatuParser() |
|||
for line in patufile: |
|||
parser.parse_record(line) |
|||
|
|||
|
|||
def main(): |
|||
"""The main function, currently just calls a dummy filename |
|||
|
|||
:returns: description |
|||
""" |
|||
parse_file("myinput.nda") |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -0,0 +1,132 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2010 Sami Haahtinen (<http://ressukka.net>). |
|||
# 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
""" |
|||
This parser implements the PATU format support. PATU format is a generic format |
|||
used by finnish banks. |
|||
""" |
|||
from openerp.addons.account_banking.parsers import models |
|||
from openerp.tools.translate import _ |
|||
from openerp.addons.account_banking_fi_patu.parser import PatuParser |
|||
|
|||
__all__ = ['Parser'] |
|||
|
|||
|
|||
class Transaction(models.mem_bank_transaction): |
|||
"""Implementation of transaction communication class for account_banking. |
|||
""" |
|||
mapping = { |
|||
"remote_account": "recipientaccount", |
|||
"remote_currency": "currency", |
|||
"transferred_amount": "amount", |
|||
"execution_date": "recorddate", |
|||
"value_date": "paymentdate", |
|||
"transfer_type": "eventtype", |
|||
"reference": "refnr", |
|||
"eventcode": "eventcode", |
|||
"message": "message" |
|||
} |
|||
|
|||
def __init__(self, record, *args, **kwargs): |
|||
"""Initialize own dict with read values.""" |
|||
super(Transaction, self).__init__(*args, **kwargs) |
|||
for key in self.mapping: |
|||
try: |
|||
setattr(self, key, record[self.mapping[key]]) |
|||
except KeyError: |
|||
pass |
|||
|
|||
def is_valid(self): |
|||
"""Override validity checks. |
|||
There are certain situations for PATU which can be validated as |
|||
invalid, but are normal. |
|||
If eventcode is 730, the transaction was initiated by the bank and |
|||
doesn't have a destination account. |
|||
""" |
|||
if self.eventcode in ["720", "710"]: |
|||
# Withdrawal from and deposit to the account |
|||
return (self.execution_date and self.transferred_amount and True) \ |
|||
or False |
|||
if self.eventcode and self.eventcode == "730": |
|||
# The transaction is bank initiated, no remote account is present |
|||
return (self.execution_date and self.transferred_amount and True) \ |
|||
or False |
|||
return super(Transaction, self).is_valid() |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
"""Implementation of bank_statement communication class of account_banking |
|||
""" |
|||
def __init__(self, record, *args, **kwargs): |
|||
""" |
|||
Set decent start values based on first transaction read |
|||
""" |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = record["statementnr"] |
|||
self.local_account = self.convert_bank_account(record["accountnr"]) |
|||
self.date = record["creationdate"] |
|||
self.start_balance = record["startingbalance"] |
|||
|
|||
def convert_bank_account(self, accountnr): |
|||
"""Convert bank account number in to a abbreviated format used in |
|||
finland""" |
|||
bank = accountnr[:6] |
|||
account = accountnr[6:].lstrip("0") |
|||
return "%s-%s" % (bank, account) |
|||
|
|||
def import_transaction(self, record): |
|||
"""Import a transaction to the statement""" |
|||
if record["recordid"] == "40": |
|||
self.end_balance = record["balance"] |
|||
elif record["recordid"] == "10" or record["recordid"] == "80": |
|||
# XXX: Sum up entries that have detailed records set for them. For |
|||
# now, ignore the parent entry |
|||
if record["receiptcode"] == "E": |
|||
return |
|||
self.transactions.append(Transaction(record)) |
|||
|
|||
|
|||
class Parser(models.parser): |
|||
code = 'FIPATU' |
|||
name = _('PATU statement sheet') |
|||
doc = _('''\ |
|||
PATU statement format defines one or more statements in each file. This parser |
|||
will parse all statements in a file and import them to OpenERP |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
patuparser = PatuParser() |
|||
for line in data.splitlines(): |
|||
# Skip empty (last) lines |
|||
if not line: |
|||
continue |
|||
record = patuparser.parse_record(line) |
|||
if record["recordid"] == "00": |
|||
# New statement |
|||
stmnt = statement(record) |
|||
result.append(stmnt) |
|||
else: |
|||
stmnt.import_transaction(record) |
|||
result.append(stmnt) |
|||
return result |
@ -0,0 +1,2 @@ |
|||
# -*- coding: utf-8 -*- |
|||
import abnamro |
@ -0,0 +1,42 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 - 2011 EduSense BV (<http://www.edusense.nl>) |
|||
# and Therp BV (<http://therp.nl>) |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'abnamro (NL) Bank Statements Import', |
|||
'version': '0.1', |
|||
'license': 'AGPL-3', |
|||
'author': ['Therp BV', 'EduSense BV'], |
|||
'website': 'https://launchpad.net/banking-addons', |
|||
'category': 'Banking addons', |
|||
'depends': ['account_banking'], |
|||
'description': ''' |
|||
Import filter for abnamro (NL) bank transaction files (txt/tab format). |
|||
|
|||
No formal specifications of the file layout are released by abnamro. You can |
|||
help improve the performance of this import filter on |
|||
https://launchpad.net/account-banking. |
|||
|
|||
Imported bank transfers are organized in statements covering periods of one |
|||
week, even if the imported files cover a different period. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,394 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>) |
|||
# 2011 - 2013 Therp BV (<http://therp.nl>) |
|||
# |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This parser follows the Dutch Banking Tools specifications which are |
|||
empirically recreated in this module. |
|||
|
|||
Dutch Banking Tools uses the concept of 'Afschrift' or Bank Statement. |
|||
Every transaction is bound to a Bank Statement. As such, this module generates |
|||
Bank Statements along with Bank Transactions. |
|||
''' |
|||
from openerp.addons.account_banking.parsers import models |
|||
from openerp.addons.account_banking.parsers.convert import str2date |
|||
from openerp.tools.translate import _ |
|||
from openerp.osv import orm |
|||
|
|||
import re |
|||
import csv |
|||
|
|||
__all__ = ['parser'] |
|||
|
|||
bt = models.mem_bank_transaction |
|||
|
|||
|
|||
class transaction_message(object): |
|||
''' |
|||
A auxiliary class to validate and coerce read values |
|||
''' |
|||
attrnames = [ |
|||
'local_account', 'local_currency', 'date', 'u1', 'u2', 'date2', |
|||
'transferred_amount', 'blob', |
|||
] |
|||
|
|||
def __init__(self, values, subno): |
|||
''' |
|||
Initialize own dict with attributes and coerce values to right type |
|||
''' |
|||
if len(self.attrnames) != len(values): |
|||
raise ValueError( |
|||
_('Invalid transaction line: expected %d columns, found ' |
|||
'%d') % (len(self.attrnames), len(values)) |
|||
) |
|||
''' Strip all values except the blob ''' |
|||
for (key, val) in zip(self.attrnames, values): |
|||
self.__dict__[key] = key == 'blob' and val or val.strip() |
|||
# for lack of a standardized locale function to parse amounts |
|||
self.local_account = self.local_account.zfill(10) |
|||
self.transferred_amount = float( |
|||
self.transferred_amount.replace(',', '.')) |
|||
self.execution_date = str2date(self.date, '%Y%m%d') |
|||
self.value_date = str2date(self.date, '%Y%m%d') |
|||
# Set statement_id based on week number |
|||
self.statement_id = self.execution_date.strftime('%Yw%W') |
|||
self.id = str(subno).zfill(4) |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
''' |
|||
Implementation of transaction communication class for account_banking. |
|||
''' |
|||
attrnames = ['local_account', 'local_currency', 'transferred_amount', |
|||
'blob', 'execution_date', 'value_date', 'id', |
|||
] |
|||
|
|||
type_map = { |
|||
# retrieved from online help in the Triodos banking application |
|||
'BEA': bt.PAYMENT_TERMINAL, # Pin |
|||
'GEA': bt.BANK_TERMINAL, # ATM |
|||
'COSTS': bt.BANK_COSTS, |
|||
'BANK': bt.ORDER, |
|||
'GIRO': bt.ORDER, |
|||
'INTL': bt.ORDER, # international order |
|||
'UNKN': bt.ORDER, # everything else |
|||
'SEPA': bt.ORDER, |
|||
'PAYB': bt.PAYMENT_BATCH, |
|||
'RETR': bt.STORNO, |
|||
} |
|||
|
|||
def __init__(self, line, *args, **kwargs): |
|||
''' |
|||
Initialize own dict with read values. |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
# Copy attributes from auxiliary class to self. |
|||
for attr in self.attrnames: |
|||
setattr(self, attr, getattr(line, attr)) |
|||
# Initialize other attributes |
|||
self.transfer_type = 'UNKN' |
|||
self.remote_account = '' |
|||
self.remote_owner = '' |
|||
self.reference = '' |
|||
self.message = '' |
|||
# Decompose structured messages |
|||
self.parse_message() |
|||
|
|||
def is_valid(self): |
|||
if not self.error_message: |
|||
if not self.transferred_amount: |
|||
self.error_message = "No transferred amount" |
|||
elif not self.execution_date: |
|||
self.error_message = "No execution date" |
|||
elif not self.remote_account and self.transfer_type not in [ |
|||
'BEA', 'GEA', 'COSTS', 'UNKN', 'PAYB', ]: |
|||
self.error_message = _( |
|||
'No remote account for transaction type %s' |
|||
) % self.transfer_type |
|||
if self.error_message: |
|||
raise orm.except_orm(_('Error !'), _(self.error_message)) |
|||
return not self.error_message |
|||
|
|||
def parse_message(self): |
|||
''' |
|||
Parse structured message parts into appropriate attributes |
|||
''' |
|||
def split_blob(line): |
|||
# here we split up the blob, which the last field in a tab |
|||
# separated statement line the blob is a *space separated* fixed |
|||
# field format with field length 32. Empty fields are ignored |
|||
col = 0 |
|||
size = 33 |
|||
res = [] |
|||
while(len(line) > col * size): |
|||
separation = (col + 1) * size - 1 |
|||
if line[col * size: separation].strip(): |
|||
part = line[col * size: separation] |
|||
# If the separation character is not a space, add it anyway |
|||
# presumably for sepa feedback strings only |
|||
if (len(line) > separation and line[separation] != ' '): |
|||
part += line[separation] |
|||
res.append(part) |
|||
col += 1 |
|||
return res |
|||
|
|||
def get_sepa_dict(field): |
|||
""" |
|||
Parses a subset of SEPA feedback strings as occur |
|||
in this non-SEPA csv format. |
|||
|
|||
The string consists of slash separated KEY/VALUE pairs, |
|||
but the slash is allowed to and known to occur in VALUE as well! |
|||
""" |
|||
def _sepa_message(field, reason): |
|||
return _( |
|||
'unable to parse SEPA string: %s - %s' % (field, reason)) |
|||
|
|||
def _get_next_key(items, start): |
|||
'''Find next key, starting from start, returns the key found, |
|||
the start position in the array and the end position + 1''' |
|||
known_keys = [ |
|||
'TRTP', 'IBAN', 'BIC', 'NAME', 'RTRN', 'EREF', 'SWOC', |
|||
'REMI', 'ADDR', 'CPRP', 'CREF', 'CSID', 'ISDT', 'MARF', |
|||
'NRTX', 'NRTXR', 'PREF', 'PURP', 'REFOB', 'RREF', 'RTYP', |
|||
'SVCL', 'SWOD', 'BENM//ID', 'ORDP//ID', 'ORDP//RID', |
|||
'ORIG//CSID', 'ORIG//MARF', 'ULTD//NAME', 'ULTD//ID', |
|||
'ULTB//NAME', 'ULTB//ID' |
|||
] |
|||
items_len = len(items) |
|||
start_index = start |
|||
# Search until start after end of items |
|||
while start_index < items_len: |
|||
end_index = start_index + 1 |
|||
while end_index < items_len: |
|||
key = '/'.join(items[start_index:end_index]) |
|||
if key in known_keys: |
|||
return (key, start_index, end_index) |
|||
end_index += 1 |
|||
start_index += 1 |
|||
return False |
|||
|
|||
items = field[1:].split('/') |
|||
assert len(items) > 1, _sepa_message(field, _('too few items')) |
|||
sepa_dict = {} |
|||
item_index = 0 |
|||
items_len = len(items) |
|||
key_info = _get_next_key(items, item_index) |
|||
assert key_info, _sepa_message( |
|||
field, _('no key found for start %d') % item_index) |
|||
assert key_info[1] == 0, _sepa_message( |
|||
field, _('invalid data found before key %s') % key_info[0]) |
|||
while key_info: |
|||
sepa_key = key_info[0] |
|||
item_index = key_info[2] |
|||
# Find where next key - if any - starts |
|||
key_info = _get_next_key(items, item_index) |
|||
value_end_index = (key_info and key_info[1]) or items_len |
|||
sepa_value = ( |
|||
( |
|||
(value_end_index > item_index) |
|||
and '/'.join(items[item_index:value_end_index])) |
|||
or '') |
|||
sepa_dict[sepa_key] = sepa_value |
|||
return sepa_dict |
|||
|
|||
def parse_type(field): |
|||
# here we process the first field, which identifies the statement |
|||
# type and in case of certain types contains additional information |
|||
transfer_type = 'UNKN' |
|||
remote_account = False |
|||
remote_owner = False |
|||
if field.startswith('/TRTP/'): |
|||
transfer_type = 'SEPA' |
|||
elif field.startswith('GIRO '): |
|||
transfer_type = 'GIRO' |
|||
# field has markup 'GIRO ACCOUNT OWNER' |
|||
# separated by clusters of space of varying size |
|||
account_match = re.match('\s*([0-9]+)\s(.*)$', field[5:]) |
|||
if account_match: |
|||
remote_account = account_match.group(1).zfill(10) |
|||
remote_owner = account_match.group(2).strip() or '' |
|||
else: |
|||
raise orm.except_orm( |
|||
_('Error !'), |
|||
_('unable to parse GIRO string: %s') % field) |
|||
elif field.startswith('BEA '): |
|||
transfer_type = 'BEA' |
|||
# columns 6 to 16 contain the terminal identifier |
|||
# column 17 contains a space |
|||
# columns 18 to 31 contain date and time in DD.MM.YY/HH.MM |
|||
# format |
|||
elif field.startswith('GEA '): |
|||
transfer_type = 'GEA' |
|||
# columns 6 to 16 contain the terminal identifier |
|||
# column 17 contains a space |
|||
# columns 18 to 31 contain date and time in DD.MM.YY/HH.MM |
|||
# format |
|||
elif field.startswith('MAANDBIJDRAGE ABNAMRO'): |
|||
transfer_type = 'COSTS' |
|||
elif re.match("^\s([0-9]+\.){3}[0-9]+\s", field): |
|||
transfer_type = 'BANK' |
|||
remote_account = field[1:13].strip().replace('.', '').zfill(10) |
|||
# column 14 to 31 is either empty or contains the remote owner |
|||
remote_owner = field[14:32].strip() |
|||
elif re.match("^EL[0-9]{13}I", field): |
|||
transfer_type = 'INTL' |
|||
elif field.startswith("TOTAAL BETALINGEN"): |
|||
transfer_type = 'PAYB' |
|||
return (transfer_type, remote_account, remote_owner) |
|||
|
|||
fields = split_blob(self.blob) |
|||
(self.transfer_type, self.remote_account, self.remote_owner) = \ |
|||
parse_type(fields[0]) |
|||
|
|||
if self.transfer_type == 'SEPA': |
|||
sepa_dict = get_sepa_dict(''.join(fields)) |
|||
sepa_type = sepa_dict.get('TRTP') or '' |
|||
self.transfer_type = { |
|||
'SEPA BATCH': 'PAYB', |
|||
'SEPA BATCH SALARIS': 'PAYB', |
|||
'SEPA TERUGBOEKING': 'RETR', |
|||
}.get(sepa_type.upper(), 'SEPA') |
|||
self.remote_account = sepa_dict.get('IBAN', False) |
|||
self.remote_bank_bic = sepa_dict.get('BIC', False) |
|||
self.remote_owner = sepa_dict.get('NAME', False) |
|||
self.reference = sepa_dict.get('REMI', '') |
|||
|
|||
# extract other information depending on type |
|||
elif self.transfer_type == 'GIRO': |
|||
if not self.remote_owner and len(fields) > 1: |
|||
# OWNER is listed in the second field if not in the first |
|||
self.remote_owner = fields[1].strip() or False |
|||
fields = [fields[0]] + fields[2:] |
|||
self.message = ' '.join(field.strip() for field in fields[1:]) |
|||
|
|||
elif self.transfer_type == 'BEA': |
|||
# second column contains remote owner and bank pass identification |
|||
self.remote_owner = ( |
|||
len(fields) > 1 and fields[1].split(',')[0].strip() or False) |
|||
# column 2 and up can contain additional messsages |
|||
# (such as transaction costs or currency conversion) |
|||
self.message = ' '.join(field.strip() for field in fields) |
|||
|
|||
elif self.transfer_type == 'BANK': |
|||
# second column contains the remote owner or the first message line |
|||
if not self.remote_owner: |
|||
self.remote_owner = ( |
|||
len(fields) > 1 and fields[1].strip() or False) |
|||
self.message = ' '.join(field.strip() for field in fields[2:]) |
|||
else: |
|||
self.message = ' '.join(field.strip() for field in fields[1:]) |
|||
|
|||
elif self.transfer_type == 'INTL': |
|||
# first column seems to consist of some kind of international |
|||
# transaction id |
|||
self.reference = fields[0].strip() |
|||
# second column seems to contain remote currency and amount |
|||
# to be processed in a later release of this module |
|||
self.message = len(fields) > 1 and fields[1].strip() or False |
|||
# third column contains iban, preceeded by a slash forward |
|||
if len(fields) > 2: |
|||
if fields[2].startswith('/'): |
|||
self.remote_account = fields[2][1:].strip() |
|||
else: |
|||
self.remote_account = fields[2].strip() |
|||
# fourth column contains remote owner |
|||
self.remote_owner = (len(fields) > 3 and fields[3].strip() or |
|||
False) |
|||
self.message += ' ' + ( |
|||
' '.join(field.strip() for field in fields[4:])) |
|||
|
|||
else: |
|||
self.message = ' '.join(field.strip() for field in fields) |
|||
|
|||
if not self.reference: |
|||
# the reference is sometimes flagged by the prefix "BETALINGSKENM." |
|||
# but can be any numeric line really |
|||
for field in fields[1:]: |
|||
m = re.match( |
|||
"^\s*((BETALINGSKENM\.)|(ACCEPTGIRO))?\s*([0-9]+" |
|||
"([ /][0-9]+)*)\s*$", |
|||
field) |
|||
if m: |
|||
self.reference = m.group(4) |
|||
break |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Implementation of bank_statement communication class of account_banking |
|||
''' |
|||
def __init__(self, msg, *args, **kwargs): |
|||
''' |
|||
Set decent start values based on first transaction read |
|||
''' |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = msg.statement_id |
|||
self.local_account = msg.local_account |
|||
self.date = str2date(msg.date, '%Y%m%d') |
|||
self.start_balance = self.end_balance = 0 # msg.start_balance |
|||
self.import_transaction(msg) |
|||
|
|||
def import_transaction(self, msg): |
|||
''' |
|||
Import a transaction and keep some house holding in the mean time. |
|||
''' |
|||
trans = transaction(msg) |
|||
self.end_balance += trans.transferred_amount |
|||
self.transactions.append(trans) |
|||
|
|||
|
|||
class parser(models.parser): |
|||
code = 'ABNAM' |
|||
country_code = 'NL' |
|||
name = _('Abnamro (NL)') |
|||
doc = _('''\ |
|||
The Dutch Abnamro format is a tab separated text format. The last of these |
|||
fields is itself a fixed length array containing transaction type, remote |
|||
account and owner. The bank does not provide a formal specification of the |
|||
format. Transactions are not explicitely tied to bank statements, although |
|||
each file covers a period of two weeks. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
lines = data.split('\n') |
|||
# Transaction lines are not numbered, so keep a tracer |
|||
subno = 0 |
|||
statement_id = False |
|||
for line in csv.reader(lines, delimiter='\t', quoting=csv.QUOTE_NONE): |
|||
# Skip empty (last) lines |
|||
if not line: |
|||
continue |
|||
subno += 1 |
|||
msg = transaction_message(line, subno) |
|||
if not statement_id: |
|||
statement_id = self.get_unique_statement_id( |
|||
cr, msg.execution_date.strftime('%Yw%W')) |
|||
msg.statement_id = statement_id |
|||
if stmnt: |
|||
stmnt.import_transaction(msg) |
|||
else: |
|||
stmnt = statement(msg) |
|||
result.append(stmnt) |
|||
return result |
@ -0,0 +1,89 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_abnamro |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:53+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:122 |
|||
#, python-format |
|||
msgid "No remote account for transaction type %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:348 |
|||
#, python-format |
|||
msgid "Abnamro (NL)" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:197 |
|||
#, python-format |
|||
msgid "invalid data found before key %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:125 |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:229 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:261 |
|||
#, python-format |
|||
msgid "Sepa transaction type %s not handled yet" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:60 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:189 |
|||
#, python-format |
|||
msgid "too few items" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:195 |
|||
#, python-format |
|||
msgid "no key found for start %d" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:162 |
|||
#, python-format |
|||
msgid "unable to parse SEPA string: %s - %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:230 |
|||
#, python-format |
|||
msgid "unable to parse GIRO string: %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:349 |
|||
#, python-format |
|||
msgid "The Dutch Abnamro format is a tab separated text format. The last of these\n" |
|||
"fields is itself a fixed length array containing transaction type, remote\n" |
|||
"account and owner. The bank does not provide a formal specification of the\n" |
|||
"format. Transactions are not explicitely tied to bank statements, although\n" |
|||
"each file covers a period of two weeks.\n" |
|||
"" |
|||
msgstr "" |
|||
|
@ -0,0 +1,94 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_abnamro |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 6.0.1\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-11-11 17:48+0000\n" |
|||
"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:122 |
|||
#, python-format |
|||
msgid "No remote account for transaction type %s" |
|||
msgstr "No remote account for transaction type %s" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:348 |
|||
#, python-format |
|||
msgid "Abnamro (NL)" |
|||
msgstr "Abnamro (NL)" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:197 |
|||
#, python-format |
|||
msgid "invalid data found before key %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:125 |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:229 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "Error !" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:261 |
|||
#, python-format |
|||
msgid "Sepa transaction type %s not handled yet" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:60 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Invalid transaction line: expected %d columns, found %d" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:189 |
|||
#, python-format |
|||
msgid "too few items" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:195 |
|||
#, python-format |
|||
msgid "no key found for start %d" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:162 |
|||
#, python-format |
|||
msgid "unable to parse SEPA string: %s - %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:230 |
|||
#, python-format |
|||
msgid "unable to parse GIRO string: %s" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:349 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Abnamro format is a tab separated text format. The last of these\n" |
|||
"fields is itself a fixed length array containing transaction type, remote\n" |
|||
"account and owner. The bank does not provide a formal specification of the\n" |
|||
"format. Transactions are not explicitely tied to bank statements, although\n" |
|||
"each file covers a period of two weeks.\n" |
|||
msgstr "" |
|||
"The Dutch Abnamro format is a tab separated text format. The last of these\n" |
|||
"fields is itself a fixed length array containing transaction type, remote\n" |
|||
"account and owner. The bank does not provide a formal specification of the\n" |
|||
"format. Transactions are not explicitely tied to bank statements, although\n" |
|||
"each file covers a period of two weeks.\n" |
@ -0,0 +1,98 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_abnamro |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 6.0.1\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:53+0000\n" |
|||
"PO-Revision-Date: 2013-12-03 11:09+0000\n" |
|||
"Last-Translator: Erwin van der Ploeg (BAS Solutions) <Unknown>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:122 |
|||
#, python-format |
|||
msgid "No remote account for transaction type %s" |
|||
msgstr "Geen tegenrekening bij transactietype %s" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:348 |
|||
#, python-format |
|||
msgid "Abnamro (NL)" |
|||
msgstr "Abnamro (NL)" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:197 |
|||
#, python-format |
|||
msgid "invalid data found before key %s" |
|||
msgstr "Ongeldige gegevesn gevonden voor key %s" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:125 |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:229 |
|||
#, python-format |
|||
msgid "Error !" |
|||
msgstr "Fout !" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:261 |
|||
#, python-format |
|||
msgid "Sepa transaction type %s not handled yet" |
|||
msgstr "Sepa transactie type %s kan nog niet worden verwerkt" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:60 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Ongeldige transactieregel: %d kolommen verwacht, %d aangetroffen" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:189 |
|||
#, python-format |
|||
msgid "too few items" |
|||
msgstr "te weinig items" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:195 |
|||
#, python-format |
|||
msgid "no key found for start %d" |
|||
msgstr "Geen key gevonden voor start %d" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:162 |
|||
#, python-format |
|||
msgid "unable to parse SEPA string: %s - %s" |
|||
msgstr "Niet mogelijk om SEPA string: %s - %s te verwerken" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:230 |
|||
#, python-format |
|||
msgid "unable to parse GIRO string: %s" |
|||
msgstr "Niet mogelijk om GIRO string: %s te verwerken" |
|||
|
|||
#. module: account_banking_nl_abnamro |
|||
#: code:addons/account_banking_nl_abnamro/abnamro.py:349 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Abnamro format is a tab separated text format. The last of these\n" |
|||
"fields is itself a fixed length array containing transaction type, remote\n" |
|||
"account and owner. The bank does not provide a formal specification of the\n" |
|||
"format. Transactions are not explicitely tied to bank statements, although\n" |
|||
"each file covers a period of two weeks.\n" |
|||
msgstr "" |
|||
"Het Nederlandse ABNAMRO-formaat is een tab-gescheiden tekstformaat. De " |
|||
"laatste\n" |
|||
"van deze velden is een tabel van vaste lengte met een transactiesoort, " |
|||
"tegenrekening\n" |
|||
"en eigenaar. De bank geeft geen formele specificatie van het formaat. " |
|||
"Transacties\n" |
|||
"zijn niet expliciet verbonden met een bankafschrift, alhoewel elk bestand " |
|||
"een periode\n" |
|||
"van twee weken beslaat.\n" |
@ -0,0 +1,28 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import girotel |
@ -0,0 +1,36 @@ |
|||
############################################################################## |
|||
# |
|||
# 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
{ |
|||
'name': 'Account Banking - Girotel', |
|||
'version': '0.62', |
|||
'license': 'AGPL-3', |
|||
'author': 'EduSense BV', |
|||
'website': 'http://www.edusense.nl', |
|||
'category': 'Account Banking', |
|||
'depends': ['account_banking'], |
|||
'data': [ |
|||
], |
|||
'description': ''' |
|||
Module to import Dutch Girotel format transation files. |
|||
|
|||
This modules contains no logic, just an import filter for account_banking. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,399 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This parser follows the Dutch Girotel specifications which are |
|||
empirically recreated in this module. |
|||
There is very little information for validating the format or the content |
|||
within. |
|||
|
|||
Dutch Girotel uses no concept of 'Afschrift' or Bank Statement. |
|||
To overcome a lot of problems, this module generates reproducible Bank |
|||
Staments per months period. |
|||
|
|||
Transaction ID's are missing, but generated on the fly based on transaction |
|||
date and sequence position therein. |
|||
|
|||
Assumptions: |
|||
1. transactions are sorted in ascending order of date. |
|||
2. new transactions are appended after previously known transactions of |
|||
the same date |
|||
3. banks maintain order in transaction lists within a single date |
|||
4. the data comes from the SWIFT-network (limited ASCII) |
|||
|
|||
Assumption 4 seems not always true, leading to wrong character conversions. |
|||
As a counter measure, all imported data is converted to SWIFT-format before |
|||
usage. |
|||
''' |
|||
from account_banking.parsers import models |
|||
from account_banking.parsers.convert import str2date |
|||
from tools.translate import _ |
|||
import re |
|||
import csv |
|||
|
|||
_SWIFT = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
|||
"/-?:().,'+ ") |
|||
|
|||
|
|||
def to_swift(astr, schemes=None): |
|||
""" |
|||
Reduce a string to SWIFT fmt |
|||
""" |
|||
schemes = schemes or ['utf-8', 'latin-1', 'ascii'] |
|||
if not isinstance(astr, unicode): |
|||
for scheme in schemes: |
|||
try: |
|||
astr = unicode(astr, scheme) |
|||
break |
|||
except UnicodeDecodeError: |
|||
pass |
|||
if not isinstance(astr, unicode): |
|||
return astr |
|||
|
|||
swift_string = [ |
|||
x in _SWIFT and x or ' ' |
|||
for x in unicodedata.normalize('NFKD', astr).encode('ascii', 'ignore') |
|||
] |
|||
return ''.join(swift_string) |
|||
|
|||
bt = models.mem_bank_transaction |
|||
|
|||
__all__ = ['parser'] |
|||
|
|||
|
|||
class transaction_message(object): |
|||
''' |
|||
A auxiliary class to validate and coerce read values |
|||
''' |
|||
attrnames = [ |
|||
'local_account', 'date', 'transfer_type', 'u1', |
|||
'remote_account', 'remote_owner', 'u2', 'transferred_amount', |
|||
'direction', 'u3', 'message', 'remote_currency', |
|||
] |
|||
# Attributes with possible non-ASCII string content |
|||
strattrs = [ |
|||
'remote_owner', 'message' |
|||
] |
|||
|
|||
ids = {} |
|||
|
|||
def __setattribute__(self, attr, value): |
|||
''' |
|||
Convert values for string content to SWIFT-allowable content |
|||
''' |
|||
if attr != 'strattrs' and attr in self.strattrs: |
|||
value = to_swift(value) |
|||
super(transaction_message, self).__setattribute__(attr, value) |
|||
|
|||
def __getattribute__(self, attr): |
|||
''' |
|||
Convert values from string content to SWIFT-allowable content |
|||
''' |
|||
retval = super(transaction_message, self).__getattribute__(attr) |
|||
return attr != ( |
|||
'strattrs' |
|||
and attr in self.strattrs |
|||
and to_swift(retval) |
|||
or retval |
|||
) |
|||
|
|||
def genid(self): |
|||
''' |
|||
Generate a new id when not assigned before |
|||
''' |
|||
if (not hasattr(self, 'id')) or not self.id: |
|||
if self.date in self.ids: |
|||
self.ids[self.date] += 1 |
|||
else: |
|||
self.ids[self.date] = 1 |
|||
self.id = self.date.strftime('%%Y%%m%%d%04d' % self.ids[self.date]) |
|||
|
|||
def __init__(self, values): |
|||
''' |
|||
Initialize own dict with attributes and coerce values to right type |
|||
''' |
|||
if len(self.attrnames) != len(values): |
|||
raise ValueError( |
|||
_('Invalid transaction line: expected %d columns, found %d') |
|||
% (len(self.attrnames), len(values)) |
|||
) |
|||
self.__dict__.update(dict(zip(self.attrnames, values))) |
|||
self.date = str2date(self.date, '%Y%m%d') |
|||
if self.direction == 'A': |
|||
self.transferred_amount = -float(self.transferred_amount) |
|||
# payment batch done via clieop |
|||
if (self.transfer_type == 'VZ' |
|||
and (not self.remote_account or self.remote_account == '0') |
|||
and (not self.message or re.match('^\s*$', self.message)) |
|||
and self.remote_owner.startswith('TOTAAL ')): |
|||
self.transfer_type = 'PB' |
|||
self.message = self.remote_owner |
|||
self.remove_owner = False |
|||
# payment batch done via sepa |
|||
if self.transfer_type == 'VZ'\ |
|||
and not self.remote_account\ |
|||
and not self.remote_owner\ |
|||
and re.match( |
|||
'^Verzamel Eurobetaling .* TOTAAL \d+ POSTEN\s*$', |
|||
self.message): |
|||
self.transfer_type = 'PB' |
|||
else: |
|||
self.transferred_amount = float(self.transferred_amount) |
|||
self.local_account = self.local_account.zfill(10) |
|||
if self.transfer_type != 'DV': |
|||
self.remote_account = self.remote_account.zfill(10) |
|||
else: |
|||
self.remote_account = False |
|||
self.execution_date = self.value_date = self.date |
|||
self.remote_owner = self.remote_owner.rstrip() |
|||
self.message = self.message.rstrip() |
|||
self.genid() |
|||
|
|||
@property |
|||
def statement_id(self): |
|||
'''Return calculated statement id''' |
|||
return self.id[:6] |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
''' |
|||
Implementation of transaction communication class for account_banking. |
|||
''' |
|||
attrnames = ['statement_id', 'remote_account', 'remote_owner', |
|||
'remote_currency', 'transferred_amount', 'execution_date', |
|||
'value_date', 'transfer_type', 'message', |
|||
] |
|||
|
|||
type_map = { |
|||
'BA': bt.PAYMENT_TERMINAL, |
|||
'BT': bt.ORDER, |
|||
'DV': bt.BANK_COSTS, |
|||
'GM': bt.BANK_TERMINAL, |
|||
'GT': bt.ORDER, |
|||
'IC': bt.DIRECT_DEBIT, |
|||
'OV': bt.ORDER, |
|||
'VZ': bt.ORDER, |
|||
'PB': bt.PAYMENT_BATCH, |
|||
} |
|||
|
|||
def __init__(self, line, *args, **kwargs): |
|||
''' |
|||
Initialize own dict with read values. |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
for attr in self.attrnames: |
|||
setattr(self, attr, getattr(line, attr)) |
|||
self.id = line.id.replace(line.statement_id, '') |
|||
self.reference = self.message[:32].rstrip() |
|||
self.parse_message() |
|||
|
|||
def is_valid(self): |
|||
''' |
|||
There are a few situations that can be signaled as 'invalid' but are |
|||
valid nontheless: |
|||
1. Invoices from the bank itself are communicated through statements. |
|||
These too have no remote_account and no remote_owner. They have a |
|||
transfer_type set to 'DV'. |
|||
2. Transfers sent through the 'International Transfers' system get |
|||
their feedback rerouted through a statement, which is not designed to |
|||
hold the extra fields needed. These transfers have their transfer_type |
|||
set to 'BT'. |
|||
3. Cash payments with debit cards are not seen as a transfer between |
|||
accounts, but as a cash withdrawal. These withdrawals have their |
|||
transfer_type set to 'BA'. |
|||
4. Cash withdrawals from banks are too not seen as a transfer between |
|||
two accounts - the cash exits the banking system. These withdrawals |
|||
have their transfer_type set to 'GM'. |
|||
5. Aggregated payment batches. These transactions have transfer type |
|||
'VZ' natively but are changed to 'PB' while parsing. These transactions |
|||
have no remote account. |
|||
''' |
|||
return bool(self.transferred_amount and self.execution_date and ( |
|||
self.remote_account or |
|||
self.transfer_type in [ |
|||
'DV', 'PB', 'BT', 'BA', 'GM', |
|||
])) |
|||
|
|||
def refold_message(self, message): |
|||
''' |
|||
Refold a previously chopped and fixed length message back into one |
|||
line |
|||
''' |
|||
msg, message = message.rstrip(), None |
|||
parts = [msg[i:i+32].rstrip() for i in range(0, len(msg), 32)] |
|||
return '\n'.join(parts) |
|||
|
|||
def parse_message(self): |
|||
''' |
|||
Parse the message as sent by the bank. Most messages are composed |
|||
of chunks of 32 characters, but there are exceptions. |
|||
''' |
|||
if self.transfer_type == 'VZ': |
|||
# Credit bank costs (interest) gets a special treatment. |
|||
if self.remote_owner.startswith('RC AFREK. REK. '): |
|||
self.transfer_type = 'DV' |
|||
|
|||
if self.transfer_type == 'DV': |
|||
# Bank costs. |
|||
# Title of action is in remote_owner, message contains additional |
|||
# info |
|||
self.reference = self.remote_owner.rstrip() |
|||
parts = [self.message[i:i+32].rstrip() |
|||
for i in range(0, len(self.message), 32) |
|||
] |
|||
if len(parts) > 3: |
|||
self.reference = parts[-1] |
|||
self.message = '\n'.join(parts[:-1]) |
|||
else: |
|||
self.message = '\n'.join(parts) |
|||
self.remote_owner = '' |
|||
|
|||
elif self.transfer_type == 'BA': |
|||
# Payment through bank terminal |
|||
# Id of terminal and some owner info is part of message |
|||
if self.execution_date < str2date('20091130', '%Y%m%d'): |
|||
parts = self.remote_owner.split('>') |
|||
else: |
|||
parts = self.remote_owner.split('>\\') |
|||
self.remote_owner = ' '.join(parts[0].split()[1:]) |
|||
if len(parts) > 1 and len(parts[1]) > 2: |
|||
self.remote_owner_city = parts[1] |
|||
self.message = self.refold_message(self.message) |
|||
self.reference = '%s %s' % (self.remote_owner, |
|||
' '.join(self.message.split()[2:4]) |
|||
) |
|||
|
|||
elif self.transfer_type == 'IC': |
|||
# Direct debit - remote_owner containts reference, while |
|||
# remote_owner is part of the message, most often as |
|||
# first part of the message. |
|||
# Sometimes this misfires, as with the tax office collecting road |
|||
# taxes, but then a once-only manual correction is sufficient. |
|||
parts = [self.message[i:i+32].rstrip() |
|||
for i in range(0, len(self.message), 32) |
|||
] |
|||
self.reference = self.remote_owner |
|||
|
|||
if not parts: |
|||
return |
|||
|
|||
if self.reference.startswith('KN: '): |
|||
self.reference = self.reference[4:] |
|||
if parts[0] == self.reference: |
|||
parts = parts[1:] |
|||
# The tax administration office seems to be the notorious exception |
|||
# to the rule |
|||
if parts[-1] == 'BELASTINGDIENST': |
|||
self.remote_owner = parts[-1].capitalize() |
|||
parts = parts[:-1] |
|||
else: |
|||
self.remote_owner = parts[0] |
|||
parts = parts[1:] |
|||
# Leave the message, to assist in manual correction of misfires |
|||
self.message = '\n'.join(parts) |
|||
|
|||
elif self.transfer_type == 'GM': |
|||
# Cash withdrawal from a bank terminal |
|||
# Structured remote_owner message containing bank info and location |
|||
if self.remote_owner.startswith('OPL. CHIPKNIP'): |
|||
# Transferring cash to debit card |
|||
self.remote_account = self.local_account |
|||
self.message = '%s: %s' % (self.remote_owner, self.message) |
|||
else: |
|||
if self.execution_date < str2date('20091130', '%Y%m%d'): |
|||
parts = self.remote_owner.split('>') |
|||
else: |
|||
parts = self.remote_owner.split('>\\') |
|||
if len(parts) > 1: |
|||
self.reference = ' '.join([x.rstrip() for x in parts]) |
|||
else: |
|||
self.reference = 'ING BANK NV %s' % parts[0].split(' ')[0] |
|||
self.remote_owner = '' |
|||
|
|||
elif self.transfer_type == 'GT': |
|||
# Normal transaction, but remote_owner can contain city, depending |
|||
# on length of total. As there is no clear pattern, leave it as |
|||
# is. |
|||
self.message = self.refold_message(self.message) |
|||
|
|||
else: |
|||
# Final default: reconstruct message from chopped fixed length |
|||
# message parts. |
|||
self.message = self.refold_message(self.message) |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Implementation of bank_statement communication class of account_banking |
|||
''' |
|||
def __init__(self, msg, start_balance=0.0, *args, **kwargs): |
|||
''' |
|||
Set decent start values based on first transaction read |
|||
''' |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = msg.statement_id |
|||
self.local_account = msg.local_account |
|||
self.date = msg.date |
|||
self.start_balance = self.end_balance = start_balance |
|||
self.import_transaction(msg) |
|||
|
|||
def import_transaction(self, msg): |
|||
''' |
|||
Import a transaction and keep some house holding in the mean time. |
|||
''' |
|||
trans = transaction(msg) |
|||
self.end_balance += trans.transferred_amount |
|||
self.transactions.append(trans) |
|||
|
|||
|
|||
class parser(models.parser): |
|||
code = 'NLGT' |
|||
name = _('Dutch Girotel - Kommagescheiden') |
|||
country_code = 'NL' |
|||
doc = _('''\ |
|||
The Dutch Girotel - Kommagescheiden format is basicly a MS Excel CSV format. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
dialect = csv.excel() |
|||
dialect.quotechar = '"' |
|||
dialect.delimiter = ',' |
|||
lines = data.split('\n') |
|||
start_balance = 0.0 |
|||
for line in csv.reader(lines, dialect=dialect): |
|||
# Skip empty (last) lines |
|||
if not line: |
|||
continue |
|||
msg = transaction_message(line) |
|||
if stmnt and stmnt.id != msg.statement_id: |
|||
start_balance = stmnt.end_balance |
|||
result.append(stmnt) |
|||
stmnt = None |
|||
if not stmnt: |
|||
stmnt = statement(msg, start_balance=start_balance) |
|||
else: |
|||
stmnt.import_transaction(msg) |
|||
result.append(stmnt) |
|||
return result |
|||
|
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,36 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_girotel |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:54+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:54+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:325 |
|||
#, python-format |
|||
msgid "Dutch Girotel - Kommagescheiden" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:327 |
|||
#, python-format |
|||
msgid "The Dutch Girotel - Kommagescheiden format is basicly a MS Excel CSV format.\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:103 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
|||
|
@ -0,0 +1,37 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_multibank |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 5.0.7\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:54+0000\n" |
|||
"PO-Revision-Date: 2013-11-11 17:50+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:325 |
|||
#, python-format |
|||
msgid "Dutch Girotel - Kommagescheiden" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:327 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Girotel - Kommagescheiden format is basicly a MS Excel CSV " |
|||
"format.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:103 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,39 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_girotel |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 5.0.12\n" |
|||
"Report-Msgid-Bugs-To: info@edusense.nl\n" |
|||
"POT-Creation-Date: 2013-10-25 15:54+0000\n" |
|||
"PO-Revision-Date: 2013-12-02 14:45+0000\n" |
|||
"Last-Translator: Jan Jurkus (GCE CAD-Service) <ict@gcecad-service.nl>\n" |
|||
"Language-Team: Dutch\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:325 |
|||
#, python-format |
|||
msgid "Dutch Girotel - Kommagescheiden" |
|||
msgstr "Nederlands Girotel - Kommagescheiden" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:327 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Girotel - Kommagescheiden format is basicly a MS Excel CSV " |
|||
"format.\n" |
|||
msgstr "" |
|||
"Het Nederlandse Girotel - kommagescheiden formaat is in opzet een MS Excel " |
|||
"CSV-formaat.\n" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:103 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Ongeldige transactieregel: %d kolommen verwacht, %d aangetroffen" |
@ -0,0 +1,38 @@ |
|||
# Brazilian Portuguese translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:54+0000\n" |
|||
"PO-Revision-Date: 2013-12-25 12:33+0000\n" |
|||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:325 |
|||
#, python-format |
|||
msgid "Dutch Girotel - Kommagescheiden" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:327 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Girotel - Kommagescheiden format is basicly a MS Excel CSV " |
|||
"format.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_girotel |
|||
#: code:addons/account_banking_nl_girotel/girotel.py:103 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,2 @@ |
|||
# -*- coding: utf-8 -*- |
|||
import ing |
@ -0,0 +1,51 @@ |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 Therp BV (<http://therp.nl>) |
|||
# (C) 2011 Smile BV (<http://smile.fr>) |
|||
# |
|||
# Based on account-banking |
|||
# (C) 2009 - 2011 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'ING (NL) Bank Statements Import', |
|||
'version': '0.1.140', |
|||
'license': 'AGPL-3', |
|||
'author': ['Smile', 'Therp BV', 'EduSense BV'], |
|||
'website': 'https://launchpad.net/banking-addons', |
|||
'category': 'Banking addons', |
|||
'depends': ['account_banking'], |
|||
'description': ''' |
|||
Module to import Dutch ING bank format transaction files. The format covered |
|||
is the CSV format with either 'dd-mm-yyyy' or 'yyyymmdd' date syntax. |
|||
|
|||
As the ING bank does not provide detailed specification concerning possible |
|||
values and their meaning for the fields in the CSV file format, the statements |
|||
are parsed according to an educated guess based on incomplete information. |
|||
You can contact the banking-addons developers through their launchpad page and |
|||
help improve the performance of this import filter on |
|||
https://launchpad.net/banking-addons. |
|||
|
|||
Note that imported bank transfers are organized in statements covering periods |
|||
of one week, even if the imported files cover a different period. |
|||
|
|||
This modules contains no logic, just an import filter for account_banking. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,38 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_ing |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:55+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:55+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:257 |
|||
#, python-format |
|||
msgid "ING Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:258 |
|||
#, python-format |
|||
msgid "The Dutch ING format is basicly a MS Excel CSV format. It is specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:62 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
|||
|
@ -0,0 +1,41 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_ing |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 6.0.3\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:55+0000\n" |
|||
"PO-Revision-Date: 2013-12-03 11:10+0000\n" |
|||
"Last-Translator: Jan Jurkus (GCE CAD-Service) <ict@gcecad-service.nl>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:257 |
|||
#, python-format |
|||
msgid "ING Bank" |
|||
msgstr "ING Bank" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:258 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch ING format is basicly a MS Excel CSV format. It is specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
msgstr "" |
|||
"Het Nederlandse ING formaat is in opzet een MS Excel CSV-formaat. Het is\n" |
|||
"duidelijk afwijkend van het Nederlandse multibank-formaat. Transacties zijn\n" |
|||
"niet gebonden aan bankafschriften.\n" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:62 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Ongeldige transactieregel: %d kolommen verwacht, %d aangetroffen" |
@ -0,0 +1,39 @@ |
|||
# Brazilian Portuguese translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:55+0000\n" |
|||
"PO-Revision-Date: 2013-12-25 12:31+0000\n" |
|||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:257 |
|||
#, python-format |
|||
msgid "ING Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:258 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch ING format is basicly a MS Excel CSV format. It is specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_ing |
|||
#: code:addons/account_banking_nl_ing/ing.py:62 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,288 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 Smile (<http://smile.fr>). |
|||
# Copyright (C) 2011 Therp BV (<http://therp.nl>). |
|||
# |
|||
# Based on the multibank module by EduSense BV |
|||
# 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from openerp.addons.account_banking.parsers import models |
|||
from openerp.addons.account_banking.parsers.convert import str2date |
|||
from openerp.tools.translate import _ |
|||
|
|||
import re |
|||
import csv |
|||
|
|||
__all__ = ['parser'] |
|||
|
|||
bt = models.mem_bank_transaction |
|||
|
|||
""" |
|||
First line states the legend |
|||
"Datum","Naam / Omschrijving","Rekening","Tegenrekening","Code","Af Bij",\ |
|||
"Bedrag (EUR)","MutatieSoort","Mededelingen |
|||
|
|||
""" |
|||
|
|||
|
|||
class transaction_message(object): |
|||
''' |
|||
A auxiliary class to validate and coerce read values |
|||
''' |
|||
attrnames = [ |
|||
'date', 'remote_owner', 'local_account', 'remote_account', |
|||
'transfer_type', 'debcred', 'transferred_amount', |
|||
'transfer_type_verbose', 'message' |
|||
] |
|||
|
|||
def __init__(self, values, subno): |
|||
''' |
|||
Initialize own dict with attributes and coerce values to right type |
|||
''' |
|||
if len(self.attrnames) != len(values): |
|||
raise ValueError( |
|||
_('Invalid transaction line: expected %d columns, found %d') |
|||
% (len(self.attrnames), len(values))) |
|||
self.__dict__.update(dict(zip(self.attrnames, values))) |
|||
# for lack of a standardized locale function to parse amounts |
|||
self.transferred_amount = float( |
|||
re.sub(',', '.', self.transferred_amount)) |
|||
if self.debcred == 'Af': |
|||
self.transferred_amount = -self.transferred_amount |
|||
try: |
|||
self.execution_date = self.value_date = str2date(self.date, |
|||
'%Y%m%d') |
|||
except ValueError: |
|||
self.execution_date = self.value_date = str2date(self.date, |
|||
'%d-%m-%Y') |
|||
self.statement_id = '' # self.value_date.strftime('%Yw%W') |
|||
self.id = str(subno).zfill(4) |
|||
self.reference = '' |
|||
# Normalize basic account numbers |
|||
self.remote_account = self.remote_account.replace('.', '').zfill(10) |
|||
self.local_account = self.local_account.replace('.', '').zfill(10) |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
''' |
|||
Implementation of transaction communication class for account_banking. |
|||
''' |
|||
attrnames = ['local_account', 'remote_account', |
|||
'remote_owner', 'transferred_amount', |
|||
'execution_date', 'value_date', 'transfer_type', |
|||
'id', 'reference', 'statement_id', 'message', |
|||
] |
|||
|
|||
""" |
|||
Presumably the same transaction types occur in the MT940 format of ING. |
|||
From www.ing.nl/Images/MT940_Technische_handleiding_tcm7-69020.pdf |
|||
|
|||
""" |
|||
type_map = { |
|||
|
|||
'AC': bt.ORDER, # Acceptgiro |
|||
'BA': bt.PAYMENT_TERMINAL, # Betaalautomaattransactie |
|||
'CH': bt.ORDER, # Cheque |
|||
'DV': bt.ORDER, # Diversen |
|||
'FL': bt.BANK_TERMINAL, # Filiaalboeking, concernboeking |
|||
'GF': bt.ORDER, # Telefonisch bankieren |
|||
'GM': bt.BANK_TERMINAL, # Geldautomaat |
|||
'GT': bt.ORDER, # Internetbankieren |
|||
'IC': bt.DIRECT_DEBIT, # Incasso |
|||
'OV': bt.ORDER, # Overschrijving |
|||
'PK': bt.BANK_TERMINAL, # Opname kantoor |
|||
'PO': bt.ORDER, # Periodieke overschrijving |
|||
'ST': bt.BANK_TERMINAL, # Storting (eigen rekening of derde) |
|||
'VZ': bt.ORDER, # Verzamelbetaling |
|||
'NO': bt.STORNO, # Storno |
|||
} |
|||
|
|||
# global expression for matching storno references |
|||
ref_expr = re.compile('REF[\*:]([0-9A-Z-z_-]+)') |
|||
# match references for Acceptgiro's through Internet banking |
|||
kn_expr = re.compile('KN: ([^ ]+)') |
|||
|
|||
def __init__(self, line, *args, **kwargs): |
|||
''' |
|||
Initialize own dict with read values. |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
# Copy attributes from auxiliary class to self. |
|||
for attr in self.attrnames: |
|||
setattr(self, attr, getattr(line, attr)) |
|||
# self.message = '' |
|||
# Decompose structured messages |
|||
self.parse_message() |
|||
# Adaptations to direct debit orders ands stornos |
|||
if self.transfer_type == 'DV' and self.transferred_amount < 0: |
|||
res = self.ref_expr.search(self.remote_owner) |
|||
if res: |
|||
self.transfer_type = 'NO' |
|||
self.reference = res.group(1) |
|||
self.remote_owner = False |
|||
else: |
|||
res = self.ref_expr.search(self.message) |
|||
if res: |
|||
self.transfer_type = 'NO' |
|||
self.reference = res.group(1) |
|||
if self.transfer_type == 'IC': |
|||
if self.transferred_amount > 0: |
|||
self.reference = self.remote_owner |
|||
else: |
|||
self.transfer_type = 'NO' |
|||
self.message = self.remote_owner + self.message |
|||
res = self.ref_expr.search(self.message) |
|||
if res: |
|||
self.reference = res.group(1) |
|||
self.storno_retry = True |
|||
self.remote_owner = False |
|||
if self.transfer_type == 'GT': |
|||
res = self.kn_expr.search(self.message) |
|||
if res: |
|||
self.reference = res.group(1) |
|||
if self.transfer_type == 'AC': |
|||
self.parse_acceptgiro() |
|||
if self.message and not self.reference: |
|||
self.reference = self.message |
|||
|
|||
def parse_acceptgiro(self): |
|||
""" |
|||
Entries of type 'Acceptgiro' can contain the reference |
|||
in the 'name' column, as well as full address information |
|||
in the 'message' column' |
|||
""" |
|||
before = False |
|||
if self.remote_owner.startswith('KN: '): |
|||
self.reference = self.remote_owner[4:] |
|||
self.remote_owner = '' |
|||
if 'KN: ' in self.message: |
|||
index = self.message.index('KN: ') |
|||
before = self.message[:index] |
|||
self.message = self.message[index:] |
|||
expression = ( |
|||
"^\s*(KN:\s*(?P<kn>[^\s]+))?(\s*)" |
|||
"(?P<navr>NAVR:\s*[^\s]+)?(\s*)(?P<after>.*?)$") |
|||
msg_match = re.match(expression, self.message) |
|||
after = msg_match.group('after') |
|||
kn = msg_match.group('kn') |
|||
navr = msg_match.group('navr') |
|||
if kn: |
|||
self.reference = kn[4:] |
|||
self.message = 'Acceptgiro %s' % (navr or '') |
|||
if after: |
|||
parts = [after[i:i+33] for i in range(0, len(after), 33)] |
|||
if parts and not self.remote_owner: |
|||
self.remote_owner = parts.pop(0).strip() |
|||
if parts: |
|||
self.remote_owner_address = [parts.pop(0).strip()] |
|||
if parts: |
|||
zip_city = parts.pop(0).strip() |
|||
zip_match = re.match( |
|||
"^(?P<zipcode>[^ ]{6})\s+(?P<city>.*?)$", zip_city) |
|||
if zip_match: |
|||
self.remote_owner_postalcode = zip_match.group('zipcode') |
|||
self.remote_owner_city = zip_match.group('city') |
|||
if before and not self.remote_owner_city: |
|||
self.remote_owner_city = before.strip() |
|||
|
|||
def is_valid(self): |
|||
if not self.error_message: |
|||
if not self.transferred_amount: |
|||
self.error_message = "No transferred amount" |
|||
elif not self.execution_date: |
|||
self.error_message = "No execution date" |
|||
elif not self.remote_account and self.transfer_type not in [ |
|||
'BA', 'FL', 'GM', 'IC', 'PK', 'ST']: |
|||
self.error_message = ( |
|||
"No remote account for transaction type %s" % |
|||
self.transfer_type) |
|||
return not self.error_message |
|||
|
|||
def parse_message(self): |
|||
''' |
|||
Parse structured message parts into appropriate attributes. |
|||
No processing done here for Triodos, maybe later. |
|||
''' |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Implementation of bank_statement communication class of account_banking |
|||
''' |
|||
def __init__(self, msg, *args, **kwargs): |
|||
''' |
|||
Set decent start values based on first transaction read |
|||
''' |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = msg.statement_id |
|||
self.local_account = msg.local_account |
|||
try: |
|||
self.date = str2date(msg.date, '%Y%m%d') |
|||
except ValueError: |
|||
self.date = str2date(msg.date, '%d-%m-%Y') |
|||
self.start_balance = self.end_balance = 0 # msg.start_balance |
|||
self.import_transaction(msg) |
|||
|
|||
def import_transaction(self, msg): |
|||
''' |
|||
Import a transaction and keep some house holding in the mean time. |
|||
''' |
|||
trans = transaction(msg) |
|||
self.end_balance += trans.transferred_amount |
|||
self.transactions.append(trans) |
|||
|
|||
|
|||
class parser(models.parser): |
|||
code = 'ING' |
|||
country_code = 'NL' |
|||
name = _('ING Bank') |
|||
doc = _('''\ |
|||
The Dutch ING format is basicly a MS Excel CSV format. It is specifically |
|||
distinct from the Dutch multibank format. Transactions are not tied to Bank |
|||
Statements. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
dialect = csv.excel() |
|||
dialect.quotechar = '"' |
|||
dialect.delimiter = ',' |
|||
lines = data.split('\n') |
|||
# Transaction lines are not numbered, so keep a tracer |
|||
subno = 0 |
|||
statement_id = False |
|||
for line in csv.reader(lines, dialect=dialect): |
|||
# Skip empty (last) lines and header line |
|||
if not line or line[0] == 'Datum': |
|||
continue |
|||
subno += 1 |
|||
msg = transaction_message(line, subno) |
|||
if not statement_id: |
|||
statement_id = self.get_unique_statement_id( |
|||
cr, msg.execution_date.strftime('%Yw%W')) |
|||
msg.statement_id = statement_id |
|||
if stmnt: |
|||
stmnt.import_transaction(msg) |
|||
else: |
|||
stmnt = statement(msg) |
|||
result.append(stmnt) |
|||
return result |
@ -0,0 +1,28 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import multibank |
@ -0,0 +1,36 @@ |
|||
# -*- coding: 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'Account Banking - NL Multibank import', |
|||
'version': '0.62', |
|||
'license': 'AGPL-3', |
|||
'author': 'EduSense BV', |
|||
'website': 'http://www.edusense.nl', |
|||
'category': 'Account Banking', |
|||
'depends': ['account_banking'], |
|||
'description': ''' |
|||
Module to import Dutch Multibank format transation files. |
|||
|
|||
This modules contains no logic, just an import filter for account_banking. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,39 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_multibank |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:56+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:56+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:292 |
|||
#, python-format |
|||
msgid "The Dutch Banking Tools format is basicly a MS Excel CSV format.\n" |
|||
"There are two sub formats: MS Excel format and MS-Excel 2004 format.\n" |
|||
"Both formats are covered with this parser. All transactions are tied\n" |
|||
"to Bank Statements.\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:291 |
|||
#, python-format |
|||
msgid "Dutch Banking Tools" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:77 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
|||
|
@ -0,0 +1,39 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_multibank |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 5.0.7\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:56+0000\n" |
|||
"PO-Revision-Date: 2013-11-11 17:50+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:292 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Banking Tools format is basicly a MS Excel CSV format.\n" |
|||
"There are two sub formats: MS Excel format and MS-Excel 2004 format.\n" |
|||
"Both formats are covered with this parser. All transactions are tied\n" |
|||
"to Bank Statements.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:291 |
|||
#, python-format |
|||
msgid "Dutch Banking Tools" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:77 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,43 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_multibank |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 5.0.7\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:56+0000\n" |
|||
"PO-Revision-Date: 2013-12-03 11:10+0000\n" |
|||
"Last-Translator: Jan Jurkus (GCE CAD-Service) <ict@gcecad-service.nl>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:292 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Banking Tools format is basicly a MS Excel CSV format.\n" |
|||
"There are two sub formats: MS Excel format and MS-Excel 2004 format.\n" |
|||
"Both formats are covered with this parser. All transactions are tied\n" |
|||
"to Bank Statements.\n" |
|||
msgstr "" |
|||
"Het Nederlandse Multibank-formaat is in opzet een MS Excel CSV-formaat.\n" |
|||
"Er zijn twee subformaten: MS Excel formaat en MS Excel 2004 formaat.\n" |
|||
"Beide formaten worden ondersteund met deze inlezer. Alle transacties zijn\n" |
|||
"gekoppeld aan bankafschriften\n" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:291 |
|||
#, python-format |
|||
msgid "Dutch Banking Tools" |
|||
msgstr "Nederlands MultiBank-formaat" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:77 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Ongeldige transactieregel: %d kolommen verwacht, %d aangetroffen" |
@ -0,0 +1,40 @@ |
|||
# Brazilian Portuguese translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:56+0000\n" |
|||
"PO-Revision-Date: 2013-12-25 12:30+0000\n" |
|||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:292 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Banking Tools format is basicly a MS Excel CSV format.\n" |
|||
"There are two sub formats: MS Excel format and MS-Excel 2004 format.\n" |
|||
"Both formats are covered with this parser. All transactions are tied\n" |
|||
"to Bank Statements.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:291 |
|||
#, python-format |
|||
msgid "Dutch Banking Tools" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_multibank |
|||
#: code:addons/account_banking_nl_multibank/multibank.py:77 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,333 @@ |
|||
# -*- 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 Affero 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This parser follows the Dutch Banking Tools specifications which are |
|||
empirically recreated in this module. |
|||
|
|||
Dutch Banking Tools uses the concept of 'Afschrift' or Bank Statement. |
|||
Every transaction is bound to a Bank Statement. As such, this module generates |
|||
Bank Statements along with Bank Transactions. |
|||
''' |
|||
from account_banking.parsers import models |
|||
from account_banking.parsers.convert import str2date |
|||
from account_banking.sepa import postalcode |
|||
from tools.translate import _ |
|||
import csv |
|||
|
|||
__all__ = ['parser'] |
|||
|
|||
bt = models.mem_bank_transaction |
|||
|
|||
|
|||
class transaction_message(object): |
|||
''' |
|||
A auxiliary class to validate and coerce read values |
|||
''' |
|||
attrnames = [ |
|||
'date', 'local_account', 'remote_account', 'remote_owner', 'u1', 'u2', |
|||
'u3', 'local_currency', 'start_balance', 'remote_currency', |
|||
'transferred_amount', 'execution_date', 'value_date', 'nr1', |
|||
'transfer_type', 'nr2', 'reference', 'message', 'statement_id' |
|||
] |
|||
|
|||
@staticmethod |
|||
def clean_account(accountno): |
|||
''' |
|||
There seems to be some SEPA movement in data: account numbers |
|||
get prefixed by zeroes as in BBAN. Convert those to 'old' local |
|||
account numbers |
|||
|
|||
Edit: All account number now follow the BBAN scheme. As SNS Bank, |
|||
from which this module was re-engineered, follows the Dutch |
|||
Banking Tools regulations, it is considered to be used by all banks |
|||
in the Netherlands which comply to it. If not, please notify us. |
|||
''' |
|||
if len(accountno) == 10: # Invalid: longest number is 9 |
|||
accountno = accountno[1:] |
|||
# 9-scheme or 7-scheme? |
|||
stripped = accountno.lstrip('0') |
|||
if len(stripped) <= 7: |
|||
accountno = stripped |
|||
return accountno |
|||
|
|||
def __init__(self, values, subno): |
|||
''' |
|||
Initialize own dict with attributes and coerce values to right type |
|||
''' |
|||
if len(self.attrnames) != len(values): |
|||
raise ValueError( |
|||
_('Invalid transaction line: expected %d columns, found %d') |
|||
% (len(self.attrnames), len(values)) |
|||
) |
|||
self.__dict__.update(dict(zip(self.attrnames, values))) |
|||
self.start_balance = float(self.start_balance) |
|||
self.transferred_amount = float(self.transferred_amount) |
|||
self.execution_date = str2date(self.execution_date, '%d-%m-%Y') |
|||
self.value_date = str2date(self.value_date, '%d-%m-%Y') |
|||
self.id = str(subno).zfill(4) |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
''' |
|||
Implementation of transaction communication class for account_banking. |
|||
''' |
|||
attrnames = ['local_account', 'local_currency', 'remote_account', |
|||
'remote_owner', 'remote_currency', 'transferred_amount', |
|||
'execution_date', 'value_date', 'transfer_type', |
|||
'reference', 'message', 'statement_id', 'id', |
|||
] |
|||
|
|||
type_map = { |
|||
'ACC': bt.ORDER, |
|||
'BEA': bt.PAYMENT_TERMINAL, |
|||
'BTL': bt.ORDER, |
|||
'DIV': bt.ORDER, |
|||
'IDB': bt.PAYMENT_TERMINAL, |
|||
'INC': bt.DIRECT_DEBIT, |
|||
'IOB': bt.ORDER, |
|||
'KNT': bt.BANK_COSTS, |
|||
'KST': bt.BANK_COSTS, |
|||
'OPN': bt.BANK_TERMINAL, |
|||
'OVS': bt.ORDER, |
|||
'PRV': bt.BANK_COSTS, |
|||
'TEL': bt.ORDER, |
|||
} |
|||
|
|||
def __init__(self, line, *args, **kwargs): |
|||
''' |
|||
Initialize own dict with read values. |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
# Copy attributes from auxiliary class to self. |
|||
for attr in self.attrnames: |
|||
setattr(self, attr, getattr(line, attr)) |
|||
# Decompose structured messages |
|||
self.parse_message() |
|||
# Set reference when bank costs |
|||
if self.type == bt.BANK_COSTS: |
|||
self.reference = self.message[:32].rstrip() |
|||
|
|||
def is_valid(self): |
|||
''' |
|||
There are a few situations that can be signaled as 'invalid' but are |
|||
valid nontheless: |
|||
|
|||
1. Transfers from one account to another under the same account holder |
|||
get not always a remote_account and remote_owner. They have their |
|||
transfer_type set to 'PRV'. |
|||
|
|||
2. Invoices from the bank itself are communicated through statements. |
|||
These too have no remote_account and no remote_owner. They have a |
|||
transfer_type set to 'KST', 'KNT' or 'DIV'. |
|||
|
|||
3. Transfers sent through the 'International Transfers' system get |
|||
their feedback rerouted through a statement, which is not designed to |
|||
hold the extra fields needed. These transfers have their transfer_type |
|||
set to 'BTL'. |
|||
|
|||
4. Cash payments with debit cards are not seen as a transfer between |
|||
accounts, but as a cash withdrawal. These withdrawals have their |
|||
transfer_type set to 'BEA'. |
|||
|
|||
5. Cash withdrawals from banks are too not seen as a transfer between |
|||
two accounts - the cash exits the banking system. These withdrawals |
|||
have their transfer_type set to 'OPN'. |
|||
''' |
|||
return (( |
|||
self.transferred_amount and self.execution_date |
|||
and self.value_date) |
|||
and (self.remote_account or self.transfer_type in [ |
|||
'KST', 'PRV', 'BTL', 'BEA', 'OPN', 'KNT', 'DIV' |
|||
] and not self.error_message)) |
|||
|
|||
def parse_message(self): |
|||
''' |
|||
Parse structured message parts into appropriate attributes |
|||
''' |
|||
if self.transfer_type == 'ACC': |
|||
# Accept Giro - structured message payment |
|||
# First part of message is redundant information - strip it |
|||
msg = self.message[self.message.index('navraagnr.'):] |
|||
self.message = ' '.join(msg.split()) |
|||
|
|||
elif self.transfer_type == 'BEA': |
|||
# Payment through payment terminal |
|||
# Remote owner is part of message, while remote_owner is set |
|||
# to the intermediate party, which we don't need. |
|||
self.remote_owner = self.message[:23].rstrip() |
|||
self.remote_owner_city = self.message[23:31].rstrip() |
|||
self.message = self.message[31:] |
|||
|
|||
elif self.transfer_type == 'BTL': |
|||
# International transfers. |
|||
# Remote party is encoded in message, including bank costs |
|||
parts = self.message.split(' ') |
|||
last = False |
|||
for part in parts: |
|||
if part.startswith('bedrag. '): |
|||
# The ordered transferred amount |
|||
currency, amount = part.split('. ')[1].split() |
|||
if self.remote_currency != currency.upper(): |
|||
self.error_message = ( |
|||
'Remote currency in message differs from ' |
|||
'transaction.' |
|||
) |
|||
else: |
|||
self.local_amount = float(amount) |
|||
elif part.startswith('koers. '): |
|||
# The currency rate used |
|||
self.exchange_rate = float(part.split('. ')[1]) |
|||
elif part.startswith('transfer prov. '): |
|||
# The provision taken by the bank |
|||
# Note that the amount must be negated to get the right |
|||
# direction |
|||
currency, costs = part.split('. ')[1].split() |
|||
self.provision_costs = -float(costs) |
|||
self.provision_costs_currency = currency.upper() |
|||
self.provision_costs_description = 'Transfer costs' |
|||
elif part.startswith('aan. '): |
|||
# The remote owner |
|||
self.remote_owner = part.replace('aan. ', '').rstrip() |
|||
last = True |
|||
elif last: |
|||
# Last parts are address lines |
|||
address = part.rstrip() |
|||
iso, pc, city = postalcode.split(address) |
|||
if pc and city: |
|||
self.remote_owner_postalcode = pc |
|||
self.remote_owner_city = city.strip() |
|||
self.remote_owner_country_code = iso |
|||
else: |
|||
self.remote_owner_address.append(address) |
|||
|
|||
elif self.transfer_type == 'DIV': |
|||
# A diverse transfer. Message can be anything, but has some |
|||
# structure |
|||
ptr = self.message.find(self.reference) |
|||
if ptr > 0: |
|||
address = self.message[:ptr].rstrip().split(' ') |
|||
length = len(address) |
|||
if length >= 1: |
|||
self.remote_owner = address[0] |
|||
if length >= 2: |
|||
self.remote_owner_address.append(address[1]) |
|||
if length >= 3: |
|||
self.remote_owner_city = address[2] |
|||
self.message = self.message[ptr:].rstrip() |
|||
if self.message.find('transactiedatum') >= 0: |
|||
rest = self.message.split('transactiedatum') |
|||
if rest[1].startswith('* '): |
|||
self.execution_date = str2date(rest[1][2:], '%d-%m-%Y') |
|||
else: |
|||
self.execution_date = str2date(rest[1][2:], '%d %m %Y') |
|||
self.message = rest[0].rstrip() |
|||
|
|||
elif self.transfer_type == 'IDB': |
|||
# Payment by iDeal transaction |
|||
# Remote owner can be part of message, while remote_owner can be |
|||
# set to the intermediate party, which we don't need. |
|||
parts = self.message.split(' ') |
|||
# Second part: structured id, date & time |
|||
subparts = parts[1].split() |
|||
datestr = '-'.join(subparts[1:4]) |
|||
timestr = ':'.join(subparts[4:]) |
|||
parts[1] = ' '.join([subparts[0], datestr, timestr]) |
|||
# Only replace reference when redundant |
|||
if self.reference == parts[0]: |
|||
if parts[2]: |
|||
self.reference = ' '.join([parts[2], datestr, timestr]) |
|||
else: |
|||
self.reference += ' '.join([datestr, timestr]) |
|||
# Optional fourth path contains remote owners name |
|||
if len(parts) > 3 and parts[-1].find(self.remote_owner) < 0: |
|||
self.remote_owner = parts[-1].rstrip() |
|||
parts = parts[:-1] |
|||
self.message = ' '.join(parts) |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Implementation of bank_statement communication class of account_banking |
|||
''' |
|||
def __init__(self, msg, *args, **kwargs): |
|||
''' |
|||
Set decent start values based on first transaction read |
|||
''' |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = msg.statement_id |
|||
self.local_account = msg.local_account |
|||
self.date = str2date(msg.date, '%d-%m-%Y') |
|||
self.start_balance = self.end_balance = msg.start_balance |
|||
self.import_transaction(msg) |
|||
|
|||
def import_transaction(self, msg): |
|||
''' |
|||
Import a transaction and keep some house holding in the mean time. |
|||
''' |
|||
trans = transaction(msg) |
|||
self.end_balance += trans.transferred_amount |
|||
self.transactions.append(trans) |
|||
|
|||
|
|||
class parser(models.parser): |
|||
code = 'NLBT' |
|||
country_code = 'NL' |
|||
name = _('Dutch Banking Tools') |
|||
doc = _('''\ |
|||
The Dutch Banking Tools format is basicly a MS Excel CSV format. |
|||
There are two sub formats: MS Excel format and MS-Excel 2004 format. |
|||
Both formats are covered with this parser. All transactions are tied |
|||
to Bank Statements. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
dialect = csv.excel() |
|||
dialect.quotechar = '"' |
|||
dialect.delimiter = ';' |
|||
lines = data.split('\n') |
|||
# Probe first record to find out which format we are parsing. |
|||
if lines and lines[0].count(',') > lines[0].count(';'): |
|||
dialect.delimiter = ',' |
|||
if lines and lines[0].count("'") > lines[0].count('"'): |
|||
dialect.quotechar = "'" |
|||
# Transaction lines are not numbered, so keep a tracer |
|||
subno = 0 |
|||
for line in csv.reader(lines, dialect=dialect): |
|||
# Skip empty (last) lines |
|||
if not line: |
|||
continue |
|||
subno += 1 |
|||
msg = transaction_message(line, subno) |
|||
if stmnt and stmnt.id != msg.statement_id: |
|||
result.append(stmnt) |
|||
stmnt = None |
|||
subno = 0 |
|||
if not stmnt: |
|||
stmnt = statement(msg) |
|||
else: |
|||
stmnt.import_transaction(msg) |
|||
result.append(stmnt) |
|||
return result |
|||
|
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,30 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 - 2011 EduSense BV (<http://www.edusense.nl>) |
|||
# and Therp BV (<http://therp.nl>) |
|||
# All Rights Reserved |
|||
# |
|||
# WARNING: This program as such is intended to be used by professional |
|||
# programmers who take the whole responsability of assessing all potential |
|||
# consequences resulting from its eventual inadequacies and bugs |
|||
# End users who are looking for a ready-to-use solution with commercial |
|||
# garantees and support are strongly adviced to contract EduSense BV |
|||
# or Therp BV |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import triodos |
@ -0,0 +1,45 @@ |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 - 2011 EduSense BV (<http://www.edusense.nl>) |
|||
# and Therp BV (<http://therp.nl>) |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
{ |
|||
'name': 'Triodos (NL) Bank Statements Import', |
|||
'version': '0.92', |
|||
'license': 'AGPL-3', |
|||
'author': ['Therp BV', 'EduSense BV'], |
|||
'website': 'https://launchpad.net/account-banking', |
|||
'category': 'Account Banking', |
|||
'depends': ['account_banking'], |
|||
'description': ''' |
|||
Module to import Dutch Triodos bank format transation files (CSV format). |
|||
|
|||
As the Triodos bank does not provide detailed specification concerning possible |
|||
values and their meaning for the fields in the CSV file format, the statements |
|||
are parsed according to an educated guess based on incomplete information. |
|||
You can contact the account-banking developers through their launchpad page and |
|||
help improve the performance of this import filter on |
|||
https://launchpad.net/account-banking. |
|||
|
|||
Note that imported bank transfers are organized in statements covering periods |
|||
of one week, even if the imported files cover a different period. |
|||
|
|||
This modules contains no logic, just an import filter for account_banking. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,38 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_triodos |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:57+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:57+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:183 |
|||
#, python-format |
|||
msgid "Triodos Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:184 |
|||
#, python-format |
|||
msgid "The Dutch Triodos format is basicly a MS Excel CSV format. It is specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:59 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
|||
|
@ -0,0 +1,43 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_triodos |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 6.0.1\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:57+0000\n" |
|||
"PO-Revision-Date: 2013-11-11 17:51+0000\n" |
|||
"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:183 |
|||
#, python-format |
|||
msgid "Triodos Bank" |
|||
msgstr "Triodos Bank" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:184 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Triodos format is basicly a MS Excel CSV format. It is " |
|||
"specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
msgstr "" |
|||
"The Dutch Triodos format is basicly a MS Excel CSV format. It is " |
|||
"specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:59 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Invalid transaction line: expected %d columns, found %d" |
@ -0,0 +1,43 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_nl_triodos |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 6.0.1\n" |
|||
"Report-Msgid-Bugs-To: support@openerp.com\n" |
|||
"POT-Creation-Date: 2013-10-25 15:57+0000\n" |
|||
"PO-Revision-Date: 2013-12-03 11:15+0000\n" |
|||
"Last-Translator: Jan Jurkus (GCE CAD-Service) <ict@gcecad-service.nl>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:183 |
|||
#, python-format |
|||
msgid "Triodos Bank" |
|||
msgstr "Triodosbank" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:184 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Triodos format is basicly a MS Excel CSV format. It is " |
|||
"specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
msgstr "" |
|||
"Het Nederlandse Triodos-formaat is in opzet een MS Excel CSV-formaat. Het " |
|||
"is\n" |
|||
"duidelijk afwijkend van het Nederlandse multibank-formaat. Transacties zijn\n" |
|||
"niet gebonden aan bankafschriften.\n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:59 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "Ongeldige transactieregel: %d kolommen verwacht, %d aangetroffen" |
@ -0,0 +1,40 @@ |
|||
# Brazilian Portuguese translation for banking-addons |
|||
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 |
|||
# This file is distributed under the same license as the banking-addons package. |
|||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: banking-addons\n" |
|||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"POT-Creation-Date: 2013-10-25 15:57+0000\n" |
|||
"PO-Revision-Date: 2013-12-25 12:29+0000\n" |
|||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
|||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"X-Launchpad-Export-Date: 2014-05-31 06:02+0000\n" |
|||
"X-Generator: Launchpad (build 17031)\n" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:183 |
|||
#, python-format |
|||
msgid "Triodos Bank" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:184 |
|||
#, python-format |
|||
msgid "" |
|||
"The Dutch Triodos format is basicly a MS Excel CSV format. It is " |
|||
"specifically\n" |
|||
"distinct from the Dutch multibank format. Transactions are not tied to Bank\n" |
|||
"Statements.\n" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_nl_triodos |
|||
#: code:addons/account_banking_nl_triodos/triodos.py:59 |
|||
#, python-format |
|||
msgid "Invalid transaction line: expected %d columns, found %d" |
|||
msgstr "" |
@ -0,0 +1,229 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>), |
|||
# 2011 Therp BV (<http://therp.nl>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
''' |
|||
This parser follows the Dutch Banking Tools specifications which are |
|||
empirically recreated in this module. |
|||
|
|||
Dutch Banking Tools uses the concept of 'Afschrift' or Bank Statement. |
|||
Every transaction is bound to a Bank Statement. As such, this module generates |
|||
Bank Statements along with Bank Transactions. |
|||
''' |
|||
|
|||
from account_banking.parsers import models |
|||
from account_banking.parsers.convert import str2date |
|||
from tools.translate import _ |
|||
|
|||
import re |
|||
import csv |
|||
|
|||
__all__ = ['parser'] |
|||
|
|||
bt = models.mem_bank_transaction |
|||
|
|||
|
|||
class transaction_message(object): |
|||
''' |
|||
A auxiliary class to validate and coerce read values |
|||
''' |
|||
attrnames = [ |
|||
'date', 'local_account', 'transferred_amount', 'debcred', |
|||
'remote_owner', 'remote_account', 'transfer_type', 'reference', |
|||
] |
|||
|
|||
def __init__(self, values, subno): |
|||
''' |
|||
Initialize own dict with attributes and coerce values to right type |
|||
''' |
|||
if len(self.attrnames) != len(values): |
|||
raise ValueError( |
|||
_('Invalid transaction line: expected %d columns, found %d') |
|||
% (len(self.attrnames), len(values))) |
|||
self.__dict__.update(dict(zip(self.attrnames, values))) |
|||
# for lack of a standardized locale function to parse amounts |
|||
self.transferred_amount = float( |
|||
re.sub(',', '.', re.sub('\.', '', self.transferred_amount))) |
|||
if self.debcred == 'Debet': |
|||
self.transferred_amount = -self.transferred_amount |
|||
self.execution_date = str2date(self.date, '%d-%m-%Y') |
|||
self.value_date = str2date(self.date, '%d-%m-%Y') |
|||
self.statement_id = '' |
|||
self.id = str(subno).zfill(4) |
|||
# Normalize basic account numbers |
|||
self.remote_account = self.remote_account.replace('.', '').zfill(10) |
|||
self.local_account = self.local_account.replace('.', '').zfill(10) |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
''' |
|||
Implementation of transaction communication class for account_banking. |
|||
''' |
|||
attrnames = [ |
|||
'local_account', |
|||
'remote_account', |
|||
'remote_owner', |
|||
'transferred_amount', |
|||
'execution_date', |
|||
'value_date', |
|||
'transfer_type', |
|||
'reference', |
|||
'id', |
|||
] |
|||
|
|||
type_map = { |
|||
# retrieved from online help in the Triodos banking application |
|||
'AC': bt.ORDER, # Acceptgiro gecodeerd |
|||
'AN': bt.ORDER, # Acceptgiro ongecodeerd |
|||
'AT': bt.ORDER, # Acceptgiro via internet |
|||
'BA': bt.PAYMENT_TERMINAL, # Betaalautomaat |
|||
'CHIP': bt.BANK_TERMINAL, # Chipknip |
|||
# 'CO': # Correctie |
|||
'DB': bt.ORDER, # Diskettebetaling |
|||
# 'DV': # Dividend |
|||
'EI': bt.DIRECT_DEBIT, # Europese Incasso |
|||
'EICO': bt.DIRECT_DEBIT, # Europese Incasso Correctie |
|||
'EIST': bt.ORDER, # Europese Incasso Storno |
|||
'ET': bt.ORDER, # Europese Transactie |
|||
'ETST': bt.ORDER, # Europese Transactie Storno |
|||
'GA': bt.BANK_TERMINAL, # Geldautomaat |
|||
'IB': bt.ORDER, # Interne Boeking |
|||
'IC': bt.DIRECT_DEBIT, # Incasso |
|||
'ID': bt.ORDER, # iDeal-betaling |
|||
'IT': bt.ORDER, # Internet transactie |
|||
'KN': bt.BANK_COSTS, # Kosten |
|||
'KO': bt.BANK_TERMINAL, # Kasopname |
|||
# 'KS': # Kwaliteitsstoring |
|||
'OV': bt.ORDER, # Overboeking. NB: can also be bt.BANK_COSTS |
|||
# when no remote_account specified! |
|||
'PO': bt.ORDER, # Periodieke Overboeking |
|||
'PR': bt.BANK_COSTS, # Provisie |
|||
# 'RE': # Rente |
|||
# 'RS': # Renteschenking |
|||
'ST': bt.ORDER, # Storno |
|||
'TG': bt.ORDER, # Telegiro |
|||
# 'VL': # Vaste Lening |
|||
'VO': bt.DIRECT_DEBIT, # Vordering overheid |
|||
'VV': bt.ORDER, # Vreemde valuta |
|||
} |
|||
|
|||
def __init__(self, line, *args, **kwargs): |
|||
''' |
|||
Initialize own dict with read values. |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
# Copy attributes from auxiliary class to self. |
|||
for attr in self.attrnames: |
|||
setattr(self, attr, getattr(line, attr)) |
|||
self.message = '' |
|||
# Decompose structured messages |
|||
self.parse_message() |
|||
if (self.transfer_type == 'OV' |
|||
and not self.remote_account |
|||
and not self.remote_owner): |
|||
self.transfer_type = 'KN' |
|||
|
|||
def is_valid(self): |
|||
if not self.error_message: |
|||
if not self.transferred_amount: |
|||
self.error_message = "No transferred amount" |
|||
elif not self.execution_date: |
|||
self.error_message = "No execution date" |
|||
elif not self.remote_account and self.transfer_type not in [ |
|||
'KN', 'TG', 'GA', 'BA', 'CHIP']: |
|||
self.error_message = ( |
|||
"No remote account for transaction type %s" % |
|||
self.transfer_type) |
|||
return not self.error_message |
|||
|
|||
def parse_message(self): |
|||
''' |
|||
Parse structured message parts into appropriate attributes. |
|||
''' |
|||
# IBAN accounts are prefixed by BIC |
|||
if self.remote_account: |
|||
parts = self.remote_account.split(' ') |
|||
if len(parts) == 2: |
|||
self.remote_bank_bic = parts[0] |
|||
self.remote_account = parts[1] |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Implementation of bank_statement communication class of account_banking |
|||
''' |
|||
def __init__(self, msg, *args, **kwargs): |
|||
''' |
|||
Set decent start values based on first transaction read |
|||
''' |
|||
super(statement, self).__init__(*args, **kwargs) |
|||
self.id = msg.statement_id |
|||
self.local_account = msg.local_account |
|||
self.date = str2date(msg.date, '%d-%m-%Y') |
|||
self.start_balance = self.end_balance = 0 # msg.start_balance |
|||
self.import_transaction(msg) |
|||
|
|||
def import_transaction(self, msg): |
|||
''' |
|||
Import a transaction and keep some house holding in the mean time. |
|||
''' |
|||
trans = transaction(msg) |
|||
self.end_balance += trans.transferred_amount |
|||
self.transactions.append(trans) |
|||
|
|||
|
|||
class parser(models.parser): |
|||
code = 'TRIOD' |
|||
country_code = 'NL' |
|||
name = _('Triodos Bank') |
|||
doc = _('''\ |
|||
The Dutch Triodos format is basicly a MS Excel CSV format. It is specifically |
|||
distinct from the Dutch multibank format. Transactions are not tied to Bank |
|||
Statements. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
stmnt = None |
|||
dialect = csv.excel() |
|||
dialect.quotechar = '"' |
|||
dialect.delimiter = ',' |
|||
lines = data.split('\n') |
|||
# Transaction lines are not numbered, so keep a tracer |
|||
subno = 0 |
|||
# fixed statement id based on import timestamp |
|||
statement_id = False |
|||
for line in csv.reader(lines, dialect=dialect): |
|||
# Skip empty (last) lines |
|||
if not line: |
|||
continue |
|||
subno += 1 |
|||
msg = transaction_message(line, subno) |
|||
if not statement_id: |
|||
statement_id = self.get_unique_statement_id( |
|||
cr, msg.execution_date.strftime('%Yw%W')) |
|||
msg.statement_id = statement_id |
|||
if stmnt: |
|||
stmnt.import_transaction(msg) |
|||
else: |
|||
stmnt = statement(msg) |
|||
result.append(stmnt) |
|||
return result |
@ -0,0 +1,26 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import account_banking_uk_hsbc |
|||
import wizard |
|||
import hsbc_mt940 |
|||
import hsbc_clientid |
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,52 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
{ |
|||
'name': 'HSBC Account Banking', |
|||
'version': '0.5', |
|||
'license': 'AGPL-3', |
|||
'author': 'credativ Ltd', |
|||
'website': 'http://www.credativ.co.uk', |
|||
'category': 'Account Banking', |
|||
'depends': ['account_banking_payment'], |
|||
'data': [ |
|||
'account_banking_uk_hsbc.xml', |
|||
'hsbc_clientid_view.xml', |
|||
'data/banking_export_hsbc.xml', |
|||
'wizard/export_hsbc_view.xml', |
|||
'security/ir.model.access.csv', |
|||
], |
|||
'description': ''' |
|||
Module to import HSBC format transation files (S.W.I.F.T MT940) and to export |
|||
payments for HSBC.net (PAYMUL). |
|||
|
|||
Currently it is targetting UK market, due to country variances of the MT940 and |
|||
PAYMUL. |
|||
|
|||
It is possible to extend this module to work with HSBC.net in other countries |
|||
and potentially other banks. |
|||
|
|||
This module adds above import/export filter to the account_banking module. |
|||
All business logic is in account_banking module. |
|||
|
|||
Initial release of this module was co-sponsored by Canonical. |
|||
''', |
|||
'installable': False, |
|||
} |
@ -0,0 +1,141 @@ |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from datetime import date |
|||
|
|||
from openerp.osv import orm, fields |
|||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DATEFORMAT |
|||
|
|||
|
|||
class hsbc_export(orm.Model): |
|||
"""HSBC Export""" |
|||
_name = 'banking.export.hsbc' |
|||
_description = __doc__ |
|||
_rec_name = 'execution_date' |
|||
|
|||
_columns = { |
|||
'payment_order_ids': fields.many2many( |
|||
'payment.order', |
|||
'account_payment_order_hsbc_rel', |
|||
'banking_export_hsbc_id', 'account_order_id', |
|||
'Payment Orders', |
|||
readonly=True), |
|||
'identification': |
|||
fields.char('Identification', size=15, readonly=True, select=True), |
|||
'execution_date': |
|||
fields.date('Execution Date', readonly=True), |
|||
'no_transactions': |
|||
fields.integer('Number of Transactions', readonly=True), |
|||
'total_amount': |
|||
fields.float('Total Amount', readonly=True), |
|||
'date_generated': |
|||
fields.datetime('Generation Date', readonly=True, select=True), |
|||
'file': |
|||
fields.binary('HSBC File', readonly=True), |
|||
'state': |
|||
fields.selection([ |
|||
('draft', 'Draft'), |
|||
('sent', 'Sent'), |
|||
('done', 'Reconciled'), |
|||
], 'State', readonly=True), |
|||
} |
|||
|
|||
_defaults = { |
|||
'date_generated': lambda *a: date.today().strftime(OE_DATEFORMAT), |
|||
'state': 'draft', |
|||
} |
|||
|
|||
|
|||
class payment_line(orm.Model): |
|||
"""The standard payment order is using a mixture of details from the |
|||
partner record and the res.partner.bank record. For, instance, the account |
|||
holder name is coming from the res.partner.bank record, but the company |
|||
name and address are coming from the partner address record. This is |
|||
problematic because the HSBC payment format is validating for alphanumeric |
|||
characters in the company name and address. So, "Great Company Ltd." and |
|||
"Great Company s.a." will cause an error because they have full-stops in |
|||
the name. |
|||
|
|||
A better approach is to use the name and address details from the |
|||
res.partner.bank record always. This way, the address details can be |
|||
sanitized for the payments, whilst being able to print the proper name and |
|||
address throughout the rest of the system e.g. on invoices. |
|||
""" |
|||
|
|||
_inherit = 'payment.line' |
|||
|
|||
def info_owner(self, cr, uid, ids, name=None, args=None, context=None): |
|||
|
|||
if not ids: |
|||
return {} |
|||
|
|||
result = {} |
|||
info = '' |
|||
for line in self.browse(cr, uid, ids, context=context): |
|||
owner = line.order_id.mode.bank_id |
|||
|
|||
name = owner.owner_name or owner.partner_id.name |
|||
st = owner.street and owner.street or '' |
|||
st1 = '' # no street2 in res.partner.bank |
|||
zip = owner.zip and owner.zip or '' |
|||
city = owner.city and owner.city or '' |
|||
zip_city = zip + ' ' + city |
|||
cntry = owner.country_id and owner.country_id.name or '' |
|||
info = name + "\n".join((st + " ", st1, zip_city, cntry)) |
|||
result[line.id] = info |
|||
return result |
|||
|
|||
def info_partner(self, cr, uid, ids, name=None, args=None, context=None): |
|||
if not ids: |
|||
return {} |
|||
result = {} |
|||
info = '' |
|||
|
|||
for line in self.browse(cr, uid, ids, context=context): |
|||
partner = line.bank_id |
|||
|
|||
name = partner.owner_name or partner.partner_id.name |
|||
st = partner.street and partner.street or '' |
|||
st1 = '' # no street2 in res.partner.bank |
|||
zip = partner.zip and partner.zip or '' |
|||
city = partner.city and partner.city or '' |
|||
zip_city = zip + ' ' + city |
|||
cntry = partner.country_id and partner.country_id.name or '' |
|||
info = name + "\n".join((st + " ", st1, zip_city, cntry)) |
|||
result[line.id] = info |
|||
|
|||
return result |
|||
|
|||
# Define the info_partner and info_owner so we can override the methods |
|||
_columns = { |
|||
'info_owner': fields.function( |
|||
info_owner, |
|||
string="Owner Account", |
|||
type="text", |
|||
help='Address of the Main Partner', |
|||
), |
|||
'info_partner': fields.function( |
|||
info_partner, |
|||
string="Destination Account", |
|||
type="text", |
|||
help='Address of the Ordering Customer.' |
|||
), |
|||
} |
@ -0,0 +1,85 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
Copyright (C) EduSense BV <http://www.edusense.nl> |
|||
All rights reserved. |
|||
|
|||
Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>) |
|||
|
|||
The licence is in the file __openerp__.py |
|||
|
|||
|
|||
--> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<!-- Make new view on HSBC exports --> |
|||
<record id="view_banking_export_hsbc_form" model="ir.ui.view"> |
|||
<field name="name">account.banking.export.hsbc.form</field> |
|||
<field name="model">banking.export.hsbc</field> |
|||
<field name="type">form</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="HSBC Export"> |
|||
<notebook> |
|||
<page string="General Information"> |
|||
<separator string="HSBC Information" colspan="4" /> |
|||
<field name="total_amount" /> |
|||
<field name="no_transactions" /> |
|||
<separator string="Processing Information" colspan="4" /> |
|||
<field name="execution_date" /> |
|||
<field name="date_generated" /> |
|||
<newline /> |
|||
<field name="file" colspan="4" /> |
|||
</page> |
|||
<page string="Payment Orders"> |
|||
<field name="payment_order_ids" colspan="4" nolabel="1"> |
|||
<tree colors="blue:state in ('draft');gray:state in ('cancel','done');black:state in ('open')" string="Payment order"> |
|||
<field name="reference"/> |
|||
<field name="date_created"/> |
|||
<field name="date_done"/> |
|||
<field name="total"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</page> |
|||
</notebook> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<record id="view_banking_export_hsbc_tree" model="ir.ui.view"> |
|||
<field name="name">account.banking.export.hsbc.tree</field> |
|||
<field name="model">banking.export.hsbc</field> |
|||
<field name="type">tree</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="HSBC Export"> |
|||
<field name="execution_date" search="2"/> |
|||
<field name="date_generated" /> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
<record model="ir.actions.act_window" id="action_account_banking_hsbcs"> |
|||
<field name="name">Generated HSBC files</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">banking.export.hsbc</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<!-- Add a menu item for it --> |
|||
<menuitem name="Generated HSBC files" |
|||
id="menu_action_account_banking_exported_hsbc_files" |
|||
parent="account_banking.menu_finance_banking_actions" |
|||
action="action_account_banking_hsbcs" |
|||
sequence="12" |
|||
/> |
|||
|
|||
<!-- Create right menu entry to see generated files --> |
|||
<act_window name="Generated HSBC files" |
|||
domain="[('payment_order_ids', '=', active_id)]" |
|||
res_model="banking.export.hsbc" |
|||
src_model="payment.order" |
|||
view_type="form" |
|||
view_mode="tree,form" |
|||
id="act_banking_export_hsbc_payment_order"/> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,29 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record model="payment.mode.type" id="export_acm_or_ezone"> |
|||
<field name="name">ACH or EZONE</field> |
|||
<field name="code">not used</field> |
|||
<field name="suitable_bank_types" |
|||
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" /> |
|||
<field name="ir_model_id" |
|||
ref="account_banking_uk_hsbc.model_banking_export_hsbc_wizard"/> |
|||
</record> |
|||
<record model="payment.mode.type" id="export_faster_payment"> |
|||
<field name="name">Faster Payment</field> |
|||
<field name="code">not used</field> |
|||
<field name="suitable_bank_types" |
|||
eval="[(6,0,[ref('base.bank_normal'),])]" /> |
|||
<field name="ir_model_id" |
|||
ref="account_banking_uk_hsbc.model_banking_export_hsbc_wizard"/> |
|||
</record> |
|||
<record model="payment.mode.type" id="export_priority_payment"> |
|||
<field name="name">Priority Payment</field> |
|||
<field name="code">not used</field> |
|||
<field name="suitable_bank_types" |
|||
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" /> |
|||
<field name="ir_model_id" |
|||
ref="account_banking_uk_hsbc.model_banking_export_hsbc_wizard"/> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,49 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
from openerp.osv import orm, fields |
|||
|
|||
|
|||
class hsbc_clientid(orm.Model): |
|||
""" |
|||
Record to hold the HSBCNet Client ID for the company. |
|||
""" |
|||
_name = 'banking.hsbc.clientid' |
|||
_description = 'HSBC Client ID' |
|||
|
|||
_columns = { |
|||
'name': fields.char('Name', size=64, required=True), |
|||
'clientid': fields.char('Client ID', size=20, required=True), |
|||
'company_id': fields.many2one('res.company', 'Company', required=True), |
|||
} |
|||
|
|||
_defaults = { |
|||
'company_id': ( |
|||
lambda self, cr, uid, c: |
|||
self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id), |
|||
} |
|||
|
|||
|
|||
class payment_order(orm.Model): |
|||
_inherit = 'payment.order' |
|||
_columns = { |
|||
'hsbc_clientid_id': fields.many2one( |
|||
'banking.hsbc.clientid', |
|||
'HSBC Client ID', |
|||
required=True, |
|||
), |
|||
} |
|||
|
|||
def _default_hsbc_clientid(self, cr, uid, context=None): |
|||
user = self.pool['res.users'].browse(cr, uid, uid, context=context) |
|||
company_id = user.company_id.id |
|||
|
|||
clientid_ids = self.pool['banking.hsbc.clientid'].search( |
|||
cr, uid, [('company_id', '=', company_id)] |
|||
) |
|||
if len(clientid_ids) == 0: |
|||
return False |
|||
else: |
|||
return clientid_ids[0] |
|||
|
|||
_defaults = { |
|||
'hsbc_clientid_id': _default_hsbc_clientid, |
|||
} |
@ -0,0 +1,76 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<!-- Add the HSBC Client ID to the Payment Order --> |
|||
<record id="view_payment_order_form" model="ir.ui.view"> |
|||
<field name="name">payment.order.form</field> |
|||
<field name="model">payment.order</field> |
|||
<field name="type">form</field> |
|||
<field name="inherit_id" ref="account_payment.view_payment_order_form"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="date_scheduled" position="after"> |
|||
<field name="hsbc_clientid_id" /> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
|
|||
|
|||
<!-- Form view for HSBC Client ID --> |
|||
<record id="banking_hsbc_clientid_form" model="ir.ui.view"> |
|||
<field name="name">banking.hsbc.clientid.form</field> |
|||
<field name="model">banking.hsbc.clientid</field> |
|||
<field name="type">form</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="HSBC Client ID"> |
|||
<group colspan="4"> |
|||
<field name="name" /> |
|||
<field name="company_id" /> |
|||
<field name="clientid" /> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<!-- Tree view for HSBC Client ID --> |
|||
<record id="banking_hsbc_clientid_tree" model="ir.ui.view"> |
|||
<field name="name">banking.hsbc.clientid.tree</field> |
|||
<field name="model">banking.hsbc.clientid</field> |
|||
<field name="type">tree</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="HSBC Client IDs"> |
|||
<field name="name" /> |
|||
<field name="company_id" /> |
|||
<field name="clientid" /> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<!-- Search view for HSBC Client ID --> |
|||
<record id="banking_hsbc_clientid_filter" model="ir.ui.view"> |
|||
<field name="name">banking.hsbc.clientid.filter</field> |
|||
<field name="model">banking.hsbc.clientid</field> |
|||
<field name="type">search</field> |
|||
<field name="arch" type="xml"> |
|||
<search string="HSBC Client IDs"> |
|||
<field name="name"/> |
|||
<field name="company_id" /> |
|||
<field name="clientid" /> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
|
|||
<!-- Action for HSBC Client ID --> |
|||
<record id="banking_hsbc_clientid_action" model="ir.actions.act_window"> |
|||
<field name="name">HSBC Client ID</field> |
|||
<field name="res_model">banking.hsbc.clientid</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="search_view_id" ref="banking_hsbc_clientid_filter"/> |
|||
</record> |
|||
|
|||
<!-- Menu for HSBC Client ID --> |
|||
<menuitem action="banking_hsbc_clientid_action" id="banking_hsbc_clientid_menu" parent="account.menu_configuration_misc"/> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,202 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
# Import of HSBC data in Swift MT940 format |
|||
# |
|||
|
|||
from account_banking.parsers import models |
|||
from mt940_parser import HSBCParser |
|||
import re |
|||
import logging |
|||
|
|||
bt = models.mem_bank_transaction |
|||
logger = logging.getLogger('hsbc_mt940') |
|||
|
|||
from openerp.tools.translate import _ |
|||
from openerp.osv import orm |
|||
|
|||
|
|||
def record2float(record, value): |
|||
if record['creditmarker'][-1] == 'C': |
|||
return float(record[value]) |
|||
return -float(record[value]) |
|||
|
|||
|
|||
class transaction(models.mem_bank_transaction): |
|||
|
|||
mapping = { |
|||
'execution_date': 'valuedate', |
|||
'value_date': 'valuedate', |
|||
'local_currency': 'currency', |
|||
'transfer_type': 'bookingcode', |
|||
'reference': 'custrefno', |
|||
'message': 'furtherinfo' |
|||
} |
|||
|
|||
type_map = { |
|||
'NTRF': bt.ORDER, |
|||
'NMSC': bt.ORDER, |
|||
'NPAY': bt.PAYMENT_BATCH, |
|||
'NCHK': bt.CHECK, |
|||
} |
|||
|
|||
def __init__(self, record, *args, **kwargs): |
|||
''' |
|||
Transaction creation |
|||
''' |
|||
super(transaction, self).__init__(*args, **kwargs) |
|||
for key, value in self.mapping.iteritems(): |
|||
if value in record: |
|||
setattr(self, key, record[value]) |
|||
|
|||
self.transferred_amount = record2float(record, 'amount') |
|||
|
|||
# Set the transfer type based on the bookingcode |
|||
if record.get('bookingcode', 'ignore') in self.type_map: |
|||
self.transfer_type = self.type_map[record['bookingcode']] |
|||
else: |
|||
# Default to the generic order, so it will be eligible for matching |
|||
self.transfer_type = bt.ORDER |
|||
|
|||
if not self.is_valid(): |
|||
logger.info("Invalid: %s", record) |
|||
|
|||
def is_valid(self): |
|||
''' |
|||
We don't have remote_account so override base |
|||
''' |
|||
return (self.execution_date |
|||
and self.transferred_amount and True) or False |
|||
|
|||
|
|||
class statement(models.mem_bank_statement): |
|||
''' |
|||
Bank statement imported data |
|||
''' |
|||
|
|||
def import_record(self, record): |
|||
def _transmission_number(): |
|||
self.id = record['transref'] |
|||
|
|||
def _account_number(): |
|||
# The wizard doesn't check for sort code |
|||
self.local_account = ( |
|||
record['sortcode'] + ' ' + record['accnum'].zfill(8) |
|||
) |
|||
|
|||
def _statement_number(): |
|||
self.id = '-'.join( |
|||
[self.id, self.local_account, record['statementnr']] |
|||
) |
|||
|
|||
def _opening_balance(): |
|||
self.start_balance = record2float(record, 'startingbalance') |
|||
self.local_currency = record['currencycode'] |
|||
|
|||
def _closing_balance(): |
|||
self.end_balance = record2float(record, 'endingbalance') |
|||
self.date = record['bookingdate'] |
|||
|
|||
def _transaction_new(): |
|||
self.transactions.append(transaction(record)) |
|||
|
|||
def _transaction_info(): |
|||
self.transaction_info(record) |
|||
|
|||
def _not_used(): |
|||
logger.info("Didn't use record: %s", record) |
|||
|
|||
rectypes = { |
|||
'20': _transmission_number, |
|||
'25': _account_number, |
|||
'28': _statement_number, |
|||
'28C': _statement_number, |
|||
'60F': _opening_balance, |
|||
'62F': _closing_balance, |
|||
'61': _transaction_new, |
|||
'86': _transaction_info, |
|||
} |
|||
|
|||
rectypes.get(record['recordid'], _not_used)() |
|||
|
|||
def transaction_info(self, record): |
|||
''' |
|||
Add extra information to transaction |
|||
''' |
|||
# Additional information for previous transaction |
|||
if len(self.transactions) < 1: |
|||
logger.info( |
|||
"Received additional information for non existent transaction:" |
|||
) |
|||
logger.info(record) |
|||
else: |
|||
transaction = self.transactions[-1] |
|||
transaction.id = ','.join(( |
|||
record[k] |
|||
for k in ( |
|||
'infoline{0}'.format(i) |
|||
for i in range(2, 5) |
|||
) |
|||
if k in record |
|||
)) |
|||
|
|||
|
|||
def raise_error(message, line): |
|||
raise orm.except_orm( |
|||
_('Import error'), |
|||
_('Error in import:') + '\n\n'.join((message, line)) |
|||
) |
|||
|
|||
|
|||
class parser_hsbc_mt940(models.parser): |
|||
code = 'HSBC-MT940' |
|||
name = _('HSBC Swift MT940 statement export') |
|||
country_code = 'GB' |
|||
doc = _('''\ |
|||
This format is available through |
|||
the HSBC web interface. |
|||
''') |
|||
|
|||
def parse(self, cr, data): |
|||
result = [] |
|||
parser = HSBCParser() |
|||
# Split into statements |
|||
statements = [st for st in re.split('[\r\n]*(?=:20:)', data)] |
|||
# Split by records |
|||
statement_list = [ |
|||
re.split('[\r\n ]*(?=:\d\d[\w]?:)', st) |
|||
for st in statements |
|||
] |
|||
|
|||
for statement_lines in statement_list: |
|||
stmnt = statement() |
|||
records = [ |
|||
parser.parse_record(record) |
|||
for record in statement_lines |
|||
] |
|||
[stmnt.import_record(r) for r in records if r is not None] |
|||
|
|||
if stmnt.is_valid(): |
|||
result.append(stmnt) |
|||
else: |
|||
logger.info("Invalid Statement:") |
|||
logger.info(records[0]) |
|||
|
|||
return result |
@ -0,0 +1,287 @@ |
|||
# Translation of OpenERP Server. |
|||
# This file contains the translation of the following modules: |
|||
# * account_banking_uk_hsbc |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: OpenERP Server 7.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2013-10-25 15:59+0000\n" |
|||
"PO-Revision-Date: 2013-10-25 15:59+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: help:banking.export.hsbc.wizard,execution_date_create:0 |
|||
msgid "This is the date the file should be processed by the bank. Don't choose a date beyond the nearest date in your payments. The latest allowed date is 30 days from now.\n" |
|||
"Please keep in mind that banks only execute on working days and typically use a delay of two days between execution date and effective transfer date." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc:0 |
|||
msgid "HSBC Information" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:234 |
|||
#, python-format |
|||
msgid "There is insufficient information. |
|||
\n" |
|||
"Both destination address and account number must be provided" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.hsbc.clientid:0 |
|||
msgid "HSBC Client IDs" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/hsbc_mt940.py:143 |
|||
#, python-format |
|||
msgid "HSBC Swift MT940 statement export" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,state:0 |
|||
#: field:banking.export.hsbc.wizard,state:0 |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:355 |
|||
#: view:banking.export.hsbc:0 |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
#: model:ir.model,name:account_banking_uk_hsbc.model_banking_export_hsbc |
|||
#: model:ir.model,name:account_banking_uk_hsbc.model_banking_export_hsbc_wizard |
|||
#, python-format |
|||
msgid "HSBC Export" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: selection:banking.export.hsbc,state:0 |
|||
msgid "Draft" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc:0 |
|||
msgid "Processing Information" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/hsbc_mt940.py:145 |
|||
#, python-format |
|||
msgid " This format is available through\n" |
|||
" the HSBC web interface.\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc:0 |
|||
#: field:banking.export.hsbc,payment_order_ids:0 |
|||
#: field:banking.export.hsbc.wizard,payment_order_ids:0 |
|||
msgid "Payment Orders" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: selection:banking.export.hsbc,state:0 |
|||
msgid "Sent" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
#: selection:banking.export.hsbc.wizard,state:0 |
|||
msgid "Finish" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc.wizard,test:0 |
|||
msgid "unknown" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.hsbc.clientid,company_id:0 |
|||
msgid "Company" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: model:ir.model,name:account_banking_uk_hsbc.model_payment_order |
|||
msgid "Payment Order" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.hsbc.clientid,clientid:0 |
|||
msgid "Client ID" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
msgid "Reference for further communication" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: model:ir.model,name:account_banking_uk_hsbc.model_payment_line |
|||
msgid "Payment Line" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
msgid "Processing Details" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: selection:banking.export.hsbc.wizard,state:0 |
|||
msgid "Create" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/hsbc_mt940.py:138 |
|||
#, python-format |
|||
msgid "Import error" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,no_transactions:0 |
|||
#: field:banking.export.hsbc.wizard,no_transactions:0 |
|||
msgid "Number of Transactions" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.hsbc.clientid,name:0 |
|||
msgid "Name" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,execution_date:0 |
|||
#: field:banking.export.hsbc.wizard,execution_date_create:0 |
|||
#: field:banking.export.hsbc.wizard,execution_date_finish:0 |
|||
msgid "Execution Date" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:266 |
|||
#, python-format |
|||
msgid "Transaction invalid: " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,total_amount:0 |
|||
#: field:banking.export.hsbc.wizard,total_amount:0 |
|||
msgid "Total Amount" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: help:banking.export.hsbc.wizard,reference:0 |
|||
msgid "The bank will use this reference in feedback communication to refer to this run. 35 characters are available." |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:315 |
|||
#, python-format |
|||
msgid "Batch invalid: " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:167 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:185 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:210 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:233 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:265 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:286 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:292 |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:314 |
|||
#, python-format |
|||
msgid "Error" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc.wizard,file_id:0 |
|||
msgid "hsbc File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
msgid "Additional message for all transactions" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.hsbc.clientid:0 |
|||
#: model:ir.actions.act_window,name:account_banking_uk_hsbc.banking_hsbc_clientid_action |
|||
#: model:ir.model,name:account_banking_uk_hsbc.model_banking_hsbc_clientid |
|||
#: model:ir.ui.menu,name:account_banking_uk_hsbc.banking_hsbc_clientid_menu |
|||
#: field:payment.order,hsbc_clientid_id:0 |
|||
msgid "HSBC Client ID" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: selection:banking.export.hsbc,state:0 |
|||
msgid "Reconciled" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc.wizard,reference:0 |
|||
msgid "Reference" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc:0 |
|||
msgid "Payment order" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:294 |
|||
#, python-format |
|||
msgid "Your company's bank account has to have a valid UK account number (not IBAN)" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,date_generated:0 |
|||
msgid "Generation Date" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc:0 |
|||
msgid "General Information" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
msgid "Export" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc.wizard,file:0 |
|||
msgid "File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: code:addons/account_banking_uk_hsbc/wizard/export_hsbc.py:287 |
|||
#, python-format |
|||
msgid "Source account invalid: " |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: view:banking.export.hsbc.wizard:0 |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,identification:0 |
|||
msgid "Identification" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: field:banking.export.hsbc,file:0 |
|||
msgid "HSBC File" |
|||
msgstr "" |
|||
|
|||
#. module: account_banking_uk_hsbc |
|||
#: model:ir.actions.act_window,name:account_banking_uk_hsbc.act_banking_export_hsbc_payment_order |
|||
#: model:ir.actions.act_window,name:account_banking_uk_hsbc.action_account_banking_hsbcs |
|||
#: model:ir.ui.menu,name:account_banking_uk_hsbc.menu_action_account_banking_exported_hsbc_files |
|||
msgid "Generated HSBC files" |
|||
msgstr "" |
|||
|
@ -0,0 +1,166 @@ |
|||
#!/usr/bin/env python |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
""" |
|||
Parser for HSBC UK MT940 format files |
|||
Based on fi_patu's parser |
|||
""" |
|||
import re |
|||
from datetime import datetime |
|||
|
|||
|
|||
class HSBCParser(object): |
|||
|
|||
def __init__(self): |
|||
recparse = dict() |
|||
patterns = {'ebcdic': "\w/\?:\(\).,'+{} -"} |
|||
|
|||
# MT940 header |
|||
recparse["20"] = ":(?P<recordid>20):(?P<transref>.{1,16})" |
|||
recparse["25"] = (":(?P<recordid>25):(?P<sortcode>\d{6})" |
|||
"(?P<accnum>\d{1,29})") |
|||
recparse["28"] = ":(?P<recordid>28C?):(?P<statementnr>.{1,8})" |
|||
|
|||
# Opening balance 60F |
|||
recparse["60F"] = (":(?P<recordid>60F):(?P<creditmarker>[CD])" |
|||
"(?P<prevstmtdate>\d{6})(?P<currencycode>.{3})" |
|||
"(?P<startingbalance>[\d,]{1,15})") |
|||
|
|||
# Transaction |
|||
recparse["61"] = """\ |
|||
:(?P<recordid>61):\ |
|||
(?P<valuedate>\d{6})(?P<bookingdate>\d{4})?\ |
|||
(?P<creditmarker>R?[CD])\ |
|||
(?P<currency>[A-Z])?\ |
|||
(?P<amount>[\d,]{1,15})\ |
|||
(?P<bookingcode>[A-Z][A-Z0-9]{3})\ |
|||
(?P<custrefno>[%(ebcdic)s]{1,16})\ |
|||
(?://)\ |
|||
(?P<bankref>[%(ebcdic)s]{1,16})?\ |
|||
(?:\n(?P<furtherinfo>[%(ebcdic)s]))?\ |
|||
""" % (patterns) |
|||
|
|||
# Further info |
|||
recparse["86"] = (":(?P<recordid>86):" |
|||
"(?P<infoline1>.{1,80})?" |
|||
"(?:\n(?P<infoline2>.{1,80}))?" |
|||
"(?:\n(?P<infoline3>.{1,80}))?" |
|||
"(?:\n(?P<infoline4>.{1,80}))?" |
|||
"(?:\n(?P<infoline5>.{1,80}))?") |
|||
|
|||
# Forward available balance (64) / Closing balance (62F) |
|||
# / Interim balance (62M) |
|||
recparse["64"] = (":(?P<recordid>64|62[FM]):" |
|||
"(?P<creditmarker>[CD])" |
|||
"(?P<bookingdate>\d{6})(?P<currencycode>.{3})" |
|||
"(?P<endingbalance>[\d,]{1,15})") |
|||
|
|||
for record in recparse: |
|||
recparse[record] = re.compile(recparse[record]) |
|||
self.recparse = recparse |
|||
|
|||
def parse_record(self, line): |
|||
""" |
|||
Parse record using regexps and apply post processing |
|||
""" |
|||
for matcher in self.recparse: |
|||
matchobj = self.recparse[matcher].match(line) |
|||
if matchobj: |
|||
break |
|||
if not matchobj: |
|||
print(" **** failed to match line '%s'" % (line)) |
|||
return |
|||
# Strip strings |
|||
matchdict = matchobj.groupdict() |
|||
|
|||
# Remove members set to None |
|||
matchdict = dict([(k, v) for k, v in matchdict.iteritems() if v]) |
|||
|
|||
matchkeys = set(matchdict.keys()) |
|||
needstrip = set([ |
|||
"transref", "accnum", "statementnr", "custrefno", |
|||
"bankref", "furtherinfo", "infoline1", "infoline2", "infoline3", |
|||
"infoline4", "infoline5", "startingbalance", "endingbalance" |
|||
]) |
|||
for field in matchkeys & needstrip: |
|||
matchdict[field] = matchdict[field].strip() |
|||
|
|||
# Convert to float. Comma is decimal separator |
|||
needsfloat = set(["startingbalance", "endingbalance", "amount"]) |
|||
for field in matchkeys & needsfloat: |
|||
matchdict[field] = float(matchdict[field].replace(',', '.')) |
|||
|
|||
# Convert date fields |
|||
needdate = set(["prevstmtdate", "valuedate", "bookingdate"]) |
|||
for field in matchkeys & needdate: |
|||
datestring = matchdict[field] |
|||
|
|||
post_check = False |
|||
if (len(datestring) == 4 |
|||
and field == "bookingdate" |
|||
and "valuedate" in matchdict): |
|||
# Get year from valuedate |
|||
datestring = matchdict['valuedate'].strftime('%y') + datestring |
|||
post_check = True |
|||
try: |
|||
matchdict[field] = datetime.strptime(datestring, '%y%m%d') |
|||
if post_check and matchdict[field] > matchdict["valuedate"]: |
|||
matchdict[field] = matchdict[field].replace( |
|||
year=matchdict[field].year-1 |
|||
) |
|||
except ValueError: |
|||
matchdict[field] = None |
|||
|
|||
return matchdict |
|||
|
|||
def parse(self, cr, data): |
|||
records = [] |
|||
# Some records are multiline |
|||
for line in data: |
|||
if len(line) <= 1: |
|||
continue |
|||
if line[0] == ':' and len(line) > 1: |
|||
records.append(line) |
|||
else: |
|||
records[-1] = '\n'.join([records[-1], line]) |
|||
|
|||
output = [] |
|||
for rec in records: |
|||
output.append(self.parse_record(rec)) |
|||
|
|||
return output |
|||
|
|||
|
|||
def parse_file(filename): |
|||
with open(filename, "r") as hsbcfile: |
|||
HSBCParser().parse(hsbcfile.readlines()) |
|||
|
|||
|
|||
def main(): |
|||
"""The main function, currently just calls a dummy filename |
|||
|
|||
:returns: description |
|||
""" |
|||
parse_file("testfile") |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -0,0 +1,3 @@ |
|||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
|||
"access_banking_hsbc_clientid","banking.hsbc.clientid","model_banking_hsbc_clientid","account_payment.group_account_payment",1,1,1,1 |
|||
"access_banking_export_hsbc","banking.export.hsbc","model_banking_export_hsbc","account_payment.group_account_payment",1,1,1,1 |
@ -0,0 +1,23 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import export_hsbc |
@ -0,0 +1,428 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>). |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import base64 |
|||
from datetime import datetime, date |
|||
from decimal import Decimal |
|||
import paymul |
|||
import string |
|||
import random |
|||
import logging |
|||
|
|||
from openerp.osv import orm, fields |
|||
from openerp.tools import ustr |
|||
from openerp.tools.translate import _ |
|||
|
|||
|
|||
def strpdate(arg, format='%Y-%m-%d'): |
|||
"""shortcut""" |
|||
return datetime.strptime(arg, format).date() |
|||
|
|||
|
|||
def strfdate(arg, format='%Y-%m-%d'): |
|||
"""shortcut""" |
|||
return arg.strftime(format) |
|||
|
|||
|
|||
class banking_export_hsbc_wizard(orm.TransientModel): |
|||
_name = 'banking.export.hsbc.wizard' |
|||
_description = 'HSBC Export' |
|||
_columns = { |
|||
'state': fields.selection( |
|||
[ |
|||
('create', 'Create'), |
|||
('finish', 'Finish') |
|||
], |
|||
'State', |
|||
readonly=True, |
|||
), |
|||
'test': fields.boolean(), |
|||
'reference': fields.char( |
|||
'Reference', size=35, |
|||
help=('The bank will use this reference in feedback communication ' |
|||
'to refer to this run. 35 characters are available.' |
|||
), |
|||
), |
|||
'execution_date_create': fields.date( |
|||
'Execution Date', |
|||
help=('This is the date the file should be processed by the bank. ' |
|||
'Don\'t choose a date beyond the nearest date in your ' |
|||
'payments. The latest allowed date is 30 days from now.\n' |
|||
'Please keep in mind that banks only execute on working ' |
|||
'days and typically use a delay of two days between ' |
|||
'execution date and effective transfer date.' |
|||
), |
|||
), |
|||
'file_id': fields.many2one( |
|||
'banking.export.hsbc', |
|||
'hsbc File', |
|||
readonly=True |
|||
), |
|||
'file': fields.related( |
|||
'file_id', 'file', type='binary', |
|||
readonly=True, |
|||
string='File', |
|||
), |
|||
'execution_date_finish': fields.related( |
|||
'file_id', 'execution_date', type='date', |
|||
readonly=True, |
|||
string='Execution Date', |
|||
), |
|||
'total_amount': fields.related( |
|||
'file_id', 'total_amount', |
|||
type='float', |
|||
string='Total Amount', |
|||
readonly=True, |
|||
), |
|||
'no_transactions': fields.integer( |
|||
'Number of Transactions', |
|||
readonly=True, |
|||
), |
|||
'payment_order_ids': fields.many2many( |
|||
'payment.order', 'rel_wiz_payorders', 'wizard_id', |
|||
'payment_order_id', 'Payment Orders', |
|||
readonly=True, |
|||
), |
|||
} |
|||
|
|||
logger = logging.getLogger('export_hsbc') |
|||
|
|||
def create(self, cursor, uid, wizard_data, context=None): |
|||
''' |
|||
Retrieve a sane set of default values based on the payment orders |
|||
from the context. |
|||
''' |
|||
|
|||
if 'execution_date_create' not in wizard_data: |
|||
po_ids = context.get('active_ids', []) |
|||
po_model = self.pool.get('payment.order') |
|||
pos = po_model.browse(cursor, uid, po_ids) |
|||
|
|||
execution_date = date.today() |
|||
|
|||
for po in pos: |
|||
if po.date_prefered == 'fixed' and po.date_planned: |
|||
execution_date = strpdate(po.date_planned) |
|||
elif po.date_prefered == 'due': |
|||
for line in po.line_ids: |
|||
if line.move_line_id.date_maturity: |
|||
date_maturity = strpdate( |
|||
line.move_line_id.date_maturity |
|||
) |
|||
if date_maturity < execution_date: |
|||
execution_date = date_maturity |
|||
|
|||
execution_date = max(execution_date, date.today()) |
|||
|
|||
# The default reference contains a /, which is invalid for PAYMUL |
|||
reference = pos[0].reference.replace('/', ' ') |
|||
|
|||
wizard_data.update({ |
|||
'execution_date_create': strfdate(execution_date), |
|||
'reference': reference, |
|||
'payment_order_ids': [(6, 0, po_ids)], |
|||
'state': 'create', |
|||
}) |
|||
|
|||
return super(banking_export_hsbc_wizard, self).create( |
|||
cursor, uid, wizard_data, context) |
|||
|
|||
def _create_account(self, oe_account, origin_country=None, |
|||
is_origin_account=False): |
|||
# let the receiving bank select the currency from the batch |
|||
currency = None |
|||
holder = oe_account.owner_name or oe_account.partner_id.name |
|||
self.logger.info('Create account %s' % (holder)) |
|||
self.logger.info('-- %s' % (oe_account.country_id.code)) |
|||
self.logger.info('-- %s' % (oe_account.acc_number)) |
|||
|
|||
if oe_account.state == 'iban': |
|||
self.logger.info('IBAN: %s' % (oe_account.acc_number)) |
|||
paymul_account = paymul.IBANAccount( |
|||
iban=oe_account.acc_number, |
|||
bic=oe_account.bank.bic, |
|||
holder=holder, |
|||
currency=currency, |
|||
) |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_EACH_OWN, |
|||
} |
|||
elif oe_account.country_id.code == 'GB': |
|||
self.logger.info('GB: %s %s' % (oe_account.country_id.code, |
|||
oe_account.acc_number)) |
|||
split = oe_account.acc_number.split(" ", 2) |
|||
if len(split) == 2: |
|||
sortcode, accountno = split |
|||
else: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
"Invalid GB acccount number '%s'" % oe_account.acc_number) |
|||
paymul_account = paymul.UKAccount( |
|||
number=accountno, |
|||
sortcode=sortcode, |
|||
holder=holder, |
|||
currency=currency, |
|||
) |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_PAYEE, |
|||
} |
|||
elif oe_account.country_id.code in ('US', 'CA'): |
|||
self.logger.info('US/CA: %s %s' % (oe_account.country_id.code, |
|||
oe_account.acc_number)) |
|||
split = oe_account.acc_number.split(' ', 2) |
|||
if len(split) == 2: |
|||
sortcode, accountno = split |
|||
else: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_("Invalid %s account number '%s'") % |
|||
(oe_account.country_id.code, oe_account.acc_number)) |
|||
paymul_account = paymul.NorthAmericanAccount( |
|||
number=accountno, |
|||
sortcode=sortcode, |
|||
holder=holder, |
|||
currency=currency, |
|||
swiftcode=oe_account.bank.bic, |
|||
country=oe_account.country_id.code, |
|||
origin_country=origin_country, |
|||
is_origin_account=is_origin_account |
|||
) |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_PAYEE, |
|||
} |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_PAYEE, |
|||
} |
|||
else: |
|||
self.logger.info('SWIFT Account: %s' % oe_account.country_id.code) |
|||
split = oe_account.acc_number.split(' ', 2) |
|||
if len(split) == 2: |
|||
sortcode, accountno = split |
|||
else: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_("Invalid %s account number '%s'") % |
|||
(oe_account.country_id.code, oe_account.acc_number)) |
|||
paymul_account = paymul.SWIFTAccount( |
|||
number=accountno, |
|||
sortcode=sortcode, |
|||
holder=holder, |
|||
currency=currency, |
|||
swiftcode=oe_account.bank.bic, |
|||
country=oe_account.country_id.code, |
|||
) |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_PAYEE, |
|||
} |
|||
transaction_kwargs = { |
|||
'charges': paymul.CHARGES_PAYEE, |
|||
} |
|||
|
|||
return paymul_account, transaction_kwargs |
|||
|
|||
def _create_transaction(self, line): |
|||
# Check on missing partner of bank account (this can happen!) |
|||
if not line.bank_id or not line.bank_id.partner_id: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('There is insufficient information.\r\n' |
|||
'Both destination address and account ' |
|||
'number must be provided') |
|||
) |
|||
|
|||
self.logger.info('====') |
|||
dest_account, transaction_kwargs = self._create_account( |
|||
line.bank_id, line.order_id.mode.bank_id.country_id.code |
|||
) |
|||
|
|||
means = { |
|||
'ACH or EZONE': paymul.MEANS_ACH_OR_EZONE, |
|||
'Faster Payment': paymul.MEANS_FASTER_PAYMENT, |
|||
'Priority Payment': paymul.MEANS_PRIORITY_PAYMENT |
|||
}.get(line.order_id.mode.type.name) |
|||
if means is None: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_("Invalid payment type mode for HSBC '%s'") |
|||
% line.order_id.mode.type.name |
|||
) |
|||
|
|||
if not line.info_partner: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_("No default address for transaction '%s'") % line.name |
|||
) |
|||
|
|||
try: |
|||
return paymul.Transaction( |
|||
amount=Decimal(str(line.amount_currency)), |
|||
currency=line.currency.name, |
|||
account=dest_account, |
|||
means=means, |
|||
name_address=line.info_partner, |
|||
customer_reference=line.name, |
|||
payment_reference=line.name, |
|||
**transaction_kwargs |
|||
) |
|||
except ValueError as exc: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('Transaction invalid: %s') + ustr(exc) |
|||
) |
|||
|
|||
def wizard_export(self, cursor, uid, wizard_data_ids, context): |
|||
''' |
|||
Wizard to actually create the HSBC file |
|||
''' |
|||
|
|||
wizard_data = self.browse(cursor, uid, wizard_data_ids, context)[0] |
|||
result_model = self.pool.get('banking.export.hsbc') |
|||
payment_orders = wizard_data.payment_order_ids |
|||
|
|||
try: |
|||
self.logger.info( |
|||
'Source - %s (%s) %s' % ( |
|||
payment_orders[0].mode.bank_id.partner_id.name, |
|||
payment_orders[0].mode.bank_id.acc_number, |
|||
payment_orders[0].mode.bank_id.country_id.code) |
|||
) |
|||
src_account = self._create_account( |
|||
payment_orders[0].mode.bank_id, |
|||
payment_orders[0].mode.bank_id.country_id.code, |
|||
is_origin_account=True |
|||
)[0] |
|||
except ValueError as exc: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('Source account invalid: ') + ustr(exc) |
|||
) |
|||
|
|||
if not isinstance(src_account, paymul.UKAccount): |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_("Your company's bank account has to have a valid UK " |
|||
"account number (not IBAN)" + ustr(type(src_account))) |
|||
) |
|||
|
|||
try: |
|||
self.logger.info('Create transactions...') |
|||
transactions = [] |
|||
hsbc_clientid = '' |
|||
for po in payment_orders: |
|||
transactions += [ |
|||
self._create_transaction(l) for l in po.line_ids |
|||
] |
|||
hsbc_clientid = po.hsbc_clientid_id.clientid |
|||
|
|||
batch = paymul.Batch( |
|||
exec_date=strpdate(wizard_data.execution_date_create), |
|||
reference=wizard_data.reference, |
|||
debit_account=src_account, |
|||
name_address=payment_orders[0].line_ids[0].info_owner, |
|||
) |
|||
batch.transactions = transactions |
|||
except ValueError as exc: |
|||
raise orm.except_orm( |
|||
_('Error'), |
|||
_('Batch invalid: ') + ustr(exc) |
|||
) |
|||
|
|||
# Generate random identifier until an unused one is found |
|||
while True: |
|||
ref = ''.join(random.choice(string.ascii_uppercase + string.digits) |
|||
for x in range(15)) |
|||
|
|||
ids = result_model.search(cursor, uid, [ |
|||
('identification', '=', ref) |
|||
]) |
|||
|
|||
if not ids: |
|||
break |
|||
|
|||
message = paymul.Message(reference=ref) |
|||
message.batches.append(batch) |
|||
interchange = paymul.Interchange(client_id=hsbc_clientid, |
|||
reference=ref, |
|||
message=message) |
|||
|
|||
export_result = { |
|||
'identification': interchange.reference, |
|||
'execution_date': batch.exec_date, |
|||
'total_amount': batch.amount(), |
|||
'no_transactions': len(batch.transactions), |
|||
'file': base64.encodestring(str(interchange)), |
|||
'payment_order_ids': [ |
|||
[6, 0, [po.id for po in payment_orders]] |
|||
], |
|||
} |
|||
file_id = result_model.create(cursor, uid, export_result, context) |
|||
|
|||
self.write(cursor, uid, [wizard_data_ids[0]], { |
|||
'file_id': file_id, |
|||
'no_transactions': len(batch.transactions), |
|||
'state': 'finish', |
|||
}, context) |
|||
|
|||
return { |
|||
'name': _('HSBC Export'), |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': self._name, |
|||
'domain': [], |
|||
'context': dict(context, active_ids=wizard_data_ids), |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'new', |
|||
'res_id': wizard_data_ids[0] or False, |
|||
} |
|||
|
|||
def wizard_cancel(self, cursor, uid, ids, context): |
|||
''' |
|||
Cancel the export: just drop the file |
|||
''' |
|||
|
|||
wizard_data = self.browse(cursor, uid, ids, context)[0] |
|||
result_model = self.pool.get('banking.export.hsbc') |
|||
|
|||
try: |
|||
result_model.unlink(cursor, uid, wizard_data.file_id.id) |
|||
except AttributeError: |
|||
# file_id missing, wizard storage gone, server was restarted |
|||
pass |
|||
|
|||
return {'type': 'ir.actions.act_window_close'} |
|||
|
|||
def wizard_save(self, cursor, uid, ids, context): |
|||
''' |
|||
Save the export: mark all payments in the file as 'sent' |
|||
''' |
|||
|
|||
wizard_data = self.browse(cursor, uid, ids, context)[0] |
|||
result_model = self.pool.get('banking.export.hsbc') |
|||
po_model = self.pool.get('payment.order') |
|||
|
|||
result_model.write(cursor, uid, [wizard_data.file_id.id], |
|||
{'state': 'sent'}) |
|||
|
|||
po_ids = [po.id for po in wizard_data.payment_order_ids] |
|||
po_model.action_sent(cursor, uid, po_ids) |
|||
|
|||
return {'type': 'ir.actions.act_window_close'} |
@ -0,0 +1,37 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="wizard_banking_export_wizard_view" model="ir.ui.view"> |
|||
<field name="name">banking.export.hsbc.wizard.view</field> |
|||
<field name="model">banking.export.hsbc.wizard</field> |
|||
<field name="type">form</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="HSBC Export"> |
|||
<field name="state" invisible="True"/> |
|||
<group states="create"> |
|||
<separator colspan="4" string="Processing Details"/> |
|||
<field name="execution_date_create"/> |
|||
<field name="test"/> |
|||
<separator colspan="4" string="Reference for further communication"/> |
|||
<field name="reference" colspan="2"/> |
|||
<separator colspan="4" string="Additional message for all transactions"/> |
|||
<newline/> |
|||
<button icon="gtk-close" special="cancel" string="Cancel"/> |
|||
<button icon="gtk-ok" string="Export" name="wizard_export" type="object"/> |
|||
</group> |
|||
<group states="finish"> |
|||
<field name="total_amount"/> |
|||
<field name="no_transactions"/> |
|||
<field name="execution_date_finish"/> |
|||
<newline/> |
|||
<!--<field name="file_id"/>--> |
|||
<field name="file"/> |
|||
<newline/> |
|||
<button icon="gtk-close" string="Cancel" name="wizard_cancel" type="object"/> |
|||
<button icon="gtk-ok" string="Finish" name="wizard_save" type="object"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,686 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from account_banking import sepa |
|||
from decimal import Decimal |
|||
import datetime |
|||
import re |
|||
import unicodedata |
|||
|
|||
from openerp.tools import ustr |
|||
|
|||
|
|||
def strip_accents(string): |
|||
res = unicodedata.normalize('NFKD', ustr(string)) |
|||
res = res.encode('ASCII', 'ignore') |
|||
return res |
|||
|
|||
|
|||
def split_account_holder(holder): |
|||
holder_parts = holder.split("\n") |
|||
|
|||
try: |
|||
line2 = holder_parts[1] |
|||
except IndexError: |
|||
line2 = '' |
|||
|
|||
return holder_parts[0], line2 |
|||
|
|||
|
|||
def address_truncate(name_address): |
|||
addr_line = name_address.upper().split("\n")[0:5] |
|||
addr_line = [s[:35] for s in addr_line] |
|||
return addr_line |
|||
|
|||
|
|||
def edifact_isalnum(s): |
|||
"""The standard says alphanumeric characters, but spaces are also |
|||
allowed |
|||
""" |
|||
return bool(re.match(r'^[A-Za-z0-9 ]*$', s)) |
|||
|
|||
|
|||
def edifact_digits(val, digits=None, mindigits=None): |
|||
if digits is None: |
|||
digits = '' |
|||
if mindigits is None: |
|||
mindigits = digits |
|||
|
|||
pattern = r'^[0-9]{' + str(mindigits) + ',' + str(digits) + r'}$' |
|||
return bool(re.match(pattern, str(val))) |
|||
|
|||
|
|||
def edifact_isalnum_size(val, digits): |
|||
pattern = r'^[A-Za-z0-9 ]{' + str(digits) + ',' + str(digits) + r'}$' |
|||
return bool(re.match(pattern, str(val))) |
|||
|
|||
|
|||
class HasCurrency(object): |
|||
def _get_currency(self): |
|||
return self._currency |
|||
|
|||
def _set_currency(self, currency): |
|||
if currency is None: |
|||
self._currency = None |
|||
else: |
|||
if not len(currency) <= 3: |
|||
raise ValueError("Currency must be <= 3 characters long: %s" % |
|||
ustr(currency)) |
|||
|
|||
if not edifact_isalnum(currency): |
|||
raise ValueError("Currency must be alphanumeric: %s" % |
|||
ustr(currency)) |
|||
|
|||
self._currency = currency.upper() |
|||
|
|||
currency = property(_get_currency, _set_currency) |
|||
|
|||
|
|||
class LogicalSection(object): |
|||
|
|||
def __str__(self): |
|||
segments = self.segments() |
|||
|
|||
def format_segment(segment): |
|||
return '+'.join( |
|||
[':'.join([str(strip_accents(y)) for y in x]) for x in segment] |
|||
) + "'" |
|||
|
|||
return "\n".join([format_segment(s) for s in segments]) |
|||
|
|||
|
|||
def _fii_segment(self, party_qualifier): |
|||
holder = split_account_holder(self.holder) |
|||
account_identification = [self.number.replace(' ', ''), holder[0]] |
|||
if holder[1] or self.currency: |
|||
account_identification.append(holder[1]) |
|||
if self.currency: |
|||
account_identification.append(self.currency) |
|||
return [ |
|||
['FII'], |
|||
[party_qualifier], |
|||
account_identification, |
|||
self.institution_identification, |
|||
[self.country], |
|||
] |
|||
|
|||
|
|||
class UKAccount(HasCurrency): |
|||
def _get_number(self): |
|||
return self._number |
|||
|
|||
def _set_number(self, number): |
|||
if not edifact_digits(number, 8): |
|||
raise ValueError("Account number must be 8 digits long: " + |
|||
str(number)) |
|||
|
|||
self._number = number |
|||
|
|||
number = property(_get_number, _set_number) |
|||
|
|||
def _get_sortcode(self): |
|||
return self._sortcode |
|||
|
|||
def _set_sortcode(self, sortcode): |
|||
if not edifact_digits(sortcode, 6): |
|||
raise ValueError("Account sort code must be 6 digits long: %s" % |
|||
ustr(sortcode)) |
|||
|
|||
self._sortcode = sortcode |
|||
|
|||
sortcode = property(_get_sortcode, _set_sortcode) |
|||
|
|||
def _get_holder(self): |
|||
return self._holder |
|||
|
|||
def _set_holder(self, holder): |
|||
holder_parts = split_account_holder(holder) |
|||
|
|||
if not len(holder_parts[0]) <= 35: |
|||
raise ValueError("Account holder must be <= 35 characters long: %s" |
|||
% ustr(holder_parts[0])) |
|||
|
|||
if not len(holder_parts[1]) <= 35: |
|||
raise ValueError("Second line of account holder must be <= 35 " |
|||
"characters long: %s" % ustr(holder_parts[1])) |
|||
|
|||
if not edifact_isalnum(holder_parts[0]): |
|||
raise ValueError("Account holder must be alphanumeric: %s" % |
|||
ustr(holder_parts[0])) |
|||
|
|||
if not edifact_isalnum(holder_parts[1]): |
|||
raise ValueError("Second line of account holder must be " |
|||
"alphanumeric: %s" % ustr(holder_parts[1])) |
|||
|
|||
self._holder = holder.upper() |
|||
|
|||
holder = property(_get_holder, _set_holder) |
|||
|
|||
def __init__(self, number, holder, currency, sortcode): |
|||
self.number = number |
|||
self.holder = holder |
|||
self.currency = currency |
|||
self.sortcode = sortcode |
|||
self.country = 'GB' |
|||
self.institution_identification = ['', '', '', self.sortcode, 154, 133] |
|||
|
|||
def fii_bf_segment(self): |
|||
return _fii_segment(self, 'BF') |
|||
|
|||
def fii_or_segment(self): |
|||
return _fii_segment(self, 'OR') |
|||
|
|||
|
|||
class NorthAmericanAccount(UKAccount): |
|||
|
|||
def _set_account_ident(self): |
|||
if self.origin_country in ('US', 'CA'): |
|||
# Use the routing number |
|||
account_ident = ['', '', '', self.sortcode, 155, 114] |
|||
else: |
|||
# Using the BIC/Swift Code |
|||
account_ident = [self.bic, 25, 5, '', '', ''] |
|||
return account_ident |
|||
|
|||
def _set_sortcode(self, sortcode): |
|||
if self.origin_country == 'CA' and self.is_origin_account: |
|||
expected_digits = 6 |
|||
else: |
|||
expected_digits = 9 |
|||
if not edifact_digits(sortcode, expected_digits): |
|||
raise ValueError("Account routing number must be %d digits long: " |
|||
"%s" % (expected_digits, ustr(sortcode))) |
|||
|
|||
self._sortcode = sortcode |
|||
|
|||
def _get_sortcode(self): |
|||
return self._sortcode |
|||
|
|||
sortcode = property(_get_sortcode, _set_sortcode) |
|||
|
|||
def _set_bic(self, bic): |
|||
if (not edifact_isalnum_size(bic, 8) |
|||
and not edifact_isalnum_size(bic, 11)): |
|||
raise ValueError("Account BIC/Swift code must be 8 or 11 " |
|||
"characters long: %s" % ustr(bic)) |
|||
self._bic = bic |
|||
|
|||
def _get_bic(self): |
|||
return self._bic |
|||
|
|||
bic = property(_get_bic, _set_bic) |
|||
|
|||
def _set_number(self, number): |
|||
if not edifact_digits(number, mindigits=1): |
|||
raise ValueError("Account number is invalid: %s" % ustr(number)) |
|||
|
|||
self._number = number |
|||
|
|||
def _get_number(self): |
|||
return self._number |
|||
|
|||
number = property(_get_number, _set_number) |
|||
|
|||
def __init__(self, number, holder, currency, sortcode, swiftcode, country, |
|||
origin_country=None, is_origin_account=False): |
|||
self.origin_country = origin_country |
|||
self.is_origin_account = is_origin_account |
|||
self.number = number |
|||
self.holder = holder |
|||
self.currency = currency |
|||
self.sortcode = sortcode |
|||
self.country = country |
|||
self.bic = swiftcode |
|||
self.institution_identification = self._set_account_ident() |
|||
|
|||
|
|||
class SWIFTAccount(UKAccount): |
|||
|
|||
def _set_account_ident(self): |
|||
# Using the BIC/Swift Code |
|||
return [self.bic, 25, 5, '', '', ''] |
|||
|
|||
def _set_sortcode(self, sortcode): |
|||
self._sortcode = sortcode |
|||
|
|||
def _get_sortcode(self): |
|||
return self._sortcode |
|||
|
|||
sortcode = property(_get_sortcode, _set_sortcode) |
|||
|
|||
def _set_bic(self, bic): |
|||
if (not edifact_isalnum_size(bic, 8) |
|||
and not edifact_isalnum_size(bic, 11)): |
|||
raise ValueError("Account BIC/Swift code must be 8 or 11 " |
|||
"characters long: %s" % ustr(bic)) |
|||
self._bic = bic |
|||
|
|||
def _get_bic(self): |
|||
return self._bic |
|||
|
|||
bic = property(_get_bic, _set_bic) |
|||
|
|||
def _set_number(self, number): |
|||
if not edifact_digits(number, mindigits=1): |
|||
raise ValueError("Account number is invalid: %s" % |
|||
ustr(number)) |
|||
|
|||
self._number = number |
|||
|
|||
def _get_number(self): |
|||
return self._number |
|||
|
|||
number = property(_get_number, _set_number) |
|||
|
|||
def __init__(self, number, holder, currency, sortcode, swiftcode, country, |
|||
origin_country=None, is_origin_account=False): |
|||
self.origin_country = origin_country |
|||
self.is_origin_account = is_origin_account |
|||
self.number = number |
|||
self.holder = holder |
|||
self.currency = currency |
|||
self.sortcode = sortcode |
|||
self.country = country |
|||
self.bic = swiftcode |
|||
self.institution_identification = self._set_account_ident() |
|||
|
|||
|
|||
class IBANAccount(HasCurrency): |
|||
def _get_iban(self): |
|||
return self._iban |
|||
|
|||
def _set_iban(self, iban): |
|||
iban_obj = sepa.IBAN(iban) |
|||
if not iban_obj.valid: |
|||
raise ValueError("IBAN is invalid: %s" % ustr(iban)) |
|||
|
|||
self._iban = iban |
|||
self.country = iban_obj.countrycode |
|||
|
|||
iban = property(_get_iban, _set_iban) |
|||
|
|||
def __init__(self, iban, bic, currency, holder): |
|||
self.iban = iban |
|||
self.number = iban |
|||
self.bic = bic |
|||
self.currency = currency |
|||
self.holder = holder |
|||
self.institution_identification = [self.bic, 25, 5, '', '', ''] |
|||
|
|||
def fii_bf_segment(self): |
|||
return _fii_segment(self, 'BF') |
|||
|
|||
|
|||
class Interchange(LogicalSection): |
|||
def _get_reference(self): |
|||
return self._reference |
|||
|
|||
def _set_reference(self, reference): |
|||
if not len(reference) <= 15: |
|||
raise ValueError("Reference must be <= 15 characters long: %s" % |
|||
ustr(reference)) |
|||
|
|||
if not edifact_isalnum(reference): |
|||
raise ValueError("Reference must be alphanumeric: %s" % |
|||
ustr(reference)) |
|||
|
|||
self._reference = reference.upper() |
|||
|
|||
reference = property(_get_reference, _set_reference) |
|||
|
|||
def __init__(self, client_id, reference, create_dt=None, message=None): |
|||
self.client_id = client_id |
|||
self.create_dt = create_dt or datetime.datetime.now() |
|||
self.reference = reference |
|||
self.message = message |
|||
|
|||
def segments(self): |
|||
segments = [] |
|||
segments.append([ |
|||
['UNB'], |
|||
['UNOA', 3], |
|||
['', '', self.client_id], |
|||
['', '', 'HEXAGON ABC'], |
|||
[self.create_dt.strftime('%y%m%d'), |
|||
self.create_dt.strftime('%H%M')], |
|||
[self.reference], |
|||
]) |
|||
segments += self.message.segments() |
|||
segments.append([ |
|||
['UNZ'], |
|||
[1], |
|||
[self.reference], |
|||
]) |
|||
return segments |
|||
|
|||
|
|||
class Message(LogicalSection): |
|||
def _get_reference(self): |
|||
return self._reference |
|||
|
|||
def _set_reference(self, reference): |
|||
if not len(reference) <= 35: |
|||
raise ValueError("Reference must be <= 35 characters long: %s" % |
|||
ustr(reference)) |
|||
|
|||
if not edifact_isalnum(reference): |
|||
raise ValueError("Reference must be alphanumeric: %s" % |
|||
ustr(reference)) |
|||
|
|||
self._reference = reference.upper() |
|||
|
|||
reference = property(_get_reference, _set_reference) |
|||
|
|||
def __init__(self, reference, dt=None): |
|||
if dt: |
|||
self.dt = dt |
|||
else: |
|||
self.dt = datetime.datetime.now() |
|||
|
|||
self.reference = reference |
|||
self.batches = [] |
|||
|
|||
def segments(self): |
|||
# HSBC only accepts one message per interchange |
|||
message_reference_number = 1 |
|||
|
|||
segments = [] |
|||
|
|||
segments.append([ |
|||
['UNH'], |
|||
[message_reference_number], |
|||
['PAYMUL', 'D', '96A', 'UN', 'FUN01G'], |
|||
]) |
|||
segments.append([ |
|||
['BGM'], |
|||
[452], |
|||
[self.reference], |
|||
[9], |
|||
]) |
|||
segments.append([ |
|||
['DTM'], |
|||
(137, self.dt.strftime('%Y%m%d'), 102), |
|||
]) |
|||
for index, batch in enumerate(self.batches): |
|||
segments += batch.segments(index + 1) |
|||
segments.append([ |
|||
['CNT'], |
|||
['39', sum([len(x.transactions) for x in self.batches])], |
|||
]) |
|||
segments.append([ |
|||
['UNT'], |
|||
[len(segments) + 1], |
|||
[message_reference_number] |
|||
]) |
|||
|
|||
return segments |
|||
|
|||
|
|||
class Batch(LogicalSection): |
|||
def _get_reference(self): |
|||
return self._reference |
|||
|
|||
def _set_reference(self, reference): |
|||
if not len(reference) <= 18: |
|||
raise ValueError("Reference must be <= 18 characters long: %s" % |
|||
ustr(reference)) |
|||
|
|||
if not edifact_isalnum(reference): |
|||
raise ValueError("Reference must be alphanumeric: %s" % |
|||
ustr(reference)) |
|||
|
|||
self._reference = reference.upper() |
|||
|
|||
reference = property(_get_reference, _set_reference) |
|||
|
|||
def __init__(self, exec_date, reference, debit_account, name_address): |
|||
self.exec_date = exec_date |
|||
self.reference = reference |
|||
self.debit_account = debit_account |
|||
self.name_address = name_address |
|||
self.transactions = [] |
|||
|
|||
def amount(self): |
|||
return sum([x.amount for x in self.transactions]) |
|||
|
|||
def segments(self, index): |
|||
if not edifact_digits(index, 6, 1): |
|||
raise ValueError("Index must be 6 digits or less: " + str(index)) |
|||
|
|||
# Store the payment means |
|||
means = None |
|||
if len(self.transactions) > 0: |
|||
means = self.transactions[0].means |
|||
|
|||
segments = [] |
|||
|
|||
if means != MEANS_PRIORITY_PAYMENT: |
|||
segments.append([ |
|||
['LIN'], |
|||
[index], |
|||
]) |
|||
segments.append([ |
|||
['DTM'], |
|||
[203, self.exec_date.strftime('%Y%m%d'), 102], |
|||
]) |
|||
segments.append([ |
|||
['RFF'], |
|||
['AEK', self.reference], |
|||
]) |
|||
|
|||
currencies = set([x.currency for x in self.transactions]) |
|||
if len(currencies) > 1: |
|||
raise ValueError("All transactions in a batch must have the " |
|||
"same currency") |
|||
|
|||
segments.append([ |
|||
['MOA'], |
|||
[9, self.amount().quantize(Decimal('0.00')), currencies.pop()], |
|||
]) |
|||
segments.append(self.debit_account.fii_or_segment()) |
|||
segments.append([ |
|||
['NAD'], |
|||
['OY'], |
|||
[''], |
|||
address_truncate(self.name_address), |
|||
]) |
|||
|
|||
for index, transaction in enumerate(self.transactions): |
|||
if transaction.means == MEANS_PRIORITY_PAYMENT: |
|||
# Need a debit-credit format for Priority Payments |
|||
segments.append([ |
|||
['LIN'], |
|||
[index+1], |
|||
]) |
|||
segments.append([ |
|||
['DTM'], |
|||
[203, self.exec_date.strftime('%Y%m%d'), 102], |
|||
]) |
|||
segments.append([ |
|||
['RFF'], |
|||
['AEK', self.reference], |
|||
]) |
|||
|
|||
# Use the transaction amount and currency for the debit line |
|||
segments.append([ |
|||
['MOA'], |
|||
[9, transaction.amount.quantize(Decimal('0.00')), |
|||
transaction.currency], |
|||
]) |
|||
segments.append(self.debit_account.fii_or_segment()) |
|||
segments.append([ |
|||
['NAD'], |
|||
['OY'], |
|||
[''], |
|||
address_truncate(self.name_address), |
|||
]) |
|||
use_index = 1 |
|||
else: |
|||
use_index = index + 1 |
|||
|
|||
segments += transaction.segments(use_index) |
|||
|
|||
return segments |
|||
|
|||
# From the spec for FCA segments: |
|||
# 13 = All charges borne by payee (or beneficiary) |
|||
# 14 = Each pay own cost |
|||
# 15 = All charges borne by payor (or ordering customer) |
|||
# For Faster Payments this should always be ‘14’ |
|||
# Where this field is not present, “14” will be used as a default. |
|||
CHARGES_PAYEE = 13 |
|||
CHARGES_EACH_OWN = 14 |
|||
CHARGES_PAYER = 15 |
|||
|
|||
# values per section 2.8.5 "PAI, Payment Instructions" of |
|||
# "HSBC - CRG Paymul Message Implementation Guide" |
|||
MEANS_ACH_OR_EZONE = 2 |
|||
MEANS_PRIORITY_PAYMENT = 52 |
|||
MEANS_FASTER_PAYMENT = 'FPS' |
|||
|
|||
CHANNEL_INTRA_COMPANY = 'Z24' |
|||
|
|||
|
|||
class Transaction(LogicalSection, HasCurrency): |
|||
def _get_amount(self): |
|||
return self._amount |
|||
|
|||
def _set_amount(self, amount): |
|||
if len(str(amount)) > 18: |
|||
raise ValueError("Amount must be shorter than 18 bytes: %s" % |
|||
ustr(amount)) |
|||
|
|||
self._amount = amount |
|||
|
|||
amount = property(_get_amount, _set_amount) |
|||
|
|||
def _get_payment_reference(self): |
|||
return self._payment_reference |
|||
|
|||
def _set_payment_reference(self, payment_reference): |
|||
if not len(payment_reference) <= 18: |
|||
raise ValueError( |
|||
"Payment reference must be <= 18 characters long: %s" % |
|||
ustr(payment_reference) |
|||
) |
|||
|
|||
if not edifact_isalnum(payment_reference): |
|||
raise ValueError("Payment reference must be alphanumeric: %s" % |
|||
ustr(payment_reference)) |
|||
|
|||
self._payment_reference = payment_reference.upper() |
|||
|
|||
payment_reference = property( |
|||
_get_payment_reference, _set_payment_reference |
|||
) |
|||
|
|||
def _get_customer_reference(self): |
|||
return self._customer_reference |
|||
|
|||
def _set_customer_reference(self, customer_reference): |
|||
if not len(customer_reference) <= 18: |
|||
raise ValueError( |
|||
"Customer reference must be <= 18 characters long: %s" % |
|||
ustr(customer_reference) |
|||
) |
|||
|
|||
if not edifact_isalnum(customer_reference): |
|||
raise ValueError("Customer reference must be alphanumeric: %s" % |
|||
ustr(customer_reference)) |
|||
|
|||
self._customer_reference = customer_reference.upper() |
|||
|
|||
customer_reference = property( |
|||
_get_customer_reference, |
|||
_set_customer_reference |
|||
) |
|||
|
|||
def __init__(self, amount, currency, account, means, |
|||
name_address=None, party_name=None, channel='', |
|||
charges=CHARGES_EACH_OWN, customer_reference=None, |
|||
payment_reference=None): |
|||
self.amount = amount |
|||
self.currency = currency |
|||
self.account = account |
|||
self.name_address = name_address |
|||
self.party_name = party_name |
|||
self.means = means |
|||
self.channel = channel |
|||
self.charges = charges |
|||
self.payment_reference = payment_reference |
|||
self.customer_reference = customer_reference |
|||
|
|||
def segments(self, index): |
|||
segments = [] |
|||
segments.append([ |
|||
['SEQ'], |
|||
[''], |
|||
[index], |
|||
]) |
|||
segments.append([ |
|||
['MOA'], |
|||
[9, self.amount.quantize(Decimal('0.00')), self.currency], |
|||
]) |
|||
|
|||
if self.customer_reference: |
|||
segments.append([ |
|||
['RFF'], |
|||
['CR', self.customer_reference], |
|||
]) |
|||
|
|||
if self.payment_reference: |
|||
segments.append([ |
|||
['RFF'], |
|||
['PQ', self.payment_reference], |
|||
]) |
|||
|
|||
if self.channel: |
|||
segments.append([ |
|||
['PAI'], |
|||
['', '', self.means, '', '', self.channel], |
|||
]) |
|||
else: |
|||
segments.append([ |
|||
['PAI'], |
|||
['', '', self.means], |
|||
]) |
|||
|
|||
segments.append([ |
|||
['FCA'], |
|||
[self.charges], |
|||
]) |
|||
|
|||
segments.append(self.account.fii_bf_segment()) |
|||
|
|||
nad_segment = [ |
|||
['NAD'], |
|||
['BE'], |
|||
[''], |
|||
] |
|||
if self.name_address: |
|||
nad_segment.append(address_truncate(self.name_address)) |
|||
else: |
|||
nad_segment.append('') |
|||
if self.party_name: |
|||
nad_segment.append(address_truncate(self.party_name)) |
|||
segments.append(nad_segment) |
|||
|
|||
return segments |
@ -0,0 +1,300 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>). |
|||
# All Rights Reserved |
|||
# |
|||
# 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 |
|||
# 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 Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import datetime |
|||
import unittest2 as unittest |
|||
import paymul |
|||
|
|||
from decimal import Decimal |
|||
|
|||
|
|||
class PaymulTestCase(unittest.TestCase): |
|||
|
|||
def setUp(self): |
|||
self.maxDiff = None |
|||
|
|||
def test_uk_high_value_priority_payment(self): |
|||
# Changes from spec example: Removed DTM for transaction, HSBC ignores |
|||
# it (section 2.8.3) |
|||
expected = """\ |
|||
UNB+UNOA:3+::ABC00000001+::HEXAGON ABC+041111:1500+UKHIGHVALUE' |
|||
UNH+1+PAYMUL:D:96A:UN:FUN01G' |
|||
BGM+452+UKHIGHVALUE+9' |
|||
DTM+137:20041111:102' |
|||
LIN+1' |
|||
DTM+203:20041112:102' |
|||
RFF+AEK:UKHIGHVALUE' |
|||
MOA+9:1.00:GBP' |
|||
FII+OR+12345678:HSBC NET TEST::GBP+:::400515:154:133+GB' |
|||
NAD+OY++HSBC BANK PLC:HSBC NET TEST:TEST:TEST:UNITED KINGDOM' |
|||
SEQ++1' |
|||
MOA+9:1.00:GBP' |
|||
RFF+CR:CRUKHV5' |
|||
RFF+PQ:PQUKHV5' |
|||
PAI+::52:::Z24' |
|||
FCA+13' |
|||
FII+BF+87654321:XYX LTD FROM FII BF 1:BEN NAME 2:GBP+:::403124:154:133+GB' |
|||
NAD+BE++SOME BANK PLC:HSBC NET TEST:TEST:TEST:UNITED KINGDOM' |
|||
CNT+39:1' |
|||
UNT+19+1' |
|||
UNZ+1+UKHIGHVALUE'""" |
|||
|
|||
src_account = paymul.UKAccount( |
|||
number=12345678, |
|||
holder='HSBC NET TEST', |
|||
currency='GBP', |
|||
sortcode=400515 |
|||
) |
|||
|
|||
dest_account = paymul.UKAccount( |
|||
number=87654321, |
|||
holder="XYX LTD FROM FII BF 1\nBEN NAME 2", |
|||
currency='GBP', |
|||
sortcode=403124 |
|||
) |
|||
|
|||
transaction = paymul.Transaction( |
|||
amount=Decimal('1.00'), |
|||
currency='GBP', |
|||
account=dest_account, |
|||
charges=paymul.CHARGES_PAYEE, |
|||
means=paymul.MEANS_PRIORITY_PAYMENT, |
|||
channel=paymul.CHANNEL_INTRA_COMPANY, |
|||
name_address="SOME BANK PLC\n" |
|||
"HSBC NET TEST\n" |
|||
"TEST\n" |
|||
"TEST\n" |
|||
"UNITED KINGDOM", |
|||
customer_reference='CRUKHV5', |
|||
payment_reference='PQUKHV5' |
|||
) |
|||
|
|||
batch = paymul.Batch( |
|||
exec_date=datetime.date(2004, 11, 12), |
|||
reference='UKHIGHVALUE', |
|||
debit_account=src_account, |
|||
name_address="HSBC BANK PLC\n" |
|||
"HSBC NET TEST\n" |
|||
"TEST\n" |
|||
"TEST\n" |
|||
"UNITED KINGDOM") |
|||
batch.transactions.append(transaction) |
|||
|
|||
message = paymul.Message(reference='UKHIGHVALUE', |
|||
dt=datetime.datetime(2004, 11, 11)) |
|||
message.batches.append(batch) |
|||
|
|||
interchange = paymul.Interchange( |
|||
client_id='ABC00000001', |
|||
reference='UKHIGHVALUE', |
|||
create_dt=datetime.datetime(2004, 11, 11, 15, 00), |
|||
message=message |
|||
) |
|||
|
|||
self.assertMultiLineEqual(expected, str(interchange)) |
|||
|
|||
def test_ezone(self): |
|||
# Changes from example in spec: Changed CNT from 27 to 39, because we |
|||
# only generate that and it makes no difference which one we use |
|||
# Removed DTM for transaction, HSBC ignores it (section 2.8.3) |
|||
|
|||
expected = """UNB+UNOA:3+::ABC12016001+::HEXAGON ABC+080110:0856+EZONE' |
|||
UNH+1+PAYMUL:D:96A:UN:FUN01G' |
|||
BGM+452+EZONE+9' |
|||
DTM+137:20080110:102' |
|||
LIN+1' |
|||
DTM+203:20080114:102' |
|||
RFF+AEK:EZONE' |
|||
MOA+9:1.00:EUR' |
|||
FII+OR+12345678:ACCOUNT HOLDER NAME::EUR+:::403124:154:133+GB' |
|||
NAD+OY++ORD PARTY NAME NADOY 01:CRG TC5 001 NADOY ADDRESS LINE 0001:CRG TC5 \ |
|||
1001 NADOY ADDRESS LINE 0002' |
|||
SEQ++1' |
|||
MOA+9:1.00:EUR' |
|||
RFF+CR:EZONE 1A' |
|||
RFF+PQ:EZONE 1A' |
|||
PAI+::2' |
|||
FCA+14' |
|||
FII+BF+DE23300308800099990031:CRG TC5 001 BENE NAME FIIBF \ |
|||
000001::EUR+AACSDE33:25:5:::+DE' |
|||
NAD+BE+++BENE NAME NADBE T1 001:CRG TC5 001T1 NADBE ADD LINE 1 0001:CRG TC5 \ |
|||
001T1 NADBE ADD LINE 2 0001' |
|||
CNT+39:1' |
|||
UNT+19+1' |
|||
UNZ+1+EZONE'""" |
|||
|
|||
src_account = paymul.UKAccount( |
|||
number=12345678, |
|||
holder='ACCOUNT HOLDER NAME', |
|||
currency='EUR', |
|||
sortcode=403124 |
|||
) |
|||
|
|||
dest_account = paymul.IBANAccount( |
|||
iban="DE23300308800099990031", |
|||
holder="CRG TC5 001 BENE NAME FIIBF 000001", |
|||
currency='EUR', |
|||
bic="AACSDE33" |
|||
) |
|||
|
|||
party_name = ("BENE NAME NADBE T1 001\n" |
|||
"CRG TC5 001T1 NADBE ADD LINE 1 0001\n" |
|||
"CRG TC5 001T1 NADBE ADD LINE 2 0001") |
|||
transaction = paymul.Transaction(amount=Decimal('1.00'), |
|||
currency='EUR', |
|||
account=dest_account, |
|||
party_name=party_name, |
|||
charges=paymul.CHARGES_EACH_OWN, |
|||
means=paymul.MEANS_EZONE, |
|||
customer_reference='EZONE 1A', |
|||
payment_reference='EZONE 1A') |
|||
|
|||
name_address = ("ORD PARTY NAME NADOY 01\n" |
|||
"CRG TC5 001 NADOY ADDRESS LINE 0001\n" |
|||
"CRG TC5 001 NADOY ADDRESS LINE 0002") |
|||
batch = paymul.Batch(exec_date=datetime.date(2008, 1, 14), |
|||
reference='EZONE', |
|||
debit_account=src_account, |
|||
name_address=name_address) |
|||
batch.transactions.append(transaction) |
|||
|
|||
message = paymul.Message(reference='EZONE', |
|||
dt=datetime.datetime(2008, 1, 10)) |
|||
message.batches.append(batch) |
|||
|
|||
interchange = paymul.Interchange( |
|||
client_id='ABC12016001', |
|||
reference='EZONE', |
|||
create_dt=datetime.datetime(2008, 1, 10, 8, 56), |
|||
message=message |
|||
) |
|||
|
|||
self.assertMultiLineEqual(expected, str(interchange)) |
|||
|
|||
def test_uk_low_value_ach_instruction_level(self): |
|||
dest_account1 = paymul.UKAccount( |
|||
number=87654321, |
|||
holder="HSBC NET RPS TEST\nHSBC BANK", |
|||
currency='GBP', |
|||
sortcode=403124 |
|||
) |
|||
name_address = ("HSBC BANK PLC\n" |
|||
"PCM\n" |
|||
"8CS37\n" |
|||
"E14 5HQ\n" |
|||
"UNITED KINGDOM") |
|||
transaction1 = paymul.Transaction(amount=Decimal('1.00'), |
|||
currency='GBP', |
|||
account=dest_account1, |
|||
name_address=name_address, |
|||
charges=paymul.CHARGES_PAYEE, |
|||
means=paymul.MEANS_ACH, |
|||
customer_reference='CREDIT', |
|||
payment_reference='CREDIT') |
|||
|
|||
dest_account2 = paymul.UKAccount( |
|||
number=12341234, |
|||
holder="HSBC NET RPS TEST\nHSBC BANK", |
|||
currency='GBP', |
|||
sortcode=403124 |
|||
) |
|||
name_address = ("HSBC BANK PLC\n" |
|||
"PCM\n" |
|||
"8CS37\n" |
|||
"E14 5HQ\n" |
|||
"UNITED KINGDOM") |
|||
transaction2 = paymul.Transaction(amount=Decimal('1.00'), |
|||
currency='GBP', |
|||
account=dest_account2, |
|||
name_address=name_address, |
|||
charges=paymul.CHARGES_PAYEE, |
|||
means=paymul.MEANS_ACH, |
|||
customer_reference='CREDIT1', |
|||
payment_reference='CREDIT1') |
|||
|
|||
name_address = ("HSBC BANK PLC\n" |
|||
"PCM\n" |
|||
"8CS37\n" |
|||
"E14 5HQ\n" |
|||
"UNITED KINGDOM") |
|||
|
|||
src_account = paymul.UKAccount(number=12345678, |
|||
holder='BHEX RPS TEST', |
|||
currency='GBP', |
|||
sortcode=401234) |
|||
batch = paymul.Batch(exec_date=datetime.date(2004, 11, 15), |
|||
reference='UKLVPLIL', |
|||
debit_account=src_account, |
|||
name_address=name_address) |
|||
batch.transactions = [transaction1, transaction2] |
|||
|
|||
message = paymul.Message( |
|||
reference='UKLVPLIL', |
|||
dt=datetime.datetime(2004, 11, 11) |
|||
) |
|||
message.batches.append(batch) |
|||
|
|||
interchange = paymul.Interchange( |
|||
client_id='ABC00000001', |
|||
reference='UKLVPLIL', |
|||
create_dt=datetime.datetime(2004, 11, 11, 15, 0), |
|||
message=message |
|||
) |
|||
|
|||
# Changes from example: |
|||
# * Change second transaction from EUR to GBP, because we don't support |
|||
# multi-currency batches |
|||
# * Removed DTM for transaction, HSBC ignores it (section 2.8.3) |
|||
expected = """\ |
|||
UNB+UNOA:3+::ABC00000001+::HEXAGON ABC+041111:1500+UKLVPLIL' |
|||
UNH+1+PAYMUL:D:96A:UN:FUN01G' |
|||
BGM+452+UKLVPLIL+9' |
|||
DTM+137:20041111:102' |
|||
LIN+1' |
|||
DTM+203:20041115:102' |
|||
RFF+AEK:UKLVPLIL' |
|||
MOA+9:2.00:GBP' |
|||
FII+OR+12345678:BHEX RPS TEST::GBP+:::401234:154:133+GB' |
|||
NAD+OY++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM' |
|||
SEQ++1' |
|||
MOA+9:1.00:GBP' |
|||
RFF+CR:CREDIT' |
|||
RFF+PQ:CREDIT' |
|||
PAI+::2' |
|||
FCA+13' |
|||
FII+BF+87654321:HSBC NET RPS TEST:HSBC BANK:GBP+:::403124:154:133+GB' |
|||
NAD+BE++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM' |
|||
SEQ++2' |
|||
MOA+9:1.00:GBP' |
|||
RFF+CR:CREDIT1' |
|||
RFF+PQ:CREDIT1' |
|||
PAI+::2' |
|||
FCA+13' |
|||
FII+BF+12341234:HSBC NET RPS TEST:HSBC BANK:GBP+:::403124:154:133+GB' |
|||
NAD+BE++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM' |
|||
CNT+39:2' |
|||
UNT+27+1' |
|||
UNZ+1+UKLVPLIL'""" |
|||
|
|||
self.assertMultiLineEqual(expected, str(interchange)) |
|||
|
|||
if __name__ == "__main__": |
|||
unittest.main() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue