[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:
parent
a6831546c1
commit
9b1ab76eb2
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue