[RFR] debit reconciliation

[ADD] storno processing
This commit is contained in:
OpenERP instance user
2011-12-11 16:00:41 +01:00
parent 5d287eefa3
commit 5c69350246
5 changed files with 186 additions and 62 deletions

View File

@@ -1,3 +1 @@
import account_payment
import account_move_line
import account_invoice
import model

View File

@@ -0,0 +1,3 @@
import account_payment
import account_move_line
import account_invoice

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import time
from osv import osv, fields
import netsvc
from tools.translate import _
@@ -49,47 +50,6 @@ class payment_order(osv.osv):
res['fields']['mode']['domain'] = domain
return res
def _reconcile_debit_order_move_line(self, cr, uid, origin_move_line_id,
transfer_move_line_id, context=None):
"""
Reconcile the debit order's move lines at generation time.
As the amount is derived directly from the counterpart move line,
we do not expect a write off. Take partially reconcilions into
account though.
"""
reconcile_obj = self.pool.get('account.move.reconcile')
move_line_obj = self.pool.get('account.move.line')
line_ids = [origin_move_line_id, transfer_move_line_id]
(origin, transfer) = move_line_obj.browse(
cr, uid, line_ids, context=context)
if origin.reconcile_partial_id:
line_ids = [x.id for x in
origin.reconcile_partial_id.line_partial_ids + [transfer]
]
total = 0.0
company_currency_id = origin.company_id.currency_id
for line in move_line_obj.read(
cr, uid, line_ids, ['debit', 'credit'], context=context):
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
full = self.pool.get('res.currency').is_zero(
cr, uid, company_currency_id, total)
vals = {
'type': 'auto',
'line_id': full and [(6, 0, line_ids)] or [(6, 0, [])],
'line_partial_ids': full and [(6, 0, [])] or [(6, 0, line_ids)],
}
if origin.reconcile_partial_id:
reconcile_obj.write(
cr, uid, origin.reconcile_partial_id.id,
vals, context=context)
else:
reconcile_obj.create(
cr, uid, vals, context=context)
for line_id in line_ids:
netsvc.LocalService("workflow").trg_trigger(
uid, 'account.move.line', line_id, cr)
def action_sent(self, cr, uid, ids, context=None):
"""
Create the moves that pay off the move lines from
@@ -137,6 +97,7 @@ class payment_order(osv.osv):
'account_id': order.mode.transfer_account_id.id,
'credit': 0.0,
'debit': line.amount,
'date': time.strftime('%Y-%m-%d'),
}
transfer_move_line_id = account_move_line_obj.create(
cr, uid, vals, context=context)
@@ -149,20 +110,196 @@ class payment_order(osv.osv):
})
reconcile_move_line_id = account_move_line_obj.create(
cr, uid, vals, context=context)
# register the debit move line on the payment line
# and call reconciliation on it
payment_line_obj.write(
cr, uid, line.id,
{'debit_move_line_id': reconcile_move_line_id},
context=context)
payment_line_obj.debit_reconcile(
cr, uid, line.id, context=context)
account_move_obj.post(cr, uid, [move_id], context=context)
self._reconcile_debit_order_move_line(
cr, uid, line.move_line_id.id, reconcile_move_line_id,
context=context)
return res
payment_order()
class payment_line(osv.osv):
_inherit = 'payment.line'
def debit_storno(self, cr, uid, payment_line_id, storno_move_line_id, context=None):
"""
Process a payment line from a direct debit order which has
been canceled by the bank or by the user:
- Undo the reconciliation of the payment line with the move
line that it originated from, and re-reconciliated with
the credit payment in the bank journal of the same amount and
on the same account.
- Mark the payment line for being reversed.
:param payment_line_id: the single id of the canceled payment line
:param storno_move_line_id: the credit payment in the bank journal
"""
if isinstance(payment_line_id, (list, tuple)):
payment_line_id = payment_line_id[0]
reconcile_obj = self.pool.get('account.move.reconcile')
move_line_obj = self.pool.get('account.move.line')
payment_line = self.browse(cr, uid, payment_line_id, context=context)
debit_move_line = payment_line.debit_move_line_id
if (not debit_move_line):
raise osv.except_osv(
_('Can not process storno'),
_('No move line for line %s') % payment_line.name)
if payment_line.storno:
raise osv.except_osv(
_('Can not process storno'),
_('Cancelation of payment line \'%s\' has already been ' +
'processed') % payment_line.name)
def is_zero(total):
return self.pool.get('res.currency').is_zero(
cr, uid, debit_move_line.company_id.currency_id, total)
# check validity of the proposed move line
torec_move_line = move_line_obj.browse(
cr, uid, storno_move_line_id, context=context)
if not (is_zero(torec_move_line.debit - debit_move_line.debit) and
is_zero(torec_move_line.credit - debit_move_line.credit) and
torec_move_line.account_id.id == debit_move_line.account_id.id):
raise osv.except_osv(
_('Can not process storno'),
_('%s is not a drop-in replacement for %s') % (
torec_move_line.name, debit_move_line.name))
if payment_line.storno:
raise osv.except_osv(
_('Can not process storno'),
_('Debit order line %s has already been cancelled') % (
payment_line.name))
# replace move line in reconciliation
reconcile_id = False
if (payment_line.move_line_id.reconcile_partial_id and
debit_move_line_id.id in
payment_line.move_line_id.reconcile_partial_id.line_partial_ids):
reconcile_id = payment_line.move_line_id.reconcile_partial_id
vals = {
'line_partial_ids':
[(3, debit_move_line_id.id), (4, torec_move_line.id)],
}
elif (payment_line.move_line_id.reconcile_id and
debit_move_line_id.id in
payment_line.move_line_id.reconcile_id.line_id):
reconcile_id = payment_line.move_line_id.reconcile_id
vals = {
'line_id':
[(3, debit_move_line_id.id), (4, torec_move_line.id)]
}
if not reconcile_id:
raise osv.except_osv(
_('Can not perform storno'),
_('Debit order line %s does not occur in the list of '
'reconciliation move lines of its origin') %
debit_move_line_id.name)
reconcile_obj.write(cr, uid, reconcile_id, vals, context=context)
self.write(cr, uid, payment_line_id, {'storno': True}, context=context)
#for line_id in line_ids:
# netsvc.LocalService("workflow").trg_trigger(
# uid, 'account.move.line', line_id, cr)
def debit_reconcile(self, cr, uid, payment_line_id, context=None):
"""
Reconcile a debit order's payment line with the the move line
that it is based on.
As the amount is derived directly from the counterpart move line,
we do not expect a write off. Take partially reconcilions into
account though.
:param payment_line_id: the single id of the canceled payment line
"""
if isinstance(payment_line_id, (list, tuple)):
payment_line_id = payment_line_id[0]
reconcile_obj = self.pool.get('account.move.reconcile')
move_line_obj = self.pool.get('account.move.line')
payment_line = self.browse(cr, uid, payment_line_id, context=context)
debit_move_line = payment_line.debit_move_line_id
torec_move_line = payment_line.move_line_id
if payment_line.storno:
raise osv.except_osv(
_('Can not reconcile'),
_('Cancelation of payment line \'%s\' has already been ' +
'processed') % payment_line.name)
if (not debit_move_line or not torec_move_line):
raise osv.except_osv(
_('Can not reconcile'),
_('No move line for line %s') % payment_line.name)
if torec_move_line.reconcile_id or torec_move_line.reconcile_partial_id:
raise osv.except_osv(
_('Error'),
_('Move line %s has already been reconciled') %
torec_move_line.name
)
if torec_move_line.reconcile_id or torec_move_line.reconcile_partial_id:
raise osv.except_osv(
_('Error'),
_('Move line %s has already been reconciled') %
torec_move_line.name
)
def get_balance(line_ids):
total = 0.0
for line in move_line_obj.read(
cr, uid, line_ids, ['debit', 'credit'], context=context):
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
return total
def is_zero(total):
return self.pool.get('res.currency').is_zero(
cr, uid, debit_move_line.company_id.currency_id, total)
line_ids = [debit_move_line.id, torec_move_line.id]
if debit_move_line.reconcile_partial_id:
line_ids = [
x.id for x in debit_move_line.reconcile_partial_id.line_partial_ids] + [torec_move_line_id]
total = get_balance(line_ids)
vals = {
'type': 'auto',
'line_id': is_zero(total) and [(6, 0, line_ids)] or [(6, 0, [])],
'line_partial_ids': is_zero(total) and [(6, 0, [])] or [(6, 0, line_ids)],
}
if debit_move_line.reconcile_partial_id:
reconcile_obj.write(
cr, uid, debit_move_line.reconcile_partial_id.id,
vals, context=context)
else:
reconcile_obj.create(
cr, uid, vals, context=context)
for line_id in line_ids:
netsvc.LocalService("workflow").trg_trigger(
uid, 'account.move.line', line_id, cr)
_columns = {
'debit_move_line_id': fields.many2one(
# this line is part of the credit side of move 2a
# from the documentation
'account.move.line', 'Debit move line',
readonly=True,
help="Move line through which the debit order pays the invoice"),
'storno': fields.boolean(
'Storno',
readonly=True,
help=("If this is true, the debit order has been canceled " +
"by the bank or by the customer")),
}
payment_line()
class payment_order_create(osv.osv_memory):
_inherit = 'payment.order.create'
@@ -204,19 +341,5 @@ class payment_order_create(osv.osv_memory):
}
payment_order_create()
class payment_line(osv.osv):
_inherit = 'payment.line'
_columns = {
'debit_move_line_id': fields.many2one(
'account.move.line', 'Debit move line',
readonly=True,
help="Move line through which the debit order pays the invoice"),
'storno': fields.boolean(
'Storno',
readonly=True,
help=("If this is true, the debit order has been canceled " +
"by the bank or by the customer")),
}
payment_line()