added place to telecommunication, added contract documents, added invoice to other contracts

This commit is contained in:
2026-03-25 16:55:48 +01:00
parent eaa94e766a
commit 3dd4f7b656
30 changed files with 3424 additions and 90 deletions
@@ -410,6 +410,105 @@ export async function removeContractMeter(req: AuthRequest, res: Response): Prom
}
}
// ==================== VERTRAGSDOKUMENTE ====================
export async function getContractDocuments(req: AuthRequest, res: Response): Promise<void> {
try {
const contractId = parseInt(req.params.id);
const documents = await prisma.contractDocument.findMany({
where: { contractId },
orderBy: { createdAt: 'desc' },
});
res.json({ success: true, data: documents } as ApiResponse);
} catch (error) {
res.status(500).json({ success: false, error: 'Fehler beim Laden der Dokumente' } as ApiResponse);
}
}
export async function uploadContractDocument(req: AuthRequest, res: Response): Promise<void> {
try {
const contractId = parseInt(req.params.id);
const { documentType, notes } = req.body;
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' } as ApiResponse);
return;
}
if (!documentType) {
res.status(400).json({ success: false, error: 'Dokumenttyp erforderlich' } as ApiResponse);
return;
}
const documentPath = `/uploads/contract-documents/${req.file.filename}`;
const doc = await prisma.contractDocument.create({
data: {
contractId,
documentType,
documentPath,
originalName: req.file.originalname,
notes: notes || null,
uploadedBy: req.user?.email,
},
});
const contract = await prisma.contract.findUnique({ where: { id: contractId }, select: { contractNumber: true, customerId: true } });
await logChange({
req, action: 'CREATE', resourceType: 'ContractDocument',
resourceId: doc.id.toString(),
label: `Dokument "${documentType}" hochgeladen für Vertrag ${contract?.contractNumber}`,
details: { typ: documentType, datei: req.file.originalname },
customerId: contract?.customerId,
});
res.status(201).json({ success: true, data: doc } as ApiResponse);
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim Hochladen',
} as ApiResponse);
}
}
export async function deleteContractDocument(req: AuthRequest, res: Response): Promise<void> {
try {
const documentId = parseInt(req.params.documentId);
const contractId = parseInt(req.params.id);
const doc = await prisma.contractDocument.findUnique({ where: { id: documentId } });
if (!doc || doc.contractId !== contractId) {
res.status(404).json({ success: false, error: 'Dokument nicht gefunden' } as ApiResponse);
return;
}
// Datei löschen
const fs = await import('fs');
const path = await import('path');
const filePath = path.join(process.cwd(), doc.documentPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
await prisma.contractDocument.delete({ where: { id: documentId } });
const contract = await prisma.contract.findUnique({ where: { id: contractId }, select: { contractNumber: true, customerId: true } });
await logChange({
req, action: 'DELETE', resourceType: 'ContractDocument',
resourceId: documentId.toString(),
label: `Dokument "${doc.documentType}" gelöscht von Vertrag ${contract?.contractNumber}`,
details: { typ: doc.documentType, datei: doc.originalName },
customerId: contract?.customerId,
});
res.json({ success: true, message: 'Dokument gelöscht' } as ApiResponse);
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim Löschen',
} as ApiResponse);
}
}
// ==================== SNOOZE (VERTRAG ZURÜCKSTELLEN) ====================
export async function snoozeContract(req: Request, res: Response): Promise<void> {
@@ -143,3 +143,38 @@ export async function deleteInvoice(req: Request, res: Response): Promise<void>
} as ApiResponse);
}
}
// ==================== CONTRACT-BASIERTE RECHNUNGEN (für alle Vertragstypen) ====================
export async function getInvoicesByContract(req: Request, res: Response): Promise<void> {
try {
const contractId = parseInt(req.params.id);
const invoices = await invoiceService.getInvoicesByContract(contractId);
res.json({ success: true, data: invoices } as ApiResponse);
} catch (error) {
res.status(500).json({ success: false, error: 'Fehler beim Laden der Rechnungen' } as ApiResponse);
}
}
export async function addInvoiceByContract(req: Request, res: Response): Promise<void> {
try {
const contractId = parseInt(req.params.id);
const { invoiceDate, invoiceType, notes } = req.body;
const invoice = await invoiceService.addInvoiceByContract(contractId, {
invoiceDate: new Date(invoiceDate),
invoiceType,
notes,
});
await logChange({
req, action: 'CREATE', resourceType: 'Invoice',
resourceId: invoice.id.toString(),
label: `Rechnung (${invoiceType}) hinzugefügt`,
});
res.status(201).json({ success: true, data: invoice } as ApiResponse);
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim Hinzufügen',
} as ApiResponse);
}
}