Gutschriften: Dateien beim Loeschen/Bearbeiten mit aufraeumen (R138)

Pentester-Hinweis: generierte Gutschrift-PDFs blieben nach dem Loeschen
der Gutschrift als verwaiste Files im Upload-Ordner liegen (harmlos, da
ohne DB-Referenz nicht mehr abrufbar - aber unsauber).

deleteCreditNote entfernt jetzt PDF (pdfPath) + Ueberweisungsbeleg
(receiptPath) von der Platte. updateCreditNote loescht das alte PDF
beim Leeren von pdfPath. Kein verwaister Ordner-Muell mehr.

Verifiziert: PDF nach Erzeugung vorhanden, nach Loeschen der Gutschrift
weg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-08 22:31:33 +02:00
co-authored by Claude Opus 4.8
parent 5fb01627b0
commit a6b1dac606
2 changed files with 25 additions and 1 deletions
+22 -1
View File
@@ -2,11 +2,26 @@
// CRUD für Vertrags-Gutschriften (Subventionen: Geld/Sachwert) inkl. // CRUD für Vertrags-Gutschriften (Subventionen: Geld/Sachwert) inkl.
// USt-Berechnung (pro Gutschrift wählbar: vatRelevant + Basis Netto/Brutto). // 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 prisma from '../lib/prisma.js';
import { ApiError } from '../utils/apiError.js'; import { ApiError } from '../utils/apiError.js';
import { assignNextNumber } from './creditNoteNumberRange.service.js'; import { assignNextNumber } from './creditNoteNumberRange.service.js';
import { CreditNoteType, CreditNoteCustomerType, CreditNoteAmountBasis } from '@prisma/client'; 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; const round2 = (n: number) => Math.round((n + Number.EPSILON) * 100) / 100;
export interface AmountResult { 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 // Nummer bleibt unverändert (einmal vergeben = fix). Ein evtl. schon
// erzeugtes PDF ist nach inhaltlicher Änderung veraltet → Pfad leeren // 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 } }); return prisma.creditNote.update({ where: { id }, data: { ...normalized, pdfPath: null } });
} }
@@ -239,6 +256,10 @@ export async function deleteCreditNote(id: number) {
if (!existing) { if (!existing) {
throw new ApiError(404, 'Gutschrift nicht gefunden'); 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 } }); return prisma.creditNote.delete({ where: { id } });
} }
+3
View File
@@ -123,6 +123,9 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
das Gutschrift-**PDF** (`credit-notes`) bleibt vertragsbasiert (eigener das Gutschrift-**PDF** (`credit-notes`) bleibt vertragsbasiert (eigener
Kunde darf seine Gutschrift laden). Anlegen/Beleg-Upload für Portal war Kunde darf seine Gutschrift laden). Anlegen/Beleg-Upload für Portal war
bereits per `blockPortal`/Portal-403 gesperrt. 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) - [~] **🧾 Gutschriftsverwaltung (Subventionen am Vertrag) Phase 1: Backend** (2026-08-06)
- Use-Case: zu einem Vertrag kann eine Subvention gewährt werden **Geld** - Use-Case: zu einem Vertrag kann eine Subvention gewährt werden **Geld**