Gutschriften Phase 3a: Firmenstammdaten (Absender) fuer PDF/ZUGFeRD

Neues CompanyProfile-Modell (Einzel-Zeile) + Migration (IF NOT EXISTS):
Firmenname, Anschrift, USt-IdNr, Steuernummer, Handelsregister,
Geschaeftsfuehrer, Kontakt, IBAN/BIC/Bank. Fliesst spaeter in
Gutschrift-PDF + ZUGFeRD-Verkaeuferdaten ein.

Backend: Service (getOrCreate/update mit Feld-Whitelist), Controller,
Routes GET/PUT /api/company-profile (settings:read/update), auditiert.

Frontend: Settings-Seite 'Firmenstammdaten (Absender)'
(/settings/company-profile) mit Firma/Anschrift, Steuer/Register,
Kontakt/Bank. Typ + companyProfileApi + Menue-Eintrag.

Phase 3b (PDF + ZUGFeRD-Erzeugung) folgt darauf auf.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:19:36 +02:00
co-authored by Claude Opus 4.8
parent 9e38cb32f0
commit 2c705272ea
11 changed files with 279 additions and 1 deletions
@@ -0,0 +1,28 @@
// ==================== FIRMENSTAMMDATEN (ABSENDER) ====================
// Einzel-Zeile. Fließt in Gutschrift-PDF + ZUGFeRD-XML (Verkäufer) ein.
import prisma from '../lib/prisma.js';
import { Prisma } from '@prisma/client';
const EDITABLE_STRING_FIELDS = [
'name', 'street', 'houseNumber', 'postalCode', 'city', 'country',
'vatId', 'taxNumber', 'commercialRegister', 'managingDirector',
'email', 'phone', 'website', 'iban', 'bic', 'bankName',
] as const;
export async function getOrCreateProfile() {
const existing = await prisma.companyProfile.findFirst();
if (existing) return existing;
return prisma.companyProfile.create({ data: {} });
}
export async function updateProfile(input: Record<string, unknown>) {
const profile = await getOrCreateProfile();
const data: Prisma.CompanyProfileUpdateInput = {};
for (const field of EDITABLE_STRING_FIELDS) {
if (typeof input[field] === 'string') {
(data as Record<string, unknown>)[field] = (input[field] as string).slice(0, 191).trim();
}
}
return prisma.companyProfile.update({ where: { id: profile.id }, data });
}