# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from datetime import datetime from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError from odoo.addons.sale.tests.common import TestSaleCommonBase class TestSaleStock(TestSaleCommonBase): @classmethod def setUpClass(cls): super().setUpClass() cls.partner = cls.env.ref("base.res_partner_1") cls.product = cls.env.ref("product.product_delivery_01") cls.product2 = cls.env.ref("product.product_delivery_02") cls.product3 = cls.env.ref("product.product_order_01") cls.carrier1 = cls.env.ref("delivery.delivery_carrier") cls.carrier2 = cls.env.ref("delivery.delivery_local_delivery") cls.stock_location = cls.env.ref("stock.stock_location_stock") cls.env["stock.quant"]._update_available_quantity( cls.product, cls.stock_location, 100 ) cls.env.user_demo = cls.env.ref("base.user_demo") def _manual_delivery_wizard(self, records, vals=None): if not vals: vals = {} return ( self.env["manual.delivery"] .with_context( active_model=records._name, active_ids=records.ids, ) .create(vals) ) def test_00_sale_manual_delivery(self): """ Test SO's manual delivery; we do it with a user without admin rights """ model_user_order = self.env["sale.order"].with_user(self.env.user_demo) order = model_user_order.create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 5.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ) ], "manual_delivery": True, } ) # confirm our standard so, check the picking order.action_confirm() self.assertFalse( order.picking_ids, 'No picking should be created for "manual delivery" orders', ) # Raise error when user try to modify sale order in confirmd stage. with self.assertRaises(UserError): order.write({"manual_delivery": False}) # open the manual delivery wizard action = order.action_manual_delivery_wizard() self.assertEqual(action["res_model"], "manual.delivery") # create a manual delivery for all ordered quantity self._manual_delivery_wizard(order).confirm() # check picking is created self.assertTrue( order.picking_ids, 'Picking should be created after "manual delivery" wizard call', ) # create a manual delivery, nothing left to ship wizard = self._manual_delivery_wizard(order) self.assertFalse( wizard.line_ids, "After picking creation for all products, " "no lines should be left in the wizard", ) wizard.confirm() self.assertEqual( len(order.picking_ids), 1.0, "Picking number should remain 1.0" ) def test_01_sale_standard_delivery(self): """ Test SO's standard delivery """ order = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 5.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ) ], "manual_delivery": False, } ) # confirm our standard so, check the picking order.action_confirm() self.assertTrue( order.picking_ids, 'Picking should be created for "standard delivery" orders', ) # deliver completely pick = order.picking_ids pick.action_assign() pick.move_line_ids.write({"quantity": 5}) pick.button_validate() # Check quantity delivered del_qty = sum(sol.qty_delivered for sol in order.order_line) self.assertEqual(del_qty, 5.0, "Delivery quantity doesn't match") def test_02_sale_various_manual_delivery(self): """ Test SO's various manual delivery """ order = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 5.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ) ], "manual_delivery": True, } ) # confirm our standard so, check the picking order.action_confirm() self.assertFalse( order.picking_ids, 'No picking should be created for "manual delivery" orders', ) # create a manual delivery for part of ordered quantity wizard = self._manual_delivery_wizard(order) wizard.line_ids.write({"quantity": 2.0}) wizard.confirm() # checking has_pending_delivery for Create Delivery button to appear self.assertTrue(order.has_pending_delivery) # check picking is created self.assertEqual( len(order.picking_ids), 1, 'Picking should be created after "manual delivery" wizard call', ) # deliver completely pick = order.picking_ids pick.action_assign() pick.move_line_ids.write({"quantity": 2}) pick.button_validate() # Check quantity delivered del_qty = sum(sol.qty_delivered for sol in order.order_line) self.assertEqual(del_qty, 2.0, "Delivery quantity doesn't match") # a manual delivery with qty 0 shouldn't do anything wizard = self._manual_delivery_wizard(order) wizard.line_ids.write({"quantity": 0.0}) wizard.confirm() self.assertEqual( len(order.picking_ids), 1.0, "No picking should've been created", ) # try to create a manual delivery with more quantity than the ordered wizard = self._manual_delivery_wizard(order) with self.assertRaises(UserError): wizard.line_ids.write({"quantity": 10.0}) wizard.confirm() # create a manual delivery, 3.0 left to ship wizard = self._manual_delivery_wizard(order) wizard.line_ids.write({"quantity": 3.0}) wizard.confirm() # checking has_pending_delivery for Create Delivery button to hide self.assertFalse(order.has_pending_delivery) self.assertEqual( len(order.picking_ids), 2.0, "Picking number doesn't match", ) def test_03_sale_selected_lines(self): """ Test SO's various manual delivery """ order1 = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 1.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ) ], "manual_delivery": True, } ) order2 = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product2.name, "product_id": self.product2.id, "product_uom_qty": 2.0, "product_uom": self.product2.uom_id.id, "price_unit": self.product2.list_price, }, ) ], "manual_delivery": True, } ) order3 = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product3.name, "product_id": self.product.id, "product_uom_qty": 3.0, "product_uom": self.product3.uom_id.id, "price_unit": self.product3.list_price, }, ) ], "manual_delivery": True, } ) # confirm our standard so, check the picking order1.action_confirm() order2.action_confirm() order3.action_confirm() some_lines = order1.order_line | order3.order_line all_lines = order1.order_line | order2.order_line | order3.order_line # create a manual delivery for part of ordered quantity wizard = self._manual_delivery_wizard(some_lines) self.assertEqual(sum(wizard.line_ids.mapped("quantity")), 4.0) wizard.confirm() # check picking is created self.assertTrue( order3.picking_ids, 'Picking should be created after "manual delivery" wizard call', ) self.assertEqual( len(order3.picking_ids.move_ids), 1, "Different sales orders should still create different pickings", ) self.assertFalse( order2.picking_ids, 'Picking should not be created after "manual delivery" wizard call', ) # test action undelivered undelivered = self.env["sale.order.line"].search( [ ("qty_to_procure", ">", 0), ("state", "=", "sale"), ("id", "in", all_lines.ids), ] ) self.assertEqual( undelivered, order2.order_line, "Bad pending qty to deliver filter", ) def test_03_sale_multi_delivery(self): self.env["stock.quant"]._update_available_quantity( self.product2, self.stock_location, 100 ) order = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 10.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ), ( 0, 0, { "name": self.product2.name, "product_id": self.product2.id, "product_uom_qty": 10.0, "product_uom": self.product2.uom_id.id, "price_unit": self.product2.list_price, }, ), ], "manual_delivery": True, } ) # confirm our standard so, check the picking order.action_confirm() self.assertFalse( order.picking_ids, 'No picking should be created for "manual delivery" orders', ) # create a manual delivery for part of ordered quantity date_now = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) wizard = self._manual_delivery_wizard( order.order_line[0], {"carrier_id": order.carrier_id.id, "date_planned": date_now}, ) wizard.line_ids.write({"quantity": 2.0}) wizard.confirm() # check picking is created self.assertEqual( len(order.picking_ids), 1, 'Picking should be created after "manual delivery" wizard call', ) first_picking = order.picking_ids self.assertEqual( first_picking.scheduled_date.replace( hour=0, minute=0, second=0, microsecond=0 ), date_now, ) # create a second manual delivery for next week date_next_week = date_now + relativedelta(weeks=1) wizard = self._manual_delivery_wizard( order.order_line[1], {"carrier_id": order.carrier_id.id, "date_planned": date_next_week}, ) wizard.line_ids.write({"quantity": 3.0}) wizard.confirm() self.assertEqual( len(order.picking_ids), 2, "Sale Manual Delivery: second picking should be created after" ' "manual delivery" wizard call with different date', ) second_picking = order.picking_ids - first_picking self.assertEqual( second_picking.scheduled_date.replace( hour=0, minute=0, second=0, microsecond=0 ), date_next_week, ) # create a third manual delivery for today (should be mixed with first) new_date_now = datetime.now() wizard = self._manual_delivery_wizard( order.order_line[0], {"carrier_id": order.carrier_id.id, "date_planned": new_date_now}, ) wizard.line_ids.write({"quantity": 5.0}) wizard.confirm() self.assertEqual( len(order.picking_ids), 2, "Sale Manual Delivery: new moves should be merged in first picking" ' after "manual delivery" wizard call with same date', ) self.assertEqual( sum(first_picking.mapped("move_ids.product_uom_qty")), 7, ) def test_04_sale_single_picking(self): """ Test SO's various manual delivery """ order = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 1.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ), ( 0, 0, { "name": self.product2.name, "product_id": self.product2.id, "product_uom_qty": 2.0, "product_uom": self.product2.uom_id.id, "price_unit": self.product2.list_price, }, ), ], "manual_delivery": True, } ) # confirm our standard so, check the picking order.action_confirm() # create a manual delivery for part of ordered quantity wizard = self._manual_delivery_wizard(order.order_line) wizard.confirm() self.assertEqual( len(order.picking_ids), 1.0, "Delivery: picking number should be 1.0" ) def test_05_sale_multi_carrier(self): order = self.env["sale.order"].create( { "partner_id": self.partner.id, "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ ( 0, 0, { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 10.0, "product_uom": self.product.uom_id.id, "price_unit": self.product.list_price, }, ), ], "manual_delivery": True, "carrier_id": self.carrier1.id, } ) # confirm our standard so, check the picking order.action_confirm() # create a manual delivery for part of ordered quantity wizard = self._manual_delivery_wizard(order, {"carrier_id": self.carrier1.id}) wizard.line_ids.write({"quantity": 2.0}) wizard.confirm() # check picking is created self.assertEqual( len(order.picking_ids), 1, 'Picking should be created after "manual delivery" wizard call', ) first_picking = order.picking_ids self.assertEqual( first_picking.carrier_id, order.carrier_id, "Picking carrier should be the one in the order", ) # create a second manual delivery with a different carrier wizard = self._manual_delivery_wizard(order, {"carrier_id": self.carrier2.id}) wizard.line_ids.write({"quantity": 2.0}) wizard.confirm() self.assertEqual( len(order.picking_ids), 2, "A different picking should've been created, as the carrier is different", ) second_picking = order.picking_ids - first_picking self.assertEqual( second_picking.carrier_id, self.carrier2, "Picking carrier should be the one selected", ) # create a third manual delivery for (should be mixed with first) wizard = self._manual_delivery_wizard(order, {"carrier_id": self.carrier1.id}) wizard.line_ids.write({"quantity": 2.0}) wizard.confirm() self.assertEqual( len(order.picking_ids), 2, "The first picking should be re-used", )