Skip to content

Commit 387a638

Browse files
committed
feat(spp_dci_demo,spp_mis_demo_v2): add Conditional Child Grant and demo household
- Add Masters household (Adam + Mary) as demo data for DCI CR flow - Hide Contact Information and Relationship fields in add-member form - Target Conditional Child Grant program in post_init_hook - Add Conditional Child Grant program with first-1,000-days eligibility - Add Health Visit event type for compliance tracking - Configure compliance manager with CEL expression support
1 parent c4f4f28 commit 387a638

8 files changed

Lines changed: 168 additions & 8 deletions

File tree

spp_dci_demo/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"data/vocabulary_data.xml",
1818
"data/dci_data_source.xml",
1919
"data/system_parameters.xml",
20+
"data/demo_household.xml",
2021
"views/cr_detail_add_member_view.xml",
2122
"views/change_request_view.xml",
2223
],
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<!-- Masters Household for DCI demo: parent adds child via CR -->
4+
5+
<!-- Father -->
6+
<record id="partner_adam_masters" model="res.partner">
7+
<field name="name">MASTERS, Adam</field>
8+
<field name="given_name">Adam</field>
9+
<field name="family_name">Masters</field>
10+
<field name="birthdate">1995-08-14</field>
11+
<field name="gender_id" ref="spp_vocabulary.code_gender_male" />
12+
<field name="is_registrant" eval="True" />
13+
<field name="is_group" eval="False" />
14+
</record>
15+
16+
<!-- Mother -->
17+
<record id="partner_mary_masters" model="res.partner">
18+
<field name="name">MASTERS, Mary</field>
19+
<field name="given_name">Mary</field>
20+
<field name="family_name">Masters</field>
21+
<field name="birthdate">2002-02-01</field>
22+
<field name="gender_id" ref="spp_vocabulary.code_gender_female" />
23+
<field name="is_registrant" eval="True" />
24+
<field name="is_group" eval="False" />
25+
</record>
26+
27+
<!-- Household -->
28+
<record id="group_masters_household" model="res.partner">
29+
<field name="name">Masters Household</field>
30+
<field name="is_registrant" eval="True" />
31+
<field name="is_group" eval="True" />
32+
</record>
33+
34+
<!-- Memberships -->
35+
<record id="membership_adam_masters" model="spp.group.membership">
36+
<field name="group" ref="group_masters_household" />
37+
<field name="individual" ref="partner_adam_masters" />
38+
<field
39+
name="membership_type_ids"
40+
eval="[(4, ref('spp_vocabulary.code_membership_type_head'))]"
41+
/>
42+
</record>
43+
44+
<record id="membership_mary_masters" model="spp.group.membership">
45+
<field name="group" ref="group_masters_household" />
46+
<field name="individual" ref="partner_mary_masters" />
47+
</record>
48+
</odoo>

spp_dci_demo/hooks.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ def post_init_hook(env):
1818
_logger.info("spp.program model not available, skipping enrollment program setup")
1919
return
2020

21-
# Find the first program
22-
program = env["spp.program"].search([], limit=1)
21+
# Find the Conditional Child Grant program (the target for DCI demo enrollment)
22+
program = env["spp.program"].search([("name", "=", "Conditional Child Grant")], limit=1)
23+
if not program:
24+
# Fall back to any program if not found
25+
program = env["spp.program"].search([], limit=1)
2326
if not program:
2427
_logger.info("No programs found, enrollment_program_id not configured")
2528
return

spp_dci_demo/views/cr_detail_add_member_view.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@
99
ref="spp_change_request_v2.spp_cr_detail_add_member_form"
1010
/>
1111
<field name="arch" type="xml">
12+
<!-- Hide Contact Information section for demo -->
13+
<xpath
14+
expr="//field[@name='phone']/parent::group/parent::group"
15+
position="attributes"
16+
>
17+
<attribute name="invisible">1</attribute>
18+
</xpath>
19+
20+
<!-- Hide Relationship to Head for demo -->
21+
<xpath expr="//field[@name='relationship_id']" position="attributes">
22+
<attribute name="invisible">1</attribute>
23+
</xpath>
24+
1225
<!-- Add Birth Verification section before the Result group -->
1326
<xpath expr="//field[@name='created_individual_id']/.." position="before">
1427
<group string="Birth Verification" name="birth_verification">

spp_mis_demo_v2/data/event_types.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@
2020
<!-- EVENT TYPE DEFINITIONS -->
2121
<!-- ═══════════════════════════════════════════════════════════════════════ -->
2222

23+
<!-- Health Visit Event Type (compliance tracking) -->
24+
<record id="event_type_health_visit" model="spp.event.type">
25+
<field name="name">Health Visit</field>
26+
<field name="code">health_visit</field>
27+
<field
28+
name="description"
29+
>Health checkup visits for children under 5, required for Conditional Child Grant compliance. Tracks immunization and growth monitoring.</field>
30+
<field name="category">visit</field>
31+
<field name="target_type">both</field>
32+
<field name="source_type">internal</field>
33+
<field name="sequence">5</field>
34+
</record>
35+
2336
<!-- Training Event Type -->
2437
<record id="event_type_training" model="spp.event.type">
2538
<field name="name">Training Session</field>

