diff --git a/spp_api_v2/README.rst b/spp_api_v2/README.rst
index 6e15b9b92..26a40cefc 100644
--- a/spp_api_v2/README.rst
+++ b/spp_api_v2/README.rst
@@ -147,6 +147,13 @@ Dependencies
Changelog
=========
+19.0.2.1.1
+~~~~~~~~~~
+
+- chore(api_v2): the API V2 configuration menu moved from Registry >
+ Configuration to Settings > Registry, alongside the other superuser
+ configuration (#1009)
+
19.0.2.1.0
~~~~~~~~~~
diff --git a/spp_api_v2/__manifest__.py b/spp_api_v2/__manifest__.py
index b1d1caa1a..213b07c74 100644
--- a/spp_api_v2/__manifest__.py
+++ b/spp_api_v2/__manifest__.py
@@ -1,7 +1,7 @@
{
"name": "OpenSPP API V2",
"category": "OpenSPP/Integration",
- "version": "19.0.2.1.0",
+ "version": "19.0.2.1.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
diff --git a/spp_api_v2/readme/HISTORY.md b/spp_api_v2/readme/HISTORY.md
index b97cad75a..6fdfa4de3 100644
--- a/spp_api_v2/readme/HISTORY.md
+++ b/spp_api_v2/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.1.1
+
+- chore(api_v2): the API V2 configuration menu moved from Registry > Configuration to Settings > Registry, alongside the other superuser configuration (#1009)
+
### 19.0.2.1.0
- Add OpenAPI polymorphic schema utilities (`utils/openapi_polymorphic.py`): `polymorphic_body()` for declaring dict-typed fields that accept one of several Pydantic models, plus an app-level OpenAPI hook that injects the corresponding `oneOf` schemas into the generated document
diff --git a/spp_api_v2/security/compliance.yaml b/spp_api_v2/security/compliance.yaml
index fff4e0e36..e90abf127 100644
--- a/spp_api_v2/security/compliance.yaml
+++ b/spp_api_v2/security/compliance.yaml
@@ -162,10 +162,10 @@ record_rules: []
menus:
# Root menu - visible to all API V2 users
- # Located under Registry > Configuration
+ # Located under Settings > Registry (OP#1009)
- id: menu_api_v2_root
name: "API V2"
- parent: spp_registry.spp_configuration_menu_root
+ parent: spp_registry.menu_registry_settings_root
groups: [group_api_v2_viewer]
# API Clients submenu
diff --git a/spp_api_v2/static/description/index.html b/spp_api_v2/static/description/index.html
index 6980d735b..433b44e60 100644
--- a/spp_api_v2/static/description/index.html
+++ b/spp_api_v2/static/description/index.html
@@ -517,19 +517,28 @@
Dependencies
-
+
+
+- chore(api_v2): the API V2 configuration menu moved from Registry >
+Configuration to Settings > Registry, alongside the other superuser
+configuration (#1009)
+
+
+
+
- Add OpenAPI polymorphic schema utilities
(utils/openapi_polymorphic.py): polymorphic_body() for
@@ -554,8 +563,8 @@
polymorphic utilities, and the overall OpenAPI document contract
-
-
+
+
- Fix SerializationFailure race when multiple Odoo workers rebuild
their routing map simultaneously (e.g. after -u all) and all try
@@ -570,15 +579,15 @@
regressions are diagnosable without raising the global log level
-
-
+
+
- Initial migration to OpenSPP2
-
+
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
@@ -586,7 +595,7 @@
Do not contact contributors directly about support or help with technical issues.
diff --git a/spp_api_v2/views/menu.xml b/spp_api_v2/views/menu.xml
index 5d998d23b..34f619f40 100644
--- a/spp_api_v2/views/menu.xml
+++ b/spp_api_v2/views/menu.xml
@@ -1,10 +1,10 @@
-
+
diff --git a/spp_farmer_registry/README.rst b/spp_farmer_registry/README.rst
index 56b61cb7c..62768bd9b 100644
--- a/spp_farmer_registry/README.rst
+++ b/spp_farmer_registry/README.rst
@@ -68,6 +68,14 @@ Model Description
Changelog
=========
+19.0.2.0.4
+~~~~~~~~~~
+
+- chore(farmer_registry): the duplicate Farmer Registry Settings section
+ is removed — the toggle it carried now lives in Registry Settings, and
+ this module's storage key is written in step with it, so enforcement
+ is unchanged (#1009)
+
19.0.2.0.3
~~~~~~~~~~
diff --git a/spp_farmer_registry/__manifest__.py b/spp_farmer_registry/__manifest__.py
index 92143e178..9374f495a 100644
--- a/spp_farmer_registry/__manifest__.py
+++ b/spp_farmer_registry/__manifest__.py
@@ -3,7 +3,7 @@
"name": "OpenSPP Farmer Registry",
"summary": "Farmer Registry with vocabulary-based fields, CEL variables, and Logic Studio integration",
"category": "OpenSPP",
- "version": "19.0.2.0.3",
+ "version": "19.0.2.0.4",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -43,7 +43,6 @@
"data/cel_constants.xml",
"data/config_parameters.xml",
"data/user_roles.xml",
- "views/res_config_settings_views.xml",
"views/farm_season_views.xml",
"views/farm_details_views.xml",
"views/farm_activity_views.xml",
diff --git a/spp_farmer_registry/models/__init__.py b/spp_farmer_registry/models/__init__.py
index a93481592..9db98934c 100644
--- a/spp_farmer_registry/models/__init__.py
+++ b/spp_farmer_registry/models/__init__.py
@@ -5,4 +5,3 @@
from . import farm_activity
from . import farm_asset
from . import farm
-from . import res_config_settings
diff --git a/spp_farmer_registry/models/res_config_settings.py b/spp_farmer_registry/models/res_config_settings.py
deleted file mode 100644
index 55eda5d74..000000000
--- a/spp_farmer_registry/models/res_config_settings.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
-
-from odoo import fields, models
-
-
-class ResConfigSettings(models.TransientModel):
- _inherit = "res.config.settings"
-
- is_registry_admin_only_crud = fields.Boolean(
- "Restrict Registry Create/Edit/Delete to Admin Only",
- help=(
- "Only administrators can add, modify, or remove registrants. "
- "Other users can still view all registry data but cannot make changes."
- ),
- default=True,
- config_parameter="spp_farmer_registry.registry_admin_only_crud",
- )
diff --git a/spp_farmer_registry/readme/HISTORY.md b/spp_farmer_registry/readme/HISTORY.md
index bedd1a055..bb6858ef1 100644
--- a/spp_farmer_registry/readme/HISTORY.md
+++ b/spp_farmer_registry/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.0.4
+
+- chore(farmer_registry): the duplicate Farmer Registry Settings section is removed — the toggle it carried now lives in Registry Settings, and this module's storage key is written in step with it, so enforcement is unchanged (#1009)
+
### 19.0.2.0.3
- fix(farm): remove the farm membership-completeness warnings entirely — both the "No head member designated" and "No members linked to this group yet" banners, their "No Head Member" / "No Members" search filters, the `member_count` and head-member list columns, and the backing `has_head_member` / `member_count` computed fields. The head-member check misfired on farm groups that did have a head, and per #1113 no such warnings should be shown (#1113)
diff --git a/spp_farmer_registry/static/description/index.html b/spp_farmer_registry/static/description/index.html
index 6d2852191..17174d3a6 100644
--- a/spp_farmer_registry/static/description/index.html
+++ b/spp_farmer_registry/static/description/index.html
@@ -436,6 +436,15 @@
+
19.0.2.0.4
+
+- chore(farmer_registry): the duplicate Farmer Registry Settings section
+is removed — the toggle it carried now lives in Registry Settings, and
+this module’s storage key is written in step with it, so enforcement
+is unchanged (#1009)
+
+
+
19.0.2.0.3
- fix(farm): remove the farm membership-completeness warnings entirely —
@@ -447,7 +456,7 @@
19.0.2.0.3
per #1113 no such warnings should be shown (#1113)
-
+
19.0.2.0.2
- fix(security): align Farm User / Farm Manager roles with the OP#951
@@ -458,7 +467,7 @@
19.0.2.0.2
spp_hazard and spp_gis_report to module dependencies.
-
+
19.0.2.0.1
- fix(views): apply spp_registry.x2many_no_padding widget to the
@@ -466,7 +475,7 @@
19.0.2.0.1
placeholder rows Odoo 19 inserts on inline list-in-form views (#943).
-
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_farmer_registry/views/res_config_settings_views.xml b/spp_farmer_registry/views/res_config_settings_views.xml
deleted file mode 100644
index ceb765c21..000000000
--- a/spp_farmer_registry/views/res_config_settings_views.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- res.config.settings.view.form.inherit.farmer_registry
- res.config.settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/spp_import_match/README.rst b/spp_import_match/README.rst
index 611fee984..f03c11050 100644
--- a/spp_import_match/README.rst
+++ b/spp_import_match/README.rst
@@ -393,6 +393,13 @@ Test 12: Security — Non-Admin Access
Changelog
=========
+19.0.2.0.3
+~~~~~~~~~~
+
+- chore(import_match): the Import Match menu moved from Registry >
+ Configuration to Settings > Registry, alongside the other superuser
+ configuration (#1009)
+
19.0.2.0.2
~~~~~~~~~~
diff --git a/spp_import_match/__manifest__.py b/spp_import_match/__manifest__.py
index 26fcef35b..bc9a573c7 100644
--- a/spp_import_match/__manifest__.py
+++ b/spp_import_match/__manifest__.py
@@ -5,7 +5,7 @@
"name": "OpenSPP Import Match",
"summary": "OpenSPP Import Match enhances data import processes by intelligently matching incoming records against existing data, preventing duplication and ensuring registry integrity. It provides configurable matching logic and supports seamless updates to existing records during bulk data onboarding.",
"category": "OpenSPP/Integration",
- "version": "19.0.2.0.2",
+ "version": "19.0.2.0.3",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
diff --git a/spp_import_match/readme/HISTORY.md b/spp_import_match/readme/HISTORY.md
index 01d95e792..67e956589 100644
--- a/spp_import_match/readme/HISTORY.md
+++ b/spp_import_match/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.0.3
+
+- chore(import_match): the Import Match menu moved from Registry > Configuration to Settings > Registry, alongside the other superuser configuration (#1009)
+
### 19.0.2.0.2
- chore(views): hide the conditional-gate columns (`Is Conditional`, `Condition Field`, `Condition Value`) from the match-rule fields list — the schema and matching-engine wiring stay in place, but no current import flow uses the gate, so the columns are kept out of the UI until a real use case lands.
diff --git a/spp_import_match/static/description/index.html b/spp_import_match/static/description/index.html
index dff790529..a6172f2aa 100644
--- a/spp_import_match/static/description/index.html
+++ b/spp_import_match/static/description/index.html
@@ -804,6 +804,14 @@
+
19.0.2.0.3
+
+- chore(import_match): the Import Match menu moved from Registry >
+Configuration to Settings > Registry, alongside the other superuser
+configuration (#1009)
+
+
+
19.0.2.0.2
- chore(views): hide the conditional-gate columns (Is Conditional,
@@ -813,7 +821,7 @@
19.0.2.0.2
UI until a real use case lands.
-
+
19.0.2.0.1
- fix(matching): add a condition_field_id Many2one column to
@@ -825,7 +833,7 @@
19.0.2.0.1
zero matches.
-
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_import_match/views/import_match_view.xml b/spp_import_match/views/import_match_view.xml
index 32488a9ce..87c181641 100644
--- a/spp_import_match/views/import_match_view.xml
+++ b/spp_import_match/views/import_match_view.xml
@@ -138,7 +138,7 @@
id="menu_spp_import_match"
name="Import Match"
action="action_spp_import_match"
- parent="spp_registry.spp_configuration_menu_root"
+ parent="spp_registry.menu_registry_settings_root"
sequence="1000"
groups="spp_security.group_spp_admin"
/>
diff --git a/spp_registry/README.rst b/spp_registry/README.rst
index a9369ecd9..fed1aade5 100644
--- a/spp_registry/README.rst
+++ b/spp_registry/README.rst
@@ -139,6 +139,16 @@ Dependencies
Changelog
=========
+19.0.2.2.1
+~~~~~~~~~~
+
+- feat(registry): registry configuration is consolidated into one
+ **Registry Settings** section in the Settings app, with the Restrict
+ Registry Edits toggle and the relocated superuser configuration menus
+ (API V2, Import Match). Changing the toggle needs a Settings
+ administrator; the section's menu is gated to match, since the
+ framework refuses a settings save from anyone else (#1009)
+
19.0.2.1.4
~~~~~~~~~~
diff --git a/spp_registry/__manifest__.py b/spp_registry/__manifest__.py
index 4b2a0948d..2687f0c93 100644
--- a/spp_registry/__manifest__.py
+++ b/spp_registry/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "OpenSPP Registry",
"category": "OpenSPP/Core",
- "version": "19.0.2.1.4",
+ "version": "19.0.2.2.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -41,6 +41,10 @@
# Wizards
"wizard/disable_registrant_view.xml",
# Views - Base
+ # res_config_settings_views.xml defines menu_registry_settings_root, which
+ # main_view.xml reparents the Configuration menu under (OP#1009), so it
+ # must load first.
+ "views/res_config_settings_views.xml",
"views/main_view.xml",
"views/reg_relationship_view.xml",
"views/reg_id_view.xml",
diff --git a/spp_registry/models/__init__.py b/spp_registry/models/__init__.py
index ca222d231..46184ab09 100644
--- a/spp_registry/models/__init__.py
+++ b/spp_registry/models/__init__.py
@@ -9,4 +9,5 @@
from . import reg_relationship
from . import registrant
from . import registry_config
+from . import res_config_settings
from . import res_users
diff --git a/spp_registry/models/res_config_settings.py b/spp_registry/models/res_config_settings.py
new file mode 100644
index 000000000..71d8e96a5
--- /dev/null
+++ b/spp_registry/models/res_config_settings.py
@@ -0,0 +1,51 @@
+# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+# Legacy config-parameter keys this toggle keeps in sync. The starter modules'
+# controllers still read their own key (spp_farmer_registry reads the first,
+# spp_starter_sp_mis reads the second), so the central "Registry Settings"
+# toggle writes both — no migration or controller change needed, and neither
+# deployment's enforcement breaks.
+_LEGACY_KEYS = (
+ "spp_farmer_registry.registry_admin_only_crud",
+ "spp_starter.registry_admin_only_crud",
+)
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+
+ is_registry_admin_only_crud = fields.Boolean(
+ "Restrict Registry Create/Edit/Delete to Admin Only",
+ default=True,
+ help=(
+ "Only administrators can add, modify, or remove registrants. "
+ "Other users can still view all registry data but cannot make changes."
+ ),
+ )
+
+ def get_values(self):
+ res = super().get_values()
+ # ir.config_parameter is a global system setting; sudo is the standard
+ # access pattern for reading it.
+ icp = self.env["ir.config_parameter"].sudo() # nosemgrep: odoo-sudo-without-context
+ # A deployment ships only one of the two starter controllers, so at most
+ # one key is set. Reflect an explicit value if present; otherwise fall
+ # back to the secure default (True), matching the legacy
+ # config_parameter default the starters used.
+ explicit = [v for v in (icp.get_param(key) for key in _LEGACY_KEYS) if v is not False]
+ res["is_registry_admin_only_crud"] = any(v == "True" for v in explicit) if explicit else True
+ return res
+
+ def set_values(self):
+ res = super().set_values()
+ # ir.config_parameter is a global system setting; sudo is the standard
+ # access pattern for writing it.
+ icp = self.env["ir.config_parameter"].sudo() # nosemgrep: odoo-sudo-without-context
+ value = "True" if self.is_registry_admin_only_crud else "False"
+ # Keep both legacy keys in sync so whichever starter controller is
+ # installed reads the value the operator set here.
+ for key in _LEGACY_KEYS:
+ icp.set_param(key, value)
+ return res
diff --git a/spp_registry/readme/HISTORY.md b/spp_registry/readme/HISTORY.md
index 3eb561570..1913a09c8 100644
--- a/spp_registry/readme/HISTORY.md
+++ b/spp_registry/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.2.1
+
+- feat(registry): registry configuration is consolidated into one **Registry Settings** section in the Settings app, with the Restrict Registry Edits toggle and the relocated superuser configuration menus (API V2, Import Match). Changing the toggle needs a Settings administrator; the section's menu is gated to match, since the framework refuses a settings save from anyone else (#1009)
+
### 19.0.2.1.4
- fix(registry): remove the dead `@api.constrains("age")` `_check_age_is_integer` guard. `age` is a non-stored compute derived from `birthdate`, so the constraint never fired and only emitted the registry-load warning `@constrains parameter 'age' is not writeable`. Computed `age` values are unchanged; stale i18n entries for the removed message are dropped
diff --git a/spp_registry/static/description/index.html b/spp_registry/static/description/index.html
index 0e6786338..e232ebded 100644
--- a/spp_registry/static/description/index.html
+++ b/spp_registry/static/description/index.html
@@ -518,6 +518,17 @@
+
19.0.2.2.1
+
+- feat(registry): registry configuration is consolidated into one
+Registry Settings section in the Settings app, with the Restrict
+Registry Edits toggle and the relocated superuser configuration menus
+(API V2, Import Match). Changing the toggle needs a Settings
+administrator; the section’s menu is gated to match, since the
+framework refuses a settings save from anyone else (#1009)
+
+
+
19.0.2.1.4
- fix(registry): remove the dead @api.constrains("age")
@@ -529,7 +540,7 @@
19.0.2.1.4
dropped
-
+
19.0.2.1.3
- fix(registry): show an ID Status column on the group form
@@ -540,7 +551,7 @@
19.0.2.1.3
(#1110)
-
+
19.0.2.1.1
- fix(views): add reusable x2many_no_padding JS widget that
@@ -550,7 +561,7 @@
19.0.2.1.1
don’t bloat the layout (#943).
-
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_registry/tests/__init__.py b/spp_registry/tests/__init__.py
index c76f4067f..2177f1fd9 100644
--- a/spp_registry/tests/__init__.py
+++ b/spp_registry/tests/__init__.py
@@ -12,3 +12,4 @@
from . import test_membership_constraints
from . import test_registrant_misc
from . import test_group_aggregation
+from . import test_res_config_settings
diff --git a/spp_registry/tests/test_res_config_settings.py b/spp_registry/tests/test_res_config_settings.py
new file mode 100644
index 000000000..24bc03ae7
--- /dev/null
+++ b/spp_registry/tests/test_res_config_settings.py
@@ -0,0 +1,125 @@
+# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
+"""Tests for the central Registry Settings (OP#1009).
+
+The "Restrict Registry Edits to Admin Only" toggle lives in spp_registry and
+keeps both legacy config-parameter keys in sync so whichever starter
+controller is installed reads the operator's choice.
+"""
+
+from lxml import etree
+
+from odoo.exceptions import AccessError
+from odoo.tests import TransactionCase, tagged
+
+FARMER_KEY = "spp_farmer_registry.registry_admin_only_crud"
+SPMIS_KEY = "spp_starter.registry_admin_only_crud"
+
+
+@tagged("post_install", "-at_install")
+class TestRegistryResConfigSettings(TransactionCase):
+ """Registry admin-only-CRUD toggle: default + dual-key sync."""
+
+ def _icp(self):
+ return self.env["ir.config_parameter"].sudo()
+
+ def _clear_keys(self):
+ self._icp().search([("key", "in", [FARMER_KEY, SPMIS_KEY])]).unlink()
+
+ def test_defaults_true_when_unset(self):
+ """With neither legacy key set, the toggle defaults to True (secure)."""
+ self._clear_keys()
+ settings = self.env["res.config.settings"].create({})
+ self.assertTrue(settings.is_registry_admin_only_crud)
+
+ def test_set_values_writes_both_keys(self):
+ """Saving the toggle writes BOTH legacy keys so either controller reads it."""
+ settings = self.env["res.config.settings"].create({"is_registry_admin_only_crud": True})
+ settings.execute()
+ self.assertEqual(self._icp().get_param(FARMER_KEY), "True")
+ self.assertEqual(self._icp().get_param(SPMIS_KEY), "True")
+
+ settings = self.env["res.config.settings"].create({"is_registry_admin_only_crud": False})
+ settings.execute()
+ self.assertEqual(self._icp().get_param(FARMER_KEY), "False")
+ self.assertEqual(self._icp().get_param(SPMIS_KEY), "False")
+
+ def test_get_values_reflects_an_explicit_key(self):
+ """An explicit value on either legacy key is reflected in the toggle."""
+ self._clear_keys()
+ self._icp().set_param(SPMIS_KEY, "False")
+ settings = self.env["res.config.settings"].create({})
+ self.assertFalse(settings.is_registry_admin_only_crud)
+
+ self._clear_keys()
+ self._icp().set_param(FARMER_KEY, "True")
+ settings = self.env["res.config.settings"].create({})
+ self.assertTrue(settings.is_registry_admin_only_crud)
+
+ # ------------------------------------------------------------------
+ # who can actually change it (OP#1009 review)
+ # ------------------------------------------------------------------
+
+ def _user(self, login, *group_xmlids):
+ return (
+ self.env["res.users"]
+ .with_context(no_reset_password=True)
+ .create(
+ {
+ "name": login,
+ "login": login,
+ "email": f"{login}@example.test",
+ "group_ids": [
+ (6, 0, [self.env.ref("base.group_user").id] + [self.env.ref(g).id for g in group_xmlids])
+ ],
+ }
+ )
+ )
+
+ def test_saving_requires_a_settings_administrator(self):
+ """res.config.settings.execute() refuses anyone who is not an Odoo admin.
+
+ Two gates, and the earlier one is the harder: res.config.settings' ACL
+ grants create only to base.group_system, so these personas cannot even
+ open the form — and execute() would refuse them afterwards anyway,
+ since is_admin means superuser or base.group_erp_manager. Neither group
+ has either, and granting one would be a privilege escalation, so the
+ menu is gated to match and this pins the behaviour it reflects.
+ """
+ for groups in (
+ ("spp_security.group_spp_admin",),
+ ("spp_registry.group_registry_config_admin",),
+ ):
+ with self.subTest(groups=groups):
+ user = self._user("cfg_" + groups[0].split(".")[-1][:20], *groups)
+
+ with self.assertRaises(AccessError):
+ self.env["res.config.settings"].with_user(user).create({}).execute()
+
+ def test_a_settings_administrator_can_save(self):
+ admin = self._user("cfg_settings_admin", "base.group_system")
+ settings = self.env["res.config.settings"].with_user(admin).create({"is_registry_admin_only_crud": False})
+
+ settings.execute()
+
+ self.assertEqual(self._icp().get_param(FARMER_KEY), "False")
+ self.assertEqual(self._icp().get_param(SPMIS_KEY), "False")
+
+ def test_the_general_settings_menu_is_gated_on_who_can_save(self):
+ """Offering the menu more widely means a form that throws on Save."""
+ menu = self.env.ref("spp_registry.menu_registry_settings_general")
+
+ self.assertIn(self.env.ref("base.group_system"), menu.group_ids)
+
+ def test_the_relocated_configuration_menus_stay_available(self):
+ """Those are ordinary actions with their own gates — they do work."""
+ root = self.env.ref("spp_registry.menu_registry_settings_root")
+ groups = root.group_ids
+
+ self.assertIn(self.env.ref("spp_security.group_spp_admin"), groups)
+ self.assertIn(self.env.ref("spp_registry.group_registry_config_admin"), groups)
+
+ def test_the_settings_help_says_who_can_change_it(self):
+ arch = etree.fromstring(self.env.ref("spp_registry.res_config_settings_registry_view_form").arch)
+ setting = arch.xpath("//setting[contains(@string, 'Restrict Registry Edits')]")[0]
+
+ self.assertIn("Settings administrator", setting.get("help") or "")
diff --git a/spp_registry/views/main_view.xml b/spp_registry/views/main_view.xml
index 7f84415d3..e8b46863a 100644
--- a/spp_registry/views/main_view.xml
+++ b/spp_registry/views/main_view.xml
@@ -12,10 +12,17 @@
groups="spp_security.group_spp_admin,spp_registry.group_registry_manager,spp_registry.group_registry_officer,spp_registry.group_registry_viewer"
/>
+
diff --git a/spp_registry/views/res_config_settings_views.xml b/spp_registry/views/res_config_settings_views.xml
new file mode 100644
index 000000000..d451da601
--- /dev/null
+++ b/spp_registry/views/res_config_settings_views.xml
@@ -0,0 +1,94 @@
+
+
+
+
+ res.config.settings.view.form.inherit.registry
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spp_starter_sp_mis/README.rst b/spp_starter_sp_mis/README.rst
index f17a89b0f..769403150 100644
--- a/spp_starter_sp_mis/README.rst
+++ b/spp_starter_sp_mis/README.rst
@@ -47,19 +47,25 @@ Key Models
This module defines no models. It extends:
-+-------------------------+--------------------------------------------+
-| Model | Extension |
-+=========================+============================================+
-| ``res.config.settings`` | Adds ``is_registry_admin_only_crud`` |
-| | boolean field |
-+-------------------------+--------------------------------------------+
++-----------------+----------------------------------------------------+
+| Model | Extension |
++=================+====================================================+
+| ``res.partner`` | Withholds create, write and delete on registrant |
+| | records from non-admin users |
++-----------------+----------------------------------------------------+
+
+The restriction is switched on and off from **Settings → Registry →
+General Settings**, which is where registry configuration now lives
+(OP#1009). This module keeps its own storage key, so its enforcement is
+unchanged.
Configuration
~~~~~~~~~~~~~
After installing:
-1. Navigate to **Settings > SP-MIS Settings**
+1. Navigate to **Settings > Registry > General Settings** (the SP-MIS
+ section was folded into it — OP#1009)
2. Enable **Restrict Registry Edits to Admin Only** to enforce read-only
registry access for non-admin users
3. When enabled, non-admin users can still read the registry, but
@@ -71,8 +77,8 @@ After installing:
UI Location
~~~~~~~~~~~
-- **Settings**: Settings > SP-MIS Settings (registry access control
- toggle)
+- **Settings**: Settings > Registry > General Settings (registry access
+ control toggle; changing it requires a Settings administrator)
- **Programs**: Inherited from ``spp_programs`` (Social Protection >
Programs)
- **Service Points**: Inherited from ``spp_service_points`` via
@@ -123,6 +129,14 @@ Dependencies
Changelog
=========
+19.0.2.1.1
+~~~~~~~~~~
+
+- chore(starter_sp_mis): the duplicate SP-MIS Settings section is
+ removed — the toggle it carried now lives in Registry Settings, and
+ this module's storage key is written in step with it, so enforcement
+ is unchanged (#1009)
+
19.0.2.1.0
~~~~~~~~~~
diff --git a/spp_starter_sp_mis/__manifest__.py b/spp_starter_sp_mis/__manifest__.py
index 3bc4ee1b7..304166d4d 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.1.0",
+ "version": "19.0.2.1.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -22,7 +22,6 @@
],
"data": [
"data/config_parameters.xml",
- "views/res_config_settings_views.xml",
],
"demo": [],
"images": [],
diff --git a/spp_starter_sp_mis/models/res_config_settings.py b/spp_starter_sp_mis/models/res_config_settings.py
index 86937f489..7ca705cd8 100644
--- a/spp_starter_sp_mis/models/res_config_settings.py
+++ b/spp_starter_sp_mis/models/res_config_settings.py
@@ -1,45 +1,10 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models
-
-# Storage key for the registry access-control setting. Imported by res_partner
-# so the toggle and its enforcement can never drift apart.
+# Storage key for the registry access-control setting.
+#
+# The toggle itself moved to the consolidated Registry Settings section in
+# spp_registry (OP#1009), which is why the field and its set_values override are
+# gone from here. The key stays: res_partner imports it for enforcement, and the
+# central toggle writes this key and spp_farmer_registry's in step, so this
+# module's controller reads exactly what it always did (OP#1009 review).
REGISTRY_ADMIN_ONLY_CRUD_PARAM = "spp_starter.registry_admin_only_crud"
-
-
-class ResConfigSettings(models.TransientModel):
- _inherit = "res.config.settings"
-
- is_registry_admin_only_crud = fields.Boolean(
- "Restrict Registry Create/Edit/Delete to Admin Only",
- help=(
- "Only administrators can add, modify, or remove registrants. "
- "Other users can still view all registry data but cannot make changes."
- ),
- config_parameter=REGISTRY_ADMIN_ONLY_CRUD_PARAM,
- )
-
- def set_values(self):
- """Persist the toggle explicitly, including when it is off (OP#1142).
-
- Odoo stores a False ``config_parameter`` by *deleting* the row, and
- ``default_get`` falls back to the field's ``default`` when the row is
- missing. Pairing that with ``default=True`` made "off" unrepresentable:
- the toggle sprang back on at every reload, while enforcement — which
- reads a missing row as False — quietly went unrestricted, so the form
- claimed the registry was locked when it was open.
-
- Writing the value as a string keeps "off" a stored fact rather than an
- absence, which is what makes the two sides agree. The install default
- now comes from ``data/config_parameters.xml`` instead of a field
- default, so a missing row can no longer mean "on".
- """
- super().set_values()
- # Writing a system configuration parameter. ir.config_parameter is
- # restricted to Settings managers, and the settings form is already
- # gated on that group, so this widens nothing.
- # nosemgrep: odoo-sudo-without-context
- self.env["ir.config_parameter"].sudo().set_param(
- REGISTRY_ADMIN_ONLY_CRUD_PARAM,
- "True" if self.is_registry_admin_only_crud else "False",
- )
diff --git a/spp_starter_sp_mis/readme/DESCRIPTION.md b/spp_starter_sp_mis/readme/DESCRIPTION.md
index a0ab4c171..0248a3de0 100644
--- a/spp_starter_sp_mis/readme/DESCRIPTION.md
+++ b/spp_starter_sp_mis/readme/DESCRIPTION.md
@@ -11,22 +11,24 @@ Starter bundle for Social Protection Management Information System (SP-MIS) depl
This module defines no models. It extends:
-| Model | Extension |
-| ---------------------- | ----------------------------------------------- |
-| `res.config.settings` | Adds `is_registry_admin_only_crud` boolean field |
+| Model | Extension |
+| ------------- | ---------------------------------------------------------------------------- |
+| `res.partner` | Withholds create, write and delete on registrant records from non-admin users |
+
+The restriction is switched on and off from **Settings → Registry → General Settings**, which is where registry configuration now lives (OP#1009). This module keeps its own storage key, so its enforcement is unchanged.
### Configuration
After installing:
-1. Navigate to **Settings > SP-MIS Settings**
+1. Navigate to **Settings > Registry > General Settings** (the SP-MIS section was folded into it — OP#1009)
2. Enable **Restrict Registry Edits to Admin Only** to enforce read-only registry access for non-admin users
3. When enabled, non-admin users can still read the registry, but creating, editing and deleting registrants is refused — and the corresponding buttons are not shown
4. Restriction applies only to `res.partner` views; program-related operations remain available based on role
### UI Location
-- **Settings**: Settings > SP-MIS Settings (registry access control toggle)
+- **Settings**: Settings > Registry > General Settings (registry access control toggle; changing it requires a Settings administrator)
- **Programs**: Inherited from `spp_programs` (Social Protection > Programs)
- **Service Points**: Inherited from `spp_service_points` via transitive dependency
diff --git a/spp_starter_sp_mis/readme/HISTORY.md b/spp_starter_sp_mis/readme/HISTORY.md
index 41131d098..9077804ae 100644
--- a/spp_starter_sp_mis/readme/HISTORY.md
+++ b/spp_starter_sp_mis/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.1.1
+
+- chore(starter_sp_mis): the duplicate SP-MIS Settings section is removed — the toggle it carried now lives in Registry Settings, and this module's storage key is written in step with it, so enforcement is unchanged (#1009)
+
### 19.0.2.1.0
- fix(starter_sp_mis): make the registry restriction hold and stop it re-locking itself. Enforcement moves from a JavaScript patch Odoo 19 no longer reads to the access check every create, write and delete passes through, so it applies over RPC and data import too; promoting a plain contact into the registry is refused as well. The setting is marked `noupdate`, with a migration for databases where an upgrade would otherwise keep switching it back on (#1142)
diff --git a/spp_starter_sp_mis/static/description/index.html b/spp_starter_sp_mis/static/description/index.html
index df0a16983..14de1359c 100644
--- a/spp_starter_sp_mis/static/description/index.html
+++ b/spp_starter_sp_mis/static/description/index.html
@@ -395,8 +395,8 @@ Key Models
This module defines no models. It extends:
-
-
+
+
| Model |
@@ -404,18 +404,23 @@ Key Models
-| res.config.settings |
-Adds is_registry_admin_only_crud
-boolean field |
+
| res.partner |
+Withholds create, write and delete on registrant
+records from non-admin users |
+The restriction is switched on and off from Settings → Registry →
+General Settings, which is where registry configuration now lives
+(OP#1009). This module keeps its own storage key, so its enforcement is
+unchanged.
Configuration
After installing:
-- Navigate to Settings > SP-MIS Settings
+- Navigate to Settings > Registry > General Settings (the SP-MIS
+section was folded into it — OP#1009)
- Enable Restrict Registry Edits to Admin Only to enforce read-only
registry access for non-admin users
- When enabled, non-admin users can still read the registry, but
@@ -428,8 +433,8 @@
Configuration
UI Location
-- Settings: Settings > SP-MIS Settings (registry access control
-toggle)
+- Settings: Settings > Registry > General Settings (registry access
+control toggle; changing it requires a Settings administrator)
- Programs: Inherited from spp_programs (Social Protection >
Programs)
- Service Points: Inherited from spp_service_points via
@@ -482,6 +487,15 @@
+
19.0.2.1.1
+
+- chore(starter_sp_mis): the duplicate SP-MIS Settings section is
+removed — the toggle it carried now lives in Registry Settings, and
+this module’s storage key is written in step with it, so enforcement
+is unchanged (#1009)
+
+
+
19.0.2.1.0
- fix(starter_sp_mis): make the registry restriction hold and stop it
@@ -493,7 +507,7 @@
19.0.2.1.0
would otherwise keep switching it back on (#1142)
-
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_starter_sp_mis/views/res_config_settings_views.xml b/spp_starter_sp_mis/views/res_config_settings_views.xml
deleted file mode 100644
index 330f4241b..000000000
--- a/spp_starter_sp_mis/views/res_config_settings_views.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
- res.config.settings.view.form.inherit.sp_mis
- res.config.settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- SP-MIS Settings
- ir.actions.act_window
- res.config.settings
-
- form
- current
- {'module': 'spp_starter_sp_mis'}
-
-