mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[RFR] debit reconciliation
[ADD] storno processing
This commit is contained in:
@@ -1,3 +1 @@
|
||||
import account_payment
|
||||
import account_move_line
|
||||
import account_invoice
|
||||
import model
|
||||
|
||||
3
account_direct_debit/model/__init__.py
Normal file
3
account_direct_debit/model/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
import account_payment
|
||||
import account_move_line
|
||||
import account_invoice
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user