Skip to content

Commit

Permalink
[FIX] rma
Browse files Browse the repository at this point in the history
Avoid reserving units that are not available. Also ensures the lot picked is the correct one by adding a dependency to stock_move_forced_lot.
  • Loading branch information
DavidJForgeFlow committed Dec 11, 2024
1 parent 8912809 commit 6854f01
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 79 deletions.
2 changes: 1 addition & 1 deletion rma/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"in odoo",
"author": "ForgeFlow",
"website": "https://github.com/ForgeFlow/stock-rma",
"depends": ["stock", "mail", "web"],
"depends": ["stock", "mail", "web", "stock_move_forced_lot"],
"demo": ["demo/stock_demo.xml"],
"data": [
"security/rma.xml",
Expand Down
88 changes: 87 additions & 1 deletion rma/tests/test_rma.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ def _create_rma_from_move(
).default_get([str(move.id), str(cls.partner_id.id)])
data = wizard.with_user(
cls.rma_basic_user
)._prepare_rma_line_from_stock_move(move)
)._prepare_rma_line_from_stock_move(
move, lot=len(move.lot_ids) == 1 and move.lot_ids[0] or False
)
data["type"] = "supplier"
if dropship:
data.update(
Expand Down Expand Up @@ -1361,3 +1363,87 @@ def test_11_customer_rma_tracking_serial(self):
mv.quantity_done = mv.product_uom_qty
picking._action_done()
self.assertEqual(picking.state, "done", "Final picking should has done state")

def test_12_move_reserving_correct_lot(self):
lot1 = self.lot_obj.create(
{
"product_id": self.product_1_serial.id,
"name": "LOT1",
}
)
lot2 = self.lot_obj.create(
{
"product_id": self.product_1_serial.id,
"name": "LOT2",
}
)
products2move = [
(
self.product_1_serial,
1,
lot1.id,
)
]
rma_supplier_id = self._create_rma_from_move(
products2move,
"supplier",
self.env.ref("base.res_partner_2"),
dropship=False,
)
rma = rma_supplier_id.rma_line_ids
rma.action_rma_to_approve()
wizard = self.rma_make_picking.with_context(
{
"active_ids": rma.ids,
"active_model": "rma.order.line",
"picking_type": "outgoing",
"active_id": rma.ids[0],
}
).create({})
wizard.action_create_picking()
res = rma.action_view_out_shipments()
self.assertTrue("res_id" in res, "Incorrect number of pickings" "created")
picking = self.env["stock.picking"].browse(res["res_id"])
self.assertEqual(len(picking), 1, "Incorrect number of pickings created")

quant_lot1 = self.env["stock.quant"].search(
[
("product_id", "=", self.product_1_serial.id),
("lot_id", "=", lot1.id),
("location_id", "=", self.stock_rma_location.id),
]
)
quant_lot1.quantity = 0
quant_lot2 = self.env["stock.quant"].search(
[
("product_id", "=", self.product_1_serial.id),
("lot_id", "=", lot2.id),
("location_id", "=", self.stock_rma_location.id),
]
)
quant_lot2.quantity = 0
moves = picking.move_lines
self.assertFalse(
moves.move_line_ids,
"There should not be any move_line created (no quantity to reserve).",
)
picking.action_assign()
self.assertFalse(
moves.move_line_ids,
"There should not be any move_line created (no quantity to reserve).",
)
quant_lot2.quantity = 1
picking.action_assign()
self.assertFalse(
moves.move_line_ids,
"There should not be any move_line created "
"(no quantity to reserve for the lot of the rma).",
)
quant_lot1.quantity = 1
picking.action_assign()
self.assertTrue(moves.move_line_ids)
self.assertEqual(moves.move_line_ids.lot_id, lot1)
for mv in picking.move_lines.move_line_ids:
mv.qty_done = mv.product_uom_qty
picking._action_done()
self.assertEqual(picking.state, "done", "Final picking should has done state")
153 changes: 76 additions & 77 deletions rma/wizards/rma_make_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def _get_procurement_data(self, item, group, qty, picking_type):
"location_id": location,
"rma_line_id": line.id,
"route_ids": route,
"lot_id": line.lot_id.id,
}
return procurement_data

