[FIX] sale: the `invoiced` field on sale.order.line should be a function field

Having a static boolean field is a mess and requires
a lot of custom code to make sure it is up-to-date,
implying a large source of possible bugs.
Using a function field should take care of updating
it when needed.
All the write() calls that were updating it were
normally removed, and there was also some cleanup
in the surrounding areas (quite minimal).

bzr revid: odo@openerp.com-20121129162629-5lt9e0v5aju36itj
This commit is contained in:
Olivier Dony 2012-11-29 17:26:29 +01:00
parent 0ad3703b18
commit 8782163830
4 changed files with 26 additions and 62 deletions

View File

@ -530,55 +530,17 @@ class sale_order(osv.osv):
return res
def action_invoice_cancel(self, cr, uid, ids, context=None):
if context is None:
context = {}
for sale in self.browse(cr, uid, ids, context=context):
for line in sale.order_line:
#
# Check if the line is invoiced (has asociated invoice
# lines from non-cancelled invoices).
#
invoiced = False
for iline in line.invoice_lines:
if iline.invoice_id and iline.invoice_id.state != 'cancel':
invoiced = True
break
# Update the line (only when needed)
if line.invoiced != invoiced:
self.pool.get('sale.order.line').write(cr, uid, [line.id], {'invoiced': invoiced}, context=context)
self.write(cr, uid, ids, {'state': 'invoice_except'}, context=context)
return True
def action_invoice_end(self, cr, uid, ids, context=None):
for order in self.browse(cr, uid, ids, context=context):
#
# Update the sale order lines state (and invoiced flag).
#
for line in order.order_line:
vals = {}
#
# Check if the line is invoiced (has asociated invoice
# lines from non-cancelled invoices).
#
invoiced = False
for iline in line.invoice_lines:
if iline.invoice_id and iline.invoice_id.state != 'cancel':
invoiced = True
break
if line.invoiced != invoiced:
vals['invoiced'] = invoiced
# If the line was in exception state, now it gets confirmed.
for this in self.browse(cr, uid, ids, context=context):
for line in this.order_line:
if line.state == 'exception':
vals['state'] = 'confirmed'
# Update the line (only when needed).
if vals:
self.pool.get('sale.order.line').write(cr, uid, [line.id], vals, context=context)
#
# Update the sales order state.
#
if order.state == 'invoice_except':
self.write(cr, uid, [order.id], {'state': 'progress'}, context=context)
self.invoice_paid_send_note(cr, uid, [order.id], context=context)
line.write({'state': 'confirmed'})
if this.state == 'invoice_except':
this.write({'state': 'progress'})
this.invoice_paid_send_note()
return True
def action_cancel(self, cr, uid, ids, context=None):
@ -732,6 +694,20 @@ class sale_order_line(osv.osv):
except Exception, ex:
return False
def _fnct_line_invoiced(self, cr, uid, ids, field_name, args, context=None):
res = dict.fromkeys(ids, False)
for this in self.browse(cr, uid, ids, context=context):
res[this.id] = this.invoice_lines and \
all(iline.invoice_id.state != 'cancel' for iline in this.invoice_lines)
return res
def _order_lines_from_invoice(self, cr, uid, ids, context=None):
# direct access to the m2m table is the less convoluted way to achieve this (and is ok ACL-wise)
cr.execute("""SELECT DISTINCT sol.id FROM sale_order_invoice_rel rel JOIN
sale_order_line sol ON (sol.order_id = rel.order_id)
WHERE rel.invoice_id = ANY(%s)""", (list(ids),))
return [i[0] for i in cr.fetchall()]
_name = 'sale.order.line'
_description = 'Sales Order Line'
_columns = {
@ -740,7 +716,8 @@ class sale_order_line(osv.osv):
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
'invoiced': fields.boolean('Invoiced', readonly=True),
'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean',
store={'account.invoice': (_order_lines_from_invoice, ['state'], 10)}),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}),
'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]},
help="From stock: When needed, the product is taken from the stock or we wait for replenishment.\nOn order: When needed, the product is purchased or produced."),
@ -770,7 +747,6 @@ class sale_order_line(osv.osv):
'product_uom_qty': 1,
'product_uos_qty': 1,
'sequence': 10,
'invoiced': 0,
'state': 'draft',
'type': 'make_to_stock',
'price_unit': 0.0,
@ -853,7 +829,6 @@ class sale_order_line(osv.osv):
if vals:
inv_id = self.pool.get('account.invoice.line').create(cr, uid, vals, context=context)
cr.execute('insert into sale_order_line_invoice_rel (order_line_id,invoice_id) values (%s,%s)', (line.id, inv_id))
self.write(cr, uid, [line.id], {'invoiced': True})
sales.add(line.order_id.id)
create_ids.append(inv_id)
# Trigger workflow events
@ -901,7 +876,7 @@ class sale_order_line(osv.osv):
def copy_data(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({'state': 'draft', 'invoiced': False, 'invoice_lines': []})
default.update({'state': 'draft', 'invoice_lines': []})
return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,

View File

@ -75,5 +75,5 @@
for invoice in sale_order.invoice_ids:
if invoice.state != 'cancel':
total_order_line += len(invoice.invoice_line)
assert total_order_line == 2, "wrong number of invoice lines"
assert total_order_line == 2, "wrong number of invoice lines, got %s" % total_order_line

View File

@ -88,8 +88,6 @@ class sale_order_line_make_invoice(osv.osv_memory):
[line.id])
for lid in line_id:
invoices[line.order_id.id].append((line, lid))
sales_order_line_obj.write(cr, uid, [line.id],
{'invoiced': True})
for result in invoices.values():
order = result[0][0].order_id
il = map(lambda x: x[1], result)

View File

@ -100,14 +100,8 @@ class stock_picking(osv.osv):
return super(stock_picking, self)._get_account_analytic_invoice(cursor, user, picking, move_line)
def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id):
sale_line_obj = self.pool.get('sale.order.line')
invoice_line_obj = self.pool.get('account.invoice.line')
if move_line.sale_line_id:
sale_line_obj.write(cursor, user, [move_line.sale_line_id.id],
{
'invoiced': True,
'invoice_lines': [(4, invoice_line_id)],
})
move_line.sale_line_id.write({'invoice_lines': [(4, invoice_line_id)]})
return super(stock_picking, self)._invoice_line_hook(cursor, user, move_line, invoice_line_id)
def _invoice_hook(self, cursor, user, picking, invoice_id):
@ -180,10 +174,7 @@ class stock_picking(osv.osv):
vals['account_analytic_id'] = self._get_account_analytic_invoice(cursor, user, picking, sale_line)
vals['invoice_id'] = invoices[result[picking.id]].id
invoice_line_id = invoice_line_obj.create(cursor, user, vals, context=context)
order_line_obj.write(cursor, user, [sale_line.id], {
'invoiced': True,
'invoice_lines': [(6, 0, [invoice_line_id])],
})
sale_line.write({'invoice_lines': [(4, invoice_line_id)]})
return result
# Redefinition of the new field in order to update the model stock.picking.out in the orm