77602bb4ac
VVL = Vertragsverlängerung beim selben Anbieter (vs. Folgevertrag = i.d.R. Anbieterwechsel). Im Gegensatz zu createFollowUpContract wird ALLES kopiert: - Provider, Tarif, Portal-Username/Passwort (verschlüsselt) - Preise (basePrice/unitPrice/bonus etc.) - Notes, Commission, Internet-Zugangsdaten, SIP-Daten, SIM-PINs - ContractDocuments (1:1, gleiche Datei-Referenz) - Detail-Tabellen (Energy/Internet/Mobile/TV/CarInsurance) komplett Berechnet: - newStartDate = oldStartDate + Vertragslaufzeit (Monate aus ContractDuration.code/description geparsed: "24M" / "24 Monate" / "2J") - newEndDate = newStartDate + Laufzeit - status = DRAFT (User bestätigt manuell) NICHT kopiert: - documentType "Auftragsformular" (das wird neu unterschrieben) - cancellation*-Felder (alter Cancel-Flow nicht relevant) Frontend: - Split-Button: Hauptaktion "Folgevertrag anlegen" + ChevronDown-Pfeil - Dropdown: "VVL anlegen" mit Bestätigungs-Modal - Modal zeigt Vorhersage des neuen Startdatums (alter Start + Vertragslaufzeit als Hinweis) History-Einträge wie bei Folgevertrag, mit eigenem VVL-Wording. Doppel-Schutz: maximal 1 Folge-/VVL-Vertrag pro Vorgänger. Live-verifiziert: - Contract #17 (FIBER, 2026-05-01, 24M) → VVL mit Start 2028-05-01 ✓ - Provider/Tarif/Preise/Credentials 1:1 übernommen - 2 Dokumente kopiert (außer Auftragsformular) - History-Einträge in beiden Verträgen vorhanden Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
164 lines
4.3 KiB
TypeScript
164 lines
4.3 KiB
TypeScript
import prisma from '../lib/prisma.js';
|
||
|
||
export interface CreateHistoryEntryData {
|
||
title: string;
|
||
description?: string;
|
||
isAutomatic?: boolean;
|
||
createdBy: string;
|
||
}
|
||
|
||
/**
|
||
* Alle Historie-Einträge für einen Vertrag abrufen
|
||
*/
|
||
export async function getHistoryEntries(contractId: number) {
|
||
return prisma.contractHistoryEntry.findMany({
|
||
where: { contractId },
|
||
orderBy: { createdAt: 'desc' },
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Einzelnen Historie-Eintrag abrufen
|
||
*/
|
||
export async function getHistoryEntry(contractId: number, entryId: number) {
|
||
return prisma.contractHistoryEntry.findFirst({
|
||
where: { id: entryId, contractId },
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Neuen Historie-Eintrag erstellen
|
||
*/
|
||
export async function createHistoryEntry(contractId: number, data: CreateHistoryEntryData) {
|
||
// Prüfen ob Vertrag existiert
|
||
const contract = await prisma.contract.findUnique({
|
||
where: { id: contractId },
|
||
});
|
||
|
||
if (!contract) {
|
||
throw new Error('Vertrag nicht gefunden');
|
||
}
|
||
|
||
return prisma.contractHistoryEntry.create({
|
||
data: {
|
||
contractId,
|
||
title: data.title,
|
||
description: data.description,
|
||
isAutomatic: data.isAutomatic ?? false,
|
||
createdBy: data.createdBy,
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Historie-Eintrag aktualisieren (nur manuelle Einträge)
|
||
*/
|
||
export async function updateHistoryEntry(
|
||
contractId: number,
|
||
entryId: number,
|
||
data: { title?: string; description?: string }
|
||
) {
|
||
const entry = await prisma.contractHistoryEntry.findFirst({
|
||
where: { id: entryId, contractId },
|
||
});
|
||
|
||
if (!entry) {
|
||
throw new Error('Historie-Eintrag nicht gefunden');
|
||
}
|
||
|
||
if (entry.isAutomatic) {
|
||
throw new Error('Automatische Einträge können nicht bearbeitet werden');
|
||
}
|
||
|
||
return prisma.contractHistoryEntry.update({
|
||
where: { id: entryId },
|
||
data: {
|
||
title: data.title,
|
||
description: data.description,
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Historie-Eintrag löschen (nur manuelle Einträge)
|
||
*/
|
||
export async function deleteHistoryEntry(contractId: number, entryId: number) {
|
||
const entry = await prisma.contractHistoryEntry.findFirst({
|
||
where: { id: entryId, contractId },
|
||
});
|
||
|
||
if (!entry) {
|
||
throw new Error('Historie-Eintrag nicht gefunden');
|
||
}
|
||
|
||
if (entry.isAutomatic) {
|
||
throw new Error('Automatische Einträge können nicht gelöscht werden');
|
||
}
|
||
|
||
return prisma.contractHistoryEntry.delete({ where: { id: entryId } });
|
||
}
|
||
|
||
/**
|
||
* Automatischen Historie-Eintrag für Folgevertrag erstellen (im Vorgängervertrag)
|
||
*/
|
||
export async function createFollowUpHistoryEntry(
|
||
previousContractId: number,
|
||
newContractNumber: string,
|
||
createdBy: string
|
||
) {
|
||
return createHistoryEntry(previousContractId, {
|
||
title: `Folgevertrag erstellt: ${newContractNumber}`,
|
||
description: `Ein neuer Folgevertrag (${newContractNumber}) wurde aus diesem Vertrag erstellt.`,
|
||
isAutomatic: true,
|
||
createdBy,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Automatischen Historie-Eintrag für neuen Folgevertrag erstellen (im neuen Vertrag selbst)
|
||
*/
|
||
export async function createNewContractFromPredecessorEntry(
|
||
newContractId: number,
|
||
previousContractNumber: string,
|
||
createdBy: string
|
||
) {
|
||
return createHistoryEntry(newContractId, {
|
||
title: `Folgevertrag zu ${previousContractNumber}`,
|
||
description: `Dieser Vertrag wurde als Folgevertrag zu ${previousContractNumber} erstellt.`,
|
||
isAutomatic: true,
|
||
createdBy,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Automatischen Historie-Eintrag für VVL (Vertragsverlängerung) im Vorgängervertrag.
|
||
*/
|
||
export async function createRenewalHistoryEntry(
|
||
previousContractId: number,
|
||
newContractNumber: string,
|
||
createdBy: string
|
||
) {
|
||
return createHistoryEntry(previousContractId, {
|
||
title: `Vertragsverlängerung erstellt: ${newContractNumber}`,
|
||
description: `Eine Vertragsverlängerung (VVL) als ${newContractNumber} wurde aus diesem Vertrag erstellt – alle Daten wurden 1:1 übernommen, das Auftragsdokument muss neu hochgeladen werden.`,
|
||
isAutomatic: true,
|
||
createdBy,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Automatischen Historie-Eintrag im neuen VVL-Vertrag.
|
||
*/
|
||
export async function createNewRenewalFromPredecessorEntry(
|
||
newContractId: number,
|
||
previousContractNumber: string,
|
||
createdBy: string
|
||
) {
|
||
return createHistoryEntry(newContractId, {
|
||
title: `VVL zu ${previousContractNumber}`,
|
||
description: `Dieser Vertrag wurde als Vertragsverlängerung (VVL) zu ${previousContractNumber} erstellt.`,
|
||
isAutomatic: true,
|
||
createdBy,
|
||
});
|
||
}
|