[FIX] purchase: update and cancel of purchase order line takes into account existing procurements

Update:
When an existing purchase order line is updated, for example if a new procurement was required,
we check if the sum of existing running linked procurements is larger than the quantity in the
purchase order. If it is the case, we update the quantity and the unit price accordingly.
Without this fix, we systematically add the procurement quantity (or the provider minimum quantity)
to the purchase order line, even if the sum of the procurements is smaller than the ordered
quantity.

Cancel:
When a procurement is cancelled, we recompute the unit price of the associated purchase order
according to the quantity and make sure the provider minimum quantity is met. If the required
quantity is zero, we cancel and unlink the purchase order line.

opw: 632175 and 632176
This commit is contained in:
Nicolas Martinelli 2015-04-29 16:36:27 +02:00
parent 5b6dcbb3a8
commit 610a461099
1 changed files with 46 additions and 8 deletions

View File

@ -1199,17 +1199,17 @@ class procurement_order(osv.osv):
def propagate_cancel(self, cr, uid, procurement, context=None):
if procurement.rule_id.action == 'buy' and procurement.purchase_line_id:
uom_obj = self.pool.get("product.uom")
purchase_line_obj = self.pool.get('purchase.order.line')
uom = procurement.purchase_line_id.product_uom
product_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, uom, context=context)
if procurement.purchase_line_id.state not in ('draft', 'cancel'):
raise osv.except_osv(_('Error!'),
_('Can not cancel this procurement as the related purchase order has been confirmed already. Please cancel the purchase order first. '))
if float_compare(procurement.purchase_line_id.product_qty, product_qty, 0, precision_rounding=uom.rounding) > 0:
purchase_line_obj.write(cr, uid, [procurement.purchase_line_id.id], {'product_qty': procurement.purchase_line_id.product_qty - product_qty}, context=context)
else:
new_qty, new_price = self._calc_new_qty_price(cr, uid, procurement, cancel=True, context=context)
if new_qty != procurement.purchase_line_id.product_qty:
purchase_line_obj.write(cr, uid, [procurement.purchase_line_id.id], {'product_qty': new_qty, 'price_unit': new_price}, context=context)
if float_compare(new_qty, 0.0, precision_rounding=procurement.product_uom.rounding) != 1:
purchase_line_obj.action_cancel(cr, uid, [procurement.purchase_line_id.id], context=context)
purchase_line_obj.unlink(cr, uid, [procurement.purchase_line_id.id], context=context)
return super(procurement_order, self).propagate_cancel(cr, uid, procurement, context=context)
def _run(self, cr, uid, procurement, context=None):
@ -1339,6 +1339,41 @@ class procurement_order(osv.osv):
'taxes_id': [(6, 0, taxes)],
}
def _calc_new_qty_price(self, cr, uid, procurement, po_line=None, cancel=False, context=None):
if not po_line:
po_line = procurement.purchase_line_id
uom_obj = self.pool.get('product.uom')
qty = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty,
procurement.product_id.uom_po_id.id)
if cancel:
qty = -qty
# Make sure we use the minimum quantity of the partner corresponding to the PO
if po_line.product_id.seller_id.id == po_line.order_id.partner_id.id:
supplierinfo_min_qty = po_line.product_id.seller_qty
else:
supplierinfo_obj = self.pool.get('product.supplierinfo')
supplierinfo_ids = supplierinfo_obj.search(cr, uid, [('name', '=', po_line.order_id.partner_id.id), ('product_tmpl_id', '=', po_line.product_id.product_tmpl_id.id)])
supplierinfo_min_qty = supplierinfo_obj.browse(cr, uid, supplierinfo_ids).min_qty
if supplierinfo_min_qty == 0.0:
qty += po_line.product_qty
else:
# Recompute quantity by adding existing running procurements.
for proc in po_line.procurement_ids:
qty += uom_obj._compute_qty(cr, uid, proc.product_uom.id, proc.product_qty,
proc.product_id.uom_po_id.id) if proc.state == 'running' else 0.0
qty = max(qty, supplierinfo_min_qty) if qty > 0.0 else 0.0
price = po_line.price_unit
if qty != po_line.product_qty:
pricelist_obj = self.pool.get('product.pricelist')
pricelist_id = po_line.order_id.partner_id.property_product_pricelist_purchase.id
price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, po_line.order_id.partner_id.id, {'uom': procurement.product_uom.id})[pricelist_id]
return qty, price
def make_po(self, cr, uid, ids, context=None):
""" Resolve the purchase from procurement, which may result in a new PO creation, a new PO line creation or a quantity change on existing PO line.
Note that some operations (as the PO creation) are made as SUPERUSER because the current user may not have rights to do it (mto product launched by a sale for example)
@ -1376,9 +1411,12 @@ class procurement_order(osv.osv):
available_po_line_ids = po_line_obj.search(cr, uid, [('order_id', '=', po_id), ('product_id', '=', line_vals['product_id']), ('product_uom', '=', line_vals['product_uom'])], context=context)
if available_po_line_ids:
po_line = po_line_obj.browse(cr, uid, available_po_line_ids[0], context=context)
po_line_obj.write(cr, SUPERUSER_ID, po_line.id, {'product_qty': po_line.product_qty + line_vals['product_qty']}, context=context)
po_line_id = po_line.id
sum_po_line_ids.append(procurement.id)
new_qty, new_price = self._calc_new_qty_price(cr, uid, procurement, po_line=po_line, context=context)
if new_qty > po_line.product_qty:
po_line_obj.write(cr, SUPERUSER_ID, po_line.id, {'product_qty': new_qty, 'price_unit': new_price}, context=context)
sum_po_line_ids.append(procurement.id)
else:
line_vals.update(order_id=po_id)
po_line_id = po_line_obj.create(cr, SUPERUSER_ID, line_vals, context=context)