[REF] stock_account: dropshipping valuation

We standardize the dropshipping by handling it as it was an receipt
directly followed by an out.

We also follow the same logic whatever the cost method, ie we don't run
fifo or update the AVCO.

task-1875873
This commit is contained in:
Simon Lejeune
2019-06-10 20:42:59 +00:00
parent 36f701e9d7
commit 5c62700698
4 changed files with 118 additions and 3 deletions
+60 -2
View File
@@ -240,6 +240,52 @@ class StockMove(models.Model):
svl_vals_list.append(svl_vals)
return self.env['stock.valuation.layer'].create(svl_vals_list)
def _create_dropshipped_svl(self, forced_quantity=None):
"""Create a `stock.valuation.layer` from `self`.
:param forced_quantity: under some circunstances, the quantity to value is different than
the initial demand of the move (Default value = None)
"""
svl_vals_list = []
for move in self:
valued_move_lines = move.move_line_ids
valued_quantity = 0
for valued_move_line in valued_move_lines:
valued_quantity += valued_move_line.product_uom_id._compute_quantity(valued_move_line.qty_done, move.product_id.uom_id)
quantity = forced_quantity or valued_quantity
unit_cost = move._get_price_unit()
if move.product_id.cost_method == 'standard':
unit_cost = move.product_id.standard_price
common_vals = dict(move._prepare_common_svl_vals(), remaining_qty=0)
# create the in
in_vals = {
'unit_cost': unit_cost,
'value': unit_cost * quantity,
'quantity': quantity,
}
in_vals.update(common_vals)
svl_vals_list.append(in_vals)
# create the out
out_vals = {
'unit_cost': unit_cost,
'value': unit_cost * quantity * -1,
'quantity': quantity * -1,
}
out_vals.update(common_vals)
svl_vals_list.append(out_vals)
return self.env['stock.valuation.layer'].create(svl_vals_list)
def _create_dropshipped_returned_svl(self, forced_quantity=None):
"""Create a `stock.valuation.layer` from `self`.
:param forced_quantity: under some circunstances, the quantity to value is different than
the initial demand of the move (Default value = None)
"""
return self._create_dropshipped_svl(forced_quantity=forced_quantity)
@api.model
def _run_fifo(self, move, quantity=None):
@@ -734,9 +780,21 @@ class StockMove(models.Model):
# Creates an account entry from stock_input to stock_output on a dropship move. https://github.com/odoo/odoo/issues/12687
journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation()
if self._is_dropshipped():
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_src, acc_dest, journal_id)
value = self.env.context.get('force_valuation_amount')
if value > 0:
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_src, acc_valuation, journal_id)
else:
force_valuation_amount = self.env.context['force_valuation_amount']
self.env.context = dict(self.env.context, force_valuation_amount=-1*force_valuation_amount)
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_valuation, acc_dest, journal_id)
elif self._is_dropshipped_returned():
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_dest, acc_src, journal_id)
value = self.env.context.get('force_valuation_amount')
if value > 0:
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_valuation, acc_src, journal_id)
else:
force_valuation_amount = self.env.context['force_valuation_amount']
self.env.context = dict(self.env.context, force_valuation_amount=-1*force_valuation_amount)
self.with_context(force_company=self.company_id.id)._create_account_move_line(acc_dest, acc_valuation, journal_id)
if self.company_id.anglo_saxon_accounting:
#eventually reconcile together the invoice and valuation accounting entries on the stock interim accounts
@@ -110,6 +110,10 @@ class StockMoveLine(models.Model):
stock_valuation_layers |= move._create_in_svl(forced_quantity=abs(diff))
elif move._is_in() and diff < 0 or move._is_out() and diff > 0:
stock_valuation_layers |= move._create_out_svl(forced_quantity=abs(diff))
elif move._is_dropshipped() and diff > 0 or move._is_dropshipped_returned() and diff < 0:
stock_valuation_layers |= move._create_dropshipped_svl(forced_quantity=abs(diff))
elif move._is_dropshipped() and diff < 0 or move._is_dropshipped_returned() and diff > 0:
stock_valuation_layers |= move._create_dropshipped_returned_svl(forced_quantity=abs(diff))
for stock_valuation_layer in stock_valuation_layers:
if not stock_valuation_layer.product_id.valuation == 'real_time':
@@ -2276,7 +2276,6 @@ class TestStockValuation(SavepointCase):
self.assertAlmostEqual(self.product1.quantity_svl, 25)
self.assertAlmostEqual(self.product1.value_svl, 350)
@skip('need to implement dropship')
def test_average_perpetual_8(self):
""" Receive 1@10, then dropship 1@20, finally return the dropship. Dropship should not
impact the price.
@@ -96,6 +96,24 @@ class TestStockValuationCommon(SavepointCase):
self.days += 1
return out_move.with_context(svl=True)
def _make_dropship_move(self, product, quantity, unit_cost=None):
dropshipped = self.env['stock.move'].create({
'name': 'dropship %s units' % str(quantity),
'product_id': product.id,
'location_id': self.supplier_location.id,
'location_dest_id': self.customer_location.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': quantity,
'picking_type_id': self.picking_type_out.id,
})
if unit_cost:
dropshipped.unit_cost = unit_cost
dropshipped._action_confirm()
dropshipped._action_assign()
dropshipped.move_line_ids.qty_done = quantity
dropshipped._action_done()
return dropshipped
def _make_return(self, move, quantity_to_return):
stock_return_picking = Form(self.env['stock.return.picking']\
.with_context(active_ids=[move.picking_id.id], active_id=move.picking_id.id, active_model='stock.picking'))
@@ -215,6 +233,33 @@ class TestStockValuationStandard(TestStockValuationCommon):
self.assertEqual(self.product1.value_svl, 50)
self.assertEqual(self.product1.quantity_svl, 5)
def test_dropship_1(self):
self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic'
move1 = self._make_dropship_move(self.product1, 10)
valuation_layers = self.product1.stock_valuation_layer_ids
self.assertEqual(len(valuation_layers), 2)
self.assertEqual(valuation_layers[0].value, 100)
self.assertEqual(valuation_layers[1].value, -100)
self.assertEqual(self.product1.value_svl, 0)
self.assertEqual(self.product1.quantity_svl, 0)
def test_change_in_past_increase_dropship_1(self):
self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic'
move1 = self._make_dropship_move(self.product1, 10)
move1.move_line_ids.with_context(svl=True).qty_done = 15
valuation_layers = self.product1.stock_valuation_layer_ids
self.assertEqual(len(valuation_layers), 4)
self.assertEqual(valuation_layers[0].value, 100)
self.assertEqual(valuation_layers[1].value, -100)
self.assertEqual(valuation_layers[2].value, 50)
self.assertEqual(valuation_layers[3].value, -50)
self.assertEqual(self.product1.value_svl, 0)
self.assertEqual(self.product1.quantity_svl, 0)
class TestStockValuationAVCO(TestStockValuationCommon):
def setUp(self):
@@ -381,6 +426,15 @@ class TestStockValuationAVCO(TestStockValuationCommon):
self.assertEqual(self.product1.standard_price, 15)
self.assertEqual(sum(self.product1.stock_valuation_layer_ids.mapped('remaining_qty')), 1)
def test_dropship_1(self):
move1 = self._make_in_move(self.product1, 1, unit_cost=10)
move2 = self._make_in_move(self.product1, 1, unit_cost=20)
move3 = self._make_dropship_move(self.product1, 1, unit_cost=10)
self.assertEqual(self.product1.value_svl, 30)
self.assertEqual(self.product1.quantity_svl, 2)
self.assertEqual(self.product1.standard_price, 15)
class TestStockValuationFIFO(TestStockValuationCommon):
def setUp(self):