diff --git a/spp_change_request_v2/static/src/js/create_change_request.js b/spp_change_request_v2/static/src/js/create_change_request.js
index 9bcf63b7..fca558cc 100644
--- a/spp_change_request_v2/static/src/js/create_change_request.js
+++ b/spp_change_request_v2/static/src/js/create_change_request.js
@@ -4,9 +4,39 @@ import {FormController} from "@web/views/form/form_controller";
import {ListController} from "@web/views/list/list_controller";
import {onWillStart} from "@odoo/owl";
import {patch} from "@web/core/utils/patch";
+import {registry} from "@web/core/registry";
import {useService} from "@web/core/utils/hooks";
import {user} from "@web/core/user";
+/**
+ * Client action to close wizard modal and then open CR detail form.
+ * Uses async/await to ensure the modal is fully closed before navigating.
+ */
+async function openCRCloseModal(env, action) {
+ const actionService = env.services.action;
+ const params = action.params || {};
+
+ await actionService.doAction(
+ {type: "ir.actions.act_window_close"},
+ {clearBreadcrumbs: true}
+ );
+
+ if (params.res_id) {
+ await actionService.doAction({
+ type: "ir.actions.act_window",
+ name: params.name || "Change Request Details",
+ res_model: params.res_model,
+ res_id: params.res_id,
+ view_mode: "form",
+ views: [[params.view_id || false, "form"]],
+ target: "current",
+ context: params.context || {},
+ });
+ }
+}
+
+registry.category("actions").add("open_cr_close_modal", openCRCloseModal);
+
patch(ListController.prototype, {
setup() {
super.setup();
diff --git a/spp_change_request_v2/wizards/create_wizard.py b/spp_change_request_v2/wizards/create_wizard.py
index d7f6ce38..811f2095 100644
--- a/spp_change_request_v2/wizards/create_wizard.py
+++ b/spp_change_request_v2/wizards/create_wizard.py
@@ -232,35 +232,38 @@ def action_create_draft(self):
}
)
- # Open the detail form directly for editing
+ # Close wizard modal and open detail form using client action
+ # The client action ensures the modal is fully closed before navigating
detail = cr.get_detail()
if detail:
view_id = self.request_type_id.get_detail_form_view_id()
return {
- "type": "ir.actions.act_window",
- "name": "Change Request Details",
- "res_model": cr.detail_res_model,
- "res_id": detail.id,
- "view_mode": "form",
- "view_id": view_id,
- "target": "current",
- "context": {
- "create": False,
- "delete": False,
- "form_view_initial_mode": "edit",
+ "type": "ir.actions.client",
+ "tag": "open_cr_close_modal",
+ "params": {
+ "name": _("Change Request Details"),
+ "res_model": cr.detail_res_model,
+ "res_id": detail.id,
+ "view_id": view_id,
+ "context": {
+ "create": False,
+ "delete": False,
+ "form_view_initial_mode": "edit",
+ },
},
}
# Fallback: open CR form if no detail model configured
return {
- "type": "ir.actions.act_window",
- "name": "Change Request",
- "res_model": "spp.change.request",
- "res_id": cr.id,
- "view_mode": "form",
- "target": "current",
- "context": {
- "form_view_initial_mode": "edit",
+ "type": "ir.actions.client",
+ "tag": "open_cr_close_modal",
+ "params": {
+ "name": _("Change Request"),
+ "res_model": "spp.change.request",
+ "res_id": cr.id,
+ "context": {
+ "form_view_initial_mode": "edit",
+ },
},
}
diff --git a/spp_programs/static/src/js/create_program.js b/spp_programs/static/src/js/create_program.js
index ac0dffa9..a7a1500b 100644
--- a/spp_programs/static/src/js/create_program.js
+++ b/spp_programs/static/src/js/create_program.js
@@ -15,6 +15,8 @@ import {useService} from "@web/core/utils/hooks";
async function openProgramCloseModal(env, action) {
const actionService = env.services.action;
const programId = action.params?.program_id;
+ const programName = action.params?.name || "Program";
+ const viewId = action.params?.view_id || false;
await actionService.doAction(
{type: "ir.actions.act_window_close"},
@@ -24,10 +26,10 @@ async function openProgramCloseModal(env, action) {
if (programId) {
await actionService.doAction({
type: "ir.actions.act_window",
- name: "Program",
+ name: programName,
res_model: "spp.program",
res_id: programId,
- views: [[false, "form"]],
+ views: [[viewId, "form"]],
target: "current",
});
}
diff --git a/spp_programs/tests/test_create_program_wiz.py b/spp_programs/tests/test_create_program_wiz.py
index 70527bba..6562cb11 100644
--- a/spp_programs/tests/test_create_program_wiz.py
+++ b/spp_programs/tests/test_create_program_wiz.py
@@ -34,7 +34,7 @@ def setUp(self):
}
)
- self.program = self._program_create_wiz.create_program()
+ self._program_action = self._program_create_wiz.create_program()
# self.cycle_manager_default = (
# self._program_create_wiz.create_cycle_manager_default(self.program.id)
# )
@@ -69,19 +69,19 @@ def test_02_create_program(self):
)
res = new_wiz.create_program()
self.assertEqual(type(res), dict, "Action should be in json format!")
- for key in ("type", "res_model", "res_id"):
+ for key in ("type", "tag", "params"):
self.assertIn(key, res.keys(), f"Key `{key}` is missing!")
self.assertEqual(
res["type"],
- "ir.actions.act_window",
+ "ir.actions.client",
"Action for program should be returned!",
)
- self.assertEqual(res["res_model"], "spp.program", "Action for program should be return!")
- self.assertTrue(res["res_id"], "New record for program should be existed!")
+ self.assertEqual(res["tag"], "open_program_close_modal", "Client action tag should match!")
+ self.assertTrue(res["params"]["program_id"], "New record for program should be existed!")
def test_03_get_eligibility_manager(self):
self._program_create_wiz.eligibility_type = "default_eligibility"
- res = self._program_create_wiz._get_eligibility_manager(self.program["res_id"])
+ res = self._program_create_wiz._get_eligibility_manager(self._program_action["params"]["program_id"])
self.assertIn("eligibility_managers", res)
@@ -115,7 +115,7 @@ def test_06_create_program(self):
"entitlement_item_ids": [Command.create({"product_id": self.product.id, "quantity": 1})],
}
)
- program = new_wiz.create_program()
+ action = new_wiz.create_program()
- self.assertEqual(program["res_model"], "spp.program")
- self.assertIsNotNone(program)
+ self.assertEqual(action["type"], "ir.actions.client")
+ self.assertTrue(action["params"]["program_id"])
diff --git a/spp_programs/tests/test_spp_cycle_compliance.py b/spp_programs/tests/test_spp_cycle_compliance.py
index ba9f0276..6a8ff8dd 100644
--- a/spp_programs/tests/test_spp_cycle_compliance.py
+++ b/spp_programs/tests/test_spp_cycle_compliance.py
@@ -21,7 +21,7 @@ def setUpClass(cls):
}
)
action = cls._test.create_program()
- cls.program = cls.env["spp.program"].browse(action["res_id"])
+ cls.program = cls.env["spp.program"].browse(action["params"]["program_id"])
@classmethod
def _create_individual(self, vals):
diff --git a/spp_programs/tests/test_spp_program_create_wizard_compliance.py b/spp_programs/tests/test_spp_program_create_wizard_compliance.py
index d49ae247..cbf28cd2 100644
--- a/spp_programs/tests/test_spp_program_create_wizard_compliance.py
+++ b/spp_programs/tests/test_spp_program_create_wizard_compliance.py
@@ -12,7 +12,7 @@ def test_01_create_program_without_compliance_manager(self):
wizard = self.program_create_wizard({})
wizard._check_compliance_manager_info()
action = wizard.create_program()
- program = self.env["spp.program"].browse(action["res_id"])
+ program = self.env["spp.program"].browse(action["params"]["program_id"])
self.assertFalse(
bool(program.compliance_manager_ids),
"Should not create compliance manager for new program!",
@@ -38,7 +38,7 @@ def test_03_create_program_default_compliance_manager(self):
}
)
action = wizard.create_program()
- program = self.env["spp.program"].browse(action["res_id"])
+ program = self.env["spp.program"].browse(action["params"]["program_id"])
self.assertTrue(
bool(program.compliance_manager_ids),
"Should create compliance manager for new program!",
diff --git a/spp_programs/views/managers/eligibility_manager_view.xml b/spp_programs/views/managers/eligibility_manager_view.xml
index 61f88970..4fd39a5d 100644
--- a/spp_programs/views/managers/eligibility_manager_view.xml
+++ b/spp_programs/views/managers/eligibility_manager_view.xml
@@ -67,7 +67,7 @@ Part of OpenSPP. See LICENSE file for full copyright and licensing details.
-
+
-
+