diff --git a/stock_request/models/stock_move.py b/stock_request/models/stock_move.py index 077452f46..3d2732963 100644 --- a/stock_request/models/stock_move.py +++ b/stock_request/models/stock_move.py @@ -48,3 +48,26 @@ class StockMove(models.Model): "that of the location." ) ) + + def copy_data(self, default=None): + if not default: + default = {} + if "allocation_ids" not in default: + default["allocation_ids"] = [] + for alloc in self.allocation_ids: + default["allocation_ids"].append( + ( + 0, + 0, + { + "stock_request_id": alloc.stock_request_id.id, + "requested_product_uom_qty": alloc.requested_product_uom_qty, + }, + ) + ) + return super(StockMove, self).copy_data(default) + + def _action_cancel(self): + res = super()._action_cancel() + self.mapped("allocation_ids.stock_request_id").check_done() + return res diff --git a/stock_request/models/stock_request.py b/stock_request/models/stock_request.py index f18cd3887..f1c545398 100644 --- a/stock_request/models/stock_request.py +++ b/stock_request/models/stock_request.py @@ -104,8 +104,16 @@ class StockRequest(models.Model): store=True, help="Quantity completed", ) + qty_cancelled = fields.Float( + "Qty Cancelled", + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty", + store=True, + help="Quantity cancelled", + ) picking_count = fields.Integer( - string="Delivery Orders", compute="_compute_picking_ids", readonly=True + string="Delivery Orders", compute="_compute_picking_ids", readonly=True, ) allocation_ids = fields.One2many( comodel_name="stock.request.allocation", @@ -136,12 +144,16 @@ class StockRequest(models.Model): ("name_uniq", "unique(name, company_id)", "Stock Request name must be unique") ] - @api.depends("allocation_ids") + @api.depends("allocation_ids", "allocation_ids.stock_move_id") def _compute_move_ids(self): for request in self: request.move_ids = request.allocation_ids.mapped("stock_move_id") - @api.depends("allocation_ids") + @api.depends( + "allocation_ids", + "allocation_ids.stock_move_id", + "allocation_ids.stock_move_id.picking_id", + ) def _compute_picking_ids(self): for request in self: request.picking_count = 0 @@ -161,12 +173,22 @@ class StockRequest(models.Model): for request in self: done_qty = sum(request.allocation_ids.mapped("allocated_product_qty")) open_qty = sum(request.allocation_ids.mapped("open_product_qty")) - request.qty_done = request.product_id.uom_id._compute_quantity( - done_qty, request.product_uom_id - ) - request.qty_in_progress = request.product_id.uom_id._compute_quantity( + uom = request.product_id.uom_id + request.qty_done = uom._compute_quantity(done_qty, request.product_uom_id) + request.qty_in_progress = uom._compute_quantity( open_qty, request.product_uom_id ) + request.qty_cancelled = ( + max( + 0, + uom._compute_quantity( + request.product_qty - done_qty - open_qty, + request.product_uom_id, + ), + ) + if request.allocation_ids + else 0 + ) @api.constrains("order_id", "requested_by") def check_order_requested_by(self): @@ -208,7 +230,7 @@ class StockRequest(models.Model): def _action_confirm(self): self._action_launch_procurement_rule() - self.state = "open" + self.write({"state": "open"}) def action_confirm(self): self._action_confirm() @@ -219,14 +241,13 @@ class StockRequest(models.Model): return True def action_cancel(self): - self.mapped("move_ids")._action_cancel() - self.state = "cancel" + self.sudo().mapped("move_ids")._action_cancel() + self.write({"state": "cancel"}) return True def action_done(self): - self.state = "done" - if self.order_id: - self.order_id.check_done() + self.write({"state": "done"}) + self.mapped("order_id").check_done() return True def check_done(self): @@ -245,8 +266,20 @@ class StockRequest(models.Model): >= 0 ): request.action_done() + elif request._check_done_allocation(): + request.action_done() return True + def _check_done_allocation(self): + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + self.ensure_one() + return ( + self.allocation_ids + and float_compare(self.qty_cancelled, 0, precision_digits=precision) > 0 + ) + def _prepare_procurement_values(self, group_id=False): """ Prepare specific key for moves or other components that diff --git a/stock_request/models/stock_request_allocation.py b/stock_request/models/stock_request_allocation.py index 4f2f962a9..8d12f75ea 100644 --- a/stock_request/models/stock_request_allocation.py +++ b/stock_request/models/stock_request_allocation.py @@ -52,11 +52,12 @@ class StockRequestAllocation(models.Model): ) allocated_product_qty = fields.Float( "Allocated Quantity", + copy=False, help="Quantity of the stock request allocated to the stock move, " "in the default UoM of the product", ) open_product_qty = fields.Float( - "Open Quantity", compute="_compute_open_product_qty" + "Open Quantity", compute="_compute_open_product_qty", ) @api.depends( @@ -78,7 +79,7 @@ class StockRequestAllocation(models.Model): ) def _compute_open_product_qty(self): for rec in self: - if rec.stock_move_id.state == "cancel": + if rec.stock_move_id.state in ["cancel", "done"]: rec.open_product_qty = 0.0 else: rec.open_product_qty = ( diff --git a/stock_request/models/stock_request_order.py b/stock_request/models/stock_request_order.py index dc5baf9b8..442e6e8a7 100644 --- a/stock_request/models/stock_request_order.py +++ b/stock_request/models/stock_request_order.py @@ -223,30 +223,28 @@ class StockRequestOrder(models.Model): line.procurement_group_id = self.procurement_group_id def action_confirm(self): - for line in self.stock_request_ids: - line.action_confirm() - self.state = "open" + self.mapped("stock_request_ids").action_confirm() + self.write({"state": "open"}) return True def action_draft(self): - for line in self.stock_request_ids: - line.action_draft() - self.state = "draft" + self.mapped("stock_request_ids").action_draft() + self.write({"state": "draft"}) return True def action_cancel(self): - for line in self.stock_request_ids: - line.action_cancel() - self.state = "cancel" + self.mapped("stock_request_ids").action_cancel() + self.write({"state": "cancel"}) return True def action_done(self): - self.state = "done" + self.write({"state": "done"}) return True def check_done(self): - if not self.stock_request_ids.filtered(lambda r: r.state != "done"): - self.action_done() + for rec in self: + if not rec.stock_request_ids.filtered(lambda r: r.state != "done"): + rec.action_done() return def action_view_transfer(self): diff --git a/stock_request/tests/test_stock_request.py b/stock_request/tests/test_stock_request.py index cbfb46477..f320cbaaf 100644 --- a/stock_request/tests/test_stock_request.py +++ b/stock_request/tests/test_stock_request.py @@ -993,3 +993,107 @@ class TestStockRequestBase(TestStockRequest): order = self.request_order.with_user(self.stock_request_user).create(vals) order.stock_request_ids.onchange_warehouse_id() self.assertEqual(order.stock_request_ids[0].location_id, self.virtual_loc) + + def test_cancellation(self): + group = self.env["procurement.group"].create({"name": "Procurement group"}) + product2 = self._create_product("SH2", "Shoes2", False) + product3 = self._create_product("SH3", "Shoes3", False) + self.product.type = "consu" + product2.type = "consu" + product3.type = "consu" + vals = { + "company_id": self.main_company.id, + "warehouse_id": self.warehouse.id, + "location_id": self.virtual_loc.id, + "procurement_group_id": group.id, + "stock_request_ids": [ + ( + 0, + 0, + { + "product_id": self.product.id, + "product_uom_id": self.product.uom_id.id, + "procurement_group_id": group.id, + "product_uom_qty": 5.0, + "company_id": self.main_company.id, + "warehouse_id": self.warehouse.id, + "location_id": self.virtual_loc.id, + }, + ), + ( + 0, + 0, + { + "product_id": product2.id, + "product_uom_id": self.product.uom_id.id, + "procurement_group_id": group.id, + "product_uom_qty": 5.0, + "company_id": self.main_company.id, + "warehouse_id": self.warehouse.id, + "location_id": self.virtual_loc.id, + }, + ), + ( + 0, + 0, + { + "product_id": product3.id, + "product_uom_id": self.product.uom_id.id, + "procurement_group_id": group.id, + "product_uom_qty": 5.0, + "company_id": self.main_company.id, + "warehouse_id": self.warehouse.id, + "location_id": self.virtual_loc.id, + }, + ), + ], + } + order = self.request_order.create(vals) + self.product.route_ids = [(6, 0, self.route.ids)] + product2.route_ids = [(6, 0, self.route.ids)] + product3.route_ids = [(6, 0, self.route.ids)] + order.action_confirm() + picking = order.picking_ids + self.assertEqual(1, len(picking)) + picking.action_assign() + self.assertEqual(3, len(picking.move_lines)) + line = picking.move_lines.filtered(lambda r: r.product_id == self.product) + line.quantity_done = 1 + sr1 = order.stock_request_ids.filtered(lambda r: r.product_id == self.product) + sr2 = order.stock_request_ids.filtered(lambda r: r.product_id == product2) + sr3 = order.stock_request_ids.filtered(lambda r: r.product_id == product3) + self.assertNotEqual(sr1.state, "done") + self.assertNotEqual(sr2.state, "done") + self.assertNotEqual(sr3.state, "done") + self.env["stock.backorder.confirmation"].create( + {"pick_ids": [(4, picking.id)]} + ).process() + sr1.refresh() + sr2.refresh() + sr3.refresh() + self.assertNotEqual(sr1.state, "done") + self.assertNotEqual(sr2.state, "done") + self.assertNotEqual(sr3.state, "done") + picking = order.picking_ids.filtered( + lambda r: r.state not in ["done", "cancel"] + ) + self.assertEqual(1, len(picking)) + picking.action_assign() + self.assertEqual(3, len(picking.move_lines)) + line = picking.move_lines.filtered(lambda r: r.product_id == self.product) + line.quantity_done = 4 + line = picking.move_lines.filtered(lambda r: r.product_id == product2) + line.quantity_done = 1 + + self.env["stock.backorder.confirmation"].create( + {"pick_ids": [(4, picking.id)]} + ).process_cancel_backorder() + sr1.refresh() + sr2.refresh() + sr3.refresh() + self.assertEqual(sr1.state, "done") + self.assertEqual(sr1.qty_cancelled, 0) + self.assertEqual(sr2.state, "done") + self.assertEqual(sr2.qty_cancelled, 4) + self.assertEqual(sr3.state, "done") + self.assertEqual(sr3.qty_cancelled, 5) diff --git a/stock_request/views/stock_request_views.xml b/stock_request/views/stock_request_views.xml index e787bed2c..3e7311add 100644 --- a/stock_request/views/stock_request_views.xml +++ b/stock_request/views/stock_request_views.xml @@ -30,6 +30,7 @@ + @@ -208,8 +209,18 @@ groups="uom.group_uom" /> - - + + +