Skip to content

Commit

Permalink
[FIX] stock: consider mto procure method in stock forecast
Browse files Browse the repository at this point in the history
Steps to reproduce:

- Enable Multi-Step Routes in the settings
- Go to Inventory > Configuration > Warehouse Management > Routes
- Unarchive MTO
- Create a storable product:
  - Routes: MTO, manufacture
  - With a bom
  - On hand qty: 10
- Create an SO for 15 units and validate
- Go back to your product form > forecasted Report
> Even thought 15 units are made to order 10 units are reported to be
> taken from stock to fulfill the SO

Cause of the issue:

The lines appearing on the reported are computed by the
`_get_report_lines` method. During this call, the quantity taken from
stock is computed by comparing the demand of the out moves with the
quantity currently in stock:
https://github.com/odoo/odoo/blob/1fdccad664cad37a69dc960444aa0c318d1bf464/addons/stock/report/report_stock_forecasted.py#L218-L223
This is incorrect because the quantity taken from stock is null if the
procure_method of the out move is make_to_stock.

Note:

The issue is not reproducible since 17.0 since the _get_report_lines has
been refactored during the stockpocalypse.

opw-4010785

closes odoo#176273

Signed-off-by: William Henrotin (whe) <whe@odoo.com>
  • Loading branch information
lase-odoo committed Aug 20, 2024
1 parent f08088a commit a0c0875
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
58 changes: 58 additions & 0 deletions addons/sale_mrp/tests/test_sale_mrp_report.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import Command
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import common, Form
from odoo.tools import html2plaintext
Expand Down Expand Up @@ -70,3 +71,60 @@ def test_deliver_and_invoice_tracked_components(self):
html = report._render_qweb_html(invoice.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n1.00\nUnits\nLOT0001', "There should be a line that specifies 1 x LOT0001")

def test_report_forecast_for_mto_procure_method(self):
"""
Check that mto moves are not reported as taking from stock in the forecast report
"""
mto_route = self.env.ref('stock.route_warehouse0_mto')
mto_route.active = True
manufacturing_route = self.env.ref('mrp.route_warehouse0_manufacture')
product = self.env['product.product'].create({
'name': 'SuperProduct',
'type': 'product',
'route_ids': [Command.set((mto_route + manufacturing_route).ids)]
})
warehouse = self.warehouse
# make 2 so: so_1 can be fulfilled and so_2 requires a replenishment
self.env['stock.quant']._update_available_quantity(product, warehouse.lot_stock_id, 10.0)
so_1, so_2 = self.env['sale.order'].create([
{
'partner_id': self.partner_a.id,
'order_line': [Command.create({
'name': product.name,
'product_id': product.id,
'product_uom_qty': 8.0,
'product_uom': product.uom_id.id,
'price_unit': product.list_price,
})]
},
{
'partner_id': self.partner_a.id,
'order_line': [Command.create({
'name': product.name,
'product_id': product.id,
'product_uom_qty': 7.0,
'product_uom': product.uom_id.id,
'price_unit': product.list_price,
})]
},

])
(so_1 | so_2).action_confirm()
report_lines = self.env['report.stock.report_product_product_replenishment'].with_context(warehouse=warehouse.id)._get_report_values(docids=product.ids)['docs']['lines']
self.assertEqual(len(report_lines), 3)
so_1_line = next(filter(lambda line: line.get('document_out') == so_1, report_lines))
self.assertEqual(
[so_1_line['quantity'], so_1_line['move_out'], so_1_line['replenishment_filled']],
[8.0, so_1.picking_ids.move_lines, True]
)
so_2_line = next(filter(lambda line: line.get('document_out') == so_2, report_lines))
self.assertEqual(
[so_2_line['quantity'], so_2_line['move_out'], so_2_line['replenishment_filled']],
[7.0, so_2.picking_ids.move_lines, False]
)
quant_line = next(filter(lambda line: not line.get('document_out'), report_lines))
self.assertEqual(
[quant_line['document_out'], quant_line['quantity'], quant_line['replenishment_filled']],
[False, 2.0, True]
)
2 changes: 1 addition & 1 deletion addons/stock/report/report_stock_forecasted.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def _reconcile_out_with_ins(lines, out, ins, demand, product_rounding, only_matc
if float_is_zero(demand, precision_rounding=product_rounding):
continue
current = currents[product.id]
taken_from_stock = min(demand, current)
taken_from_stock = min(demand, current) if out.procure_method != 'make_to_order' else 0
if not float_is_zero(taken_from_stock, precision_rounding=product_rounding):
currents[product.id] -= taken_from_stock
demand -= taken_from_stock
Expand Down

0 comments on commit a0c0875

Please sign in to comment.