Neue Seite Rechnungen → Export mit Zeitraum-/Status-Filter und drei Downloads, nie Entwürfe: - Rechnungsjournal als CSV (Semikolon, Dezimalkomma, UTF-8-BOM): Nummer, Datum, Kunde, Netto/USt/Brutto, effektiver USt-Satz, Status, Zahlungsdatum, aufgelöste Storno-Referenzen. - Alle zugehörigen Rechnungs-PDFs als ZIP (die eigentlichen Belege, wie von GoBD neben dem Journal verlangt), gebaut über das lokale zip-Binary wie in ops/backup.ts. - Echter DATEV-Buchungsstapel (EXTF, Format 700/21) zum direkten Import beim Steuerberater. Pro Rechnung eine Buchungszeile je USt-Satz-Gruppe (Automatikkonten-Verfahren: Konto = aus der Kundennummer abgeleitetes Debitorenkonto, Gegenkonto = konfiguriertes Erlöskonto des jeweiligen Satzes – kein geratener BU-Schlüssel nötig). Kopf- und Buchungszeilen-Layout (31 bzw. 125 Felder) gegen die offizielle DATEV-Formatbeschreibung und ein reales Beispiel aus github.com/ledermann/datev verifiziert, nicht aus dem Gedächtnis geraten. Ausgabe in Windows-1252 (eigener Encoder: Node kennt nur striktes ISO-8859-1, das den Halbgeviertstrich in den DATEV- Spaltennamen stillschweigend verstümmelt hätte – beim Testlauf gefunden und behoben). Dafür neue DATEV-Stammdaten unter Einstellungen → Firma (Berater-/ Mandantennummer, SKR, Sachkontenlänge, Wirtschaftsjahresbeginn, Erlöskonten je Steuersatz; Migration 031). Ohne diese Angaben liefert der DATEV-Export eine klare Fehlermeldung statt einer falschen Datei; das CSV/ZIP funktioniert unabhängig davon immer. Gegen echte Rechnungen (RE-1001/RE-1002, Original + Storno) über eine temporäre Test-Session verifiziert: CSV/ZIP/DATEV liefern korrekte Inhalte, Storno kehrt Soll/Haben korrekt um, Feldzahlen exakt 31/125, Encoding rund-trip-fest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| apps | ||
| bundles | ||
| data | ||
| docs | ||
| migrations | ||
| ops | ||
| packages | ||
| .env.example | ||
| .gitignore | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
Kundencenter
Modulares Kundencenter für Hosting, Server und Lizenzen (Modularer Monolith, TypeScript).
| Prozess | Pfad | Port (nur 127.0.0.1) | Aufgabe |
|---|---|---|---|
kc-api |
apps/api |
4100 | Fastify-API (/v1/*), Module, Policy, Audit |
kc-worker |
apps/worker |
4102 (Health) | Persistente Aufträge, Discord-Bot |
kc-web |
apps/web |
4101 | Next.js-Oberfläche, Proxy /api/* → API |
Stand (Grundsystem)
Login mit Passwort + TOTP, Wiederherstellungscodes, Sitzungsverwaltung, Passwort-Reset, Einladungen, Benutzerverwaltung (Mitarbeiterrollen),
Kunden/Organisationen anlegen und verwalten, Rechnungsanschrift, Audit-Protokoll mit Hash-Kette, persistente Job-Queue, Discord-Bot (optional).
Produkte (versioniert), Bestellungen mit Freigabe und unveränderlichem Preis-Snapshot, Verträge mit Kündigung/Verlängerung, Provisionierung über Connectoren (docs/produkte-bestellungen-vertraege.md), Connector-Framework (docs/connector-vertrag.md).
Support-Tickets (je Kunde, mit internen Notizen für Personal, Dateianhänge als Bild/PDF, Discord-Benachrichtigung).
Rechnungen (docs/rechnungen.md): Entwurf → Ausstellen (unveränderlich) → Bezahlt/Storno, PDF-Erzeugung, Firmenstammdaten unter Einstellungen → Firma.
Noch nicht vorhanden: Zahlungsanbieter-Anbindung, automatische wiederkehrende Rechnungsstellung, E-Mail-Versand von Rechnungen, Selbstregistrierung, Plesk-Connector, Domain-Registrierung über eine Registrar-API (aktuell manuell über die Domain-Aufstellung).
Modularität
API-Module liegen in apps/api/src/modules/<name> und implementieren KcModule (core/module.ts).
Sie werden in modules/index.ts eingetragen, registrieren Routen und Rechte und sprechen nur über core/* und Jobs miteinander.
Neue Fähigkeiten (Connectoren, Rechnungen, Tickets) kommen als weitere Module hinzu.
Konfiguration
Geheimnisse liegen nicht im Repo, sondern in /etc/kundencenter/*.env (chmod 600): db.env, app.env, discord.env, optional SMTP in app.env.
Vorlage: .env.example. Wichtig: KC_BASE_URL muss exakt der Browser-URL entsprechen (Origin-Prüfung gegen CSRF).
Betrieb
systemctl status kc-api kc-worker kc-web # Dienste (Autostart, Restart=always)
journalctl -u kc-api -f # Logs
pnpm install && pnpm build # Update: danach chown -R kundencenter und systemctl restart kc-*
pnpm migrate # Migrationen (migrations/*.sql, einmalig je Datei)
pnpm cli create-superadmin --email=… --name=… --password=…
Health: GET :4100/v1/health, GET :4100/v1/ready, GET :4102/health.
Tests
cd apps/api && pnpm test – Integrationstests gegen eine separate Datenbank kundencenter_test (wird bei jedem Lauf neu erstellt).
Abgedeckt: Login, Sperre, CSRF, 2FA-Pflicht, Sitzungen, Kundenanlage, Mandantentrennung, Rechte, Audit-Kette/Maskierung.
Discord-Bot
In /etc/kundencenter/discord.env setzen: DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, DISCORD_ADMIN_CHANNEL_ID, DISCORD_STAFF_USER_IDS (kommagetrennt), dann systemctl restart kc-worker.
Befehle: /kc-status, /kc-kunde suche:<Text> (nur für freigegebene Discord-User, Antworten nur für sie sichtbar). In Kanäle gehen nur Ereignisse ohne personenbezogene Daten (z. B. „Neuer Kunde angelegt: K-10001“).
Backup / Restore (Datenbank)
set -a; . /etc/kundencenter/db.env; set +a
MYSQL_PWD=$DB_PASSWORD mysqldump -h127.0.0.1 -u$DB_USER --single-transaction --routines $DB_NAME | gzip > kundencenter-$(date +%F).sql.gz
gunzip -c kundencenter-DATUM.sql.gz | MYSQL_PWD=$DB_PASSWORD mysql -h127.0.0.1 -u$DB_USER $DB_NAME # Restore in leere DB
Automatisiertes, verschlüsseltes Backup mit Restore-Test ist noch offen (siehe Plane).