Integrates vocabulary-aware functions into the CEL (Common Expression
Language) expression engine for eligibility rules. Extends the CEL
translator to resolve vocabulary codes by URI or alias and translate
vocabulary function calls into Odoo domains. Auto-installs when both
spp_cel_domain and spp_vocabulary are present.
- CEL Function Registration: Registers vocabulary functions
(
code(),in_group(),code_eq(),head()) with the CEL function registry for use in eligibility expressions - Domain Translation: Extends
spp.cel.translatorto translate vocabulary function calls into Odoo domains that checkuriandreference_urifields - Semantic Helpers: Provides shorthand functions (
is_female(),is_male(),is_head(),is_pregnant()) that map to predefined concept groups - Concept Group Management: Creates standard concept groups on installation (gender, household roles, pregnancy status, hazards, age groups, disability)
- Local Code Support: Handles semantic equality for local codes that
map to standard codes via
reference_uri
| Model | Description |
|---|---|
spp.cel.vocabulary.functions |
Manages vocabulary function registration with the CEL function registry |
spp.cel.translator
(inherited) |
Extended to translate vocabulary function calls into Odoo domains |
After installing (happens automatically when both dependencies are present):
- Navigate to Settings > Vocabularies > Concept Groups
- Review the auto-created concept groups:
feminine_gender,masculine_gender,head_of_household,pregnant_eligible,climate_hazards,geophysical_hazards,children,adults,elderly,persons_with_disability - Add vocabulary codes to each group using the Codes tab to define which codes belong to each concept
Example eligibility rules using vocabulary functions:
// Code resolution
r.gender_id == code("female")
r.gender_id == code("urn:iso:std:iso:5218#2")
// Group membership
in_group(r.gender_id, "feminine_gender")
members.exists(m, in_group(m.relationship_type_id, "head_of_household"))
// Semantic helpers
is_female(r.gender_id) and age_years(r.birthdate) >= 18
members.exists(m, is_male(m.gender_id) and is_head(m.relationship_type_id))
// Head of household check
members.exists(m, head(m) and is_female(m.gender_id))| Group | Access |
|---|---|
| All | Read-only |
No write access required as function registration happens automatically
via post_init_hook.
- Inherit
spp.cel.translatorand override_to_plan()to add custom vocabulary function translations - Add new semantic helper functions to
services/cel_vocabulary_functions.pyand register them inVOCABULARY_FUNCTIONSdict - Create additional concept groups via data files or UI to support domain-specific eligibility patterns
spp_cel_domain, spp_vocabulary
Table of contents
This module has no views of its own. All UI interaction is through views
provided by spp_vocabulary and spp_cel_domain. This guide covers
what to verify after installing spp_cel_vocabulary.
- Install
spp_cel_vocabulary(auto-installs when bothspp_cel_domainandspp_vocabularyare present) - Log in as an admin user
- Have at least one vocabulary with codes (e.g., a gender vocabulary)
Path: Settings > Vocabularies > Concept Groups
Steps:
- Navigate to Settings > Vocabularies > Concept Groups
- Verify the following 10 groups exist:
| Name | Label | CEL Function |
|---|---|---|
feminine_gender |
Feminine Gender | is_female |
masculine_gender |
Masculine Gender | is_male |
head_of_household |
Head of Household | is_head |
pregnant_eligible |
Pregnant/Eligible | is_pregnant |
climate_hazards |
Climate-related Hazards | (empty) |
geophysical_hazards |
Geophysical Hazards | (empty) |
children |
Children | (empty) |
adults |
Adults | (empty) |
elderly |
Elderly/Senior Citizens | (empty) |
persons_with_disability |
Persons with Disability | (empty) |
- Open
feminine_genderand verify:- Label: "Feminine Gender"
- CEL Function:
is_female - Description is populated
- Codes tab is empty (groups are created empty by default)
Expected: All 10 groups are present with correct labels, CEL functions, and descriptions.
Path: Settings > Vocabularies > Concept Groups > (select group) > Codes tab
Precondition: A gender vocabulary with at least a "Female" code must exist.
Steps:
- Open
feminine_genderconcept group - Click Edit
- Go to the Codes tab
- Click Add a line
- Search for and select your "Female" vocabulary code
- Click Save
Verify:
- The code appears in the Codes tab with its vocabulary, code value, and display name
- If the code has a URI, it should be visible (may need Technical tab
for
code_urisfield)
Repeat for masculine_gender with a "Male" code if available.
Steps:
- Note the current concept groups and their IDs
- Upgrade
spp_cel_vocabularymodule (Settings > Apps > spp_cel_vocabulary > Upgrade) - Navigate back to Settings > Vocabularies > Concept Groups
Expected:
- No duplicate groups were created
- Existing groups retain their codes (codes added in Test 2 are still there)
- All 10 groups still present
Path: Custom > CEL Domain > Tools > Rule Preview
Precondition: Codes have been added to feminine_gender group
(Test 2).
Steps:
- Navigate to Custom > CEL Domain > Tools > Rule Preview
- Select a model (e.g., the individual registrant model)
- Enter the expression:
is_female(r.gender_id) - Click Validate & Preview
Expected:
- Validation succeeds (no error)
- The Summary tab shows:
preview_count: number of matching records (may be 0 if no registrants have gender set)explain_text: a human-readable explanation mentioningfeminine_genderand URIs
Repeat with these expressions:
| Expression | Should Validate |
|---|---|
is_female(r.gender_id) |
Yes |
is_male(r.gender_id) |
Yes |
in_group(r.gender_id, "feminine_gender") |
Yes |
code_eq(r.gender_id, "female") |
Yes (if code exists) |
r.gender_id == code("female") |
Yes (if code exists) |
members.exists(m, head(m) and is_female(m.gender_id)) |
Yes |
in_group(r.gender_id, "nonexistent_group") |
Yes (validates but matches nothing) |
is_female(r.name) |
Depends on translator behavior |
Path: Custom > CEL Domain > Tools > Rule Preview
Precondition: feminine_gender group has at least one code with a
URI.
Steps:
- Open Rule Preview
- Enter:
is_female(r.gender_id) - Click Validate & Preview
- Check the Summary tab explanation text
Expected:
- The explanation should reference
feminine_genderand list the code URIs - The generated domain should check both
gender_id.uriandgender_id.reference_uri
Path: Custom > CEL Domain > Tools > Rule Preview
Precondition: climate_hazards group exists but has no codes
assigned.
Steps:
- Open Rule Preview
- Enter:
in_group(r.gender_id, "climate_hazards") - Click Validate & Preview
Expected:
- Validation succeeds but matches 0 records
- The explanation should indicate the group is empty (e.g.,
[EMPTY GROUP]) - Check Odoo logs for a
[CEL Vocabulary]warning about the empty group
Steps:
- Open Rule Preview
- Enter:
in_group(r.gender_id, "does_not_exist") - Click Validate & Preview
Expected:
- Validation succeeds but matches 0 records
- The explanation should indicate the group was not found (e.g.,
[GROUP NOT FOUND]) - Check Odoo logs for a warning with guidance: "Check Settings > Vocabularies > Concept Groups"
Precondition: A local vocabulary code exists with
is_local = True and a reference_uri pointing to a standard
code's URI. Both codes are in the same concept group.
Steps:
- Create a local code (e.g., "Babae" with
reference_uripointing to the Female standard code's URI) - Add both the standard and local codes to
feminine_gendergroup - Open Rule Preview
- Enter:
is_female(r.gender_id) - Click Validate & Preview
Expected:
- The domain checks both
uriandreference_urifields - Records with either the standard code or the local code should be matched
Steps:
- Log in as a non-admin user (base user group)
- Navigate to Settings > Vocabularies > Concept Groups
Expected:
- User can view concept groups (read access)
- User cannot create, edit, or delete concept groups
Path: Settings > Vocabularies > Concept Groups
Steps:
- Use the search bar to search for "gender"
- Apply the "Has CEL Function" filter
Expected:
- Search by "gender" finds
feminine_genderandmasculine_gender - "Has CEL Function" filter shows only groups with a CEL function set
(
feminine_gender,masculine_gender,head_of_household,pregnant_eligible)
If expressions return unexpected results:
- Check logs for
[CEL Vocabulary]entries — error messages include guidance on how to fix the issue - Verify codes are in groups — open the concept group and check the Codes tab
- Verify code URIs — codes need a
urifield populated for domain matching - Check field names — vocabulary functions require Many2one fields
pointing to
spp.vocabulary.code(e.g.,gender_id, notgender)
- 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 feedback.
Do not contact contributors directly about support or help with technical issues.
- OpenSPP.org
Current maintainers:
This module is part of the OpenSPP/OpenSPP2 project on GitHub.
You are welcome to contribute.





