[FIX] cascading deletes for banking_import_transaction and

account_bank_statement_line didn't work
[ADD] function to split off some amount from a statement line
[ADD] unsplit transactions/statement lines when deleting split ones
[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:
Holger Brunn
2013-04-16 18:57:20 +02:00
parent c3b5974fd0
commit 822a32cbe9
3 changed files with 189 additions and 52 deletions

View File

@@ -1587,6 +1587,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',
@@ -1638,7 +1654,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(
@@ -1709,7 +1725,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),
@@ -1730,6 +1746,8 @@ class account_bank_statement_line(osv.osv):
'state': fields.selection(
[('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
readonly=True, required=True),
'parent_id': fields.many2one('account.bank.statement.line',
'Parent'),
}
_defaults = {
@@ -1872,6 +1890,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]
@@ -1881,6 +1901,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)
@@ -1920,6 +1946,46 @@ 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
child_statement_ids.append(
self.create(cr, uid, statement_line_data,
context=context))
this.write({'amount': this.amount - amount})
return child_statement_ids
account_bank_statement_line()
class account_bank_statement(osv.osv):

View File

@@ -96,6 +96,8 @@ class banking_transaction_wizard(osv.osv_memory):
# 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 +169,96 @@ 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_id or manual_invoice_id \
or 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 = (
([manual_invoice_id] if manual_invoice_id else []) +
[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 = (
([manual_move_line_id] if manual_move_line_id else []) +
[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.credit or move_line.debit)[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 +290,21 @@ 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)
wiz.write({'partner_id': False})
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",
@@ -356,8 +409,18 @@ class banking_transaction_wizard(osv.osv_memory):
'manual_move_line_id': fields.many2one(
'account.move.line', 'Or match this entry',
domain=[('account_id.reconcile', '=', True),
('reconcile_id', '=', False)],
),
('reconcile_id', '=', False)]),
'manual_invoice_ids': fields.many2many(
'account.invoice',
'banking_transaction_wizard_account_invoice_rel',
'wizard_id', 'invoice_id', string='Match following invoices',
domain=[('reconciled', '=', False)]),
'manual_move_line_ids': fields.many2many(
'account.move.line',
'banking_transaction_wizard_account_move_line_rel',
'wizard_id', 'move_line_id', string='Or match this entries',
domain=[('account_id.reconcile', '=', True),
('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(

View File

@@ -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"/>
@@ -114,6 +115,13 @@
type="object"
string="Match"/>
</page>
<page string="Multiple manual matches" attrs="{'invisible': ['|', ('match_type', '!=', False), ('statement_line_parent_id', '!=', False)]}">
<field name="manual_invoice_ids" colspan="4"/>
<field name="manual_move_line_ids" colspan="4"/>
<button name="trigger_write"
type="object"
string="Match" />
</page>
<page string="Write-Off" attrs="{'invisible': [('match_type', '=', False)]}">
<group colspan="2" col="2">
<label string="Choose what you want to do with the eventual difference between the paid amount and the sum of allocated amounts. You can either choose to keep open this difference on the partner's account, or reconcile it with the payment." colspan="2"/>