mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[FIX] do an actual partial reconciliation at statement confirm time
[FIX] no context in match_invoice() [FIX] transaction update and browse again after split [ADD] transaction parent_id relation for split records [ADD] residual and write-off infrastructure [ADD] option to disable reconciliation on bank transaction
This commit is contained in:
@@ -453,7 +453,9 @@ class account_bank_statement(osv.osv):
|
|||||||
"""
|
"""
|
||||||
if st_line.reconcile_id:
|
if st_line.reconcile_id:
|
||||||
account_move_line_obj.write(cr, uid, [torec], {
|
account_move_line_obj.write(cr, uid, [torec], {
|
||||||
'reconcile_id': st_line.reconcile_id.id }, context=context)
|
(st_line.reconcile_id.line_partial_ids and
|
||||||
|
'reconcile_partial_id' or 'reconcile_id'):
|
||||||
|
st_line.reconcile_id.id }, context=context)
|
||||||
for move_line in (st_line.reconcile_id.line_id or []) + (
|
for move_line in (st_line.reconcile_id.line_id or []) + (
|
||||||
st_line.reconcile_id.line_partial_ids or []):
|
st_line.reconcile_id.line_partial_ids or []):
|
||||||
netsvc.LocalService("workflow").trg_trigger(
|
netsvc.LocalService("workflow").trg_trigger(
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ class banking_import_transaction(osv.osv):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _match_invoice(self, cr, uid, trans, move_lines,
|
def _match_invoice(self, cr, uid, trans, move_lines,
|
||||||
partner_ids, bank_account_ids, log, linked_invoices):
|
partner_ids, bank_account_ids, log, linked_invoices,
|
||||||
|
context=None):
|
||||||
'''
|
'''
|
||||||
Find the invoice belonging to this reference - if there is one
|
Find the invoice belonging to this reference - if there is one
|
||||||
Use the sales journal to check.
|
Use the sales journal to check.
|
||||||
@@ -405,17 +406,15 @@ class banking_import_transaction(osv.osv):
|
|||||||
dict(
|
dict(
|
||||||
transferred_amount = trans.transferred_amount - expected,
|
transferred_amount = trans.transferred_amount - expected,
|
||||||
transaction = trans.transaction + 'b',
|
transaction = trans.transaction + 'b',
|
||||||
|
parent_id = trans.id,
|
||||||
), context=context)
|
), context=context)
|
||||||
|
# update the current record
|
||||||
self.write(cr, uid, trans.id, dict(
|
self.write(cr, uid, trans.id, dict(
|
||||||
transaction = trans.transaction + 'a'), context)
|
transferred_amount = expected,
|
||||||
# NOTE: the following is debatable. By copying the
|
transaction = trans.transaction + 'a',
|
||||||
# eyecatcher of the invoice itself, we enhance the
|
), context)
|
||||||
# tracability of the invoices, but we degrade the
|
# rebrowse the current record after writing
|
||||||
# tracability of the bank transactions. When debugging, it
|
trans=self.browse(cr, uid, trans.id, context=context)
|
||||||
# is wise to disable this line.
|
|
||||||
# Stefan: disabled for interactive mode
|
|
||||||
# trans.reference = eyecatcher(move_line.invoice)
|
|
||||||
|
|
||||||
if move_line:
|
if move_line:
|
||||||
account_ids = [
|
account_ids = [
|
||||||
x.id for x in bank_account_ids
|
x.id for x in bank_account_ids
|
||||||
@@ -691,8 +690,16 @@ class banking_import_transaction(osv.osv):
|
|||||||
continue
|
continue
|
||||||
if transaction.match_type not in self.reconcile_map:
|
if transaction.match_type not in self.reconcile_map:
|
||||||
raise osv.except_osv(
|
raise osv.except_osv(
|
||||||
_("Cannot reconcile type %s" % transaction.match_type),
|
_("Cannot reconcile"),
|
||||||
_("No method found to reconcile this type"))
|
_("Cannot reconcile type %s. No method found to reconcile this type") %
|
||||||
|
transaction.match_type
|
||||||
|
)
|
||||||
|
if transaction.residual and transaction.writeoff_account_id:
|
||||||
|
raise osv.except_osv(
|
||||||
|
_("Cannot reconcile"),
|
||||||
|
_("Bank transaction %s has a residual amount, and a write-off account. Write off not implemented.") %
|
||||||
|
transaction.statement_line_id.name
|
||||||
|
)
|
||||||
# run the method that is appropriate for this match type
|
# run the method that is appropriate for this match type
|
||||||
reconcile_id = self.reconcile_map[transaction.match_type](
|
reconcile_id = self.reconcile_map[transaction.match_type](
|
||||||
self, cr, uid, transaction.id, context)
|
self, cr, uid, transaction.id, context)
|
||||||
@@ -1049,6 +1056,7 @@ class banking_import_transaction(osv.osv):
|
|||||||
transferred_amount = transaction.provision_costs,
|
transferred_amount = transaction.provision_costs,
|
||||||
remote_currency = transaction.provision_costs_currency,
|
remote_currency = transaction.provision_costs_currency,
|
||||||
message = transaction.provision_costs_description,
|
message = transaction.provision_costs_description,
|
||||||
|
parent_id = transaction.id,
|
||||||
), context)
|
), context)
|
||||||
|
|
||||||
injected.append(self.browse(cr, uid, cost_id, context))
|
injected.append(self.browse(cr, uid, cost_id, context))
|
||||||
@@ -1066,7 +1074,8 @@ class banking_import_transaction(osv.osv):
|
|||||||
provision_costs_currency = False,
|
provision_costs_currency = False,
|
||||||
provision_costs_description = False,
|
provision_costs_description = False,
|
||||||
), context=context)
|
), context=context)
|
||||||
|
# rebrowse the current record after writing
|
||||||
|
transaction=self.browse(cr, uid, transaction.id, context=context)
|
||||||
# Match full direct debit orders
|
# Match full direct debit orders
|
||||||
if transaction.type == bt.DIRECT_DEBIT:
|
if transaction.type == bt.DIRECT_DEBIT:
|
||||||
move_info = self._match_debit_order( # TODO reconcile preservation
|
move_info = self._match_debit_order( # TODO reconcile preservation
|
||||||
@@ -1154,9 +1163,9 @@ class banking_import_transaction(osv.osv):
|
|||||||
move_info, remainder = self._match_invoice(
|
move_info, remainder = self._match_invoice(
|
||||||
cr, uid, transaction, move_lines, partner_ids,
|
cr, uid, transaction, move_lines, partner_ids,
|
||||||
partner_banks, results['log'], linked_invoices,
|
partner_banks, results['log'], linked_invoices,
|
||||||
)
|
context=context)
|
||||||
if remainder:
|
if remainder:
|
||||||
injected.append(remainder)
|
injected.append(self.browse(cr, uid, remainder, context))
|
||||||
|
|
||||||
account_id = move_info and move_info.get('account_id', False)
|
account_id = move_info and move_info.get('account_id', False)
|
||||||
if not account_id:
|
if not account_id:
|
||||||
@@ -1269,6 +1278,38 @@ class banking_import_transaction(osv.osv):
|
|||||||
'id': 'transaction'
|
'id': 'transaction'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _get_residual(self, cr, uid, ids, name, args, context=None):
|
||||||
|
"""
|
||||||
|
Calculate the residual against the candidate reconciliation.
|
||||||
|
When
|
||||||
|
- residual > 0 and transferred amount > 0, or
|
||||||
|
- residual < 0 and transferred amount < 0
|
||||||
|
|
||||||
|
the result is a partial reconciliation. In the other cases,
|
||||||
|
a new statement line can be split off.
|
||||||
|
|
||||||
|
We should give users the option to reconcile with writeoff
|
||||||
|
or partial reconciliation / new statement line
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not ids:
|
||||||
|
return {}
|
||||||
|
res = dict([(x, False) for x in ids])
|
||||||
|
move_line_obj = self.pool.get('account.move.line')
|
||||||
|
for transaction in self.browse(cr, uid, ids, context):
|
||||||
|
if (transaction.match_type in
|
||||||
|
[('invoice'), ('move'), ('manual')]):
|
||||||
|
rec_moves = (
|
||||||
|
transaction.move_line_id.reconcile_id and
|
||||||
|
transaction.move_line_id.reconcile_id.line_id or
|
||||||
|
transaction.move_line_id.reconcile_partial_id and
|
||||||
|
transaction.move_line_id.reconcile_partial_id.line_partial_ids or
|
||||||
|
[transaction.move_line_id])
|
||||||
|
res[transaction.id] = (
|
||||||
|
move_line_obj.get_balance(cr, uid, [x.id for x in rec_moves])
|
||||||
|
- transaction.transferred_amount)
|
||||||
|
return res
|
||||||
|
|
||||||
def _get_match_multi(self, cr, uid, ids, name, args, context=None):
|
def _get_match_multi(self, cr, uid, ids, name, args, context=None):
|
||||||
"""
|
"""
|
||||||
Indicate in the wizard that multiple matches have been found
|
Indicate in the wizard that multiple matches have been found
|
||||||
@@ -1333,11 +1374,13 @@ class banking_import_transaction(osv.osv):
|
|||||||
'account.bank.statement.line', 'Statement line'),
|
'account.bank.statement.line', 'Statement line'),
|
||||||
'statement_id': fields.many2one(
|
'statement_id': fields.many2one(
|
||||||
'account.bank.statement', 'Statement'),
|
'account.bank.statement', 'Statement'),
|
||||||
|
'parent_id': fields.many2one(
|
||||||
|
'banking.import.transaction', 'Split off from this transaction'),
|
||||||
# match fields
|
# match fields
|
||||||
'match_type': fields.selection(
|
'match_type': fields.selection(
|
||||||
[('move','Move'), ('invoice', 'Invoice'), ('payment', 'Payment'),
|
[('manual', 'Manual'), ('move','Move'), ('invoice', 'Invoice'),
|
||||||
('payment_order', 'Payment order'), ('storno', 'Storno')],
|
('payment', 'Payment'), ('payment_order', 'Payment order'),
|
||||||
|
('storno', 'Storno')],
|
||||||
'Match type'),
|
'Match type'),
|
||||||
'match_multi': fields.function(
|
'match_multi': fields.function(
|
||||||
_get_match_multi, method=True, string='Multi match',
|
_get_match_multi, method=True, string='Multi match',
|
||||||
@@ -1359,6 +1402,12 @@ class banking_import_transaction(osv.osv):
|
|||||||
'invoice_id': fields.many2one(
|
'invoice_id': fields.many2one(
|
||||||
'account.invoice', 'Invoice to reconcile'),
|
'account.invoice', 'Invoice to reconcile'),
|
||||||
'payment_line_id': fields.many2one('payment.line', 'Payment line'),
|
'payment_line_id': fields.many2one('payment.line', 'Payment line'),
|
||||||
|
'residual': fields.function(
|
||||||
|
_get_residual, method=True, string='Residual', type='float'),
|
||||||
|
'writeoff_account_id': fields.many2one(
|
||||||
|
'account.account', 'Write off account'),
|
||||||
|
'writeoff_move_line_id': fields.many2one(
|
||||||
|
'account.move.line', 'Write off move line'),
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'company_id': lambda s,cr,uid,c:
|
'company_id': lambda s,cr,uid,c:
|
||||||
@@ -1377,6 +1426,9 @@ class account_bank_statement_line(osv.osv):
|
|||||||
'match_multi': fields.related(
|
'match_multi': fields.related(
|
||||||
'import_transaction_id', 'match_multi', type='boolean',
|
'import_transaction_id', 'match_multi', type='boolean',
|
||||||
string='Multi match'),
|
string='Multi match'),
|
||||||
|
'residual': fields.related(
|
||||||
|
'import_transaction_id', 'residual', type='float',
|
||||||
|
string='Residual'),
|
||||||
'duplicate': fields.related(
|
'duplicate': fields.related(
|
||||||
'import_transaction_id', 'duplicate', type='boolean',
|
'import_transaction_id', 'duplicate', type='boolean',
|
||||||
string='Possible duplicate import'),
|
string='Possible duplicate import'),
|
||||||
|
|||||||
@@ -113,12 +113,29 @@ class banking_transaction_wizard(osv.osv_memory):
|
|||||||
_("No entry found for the selected invoice. " +
|
_("No entry found for the selected invoice. " +
|
||||||
"Try manual reconciliation."))
|
"Try manual reconciliation."))
|
||||||
|
|
||||||
def select_match(self, cr, uid, ids, context=None):
|
def trigger_write(self, cr, uid, ids, context=None):
|
||||||
"""
|
"""
|
||||||
Just a button that triggers a write.
|
Just a button that triggers a write.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def disable_match(self, cr, uid, ids, context=None):
|
||||||
|
vals = (dict([(x, False) for x in [
|
||||||
|
'match_type',
|
||||||
|
'move_line_id',
|
||||||
|
'invoice_id',
|
||||||
|
'manual_invoice_id',
|
||||||
|
'manual_move_line_id',
|
||||||
|
'payment_line_id',
|
||||||
|
]] +
|
||||||
|
[(x, [(6, 0, [])]) for x in [
|
||||||
|
'move_line_ids',
|
||||||
|
'invoice_ids',
|
||||||
|
'payment_order_ids',
|
||||||
|
]]))
|
||||||
|
self.write(cr, uid, ids, vals, context=context)
|
||||||
|
return True
|
||||||
|
|
||||||
def reverse_duplicate(self, cr, uid, ids, context=None):
|
def reverse_duplicate(self, cr, uid, ids, context=None):
|
||||||
if isinstance(ids, (int, float)):
|
if isinstance(ids, (int, float)):
|
||||||
ids = [ids]
|
ids = [ids]
|
||||||
@@ -144,7 +161,7 @@ class banking_transaction_wizard(osv.osv_memory):
|
|||||||
return {'nodestroy': False, 'type': 'ir.actions.act_window_close'}
|
return {'nodestroy': False, 'type': 'ir.actions.act_window_close'}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'match_type': _get_default_match_type,
|
# 'match_type': _get_default_match_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
@@ -162,6 +179,10 @@ class banking_transaction_wizard(osv.osv_memory):
|
|||||||
'residual': fields.related(
|
'residual': fields.related(
|
||||||
'import_transaction_id', 'residual', type='float',
|
'import_transaction_id', 'residual', type='float',
|
||||||
string='Residual'),
|
string='Residual'),
|
||||||
|
'writeoff_account_id': fields.related(
|
||||||
|
'import_transaction_id', 'writeoff_account_id',
|
||||||
|
type='many2one', relation='account.account',
|
||||||
|
string='Write off account'),
|
||||||
'payment_line_id': fields.related(
|
'payment_line_id': fields.related(
|
||||||
'import_transaction_id', 'payment_line_id', string="Matching payment or storno",
|
'import_transaction_id', 'payment_line_id', string="Matching payment or storno",
|
||||||
type='many2one', relation='payment.line'),
|
type='many2one', relation='payment.line'),
|
||||||
@@ -189,10 +210,9 @@ class banking_transaction_wizard(osv.osv_memory):
|
|||||||
'match_multi': fields.related(
|
'match_multi': fields.related(
|
||||||
'import_transaction_id', 'match_multi',
|
'import_transaction_id', 'match_multi',
|
||||||
type="boolean", string='Multiple matches'),
|
type="boolean", string='Multiple matches'),
|
||||||
'match_type': fields.selection(
|
'match_type': fields.related(
|
||||||
[('manual', 'Manual'), ('move','Move'), ('invoice', 'Invoice'),
|
'import_transaction_id', 'match_type',
|
||||||
('payment', 'Payment'), ('payment_order', 'Payment order'),
|
type="char", size=16, string='Match type', readonly=True),
|
||||||
('storno', 'Storno')], 'Match type', readonly=True),
|
|
||||||
'manual_invoice_id': fields.many2one(
|
'manual_invoice_id': fields.many2one(
|
||||||
'account.invoice', 'Match this invoice',
|
'account.invoice', 'Match this invoice',
|
||||||
domain=[('state', '=', 'open')]),
|
domain=[('state', '=', 'open')]),
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
<field name="duplicate" invisible="True"/>
|
<field name="duplicate" invisible="True"/>
|
||||||
|
|
||||||
<group colspan="2" col="2">
|
<group colspan="2" col="2">
|
||||||
|
|
||||||
|
<!-- Duplicate flagging -->
|
||||||
<group colspan="2" col="2" attrs="{'invisible': [('duplicate', '=', False)]}">
|
<group colspan="2" col="2" attrs="{'invisible': [('duplicate', '=', False)]}">
|
||||||
<separator string="Duplicate flag"/>
|
<separator string="Duplicate flag"/>
|
||||||
<label colspan="2" string="This bank transfer was marked as a duplicate. You can either confirm that this is not the case, or remove the bank transfer from the system."/>
|
<label colspan="2" string="This bank transfer was marked as a duplicate. You can either confirm that this is not the case, or remove the bank transfer from the system."/>
|
||||||
@@ -24,29 +26,67 @@
|
|||||||
type="object"
|
type="object"
|
||||||
string="Remove duplicate flag"/>
|
string="Remove duplicate flag"/>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
|
<!-- (semi-) automatic matching and selection -->
|
||||||
|
|
||||||
<group colspan="2" col="2">
|
<group colspan="2" col="2">
|
||||||
|
|
||||||
<separator string="System Match" colspan="2"/>
|
<separator string="System Match" colspan="2"/>
|
||||||
<field name="match_type"/>
|
<field name="match_type"/>
|
||||||
<group attrs="{'invisible': [('match_multi', '=', False)]}">
|
<group attrs="{'invisible': [('match_multi', '=', False)]}">
|
||||||
<label colspan="2" string="Multiple matches were found for this bank transfer. You must pick one of the matches or select a match manually below." />
|
<label colspan="2" string="Multiple matches were found for this bank transfer. You must pick one of the matches or select a match manually below." />
|
||||||
</group>
|
</group>
|
||||||
<field name='payment_line_id'
|
<field name='payment_line_id'
|
||||||
attrs="{'invisible': [('match_type', 'not in', [('payment', 'storno')])]}"/>
|
attrs="{'invisible': [('match_type', 'not in', [('payment', 'storno')])]}"
|
||||||
|
/>
|
||||||
<group attrs="{'readonly': [('match_multi', '!=' True)]}">
|
<group attrs="{'readonly': [('match_multi', '!=' True)]}">
|
||||||
<field name='invoice_id'
|
<field name='invoice_id'
|
||||||
attrs="{'invisible': [('match_type', '!=', 'invoice')]}"
|
attrs="{'invisible': [('match_type', '!=', 'invoice')]}"
|
||||||
domain="[('id', 'in', invoice_ids[0][2])]"/>
|
domain="[('id', 'in', invoice_ids[0][2])]"
|
||||||
|
/>
|
||||||
<field name='move_line_id'
|
<field name='move_line_id'
|
||||||
attrs="{'invisible': [('match_type', '!=', 'move')]}"/>
|
attrs="{'invisible': [('match_type', '!=', 'move')]}"
|
||||||
|
domain="[('id', 'in', move_line_ids[0][2])]"
|
||||||
|
/>
|
||||||
<field name='payment_order_id'
|
<field name='payment_order_id'
|
||||||
attrs="{'invisible': [('match_type', '!=', 'payment_order')]}"/>
|
attrs="{'invisible': [('match_type', '!=', 'payment_order')]}"
|
||||||
|
domain="[('id', 'in', payment_order_ids[0][2])]"
|
||||||
|
/>
|
||||||
</group>
|
</group>
|
||||||
<button colspan="1"
|
<button colspan="1"
|
||||||
name="select_match"
|
name="trigger_write"
|
||||||
type="object"
|
type="object"
|
||||||
attrs="{'invisible': [('match_multi', '=', False)]}"
|
attrs="{'invisible': [('match_multi', '=', False)]}"
|
||||||
string="Select"/>
|
string="Select"/>
|
||||||
<newline/>
|
<newline/>
|
||||||
|
|
||||||
|
<!-- Manual selection -->
|
||||||
|
|
||||||
|
<separator string="Manual match" colspan="2"/>
|
||||||
|
<field name="manual_invoice_id"/>
|
||||||
|
<field name="manual_move_line_id"/>
|
||||||
|
<newline/>
|
||||||
|
<button colspan="1"
|
||||||
|
name="match_manual"
|
||||||
|
type="object"
|
||||||
|
string="Match"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- residual and write off -->
|
||||||
|
|
||||||
|
<group attrs="{'invisible': [('residual', '=', False)]}" colspan="2" col="2">
|
||||||
|
<separator string="Residual write-off"/>
|
||||||
|
<field name="residual"/><field name="writeoff_account_id"/>
|
||||||
|
<label string="You can set a write-off account for the residual of this reconciliation" colspan="2"/>
|
||||||
|
<button colspan="1"
|
||||||
|
name="trigger_write"
|
||||||
|
type="object"
|
||||||
|
string="Set write-off account"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- Redo automatic match -->
|
||||||
|
|
||||||
|
<group colspan="2" col="2">
|
||||||
<separator string="Retry system match"/>
|
<separator string="Retry system match"/>
|
||||||
<label string="You can let the system try to match this bank statement line again after you have made any changes in the database (for instance, add an invoice or a bank account)." colspan="2"/>
|
<label string="You can let the system try to match this bank statement line again after you have made any changes in the database (for instance, add an invoice or a bank account)." colspan="2"/>
|
||||||
<newline/>
|
<newline/>
|
||||||
@@ -55,14 +95,15 @@
|
|||||||
type="object"
|
type="object"
|
||||||
string="Match again"/>
|
string="Match again"/>
|
||||||
</group>
|
</group>
|
||||||
<separator string="Manual match" colspan="2"/>
|
<group attrs="{'invisible': [('match_type', '==', False)]}" colspan="2" col="2">
|
||||||
<field name="manual_invoice_id"/>
|
<separator string="Disable reconciliation"/>
|
||||||
<field name="manual_move_line_id"/>
|
<label string="You can disable the reconciliation of this bank transfer" colspan="2"/>
|
||||||
<newline/>
|
<newline/>
|
||||||
<button colspan="1"
|
<button colspan="1"
|
||||||
name="match_manual"
|
name="disable_match"
|
||||||
type="object"
|
type="object"
|
||||||
string="Match"/>
|
string="Disable reconciliation"/>
|
||||||
|
</group>
|
||||||
<group colspan="2">
|
<group colspan="2">
|
||||||
<separator/>
|
<separator/>
|
||||||
<button icon="gtk-ok" string="Done" special="cancel"/>
|
<button icon="gtk-ok" string="Done" special="cancel"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user