"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getContracts = getContracts; exports.getContract = getContract; exports.createContract = createContract; exports.updateContract = updateContract; exports.deleteContract = deleteContract; exports.createFollowUp = createFollowUp; exports.getContractPassword = getContractPassword; exports.getSimCardCredentials = getSimCardCredentials; exports.getInternetCredentials = getInternetCredentials; exports.getSipCredentials = getSipCredentials; exports.getCockpit = getCockpit; exports.addSuccessorMeter = addSuccessorMeter; exports.removeContractMeter = removeContractMeter; exports.snoozeContract = snoozeContract; const prisma_js_1 = __importDefault(require("../lib/prisma.js")); const contractService = __importStar(require("../services/contract.service.js")); const contractCockpitService = __importStar(require("../services/contractCockpit.service.js")); const contractHistoryService = __importStar(require("../services/contractHistory.service.js")); const authorizationService = __importStar(require("../services/authorization.service.js")); const audit_service_js_1 = require("../services/audit.service.js"); async function getContracts(req, res) { 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)); res.json({ success: true, data: treeData }); return; } // Für Kundenportal-Benutzer: nur eigene + vertretene Kunden MIT Vollmacht let customerIds; if (req.user?.isCustomerPortal && req.user.customerId) { // Eigene Customer-ID immer customerIds = [req.user.customerId]; // Vertretene Kunden nur wenn Vollmacht erteilt const representedIds = req.user.representedCustomerIds || []; for (const repCustId of representedIds) { const hasAuth = await authorizationService.hasAuthorization(repCustId, req.user.customerId); if (hasAuth) { customerIds.push(repCustId); } } } const result = await contractService.getAllContracts({ customerId: customerId ? parseInt(customerId) : undefined, customerIds, // Wird nur für Kundenportal-Benutzer gesetzt type: type, status: status, search: search, page: page ? parseInt(page) : undefined, limit: limit ? parseInt(limit) : undefined, }); res.json({ success: true, data: result.contracts, pagination: result.pagination, }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Laden der Verträge', }); } } async function getContract(req, res) { try { const contract = await contractService.getContractById(parseInt(req.params.id)); if (!contract) { res.status(404).json({ success: false, error: 'Vertrag nicht gefunden', }); return; } // Für Kundenportal-Benutzer: Zugriff nur auf eigene + vertretene Kunden MIT Vollmacht if (req.user?.isCustomerPortal && req.user.customerId) { const allowedCustomerIds = [req.user.customerId]; const representedIds = req.user.representedCustomerIds || []; for (const repCustId of representedIds) { const hasAuth = await authorizationService.hasAuthorization(repCustId, req.user.customerId); if (hasAuth) { allowedCustomerIds.push(repCustId); } } if (!allowedCustomerIds.includes(contract.customerId)) { res.status(403).json({ success: false, error: 'Kein Zugriff auf diesen Vertrag', }); return; } } res.json({ success: true, data: contract }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Laden des Vertrags', }); } } async function createContract(req, res) { try { const contract = await contractService.createContract(req.body); await (0, audit_service_js_1.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 }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Erstellen des Vertrags', }); } } async function updateContract(req, res) { try { const contractId = parseInt(req.params.id); // Vorherigen Stand laden für Audit-Vergleich const before = await prisma_js_1.default.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 = {}; const fieldLabels = { 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 = { 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[key]; const norm = (v) => (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[key]; const norm = (v) => (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 (0, audit_service_js_1.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 }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Aktualisieren des Vertrags', }); } } async function deleteContract(req, res) { try { const contractId = parseInt(req.params.id); const contract = await prisma_js_1.default.contract.findUnique({ where: { id: contractId }, select: { contractNumber: true, customerId: true } }); await contractService.deleteContract(contractId); await (0, audit_service_js_1.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' }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Löschen des Vertrags', }); } } async function createFollowUp(req, res) { try { const previousContractId = parseInt(req.params.id); // Vorgängervertrag laden für Vertragsnummer const previousContract = await prisma_js_1.default.contract.findUnique({ where: { id: previousContractId }, select: { contractNumber: true }, }); if (!previousContract) { res.status(404).json({ success: false, error: 'Vorgängervertrag nicht gefunden' }); 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); await (0, audit_service_js_1.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 }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Erstellen des Folgevertrags', }); } } async function getContractPassword(req, res) { try { const password = await contractService.getContractPassword(parseInt(req.params.id)); if (password === null) { res.status(404).json({ success: false, error: 'Kein Passwort hinterlegt', }); return; } res.json({ success: true, data: { password } }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Entschlüsseln des Passworts', }); } } async function getSimCardCredentials(req, res) { try { const credentials = await contractService.getSimCardCredentials(parseInt(req.params.simCardId)); res.json({ success: true, data: credentials }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Entschlüsseln der SIM-Karten-Daten', }); } } async function getInternetCredentials(req, res) { try { const credentials = await contractService.getInternetCredentials(parseInt(req.params.id)); res.json({ success: true, data: credentials }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Entschlüsseln des Internet-Passworts', }); } } async function getSipCredentials(req, res) { try { const credentials = await contractService.getSipCredentials(parseInt(req.params.phoneNumberId)); res.json({ success: true, data: credentials }); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Entschlüsseln des SIP-Passworts', }); } } // ==================== VERTRAGS-COCKPIT ==================== async function getCockpit(req, res) { try { const cockpitData = await contractCockpitService.getCockpitData(); res.json({ success: true, data: cockpitData }); } catch (error) { console.error('Cockpit error:', error); res.status(500).json({ success: false, error: 'Fehler beim Laden des Vertrags-Cockpits', }); } } // ==================== FOLGEZÄHLER ==================== async function addSuccessorMeter(req, res) { try { const contractId = parseInt(req.params.id); const { meterId, installedAt, finalReadingPrevious } = req.body; const contract = await prisma_js_1.default.contract.findUnique({ where: { id: contractId }, include: { energyDetails: { include: { contractMeters: { orderBy: { position: 'asc' } } } } }, }); if (!contract?.energyDetails) { res.status(404).json({ success: false, error: 'Energievertrag nicht gefunden' }); return; } const ecdId = contract.energyDetails.id; const existingMeters = contract.energyDetails.contractMeters; const nextPosition = existingMeters.length > 0 ? Math.max(...existingMeters.map(m => m.position)) + 1 : 0; // Vorherigen Zähler als gewechselt markieren if (existingMeters.length > 0 && finalReadingPrevious !== undefined) { const prevMeter = existingMeters[existingMeters.length - 1]; await prisma_js_1.default.contractMeter.update({ where: { id: prevMeter.id }, data: { removedAt: installedAt ? new Date(installedAt) : new Date(), finalReading: parseFloat(finalReadingPrevious), }, }); } const contractMeter = await prisma_js_1.default.contractMeter.create({ data: { energyContractDetailsId: ecdId, meterId: parseInt(meterId), position: nextPosition, installedAt: installedAt ? new Date(installedAt) : new Date(), }, include: { meter: { include: { readings: true } } }, }); // Aktuellen Zähler am Vertrag aktualisieren await prisma_js_1.default.energyContractDetails.update({ where: { id: ecdId }, data: { meterId: parseInt(meterId) }, }); await (0, audit_service_js_1.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 }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Hinzufügen des Folgezählers', }); } } async function removeContractMeter(req, res) { try { const contractMeterId = parseInt(req.params.contractMeterId); const contractId = parseInt(req.params.id); await prisma_js_1.default.contractMeter.delete({ where: { id: contractMeterId } }); await (0, audit_service_js_1.logChange)({ req, action: 'DELETE', resourceType: 'ContractMeter', resourceId: contractMeterId.toString(), label: `Folgezähler entfernt von Vertrag #${contractId}`, }); res.json({ success: true, data: null }); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Fehler beim Entfernen', }); } } // ==================== SNOOZE (VERTRAG ZURÜCKSTELLEN) ==================== async function snoozeContract(req, res) { try { const id = parseInt(req.params.id); const { nextReviewDate, months } = req.body; let reviewDate = 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_js_1.default.contract.update({ where: { id }, data: { nextReviewDate: reviewDate }, select: { id: true, contractNumber: true, nextReviewDate: true, }, }); await (0, audit_service_js_1.logChange)({ req, action: 'UPDATE', resourceType: 'Contract', resourceId: id.toString(), label: `Vertrag ${updated.contractNumber} zurückgestellt`, }); res.json({ success: true, data: updated, message: reviewDate ? `Vertrag zurückgestellt bis ${reviewDate.toLocaleDateString('de-DE')}` : 'Zurückstellung aufgehoben', }); } catch (error) { console.error('Snooze error:', error); res.status(500).json({ success: false, error: 'Fehler beim Zurückstellen des Vertrags', }); } } //# sourceMappingURL=contract.controller.js.map