agdar/TENANT_SETTINGS_IMPLEMENTATION.md
2025-11-02 14:35:35 +03:00

391 lines
10 KiB
Markdown

# 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