Files
bank-payment/bank_statement_instant_voucher/model/account_voucher_instant.py
2014-10-20 13:16:04 +02:00

320 lines
13 KiB
Python

# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2012 - 2013 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.addons.decimal_precision import decimal_precision as dp
class instant_voucher(orm.TransientModel):
_name = 'account.voucher.instant'
_description = 'Instant Voucher'
def cancel(self, cr, uid, ids, context=None):
"""
Delete the voucher and close window
"""
assert len(ids) == 1, "Will only take one resource id"
instant = self.browse(cr, uid, ids[0], context=context)
if instant.voucher_id:
self.pool.get('account.voucher').cancel_voucher(
cr, uid, [instant.voucher_id.id], context=context)
self.pool.get('account.voucher').unlink(
cr, uid, [instant.voucher_id.id], context=context)
return {'type': 'ir.actions.act_window_close'}
def get_voucher_defaults(self, cr, uid, vals, context=None):
"""
Gather conditional defaults based on given key, value pairs
:param vals: dictionary of key, value pairs
:returns: dictionary of default values for fields not in vals
"""
values_pool = self.pool.get('ir.values')
voucher_pool = self.pool.get('account.voucher')
res = {}
for (key, val) in vals.iteritems():
if val and voucher_pool._all_columns[key].column.change_default:
for default in values_pool.get_defaults(
cr, uid, 'account.voucher', '%s=%s' % (key, val)):
if default[1] not in vals:
res[default[1]] = default[2]
return res
def create_voucher(self, cr, uid, ids, context=None):
"""
Create a fully fledged voucher counterpart for the
statement line. User only needs to process taxes and may
adapt cost/income account.
"""
assert len(ids) == 1, "Will only take one resource id"
voucher_pool = self.pool.get('account.voucher')
period_pool = self.pool.get('account.period')
instant = self.browse(cr, uid, ids[0], context=context)
line = instant.statement_line_id
voucher_type = line.amount < 0 and 'purchase' or 'sale'
journal_ids = self.pool.get('account.journal').search(
cr, uid, [('company_id', '=', line.company_id.id),
('type', '=', voucher_type)])
if not journal_ids:
orm.exept_orm(
_('Error'),
_('No %s journal defined') % voucher_type)
journal = self.pool.get('account.journal').browse(
cr, uid, journal_ids[0], context=context)
if journal.type in ('sale', 'sale_refund'):
line_account_id = (
journal.default_credit_account_id and
journal.default_credit_account_id.id or False
)
elif journal.type in ('purchase', 'expense', 'purchase_refund'):
line_account_id = (
journal.default_debit_account_id and
journal.default_debit_account_id.id or False
)
vals = {
'name': (_('Voucher for statement line %s.%s') %
(line.statement_id.name, line.name)),
'reference': line.ref or False,
'company_id': line.company_id.id,
'partner_id': instant.partner_id.id,
'date': line.date or False,
'account_id': line.account_id.id,
'type': voucher_type,
'line_ids': [(0, 0, {'amount': abs(line.amount),
'account_id': line_account_id,
'type': line.amount < 0 and 'dr' or 'cr',
'name': line.ref or False,
})],
'amount': line.amount and abs(line.amount) or False,
'journal_id': journal_ids[0],
}
if vals['date']:
period_ids = period_pool.find(
cr, uid, vals['date'], context=context
)
if period_ids:
vals['period_id'] = period_ids[0]
vals.update(self.get_voucher_defaults(cr, uid, vals, context=context))
voucher_id = voucher_pool.create(
cr, uid, vals, context=context)
self.write(
cr, uid, ids[0],
{'voucher_id': voucher_id,
'state': 'ready',
'type': voucher_type,
}, context=context)
return {
'name': self._description,
'view_type': 'form',
'view_mode': 'form',
'res_model': self._name,
'domain': [],
'context': context,
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': ids[0],
'nodestroy': False,
}
def dummy(self, cr, uid, ids, context=None):
return {
'name': self._description,
'view_type': 'form',
'view_mode': 'form',
'res_model': self._name,
'domain': [],
'context': context,
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': ids[0],
'nodestroy': False,
}
def default_get(self, cr, uid, fields_list, context=None):
"""
Gather sane default values from the originating statement line
"""
res = super(instant_voucher, self).default_get(
cr, uid, fields_list, context=context)
if 'statement_line_id' in fields_list:
res['statement_line_id'] = (
context.get('active_id') or
context.get('active_ids') and context.get('active_ids')[0])
if not res['statement_line_id']:
raise orm.except_orm(
_('Error'),
_('Cannot determine statement line'))
line = self.pool.get('account.bank.statement.line').browse(
cr, uid, res['statement_line_id'], context=context)
if 'balance' in fields_list:
res['balance'] = line.amount
if 'ref' in fields_list:
res['ref'] = line.ref
if 'partner_id' in fields_list:
if line.partner_id:
res['partner_id'] = line.partner_id.id
return res
def _get_balance(self, cr, uid, ids, field_name, args, context=None):
"""
Compute the expected residual
TODO: currency conversion
"""
res = {}
for instant in self.browse(cr, uid, ids, context=context):
if instant.voucher_id and instant.voucher_id.state == 'posted':
amount = instant.statement_line_id.amount
counteramount = 0.0
statement_account_id = instant.statement_line_id.account_id.id
for line in instant.voucher_id.move_ids:
if line.account_id.id == statement_account_id:
counteramount = line.debit - line.credit
for line in instant.voucher_id.move_ids:
if line.account_id.id == statement_account_id:
counteramount = line.debit - line.credit
else:
amount = abs(instant.statement_line_id.amount)
counteramount = abs(instant.voucher_id
and instant.voucher_id.amount or 0.0)
res[instant.id] = amount - counteramount
return res
def confirm(self, cr, uid, ids, context=None):
"""
Post the voucher if necessary
Post the voucher's move lines if necessary
Sanity checks on currency and residual = 0.0
If the account_banking module is installed, perform matching
and reconciliation. If not, the user is left to manual
reconciliation of OpenERP.
"""
assert len(ids) == 1, "Will only take one resource id"
statement_line_obj = self.pool.get('account.bank.statement.line')
voucher_obj = self.pool.get('account.voucher')
move_obj = self.pool.get('account.move')
instant = self.browse(cr, uid, ids[0], context=context)
statement_line = instant.statement_line_id
voucher_currency = (instant.voucher_id.currency_id and
instant.voucher_id.currency_id or
instant.voucher_id.company_id.currency_id)
if (statement_line.statement_id.currency.id != voucher_currency.id):
raise orm.except_orm(
_("Error"),
_("Currency on the bank statement line needs to be the "
"same as on the voucher. Currency conversion is not yet "
"supported."))
if instant.voucher_id.state != 'posted':
voucher_obj.proforma_voucher(
cr, uid, [instant.voucher_id.id], context=context)
instant.refresh()
if instant.voucher_id.state != 'posted':
raise orm.except_orm(
_("Error"),
_("The voucher could not be posted."))
if instant.voucher_id.move_id.state != 'posted':
move_obj.post(
cr, uid, [instant.voucher_id.move_id.id], context=context)
instant.refresh()
if instant.voucher_id.move_id.state != 'posted':
raise orm.except_orm(
_("Error"),
_("The voucher's move line could not be posted."))
if not self.pool.get('res.currency').is_zero(
cr, uid, voucher_currency, instant.balance):
raise orm.except_orm(
_("Error"),
_("The amount on the bank statement line needs to be the "
"same as on the voucher. Write-off is not yet "
"supported."))
# Banking Addons integration:
# Gather the info needed to match the bank statement line
# and trigger its posting and reconciliation.
if 'import_transaction_id' in statement_line_obj._columns:
if instant.statement_line_id.state == 'confirmed':
raise orm.except_orm(
_("Error"),
_("Cannot match a confirmed statement line"))
if not statement_line.import_transaction_id:
statement_line_obj.create_instant_transaction(
cr, uid, statement_line.id, context=context)
statement_line.refresh()
for line in instant.voucher_id.move_ids:
if line.account_id.id == statement_line.account_id.id:
self.pool.get('banking.import.transaction').write(
cr,
uid,
statement_line.import_transaction_id.id,
{
'move_line_id': line.id,
'move_line_ids': [(6, 0, [line.id])],
'match_type': 'move',
'invoice_id': False,
'invoice_ids': [(6, 0, [])],
},
context=context
)
statement_line_obj.confirm(
cr, uid, [statement_line.id], context=context
)
break
return {'type': 'ir.actions.act_window_close'}
_columns = {
'balance': fields.function(
_get_balance,
type='float',
digits_compute=dp.get_precision('Account'),
string="Balance",),
'partner_id': fields.many2one(
'res.partner',
'Partner',
required=True),
'statement_line_id': fields.many2one(
'account.bank.statement.line',
'Bank statement line',
readonly=True),
'ref': fields.related(
'statement_line_id', 'ref',
type="char", size="48",
readonly=True,
string="Reference"),
'voucher_id': fields.many2one(
'account.voucher',
'Voucher',
readonly=True),
'state': fields.selection(
[('init', 'init'),
('ready', 'ready'),
('confirm', 'confirm')],
'State'),
'type': fields.selection(
[('sale', 'Sale'),
('purchase', 'Purchase')],
'Voucher type'),
}
_defaults = {'state': 'init'}