mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
PEP8 on account_banking
This commit is contained in:
@@ -11,8 +11,8 @@
|
|||||||
# garantees and support are strongly adviced to contract EduSense BV
|
# garantees and support are strongly adviced to contract EduSense BV
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
import sepa
|
import sepa
|
||||||
import record
|
import record
|
||||||
import banking_import_transaction
|
import banking_import_transaction
|
||||||
@@ -33,5 +34,3 @@ import wizard
|
|||||||
import res_partner
|
import res_partner
|
||||||
import res_bank
|
import res_bank
|
||||||
import res_partner_bank
|
import res_partner_bank
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|||||||
@@ -70,7 +70,8 @@
|
|||||||
+ IBAN accounts are the standard in the SEPA countries
|
+ IBAN accounts are the standard in the SEPA countries
|
||||||
+ local accounts are derived from SEPA (excluding Turkey) but are
|
+ local accounts are derived from SEPA (excluding Turkey) but are
|
||||||
considered to be identical to the corresponding SEPA account.
|
considered to be identical to the corresponding SEPA account.
|
||||||
+ Banks are identified with either Country + Bank code + Branch code or BIC
|
+ Banks are identified with either Country + Bank code + Branch code or
|
||||||
|
BIC
|
||||||
+ Each bank can have its own pace in introducing SEPA into their
|
+ Each bank can have its own pace in introducing SEPA into their
|
||||||
communication with their customers.
|
communication with their customers.
|
||||||
+ National online databases can be used to convert BBAN's to IBAN's.
|
+ National online databases can be used to convert BBAN's to IBAN's.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||||
#
|
#
|
||||||
# All other contributions are (C) by their respective contributors
|
# All other contributions are (C) by their respective contributors
|
||||||
#
|
#
|
||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
@@ -86,11 +86,11 @@ class account_banking_account_settings(orm.Model):
|
|||||||
string='Partner'),
|
string='Partner'),
|
||||||
'default_credit_account_id': fields.many2one(
|
'default_credit_account_id': fields.many2one(
|
||||||
'account.account', 'Default credit account', select=True,
|
'account.account', 'Default credit account', select=True,
|
||||||
help=('The account to use when an unexpected payment was signaled. '
|
help=('The account to use when an unexpected payment was signaled.'
|
||||||
'This can happen when a direct debit payment is cancelled '
|
' This can happen when a direct debit payment is cancelled '
|
||||||
'by a customer, or when no matching payment can be found. '
|
'by a customer, or when no matching payment can be found. '
|
||||||
' Mind that you can correct movements before confirming them.'
|
'Mind that you can correct movements before confirming them.'
|
||||||
),
|
),
|
||||||
required=True
|
required=True
|
||||||
),
|
),
|
||||||
'default_debit_account_id': fields.many2one(
|
'default_debit_account_id': fields.many2one(
|
||||||
@@ -98,27 +98,27 @@ class account_banking_account_settings(orm.Model):
|
|||||||
select=True, required=True,
|
select=True, required=True,
|
||||||
help=('The account to use when an unexpected payment is received. '
|
help=('The account to use when an unexpected payment is received. '
|
||||||
'This can be needed when a customer pays in advance or when '
|
'This can be needed when a customer pays in advance or when '
|
||||||
'no matching invoice can be found. Mind that you can correct '
|
'no matching invoice can be found. Mind that you can '
|
||||||
'movements before confirming them.'
|
'correct movements before confirming them.'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'costs_account_id': fields.many2one(
|
'costs_account_id': fields.many2one(
|
||||||
'account.account', 'Bank Costs Account', select=True,
|
'account.account', 'Bank Costs Account', select=True,
|
||||||
help=('The account to use when the bank invoices its own costs. '
|
help=('The account to use when the bank invoices its own costs. '
|
||||||
'Leave it blank to disable automatic invoice generation '
|
'Leave it blank to disable automatic invoice generation '
|
||||||
'on bank costs.'
|
'on bank costs.'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'invoice_journal_id': fields.many2one(
|
'invoice_journal_id': fields.many2one(
|
||||||
'account.journal', 'Costs Journal',
|
'account.journal', 'Costs Journal',
|
||||||
help=('This is the journal used to create invoices for bank costs.'
|
help=('This is the journal used to create invoices for bank costs.'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'bank_partner_id': fields.many2one(
|
'bank_partner_id': fields.many2one(
|
||||||
'res.partner', 'Bank Partner',
|
'res.partner', 'Bank Partner',
|
||||||
help=('The partner to use for bank costs. Banks are not partners '
|
help=('The partner to use for bank costs. Banks are not partners '
|
||||||
'by default. You will most likely have to create one.'
|
'by default. You will most likely have to create one.'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ class account_banking_account_settings(orm.Model):
|
|||||||
return user['company_id'][0]
|
return user['company_id'][0]
|
||||||
return self.pool.get('res.company').search(
|
return self.pool.get('res.company').search(
|
||||||
cr, uid, [('parent_id', '=', False)])[0]
|
cr, uid, [('parent_id', '=', False)])[0]
|
||||||
|
|
||||||
def _default_partner_id(self, cr, uid, context=None, company_id=False):
|
def _default_partner_id(self, cr, uid, context=None, company_id=False):
|
||||||
if not company_id:
|
if not company_id:
|
||||||
company_id = self._default_company(cr, uid, context=context)
|
company_id = self._default_company(cr, uid, context=context)
|
||||||
@@ -196,7 +196,7 @@ class account_banking_account_settings(orm.Model):
|
|||||||
values['journal_id'] = bank['journal_id'][0]
|
values['journal_id'] = bank['journal_id'][0]
|
||||||
return {'value': values}
|
return {'value': values}
|
||||||
|
|
||||||
def onchange_company_id (
|
def onchange_company_id(
|
||||||
self, cr, uid, ids, company_id=False, context=None):
|
self, cr, uid, ids, company_id=False, context=None):
|
||||||
if not company_id:
|
if not company_id:
|
||||||
return {}
|
return {}
|
||||||
@@ -229,37 +229,59 @@ class account_banking_imported_file(orm.Model):
|
|||||||
_description = __doc__
|
_description = __doc__
|
||||||
_rec_name = 'date'
|
_rec_name = 'date'
|
||||||
_columns = {
|
_columns = {
|
||||||
'company_id': fields.many2one('res.company', 'Company',
|
'company_id': fields.many2one(
|
||||||
select=True, readonly=True
|
'res.company',
|
||||||
),
|
'Company',
|
||||||
'date': fields.datetime('Import Date', readonly=True, select=True,
|
select=True,
|
||||||
states={'draft': [('readonly', False)]}
|
readonly=True,
|
||||||
),
|
),
|
||||||
'format': fields.char('File Format', size=20, readonly=True,
|
'date': fields.datetime(
|
||||||
states={'draft': [('readonly', False)]}
|
'Import Date',
|
||||||
),
|
readonly=True,
|
||||||
'file': fields.binary('Raw Data', readonly=True,
|
select=True,
|
||||||
states={'draft': [('readonly', False)]}
|
states={'draft': [('readonly', False)]},
|
||||||
),
|
),
|
||||||
'file_name': fields.char('File name', size=256),
|
'format': fields.char(
|
||||||
'log': fields.text('Import Log', readonly=True,
|
'File Format',
|
||||||
states={'draft': [('readonly', False)]}
|
size=20,
|
||||||
),
|
readonly=True,
|
||||||
'user_id': fields.many2one('res.users', 'Responsible User',
|
states={'draft': [('readonly', False)]},
|
||||||
readonly=True, select=True,
|
),
|
||||||
states={'draft': [('readonly', False)]}
|
'file': fields.binary(
|
||||||
),
|
'Raw Data',
|
||||||
'state': fields.selection(
|
readonly=True,
|
||||||
[('unfinished', 'Unfinished'),
|
states={'draft': [('readonly', False)]},
|
||||||
('error', 'Error'),
|
),
|
||||||
('review', 'Review'),
|
'file_name': fields.char('File name', size=256),
|
||||||
('ready', 'Finished'),
|
'log': fields.text(
|
||||||
], 'State', select=True, readonly=True
|
'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,
|
||||||
),
|
),
|
||||||
'statement_ids': fields.one2many('account.bank.statement',
|
|
||||||
'banking_id', 'Statements',
|
|
||||||
readonly=False,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'date': fields.date.context_today,
|
'date': fields.date.context_today,
|
||||||
@@ -284,11 +306,17 @@ class account_bank_statement(orm.Model):
|
|||||||
_inherit = 'account.bank.statement'
|
_inherit = 'account.bank.statement'
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'period_id': fields.many2one('account.period', 'Period',
|
'period_id': fields.many2one(
|
||||||
required=False, readonly=True),
|
'account.period',
|
||||||
'banking_id': fields.many2one('account.banking.imported.file',
|
'Period',
|
||||||
'Imported File', readonly=True,
|
required=False,
|
||||||
),
|
readonly=True,
|
||||||
|
),
|
||||||
|
'banking_id': fields.many2one(
|
||||||
|
'account.banking.imported.file',
|
||||||
|
'Imported File',
|
||||||
|
readonly=True,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
@@ -316,12 +344,12 @@ class account_bank_statement(orm.Model):
|
|||||||
statement.write({'period_id': line.period_id.id})
|
statement.write({'period_id': line.period_id.id})
|
||||||
statement.refresh()
|
statement.refresh()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Redefine the constraint, or it still refer to the original method
|
# Redefine the constraint, or it still refer to the original method
|
||||||
_constraints = [
|
_constraints = [
|
||||||
(_check_company_id,
|
(_check_company_id,
|
||||||
'The journal and period chosen have to belong to the same company.',
|
'The journal and period chosen have to belong to the same company.',
|
||||||
['journal_id','period_id']),
|
['journal_id', 'period_id']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_period(self, cr, uid, date=False, context=None):
|
def _get_period(self, cr, uid, date=False, context=None):
|
||||||
@@ -410,7 +438,7 @@ class account_bank_statement(orm.Model):
|
|||||||
account_move_obj.post(
|
account_move_obj.post(
|
||||||
cr, uid, [st_line.voucher_id.move_id.id], context={})
|
cr, uid, [st_line.voucher_id.move_id.id], context={})
|
||||||
else:
|
else:
|
||||||
# Write stored reconcile_id and pay invoices through workflow
|
# Write stored reconcile_id and pay invoices through workflow
|
||||||
if st_line.reconcile_id:
|
if st_line.reconcile_id:
|
||||||
move_ids = [move.id for move in st_line.move_ids]
|
move_ids = [move.id for move in st_line.move_ids]
|
||||||
torec = account_move_line_obj.search(
|
torec = account_move_line_obj.search(
|
||||||
@@ -419,11 +447,12 @@ class account_bank_statement(orm.Model):
|
|||||||
('account_id', '=', st_line.account_id.id)],
|
('account_id', '=', st_line.account_id.id)],
|
||||||
context=context)
|
context=context)
|
||||||
account_move_line_obj.write(cr, uid, torec, {
|
account_move_line_obj.write(cr, uid, torec, {
|
||||||
(st_line.reconcile_id.line_partial_ids and
|
(st_line.reconcile_id.line_partial_ids
|
||||||
'reconcile_partial_id' or 'reconcile_id'):
|
and 'reconcile_partial_id'
|
||||||
st_line.reconcile_id.id }, context=context)
|
or 'reconcile_id'): st_line.reconcile_id.id
|
||||||
|
}, context=context)
|
||||||
for move_line in (st_line.reconcile_id.line_id or []) + (
|
for move_line in (st_line.reconcile_id.line_id or []) + (
|
||||||
st_line.reconcile_id.line_partial_ids or []):
|
st_line.reconcile_id.line_partial_ids or []):
|
||||||
netsvc.LocalService("workflow").trg_trigger(
|
netsvc.LocalService("workflow").trg_trigger(
|
||||||
uid, 'account.move.line', move_line.id, cr)
|
uid, 'account.move.line', move_line.id, cr)
|
||||||
return res
|
return res
|
||||||
@@ -438,7 +467,7 @@ class account_bank_statement(orm.Model):
|
|||||||
if ids and isinstance(ids, (int, long)):
|
if ids and isinstance(ids, (int, long)):
|
||||||
ids = [ids]
|
ids = [ids]
|
||||||
noname_ids = self.search(
|
noname_ids = self.search(
|
||||||
cr, uid, [('id', 'in', ids),('name', '=', '/')],
|
cr, uid, [('id', 'in', ids), ('name', '=', '/')],
|
||||||
context=context)
|
context=context)
|
||||||
for st in self.browse(cr, uid, noname_ids, context=context):
|
for st in self.browse(cr, uid, noname_ids, context=context):
|
||||||
if st.journal_id.sequence_id:
|
if st.journal_id.sequence_id:
|
||||||
@@ -451,7 +480,7 @@ class account_bank_statement(orm.Model):
|
|||||||
cr, uid, st.journal_id.sequence_id.id, context=c)
|
cr, uid, st.journal_id.sequence_id.id, context=c)
|
||||||
self.write(
|
self.write(
|
||||||
cr, uid, ids, {'name': st_number}, context=context)
|
cr, uid, ids, {'name': st_number}, context=context)
|
||||||
|
|
||||||
return super(account_bank_statement, self).button_confirm_bank(
|
return super(account_bank_statement, self).button_confirm_bank(
|
||||||
cr, uid, ids, context)
|
cr, uid, ids, context)
|
||||||
|
|
||||||
@@ -464,7 +493,7 @@ class account_voucher(orm.Model):
|
|||||||
context = {}
|
context = {}
|
||||||
if not context.get('period_id') and context.get('move_line_ids'):
|
if not context.get('period_id') and context.get('move_line_ids'):
|
||||||
move_line = self.pool.get('account.move.line').browse(
|
move_line = self.pool.get('account.move.line').browse(
|
||||||
cr, uid , context.get('move_line_ids')[0], context=context)
|
cr, uid, context.get('move_line_ids')[0], context=context)
|
||||||
return move_line.period_id.id
|
return move_line.period_id.id
|
||||||
return super(account_voucher, self)._get_period(cr, uid, context)
|
return super(account_voucher, self)._get_period(cr, uid, context)
|
||||||
|
|
||||||
@@ -498,8 +527,8 @@ class account_bank_statement_line(orm.Model):
|
|||||||
which is inaccessible from within this method.
|
which is inaccessible from within this method.
|
||||||
'''
|
'''
|
||||||
res_users_obj = self.pool.get('res.users')
|
res_users_obj = self.pool.get('res.users')
|
||||||
return res_users_obj.browse(cr, uid, uid,
|
return res_users_obj.browse(
|
||||||
context=context).company_id.currency_id.id
|
cr, uid, uid, context=context).company_id.currency_id.id
|
||||||
|
|
||||||
def _get_invoice_id(self, cr, uid, ids, name, args, context=None):
|
def _get_invoice_id(self, cr, uid, ids, name, args, context=None):
|
||||||
res = {}
|
res = {}
|
||||||
@@ -509,7 +538,7 @@ class account_bank_statement_line(orm.Model):
|
|||||||
st_line.reconcile_id and
|
st_line.reconcile_id and
|
||||||
(st_line.reconcile_id.line_id or
|
(st_line.reconcile_id.line_id or
|
||||||
st_line.reconcile_id.line_partial_ids) or
|
st_line.reconcile_id.line_partial_ids) or
|
||||||
st_line.import_transaction_id and
|
st_line.import_transaction_id and
|
||||||
st_line.import_transaction_id.move_line_id and
|
st_line.import_transaction_id.move_line_id and
|
||||||
[st_line.import_transaction_id.move_line_id] or []):
|
[st_line.import_transaction_id.move_line_id] or []):
|
||||||
if move_line.invoice:
|
if move_line.invoice:
|
||||||
@@ -519,36 +548,70 @@ class account_bank_statement_line(orm.Model):
|
|||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
# Redefines. Todo: refactor away to view attrs
|
# Redefines. Todo: refactor away to view attrs
|
||||||
'amount': fields.float('Amount', readonly=True,
|
'amount': fields.float(
|
||||||
digits_compute=dp.get_precision('Account'),
|
'Amount',
|
||||||
states={'draft': [('readonly', False)]}),
|
readonly=True,
|
||||||
'ref': fields.char('Ref.', size=32, readonly=True,
|
digits_compute=dp.get_precision('Account'),
|
||||||
states={'draft': [('readonly', False)]}),
|
states={'draft': [('readonly', False)]},
|
||||||
'name': fields.char('Name', size=64, required=False, readonly=True,
|
),
|
||||||
states={'draft': [('readonly', False)]}),
|
'ref': fields.char(
|
||||||
'date': fields.date('Date', required=True, readonly=True,
|
'Ref.',
|
||||||
states={'draft': [('readonly', False)]}),
|
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
|
# New columns
|
||||||
'trans': fields.char('Bank Transaction ID', size=15, required=False,
|
'trans': fields.char(
|
||||||
readonly=True,
|
'Bank Transaction ID',
|
||||||
states={'draft':[('readonly', False)]},
|
size=15,
|
||||||
),
|
required=False,
|
||||||
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account',
|
readonly=True,
|
||||||
required=False, readonly=True,
|
states={'draft': [('readonly', False)]},
|
||||||
states={'draft':[('readonly', False)]},
|
),
|
||||||
),
|
'partner_bank_id': fields.many2one(
|
||||||
'period_id': fields.many2one('account.period', 'Period', required=True,
|
'res.partner.bank',
|
||||||
states={'confirmed': [('readonly', True)]}),
|
'Bank Account',
|
||||||
'currency': fields.many2one('res.currency', 'Currency', required=True,
|
required=False,
|
||||||
states={'confirmed': [('readonly', True)]}),
|
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(
|
'reconcile_id': fields.many2one(
|
||||||
'account.move.reconcile', 'Reconciliation', readonly=True
|
'account.move.reconcile',
|
||||||
),
|
'Reconciliation',
|
||||||
|
readonly=True,
|
||||||
|
),
|
||||||
'invoice_id': fields.function(
|
'invoice_id': fields.function(
|
||||||
_get_invoice_id, method=True, string='Linked Invoice',
|
_get_invoice_id,
|
||||||
type='many2one', relation='account.invoice'
|
method=True,
|
||||||
),
|
string='Linked Invoice',
|
||||||
|
type='many2one',
|
||||||
|
relation='account.invoice',
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
@@ -577,9 +640,9 @@ class invoice(orm.Model):
|
|||||||
_inherit = 'account.invoice'
|
_inherit = 'account.invoice'
|
||||||
|
|
||||||
def test_undo_paid(self, cr, uid, ids, context=None):
|
def test_undo_paid(self, cr, uid, ids, context=None):
|
||||||
"""
|
"""
|
||||||
Called from the workflow. Used to unset paid state on
|
Called from the workflow. Used to unset paid state on
|
||||||
invoices that were paid with bank transfers which are being cancelled
|
invoices that were paid with bank transfers which are being cancelled
|
||||||
"""
|
"""
|
||||||
for invoice in self.read(cr, uid, ids, ['reconciled'], context):
|
for invoice in self.read(cr, uid, ids, ['reconciled'], context):
|
||||||
if invoice['reconciled']:
|
if invoice['reconciled']:
|
||||||
@@ -592,22 +655,20 @@ class invoice(orm.Model):
|
|||||||
'''
|
'''
|
||||||
return [('none', _('Free Reference')),
|
return [('none', _('Free Reference')),
|
||||||
('structured', _('Structured Reference')),
|
('structured', _('Structured Reference')),
|
||||||
]
|
]
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'reference_type': fields.selection(_get_reference_type,
|
'reference_type': fields.selection(_get_reference_type,
|
||||||
'Reference Type', required=True
|
'Reference Type', required=True
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice()
|
|
||||||
|
|
||||||
|
|
||||||
class account_move_line(orm.Model):
|
class account_move_line(orm.Model):
|
||||||
_inherit = "account.move.line"
|
_inherit = "account.move.line"
|
||||||
|
|
||||||
def get_balance(self, cr, uid, ids, context=None):
|
def get_balance(self, cr, uid, ids, context=None):
|
||||||
"""
|
"""
|
||||||
Return the balance of any set of move lines.
|
Return the balance of any set of move lines.
|
||||||
|
|
||||||
Not to be confused with the 'balance' field on this model, which
|
Not to be confused with the 'balance' field on this model, which
|
||||||
@@ -617,9 +678,6 @@ class account_move_line(orm.Model):
|
|||||||
if not ids:
|
if not ids:
|
||||||
return total
|
return total
|
||||||
for line in self.read(
|
for line in self.read(
|
||||||
cr, uid, ids, ['debit', 'credit'], context=context):
|
cr, uid, ids, ['debit', 'credit'], context=context):
|
||||||
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
|
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
|
||||||
return total
|
return total
|
||||||
account_move_line()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@
|
|||||||
# Copyright (C) 2011 Therp BV (<http://therp.nl>)
|
# Copyright (C) 2011 Therp BV (<http://therp.nl>)
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -24,10 +24,11 @@ __name__ = ("account.bank.statement.line:: set new field 'state' to "
|
|||||||
"confirmed for all statement lines belonging to confirmed "
|
"confirmed for all statement lines belonging to confirmed "
|
||||||
"statements")
|
"statements")
|
||||||
|
|
||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
cr.execute ("UPDATE account_bank_statement_line as sl "
|
cr.execute("UPDATE account_bank_statement_line as sl "
|
||||||
" SET state = 'confirmed'"
|
" SET state = 'confirmed'"
|
||||||
" FROM account_bank_statement as s "
|
" FROM account_bank_statement as s "
|
||||||
" WHERE sl.statement_id = s.id "
|
" WHERE sl.statement_id = s.id "
|
||||||
" AND s.state = 'confirm' "
|
" AND s.state = 'confirm' "
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
if not version:
|
if not version:
|
||||||
return
|
return
|
||||||
@@ -26,7 +27,7 @@ def migrate(cr, version):
|
|||||||
# workflow state moved to another, new module
|
# workflow state moved to another, new module
|
||||||
cr.execute(
|
cr.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE ir_model_data
|
UPDATE ir_model_data
|
||||||
SET module = 'account_banking_payment'
|
SET module = 'account_banking_payment'
|
||||||
WHERE name = 'trans_done_sent'
|
WHERE name = 'trans_done_sent'
|
||||||
AND module = 'account_direct_debit'
|
AND module = 'account_direct_debit'
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
if not version:
|
if not version:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
def table_exists(cr, table):
|
def table_exists(cr, table):
|
||||||
""" Check whether a certain table or view exists """
|
""" Check whether a certain table or view exists """
|
||||||
cr.execute(
|
cr.execute(
|
||||||
@@ -26,6 +27,7 @@ def table_exists(cr, table):
|
|||||||
(table,))
|
(table,))
|
||||||
return cr.fetchone()[0] == 1
|
return cr.fetchone()[0] == 1
|
||||||
|
|
||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
"""
|
"""
|
||||||
Migration script for semantic changes in account_banking_payment_export.
|
Migration script for semantic changes in account_banking_payment_export.
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -19,6 +19,6 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
import models
|
from . import models
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -29,14 +29,17 @@ try:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
from mx import DateTime as datetime
|
from mx import DateTime as datetime
|
||||||
|
|
||||||
|
|
||||||
def str2date(datestr, format='%d/%m/%y'):
|
def str2date(datestr, format='%d/%m/%y'):
|
||||||
'''Convert a string to a datatime object'''
|
'''Convert a string to a datatime object'''
|
||||||
return datetime.strptime(datestr, format)
|
return datetime.strptime(datestr, format)
|
||||||
|
|
||||||
|
|
||||||
def date2str(date, format='%Y-%m-%d'):
|
def date2str(date, format='%Y-%m-%d'):
|
||||||
'''Convert a datetime object to a string'''
|
'''Convert a datetime object to a string'''
|
||||||
return date.strftime(format)
|
return date.strftime(format)
|
||||||
|
|
||||||
|
|
||||||
def date2date(datestr, fromfmt='%d/%m/%y', tofmt='%Y-%m-%d'):
|
def date2date(datestr, fromfmt='%d/%m/%y', tofmt='%Y-%m-%d'):
|
||||||
'''
|
'''
|
||||||
Convert a date in a string to another string, in a different
|
Convert a date in a string to another string, in a different
|
||||||
@@ -44,7 +47,9 @@ def date2date(datestr, fromfmt='%d/%m/%y', tofmt='%Y-%m-%d'):
|
|||||||
'''
|
'''
|
||||||
return date2str(str2date(datestr, fromfmt), tofmt)
|
return date2str(str2date(datestr, fromfmt), tofmt)
|
||||||
|
|
||||||
_SWIFT = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/-?:().,'+ "
|
_SWIFT = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
"/-?:().,'+ ")
|
||||||
|
|
||||||
|
|
||||||
def to_swift(astr, schemes=['utf-8', 'latin-1', 'ascii']):
|
def to_swift(astr, schemes=['utf-8', 'latin-1', 'ascii']):
|
||||||
'''
|
'''
|
||||||
@@ -62,7 +67,7 @@ def to_swift(astr, schemes=['utf-8', 'latin-1', 'ascii']):
|
|||||||
|
|
||||||
s = [x in _SWIFT and x or ' '
|
s = [x in _SWIFT and x or ' '
|
||||||
for x in unicodedata.normalize('NFKD', astr).encode('ascii', 'ignore')
|
for x in unicodedata.normalize('NFKD', astr).encode('ascii', 'ignore')
|
||||||
]
|
]
|
||||||
return ''.join(s)
|
return ''.join(s)
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
import re
|
import re
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
|
|
||||||
|
|
||||||
class mem_bank_statement(object):
|
class mem_bank_statement(object):
|
||||||
'''
|
'''
|
||||||
A mem_bank_statement is a real life projection of a bank statement paper
|
A mem_bank_statement is a real life projection of a bank statement paper
|
||||||
@@ -34,9 +35,15 @@ class mem_bank_statement(object):
|
|||||||
'''
|
'''
|
||||||
# Lock attributes to enable parsers to trigger non-conformity faults
|
# Lock attributes to enable parsers to trigger non-conformity faults
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
'start_balance','end_balance', 'date', 'local_account',
|
'start_balance',
|
||||||
'local_currency', 'id', 'transactions'
|
'end_balance',
|
||||||
|
'date',
|
||||||
|
'local_account',
|
||||||
|
'local_currency',
|
||||||
|
'id',
|
||||||
|
'transactions'
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(mem_bank_statement, self).__init__(*args, **kwargs)
|
super(mem_bank_statement, self).__init__(*args, **kwargs)
|
||||||
self.id = ''
|
self.id = ''
|
||||||
@@ -59,6 +66,7 @@ class mem_bank_statement(object):
|
|||||||
check += float(transaction.transferred_amount)
|
check += float(transaction.transferred_amount)
|
||||||
return abs(check - float(self.end_balance)) < 0.0001
|
return abs(check - float(self.end_balance)) < 0.0001
|
||||||
|
|
||||||
|
|
||||||
class mem_bank_transaction(object):
|
class mem_bank_transaction(object):
|
||||||
'''
|
'''
|
||||||
A mem_bank_transaction is a real life copy of a bank transfer. Mapping to
|
A mem_bank_transaction is a real life copy of a bank transfer. Mapping to
|
||||||
@@ -102,7 +110,7 @@ class mem_bank_transaction(object):
|
|||||||
# remote_currency
|
# remote_currency
|
||||||
|
|
||||||
'transferred_amount',
|
'transferred_amount',
|
||||||
# The actual amount transferred -
|
# The actual amount transferred -
|
||||||
# negative means sent, positive means received
|
# negative means sent, positive means received
|
||||||
# Most banks use the local_currency to express this amount, but there
|
# Most banks use the local_currency to express this amount, but there
|
||||||
# may be exceptions I'm unaware of.
|
# may be exceptions I'm unaware of.
|
||||||
@@ -126,7 +134,8 @@ class mem_bank_transaction(object):
|
|||||||
# The other parties postal code belonging to the address
|
# The other parties postal code belonging to the address
|
||||||
|
|
||||||
'remote_owner_country_code',
|
'remote_owner_country_code',
|
||||||
# The other parties two letter ISO country code belonging to the previous
|
# The other parties two letter ISO country code belonging to the
|
||||||
|
# previous
|
||||||
|
|
||||||
'remote_owner_custno',
|
'remote_owner_custno',
|
||||||
# The other parties customer number
|
# The other parties customer number
|
||||||
@@ -175,7 +184,7 @@ class mem_bank_transaction(object):
|
|||||||
# An error message for interaction with the user
|
# An error message for interaction with the user
|
||||||
# Only used when mem_transaction.valid returns False.
|
# Only used when mem_transaction.valid returns False.
|
||||||
'error_message',
|
'error_message',
|
||||||
|
|
||||||
# Storno attribute. When True, make the cancelled debit eligible for
|
# Storno attribute. When True, make the cancelled debit eligible for
|
||||||
# a next direct debit run
|
# a next direct debit run
|
||||||
'storno_retry',
|
'storno_retry',
|
||||||
@@ -213,7 +222,7 @@ class mem_bank_transaction(object):
|
|||||||
# Will be selected for matching.
|
# Will be selected for matching.
|
||||||
# STORNO A failed or reversed attempt at direct debit.
|
# STORNO A failed or reversed attempt at direct debit.
|
||||||
# Either due to an action on the payer's side
|
# Either due to an action on the payer's side
|
||||||
# or a failure observed by the bank (lack of
|
# or a failure observed by the bank (lack of
|
||||||
# credit for instance)
|
# credit for instance)
|
||||||
#
|
#
|
||||||
# Perhaps more will follow.
|
# Perhaps more will follow.
|
||||||
@@ -230,7 +239,7 @@ class mem_bank_transaction(object):
|
|||||||
DIRECT_DEBIT = 'DD'
|
DIRECT_DEBIT = 'DD'
|
||||||
ORDER = 'DO'
|
ORDER = 'DO'
|
||||||
PAYMENT_BATCH = 'PB'
|
PAYMENT_BATCH = 'PB'
|
||||||
PAYMENT_TERMINAL = 'PT'
|
PAYMENT_TERMINAL = 'PT'
|
||||||
PERIODIC_ORDER = 'PO'
|
PERIODIC_ORDER = 'PO'
|
||||||
STORNO = 'ST'
|
STORNO = 'ST'
|
||||||
|
|
||||||
@@ -270,7 +279,7 @@ class mem_bank_transaction(object):
|
|||||||
if value in self.types:
|
if value in self.types:
|
||||||
self.transfer_type = value
|
self.transfer_type = value
|
||||||
else:
|
else:
|
||||||
raise ValueError, _('Invalid value for transfer_type')
|
raise ValueError(_('Invalid value for transfer_type'))
|
||||||
|
|
||||||
type = property(_get_type, _set_type)
|
type = property(_get_type, _set_type)
|
||||||
|
|
||||||
@@ -282,6 +291,7 @@ class mem_bank_transaction(object):
|
|||||||
return (self.execution_date and self.remote_account
|
return (self.execution_date and self.remote_account
|
||||||
and self.transferred_amount and True) or False
|
and self.transferred_amount and True) or False
|
||||||
|
|
||||||
|
|
||||||
class parser_type(type):
|
class parser_type(type):
|
||||||
'''
|
'''
|
||||||
Meta annex factory class for house keeping and collecting parsers.
|
Meta annex factory class for house keeping and collecting parsers.
|
||||||
@@ -314,11 +324,13 @@ class parser_type(type):
|
|||||||
keys.sort()
|
keys.sort()
|
||||||
return [(parsers[x].code, parsers[x].name) for x in keys]
|
return [(parsers[x].code, parsers[x].name) for x in keys]
|
||||||
|
|
||||||
|
|
||||||
def create_parser(code):
|
def create_parser(code):
|
||||||
if code in parser_type.parser_by_code:
|
if code in parser_type.parser_by_code:
|
||||||
return parser_type.parser_by_code[code]()
|
return parser_type.parser_by_code[code]()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class parser(object):
|
class parser(object):
|
||||||
'''
|
'''
|
||||||
A parser delivers the interface for any parser object. Inherit from
|
A parser delivers the interface for any parser object. Inherit from
|
||||||
@@ -384,7 +396,7 @@ class parser(object):
|
|||||||
def parse(self, cr, data):
|
def parse(self, cr, data):
|
||||||
'''
|
'''
|
||||||
Parse data.
|
Parse data.
|
||||||
|
|
||||||
data is a raw in memory file object. You have to split it in
|
data is a raw in memory file object. You have to split it in
|
||||||
whatever chunks you see fit for parsing. It should return a list
|
whatever chunks you see fit for parsing. It should return a list
|
||||||
of mem_bank_statement objects. Every mem_bank_statement object
|
of mem_bank_statement objects. Every mem_bank_statement object
|
||||||
@@ -400,7 +412,7 @@ class parser(object):
|
|||||||
be used as a prefix. Adding a tracer (day resolution) can create
|
be used as a prefix. Adding a tracer (day resolution) can create
|
||||||
uniqueness. Adding unique statement ids can add to the robustness of
|
uniqueness. Adding unique statement ids can add to the robustness of
|
||||||
your transaction numbering.
|
your transaction numbering.
|
||||||
|
|
||||||
Just mind that users can create random (file)containers with
|
Just mind that users can create random (file)containers with
|
||||||
transactions in it. Try not to depend on order of appearance within
|
transactions in it. Try not to depend on order of appearance within
|
||||||
these files. If in doubt: sort.
|
these files. If in doubt: sort.
|
||||||
@@ -408,5 +420,3 @@ class parser(object):
|
|||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
_('This is a stub. Please implement your own.')
|
_('This is a stub. Please implement your own.')
|
||||||
)
|
)
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -31,16 +31,19 @@ from datetime import datetime, date
|
|||||||
# Correct python2.4 issues
|
# Correct python2.4 issues
|
||||||
try:
|
try:
|
||||||
datetime.strptime
|
datetime.strptime
|
||||||
|
|
||||||
def strpdate(str, format):
|
def strpdate(str, format):
|
||||||
return datetime.strptime(str, format).date()
|
return datetime.strptime(str, format).date()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def strpdate(str, format):
|
def strpdate(str, format):
|
||||||
tm = time.strptime(str, format)
|
tm = time.strptime(str, format)
|
||||||
return date(tm.tm_year, tm.tm_mon, tm.tm_mday)
|
return date(tm.tm_year, tm.tm_mon, tm.tm_mday)
|
||||||
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
'''Base Field class - fixed length left aligned string field in a record'''
|
'''Base Field class - fixed length left aligned string field in a record'''
|
||||||
def __init__(self, name, length=1, fillchar=' ', cast=str):
|
def __init__(self, name, length=1, fillchar=' ', cast=str):
|
||||||
@@ -57,11 +60,14 @@ class Field(object):
|
|||||||
|
|
||||||
def take(self, buffer):
|
def take(self, buffer):
|
||||||
offset = hasattr(self, 'offset') and self.offset or 0
|
offset = hasattr(self, 'offset') and self.offset or 0
|
||||||
return self.cast(buffer[offset:offset + self.length].rstrip(self.fillchar))
|
return self.cast(buffer[offset:offset + self.length].rstrip(
|
||||||
|
self.fillchar)
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s "%s"' % (self.__class__.__name__, self.name)
|
return '%s "%s"' % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
|
|
||||||
class Filler(Field):
|
class Filler(Field):
|
||||||
'''Constant value field'''
|
'''Constant value field'''
|
||||||
def __init__(self, name, length=1, value=' '):
|
def __init__(self, name, length=1, value=' '):
|
||||||
@@ -73,9 +79,10 @@ class Filler(Field):
|
|||||||
|
|
||||||
def format(self, value):
|
def format(self, value):
|
||||||
return super(Filler, self).format(
|
return super(Filler, self).format(
|
||||||
self.value * (self.length / len(self.value) +1)
|
self.value * (self.length / len(self.value) + 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DateField(Field):
|
class DateField(Field):
|
||||||
'''Variable date field'''
|
'''Variable date field'''
|
||||||
def __init__(self, name, format='%Y-%m-%d', auto=False, cast=str):
|
def __init__(self, name, format='%Y-%m-%d', auto=False, cast=str):
|
||||||
@@ -98,6 +105,7 @@ class DateField(Field):
|
|||||||
return strpdate(value, self.dateformat)
|
return strpdate(value, self.dateformat)
|
||||||
return self.auto and date.today() or None
|
return self.auto and date.today() or None
|
||||||
|
|
||||||
|
|
||||||
class RightAlignedField(Field):
|
class RightAlignedField(Field):
|
||||||
'''Deviation of Field: right aligned'''
|
'''Deviation of Field: right aligned'''
|
||||||
def format(self, value):
|
def format(self, value):
|
||||||
@@ -107,7 +115,10 @@ class RightAlignedField(Field):
|
|||||||
|
|
||||||
def take(self, buffer):
|
def take(self, buffer):
|
||||||
offset = hasattr(self, 'offset') and self.offset or 0
|
offset = hasattr(self, 'offset') and self.offset or 0
|
||||||
return self.cast(buffer[offset:offset + self.length].lstrip(self.fillchar))
|
return self.cast(buffer[offset:offset + self.length].lstrip(
|
||||||
|
self.fillchar)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NumberField(RightAlignedField):
|
class NumberField(RightAlignedField):
|
||||||
'''Deviation of Field: left zero filled'''
|
'''Deviation of Field: left zero filled'''
|
||||||
@@ -118,6 +129,7 @@ class NumberField(RightAlignedField):
|
|||||||
def format(self, value):
|
def format(self, value):
|
||||||
return super(NumberField, self).format(self.cast(value or ''))
|
return super(NumberField, self).format(self.cast(value or ''))
|
||||||
|
|
||||||
|
|
||||||
class RecordType(object):
|
class RecordType(object):
|
||||||
fields = []
|
fields = []
|
||||||
|
|
||||||
@@ -130,7 +142,7 @@ class RecordType(object):
|
|||||||
offset += field.length
|
offset += field.length
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return reduce(lambda x,y: x+y.length, self.fields, 0)
|
return reduce(lambda x, y: x + y.length, self.fields, 0)
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return any(lambda x, y=key: x.name == y, self.fields)
|
return any(lambda x, y=key: x.name == y, self.fields)
|
||||||
@@ -139,7 +151,7 @@ class RecordType(object):
|
|||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
if field.name == key:
|
if field.name == key:
|
||||||
return field
|
return field
|
||||||
raise KeyError, 'No such field: %s' % key
|
raise KeyError('No such field: %s' % key)
|
||||||
|
|
||||||
def format(self, buffer):
|
def format(self, buffer):
|
||||||
result = []
|
result = []
|
||||||
@@ -150,7 +162,8 @@ class RecordType(object):
|
|||||||
def take(self, buffer):
|
def take(self, buffer):
|
||||||
return dict(zip([x.name for x in self.fields],
|
return dict(zip([x.name for x in self.fields],
|
||||||
[x.take(buffer) for x in self.fields]
|
[x.take(buffer) for x in self.fields]
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
class Record(object):
|
class Record(object):
|
||||||
_recordtype = None
|
_recordtype = None
|
||||||
@@ -159,12 +172,12 @@ class Record(object):
|
|||||||
if hasattr(self, '_fields') and self._fields:
|
if hasattr(self, '_fields') and self._fields:
|
||||||
self._recordtype = RecordType(self._fields)
|
self._recordtype = RecordType(self._fields)
|
||||||
if not self._recordtype and not recordtype:
|
if not self._recordtype and not recordtype:
|
||||||
raise ValueError, 'No recordtype specified'
|
raise ValueError('No recordtype specified')
|
||||||
if not self._recordtype:
|
if not self._recordtype:
|
||||||
self._recordtype = recordtype()
|
self._recordtype = recordtype()
|
||||||
self._length = len(self._recordtype)
|
self._length = len(self._recordtype)
|
||||||
self._value = value.ljust(self._length)[:self._length]
|
self._value = value.ljust(self._length)[:self._length]
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self._length
|
return self._length
|
||||||
|
|
||||||
@@ -173,23 +186,24 @@ class Record(object):
|
|||||||
super(Record, self).__setattr__(attr, value)
|
super(Record, self).__setattr__(attr, value)
|
||||||
else:
|
else:
|
||||||
field = self._recordtype[attr]
|
field = self._recordtype[attr]
|
||||||
self._value = self._value[:field.offset] + \
|
self._value = (
|
||||||
field.format(value) + \
|
self._value[:field.offset] +
|
||||||
self._value[field.offset + field.length:]
|
field.format(value) +
|
||||||
|
self._value[field.offset + field.length:]
|
||||||
|
)
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr.startswith('_'):
|
if attr.startswith('_'):
|
||||||
return super(Record, self).__getattr__(attr)
|
return super(Record, self).__getattr__(attr)
|
||||||
field = self._recordtype[attr]
|
field = self._recordtype[attr]
|
||||||
return field.take(self._value)
|
return field.take(self._value)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self._recordtype.format(self._value)
|
return self._recordtype.format(self._value)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return unicode(self.cast(self))
|
return unicode(self.cast(self))
|
||||||
|
|
||||||
|
|
||||||
def asciify(str):
|
def asciify(str):
|
||||||
return unicodedata.normalize('NFKD', str).encode('ascii', 'ignore')
|
return unicodedata.normalize('NFKD', str).encode('ascii', 'ignore')
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# Copyright 2011 - 2014 Therp BV (<http://therp.nl>).
|
# Copyright 2011 - 2014 Therp BV (<http://therp.nl>).
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
@@ -25,7 +25,7 @@ class ResBank(orm.Model):
|
|||||||
|
|
||||||
def online_bank_info(self, cr, uid, bic, context=None):
|
def online_bank_info(self, cr, uid, bic, context=None):
|
||||||
"""
|
"""
|
||||||
API hook for legacy online lookup of BICs,
|
API hook for legacy online lookup of BICs,
|
||||||
to be removed in OpenERP 8.0.
|
to be removed in OpenERP 8.0.
|
||||||
"""
|
"""
|
||||||
return False, False
|
return False, False
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ResPartner(orm.Model):
|
|||||||
self, cr, uid, ids, get_property_account, context=None):
|
self, cr, uid, ids, get_property_account, context=None):
|
||||||
"""
|
"""
|
||||||
Returns the property journal account for the given partners ids.
|
Returns the property journal account for the given partners ids.
|
||||||
|
|
||||||
:param get_property_account: method of this object that takes
|
:param get_property_account: method of this object that takes
|
||||||
a partner browse record and returns a field name of type many2one.
|
a partner browse record and returns a field name of type many2one.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
__all__ = ['IBAN', 'BBAN']
|
__all__ = ['IBAN', 'BBAN']
|
||||||
|
|
||||||
|
|
||||||
def modulo_97_base10(abuffer):
|
def modulo_97_base10(abuffer):
|
||||||
'''
|
'''
|
||||||
Calculate the modulo 97 value of a string in base10
|
Calculate the modulo 97 value of a string in base10
|
||||||
@@ -55,6 +56,7 @@ def modulo_97_base10(abuffer):
|
|||||||
checksum %= 97
|
checksum %= 97
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
|
|
||||||
def base36_to_base10str(abuffer):
|
def base36_to_base10str(abuffer):
|
||||||
'''
|
'''
|
||||||
Convert a base36 string value to a string of base10 digits.
|
Convert a base36 string value to a string of base10 digits.
|
||||||
@@ -67,6 +69,7 @@ def base36_to_base10str(abuffer):
|
|||||||
result += digit
|
result += digit
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class BBANFormat(object):
|
class BBANFormat(object):
|
||||||
'''
|
'''
|
||||||
A BBANFormat is an auxilliary class for IBAN. It represents the composition
|
A BBANFormat is an auxilliary class for IBAN. It represents the composition
|
||||||
@@ -80,7 +83,7 @@ class BBANFormat(object):
|
|||||||
Specify the structure of the SEPA account in relation to the local
|
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
|
account. The XXZZ prefix that all SEPA accounts have is not part of
|
||||||
the structure in BBANFormat.
|
the structure in BBANFormat.
|
||||||
|
|
||||||
ibanfmt: string of identifiers from position 5 (start = 1):
|
ibanfmt: string of identifiers from position 5 (start = 1):
|
||||||
A = Account position
|
A = Account position
|
||||||
N = Account digit
|
N = Account digit
|
||||||
@@ -95,7 +98,7 @@ class BBANFormat(object):
|
|||||||
leading-zero-stripped account numbers.
|
leading-zero-stripped account numbers.
|
||||||
|
|
||||||
Example: (NL) 'CCCCAAAAAAAAAA'
|
Example: (NL) 'CCCCAAAAAAAAAA'
|
||||||
will convert 'INGB0001234567' into
|
will convert 'INGB0001234567' into
|
||||||
bankcode 'INGB' and account '0001234567'
|
bankcode 'INGB' and account '0001234567'
|
||||||
|
|
||||||
bbanfmt: string of placeholders for the local bank account
|
bbanfmt: string of placeholders for the local bank account
|
||||||
@@ -119,7 +122,7 @@ class BBANFormat(object):
|
|||||||
self._iban = ibanfmt
|
self._iban = ibanfmt
|
||||||
self._bban = bbanfmt
|
self._bban = bbanfmt
|
||||||
self._nolz = nolz
|
self._nolz = nolz
|
||||||
|
|
||||||
def __extract__(self, spec, value):
|
def __extract__(self, spec, value):
|
||||||
'''Extract the value based on the spec'''
|
'''Extract the value based on the spec'''
|
||||||
i = self._iban.find(spec)
|
i = self._iban.find(spec)
|
||||||
@@ -147,7 +150,7 @@ class BBANFormat(object):
|
|||||||
else:
|
else:
|
||||||
prefix = ''
|
prefix = ''
|
||||||
return prefix + self.__extract__('A', iban)
|
return prefix + self.__extract__('A', iban)
|
||||||
|
|
||||||
def BBAN(self, iban):
|
def BBAN(self, iban):
|
||||||
'''
|
'''
|
||||||
Format the BBAN part of the IBAN in iban following the local
|
Format the BBAN part of the IBAN in iban following the local
|
||||||
@@ -178,6 +181,7 @@ class BBANFormat(object):
|
|||||||
i += 1
|
i += 1
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class IBAN(str):
|
class IBAN(str):
|
||||||
'''
|
'''
|
||||||
A IBAN string represents a SEPA bank account number. This class provides
|
A IBAN string represents a SEPA bank account number. This class provides
|
||||||
@@ -272,7 +276,7 @@ class IBAN(str):
|
|||||||
if item.isalnum():
|
if item.isalnum():
|
||||||
init += item
|
init += item
|
||||||
elif item not in ' \t.-':
|
elif item not in ' \t.-':
|
||||||
raise ValueError, 'Invalid chars found in IBAN number'
|
raise ValueError('Invalid chars found in IBAN number')
|
||||||
return str.__new__(cls, init)
|
return str.__new__(cls, init)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -287,8 +291,7 @@ class IBAN(str):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, BIC=None, countrycode=None, BBAN=None, bankcode=None,
|
def create(cls, BIC=None, countrycode=None, BBAN=None, bankcode=None,
|
||||||
branchcode=None, account=None
|
branchcode=None, account=None):
|
||||||
):
|
|
||||||
'''
|
'''
|
||||||
Create a IBAN number from a BBAN and a country code. Optionaly create
|
Create a IBAN number from a BBAN and a country code. Optionaly create
|
||||||
a BBAN from BBAN components before generation.
|
a BBAN from BBAN components before generation.
|
||||||
@@ -304,20 +307,17 @@ class IBAN(str):
|
|||||||
if countrycode:
|
if countrycode:
|
||||||
countrycode = countrycode.upper()
|
countrycode = countrycode.upper()
|
||||||
else:
|
else:
|
||||||
raise ValueError, \
|
raise ValueError('Either BIC or countrycode is required')
|
||||||
'Either BIC or countrycode is required'
|
|
||||||
|
|
||||||
if countrycode not in cls.countries:
|
if countrycode not in cls.countries:
|
||||||
raise ValueError, \
|
raise ValueError('%s is not a SEPA country' % countrycode)
|
||||||
'%s is not a SEPA country' % countrycode
|
|
||||||
format = cls.BBAN_formats[countrycode]
|
format = cls.BBAN_formats[countrycode]
|
||||||
|
|
||||||
if BBAN:
|
if BBAN:
|
||||||
if len(BBAN) == len(format._iban):
|
if len(BBAN) == len(format._iban):
|
||||||
ibanno = cls(countrycode + '00' + BBAN)
|
ibanno = cls(countrycode + '00' + BBAN)
|
||||||
return cls(countrycode + ibanno.checksum + BBAN)
|
return cls(countrycode + ibanno.checksum + BBAN)
|
||||||
raise ValueError, \
|
raise ValueError('Insufficient data to generate IBAN')
|
||||||
'Insufficient data to generate IBAN'
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def valid(self):
|
def valid(self):
|
||||||
@@ -325,8 +325,10 @@ class IBAN(str):
|
|||||||
Check if the string + check digits deliver a valid checksum
|
Check if the string + check digits deliver a valid checksum
|
||||||
'''
|
'''
|
||||||
_buffer = self[4:] + self[:4]
|
_buffer = self[4:] + self[:4]
|
||||||
return self.countrycode in self.countries and \
|
return (
|
||||||
int(base36_to_base10str(_buffer)) % 97 == 1
|
self.countrycode in self.countries
|
||||||
|
and int(base36_to_base10str(_buffer)) % 97 == 1
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
'''
|
'''
|
||||||
@@ -387,7 +389,7 @@ class IBAN(str):
|
|||||||
The bank code seems to be world wide unique. Knowing this,
|
The bank code seems to be world wide unique. Knowing this,
|
||||||
one can use the country + bankcode info from BIC to narrow a
|
one can use the country + bankcode info from BIC to narrow a
|
||||||
search for the bank itself.
|
search for the bank itself.
|
||||||
|
|
||||||
Note that some countries use one single localization code for
|
Note that some countries use one single localization code for
|
||||||
all bank transactions in that country, while others do not. This
|
all bank transactions in that country, while others do not. This
|
||||||
makes it impossible to use an algorithmic approach for generating
|
makes it impossible to use an algorithmic approach for generating
|
||||||
@@ -421,22 +423,24 @@ class IBAN(str):
|
|||||||
'''
|
'''
|
||||||
return self[4:]
|
return self[4:]
|
||||||
|
|
||||||
|
|
||||||
class BBAN(object):
|
class BBAN(object):
|
||||||
'''
|
'''
|
||||||
Class to reformat a local BBAN account number to IBAN specs.
|
Class to reformat a local BBAN account number to IBAN specs.
|
||||||
Simple validation based on length of spec string elements and real data.
|
Simple validation based on length of spec string elements and real data.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_length(fmt, element):
|
def _get_length(fmt, element):
|
||||||
'''
|
'''
|
||||||
Internal method to calculate the length of a parameter in a
|
Internal method to calculate the length of a parameter in a
|
||||||
formatted string
|
formatted string
|
||||||
'''
|
'''
|
||||||
i = 0; max_i = len(fmt._iban)
|
i = 0
|
||||||
|
max_i = len(fmt._iban)
|
||||||
while i < max_i:
|
while i < max_i:
|
||||||
if fmt._iban[i] == element:
|
if fmt._iban[i] == element:
|
||||||
next = i +1
|
next = i + 1
|
||||||
while next < max_i and fmt._iban[next] == element:
|
while next < max_i and fmt._iban[next] == element:
|
||||||
next += 1
|
next += 1
|
||||||
return next - i
|
return next - i
|
||||||
@@ -453,7 +457,10 @@ class BBAN(object):
|
|||||||
if countrycode.upper() in IBAN.countries:
|
if countrycode.upper() in IBAN.countries:
|
||||||
self._fmt = IBAN.BBAN_formats[countrycode.upper()]
|
self._fmt = IBAN.BBAN_formats[countrycode.upper()]
|
||||||
res = ''
|
res = ''
|
||||||
i = 0; j = 0; max_i = len(self._fmt._bban); max_j = len(bban)
|
i = 0
|
||||||
|
j = 0
|
||||||
|
max_i = len(self._fmt._bban)
|
||||||
|
max_j = len(bban)
|
||||||
while i < max_i and j < max_j:
|
while i < max_i and j < max_j:
|
||||||
while bban[j] in ' \t' and j < max_j:
|
while bban[j] in ' \t' and j < max_j:
|
||||||
j += 1
|
j += 1
|
||||||
@@ -475,7 +482,7 @@ class BBAN(object):
|
|||||||
# Note that many accounts in the IBAN standard
|
# Note that many accounts in the IBAN standard
|
||||||
# are allowed to have leading zeros, so zfill
|
# are allowed to have leading zeros, so zfill
|
||||||
# to full spec length for visual validation.
|
# to full spec length for visual validation.
|
||||||
#
|
#
|
||||||
# Note 2: this may look funny to some, as most
|
# Note 2: this may look funny to some, as most
|
||||||
# local schemes strip leading zeros. It allows
|
# local schemes strip leading zeros. It allows
|
||||||
# us however to present the user a visual feedback
|
# us however to present the user a visual feedback
|
||||||
@@ -512,15 +519,16 @@ class BBAN(object):
|
|||||||
'''Simple check if BBAN is in the right format'''
|
'''Simple check if BBAN is in the right format'''
|
||||||
return self._bban and True or False
|
return self._bban and True or False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
for arg in sys.argv[1:]:
|
for arg in sys.argv[1:]:
|
||||||
iban = IBAN(arg)
|
iban = IBAN(arg)
|
||||||
print 'IBAN:', iban
|
print('IBAN:', iban)
|
||||||
print 'country code:', iban.countrycode
|
print('country code:', iban.countrycode)
|
||||||
print 'bank code:', iban.bankcode
|
print('bank code:', iban.bankcode)
|
||||||
print 'branch code:', iban.branchcode
|
print('branch code:', iban.branchcode)
|
||||||
print 'BBAN:', iban.BBAN
|
print('BBAN:', iban.BBAN)
|
||||||
print 'localized BBAN:', iban.localized_BBAN
|
print('localized BBAN:', iban.localized_BBAN)
|
||||||
print 'check digits:', iban.checkdigits
|
print('check digits:', iban.checkdigits)
|
||||||
print 'checksum:', iban.checksum
|
print('checksum:', iban.checksum)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -27,6 +27,7 @@ import re
|
|||||||
|
|
||||||
__all__ = ['split', 'get', 'PostalCode']
|
__all__ = ['split', 'get', 'PostalCode']
|
||||||
|
|
||||||
|
|
||||||
class PostalCode(object):
|
class PostalCode(object):
|
||||||
'''
|
'''
|
||||||
The PostalCode class is a wrapper around PostCodeFormat and an internal
|
The PostalCode class is a wrapper around PostCodeFormat and an internal
|
||||||
@@ -46,11 +47,11 @@ class PostalCode(object):
|
|||||||
'''
|
'''
|
||||||
# Sort formats on length, longest first
|
# Sort formats on length, longest first
|
||||||
formats = [(len(x), x) for x in format.split('|')]
|
formats = [(len(x), x) for x in format.split('|')]
|
||||||
formats = [x[1] for x in sorted(formats, lambda x,y: -cmp(x,y))]
|
formats = [x[1] for x in sorted(formats, lambda x, y: -cmp(x, y))]
|
||||||
self.res = [re.compile(x.replace('#', '\\d').replace('@','[A-Z]'))
|
self.res = [re.compile(x.replace('#', '\\d').replace('@', '[A-Z]'))
|
||||||
for x in formats
|
for x in formats
|
||||||
]
|
]
|
||||||
|
|
||||||
def get(self, str_):
|
def get(self, str_):
|
||||||
'''
|
'''
|
||||||
Return the postal code from the string str_
|
Return the postal code from the string str_
|
||||||
@@ -99,7 +100,8 @@ class PostalCode(object):
|
|||||||
'IM': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA',
|
'IM': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA',
|
||||||
'IL': '#####', 'IT': '####', 'JM': '', 'JP': '###-####',
|
'IL': '#####', 'IT': '####', 'JM': '', 'JP': '###-####',
|
||||||
'JE': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA',
|
'JE': '@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA',
|
||||||
'JO': '#####', 'KZ': '######', 'KE': '#####', 'KI': '', 'KP': '###-###',
|
'JO': '#####', 'KZ': '######', 'KE': '#####', 'KI': '',
|
||||||
|
'KP': '###-###',
|
||||||
'KR': 'SEOUL ###-###', 'KW': '#####', 'KG': '######', 'LA': '#####',
|
'KR': 'SEOUL ###-###', 'KW': '#####', 'KG': '######', 'LA': '#####',
|
||||||
'LV': 'LV-####', 'LB': '#### ####|####', 'LS': '###', 'LR': '####',
|
'LV': 'LV-####', 'LB': '#### ####|####', 'LS': '###', 'LR': '####',
|
||||||
'LY': '', 'LI': '####', 'LT': 'LT-#####', 'LU': '####', 'MO': '',
|
'LY': '', 'LI': '####', 'LT': 'LT-#####', 'LU': '####', 'MO': '',
|
||||||
@@ -139,7 +141,7 @@ class PostalCode(object):
|
|||||||
country <iso>.
|
country <iso>.
|
||||||
|
|
||||||
Returns iso, postal code and the remaining part of <str_>.
|
Returns iso, postal code and the remaining part of <str_>.
|
||||||
|
|
||||||
When iso is filled but postal code remains empty, no postal code could
|
When iso is filled but postal code remains empty, no postal code could
|
||||||
be found according to the rules of iso.
|
be found according to the rules of iso.
|
||||||
|
|
||||||
@@ -155,14 +157,12 @@ class PostalCode(object):
|
|||||||
|
|
||||||
# Find optimum (= max length postalcode) when iso code is unknown
|
# Find optimum (= max length postalcode) when iso code is unknown
|
||||||
all = {}
|
all = {}
|
||||||
opt_iso = ''
|
|
||||||
max_l = 0
|
max_l = 0
|
||||||
for key in cls._formats.iterkeys():
|
for key in cls._formats.iterkeys():
|
||||||
i, p, c = cls.split(str_, key)
|
i, p, c = cls.split(str_, key)
|
||||||
l = len(p)
|
l = len(p)
|
||||||
if l > max_l:
|
if l > max_l:
|
||||||
max_l = l
|
max_l = l
|
||||||
opt_iso = i
|
|
||||||
if l in all:
|
if l in all:
|
||||||
all[l].append((i, p, c))
|
all[l].append((i, p, c))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Define a struct class which behaves like a dict, but allows using
|
Define a struct class which behaves like a dict, but allows using
|
||||||
object.attr alongside object['attr'].
|
object.attr alongside object['attr'].
|
||||||
@@ -25,6 +26,7 @@ object.attr alongside object['attr'].
|
|||||||
|
|
||||||
__all__ = ['struct']
|
__all__ = ['struct']
|
||||||
|
|
||||||
|
|
||||||
class struct(dict):
|
class struct(dict):
|
||||||
'''
|
'''
|
||||||
Ease working with dicts. Allow dict.key alongside dict['key']
|
Ease working with dicts. Allow dict.key alongside dict['key']
|
||||||
@@ -52,4 +54,4 @@ class struct(dict):
|
|||||||
else:
|
else:
|
||||||
fmt = '%*.*s%%s: %%s' % (indent, indent, '')
|
fmt = '%*.*s%%s: %%s' % (indent, indent, '')
|
||||||
for item in self.iteritems():
|
for item in self.iteritems():
|
||||||
print fmt % item
|
print(fmt % item)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -18,8 +18,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
import bank_import
|
|
||||||
import banking_transaction_wizard
|
|
||||||
import link_partner
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
from . import bank_import
|
||||||
|
from . import banking_transaction_wizard
|
||||||
|
from . import link_partner
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -23,11 +23,13 @@
|
|||||||
# Kaspars Vilkens (KNdati): lenghty discussions, bugreports and bugfixes
|
# Kaspars Vilkens (KNdati): lenghty discussions, bugreports and bugfixes
|
||||||
# Stefan Rijnhart (Therp): bugreport and bugfix
|
# Stefan Rijnhart (Therp): bugreport and bugfix
|
||||||
#
|
#
|
||||||
|
|
||||||
'''
|
'''
|
||||||
This module contains the business logic of the wizard account_banking_import.
|
This module contains the business logic of the wizard account_banking_import.
|
||||||
The parsing is done in the parser modules. Every parser module is required to
|
The parsing is done in the parser modules. Every parser module is required to
|
||||||
use parser.models as a mean of communication with the business logic.
|
use parser.models as a mean of communication with the business logic.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
from openerp.osv import orm, fields
|
from openerp.osv import orm, fields
|
||||||
@@ -44,6 +46,7 @@ bt = models.mem_bank_transaction
|
|||||||
# the real payment date. This can occur with online transactions (web shops).
|
# the real payment date. This can occur with online transactions (web shops).
|
||||||
payment_window = datetime.timedelta(days=10)
|
payment_window = datetime.timedelta(days=10)
|
||||||
|
|
||||||
|
|
||||||
def parser_types(*args, **kwargs):
|
def parser_types(*args, **kwargs):
|
||||||
'''Delay evaluation of parser types until start of wizard, to allow
|
'''Delay evaluation of parser types until start of wizard, to allow
|
||||||
depending modules to initialize and add their parsers to the list
|
depending modules to initialize and add their parsers to the list
|
||||||
@@ -57,18 +60,26 @@ class banking_import_line(orm.TransientModel):
|
|||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Name', size=64),
|
'name': fields.char('Name', size=64),
|
||||||
'date': fields.date('Date', readonly=True),
|
'date': fields.date('Date', readonly=True),
|
||||||
'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')),
|
'amount': fields.float(
|
||||||
|
'Amount',
|
||||||
|
digits_compute=dp.get_precision('Account'),
|
||||||
|
),
|
||||||
'statement_line_id': fields.many2one(
|
'statement_line_id': fields.many2one(
|
||||||
'account.bank.statement.line',
|
'account.bank.statement.line',
|
||||||
'Resulting statement line', readonly=True),
|
'Resulting statement line', readonly=True),
|
||||||
'type': fields.selection([
|
'type': fields.selection([
|
||||||
('supplier','Supplier'),
|
('supplier', 'Supplier'),
|
||||||
('customer','Customer'),
|
('customer', 'Customer'),
|
||||||
('general','General')
|
('general', 'General')
|
||||||
], 'Type', required=True),
|
], 'Type', required=True),
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner'),
|
'partner_id': fields.many2one('res.partner', 'Partner'),
|
||||||
'statement_id': fields.many2one('account.bank.statement', 'Statement',
|
'statement_id': fields.many2one(
|
||||||
select=True, required=True, ondelete='cascade'),
|
'account.bank.statement',
|
||||||
|
'Statement',
|
||||||
|
select=True,
|
||||||
|
required=True,
|
||||||
|
ondelete='cascade',
|
||||||
|
),
|
||||||
'ref': fields.char('Reference', size=32),
|
'ref': fields.char('Reference', size=32),
|
||||||
'note': fields.text('Notes'),
|
'note': fields.text('Notes'),
|
||||||
'period_id': fields.many2one('account.period', 'Period'),
|
'period_id': fields.many2one('account.period', 'Period'),
|
||||||
@@ -83,16 +94,19 @@ class banking_import_line(orm.TransientModel):
|
|||||||
'account.invoice', 'banking_import_line_invoice_rel',
|
'account.invoice', 'banking_import_line_invoice_rel',
|
||||||
'line_id', 'invoice_id'),
|
'line_id', 'invoice_id'),
|
||||||
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account'),
|
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account'),
|
||||||
'transaction_type': fields.selection([
|
'transaction_type': fields.selection(
|
||||||
|
[
|
||||||
# TODO: payment terminal etc...
|
# TODO: payment terminal etc...
|
||||||
('invoice', 'Invoice payment'),
|
('invoice', 'Invoice payment'),
|
||||||
('storno', 'Canceled debit order'),
|
('storno', 'Canceled debit order'),
|
||||||
('bank_costs', 'Bank costs'),
|
('bank_costs', 'Bank costs'),
|
||||||
('unknown', 'Unknown'),
|
('unknown', 'Unknown'),
|
||||||
], 'Transaction type'),
|
],
|
||||||
|
'Transaction type',
|
||||||
|
),
|
||||||
'duplicate': fields.boolean('Duplicate'),
|
'duplicate': fields.boolean('Duplicate'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class banking_import(orm.TransientModel):
|
class banking_import(orm.TransientModel):
|
||||||
_name = 'account.banking.bank.import'
|
_name = 'account.banking.bank.import'
|
||||||
@@ -119,7 +133,8 @@ class banking_import(orm.TransientModel):
|
|||||||
if not parser:
|
if not parser:
|
||||||
raise orm.except_orm(
|
raise orm.except_orm(
|
||||||
_('ERROR!'),
|
_('ERROR!'),
|
||||||
_('Unable to import parser %(parser)s. Parser class not found.') %
|
_('Unable to import parser %(parser)s. Parser class not '
|
||||||
|
'found.') %
|
||||||
{'parser': parser_code}
|
{'parser': parser_code}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -133,16 +148,17 @@ class banking_import(orm.TransientModel):
|
|||||||
if any([x for x in statements if not x.is_valid()]):
|
if any([x for x in statements if not x.is_valid()]):
|
||||||
raise orm.except_orm(
|
raise orm.except_orm(
|
||||||
_('ERROR!'),
|
_('ERROR!'),
|
||||||
_('The imported statements appear to be invalid! Check your file.')
|
_('The imported statements appear to be invalid! Check your '
|
||||||
|
'file.')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create the file now, as the statements need to be linked to it
|
# Create the file now, as the statements need to be linked to it
|
||||||
import_id = statement_file_obj.create(cr, uid, dict(
|
import_id = statement_file_obj.create(cr, uid, dict(
|
||||||
company_id = company.id,
|
company_id=company.id,
|
||||||
file = statements_file,
|
file=statements_file,
|
||||||
file_name = banking_import.file_name,
|
file_name=banking_import.file_name,
|
||||||
state = 'unfinished',
|
state='unfinished',
|
||||||
format = parser.name,
|
format=parser.name,
|
||||||
))
|
))
|
||||||
|
|
||||||
bank_country_code = False
|
bank_country_code = False
|
||||||
@@ -151,14 +167,14 @@ class banking_import(orm.TransientModel):
|
|||||||
|
|
||||||
# Results
|
# Results
|
||||||
results = struct(
|
results = struct(
|
||||||
stat_loaded_cnt = 0,
|
stat_loaded_cnt=0,
|
||||||
trans_loaded_cnt = 0,
|
trans_loaded_cnt=0,
|
||||||
stat_skipped_cnt = 0,
|
stat_skipped_cnt=0,
|
||||||
trans_skipped_cnt = 0,
|
trans_skipped_cnt=0,
|
||||||
trans_matched_cnt = 0,
|
trans_matched_cnt=0,
|
||||||
bank_costs_invoice_cnt = 0,
|
bank_costs_invoice_cnt=0,
|
||||||
error_cnt = 0,
|
error_cnt=0,
|
||||||
log = [],
|
log=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Caching
|
# Caching
|
||||||
@@ -175,7 +191,9 @@ class banking_import(orm.TransientModel):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Create fallback currency code
|
# Create fallback currency code
|
||||||
currency_code = statement.local_currency or company.currency_id.name
|
currency_code = (
|
||||||
|
statement.local_currency or company.currency_id.name
|
||||||
|
)
|
||||||
|
|
||||||
# Check cache for account info/currency
|
# Check cache for account info/currency
|
||||||
if statement.local_account in info and \
|
if statement.local_account in info and \
|
||||||
@@ -190,8 +208,10 @@ class banking_import(orm.TransientModel):
|
|||||||
)
|
)
|
||||||
if not account_info:
|
if not account_info:
|
||||||
results.log.append(
|
results.log.append(
|
||||||
_('Statements found for unknown account %(bank_account)s') %
|
_('Statements found for unknown account '
|
||||||
{'bank_account': statement.local_account}
|
'%(bank_account)s') % {
|
||||||
|
'bank_account': statement.local_account
|
||||||
|
}
|
||||||
)
|
)
|
||||||
error_accounts[statement.local_account] = True
|
error_accounts[statement.local_account] = True
|
||||||
results.error_cnt += 1
|
results.error_cnt += 1
|
||||||
@@ -200,7 +220,7 @@ class banking_import(orm.TransientModel):
|
|||||||
results.log.append(
|
results.log.append(
|
||||||
_('Statements found for account %(bank_account)s, '
|
_('Statements found for account %(bank_account)s, '
|
||||||
'but no default journal was defined.'
|
'but no default journal was defined.'
|
||||||
) % {'bank_account': statement.local_account}
|
) % {'bank_account': statement.local_account}
|
||||||
)
|
)
|
||||||
error_accounts[statement.local_account] = True
|
error_accounts[statement.local_account] = True
|
||||||
results.error_cnt += 1
|
results.error_cnt += 1
|
||||||
@@ -210,7 +230,7 @@ class banking_import(orm.TransientModel):
|
|||||||
currency_code = account_info.currency_id.name
|
currency_code = account_info.currency_id.name
|
||||||
|
|
||||||
# Cache results
|
# Cache results
|
||||||
if not statement.local_account in info:
|
if statement.local_account not in info:
|
||||||
info[statement.local_account] = {
|
info[statement.local_account] = {
|
||||||
currency_code: account_info
|
currency_code: account_info
|
||||||
}
|
}
|
||||||
@@ -222,22 +242,22 @@ class banking_import(orm.TransientModel):
|
|||||||
and account_info.currency_id.name != statement.local_currency:
|
and account_info.currency_id.name != statement.local_currency:
|
||||||
# TODO: convert currencies?
|
# TODO: convert currencies?
|
||||||
results.log.append(
|
results.log.append(
|
||||||
_('Statement %(statement_id)s for account %(bank_account)s'
|
_('Statement %(statement_id)s for account %(bank_account)s'
|
||||||
' uses different currency than the defined bank journal.'
|
' uses different currency than the defined bank journal.'
|
||||||
) % {
|
) % {
|
||||||
'bank_account': statement.local_account,
|
'bank_account': statement.local_account,
|
||||||
'statement_id': statement.id
|
'statement_id': statement.id
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
error_accounts[statement.local_account] = True
|
error_accounts[statement.local_account] = True
|
||||||
results.error_cnt += 1
|
results.error_cnt += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check existence of previous statement
|
# Check existence of previous statement
|
||||||
# Less well defined formats can resort to a
|
# Less well defined formats can resort to a
|
||||||
# dynamically generated statement identification
|
# dynamically generated statement identification
|
||||||
# (e.g. a datetime string of the moment of import)
|
# (e.g. a datetime string of the moment of import)
|
||||||
# and have potential duplicates flagged by the
|
# and have potential duplicates flagged by the
|
||||||
# matching procedure
|
# matching procedure
|
||||||
statement_ids = statement_obj.search(cr, uid, [
|
statement_ids = statement_obj.search(cr, uid, [
|
||||||
('name', '=', statement.id),
|
('name', '=', statement.id),
|
||||||
@@ -251,7 +271,8 @@ class banking_import(orm.TransientModel):
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Get the period for the statement (as bank statement object checks this)
|
# Get the period for the statement (as bank statement object
|
||||||
|
# checks this)
|
||||||
period_ids = period_obj.search(
|
period_ids = period_obj.search(
|
||||||
cr, uid, [
|
cr, uid, [
|
||||||
('company_id', '=', company.id),
|
('company_id', '=', company.id),
|
||||||
@@ -259,7 +280,7 @@ class banking_import(orm.TransientModel):
|
|||||||
('date_stop', '>=', statement.date),
|
('date_stop', '>=', statement.date),
|
||||||
('special', '=', False),
|
('special', '=', False),
|
||||||
], context=context)
|
], context=context)
|
||||||
|
|
||||||
if not period_ids:
|
if not period_ids:
|
||||||
results.log.append(
|
results.log.append(
|
||||||
_('No period found covering statement date %(date)s, '
|
_('No period found covering statement date %(date)s, '
|
||||||
@@ -272,17 +293,17 @@ class banking_import(orm.TransientModel):
|
|||||||
|
|
||||||
# Create the bank statement record
|
# Create the bank statement record
|
||||||
statement_id = statement_obj.create(cr, uid, dict(
|
statement_id = statement_obj.create(cr, uid, dict(
|
||||||
name = statement.id,
|
name=statement.id,
|
||||||
journal_id = account_info.journal_id.id,
|
journal_id=account_info.journal_id.id,
|
||||||
date = convert.date2str(statement.date),
|
date=convert.date2str(statement.date),
|
||||||
balance_start = statement.start_balance,
|
balance_start=statement.start_balance,
|
||||||
balance_end_real = statement.end_balance,
|
balance_end_real=statement.end_balance,
|
||||||
balance_end = statement.end_balance,
|
balance_end=statement.end_balance,
|
||||||
state = 'draft',
|
state='draft',
|
||||||
user_id = uid,
|
user_id=uid,
|
||||||
banking_id = import_id,
|
banking_id=import_id,
|
||||||
company_id = company.id,
|
company_id=company.id,
|
||||||
period_id = period_ids[0],
|
period_id=period_ids[0],
|
||||||
))
|
))
|
||||||
imported_statement_ids.append(statement_id)
|
imported_statement_ids.append(statement_id)
|
||||||
|
|
||||||
@@ -294,7 +315,8 @@ class banking_import(orm.TransientModel):
|
|||||||
values = {}
|
values = {}
|
||||||
for attr in transaction.__slots__ + ['type']:
|
for attr in transaction.__slots__ + ['type']:
|
||||||
if attr in import_transaction_obj.column_map:
|
if attr in import_transaction_obj.column_map:
|
||||||
values[import_transaction_obj.column_map[attr]] = eval('transaction.%s' % attr)
|
values[import_transaction_obj.column_map[attr]] = \
|
||||||
|
eval('transaction.%s' % attr)
|
||||||
elif attr in import_transaction_obj._columns:
|
elif attr in import_transaction_obj._columns:
|
||||||
values[attr] = eval('transaction.%s' % attr)
|
values[attr] = eval('transaction.%s' % attr)
|
||||||
values['statement_id'] = statement_id
|
values['statement_id'] = statement_id
|
||||||
@@ -305,35 +327,17 @@ class banking_import(orm.TransientModel):
|
|||||||
transaction_id = import_transaction_obj.create(
|
transaction_id = import_transaction_obj.create(
|
||||||
cr, uid, values, context=context)
|
cr, uid, values, context=context)
|
||||||
transaction_ids.append(transaction_id)
|
transaction_ids.append(transaction_id)
|
||||||
|
|
||||||
results.stat_loaded_cnt += 1
|
results.stat_loaded_cnt += 1
|
||||||
|
|
||||||
import_transaction_obj.match(cr, uid, transaction_ids, results=results, context=context)
|
import_transaction_obj.match(
|
||||||
|
cr, uid, transaction_ids, results=results, context=context
|
||||||
#recompute statement end_balance for validation
|
)
|
||||||
|
|
||||||
|
# recompute statement end_balance for validation
|
||||||
statement_obj.button_dummy(
|
statement_obj.button_dummy(
|
||||||
cr, uid, imported_statement_ids, context=context)
|
cr, uid, imported_statement_ids, context=context)
|
||||||
|
|
||||||
|
|
||||||
# Original code. Didn't take workflow logistics into account...
|
|
||||||
#
|
|
||||||
#cr.execute(
|
|
||||||
# "UPDATE payment_order o "
|
|
||||||
# "SET state = 'done', "
|
|
||||||
# "date_done = '%s' "
|
|
||||||
# "FROM payment_line l "
|
|
||||||
# "WHERE o.state = 'sent' "
|
|
||||||
# "AND o.id = l.order_id "
|
|
||||||
# "AND l.id NOT IN ("
|
|
||||||
# "SELECT DISTINCT id FROM payment_line "
|
|
||||||
# "WHERE date_done IS NULL "
|
|
||||||
# "AND id IN (%s)"
|
|
||||||
# ")" % (
|
|
||||||
# time.strftime('%Y-%m-%d'),
|
|
||||||
# ','.join([str(x) for x in payment_line_ids])
|
|
||||||
# )
|
|
||||||
#)
|
|
||||||
|
|
||||||
report = [
|
report = [
|
||||||
'%s: %s' % (_('Total number of statements'),
|
'%s: %s' % (_('Total number of statements'),
|
||||||
results.stat_skipped_cnt + results.stat_loaded_cnt),
|
results.stat_skipped_cnt + results.stat_loaded_cnt),
|
||||||
@@ -360,15 +364,18 @@ class banking_import(orm.TransientModel):
|
|||||||
text_log = '\n'.join(report + results.log)
|
text_log = '\n'.join(report + results.log)
|
||||||
state = results.error_cnt and 'error' or 'ready'
|
state = results.error_cnt and 'error' or 'ready'
|
||||||
statement_file_obj.write(cr, uid, import_id, dict(
|
statement_file_obj.write(cr, uid, import_id, dict(
|
||||||
state = state, log = text_log,
|
state=state,
|
||||||
), context)
|
log=text_log,
|
||||||
|
), context)
|
||||||
if not imported_statement_ids or not results.trans_loaded_cnt:
|
if not imported_statement_ids or not results.trans_loaded_cnt:
|
||||||
# file state can be 'ready' while import state is 'error'
|
# file state can be 'ready' while import state is 'error'
|
||||||
state = 'error'
|
state = 'error'
|
||||||
self.write(cr, uid, [ids[0]], dict(
|
self.write(cr, uid, [ids[0]], dict(
|
||||||
import_id = import_id, log = text_log, state = state,
|
import_id=import_id,
|
||||||
statement_ids = [(6, 0, imported_statement_ids)],
|
log=text_log,
|
||||||
), context)
|
state=state,
|
||||||
|
statement_ids=[(6, 0, imported_statement_ids)],
|
||||||
|
), context)
|
||||||
return {
|
return {
|
||||||
'name': (state == 'ready' and _('Review Bank Statements') or
|
'name': (state == 'ready' and _('Review Bank Statements') or
|
||||||
_('Error')),
|
_('Error')),
|
||||||
@@ -393,13 +400,15 @@ class banking_import(orm.TransientModel):
|
|||||||
),
|
),
|
||||||
'file_name': fields.char('File name', size=256),
|
'file_name': fields.char('File name', size=256),
|
||||||
'file': fields.binary(
|
'file': fields.binary(
|
||||||
'Statements File', required=True,
|
'Statements File',
|
||||||
help = ('The Transactions File to import. Please note that while it is '
|
required=True,
|
||||||
'perfectly safe to reload the same file multiple times or to load in '
|
help=('The Transactions File to import. Please note that while it '
|
||||||
'timeframe overlapping statements files, there are formats that may '
|
'is perfectly safe to reload the same file multiple times '
|
||||||
'introduce different sequencing, which may create double entries.\n\n'
|
'or to load in timeframe overlapping statements files, '
|
||||||
'To stay on the safe side, always load bank statements files using the '
|
'there are formats that may introduce different '
|
||||||
'same format.'),
|
'sequencing, which may create double entries.\n\n'
|
||||||
|
'To stay on the safe side, always load bank statements '
|
||||||
|
'files using the same format.'),
|
||||||
states={
|
states={
|
||||||
'ready': [('readonly', True)],
|
'ready': [('readonly', True)],
|
||||||
'error': [('readonly', True)],
|
'error': [('readonly', True)],
|
||||||
@@ -435,8 +444,8 @@ class banking_import(orm.TransientModel):
|
|||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'state': 'init',
|
'state': 'init',
|
||||||
'company': lambda s,cr,uid,c:
|
'company': lambda s, cr, uid, c:
|
||||||
s.pool.get('res.company')._company_default_get(
|
s.pool.get('res.company')._company_default_get(
|
||||||
cr, uid, 'bank.import.transaction', context=c),
|
cr, uid, 'bank.import.transaction', context=c),
|
||||||
'parser': _default_parser_type,
|
'parser': _default_parser_type,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -50,7 +50,7 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None):
|
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None):
|
||||||
"""
|
"""
|
||||||
Return a popup window for this model
|
Return a popup window for this model
|
||||||
"""
|
"""
|
||||||
if isinstance(ids, (int, long)):
|
if isinstance(ids, (int, long)):
|
||||||
@@ -78,10 +78,10 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
import_transaction_obj = self.pool.get('banking.import.transaction')
|
import_transaction_obj = self.pool.get('banking.import.transaction')
|
||||||
trans_id = self.read(
|
trans_id = self.read(
|
||||||
cr, uid, ids[0], ['import_transaction_id'],
|
cr, uid, ids[0], ['import_transaction_id'],
|
||||||
context=context)['import_transaction_id'][0] # many2one tuple
|
context=context)['import_transaction_id'][0] # many2one tuple
|
||||||
import_transaction_obj.match(cr, uid, [trans_id], context=context)
|
import_transaction_obj.match(cr, uid, [trans_id], context=context)
|
||||||
return self.create_act_window(cr, uid, ids, context=None)
|
return self.create_act_window(cr, uid, ids, context=None)
|
||||||
|
|
||||||
def write(self, cr, uid, ids, vals, context=None):
|
def write(self, cr, uid, ids, vals, context=None):
|
||||||
"""
|
"""
|
||||||
Implement a trigger to retrieve the corresponding move line
|
Implement a trigger to retrieve the corresponding move line
|
||||||
@@ -121,22 +121,27 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
# Given the arity of the relation, there is are always
|
# Given the arity of the relation, there is are always
|
||||||
# multiple possibilities but the move lines here are
|
# multiple possibilities but the move lines here are
|
||||||
# prefiltered for having account_id.type payable/receivable
|
# prefiltered for having account_id.type payable/receivable
|
||||||
# and the regular invoice workflow should only come up with
|
# and the regular invoice workflow should only come up with
|
||||||
# one of those only.
|
# one of those only.
|
||||||
for move_line in wiz.import_transaction_id.move_line_ids:
|
for move_line in wiz.import_transaction_id.move_line_ids:
|
||||||
if (move_line.invoice ==
|
if (move_line.invoice ==
|
||||||
wiz.import_transaction_id.invoice_id):
|
wiz.import_transaction_id.invoice_id):
|
||||||
transaction_obj.write(
|
transaction_obj.write(
|
||||||
cr, uid, wiz.import_transaction_id.id,
|
cr, uid, wiz.import_transaction_id.id,
|
||||||
{ 'move_line_id': move_line.id, }, context=context)
|
{'move_line_id': move_line.id, },
|
||||||
|
context=context
|
||||||
|
)
|
||||||
statement_line_obj.write(
|
statement_line_obj.write(
|
||||||
cr, uid, wiz.import_transaction_id.statement_line_id.id,
|
cr, uid,
|
||||||
{ 'partner_id': move_line.partner_id.id or False,
|
wiz.import_transaction_id.statement_line_id.id,
|
||||||
'account_id': move_line.account_id.id,
|
{
|
||||||
}, context=context)
|
'partner_id': (
|
||||||
|
move_line.partner_id.id or False),
|
||||||
|
'account_id': move_line.account_id.id,
|
||||||
|
}, context=context)
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
# Cannot match the invoice
|
# Cannot match the invoice
|
||||||
if not found:
|
if not found:
|
||||||
orm.except_orm(
|
orm.except_orm(
|
||||||
_("No entry found for the selected invoice"),
|
_("No entry found for the selected invoice"),
|
||||||
@@ -150,15 +155,16 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
# Rewrite *2many directive notation
|
# Rewrite *2many directive notation
|
||||||
if manual_invoice_ids:
|
if manual_invoice_ids:
|
||||||
manual_invoice_ids = (
|
manual_invoice_ids = (
|
||||||
[i[1] for i in manual_invoice_ids if i[0]==4] +
|
[i[1] for i in manual_invoice_ids if i[0] == 4] +
|
||||||
[j for i in manual_invoice_ids if i[0]==6 for j in i[2]])
|
[j for i in manual_invoice_ids if i[0] == 6 for j in i[2]])
|
||||||
if manual_move_line_ids:
|
if manual_move_line_ids:
|
||||||
manual_move_line_ids = (
|
manual_move_line_ids = (
|
||||||
[i[1] for i in manual_move_line_ids if i[0]==4] +
|
[i[1] for i in manual_move_line_ids if i[0] == 4] +
|
||||||
[j for i in manual_move_line_ids if i[0]==6 for j in i[2]])
|
[j for i in manual_move_line_ids
|
||||||
|
if i[0] == 6 for j in i[2]])
|
||||||
for wiz in self.browse(cr, uid, ids, context=context):
|
for wiz in self.browse(cr, uid, ids, context=context):
|
||||||
#write can be called multiple times for the same values
|
# write can be called multiple times for the same values
|
||||||
#that doesn't hurt above, but it does here
|
# that doesn't hurt above, but it does here
|
||||||
if wiz.match_type and (
|
if wiz.match_type and (
|
||||||
len(manual_move_line_ids) > 1 or
|
len(manual_move_line_ids) > 1 or
|
||||||
len(manual_invoice_ids) > 1):
|
len(manual_invoice_ids) > 1):
|
||||||
@@ -171,7 +177,8 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
found_move_line = False
|
found_move_line = False
|
||||||
if invoice.move_id:
|
if invoice.move_id:
|
||||||
for line in invoice.move_id.line_id:
|
for line in invoice.move_id.line_id:
|
||||||
if line.account_id.type in ('receivable', 'payable'):
|
if line.account_id.type in ('receivable',
|
||||||
|
'payable'):
|
||||||
todo.append((invoice.id, line.id))
|
todo.append((invoice.id, line.id))
|
||||||
found_move_line = True
|
found_move_line = True
|
||||||
break
|
break
|
||||||
@@ -181,12 +188,13 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
_("No entry found for the selected invoice. "))
|
_("No entry found for the selected invoice. "))
|
||||||
for move_line_id in manual_move_line_ids:
|
for move_line_id in manual_move_line_ids:
|
||||||
todo_entry = [False, move_line_id]
|
todo_entry = [False, move_line_id]
|
||||||
move_line=move_line_obj.read(
|
move_line = move_line_obj.read(
|
||||||
cr,
|
cr,
|
||||||
uid,
|
uid,
|
||||||
move_line_id,
|
move_line_id,
|
||||||
['invoice'],
|
['invoice'],
|
||||||
context=context)
|
context=context
|
||||||
|
)
|
||||||
if move_line['invoice']:
|
if move_line['invoice']:
|
||||||
todo_entry[0] = move_line['invoice'][0]
|
todo_entry[0] = move_line['invoice'][0]
|
||||||
todo.append(todo_entry)
|
todo.append(todo_entry)
|
||||||
@@ -194,25 +202,27 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
while todo:
|
while todo:
|
||||||
todo_entry = todo.pop()
|
todo_entry = todo.pop()
|
||||||
move_line = move_line_obj.browse(
|
move_line = move_line_obj.browse(
|
||||||
cr, uid, todo_entry[1], context)
|
cr, uid, todo_entry[1], context)
|
||||||
transaction_id = wiz.import_transaction_id.id
|
transaction_id = wiz.import_transaction_id.id
|
||||||
statement_line_id = wiz.statement_line_id.id
|
statement_line_id = wiz.statement_line_id.id
|
||||||
|
|
||||||
if len(todo) > 0:
|
if len(todo) > 0:
|
||||||
statement_line_id = wiz.statement_line_id.split_off(
|
statement_line_id = wiz.statement_line_id.split_off(
|
||||||
move_line.debit or -move_line.credit)[0]
|
move_line.debit or -move_line.credit)[0]
|
||||||
transaction_id = statement_line_obj.browse(
|
transaction_id = statement_line_obj.browse(
|
||||||
cr,
|
cr,
|
||||||
uid,
|
uid,
|
||||||
statement_line_id,
|
statement_line_id,
|
||||||
context=context).import_transaction_id.id
|
context=context
|
||||||
|
).import_transaction_id.id
|
||||||
|
|
||||||
vals = {
|
vals = {
|
||||||
'move_line_id': todo_entry[1],
|
'move_line_id': todo_entry[1],
|
||||||
'move_line_ids': [(6, 0, [todo_entry[1]])],
|
'move_line_ids': [(6, 0, [todo_entry[1]])],
|
||||||
'invoice_id': todo_entry[0],
|
'invoice_id': todo_entry[0],
|
||||||
'invoice_ids': [(6, 0,
|
'invoice_ids': [
|
||||||
[todo_entry[0]] if todo_entry[0] else [])],
|
(6, 0, [todo_entry[0]] if todo_entry[0] else [])
|
||||||
|
],
|
||||||
'match_type': 'manual',
|
'match_type': 'manual',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +231,7 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
|
|
||||||
st_line_vals = {
|
st_line_vals = {
|
||||||
'account_id': move_line_obj.read(
|
'account_id': move_line_obj.read(
|
||||||
cr, uid, todo_entry[1],
|
cr, uid, todo_entry[1],
|
||||||
['account_id'], context=context)['account_id'][0],
|
['account_id'], context=context)['account_id'][0],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +241,7 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
).partner_id.commercial_partner_id.id
|
).partner_id.commercial_partner_id.id
|
||||||
|
|
||||||
statement_line_obj.write(
|
statement_line_obj.write(
|
||||||
cr, uid, statement_line_id,
|
cr, uid, statement_line_id,
|
||||||
st_line_vals, context=context)
|
st_line_vals, context=context)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -255,22 +265,19 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
# Get the bank account setting record, to reset the account
|
# Get the bank account setting record, to reset the account
|
||||||
account_id = False
|
account_id = False
|
||||||
journal_id = wiz.statement_line_id.statement_id.journal_id.id
|
journal_id = wiz.statement_line_id.statement_id.journal_id.id
|
||||||
setting_ids = settings_pool.find(cr, uid, journal_id, context=context)
|
setting_ids = settings_pool.find(
|
||||||
|
cr, uid, journal_id, context=context
|
||||||
|
)
|
||||||
|
|
||||||
# Restore partner id from the bank account or else reset
|
# Restore partner id from the bank account or else reset
|
||||||
partner_id = False
|
partner_id = False
|
||||||
if (wiz.statement_line_id.partner_bank_id and
|
if (wiz.statement_line_id.partner_bank_id and
|
||||||
wiz.statement_line_id.partner_bank_id.partner_id):
|
wiz.statement_line_id.partner_bank_id.partner_id):
|
||||||
partner_id = wiz.statement_line_id.partner_bank_id.partner_id.id
|
partner_id = (
|
||||||
|
wiz.statement_line_id.partner_bank_id.partner_id.id
|
||||||
|
)
|
||||||
wiz.write({'partner_id': partner_id})
|
wiz.write({'partner_id': partner_id})
|
||||||
|
|
||||||
# Select account type by parter customer or supplier,
|
|
||||||
# or default based on amount sign
|
|
||||||
if wiz.amount < 0:
|
|
||||||
account_type = 'payable'
|
|
||||||
else:
|
|
||||||
account_type = 'receivable'
|
|
||||||
|
|
||||||
bank_partner = False
|
bank_partner = False
|
||||||
if partner_id:
|
if partner_id:
|
||||||
bank_partner = wiz.statement_line_id.partner_bank_id.partner_id
|
bank_partner = wiz.statement_line_id.partner_bank_id.partner_id
|
||||||
@@ -295,13 +302,18 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
wiz.statement_line_id.write({'account_id': account_id})
|
wiz.statement_line_id.write({'account_id': account_id})
|
||||||
|
|
||||||
if wiz.statement_line_id:
|
if wiz.statement_line_id:
|
||||||
#delete splits causing an unsplit if this is a split
|
# delete splits causing an unsplit if this is a split
|
||||||
#transaction
|
# transaction
|
||||||
statement_pool.unlink(cr, uid,
|
statement_pool.unlink(
|
||||||
statement_pool.search(cr, uid,
|
cr,
|
||||||
[('parent_id', '=', wiz.statement_line_id.id)],
|
uid,
|
||||||
context=context),
|
statement_pool.search(
|
||||||
context=context)
|
cr, uid,
|
||||||
|
[('parent_id', '=', wiz.statement_line_id.id)],
|
||||||
|
context=context
|
||||||
|
),
|
||||||
|
context=context
|
||||||
|
)
|
||||||
|
|
||||||
if wiz.import_transaction_id:
|
if wiz.import_transaction_id:
|
||||||
wiz.import_transaction_id.clear_and_write()
|
wiz.import_transaction_id.clear_and_write()
|
||||||
@@ -313,15 +325,15 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
ids = [ids]
|
ids = [ids]
|
||||||
transaction_obj = self.pool.get('banking.import.transaction')
|
transaction_obj = self.pool.get('banking.import.transaction')
|
||||||
for wiz in self.read(
|
for wiz in self.read(
|
||||||
cr, uid, ids, ['duplicate', 'import_transaction_id'],
|
cr, uid, ids, ['duplicate', 'import_transaction_id'],
|
||||||
context=context):
|
context=context):
|
||||||
transaction_obj.write(
|
transaction_obj.write(
|
||||||
cr, uid, wiz['import_transaction_id'][0],
|
cr, uid, wiz['import_transaction_id'][0],
|
||||||
{'duplicate': not wiz['duplicate']}, context=context)
|
{'duplicate': not wiz['duplicate']}, context=context)
|
||||||
return self.create_act_window(cr, uid, ids, context=None)
|
return self.create_act_window(cr, uid, ids, context=None)
|
||||||
|
|
||||||
def button_done(self, cr, uid, ids, context=None):
|
def button_done(self, cr, uid, ids, context=None):
|
||||||
return {'type': 'ir.actions.act_window_close'}
|
return {'type': 'ir.actions.act_window_close'}
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Name', size=64),
|
'name': fields.char('Name', size=64),
|
||||||
@@ -349,22 +361,26 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
'statement_line_id', 'parent_id', type='many2one',
|
'statement_line_id', 'parent_id', type='many2one',
|
||||||
relation='account.bank.statement.line', readonly=True),
|
relation='account.bank.statement.line', readonly=True),
|
||||||
'import_transaction_id': fields.related(
|
'import_transaction_id': fields.related(
|
||||||
'statement_line_id', 'import_transaction_id',
|
'statement_line_id', 'import_transaction_id',
|
||||||
string="Import transaction",
|
string="Import transaction",
|
||||||
type='many2one', relation='banking.import.transaction'),
|
type='many2one', relation='banking.import.transaction'),
|
||||||
'residual': fields.related(
|
'residual': fields.related(
|
||||||
'import_transaction_id', 'residual', type='float',
|
'import_transaction_id', 'residual', type='float',
|
||||||
string='Residual', readonly=True),
|
string='Residual', readonly=True),
|
||||||
'writeoff_account_id': fields.related(
|
'writeoff_account_id': fields.related(
|
||||||
'import_transaction_id', 'writeoff_account_id',
|
'import_transaction_id', 'writeoff_account_id',
|
||||||
type='many2one', relation='account.account',
|
type='many2one', relation='account.account',
|
||||||
string='Write-off account'),
|
string='Write-off account'),
|
||||||
'invoice_ids': fields.related(
|
'invoice_ids': fields.related(
|
||||||
'import_transaction_id', 'invoice_ids', string="Matching invoices",
|
'import_transaction_id', 'invoice_ids', string="Matching invoices",
|
||||||
type='many2many', relation='account.invoice'),
|
type='many2many', relation='account.invoice'),
|
||||||
'invoice_id': fields.related(
|
'invoice_id': fields.related(
|
||||||
'import_transaction_id', 'invoice_id', string="Invoice to reconcile",
|
'import_transaction_id',
|
||||||
type='many2one', relation='account.invoice'),
|
'invoice_id',
|
||||||
|
string="Invoice to reconcile",
|
||||||
|
type='many2one',
|
||||||
|
relation='account.invoice',
|
||||||
|
),
|
||||||
'move_line_ids': fields.related(
|
'move_line_ids': fields.related(
|
||||||
'import_transaction_id', 'move_line_ids', string="Entry lines",
|
'import_transaction_id', 'move_line_ids', string="Entry lines",
|
||||||
type='many2many', relation='account.move.line'),
|
type='many2many', relation='account.move.line'),
|
||||||
@@ -372,15 +388,20 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
'import_transaction_id', 'move_line_id', string="Entry line",
|
'import_transaction_id', 'move_line_id', string="Entry line",
|
||||||
type='many2one', relation='account.move.line'),
|
type='many2one', relation='account.move.line'),
|
||||||
'duplicate': fields.related(
|
'duplicate': fields.related(
|
||||||
'import_transaction_id', 'duplicate', string='Flagged as duplicate',
|
'import_transaction_id',
|
||||||
type='boolean'),
|
'duplicate',
|
||||||
|
string='Flagged as duplicate',
|
||||||
|
type='boolean',
|
||||||
|
),
|
||||||
'match_multi': fields.related(
|
'match_multi': fields.related(
|
||||||
'import_transaction_id', 'match_multi',
|
'import_transaction_id', 'match_multi',
|
||||||
type="boolean", string='Multiple matches'),
|
type="boolean", string='Multiple matches'),
|
||||||
'match_type': fields.related(
|
'match_type': fields.related(
|
||||||
'import_transaction_id', 'match_type', type='selection',
|
'import_transaction_id',
|
||||||
|
'match_type',
|
||||||
|
type='selection',
|
||||||
selection=[
|
selection=[
|
||||||
('move','Move'),
|
('move', 'Move'),
|
||||||
('invoice', 'Invoice'),
|
('invoice', 'Invoice'),
|
||||||
('payment', 'Payment line'),
|
('payment', 'Payment line'),
|
||||||
('payment_order', 'Payment order'),
|
('payment_order', 'Payment order'),
|
||||||
@@ -388,8 +409,10 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
('manual', 'Manual'),
|
('manual', 'Manual'),
|
||||||
('payment_manual', 'Payment line (manual)'),
|
('payment_manual', 'Payment line (manual)'),
|
||||||
('payment_order_manual', 'Payment order (manual)'),
|
('payment_order_manual', 'Payment order (manual)'),
|
||||||
],
|
],
|
||||||
string='Match type', readonly=True),
|
string='Match type',
|
||||||
|
readonly=True,
|
||||||
|
),
|
||||||
'manual_invoice_ids': fields.many2many(
|
'manual_invoice_ids': fields.many2many(
|
||||||
'account.invoice',
|
'account.invoice',
|
||||||
'banking_transaction_wizard_account_invoice_rel',
|
'banking_transaction_wizard_account_invoice_rel',
|
||||||
@@ -401,8 +424,17 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
'wizard_id', 'move_line_id', string='Or match one or more entries',
|
'wizard_id', 'move_line_id', string='Or match one or more entries',
|
||||||
domain=[('account_id.reconcile', '=', True),
|
domain=[('account_id.reconcile', '=', True),
|
||||||
('reconcile_id', '=', False)]),
|
('reconcile_id', '=', False)]),
|
||||||
'payment_option': fields.related('import_transaction_id','payment_option', string='Payment Difference', type='selection', required=True,
|
'payment_option': fields.related(
|
||||||
selection=[('without_writeoff', 'Keep Open'),('with_writeoff', 'Reconcile Payment Balance')]),
|
'import_transaction_id',
|
||||||
|
'payment_option',
|
||||||
|
string='Payment Difference',
|
||||||
|
type='selection',
|
||||||
|
required=True,
|
||||||
|
selection=[
|
||||||
|
('without_writeoff', 'Keep Open'),
|
||||||
|
('with_writeoff', 'Reconcile Payment Balance')
|
||||||
|
],
|
||||||
|
),
|
||||||
'writeoff_analytic_id': fields.related(
|
'writeoff_analytic_id': fields.related(
|
||||||
'import_transaction_id', 'writeoff_analytic_id',
|
'import_transaction_id', 'writeoff_analytic_id',
|
||||||
type='many2one', relation='account.analytic.account',
|
type='many2one', relation='account.analytic.account',
|
||||||
@@ -411,9 +443,11 @@ class banking_transaction_wizard(orm.TransientModel):
|
|||||||
'statement_line_id', 'analytic_account_id',
|
'statement_line_id', 'analytic_account_id',
|
||||||
type='many2one', relation='account.analytic.account',
|
type='many2one', relation='account.analytic.account',
|
||||||
string="Analytic Account"),
|
string="Analytic Account"),
|
||||||
'move_currency_amount': fields.related('import_transaction_id','move_currency_amount',
|
'move_currency_amount': fields.related(
|
||||||
type='float', string='Match Currency Amount', readonly=True),
|
'import_transaction_id',
|
||||||
}
|
'move_currency_amount',
|
||||||
|
type='float',
|
||||||
banking_transaction_wizard()
|
string='Match Currency Amount',
|
||||||
|
readonly=True,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
@@ -24,7 +24,7 @@ from openerp.addons.account_banking import sepa
|
|||||||
from openerp.addons.account_banking.struct import struct
|
from openerp.addons.account_banking.struct import struct
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'get_period',
|
'get_period',
|
||||||
'get_bank_accounts',
|
'get_bank_accounts',
|
||||||
'get_partner',
|
'get_partner',
|
||||||
'get_country_id',
|
'get_country_id',
|
||||||
@@ -32,6 +32,7 @@ __all__ = [
|
|||||||
'create_bank_account',
|
'create_bank_account',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_period(pool, cr, uid, date, company, log=None):
|
def get_period(pool, cr, uid, date, company, log=None):
|
||||||
'''
|
'''
|
||||||
Wrapper over account_period.find() to log exceptions of
|
Wrapper over account_period.find() to log exceptions of
|
||||||
@@ -43,7 +44,7 @@ def get_period(pool, cr, uid, date, company, log=None):
|
|||||||
try:
|
try:
|
||||||
period_ids = pool.get('account.period').find(
|
period_ids = pool.get('account.period').find(
|
||||||
cr, uid, dt=date, context=context)
|
cr, uid, dt=date, context=context)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
if log is None:
|
if log is None:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
@@ -51,6 +52,7 @@ def get_period(pool, cr, uid, date, company, log=None):
|
|||||||
return False
|
return False
|
||||||
return period_ids[0]
|
return period_ids[0]
|
||||||
|
|
||||||
|
|
||||||
def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
|
def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
|
||||||
'''
|
'''
|
||||||
Get the bank account with account number account_number
|
Get the bank account with account number account_number
|
||||||
@@ -72,6 +74,7 @@ def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
|
|||||||
return []
|
return []
|
||||||
return partner_bank_obj.browse(cr, uid, bank_account_ids)
|
return partner_bank_obj.browse(cr, uid, bank_account_ids)
|
||||||
|
|
||||||
|
|
||||||
def _has_attr(obj, attr):
|
def _has_attr(obj, attr):
|
||||||
# Needed for dangling addresses and a weird exception scheme in
|
# Needed for dangling addresses and a weird exception scheme in
|
||||||
# OpenERP's orm.
|
# OpenERP's orm.
|
||||||
@@ -80,6 +83,7 @@ def _has_attr(obj, attr):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_partner(pool, cr, uid, name, address, postal_code, city,
|
def get_partner(pool, cr, uid, name, address, postal_code, city,
|
||||||
country_id, log, context=None):
|
country_id, log, context=None):
|
||||||
'''
|
'''
|
||||||
@@ -87,7 +91,7 @@ def get_partner(pool, cr, uid, name, address, postal_code, city,
|
|||||||
|
|
||||||
If multiple partners are found with the same name, select the first and
|
If multiple partners are found with the same name, select the first and
|
||||||
add a warning to the import log.
|
add a warning to the import log.
|
||||||
|
|
||||||
TODO: revive the search by lines from the address argument
|
TODO: revive the search by lines from the address argument
|
||||||
'''
|
'''
|
||||||
partner_obj = pool.get('res.partner')
|
partner_obj = pool.get('res.partner')
|
||||||
@@ -115,7 +119,8 @@ def get_partner(pool, cr, uid, name, address, postal_code, city,
|
|||||||
key = name.lower()
|
key = name.lower()
|
||||||
partners = []
|
partners = []
|
||||||
for partner in partner_obj.read(
|
for partner in partner_obj.read(
|
||||||
cr, uid, partner_search_ids, ['name', 'commercial_partner_id'], context=context):
|
cr, uid, partner_search_ids, ['name', 'commercial_partner_id'],
|
||||||
|
context=context):
|
||||||
if (len(partner['name']) > 3 and partner['name'].lower() in key):
|
if (len(partner['name']) > 3 and partner['name'].lower() in key):
|
||||||
partners.append(partner)
|
partners.append(partner)
|
||||||
partners.sort(key=lambda x: len(x['name']), reverse=True)
|
partners.sort(key=lambda x: len(x['name']), reverse=True)
|
||||||
@@ -126,6 +131,7 @@ def get_partner(pool, cr, uid, name, address, postal_code, city,
|
|||||||
'name %(name)s') % {'name': name})
|
'name %(name)s') % {'name': name})
|
||||||
return partner_ids and partner_ids[0] or False
|
return partner_ids and partner_ids[0] or False
|
||||||
|
|
||||||
|
|
||||||
def get_company_bank_account(pool, cr, uid, account_number, currency,
|
def get_company_bank_account(pool, cr, uid, account_number, currency,
|
||||||
company, log):
|
company, log):
|
||||||
'''
|
'''
|
||||||
@@ -139,16 +145,16 @@ def get_company_bank_account(pool, cr, uid, account_number, currency,
|
|||||||
return False
|
return False
|
||||||
elif len(bank_accounts) != 1:
|
elif len(bank_accounts) != 1:
|
||||||
log.append(
|
log.append(
|
||||||
_('More than one bank account was found with the same number %(account_no)s')
|
_('More than one bank account was found with the same number '
|
||||||
% dict(account_no = account_number)
|
'%(account_no)s') % dict(account_no=account_number)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
if bank_accounts[0].partner_id.id != company.partner_id.id:
|
if bank_accounts[0].partner_id.id != company.partner_id.id:
|
||||||
log.append(
|
log.append(
|
||||||
_('Account %(account_no)s is not owned by %(partner)s')
|
_('Account %(account_no)s is not owned by %(partner)s')
|
||||||
% dict(account_no = account_number,
|
% dict(account_no=account_number,
|
||||||
partner = company.partner_id.name,
|
partner=company.partner_id.name,
|
||||||
))
|
))
|
||||||
return False
|
return False
|
||||||
results.account = bank_accounts[0]
|
results.account = bank_accounts[0]
|
||||||
bank_settings_obj = pool.get('account.banking.account.settings')
|
bank_settings_obj = pool.get('account.banking.account.settings')
|
||||||
@@ -189,8 +195,9 @@ def get_company_bank_account(pool, cr, uid, account_number, currency,
|
|||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
|
def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
|
||||||
name=None):
|
name=None, context=None):
|
||||||
'''
|
'''
|
||||||
Find or create the bank with the provided BIC code.
|
Find or create the bank with the provided BIC code.
|
||||||
When online, the SWIFT database will be consulted in order to
|
When online, the SWIFT database will be consulted in order to
|
||||||
@@ -231,38 +238,41 @@ def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
|
|||||||
bank_id = False
|
bank_id = False
|
||||||
|
|
||||||
if online:
|
if online:
|
||||||
info, address = bank_obj.online_bank_info(cr, uid, bic, context=context)
|
info, address = bank_obj.online_bank_info(
|
||||||
|
cr, uid, bic, context=context
|
||||||
|
)
|
||||||
if info:
|
if info:
|
||||||
bank_id = bank_obj.create(cr, uid, dict(
|
bank_id = bank_obj.create(cr, uid, dict(
|
||||||
code = info.code,
|
code=info.code,
|
||||||
name = info.name,
|
name=info.name,
|
||||||
street = address.street,
|
street=address.street,
|
||||||
street2 = address.street2,
|
street2=address.street2,
|
||||||
zip = address.zip,
|
zip=address.zip,
|
||||||
city = address.city,
|
city=address.city,
|
||||||
country = country_id,
|
country=country_id,
|
||||||
bic = info.bic[:8],
|
bic=info.bic[:8],
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
info = struct(name=name, code=code)
|
info = struct(name=name, code=code)
|
||||||
|
|
||||||
if not online or not bank_id:
|
if not online or not bank_id:
|
||||||
bank_id = bank_obj.create(cr, uid, dict(
|
bank_id = bank_obj.create(cr, uid, dict(
|
||||||
code = info.code or 'UNKNOW',
|
code=info.code or 'UNKNOW', # FIXME: Typo?
|
||||||
name = info.name or _('Unknown Bank'),
|
name=info.name or _('Unknown Bank'),
|
||||||
country = country_id,
|
country=country_id,
|
||||||
bic = bic,
|
bic=bic,
|
||||||
))
|
))
|
||||||
return bank_id, country_id
|
return bank_id, country_id
|
||||||
|
|
||||||
|
|
||||||
def get_country_id(pool, cr, uid, transaction, context=None):
|
def get_country_id(pool, cr, uid, transaction, context=None):
|
||||||
"""
|
"""
|
||||||
Derive a country id from the info on the transaction.
|
Derive a country id from the info on the transaction.
|
||||||
|
|
||||||
:param transaction: browse record of a transaction
|
:param transaction: browse record of a transaction
|
||||||
:returns: res.country id or False
|
:returns: res.country id or False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
country_code = False
|
country_code = False
|
||||||
iban = sepa.IBAN(transaction.remote_account)
|
iban = sepa.IBAN(transaction.remote_account)
|
||||||
if iban.valid:
|
if iban.valid:
|
||||||
@@ -283,6 +293,7 @@ def get_country_id(pool, cr, uid, transaction, context=None):
|
|||||||
country_id = company.partner_id.country.id
|
country_id = company.partner_id.country.id
|
||||||
return country_id
|
return country_id
|
||||||
|
|
||||||
|
|
||||||
def create_bank_account(pool, cr, uid, partner_id,
|
def create_bank_account(pool, cr, uid, partner_id,
|
||||||
account_number, holder_name, address, city,
|
account_number, holder_name, address, city,
|
||||||
country_id, bic=False,
|
country_id, bic=False,
|
||||||
@@ -291,9 +302,9 @@ def create_bank_account(pool, cr, uid, partner_id,
|
|||||||
Create a matching bank account with this holder for this partner.
|
Create a matching bank account with this holder for this partner.
|
||||||
'''
|
'''
|
||||||
values = struct(
|
values = struct(
|
||||||
partner_id = partner_id,
|
partner_id=partner_id,
|
||||||
owner_name = holder_name,
|
owner_name=holder_name,
|
||||||
country_id = country_id,
|
country_id=country_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Are we dealing with IBAN?
|
# Are we dealing with IBAN?
|
||||||
@@ -325,5 +336,3 @@ def create_bank_account(pool, cr, uid, partner_id,
|
|||||||
# Create bank account and return
|
# Create bank account and return
|
||||||
return pool.get('res.partner.bank').create(
|
return pool.get('res.partner.bank').create(
|
||||||
cr, uid, values, context=context)
|
cr, uid, values, context=context)
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from openerp.tools.translate import _
|
|||||||
from openerp.addons.account_banking.wizard import banktools
|
from openerp.addons.account_banking.wizard import banktools
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
|
|
||||||
class link_partner(orm.TransientModel):
|
class link_partner(orm.TransientModel):
|
||||||
_name = 'banking.link_partner'
|
_name = 'banking.link_partner'
|
||||||
_description = 'Link partner'
|
_description = 'Link partner'
|
||||||
@@ -66,14 +67,14 @@ class link_partner(orm.TransientModel):
|
|||||||
'mobile': fields.char('Mobile', size=64),
|
'mobile': fields.char('Mobile', size=64),
|
||||||
'is_company': fields.boolean('Is a Company'),
|
'is_company': fields.boolean('Is a Company'),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'is_company': True,
|
'is_company': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def create(self, cr, uid, vals, context=None):
|
def create(self, cr, uid, vals, context=None):
|
||||||
"""
|
"""
|
||||||
Get default values from the transaction data
|
Get default values from the transaction data
|
||||||
on the statement line
|
on the statement line
|
||||||
"""
|
"""
|
||||||
if vals and vals.get('statement_line_id'):
|
if vals and vals.get('statement_line_id'):
|
||||||
@@ -86,7 +87,7 @@ class link_partner(orm.TransientModel):
|
|||||||
raise orm.except_orm(
|
raise orm.except_orm(
|
||||||
_('Error'),
|
_('Error'),
|
||||||
_('Statement line is already linked to a bank account '))
|
_('Statement line is already linked to a bank account '))
|
||||||
|
|
||||||
if not(transaction
|
if not(transaction
|
||||||
and transaction.remote_account):
|
and transaction.remote_account):
|
||||||
raise orm.except_orm(
|
raise orm.except_orm(
|
||||||
@@ -124,17 +125,17 @@ class link_partner(orm.TransientModel):
|
|||||||
|
|
||||||
return super(link_partner, self).create(
|
return super(link_partner, self).create(
|
||||||
cr, uid, vals, context=context)
|
cr, uid, vals, context=context)
|
||||||
|
|
||||||
def update_partner_values(self, cr, uid, wizard, values, context=None):
|
def update_partner_values(self, cr, uid, wizard, values, context=None):
|
||||||
"""
|
"""
|
||||||
Updates the new partner values with the values from the wizard
|
Updates the new partner values with the values from the wizard
|
||||||
|
|
||||||
:param wizard: read record of wizard (with load='_classic_write')
|
:param wizard: read record of wizard (with load='_classic_write')
|
||||||
:param values: the dictionary of partner values that will be updated
|
:param values: the dictionary of partner values that will be updated
|
||||||
"""
|
"""
|
||||||
for field in ['is_company',
|
for field in ['is_company',
|
||||||
'name',
|
'name',
|
||||||
'street',
|
'street',
|
||||||
'street2',
|
'street2',
|
||||||
'zip',
|
'zip',
|
||||||
'city',
|
'city',
|
||||||
@@ -148,7 +149,7 @@ class link_partner(orm.TransientModel):
|
|||||||
if wizard[field]:
|
if wizard[field]:
|
||||||
values[field] = wizard[field]
|
values[field] = wizard[field]
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def link_partner(self, cr, uid, ids, context=None):
|
def link_partner(self, cr, uid, ids, context=None):
|
||||||
statement_line_obj = self.pool.get(
|
statement_line_obj = self.pool.get(
|
||||||
'account.bank.statement.line')
|
'account.bank.statement.line')
|
||||||
@@ -160,13 +161,13 @@ class link_partner(orm.TransientModel):
|
|||||||
wiz_read = self.read(
|
wiz_read = self.read(
|
||||||
cr, uid, ids[0], context=context, load='_classic_write')
|
cr, uid, ids[0], context=context, load='_classic_write')
|
||||||
partner_vals = {
|
partner_vals = {
|
||||||
'type': 'default',
|
'type': 'default',
|
||||||
}
|
}
|
||||||
self.update_partner_values(
|
self.update_partner_values(
|
||||||
cr, uid, wiz_read, partner_vals, context=context)
|
cr, uid, wiz_read, partner_vals, context=context)
|
||||||
partner_id = self.pool.get('res.partner').create(
|
partner_id = self.pool.get('res.partner').create(
|
||||||
cr, uid, partner_vals, context=context)
|
cr, uid, partner_vals, context=context)
|
||||||
|
|
||||||
partner_bank_id = banktools.create_bank_account(
|
partner_bank_id = banktools.create_bank_account(
|
||||||
self.pool, cr, uid, partner_id,
|
self.pool, cr, uid, partner_id,
|
||||||
wiz.remote_account, wiz.name,
|
wiz.remote_account, wiz.name,
|
||||||
@@ -185,10 +186,10 @@ class link_partner(orm.TransientModel):
|
|||||||
{'partner_bank_id': partner_bank_id,
|
{'partner_bank_id': partner_bank_id,
|
||||||
'partner_id': partner_id}, context=context)
|
'partner_id': partner_id}, context=context)
|
||||||
|
|
||||||
return {'type': 'ir.actions.act_window_close'}
|
return {'type': 'ir.actions.act_window_close'}
|
||||||
|
|
||||||
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None):
|
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None):
|
||||||
"""
|
"""
|
||||||
Return a popup window for this model
|
Return a popup window for this model
|
||||||
"""
|
"""
|
||||||
if isinstance(ids, (int, long)):
|
if isinstance(ids, (int, long)):
|
||||||
@@ -205,5 +206,3 @@ class link_partner(orm.TransientModel):
|
|||||||
'res_id': ids[0],
|
'res_id': ids[0],
|
||||||
'nodestroy': nodestroy,
|
'nodestroy': nodestroy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user