---
myst:
html_meta:
"title": "Registry"
"description": "Learn how to customize OpenSPP registry system by adding new top-level group types and extending registry functionality"
"keywords": "OpenSPP, registry customization, group types, registry system, module development, registry extension"
---
# Registry
This article explains how to customize OpenSPP's registry system by introducing a new **top-level group** type.
As a practical example, we'll add a new top-level group type (such as "Village") that can contain regular groups (households), along with custom UI, data, and actions.
**Core Models**
- **`res.partner`**: Main registry model for individuals and groups.
- **`g2p.group.kind`**: Defines types of groups (e.g., Household, Village).
- **`g2p.group.membership`**: Manages group membership relationships.
**Key Features**
- Hierarchical group structure (e.g., Villages > Households > Individuals)
- Custom group kinds and indicators
- Computed statistics for top-level groups
- Custom actions for navigation and reporting
## Prerequisites
- Solid understanding of Odoo 17 module development, including Python, XML, and XPath.
- Required modules: `g2p_registry_base`, `g2p_registry_group`, `g2p_registry_individual`, `g2p_registry_membership`, `spp_registry_group_hierarchy`.
- To set up OpenSPP for development, please refer to the {doc}`Development Setup Guide <../setup>`.
## Module Structure
A typical registry customization module follows the standard Odoo structure.
Here's the structure for our example module, `spp_top_level_groups`:
```
spp_top_level_groups/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ ├── res_partner.py
│ └── group_kind.py
├── views/
│ ├── top_level_group_views.xml
│ ├── group_views.xml
│ └── group_kind_views.xml
├── data/
│ └── group_kind_data.xml
└── security/
└── ir.model.access.csv
```
## Step-by-Step Guide
### Create the Module Scaffold
Create a new directory for your module (e.g., `spp_top_level_groups`) and populate it with the files and folders shown above.
### Define Module Manifest
Create a manifest file with the necessary dependencies and data files:
```python
{
"name": "OpenSPP Top Level Groups",
"summary": "Adds top-level group types like villages and communities to the registry system.",
"category": "OpenSPP",
"version": "17.0.1.0.0",
"author": "Your Organization",
"website": "https://your-website.com",
"license": "LGPL-3",
"depends": [
"base",
"g2p_registry_base",
"g2p_registry_individual",
"g2p_registry_group",
"g2p_registry_membership",
"spp_registry_group_hierarchy",
],
"data": [
"data/group_kind_data.xml",
"views/group_kind_views.xml",
"views/top_level_group_views.xml",
"views/group_views.xml",
"security/ir.model.access.csv",
],
"application": True,
"installable": True,
"auto_install": False,
}
```
### Add Custom Group Kind Data
Create `data/group_kind_data.xml` to define the new group kind:
```xml
VillageA village that contains multiple householdsTrue
```
### Extend the Registry Model
Create `models/res_partner.py` to add custom fields, indicators, and actions:
```python
from odoo import fields, models, api
class G2PTopLevelGroup(models.Model):
_inherit = "res.partner"
village_code = fields.Char("Village Code", help="Official village code or identifier")
population = fields.Integer("Population", help="Total population of the village/community")
total_households = fields.Integer(
"Total Households", compute="_compute_total_households", store=True,
help="Total number of households in this village"
)
total_individuals = fields.Integer(
"Total Individuals", compute="_compute_total_individuals", store=True,
help="Total number of individuals in this village"
)
avg_household_size = fields.Float(
"Average Household Size", compute="_compute_avg_household_size", store=True,
help="Average number of individuals per household"
)
def _compute_total_households(self):
for rec in self:
if not rec.is_group or not rec.kind or rec.kind.name != 'Village':
rec.total_households = 0
continue
household_groups = rec.group_membership_ids.mapped('individual').filtered(
lambda x: x.is_group and x.kind and x.kind.name == 'Household'
)
rec.total_households = len(household_groups)
def _compute_total_individuals(self):
for rec in self:
if not rec.is_group or not rec.kind or rec.kind.name != 'Village':
rec.total_individuals = 0
continue
total = 0
household_groups = rec.group_membership_ids.mapped('individual').filtered(
lambda x: x.is_group and x.kind and x.kind.name == 'Household'
)
for household in household_groups:
total += len(household.group_membership_ids.mapped('individual').filtered(lambda x: not x.is_group))
rec.total_individuals = total
def _compute_avg_household_size(self):
for rec in self:
if rec.total_households > 0:
rec.avg_household_size = rec.total_individuals / rec.total_households
else:
rec.avg_household_size = 0.0
def action_view_households(self):
self.ensure_one()
household_groups = self.group_membership_ids.mapped('individual').filtered(
lambda x: x.is_group and x.kind and x.kind.name == 'Household'
)
return {
'name': f'Households in {self.name}',
'type': 'ir.actions.act_window',
'res_model': 'res.partner',
'view_mode': 'tree,form',
'domain': [('id', 'in', household_groups.ids)],
'context': {'default_is_group': True, 'default_kind': self.env.ref('g2p_registry_group.group_kind_household').id},
}
def action_view_individuals(self):
self.ensure_one()
all_individuals = self.env['res.partner']
household_groups = self.group_membership_ids.mapped('individual').filtered(
lambda x: x.is_group and x.kind and x.kind.name == 'Household'
)
for household in household_groups:
individuals = household.group_membership_ids.mapped('individual').filtered(lambda x: not x.is_group)
all_individuals |= individuals
return {
'name': f'Individuals in {self.name}',
'type': 'ir.actions.act_window',
'res_model': 'res.partner',
'view_mode': 'tree,form',
'domain': [('id', 'in', all_individuals.ids)],
'context': {'default_is_group': False},
}
```
### Extend Group Kind Model
Create `models/group_kind.py` to add custom flags:
```python
from odoo import fields, models
class SPPGroupKind(models.Model):
_inherit = "g2p.group.kind"
is_top_level_group = fields.Boolean("Is Top Level Group", default=False, help="Indicates if this group kind represents a top-level group")
can_contain_households = fields.Boolean("Can Contain Households", default=False, help="Indicates if this group kind can contain household groups")
```
### Create View Extensions
#### Top Level Group Views (`views/top_level_group_views.xml`)
Update all field names to remove the prefix:
```xml
view_villages_formres.partnerview_villages_treeres.partnerview_villages_searchres.partnerVillagesres.partnertree,form[('is_group', '=', True), ('kind.name', '=', 'Village')]{'default_is_group': True, 'default_kind': ref('group_kind_village')}
Create your first village!
Villages can contain multiple households as members.
```
#### Group Kind Views (`views/group_kind_views.xml`)
```xml
view_group_kind_tree_top_levelg2p.group.kind
```
#### Group Views (`views/group_views.xml`)
```xml
view_groups_form_village_infores.partner
```
### Add Security Access
Create `security/ir.model.access.csv`:
```
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_top_level_groups_admin,top.level.groups.admin,base.model_res_partner,g2p_registry_base.group_g2p_admin,1,1,1,1
```
### Install and Test
1. **Install the module** via the Apps menu.
2. **Configure group kinds**: Registry > Configuration > Group Kinds.
Ensure "Village" is present and allows group/individual members.
3. **Create villages**: Registry > Groups > Villages.
Add households as members.
4. **Test**: Use action buttons to view households and individuals.
Check computed indicators.
### Example Use Case
```
Village A (Top Level Group)
├── Household 1 (Regular Group)
│ ├── John Doe (Individual)
│ └── Jane Doe (Individual)
└── Household 2 (Regular Group)
├── Bob Smith (Individual)
└── Alice Smith (Individual)
```
## Best Practices
- Use descriptive names for group kinds.
- Enable hierarchy only for appropriate group kinds.
- Use domains and validation to enforce group relationships.
- Provide clear navigation and action buttons for users.
## References
For more information on extending OpenSPP modules, refer to:
- [Odoo 17 Developer Documentation](https://www.odoo.com/documentation/17.0/developer/)
- Related guides: {doc}`Customizing Fields `, {doc}`Customizing Indicators `