spp_mis_demo_v2/models/demo_programs.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@
1010
2. Demonstrate different CEL expression patterns
1111
3. Link to existing Logic Packs from spp_studio
1212
13-
Program Catalog (6 programs):
13+
Program Catalog (7 programs):
1414
1. Universal Child Grant - Member aggregation (child_benefit pack)
15-
2. Elderly Social Pension - Age + constants (social_pension pack)
16-
3. Emergency Relief Fund - Cached metrics (vulnerability_assessment pack)
17-
4. Cash Transfer Program - Poverty threshold (cash_transfer_basic pack)
18-
5. Disability Support Grant - Member existence (disability_assistance pack)
19-
6. Food Assistance - Basic active check (no pack, simple CEL)
15+
2. Conditional Child Grant - First 1,000 days with compliance (child_benefit pack)
16+
3. Elderly Social Pension - Age + constants (social_pension pack)
17+
4. Emergency Relief Fund - Cached metrics (vulnerability_assessment pack)
18+
5. Cash Transfer Program - Poverty threshold (cash_transfer_basic pack)
19+
6. Disability Support Grant - Member existence (disability_assistance pack)
20+
7. Food Assistance - Basic active check (no pack, simple CEL)
2021
2122
CEL Expression Patterns Demonstrated:
2223
- Field comparison: r.active == true
2324
- Computed variables: age >= retirement_age
2425
- Aggregate variables: hh_total_income < poverty_line, child_count > 0
2526
- Compound conditions: dependency_ratio >= 1.5 or (is_female_headed and elderly_count > 0)
2627
- Arithmetic with variables: base_child_grant * child_count, disabled_count * disability_grant_per_member
28+
- Compliance criteria: members.exists(m, age_years(m.birthdate) < 5)
2729
"""
2830

2931
# Demo programs aligned with spec and Logic Packs
@@ -55,6 +57,37 @@
5557
"Logic Pack: child_benefit",
5658
],
5759
},
60+
{
61+
"id": "conditional_child_grant",
62+
"name": "Conditional Child Grant",
63+
"description": "Monthly grant for households with pregnant women and children aged 0-2. "
64+
"Targets the critical first 1,000 days of life to support nutrition and "
65+
"health-seeking behavior. Compliance requires prenatal visits, health "
66+
"checkups, and immunizations.",
67+
"target_type": "group",
68+
"entitlement_amount": 10.0,
69+
"entitlement_formula": "first_1000_days_grant",
70+
"cycle_duration": 30, # Monthly
71+
# CEL: Households with children under 2 (first 1,000 days)
72+
# Pattern: Member age check via members.exists()
73+
"cel_expression": "r.is_group == true and members.exists(m, age_years(m.birthdate) < 2)",
74+
# Compliance: prenatal visits, health checkups, immunizations
75+
"compliance_cel_expression": "members.exists(m, age_years(m.birthdate) <= 2)",
76+
# Link to Logic Pack
77+
"logic_pack": "child_benefit",
78+
"use_logic_studio": True,
79+
"logic_name": "Conditional Child Grant Eligibility",
80+
"expression_type": "filter",
81+
"stories": [],
82+
"demo_points": [
83+
"Conditional cash transfer",
84+
"First 1,000 days targeting (0-2 years)",
85+
"Health visit and immunization compliance",
86+
"Compliance manager with CEL expression",
87+
"CEL: members.exists() for eligibility and compliance",
88+
"Logic Pack: child_benefit",
89+
],
90+
},
5891
{
5992
"id": "elderly_social_pension",
6093
"name": "Elderly Social Pension",

spp_mis_demo_v2/models/demo_variables.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
# Fixed program amounts
4747
"elderly_pension_amount": 100,
4848
"cash_transfer_amount": 150,
49+
"first_1000_days_grant": 10, # Monthly per-beneficiary
4950
}
5051

5152

spp_mis_demo_v2/models/mis_demo_generator.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,10 @@ def _create_demo_programs(self, stats):
10951095
if program_def.get("cycle_duration"):
10961096
self._configure_cycle_manager(program, program_def)
10971097

1098+
# Configure compliance manager if compliance CEL expression specified
1099+
if program_def.get("compliance_cel_expression"):
1100+
self._configure_compliance_manager(program, program_def)
1101+
10981102
except Exception as e:
10991103
_logger.error(
11001104
"Error creating program (program_id=%s): %s",
@@ -1199,6 +1203,50 @@ def _configure_cycle_manager(self, program, program_def):
11991203
e,
12001204
)
12011205

1206+
def _configure_compliance_manager(self, program, program_def):
1207+
"""Configure the compliance manager with a CEL expression.
1208+
1209+
Sets the compliance CEL expression for ongoing beneficiary verification.
1210+
"""
1211+
try:
1212+
compliance_manager = program.get_manager(program.MANAGER_COMPLIANCE)
1213+
if not compliance_manager:
1214+
_logger.warning(
1215+
"No compliance manager found for program (program_id=%s)",
1216+
program.id,
1217+
)
1218+
return
1219+
1220+
cel_expression = program_def.get("compliance_cel_expression")
1221+
if not cel_expression:
1222+
return
1223+
1224+
if "compliance_cel_expression" not in compliance_manager._fields:
1225+
_logger.info(
1226+
"Compliance CEL not available for program (program_id=%s)",
1227+
program.id,
1228+
)
1229+
return
1230+
1231+
compliance_manager.write(
1232+
{
1233+
"compliance_cel_mode": "cel",
1234+
"compliance_cel_expression": cel_expression,
1235+
}
1236+
)
1237+
_logger.info(
1238+
"Configured compliance CEL for program (program_id=%s): %s",
1239+
program.id,
1240+
cel_expression,
1241+
)
1242+
1243+
except Exception as e:
1244+
_logger.warning(
1245+
"Could not configure compliance manager for program (program_id=%s): %s",
1246+
program.id,
1247+
e,
1248+
)
1249+
12021250
def _configure_eligibility_manager(self, program, program_def):
12031251
"""Configure the eligibility manager with CEL expression.
12041252

0 commit comments

Comments
 (0)