mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[ADD] allow to manually match multiple invoices/move lines causing the
statement line to be split according to the amounts given
This commit is contained in:
@@ -48,6 +48,9 @@
|
||||
'wizard/link_partner.xml',
|
||||
'workflow/account_invoice.xml',
|
||||
],
|
||||
'js': [
|
||||
'static/src/js/account_banking.js',
|
||||
],
|
||||
'demo_xml': [],
|
||||
'external_dependencies': {
|
||||
'python' : ['BeautifulSoup'],
|
||||
|
||||
@@ -299,9 +299,12 @@
|
||||
<xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='amount']" position="after">
|
||||
<field name="match_type"/>
|
||||
<field name="residual"/>
|
||||
<button name="match_wizard" states="draft"
|
||||
<field name="parent_id" invisible="1" />
|
||||
<button name="match_wizard"
|
||||
string="Match"
|
||||
icon="terp-gtk-jump-to-ltr"
|
||||
attrs="{'invisible': ['|', ('parent_id', '!=', False),
|
||||
('state', '!=', 'draft')]}"
|
||||
type="object"/>
|
||||
<field name="match_multi" invisible="1"/>
|
||||
<field name="duplicate" invisible="1"/>
|
||||
@@ -537,9 +540,12 @@
|
||||
<field name="amount"/>
|
||||
<field name="match_type"/>
|
||||
<field name="residual"/>
|
||||
<button name="match_wizard" states="draft"
|
||||
<field name="parent_id" invisible="1" />
|
||||
<button name="match_wizard"
|
||||
string="Match"
|
||||
icon="terp-gtk-jump-to-ltr"
|
||||
attrs="{'invisible': ['|', ('parent_id', '!=', False),
|
||||
('state', '!=', 'draft')]}"
|
||||
type="object"/>
|
||||
<field name="match_multi" invisible="1"/>
|
||||
<field name="duplicate" invisible="1"/>
|
||||
|
||||
@@ -1474,7 +1474,7 @@ class banking_import_transaction(osv.osv):
|
||||
not(transaction.move_currency_amount is False)):
|
||||
res[transaction.id] = (
|
||||
transaction.move_currency_amount -
|
||||
transaction.transferred_amount
|
||||
transaction.statement_line_id.amount
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -1561,6 +1561,22 @@ class banking_import_transaction(osv.osv):
|
||||
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Unsplit if this if a split transaction
|
||||
"""
|
||||
for this in self.browse(cr, uid, ids, context):
|
||||
if this.parent_id:
|
||||
this.parent_id.write(
|
||||
{'transferred_amount':
|
||||
this.parent_id.transferred_amount + \
|
||||
this.transferred_amount,
|
||||
})
|
||||
this.parent_id.refresh()
|
||||
return super(banking_import_transaction, self).unlink(
|
||||
cr, uid, ids, context=context)
|
||||
|
||||
|
||||
column_map = {
|
||||
# used in bank_import.py, converting non-osv transactions
|
||||
'statement_id': 'statement',
|
||||
@@ -1612,7 +1628,7 @@ class banking_import_transaction(osv.osv):
|
||||
'duplicate': fields.boolean('duplicate'),
|
||||
'statement_line_id': fields.many2one(
|
||||
'account.bank.statement.line', 'Statement line',
|
||||
ondelete='CASCADE'),
|
||||
ondelete='cascade'),
|
||||
'statement_id': fields.many2one(
|
||||
'account.bank.statement', 'Statement'),
|
||||
'parent_id': fields.many2one(
|
||||
@@ -1701,7 +1717,7 @@ class account_bank_statement_line(osv.osv):
|
||||
_columns = {
|
||||
'import_transaction_id': fields.many2one(
|
||||
'banking.import.transaction',
|
||||
'Import transaction', readonly=True, delete='cascade'),
|
||||
'Import transaction', readonly=True, ondelete='cascade'),
|
||||
'match_multi': fields.related(
|
||||
'import_transaction_id', 'match_multi', type='boolean',
|
||||
string='Multi match', readonly=True),
|
||||
@@ -1725,6 +1741,8 @@ class account_bank_statement_line(osv.osv):
|
||||
'link_partner_ok': fields.function(
|
||||
_get_link_partner_ok, type='boolean',
|
||||
string='Can link partner'),
|
||||
'parent_id': fields.many2one('account.bank.statement.line',
|
||||
'Parent'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
@@ -1927,6 +1945,8 @@ class account_bank_statement_line(osv.osv):
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Don't allow deletion of a confirmed statement line
|
||||
If this statement line comes from a split transaction, give the
|
||||
amount back
|
||||
"""
|
||||
if type(ids) is int:
|
||||
ids = [ids]
|
||||
@@ -1936,6 +1956,12 @@ class account_bank_statement_line(osv.osv):
|
||||
_('Confirmed Statement Line'),
|
||||
_("You cannot delete a confirmed Statement Line"
|
||||
": '%s'" % line.name))
|
||||
if line.parent_id:
|
||||
line.parent_id.write(
|
||||
{
|
||||
'amount': line.parent_id.amount + line.amount,
|
||||
})
|
||||
line.parent_id.refresh()
|
||||
return super(account_bank_statement_line, self).unlink(
|
||||
cr, uid, ids, context=context)
|
||||
|
||||
@@ -1975,6 +2001,50 @@ class account_bank_statement_line(osv.osv):
|
||||
'import_transaction_id': res},
|
||||
context=context)
|
||||
|
||||
def split_off(self, cr, uid, ids, amount, context=None):
|
||||
"""
|
||||
Create a child statement line with amount, deduce that from this line,
|
||||
change transactions accordingly
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
transaction_pool = self.pool.get('banking.import.transaction')
|
||||
|
||||
child_statement_ids = []
|
||||
for this in self.browse(cr, uid, ids, context):
|
||||
transaction_data = transaction_pool.copy_data(
|
||||
cr, uid, this.import_transaction_id.id)
|
||||
transaction_data['transferred_amount'] = amount
|
||||
transaction_data['message'] = (
|
||||
(transaction_data['message'] or '') + _(' (split)'))
|
||||
transaction_data['parent_id'] = this.import_transaction_id.id
|
||||
transaction_id = transaction_pool.create(
|
||||
cr,
|
||||
uid,
|
||||
transaction_data,
|
||||
context=dict(
|
||||
context, transaction_no_duplicate_search=True))
|
||||
|
||||
statement_line_data = self.copy_data(
|
||||
cr, uid, this.id)
|
||||
statement_line_data['amount'] = amount
|
||||
statement_line_data['name'] = (
|
||||
(statement_line_data['name'] or '') + _(' (split)'))
|
||||
statement_line_data['import_transaction_id'] = transaction_id
|
||||
statement_line_data['parent_id'] = this.id
|
||||
statement_line_id = self.create(
|
||||
cr, uid, statement_line_data, context=context)
|
||||
|
||||
child_statement_ids.append(statement_line_id)
|
||||
transaction_pool.write(
|
||||
cr, uid, transaction_id, {
|
||||
'statement_line_id': statement_line_id,
|
||||
}, context=context)
|
||||
this.write({'amount': this.amount - amount})
|
||||
|
||||
return child_statement_ids
|
||||
|
||||
account_bank_statement_line()
|
||||
|
||||
class account_bank_statement(osv.osv):
|
||||
|
||||
@@ -139,9 +139,9 @@ msgid "remote_bank_bic"
|
||||
msgstr "remote_bank_bic"
|
||||
|
||||
#. module: account_banking
|
||||
#: field:banking.transaction.wizard,manual_invoice_id:0
|
||||
msgid "Match this invoice"
|
||||
msgstr "Match deze factuur"
|
||||
#: field:banking.transaction.wizard,manual_invoice_ids:0
|
||||
msgid "Match one or more invoices"
|
||||
msgstr "Match een of meerdere facturen"
|
||||
|
||||
#. module: account_banking
|
||||
#: field:banking.import.transaction,remote_bank_ibei:0
|
||||
@@ -1022,9 +1022,9 @@ msgid "Review"
|
||||
msgstr "Herzien"
|
||||
|
||||
#. module: account_banking
|
||||
#: field:banking.transaction.wizard,manual_move_line_id:0
|
||||
msgid "Or match this entry"
|
||||
msgstr "Of koppel deze boekingsregel"
|
||||
#: field:banking.transaction.wizard,manual_move_line_ids:0
|
||||
msgid "Or match one or more entries"
|
||||
msgstr "Of koppel deze boekingsregel(s)"
|
||||
|
||||
#. module: account_banking
|
||||
#: help:payment.mode.type,name:0
|
||||
|
||||
51
account_banking/static/src/js/account_banking.js
Normal file
51
account_banking/static/src/js/account_banking.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (C) 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# WARNING: This program as such is intended to be used by professional
|
||||
# programmers who take the whole responsability of assessing all potential
|
||||
# consequences resulting from its eventual inadequacies and bugs
|
||||
# End users who are looking for a ready-to-use solution with commercial
|
||||
# garantees and support are strongly adviced to contract EduSense BV
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
openerp.account_banking = function(openerp)
|
||||
{
|
||||
var _t = openerp.web._t;
|
||||
openerp.web.Dialog.include(
|
||||
{
|
||||
on_close: function()
|
||||
{
|
||||
this._super.apply(this, arguments);
|
||||
if(this.dialog_title == _t("Match transaction"))
|
||||
{
|
||||
if(this.widget_parent.widget_children[0].views.form.controller)
|
||||
{
|
||||
this.widget_parent.widget_children[0].views.form.controller.reload();
|
||||
}
|
||||
if(this.widget_parent.widget_children[0].views.page.controller)
|
||||
{
|
||||
this.widget_parent.widget_children[0].views.page.controller.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -94,8 +94,8 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
# The following fields get never written
|
||||
# they are just triggers for manual matching
|
||||
# which populates regular fields on the transaction
|
||||
manual_invoice_id = vals.pop('manual_invoice_id', False)
|
||||
manual_move_line_id = vals.pop('manual_move_line_id', False)
|
||||
manual_invoice_ids = vals.pop('manual_invoice_ids', [])
|
||||
manual_move_line_ids = vals.pop('manual_move_line_ids', [])
|
||||
|
||||
# Support for writing fields.related is still flakey:
|
||||
# https://bugs.launchpad.net/openobject-server/+bug/915975
|
||||
@@ -167,55 +167,93 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
_("No entry found for the selected invoice. " +
|
||||
"Try manual reconciliation."))
|
||||
|
||||
if manual_move_line_id or manual_invoice_id:
|
||||
if manual_move_line_ids or manual_invoice_ids:
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
invoice_obj = self.pool.get('account.invoice')
|
||||
statement_line_obj = self.pool.get('account.bank.statement.line')
|
||||
for wiz in self.browse(
|
||||
cr, uid, ids, context=context):
|
||||
move_line_id = False
|
||||
invoice_id = manual_invoice_id
|
||||
if invoice_id:
|
||||
invoice = invoice_obj.browse(
|
||||
cr, uid, manual_invoice_id, context=context)
|
||||
manual_invoice_ids = (
|
||||
[i[1] for i in manual_invoice_ids if i[0]==4] +
|
||||
[j for i in manual_invoice_ids if i[0]==6 for j in i[2]])
|
||||
manual_move_line_ids = (
|
||||
[i[1] for i in manual_move_line_ids if i[0]==4] +
|
||||
[j for i in manual_move_line_ids if i[0]==6 for j in i[2]])
|
||||
for wiz in self.browse(cr, uid, ids, context=context):
|
||||
#write can be called multiple times for the same values
|
||||
#that doesn't hurt above, but it does here
|
||||
if wiz.match_type and (
|
||||
len(manual_move_line_ids) > 1 or
|
||||
len(manual_invoice_ids) > 1):
|
||||
continue
|
||||
|
||||
todo = []
|
||||
|
||||
for invoice in invoice_obj.browse(
|
||||
cr, uid, manual_invoice_ids, context=context):
|
||||
found_move_line = False
|
||||
if invoice.move_id:
|
||||
for line in invoice.move_id.line_id:
|
||||
if line.account_id.type in ('receivable', 'payable'):
|
||||
move_line_id = line.id
|
||||
todo.append((invoice.id, line.id))
|
||||
found_move_line = True
|
||||
break
|
||||
if not move_line_id:
|
||||
osv.except_osv(
|
||||
if not found_move_line:
|
||||
raise osv.except_osv(
|
||||
_("Cannot select for reconcilion"),
|
||||
_("No entry found for the selected invoice. "))
|
||||
else:
|
||||
move_line_id = manual_move_line_id
|
||||
move_line = move_line_obj.read(
|
||||
cr, uid, move_line_id, ['invoice'], context=context)
|
||||
invoice_id = (move_line['invoice'] and
|
||||
move_line['invoice'][0])
|
||||
vals = {
|
||||
'move_line_id': move_line_id,
|
||||
'move_line_ids': [(6, 0, [move_line_id])],
|
||||
'invoice_id': invoice_id,
|
||||
'invoice_ids': [(6, 0, invoice_id and
|
||||
[invoice_id] or [])],
|
||||
'match_type': 'manual',
|
||||
}
|
||||
transaction_obj.clear_and_write(
|
||||
cr, uid, wiz.import_transaction_id.id,
|
||||
vals, context=context)
|
||||
st_line_vals = {
|
||||
'account_id': move_line_obj.read(
|
||||
cr, uid, move_line_id,
|
||||
['account_id'], context=context)['account_id'][0],
|
||||
}
|
||||
if invoice_id:
|
||||
st_line_vals['partner_id'] = invoice_obj.read(
|
||||
cr, uid, invoice_id,
|
||||
['partner_id'], context=context)['partner_id'][0]
|
||||
statement_line_obj.write(
|
||||
cr, uid, wiz.import_transaction_id.statement_line_id.id,
|
||||
st_line_vals, context=context)
|
||||
for move_line_id in manual_move_line_ids:
|
||||
todo_entry = [False, move_line_id]
|
||||
move_line=move_line_obj.read(
|
||||
cr,
|
||||
uid,
|
||||
move_line_id,
|
||||
['invoice'],
|
||||
context=context)
|
||||
if move_line['invoice']:
|
||||
todo_entry[0] = move_line['invoice'][0]
|
||||
todo.append(todo_entry)
|
||||
|
||||
while todo:
|
||||
todo_entry = todo.pop()
|
||||
move_line = move_line_obj.browse(
|
||||
cr, uid, todo_entry[1], context)
|
||||
transaction_id = wiz.import_transaction_id.id
|
||||
statement_line_id = wiz.statement_line_id.id
|
||||
|
||||
if len(todo) > 0:
|
||||
statement_line_id = wiz.statement_line_id.split_off(
|
||||
move_line.debit or -move_line.credit)[0]
|
||||
transaction_id = statement_line_obj.browse(
|
||||
cr,
|
||||
uid,
|
||||
statement_line_id,
|
||||
context=context).import_transaction_id.id
|
||||
|
||||
vals = {
|
||||
'move_line_id': todo_entry[1],
|
||||
'move_line_ids': [(6, 0, [todo_entry[1]])],
|
||||
'invoice_id': todo_entry[0],
|
||||
'invoice_ids': [(6, 0,
|
||||
[todo_entry[0]] if todo_entry[0] else [])],
|
||||
'match_type': 'manual',
|
||||
}
|
||||
|
||||
transaction_obj.clear_and_write(
|
||||
cr, uid, transaction_id, vals, context=context)
|
||||
|
||||
st_line_vals = {
|
||||
'account_id': move_line_obj.read(
|
||||
cr, uid, todo_entry[1],
|
||||
['account_id'], context=context)['account_id'][0],
|
||||
}
|
||||
|
||||
if todo_entry[0]:
|
||||
st_line_vals['partner_id'] = invoice_obj.read(
|
||||
cr, uid, todo_entry[0],
|
||||
['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
statement_line_obj.write(
|
||||
cr, uid, statement_line_id,
|
||||
st_line_vals, context=context)
|
||||
return res
|
||||
|
||||
def trigger_write(self, cr, uid, ids, context=None):
|
||||
@@ -247,14 +285,26 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
account_id = setting.default_debit_account_id and setting.default_debit_account_id.id
|
||||
statement_pool.write(cr, uid, wiz.statement_line_id.id, {'account_id':account_id})
|
||||
|
||||
self.write(cr, uid, wiz.id, {'partner_id': False}, context=context)
|
||||
# Restore partner id from the bank account or else reset
|
||||
partner_id = False
|
||||
if (wiz.statement_line_id.partner_bank_id and
|
||||
wiz.statement_line_id.partner_bank_id.partner_id):
|
||||
partner_id = wiz.statement_line_id.partner_bank_id.partner_id.id
|
||||
wiz.write({'partner_id': partner_id})
|
||||
|
||||
if wiz.statement_line_id:
|
||||
#delete splits causing an unsplit if this is a split
|
||||
#transaction
|
||||
statement_pool.unlink(cr, uid,
|
||||
statement_pool.search(cr, uid,
|
||||
[('parent_id', '=', wiz.statement_line_id.id)],
|
||||
context=context),
|
||||
context=context)
|
||||
|
||||
if wiz.import_transaction_id:
|
||||
wiz.import_transaction_id.clear_and_write()
|
||||
|
||||
|
||||
wizs = self.read(
|
||||
cr, uid, ids, ['import_transaction_id'], context=context)
|
||||
trans_ids = [x['import_transaction_id'][0] for x in wizs
|
||||
if x['import_transaction_id']]
|
||||
self.pool.get('banking.import.transaction').clear_and_write(
|
||||
cr, uid, trans_ids, context=context)
|
||||
return True
|
||||
|
||||
def reverse_duplicate(self, cr, uid, ids, context=None):
|
||||
@@ -281,7 +331,7 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
return res
|
||||
|
||||
def button_done(self, cr, uid, ids, context=None):
|
||||
return {'nodestroy': False, 'type': 'ir.actions.act_window_close'}
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
_defaults = {
|
||||
# 'match_type': _get_default_match_type,
|
||||
@@ -305,6 +355,9 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
'statement_line_id', 'partner_id',
|
||||
type='many2one', relation='res.partner',
|
||||
string="Partner", readonly=True),
|
||||
'statement_line_parent_id': fields.related(
|
||||
'statement_line_id', 'parent_id', type='many2one',
|
||||
relation='account.bank.statement.line', readonly=True),
|
||||
'import_transaction_id': fields.related(
|
||||
'statement_line_id', 'import_transaction_id',
|
||||
string="Import transaction",
|
||||
@@ -350,14 +403,17 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
'match_type': fields.related(
|
||||
'import_transaction_id', 'match_type',
|
||||
type="char", size=16, string='Match type', readonly=True),
|
||||
'manual_invoice_id': fields.many2one(
|
||||
'account.invoice', 'Match this invoice',
|
||||
'manual_invoice_ids': fields.many2many(
|
||||
'account.invoice',
|
||||
'banking_transaction_wizard_account_invoice_rel',
|
||||
'wizard_id', 'invoice_id', string='Match one or more invoices',
|
||||
domain=[('reconciled', '=', False)]),
|
||||
'manual_move_line_id': fields.many2one(
|
||||
'account.move.line', 'Or match this entry',
|
||||
'manual_move_line_ids': fields.many2many(
|
||||
'account.move.line',
|
||||
'banking_transaction_wizard_account_move_line_rel',
|
||||
'wizard_id', 'move_line_id', string='Or match one or more entries',
|
||||
domain=[('account_id.reconcile', '=', True),
|
||||
('reconcile_id', '=', False)],
|
||||
),
|
||||
('reconcile_id', '=', False)]),
|
||||
'payment_option': fields.related('import_transaction_id','payment_option', string='Payment Difference', type='selection', required=True,
|
||||
selection=[('without_writeoff', 'Keep Open'),('with_writeoff', 'Reconcile Payment Balance')]),
|
||||
'writeoff_analytic_id': fields.related(
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<form string="Match transaction">
|
||||
<!-- fields used for form logic -->
|
||||
<field name="payment_order_ids" invisible="True"/>
|
||||
<field name="statement_line_parent_id" invisible="True"/>
|
||||
<field name="invoice_ids" invisible="True"/>
|
||||
<field name="move_line_ids" invisible="True"/>
|
||||
<field name="match_multi" invisible="True"/>
|
||||
@@ -89,30 +90,18 @@
|
||||
name="trigger_match"
|
||||
type="object"
|
||||
string="Match again"/>
|
||||
<!-- Manual selection -->
|
||||
</page>
|
||||
<!-- Manual selection -->
|
||||
<page string="Manual match">
|
||||
<field name="manual_invoice_id"/>
|
||||
<!--
|
||||
Specify alternative tree_view_ref as a
|
||||
workaround for lp:1073521 in OpenERP 6.1
|
||||
Need to also define 'view_mode' to prevent
|
||||
an instant editable tree view
|
||||
reconstruction by account.move.line's
|
||||
fields_view_get().
|
||||
Both are not needed in OpenERP 6.0 or 7.0.
|
||||
-->
|
||||
<field name="manual_move_line_id"
|
||||
context="{
|
||||
'tree_view_ref': 'account.view_move_line_tax_tree',
|
||||
'view_mode': 'yes'
|
||||
}"
|
||||
<field name="manual_invoice_ids" colspan="4"
|
||||
context="{'search_default_partner_id': partner_id}"
|
||||
/>
|
||||
<newline/>
|
||||
<button colspan="1"
|
||||
name="trigger_write"
|
||||
<field name="manual_move_line_ids" colspan="4"
|
||||
context="{'search_default_partner_id': partner_id}"
|
||||
/>
|
||||
<button name="trigger_write"
|
||||
type="object"
|
||||
string="Match"/>
|
||||
string="Match" />
|
||||
</page>
|
||||
<page string="Write-Off" attrs="{'invisible': [('match_type', '=', False)]}">
|
||||
<group colspan="2" col="2">
|
||||
@@ -139,7 +128,7 @@
|
||||
</notebook>
|
||||
<group colspan="2">
|
||||
<separator/>
|
||||
<button icon="gtk-ok" string="Done" special="cancel"/>
|
||||
<button icon="gtk-ok" string="Close" special="cancel"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user