- Added ESET sync functionality to periodically fetch devices and incidents. - Created new ESET service for API interactions, including authentication and data retrieval. - Introduced new database tables for storing ESET incidents and hardware contacts. - Updated hardware assets schema to include ESET-specific fields (UUID, specs, group). - Developed frontend templates for ESET overview, import, and testing. - Enhanced existing hardware creation form to auto-generate AnyDesk links. - Added global logout functionality to clear user session data. - Improved error handling and logging for ESET API interactions.
493 lines
15 KiB
Markdown
493 lines
15 KiB
Markdown
# Implementeringsplan: Sag-modulet (Case Module)
|
|
|
|
## Oversigt - Hvad er "Sag"?
|
|
|
|
**Sag-modulet** er hjertet i BMC Hub's nye relation- og proces-styringssystem. I stedet for at have separate systemer for "tickets", "opgaver" og "ordrer", har vi én universel entitet: **en Sag**.
|
|
|
|
### Kerneidéen (meget vigtig!)
|
|
|
|
> **Der er kun én ting: en Sag. Tickets, opgaver og ordrer er blot sager med forskellige relationer, tags og moduler.**
|
|
|
|
**Eksempler:**
|
|
|
|
1. **Kunde ringer og skal have ny skærm**
|
|
- Dette er en *Sag* (ticket-type med tag: `support`)
|
|
- Den får tag: `urgent` fordi det er ekspres
|
|
|
|
2. **Indkøb af skærm hos leverandør**
|
|
- Dette er også en *Sag* (ordre-type med tag: `indkøb`)
|
|
- Den er *relateret til* den første sag som "afledt_af"
|
|
- Ansvarlig: Indkøbschef
|
|
|
|
3. **Ompakning og afsendelse af skærm**
|
|
- Dette er en *Sag* (opgave-type med tag: `ompakning`)
|
|
- Den er relateret til indkøbssagen som "udførelse_for"
|
|
- Ansvarlig: Lagermedarbejder
|
|
- Deadline: I dag
|
|
|
|
**Alle tre er samme datatype i databasen.** Forskellen er:
|
|
- Hvilke *tags* de har
|
|
- Hvilken *kunde/kontakt* de er knyttet til
|
|
- Hvilke *relationer* de har til andre sager
|
|
- Hvem der er *ansvarlig*
|
|
|
|
### Hvad betyder det for systemet?
|
|
|
|
**Uden Sag-modulet:**
|
|
- Du skal have en "Ticket-sektion" for support
|
|
- Du skal have en "Task-sektion" for opgaver
|
|
- Du skal have en "Order-sektion" for ordrer
|
|
- De snakker ikke sammen naturligt
|
|
- Data-duplikering
|
|
- Kompleks logik
|
|
|
|
**Med Sag-modulet:**
|
|
- Ét API endpoint: `/api/v1/sag`
|
|
- Ét UI-område: "Sager" med intelligente filtre
|
|
- Relationer er førsteklasses borgere (se hvad der hænger sammen)
|
|
- Tags styr flowet (f.eks. "support" + "urgent" = prioriteret)
|
|
- Sager kan "vokse": Start som ticket → bliv til ordre → bliv til installation
|
|
- Alt er søgbart og filterabelt på tværs af domæner
|
|
|
|
---
|
|
|
|
## Teknisk arkitektur
|
|
|
|
### Databasestruktur
|
|
|
|
Sag-modulet bruger tre hovedtabeller (med `sag_` prefix):
|
|
|
|
#### **sag_sager** (Hovedtabel for sager)
|
|
```
|
|
id (primary key)
|
|
titel (VARCHAR) - kort navn på sagen
|
|
beskrivelse (TEXT) - detaljeret beskrivelse
|
|
template_key (VARCHAR) - struktur-template (f.eks. "ticket", "opgave", "ordre") - default NULL
|
|
status (VARCHAR) - "åben" eller "lukket"
|
|
customer_id (foreign key) - hvilken kunde sagen handler om - NULLABLE
|
|
ansvarlig_bruger_id (foreign key) - hvem skal håndtere den
|
|
created_by_user_id (foreign key) - hvem oprettede sagen
|
|
deadline (TIMESTAMP) - hvornår skal det være færdigt
|
|
created_at (TIMESTAMP)
|
|
updated_at (TIMESTAMP)
|
|
deleted_at (TIMESTAMP) - soft-delete: sættes når sagen "slettes"
|
|
```
|
|
|
|
**Soft-delete:** Når du sletter en sag, bliver `deleted_at` sat til nu. Sagen bliver ikke fjernet fra DB. Det betyder:
|
|
- Du kan gendanne data hvis modulet deaktiveres
|
|
- Historien bevares (audit trail)
|
|
- Relations er intakte hvis du genopretter
|
|
|
|
#### **sag_relationer** (Hvordan sager hænger sammen)
|
|
```
|
|
id (primary key)
|
|
kilde_sag_id (foreign key) - hvilken sag relationen STARTER fra (retning: fra denne)
|
|
målsag_id (foreign key) - hvilken sag relationen PEGER PÅ (retning: til denne)
|
|
relationstype (VARCHAR) - f.eks. "parent_of", "child_of", "derived_from", "blocks", "executes_for"
|
|
created_at (TIMESTAMP)
|
|
deleted_at (TIMESTAMP) - soft-delete
|
|
```
|
|
|
|
**Eksempel (retningsbestemt):**
|
|
- Sag 1 (kundesamtale) → Sag 5 (indkøb af skærm)
|
|
- kilde_sag_id: 1, målsag_id: 5
|
|
- relationstype: "derives" eller "parent_of"
|
|
- Betyder: "Sag 1 er forælder/genererer Sag 5"
|
|
|
|
**Note:** Relationer er enrettet. For bidirektionale links oprettes to relations (1→5 og 5→1).
|
|
|
|
#### **sag_tags** (Hvordan vi kategoriserer sager)
|
|
```
|
|
id (primary key)
|
|
sag_id (foreign key) - hvilken sag tagget tilhører
|
|
tag_navn (VARCHAR) - f.eks. "support", "urgent", "vip", "ompakning"
|
|
state (VARCHAR) - "aktiv" eller "inaktiv" - default "aktiv"
|
|
closed_at (TIMESTAMP) - hvornår tagget blev lukket/inaktiveret - NULLABLE
|
|
created_at (TIMESTAMP)
|
|
deleted_at (TIMESTAMP) - soft-delete
|
|
```
|
|
|
|
**Tags bruges til:**
|
|
- Filtrering: "Vis alle sager med tag = support"
|
|
- Workflow: "Sager med tag = urgent skal løses i dag"
|
|
- Kategorisering: "Alle sager med tag = ompakning"
|
|
|
|
### API-endpoints
|
|
|
|
**Sager CRUD:**
|
|
- `GET /api/v1/cases` - Liste alle sager (filter efter tags, status, ansvarlig)
|
|
- `POST /api/v1/cases` - Opret ny sag
|
|
- `GET /api/v1/cases/{id}` - Vis detaljer om en sag
|
|
- `PATCH /api/v1/cases/{id}` - Opdater en sag
|
|
- `DELETE /api/v1/cases/{id}` - Slet en sag (soft-delete, sætter deleted_at)
|
|
|
|
**Relationer:**
|
|
- `GET /api/v1/cases/{id}/relations` - Vis alle relaterede sager
|
|
- `POST /api/v1/cases/{id}/relations` - Tilføj relation til anden sag
|
|
- `DELETE /api/v1/cases/{id}/relations/{relation_id}` - Fjern relation
|
|
|
|
**Tags:**
|
|
- `GET /api/v1/cases/{id}/tags` - Vis alle tags på sagen
|
|
- `POST /api/v1/cases/{id}/tags` - Tilføj tag
|
|
- `DELETE /api/v1/cases/{id}/tags/{tag_id}` - Fjern tag
|
|
|
|
### UI-koncept
|
|
|
|
**Sag-listen** (`/sag`):
|
|
- Alle dine sager på ét sted
|
|
- Filter: "Mine sager", "Åbne sager", "Sager med tag=support", "Sager med tag=urgent"
|
|
- Søgebar
|
|
- Sortering efter deadline, oprettelsestid, status
|
|
|
|
**Sag-listen** (`/cases`):
|
|
**Sag-detaljer** (`/cases/{id}`):
|
|
- Hovedinfo: titel, beskrivelse, status, deadline
|
|
- **Relaterede sager**: Sektioner som:
|
|
- "Forælder-sag" (hvis denne sag er en del af noget større)
|
|
- "Barn-sager" (sager der er afledt af denne)
|
|
- "Blokeret af" (sager der holder denne op)
|
|
- "Udfører for" (hvis denne er udførelsessag for noget)
|
|
- **Tags**: Viste tags, mulighed for at tilføje flere
|
|
- **Ansvarlig**: Hvem skal håndtere det
|
|
- **Historie**: Hvis modulet får aktivitetslog senere
|
|
|
|
**Designet:**
|
|
- Nordic Top minimalistisk design
|
|
- Dark mode support
|
|
- Responsive (mobil-venligt)
|
|
- Intuitivt navigation mellem relaterede sager
|
|
|
|
---
|
|
|
|
## Implementeringsplan - Trin for trin
|
|
|
|
### Fase 1: Modul-struktur (forberedelse)
|
|
|
|
#### Trin 1.1: Opret modul-mappen
|
|
```
|
|
app/modules/sag/
|
|
├── module.json # Modulets metadata
|
|
├── README.md # Dokumentation
|
|
├── backend/
|
|
│ ├── __init__.py
|
|
│ └── router.py # FastAPI endpoints
|
|
├── frontend/
|
|
│ ├── __init__.py
|
|
│ └── views.py # HTML views
|
|
├── templates/
|
|
│ ├── index.html # Sag-liste
|
|
│ └── detail.html # Sag-detaljer
|
|
└── migrations/
|
|
└── 001_init.sql # Database schema
|
|
```
|
|
|
|
#### Trin 1.2: Opret module.json
|
|
```json
|
|
{
|
|
"name": "sag",
|
|
"version": "1.0.0",
|
|
"description": "Universel sag-håndtering - tickets, opgaver og ordrer som sager med relationer",
|
|
"author": "BMC Networks",
|
|
"enabled": true,
|
|
"dependencies": [],
|
|
"table_prefix": "sag_",
|
|
"api_prefix": "/api/v1/cases",
|
|
"tags": ["Sag", "Case Management"],
|
|
"config": {
|
|
"safety_switches": {
|
|
"read_only": false,
|
|
"dry_run": false
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Fase 2: Database-setup
|
|
|
|
#### Trin 2.1: Opret migrations/001_init.sql
|
|
|
|
SQL-migrations definerer tabeller for sager, relationer og tags. Se `migrations/001_init.sql` for detaljer.
|
|
|
|
**Vigtige points:**
|
|
- Alle tabelnavne starter med `sag_`
|
|
- Soft-delete: `deleted_at` kolonne hvor man checker `WHERE deleted_at IS NULL`
|
|
- Foreign keys til `customers` for at linke til kundedata
|
|
- Indexes for performance
|
|
- Triggers til auto-update af `updated_at`
|
|
|
|
**Eksempel-query (queries filtrerer soft-deleted):**
|
|
```sql
|
|
SELECT * FROM sag_sager
|
|
WHERE customer_id = %s
|
|
AND deleted_at IS NULL
|
|
ORDER BY created_at DESC;
|
|
```
|
|
|
|
### Fase 3: Backend-API
|
|
|
|
#### Trin 3.1: Opret backend/router.py
|
|
|
|
Implementer alle 9 API-endpoints med disse mønstre:
|
|
|
|
**GET /cases (list):**
|
|
```python
|
|
@router.get("/cases")
|
|
async def list_sager(
|
|
status: str = None,
|
|
tag: str = None,
|
|
customer_id: int = None,
|
|
ansvarlig_bruger_id: int = None
|
|
):
|
|
# Build query med WHERE deleted_at IS NULL
|
|
# Filter efter parameters
|
|
# Return liste
|
|
```
|
|
|
|
**POST /cases (create):**
|
|
```python
|
|
@router.post("/cases")
|
|
async def create_sag(sag_data: dict):
|
|
# Validér input
|
|
# INSERT INTO sag_sager
|
|
# RETURNING *
|
|
# Return ny sag
|
|
```
|
|
|
|
**GET /cases/{id}:**
|
|
```python
|
|
@router.get("/cases/{id}")
|
|
async def get_sag(id: int):
|
|
# SELECT * FROM sag_sager WHERE id = %s AND deleted_at IS NULL
|
|
# Hvis ikke found: HTTPException(404)
|
|
# Return sag detaljer
|
|
```
|
|
|
|
**PATCH /cases/{id} (update):**
|
|
```python
|
|
@router.patch("/cases/{id}")
|
|
async def update_sag(id: int, updates: dict):
|
|
# UPDATE sag_sager SET ... WHERE id = %s
|
|
# Automatisk updated_at via trigger
|
|
# Return opdateret sag
|
|
```
|
|
|
|
**DELETE /cases/{id} (soft-delete):**
|
|
```python
|
|
@router.delete("/cases/{id}")
|
|
async def delete_sag(id: int):
|
|
# UPDATE sag_sager SET deleted_at = NOW() WHERE id = %s
|
|
# Return success
|
|
```
|
|
|
|
**Relationer endpoints:** Lignende pattern for `/cases/{id}/relations`
|
|
|
|
**Tags endpoints:** Lignende pattern for `/cases/{id}/tags`
|
|
|
|
**Vigtige mønstre:**
|
|
- Altid bruge `execute_query()` fra `app.core.database`
|
|
- Parameteriserede queries (`%s` placeholders)
|
|
- `RealDictCursor` for dict-like row access
|
|
- Filtrer `WHERE deleted_at IS NULL` på alle SELECT queries
|
|
- Eksportér router som `router` (module loader leder efter denne)
|
|
|
|
### Fase 4: Frontend-views
|
|
|
|
#### Trin 4.1: Opret frontend/views.py
|
|
|
|
```python
|
|
from fastapi import APIRouter
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
router = APIRouter()
|
|
templates = Jinja2Templates(directory="app/modules/sag/templates")
|
|
|
|
@router.get("/cases", response_class=HTMLResponse)
|
|
async def cases_liste(request):
|
|
# Hent sager fra API
|
|
return templates.TemplateResponse("index.html", {"request": request, "cases": ...})
|
|
|
|
@router.get("/cases/{id}", response_class=HTMLResponse)
|
|
async def sag_detaljer(request, id: int):
|
|
# Hent sag + relationer + tags
|
|
return templates.TemplateResponse("detail.html", {"request": request, "sag": ..., "relationer": ...})
|
|
```
|
|
|
|
### Fase 5: Frontend-templates
|
|
|
|
#### Trin 5.1: Opret templates/index.html
|
|
|
|
Sag-listen med:
|
|
- Search-bar
|
|
- Filter-knapper (status, tags, ansvarlig)
|
|
- Tabel/kort-view med alle sager
|
|
- Klikkable sager der går til `/sag/{id}`
|
|
- Nordic Top design med dark mode
|
|
|
|
#### Trin 5.2: Opret templates/detail.html
|
|
|
|
Sag-detaljer med:
|
|
- Hovedinfo: titel, beskrivelse, status, deadline, ansvarlig
|
|
- Sektioner: "Relaterede sager", "Tags", "Aktivitet" (hvis implementeret senere)
|
|
- Knap til at redigere sagen
|
|
- Knap til at tilføje relation
|
|
- Knap til at tilføje tag
|
|
- Mulighed for at se og slette relationer/tags
|
|
|
|
### Fase 6: Test og aktivering
|
|
|
|
#### Trin 6.1: Test databasen
|
|
```bash
|
|
docker compose exec db psql -U bmc_admin -d bmc_hub -c "SELECT * FROM sag_sager;"
|
|
```
|
|
|
|
#### Trin 6.2: Test API-endpoints
|
|
```bash
|
|
# Opret sag
|
|
curl -X POST http://localhost:8001/api/v1/cases \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"titel": "Test sag", "customer_id": 1}'
|
|
|
|
# Hent sag
|
|
curl http://localhost:8001/api/v1/cases/1
|
|
|
|
# Hent sag-liste
|
|
curl http://localhost:8001/api/v1/cases
|
|
```
|
|
|
|
#### Trin 6.3: Test frontend
|
|
- Besøg http://localhost:8001/cases
|
|
- Se sag-liste
|
|
- Klik på sag → se detaljer
|
|
- Tilføj tag, relation
|
|
|
|
#### Trin 6.4: Test soft-delete
|
|
- Slet sag via `DELETE /cases/{id}`
|
|
- Check databasen: `deleted_at` skal være sat
|
|
- Verify den ikke vises i list-endpoints mere
|
|
|
|
#### Trin 6.5: Test modul-deaktivering
|
|
- Rediger `module.json`: sæt `"enabled": false`
|
|
- Restart Docker: `docker compose restart api`
|
|
- Besøg http://localhost:8001/cases → 404
|
|
- Besøg http://localhost:8001/api/v1/cases → 404
|
|
- Revert: `"enabled": true`, restart, verifiér det virker igen
|
|
|
|
### Fase 7: Dokumentation
|
|
|
|
#### Trin 7.1: Opret README.md i modulet
|
|
Dokumenter:
|
|
- Hvad modulet gør
|
|
- API-endpoints med eksempler
|
|
- Database-schema
|
|
- Hvordan man bruger relationer og tags
|
|
- Eksempel-workflows
|
|
|
|
---
|
|
|
|
## Vigtige principper under implementeringen
|
|
|
|
### 1. **Soft-delete først**
|
|
Alle `DELETE` operationer sætter `deleted_at` til `NOW()` i stedet for at slette fysisk. Det betyder:
|
|
- Data bevares hvis modulet deaktiveres
|
|
- Audit trail bevares
|
|
- Relationer forbliver intakte
|
|
|
|
### 2. **Always filter deleted_at**
|
|
Alle SELECT queries skal have:
|
|
```sql
|
|
WHERE deleted_at IS NULL
|
|
```
|
|
|
|
Undtagelse: Admin-sider der skal se "deleted history" (implementeres senere).
|
|
|
|
### 3. **Foreign keys til customers**
|
|
Alle sager skal være knyttet til en `customer_id`. Det gør det muligt at:
|
|
- Lave customer-specifikke views senere
|
|
- Sikre data-isolation
|
|
- Tracke hvem sagerne handler om
|
|
|
|
### 4. **Relationer er data**
|
|
Relationer er ikke blot links - de er egne database-records med type og soft-delete. Det betyder:
|
|
- Du kan se hele historien af relationer
|
|
- Du kan "gendanne" relationer hvis de slettes
|
|
- Relationstyper er konfigurerbare
|
|
|
|
### 5. **Tags driver visibility**
|
|
Tags bruges til:
|
|
- UI-filtre: "Vis kun sager med tag=urgent"
|
|
- Workflow: "Sager med tag=support skal have SLA"
|
|
- Kategorisering: "Alt med tag=ompakning"
|
|
|
|
---
|
|
|
|
## Hvad efter?
|
|
|
|
Når Sag-modulet er live, kan du:
|
|
|
|
1. **Konvertere tickets til sager** - Migrationsscript der tager gamle tickets og laver dem til sager
|
|
2. **Konvertere opgaver til sager** - Samme pattern
|
|
3. **Tilføje aktivitetslog** - "Hvem ændrede hvad hvornår" på hver sag
|
|
4. **Integrere med e-conomic** - Når en sag får tag=faktura, oprettes den som ordre i e-conomic
|
|
5. **Tilføje workflowkonfiguration** - "Hvis status=i_gang og tag=urgent, send reminder hver dag"
|
|
6. **Tilføje dependencies** - "Sag B kan ikke starte før Sag A er done"
|
|
7. **Tilføje SLA-tracking** - "Support-sager skal løses inden 24 timer"
|
|
|
|
Men først: **Få grundlaget på plads med denne modul-implementering.**
|
|
|
|
---
|
|
|
|
## Kommandoer til at komme i gang
|
|
|
|
```bash
|
|
# Gå til workspace
|
|
cd /Users/christianthomas/DEV/bmc_hub_dev
|
|
|
|
# Se hvor vi er
|
|
docker compose ps -a
|
|
|
|
# Start dev-miljø hvis det ikke kører
|
|
docker compose up -d
|
|
|
|
# Se logs
|
|
docker compose logs -f api
|
|
|
|
# Efter at have lavet koden: restart API
|
|
docker compose restart api
|
|
|
|
# Test at modulet loadet
|
|
docker compose logs api | grep -i "sag"
|
|
|
|
# Manuelt test af database-migration
|
|
docker compose exec db psql -U bmc_admin -d bmc_hub -c "\dt sag_*"
|
|
```
|
|
|
|
---
|
|
|
|
## Tidsestimation
|
|
|
|
- **Fase 1-2 (modul + database)**: 30 min
|
|
- **Fase 3 (backend API)**: 1-2 timer
|
|
- **Fase 4-5 (frontend)**: 1-2 timer
|
|
- **Fase 6 (test)**: 30 min
|
|
- **Fase 7 (dokumentation)**: 30 min
|
|
|
|
**Total: 4-6 timer**
|
|
|
|
---
|
|
|
|
## TL;DR - for implementer
|
|
|
|
1. Opret `app/modules/sag/` med standard-struktur
|
|
2. Opret `module.json` med `"enabled": true`
|
|
3. Opret `migrations/001_init.sql` med 3 tabeller (`sag_sager`, `sag_relationer`, `sag_tags`)
|
|
4. Implementer 9 API-endpoints i `backend/router.py` (alle queries filtrerer `deleted_at IS NULL`)
|
|
5. Implementer 2 HTML-views i `frontend/views.py` (liste + detaljer)
|
|
6. Opret 2 templates i `templates/` (index.html + detail.html)
|
|
7. Test endpoints og UI
|
|
8. Verifiér soft-delete virker
|
|
9. Verifiér modulet kan deaktiveres og data bevares
|
|
10. Skrive README.md
|
|
|
|
**Modulet bliver automatisk loadet af system - ingen manual registration nødvendig.**
|