[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:
parent
fed0ea5392
commit
dce11788fa
|
@ -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,
|
||||
|
|
|
@ -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,12 +220,12 @@ 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():
|
||||
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:
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue