Cost Centers API
Die Cost-Centers-API verwaltet Kostenstellen als zentrale Stammdaten — mit Code/Name, Lifecycle (DRAFT/ACTIVE/CLOSED), Gültigkeitszeitraum, Budget, Hierarchie (Kostenstellengruppen/Rollups), Verantwortlichem und ERP-Sync. Kostenstellen werden Assets, Verträgen und Lizenzen per costCenterId zugewiesen und bilden die Grundlage der Kostenberichte.
Authentifizierung & Permissions
Jede Aktion hat ein eigenes Recht unter costCenters.*, das für alle Kostenstellen gleichermaßen gilt. Einschränkungen auf einzelne Kostenstellen oder deren Verantwortliche gibt es nicht. Details siehe User Management & RBAC.
| Aktion | Permission |
|---|---|
| Anzeigen / Liste / Suche | costCenters.view |
| Budget-Beträge sehen und setzen | costCenters.viewBudget |
| Erstellen | costCenters.create |
| Aktualisieren | costCenters.update |
| Löschen (Soft-Delete) | costCenters.delete |
| Zusammenführen | costCenters.merge (Default: Admin) |
| Import / ERP-Sync | costCenters.import |
Benutzer und API-Keys: Alle Lesezugriffe (Liste, Suche, Detail, Aktivitätsverlauf) stehen angemeldeten Benutzern und API-Keys mit costCenters.view gleichermaßen offen — der ERP-Key, der importieren darf, kann also auch die Liste zum Abgleich lesen. Anlegen, Ändern, Löschen und Zusammenführen erfordern einen angemeldeten Benutzer; ein X-API-Key wird dort mit 403 abgelehnt ("This operation requires a logged-in user account, not an API key"). POST /api/cost-centers/import akzeptiert beides. So bleibt die manuelle Pflege einer Person zugeordnet (Audit), während Lesen und ERP-Sync per Key laufen.
Budget-Beträge sind gesondert geschützt: Ohne costCenters.viewBudget enthält jede Antwort budget: null — Liste, Detail und auch die Antwort auf Anlegen/Ändern. Das Feld ist immer vorhanden, nur der Wert fehlt. Mit 403 COST_CENTER_BUDGET_FORBIDDEN abgelehnt werden: Sortieren nach Budget (?sort=budget:…, die Reihenfolge würde die Beträge verraten) und budget im Anlege-/Änderungs-Body. Im Import wird die betroffene Zeile als fehlerhaft gemeldet, der Import läuft weiter. Wer das Budget nicht sehen darf, darf es auch nicht setzen. Im Kostenbericht steuert reports.viewBudget die Sichtbarkeit derselben Beträge.
Endpoints Übersicht
| Method | Endpoint | Beschreibung | Permission |
|---|---|---|---|
GET | /api/cost-centers | Paginierte, filter-spec-getriebene Liste | costCenters.view |
GET | /api/cost-centers/search?q=&per= | Suche für Auswahlfelder: nur zuweisbare (ACTIVE und gültige) Kostenstellen. Erlaubt sind nur q und per; andere Parameter (z. B. limit) → 400 | costCenters.view |
GET | /api/cost-centers/:id | Einzelne Kostenstelle | costCenters.view |
GET | /api/cost-centers/:id/activities | Activity-Trail der Kostenstelle | costCenters.view |
POST | /api/cost-centers | Kostenstelle erstellen (source=MANUAL) | costCenters.create |
PATCH | /api/cost-centers/:id | Kostenstelle aktualisieren | costCenters.update |
DELETE | /api/cost-centers/:id | Soft-Delete (Status → CLOSED) → 204; mit aktiven Unterkostenstellen: 400 COST_CENTER_HAS_CHILDREN (+ details.children) | costCenters.delete |
POST | /api/cost-centers/:id/merge | Referenzen (Assets/Verträge/Lizenzen) UND Kinder auf targetId umhängen, Quelle schließen — alles in einem Schritt. Das Ziel muss zuweisbar sein und darf keine Unterkostenstelle der Quelle sein. Bei zwei gleichzeitigen, gegenläufigen Merges verliert einer mit 409 COST_CENTER_MERGE_CONFLICT | costCenters.merge |
POST | /api/cost-centers/import | Idempotenter Upsert (ERP-Sync, source=SYNCED) — User ODER API-Key | costCenters.import |
Felder
| Feld | Typ | Beschreibung |
|---|---|---|
code | String (1–50) | Kostenstellen-Nr./-Kürzel — eindeutig, Pflicht |
name | String (1–200) | Anzeigename — Pflicht |
description | String? (≤2000) | Beschreibung |
color | String (#rrggbb) | Badge-Farbe (Default #3b82f6) |
sortOrder | Int | Sortierreihenfolge (nur via Import setzbar, kein Create/Update-Input) |
status | enum | DRAFT, ACTIVE, CLOSED |
validFrom / validUntil | DateTime? | Gültigkeitszeitraum (steuert Zuweisbarkeit); validFrom ≤ validUntil erzwungen (400 COST_CENTER_VALIDITY_INVALID) |
ownerId | String? | Verantwortlicher User — muss existieren und darf nicht archiviert sein (400 COST_CENTER_OWNER_NOT_FOUND) |
budget | Decimal? (14,2) | Budget (in Systemwährung — siehe unten) |
parentId | String? | Übergeordnete Kostenstelle (Hierarchie/Rollups). Löschen räumt die Hierarchie NICHT auf — Kinder hängt nur der Merge um, damit ein Umbau eine bewusste Entscheidung bleibt |
externalId | String? (≤100) | Stabiler ERP-Schlüssel (SAP/DATEV) — eindeutig |
source | enum | MANUAL, SYNCED (read-only, vom System gesetzt) |
Währung: Kostenstellen haben kein eigenes Währungsfeld — budget wird in der globalen Systemwährung (general settings: systemCurrency) interpretiert. Es findet keine Umrechnung statt. Siehe Settings & Global Search API.
Kostenstelle erstellen
POST /api/cost-centers
{
"code": "CC-1000",
"name": "IT Operations",
"description": "Betrieb & Infrastruktur",
"color": "#3b82f6",
"status": "ACTIVE",
"validFrom": "2026-01-01T00:00:00Z",
"ownerId": "clx-user-id",
"budget": 250000.00,
"parentId": "clx-parent-cost-center-id"
}
Response (201 Created)
{
"id": "clx...",
"code": "CC-1000",
"name": "IT Operations",
"status": "ACTIVE",
"color": "#3b82f6",
"source": "MANUAL",
"createdAt": "2026-06-18T08:00:00.000Z"
}
Über diese Route ist source immer MANUAL. SYNCED-Datensätze entstehen ausschließlich über den Import-/ERP-Sync-Endpoint.
ERP-Import (Sync)
Idempotenter Upsert, gekeyt auf externalId (falls vorhanden), sonst code. Importierte Datensätze werden mit source=SYNCED markiert. Ein zuvor soft-gelöschter Code wird beim erneuten Import wiederhergestellt. Bis zu 1000 Einträge pro Request. Dieser Endpoint akzeptiert sowohl User- als auch API-Key-Aktoren.
POST /api/cost-centers/import
X-API-Key: <your-api-key>
{
"items": [
{
"code": "CC-1000",
"name": "IT Operations",
"externalId": "SAP-1000",
"status": "ACTIVE",
"budget": 250000.00
},
{
"code": "CC-2000",
"name": "Facility Management",
"externalId": "SAP-2000",
"status": "ACTIVE"
}
]
}
Hinweis: parentId (cuid) ist optional; eine Hierarchie-Verknüpfung per Parent-Code wird hier bewusst NICHT unterstützt (dafür Update/UI nutzen).
Lifecycle & Zuweisbarkeit
| Status | Beschreibung |
|---|---|
DRAFT | Entwurf — noch nicht zuweisbar |
ACTIVE | Aktiv — innerhalb des Gültigkeitszeitraums zuweisbar |
CLOSED | Geschlossen (Soft-Delete) — nicht mehr zuweisbar |
Kostenstellen werden Assets, Verträgen und Lizenzen über das Feld costCenterId zugewiesen. Die Zuweisbarkeit prüft der Server: nur ACTIVE-Kostenstellen innerhalb ihres Gültigkeitszeitraums sind zuweisbar — eine CLOSED oder abgelaufene Kostenstelle führt zu 400 Bad Request. Die Zuweisung ist ein einfaches Feld am jeweiligen Objekt, keine Verknüpfung über die Linking-API.