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)
|
||||
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-
|
||||
|
||||
@@ -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**
|
||||
|
||||
Reference in New Issue
Block a user