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