mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[FIX] Bank import writes to browse object
[ADD] direct debit order: process storno during bank import [ADD] bank import: add hooks for processing debit orders and stornos [ADD] direct debit order: pre-select move lines on reference substring configured in payment mode [ADD] payment term for direct debit invoices [ADD] payment line views: add storno field [RFR] standardize storno and debit order processing during bank import
This commit is contained in:
@@ -751,6 +751,26 @@ class payment_line(osv.osv):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
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_id: the bank account's currency id
|
||||||
|
: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()
|
payment_line()
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ class mem_bank_transaction(object):
|
|||||||
# An error message for interaction with the user
|
# An error message for interaction with the user
|
||||||
# Only used when mem_transaction.valid returns False.
|
# Only used when mem_transaction.valid returns False.
|
||||||
'error_message',
|
'error_message',
|
||||||
|
|
||||||
|
# Storno attribute. When True, make the cancelled debit eligible for
|
||||||
|
# a next direct debit run
|
||||||
|
'storno_retry',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -206,6 +210,10 @@ class mem_bank_transaction(object):
|
|||||||
# PERIODIC_ORDER An automated payment by the bank on your behalf.
|
# PERIODIC_ORDER An automated payment by the bank on your behalf.
|
||||||
# Always outgoing.
|
# Always outgoing.
|
||||||
# Will be selected for matching.
|
# Will be selected for matching.
|
||||||
|
# STORNO A failed or reversed attempt at direct debit.
|
||||||
|
# Either due to an action on the payer's side
|
||||||
|
# or a failure observed by the bank (lack of
|
||||||
|
# credit for instance)
|
||||||
#
|
#
|
||||||
# Perhaps more will follow.
|
# Perhaps more will follow.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ class banking_import(osv.osv_memory):
|
|||||||
retval.type = 'general'
|
retval.type = 'general'
|
||||||
|
|
||||||
if partial:
|
if partial:
|
||||||
move_line.reconcile_partial_id = reconcile_obj.create(
|
reconcile_obj.create(
|
||||||
cursor, uid, {
|
cursor, uid, {
|
||||||
'type': 'auto',
|
'type': 'auto',
|
||||||
'line_partial_ids': [(4, 0, [move_line.id])]
|
'line_partial_ids': [(4, 0, [move_line.id])],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -90,19 +90,56 @@ class banking_import(osv.osv_memory):
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
partial_ids = []
|
partial_ids = []
|
||||||
move_line.reconcile_id = reconcile_obj.create(
|
reconcile_obj.create(
|
||||||
cursor, uid, {
|
cursor, uid, {
|
||||||
'type': 'auto',
|
'type': 'auto',
|
||||||
'line_id': [
|
'line_id': [(6, 0, [move_line.id] + partial_ids)],
|
||||||
(4, x, False) for x in [move_line.id] + partial_ids
|
'line_partial_ids': [(6, 0, [])],
|
||||||
],
|
|
||||||
'line_partial_ids': [
|
|
||||||
(3, x, False) for x in partial_ids
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
def _link_storno(
|
||||||
|
self, cr, uid, trans, account_info, log, context=None):
|
||||||
|
payment_line_obj = self.pool.get('payment.line')
|
||||||
|
move_line_obj = self.pool.get('account.move.line')
|
||||||
|
line_ids = payment_line_obj.search(
|
||||||
|
cr, uid, [
|
||||||
|
('order_id.payment_order_type', '=', 'debit'),
|
||||||
|
('order_id.state', 'in', ['sent', 'done']),
|
||||||
|
('communication', '=', trans.reference)
|
||||||
|
], context=context)
|
||||||
|
if len(line_ids) == 1:
|
||||||
|
reconcile_id = payment_line_obj.debit_storno(
|
||||||
|
cr, uid, line_ids[0], trans.transferred_amount,
|
||||||
|
account_info.currency_id, trans.storno_retry, context=None)
|
||||||
|
if reconcile_id:
|
||||||
|
# we need to retrieve the move line as per consistency
|
||||||
|
# but it is only used to retrieve the account_id to book
|
||||||
|
# the transfer to. By necessity, we can use any of the
|
||||||
|
# move lines from the reconcile as all of them should have
|
||||||
|
# the same account.
|
||||||
|
move_line_ids = move_line_obj.search(
|
||||||
|
cr, uid,
|
||||||
|
[
|
||||||
|
'|', ('reconcile_id', '=', reconcile_id),
|
||||||
|
('reconcile_partial_id', '=', reconcile_id),
|
||||||
|
]
|
||||||
|
, context=context)
|
||||||
|
if move_line_ids:
|
||||||
|
move_line=move_line_obj.browse(
|
||||||
|
cr, uid, move_line_ids[0], context=context)
|
||||||
|
return struct(
|
||||||
|
move_line=move_line,
|
||||||
|
partner_id=False,
|
||||||
|
partner_bank_id=False,
|
||||||
|
reference=False,
|
||||||
|
type='general',
|
||||||
|
)
|
||||||
|
# TODO log the reason why there is no result for transfers marked
|
||||||
|
# as storno
|
||||||
|
return False
|
||||||
|
|
||||||
def _link_debit_order(
|
def _link_debit_order(
|
||||||
self, cr, uid, trans, account_info, log, context=None):
|
self, cr, uid, trans, account_info, log, context=None):
|
||||||
|
|
||||||
@@ -309,6 +346,7 @@ class banking_import(osv.osv_memory):
|
|||||||
]
|
]
|
||||||
|
|
||||||
move_line = False
|
move_line = False
|
||||||
|
|
||||||
if candidates and len(candidates) > 0:
|
if candidates and len(candidates) > 0:
|
||||||
# Now a possible selection of invoices has been found, check the
|
# Now a possible selection of invoices has been found, check the
|
||||||
# amounts expected and received.
|
# amounts expected and received.
|
||||||
@@ -753,6 +791,9 @@ class banking_import(osv.osv_memory):
|
|||||||
if transaction.type == bt.DIRECT_DEBIT:
|
if transaction.type == bt.DIRECT_DEBIT:
|
||||||
move_info = self._link_debit_order(
|
move_info = self._link_debit_order(
|
||||||
cursor, uid, transaction, account_info, results.log, context)
|
cursor, uid, transaction, account_info, results.log, context)
|
||||||
|
if transaction.type == bt.STORNO:
|
||||||
|
move_info = self._link_storno(
|
||||||
|
cursor, uid, transaction, account_info, results.log, context)
|
||||||
# Allow inclusion of generated bank invoices
|
# Allow inclusion of generated bank invoices
|
||||||
if transaction.type == bt.BANK_COSTS:
|
if transaction.type == bt.BANK_COSTS:
|
||||||
lines = self._link_costs(
|
lines = self._link_costs(
|
||||||
@@ -864,7 +905,12 @@ class banking_import(osv.osv_memory):
|
|||||||
)
|
)
|
||||||
if move_info:
|
if move_info:
|
||||||
values.type = move_info.type
|
values.type = move_info.type
|
||||||
values.reconcile_id = move_info.move_line.reconcile_id.id
|
values.reconcile_id = (
|
||||||
|
move_info.move_line.reconcile_id and
|
||||||
|
move_info.move_line.reconcile_id.id or
|
||||||
|
move_info.move_line.reconcile_partial_id and
|
||||||
|
move_info.move_line.reconcile_partial_id.id
|
||||||
|
)
|
||||||
values.partner_id = move_info.partner_id
|
values.partner_id = move_info.partner_id
|
||||||
values.partner_bank_id = move_info.partner_bank_id
|
values.partner_bank_id = move_info.partner_bank_id
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ class transaction_message(object):
|
|||||||
# 'remote_owner', 'remote_account', 'transfer_type', 'reference',
|
# 'remote_owner', 'remote_account', 'transfer_type', 'reference',
|
||||||
]
|
]
|
||||||
|
|
||||||
ref_expr = re.compile('REF[\*:]([0-9A-Z-z_-]+)')
|
|
||||||
|
|
||||||
def __init__(self, values, subno):
|
def __init__(self, values, subno):
|
||||||
'''
|
'''
|
||||||
Initialize own dict with attributes and coerce values to right type
|
Initialize own dict with attributes and coerce values to right type
|
||||||
@@ -85,7 +83,7 @@ class transaction(models.mem_bank_transaction):
|
|||||||
attrnames = ['local_account', 'remote_account',
|
attrnames = ['local_account', 'remote_account',
|
||||||
'remote_owner', 'transferred_amount',
|
'remote_owner', 'transferred_amount',
|
||||||
'execution_date', 'effective_date', 'transfer_type',
|
'execution_date', 'effective_date', 'transfer_type',
|
||||||
'reference', 'id',
|
'id', #'reference',
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -112,6 +110,9 @@ class transaction(models.mem_bank_transaction):
|
|||||||
'NO': bt.STORNO, # Storno
|
'NO': bt.STORNO, # Storno
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# global expression for matching storno references
|
||||||
|
ref_expr = re.compile('REF[\*:]([0-9A-Z-z_-]+)')
|
||||||
|
|
||||||
def __init__(self, line, *args, **kwargs):
|
def __init__(self, line, *args, **kwargs):
|
||||||
'''
|
'''
|
||||||
Initialize own dict with read values.
|
Initialize own dict with read values.
|
||||||
@@ -119,15 +120,15 @@ class transaction(models.mem_bank_transaction):
|
|||||||
super(transaction, self).__init__(*args, **kwargs)
|
super(transaction, self).__init__(*args, **kwargs)
|
||||||
# Copy attributes from auxiliary class to self.
|
# Copy attributes from auxiliary class to self.
|
||||||
for attr in self.attrnames:
|
for attr in self.attrnames:
|
||||||
if attr == 'reference':
|
#if attr == 'reference':
|
||||||
setattr(self, 'reference', False)
|
# setattr(self, 'reference', False)
|
||||||
else:
|
#else:
|
||||||
setattr(self, attr, getattr(line, attr))
|
setattr(self, attr, getattr(line, attr))
|
||||||
# self.message = ''
|
# self.message = ''
|
||||||
# Decompose structured messages
|
# Decompose structured messages
|
||||||
self.parse_message()
|
self.parse_message()
|
||||||
# Adaptations to direct debit orders ands stornos
|
# Adaptations to direct debit orders ands stornos
|
||||||
if self.transfer_type == 'DV':
|
if self.transfer_type == 'DV' and self.transferred_amount < 0:
|
||||||
res = self.ref_expr.search(self.remote_owner)
|
res = self.ref_expr.search(self.remote_owner)
|
||||||
if res:
|
if res:
|
||||||
self.transfer_type = 'NO'
|
self.transfer_type = 'NO'
|
||||||
@@ -139,7 +140,15 @@ class transaction(models.mem_bank_transaction):
|
|||||||
self.transfer_type = 'NO'
|
self.transfer_type = 'NO'
|
||||||
self.reference = res.group(1)
|
self.reference = res.group(1)
|
||||||
if self.transfer_type == 'IC':
|
if self.transfer_type == 'IC':
|
||||||
self.reference = self.remote_owner
|
if self.transferred_amount > 0:
|
||||||
|
self.reference = self.remote_owner
|
||||||
|
else:
|
||||||
|
self.transfer_type = 'NO'
|
||||||
|
self.message = self.remote_owner + self.message
|
||||||
|
res = self.ref_expr.search(self.message)
|
||||||
|
if res:
|
||||||
|
self.reference = res.group(1)
|
||||||
|
self.storno_retry = True
|
||||||
self.remote_owner = False
|
self.remote_owner = False
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
'view/account_payment.xml',
|
'view/account_payment.xml',
|
||||||
'view/account_invoice.xml',
|
'view/account_invoice.xml',
|
||||||
'workflow/account_invoice.xml',
|
'workflow/account_invoice.xml',
|
||||||
|
'data/account_payment_term.xml',
|
||||||
],
|
],
|
||||||
'demo_xml': [],
|
'demo_xml': [],
|
||||||
'description': '''
|
'description': '''
|
||||||
|
|||||||
16
account_direct_debit/data/account_payment_term.xml
Normal file
16
account_direct_debit/data/account_payment_term.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="payment_term_direct_debit" model="account.payment.term">
|
||||||
|
<field name="name">Direct debit</field>
|
||||||
|
<field name="note">Direct debit in 14 days</field>
|
||||||
|
</record>
|
||||||
|
<record id="payment_term_line_direct_debit" model="account.payment.term.line">
|
||||||
|
<field name="name">Direct debit in 14 days</field>
|
||||||
|
<field name="value">balance</field>
|
||||||
|
<field eval="14" name="days"/>
|
||||||
|
<field eval="0" name="days2"/>
|
||||||
|
<field eval="payment_term_direct_debit" name="payment_id"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
@@ -19,6 +19,21 @@ class payment_mode(osv.osv):
|
|||||||
help=('Journal to write payment entries when confirming ' +
|
help=('Journal to write payment entries when confirming ' +
|
||||||
'a debit order of this mode'),
|
'a debit order of this mode'),
|
||||||
),
|
),
|
||||||
|
'reference_filter': fields.char(
|
||||||
|
'Reference filter', size=16,
|
||||||
|
help=(
|
||||||
|
'Optional substring filter on move line references. ' +
|
||||||
|
'You can use this in combination with a specific journal ' +
|
||||||
|
'for items that you want to handle with this mode. Use ' +
|
||||||
|
'a separate sequence for the journal with a distinguished ' +
|
||||||
|
'prefix or suffix and enter that character string here.'),
|
||||||
|
),
|
||||||
|
# 'payment_term_ids': fields.many2many(
|
||||||
|
# 'account.payment.term', 'account_payment_order_terms_rel',
|
||||||
|
# 'mode_id', 'term_id', 'Payment terms',
|
||||||
|
# help=('Limit selected invoices to invoices with these payment ' +
|
||||||
|
# 'terms')
|
||||||
|
# ),
|
||||||
}
|
}
|
||||||
payment_mode()
|
payment_mode()
|
||||||
|
|
||||||
@@ -77,14 +92,17 @@ class payment_order(osv.osv):
|
|||||||
for line in order_line.debit_move_line_id.move_id.line_id:
|
for line in order_line.debit_move_line_id.move_id.line_id:
|
||||||
if line.account_id.type == 'other' and not line.reconcile_id:
|
if line.account_id.type == 'other' and not line.reconcile_id:
|
||||||
line_ids.append(line.id)
|
line_ids.append(line.id)
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
|
||||||
if is_zero(order.line_ids[0].debit_move_line_id,
|
if is_zero(order.line_ids[0].debit_move_line_id,
|
||||||
get_balance(line_ids) - amount):
|
get_balance(line_ids) - amount):
|
||||||
reconcile_id = self.pool.get('account.move.reconcile').create(
|
reconcile_id = self.pool.get('account.move.reconcile').create(
|
||||||
cr, uid,
|
cr, uid,
|
||||||
{'type': 'auto', 'line_id': [(6, 0, line_ids)]},
|
{'type': 'auto', 'line_id': [(6, 0, line_ids)]},
|
||||||
context)
|
context)
|
||||||
|
# set direct debit order to finished state
|
||||||
|
wf_service = netsvc.LocalService('workflow')
|
||||||
|
wf_service.trg_validate(
|
||||||
|
uid, 'payment.order', payment_order_id, 'done', cr)
|
||||||
|
|
||||||
return reconcile_id
|
return reconcile_id
|
||||||
|
|
||||||
def action_sent(self, cr, uid, ids, context=None):
|
def action_sent(self, cr, uid, ids, context=None):
|
||||||
@@ -165,86 +183,99 @@ payment_order()
|
|||||||
class payment_line(osv.osv):
|
class payment_line(osv.osv):
|
||||||
_inherit = 'payment.line'
|
_inherit = 'payment.line'
|
||||||
|
|
||||||
def debit_storno(self, cr, uid, payment_line_id, storno_move_line_id, context=None):
|
def debit_storno(self, cr, uid, payment_line_id, amount,
|
||||||
|
currency_id, storno_retry=True, context=None):
|
||||||
"""
|
"""
|
||||||
Process a payment line from a direct debit order which has
|
The processing of a storno is triggered by a debit
|
||||||
been canceled by the bank or by the user:
|
transfer on one of the company's bank accounts.
|
||||||
- Undo the reconciliation of the payment line with the move
|
This method offers to re-reconcile the original debit
|
||||||
line that it originated from, and re-reconciliated with
|
payment. For this purpose, we have registered that
|
||||||
the credit payment in the bank journal of the same amount and
|
payment move on the payment line.
|
||||||
on the same account.
|
|
||||||
- Mark the payment line for being reversed.
|
Return the (now incomplete) reconcile id. The caller MUST
|
||||||
|
re-reconcile this reconcile with the bank transfer and
|
||||||
:param payment_line_id: the single id of the canceled payment line
|
re-open the associated invoice.
|
||||||
:param storno_move_line_id: the credit payment in the bank journal
|
|
||||||
|
:param payment_line_id: the single payment line id
|
||||||
|
:param amount: the (signed) amount debited from the bank account
|
||||||
|
:param currency_id: the bank account's currency id
|
||||||
|
:param boolean storno_retry: when True, attempt to reopen the invoice, \
|
||||||
|
set the invoice to 'Debit denied' otherwise.
|
||||||
|
:return: an incomplete reconcile for the caller to fill
|
||||||
|
:rtype: database id of an account.move.reconcile resource.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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')
|
move_line_obj = self.pool.get('account.move.line')
|
||||||
payment_line = self.browse(cr, uid, payment_line_id, context=context)
|
reconcile_obj = self.pool.get('account.move.reconcile')
|
||||||
|
line = self.browse(cr, uid, payment_line_id)
|
||||||
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
|
reconcile_id = False
|
||||||
if (payment_line.move_line_id.reconcile_partial_id and
|
if (line.debit_move_line_id and not line.storno and
|
||||||
debit_move_line_id.id in
|
self.pool.get('res.currency').is_zero(
|
||||||
payment_line.move_line_id.reconcile_partial_id.line_partial_ids):
|
cr, uid, currency_id, (
|
||||||
reconcile_id = payment_line.move_line_id.reconcile_partial_id
|
(line.debit_move_line_id.credit or 0.0) -
|
||||||
vals = {
|
(line.debit_move_line_id.debit or 0.0) + amount))):
|
||||||
'line_partial_ids':
|
# Two different cases, full and partial
|
||||||
[(3, debit_move_line_id.id), (4, torec_move_line.id)],
|
# Both cases differ subtly in the procedure to follow
|
||||||
}
|
# Needs refractoring, but why is this not in the OpenERP API?
|
||||||
elif (payment_line.move_line_id.reconcile_id and
|
if line.debit_move_line_id.reconcile_partial_id:
|
||||||
debit_move_line_id.id in
|
reconcile_id = line.debit_move_line_id.reconcile_partial_id.id
|
||||||
payment_line.move_line_id.reconcile_id.line_id):
|
attribute = 'reconcile_partial_id'
|
||||||
reconcile_id = payment_line.move_line_id.reconcile_id
|
if len(line.debit_move_line_id.reconcile_id.line_partial_ids) == 2:
|
||||||
vals = {
|
# reuse the simple reconcile for the storno transfer
|
||||||
'line_id':
|
reconcile_obj.write(
|
||||||
[(3, debit_move_line_id.id), (4, torec_move_line.id)]
|
cr, uid, reconcile_id, {
|
||||||
}
|
'line_id': [(6, 0, line.debit_move_line_id.id)],
|
||||||
if not reconcile_id:
|
'line_partial_ids': [(6, 0, [])],
|
||||||
raise osv.except_osv(
|
}, context=context)
|
||||||
_('Can not perform storno'),
|
else:
|
||||||
_('Debit order line %s does not occur in the list of '
|
# split up the original reconcile in a partial one
|
||||||
'reconciliation move lines of its origin') %
|
# and a new one for reconciling the storno transfer
|
||||||
debit_move_line_id.name)
|
reconcile_obj.write(
|
||||||
reconcile_obj.write(cr, uid, reconcile_id, vals, context=context)
|
cr, uid, reconcile_id, {
|
||||||
self.write(cr, uid, payment_line_id, {'storno': True}, context=context)
|
'line_partial_ids': [(3, line.debit_move_line_id.id)],
|
||||||
#for line_id in line_ids:
|
}, context=context)
|
||||||
# netsvc.LocalService("workflow").trg_trigger(
|
reconcile_id = reconcile_obj.create(
|
||||||
# uid, 'account.move.line', line_id, cr)
|
cr, uid, {
|
||||||
|
'type': 'auto',
|
||||||
|
'line_id': [(6, 0, line.debit_move_line_id.id)],
|
||||||
|
}, context=context)
|
||||||
|
elif line.debit_move_line_id.reconcile_id:
|
||||||
|
reconcile_id = line.debit_move_line_id.reconcile_id.id
|
||||||
|
if len(line.debit_move_line_id.reconcile_id.line_id) == 2:
|
||||||
|
# reuse the simple reconcile for the storno transfer
|
||||||
|
reconcile_obj.write(
|
||||||
|
cr, uid, reconcile_id, {
|
||||||
|
'line_id': [(6, 0, [line.debit_move_line_id.id])]
|
||||||
|
}, context=context)
|
||||||
|
else:
|
||||||
|
# split up the original reconcile in a partial one
|
||||||
|
# and a new one for reconciling the storno transfer
|
||||||
|
partial_ids = [
|
||||||
|
x.id for x in line.debit_move_line_id.reconcile_id.line_id
|
||||||
|
if x.id != line.debit_move_line_id.id
|
||||||
|
]
|
||||||
|
reconcile_obj.write(
|
||||||
|
cr, uid, reconcile_id, {
|
||||||
|
'line_partial_ids': [(6, 0, partial_ids)],
|
||||||
|
'line_id': [(6, 0, [])],
|
||||||
|
}, context=context)
|
||||||
|
reconcile_id = reconcile_obj.create(
|
||||||
|
cr, uid, {
|
||||||
|
'type': 'auto',
|
||||||
|
'line_id': [(6, 0, line.debit_move_line_id.id)],
|
||||||
|
}, context=context)
|
||||||
|
# mark the payment line for storno processed
|
||||||
|
if reconcile_id:
|
||||||
|
self.write(cr, uid, [payment_line_id],
|
||||||
|
{'storno': True}, context=context)
|
||||||
|
# put forth the invoice workflow
|
||||||
|
if line.move_line_id.invoice:
|
||||||
|
activity = (storno_retry and 'open_test'
|
||||||
|
or 'invoice_debit_denied')
|
||||||
|
netsvc.LocalService("workflow").trg_validate(
|
||||||
|
uid, 'account.invoice', line.move_line_id.invoice.id,
|
||||||
|
activity, cr)
|
||||||
|
return reconcile_id
|
||||||
|
|
||||||
def debit_reconcile(self, cr, uid, payment_line_id, context=None):
|
def debit_reconcile(self, cr, uid, payment_line_id, context=None):
|
||||||
"""
|
"""
|
||||||
@@ -356,9 +387,29 @@ class payment_order_create(osv.osv_memory):
|
|||||||
payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
|
payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
|
||||||
# Search for move line to pay:
|
# Search for move line to pay:
|
||||||
if payment.payment_order_type == 'debit':
|
if payment.payment_order_type == 'debit':
|
||||||
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'receivable'), ('amount_to_receive', '>', 0)]
|
domain = [
|
||||||
|
('reconcile_id', '=', False),
|
||||||
|
('account_id.type', '=', 'receivable'),
|
||||||
|
('amount_to_receive', '>', 0),
|
||||||
|
]
|
||||||
|
# cannot filter on properties of (searchable)
|
||||||
|
# function fields. Needs work in expression.expression.parse()
|
||||||
|
# Currently gives an SQL error.
|
||||||
|
# # apply payment term filter
|
||||||
|
# if payment.mode.payment_term_ids:
|
||||||
|
# term_ids = [term.id for term in payment.mode.payment_term_ids]
|
||||||
|
# domain = domain + [
|
||||||
|
# '|', ('invoice', '=', False),
|
||||||
|
# ('invoice.payment_term', 'in', term_ids),
|
||||||
|
# ]
|
||||||
else:
|
else:
|
||||||
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
|
domain = [
|
||||||
|
('reconcile_id', '=', False),
|
||||||
|
('account_id.type', '=', 'payable'),
|
||||||
|
('amount_to_pay', '>', 0)
|
||||||
|
]
|
||||||
|
if payment.mode.reference_filter:
|
||||||
|
domain.append(('ref', 'ilike', payment.mode.reference_filter))
|
||||||
# domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
|
# domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
|
||||||
### end account_direct_debit ###
|
### end account_direct_debit ###
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@
|
|||||||
icon="gtk-find"
|
icon="gtk-find"
|
||||||
/>
|
/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//tree[@string='Payment Line']" position="inside">
|
||||||
|
<!-- the attrs do not work like this, apparently -->
|
||||||
|
<field name="storno" attrs="{'invisible': [(parent.payment_order_type, '!=', 'debit')]}"/>
|
||||||
|
</xpath>
|
||||||
</data>
|
</data>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -59,8 +63,22 @@
|
|||||||
<field name="type" position="after">
|
<field name="type" position="after">
|
||||||
<field name="transfer_account_id"/>
|
<field name="transfer_account_id"/>
|
||||||
<field name="transfer_journal_id"/>
|
<field name="transfer_journal_id"/>
|
||||||
|
<field name="reference_filter"/>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_payment_line_tree" model="ir.ui.view">
|
||||||
|
<field name="name">Payment Lines</field>
|
||||||
|
<field name="model">payment.line</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="inherit_id" ref="account_payment.view_payment_line_tree"/>
|
||||||
|
<field eval="4" name="priority"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="storno"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<record id="debit_denied_to_open" model="workflow.transition">
|
<record id="debit_denied_to_open" model="workflow.transition">
|
||||||
<field name="act_from" ref="act_debit_denied"/>
|
<field name="act_from" ref="act_debit_denied"/>
|
||||||
<field name="act_to" ref="account.act_open_test"/>
|
<field name="act_to" ref="account.act_open_test"/>
|
||||||
|
<field name="signal">open_test</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|||||||
Reference in New Issue
Block a user