diff --git a/account_banking/__init__.py b/account_banking/__init__.py index 0640b6cc6..b4ebb7caf 100644 --- a/account_banking/__init__.py +++ b/account_banking/__init__.py @@ -11,16 +11,16 @@ # garantees and support are strongly adviced to contract EduSense BV # # This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by +# it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU Affero General Public License for more details. # -# You should have received a copy of the GNU General Public License +# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## diff --git a/account_banking/__openerp__.py b/account_banking/__openerp__.py index 552803eb6..81bc377d5 100644 --- a/account_banking/__openerp__.py +++ b/account_banking/__openerp__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ############################################################################## # # Copyright (C) 2009 EduSense BV (). @@ -8,46 +9,40 @@ # # All Rights Reserved # -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract EduSense BV -# # This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU Affero General Public License for more details. # -# You should have received a copy of the GNU General Public License +# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## + { 'name': 'Account Banking', 'version': '0.1.136', - 'license': 'GPL-3', + 'license': 'AGPL-3', 'author': 'Banking addons community', 'website': 'https://launchpad.net/banking-addons', 'category': 'Banking addons', - 'depends': ['base', 'account', 'base_iban', 'account_payment', - 'account_iban_preserve_domestic'], - 'init_xml': [], - 'update_xml': [ + 'depends': [ + 'account_voucher', + 'account_iban_preserve_domestic', + ], + 'data': [ 'security/ir.model.access.csv', 'data/account_banking_data.xml', 'wizard/bank_import_view.xml', 'account_banking_view.xml', - 'account_banking_workflow.xml', 'wizard/banking_transaction_wizard.xml', 'workflow/account_invoice.xml', ], - 'demo_xml': [], 'external_dependencies': { 'python' : ['BeautifulSoup'], }, @@ -106,6 +101,5 @@ + No special configuration needed for the parsers, new parsers are recognized and made available at server (re)start. ''', - 'active': False, - 'installable': False, + 'installable': True, } diff --git a/account_banking/account_banking.py b/account_banking/account_banking.py index 7f68b1cca..8a6dd055f 100644 --- a/account_banking/account_banking.py +++ b/account_banking/account_banking.py @@ -1,20 +1,24 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # # Copyright (C) 2009 EduSense BV (). +# (C) 2011 - 2013 Therp BV (). +# +# All other contributions are (C) by their respective contributors +# # All Rights Reserved # # This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU Affero General Public License for more details. # -# You should have received a copy of the GNU General Public License +# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## @@ -57,20 +61,20 @@ Modifications are extensive: default behavior is to flag the orders as 'sent', not as 'done'. Rejected payments from the bank receive on import the status 'rejected'. ''' -import time -import sepa -from osv import osv, fields -from tools.translate import _ -from wizard.banktools import get_or_create_bank -import decimal_precision as dp -import netsvc -from openerp import SUPERUSER_ID + +from openerp.osv import orm, fields +from openerp.tools.translate import _ +from openerp import netsvc, SUPERUSER_ID +from openerp.addons.decimal_precision import decimal_precision as dp +from openerp.addons.account_banking import sepa +from openerp.addons.account_banking.wizard.banktools import get_or_create_bank def warning(title, message): '''Convenience routine''' return {'warning': {'title': title, 'message': message}} -class account_banking_account_settings(osv.osv): + +class account_banking_account_settings(orm.Model): '''Default Journal for Bank Account''' _name = 'account.banking.account.settings' _description = __doc__ @@ -122,13 +126,6 @@ class account_banking_account_settings(osv.osv): ), ), - #'multi_currency': fields.boolean( - # 'Multi Currency Bank Account', required=True, - # help=('Select this if your bank account is able to handle ' - # 'multiple currencies in parallel without coercing to ' - # 'a single currency.' - # ), - #), } def _default_company(self, cr, uid, context=None): @@ -162,7 +159,8 @@ class account_banking_account_settings(osv.osv): if not company_id: company_id = self._default_company(cr, uid, context=context) partner_id = self.pool.get('res.company').read( - cr, uid, company_id, ['partner_id'], context=context)['partner_id'][0] + cr, uid, company_id, ['partner_id'], + context=context)['partner_id'][0] bank_ids = self.pool.get('res.partner.bank').search( cr, uid, [('partner_id', '=', partner_id)], context=context) return bank_ids and bank_ids[0] or False @@ -177,7 +175,8 @@ class account_banking_account_settings(osv.osv): 'res.partner', context=localcontext) return account_def and account_def.id or False - def _default_credit_account_id(self, cr, uid, context=None, company_id=False): + def _default_credit_account_id( + self, cr, uid, context=None, company_id=False): localcontext = context and context.copy() or {} localcontext['force_company'] = ( company_id or self._default_company(cr, uid, context=context)) @@ -187,9 +186,9 @@ class account_banking_account_settings(osv.osv): return account_def and account_def.id or False def find(self, cr, uid, journal_id, partner_bank_id=False, context=None): - domain = [('journal_id','=',journal_id)] + domain = [('journal_id', '=', journal_id)] if partner_bank_id: - domain.append(('partner_bank_id','=',partner_bank_id)) + domain.append(('partner_bank_id', '=', partner_bank_id)) return self.search(cr, uid, domain, context=context) def onchange_partner_bank_id( @@ -225,11 +224,11 @@ class account_banking_account_settings(osv.osv): 'default_debit_account_id': _default_debit_account_id, 'default_credit_account_id': _default_credit_account_id, 'partner_bank_id': _default_partner_bank_id, - #'multi_currency': lambda *a: False, } account_banking_account_settings() -class account_banking_imported_file(osv.osv): + +class account_banking_imported_file(orm.Model): '''Imported Bank Statements File''' _name = 'account.banking.imported.file' _description = __doc__ @@ -267,74 +266,13 @@ class account_banking_imported_file(osv.osv): ), } _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'date': fields.date.context_today, 'user_id': lambda self, cursor, uid, context: uid, } account_banking_imported_file() -class payment_mode_type(osv.osv): - _name= 'payment.mode.type' - _description= 'Payment Mode Type' - _columns= { - 'name': fields.char( - 'Name', size=64, required=True, - help='Payment Type' - ), - 'code': fields.char( - 'Code', size=64, required=True, - help='Specify the Code for Payment Type' - ), - # Setting suitable_bank_types to required pending - # https://bugs.launchpad.net/openobject-addons/+bug/786845 - 'suitable_bank_types': fields.many2many( - 'res.partner.bank.type', - 'bank_type_payment_type_rel', - 'pay_type_id','bank_type_id', - 'Suitable bank types', required=True), - 'ir_model_id': fields.many2one( - 'ir.model', 'Payment wizard', - help=('Select the Payment Wizard for payments of this type. ' - 'Leave empty for manual processing'), - domain=[('osv_memory', '=', True)], - ), - 'payment_order_type': fields.selection( - [('payment', 'Payment'),('debit', 'Direct debit')], - 'Payment order type', required=True, - ), - } - _defaults = { - 'payment_order_type': lambda *a: 'payment', - } - -payment_mode_type() - -class payment_mode(osv.osv): - ''' Restoring the payment type from version 5, - used to select the export wizard (if any) ''' - _inherit = "payment.mode" - - def suitable_bank_types(self, cr, uid, payment_mode_id=None, context=None): - """ Reinstates functional code for suitable bank type filtering. - Current code in account_payment is disfunctional. - """ - res = [] - payment_mode = self.browse( - cr, uid, payment_mode_id, context) - if (payment_mode and payment_mode.type and - payment_mode.type.suitable_bank_types): - res = [type.code for type in payment_mode.type.suitable_bank_types] - return res - - _columns = { - 'type': fields.many2one( - 'payment.mode.type', 'Payment type', - help='Select the Payment Type for the Payment Mode.' - ), - } -payment_mode() - -class account_bank_statement(osv.osv): +class account_bank_statement(orm.Model): ''' Extensions from account_bank_statement: 1. Removed period_id (transformed to optional boolean) - as it is no @@ -347,45 +285,6 @@ class account_bank_statement(osv.osv): ''' _inherit = 'account.bank.statement' _order = 'id' - _abf_others = [] - _abf_others_loaded = False - - def __init__(self, *args, **kwargs): - ''' - See where we stand in the order of things - ''' - super(account_bank_statement, self).__init__(*args, **kwargs) - if not self._abf_others_loaded: - self._abf_others_loaded = True - self._abf_others = [x for x in self.__class__.__mro__ - if x.__module__.split('.')[0] not in [ - 'osv', 'account', 'account_banking', - '__builtin__' - ] - ] - - #def _currency(self, cursor, user, ids, name, args, context=None): - # ''' - # Calculate currency from contained transactions - # ''' - # res = {} - # res_currency_obj = self.pool.get('res.currency') - # res_users_obj = self.pool.get('res.users') - # default_currency = res_users_obj.browse(cursor, user, - # user, context=context).company_id.currency_id - # for statement in self.browse(cursor, user, ids, context=context): - # currency = statement.journal_id.currency - # if not currency: - # currency = default_currency - # res[statement.id] = currency.id - # currency_names = {} - # for currency_id, currency_name in res_currency_obj.name_get(cursor, - # user, res.values(), context=context): - # currency_names[currency_id] = currency_name - # for statement_id in res.keys(): - # currency_id = res[statement_id] - # res[statement_id] = (currency_id, currency_names[currency_id]) - # return res _columns = { 'period_id': fields.many2one('account.period', 'Period', @@ -393,13 +292,10 @@ class account_bank_statement(osv.osv): 'banking_id': fields.many2one('account.banking.imported.file', 'Imported File', readonly=True, ), - # 'currency': fields.function(_currency, method=True, string='Currency', - # type='many2one', relation='res.currency'), } _defaults = { - 'period_id': lambda *a: False, - # 'currency': _currency, + 'period_id': False, } def _check_company_id(self, cr, uid, ids, context=None): @@ -408,19 +304,21 @@ class account_bank_statement(osv.osv): move of period_id to the statement line """ for statement in self.browse(cr, uid, ids, context=context): - if (statement.period_id and - statement.company_id.id != statement.period_id.company_id.id): - return False for line in statement.line_ids: if (line.period_id and statement.company_id.id != line.period_id.company_id.id): return False - return True + if not statement.period_id: + statement.write({'period_id': line.period_id.id}) + return super(account_bank_statement, self)._check_company_id( + cr, uid, ids, context=context) # Redefine the constraint, or it still refer to the original method _constraints = [ - (_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']), - ] + (_check_company_id, + 'The journal and period chosen have to belong to the same company.', + ['journal_id','period_id']), + ] def _get_period(self, cursor, uid, date, context=None): ''' @@ -430,231 +328,141 @@ class account_bank_statement(osv.osv): periods = period_obj.find(cursor, uid, dt=date, context=context) return periods and periods[0] or False - #def compute(self, cursor, uid, ids, context=None): - # ''' - # Compute start and end balance with mixed currencies. - # ''' - # return None + def _prepare_move( + self, cr, uid, st_line, st_line_number, context=None): + """ + Add the statement line's period to the move, overwriting + the period on the statement + """ + res = super(account_bank_statement, self)._prepare_move( + cr, uid, st_line, st_line_number, context=context) + if context and context.get('period_id'): + res['period_id'] = context['period_id'] + return res + + def _prepare_move_line_vals( + self, cr, uid, st_line, move_id, debit, credit, currency_id=False, + amount_currency=False, account_id=False, analytic_id=False, + partner_id=False, context=None): + """ + Add the statement line's period to the move lines, overwriting + the period on the statement + """ + res = super(account_bank_statement, self)._prepare_move_line_vals( + cr, uid, st_line, move_id, debit, credit, currency_id=currency_id, + amount_currency=amount_currency, account_id=account_id, + analytic_id=analytic_id, partner_id=partner_id, context=context) + if context and context.get('period_id'): + res['period_id'] = context['period_id'] + return res def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, st_line_number, context=None): - # This is largely a copy of the original code in account - # Modifications are marked with AB - # Modifications by account_voucher are merged below. - # As there is no valid inheritance mechanism for large actions, this - # is the only option to add functionality to existing actions. - # WARNING: when the original code changes, this trigger has to be - # updated in sync. - if context is None: context = {} - res_currency_obj = self.pool.get('res.currency') account_move_obj = self.pool.get('account.move') account_move_line_obj = self.pool.get('account.move.line') account_bank_statement_line_obj = self.pool.get( 'account.bank.statement.line') st_line = account_bank_statement_line_obj.browse( cr, uid, st_line_id, context=context) + + # Take period from statement line and write to context + # this will be picked up by the _prepare_move* methods period_id = self._get_period( - cr, uid, st_line.date, context=context) # AB - # Start account voucher - # Post the voucher and update links between statement and moves + cr, uid, st_line.date, context=context) + localctx = context.copy() + localctx['period_id'] = period_id + + # Write date & period on the voucher, delegate to account_voucher's + # override of this method. Then post the related move and return. if st_line.voucher_id: voucher_pool = self.pool.get('account.voucher') - wf_service = netsvc.LocalService("workflow") voucher_pool.write( cr, uid, [st_line.voucher_id.id], { - 'number': st_line_number, 'date': st_line.date, - 'period_id': period_id, # AB + 'period_id': period_id, }, context=context) - if st_line.voucher_id.state == 'cancel': - voucher_pool.action_cancel_draft( - cr, uid, [st_line.voucher_id.id], context=context) - wf_service.trg_validate( - uid, 'account.voucher', st_line.voucher_id.id, 'proforma_voucher', cr) - v = voucher_pool.browse( - cr, uid, st_line.voucher_id.id, context=context) - account_bank_statement_line_obj.write(cr, uid, [st_line_id], { - 'move_ids': [(4, v.move_id.id, False)] - }) - account_move_line_obj.write( - cr, uid, [x.id for x in v.move_ids], - {'statement_id': st_line.statement_id.id}, context=context) - # End of account_voucher - st_line.refresh() - # AB: The voucher journal isn't automatically posted, so post it (if needed) + res = super(account_bank_statement, self).create_move_from_st_line( + cr, uid, st_line_id, company_currency_id, st_line_number, + context=localctx) + + st_line.refresh() + if st_line.voucher_id: if not st_line.voucher_id.journal_id.entry_posted: - account_move_obj.post(cr, uid, [st_line.voucher_id.move_id.id], context={}) - return True - - st = st_line.statement_id - - context.update({'date': st_line.date}) - ctxt = context.copy() # AB - ctxt['company_id'] = st_line.company_id.id # AB - - move_id = account_move_obj.create(cr, uid, { - 'journal_id': st.journal_id.id, - 'period_id': period_id, # AB - 'date': st_line.date, - 'name': st_line_number, - }, context=context) - account_bank_statement_line_obj.write(cr, uid, [st_line.id], { - 'move_ids': [(4, move_id, False)] - }) - - torec = [] - if st_line.amount >= 0: - account_id = st.journal_id.default_credit_account_id.id + account_move_obj.post( + cr, uid, [st_line.voucher_id.move_id.id], context={}) else: - account_id = st.journal_id.default_debit_account_id.id - - acc_cur = ((st_line.amount <= 0 and - st.journal_id.default_debit_account_id) or - st_line.account_id) - context.update({ - 'res.currency.compute.account': acc_cur, - }) - amount = res_currency_obj.compute(cr, uid, st.currency.id, - company_currency_id, st_line.amount, context=context) - - val = { - 'name': st_line.name, - 'date': st_line.date, - 'ref': st_line.ref, - 'move_id': move_id, - 'partner_id': (((st_line.partner_id) and st_line.partner_id.id) or - False), - 'account_id': (st_line.account_id) and st_line.account_id.id, - 'credit': ((amount>0) and amount) or 0.0, - 'debit': ((amount<0) and -amount) or 0.0, - 'statement_id': st.id, - 'journal_id': st.journal_id.id, - 'period_id': period_id, # AB - 'currency_id': st.currency.id, - 'analytic_account_id': (st_line.analytic_account_id and - st_line.analytic_account_id.id or - False), - } - - if st.currency.id <> company_currency_id: - amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, - st.currency.id, amount, context=context) - val['amount_currency'] = -amount_cur - - if (st_line.account_id and st_line.account_id.currency_id and - st_line.account_id.currency_id.id <> company_currency_id): - val['currency_id'] = st_line.account_id.currency_id.id - amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, - st_line.account_id.currency_id.id, amount, context=context) - val['amount_currency'] = -amount_cur - - move_line_id = account_move_line_obj.create( - cr, uid, val, context=context) - torec.append(move_line_id) - - # Fill the secondary amount/currency - # if currency is not the same than the company - amount_currency = False - currency_id = False - if st.currency.id <> company_currency_id: - amount_currency = st_line.amount - currency_id = st.currency.id - account_move_line_obj.create(cr, uid, { - 'name': st_line.name, - 'date': st_line.date, - 'ref': st_line.ref, - 'move_id': move_id, - 'partner_id': (((st_line.partner_id) and st_line.partner_id.id) or - False), - 'account_id': account_id, - 'credit': ((amount < 0) and -amount) or 0.0, - 'debit': ((amount > 0) and amount) or 0.0, - 'statement_id': st.id, - 'journal_id': st.journal_id.id, - 'period_id': period_id, # AB - 'amount_currency': amount_currency, - 'currency_id': currency_id, - }, context=context) - - for line in account_move_line_obj.browse(cr, uid, [x.id for x in - account_move_obj.browse(cr, uid, move_id, - context=context).line_id], - context=context): - if line.state <> 'valid': - raise osv.except_osv(_('Error !'), - _('Journal Item "%s" is not valid') % line.name) - - # Bank statements will not consider boolean on journal entry_posted - account_move_obj.post(cr, uid, [move_id], context=context) - - """ - Account-banking: - - Write stored reconcile_id - - Pay invoices through workflow - - Does not apply to voucher integration, but only to - payments and payment orders - """ - if st_line.reconcile_id: - account_move_line_obj.write(cr, uid, torec, { - (st_line.reconcile_id.line_partial_ids and - 'reconcile_partial_id' or 'reconcile_id'): - st_line.reconcile_id.id }, context=context) - for move_line in (st_line.reconcile_id.line_id or []) + ( - st_line.reconcile_id.line_partial_ids or []): - netsvc.LocalService("workflow").trg_trigger( - uid, 'account.move.line', move_line.id, cr) - #""" End account-banking """ - - return move_id + # Write stored reconcile_id and pay invoices through workflow + if st_line.reconcile_id: + move_ids = [move.id for move in st_line.move_ids] + torec = account_move_obj.search( + cr, uid, [ + ('move_id', 'in', move_ids), + ('account_id', '=', st_line.account_id.id)], + context=context) + account_move_line_obj.write(cr, uid, torec, { + (st_line.reconcile_id.line_partial_ids and + 'reconcile_partial_id' or 'reconcile_id'): + st_line.reconcile_id.id }, context=context) + for move_line in (st_line.reconcile_id.line_id or []) + ( + st_line.reconcile_id.line_partial_ids or []): + netsvc.LocalService("workflow").trg_trigger( + uid, 'account.move.line', move_line.id, cr) + return res def button_confirm_bank(self, cr, uid, ids, context=None): - if context is None: context = {} + """ + Assign journal sequence to statements without a name + """ + if context is None: + context = {} obj_seq = self.pool.get('ir.sequence') - if not isinstance(ids, list): ids = [ids] - noname_ids = self.search(cr, uid, [('id','in',ids),('name','=','/')]) + if ids and isinstance(ids, (int, long)): + ids = [ids] + noname_ids = self.search( + cr, uid, [('id', 'in', ids),('name', '=', '/')], + context=context) for st in self.browse(cr, uid, noname_ids, context=context): - if st.journal_id.sequence_id: - year = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, st.date)).fiscalyear_id.id - c = {'fiscalyear_id': year} - st_number = obj_seq.get_id(cr, uid, st.journal_id.sequence_id.id, context=c) - self.write(cr, uid, ids, {'name': st_number}) + if st.journal_id.sequence_id: + period_id = self._get_period(cr, uid, st.date) + year = self.pool.get('account.period').browse( + cr, uid, period_id, context=context).fiscalyear_id.id + c = {'fiscalyear_id': year} + st_number = obj_seq.get_id( + cr, uid, st.journal_id.sequence_id.id, context=c) + self.write( + cr, uid, ids, {'name': st_number}, context=context) - return super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, context) + return super(account_bank_statement, self).button_confirm_bank( + cr, uid, ids, context) account_bank_statement() -class account_voucher(osv.osv): + +class account_voucher(orm.Model): _inherit = 'account.voucher' def _get_period(self, cr, uid, context=None): - if context is None: context = {} + if context is None: + context = {} if not context.get('period_id') and context.get('move_line_ids'): - res = self.pool.get('account.move.line').browse(cr, uid , context.get('move_line_ids'))[0].period_id.id - context['period_id'] = res + return self.pool.get('account.move.line').browse( + cr, uid , context.get('move_line_ids'))[0].period_id.id return super(account_voucher, self)._get_period(cr, uid, context) - def create(self, cr, uid, values, context=None): - if values.get('period_id') == False and context.get('move_line_ids'): - values['period_id'] = self._get_period(cr, uid, context) - return super(account_voucher, self).create(cr, uid, values, context) - account_voucher() -class account_bank_statement_line(osv.osv): + +class account_bank_statement_line(orm.Model): ''' Extension on basic class: 1. Extra links to account.period and res.partner.bank for tracing and matching. 2. Extra 'trans' field to carry the transaction id of the bank. - 3. Extra 'international' flag to indicate the missing of a remote - account number. Some banks use seperate international banking - modules that do not integrate with the standard transaction files. - 4. Readonly states for most fields except when in draft. + 3. Readonly states for most fields except when in draft. ''' _inherit = 'account.bank.statement.line' _description = 'Bank Transaction' @@ -664,26 +472,6 @@ class account_bank_statement_line(osv.osv): periods = self.pool.get('account.period').find(cursor, user, dt=date) return periods and periods[0] or False - def _seems_international(self, cursor, user, context=None): - ''' - Some banks have seperate international banking modules which do not - translate correctly into the national formats. Instead, they - leave key fields blank and signal this anomaly with a special - transfer type. - With the introduction of SEPA, this may worsen greatly, as SEPA - payments are considered to be analogous to international payments - by most local formats. - ''' - # Quick and dirty check: if remote bank account is missing, assume - # international transfer - return not ( - context.get('partner_bank_id') and context['partner_bank_id'] - ) - # Not so dirty check: check if partner_id is set. If it is, check the - # default/invoice addresses country. If it is the same as our - # company's, its local, else international. - # TODO: to be done - def _get_currency(self, cursor, user, context=None): ''' Get the default currency (required to allow other modules to function, @@ -695,30 +483,6 @@ class account_bank_statement_line(osv.osv): return res_users_obj.browse(cursor, user, user, context=context).company_id.currency_id.id - #def _reconcile_amount(self, cursor, user, ids, name, args, context=None): - # ''' - # Redefinition from the original: don't use the statements currency, but - # the transactions currency. - # ''' - # if not ids: - # return {} - - # res_currency_obj = self.pool.get('res.currency') - # res_users_obj = self.pool.get('res.users') - - # res = {} - # company_currency_id = res_users_obj.browse(cursor, user, user, - # context=context).company_id.currency_id.id - - # for line in self.browse(cursor, user, ids, context=context): - # if line.reconcile_id: - # res[line.id] = res_currency_obj.compute(cursor, user, - # company_currency_id, line.currency.id, - # line.reconcile_id.total_entry, context=context) - # else: - # res[line.id] = 0.0 - # return res - def _get_invoice_id(self, cr, uid, ids, name, args, context=None): res = {} for st_line in self.browse(cr, uid, ids, context): @@ -736,7 +500,7 @@ class account_bank_statement_line(osv.osv): return res _columns = { - # Redefines + # Redefines. Todo: refactor away to view attrs 'amount': fields.float('Amount', readonly=True, digits_compute=dp.get_precision('Account'), states={'draft': [('readonly', False)]}), @@ -746,8 +510,6 @@ class account_bank_statement_line(osv.osv): states={'draft': [('readonly', False)]}), 'date': fields.date('Date', required=True, readonly=True, states={'draft': [('readonly', False)]}), - #'reconcile_amount': fields.function(_reconcile_amount, - # string='Amount reconciled', method=True, type='float'), # New columns 'trans': fields.char('Bank Transaction ID', size=15, required=False, @@ -762,12 +524,6 @@ class account_bank_statement_line(osv.osv): states={'confirmed': [('readonly', True)]}), 'currency': fields.many2one('res.currency', 'Currency', required=True, states={'confirmed': [('readonly', True)]}), - - # Not used yet, but usefull in the future. - 'international': fields.boolean('International Transaction', - required=False, - states={'confirmed': [('readonly', True)]}, - ), 'reconcile_id': fields.many2one( 'account.move.reconcile', 'Reconciliation', readonly=True ), @@ -779,427 +535,13 @@ class account_bank_statement_line(osv.osv): _defaults = { 'period_id': _get_period, - 'international': _seems_international, 'currency': _get_currency, } account_bank_statement_line() -class payment_line(osv.osv): - ''' - Add extra export_state and date_done fields; make destination bank account - mandatory, as it makes no sense to send payments into thin air. - Edit: Payments can be by cash too, which is prohibited by mandatory bank - accounts. - ''' - _inherit = 'payment.line' - _columns = { - # New fields - 'export_state': fields.selection([ - ('draft', 'Draft'), - ('open','Confirmed'), - ('cancel','Cancelled'), - ('sent', 'Sent'), - ('rejected', 'Rejected'), - ('done','Done'), - ], 'State', select=True - ), - 'msg': fields.char('Message', size=255, required=False, readonly=True), - # Redefined fields: added states - 'date_done': fields.datetime('Date Confirmed', select=True, - readonly=True), - 'name': fields.char( - 'Your Reference', size=64, required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'communication': fields.char( - 'Communication', size=64, required=False, - help=("Used as the message between ordering customer and current " - "company. Depicts 'What do you want to say to the recipient" - " about this order ?'" - ), - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'communication2': fields.char( - 'Communication 2', size=128, - help='The successor message of Communication.', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'move_line_id': fields.many2one( - 'account.move.line', 'Entry line', - domain=[('reconcile_id','=', False), - ('account_id.type', '=','payable') - ], - help=('This Entry Line will be referred for the information of ' - 'the ordering customer.' - ), - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'amount_currency': fields.float( - 'Amount in Partner Currency', digits=(16,2), - required=True, - help='Payment amount in the partner currency', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'currency': fields.many2one( - 'res.currency', 'Partner Currency', required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'bank_id': fields.many2one( - 'res.partner.bank', 'Destination Bank account', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'order_id': fields.many2one( - 'payment.order', 'Order', required=True, - ondelete='cascade', select=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'partner_id': fields.many2one( - 'res.partner', string="Partner", required=True, - help='The Ordering Customer', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'date': fields.date( - 'Payment Date', - help=("If no payment date is specified, the bank will treat this " - "payment line directly" - ), - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'state': fields.selection([ - ('normal','Free'), - ('structured','Structured') - ], 'Communication Type', required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - } - _defaults = { - 'export_state': lambda *a: 'draft', - 'date_done': lambda *a: False, - 'msg': lambda *a: '', - } - - def fields_get(self, cr, uid, fields=None, context=None): - res = super(payment_line, self).fields_get(cr, uid, fields, context) - if 'communication' in res: - res['communication'].setdefault('states', {}) - res['communication']['states']['structured'] = [('required', True)] - if 'communication2' in res: - res['communication2'].setdefault('states', {}) - res['communication2']['states']['structured'] = [('readonly', True)] - res['communication2']['states']['normal'] = [('readonly', False)] - - return res - - """ - Hooks for processing direct debit orders, such as implemented in - account_direct_debit module. - """ - def get_storno_account_id(self, cr, uid, payment_line_id, amount, - currency_id, context=None): - """ - Hook for verifying a match of the payment line with the amount. - Return the account associated with the storno. - Used in account_banking interactive mode - :param payment_line_id: the single payment line id - :param amount: the (signed) amount debited from the bank account - :param currency: the bank account's currency *browse object* - :return: an account if there is a full match, False otherwise - :rtype: database id of an account.account resource. - """ - - return False - - def debit_storno(self, cr, uid, payment_line_id, amount, - currency_id, storno_retry=True, context=None): - """ - Hook for handling a canceled item of a direct debit order. - Presumably called from a bank statement import routine. - - Decide on the direction that the invoice's workflow needs to take. - You may optionally return an incomplete reconcile for the caller - to reconcile the now void payment. - - :param payment_line_id: the single payment line id - :param amount: the (negative) amount debited from the bank account - :param currency: the bank account's currency *browse object* - :param boolean storno_retry: whether the storno is considered fatal \ - or not. - :return: an incomplete reconcile for the caller to fill - :rtype: database id of an account.move.reconcile resource. - """ - - return False - -payment_line() - -class payment_order(osv.osv): - ''' - Enable extra states for payment exports - ''' - _inherit = 'payment.order' - - _columns = { - 'date_scheduled': fields.date( - 'Scheduled date if fixed', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - help='Select a date if you have chosen Preferred Date to be fixed.' - ), - 'reference': fields.char( - 'Reference', size=128, required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'mode': fields.many2one( - 'payment.mode', 'Payment mode', select=True, required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - help='Select the Payment Mode to be applied.', - ), - 'state': fields.selection([ - ('draft', 'Draft'), - ('open','Confirmed'), - ('cancel','Cancelled'), - ('sent', 'Sent'), - ('rejected', 'Rejected'), - ('done','Done'), - ], 'State', select=True - ), - 'line_ids': fields.one2many( - 'payment.line', 'order_id', 'Payment lines', - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'user_id': fields.many2one( - 'res.users','User', required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - ), - 'date_prefered': fields.selection([ - ('now', 'Directly'), - ('due', 'Due date'), - ('fixed', 'Fixed date') - ], "Preferred date", change_default=True, required=True, - states={ - 'sent': [('readonly', True)], - 'rejected': [('readonly', True)], - 'done': [('readonly', True)] - }, - help=("Choose an option for the Payment Order:'Fixed' stands for a " - "date specified by you.'Directly' stands for the direct " - "execution.'Due date' stands for the scheduled date of " - "execution." - ) - ), - 'payment_order_type': fields.selection( - [('payment', 'Payment'),('debit', 'Direct debit')], - 'Payment order type', required=True, - ), - 'date_sent': fields.date('Send date', readonly=True), - } - - _defaults = { - 'payment_order_type': lambda *a: 'payment', - } - - def launch_wizard(self, cr, uid, ids, context=None): - """ - Search for a wizard to launch according to the type. - If type is manual. just confirm the order. - Previously (pre-v6) in account_payment/wizard/wizard_pay.py - """ - if context == None: - context = {} - result = {} - orders = self.browse(cr, uid, ids, context) - order = orders[0] - # check if a wizard is defined for the first order - if order.mode.type and order.mode.type.ir_model_id: - context['active_ids'] = ids - wizard_model = order.mode.type.ir_model_id.model - wizard_obj = self.pool.get(wizard_model) - wizard_id = wizard_obj.create(cr, uid, {}, context) - result = { - 'name': wizard_obj._description or 'Payment Order Export', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': wizard_model, - 'domain': [], - 'context': context, - 'type': 'ir.actions.act_window', - 'target': 'new', - 'res_id': wizard_id, - 'nodestroy': True, - } - else: - # should all be manual orders without type or wizard model - for order in orders[1:]: - if order.mode.type and order.mode.type.ir_model_id: - raise osv.except_osv( - _('Error'), - _('You can only combine payment orders of the same type') - ) - # process manual payments - wf_service = netsvc.LocalService('workflow') - for order_id in ids: - wf_service.trg_validate(uid, 'payment.order', order_id, 'sent', cr) - return result - - def _write_payment_lines(self, cursor, uid, ids, **kwargs): - ''' - ORM method for setting attributes of corresponding payment.line objects. - Note that while this is ORM compliant, it is also very ineffecient due - to the absence of filters on writes and hence the requirement to - filter on the client(=OpenERP server) side. - ''' - if not hasattr(ids, '__iter__'): - ids = [ids] - payment_line_obj = self.pool.get('payment.line') - line_ids = payment_line_obj.search( - cursor, uid, [ - ('order_id', 'in', ids) - ]) - payment_line_obj.write(cursor, uid, line_ids, kwargs) - - def set_to_draft(self, cursor, uid, ids, *args): - ''' - Set both self and payment lines to state 'draft'. - ''' - self._write_payment_lines(cursor, uid, ids, export_state='draft') - return super(payment_order, self).set_to_draft( - cursor, uid, ids, *args - ) - - def action_sent(self, cursor, uid, ids, *args): - ''' - Set both self and payment lines to state 'sent'. - ''' - self._write_payment_lines(cursor, uid, ids, export_state='sent') - self.write(cursor, uid, ids, {'state':'sent', - 'date_sent': time.strftime('%Y-%m-%d')}) - return True - - def action_rejected(self, cursor, uid, ids, *args): - ''' - Set both self and payment lines to state 'rejected'. - ''' - self._write_payment_lines(cursor, uid, ids, export_state='rejected') - wf_service = netsvc.LocalService('workflow') - for id in ids: - wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cursor) - return True - - def set_done(self, cursor, uid, ids, *args): - ''' - Extend standard transition to update children as well. - ''' - self._write_payment_lines(cursor, uid, ids, - export_state='done', - date_done=time.strftime('%Y-%m-%d') - ) - return super(payment_order, self).set_done( - cursor, uid, ids, *args - ) - - def get_wizard(self, type): - ''' - Intercept manual bank payments to include 'sent' state. Default - 'manual' payments are flagged 'done' immediately. - ''' - if type == 'BANKMAN': - # Note that self._module gets overwritten by inheriters, so make - # the module name hard coded. - return 'account_banking', 'wizard_account_banking_payment_manual' - return super(payment_order, self).get_wizard(type) - - """ - Hooks for processing direct debit orders, such as implemented in - account_direct_debit module. - """ - def debit_reconcile_transfer( - self, cr, uid, payment_order_id, amount, currency, context=None): - """ - Reconcile the payment order if the amount is correct. Return the - id of the reconciliation. - """ - raise osv.except_osv( - _("Cannot reconcile"), - _("Cannot reconcile debit order: "+ - "Not implemented.")) - - def debit_unreconcile_transfer( - self, cr, uid, payment_order_id, reconcile_id, amount, currency, - context=None): - """ Unreconcile the payment_order if at all possible """ - raise osv.except_osv( - _("Cannot unreconcile"), - _("Cannot unreconcile debit order: "+ - "Not implemented.")) - -payment_order() - -class res_partner_bank(osv.osv): +class res_partner_bank(orm.Model): ''' This is a hack to circumvent the very limited but widely used base_iban dependency. The usage of __mro__ requires inside information of @@ -1229,15 +571,13 @@ class res_partner_bank(osv.osv): self._founder.__init__(*args, **kwargs) mro = self.__class__.__mro__ for i in range(len(mro)): - if mro[i].__module__.startswith('base.'): + if mro[i].__module__.startswith('openerp.addons.base.'): self._founder = mro[i] break def init(self, cr): ''' Update existing iban accounts to comply to new regime - Note that usage of the ORM is not possible here, as the ORM cannot - search on values not provided by the client. ''' partner_bank_obj = self.pool.get('res.partner.bank') @@ -1270,10 +610,11 @@ class res_partner_bank(osv.osv): Create dual function IBAN account for SEPA countries ''' if vals.get('state') == 'iban': - iban = vals.get('acc_number',False) or vals.get('acc_number_domestic',False) + iban = (vals.get('acc_number') + or vals.get('acc_number_domestic', False)) vals['acc_number'], vals['acc_number_domestic'] = ( self._correct_IBAN(iban)) - return self._founder.create(cursor, uid, vals, context) + return self._founder.create(self, cursor, uid, vals, context) def write(self, cr, uid, ids, vals, context=None): ''' @@ -1293,7 +634,7 @@ class res_partner_bank(osv.osv): self._correct_IBAN(account['acc_number'])) else: vals['acc_number_domestic'] = False - self._founder.write(cr, uid, account['id'], vals, context) + self._founder.write(self, cr, uid, account['id'], vals, context) return True def search(self, cursor, uid, args, *rest, **kwargs): @@ -1355,10 +696,9 @@ class res_partner_bank(osv.osv): # Extend search filter newargs = extended_search_expression(args) - # Original search (_founder) - results = self._founder.search(cursor, uid, newargs, - *rest, **kwargs - ) + # Original search + results = super(res_partner_bank, self).search( + cursor, uid, newargs, *rest, **kwargs) return results def read( @@ -1369,7 +709,7 @@ class res_partner_bank(osv.osv): ''' if fields and 'state' not in fields: fields.append('state') - records = self._founder.read(cr, uid, ids, fields, context, load) + records = self._founder.read(self, cr, uid, ids, fields, context, load) is_list = True if not isinstance(records, list): records = [records,] @@ -1429,6 +769,9 @@ class res_partner_bank(osv.osv): Trigger to find IBAN. When found: 1. Reformat BBAN 2. Autocomplete bank + + TODO: prevent unnecessary assignment of country_ids and + browsing of the country ''' if not acc_number: return {} @@ -1488,6 +831,7 @@ class res_partner_bank(osv.osv): if country_ids: country = country_obj.browse( cursor, uid, country_ids[0], context=context) + values['country_id'] = country_ids[0] if country and country.code in sepa.IBAN.countries: try: info = sepa.online.account_info(country.code, acc_number) @@ -1502,9 +846,8 @@ class res_partner_bank(osv.osv): info.bic or iban_acc.BIC_searchkey, name = info.bank ) - values['country_id'] = country_id or \ - country_ids and country_ids[0] or \ - False + if country_id: + values['country_id'] = country_id values['bank'] = bank_id or False if info.bic: values['bank_bic'] = info.bic @@ -1513,8 +856,8 @@ class res_partner_bank(osv.osv): if info is None: result.update(warning( _('Invalid data'), - _('The account number appears to be invalid for %(country)s') - % {'country': country.name} + _('The account number appears to be invalid for %s') + % country.name )) except NotImplementedError: if country.code in sepa.IBAN.countries: @@ -1524,8 +867,8 @@ class res_partner_bank(osv.osv): else: result.update(warning( _('Invalid format'), - _('The account number has the wrong format for %(country)s') - % {'country': country.name} + _('The account number has the wrong format for %s') + % country.name )) return result @@ -1558,15 +901,10 @@ class res_partner_bank(osv.osv): _("The IBAN number doesn't seem to be correct") ) - _constraints = [ - # Cannot have this as a constraint as it is rejecting valid numbers from GB and DE - # It works much better without this constraint! - #(check_iban, _("The IBAN number doesn't seem to be correct"), ["acc_number"]) - ] - res_partner_bank() -class res_bank(osv.osv): + +class res_bank(orm.Model): ''' Add a on_change trigger to automagically fill bank details from the online SWIFT database. Allow hand filled names to overrule SWIFT names. @@ -1609,7 +947,8 @@ class res_bank(osv.osv): res_bank() -class invoice(osv.osv): + +class invoice(orm.Model): ''' Create other reference types as well. @@ -1622,6 +961,9 @@ class invoice(osv.osv): Don't forget to redefine the column "reference_type" as below or your method will never be triggered. + + TODO: move 'structured' part to account_banking_payment module + where it belongs ''' _inherit = 'account.invoice' @@ -1651,13 +993,16 @@ class invoice(osv.osv): invoice() -class account_move_line(osv.osv): + +class account_move_line(orm.Model): _inherit = "account.move.line" def get_balance(self, cr, uid, ids, context=None): """ Return the balance of any set of move lines. - Surely this exists somewhere in account base, but I missed it. + + Not to be confused with the 'balance' field on this model, which + returns the account balance that the move line applies to. """ total = 0.0 if not ids: diff --git a/account_banking/account_banking_view.xml b/account_banking/account_banking_view.xml index 4b26380a0..104f8d1c2 100644 --- a/account_banking/account_banking_view.xml +++ b/account_banking/account_banking_view.xml @@ -134,16 +134,7 @@ - - account.banking.imported.line.search - account.bank.imported.line - search - - - - - - + Imported Bank Statements Files ir.actions.act_window @@ -193,7 +184,12 @@ account.bank.statement tree - + + + + + @@ -204,92 +200,52 @@ form + + + + + - + + black:state == 'confirmed';darkmagenta:match_multi == True;crimson:duplicate == True;grey:state == 'draft'; - - - - - - - - - - - account.bank.statement.form.banking-2 - - account.bank.statement - form - - - - - - - - account.bank.statement.form.banking-3 - - account.bank.statement - form - - - - - - - - - - - - - - account.bank.statement.tree.banking-2 - - account.bank.statement - tree - - - - - - + + 1 + - - - account.bank.statement.form.banking-4 - - account.bank.statement - form - - - - - - + + 1 + - - - account.bank.statement.form.banking-5 - - account.bank.statement - form - - - + + + + + + + - + + + + + - - - - - res.partner.bank.form.banking-2 res.partner.bank @@ -399,53 +295,6 @@ - - - - res.partner.form.banking-2 - res.partner - - - form - - - - onchange_acc_number(acc_number, acc_number_domestic, state, partner_id, country_id) - - - onchange_domestic(acc_number_domestic, partner_id, country_id) - - - - - - res.partner.form.banking-3 - res.partner - - - form - - - - onchange_acc_number(acc_number, acc_number_domestic, state, partner_id, country_id) - - - onchange_domestic(acc_number_domestic, partner_id, country_id) - - - - - res.bank.form.banking-1 @@ -459,54 +308,6 @@ - - - payment.mode.form.inherit - payment.mode - - form - - - - - - - - payment.mode.tree.inherit - payment.mode - - tree - - - - - - - - - - view.payment.mode.type.form - payment.mode.type - form - -
- - - - - - - -
- - - - - - - Bank statement line tree view account.bank.statement.line @@ -525,25 +326,25 @@ - - - + + + + + diff --git a/account_banking_payment/view/bank_payment_manual.xml b/account_banking_payment/view/bank_payment_manual.xml new file mode 100644 index 000000000..64e8bd08a --- /dev/null +++ b/account_banking_payment/view/bank_payment_manual.xml @@ -0,0 +1,16 @@ + + + + + Form for manual payment wizard + payment.manual + form + + +