diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 139dcaf92a8..8060e55d74b 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -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): diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index 4c2998474f4..bfd1d69c01d 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -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):