From 74616ee9d2b5087b7a29a2ae7b68132eacc1b1a2 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Mon, 16 Mar 2020 15:45:05 +0000 Subject: [PATCH] [FIX] stock: remove False out warehouse management Currently the quantity outside warehouse in the forecast report is erroneous. It only takes the quants and not the stock.move incoming and outgoing for those location. So the qty is only correct for the current datetime. Remove this functionality and remove the filter 'warehouse is set' since it's always the case. closes odoo/odoo#47997 Task: 2217047 Signed-off-by: Simon Lejeune (sle) --- addons/stock/report/report_stock_quantity.py | 7 +- addons/stock/report/report_stock_quantity.xml | 4 - addons/stock/tests/test_report.py | 249 +++++++++++++++++- 3 files changed, 251 insertions(+), 9 deletions(-) diff --git a/addons/stock/report/report_stock_quantity.py b/addons/stock/report/report_stock_quantity.py index ebaf173980f..20b85686baf 100644 --- a/addons/stock/report/report_stock_quantity.py +++ b/addons/stock/report/report_stock_quantity.py @@ -54,7 +54,8 @@ LEFT JOIN product_template pt on pt.id=pp.product_tmpl_id WHERE pt.type = 'product' AND product_qty != 0 AND - (whs.id IS NULL or whd.id IS NULL OR whs.id != whd.id) AND + (whs.id IS NOT NULL OR whd.id IS NOT NULL) AND + (whs.id IS NULL OR whd.id IS NULL OR whs.id != whd.id) AND m.state NOT IN ('cancel', 'draft', 'done') UNION SELECT @@ -72,7 +73,8 @@ FROM LEFT JOIN stock_location l on (l.id=q.location_id) LEFT JOIN stock_warehouse wh ON l.parent_path like concat('%/', wh.view_location_id, '/%') WHERE - l.usage = 'internal' + l.usage = 'internal' AND + wh IS NOT NULL UNION SELECT m.id, @@ -109,6 +111,7 @@ LEFT JOIN product_template pt on pt.id=pp.product_tmpl_id WHERE pt.type = 'product' AND product_qty != 0 AND + (whs.id IS NOT NULL OR whd.id IS NOT NULL) AND (whs.id IS NULL or whd.id IS NULL OR whs.id != whd.id) AND m.state NOT IN ('cancel', 'draft') ); diff --git a/addons/stock/report/report_stock_quantity.xml b/addons/stock/report/report_stock_quantity.xml index 81ec015759b..f9a2e1c00bb 100644 --- a/addons/stock/report/report_stock_quantity.xml +++ b/addons/stock/report/report_stock_quantity.xml @@ -26,8 +26,6 @@ - - @@ -42,7 +40,6 @@ graph { 'search_default_filter_forecast': 1, - 'search_default_below_warehouse': 1, 'graph_groupbys': ['date:day', 'state', 'product_id'], } @@ -53,7 +50,6 @@ graph { 'search_default_filter_forecast': 1, - 'search_default_below_warehouse': 1, 'graph_groupbys': ['date:day', 'state', 'product_id'], } diff --git a/addons/stock/tests/test_report.py b/addons/stock/tests/test_report.py index bd567f424f6..9acac53a2a3 100644 --- a/addons/stock/tests/test_report.py +++ b/addons/stock/tests/test_report.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import odoo -import odoo.tests + +from datetime import date, datetime, timedelta + +from odoo.tests.common import Form, TransactionCase -class TestReports(odoo.tests.TransactionCase): +class TestReports(TransactionCase): def test_reports(self): product1 = self.env['product.product'].create({ 'name': 'Mellohi', @@ -25,3 +27,244 @@ class TestReports(odoo.tests.TransactionCase): rendering, qweb_type = report.render_qweb_text(lot1.id) self.assertEqual(target, rendering.replace(b' ', b''), 'The rendering is not good') self.assertEqual(qweb_type, 'text', 'the report type is not good') + + def test_report_quantity_1(self): + product_form = Form(self.env['product.product']) + product_form.type = 'product' + product_form.name = 'Product' + product = product_form.save() + + warehouse = self.env['stock.warehouse'].search([], limit=1) + stock = self.env['stock.location'].create({ + 'name': 'New Stock', + 'usage': 'internal', + 'location_id': warehouse.view_location_id.id, + }) + + # Inventory Adjustement of 50.0 today. + self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': product.id, + 'location_id': stock.id, + 'inventory_quantity': 50 + }) + self.env['stock.move'].flush() + report_records_today = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty'], [], lazy=False) + report_records_tomorrow = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() + timedelta(days=1))], + ['product_qty'], []) + report_records_yesterday = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() - timedelta(days=1))], + ['product_qty'], []) + self.assertEqual(sum([r['product_qty'] for r in report_records_today]), 50.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow]), 50.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_yesterday]), 0.0) + + # Delivery of 20.0 units tomorrow + move_out = self.env['stock.move'].create({ + 'name': 'Move Out 20', + 'date_expected': datetime.now() + timedelta(days=1), + 'location_id': stock.id, + 'location_dest_id': self.env.ref('stock.stock_location_customers').id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 20.0, + }) + self.env['stock.move'].flush() + report_records_tomorrow = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() + timedelta(days=1))], + ['product_qty'], []) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow]), 50.0) + move_out._action_confirm() + self.env['stock.move'].flush() + report_records_tomorrow = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() + timedelta(days=1))], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'forecast']), 30.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'out']), -20.0) + report_records_today = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records_today if r['state'] == 'forecast']), 50.0) + + # Receipt of 10.0 units tomorrow + move_in = self.env['stock.move'].create({ + 'name': 'Move In 10', + 'date_expected': datetime.now() + timedelta(days=1), + 'location_id': self.env.ref('stock.stock_location_suppliers').id, + 'location_dest_id': stock.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 10.0, + }) + move_in._action_confirm() + self.env['stock.move'].flush() + report_records_tomorrow = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() + timedelta(days=1))], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'forecast']), 40.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'out']), -20.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'in']), 10.0) + report_records_today = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records_today if r['state'] == 'forecast']), 50.0) + + # Delivery of 20.0 units tomorrow + move_out = self.env['stock.move'].create({ + 'name': 'Move Out 30 - Day-1', + 'date_expected': datetime.now() - timedelta(days=1), + 'location_id': stock.id, + 'location_dest_id': self.env.ref('stock.stock_location_customers').id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 30.0, + }) + move_out._action_confirm() + self.env['stock.move'].flush() + report_records_today = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty', 'state'], ['state'], lazy=False) + report_records_tomorrow = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() + timedelta(days=1))], + ['product_qty', 'state'], ['state'], lazy=False) + report_records_yesterday = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today() - timedelta(days=1))], + ['product_qty', 'state'], ['state'], lazy=False) + + self.assertEqual(sum([r['product_qty'] for r in report_records_yesterday if r['state'] == 'forecast']), -30.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_yesterday if r['state'] == 'out']), -30.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_yesterday if r['state'] == 'in']), 0.0) + + self.assertEqual(sum([r['product_qty'] for r in report_records_today if r['state'] == 'forecast']), 20.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_today if r['state'] == 'out']), 0.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_today if r['state'] == 'in']), 0.0) + + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'forecast']), 10.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'out']), -20.0) + self.assertEqual(sum([r['product_qty'] for r in report_records_tomorrow if r['state'] == 'in']), 10.0) + + def test_report_quantity_2(self): + """ Not supported case. + """ + product_form = Form(self.env['product.product']) + product_form.type = 'product' + product_form.name = 'Product' + product = product_form.save() + + warehouse = self.env['stock.warehouse'].search([], limit=1) + stock = self.env['stock.location'].create({ + 'name': 'Stock Under Warehouse', + 'usage': 'internal', + 'location_id': warehouse.view_location_id.id, + }) + stock_without_wh = self.env['stock.location'].create({ + 'name': 'Stock Outside Warehouse', + 'usage': 'internal', + 'location_id': self.env.ref('stock.stock_location_locations').id, + }) + self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': product.id, + 'location_id': stock.id, + 'inventory_quantity': 50 + }) + self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': product.id, + 'location_id': stock_without_wh.id, + 'inventory_quantity': 50 + }) + move = self.env['stock.move'].create({ + 'name': 'Move outside warehouse', + 'location_id': stock.id, + 'location_dest_id': stock_without_wh.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 10.0, + }) + move._action_confirm() + self.env['stock.move'].flush() + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today()), ('warehouse_id', '!=', False)], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records if r['state'] == 'forecast']), 40.0) + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records if r['state'] == 'forecast']), 40.0) + move = self.env['stock.move'].create({ + 'name': 'Move outside warehouse', + 'location_id': stock_without_wh.id, + 'location_dest_id': self.env.ref('stock.stock_location_customers').id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 10.0, + }) + move._action_confirm() + self.env['stock.move'].flush() + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty', 'state'], ['state'], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records if r['state'] == 'forecast']), 40.0) + + def test_report_quantity_3(self): + product_form = Form(self.env['product.product']) + product_form.type = 'product' + product_form.name = 'Product' + product = product_form.save() + + warehouse = self.env['stock.warehouse'].search([], limit=1) + stock = self.env['stock.location'].create({ + 'name': 'Rack', + 'usage': 'view', + 'location_id': warehouse.view_location_id.id, + }) + stock_real_loc = self.env['stock.location'].create({ + 'name': 'Drawer', + 'usage': 'internal', + 'location_id': stock.id, + }) + + self.env['stock.move'].flush() + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty'], [], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records if r['product_qty']]), 0.0) + + # Receipt of 20.0 units tomorrow + move_in = self.env['stock.move'].create({ + 'name': 'Move In 20', + 'location_id': self.env.ref('stock.stock_location_suppliers').id, + 'location_dest_id': stock.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 20.0, + }) + move_in._action_confirm() + move_in.move_line_ids.location_dest_id = stock_real_loc.id + move_in.move_line_ids.qty_done = 20.0 + move_in._action_done() + self.env['stock.move'].flush() + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty'], [], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records]), 20.0) + + # Delivery of 10.0 units tomorrow + move_out = self.env['stock.move'].create({ + 'name': 'Move Out 10', + 'location_id': stock.id, + 'location_dest_id': self.env.ref('stock.stock_location_customers').id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 10.0, + }) + move_out._action_confirm() + move_out._action_assign() + move_out.move_line_ids.qty_done = 10.0 + move_out._action_done() + self.env['stock.move'].flush() + report_records = self.env['report.stock.quantity'].read_group( + [('product_id', '=', product.id), ('date', '=', date.today())], + ['product_qty'], [], lazy=False) + self.assertEqual(sum([r['product_qty'] for r in report_records]), 10.0)