From 7b8880b7b84a8b970360008b9d24be7cf54ef121 Mon Sep 17 00:00:00 2001 From: emjay0921 Date: Wed, 11 Feb 2026 11:00:20 +0800 Subject: [PATCH] fix(js): resolve create button conflict between Programs and Change Request Both spp_programs and spp_change_request_v2 used position="replace" on the same ListView control-panel-create-button slot. Whichever loaded last overwrote the other, causing Programs to lose its "Create Program" button. Move the generic extensible create button pattern to spp_base_common so any module can set customListCreateButton config and override onCustomListCreate() without conflicting. Same fix applied for FormView hideFormCreateButton. --- spp_base_common/__manifest__.py | 2 + .../static/src/js/custom_list_create.js | 25 ++++++ .../src/xml/custom_list_create_template.xml | 46 +++++++++++ .../static/src/js/create_change_request.js | 45 ++++++++--- .../xml/create_change_request_template.xml | 44 +---------- spp_programs/static/src/js/create_program.js | 78 +++++++++++++++---- .../src/xml/create_program_template.xml | 41 +--------- 7 files changed, 175 insertions(+), 106 deletions(-) create mode 100644 spp_base_common/static/src/js/custom_list_create.js create mode 100644 spp_base_common/static/src/xml/custom_list_create_template.xml 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' - - +