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

10 KiB

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

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

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

# 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

# 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.

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():

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:

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