# Tenant Settings Templates Implementation ## Overview This document describes the implementation of the tenant settings templates system, which allows administrators to configure tenant-specific settings through a structured, validated interface. ## Architecture ### Models #### 1. SettingTemplate Defines the structure and validation rules for available settings. **Key Fields:** - `key`: Unique identifier (e.g., 'basic_clinic_name_en') - `category`: Organizes settings into logical groups - `data_type`: Defines the type of value (STRING, INTEGER, BOOLEAN, CHOICE, FILE, ENCRYPTED, etc.) - `is_required`: Whether the setting must be provided - `validation_regex`: Optional regex pattern for validation - `choices`: For CHOICE type, defines available options - `order`: Display order within category **Categories:** - BASIC: Basic clinic information (name, logo, colors) - VAT: VAT registration details - ADDRESS: Physical address information - ZATCA: ZATCA e-invoicing configuration - NPHIES: NPHIES integration settings - SMS: SMS/WhatsApp integration - LAB: Laboratory integration - RADIOLOGY: Radiology integration #### 2. TenantSetting Stores actual setting values for each tenant. **Key Fields:** - `tenant`: Foreign key to Tenant - `template`: Foreign key to SettingTemplate - `value`: Text field for most data types - `encrypted_value`: Binary field for sensitive data - `file_value`: File field for uploads - `updated_by`: Tracks who made the change ### Service Layer #### TenantSettingsService Located in `core/settings_service.py`, provides: **Key Methods:** - `get_setting(key, default)`: Retrieve a setting with type conversion - `set_setting(key, value, user)`: Save a setting with validation - `get_category_settings(category)`: Get all settings in a category - `validate_required_settings()`: Check if all required settings are set - `get_missing_required_settings()`: List missing required settings - `export_settings()`: Export settings as JSON - `import_settings(data, user)`: Import settings from JSON **Features:** - Type-safe value retrieval - Automatic type conversion - Validation against template rules - Encryption/decryption for sensitive data - Caching for performance (5-minute TTL) - Audit trail via updated_by field ## Admin Interface ### Enhanced TenantAdmin The Tenant admin interface now includes: 1. **Settings Status Indicator**: Shows completion status in list view 2. **Inline Settings Editor**: Edit settings directly on tenant page 3. **Visual Indicators**: Color-coded required vs optional fields 4. **Help Text**: Inline guidance for each setting 5. **Settings Status Detail**: Detailed view of missing required settings ### SettingTemplateAdmin Manage setting templates: - Create/edit setting definitions - Set validation rules - Define choices for dropdown fields - Control display order ### TenantSettingAdmin View and manage individual setting values: - Filter by category and tenant - Search by setting name or value - View audit history - Masked display for encrypted values ## Usage ### For Administrators #### Configuring Tenant Settings 1. Navigate to Django Admin → Core → Tenants 2. Select a tenant to edit 3. Scroll to the "Tenant Settings" inline section 4. Add/edit settings as needed 5. Required settings are marked with visual indicators 6. Save the tenant #### Checking Settings Status The tenant list view shows a "Settings Status" column: - ✓ Complete: All required settings configured - ✗ X Missing: Number of missing required settings ### For Developers #### Retrieving Settings ```python from core.settings_service import get_tenant_settings_service # Get service for a tenant service = get_tenant_settings_service(tenant) # Get a single setting clinic_name = service.get_setting('basic_clinic_name_en') vat_number = service.get_setting('vat_registration_number') # Get with default value logo = service.get_setting('basic_logo', default=None) # Get all settings in a category basic_settings = service.get_category_settings('BASIC') zatca_settings = service.get_category_settings('ZATCA') # Get all settings all_settings = service.get_all_settings() ``` #### Setting Values ```python from core.settings_service import get_tenant_settings_service service = get_tenant_settings_service(tenant) # Set a setting service.set_setting('basic_clinic_name_en', 'Agdar Centre', user=request.user) # Set encrypted value service.set_setting('zatca_otp', 'secret123', user=request.user) # Set boolean service.set_setting('sms_enable_notifications', True, user=request.user) ``` #### Validation ```python # Check if all required settings are configured is_valid = service.validate_required_settings() # Get list of missing required settings missing = service.get_missing_required_settings() for template in missing: print(f"Missing: {template.label_en} ({template.get_category_display()})") ``` #### Export/Import ```python # Export settings settings_dict = service.export_settings() # Import settings count = service.import_settings(settings_dict, user=request.user) print(f"Imported {count} settings") ``` ## Setting Templates ### Current Templates (40 total) #### Basic Information (6) - Clinic Name (English) - Required - Clinic Name (Arabic) - Clinic Code - Required - Clinic Logo - Primary Brand Color - Secondary Brand Color #### VAT Registration (3) - VAT Registration Number - Required - Tax ID - Commercial Registration Number #### Address Information (6) - Street Address - Required - Building Number - City - Required - Postal Code - Required - Country Code - Required - Additional Number #### ZATCA E-Invoicing (7) - ZATCA Environment - Required - ZATCA OTP - Required - CSID (auto-generated) - Certificate (auto-generated) - Private Key (auto-generated) - Device Name - Solution Name #### NPHIES Integration (5) - NPHIES Environment - Required - Client ID - Required - Client Secret - Required (encrypted) - Organization License Number - Required - Provider License Number #### SMS/WhatsApp Integration (7) - SMS Provider - Required - Account SID - Required - Auth Token - Required (encrypted) - SMS Phone Number - Required - WhatsApp Number - Enable SMS Notifications - Enable WhatsApp Notifications #### Lab Integration (3) - Lab API URL - Lab API Key (encrypted) - Enable Lab Integration #### Radiology Integration (3) - Radiology API URL - Radiology API Key (encrypted) - Enable Radiology Integration ## Management Commands ### populate_setting_templates Populates or updates setting templates with default definitions. ```bash python manage.py populate_setting_templates ``` This command: - Creates new templates if they don't exist - Updates existing templates with new definitions - Is idempotent (safe to run multiple times) ## Security ### Encrypted Fields Sensitive data (API keys, tokens, passwords) are stored encrypted: - Uses Fernet symmetric encryption - Encryption key derived from Django SECRET_KEY - Encrypted values stored in `encrypted_value` binary field - Displayed as "***ENCRYPTED***" in admin interface ### Audit Trail All setting changes are tracked: - `updated_by`: User who made the change - `updated_at`: Timestamp of change - Historical records via django-simple-history ## Performance ### Caching Settings are cached for 5 minutes to reduce database queries: - Cache key format: `tenant_setting:{tenant_id}:{setting_key}` - Automatic cache invalidation on update - Manual cache clearing: `service.clear_cache()` ### Database Indexes Optimized queries with indexes on: - `tenant` + `template` (unique together) - `template.category` + `template.order` - `template.key` ## Migration Path ### From Legacy JSON Settings The old `Tenant.settings` JSONField is preserved for backward compatibility: - Marked as "Legacy Settings (Deprecated)" in admin - Collapsed by default - Can be migrated to new system using import/export ### Migration Steps 1. Export existing settings from JSON field 2. Map to new setting keys 3. Import using `service.import_settings()` 4. Verify all settings migrated correctly 5. Remove old JSON field in future migration ## Extending the System ### Adding New Settings 1. Add template definition to `populate_setting_templates.py` 2. Run `python manage.py populate_setting_templates` 3. New setting appears in admin interface ### Adding New Categories 1. Add category to `SettingTemplate.Category` choices in `core/models.py` 2. Create migration: `python manage.py makemigrations` 3. Run migration: `python manage.py migrate` 4. Add templates for new category ### Custom Validation Add custom validation in `TenantSettingsService._validate_value()`: ```python elif template.data_type == SettingTemplate.DataType.CUSTOM: # Add custom validation logic if not custom_validator(value): raise ValidationError("Custom validation failed") ``` ## Testing ### Unit Tests Test the service layer: ```python from django.test import TestCase from core.models import Tenant, SettingTemplate from core.settings_service import get_tenant_settings_service class TenantSettingsServiceTest(TestCase): def setUp(self): self.tenant = Tenant.objects.create(name="Test Clinic", code="TEST") self.service = get_tenant_settings_service(self.tenant) def test_get_setting(self): value = self.service.get_setting('basic_clinic_name_en', 'Default') self.assertEqual(value, 'Default') def test_set_setting(self): self.service.set_setting('basic_clinic_name_en', 'Test Clinic') value = self.service.get_setting('basic_clinic_name_en') self.assertEqual(value, 'Test Clinic') ``` ## Troubleshooting ### Common Issues **Issue**: Settings not appearing in admin - **Solution**: Run `python manage.py populate_setting_templates` **Issue**: Validation errors when saving - **Solution**: Check validation_regex in template definition **Issue**: Encrypted values not decrypting - **Solution**: Ensure SECRET_KEY hasn't changed **Issue**: Cache not updating - **Solution**: Call `service.clear_cache()` or wait 5 minutes ## Future Enhancements Potential improvements: 1. Settings versioning and rollback 2. Settings templates per tenant type 3. Conditional settings (show/hide based on other settings) 4. Settings import/export via admin UI 5. Settings validation dashboard 6. Multi-language support for all help text 7. Settings change notifications 8. Bulk settings update across tenants ## Support For issues or questions: 1. Check this documentation 2. Review code comments in `core/models.py` and `core/settings_service.py` 3. Check Django admin for validation errors 4. Review audit logs for setting changes