bmc_hub/SAG_MODULE_PLAN.md
Christian 25168108d6 feat(sag): Initialize case management module with CRUD operations, relations, and tags
- Added backend API routes for case management including listing, creating, updating, and deleting cases.
- Implemented relations and tags functionality for cases.
- Created frontend views for displaying case lists and details with filtering options.
- Added database migration scripts to set up necessary tables and indexes.
- Included HTML templates for case listing and detail views with responsive design.
- Configured module metadata in module.json for integration.
2026-01-29 23:07:33 +01:00

15 KiB

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

{
  "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):

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

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

@router.post("/cases")
async def create_sag(sag_data: dict):
    # Validér input
    # INSERT INTO sag_sager
    # RETURNING *
    # Return ny sag

GET /cases/{id}:

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

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

@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

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

docker compose exec db psql -U bmc_admin -d bmc_hub -c "SELECT * FROM sag_sager;"

Trin 6.2: Test API-endpoints

# 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

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

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:

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

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