[FIX] stock_account, purchase: updating the standard price of products in cost method == 'average' on receptions

bzr revid: qdp-launchpad@openerp.com-20130905103435-ch505yi7qyvxz0pz
This commit is contained in:
Quentin (OpenERP) 2013-09-05 12:34:35 +02:00
commit 70d2733d1b
4 changed files with 52 additions and 29 deletions

View File

@ -685,7 +685,11 @@ class purchase_order(osv.osv):
def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, context=None):
''' prepare the stock move data from the PO line '''
type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'picking_type_in')[1]
type = self.pool.get("stock.picking.type").browse(cr, uid, type_id, context=context)
price_unit = order_line.price_unit
if order_line.product_uom.id != order_line.product_id.uom_id.id:
price_unit *= order_line.product_uom.factor
if order.currency_id.id != order.company_id.currency_id.id:
price_unit = self.pool.get('res.currency').compute(cr, uid, order.currency_id.id, order.company_id.currency_id.id, price_unit, context=context)
return {
'name': order_line.name or '',
@ -704,7 +708,7 @@ class purchase_order(osv.osv):
'state': 'draft',
'purchase_line_id': order_line.id,
'company_id': order.company_id.id,
'price_unit': order_line.price_unit,
'price_unit': price_unit,
'picking_type_id': type_id,
}

View File

@ -26,28 +26,11 @@
product_uom: product.product_uom_categ_kgm
price_unit: 60.0
name: 'Average Ice Cream'
-
I create a draft Purchase Order for second incoming shipment for 30 pieces at 80€
-
!record {model: purchase.order, id: purchase_order_average2}:
partner_id: base.res_partner_3
location_id: stock.stock_location_stock
pricelist_id: 1
order_line:
- product_id: product_average_icecream
product_qty: 30.0
product_uom: product.product_uom_categ_kgm
price_unit: 80.0
name: 'Average Ice Cream'
-
I confirm the first purchase order
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_average1}
-
I confirm the second purchase order
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_average2}
-
I check the "Approved" status of purchase order 1
-
!assert {model: purchase.order, id: purchase_order_average1}:
@ -66,6 +49,23 @@
assert product.qty_available == 10.0, 'Wrong quantity in stock after first reception'
assert product.standard_price == 60.0, 'Standard price should be the price of the first reception!'
-
I create a draft Purchase Order for second incoming shipment for 30 pieces at 80€
-
!record {model: purchase.order, id: purchase_order_average2}:
partner_id: base.res_partner_3
location_id: stock.stock_location_stock
pricelist_id: 1
order_line:
- product_id: product_average_icecream
product_qty: 30.0
product_uom: product.product_uom_categ_kgm
price_unit: 80.0
name: 'Average Ice Cream'
-
I confirm the second purchase order
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_average2}
-
Process the reception of purchase order 2
-
!python {model: stock.picking}: |
@ -88,7 +88,9 @@
picking_id: outgoing_average_shipment
product_id: product_average_icecream
product_uom: product.product_uom_kgm
product_qty: 20.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
product_uom_qty: 20.0
-
I assign this outgoing shipment and process the delivery
-
@ -102,7 +104,7 @@
assert self.browse(cr, uid, ref("product_average_icecream")).standard_price == 75.0, 'Standard price should not have changed with outgoing picking!'
assert self.browse(cr, uid, ref("product_average_icecream")).qty_available == 20.0, 'Pieces were not picked correctly as the quantity on hand is wrong'
-
Make a new purchase order with 500 g Average Ice Cream at a price of 100 TODO the unit_price is 1 here
Make a new purchase order with 500 g Average Ice Cream at a price of 0.2€/g
-
!record {model: purchase.order, id: purchase_order_average3}:
partner_id: base.res_partner_3
@ -112,7 +114,7 @@
- product_id: product_average_icecream
product_qty: 500.0
product_uom: product.product_uom_gram
price_unit: 1.0
price_unit: 0.2
name: 'Average Ice Cream'
-
I confirm the first purchase order
@ -125,9 +127,9 @@
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_average3")).picking_ids
self.do_partial(cr, uid, [pick_ids[0].id])
-
Check price is (75.0*20 + 500) / 20.5 = 97.56097561
Check price is (75.0*20 + 200*0.5) / 20.5 = 78.04878
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref("product_average_icecream"))
assert product.qty_available == 20.5, 'Reception of purchase order in grams leads to wrong quantity in stock'
assert round(self.browse(cr, uid, ref("product_average_icecream")).standard_price, 2) == 97.56, 'Standard price as average price of third reception with other UoM incorrect!'
assert round(self.browse(cr, uid, ref("product_average_icecream")).standard_price, 2) == 78.05, 'Standard price as average price of third reception with other UoM incorrect! Got %s instead of 78.05' % (round(self.browse(cr, uid, ref("product_average_icecream")).standard_price, 2),)

