[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:
commit
70d2733d1b
|
@ -685,7 +685,11 @@ class purchase_order(osv.osv):
|
||||||
def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, context=None):
|
def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, context=None):
|
||||||
''' prepare the stock move data from the PO line '''
|
''' 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_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 {
|
return {
|
||||||
'name': order_line.name or '',
|
'name': order_line.name or '',
|
||||||
|
@ -704,7 +708,7 @@ class purchase_order(osv.osv):
|
||||||
'state': 'draft',
|
'state': 'draft',
|
||||||
'purchase_line_id': order_line.id,
|
'purchase_line_id': order_line.id,
|
||||||
'company_id': order.company_id.id,
|
'company_id': order.company_id.id,
|
||||||
'price_unit': order_line.price_unit,
|
'price_unit': price_unit,
|
||||||
'picking_type_id': type_id,
|
'picking_type_id': type_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,28 +26,11 @@
|
||||||
product_uom: product.product_uom_categ_kgm
|
product_uom: product.product_uom_categ_kgm
|
||||||
price_unit: 60.0
|
price_unit: 60.0
|
||||||
name: 'Average Ice Cream'
|
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
|
I confirm the first purchase order
|
||||||
-
|
-
|
||||||
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_average1}
|
!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
|
I check the "Approved" status of purchase order 1
|
||||||
-
|
-
|
||||||
!assert {model: purchase.order, id: purchase_order_average1}:
|
!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.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!'
|
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
|
Process the reception of purchase order 2
|
||||||
-
|
-
|
||||||
!python {model: stock.picking}: |
|
!python {model: stock.picking}: |
|
||||||
|
@ -88,7 +88,9 @@
|
||||||
picking_id: outgoing_average_shipment
|
picking_id: outgoing_average_shipment
|
||||||
product_id: product_average_icecream
|
product_id: product_average_icecream
|
||||||
product_uom: product.product_uom_kgm
|
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
|
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")).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'
|
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}:
|
!record {model: purchase.order, id: purchase_order_average3}:
|
||||||
partner_id: base.res_partner_3
|
partner_id: base.res_partner_3
|
||||||
|
@ -112,7 +114,7 @@
|
||||||
- product_id: product_average_icecream
|
- product_id: product_average_icecream
|
||||||
product_qty: 500.0
|
product_qty: 500.0
|
||||||
product_uom: product.product_uom_gram
|
product_uom: product.product_uom_gram
|
||||||
price_unit: 1.0
|
price_unit: 0.2
|
||||||
name: 'Average Ice Cream'
|
name: 'Average Ice Cream'
|
||||||
-
|
-
|
||||||
I confirm the first purchase order
|
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
|
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])
|
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}: |
|
!python {model: product.product}: |
|
||||||
product = self.browse(cr, uid, ref("product_average_icecream"))
|
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 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),)
|
||||||
|
|
|
@ -980,13 +980,13 @@ class stock_move(osv.osv):
|
||||||
_log_create = False
|
_log_create = False
|
||||||
|
|
||||||
def get_price_unit(self, cr, uid, move, context=None):
|
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
|
return move.price_unit or move.product_id.standard_price
|
||||||
|
|
||||||
def name_get(self, cr, uid, ids, context=None):
|
def name_get(self, cr, uid, ids, context=None):
|
||||||
res = []
|
res = []
|
||||||
for line in self.browse(cr, uid, ids, context=context):
|
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:
|
if line.product_id.code:
|
||||||
name = line.product_id.code + ': ' + name
|
name = line.product_id.code + ': ' + name
|
||||||
if line.picking_id.origin:
|
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"\
|
"* Available: When products are reserved, it is set to \'Available\'.\n"\
|
||||||
"* Done: When the shipment is processed, the state is \'Done\'."),
|
"* 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_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
|
||||||
'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)"),
|
|
||||||
|
|
||||||
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
|
'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),
|
'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),
|
'sequence_id': fields.many2one('ir.sequence', 'Sequence', required=True),
|
||||||
'default_location_src_id': fields.many2one('stock.location', 'Default Source Location'),
|
'default_location_src_id': fields.many2one('stock.location', 'Default Source Location'),
|
||||||
'default_location_dest_id': fields.many2one('stock.location', 'Default Destination 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),
|
'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'),
|
'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'),
|
||||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
||||||
|
|
|
@ -206,10 +206,27 @@ class stock_move(osv.osv):
|
||||||
|
|
||||||
def product_price_update(self, cr, uid, ids, context=None):
|
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')
|
product_obj = self.pool.get('product.product')
|
||||||
for move in self.browse(cr, uid, ids, context=context):
|
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 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 any([q.qty <= 0 for q in move.quant_ids]):
|
||||||
#if there is a negative quant, the standard price shouldn't be updated
|
#if there is a negative quant, the standard price shouldn't be updated
|
||||||
|
|
Loading…
Reference in New Issue