From fd8255eb01956fd937c407ed0c657a322f56ff3f Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Thu, 20 Aug 2026 17:08:27 +0800 Subject: [PATCH 1/3] fix(spp_studio): remove or rewrite pack items with non-evaluable CEL expressions 24 of the 106 shipped spp.studio.pack.item records carried filter expressions referencing registrant fields or studio variables that exist in no module, so the logic they install could never translate or evaluate, and they fail spp_cel_load_testing's strict studio-validation suite (gating #432). Rewrite the 4 items where a meaningful sub-expression survives (OVC Child Age Eligibility, OVC Vulnerable Household Check, GMI Residency Requirement, Public Works Poverty Status Check); remove the other 20, which have no working near-equivalent. Every kept expression was verified through the resolver+translator path the validation test uses. Pack data is noupdate, so a migration applies the same removals and rewrites to existing databases. Bump to 19.0.2.0.2. Fixes #431 --- spp_studio/__manifest__.py | 2 +- spp_studio/data/packs/cct_program.xml | 15 -- .../data/packs/disability_assistance.xml | 15 -- spp_studio/data/packs/exclusion_criteria.xml | 157 ------------------ .../data/packs/geographic_targeting.xml | 15 -- .../data/packs/guaranteed_minimum_income.xml | 21 +-- spp_studio/data/packs/ovc_support.xml | 42 +---- spp_studio/data/packs/public_works.xml | 30 +--- spp_studio/data/packs/social_pension.xml | 18 -- .../migrations/19.0.2.0.2/post-migration.py | 96 +++++++++++ spp_studio/readme/HISTORY.md | 4 + 11 files changed, 108 insertions(+), 307 deletions(-) create mode 100644 spp_studio/migrations/19.0.2.0.2/post-migration.py diff --git a/spp_studio/__manifest__.py b/spp_studio/__manifest__.py index dab2e9e6d..b6cda1e29 100644 --- a/spp_studio/__manifest__.py +++ b/spp_studio/__manifest__.py @@ -1,6 +1,6 @@ { "name": "OpenSPP Studio", - "version": "19.0.2.0.1", + "version": "19.0.2.0.2", "category": "OpenSPP/Configuration", "summary": "No-code customization interface for OpenSPP", "author": "OpenSPP.org", diff --git a/spp_studio/data/packs/cct_program.xml b/spp_studio/data/packs/cct_program.xml index a206c838f..b5b1712e0 100644 --- a/spp_studio/data/packs/cct_program.xml +++ b/spp_studio/data/packs/cct_program.xml @@ -58,21 +58,6 @@ 30 - - - - Health Conditionality Compliance - group - Children under 5 meeting health checkup and vaccination requirements. - filter - {"cel_expression": "children_0_5 == 0 || (health_checkups_completed && vaccinations_current)"} - 40 - - diff --git a/spp_studio/data/packs/disability_assistance.xml b/spp_studio/data/packs/disability_assistance.xml index e2d6de08c..466f9991f 100644 --- a/spp_studio/data/packs/disability_assistance.xml +++ b/spp_studio/data/packs/disability_assistance.xml @@ -13,21 +13,6 @@ available - - - - Disability Status Verification - individual - Verifies certified disability status from medical assessment. - filter - {"cel_expression": "has_disability && disability_certified"} - 10 - - diff --git a/spp_studio/data/packs/exclusion_criteria.xml b/spp_studio/data/packs/exclusion_criteria.xml index 10e627951..a1b5a94a5 100644 --- a/spp_studio/data/packs/exclusion_criteria.xml +++ b/spp_studio/data/packs/exclusion_criteria.xml @@ -13,49 +13,6 @@ available - - - - Government Employee Exclusion - group - Exclude households with government/public sector employees. - filter - {"cel_expression": "!has_government_employee"} - 10 - - - - - - Formal Sector Exclusion - group - Exclude households with formal sector employment (for informal sector targeting). - filter - {"cel_expression": "!has_formal_employment || formal_income < 5000"} - 20 - - - - - - Vehicle Ownership Exclusion - group - Exclude households owning motorized vehicles (cars, motorcycles above threshold). - filter - {"cel_expression": "!owns_car && motorcycle_count <= 1"} - 30 - - @@ -68,118 +25,4 @@ {"cel_expression": "land_hectares <= 2"} 40 - - - - - Business Ownership Exclusion - both - Exclude households owning registered businesses above threshold. - filter - {"cel_expression": "!owns_registered_business || business_revenue < 50000"} - 50 - - - - - - Housing Quality Exclusion - both - Exclude households with high-quality/permanent housing (proxy for wealth). - filter - {"cel_expression": "housing_quality_score <= 3 || !has_permanent_structure"} - 60 - - - - - - Pension Receipt Exclusion - both - Exclude individuals already receiving contributory pension. - filter - {"cel_expression": "!receives_contributory_pension || pension_amount < 1000"} - 70 - - - - - - Duplicate Program Exclusion - both - Exclude beneficiaries already enrolled in similar programs. - filter - {"cel_expression": "!enrolled_in_similar_program"} - 80 - - - - - - Income Tax Payer Exclusion - both - Exclude households paying income tax (above tax threshold). - filter - {"cel_expression": "!pays_income_tax"} - 90 - - - - - - Bank Balance Exclusion - both - Exclude households with bank balances above threshold. - filter - {"cel_expression": "total_bank_balance <= 50000"} - 100 - - - - - - Livestock Ownership Exclusion - both - Exclude households with livestock above threshold (in Tropical Livestock Units). - filter - {"cel_expression": "livestock_tlu <= 5"} - 110 - - - - - - Institutional Residence Exclusion - both - Exclude individuals residing in institutions (prisons, care homes with full support). - filter - {"cel_expression": "!in_institutional_care"} - 120 - diff --git a/spp_studio/data/packs/geographic_targeting.xml b/spp_studio/data/packs/geographic_targeting.xml index cbaba22db..b55dcd57c 100644 --- a/spp_studio/data/packs/geographic_targeting.xml +++ b/spp_studio/data/packs/geographic_targeting.xml @@ -79,21 +79,6 @@ 40 - - - - Limited Service Access - both - Location has limited access to basic services (health, education, markets). - filter - {"cel_expression": "distance_to_health_facility > 10 || distance_to_school > 5"} - 50 - - diff --git a/spp_studio/data/packs/guaranteed_minimum_income.xml b/spp_studio/data/packs/guaranteed_minimum_income.xml index 8c5818070..71db14fbb 100644 --- a/spp_studio/data/packs/guaranteed_minimum_income.xml +++ b/spp_studio/data/packs/guaranteed_minimum_income.xml @@ -28,21 +28,6 @@ 10 - - - - Employment Barriers Check - group - Working-age members have legitimate barriers to employment. - filter - {"cel_expression": "has_disabled_member || has_caregiver_responsibilities || is_single_parent || no_working_age_members"} - 20 - - @@ -65,11 +50,9 @@ individual Legal residency and minimum duration requirement. + >Minimum residency duration requirement (12 months). filter - {"cel_expression": "is_legal_resident && residency_months >= 12"} + {"cel_expression": "residency_months >= 12"} 40 diff --git a/spp_studio/data/packs/ovc_support.xml b/spp_studio/data/packs/ovc_support.xml index b56437056..da0b3a58c 100644 --- a/spp_studio/data/packs/ovc_support.xml +++ b/spp_studio/data/packs/ovc_support.xml @@ -13,33 +13,14 @@ available - - - - Orphan Status Check - individual - Child has lost one or both parents (single or double orphan). - filter - {"cel_expression": "is_orphan || (!has_mother && !has_father)"} - 10 - - Child Age Eligibility individual - Child must be under child_age_limit, or under 25 if in full-time education. + Child must be under child_age_limit. filter - {"cel_expression": "age < child_age_limit || (age < 25 && is_in_education)"} + {"cel_expression": "age < child_age_limit"} 20 @@ -50,29 +31,14 @@ group Household meets vulnerability criteria (elderly-headed, child-headed, or skipped-generation). + >Household meets vulnerability criteria (elderly-headed or child-headed). filter {"cel_expression": "is_elderly_headed || is_child_headed || is_skipped_generation || has_chronically_ill_head"} + >{"cel_expression": "is_elderly_headed || is_child_headed"} 30 - - - - School Enrollment Requirement - individual - School-age children must be enrolled in education (soft conditionality). - filter - {"cel_expression": "age < 6 || is_enrolled_in_school || has_enrollment_exemption"} - 40 - - diff --git a/spp_studio/data/packs/public_works.xml b/spp_studio/data/packs/public_works.xml index 0f4dab33b..697118876 100644 --- a/spp_studio/data/packs/public_works.xml +++ b/spp_studio/data/packs/public_works.xml @@ -28,21 +28,6 @@ 10 - - - - Physical Work Capability - individual - Beneficiary is physically able to perform manual labor (with accommodations for light duty). - filter - {"cel_expression": "can_perform_manual_labor || eligible_for_light_duty"} - 20 - - @@ -69,23 +54,10 @@ filter {"cel_expression": "per_capita_income <= poverty_threshold || is_food_insecure"} + >{"cel_expression": "per_capita_income <= poverty_threshold"} 40 - - - - Seasonal Availability - both - Program operates during lean/off-farm season. - filter - {"cel_expression": "is_lean_season || !has_active_farm_work"} - 50 - - diff --git a/spp_studio/data/packs/social_pension.xml b/spp_studio/data/packs/social_pension.xml index e0268fb45..b9f4a0b95 100644 --- a/spp_studio/data/packs/social_pension.xml +++ b/spp_studio/data/packs/social_pension.xml @@ -26,24 +26,6 @@ 10 - - - - No Formal Pension Check - individual - Excludes individuals already receiving formal/contributory pension benefits. - filter - {"cel_expression": "!has_formal_pension || formal_pension_amount == 0"} - 20 - - diff --git a/spp_studio/migrations/19.0.2.0.2/post-migration.py b/spp_studio/migrations/19.0.2.0.2/post-migration.py new file mode 100644 index 000000000..f067d0517 --- /dev/null +++ b/spp_studio/migrations/19.0.2.0.2/post-migration.py @@ -0,0 +1,96 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Fix shipped logic-pack filter items whose CEL expressions cannot evaluate (#431). + +24 ``spp.studio.pack.item`` records shipped with filter expressions referencing +registrant fields or studio variables that exist in no module, so installing +them produced logic that could never translate or run. The pack data files are +``noupdate="1"``, so existing databases keep the broken records after the XML +is corrected - this migration applies the same fix directly: + +- items whose expression has no working near-equivalent are deleted; +- items where a meaningful sub-expression survives are rewritten to it + (matching the corrected pack data files). +""" + +import json +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + +REMOVED_ITEMS = [ + "spp_studio.pack_disability_item_status_check", + "spp_studio.pack_ovc_item_orphan_status", + "spp_studio.pack_gmi_item_work_barriers", + "spp_studio.pack_ovc_item_school_enrollment", + "spp_studio.pack_social_pension_item_no_formal_pension", + "spp_studio.pack_pw_item_physical", + "spp_studio.pack_pw_item_seasonal", + "spp_studio.pack_cct_item_health_compliance", + "spp_studio.pack_geo_item_service_access", + "spp_studio.pack_excl_item_govt_employee", + "spp_studio.pack_excl_item_formal_sector", + "spp_studio.pack_excl_item_vehicle", + "spp_studio.pack_excl_item_business", + "spp_studio.pack_excl_item_housing", + "spp_studio.pack_excl_item_pension", + "spp_studio.pack_excl_item_other_programs", + "spp_studio.pack_excl_item_tax", + "spp_studio.pack_excl_item_bank_balance", + "spp_studio.pack_excl_item_livestock", + "spp_studio.pack_excl_item_institutional", +] + +# xmlid -> (new cel_expression, new description) +REWRITTEN_ITEMS = { + "spp_studio.pack_ovc_item_child_age": ( + "age < child_age_limit", + "Child must be under child_age_limit.", + ), + "spp_studio.pack_ovc_item_vulnerable_hh": ( + "is_elderly_headed || is_child_headed", + "Household meets vulnerability criteria (elderly-headed or child-headed).", + ), + "spp_studio.pack_gmi_item_residency": ( + "residency_months >= 12", + "Minimum residency duration requirement (12 months).", + ), + "spp_studio.pack_pw_item_poverty": ( + "per_capita_income <= poverty_threshold", + "Household meets poverty criteria for public works targeting.", + ), +} + + +def migrate(cr, version): + if not version: + return + + env = api.Environment(cr, SUPERUSER_ID, {}) + + removed = 0 + for xmlid in REMOVED_ITEMS: + item = env.ref(xmlid, raise_if_not_found=False) + if item: + item.unlink() + removed += 1 + + rewritten = 0 + for xmlid, (expression, description) in REWRITTEN_ITEMS.items(): + item = env.ref(xmlid, raise_if_not_found=False) + if not item: + continue + item.write( + { + "logic_data": json.dumps({"cel_expression": expression}), + "description": description, + } + ) + rewritten += 1 + + _logger.info( + "spp_studio 19.0.2.0.2: removed %d and rewrote %d shipped pack items with non-evaluable CEL expressions (#431)", + removed, + rewritten, + ) diff --git a/spp_studio/readme/HISTORY.md b/spp_studio/readme/HISTORY.md index 1354baa34..d6867ff54 100644 --- a/spp_studio/readme/HISTORY.md +++ b/spp_studio/readme/HISTORY.md @@ -1,3 +1,7 @@ +### 19.0.2.0.2 + +- fix(data): repair the 24 shipped logic-pack filter items whose CEL expressions referenced registrant fields or studio variables that exist in no module, so they could never translate or evaluate (#431). 4 items are rewritten to the working part of their expression (OVC Child Age Eligibility, OVC Vulnerable Household Check, GMI Residency Requirement, Public Works Poverty Status Check); the other 20 are removed because no near-equivalent field or variable exists (Disability Status Verification, OVC Orphan Status Check, OVC School Enrollment Requirement, Social Pension No Formal Pension Check, GMI Employment Barriers Check, Public Works Physical Work Capability and Seasonal Availability, CCT Health Conditionality Compliance, Geographic Limited Service Access, and 11 Exclusion Criteria items: Government Employee, Formal Sector, Vehicle Ownership, Business Ownership, Housing Quality, Pension Receipt, Duplicate Program, Income Tax Payer, Bank Balance, Livestock Ownership, Institutional Residence). Pack data is `noupdate`, so a migration applies the same removals/rewrites to existing databases. + ### 19.0.2.0.1 - fix(security): drop the Program Manager → `group_studio_viewer` extension per the OP#951 menu audit (Program Manager should NOT see the Studio top-level menu). Removes `data/user_roles.xml` from the module entirely; System Admin retains Studio visibility via `spp_security.group_spp_admin` → `group_studio_manager` (wired in `spp_studio/security/groups.xml`). From de3501bb94f0cab4a4643ac9f18edd46977c9d5e Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Thu, 20 Aug 2026 17:35:02 +0800 Subject: [PATCH 2/3] fix(spp_studio): restore Institutional Residence Exclusion with real variable Cross-module sweep found that core spp_registry already defines the in_institutional_care field the item filters on - it only lacked a variable definition. Add the in_institutional_care standard variable over that field and keep the shipped expression, instead of removing the item. standard_variables.xml is not noupdate, so existing databases receive the variable through the regular upgrade data load; removals in the 19.0.2.0.2 migration drop from 20 to 19. --- spp_studio/data/packs/exclusion_criteria.xml | 13 +++++++++++++ spp_studio/data/standard_variables.xml | 16 ++++++++++++++++ .../migrations/19.0.2.0.2/post-migration.py | 7 ++++++- spp_studio/readme/HISTORY.md | 2 +- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/spp_studio/data/packs/exclusion_criteria.xml b/spp_studio/data/packs/exclusion_criteria.xml index a1b5a94a5..04529c70c 100644 --- a/spp_studio/data/packs/exclusion_criteria.xml +++ b/spp_studio/data/packs/exclusion_criteria.xml @@ -25,4 +25,17 @@ {"cel_expression": "land_hectares <= 2"} 40 + + + + + Institutional Residence Exclusion + both + Exclude individuals residing in institutions (prisons, care homes with full support). + filter + {"cel_expression": "!in_institutional_care"} + 120 + diff --git a/spp_studio/data/standard_variables.xml b/spp_studio/data/standard_variables.xml index 276e3892a..2455c120f 100644 --- a/spp_studio/data/standard_variables.xml +++ b/spp_studio/data/standard_variables.xml @@ -697,6 +697,22 @@ True + + in_institutional_care + In Institutional Care + Whether the individual currently resides in institutional care (e.g., nursing home, care facility, orphanage) + + boolean + field + res.partner + in_institutional_care + in_institutional_care + both + True + + diff --git a/spp_studio/migrations/19.0.2.0.2/post-migration.py b/spp_studio/migrations/19.0.2.0.2/post-migration.py index f067d0517..8ff75f94e 100644 --- a/spp_studio/migrations/19.0.2.0.2/post-migration.py +++ b/spp_studio/migrations/19.0.2.0.2/post-migration.py @@ -10,6 +10,12 @@ - items whose expression has no working near-equivalent are deleted; - items where a meaningful sub-expression survives are rewritten to it (matching the corrected pack data files). + +One flagged item needs no handling here: Institutional Residence Exclusion +keeps its shipped expression - it only lacked a variable definition, and the +new ``in_institutional_care`` standard variable (over the existing +``spp_registry`` field) is created by the regular data load, which is not +``noupdate`` for ``standard_variables.xml``. """ import json @@ -39,7 +45,6 @@ "spp_studio.pack_excl_item_tax", "spp_studio.pack_excl_item_bank_balance", "spp_studio.pack_excl_item_livestock", - "spp_studio.pack_excl_item_institutional", ] # xmlid -> (new cel_expression, new description) diff --git a/spp_studio/readme/HISTORY.md b/spp_studio/readme/HISTORY.md index d6867ff54..6a25d1d1d 100644 --- a/spp_studio/readme/HISTORY.md +++ b/spp_studio/readme/HISTORY.md @@ -1,6 +1,6 @@ ### 19.0.2.0.2 -- fix(data): repair the 24 shipped logic-pack filter items whose CEL expressions referenced registrant fields or studio variables that exist in no module, so they could never translate or evaluate (#431). 4 items are rewritten to the working part of their expression (OVC Child Age Eligibility, OVC Vulnerable Household Check, GMI Residency Requirement, Public Works Poverty Status Check); the other 20 are removed because no near-equivalent field or variable exists (Disability Status Verification, OVC Orphan Status Check, OVC School Enrollment Requirement, Social Pension No Formal Pension Check, GMI Employment Barriers Check, Public Works Physical Work Capability and Seasonal Availability, CCT Health Conditionality Compliance, Geographic Limited Service Access, and 11 Exclusion Criteria items: Government Employee, Formal Sector, Vehicle Ownership, Business Ownership, Housing Quality, Pension Receipt, Duplicate Program, Income Tax Payer, Bank Balance, Livestock Ownership, Institutional Residence). Pack data is `noupdate`, so a migration applies the same removals/rewrites to existing databases. +- fix(data): repair the 24 shipped logic-pack filter items whose CEL expressions referenced registrant fields or studio variables that exist in no module, so they could never translate or evaluate (#431). 1 item is fixed properly: Institutional Residence Exclusion keeps its expression, backed by a new `in_institutional_care` standard variable over the existing `spp_registry` field of the same name. 4 items are rewritten to the working part of their expression (OVC Child Age Eligibility, OVC Vulnerable Household Check, GMI Residency Requirement, Public Works Poverty Status Check). The other 19 are removed because no near-equivalent field or variable exists anywhere in the platform (Disability Status Verification, OVC Orphan Status Check, OVC School Enrollment Requirement, Social Pension No Formal Pension Check, GMI Employment Barriers Check, Public Works Physical Work Capability and Seasonal Availability, CCT Health Conditionality Compliance, Geographic Limited Service Access, and 10 Exclusion Criteria items: Government Employee, Formal Sector, Vehicle Ownership, Business Ownership, Housing Quality, Pension Receipt, Duplicate Program, Income Tax Payer, Bank Balance, Livestock Ownership). Pack data is `noupdate`, so a migration applies the same removals/rewrites to existing databases. ### 19.0.2.0.1 From 6751987a61d240250a2eacf2ee7d49ca7fe4db55 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Thu, 20 Aug 2026 17:43:37 +0800 Subject: [PATCH 3/3] docs(spp_studio): apply CI-generated README for 19.0.2.0.2 fragment --- spp_studio/README.rst | 24 +++++++++++++++++++++ spp_studio/static/description/index.html | 27 +++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/spp_studio/README.rst b/spp_studio/README.rst index 0fe4bef88..a108a4845 100644 --- a/spp_studio/README.rst +++ b/spp_studio/README.rst @@ -149,6 +149,30 @@ Dependencies Changelog ========= +19.0.2.0.2 +~~~~~~~~~~ + +- fix(data): repair the 24 shipped logic-pack filter items whose CEL + expressions referenced registrant fields or studio variables that + exist in no module, so they could never translate or evaluate (#431). + 1 item is fixed properly: Institutional Residence Exclusion keeps its + expression, backed by a new ``in_institutional_care`` standard + variable over the existing ``spp_registry`` field of the same name. 4 + items are rewritten to the working part of their expression (OVC Child + Age Eligibility, OVC Vulnerable Household Check, GMI Residency + Requirement, Public Works Poverty Status Check). The other 19 are + removed because no near-equivalent field or variable exists anywhere + in the platform (Disability Status Verification, OVC Orphan Status + Check, OVC School Enrollment Requirement, Social Pension No Formal + Pension Check, GMI Employment Barriers Check, Public Works Physical + Work Capability and Seasonal Availability, CCT Health Conditionality + Compliance, Geographic Limited Service Access, and 10 Exclusion + Criteria items: Government Employee, Formal Sector, Vehicle Ownership, + Business Ownership, Housing Quality, Pension Receipt, Duplicate + Program, Income Tax Payer, Bank Balance, Livestock Ownership). Pack + data is ``noupdate``, so a migration applies the same + removals/rewrites to existing databases. + 19.0.2.0.1 ~~~~~~~~~~ diff --git a/spp_studio/static/description/index.html b/spp_studio/static/description/index.html index 52b9095a5..3b78230b6 100644 --- a/spp_studio/static/description/index.html +++ b/spp_studio/static/description/index.html @@ -532,6 +532,31 @@

