From 1eed93c1a8eed48cb885dd45efe5ec11d161eb1b Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 10 Jul 2026 14:22:53 +0800 Subject: [PATCH 1/2] feat(registry): per-action opt-out for registry admin-only CRUD restriction Honor bypass_registry_admin_only_crud in an action's context so that action's res.partner views are exempt from the registry restriction while other partner views keep it. The flag follows Odoo's standard context propagation (relational dialogs and button-opened actions from the exempt view inherit it), and exempt form views also disable useSendBeaconToSaveUrgently: authoring actions typically carry default_* keys that make a new record dirty before the user types, so the blur-triggered beacon save would raise validation errors on every tab refocus. Downstreams that need a dedicated non-admin role to author one registry list can set the flag on that action instead of counter-patching the controllers. The flag is a UI affordance, not a security boundary: server-side ACLs and record rules remain the real enforcement, as documented in the docstring and DESCRIPTION.md. --- spp_starter_sp_mis/__manifest__.py | 2 +- spp_starter_sp_mis/readme/DESCRIPTION.md | 9 +++++ spp_starter_sp_mis/readme/HISTORY.md | 8 ++++ .../static/src/js/registry_restriction.js | 40 +++++++++++++++++-- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/spp_starter_sp_mis/__manifest__.py b/spp_starter_sp_mis/__manifest__.py index 254a020a..d1545828 100644 --- a/spp_starter_sp_mis/__manifest__.py +++ b/spp_starter_sp_mis/__manifest__.py @@ -4,7 +4,7 @@ "name": "OpenSPP Starter: SP-MIS", "summary": "Complete SP-MIS bundle with Social Registry, Programs, and Service Points", "category": "OpenSPP", - "version": "19.0.2.0.0", + "version": "19.0.2.1.0", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_starter_sp_mis/readme/DESCRIPTION.md b/spp_starter_sp_mis/readme/DESCRIPTION.md index 60fba3cb..773ee86f 100644 --- a/spp_starter_sp_mis/readme/DESCRIPTION.md +++ b/spp_starter_sp_mis/readme/DESCRIPTION.md @@ -39,6 +39,15 @@ The registry restriction uses: - **JavaScript Patches**: Modifies `FormController` and `ListController` for `res.partner` model - **Admin Check**: Users in `spp_security.group_spp_admin` bypass all restrictions - **MutationObserver**: Monitors DOM changes to re-apply restrictions dynamically +- **Per-Action Opt-Out**: An action can exempt its own views by setting + `bypass_registry_admin_only_crud: True` in its context (e.g. an action that puts a + dedicated non-admin role in charge of authoring one registry list). Like the + restriction itself this is client-side only, not a security boundary — server-side + ACLs and record rules remain the real enforcement. The flag follows Odoo's normal + context propagation, so relational dialogs and actions opened via buttons from + inside the exempt view inherit it; exempt form views also disable the blur-triggered + urgent (beacon) save so partially-filled new records do not raise validation errors + on tab switch ### Included Modules diff --git a/spp_starter_sp_mis/readme/HISTORY.md b/spp_starter_sp_mis/readme/HISTORY.md index 4aaf9afe..6e9e93a1 100644 --- a/spp_starter_sp_mis/readme/HISTORY.md +++ b/spp_starter_sp_mis/readme/HISTORY.md @@ -1,3 +1,11 @@ +### 19.0.2.1.0 + +- feat(ui): honor `bypass_registry_admin_only_crud` in an action's context to exempt + that action's views (including relational dialogs opened from them) from the + registry admin-only CRUD restriction; exempt form views also disable the + blur-triggered urgent (beacon) save so partially-filled new records do not raise + validation errors on tab switch + ### 19.0.2.0.0 - Initial migration to OpenSPP2 diff --git a/spp_starter_sp_mis/static/src/js/registry_restriction.js b/spp_starter_sp_mis/static/src/js/registry_restriction.js index 9f41fe02..7459699a 100644 --- a/spp_starter_sp_mis/static/src/js/registry_restriction.js +++ b/spp_starter_sp_mis/static/src/js/registry_restriction.js @@ -8,6 +8,24 @@ * * The restriction check is cached and resolved before the form/list setup * so that modelParams can return mode: "readonly" before the model is created. + * + * Per-action opt-out: an action can exempt its own view from the restriction + * by setting ``bypass_registry_admin_only_crud: True`` in its context (e.g. + * an action that puts a dedicated non-admin role in charge of authoring one + * registry list). + * + * Opt-out scope and caveats: + * - The flag is a UI affordance, NOT a security boundary. Like the + * restriction itself, it changes nothing server-side: ACLs and record + * rules remain the real enforcement, and any user who can write + * ``ir.actions.act_window`` records can set the flag. + * - The flag follows Odoo's normal context propagation: relational dialogs + * (many2one create/edit and search-more, x2many record dialogs), expanded + * dialogs, and actions opened via buttons from inside the exempt view all + * inherit it. Scope bypass actions deliberately. + * - Exempt form views also disable the blur-triggered urgent (beacon) save, + * because authoring actions typically carry ``default_*`` context keys + * that make a new record dirty before the user types. */ import {FormController} from "@web/views/form/form_controller"; @@ -20,6 +38,12 @@ import {onMounted, onPatched, onWillStart, onWillUnmount} from "@odoo/owl"; // Models affected by the registry restriction const REGISTRY_MODELS = ["res.partner"]; +// An action opts its own view out of the restriction by setting this flag +// in its context. See the module docstring for scope and caveats. +function actionBypassesRestriction(controller) { + return Boolean(controller.props.context?.bypass_registry_admin_only_crud); +} + // Cache the restriction check to avoid repeated RPC calls within the same session. // Resolved once at module load time so it's available synchronously in setup(). let _restrictionResult = null; @@ -69,13 +93,16 @@ const BLOCKED_ACTIONS = ["delete", "archive", "unarchive", "duplicate"]; patch(FormController.prototype, { setup() { const modelName = this.props.resModel; + const bypassed = actionBypassesRestriction(this); // Check synchronous cache BEFORE super.setup() creates the model this._registryRestricted = - REGISTRY_MODELS.includes(modelName) && _restrictionResult === true; + REGISTRY_MODELS.includes(modelName) && + !bypassed && + _restrictionResult === true; super.setup(...arguments); - if (!REGISTRY_MODELS.includes(modelName)) { + if (!REGISTRY_MODELS.includes(modelName) || bypassed) { return; } @@ -156,6 +183,13 @@ patch(FormController.prototype, { if (this._registryRestricted) { params.config.mode = "readonly"; } + if (actionBypassesRestriction(this)) { + // Bypass actions typically set default_* keys that make a new + // record dirty before the user types; the blur-triggered urgent + // (beacon) save would then raise validation errors on every tab + // refocus. Let the user decide when to save. + params.useSendBeaconToSaveUrgently = false; + } return params; }, @@ -182,7 +216,7 @@ patch(ListController.prototype, { super.setup(...arguments); const modelName = this.props.resModel; - if (!REGISTRY_MODELS.includes(modelName)) { + if (!REGISTRY_MODELS.includes(modelName) || actionBypassesRestriction(this)) { return; } From de23a453bd2a265e23703cdc39e779e70115497d Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 10 Jul 2026 14:22:53 +0800 Subject: [PATCH 2/2] docs: regenerate README from fragments (CI-generated diff applied verbatim) --- spp_starter_sp_mis/README.rst | 20 ++++++++++++++++++ .../static/description/index.html | 21 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/spp_starter_sp_mis/README.rst b/spp_starter_sp_mis/README.rst index dd132e79..8652f014 100644 --- a/spp_starter_sp_mis/README.rst +++ b/spp_starter_sp_mis/README.rst @@ -91,6 +91,16 @@ The registry restriction uses: restrictions - **MutationObserver**: Monitors DOM changes to re-apply restrictions dynamically +- **Per-Action Opt-Out**: An action can exempt its own views by setting + ``bypass_registry_admin_only_crud: True`` in its context (e.g. an + action that puts a dedicated non-admin role in charge of authoring one + registry list). Like the restriction itself this is client-side only, + not a security boundary — server-side ACLs and record rules remain the + real enforcement. The flag follows Odoo's normal context propagation, + so relational dialogs and actions opened via buttons from inside the + exempt view inherit it; exempt form views also disable the + blur-triggered urgent (beacon) save so partially-filled new records do + not raise validation errors on tab switch Included Modules ~~~~~~~~~~~~~~~~ @@ -119,6 +129,16 @@ Dependencies Changelog ========= +19.0.2.1.0 +~~~~~~~~~~ + +- feat(ui): honor ``bypass_registry_admin_only_crud`` in an action's + context to exempt that action's views (including relational dialogs + opened from them) from the registry admin-only CRUD restriction; + exempt form views also disable the blur-triggered urgent (beacon) save + so partially-filled new records do not raise validation errors on tab + switch + 19.0.2.0.0 ~~~~~~~~~~ diff --git a/spp_starter_sp_mis/static/description/index.html b/spp_starter_sp_mis/static/description/index.html index 623eada2..41a1a7f1 100644 --- a/spp_starter_sp_mis/static/description/index.html +++ b/spp_starter_sp_mis/static/description/index.html @@ -448,6 +448,16 @@

Implementation Details

restrictions
  • MutationObserver: Monitors DOM changes to re-apply restrictions dynamically
  • +
  • Per-Action Opt-Out: An action can exempt its own views by setting +bypass_registry_admin_only_crud: True in its context (e.g. an +action that puts a dedicated non-admin role in charge of authoring one +registry list). Like the restriction itself this is client-side only, +not a security boundary — server-side ACLs and record rules remain the +real enforcement. The flag follows Odoo’s normal context propagation, +so relational dialogs and actions opened via buttons from inside the +exempt view inherit it; exempt form views also disable the +blur-triggered urgent (beacon) save so partially-filled new records do +not raise validation errors on tab switch
  • @@ -478,6 +488,17 @@

    Changelog

    +

    19.0.2.1.0

    +
      +
    • feat(ui): honor bypass_registry_admin_only_crud in an action’s +context to exempt that action’s views (including relational dialogs +opened from them) from the registry admin-only CRUD restriction; +exempt form views also disable the blur-triggered urgent (beacon) save +so partially-filled new records do not raise validation errors on tab +switch
    • +
    +
    +

    19.0.2.0.0

    • Initial migration to OpenSPP2