Neuer Vertragsstatus "Gekuendigt / bestaetigt" + Kuendigungs-Workflow
Bisheriges "Gekuendigt" (CANCELLED) umbenannt in "Gekuendigt / Bestaetigung
abwarten" und wird jetzt automatisch gesetzt, sobald ein Kuendigungsschreiben
hochgeladen wird. Neuer Status CANCELLED_CONFIRMED ("Gekuendigt / bestaetigt")
wird automatisch gesetzt, sobald ein Kuendigungsbestaetigungsdatum vorliegt
(Dokument fuellt das Datum oder manuell) + Vertragsende = Kuendigungsdatum.
Schema: Enum-Wert CANCELLED_CONFIRMED + Migration (idempotentes MODIFY COLUMN);
Daten-Migration hebt bestehende CANCELLED (alte Logik: nur bei Bestaetigung
gesetzt) auf CANCELLED_CONFIRMED.
Backend: neue Trigger maybeMarkAwaitingConfirmationOnLetter (Schreiben->CANCELLED)
im Upload-Handler; maybeCancelOnCancellationConfirmation setzt jetzt
CANCELLED_CONFIRMED (auch aus CANCELLED). Cockpit-Semantik mitgewandert
(Fristen-Skip/"beendet" fuer CANCELLED_CONFIRMED; Ladeliste + Kuendigungs-
bestaetigungs-Filter erweitert).
Frontend: Labels/Farben/Status-Erklaerungen + Status-Dropdown in ContractList,
ContractDetail, ContractForm, ContractDetailModal, CustomerDetail
(CANCELLED orange "abwarten", CANCELLED_CONFIRMED rot).
Verifiziert: tsc+build gruen; Schreiben->CANCELLED, Bestaetigung->
CANCELLED_CONFIRMED+Enddatum; Daten-Migration idempotent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -195,9 +195,13 @@ export async function maybeCancelOnCancellationConfirmation(
|
||||
const updateData: Record<string, unknown> = {};
|
||||
const changes: Record<string, { vorher: unknown; nachher: unknown }> = {};
|
||||
|
||||
if (c.status === 'ACTIVE') {
|
||||
updateData.status = 'CANCELLED';
|
||||
changes.status = { vorher: 'ACTIVE', nachher: 'CANCELLED' };
|
||||
// Kündigungsbestätigung liegt vor (Datum/Dokument) → "Gekündigt / bestätigt".
|
||||
// Von jedem noch "lebenden" Status heben – auch aus "Gekündigt / Bestätigung
|
||||
// abwarten" (CANCELLED) – aber NIE aus DRAFT (Vorlage) oder DEACTIVATED
|
||||
// (archiviert). Ein bereits bestätigter Vertrag bleibt bestätigt (No-op).
|
||||
if (['ACTIVE', 'PENDING', 'ONGOING', 'EXPIRED', 'CANCELLED'].includes(c.status)) {
|
||||
updateData.status = 'CANCELLED_CONFIRMED';
|
||||
changes.status = { vorher: c.status, nachher: 'CANCELLED_CONFIRMED' };
|
||||
}
|
||||
|
||||
// Vertragsende = Kündigungsdatum (Bestätigungsdatum), falls vorhanden.
|
||||
@@ -227,6 +231,45 @@ export async function maybeCancelOnCancellationConfirmation(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt einen Vertrag automatisch auf "Gekündigt / Bestätigung abwarten"
|
||||
* (CANCELLED), sobald ein Kündigungsschreiben hinterlegt wurde. Hebt nur noch
|
||||
* "lebende" Verträge (ACTIVE/PENDING/ONGOING/EXPIRED) an – ein bereits bestätigt
|
||||
* gekündigter Vertrag (CANCELLED_CONFIRMED) wird NICHT zurückgestuft, ein
|
||||
* Entwurf (DRAFT) oder archivierter (DEACTIVATED) nicht angefasst.
|
||||
*/
|
||||
export async function maybeMarkAwaitingConfirmationOnLetter(
|
||||
contractId: number,
|
||||
req: unknown,
|
||||
): Promise<void> {
|
||||
const c = await prisma.contract.findUnique({
|
||||
where: { id: contractId },
|
||||
select: {
|
||||
status: true,
|
||||
contractNumber: true,
|
||||
customerId: true,
|
||||
cancellationLetterPath: true,
|
||||
},
|
||||
});
|
||||
if (!c || !c.cancellationLetterPath) return;
|
||||
if (!['ACTIVE', 'PENDING', 'ONGOING', 'EXPIRED'].includes(c.status)) return;
|
||||
|
||||
await prisma.contract.update({
|
||||
where: { id: contractId },
|
||||
data: { status: 'CANCELLED' },
|
||||
});
|
||||
|
||||
await logChange({
|
||||
req,
|
||||
action: 'UPDATE',
|
||||
resourceType: 'Contract',
|
||||
resourceId: contractId.toString(),
|
||||
label: `Vertrag ${c.contractNumber} automatisch auf "Gekündigt / Bestätigung abwarten" gesetzt (Kündigungsschreiben)`,
|
||||
details: { status: { vorher: c.status, nachher: 'CANCELLED' }, trigger: 'Kündigungsschreiben' },
|
||||
customerId: c.customerId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function maybeActivateOnDeliveryConfirmation(
|
||||
contractId: number,
|
||||
documentType: string,
|
||||
|
||||
Reference in New Issue
Block a user