Vertrag: neuer Status ONGOING (Läuft weiter auf unbestimmte Zeit)
Enum-Wert ONGOING neu, MariaDB-Migration (ans Ende gehängt, damit keine Rows umgeschrieben werden). Prisma Client neu generiert. Cockpit-Logik: ONGOING wird in die Query mit reingeholt (damit DSGVO-/Consent-Warnungen weiter greifen), aber alle Fristen- Blöcke pro Vertrag geskippt: - Kündigungsfrist - Vertragsende - Zwischenrechnung-Frist Kein Skip für Daten-Qualität (fehlende Adresse, Bank, Portal- Zugang etc.) – die sind auch für unbefristete Verträge relevant. Frontend: statusLabels/statusVariants/statusDescriptions in ContractDetail, ContractList, CustomerDetail und ContractDetailModal um ONGOING ergänzt. Status-Select im ContractForm bekommt die neue Option zwischen ACTIVE und CANCELLED. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
-- Neuer Vertrags-Status ONGOING ("Läuft weiter auf unbestimmte Zeit").
|
||||
-- Verträge in diesem Status werden vom Cockpit bei Fristen-/Reminder-
|
||||
-- Berechnungen ignoriert, weil es kein Ende gibt, das getriggered werden
|
||||
-- könnte. MariaDB ENUM: neue Werte müssen an das Ende gehängt werden,
|
||||
-- sonst verschieben sich die internen Indices und alle bestehenden Rows
|
||||
-- werden neu geschrieben.
|
||||
|
||||
ALTER TABLE `Contract`
|
||||
MODIFY COLUMN `status`
|
||||
ENUM('DRAFT', 'PENDING', 'ACTIVE', 'CANCELLED', 'EXPIRED', 'DEACTIVATED', 'ONGOING')
|
||||
NOT NULL DEFAULT 'DRAFT';
|
||||
@@ -644,6 +644,11 @@ enum ContractStatus {
|
||||
CANCELLED
|
||||
EXPIRED
|
||||
DEACTIVATED
|
||||
// "Läuft weiter auf unbestimmte Zeit" – Vertrag ist unbefristet aktiv.
|
||||
// Cockpit-Fristen (Kündigungsvorlauf, Verlängerungs-Reminder etc.)
|
||||
// ignorieren diesen Status, weil es kein Ende gibt, das getriggered
|
||||
// werden könnte.
|
||||
ONGOING
|
||||
}
|
||||
|
||||
model Contract {
|
||||
|
||||
@@ -198,11 +198,13 @@ export async function getCockpitData(opts?: { customerIds?: number[] }): Promise
|
||||
? { customerId: { in: opts.customerIds } }
|
||||
: {};
|
||||
|
||||
// Lade alle relevanten Verträge (inkl. CANCELLED/DEACTIVATED für Schlussrechnung-Check)
|
||||
// Lade alle relevanten Verträge (inkl. CANCELLED/DEACTIVATED für
|
||||
// Schlussrechnung-Check, inkl. ONGOING damit DSGVO-/Consent-Warnungen
|
||||
// weiter greifen – Fristen-Blöcke werden pro Vertrag geskippt, s.u.).
|
||||
const contracts = await prisma.contract.findMany({
|
||||
where: {
|
||||
status: {
|
||||
in: ['ACTIVE', 'PENDING', 'DRAFT', 'CANCELLED', 'DEACTIVATED', 'EXPIRED'],
|
||||
in: ['ACTIVE', 'PENDING', 'DRAFT', 'CANCELLED', 'DEACTIVATED', 'EXPIRED', 'ONGOING'],
|
||||
},
|
||||
...customerScopeFilter,
|
||||
},
|
||||
@@ -354,9 +356,16 @@ export async function getCockpitData(opts?: { customerIds?: number[] }): Promise
|
||||
// Prüfen ob aktiver Folgevertrag existiert - dann keine Kündigungswarnungen nötig
|
||||
const hasActiveFollowUp = contract.followUpContract?.status === 'ACTIVE';
|
||||
|
||||
// ONGOING = "Läuft weiter auf unbestimmte Zeit": es gibt kein Ende,
|
||||
// also alle Fristen-bezogenen Blöcke (Kündigung, Verlängerung,
|
||||
// Lieferbestätigung) für diesen Vertrag komplett skippen. DSGVO-/
|
||||
// Consent-Checks laufen weiter, weil die pro Kunde greifen, nicht
|
||||
// pro Vertrag.
|
||||
const skipDeadlines = contract.status === 'ONGOING';
|
||||
|
||||
// 1. KÜNDIGUNGSFRIST (nur wenn kein aktiver Folgevertrag UND Snooze nicht aktiv)
|
||||
// Snooze unterdrückt NUR Fristen-bezogene Warnungen!
|
||||
if (!hasActiveFollowUp && !snoozeActive) {
|
||||
if (!hasActiveFollowUp && !snoozeActive && !skipDeadlines) {
|
||||
const cancellationDeadline = calculateCancellationDeadline(
|
||||
contract.endDate,
|
||||
contract.cancellationPeriod?.code
|
||||
@@ -401,9 +410,9 @@ export async function getCockpitData(opts?: { customerIds?: number[] }): Promise
|
||||
}
|
||||
}
|
||||
|
||||
// 2. VERTRAGSENDE (nur wenn Snooze nicht aktiv)
|
||||
// 2. VERTRAGSENDE (nur wenn Snooze nicht aktiv, und nicht ONGOING)
|
||||
// Snooze unterdrückt NUR Fristen-bezogene Warnungen!
|
||||
if (!snoozeActive) {
|
||||
if (!snoozeActive && !skipDeadlines) {
|
||||
const daysToEnd = daysUntil(contract.endDate);
|
||||
if (daysToEnd !== null && daysToEnd <= okDays) {
|
||||
const urgency = getUrgencyByDays(daysToEnd, criticalDays, warningDays, okDays);
|
||||
@@ -621,7 +630,8 @@ export async function getCockpitData(opts?: { customerIds?: number[] }): Promise
|
||||
// 13b. ZWISCHENRECHNUNG FEHLT/ÜBERFÄLLIG (wenn Vertrag > 12 Monate läuft)
|
||||
// Für alle Status außer DRAFT und nicht gekündigt/deaktiviert
|
||||
// Auch EXPIRED zählt hier, da der Vertrag ohne Kündigung weiterläuft!
|
||||
if (contract.startDate && contract.status !== 'DRAFT' && !isContractTerminated) {
|
||||
// ONGOING wird ebenfalls ausgeschlossen – ist ein Fristen-Check.
|
||||
if (contract.startDate && contract.status !== 'DRAFT' && !isContractTerminated && !skipDeadlines) {
|
||||
const startDate = new Date(contract.startDate);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
const daysSinceStart = Math.floor((now.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
Reference in New Issue
Block a user