From dce11788fa969a470a4e33f005a66930e880309d Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Fri, 8 Aug 2014 10:58:14 +0200 Subject: [PATCH] [FIX] stock_landed_costs: fixed several issues related to the computation and the accounting entries creation. Added a yaml test to cover the different split methods --- addons/stock_landed_costs/__openerp__.py | 1 + .../stock_landed_costs/stock_landed_costs.py | 72 +++++------ .../stock_landed_costs_view.xml | 7 +- .../test/stock_landed_costs.yml | 114 ++++++++++++++++++ 4 files changed, 150 insertions(+), 44 deletions(-) create mode 100644 addons/stock_landed_costs/test/stock_landed_costs.yml diff --git a/addons/stock_landed_costs/__openerp__.py b/addons/stock_landed_costs/__openerp__.py index 9ab5bc7023d..dc15e254503 100644 --- a/addons/stock_landed_costs/__openerp__.py +++ b/addons/stock_landed_costs/__openerp__.py @@ -44,6 +44,7 @@ This module allows you to easily add extra costs on pickings and decide the spli 'stock_landed_costs_data.xml', ], 'test': [ + 'test/stock_landed_costs.yml' ], 'installable': True, 'application': True, diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 16a292fab8a..5298924313e 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -51,17 +51,11 @@ class stock_landed_cost(osv.osv): cost_to_recompute.append(line.cost_id.id) return cost_to_recompute - def onchange_pickings(self, cr, uid, ids, picking_ids=None, context=None): - result = {'valuation_adjustment_lines': []} - line_obj = self.pool.get('stock.valuation.adjustment.lines') + def get_valuation_lines(self, cr, uid, ids, picking_ids=None, context=None): picking_obj = self.pool.get('stock.picking') lines = [] - for cost in self.browse(cr, uid, ids, context=context): - line_ids = [line.id for line in cost.valuation_adjustment_lines] - line_obj.unlink(cr, uid, line_ids, context=context) - picking_ids = picking_ids and picking_ids[0][2] or False if not picking_ids: - return {'value': result} + return lines for picking in picking_obj.browse(cr, uid, picking_ids): for move in picking.move_lines: @@ -74,10 +68,11 @@ class stock_landed_cost(osv.osv): volume = move.product_id and move.product_id.volume * move.product_qty for quant in move.quant_ids: total_cost += quant.cost - vals = dict(product_id=move.product_id.id, move_id=move.id, quantity=move.product_uom_qty, former_cost=total_cost * total_qty, weight=weight, volume=volume, flag='original') + vals = dict(product_id=move.product_id.id, move_id=move.id, quantity=move.product_uom_qty, former_cost=total_cost * total_qty, weight=weight, volume=volume) lines.append(vals) - result['valuation_adjustment_lines'] = lines - return {'value': result} + if not lines: + raise osv.except_osv(_('Error!'), _('The selected picking does not contain any move that would be impacted by landed costs. Landed costs are only possible for products configured in real time valuation with real price costing method. Please make sure it is the case, or you selected the correct picking')) + return lines _columns = { 'name': fields.char('Name', track_visibility='always', readonly=True, copy=False), @@ -177,34 +172,33 @@ class stock_landed_cost(osv.osv): def compute_landed_cost(self, cr, uid, ids, context=None): line_obj = self.pool.get('stock.valuation.adjustment.lines') + unlink_ids = line_obj.search(cr, uid, [('cost_id', 'in', ids)], context=context) + line_obj.unlink(cr, uid, unlink_ids, context=context) + towrite_dict = {} for cost in self.browse(cr, uid, ids, context=None): + if not cost.picking_ids: + continue + picking_ids = [p.id for p in cost.picking_ids] total_qty = 0.0 total_cost = 0.0 total_weight = 0.0 total_volume = 0.0 total_line = 0.0 - for line in cost.valuation_adjustment_lines: - if line.flag == 'original': - total_qty += line.quantity - total_cost += line.former_cost - total_weight += line.weight - total_volume += line.volume - total_line += 1 - - unlink_ids = line_obj.search(cr, uid, [('cost_id', 'in', ids), ('flag', '=', 'duplicate')], context=context) - line_obj.unlink(cr, uid, unlink_ids, context=context) - for cost in self.browse(cr, uid, ids, context=None): - count = 0.0 for line in cost.cost_lines: - count += 1 - for valuation in cost.valuation_adjustment_lines: - if count == 1: - line_obj.write(cr, uid, valuation.id, {'cost_line_id': line.id}, context=context) - continue - line_obj.copy(cr, uid, valuation.id, default={'cost_line_id': line.id, 'flag': 'duplicate'}, context=context) - - for cost in self.browse(cr, uid, ids, context=None): - dict = {} + vals = self.get_valuation_lines(cr, uid, [cost.id], picking_ids=picking_ids, context=context) + for v in vals: + v.update({'cost_id': cost.id, 'cost_line_id': line.id}) + self.pool.get('stock.valuation.adjustment.lines').create(cr, uid, v, context=context) + if line.split_method == 'by_quantity': + total_qty += v.get('quantity', 0.0) + elif line.split_method == 'by_current_cost_price': + total_cost += v.get('former_cost', 0.0) + elif line.split_method == 'by_weight': + total_weight += v.get('weight', 0.0) + elif line.split_method == 'by_volume': + total_volume += v.get('volume', 0.0) + else: + total_line += 1 for line in cost.cost_lines: for valuation in cost.valuation_adjustment_lines: value = 0.0 @@ -226,13 +220,13 @@ class stock_landed_cost(osv.osv): else: value = (line.price_unit / total_line) - if valuation.id not in dict: - dict[valuation.id] = value + if valuation.id not in towrite_dict: + towrite_dict[valuation.id] = value else: - dict[valuation.id] += value - - for key, value in dict.items(): - line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context) + towrite_dict[valuation.id] += value + if towrite_dict: + for key, value in towrite_dict.items(): + line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context) return True @@ -297,14 +291,12 @@ class stock_valuation_adjustment_lines(osv.osv): 'former_cost_per_unit': fields.function(_amount_final, multi='cost', string='Former Cost(Per Unit)', type='float', digits_compute=dp.get_precision('Account'), store=True), 'additional_landed_cost': fields.float('Additional Landed Cost', digits_compute=dp.get_precision('Product Price')), 'final_cost': fields.function(_amount_final, multi='cost', string='Final Cost', type='float', digits_compute=dp.get_precision('Account'), store=True), - 'flag': fields.selection([('original', 'Original'), ('duplicate', 'Duplicate')], 'Flag', readonly=True), } _defaults = { 'quantity': 1.0, 'weight': 1.0, 'volume': 1.0, - 'flag': 'original', } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_landed_costs/stock_landed_costs_view.xml b/addons/stock_landed_costs/stock_landed_costs_view.xml index d2e3cdac44d..a527efa99af 100644 --- a/addons/stock_landed_costs/stock_landed_costs_view.xml +++ b/addons/stock_landed_costs/stock_landed_costs_view.xml @@ -23,8 +23,7 @@ - + @@ -89,8 +88,8 @@ - - + + diff --git a/addons/stock_landed_costs/test/stock_landed_costs.yml b/addons/stock_landed_costs/test/stock_landed_costs.yml new file mode 100644 index 00000000000..98c739a53b6 --- /dev/null +++ b/addons/stock_landed_costs/test/stock_landed_costs.yml @@ -0,0 +1,114 @@ +- + In order to test the landed costs feature of stock, I create a landed cost, confirm it and check its account move created +- + I create 2 products with different volume and gross weight and configure them for real_time valuation and real price costing method +- + !record {model: product.product, id: product_landed_cost_1}: + name: "LC product 1" + cost_method: real + valuation: real_time + weight: 10 + volume: 1 + property_stock_account_input: account.o_expense + property_stock_account_output: account.o_income +- + !record {model: product.product, id: product_landed_cost_2}: + name: "LC product 2" + cost_method: real + valuation: real_time + weight: 20 + volume: 1.5 + property_stock_account_input: account.o_expense + property_stock_account_output: account.o_income +- + I create 2 picking moving those products +- + !record {model: stock.picking, id: picking_landed_cost_1}: + name: 'LC_pick_1' + picking_type_id: stock.picking_type_out + move_lines: + - name: move 1 + product_id: product_landed_cost_1 + product_uom_qty: 5 + product_uom: product.product_uom_unit + product_uos_qty: 5 + product_uos: product.product_uom_unit + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers +- + !record {model: stock.picking, id: picking_landed_cost_2}: + name: 'LC_pick_2' + picking_type_id: stock.picking_type_out + move_lines: + - name: move 2 + product_id: product_landed_cost_2 + product_uom_qty: 10 + product_uom: product.product_uom_unit + product_uos_qty: 10 + product_uos: product.product_uom_unit + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers +- + I create a landed cost for those 2 pickings +- + !record {model: stock.landed.cost, id: stock_landed_cost_1}: + picking_ids: [picking_landed_cost_1, picking_landed_cost_2] + account_journal_id: account.expenses_journal + cost_lines: + - name: 'equal split' + split_method: 'equal' + price_unit: 10 + product_id: product.product_product_2 + - name: 'split by quantity' + split_method: 'by_quantity' + price_unit: 150 + product_id: product.product_product_2 + - name: 'split by weight' + split_method: 'by_weight' + price_unit: 250 + product_id: product.product_product_2 + - name: 'split by volume' + split_method: 'by_volume' + price_unit: 20 + product_id: product.product_product_2 + valuation_adjustment_lines: [] +- + I compute the landed cost using Compute button +- + !python {model: stock.landed.cost}: | + self.compute_landed_cost(cr, uid, [ref("stock_landed_cost_1")]) + +- + I check the valuation adjustment lines +- + !python {model: stock.landed.cost}: | + landed_cost = self.browse(cr, uid, ref("stock_landed_cost_1")) + for valuation in landed_cost.valuation_adjustment_lines: + if valuation.cost_line_id.name == 'equal split': + assert valuation.additional_landed_cost == 5 + elif valuation.cost_line_id.name == 'split by quantity' and valuation.move_id.name == "move 1": + assert valuation.additional_landed_cost == 50 + elif valuation.cost_line_id.name == 'split by quantity' and valuation.move_id.name == "move 2": + assert valuation.additional_landed_cost == 100 + elif valuation.cost_line_id.name == 'split by weight' and valuation.move_id.name == "move 1": + assert valuation.additional_landed_cost == 50 + elif valuation.cost_line_id.name == 'split by weight' and valuation.move_id.name == "move 2": + assert valuation.additional_landed_cost == 200 + elif valuation.cost_line_id.name == 'split by volume' and valuation.move_id.name == "move 1": + assert valuation.additional_landed_cost == 5 + elif valuation.cost_line_id.name == 'split by volume' and valuation.move_id.name == "move 2": + assert valuation.additional_landed_cost == 15 + else: + raise 'unrecognized valuation adjustment line' +- + I confirm the landed cost +- + !python {model: stock.landed.cost}: | + self.button_validate(cr, uid, [ref("stock_landed_cost_1")]) +- + I check that the landed cost is now "Closed" and that it has an accounting entry +- + !assert {model: stock.landed.cost, id: stock_landed_cost_1}: + - state == 'done' + - account_move_id + - len(account_move_id.line_id) == 16