Changelog

+

19.0.2.0.2

+
    +
  • fix(data): repair the 24 shipped logic-pack filter items whose CEL +expressions referenced registrant fields or studio variables that +exist in no module, so they could never translate or evaluate (#431). +1 item is fixed properly: Institutional Residence Exclusion keeps its +expression, backed by a new in_institutional_care standard +variable over the existing spp_registry field of the same name. 4 +items are rewritten to the working part of their expression (OVC Child +Age Eligibility, OVC Vulnerable Household Check, GMI Residency +Requirement, Public Works Poverty Status Check). The other 19 are +removed because no near-equivalent field or variable exists anywhere +in the platform (Disability Status Verification, OVC Orphan Status +Check, OVC School Enrollment Requirement, Social Pension No Formal +Pension Check, GMI Employment Barriers Check, Public Works Physical +Work Capability and Seasonal Availability, CCT Health Conditionality +Compliance, Geographic Limited Service Access, and 10 Exclusion +Criteria items: Government Employee, Formal Sector, Vehicle Ownership, +Business Ownership, Housing Quality, Pension Receipt, Duplicate +Program, Income Tax Payer, Bank Balance, Livestock Ownership). Pack +data is noupdate, so a migration applies the same +removals/rewrites to existing databases.
  • +
+
+

19.0.2.0.1

  • fix(security): drop the Program Manager → group_studio_viewer @@ -542,7 +567,7 @@

    19.0.2.0.1

    spp_studio/security/groups.xml).
-
+

19.0.2.0.0

  • Initial migration to OpenSPP2