[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

This commit is contained in:
qdp-odoo 2014-08-08 10:58:14 +02:00
parent fed0ea5392
commit dce11788fa
4 changed files with 150 additions and 44 deletions

View File

@ -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,

View File

@ -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:

View File

@ -23,8 +23,7 @@
<group>
<group>
<field name="date"/>
<field name="picking_ids" widget="many2many_tags" domain="[('state', '=', 'done')]"
on_change="onchange_pickings(picking_ids, context)"/>
<field name="picking_ids" widget="many2many_tags" domain="[('state', '=', 'done')]"/>
</group>
<group>
<field name="account_journal_id"/>
@ -89,8 +88,8 @@
<tree string="Valuation Adjustments" editable="bottom">
<field name="cost_line_id" readonly="1"/>
<field name="product_id" readonly="1"/>
<field name="weight"/>
<field name="volume"/>
<field name="weight" readonly="1"/>
<field name="volume" readonly="1"/>
<field name="quantity" readonly="1"/>
<field name="former_cost_per_unit" readonly="1"/>
<field name="former_cost" readonly="1"/>

View File

@ -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