mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] stock_request: Improve cancellation detection
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<field name="product_uom_qty" />
|
||||
<field name="qty_in_progress" />
|
||||
<field name="qty_done" />
|
||||
<field name="qty_cancelled" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
@@ -208,8 +209,18 @@
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
</div>
|
||||
<field name="qty_in_progress" />
|
||||
<field name="qty_done" />
|
||||
<field
|
||||
name="qty_in_progress"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
<field
|
||||
name="qty_done"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
<field
|
||||
name="qty_cancelled"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
||||
Reference in New Issue
Block a user