diff --git a/backend/src/services/creditNote.service.ts b/backend/src/services/creditNote.service.ts index d5803b96..3157fe60 100644 --- a/backend/src/services/creditNote.service.ts +++ b/backend/src/services/creditNote.service.ts @@ -2,11 +2,26 @@ // CRUD für Vertrags-Gutschriften (Subventionen: Geld/Sachwert) inkl. // USt-Berechnung (pro Gutschrift wählbar: vatRelevant + Basis Netto/Brutto). +import fs from 'fs'; +import path from 'path'; import prisma from '../lib/prisma.js'; import { ApiError } from '../utils/apiError.js'; import { assignNextNumber } from './creditNoteNumberRange.service.js'; import { CreditNoteType, CreditNoteCustomerType, CreditNoteAmountBasis } from '@prisma/client'; +// Löscht eine hochgeladene/erzeugte Datei von der Platte (best effort). +function deleteFileIfExists(filePath: string | null) { + if (!filePath) return; + const absolute = path.join(process.cwd(), filePath); + if (fs.existsSync(absolute)) { + try { + fs.unlinkSync(absolute); + } catch (error) { + console.error('Fehler beim Löschen der Gutschrift-Datei:', absolute, error); + } + } +} + const round2 = (n: number) => Math.round((n + Number.EPSILON) * 100) / 100; export interface AmountResult { @@ -230,7 +245,9 @@ export async function updateCreditNote(id: number, input: CreateCreditNoteInput) } // Nummer bleibt unverändert (einmal vergeben = fix). Ein evtl. schon // erzeugtes PDF ist nach inhaltlicher Änderung veraltet → Pfad leeren - // (der User erzeugt es bei Bedarf neu). + // (der User erzeugt es bei Bedarf neu) und die alte Datei entfernen, + // damit sie nicht verwaist liegen bleibt. + if (existing.pdfPath) deleteFileIfExists(existing.pdfPath); return prisma.creditNote.update({ where: { id }, data: { ...normalized, pdfPath: null } }); } @@ -239,6 +256,10 @@ export async function deleteCreditNote(id: number) { if (!existing) { throw new ApiError(404, 'Gutschrift nicht gefunden'); } + // Verwaiste Dateien vermeiden: generiertes PDF + Überweisungsbeleg von der + // Platte entfernen (Pentest R138-Hinweis). + deleteFileIfExists(existing.pdfPath); + deleteFileIfExists(existing.receiptPath); return prisma.creditNote.delete({ where: { id } }); } diff --git a/docs/todo.md b/docs/todo.md index 8c4ec332..58a8d312 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -123,6 +123,9 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung das Gutschrift-**PDF** (`credit-notes`) bleibt vertragsbasiert (eigener Kunde darf seine Gutschrift laden). Anlegen/Beleg-Upload für Portal war bereits per `blockPortal`/Portal-403 gesperrt. + - **Datei-Cleanup (R138-Hinweis):** Beim Löschen einer Gutschrift werden + PDF + Beleg von der Platte entfernt; beim Bearbeiten (pdfPath wird + geleert) wird das alte PDF gelöscht → keine verwaisten Dateien mehr. - [~] **🧾 Gutschriftsverwaltung (Subventionen am Vertrag) – Phase 1: Backend** (2026-08-06) - Use-Case: zu einem Vertrag kann eine Subvention gewährt werden – **Geld**