View File

@ -980,13 +980,13 @@ class stock_move(osv.osv):
_log_create = False
def get_price_unit(self, cr, uid, move, context=None):
""" Returns the unit price to store on the move """
""" Returns the unit price to store on the quant """
return move.price_unit or move.product_id.standard_price
def name_get(self, cr, uid, ids, context=None):
res = []
for line in self.browse(cr, uid, ids, context=context):
name = line.location_id.name+' > '+line.location_dest_id.name
name = line.location_id.name + ' > ' + line.location_dest_id.name
if line.product_id.code:
name = line.product_id.code + ': ' + name
if line.picking_id.origin:
@ -1126,8 +1126,7 @@ class stock_move(osv.osv):
"* Available: When products are reserved, it is set to \'Available\'.\n"\
"* Done: When the shipment is processed, the state is \'Done\'."),
'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"), # as it's a technical field, we intentionally don't provide the digits attribute
'price_currency_id': fields.many2one('res.currency', 'Currency for average price', help="Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used)"),
'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used). Value given in company currency and in product uom."), # as it's a technical field, we intentionally don't provide the digits attribute
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Order of", select=True),
@ -2421,6 +2420,7 @@ class stock_picking_type(osv.osv):
'sequence_id': fields.many2one('ir.sequence', 'Sequence', required=True),
'default_location_src_id': fields.many2one('stock.location', 'Default Source Location'),
'default_location_dest_id': fields.many2one('stock.location', 'Default Destination Location'),
#TODO: change field name to "code" as it's not a many2one anymore
'code_id': fields.selection([('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')], 'Picking type code', required=True),
'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),

View File

@ -206,10 +206,27 @@ class stock_move(osv.osv):
def product_price_update(self, cr, uid, ids, context=None):
'''
This method adapts the price on the product when necessary (if the cost_method is 'real'), so that a return or an inventory loss is made using the last value used for an outgoing valuation.
This method adapts the price on the product when necessary
'''
product_obj = self.pool.get('product.product')
for move in self.browse(cr, uid, ids, context=context):
#adapt standard price on incomming moves if the product cost_method is 'average'
if (move.location_id.usage == 'supplier') and (move.product_id.cost_method == 'average'):
product = move.product_id
company_currency_id = move.company_id.currency_id.id
ctx = {'currency_id': company_currency_id}
product_avail = product.qty_available
if product.qty_available <= 0:
new_std_price = move.price_unit
else:
# Get the standard price
amount_unit = product.price_get('standard_price', context=ctx)[product.id]
new_std_price = (amount_unit * (product_avail - move.product_qty) + (move.price_unit * move.product_qty)) / product_avail
# Write the field according to price type field
product_obj.write(cr, uid, [product.id], {'standard_price': new_std_price}, context=context)
#adapt standard price on outgoing moves if the product cost_method is 'real', so that a return
#or an inventory loss is made using the last value used for an outgoing valuation.
if move.product_id.cost_method == 'real' and move.location_dest_id.usage != 'internal':
if any([q.qty <= 0 for q in move.quant_ids]):
#if there is a negative quant, the standard price shouldn't be updated