[IMP] purchase: invoice state of PO line calculated from invoice state of move

Before this fix, a PO line was set as invoiced if all associated invoices were
validated. This is an issue if the invoice_method of a PO is set on 'picking',
and that only a partial quantity is received and invoiced. Indeed, in this case
it was never possible to meet the condition, and the PO could not be set to
'done'.

After this fix, the PO line invoice state is calculated from the invoice state
of the related moves, if the invoice_method of a PO is set on 'picking'. This
is more logical, and moreover this makes possible to set a PO line as invoiced
as soon as all the related moves are done or cancelled.

opw-644399
This commit is contained in:
Nicolas Martinelli 2015-07-31 16:18:03 +02:00
parent a6831546c1
commit 9b1ab76eb2
2 changed files with 36 additions and 17 deletions

View File

@ -990,6 +990,30 @@ class purchase_order(osv.osv):
return orders_info
def _set_po_lines_invoiced(self, cr, uid, ids, context=None):
for po in self.browse(cr, uid, ids, context=context):
is_invoiced = []
if po.invoice_method == 'picking':
# We determine the invoiced state of the PO line based on the invoiced state
# of the associated moves. This should cover all possible cases:
# - all moves are done and invoiced
# - a PO line is split into multiple moves (e.g. if multiple pickings): some
# pickings are done, some are in progress, some are cancelled
for po_line in po.order_line:
if (po_line.move_ids and
all(move.state in ('done', 'cancel') for move in po_line.move_ids) and
not all(move.state == 'cancel' for move in po_line.move_ids) and
all(move.invoice_state == 'invoiced' for move in po_line.move_ids if move.state == 'done')):
is_invoiced.append(po_line.id)
else:
for po_line in po.order_line:
if (po_line.invoice_lines and
all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines)):
is_invoiced.append(po_line.id)
if is_invoiced:
self.pool['purchase.order.line'].write(cr, uid, is_invoiced, {'invoiced': True})
workflow.trg_write(uid, 'purchase.order', po.id, cr)
class purchase_order_line(osv.osv):
def _amount_line(self, cr, uid, ids, prop, arg, context=None):
@ -1583,22 +1607,9 @@ class account_invoice(osv.Model):
else:
user_id = uid
po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context)
for order in purchase_order_obj.browse(cr, user_id, po_ids, context=context):
purchase_order_obj.message_post(cr, user_id, order.id, body=_("Invoice received"), context=context)
invoiced = []
shipped = True
# for invoice method manual or order, don't care about shipping state
# for invoices based on incoming shippment, beware of partial deliveries
if (order.invoice_method == 'picking' and
not all(picking.invoice_state in ['invoiced'] for picking in order.picking_ids)):
shipped = False
for po_line in order.order_line:
if (po_line.invoice_lines and
all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines)):
invoiced.append(po_line.id)
if invoiced and shipped:
self.pool['purchase.order.line'].write(cr, user_id, invoiced, {'invoiced': True})
workflow.trg_write(user_id, 'purchase.order', order.id, cr)
for po_id in po_ids:
purchase_order_obj.message_post(cr, user_id, po_id, body=_("Invoice received"), context=context)
purchase_order_obj._set_po_lines_invoiced(cr, user_id, [po_id], context=context)
return res
def confirm_paid(self, cr, uid, ids, context=None):

View File

@ -44,15 +44,23 @@ class stock_move(osv.osv):
res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
from openerp import workflow
if vals.get('state') in ['done', 'cancel']:
po_to_check = []
for move in self.browse(cr, uid, ids, context=context):
if move.purchase_line_id and move.purchase_line_id.order_id:
order_id = move.purchase_line_id.order_id.id
order = move.purchase_line_id.order_id
order_id = order.id
# update linked purchase order as superuser as the warehouse
# user may not have rights to access purchase.order
if self.pool.get('purchase.order').test_moves_done(cr, uid, [order_id], context=context):
workflow.trg_validate(SUPERUSER_ID, 'purchase.order', order_id, 'picking_done', cr)
if self.pool.get('purchase.order').test_moves_except(cr, uid, [order_id], context=context):
workflow.trg_validate(SUPERUSER_ID, 'purchase.order', order_id, 'picking_cancel', cr)
if order_id not in po_to_check and vals['state'] == 'cancel' and order.invoice_method == 'picking':
po_to_check.append(order_id)
# Some moves which are cancelled might be part of a PO line which is partially
# invoiced, so we check if some PO line can be set on "invoiced = True".
if po_to_check:
self.pool.get('purchase.order')._set_po_lines_invoiced(cr, uid, po_to_check, context=context)
return res
def copy(self, cr, uid, id, default=None, context=None):