From 3a94db44128e4c5346ef2018e7a4716aa499f893 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 3 May 2013 11:56:35 +0200 Subject: [PATCH] [FIX] Currency and UoM issues for accounting entries bzr revid: jco@openerp.com-20130503095635-laak8y0vs6y7wuwt --- addons/stock/stock.py | 5 +- addons/stock/wizard/stock_partial_move.py | 2 +- addons/stock/wizard/stock_partial_picking.py | 4 +- addons/stock_fifo_lifo/stock_fifo_lifo.py | 27 +++--- .../stock_fifo_lifo/test/fifolifo_price.yml | 84 +++++++++++++++++-- 5 files changed, 99 insertions(+), 23 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 9d3e3946b62..a5f8a24357f 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2263,7 +2263,7 @@ class stock_move(osv.osv): #I use if move.product_id.cost_method != 'standard' and move.price_unit: - reference_amount = qty * move.price_unit + reference_amount = move.product_qty * move.price_unit #Using move.price_qty instead of qty to have correct amount reference_currency_id = move.price_currency_id.id or reference_currency_id # Otherwise we default to the company's valuation price type, considering that the values of the @@ -2331,7 +2331,6 @@ class stock_move(osv.osv): account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_dest, acc_valuation, reference_amount, reference_currency_id, context))] else: account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_src, acc_valuation, reference_amount, reference_currency_id, context))] - move_obj = self.pool.get('account.move') for j_id, move_lines in account_moves: move_obj.create(cr, uid, @@ -2436,7 +2435,7 @@ class stock_move(osv.osv): # fix credit line: credit_line_vals['credit'] = cur_obj.compute(cr, uid, reference_currency_id, src_main_currency_id, reference_amount, context=context) if (not src_acct.currency_id) or src_acct.currency_id.id == reference_currency_id: - credit_line_vals.update(currency_id=reference_currency_id, amount_currency=reference_amount) + credit_line_vals.update(currency_id=reference_currency_id, amount_currency=-reference_amount) if reference_currency_id != dest_main_currency_id: # fix debit line: debit_line_vals['debit'] = cur_obj.compute(cr, uid, reference_currency_id, dest_main_currency_id, reference_amount, context=context) diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py index 89744615f22..5a979050f1b 100644 --- a/addons/stock/wizard/stock_partial_move.py +++ b/addons/stock/wizard/stock_partial_move.py @@ -75,7 +75,7 @@ class stock_partial_move(osv.osv_memory): 'prodlot_id': move.prodlot_id.id, } moves_ids.append(move_id) - if (move.move_id.picking_id.type == 'in') and (move.product_id.cost_method == 'average'): + if (move.move_id.picking_id.type == 'in') and (move.product_id.cost_method != 'standard'): partial_data['move%s' % (move_id)].update(product_price=move.cost, product_currency=move.currency.id) self.pool.get('stock.move').do_partial(cr, uid, moves_ids, partial_data, context=context) diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py index 577a5cb4d5a..b95c3760030 100644 --- a/addons/stock/wizard/stock_partial_picking.py +++ b/addons/stock/wizard/stock_partial_picking.py @@ -151,7 +151,7 @@ class stock_partial_picking(osv.osv_memory): 'location_id' : move.location_id.id, 'location_dest_id' : move.location_dest_id.id, } - if move.picking_id.type == 'in' and move.product_id.cost_method == 'average': + if move.picking_id.type == 'in' and move.product_id.cost_method != 'standard': partial_move.update(update_cost=True, **self._product_cost_for_average_update(cr, uid, move)) return partial_move @@ -208,7 +208,7 @@ class stock_partial_picking(osv.osv_memory): 'product_uom': wizard_line.product_uom.id, 'prodlot_id': wizard_line.prodlot_id.id, } - if (picking_type == 'in') and (wizard_line.product_id.cost_method == 'average'): + if (picking_type == 'in') and (wizard_line.product_id.cost_method != 'standard'): partial_data['move%s' % (wizard_line.move_id.id)].update(product_price=wizard_line.cost, product_currency=wizard_line.currency.id) stock_picking.do_partial(cr, uid, [partial.picking_id.id], partial_data, context=context) diff --git a/addons/stock_fifo_lifo/stock_fifo_lifo.py b/addons/stock_fifo_lifo/stock_fifo_lifo.py index 6c0b46a9a0b..75dd3ca322b 100644 --- a/addons/stock_fifo_lifo/stock_fifo_lifo.py +++ b/addons/stock_fifo_lifo/stock_fifo_lifo.py @@ -40,7 +40,8 @@ class product_product (osv.osv): def get_stock_matchings_fifolifo(self, cr, uid, ids, qty, fifo, product_uom_id = False, currency_id = False, context=None): ''' - This method returns a list of tuples for which the stock moves are working fifo/lifo + This method returns a list of tuples for which the stock moves are working fifo/lifo: + (move_in_id, qty in uom of move out, price (converted to move out), qty in uom of move in This should be called for only one product at a time UoMs and currencies from the corresponding moves are converted towards that given in the params ''' @@ -64,33 +65,34 @@ class product_product (osv.osv): currency_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.id if fifo: - move_ids = move_obj.search(cr, uid, [('company_id','=', company_id), ('qty_remaining', '>', 0), ('state', '=', 'done'), + move_in_ids = move_obj.search(cr, uid, [('company_id','=', company_id), ('qty_remaining', '>', 0), ('state', '=', 'done'), ('type', '=', 'in'), ('product_id', '=', product.id)], order = 'date', context=context) else: - move_ids = move_obj.search(cr, uid, [('company_id','=', company_id), ('qty_remaining', '>', 0), ('state', '=', 'done'), + move_in_ids = move_obj.search(cr, uid, [('company_id','=', company_id), ('qty_remaining', '>', 0), ('state', '=', 'done'), ('type', '=', 'in'), ('product_id', '=', product.id)], order = 'date desc', context=context) tuples = [] qty_to_go = qty - for move in move_obj.browse(cr, uid, move_ids, context=context): + for move in move_obj.browse(cr, uid, move_in_ids, context=context): #Convert to UoM of product each time uom_from = move.product_uom.id qty_from = move.product_qty product_qty = uom_obj._compute_qty(cr, uid, uom_from, qty_from, product_uom_id) #Convert currency from in move currency id to out move currency - if move.price_currency_id and (move.price_currency_id.id != currency_id): - new_price = currency_obj.compute(cr, uid, move.price_currency_id, currency_id, + if move.price_currency_id and (move.price_currency_id.id != currency_id): + new_price = currency_obj.compute(cr, uid, move.price_currency_id.id, currency_id, move.price_unit) + else: new_price = move.price_unit new_price = uom_obj._compute_price(cr, uid, uom_from, new_price, product_uom_id) if qty_to_go - product_qty >= 0: - tuples.append((move.id, product_qty, new_price),) + tuples.append((move.id, product_qty, new_price, qty_from),) qty_to_go -= product_qty else: - tuples.append((move.id, qty_to_go, new_price),) + tuples.append((move.id, qty_to_go, new_price, qty_from * qty_to_go / product_qty),) break return tuples @@ -154,10 +156,11 @@ class stock_move(osv.osv): match_id = matching_obj.create(cr, uid, matchvals, context=context) move_in = self.browse(cr, uid, match[0], context=context) #Reduce remaining quantity - self.write(cr, uid, match[0], { 'qty_remaining': move_in.qty_remaining - match[1]}, context=context) + self.write(cr, uid, match[0], { 'qty_remaining': move_in.qty_remaining - match[3]}, context=context) price_amount += match[1] * match[2] amount += match[1] self.write(cr, uid, move.id, {'price_unit': price_amount / amount}, context=context) + #This price has to be put as the new standard price for the product, but needs to be converted to product UoM and currency #convert uom of qty uom_id = product.uom_id.id @@ -174,8 +177,10 @@ class stock_move(osv.osv): product_obj.write(cr, uid, product.id, {'standard_price': new_price / product_qty}, context=ctx) # When the move is products returned to supplier or return products from customer # then the price should be the price from the original move - if move.move_returned_from and cost_method in ['fifo', 'lifo']: - self.write(cr, uid, move.id, {'price_unit': move.move_returned_from.price_unit}, context=context) + elif cost_method in ['fifo', 'lifo']: #if move.move_returned_from and cost_method in ['fifo', 'lifo']: + self.write(cr, uid, [move.id], + {'price_unit': product_price, + 'price_currency_id': product_currency}) return True class stock_move_matching(osv.osv): diff --git a/addons/stock_fifo_lifo/test/fifolifo_price.yml b/addons/stock_fifo_lifo/test/fifolifo_price.yml index bcfd1c6e3a6..9c7aded4b28 100644 --- a/addons/stock_fifo_lifo/test/fifolifo_price.yml +++ b/addons/stock_fifo_lifo/test/fifolifo_price.yml @@ -11,13 +11,13 @@ uom_id: product.product_uom_kgm uom_po_id: product.product_uom_kgm procure_method: make_to_stock - valuation: real_time cost_method: fifo + valuation: real_time property_stock_account_input: account.o_expense property_stock_account_output: account.o_income description: FIFO Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events. - - I create a draft Purchase Order for first in move for 10 pieces at 60 euro + I create a draft Purchase Order for first in move for 10 kg at 60 euro - !record {model: purchase.order, id: purchase_order_fifo1}: partner_id: base.res_partner_3 @@ -30,7 +30,7 @@ price_unit: 50.0 name: 'FIFO Ice Cream' - - I create a draft Purchase Order for second shipment for 30 pieces at 80 euro + I create a draft Purchase Order for second shipment for 30 kg at 80 euro - !record {model: purchase.order, id: purchase_order_fifo2}: partner_id: base.res_partner_3 @@ -103,9 +103,9 @@ !python {model: stock.partial.picking}: | partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment")], 'default_type':'out'}) self.do_partial(cr, uid, [partial_id]) -- +- Check 2 stock move matchings were created -- +- !python {model: stock.picking}: | assert len(self.browse(cr, uid, ref("outgoing_fifo_shipment")).move_lines[0].matching_ids_out) == 2, 'Should have created 2 matchings' - @@ -146,4 +146,76 @@ Check product price changed to 80.0 - !python {model: product.product}: | - assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 80.0 \ No newline at end of file + assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 80.0 +- + Create new pricelist in usd +- + !record {model: product.pricelist, id: supplier_pricelist_usd}: + name: Supplier Pricelist + type: purchase + currency_id: base.USD + version_id: + - name: v1.0 + items_id: + - name: 20% Discount given by my supplier + sequence: 1 + price_min_margin: 2 + price_max_margin: 5 + product_id: product_fifo_icecream + base: -2 +- + Purchase order in usd +- + I create a draft Purchase Order for 30000 g at 0.150 USD +- + !record {model: purchase.order, id: purchase_order_fifo_usd}: + partner_id: base.res_partner_3 + location_id: stock.stock_location_stock + pricelist_id: supplier_pricelist_usd + order_line: + - product_id: product_fifo_icecream + product_qty: 30000 + product_uom: product.product_uom_gram + price_unit: 0.150 + name: 'FIFO Ice Cream' +- + I confirm the first purchase order +- + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_fifo_usd} +- + Process the reception of purchase order with usd +- + !python {model: stock.partial.picking}: | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_usd")).picking_ids + partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) + self.do_partial(cr, uid, [partial_id]) +- + We create delivery order of 49500 gram +- + !record {model: stock.picking, id: outgoing_fifo_shipment_cur}: + type: out +- + Picking needs movement from stock +- + !record {model: stock.move, id: outgoing_shipment_fifo_icecream_cur}: + picking_id: outgoing_fifo_shipment_cur + product_id: product_fifo_icecream + product_uom: product.product_uom_gram + product_qty: 49500 + type: out +- + Process the delivery of the outgoing shipment +- + !python {model: stock.partial.picking}: | + partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_cur")], 'default_type':'out'}) + self.do_partial(cr, uid, [partial_id]) +- + Print price +- + !python {model: product.product}: | + print self.browse(cr, uid, ref("product_fifo_icecream")).standard_price + print self.browse(cr, uid, ref("product_fifo_icecream")).qty_available + list = self.pool.get("stock.move.matching").search(cr, uid, []) + for move in self.pool.get("stock.move.matching").browse(cr, uid, list): + print move.price_unit + print move.qty