diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/stock/tests/test_multicompany.py | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/stock/tests/test_multicompany.py')
| -rw-r--r-- | addons/stock/tests/test_multicompany.py | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/addons/stock/tests/test_multicompany.py b/addons/stock/tests/test_multicompany.py new file mode 100644 index 00000000..44f981f2 --- /dev/null +++ b/addons/stock/tests/test_multicompany.py @@ -0,0 +1,598 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.exceptions import UserError +from odoo.tests.common import SavepointCase, Form + + +class TestMultiCompany(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestMultiCompany, cls).setUpClass() + group_user = cls.env.ref('base.group_user') + group_stock_manager = cls.env.ref('stock.group_stock_manager') + + cls.company_a = cls.env['res.company'].create({'name': 'Company A'}) + cls.company_b = cls.env['res.company'].create({'name': 'Company B'}) + cls.warehouse_a = cls.env['stock.warehouse'].search([('company_id', '=', cls.company_a.id)], limit=1) + cls.warehouse_b = cls.env['stock.warehouse'].search([('company_id', '=', cls.company_b.id)], limit=1) + cls.stock_location_a = cls.warehouse_a.lot_stock_id + cls.stock_location_b = cls.warehouse_b.lot_stock_id + + cls.user_a = cls.env['res.users'].create({ + 'name': 'user company a with access to company b', + 'login': 'user a', + 'groups_id': [(6, 0, [group_user.id, group_stock_manager.id])], + 'company_id': cls.company_a.id, + 'company_ids': [(6, 0, [cls.company_a.id, cls.company_b.id])] + }) + cls.user_b = cls.env['res.users'].create({ + 'name': 'user company a with access to company b', + 'login': 'user b', + 'groups_id': [(6, 0, [group_user.id, group_stock_manager.id])], + 'company_id': cls.company_b.id, + 'company_ids': [(6, 0, [cls.company_a.id, cls.company_b.id])] + }) + + def test_picking_type_1(self): + """As a user of Company A, check it is not possible to use a warehouse of Company B in a + picking type of Company A. + """ + picking_type_company_a = self.env['stock.picking.type'].search([ + ('company_id', '=', self.company_a.id) + ], limit=1) + with self.assertRaises(UserError): + picking_type_company_a.warehouse_id = self.warehouse_b + + def test_picking_type_2(self): + """As a user of Company A, check it is not possible to change the company on an existing + picking type of Company A to Company B. + """ + picking_type_company_a = self.env['stock.picking.type'].search([ + ('company_id', '=', self.company_a.id) + ], limit=1) + with self.assertRaises(UserError): + picking_type_company_a.with_user(self.user_a).company_id = self.company_b + + def test_putaway_1(self): + """As a user of Company A, create a putaway rule with locations of Company A and set the + company to Company B before saving. Check it is not possible. + """ + stock_location_a_1 = self.env['stock.location'].with_user(self.user_a).create({ + 'location_id': self.stock_location_a.id, + 'usage': 'internal', + 'name': 'A_1', + }) + putaway_form = Form(self.env['stock.putaway.rule']) + putaway_form.location_in_id = self.stock_location_a + putaway_form.location_out_id = stock_location_a_1 + putaway_form.company_id = self.company_b + with self.assertRaises(UserError): + putaway_form.save() + + def test_putaway_2(self): + """As a user of Company A, check it is not possible to change the company on an existing + putaway rule to Company B. + """ + stock_location_a_1 = self.env['stock.location'].with_user(self.user_a).create({ + 'name': 'A_1', + 'location_id': self.stock_location_a.id, + 'usage': 'internal', + }) + putaway_rule = self.env['stock.putaway.rule'].with_user(self.user_a).create({ + 'location_in_id': self.stock_location_a.id, + 'location_out_id': stock_location_a_1.id + }) + with self.assertRaises(UserError): + putaway_rule.company_id = self.company_b + + def test_company_1(self): + """Check it is not possible to use the internal transit location of Company B on Company A.""" + with self.assertRaises(UserError): + self.company_a.internal_transit_location_id = self.company_b.internal_transit_location_id + + def test_partner_1(self): + """On a partner without company, as a user of Company B, check it is not possible to use a + location limited to Company A as `property_stock_supplier` or `property_stock_customer`. + """ + shared_partner = self.env['res.partner'].create({ + 'name': 'Shared Partner', + 'company_id': False, + }) + with self.assertRaises(UserError): + shared_partner.with_user(self.user_b).property_stock_customer = self.stock_location_a + + def test_inventory_1(self): + """Create an inventory in Company A for a product limited to Company A and, as a user of company + B, start the inventory and set its counted quantity to 10 before validating. The inventory + lines and stock moves should belong to Company A. The inventory loss location used should be + the one of Company A. + """ + product = self.env['product.product'].create({ + 'type': 'product', + 'company_id': self.company_a.id, + 'name': 'Product limited to company A', + }) + inventory = self.env['stock.inventory'].with_user(self.user_a).create({}) + self.assertEqual(inventory.company_id, self.company_a) + inventory.with_user(self.user_b).action_start() + inventory.with_user(self.user_b).line_ids = [(0, 0, { + 'product_qty': 10, + 'product_id': product.id, + 'location_id': self.stock_location_a.id, + })] + inventory.with_user(self.user_b).action_validate() + self.assertEqual(inventory.line_ids.company_id, self.company_a) + self.assertEqual(inventory.move_ids.company_id, self.company_a) + self.assertEqual(inventory.move_ids.location_id.company_id, self.company_a) + + def test_inventory_2(self): + """Create an empty inventory in Company A and check it is not possible to use products limited + to Company B in it. + """ + product = self.env['product.product'].create({ + 'name': 'product limited to company b', + 'company_id': self.company_b.id, + 'type': 'product' + }) + inventory = self.env['stock.inventory'].with_user(self.user_a).create({}) + inventory.with_user(self.user_a).action_start() + inventory.with_user(self.user_a).line_ids = [(0, 0, { + 'product_id': product.id, + 'product_qty': 10, + 'location_id': self.stock_location_a.id, + })] + with self.assertRaises(UserError): + inventory.with_user(self.user_a).action_validate() + + def test_inventory_3(self): + """As a user of Company A, check it is not possible to start an inventory adjustment for + a product limited to Company B. + """ + product = self.env['product.product'].create({ + 'name': 'product limited to company b', + 'company_id': self.company_b.id, + 'type': 'product' + }) + inventory = self.env['stock.inventory'].with_user(self.user_a).create({'product_ids': [(4, product.id)]}) + with self.assertRaises(UserError): + inventory.with_user(self.user_a).action_start() + + def test_picking_1(self): + """As a user of Company A, create a picking and use a picking type of Company B, check the + create picking belongs to Company B. + """ + picking_type_company_b = self.env['stock.picking.type'].search([('company_id', '=', self.company_b.id)], limit=1) + picking_form = Form(self.env['stock.picking'].with_user(self.user_a)) + picking_form.picking_type_id = picking_type_company_b + picking = picking_form.save() + self.assertEqual(picking.company_id, self.company_b) + + def test_location_1(self): + """Check it is not possible to set a location of Company B under a location of Company A.""" + with self.assertRaises(UserError): + self.stock_location_b.location_id = self.stock_location_a + + def test_lot_1(self): + """Check it is possible to create a stock.production.lot with the same name in Company A and + Company B""" + product_lot = self.env['product.product'].create({ + 'type': 'product', + 'tracking': 'lot', + 'name': 'product lot', + }) + self.env['stock.production.lot'].create({ + 'name': 'lotA', + 'company_id': self.company_a.id, + 'product_id': product_lot.id, + }) + self.env['stock.production.lot'].create({ + 'name': 'lotA', + 'company_id': self.company_b.id, + 'product_id': product_lot.id, + }) + + def test_lot_2(self): + """Validate a picking of Company A receiving lot1 while being logged into Company B. Check + the lot is created in Company A. + """ + product = self.env['product.product'].create({ + 'type': 'product', + 'tracking': 'serial', + 'name': 'product', + }) + picking = self.env['stock.picking'].with_user(self.user_a).create({ + 'picking_type_id': self.warehouse_a.in_type_id.id, + 'location_id': self.env.ref('stock.stock_location_suppliers').id, + 'location_dest_id': self.stock_location_a.id, + }) + self.assertEqual(picking.company_id, self.company_a) + move1 = self.env['stock.move'].create({ + 'name': 'test_lot_2', + 'picking_type_id': picking.picking_type_id.id, + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': 1.0, + 'picking_id': picking.id, + 'company_id': picking.company_id.id, + }) + picking.with_user(self.user_b).action_confirm() + self.assertEqual(picking.state, 'assigned') + move1.with_user(self.user_b).move_line_ids[0].qty_done = 1 + move1.with_user(self.user_b).move_line_ids[0].lot_name = 'receipt_serial' + self.assertEqual(move1.move_line_ids[0].company_id, self.company_a) + picking.with_user(self.user_b).button_validate() + self.assertEqual(picking.state, 'done') + created_serial = self.env['stock.production.lot'].search([ + ('name', '=', 'receipt_serial') + ]) + self.assertEqual(created_serial.company_id, self.company_a) + + def test_orderpoint_1(self): + """As a user of company A, create an orderpoint for company B. Check itsn't possible to + use a warehouse of companny A""" + product = self.env['product.product'].create({ + 'type': 'product', + 'name': 'shared product', + }) + orderpoint = Form(self.env['stock.warehouse.orderpoint'].with_user(self.user_a)) + orderpoint.company_id = self.company_b + orderpoint.warehouse_id = self.warehouse_b + orderpoint.location_id = self.stock_location_a + orderpoint.product_id = product + with self.assertRaises(UserError): + orderpoint.save() + orderpoint.location_id = self.stock_location_b + orderpoint = orderpoint.save() + self.assertEqual(orderpoint.company_id, self.company_b) + + def test_orderpoint_2(self): + """As a user of Company A, check it is not possible to change the company on an existing + orderpoint to Company B. + """ + product = self.env['product.product'].create({ + 'type': 'product', + 'name': 'shared product', + }) + orderpoint = Form(self.env['stock.warehouse.orderpoint'].with_user(self.user_a)) + orderpoint.company_id = self.company_a + orderpoint.warehouse_id = self.warehouse_a + orderpoint.location_id = self.stock_location_a + orderpoint.product_id = product + orderpoint = orderpoint.save() + self.assertEqual(orderpoint.company_id, self.company_a) + with self.assertRaises(UserError): + orderpoint.company_id = self.company_b.id + + def test_product_1(self): + """ As an user of Company A, checks we can or cannot create new product + depending of its `company_id`.""" + # Creates a new product with no company_id and set a responsible. + # The product must be created as there is no company on the product. + product_form = Form(self.env['product.template'].with_user(self.user_a)) + product_form.name = 'Paramite Pie' + product_form.responsible_id = self.user_b + product = product_form.save() + + self.assertEqual(product.company_id.id, False) + self.assertEqual(product.responsible_id.id, self.user_b.id) + + # Creates a new product belong to Company A and set a responsible belong + # to Company B. The product mustn't be created as the product and the + # user don't belong of the same company. + self.user_b.company_ids = [(6, 0, [self.company_b.id])] + product_form = Form(self.env['product.template'].with_user(self.user_a)) + product_form.name = 'Meech Munchy' + product_form.company_id = self.company_a + product_form.responsible_id = self.user_b + + with self.assertRaises(UserError): + # Raises an UserError for company incompatibility. + product = product_form.save() + + # Creates a new product belong to Company A and set a responsible belong + # to Company A & B (default B). The product must be created as the user + # belongs to product's company. + self.user_b.company_ids = [(6, 0, [self.company_a.id, self.company_b.id])] + product_form = Form(self.env['product.template'].with_user(self.user_a)) + product_form.name = 'Scrab Cake' + product_form.company_id = self.company_a + product_form.responsible_id = self.user_b + product = product_form.save() + + self.assertEqual(product.company_id.id, self.company_a.id) + self.assertEqual(product.responsible_id.id, self.user_b.id) + + def test_warehouse_1(self): + """As a user of Company A, on its main warehouse, see it is impossible to change the + company_id, to use a view location of another company, to set a picking type to one + of another company + """ + with self.assertRaises(UserError): + self.warehouse_a.company_id = self.company_b.id + with self.assertRaises(UserError): + self.warehouse_a.view_location_id = self.warehouse_b.view_location_id + with self.assertRaises(UserError): + self.warehouse_a.pick_type_id = self.warehouse_b.pick_type_id + + def test_move_1(self): + """See it is not possible to confirm a stock move of Company A with a picking type of + Company B. + """ + product = self.env['product.product'].create({ + 'name': 'p1', + 'type': 'product' + }) + picking_type_b = self.env['stock.picking.type'].search([ + ('company_id', '=', self.company_b.id), + ], limit=1) + move = self.env['stock.move'].create({ + 'company_id': self.company_a.id, + 'picking_type_id': picking_type_b.id, + 'location_id': self.stock_location_a.id, + 'location_dest_id': self.stock_location_a.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'name': 'stock_move', + }) + with self.assertRaises(UserError): + move._action_confirm() + + def test_move_2(self): + """See it is not possible to confirm a stock move of Company A with a destination location + of Company B. + """ + product = self.env['product.product'].create({ + 'name': 'p1', + 'type': 'product' + }) + picking_type_b = self.env['stock.picking.type'].search([ + ('company_id', '=', self.company_b.id), + ], limit=1) + move = self.env['stock.move'].create({ + 'company_id': self.company_a.id, + 'picking_type_id': picking_type_b.id, + 'location_id': self.stock_location_a.id, + 'location_dest_id': self.stock_location_b.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'name': 'stock_move', + }) + with self.assertRaises(UserError): + move._action_confirm() + + def test_move_3(self): + """See it is not possible to confirm a stock move of Company A with a product restricted to + Company B. + """ + product = self.env['product.product'].create({ + 'name': 'p1', + 'type': 'product', + 'company_id': self.company_b.id, + }) + picking_type_b = self.env['stock.picking.type'].search([ + ('company_id', '=', self.company_b.id), + ], limit=1) + move = self.env['stock.move'].create({ + 'company_id': self.company_a.id, + 'picking_type_id': picking_type_b.id, + 'location_id': self.stock_location_a.id, + 'location_dest_id': self.stock_location_a.id, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'name': 'stock_move', + }) + with self.assertRaises(UserError): + move._action_confirm() + + def test_intercom_lot_push(self): + """ Create a push rule to transfer products received in inter company + transit location to company b. Move a lot product from company a to the + transit location. Check the move created by the push rule is not chained + with previous move, and no product are reserved from inter-company + transit. """ + supplier_location = self.env.ref('stock.stock_location_suppliers') + intercom_location = self.env.ref('stock.stock_location_inter_wh') + intercom_location.write({'active': True}) + + product_lot = self.env['product.product'].create({ + 'type': 'product', + 'tracking': 'lot', + 'name': 'product lot', + }) + + picking_type_to_transit = self.env['stock.picking.type'].create({ + 'name': 'To Transit', + 'sequence_code': 'TRANSIT', + 'code': 'outgoing', + 'company_id': self.company_a.id, + 'warehouse_id': False, + 'default_location_src_id': self.stock_location_a.id, + 'default_location_dest_id': intercom_location.id, + 'sequence_id': self.env['ir.sequence'].create({ + 'code': 'transit', + 'name': 'transit sequence', + 'company_id': self.company_a.id, + }).id, + }) + + route = self.env['stock.location.route'].create({ + 'name': 'Push', + 'company_id': False, + 'rule_ids': [(0, False, { + 'name': 'create a move to company b', + 'company_id': self.company_b.id, + 'location_src_id': intercom_location.id, + 'location_id': self.stock_location_b.id, + 'action': 'push', + 'auto': 'manual', + 'picking_type_id': self.warehouse_b.in_type_id.id, + })], + }) + + move_from_supplier = self.env['stock.move'].create({ + 'company_id': self.company_a.id, + 'name': 'test_from_supplier', + 'location_id': supplier_location.id, + 'location_dest_id': self.stock_location_a.id, + 'product_id': product_lot.id, + 'product_uom': product_lot.uom_id.id, + 'product_uom_qty': 1.0, + 'picking_type_id': self.warehouse_a.in_type_id.id, + }) + move_from_supplier._action_confirm() + move_line_1 = move_from_supplier.move_line_ids[0] + move_line_1.lot_name = 'lot 1' + move_line_1.qty_done = 1.0 + move_from_supplier._action_done() + lot_1 = move_line_1.lot_id + + move_to_transit = self.env['stock.move'].create({ + 'company_id': self.company_a.id, + 'name': 'test_to_transit', + 'location_id': self.stock_location_a.id, + 'location_dest_id': intercom_location.id, + 'product_id': product_lot.id, + 'product_uom': product_lot.uom_id.id, + 'product_uom_qty': 1.0, + 'picking_type_id': picking_type_to_transit.id, + 'route_ids': [(4, route.id)], + }) + move_to_transit._action_confirm() + move_to_transit._action_assign() + move_line_2 = move_to_transit.move_line_ids[0] + self.assertTrue(move_line_2.lot_id, move_line_1.lot_id) + move_line_2.qty_done = 1.0 + move_to_transit._action_done() + + move_push = self.env['stock.move'].search([('location_id', '=', intercom_location.id), + ('product_id', '=', product_lot.id)]) + self.assertTrue(move_push, 'No move created from push rules') + self.assertEqual(move_push.state, "assigned") + self.assertTrue(move_push.move_line_ids, "No move line created for the move") + self.assertFalse(move_push in move_to_transit.move_dest_ids, + "Chained move created in transit location") + self.assertNotEqual(move_push.move_line_ids.lot_id, move_line_2.lot_id, + "Reserved from transit location") + picking_receipt = move_push.picking_id + with self.assertRaises(UserError): + picking_receipt.button_validate() + + move_line_3 = move_push.move_line_ids[0] + move_line_3.lot_name = 'lot 2' + move_line_3.qty_done = 1.0 + picking_receipt.button_validate() + lot_2 = move_line_3.lot_id + self.assertEqual(lot_1.company_id, self.company_a) + self.assertEqual(lot_1.name, 'lot 1') + self.assertEqual(self.env['stock.quant']._get_available_quantity(product_lot, intercom_location, lot_1), 1.0) + self.assertEqual(lot_2.company_id, self.company_b) + self.assertEqual(lot_2.name, 'lot 2') + self.assertEqual(self.env['stock.quant']._get_available_quantity(product_lot, self.stock_location_b, lot_2), 1.0) + + def test_intercom_lot_pull(self): + """Use warehouse of comany a to resupply warehouse of company b. Check + pull rule works correctly in two companies and moves are unchained from + inter-company transit location.""" + customer_location = self.env.ref('stock.stock_location_customers') + supplier_location = self.env.ref('stock.stock_location_suppliers') + intercom_location = self.env.ref('stock.stock_location_inter_wh') + intercom_location.write({'active': True}) + partner = self.env['res.partner'].create({'name': 'Deco Addict'}) + self.warehouse_a.resupply_wh_ids = [(6, 0, [self.warehouse_b.id])] + resupply_route = self.env['stock.location.route'].search([('supplier_wh_id', '=', self.warehouse_b.id), + ('supplied_wh_id', '=', self.warehouse_a.id)]) + self.assertTrue(resupply_route, "Resupply route not found") + + product_lot = self.env['product.product'].create({ + 'type': 'product', + 'tracking': 'lot', + 'name': 'product lot', + 'route_ids': [(4, resupply_route.id), (4, self.env.ref('stock.route_warehouse0_mto').id)], + }) + + move_sup_to_whb = self.env['stock.move'].create({ + 'company_id': self.company_b.id, + 'name': 'from_supplier_to_whb', + 'location_id': supplier_location.id, + 'location_dest_id': self.warehouse_b.lot_stock_id.id, + 'product_id': product_lot.id, + 'product_uom': product_lot.uom_id.id, + 'product_uom_qty': 1.0, + 'picking_type_id': self.warehouse_b.in_type_id.id, + }) + move_sup_to_whb._action_confirm() + move_line_1 = move_sup_to_whb.move_line_ids[0] + move_line_1.lot_name = 'lot b' + move_line_1.qty_done = 1.0 + move_sup_to_whb._action_done() + lot_b = move_line_1.lot_id + + picking_out = self.env['stock.picking'].create({ + 'company_id': self.company_a.id, + 'partner_id': partner.id, + 'picking_type_id': self.warehouse_a.out_type_id.id, + 'location_id': self.stock_location_a.id, + 'location_dest_id': customer_location.id, + }) + move_wha_to_cus = self.env['stock.move'].create({ + 'name': "WH_A to Customer", + 'product_id': product_lot.id, + 'product_uom_qty': 1, + 'product_uom': product_lot.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location_a.id, + 'location_dest_id': customer_location.id, + 'warehouse_id': self.warehouse_a.id, + 'procure_method': 'make_to_order', + 'company_id': self.company_a.id, + }) + picking_out.action_confirm() + + move_whb_to_transit = self.env['stock.move'].search([('location_id', '=', self.stock_location_b.id), + ('product_id', '=', product_lot.id)]) + move_transit_to_wha = self.env['stock.move'].search([('location_id', '=', intercom_location.id), + ('product_id', '=', product_lot.id)]) + self.assertTrue(move_whb_to_transit, "No move created by pull rule") + self.assertTrue(move_transit_to_wha, "No move created by pull rule") + self.assertTrue(move_wha_to_cus in move_transit_to_wha.move_dest_ids, + "Moves are not chained") + self.assertFalse(move_transit_to_wha in move_whb_to_transit.move_dest_ids, + "Chained move created in transit location") + self.assertEqual(move_wha_to_cus.state, "waiting") + self.assertEqual(move_transit_to_wha.state, "waiting") + self.assertEqual(move_whb_to_transit.state, "confirmed") + + (move_wha_to_cus + move_whb_to_transit + move_transit_to_wha).picking_id.action_assign() + self.assertEqual(move_wha_to_cus.state, "waiting") + self.assertEqual(move_transit_to_wha.state, "assigned") + self.assertEqual(move_whb_to_transit.state, "assigned") + + res_dict = move_whb_to_transit.picking_id.button_validate() + self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer') + wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save() + wizard.process() + self.assertEqual(self.env['stock.quant']._get_available_quantity(product_lot, intercom_location, lot_b), 1.0) + with self.assertRaises(UserError): + move_transit_to_wha.picking_id.button_validate() + + move_line_2 = move_transit_to_wha.move_line_ids[0] + move_line_2.lot_name = 'lot a' + move_line_2.qty_done = 1.0 + move_transit_to_wha._action_done() + lot_a = move_line_2.lot_id + + move_wha_to_cus._action_assign() + self.assertEqual(move_wha_to_cus.state, "assigned") + res_dict = move_wha_to_cus.picking_id.button_validate() + self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer') + wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save() + wizard.process() + self.assertEqual(self.env['stock.quant']._get_available_quantity(product_lot, customer_location, lot_a), 1.0) + + self.assertEqual(lot_a.company_id, self.company_a) + self.assertEqual(lot_a.name, 'lot a') + self.assertEqual(lot_b.company_id, self.company_b) + self.assertEqual(lot_b.name, 'lot b') |
