Skip to content

feat(disability_registry): stabilization improvements for spp_disability_registry (#1047)#263

Merged
emjay0921 merged 16 commits into
19.0from
feat/1047-disability-registry-improvements
Jul 2, 2026
Merged

feat(disability_registry): stabilization improvements for spp_disability_registry (#1047)#263
emjay0921 merged 16 commits into
19.0from
feat/1047-disability-registry-improvements

Conversation

@emjay0921

Copy link
Copy Markdown
Contributor

Why is this change needed?

Umbrella #1047 tracked the bugs and improvements needed to stabilise spp_disability_registry. This branch consolidates all of that work — every child ticket is now resolved (10 Closed + #1051 Test pass), so the module is ready for review as a single reviewable unit.

Tickets consolidated on this branch:

Ticket Summary
#1022 Approved disability record now recognised in the registry (+ questionnaire required)
#1048 Add CFM 2-4 questionnaire
#1049 Add CFM 5-17 questionnaire
#1050 Automate assessment type by age of registrant (with manual override)
#1051 Verify "Has disability" computation for WG-SS
#1052 Improve assistive device management
#1053 Proxy information / proxy response by assessment type (+ submit gates)
#1054 Move "Impairment Classification" to its own tab (multi-row)
#1060 Configurable approval workflow for disability assessment
#1061 New spp_starter_disability_registry bundle
#1068 Rearrange assessment tabs + overview impairment table + wording fixes

How was the change implemented?

  • spp_disability_registry (enhanced):
    • Assessments (models/assessment.py): age-driven assessment-type selection with manual override; CFM 2-4 and CFM 5-17 questionnaires; WG-SS "Has disability" computation; proxy response captured per assessment type; configurable multi-tier approval workflow; review/proxy submit gates.
    • Impairment classification on its own tab as a multi-row model (models/impairment.py), with an overview table.
    • Assistive device management improvements (models/assistive_device.py, data/vocabulary_device.xml).
    • Registry recognition: approved assessments are now reflected on the registrant (models/registrant.py).
    • Settings (models/res_config_settings.py + views): expose the approval-workflow and questionnaire configuration in the GUI.
    • New ACL rows in security/ir.model.access.csv; assessment/registrant view rework; menu additions.
  • spp_starter_disability_registry (new): a starter bundle module (manifest, config parameters, description, icon) that packages the disability registry for deployment.

New unit tests

  • spp_disability_registry/tests/test_assessment.py — expanded coverage for age-driven type selection, CFM questionnaires, WG-SS computation, proxy, and approval workflow.
  • spp_disability_registry/tests/test_cel_functions.py, tests/test_registrant.py — updated.
  • spp_starter_disability_registry/tests/test_starter_disability_registry.py — new bundle install test.

Unit tests executed by the author

CI runs the spp_disability_registry and spp_starter_disability_registry test jobs on this PR.

How to test manually

Install spp_disability_registry (or the spp_starter_disability_registry bundle), create a disability assessment, and verify: the assessment type is auto-selected by the registrant's age (overridable); the CFM 2-4 / 5-17 / WG-SS questionnaires appear as appropriate; the "Has disability" flag computes correctly for WG-SS; proxy response is captured per type; the configured approval workflow gates submission; impairment classification is a multi-row tab with an overview table; and an approved assessment is recognised on the registrant.

Related links

Parent: #1047 (Disability Registry - Bugs and improvements). Children: #1022, #1048, #1049, #1050, #1051, #1052, #1053, #1054, #1060, #1061, #1068.

emjay0921 added 15 commits June 23, 2026 11:39
- Show Devices smart button even at zero count
- Remove has_disability prerequisite to add/view assistive devices
- Make status editable inline (dropdown) with row-level color; surface
  provision date and provider when status is Provided
- Drop inaccurate DCI CD.DR.04 / ISO 9999 claims from device vocabulary
- Reword assistive-device readme bullet (status, not status workflow)
…rride (#1050)

- Add Disability Registry settings with an 'Allow manual assessment type'
  toggle (res.config.settings + Configuration > Settings menu)
- Default: assessment type is readonly and auto-determined from the
  registrant's age; a date of birth is required (validation)
- When the toggle is enabled: assessment type is a manual dropdown and a
  date of birth is no longer required
- Rename the 'WG Assessment' tab to 'WG/CFM Assessment' and show the WG-SS
  questionnaire only for the adult type; add a placeholder for the CFM
  forms (added by #1048 / #1049)
- Remove the standalone 'Response Information' tab and move proxy fields to
  the top of the WG/CFM Assessment tab
- Drive the proxy response flag by assessment type: forced on for CFM 2-4
  and (unless self-report is enabled) CFM 5-17; optional for WG-SS
- Add config: allow self-report on CFM 5-17 (+ optional minimum age) and
  allow proxy report on WG-SS (default on)
- Add a 'Reason for Proxy' dropdown shown for WG-SS proxy responses
- Manage the 'allow proxy on WG-SS' parameter explicitly so unticking the
  default-on setting persists (config_parameter set_param(False) deletes the
  param and the field default would re-tick it)
- Add the WG/UNICEF Child Functioning Module 2-4 instrument (16 questions,
  CF1-CF16) as fields on the assessment, with the difficulty and behaviour
  response scales and the glasses/hearing-aid/walking-equipment branching
- Compute has_disability per assessment type: CFM 2-4 uses the concluding
  answer per domain (standard threshold) plus the 'a lot more' behaviour
  threshold; WG-SS logic unchanged
- Show the CFM 2-4 form only for the cfm_2_4 type; placeholder remains for
  cfm_5_17 (#1049)
- Restyle both WG-SS and CFM 2-4 as carded questionnaires with horizontal
  radio options for clearer data entry
- Add the WG/UNICEF Child Functioning Module 5-17 instrument (24 questions,
  CF1-CF24) as fields on the assessment, with the difficulty scale, the
  frequency scale for anxiety/depression, and the glasses/hearing-aid/
  walking-equipment branching (100 m / 500 m distances)
- Score has_disability for CFM 5-17: standard threshold on concluding
  answers; mobility uses the with-aid answer (either distance) when
  equipment is used, otherwise the compared-with-peers answer; anxiety and
  depression use a 'daily' frequency threshold
- Show the CFM 5-17 carded questionnaire for the cfm_5_17 type, replacing
  the placeholder; same horizontal-radio layout as CFM 2-4
…multi-row (#1054)

- Add spp.disability.impairment line model (type + cause + severity per row)
  with ACLs; expose as impairment_line_ids on the assessment
- Move Impairment Classification to its own tab with an editable list;
  Overview shows a read-only Classification Summary
- Derive the assessment-level severity_level_id (most severe line) and
  impairment_type_ids (union) from the lines so the registrant propagation,
  list/badge decorations, filters, concept groups and CEL functions keep
  working unchanged; drop the per-assessment impairment_cause_id
- Update demo data to use impairment lines (adult now has two)
- Update tests to record severity via impairment lines; fix a stale test
  that called the _onchange_assessment_type removed in #1053
…#1060)

- Make the assessment use a configured approval definition: override
  _get_approval_definition / _resolve_approval_definition to return the
  workflow selected in Disability Registry settings (same pattern as
  program cycles/entitlements and change-request types)
- Add 'Assessment approval workflow' to Disability Registry settings, a
  reference to an spp.approval.definition for the assessment model (created
  in Approvals > Approval Definitions); no default is seeded
- Hide Submit and show a guidance alert until a workflow is configured
- Gate Approve/Reject by the computed can_approve/can_reject so the
  configured approvers drive the buttons instead of a fixed group
…estionnaire (#1022)

- Re-sync the registrant's disability status after approval/rejection/reset:
  the approval mixin writes approval_state via raw SQL (optimistic locking),
  which bypasses ORM dependency tracking, so the registrant's current
  assessment / has_disability never recomputed. Override _on_approve,
  _on_reject and _on_reset_to_draft to call modified(['approval_state'])
- Require the WG/CFM questionnaire to be complete enough to compute a result
  before submitting: add questionnaire_complete, gate _check_can_submit and
  hide Submit with a guidance alert until it is answered
- Correct the registrant message ('No approved assessment currently
  identifies a disability' instead of claiming none exists)
- Make the severity in the disability smart button display-only (no_open)
  so it no longer navigates to the vocabulary record
…0 r2)

- Block assessments for registrants under 2 and when no date of birth is set
  (age-enforced mode): hide Create/New Assessment with a guidance message via
  can_create_disability_assessment, and add a constraint blocking under-2
- Hide 'Age at Assessment' on the Overview when the registrant has no DOB
- Remove '(default)' wording from the manual-assessment-type setting help
- Exclude the registrant from the Proxy Respondent dropdown
- Hide the proxy checkbox for WG-SS when proxy reporting is disabled
  (instead of showing it greyed out)
- Require the minimum self-report age (5-17) when self-report on CFM 5-17
  is enabled; validate the range on save
- Remove '(default)'/'Disabled by default' wording from the proxy settings help
… r2)

- Render the Yes/No gate questions (glasses, hearing aid, walking equipment)
  as Yes/No selections instead of a single checkbox; branch on 'yes'
- Use the exact WG/UNICEF question wording from the ticket
- Split the questionnaire into one section per domain (Vision, Hearing,
  Mobility, Dexterity, Communication, Learning, Playing, Controlling
  Behaviour) matching the instrument
- Convert glasses/hearing aid/walking-equipment gates from a single
  checkbox to explicit Yes/No options
- Align all CFM 5-17 question labels with the exact instrument wording
- Split combined sections into one card per functional domain (Vision,
  Hearing, Mobility, Self-care, Communication, Learning, Remembering,
  Concentrating, Accepting change, Controlling behaviour, Making
  friends, Anxiety, Depression)
…(#1068)

#1068 — Rearrange assessment tab:
- Config (Disability Registry settings): per-tab Show/Required toggles for
  Impairment Classification / WG-CFM / Support Needs, plus Show Assistive
  Devices on Support Needs (all default on)
- Overview: remove Has Disability? and the Classification Summary section
- Impairment tab: yes/no gate ("any impairments or functional limitations to
  record?") — Yes shows the list + requires a row; No completes the tab
- Support Needs: assistive-device request list (Status "Needed", no provision
  date); on approval each becomes a needed device on the registrant
- Submit gate is now config-driven: every displayed + required tab must be
  complete (Support has no content gate)
- Registrant Disability Status card degrades gracefully for questionnaire-only
  disabilities (no impairment severity / review)

BM rewordings:
- WG-SS communicating: '...in their usual language?'
- CFM 2-4 and 5-17 'without his/her equipment or assistance' walking questions
  no longer offer 'No difficulty'
- Questionnaire editable in draft/revision, locked once submitted
- Proxy minimum self-report age (5-17) blank by default, cleared on disable
… (#1061)

Bundle module installing the Disability Registry stack in one step:
everything from spp_starter_social_registry except the DCI client modules
(spp_dci_client, _crvs, _ibr, _dr), plus spp_disability_registry. Sets
spp_starter.registry_type = disability_registry.
… submit gates (#1068 #1053)

#1068 (overview assessment table):
- Add a 'Disability according to WG/CFM' checkbox column (has_disability).
- Show each impairment type with its own severity on a separate line via a
  new impairment_severity_display field, replacing the impairment-type tags
  and the single overall-severity badge in the history table.
- Add a 'Review Schedule' configuration (Show / Required to submit, default
  on) following the existing assessment-tab toggle pattern; the section
  hides when off and review_category is gated at submit when required.

#1053 (proxy information):
- Add a 'Require proxy details on proxy responses' setting (default on).
  When on and a proxy responded, the respondent and relationship must be
  recorded before the assessment can be submitted for approval.

Both submit gates fold into assessment_complete (draft saves still allowed).
Tests: updated 2 gate tests for the now-required review schedule and added 3
new tests (review gate, proxy gate, per-line impairment display).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly enhances the OpenSPP Disability Registry by introducing the WG/UNICEF Child Functioning Module (CFM) for children, implementing a multi-line impairment classification model, and adding configurable assessment tabs with submission completeness gates. It also introduces a new starter bundle module for one-click installation of the Disability Registry stack. Feedback on these changes includes resolving a persistence issue where the disability_support_show_devices parameter is missing from _DEFAULT_TRUE_PARAMS in the configuration settings, and optimizing the _materialize_device_requests method to batch database queries instead of executing them inside a loop.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +111 to +122
_DEFAULT_TRUE_PARAMS = {
"disability_allow_proxy_wg_ss": "spp_disability_registry.allow_proxy_wg_ss",
"disability_require_proxy_details": "spp_disability_registry.require_proxy_details",
"disability_display_impairment": "spp_disability_registry.display_impairment",
"disability_display_wg": "spp_disability_registry.display_wg",
"disability_display_support": "spp_disability_registry.display_support",
"disability_display_review": "spp_disability_registry.display_review",
"disability_require_impairment": "spp_disability_registry.require_impairment",
"disability_require_wg": "spp_disability_registry.require_wg",
"disability_require_support": "spp_disability_registry.require_support",
"disability_require_review": "spp_disability_registry.require_review",
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The configuration parameter disability_support_show_devices is defined as a field on ResConfigSettings but is missing from _DEFAULT_TRUE_PARAMS. Because it does not have config_parameter set on the field definition, its value will not be persisted in the database and will reset to its default value every time the settings are loaded or saved. Adding it to _DEFAULT_TRUE_PARAMS ensures it is correctly saved and retrieved.

Suggested change
_DEFAULT_TRUE_PARAMS = {
"disability_allow_proxy_wg_ss": "spp_disability_registry.allow_proxy_wg_ss",
"disability_require_proxy_details": "spp_disability_registry.require_proxy_details",
"disability_display_impairment": "spp_disability_registry.display_impairment",
"disability_display_wg": "spp_disability_registry.display_wg",
"disability_display_support": "spp_disability_registry.display_support",
"disability_display_review": "spp_disability_registry.display_review",
"disability_require_impairment": "spp_disability_registry.require_impairment",
"disability_require_wg": "spp_disability_registry.require_wg",
"disability_require_support": "spp_disability_registry.require_support",
"disability_require_review": "spp_disability_registry.require_review",
}
_DEFAULT_TRUE_PARAMS = {
"disability_allow_proxy_wg_ss": "spp_disability_registry.allow_proxy_wg_ss",
"disability_require_proxy_details": "spp_disability_registry.require_proxy_details",
"disability_display_impairment": "spp_disability_registry.display_impairment",
"disability_display_wg": "spp_disability_registry.display_wg",
"disability_display_support": "spp_disability_registry.display_support",
"disability_display_review": "spp_disability_registry.display_review",
"disability_require_impairment": "spp_disability_registry.require_impairment",
"disability_require_wg": "spp_disability_registry.require_wg",
"disability_require_support": "spp_disability_registry.require_support",
"disability_require_review": "spp_disability_registry.require_review",
"disability_support_show_devices": "spp_disability_registry.support_show_devices",
}

Comment on lines +883 to +906
def _materialize_device_requests(self):
"""On approval, turn each Support-Needs device request into a real
spp.assistive.device (status 'needed') on the registrant so it shows on
the Overview / registrant (OP#1068). Skips a device type that already
has a pending 'needed' record to avoid duplicates on re-approval.
"""
Device = self.env["spp.assistive.device"]
for rec in self:
for req in rec.device_request_ids:
already = Device.search_count(
[
("registrant_id", "=", rec.registrant_id.id),
("device_type_id", "=", req.device_type_id.id),
("status", "=", "needed"),
]
)
if not already:
Device.create(
{
"registrant_id": rec.registrant_id.id,
"device_type_id": req.device_type_id.id,
"status": "needed",
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The _materialize_device_requests method executes a search_count query inside a nested loop over self and device_request_ids. This results in an $O(N \times M)$ database query pattern, which can cause significant performance degradation when processing records in bulk. Batching the existence check and the creation of missing devices will greatly improve database efficiency.

    def _materialize_device_requests(self):
        """On approval, turn each Support-Needs device request into a real
        spp.assistive.device (status 'needed') on the registrant so it shows on
        the Overview / registrant (OP#1068). Skips a device type that already
        has a pending 'needed' record to avoid duplicates on re-approval.
        """
        Device = self.env["spp.assistive.device"]
        req_data = []
        for rec in self:
            for req in rec.device_request_ids:
                req_data.append((rec.registrant_id.id, req.device_type_id.id))
        if not req_data:
            return
        registrant_ids = list({r_id for r_id, _ in req_data})
        device_type_ids = list({dt_id for _, dt_id in req_data})
        existing_devices = Device.search([
            ("registrant_id", "in", registrant_ids),
            ("device_type_id", "in", device_type_ids),
            ("status", "=", "needed"),
        ])
        existing_set = {(d.registrant_id.id, d.device_type_id.id) for d in existing_devices}
        devices_to_create = []
        for rec in self:
            for req in rec.device_request_ids:
                key = (rec.registrant_id.id, req.device_type_id.id)
                if key not in existing_set:
                    devices_to_create.append({
                        "registrant_id": rec.registrant_id.id,
                        "device_type_id": req.device_type_id.id,
                        "status": "needed",
                    })
                    existing_set.add(key)
        if devices_to_create:
            Device.create(devices_to_create)
References
  1. When processing records in bulk, avoid evaluating filters or permissions individually per record to prevent O(N * M) database query patterns. Instead, perform individual checks (such as consent) first, and then evaluate filters in a single batched query.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.07018% with 75 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.93%. Comparing base (7c329cf) to head (260c778).
⚠️ Report is 175 commits behind head on 19.0.

Files with missing lines Patch % Lines
spp_disability_registry/models/assessment.py 72.48% 71 Missing ⚠️
..._disability_registry/models/res_config_settings.py 95.12% 2 Missing ⚠️
spp_disability_registry/models/registrant.py 95.45% 1 Missing ⚠️
spp_starter_disability_registry/__manifest__.py 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #263      +/-   ##
==========================================
+ Coverage   71.68%   71.93%   +0.25%     
==========================================
  Files        1010     1030      +20     
  Lines       60072    61239    +1167     
==========================================
+ Hits        43062    44052     +990     
- Misses      17010    17187     +177     
Flag Coverage Δ
spp_base_common 90.26% <ø> (ø)
spp_disability_registry 83.87% <78.29%> (-7.99%) ⬇️
spp_programs 65.27% <ø> (+0.14%) ⬆️
spp_registry 86.83% <ø> (?)
spp_security 66.66% <ø> (ø)
spp_starter_disability_registry 0.00% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_disability_registry/__manifest__.py 0.00% <ø> (ø)
spp_disability_registry/models/__init__.py 100.00% <100.00%> (ø)
spp_disability_registry/models/assistive_device.py 100.00% <100.00%> (ø)
spp_disability_registry/models/impairment.py 100.00% <100.00%> (ø)
spp_disability_registry/models/registrant.py 98.33% <95.45%> (-1.67%) ⬇️
spp_starter_disability_registry/__manifest__.py 0.00% <0.00%> (ø)
..._disability_registry/models/res_config_settings.py 95.12% <95.12%> (ø)
spp_disability_registry/models/assessment.py 77.74% <72.48%> (-16.94%) ⬇️

... and 22 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@emjay0921 emjay0921 merged commit f260ffd into 19.0 Jul 2, 2026
21 checks passed
@emjay0921 emjay0921 deleted the feat/1047-disability-registry-improvements branch July 2, 2026 05:17
@emjay0921 emjay0921 restored the feat/1047-disability-registry-improvements branch July 2, 2026 09:27
emjay0921 added a commit that referenced this pull request Jul 2, 2026
Revert "feat(disability_registry): stabilization improvements for spp_disability_registry (#1047)" (#263)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant