diff --git a/spp_base_common/__manifest__.py b/spp_base_common/__manifest__.py
index 8792f3b..77f2c4f 100644
--- a/spp_base_common/__manifest__.py
+++ b/spp_base_common/__manifest__.py
@@ -34,6 +34,8 @@
"assets": {
"web.assets_backend": [
"spp_base_common/static/src/scss/navbar.scss",
+ "spp_base_common/static/src/js/custom_list_create.js",
+ "spp_base_common/static/src/xml/custom_list_create_template.xml",
],
"web._assets_primary_variables": [
"spp_base_common/static/src/scss/colors.scss",
diff --git a/spp_base_common/static/src/js/custom_list_create.js b/spp_base_common/static/src/js/custom_list_create.js
new file mode 100644
index 0000000..2966120
--- /dev/null
+++ b/spp_base_common/static/src/js/custom_list_create.js
@@ -0,0 +1,25 @@
+/** @odoo-module **/
+
+import {ListController} from "@web/views/list/list_controller";
+import {patch} from "@web/core/utils/patch";
+
+/**
+ * Base handler for the generic custom create button.
+ * Modules override this method via patch to handle their specific model.
+ *
+ * Usage in other modules:
+ * patch(ListController.prototype, {
+ * async onCustomListCreate() {
+ * if (this.model.root.resModel === "my.model") {
+ * // open wizard
+ * return;
+ * }
+ * return super.onCustomListCreate(...arguments);
+ * },
+ * });
+ */
+patch(ListController.prototype, {
+ async onCustomListCreate() {
+ // Base no-op — overridden by consuming modules
+ },
+});
diff --git a/spp_base_common/static/src/xml/custom_list_create_template.xml b/spp_base_common/static/src/xml/custom_list_create_template.xml
new file mode 100644
index 0000000..fc16d05
--- /dev/null
+++ b/spp_base_common/static/src/xml/custom_list_create_template.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ !hideFormCreateButton
+
+
+
+
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 e9a8ee0..9bcf63b 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
@@ -1,5 +1,6 @@
/* @odoo-module */
+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";
@@ -11,25 +12,47 @@ patch(ListController.prototype, {
super.setup();
this.actionService = useService("action");
onWillStart(async () => {
+ if (this.model.root.resModel !== "spp.change.request") {
+ return;
+ }
const is_admin = await user.hasGroup("spp_security.group_spp_admin");
const is_cr_user = await user.hasGroup(
"spp_change_request_v2.group_cr_user"
);
- this.canCreateChangeRequest = is_admin || is_cr_user;
+ if (is_admin || is_cr_user) {
+ this.customListCreateButton = {
+ label: "New Request",
+ title: "Create a New Change Request",
+ className: "o_list_button_add_cr",
+ };
+ }
});
},
- async loadChangeRequestWizard() {
- if (this.model.root.resModel !== "spp.change.request") {
+ /**
+ * Opens the Create Change Request wizard when the custom button is clicked.
+ */
+ async onCustomListCreate() {
+ if (this.model.root.resModel === "spp.change.request") {
+ await this.actionService.doAction(
+ "spp_change_request_v2.action_cr_create_wizard",
+ {
+ onClose: async () => {
+ await this.model.root.load();
+ },
+ }
+ );
return;
}
- await this.actionService.doAction(
- "spp_change_request_v2.action_cr_create_wizard",
- {
- onClose: async () => {
- await this.model.root.load();
- },
- }
- );
+ return super.onCustomListCreate(...arguments);
+ },
+});
+
+patch(FormController.prototype, {
+ setup() {
+ super.setup();
+ if (this.props.resModel === "spp.change.request") {
+ this.hideFormCreateButton = true;
+ }
},
});
diff --git a/spp_change_request_v2/static/src/xml/create_change_request_template.xml b/spp_change_request_v2/static/src/xml/create_change_request_template.xml
index 331cebe..2dd821d 100644
--- a/spp_change_request_v2/static/src/xml/create_change_request_template.xml
+++ b/spp_change_request_v2/static/src/xml/create_change_request_template.xml
@@ -1,46 +1,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- model.root.resModel != 'spp.change.request'
-
-
+
diff --git a/spp_programs/static/src/js/create_program.js b/spp_programs/static/src/js/create_program.js
index 77f1e9c..ac0dffa 100644
--- a/spp_programs/static/src/js/create_program.js
+++ b/spp_programs/static/src/js/create_program.js
@@ -1,40 +1,86 @@
/** @odoo-module **/
+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 {user} from "@web/core/user";
import {useService} from "@web/core/utils/hooks";
+/**
+ * Client action to close modal and then open program form.
+ * This ensures proper sequencing with async/await.
+ */
+async function openProgramCloseModal(env, action) {
+ const actionService = env.services.action;
+ const programId = action.params?.program_id;
+
+ await actionService.doAction(
+ {type: "ir.actions.act_window_close"},
+ {clearBreadcrumbs: true}
+ );
+
+ if (programId) {
+ await actionService.doAction({
+ type: "ir.actions.act_window",
+ name: "Program",
+ res_model: "spp.program",
+ res_id: programId,
+ views: [[false, "form"]],
+ target: "current",
+ });
+ }
+}
+
+registry.category("actions").add("open_program_close_modal", openProgramCloseModal);
+
patch(ListController.prototype, {
setup() {
super.setup();
this.actionService = useService("action");
onWillStart(async () => {
- // Check if user has permission to create programs
- // Groups: spp_security.group_spp_admin OR spp_programs.group_programs_manager
+ if (this.model.root.resModel !== "spp.program") {
+ return;
+ }
const is_admin = await user.hasGroup("spp_security.group_spp_admin");
- const is_program_manager = await user.hasGroup("spp_programs.group_programs_manager");
- this.canCreateProgram = is_admin || is_program_manager;
+ const is_program_manager = await user.hasGroup(
+ "spp_programs.group_programs_manager"
+ );
+ if (is_admin || is_program_manager) {
+ this.customListCreateButton = {
+ label: "Create Program",
+ title: "Create a New Program",
+ className: "o_list_button_add_program",
+ };
+ }
});
},
/**
- * Opens the Create Program wizard when the "Create Program" button is clicked.
- * This is called from the custom button in the Programs list view.
+ * Opens the Create Program wizard when the custom button is clicked.
*/
- async load_wizard() {
- // Only proceed if we're on the spp.program model
- if (this.model.root.resModel !== "spp.program") {
+ async onCustomListCreate() {
+ if (this.model.root.resModel === "spp.program") {
+ await this.actionService.doAction(
+ "spp_programs.action_create_program_wizard",
+ {
+ onClose: async () => {
+ await this.model.root.load();
+ },
+ }
+ );
return;
}
+ return super.onCustomListCreate(...arguments);
+ },
+});
- // Open the create program wizard action
- await this.actionService.doAction("spp_programs.action_create_program_wizard", {
- onClose: async () => {
- // Refresh the list after the wizard closes
- await this.model.root.load();
- },
- });
+patch(FormController.prototype, {
+ setup() {
+ super.setup();
+ if (this.props.resModel === "spp.program") {
+ this.hideFormCreateButton = true;
+ }
},
});
diff --git a/spp_programs/static/src/xml/create_program_template.xml b/spp_programs/static/src/xml/create_program_template.xml
index 38dd9c2..2dd821d 100644
--- a/spp_programs/static/src/xml/create_program_template.xml
+++ b/spp_programs/static/src/xml/create_program_template.xml
@@ -1,43 +1,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- model.root.resModel != 'spp.program'
-
-
+