diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 4d42b22c65b..3b0f638263d 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -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, } diff --git a/addons/purchase/test/average_price.yml b/addons/purchase/test/average_price.yml index 79bae838f7e..f39a23076f0 100644 --- a/addons/purchase/test/average_price.yml +++ b/addons/purchase/test/average_price.yml @@ -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!' \ No newline at end of file + 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),) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index a41547ac74f..65e6cdf598e 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -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'), diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index ecb14b1d814..b4d94433f31 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -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