[IMP] account_voucher: Implement writoff while validating vouchers

bzr revid: psi@tinyerp.co.in-20101112131546-h8cqx7p5ogbwrhq8
This commit is contained in:
psi (Open ERP) 2010-11-12 18:45:46 +05:30
parent 92ffc02451
commit ac2b77a595
6 changed files with 302 additions and 5 deletions

View File

@ -43,6 +43,7 @@
"account_voucher_report.xml",
"wizard/account_voucher_unreconcile_view.xml",
"wizard/account_statement_from_invoice_view.xml",
"wizard/account_voucher_payment_option.xml",
"account_voucher_view.xml",
"voucher_payment_receipt_view.xml",
"voucher_sales_purchase_view.xml",

View File

@ -541,6 +541,38 @@ class account_voucher(osv.osv):
self.write(cr, uid, ids, res)
return True
def do_check(self, cr, uid, ids, context=None):
cur_obj = self.pool.get('res.currency')
mod_obj = self.pool.get('ir.model.data')
if context is None:
context = {}
voucher = self.browse(cr, uid, [ids[0]], context=context)[0]
debit= credit = 0.0
if voucher.line_dr_ids:
for line in voucher.line_dr_ids:
debit += line.amount_original
if voucher.line_cr_ids:
for line in voucher.line_cr_ids:
credit += line.amount_original
if (voucher.amount + debit) != credit:
model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_account_voucher_pay_writeoff')], context=context)
resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
context.update({'voucher_id': ids[0]})
return {
'name': _('Information addendum'),
'context': context,
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.voucher.pay.writeoff',
'views': [(resource_id, 'form')],
'type': 'ir.actions.act_window',
'target': 'new',
'nodestroy': True
}
else:
self.action_move_line_create(cr, uid, ids, context=context)
return True
def unlink(self, cr, uid, ids, context=None):
for t in self.read(cr, uid, ids, ['state'], context=context):
if t['state'] not in ('draft', 'cancel'):
@ -568,7 +600,6 @@ class account_voucher(osv.osv):
return {'value':res}
def action_move_line_create(self, cr, uid, ids, context=None):
def _get_payment_term_lines(term_id, amount):
term_pool = self.pool.get('account.payment.term')
if term_id and amount:
@ -648,7 +679,6 @@ class account_voucher(osv.osv):
line_total = line_total - currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
elif inv.type == 'purchase':
line_total = line_total + currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
for line in inv.line_ids:
if not line.amount:
continue
@ -716,7 +746,6 @@ class account_voucher(osv.osv):
else:
account_id = inv.partner_id.property_account_payable.id
move_line['account_id'] = account_id
move_line_pool.create(cr, uid, move_line)
self.write(cr, uid, [inv.id], {
@ -730,6 +759,110 @@ class account_voucher(osv.osv):
move_line_pool.reconcile_partial(cr, uid, rec_ids)
return True
def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
if context is None:
context = {}
seq_obj = self.pool.get('ir.sequence')
#TODO check if we can use different period for payment and the writeoff line
assert len(ids)==1, "Can only pay one voucher at a time"
voucher = self.browse(cr, uid, ids[0])
if voucher.number:
name = voucher.number
elif voucher.journal_id.sequence_id:
name = seq_obj.get_id(cr, uid, voucher.journal_id.sequence_id.id)
else:
raise osv.except_osv(_('Error !'), _('Please define a sequence on the journal !'))
if not voucher.reference:
ref = name.replace('/','')
else:
ref = voucher.reference
src_account_id = voucher.account_id.id
# Take the seq as name for move
types = {'sale': -1, 'purchase': 1, 'payment': 1, 'receipt': -1}
direction = types[voucher.type]
#take the choosen date
if 'date_p' in context and context['date_p']:
date = context['date_p']
else:
date = time.strftime('%Y-%m-%d')
# Take the amount in currency and the currency of the payment
if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
amount_currency = context['amount_currency']
currency_id = context['currency_id']
else:
amount_currency = False
currency_id = False
pay_journal = self.pool.get('account.journal').read(cr, uid, pay_journal_id, ['type'], context=context)
if voucher.type in ('sale', 'receipt'):
if pay_journal['type'] == 'bank':
entry_type = 'bank_pay_voucher' # Bank payment
else:
entry_type = 'pay_voucher' # Cash payment
else:
entry_type = 'cont_voucher'
# Pay attention to the sign for both debit/credit AND amount_currency
l1 = {
'debit': direction * pay_amount>0 and direction * pay_amount,
'credit': direction * pay_amount<0 and - direction * pay_amount,
'account_id': src_account_id,
'partner_id': voucher.partner_id.id,
'ref':ref,
'date': date,
'currency_id':currency_id,
'amount_currency':amount_currency and direction * amount_currency or 0.0,
'company_id': voucher.company_id.id,
}
l2 = {
'debit': direction * pay_amount<0 and - direction * pay_amount,
'credit': direction * pay_amount>0 and direction * pay_amount,
'account_id': pay_account_id,
'partner_id': voucher.partner_id.id,
'ref':ref,
'date': date,
'currency_id':currency_id,
'amount_currency':amount_currency and - direction * amount_currency or 0.0,
'company_id': voucher.company_id.id,
}
if not name:
name = voucher.line_ids and voucher.line_ids[0].name or voucher.number
l1['name'] = name
l2['name'] = name
lines = [(0, 0, l1), (0, 0, l2)]
move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'name': name, 'date': date}
move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
line_ids = []
total = 0.0
line = self.pool.get('account.move.line')
move_ids = [move_id,]
if voucher.move_id:
move_ids.append(voucher.move_id.id)
cr.execute('SELECT id FROM account_move_line '\
'WHERE move_id IN %s',
((move_id,),))
lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
for l in lines:
if l.account_id.id == src_account_id:
line_ids.append(l.id)
total += (l.debit or 0.0) - (l.credit or 0.0)
voc_id, name = self.name_get(cr, uid, [voucher.id], context=context)[0]
if (not round(total,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))) or writeoff_acc_id:
self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
else:
code = voucher.currency_id.code
# TODO: use currency's formatting function
msg = _("Invoice '%s' is paid partially: %s%s of %s%s (%s%s remaining)") % \
(name, pay_amount, code, voucher.amount_total, code, total, code)
self.log(cr, uid, voc_id, msg)
self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
# Update the stored value (fields.function), so we write to trigger recompute
self.write(cr, uid, ids, {'state':'posted'}, context=context)
return True
def copy(self, cr, uid, id, default={}, context=None):
default.update({
'state': 'draft',

View File

@ -217,7 +217,7 @@
<button name="cancel_voucher" string="Cancel" states="draft,proforma" icon="gtk-cancel"/>
<button name="cancel_voucher" string="Unreconcile" type="object" states="posted" icon="terp-stock_effects-object-colorize" confirm="Are you sure to unreconcile this record ?"/>
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft" icon="terp-stock_effects-object-colorize"/>
<button name="proforma_voucher" string="Validate" states="draft" icon="gtk-go-forward"/>
<button name="do_check" type="object" string="Validate" states="draft" icon="gtk-go-forward"/>
</group>
</form>
</field>
@ -325,7 +325,7 @@
<button name="cancel_voucher" string="Cancel" states="draft,proforma" icon="gtk-cancel"/>
<button name="cancel_voucher" string="Unreconcile" type="object" states="posted" icon="terp-stock_effects-object-colorize" confirm="Are you sure to unreconcile this record ?"/>
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft" icon="terp-stock_effects-object-colorize"/>
<button name="proforma_voucher" string="Validate" states="draft" icon="gtk-go-forward"/>
<button name="do_check" type="object" string="Validate" states="draft" icon="gtk-go-forward"/>
</group>
</form>
</field>

View File

@ -21,5 +21,6 @@
import account_voucher_unreconcile
import account_statement_from_invoice
import account_voucher_payment_option
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 osv import fields, osv
from tools.translate import _
class account_voucher_pay_writeoff(osv.osv_memory):
"""
Opens the write off amount pay form.
"""
_name = "account.voucher.pay.writeoff"
_description = "Pay Voucher"
_columns = {
'payment_option':fields.selection([
('not_reconcile', 'Do not reconcile balance'),
('close_balance', 'close the balance'),
], 'Payment Option', required=True),
'writeoff_acc_id': fields.many2one('account.account', 'Write-Off account'),
'writeoff_journal_id': fields.many2one('account.journal', 'Write-Off journal'),
'comment': fields.char('Comment', size=64, required=True),
'analytic_id': fields.many2one('account.analytic.account','Analytic Account'),
}
_defaults = {
'payment_option': 'not_reconcile',
'comment': 'Write-Off',
}
def pay_and_reconcile_writeoff(self, cr, uid, ids, context=None):
voucher_obj = self.pool.get('account.voucher')
data = self.read(cr, uid, ids,context=context)[0]
voucher_id = context.get('voucher_id', False)
if data['payment_option'] == 'not_reconcile':
voucher_obj.action_move_line_create(cr, uid, [voucher_id], context=context)
return {}
context.update({'write_off':data})
self.pool.get('account.voucher.pay').pay_and_reconcile(cr, uid, [voucher_id], context=context)
return {}
account_voucher_pay_writeoff()
class account_voucher_pay(osv.osv_memory):
"""
Generate pay invoice wizard, user can make partial or full payment for invoice.
"""
_name = "account.voucher.pay"
_description = "Pay Voucher"
def pay_and_reconcile(self, cr, uid, ids, context=None):
cur_obj = self.pool.get('res.currency')
voucher_obj = self.pool.get('account.voucher')
if context is None:
context = {}
voucher = voucher_obj.browse(cr, uid, [ids[0]], context=context)[0]
writeoff_account_id = False
writeoff_journal_id = False
comment = False
if 'write_off' in context and context['write_off'] :
writeoff_account_id = context['write_off']['writeoff_acc_id']
writeoff_journal_id = context['write_off']['writeoff_journal_id']
comment = context['write_off']['comment']
amount = voucher.amount
journal = voucher.journal_id
# Compute the amount in company's currency, with the journal currency (which is equal to payment currency)
# when it is needed : If payment currency (according to selected journal.currency) is <> from company currency
if journal.currency and voucher.company_id.currency_id.id<>journal.currency.id:
ctx = {'date': voucher.date}
amount = cur_obj.compute(cr, uid, journal.currency.id, voucher.company_id.currency_id.id, amount, context=ctx)
currency_id = journal.currency.id
# Put the paid amount in currency, and the currency, in the context if currency is different from company's currency
context.update({'amount_currency': voucher.amount, 'currency_id': currency_id})
if voucher.company_id.currency_id.id<>voucher.currency_id.id:
ctx = {'date':voucher.date}
amount = cur_obj.compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, amount, context=ctx)
currency_id = voucher.currency_id.id
# Put the paid amount in currency, and the currency, in the context if currency is different from company's currency
context.update({'amount_currency':voucher.amount,'currency_id':currency_id})
# Take the choosen date
if comment:
context.update({'date_p':voucher.date, 'comment':comment})
else:
context.update({'date_p':voucher.date, 'comment':False})
acc_id = journal.default_credit_account_id and journal.default_credit_account_id.id
if not acc_id:
raise osv.except_osv(_('Error !'), _('Your journal must have a default credit and debit account.'))
voucher_obj.pay_and_reconcile(cr, uid, ids,
amount, acc_id, voucher.period_id.id, journal.id, writeoff_account_id,
voucher.period_id, writeoff_journal_id, context, voucher.name)
return {}
account_voucher_pay()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_voucher_pay_writeoff" model="ir.ui.view">
<field name="name">account.voucher.pay.writeoff.form</field>
<field name="model">account.voucher.pay.writeoff</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Information addendum">
<group height="280" width="590">
<field name="payment_option"/>
<group colspan="4" attrs="{'invisible':[('payment_option','!=','close_balance')]}">
<separator string="Write-Off Move" colspan="4"/>
<field name="writeoff_journal_id" attrs="{'required':[('payment_option','=','close_balance')]}"/>
<field name="writeoff_acc_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]" attrs="{'required':[('payment_option','=','close_balance')]}"/>
<field name="comment"/>
<group colspan="4" groups="analytic.group_analytic_accounting">
<separator string="Analytic" colspan="4"/>
<field name="analytic_id"/>
</group>
</group>
<separator colspan="4"/>
<group colspan="4" col="6">
<button icon="gtk-close" special="cancel" string="Close"/>
<button icon="gtk-execute" string="Pay and reconcile" name="pay_and_reconcile_writeoff" type="object"/>
</group>
</group>
</form>
</field>
</record>
<record id="action_account_voucher_pay_writeoff" model="ir.actions.act_window">
<field name="name">Payment Writeoff</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.voucher.pay.writeoff</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_account_voucher_pay_writeoff"/>
<field name="context">{'record_id' : active_id}</field>
<field name="target">new</field>
</record>
</data>
</openerp>