Vertrag-Update: Kuendigungs-Datumsfelder robust normalisieren (R138)

cancellationConfirmationDate / cancellationConfirmationOptionsDate im
PUT /contracts/:id ueber validateOptionalIsoDate normalisieren: nimmt
Datum-only (YYYY-MM-DD) UND volles ISO, liefert einen sauberen Date an
Prisma. Vorher lehnte Prisma ein Datum-only ab (400 statt Verarbeitung).
Ungueltige Formate -> sauberes 400. Konsistent zum Upload-Pfad, der
confirmationDate bereits so validiert.

Verifiziert: '2027-09-13' akzeptiert + als DATETIME geschrieben;
deutsches Format abgelehnt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-08 21:40:31 +02:00
co-authored by Claude Opus 4.8
parent 0f51a1cc3b
commit 5fb01627b0
2 changed files with 33 additions and 0 deletions
@@ -230,6 +230,28 @@ export async function updateContract(req: AuthRequest, res: Response): Promise<v
// HTML/JS-Strip auf allen String-Werten (Pentest 2026-05-24, 31.1)
const sanitizedBody = sanitizeContractBody(req.body);
// Kündigungs-Datumsfelder robust normalisieren: `validateOptionalIsoDate`
// akzeptiert Datum-only (YYYY-MM-DD) UND volles ISO und liefert einen
// sauberen Date-Wert an Prisma. Ohne das lehnte Prisma ein Datum-only
// ab (Pentest R138, Hygiene-Punkt). Konsistent zum Upload-Pfad, der
// `confirmationDate` bereits so validiert.
for (const df of ['cancellationConfirmationDate', 'cancellationConfirmationOptionsDate'] as const) {
if (df in (sanitizedBody as Record<string, unknown>)) {
let validated: string | null;
try {
validated = validateOptionalIsoDate((sanitizedBody as Record<string, unknown>)[df], df);
} catch (err) {
res.status(400).json({
success: false,
error: err instanceof Error ? err.message : 'Ungültiges Datum',
} as ApiResponse);
return;
}
(sanitizedBody as Record<string, unknown>)[df] = validated ? new Date(validated) : null;
}
}
const contract = await contractService.updateContract(contractId, sanitizedBody as any);
// Wenn über das Formular ein (neues/geändertes) Kündigungsbestätigungs-
+11
View File
@@ -112,6 +112,17 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
ACTIVE/DRAFT/CANCELLED. Eigene Liste `cancellationConfirmations` im
Cockpit-Result (weil bereits CANCELLED-Verträge mangels „Issue" sonst nicht
auftauchen) + neue Filter-Option im Cockpit-Dropdown. Verifiziert.
- **Nachbesserungen Pentest R138:**
- `endDate` wird bei **DRAFT** nicht mehr gesetzt (Entwurf = Vorlage);
nur ACTIVE/CANCELLED bekommen Vertragsende = Kündigungsdatum.
- `cancellationConfirmationDate`/`-OptionsDate` im Update-Pfad über
`validateOptionalIsoDate` normalisiert → akzeptiert Datum-only UND
volles ISO (vorher lehnte Prisma Datum-only ab), konsistent zum Upload.
- **Gutschrift-Dateien:** Überweisungsbelege (`credit-note-receipts`) sind
jetzt **staff-only** (neuer FileOwner-Typ `contract-staff`, Portal 403);
das Gutschrift-**PDF** (`credit-notes`) bleibt vertragsbasiert (eigener
Kunde darf seine Gutschrift laden). Anlegen/Beleg-Upload für Portal war
bereits per `blockPortal`/Portal-403 gesperrt.
- [~] **🧾 Gutschriftsverwaltung (Subventionen am Vertrag) Phase 1: Backend** (2026-08-06)
- Use-Case: zu einem Vertrag kann eine Subvention gewährt werden **Geld**