Expand Down Expand Up @@ -216,99 +217,97 @@ def action_create_picking(self):
move_line_model = self.env["stock.move.line"]
picking_type = self.env.context.get("picking_type")
if picking_type == "outgoing":
pickings = self.mapped("item_ids.line_id")._get_out_pickings()
action = self.item_ids.line_id.action_view_out_shipments()
else:
pickings = self.mapped("item_ids.line_id")._get_in_pickings()
action = self.item_ids.line_id.action_view_in_shipments()
# Force the reservation of the RMA specific lot for incoming shipments.
# FIXME: still needs fixing, not reserving appropriate serials.
for move in pickings.move_lines.filtered(
lambda x: x.state not in ("draft", "cancel", "done", "waiting")
and x.rma_line_id
and x.product_id.tracking in ("lot", "serial")
and x.rma_line_id.lot_id
):
# Force the reservation of the RMA specific lot for incoming shipments.
is_final_step = self._is_final_step(move)
move.move_line_ids.unlink()
reference_moves = (
not is_final_step
and move.rma_line_id._get_stock_move_reference()
or self.env["stock.move"]
)
package = reference_moves.mapped("move_line_ids.result_package_id")
quants = self.env["stock.quant"]._gather(
move.product_id,
move.location_id,
lot_id=move.rma_line_id.lot_id,
package_id=len(package) == 1 and package or False,
)
move_line_data = move._prepare_move_line_vals(
reserved_quant=(len(quants) == 1) and quants or False
)
move_line_data.update(
{
"qty_done": 0,
}
)
if move.rma_line_id.lot_id and not quants:
# CHECK ME: force al least has lot assigned if quant is not found
move_line_data.update(
{
"lot_id": move.rma_line_id.lot_id.id,
}
for move in pickings.move_lines.filtered(
lambda x: x.state not in ("draft", "cancel", "done", "waiting")
and x.rma_line_id
and x.product_id.tracking in ("lot", "serial")
and x.rma_line_id.lot_id
):
# Force the reservation of the RMA specific lot for incoming shipments.
is_final_step = self._is_final_step(move)
move.move_line_ids.unlink()
reference_moves = (
not is_final_step
and move.rma_line_id._get_stock_move_reference()
or self.env["stock.move"]
)
if move.product_id.tracking == "serial":
move.write(
{
"lot_ids": move.rma_line_id.lot_id.ids,
}
package = reference_moves.mapped("move_line_ids.result_package_id")
quants = self.env["stock.quant"]._gather(
move.product_id,
move.location_id,
lot_id=move.rma_line_id.lot_id,
package_id=len(package) == 1 and package or False,
)
move_line_data = move._prepare_move_line_vals(
reserved_quant=(len(quants) == 1) and quants or False
)
move_line_data.update(
{
"product_uom_qty": 1.0,
"qty_done": 0,
}
)
if move.move_line_ids:
move.move_line_ids.with_context(
bypass_reservation_update=True
).write(
if move.rma_line_id.lot_id and not quants:
# CHECK ME: force al least has lot assigned if quant is not found
move_line_data.update(
{
"lot_id": move.rma_line_id.lot_id.id,
}
)
if move.product_id.tracking == "serial":
move.write(
{
"lot_ids": move.rma_line_id.lot_id.ids,
}
)
move_line_data.update(
{
"lot_id": move_line_data.get("lot_id"),
"package_id": move_line_data.get("package_id"),
"result_package_id": move_line_data.get(
"result_package_id", False
),
"product_uom_qty": 1.0,
}
)
if (
len(quants) == 1
and quants.reserved_quantity == 0
and quants.quantity == 1
and quants.location_id.usage not in ("customer", "supplier")
):
quants.sudo().write(
{"reserved_quantity": quants.reserved_quantity + 1}
if move.move_line_ids:
move.move_line_ids.with_context(
bypass_reservation_update=True
).write(
{
"lot_id": move_line_data.get("lot_id"),
"package_id": move_line_data.get("package_id"),
"result_package_id": move_line_data.get(
"result_package_id", False
),
"product_uom_qty": 1.0,
}
)
if (
len(quants) == 1
and quants.reserved_quantity == 0
and quants.quantity == 1
and quants.location_id.usage not in ("customer", "supplier")
):
quants.sudo().write(
{"reserved_quantity": quants.reserved_quantity + 1}
)
elif move.product_id.tracking == "lot":
if picking_type == "incoming":
qty = self.item_ids.filtered(
lambda x: x.line_id.id == move.rma_line_id.id
).qty_to_receive
else:
qty = self.item_ids.filtered(
lambda x: x.line_id.id == move.rma_line_id.id
).qty_to_deliver
move_line_data.update(
{
"product_uom_qty": qty if picking_type == "incoming" else 0,
}
)
elif move.product_id.tracking == "lot":
if picking_type == "incoming":
qty = self.item_ids.filtered(
lambda x: x.line_id.id == move.rma_line_id.id
).qty_to_receive
else:
qty = self.item_ids.filtered(
lambda x: x.line_id.id == move.rma_line_id.id
).qty_to_deliver
move_line_data.update(
{
"product_uom_qty": qty if picking_type == "incoming" else 0,
}
)
if not move.move_line_ids:
move_line_model.create(move_line_data)
pickings.with_context(force_no_bypass_reservation=True).action_assign()
if not move.move_line_ids:
move_line_model.create(move_line_data)
pickings.with_context(force_no_bypass_reservation=True).action_assign()
return action

def action_cancel(self):
Expand Down

0 comments on commit 6854f01

Please sign in to comment.