From dd6fd233ed357a34bf96fb41524b06200af736b5 Mon Sep 17 00:00:00 2001 From: Ahmed Khalaf Date: Wed, 29 Mar 2023 10:13:49 +0000 Subject: [PATCH] [FIX] stock: quantities in transit not seen by report Before this commit, if there is a demand for quantities and they are available but reserved in transit (not yet in a stock location) they are shown as not available. To reproduce: 1) Have 2/3 step incoming shipments in warehouse 2) Purchase a quantity to create the incoming transfer 3) Validate the first step so that quantity is now in Input location 4) Create a MO with the component with quantity only in Input location 5) Go to forecast report of component, the quantity is shown not available. closes odoo/odoo#117372 X-original-commit: 39ff1feae9f44b64ee3fff6ccee837ac4922ad1e Signed-off-by: Arnold Moyaux (arm) Signed-off-by: Ahmed Khalaf --- addons/stock/report/stock_forecasted.py | 11 ++++++--- addons/stock/tests/test_report.py | 33 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/addons/stock/report/stock_forecasted.py b/addons/stock/report/stock_forecasted.py index e75dc62718e..dab2705aa29 100644 --- a/addons/stock/report/stock_forecasted.py +++ b/addons/stock/report/stock_forecasted.py @@ -186,7 +186,7 @@ class StockForecasted(models.AbstractModel): def _get_report_lines(self, product_template_ids, product_ids, wh_location_ids, wh_stock_location, read=True): - def _get_out_move_reserved_data(out, ins, used_reserved_moves): + def _get_out_move_reserved_data(out, ins, used_reserved_moves, currents): reserved_out = 0 linked_moves = self.env['stock.move'].browse(out._rollup_move_origs()).filtered(lambda m: m.id not in ins.ids) # the move to show when qty is reserved @@ -203,6 +203,7 @@ class StockForecasted(models.AbstractModel): # add to reserved line data reserved_out += reserved used_reserved_moves[move] += reserved + currents[(out.product_id.id, move.location_id.id)] -= reserved if float_compare(reserved_out, out.product_qty, precision_rounding=move.product_id.uom_id.rounding) >= 0: break @@ -292,14 +293,16 @@ class StockForecasted(models.AbstractModel): # any sublocation qties will be added to the main stock location qty if location_id in wh_stock_sub_location_ids: location_id = wh_stock_location.id - currents[(qty['product_id'][0], location_id)] += qty['quantity'] - qty['reserved_quantity'] + currents[(qty['product_id'][0], location_id)] += qty['quantity'] moves_data = {} for _, out_moves in outs_per_product.items(): # to handle multiple out wtih same in (ex: same pick/pack for 2 outs) used_reserved_moves = defaultdict(float) - # for all out moves, check for linked moves and remove qty from current stock + # for all out moves, check for linked moves and count reserved quantity + for out in out_moves: + moves_data[out] = _get_out_move_reserved_data(out, ins, used_reserved_moves, currents) + # another loop to remove qty from current stock after reserved is counted for for out in out_moves: - moves_data[out] = _get_out_move_reserved_data(out, ins, used_reserved_moves) data = _get_out_move_taken_from_stock_data(out, currents, moves_data[out]) moves_data[out].update(data) lines = [] diff --git a/addons/stock/tests/test_report.py b/addons/stock/tests/test_report.py index fd525cc1dd7..01cca185b15 100644 --- a/addons/stock/tests/test_report.py +++ b/addons/stock/tests/test_report.py @@ -1227,6 +1227,39 @@ class TestReports(TestReportsCommon): self.assertEqual(all_delivery.move_ids.mapped("forecast_availability"), [3, 3, -3.0, -3.0]) + def test_report_forecast_12_reserved_transit(self): + """ Tests the transit feature, in 2 step incoming shipment warehouse, create + incoming transfer, validate it, create outgoing transfer and check report to show + quantities needed are in transit + """ + grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') + grp_multi_routes = self.env.ref('stock.group_adv_location') + self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]}) + self.env.user.write({'groups_id': [(4, grp_multi_routes.id)]}) + # Warehouse config. + warehouse = self.env.ref('stock.warehouse0') + warehouse.reception_steps = 'two_steps' + outgoing = Form(self.env['stock.picking']) + outgoing.picking_type_id = self.picking_type_out + with outgoing.move_ids_without_package.new() as move: + move.product_id = self.product + move.product_uom_qty = 2 + outgoing = outgoing.save() + outgoing.action_confirm() + incoming = Form(self.env['stock.picking']) + incoming.picking_type_id = self.picking_type_in + with incoming.move_ids_without_package.new() as move: + move.product_id = self.product + move.product_uom_qty = 2 + incoming = incoming.save() + incoming.action_confirm() + incoming.action_set_quantities_to_reservation() + incoming.button_validate() + _, _, lines = self.get_report_forecast(product_template_ids=self.product_template.ids) + self.assertEqual(len(lines), 1) + self.assertEqual(bool(lines[0]['move_out']), True) + self.assertEqual(lines[0]['in_transit'], True) + def test_report_reception_1_one_receipt(self): """ Create 2 deliveries and 1 receipt where some of the products being received can be reserved for the deliveries. Check that the reception report correctly