--- myst: html_meta: "title": "Change Request Module Development" "description": "Complete guide to creating custom Change Request modules in OpenSPP with validation workflows, UI creation, and data update logic." "keywords": "OpenSPP, change request, module development, custom CR, validation workflow, Odoo development" --- # Change Request The OpenSPP platform provides a flexible and extensible Change Request (CR) framework. This allows developers to create custom modules for specific data modification scenarios beyond the standard ones. This guide will walk you through the process of creating a new Change Request Type module, using the {doc}`spp_change_request_add_children_demo ` module as a practical reference. By the end of this guide, you will be able to: - Structure a new Change Request module. - Define a custom model to hold CR-specific data. - Implement validation and data update logic. - Create the necessary user interface (views and menus). - Configure validation workflows and security. ## Prerequisites - Solid understanding of Odoo 17 module development, including Python, XML, and XPath. - Familiarity with the OpenSPP core modules, especially `OpenSPP Change Request` ({doc}`spp_change_request `). - To set up OpenSPP for development, please refer to the {doc}`Development Setup Guide <../setup>`. ## Module Structure A typical Change Request Type module follows the standard Odoo module structure. Here's the structure of our reference module, {doc}`spp_change_request_add_children_demo `: ```text spp_change_request_add_children_demo/ ├── __init__.py ├── __manifest__.py ├── data/ │ ├── spp_change_request_validation_sequence_data.xml │ └── spp_dms_category_data.xml ├── models/ │ ├── __init__.py │ └── change_request_add_children.py ├── security/ │ └── ir.model.access.csv ├── tests/ │ ├── __init__.py │ └── test_create_cr.py └── views/ ├── change_request_add_children_views.xml └── menus.xml ``` --- ## Step-by-Step Guide ### Create the Module Scaffold Start by creating a new directory for your module (e.g., `spp_change_request_custom_type`) and populate it with the basic Odoo module files (`__init__.py`, `__manifest__.py`) and the directory structure shown above. ### Define the Manifest (`__manifest__.py`) The manifest file declares your module's metadata and dependencies. It's crucial to list all the modules your CR type will interact with. For our "Add Child/Member" example, the dependencies are: ```python # __manifest__.py { "name": "OpenSPP Change Request Demo: Add Child/Member", "summary": "This module is a demo for creating a new change request type for adding children/members to a group.", "version": "17.0.1.0.0", "category": "OpenSPP/Change Request", "author": "OpenSPP", "website": "https://openspp.org", "license": "LGPL-3", "depends": [ "spp_change_request", "g2p_registry_group", "g2p_registry_individual", "g2p_registry_membership", "spp_service_points", "phone_validation", ], "data": [ "security/ir.model.access.csv", "data/spp_dms_category_data.xml", "data/spp_change_request_validation_sequence_data.xml", "views/change_request_add_children_views.xml", "views/menus.xml", ], "installable": True, "auto_install": False, } ``` Your dependencies will vary based on the data you need to access and modify. ### Create the Change Request Models This is the heart of your module. You'll create a new model that holds the specific data for your change request type. 1. **Create the model file**: In your `models/` directory, create a Python file (e.g., `models/change_request_custom.py`). 2. **Define the model**: - The model name (`_name`) should be descriptive, like `spp.change.request.add.children`. - Inherit from `spp.change.request.source.mixin` and `spp.change.request.validation.sequence.mixin`. These mixins provide the core logic for state management, validation, and applying changes. - Define the fields required to capture the information for the change. ```python # From: spp_change_request_add_children_demo/models/change_request_add_children.py from odoo import _, api, fields, models from odoo.exceptions import ValidationError class ChangeRequestAddChildren(models.Model): _name = "spp.change.request.add.children" _inherit = [ "spp.change.request.source.mixin", "spp.change.request.validation.sequence.mixin", ] _description = "Add Child/Member Change Request Type" # Constants for validation form and required documents VALIDATION_FORM = "spp_change_request_add_children_demo.view_change_request_add_children_validation_form" REQUIRED_DOCUMENT_TYPE = [ "spp_change_request_add_children_demo.spp_dms_add_children", ] registrant_id = fields.Many2one( "res.partner", "Add to Group", domain=[("is_registrant", "=", True), ("is_group", "=", True)], ) # Fields specific to this CR type family_name = fields.Char() given_name = fields.Char() birthdate = fields.Date("Date of Birth") gender = fields.Many2one("gender.type") # ... other fields ``` 3. **Implement `validate_data()`**: Override this method to add custom validation logic. This method is called before the request is submitted for approval. Raise a `ValidationError` if the data is invalid. ```python # From: spp_change_request_add_children_demo/models/change_request_add_children.py def validate_data(self): super().validate_data() # Checks for required documents error_message = [] if not self.family_name: error_message.append(_("The Family Name is required!")) if not self.given_name: error_message.append(_("The First Name is required!")) # ... more checks if error_message: raise ValidationError("\n".join(error_message)) return ``` 4. **Implement `update_live_data()`**: Override this method to define how the system's data should be modified once the CR is approved and applied. This is where you create or update records. ```python # From: spp_change_request_add_children_demo/models/change_request_add_children.py def update_live_data(self): self.ensure_one() # Create a new individual (res.partner) individual_id = self.env["res.partner"].create( { "is_registrant": True, "is_group": False, "name": self.full_name, "family_name": self.family_name, "given_name": self.given_name, # ... other fields from the CR form } ) # Add the new individual to the group self.env["g2p.group.membership"].create( { "group": self.registrant_id.id, "individual": individual_id.id, # ... other membership details } ) ``` 5. **Register the New Request Type**: You need to make the system aware of your new CR type. Inherit the base `spp.change.request` model and extend the `_selection_request_type_ref_id` method. This adds your new type to the dropdown list when creating a new CR. ```python # From: spp_change_request_add_children_demo/models/change_request_add_children.py class ChangeRequestTypeCustomAddChildren(models.Model): _inherit = "spp.change.request" # If the CR type applies only to a specific registrant type (e.g., Group), # you can override the domain. registrant_id = fields.Many2one( "res.partner", "Registrant", domain=[("is_registrant", "=", True), ("is_group", "=", True)], ) @api.model def _selection_request_type_ref_id(self): selection = super()._selection_request_type_ref_id() new_request_type = ("spp.change.request.add.children", "Add Child/Member") if new_request_type not in selection: selection.append(new_request_type) return selection ``` ### Create the User Interface (Views) Define the form view for your custom CR type. 1. **Create the view file**: In your `views/` directory, create an XML file (e.g., `views/change_request_custom_views.xml`). 2. **Define the form view**: - The view record's `model` should be your new CR model (`spp.change.request.add.children`). - Structure the form with a `header` for status and buttons, and a `sheet` for the data fields. - Use Odoo's XML view syntax to lay out your fields. ```xml spp.change.request.add.children.form spp.change.request.add.children
``` 3. **Create Menu Items**: Add a menu item so users can create new instances of your CR type. ```xml Add Child/Member spp.change.request tree,form {'default_request_type': 'spp.change.request.add.children'} [('request_type', '=', 'spp.change.request.add.children')] ``` ### Define Data Files Use XML data files to configure aspects of your CR type. - **Validation Sequence**: In `data/spp_change_request_validation_sequence_data.xml`, define the approval steps. Each record links your `request_type`, a validation `stage_id`, and the `validation_group_id` (a `res.groups` record) responsible for that stage. ```xml 1 spp.change.request.add.children 2 spp.change.request.add.children ``` - **DMS Categories**: In `data/spp_dms_category_data.xml`, define any specific document categories required for your CR. ```xml Add Children Request Form ``` ### Set Up Security Grant users access to your new model in security/ir.model.access.csv. At a minimum, you need to provide access to the base user group. ```csv # From: spp_change_request_add_children_demo/security/ir.model.access.csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_spp_change_request_add_children_user,spp.change.request.add.children.user,model_spp_change_request_add_children,base.group_user,1,1,1,1 ``` ### Install and Test 1. Install or upgrade the module through the Apps menu. 2. Create a new change request. 3. Check if the new change request type is in the selection field. ## Best Practices Always start with a clear definition of the data you need to collect and the system changes that need to occur upon approval. ## References For more information on extending OpenSPP modules, refer to: - [Odoo 17 Developer Documentation](https://www.odoo.com/documentation/17.0/developer/) - [Change Request Module Source](https://github.com/OpenSPP/openspp-modules/tree/17.0/spp_change_request) - [OpenSPP Change Request Demo: Add Child/Member Module Source](https://github.com/OpenSPP/openspp-modules/tree/17.0/spp_change_request_add_children_demo)