contracts: VVL (Vertragsverlängerung) als Split-Button neben Folgevertrag
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>
This commit is contained in:
@@ -256,6 +256,58 @@ export async function createFollowUp(req: AuthRequest, res: Response): Promise<v
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VVL = Vertragsverlängerung beim selben Anbieter.
|
||||
* Erstellt einen neuen Vertrag mit allen Daten des Vorgängers (außer
|
||||
* Auftragsdokument), Startdatum = altes Start + Vertragslaufzeit.
|
||||
*/
|
||||
export async function createRenewal(req: AuthRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const previousContractId = parseInt(req.params.id);
|
||||
|
||||
const previousContract = await prisma.contract.findUnique({
|
||||
where: { id: previousContractId },
|
||||
select: { contractNumber: true },
|
||||
});
|
||||
if (!previousContract) {
|
||||
res.status(404).json({ success: false, error: 'Vorgängervertrag nicht gefunden' } as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
const contract = await contractService.createRenewalContract(previousContractId);
|
||||
if (!contract) {
|
||||
res.status(500).json({ success: false, error: 'VVL konnte nicht erstellt werden' } as ApiResponse);
|
||||
return;
|
||||
}
|
||||
const createdBy = req.user?.email || 'unbekannt';
|
||||
|
||||
await contractHistoryService.createRenewalHistoryEntry(
|
||||
previousContractId,
|
||||
contract.contractNumber,
|
||||
createdBy,
|
||||
);
|
||||
await contractHistoryService.createNewRenewalFromPredecessorEntry(
|
||||
contract.id,
|
||||
previousContract.contractNumber,
|
||||
createdBy,
|
||||
);
|
||||
|
||||
await logChange({
|
||||
req, action: 'CREATE', resourceType: 'Contract',
|
||||
resourceId: contract.id.toString(),
|
||||
label: `VVL erstellt für ${previousContract.contractNumber}`,
|
||||
customerId: contract.customerId,
|
||||
});
|
||||
|
||||
res.status(201).json({ success: true, data: contract } as ApiResponse);
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Erstellen der VVL',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getContractPassword(req: AuthRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const contractId = parseInt(req.params.id);
|
||||
|
||||
Reference in New Issue
Block a user