opencrm/backend/src/controllers/contract.controller.ts

275 lines
9.0 KiB
TypeScript

import { Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
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 { ApiResponse, AuthRequest } from '../types/index.js';
const prisma = new PrismaClient();
export async function getContracts(req: AuthRequest, res: Response): Promise<void> {
try {
const { customerId, type, status, search, page, limit, tree } = req.query;
// Baumstruktur für Kundenansicht
if (tree === 'true' && customerId) {
const treeData = await contractService.getContractTreeForCustomer(
parseInt(customerId as string)
);
res.json({ success: true, data: treeData } as ApiResponse);
return;
}
// Für Kundenportal-Benutzer: nur eigene + vertretene Kunden-Verträge anzeigen
let customerIds: number[] | undefined;
if (req.user?.isCustomerPortal && req.user.customerId) {
// Eigene Customer-ID + alle vertretenen Kunden-IDs
customerIds = [req.user.customerId, ...(req.user.representedCustomerIds || [])];
}
const result = await contractService.getAllContracts({
customerId: customerId ? parseInt(customerId as string) : undefined,
customerIds, // Wird nur für Kundenportal-Benutzer gesetzt
type: type as any,
status: status as any,
search: search as string,
page: page ? parseInt(page as string) : undefined,
limit: limit ? parseInt(limit as string) : undefined,
});
res.json({
success: true,
data: result.contracts,
pagination: result.pagination,
} as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Laden der Verträge',
} as ApiResponse);
}
}
export async function getContract(req: AuthRequest, res: Response): Promise<void> {
try {
const contract = await contractService.getContractById(parseInt(req.params.id));
if (!contract) {
res.status(404).json({
success: false,
error: 'Vertrag nicht gefunden',
} as ApiResponse);
return;
}
// Für Kundenportal-Benutzer: Zugriff nur auf eigene + vertretene Kunden-Verträge
if (req.user?.isCustomerPortal && req.user.customerId) {
const allowedCustomerIds = [req.user.customerId, ...(req.user.representedCustomerIds || [])];
if (!allowedCustomerIds.includes(contract.customerId)) {
res.status(403).json({
success: false,
error: 'Kein Zugriff auf diesen Vertrag',
} as ApiResponse);
return;
}
}
res.json({ success: true, data: contract } as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Laden des Vertrags',
} as ApiResponse);
}
}
export async function createContract(req: Request, res: Response): Promise<void> {
try {
const contract = await contractService.createContract(req.body);
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 des Vertrags',
} as ApiResponse);
}
}
export async function updateContract(req: Request, res: Response): Promise<void> {
try {
const contract = await contractService.updateContract(parseInt(req.params.id), req.body);
res.json({ success: true, data: contract } as ApiResponse);
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim Aktualisieren des Vertrags',
} as ApiResponse);
}
}
export async function deleteContract(req: Request, res: Response): Promise<void> {
try {
await contractService.deleteContract(parseInt(req.params.id));
res.json({ success: true, message: 'Vertrag gelöscht' } as ApiResponse);
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim Löschen des Vertrags',
} as ApiResponse);
}
}
export async function createFollowUp(req: AuthRequest, res: Response): Promise<void> {
try {
const previousContractId = parseInt(req.params.id);
// Vorgängervertrag laden für Vertragsnummer
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.createFollowUpContract(previousContractId);
const createdBy = req.user?.email || 'unbekannt';
// Historie-Eintrag für den Vorgängervertrag erstellen
await contractHistoryService.createFollowUpHistoryEntry(
previousContractId,
contract.contractNumber,
createdBy
);
// Historie-Eintrag für den neuen Folgevertrag erstellen
await contractHistoryService.createNewContractFromPredecessorEntry(
contract.id,
previousContract.contractNumber,
createdBy
);
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 des Folgevertrags',
} as ApiResponse);
}
}
export async function getContractPassword(req: Request, res: Response): Promise<void> {
try {
const password = await contractService.getContractPassword(parseInt(req.params.id));
if (password === null) {
res.status(404).json({
success: false,
error: 'Kein Passwort hinterlegt',
} as ApiResponse);
return;
}
res.json({ success: true, data: { password } } as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Entschlüsseln des Passworts',
} as ApiResponse);
}
}
export async function getSimCardCredentials(req: Request, res: Response): Promise<void> {
try {
const credentials = await contractService.getSimCardCredentials(parseInt(req.params.simCardId));
res.json({ success: true, data: credentials } as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Entschlüsseln der SIM-Karten-Daten',
} as ApiResponse);
}
}
export async function getInternetCredentials(req: Request, res: Response): Promise<void> {
try {
const credentials = await contractService.getInternetCredentials(parseInt(req.params.id));
res.json({ success: true, data: credentials } as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Entschlüsseln des Internet-Passworts',
} as ApiResponse);
}
}
export async function getSipCredentials(req: Request, res: Response): Promise<void> {
try {
const credentials = await contractService.getSipCredentials(parseInt(req.params.phoneNumberId));
res.json({ success: true, data: credentials } as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: 'Fehler beim Entschlüsseln des SIP-Passworts',
} as ApiResponse);
}
}
// ==================== VERTRAGS-COCKPIT ====================
export async function getCockpit(req: AuthRequest, res: Response): Promise<void> {
try {
const cockpitData = await contractCockpitService.getCockpitData();
res.json({ success: true, data: cockpitData } as ApiResponse);
} catch (error) {
console.error('Cockpit error:', error);
res.status(500).json({
success: false,
error: 'Fehler beim Laden des Vertrags-Cockpits',
} as ApiResponse);
}
}
// ==================== SNOOZE (VERTRAG ZURÜCKSTELLEN) ====================
export async function snoozeContract(req: Request, res: Response): Promise<void> {
try {
const id = parseInt(req.params.id);
const { nextReviewDate, months } = req.body;
let reviewDate: Date | null = null;
if (nextReviewDate) {
// Explizites Datum angegeben
reviewDate = new Date(nextReviewDate);
} else if (months) {
// Monate angegeben → berechne Datum
reviewDate = new Date();
reviewDate.setMonth(reviewDate.getMonth() + months);
}
// Wenn beides leer → nextReviewDate wird auf null gesetzt (Snooze aufheben)
const updated = await prisma.contract.update({
where: { id },
data: { nextReviewDate: reviewDate },
select: {
id: true,
contractNumber: true,
nextReviewDate: true,
},
});
res.json({
success: true,
data: updated,
message: reviewDate
? `Vertrag zurückgestellt bis ${reviewDate.toLocaleDateString('de-DE')}`
: 'Zurückstellung aufgehoben',
} as ApiResponse);
} catch (error) {
console.error('Snooze error:', error);
res.status(500).json({
success: false,
error: 'Fehler beim Zurückstellen des Vertrags',
} as ApiResponse);
}
}