added invoices and status in cockpit, created info button for contract status types

This commit is contained in:
2026-02-08 01:18:12 +01:00
parent 1ad4fe0819
commit aee48a8ccb
45 changed files with 4543 additions and 863 deletions
+18
View File
@@ -185,6 +185,24 @@ router.post(
cachedEmailController.saveEmailAsPdf
);
// E-Mail als PDF exportieren und als Rechnung speichern
// POST /api/emails/:id/save-as-invoice { invoiceDate, invoiceType, notes? }
router.post(
'/emails/:id/save-as-invoice',
authenticate,
requirePermission('contracts:update'),
cachedEmailController.saveEmailAsInvoice
);
// Anhang als Rechnung speichern
// POST /api/emails/:id/attachments/:filename/save-as-invoice { invoiceDate, invoiceType, notes? }
router.post(
'/emails/:id/attachments/:filename/save-as-invoice',
authenticate,
requirePermission('contracts:update'),
cachedEmailController.saveAttachmentAsInvoice
);
// ==================== VERTRAGSZUORDNUNG ====================
// E-Mail Vertrag zuordnen
+54
View File
@@ -0,0 +1,54 @@
import { Router } from 'express';
import * as invoiceController from '../controllers/invoice.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// ==================== INVOICE CRUD ====================
// Alle Rechnungen für ein EnergyContractDetails abrufen
// GET /api/energy-details/:ecdId/invoices
router.get(
'/:ecdId/invoices',
authenticate,
requirePermission('contracts:read'),
invoiceController.getInvoices
);
// Einzelne Rechnung abrufen
// GET /api/energy-details/:ecdId/invoices/:invoiceId
router.get(
'/:ecdId/invoices/:invoiceId',
authenticate,
requirePermission('contracts:read'),
invoiceController.getInvoice
);
// Neue Rechnung hinzufügen
// POST /api/energy-details/:ecdId/invoices
router.post(
'/:ecdId/invoices',
authenticate,
requirePermission('contracts:update'),
invoiceController.addInvoice
);
// Rechnung aktualisieren
// PUT /api/energy-details/:ecdId/invoices/:invoiceId
router.put(
'/:ecdId/invoices/:invoiceId',
authenticate,
requirePermission('contracts:update'),
invoiceController.updateInvoice
);
// Rechnung löschen
// DELETE /api/energy-details/:ecdId/invoices/:invoiceId
router.delete(
'/:ecdId/invoices/:invoiceId',
authenticate,
requirePermission('contracts:delete'),
invoiceController.deleteInvoice
);
export default router;
+95
View File
@@ -658,4 +658,99 @@ router.delete(
(req: AuthRequest, res: Response) => handleContractDocumentDelete(req, res, 'cancellationConfirmationOptionsPath')
);
// ==================== RECHNUNGS-DOKUMENTE ====================
// Upload für Rechnungs-Dokument
router.post(
'/invoices/:id',
authenticate,
requirePermission('contracts:update'),
setUploadDir('invoices'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
const invoiceId = parseInt(req.params.id);
const relativePath = `/uploads/invoices/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const invoice = await prisma.invoice.findUnique({ where: { id: invoiceId } });
if (!invoice) {
res.status(404).json({ success: false, error: 'Rechnung nicht gefunden' });
return;
}
if (invoice.documentPath) {
const oldPath = path.join(process.cwd(), invoice.documentPath);
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
}
// Invoice in der DB aktualisieren
await prisma.invoice.update({
where: { id: invoiceId },
data: { documentPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Invoice upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Löschen von Rechnungs-Dokument
router.delete(
'/invoices/:id',
authenticate,
requirePermission('contracts:update'),
async (req: AuthRequest, res: Response) => {
try {
const invoiceId = parseInt(req.params.id);
const invoice = await prisma.invoice.findUnique({ where: { id: invoiceId } });
if (!invoice) {
res.status(404).json({ success: false, error: 'Rechnung nicht gefunden' });
return;
}
if (!invoice.documentPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei löschen
const filePath = path.join(process.cwd(), invoice.documentPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.invoice.update({
where: { id: invoiceId },
data: { documentPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Invoice delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
export default router;