complete new audit system

This commit is contained in:
2026-03-21 18:23:54 +01:00
parent 38b3b7da73
commit fd55742c57
159 changed files with 2841 additions and 736 deletions
+105 -6
View File
@@ -1,12 +1,11 @@
import { Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma.js';
import * as contractService from '../services/contract.service.js';
import * as contractCockpitService from '../services/contractCockpit.service.js';
import * as contractHistoryService from '../services/contractHistory.service.js';
import * as authorizationService from '../services/authorization.service.js';
import { ApiResponse, AuthRequest } from '../types/index.js';
const prisma = new PrismaClient();
import { logChange } from '../services/audit.service.js';
export async function getContracts(req: AuthRequest, res: Response): Promise<void> {
try {
@@ -100,6 +99,12 @@ export async function getContract(req: AuthRequest, res: Response): Promise<void
export async function createContract(req: Request, res: Response): Promise<void> {
try {
const contract = await contractService.createContract(req.body);
await logChange({
req, action: 'CREATE', resourceType: 'Contract',
resourceId: contract.id.toString(),
label: `Vertrag ${contract.contractNumber} angelegt`,
customerId: contract.customerId,
});
res.status(201).json({ success: true, data: contract } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -109,9 +114,69 @@ export async function createContract(req: Request, res: Response): Promise<void>
}
}
export async function updateContract(req: Request, res: Response): Promise<void> {
export async function updateContract(req: AuthRequest, res: Response): Promise<void> {
try {
const contract = await contractService.updateContract(parseInt(req.params.id), req.body);
const contractId = parseInt(req.params.id);
// Vorherigen Stand laden für Audit-Vergleich
const before = await prisma.contract.findUnique({
where: { id: contractId },
include: { energyDetails: true, internetDetails: true, mobileDetails: true, tvDetails: true, carInsuranceDetails: true },
});
const contract = await contractService.updateContract(contractId, req.body);
// Geänderte Felder ermitteln
const changes: Record<string, { von: unknown; nach: unknown }> = {};
const fieldLabels: Record<string, string> = {
status: 'Status', startDate: 'Vertragsbeginn', endDate: 'Vertragsende',
portalUsername: 'Portal-Benutzername', customerNumberAtProvider: 'Kundennummer beim Anbieter',
providerId: 'Anbieter', tariffId: 'Tarif', cancellationPeriodId: 'Kündigungsfrist',
contractDurationId: 'Vertragslaufzeit', platformId: 'Vertriebsplattform',
cancellationDate: 'Kündigungsdatum', cancellationSentDate: 'Kündigung gesendet am',
identityDocumentId: 'Ausweis', bankCardId: 'Bankverbindung', addressId: 'Adresse',
commission: 'Provision', notes: 'Notizen',
};
const energyLabels: Record<string, string> = {
meterId: 'Zähler', maloId: 'MaLo-ID', annualConsumption: 'Jahresverbrauch',
basePrice: 'Grundpreis', unitPrice: 'Arbeitspreis', unitPriceNt: 'NT-Arbeitspreis', bonus: 'Bonus',
};
// Hauptfelder vergleichen
const body = req.body;
if (before) {
for (const [key, newVal] of Object.entries(body)) {
if (['energyDetails', 'internetDetails', 'mobileDetails', 'tvDetails', 'carInsuranceDetails', 'password'].includes(key)) continue;
const oldVal = (before as any)[key];
const norm = (v: unknown) => (v === null || v === undefined || v === '' ? null : v);
if (JSON.stringify(norm(oldVal)) !== JSON.stringify(norm(newVal))) {
const label = fieldLabels[key] || key;
changes[label] = { von: oldVal ?? '-', nach: newVal ?? '-' };
}
}
// Energie-Details vergleichen
if (body.energyDetails && before.energyDetails) {
for (const [key, newVal] of Object.entries(body.energyDetails)) {
const oldVal = (before.energyDetails as any)[key];
const norm = (v: unknown) => (v === null || v === undefined || v === '' ? null : v);
if (JSON.stringify(norm(oldVal)) !== JSON.stringify(norm(newVal))) {
const label = energyLabels[key] || key;
changes[label] = { von: oldVal ?? '-', nach: newVal ?? '-' };
}
}
}
}
const changeList = Object.entries(changes).map(([f, c]) => `${f}: ${c.von}${c.nach}`).join(', ');
await logChange({
req, action: 'UPDATE', resourceType: 'Contract',
resourceId: contractId.toString(),
label: changeList
? `Vertrag ${before?.contractNumber || contractId} aktualisiert: ${changeList}`
: `Vertrag ${before?.contractNumber || contractId} aktualisiert`,
details: Object.keys(changes).length > 0 ? changes : undefined,
customerId: before?.customerId,
});
res.json({ success: true, data: contract } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -123,7 +188,15 @@ export async function updateContract(req: Request, res: Response): Promise<void>
export async function deleteContract(req: Request, res: Response): Promise<void> {
try {
await contractService.deleteContract(parseInt(req.params.id));
const contractId = parseInt(req.params.id);
const contract = await prisma.contract.findUnique({ where: { id: contractId }, select: { contractNumber: true, customerId: true } });
await contractService.deleteContract(contractId);
await logChange({
req, action: 'DELETE', resourceType: 'Contract',
resourceId: contractId.toString(),
label: `Vertrag ${contract?.contractNumber} gelöscht`,
customerId: contract?.customerId,
});
res.json({ success: true, message: 'Vertrag gelöscht' } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -165,6 +238,13 @@ export async function createFollowUp(req: AuthRequest, res: Response): Promise<v
createdBy
);
await logChange({
req, action: 'CREATE', resourceType: 'Contract',
resourceId: contract.id.toString(),
label: `Folgevertrag erstellt für ${previousContract.contractNumber}`,
customerId: contract.customerId,
});
res.status(201).json({ success: true, data: contract } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -295,6 +375,13 @@ export async function addSuccessorMeter(req: AuthRequest, res: Response): Promis
data: { meterId: parseInt(meterId) },
});
await logChange({
req, action: 'CREATE', resourceType: 'ContractMeter',
resourceId: contractMeter.id.toString(),
label: `Folgezähler hinzugefügt zu Vertrag #${contractId}`,
customerId: contract.customerId,
});
res.json({ success: true, data: contractMeter } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -307,7 +394,13 @@ export async function addSuccessorMeter(req: AuthRequest, res: Response): Promis
export async function removeContractMeter(req: AuthRequest, res: Response): Promise<void> {
try {
const contractMeterId = parseInt(req.params.contractMeterId);
const contractId = parseInt(req.params.id);
await prisma.contractMeter.delete({ where: { id: contractMeterId } });
await logChange({
req, action: 'DELETE', resourceType: 'ContractMeter',
resourceId: contractMeterId.toString(),
label: `Folgezähler entfernt von Vertrag #${contractId}`,
});
res.json({ success: true, data: null } as ApiResponse);
} catch (error) {
res.status(400).json({
@@ -346,6 +439,12 @@ export async function snoozeContract(req: Request, res: Response): Promise<void>
},
});
await logChange({
req, action: 'UPDATE', resourceType: 'Contract',
resourceId: id.toString(),
label: `Vertrag ${updated.contractNumber} zurückgestellt`,
});
res.json({
success: true,
data: updated,