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)