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:
@@ -21,7 +21,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
PENDING: 'Ausstehend',
|
||||
ACTIVE: 'Aktiv',
|
||||
CANCELLED: 'Gekündigt',
|
||||
CANCELLED: 'Gekündigt / Bestätigung abwarten',
|
||||
CANCELLED_CONFIRMED: 'Gekündigt / bestätigt',
|
||||
EXPIRED: 'Abgelaufen',
|
||||
DEACTIVATED: 'Deaktiviert',
|
||||
ONGOING: 'Läuft weiter (unbestimmt)',
|
||||
@@ -30,7 +31,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
const statusVariants: Record<ContractStatus, 'success' | 'warning' | 'danger' | 'default'> = {
|
||||
ACTIVE: 'success',
|
||||
PENDING: 'warning',
|
||||
CANCELLED: 'danger',
|
||||
CANCELLED: 'warning',
|
||||
CANCELLED_CONFIRMED: 'danger',
|
||||
EXPIRED: 'danger',
|
||||
DRAFT: 'default',
|
||||
DEACTIVATED: 'default',
|
||||
|
||||
@@ -138,7 +138,8 @@ export default function AssignToContractModal({
|
||||
px-2 py-0.5 text-xs rounded-full
|
||||
${contract.status === 'ACTIVE' ? 'bg-green-100 text-green-800' :
|
||||
contract.status === 'PENDING' ? 'bg-yellow-100 text-yellow-800' :
|
||||
contract.status === 'CANCELLED' ? 'bg-red-100 text-red-800' :
|
||||
contract.status === 'CANCELLED' ? 'bg-orange-100 text-orange-800' :
|
||||
contract.status === 'CANCELLED_CONFIRMED' ? 'bg-red-100 text-red-800' :
|
||||
'bg-gray-100 text-gray-800'}
|
||||
`}>
|
||||
{contract.status}
|
||||
|
||||
@@ -43,7 +43,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
PENDING: 'Ausstehend',
|
||||
ACTIVE: 'Aktiv',
|
||||
CANCELLED: 'Gekündigt',
|
||||
CANCELLED: 'Gekündigt / Bestätigung abwarten',
|
||||
CANCELLED_CONFIRMED: 'Gekündigt / bestätigt',
|
||||
EXPIRED: 'Abgelaufen',
|
||||
DEACTIVATED: 'Deaktiviert',
|
||||
ONGOING: 'Läuft weiter (unbestimmt)',
|
||||
@@ -52,7 +53,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
const statusVariants: Record<ContractStatus, 'success' | 'warning' | 'danger' | 'default'> = {
|
||||
ACTIVE: 'success',
|
||||
PENDING: 'warning',
|
||||
CANCELLED: 'danger',
|
||||
CANCELLED: 'warning',
|
||||
CANCELLED_CONFIRMED: 'danger',
|
||||
EXPIRED: 'danger',
|
||||
DRAFT: 'default',
|
||||
DEACTIVATED: 'default',
|
||||
@@ -66,7 +68,8 @@ const statusDescriptions = [
|
||||
{ status: 'ACTIVE', label: 'Aktiv', description: 'Vertrag läuft normal', color: 'text-green-600' },
|
||||
{ status: 'ONGOING', label: 'Läuft weiter (unbestimmt)', description: 'Vertrag läuft unbefristet – alle Fristen im Cockpit werden ignoriert', color: 'text-green-700' },
|
||||
{ status: 'EXPIRED', label: 'Abgelaufen', description: 'Laufzeit vorbei, läuft aber ohne Kündigung weiter', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt', description: 'Aktive Kündigung eingereicht, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt / Bestätigung abwarten', description: 'Kündigungsschreiben raus – Bestätigung des Anbieters steht noch aus', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED_CONFIRMED', label: 'Gekündigt / bestätigt', description: 'Kündigung vom Anbieter bestätigt, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'DEACTIVATED', label: 'Deaktiviert', description: 'Manuell beendet/archiviert', color: 'text-gray-500' },
|
||||
];
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ const statusOptions = [
|
||||
{ value: 'PENDING', label: 'Ausstehend' },
|
||||
{ value: 'ACTIVE', label: 'Aktiv' },
|
||||
{ value: 'ONGOING', label: 'Läuft weiter (unbestimmt)' },
|
||||
{ value: 'CANCELLED', label: 'Gekündigt' },
|
||||
{ value: 'CANCELLED', label: 'Gekündigt / Bestätigung abwarten' },
|
||||
{ value: 'CANCELLED_CONFIRMED', label: 'Gekündigt / bestätigt' },
|
||||
{ value: 'EXPIRED', label: 'Abgelaufen' },
|
||||
{ value: 'DEACTIVATED', label: 'Deaktiviert' },
|
||||
];
|
||||
@@ -60,7 +61,8 @@ const statusDescriptions = [
|
||||
{ status: 'ACTIVE', label: 'Aktiv', description: 'Vertrag läuft normal', color: 'text-green-600' },
|
||||
{ status: 'ONGOING', label: 'Läuft weiter (unbestimmt)', description: 'Vertrag läuft unbefristet – alle Fristen im Cockpit werden ignoriert', color: 'text-green-700' },
|
||||
{ status: 'EXPIRED', label: 'Abgelaufen', description: 'Laufzeit vorbei, läuft aber ohne Kündigung weiter', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt', description: 'Aktive Kündigung eingereicht, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt / Bestätigung abwarten', description: 'Kündigungsschreiben raus – Bestätigung des Anbieters steht noch aus', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED_CONFIRMED', label: 'Gekündigt / bestätigt', description: 'Kündigung vom Anbieter bestätigt, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'DEACTIVATED', label: 'Deaktiviert', description: 'Manuell beendet/archiviert', color: 'text-gray-500' },
|
||||
];
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
PENDING: 'Ausstehend',
|
||||
ACTIVE: 'Aktiv',
|
||||
CANCELLED: 'Gekündigt',
|
||||
CANCELLED: 'Gekündigt / Bestätigung abwarten',
|
||||
CANCELLED_CONFIRMED: 'Gekündigt / bestätigt',
|
||||
EXPIRED: 'Abgelaufen',
|
||||
DEACTIVATED: 'Deaktiviert',
|
||||
ONGOING: 'Läuft weiter (unbestimmt)',
|
||||
@@ -40,7 +41,8 @@ const statusLabels: Record<ContractStatus, string> = {
|
||||
const statusVariants: Record<ContractStatus, 'success' | 'warning' | 'danger' | 'default'> = {
|
||||
ACTIVE: 'success',
|
||||
PENDING: 'warning',
|
||||
CANCELLED: 'danger',
|
||||
CANCELLED: 'warning',
|
||||
CANCELLED_CONFIRMED: 'danger',
|
||||
EXPIRED: 'danger',
|
||||
DRAFT: 'default',
|
||||
DEACTIVATED: 'default',
|
||||
@@ -54,7 +56,8 @@ const statusDescriptions = [
|
||||
{ status: 'ACTIVE', label: 'Aktiv', description: 'Vertrag läuft normal', color: 'text-green-600' },
|
||||
{ status: 'ONGOING', label: 'Läuft weiter (unbestimmt)', description: 'Vertrag läuft unbefristet – alle Fristen im Cockpit werden ignoriert', color: 'text-green-700' },
|
||||
{ status: 'EXPIRED', label: 'Abgelaufen', description: 'Laufzeit vorbei, läuft aber ohne Kündigung weiter', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt', description: 'Aktive Kündigung eingereicht, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt / Bestätigung abwarten', description: 'Kündigungsschreiben raus – Bestätigung des Anbieters steht noch aus', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED_CONFIRMED', label: 'Gekündigt / bestätigt', description: 'Kündigung vom Anbieter bestätigt, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'DEACTIVATED', label: 'Deaktiviert', description: 'Manuell beendet/archiviert', color: 'text-gray-500' },
|
||||
];
|
||||
|
||||
|
||||
@@ -1556,7 +1556,8 @@ function MetersTab({
|
||||
)}
|
||||
<Badge variant={
|
||||
ct.status === 'ACTIVE' ? 'success'
|
||||
: ct.status === 'CANCELLED' ? 'danger'
|
||||
: ct.status === 'CANCELLED' ? 'warning'
|
||||
: ct.status === 'CANCELLED_CONFIRMED' ? 'danger'
|
||||
: ct.status === 'EXPIRED' ? 'warning'
|
||||
: 'default'
|
||||
}>{ct.status}</Badge>
|
||||
@@ -1739,10 +1740,22 @@ function ContractsTab({
|
||||
CAR_INSURANCE: 'KFZ-Versicherung',
|
||||
};
|
||||
|
||||
const statusLabels: Record<string, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
PENDING: 'Ausstehend',
|
||||
ACTIVE: 'Aktiv',
|
||||
CANCELLED: 'Gekündigt / Bestätigung abwarten',
|
||||
CANCELLED_CONFIRMED: 'Gekündigt / bestätigt',
|
||||
EXPIRED: 'Abgelaufen',
|
||||
DEACTIVATED: 'Deaktiviert',
|
||||
ONGOING: 'Läuft weiter (unbestimmt)',
|
||||
};
|
||||
|
||||
const statusVariants: Record<string, 'success' | 'warning' | 'danger' | 'default'> = {
|
||||
ACTIVE: 'success',
|
||||
PENDING: 'warning',
|
||||
CANCELLED: 'danger',
|
||||
CANCELLED: 'warning',
|
||||
CANCELLED_CONFIRMED: 'danger',
|
||||
EXPIRED: 'danger',
|
||||
DRAFT: 'default',
|
||||
DEACTIVATED: 'default',
|
||||
@@ -1755,7 +1768,8 @@ function ContractsTab({
|
||||
{ status: 'ACTIVE', label: 'Aktiv', description: 'Vertrag läuft normal', color: 'text-green-600' },
|
||||
{ status: 'ONGOING', label: 'Läuft weiter (unbestimmt)', description: 'Vertrag läuft unbefristet – alle Fristen im Cockpit werden ignoriert', color: 'text-green-700' },
|
||||
{ status: 'EXPIRED', label: 'Abgelaufen', description: 'Laufzeit vorbei, läuft aber ohne Kündigung weiter', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt', description: 'Aktive Kündigung eingereicht, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'CANCELLED', label: 'Gekündigt / Bestätigung abwarten', description: 'Kündigungsschreiben raus – Bestätigung des Anbieters steht noch aus', color: 'text-orange-600' },
|
||||
{ status: 'CANCELLED_CONFIRMED', label: 'Gekündigt / bestätigt', description: 'Kündigung vom Anbieter bestätigt, Vertrag endet', color: 'text-red-600' },
|
||||
{ status: 'DEACTIVATED', label: 'Deaktiviert', description: 'Manuell beendet/archiviert', color: 'text-gray-500' },
|
||||
];
|
||||
|
||||
@@ -1818,7 +1832,7 @@ function ContractsTab({
|
||||
<CopyButton value={contract.contractNumber} />
|
||||
</Link>
|
||||
<Badge>{typeLabels[contract.type] || contract.type}</Badge>
|
||||
<Badge variant={statusVariants[contract.status] || 'default'}>{contract.status}</Badge>
|
||||
<Badge variant={statusVariants[contract.status] || 'default'}>{statusLabels[contract.status] || contract.status}</Badge>
|
||||
{depth === 0 && !isPredecessor && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowStatusInfo(true); }}
|
||||
|
||||
@@ -558,7 +558,7 @@ export interface Tariff {
|
||||
}
|
||||
|
||||
export type ContractType = 'ELECTRICITY' | 'GAS' | 'DSL' | 'CABLE' | 'FIBER' | 'MOBILE' | 'TV' | 'CAR_INSURANCE';
|
||||
export type ContractStatus = 'DRAFT' | 'PENDING' | 'ACTIVE' | 'CANCELLED' | 'EXPIRED' | 'DEACTIVATED' | 'ONGOING';
|
||||
export type ContractStatus = 'DRAFT' | 'PENDING' | 'ACTIVE' | 'CANCELLED' | 'CANCELLED_CONFIRMED' | 'EXPIRED' | 'DEACTIVATED' | 'ONGOING';
|
||||
|
||||
export interface Contract {
|
||||
id: number;
|
||||
|
||||
Reference in New Issue
Block a user