# BMC Hub Module System ## Oversigt BMC Hub har nu et dynamisk modul-system der tillader isoleret udvikling af features uden at påvirke core systemet. Moduler kan udvikles, testes og deployes uafhængigt. ## Arkitektur ### Core vs Modules **Core System** (forbliver i `app/`): - `auth/` - Authentication - `customers/` - Customer management - `hardware/` - Hardware tracking - `billing/` - Billing integration - `contacts/` - Contact management - `vendors/` - Vendor management - `settings/` - Settings - `system/` - System utilities - `dashboard/` - Dashboard - `devportal/` - Developer portal - `timetracking/` - Time tracking - `emails/` - Email system **Dynamic Modules** (nye features i `app/modules/`): - Isolerede i egen directory - Dynamisk loaded ved startup - Hot-reload support (restart påkrævet) - Egen database namespace (table prefix) - Egen konfiguration (miljøvariable) - Egne migrations ### File Struktur ``` app/modules/ ├── _template/ # Template for nye moduler (IKKE loaded) │ ├── module.json # Metadata og config │ ├── README.md │ ├── backend/ │ │ ├── __init__.py │ │ └── router.py # FastAPI endpoints │ ├── frontend/ │ │ ├── __init__.py │ │ └── views.py # HTML views │ ├── templates/ │ │ └── index.html # Jinja2 templates │ └── migrations/ │ └── 001_init.sql # Database migrations │ └── my_module/ # Eksempel modul ├── module.json ├── ... ``` ## Opret Nyt Modul ### Via CLI Tool ```bash python scripts/create_module.py my_feature "My awesome feature" ``` Dette opretter: - Komplet modul struktur - Opdateret `module.json` med modul navn - Placeholder kode i router og views - Database migration template ### Manuel Oprettelse 1. **Kopiér template:** ```bash cp -r app/modules/_template app/modules/my_module ``` 2. **Rediger `module.json`:** ```json { "name": "my_module", "version": "1.0.0", "description": "Min feature beskrivelse", "author": "Dit navn", "enabled": false, "dependencies": [], "table_prefix": "mymod_", "api_prefix": "/api/v1/mymod", "tags": ["My Module"] } ``` 3. **Opdater kode:** - `backend/router.py` - Implementer dine endpoints - `frontend/views.py` - Implementer HTML views - `templates/index.html` - Design UI - `migrations/001_init.sql` - Opret database tabeller ## Database Isolering ### Customer Linking Pattern **VIGTIG ARKITEKTUR BESLUTNING:** Core `customers` tabel er **single source of truth** for økonomi, fakturering og CVR. #### Hvornår skal moduler have egen kunde-tabel? **✅ Opret modul-specifik kunde-tabel hvis:** - Modulet syncer fra eksternt system (vTiger, CRM, etc.) - Mange module-specifikke felter nødvendige (external_id, sync_status, etc.) - Custom lifecycle/workflow - **Eksempel:** `tmodule_customers` (sync fra vTiger) **🚫 Brug direkte `customers.id` foreign key hvis:** - Simple relationer uden external sync - Få/ingen custom felter - Standard workflow - **Eksempel:** `ticket_system` → direkte FK til `customers.id` #### Hvis modul-kunde-tabel oprettes: **SKAL altid have:** ```sql hub_customer_id INTEGER REFERENCES customers(id) -- Link til core ``` **SKAL have auto-link trigger:** ```sql CREATE OR REPLACE FUNCTION auto_link_{module}_customer() RETURNS TRIGGER AS $$ DECLARE matched_hub_id INTEGER; BEGIN IF NEW.hub_customer_id IS NOT NULL THEN RETURN NEW; END IF; SELECT id INTO matched_hub_id FROM customers WHERE LOWER(TRIM(name)) = LOWER(TRIM(NEW.name)) LIMIT 1; IF matched_hub_id IS NOT NULL THEN NEW.hub_customer_id := matched_hub_id; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_auto_link_{module}_customer BEFORE INSERT OR UPDATE OF name ON {module}_customers FOR EACH ROW EXECUTE FUNCTION auto_link_{module}_customer(); ``` **Hvorfor dette pattern?** - ✅ E-conomic export virker automatisk (via hub_customer_id → customers.economic_customer_number) - ✅ Billing integration automatisk - ✅ Ingen manuel linking nødvendig - ✅ Nye sync'ede kunder linkes automatisk Se `migrations/028_auto_link_tmodule_customers.sql` for live eksempel. ### Table Prefix Pattern Alle tabeller SKAL bruge prefix fra `module.json`: ```sql -- BAD: Risiko for kollision CREATE TABLE customers (...); -- GOOD: Isoleret med prefix CREATE TABLE mymod_customers (...); ``` ### Database Queries Brug ALTID helper functions: ```python from app.core.database import execute_query, execute_insert # Hent data customers = execute_query( "SELECT * FROM mymod_customers WHERE active = %s", (True,) ) # Insert med auto-returned ID customer_id = execute_insert( "INSERT INTO mymod_customers (name) VALUES (%s)", ("Test",) ) ``` ## Konfiguration ### Environment Variables Modul-specifik config følger pattern: `MODULES__{MODULE_NAME}__{KEY}` **Eksempel `.env`:** ```bash # Global module system MODULES_ENABLED=true MODULES_AUTO_RELOAD=true # Specific module config MODULES__MY_MODULE__API_KEY=secret123 MODULES__MY_MODULE__READ_ONLY=false MODULES__MY_MODULE__DRY_RUN=false ``` ### I Kode ```python from app.core.config import get_module_config # Hent config med fallback api_key = get_module_config("my_module", "API_KEY") read_only = get_module_config("my_module", "READ_ONLY", "false") == "true" ``` ## Safety Switches ### Best Practice: Safety First Alle moduler BØR have safety switches: ```python # I backend/router.py read_only = get_module_config("my_module", "READ_ONLY", "true") == "true" dry_run = get_module_config("my_module", "DRY_RUN", "true") == "true" if read_only: return {"success": False, "message": "READ_ONLY mode"} if dry_run: logger.info("🧪 DRY_RUN: Would perform action") return {"success": True, "dry_run": True} ``` **Anbefalet defaults:** - `READ_ONLY=true` - Bloker alle writes indtil eksplicit enabled - `DRY_RUN=true` - Log actions uden at udføre ## Enable/Disable Moduler ### Via API ```bash # Enable curl -X POST http://localhost:8000/api/v1/modules/my_module/enable # Disable curl -X POST http://localhost:8000/api/v1/modules/my_module/disable # List alle curl http://localhost:8000/api/v1/modules ``` ### Manuelt Rediger `app/modules/my_module/module.json`: ```json { "enabled": true } ``` **Vigtigt:** Kræver app restart! ```bash docker-compose restart api ``` ## Migrations ### Kør Migration ```bash # Via psql psql -U bmc_hub -d bmc_hub -f app/modules/my_module/migrations/001_init.sql # Via Python python apply_migration.py my_module 001_init.sql ``` ### Migration Best Practices 1. **Nummering:** Sekventiel (001, 002, 003...) 2. **Idempotent:** Brug `CREATE TABLE IF NOT EXISTS` 3. **Table prefix:** Alle tabeller med prefix 4. **Rollback:** Inkluder rollback instructions i kommentar **Eksempel:** ```sql -- Migration: 001_init.sql -- Rollback: DROP TABLE mymod_items; CREATE TABLE IF NOT EXISTS mymod_items ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ## Development Workflow ### 1. Opret Modul ```bash python scripts/create_module.py invoice_parser "Parse invoice PDFs" ``` ### 2. Implementer Features Rediger: - `backend/router.py` - API endpoints - `frontend/views.py` - HTML pages - `templates/*.html` - UI components ### 3. Kør Migration ```bash psql -U bmc_hub -d bmc_hub -f app/modules/invoice_parser/migrations/001_init.sql ``` ### 4. Enable Modul ```json // module.json { "enabled": true } ``` ### 5. Restart & Test ```bash docker-compose restart api # Test API curl http://localhost:8000/api/v1/invoice_parser/health # Test UI open http://localhost:8000/invoice_parser ``` ## Hot Reload Systemet understøtter hot-reload i development mode: ```bash # I docker-compose.yml environment: - ENABLE_RELOAD=true # Source code mounted volumes: - ./app:/app/app:ro ``` **Når skal jeg genstarte?** - ✅ **IKKE nødvendigt:** Python kode ændringer i eksisterende filer - ❌ **Restart påkrævet:** Enable/disable modul, nye filer, module.json ændringer ## Fejlhåndtering ### Startup Errors Hvis et modul fejler under loading: - Core systemet fortsætter - Modulet bliver ikke loaded - Fejl logges til console + `logs/app.log` **Log output:** ``` 📦 Fundet modul: my_module v1.0.0 (enabled=true) ❌ Kunne ikke loade modul my_module: ModuleNotFoundError ✅ Loaded 2 modules: ['other_module', 'third_module'] ``` ### Runtime Errors Endpoints i moduler er isolerede: - Exception i ét modul påvirker ikke andre - FastAPI returner 500 med error message - Logger fejl med module context ## API Documentation Alle modul endpoints vises automatisk i FastAPI docs: ``` http://localhost:8000/api/docs ``` Endpoints grupperes under modul tags fra `module.json`. ## Testing ### Unit Tests ```python # tests/modules/test_my_module.py import pytest from app.core.database import execute_query def test_my_module_item_creation(): result = execute_query( "SELECT * FROM mymod_items WHERE name = %s", ("Test",), fetchone=True ) assert result is not None ``` ### Integration Tests Brug samme test database som core: ```python @pytest.fixture def test_db(): # Setup test data yield # Cleanup ``` ## Eksempel Modul Se `app/modules/_template/` for komplet working example. **Key files:** - `backend/router.py` - CRUD endpoints med safety switches - `frontend/views.py` - HTML view med Jinja2 - `migrations/001_init.sql` - Table creation med prefix - `module.json` - Metadata og config ## Troubleshooting ### Modul loader ikke 1. **Check `enabled` flag:** ```bash cat app/modules/my_module/module.json | grep enabled ``` 2. **Check logs:** ```bash docker-compose logs -f api | grep my_module ``` 3. **Verify dependencies:** Hvis modul har dependencies i `module.json`, check at de er loaded først. ### Database fejl 1. **Check table prefix:** ```sql SELECT tablename FROM pg_tables WHERE tablename LIKE 'mymod_%'; ``` 2. **Verify migration:** ```bash psql -U bmc_hub -d bmc_hub -c "\d mymod_items" ``` ### Import fejl Sørg for at `__init__.py` findes i alle directories: ```bash touch app/modules/my_module/backend/__init__.py touch app/modules/my_module/frontend/__init__.py ``` ## Best Practices ### ✅ DO - Brug table prefix konsistent - Implementer safety switches (READ_ONLY, DRY_RUN) - Log alle actions med emoji prefix - Brug `execute_query()` helpers - Dokumenter API endpoints i docstrings - Version moduler semantisk (1.0.0 → 1.1.0) - Test isoleret før enable ### ❌ DON'T - Tilgå andre modulers tabeller direkte - Hardcode credentials i kode - Skip migration versioning - Glem table prefix - Disable safety switches uden grund - Commit `.env` files ## Migration Path ### Eksisterende Features → Moduler? **Beslutning:** Core features forbliver i `app/` structure. **Rationale:** - Proven patterns - Stabil foundation - Ingen gevinst ved migration - Risk af breakage **Nye features:** Brug modul-system fra dag 1. ## Fremtidig Udvidelse ### Potentielle Features 1. **Hot reload uden restart** - inotify watching af module.json 2. **Module marketplace** - External repository 3. **Dependency resolution** - Automatisk enable dependencies 4. **Version constraints** - Min/max BMC Hub version 5. **Module API versioning** - Breaking changes support 6. **Rollback support** - Automatic migration rollback 7. **Module metrics** - Usage tracking per module 8. **Module permissions** - RBAC per module ## Support **Issues:** Se logs i `logs/app.log` **Documentation:** Se `app/modules/_template/README.md` **Examples:** Se template implementation --- **Status:** ✅ Klar til brug (13. december 2025)