# Copyright 2026 Criptomart # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import Command from odoo.exceptions import UserError from odoo.tests import TransactionCase from odoo.tests import tagged @tagged("-at_install", "post_install") class TestPickingCollected(TransactionCase): """Collected checks and ordered quantities on transfers outside a batch.""" @classmethod def setUpClass(cls): super().setUpClass() cls.stock_location = cls.env.ref("stock.stock_location_stock") cls.picking_type_out = cls.env.ref("stock.picking_type_out") cls.picking_type_out.collected_check_required = True cls.env.company.write( { "batch_detailed_restriction_enabled": True, "batch_detailed_restriction_scope": "processed", "batch_summary_restriction_enabled": False, } ) kg = cls.env.ref("uom.product_uom_kgm") cls.product = cls.env["product.product"].create( { "name": "Collected Pears", "is_storable": True, "uom_id": kg.id, "uom_po_id": kg.id, } ) cls.env["stock.quant"]._update_available_quantity( cls.product, cls.stock_location, 50.0 ) cls.partner = cls.env["res.partner"].create({"name": "Pear Lover"}) def _create_sale_picking(self, qty=2.0): order = self.env["sale.order"].create( { "partner_id": self.partner.id, "order_line": [ Command.create( {"product_id": self.product.id, "product_uom_qty": qty} ) ], } ) order.action_confirm() picking = order.picking_ids picking.action_assign() return picking def test_loose_picking_blocks_until_collected(self): picking = self._create_sale_picking() with self.assertRaisesRegex(UserError, self.product.display_name): picking.button_validate() picking.move_ids.is_collected = True picking.button_validate() self.assertEqual(picking.state, "done") def test_picking_type_without_check_is_not_blocked(self): self.picking_type_out.collected_check_required = False picking = self._create_sale_picking() picking.button_validate() self.assertEqual(picking.state, "done") def test_batch_picking_follows_the_batch_restriction(self): """The batch summary flag is not required by the loose transfer check.""" self.env.company.batch_summary_restriction_enabled = True picking = self._create_sale_picking() batch = self.env["stock.picking.batch"].create( {"picking_ids": [Command.set(picking.ids)]} ) batch.action_confirm() picking.move_ids.is_collected = True with self.assertRaisesRegex(UserError, "product summary"): batch.action_done() def test_collecting_the_move_collects_its_lines(self): picking = self._create_sale_picking() move = picking.move_ids self.assertFalse(move.is_collected) move.is_collected = True self.assertTrue(all(move.move_line_ids.mapped("is_collected"))) self.assertTrue(move.picked) move.move_line_ids.is_collected = False self.assertFalse(move.is_collected) def test_ordered_quantity_survives_weighing(self): picking = self._create_sale_picking(2.0) move = picking.move_ids move.move_line_ids.write({"quantity": 1.5, "picked": True}) self.assertEqual(move.product_uom_qty, 1.5) self.assertEqual(move.sale_ordered_qty, 2.0) self.assertEqual(move.move_line_ids.sale_ordered_qty, 2.0) def test_move_line_demand_follows_the_move(self): picking = self._create_sale_picking(2.0) move = picking.move_ids line = move.move_line_ids self.assertEqual(line.move_demand_qty, 2.0) gram = self.env.ref("uom.product_uom_gram") line.product_uom_id = gram self.assertEqual(line.move_demand_qty, 2000.0) move.product_uom_qty = 3.0 self.assertEqual(line.move_demand_qty, 3000.0) def test_move_line_demand_without_sale(self): """Unlike the ordered quantity, the demand needs no sale order.""" picking = self.env["stock.picking"].create( { "picking_type_id": self.picking_type_out.id, "location_id": self.stock_location.id, "location_dest_id": self.env.ref("stock.stock_location_customers").id, "move_ids": [ Command.create( { "name": self.product.name, "product_id": self.product.id, "product_uom_qty": 4.0, "product_uom": self.product.uom_id.id, "location_id": self.stock_location.id, "location_dest_id": self.env.ref( "stock.stock_location_customers" ).id, } ) ], } ) picking.action_confirm() picking.action_assign() line = picking.move_ids.move_line_ids self.assertEqual(line.move_demand_qty, 4.0) self.assertEqual(line.sale_ordered_qty, 0.0) def test_batch_summary_ordered_quantity(self): pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0) batch = self.env["stock.picking.batch"].create( {"picking_ids": [Command.set(pickings.ids)]} ) pickings[0].move_ids.move_line_ids.write({"quantity": 1.5, "picked": True}) batch._compute_summary_line_ids() line = batch.summary_line_ids self.assertEqual(line.qty_ordered, 5.0) self.assertEqual(line.qty_demanded, 4.5) def test_report_prints_every_delivery_slip(self): pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0) batch = self.env["stock.picking.batch"].create( {"picking_ids": [Command.set(pickings.ids)]} ) html, _report_format = self.env["ir.actions.report"]._render_qweb_html( "stock_picking_batch_collect.report_batch_deliveryslip", batch.ids ) for picking in pickings: self.assertIn(picking.name, html.decode())