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:
@@ -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)
|
// HTML/JS-Strip auf allen String-Werten (Pentest 2026-05-24, 31.1)
|
||||||
const sanitizedBody = sanitizeContractBody(req.body);
|
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);
|
const contract = await contractService.updateContract(contractId, sanitizedBody as any);
|
||||||
|
|
||||||
// Wenn über das Formular ein (neues/geändertes) Kündigungsbestätigungs-
|
// Wenn über das Formular ein (neues/geändertes) Kündigungsbestätigungs-
|
||||||
|
|||||||
@@ -112,6 +112,17 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
|||||||
ACTIVE/DRAFT/CANCELLED. Eigene Liste `cancellationConfirmations` im
|
ACTIVE/DRAFT/CANCELLED. Eigene Liste `cancellationConfirmations` im
|
||||||
Cockpit-Result (weil bereits CANCELLED-Verträge mangels „Issue" sonst nicht
|
Cockpit-Result (weil bereits CANCELLED-Verträge mangels „Issue" sonst nicht
|
||||||
auftauchen) + neue Filter-Option im Cockpit-Dropdown. Verifiziert.
|
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)
|
- [~] **🧾 Gutschriftsverwaltung (Subventionen am Vertrag) – Phase 1: Backend** (2026-08-06)
|
||||||
- Use-Case: zu einem Vertrag kann eine Subvention gewährt werden – **Geld**
|
- Use-Case: zu einem Vertrag kann eine Subvention gewährt werden – **Geld**
|
||||||
|
|||||||
Reference in New Issue
Block a user