- Updated CustomersPage.vue to use decimalString for standard discount percent. - Enhanced IncomingInvoicesPage.vue to format item quantities, unit prices, and tax rates using decimalString. - Improved ItemsPage.vue with new supplier price management and decimal formatting for prices. - Modified OrganizationSetupPage.vue to use a dropdown for default tax rates and ensure numeric input for payment days. - Updated OutgoingInvoicesPage.vue to apply decimal formatting for customer discounts and item details. - Enhanced PriceImportsPage.vue to include additional fields in the import format. - Improved PriceRulesPage.vue to use decimal input for markup percentages. - Updated QuotesPage.vue to apply decimal formatting for customer discounts and item details. - Enhanced SuppliersPage.vue to use decimal input for standard discount percent. - Added a new SQL migration to set default unit for items to 'Stck'. - Introduced format.ts for centralized decimal and currency formatting utilities.
2353 lines
66 KiB
Markdown
2353 lines
66 KiB
Markdown
# Planung Firmen-Software
|
|
|
|
Stand: 2026-05-22
|
|
|
|
## Zielbild
|
|
|
|
Die Software wird als mehrplatzfähiges Firmensystem geplant:
|
|
|
|
- lokaler Betrieb mit eigenem Server bei einer Firma
|
|
- öffentlicher SaaS-Betrieb mit vielen Firmen auf einer Instanz
|
|
- Backend in Rust
|
|
- PostgreSQL als Datenbank
|
|
- Kommunikation zwischen Clients und Backend per WebSocket für Live-Daten
|
|
- zusätzliche REST-API für externe Zugriffe, Importe und Integrationen
|
|
- Webfrontend für Browser
|
|
- nativer Desktopclient für Linux, Windows und macOS
|
|
- gleiche fachliche Funktionen in Webclient und Desktopclient
|
|
- Live-Aktualisierung der UI, wenn Backend-Daten geändert werden
|
|
- verschlüsselte Kommunikation zwischen Clients und Backend
|
|
- Offline-Fähigkeit im Desktopclient
|
|
- automatische Updates für den Desktopclient
|
|
|
|
## Begriffe
|
|
|
|
Der Begriff `Kunde` wird nur für Kunden einer Firma verwendet, also z.B. für
|
|
Rechnungsempfänger.
|
|
|
|
Für die Betreiber-/Mandantenebene wird stattdessen `Firma` verwendet. Eine Firma
|
|
ist die Organisation, die das System nutzt und ein eigenes PostgreSQL-Schema
|
|
bekommt.
|
|
|
|
Technische Begriffe:
|
|
|
|
- `Firma`: fachlicher Mandant, also Nutzerorganisation des Systems
|
|
- `organization`: technischer Name für eine Firma in Code, API und Datenbank
|
|
- `Firmenschema`: PostgreSQL-Schema einer Firma, z.B. `company_<organization_id>`
|
|
- `User`: globaler Benutzeraccount in `public`
|
|
- `Kunde`: Kunde einer Firma innerhalb eines Firmenschemas
|
|
- `Vorgang`: fachlicher Sammelbegriff für Aktivität, Aufgabe, Wiedervorlage,
|
|
Termin oder interne Bearbeitung mit Bezug zu Kunden, Lieferanten, Dokumenten
|
|
oder Belegen
|
|
- `activity`: technischer Name für einen Vorgang in Code, API und Datenbank
|
|
|
|
Namensregel:
|
|
|
|
- Tabellen, Spalten, API-Felder und Code-Bezeichner sind englisch.
|
|
- Die UI zeigt deutsche Begriffe und Labels.
|
|
- Deutsche UI-Texte, Fehlermeldungen und Dokumentation verwenden echte Umlaute
|
|
(`ä`, `ö`, `ü`, `Ä`, `Ö`, `Ü`) und `ß`. ASCII-Umschreibungen wie `ae`,
|
|
`oe`, `ue` oder `ss` werden nur in technischen Bezeichnern verwendet, wenn
|
|
das nötig ist.
|
|
- Beispiel: Datenbank/API `organization`, UI `Firma`.
|
|
|
|
## Mandantenmodell
|
|
|
|
Jede Firma bekommt ein eigenes PostgreSQL-Schema. Das trennt operative Daten im
|
|
SaaS-Betrieb klar voneinander und funktioniert gleichzeitig für lokale
|
|
Einzelfirmen-Installationen.
|
|
|
|
Wichtige Entscheidung:
|
|
|
|
- In `public` liegen nur globale Daten, die für Login und Firmenzuordnung
|
|
notwendig sind.
|
|
- Alle fachlichen Daten, Rollen, Rechte, Einstellungen, Nummernkreise und Logs
|
|
liegen im jeweiligen Firmenschema.
|
|
- Ein User kann mehreren Firmen zugeordnet sein.
|
|
- Eine Firma verwaltet nicht mehrere weitere Firmen/Mandanten in derselben
|
|
Installation.
|
|
|
|
### `public`-Schema
|
|
|
|
`public` bleibt bewusst klein.
|
|
|
|
Geplante Tabellen:
|
|
|
|
- `users`
|
|
- `organizations`
|
|
- `user_organizations`
|
|
- `organization_domains`, optional für SaaS-Subdomains
|
|
- `auth_identities`, für Passwortlogin, SSO und externe Identity Provider
|
|
- `sessions` oder `refresh_tokens`
|
|
|
|
Nicht in `public`:
|
|
|
|
- Rollen
|
|
- Rechte
|
|
- Audit-Log
|
|
- Fachmodule
|
|
- Nummernkreise
|
|
- API-Zugangsdaten
|
|
- Firmeneinstellungen
|
|
|
|
Diese Daten gehören in das jeweilige Firmenschema.
|
|
|
|
### Firmenschema
|
|
|
|
Schema-Namensvorschlag:
|
|
|
|
- `company_<organization_id>`
|
|
|
|
`organization_id` ist eine technische, nicht sprechende Kennung. Schema-Namen
|
|
enthalten keine Firmennamen.
|
|
|
|
Geplante Tabellen je Firmenschema:
|
|
|
|
- `roles`
|
|
- `permissions`
|
|
- `role_permissions`
|
|
- `user_roles`
|
|
- `settings`
|
|
- `number_ranges`
|
|
- `audit_log`
|
|
- `customers`
|
|
- `suppliers`
|
|
- `contacts`
|
|
- `addresses`
|
|
- `items`
|
|
- `item_price_sources`
|
|
- `item_prices`
|
|
- `price_rules`
|
|
- `customer_price_terms`
|
|
- `supplier_price_terms`
|
|
- `cash_discount_terms`
|
|
- `stock_movements`
|
|
- `quotes`
|
|
- `quote_versions`
|
|
- `quote_items`
|
|
- `incoming_invoices`
|
|
- `incoming_invoice_items`
|
|
- `outgoing_invoices`
|
|
- `outgoing_invoice_items`
|
|
- `communications`
|
|
- `activities`
|
|
- `activity_links`
|
|
- `tasks`
|
|
- `document_templates`
|
|
- `documents`
|
|
- `document_versions`
|
|
- `imports`
|
|
- `import_mappings`
|
|
- `api_connectors`
|
|
- `webhooks`
|
|
|
|
### Mandantenauflösung
|
|
|
|
Vorgeschlagener Ablauf:
|
|
|
|
1. User meldet sich global an.
|
|
2. Backend liest aus `public.user_organizations`, welchen Firmen der User zugeordnet ist.
|
|
3. Falls der User mehrere Firmen hat, wählt er eine aktive Firma aus.
|
|
4. Backend erstellt ein Token oder eine Session mit aktiver `organization_id`.
|
|
5. Jeder REST-Request und jede WebSocket-Verbindung wird gegen diese aktive Firma
|
|
geprüft.
|
|
6. Datenbankzugriffe setzen kontrolliert das passende Firmenschema.
|
|
|
|
Für den öffentlichen Betrieb kann zusätzlich eine Subdomain genutzt werden:
|
|
|
|
- `firma-a.example.com`
|
|
- `firma-b.example.com`
|
|
|
|
Die Subdomain darf aber nur eine Vorauswahl sein. Die finale Berechtigung kommt
|
|
aus `public.user_organizations`.
|
|
|
|
## Betriebsarten
|
|
|
|
### Eigener Server bei einer Firma
|
|
|
|
- eine Backend-Instanz
|
|
- eine PostgreSQL-Datenbank
|
|
- genau eine Firma
|
|
- optional mehrere User
|
|
- dieselbe Mandantenarchitektur wie im SaaS-Betrieb
|
|
- Installation per Docker Compose, Systemdienst oder Installationsprogramm
|
|
- keine öffentliche Selbstregistrierung
|
|
- Firmenschema wird während Installation oder beim ersten Start erzeugt
|
|
- erster Firmen-User wird während Installation oder Erstkonfiguration angelegt
|
|
|
|
Die lokale Version soll bewusst nur eine Firma verwalten können. Dadurch bleibt
|
|
die lokale Installation einfacher, nutzt intern aber trotzdem dieselbe
|
|
Firmenschema-Architektur wie der öffentliche Server.
|
|
|
|
### Öffentlicher SaaS-Server
|
|
|
|
- Betrieb durch uns
|
|
- eine oder mehrere Backend-Instanzen
|
|
- zentrale PostgreSQL-Datenbank
|
|
- viele Firmen, jeweils eigenes Schema
|
|
- TLS zwingend
|
|
- Backup, Monitoring, Rate-Limits und Mandantentrennung zwingend
|
|
- Benutzer können mehreren Firmen zugeordnet sein
|
|
- öffentliche Registrierung möglich
|
|
- Freischaltung neuer Firmen erfolgt durch einen Administrator
|
|
- vor Freischaltung ist kein produktiver Zugriff auf das Firmenschema möglich
|
|
|
|
## Registrierung und Freischaltung
|
|
|
|
### Öffentliche Registrierung
|
|
|
|
Für den öffentlichen Server gibt es eine Registrierungsmaske.
|
|
|
|
Pflichtangaben:
|
|
|
|
- Firmenname
|
|
- E-Mail-Adresse
|
|
|
|
Ablauf:
|
|
|
|
1. Interessent gibt Firmenname und E-Mail-Adresse ein.
|
|
2. Server legt einen globalen User in `public.users` an, falls die E-Mail noch
|
|
nicht existiert.
|
|
3. Server legt einen Firmeneintrag in `public.organizations` mit Status `pending_approval`
|
|
an.
|
|
4. Server legt die Zuordnung in `public.user_organizations` an.
|
|
5. Server erzeugt das Firmenschema entweder sofort im gesperrten Zustand oder
|
|
erst nach Freischaltung. Bevorzugt: Schema erst nach Admin-Freischaltung
|
|
erzeugen, damit abgelehnte Registrierungen keine operativen Strukturen
|
|
hinterlassen.
|
|
6. Ein Administrator prüft und schaltet die Firma frei.
|
|
7. Bei Freischaltung erzeugt das Backend das Firmenschema, Basistabellen,
|
|
Standardrollen, Standardrechte und Grundeinstellungen.
|
|
8. Server erzeugt ein zufälliges Initialpasswort.
|
|
9. Server sendet eine E-Mail an die registrierte Adresse.
|
|
10. User meldet sich mit E-Mail-Adresse und Initialpasswort an.
|
|
11. Beim ersten Login muss das Passwort sofort geändert werden.
|
|
12. Danach kann der User Firmendaten, Einstellungen, Nummernkreise, Vorlagen und
|
|
weitere Stammdaten festlegen.
|
|
|
|
Die E-Mail-Adresse ist der Username.
|
|
|
|
### Admin-Freischaltung
|
|
|
|
Neue Firmenregistrierungen stehen in einer Admin-Oberfläche.
|
|
|
|
Admin-Aktionen:
|
|
|
|
- Registrierung ansehen
|
|
- Firma freischalten
|
|
- Registrierung ablehnen
|
|
- E-Mail erneut senden
|
|
- Firmenschema-Erstellung erneut versuchen, falls ein technischer Fehler auftrat
|
|
|
|
Statusmodell für `public.organizations`:
|
|
|
|
- `pending_approval`
|
|
- `approved`
|
|
- `active`
|
|
- `rejected`
|
|
- `suspended`
|
|
|
|
Vorgeschlagene Bedeutung:
|
|
|
|
- `pending_approval`: Registrierung eingegangen, aber noch nicht freigegeben
|
|
- `approved`: durch Admin freigegeben, technische Anlage läuft oder steht bevor
|
|
- `active`: Firma ist nutzbar
|
|
- `rejected`: Registrierung wurde abgelehnt
|
|
- `suspended`: Firma wurde nachträglich gesperrt
|
|
|
|
### Initialpasswort und Passwortwechsel
|
|
|
|
Initialpasswörter werden zufällig erzeugt und per E-Mail versendet.
|
|
|
|
Sicherheitsregeln:
|
|
|
|
- Passwort wird nie im Klartext gespeichert.
|
|
- Passwort-Hash liegt in `public.users.password_hash`.
|
|
- User bekommt Status `must_change_password`.
|
|
- Beim ersten Login sind nur Passwortänderung und Logout erlaubt.
|
|
- Initialpasswort sollte zeitlich ablaufen.
|
|
- Nach erfolgreicher Änderung wird `must_change_password` entfernt.
|
|
|
|
Optional spätere Verbesserung:
|
|
|
|
- Statt Klartextpasswort per E-Mail wird ein einmaliger Set-Password-Link
|
|
verschickt. Das ist sicherer, aber der aktuell geplante Ablauf ist:
|
|
zufälliges Passwort per E-Mail.
|
|
|
|
### User zur Firma einladen
|
|
|
|
Ein berechtigter Firmen-User kann weitere User einladen.
|
|
|
|
Ablauf:
|
|
|
|
1. Firmen-User gibt neue E-Mail-Adresse ein.
|
|
2. Backend prüft Recht, z.B. `users.invite`.
|
|
3. Falls die E-Mail in `public.users` noch nicht existiert, wird ein globaler
|
|
User angelegt.
|
|
4. Backend legt oder aktualisiert `public.user_organizations`.
|
|
5. Im Firmenschema werden die gewünschten Rollen in `user_roles` vergeben.
|
|
6. Backend erzeugt ein zufälliges Initialpasswort oder ein neues Initialpasswort,
|
|
falls der User noch kein aktives Passwort für diese Installation hat.
|
|
7. Backend setzt `must_change_password`.
|
|
8. Backend sendet eine Einladung per E-Mail.
|
|
9. Eingeladener User meldet sich mit E-Mail und Initialpasswort an.
|
|
10. Passwortänderung ist beim ersten Login zwingend.
|
|
|
|
Wenn ein User bereits in einer anderen Firma aktiv ist, darf dessen bestehendes
|
|
Passwort nicht ungefragt überschrieben werden. In diesem Fall sollte die
|
|
Einladung als Firmenzuordnung erfolgen und der User sich mit seinem bestehenden
|
|
Login anmelden. Für diesen Fall kann die Einladung einen Hinweis statt eines
|
|
neuen Passworts senden.
|
|
|
|
### Lokale Erstinstallation
|
|
|
|
Für lokale Installationen wird ein eigener Erstkonfigurationsprozess geplant.
|
|
|
|
Ablauf:
|
|
|
|
1. Installation richtet Backend und PostgreSQL ein.
|
|
2. Beim ersten Start erkennt das Backend, dass keine Firma existiert.
|
|
3. Setup-Maske fragt Firmenname, Admin-E-Mail und Initialpasswort ab oder erzeugt
|
|
ein zufälliges Passwort.
|
|
4. Backend erstellt genau eine Firma und genau ein Firmenschema.
|
|
5. Admin-User wird als `owner` zugeordnet.
|
|
6. Danach wird der Setup-Modus dauerhaft deaktiviert.
|
|
|
|
Eine zweite Firma darf in der lokalen Version nicht angelegt werden.
|
|
|
|
## Sicherheit
|
|
|
|
Mindestanforderungen:
|
|
|
|
- TLS für öffentlichen Betrieb
|
|
- Zertifikatsverwaltung über Reverse Proxy, z.B. Caddy, Traefik oder Nginx
|
|
- zusätzliche sessionbasierte Nutzdatenverschlüsselung oberhalb von HTTPS/WSS
|
|
- keine fachlichen Nutzdaten unverschlüsselt über das Netzwerk
|
|
- keine fachlichen Nutzdaten unverschlüsselt in PostgreSQL
|
|
- Passwort-Hashing mit Argon2id oder vergleichbar
|
|
- Zwei-Faktor-Authentifizierung
|
|
- Single Sign-On
|
|
- rollenbasierte Rechte
|
|
- Rechte pro atomarer Aktion mit Lesen, Schreiben und Löschen
|
|
- firmenschema-bewusste Datenbankzugriffe
|
|
- Audit-Log je Firmenschema
|
|
- sichere Speicherung von API-Zugangsdaten
|
|
- Backups und Restore-Konzept je Firma
|
|
- Schutz gegen unautorisierte WebSocket-Abos
|
|
- Initialpasswörter laufen ab
|
|
- Passwortwechsel beim ersten Login erzwingen
|
|
- Admin-Freischaltung für SaaS-Registrierungen
|
|
|
|
## Kommunikation und Verschlüsselung
|
|
|
|
### Grundsatz
|
|
|
|
Alle produktiven Verbindungen zwischen Client und Backend müssen verschlüsselt
|
|
sein.
|
|
|
|
Protokolle:
|
|
|
|
- REST über HTTPS
|
|
- WebSocket über WSS
|
|
|
|
Unverschlüsselte Verbindungen sind nicht vorgesehen. Das gilt auch für lokale
|
|
Installationen. Für Entwicklung können gesonderte Debug-Schalter existieren, sie
|
|
dürfen aber nicht Teil des produktiven Betriebs sein.
|
|
|
|
Zusätzlich zu HTTPS/WSS werden fachliche Nutzdaten auf Anwendungsebene
|
|
verschlüsselt. TLS schützt den Transportkanal, die Anwendungsschicht-
|
|
Verschlüsselung schützt die eigentlichen Payloads innerhalb der Session.
|
|
|
|
Grundsatz:
|
|
|
|
- keine fachlichen Nutzdaten werden unverschlüsselt übertragen
|
|
- keine fachlichen Nutzdaten werden unverschlüsselt in PostgreSQL gespeichert
|
|
- Metadaten werden nur dann unverschlüsselt gespeichert, wenn sie technisch für
|
|
Routing, Login, Rechteprüfung, Indizes oder Betrieb zwingend notwendig sind
|
|
- Klartext existiert nur kurzzeitig im Backend-Prozessspeicher während der
|
|
Verarbeitung
|
|
|
|
### Sessionbasierte Nutzdatenverschlüsselung
|
|
|
|
Für jede angemeldete Client-Session wird ein eigener Session-Schlüssel
|
|
ausgehandelt oder vom Backend sicher bereitgestellt.
|
|
|
|
Ziel:
|
|
|
|
- REST-Payloads zusätzlich zu HTTPS verschlüsseln
|
|
- WebSocket-Payloads zusätzlich zu WSS verschlüsseln
|
|
- Replay und Manipulation über AEAD-Verfahren verhindern
|
|
- Session-Schlüssel beim Logout, Passwortwechsel, User-Sperre oder Ablauf
|
|
ungültig machen
|
|
|
|
Empfohlenes Verfahren:
|
|
|
|
- asymmetrischer Schlüsselaustausch beim Login oder bei Firmenauswahl
|
|
- danach symmetrische Verschlüsselung pro Session
|
|
- AEAD-Verfahren, z.B. AES-256-GCM oder XChaCha20-Poly1305
|
|
- jede Nachricht bekommt Nonce und Authentifizierungstag
|
|
- Nonces dürfen pro Session-Schlüssel nie wiederverwendet werden
|
|
- Protokollversion und Nachrichtentyp werden als Additional Authenticated Data
|
|
einbezogen
|
|
|
|
Abstraktes Nachrichtenformat:
|
|
|
|
```json
|
|
{
|
|
"enc": "v1",
|
|
"kid": "session-key-id",
|
|
"nonce": "...",
|
|
"ciphertext": "...",
|
|
"tag": "..."
|
|
}
|
|
```
|
|
|
|
Für WebSocket bedeutet das:
|
|
|
|
- `hello` und Schlüsselaushandlung laufen vor verschlüsselten Fachnachrichten
|
|
- danach sind Subscribe-, Command- und Event-Payloads verschlüsselt
|
|
- technische Hüllfelder dürfen sichtbar bleiben, soweit sie für Routing nötig
|
|
sind, z.B. `enc`, `kid`, `nonce`
|
|
|
|
Für REST bedeutet das:
|
|
|
|
- Authentifizierung und Sessionaufbau laufen über HTTPS
|
|
- fachliche Request- und Response-Bodies werden danach verschlüsselt
|
|
- Datei-Uploads werden ebenfalls clientseitig oder vor Speicherung verschlüsselt
|
|
|
|
### Datenbankverschlüsselung
|
|
|
|
PostgreSQL speichert keine fachlichen Nutzdaten im Klartext.
|
|
|
|
Geplantes Modell:
|
|
|
|
- feld- oder dokumentbasierte Verschlüsselung auf Anwendungsebene
|
|
- PostgreSQL sieht für fachliche Felder nur Ciphertext
|
|
- je Firma eigener Data Encryption Key
|
|
- je Datensatz oder je Feld eigene Nonce
|
|
- AEAD mit Authentifizierungstag
|
|
- Schlüsselmaterial liegt nicht im Firmenschema
|
|
- Master-Key kommt aus Server-Konfiguration, Secret Store oder später HSM/KMS
|
|
|
|
Schlüsselhierarchie:
|
|
|
|
1. Master Key, außerhalb der Datenbank
|
|
2. Firmenschlüssel pro Firma
|
|
3. optional Bereichs- oder Tabellenschlüssel
|
|
4. Datensatzverschlüsselung mit eigener Nonce
|
|
|
|
Konsequenzen:
|
|
|
|
- Volltextsuche auf verschlüsselten Feldern ist nicht direkt möglich
|
|
- Sortierung und Filterung auf verschlüsselten Klartextwerten ist eingeschränkt
|
|
- für benötigte Suche braucht es bewusst geplante Such-/Indexwerte
|
|
- solche Suchwerte dürfen keine sensiblen Klartexte enthalten
|
|
- Logs, Audit-Log und Change-Log dürfen keine entschlüsselten Nutzdaten enthalten
|
|
|
|
Pragmatische Einordnung:
|
|
|
|
- Passwörter werden nicht verschlüsselt, sondern gehasht.
|
|
- technische IDs, Statuswerte, Sequenzen und Foreign Keys können als notwendige
|
|
Metadaten unverschlüsselt bleiben, wenn sie für Betrieb, Routing,
|
|
Rechteprüfung und referenzielle Integrität nötig sind.
|
|
- besonders sensible Inhalte wie Adressen, Kommunikationsdaten, Rechnungsinhalte,
|
|
Dokumentmetadaten, API-Secrets und Preise werden verschlüsselt gespeichert.
|
|
|
|
### Öffentlicher Server
|
|
|
|
Der öffentliche Server läuft hinter einem Reverse Proxy.
|
|
|
|
Empfohlene Aufgaben des Reverse Proxy:
|
|
|
|
- TLS-Terminierung
|
|
- Zertifikatsverwaltung
|
|
- HTTP-zu-HTTPS-Redirect
|
|
- Weiterleitung von WebSocket-Upgrades
|
|
- Rate-Limits für Login und Registrierung
|
|
- Request-Größenlimits für Uploads
|
|
- Security Header
|
|
|
|
Geeignete Optionen:
|
|
|
|
- Caddy
|
|
- Traefik
|
|
- Nginx
|
|
|
|
Empfehlung für den Start:
|
|
|
|
- Caddy oder Traefik, weil automatische Zertifikatsverwaltung einfacher ist.
|
|
|
|
Backend-intern:
|
|
|
|
- Backend lauscht nur im internen Netz oder auf localhost.
|
|
- Extern erreichbar ist nur der Reverse Proxy.
|
|
- Der Reverse Proxy leitet an das Backend weiter.
|
|
|
|
### Zertifikate
|
|
|
|
Öffentlicher SaaS-Betrieb:
|
|
|
|
- Zertifikate automatisch per ACME/Let's Encrypt
|
|
- automatische Erneuerung
|
|
- TLS mindestens Version 1.2, bevorzugt TLS 1.3
|
|
- HSTS nach stabiler Domain- und Zertifikatskonfiguration aktivieren
|
|
|
|
Lokaler Betrieb:
|
|
|
|
- ebenfalls HTTPS/WSS
|
|
- möglich über selbstsigniertes Zertifikat oder lokale Zertifizierungsstelle
|
|
- Installationsprogramm kann ein lokales Zertifikat erzeugen
|
|
- Desktopclient muss lokale Zertifikate sauber vertrauen können
|
|
|
|
Für lokale Installationen muss das Installationsprogramm die Zertifikatsfrage
|
|
lösen, z.B. durch lokale Zertifizierungsstelle, Zertifikatimport oder klaren
|
|
Administrationsdialog.
|
|
|
|
### REST-Kommunikation
|
|
|
|
REST wird für zustandsändernde Befehle, Dateiübertragungen und externe
|
|
Integrationen verwendet.
|
|
|
|
Eigenschaften:
|
|
|
|
- JSON als Standardformat
|
|
- HTTPS verpflichtend im SaaS-Betrieb
|
|
- Bearer Token im `Authorization`-Header
|
|
- API-Version im Pfad, z.B. `/api/v1/...`
|
|
- fachliche Request- und Response-Bodies werden nach Sessionaufbau zusätzlich
|
|
verschlüsselt
|
|
- idempotente Endpunkte dort, wo Wiederholungen realistisch sind
|
|
- Request-ID pro Anfrage für Logging und Fehlersuche
|
|
|
|
Beispiel-Header:
|
|
|
|
```text
|
|
Authorization: Bearer <access_token>
|
|
X-Request-Id: <uuid>
|
|
```
|
|
|
|
### WebSocket-Kommunikation
|
|
|
|
WebSocket wird für Live-Updates, Subscriptions und schnelle UI-Aktualisierung
|
|
verwendet.
|
|
|
|
Öffentlicher Betrieb:
|
|
|
|
- ausschließlich `wss://`
|
|
- WebSocket-Verbindung erst nach Login und Firmenauswahl
|
|
- Authentifizierung über kurzlebigen Socket-Token oder Access Token
|
|
- aktive Firma ist im Token oder in der Socket-Startnachricht festgelegt
|
|
- jede Subscription wird gegen Rechte geprüft
|
|
|
|
Empfohlener Verbindungsaufbau:
|
|
|
|
1. Client meldet sich per REST an.
|
|
2. Client wählt aktive Firma.
|
|
3. Backend gibt Access Token und optional separaten kurzlebigen WebSocket-Token.
|
|
4. Client öffnet `wss://server/ws`.
|
|
5. Client sendet eine `hello`-Nachricht mit Token und gewünschter Protokollversion.
|
|
6. Backend prüft Token, Firma, Userstatus und Rechte.
|
|
7. Backend bestätigt mit `hello_ack`.
|
|
8. Client abonniert Topics.
|
|
|
|
Beispielnachrichten:
|
|
|
|
```json
|
|
{
|
|
"type": "hello",
|
|
"protocol_version": 1,
|
|
"token": "<socket_token>"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"type": "subscribe",
|
|
"topic": "customers",
|
|
"since_sequence": 12345
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"type": "event",
|
|
"topic": "customers",
|
|
"sequence": 12346,
|
|
"operation": "updated",
|
|
"entity_id": "..."
|
|
}
|
|
```
|
|
|
|
### Token und Sessions
|
|
|
|
Geplantes Modell:
|
|
|
|
- Access Token kurzlebig
|
|
- Refresh Token länger gültig, aber serverseitig widerrufbar
|
|
- Refresh Token wird nur gehasht gespeichert
|
|
- WebSocket-Token optional separat und sehr kurzlebig
|
|
- Passwortwechsel, User-Deaktivierung oder Firmen-Sperre widerrufen Sessions
|
|
|
|
Für den Webclient:
|
|
|
|
- Access Token möglichst nur im Speicher halten
|
|
- Refresh Token bevorzugt als `HttpOnly`, `Secure`, `SameSite` Cookie
|
|
- bei reinem API-Betrieb alternativ sichere Token-Speicherung bewusst festlegen
|
|
|
|
Für den Desktopclient:
|
|
|
|
- Refresh Token im Betriebssystem-Keychain speichern
|
|
- Linux: Secret Service/KWallet, falls verfügbar
|
|
- Windows: Credential Manager oder DPAPI
|
|
- macOS: Keychain
|
|
- keine Tokens im Klartext in Konfigurationsdateien
|
|
|
|
### Nachrichtenintegrität und Replay-Schutz
|
|
|
|
TLS schützt Transport und Integrität auf Verbindungsebene. Zusätzlich sollen
|
|
kritische Operationen nachvollziehbar und wiederholungssicher sein.
|
|
|
|
Maßnahmen:
|
|
|
|
- Request-ID pro REST-Anfrage
|
|
- serverseitige Idempotency Keys für kritische POST-Aktionen, z.B.
|
|
Rechnungserstellung
|
|
- monoton steigende `sequence` im `change_log`
|
|
- WebSocket-Events enthalten `sequence`
|
|
- Clients können nach Reconnect ab einer bekannten `sequence` nachladen
|
|
|
|
### E-Mail-Kommunikation
|
|
|
|
System-E-Mails werden über eine Outbox versendet.
|
|
|
|
Anwendungsfälle:
|
|
|
|
- Initialpasswort nach Admin-Freischaltung
|
|
- Einladung neuer Firmen-User
|
|
- später Passwort-Reset
|
|
- später 2FA-/Sicherheitsbenachrichtigungen
|
|
|
|
Sicherheitsanforderungen:
|
|
|
|
- SMTP-Zugangsdaten nicht im Firmenschema im Klartext speichern
|
|
- zentrale SMTP-Konfiguration für SaaS-Betrieb
|
|
- lokale SMTP-Konfiguration für lokale Installation möglich
|
|
- Versandversuche und Fehler in `public.email_outbox` protokollieren
|
|
|
|
### API-Zugangsdaten von Lieferanten
|
|
|
|
Lieferanten-API-Zugangsdaten liegen im Firmenschema, müssen aber verschlüsselt
|
|
gespeichert werden.
|
|
|
|
Geplantes Modell:
|
|
|
|
- Verschlüsselung vor Speicherung in PostgreSQL
|
|
- Master-Key aus Server-Konfiguration oder Secret Store
|
|
- je Firma optional abgeleiteter Schlüssel
|
|
- Rotation des Master-Keys später einplanen
|
|
- Secrets nie im Audit-Log oder Change-Log im Klartext speichern
|
|
|
|
### Webhooks
|
|
|
|
Ausgehende Webhooks müssen signiert werden.
|
|
|
|
Anforderungen:
|
|
|
|
- HTTPS-Ziel-URLs bevorzugen
|
|
- HMAC-Signatur pro Webhook
|
|
- Timestamp im Signaturmaterial
|
|
- Retry mit Backoff
|
|
- Dead-Letter-Status nach wiederholtem Fehler
|
|
- kein Mitsenden sensibler Secrets
|
|
|
|
### Lokales Netzwerk und Server-Erkennung
|
|
|
|
Für den nativen Desktopclient müssen lokale Server auffindbar oder konfigurierbar
|
|
sein.
|
|
|
|
Optionen:
|
|
|
|
- manuelle Server-URL
|
|
- gespeicherte Serverprofile
|
|
- spätere automatische LAN-Erkennung
|
|
|
|
Empfehlung für den Start:
|
|
|
|
- manuelle Server-URL
|
|
- klare Anzeige, ob Verbindung verschlüsselt ist
|
|
- Warnung bei Zertifikats- oder Vertrauensproblemen
|
|
|
|
### Fehlerfälle und Reconnect
|
|
|
|
Clients müssen robuste Verbindungslogik haben.
|
|
|
|
Vorgaben:
|
|
|
|
- automatischer Reconnect mit Backoff
|
|
- Token-Refresh vor erneutem WebSocket-Aufbau
|
|
- nach Reconnect Sync über `since_sequence`
|
|
- sichtbarer Offline-/Verbindungsstatus in der UI
|
|
- keine stillschweigenden Datenverluste bei abgebrochenen Schreiboperationen
|
|
|
|
## Rollen und Rechte
|
|
|
|
Es gibt feste Standardrollen. Die Rechte selbst sind granular aufgebaut.
|
|
|
|
Rechtemodell:
|
|
|
|
- jedes fachliche Atom bekommt eigene Rechte
|
|
- mindestens `read`, `write`, `delete`
|
|
- Rechte werden im Firmenschema gespeichert
|
|
- feste Rollen bündeln Rechte
|
|
- die erste Person einer Firma ist der Besitzer (`owner`) und erhält beim
|
|
Anlegen der Firma alle initial vorgesehenen Rollen/Rechte
|
|
- nur der Besitzer darf Benutzer einladen und Rollen/Rechte für andere Benutzer
|
|
vergeben oder ändern
|
|
- alle weiteren Firmenbenutzer erhalten Rechte nicht automatisch, sondern erst
|
|
durch manuelle Freischaltung im Admin-Fenster `Benutzerrechte`
|
|
- jede größere Funktion bekommt eigene atomare Rechte, z.B. Kunden,
|
|
Lieferanten, Artikel, Angebote, Eingangsrechnungen, Ausgangsrechnungen,
|
|
Aktivitäten, Kommunikation, Preislistenimporte, Preis-API-Abgleiche,
|
|
Einstellungen, Benutzerverwaltung, Nummernkreise, Dokumente, Berichte und
|
|
Audit-Log
|
|
|
|
Beispielrollen:
|
|
|
|
- `owner`
|
|
- `admin`
|
|
- `sales`
|
|
- `accounting`
|
|
- `purchasing`
|
|
- `warehouse`
|
|
- `viewer`
|
|
|
|
Beispielrechte:
|
|
|
|
- `customers.read`
|
|
- `customers.write`
|
|
- `customers.delete`
|
|
- `activities.read`
|
|
- `activities.write`
|
|
- `activities.delete`
|
|
- `quotes.read`
|
|
- `quotes.write`
|
|
- `quotes.delete`
|
|
- `outgoing_invoices.read`
|
|
- `outgoing_invoices.write`
|
|
- `outgoing_invoices.delete`
|
|
- `items.read`
|
|
- `items.write`
|
|
- `items.delete`
|
|
- `price_rules.write`
|
|
- `imports.write`
|
|
- `settings.write`
|
|
|
|
## Fachmodule
|
|
|
|
### Dashboard
|
|
|
|
Die Startmaske ist ein Dashboard mit Übersicht über laufende und offene Vorgänge.
|
|
|
|
Inhalte:
|
|
|
|
- offene Angebote
|
|
- fällige Aufgaben
|
|
- offene Eingangsrechnungen
|
|
- offene Ausgangsrechnungen
|
|
- aktuelle Importläufe
|
|
- Preisänderungen
|
|
- Warnungen aus Lagerbestand oder Preisberechnung
|
|
- offene/fällige Vorgänge
|
|
- Wiedervorlagen
|
|
|
|
### Kunden
|
|
|
|
Verwaltung von Firmen- und Privatkunden der jeweiligen Firma.
|
|
|
|
Geplante Daten:
|
|
|
|
- Kundennummer
|
|
- Name/Firma
|
|
- Kundentyp
|
|
- Rechnungsadresse
|
|
- Lieferadresse
|
|
- Ansprechpartner
|
|
- Kommunikationsdaten
|
|
- Zahlungsbedingungen
|
|
- Standardrabatt
|
|
- Skonto-Regel, falls abweichend von den Firmeneinstellungen
|
|
- USt-IdNr.
|
|
- Notizen
|
|
- Status
|
|
|
|
### Lieferanten
|
|
|
|
Verwaltung von Bezugsquellen und Einkaufskonditionen.
|
|
|
|
Geplante Daten:
|
|
|
|
- Lieferantennummer
|
|
- Name/Firma
|
|
- Adressen
|
|
- Ansprechpartner
|
|
- Zahlungsbedingungen
|
|
- Skonto-Regel, falls vom Lieferanten vorgegeben
|
|
- Artikelreferenzen
|
|
- Preislistenquellen
|
|
- API-Zugangsdaten
|
|
|
|
### Artikel und Lager
|
|
|
|
Zentrale Artikelverwaltung mit Preisberechnung und Lagerbestand.
|
|
|
|
Geplante Daten:
|
|
|
|
- Artikelnummer
|
|
- Bezeichnung
|
|
- Beschreibung
|
|
- Einheit
|
|
- Einkaufspreis
|
|
- Verkaufspreis
|
|
- Steuersatz
|
|
- Hersteller-Code / Herstellerartikelnummer
|
|
- mehrere Lieferantenreferenzen je Artikel
|
|
- externe Artikelnummer je Lieferant
|
|
- Einkaufspreis je Lieferant und externer Artikelnummer
|
|
- Währung je Lieferantenpreis
|
|
- Kennzeichnung bevorzugter Lieferant
|
|
- EAN/GTIN
|
|
- Preisgültigkeit
|
|
- Lagerbestand
|
|
- Mindestbestand
|
|
- Lagerbewegungen
|
|
|
|
Preisberechnung:
|
|
|
|
- Einkaufspreis fest festlegen
|
|
- Einkaufspreis aus Quelle übernehmen
|
|
- Einkaufspreis mal Multiplikator
|
|
- Staffelpreise
|
|
- günstigsten aktiven Lieferantenpreis ermitteln
|
|
- bevorzugten Lieferanten optional gegenüber dem günstigsten Preis priorisieren
|
|
- kundenspezifische Preise
|
|
- kundenbezogener Standardrabatt
|
|
- positionsbezogener Sonderpreis oder Sonderrabatt
|
|
- Skonto-Regeln für Zahlung innerhalb definierter Fristen
|
|
- Projektpreise
|
|
- Rundungsregeln
|
|
- Preisgültigkeit
|
|
- Preisänderungshistorie
|
|
|
|
Preis- und Zahlungsbedingungen:
|
|
|
|
- Kunden können einen festen Standardrabatt erhalten.
|
|
- Lieferanten können eigene Skonto- und Zahlungsbedingungen haben.
|
|
- Firmeneinstellungen definieren Standard-Skonto und Standard-Zahlungsziele.
|
|
- Kunden- oder Lieferanteneinstellungen überschreiben die Firmenstandards.
|
|
- Rechnungspositionen übernehmen zunächst den aktuellen Artikelpreis und die
|
|
anwendbaren Rabattregeln.
|
|
- Der Einzelpreis einer Rechnungsposition darf manuell überschrieben werden,
|
|
z.B. wegen Sonderabsprachen, Kulanz oder Spezialrabatt.
|
|
- Preisüberschreibungen müssen als solche gespeichert werden, inkl. optionalem
|
|
Grund und User.
|
|
|
|
Geplante Tabellen:
|
|
|
|
- `customer_price_terms`: kundenspezifische Rabatte und Preisbedingungen
|
|
- `supplier_price_terms`: lieferantenspezifische Einkaufs-/Zahlungsbedingungen
|
|
- `cash_discount_terms`: Skonto-Regeln mit Prozent, Frist und Gültigkeit
|
|
|
|
`cash_discount_terms` ist bewusst eine eigene Tabelle, damit Firmeneinstellung,
|
|
Kunde, Lieferant und später ggf. einzelne Belege auf dieselben strukturierten
|
|
Skonto-Regeln verweisen können.
|
|
|
|
### Angebote
|
|
|
|
Angebote erstellen, versionieren, verwalten und in Rechnungen überführen.
|
|
|
|
Geplante Funktionen:
|
|
|
|
- frei konfigurierbarer Angebotsnummernkreis
|
|
- Kunde auswählen
|
|
- Positionen aus Artikeln oder Freitext
|
|
- Rabatte
|
|
- Steuern
|
|
- Zahlungs-/Lieferbedingungen
|
|
- Status: Entwurf, in Freigabe, freigegeben, gesendet, angenommen, abgelehnt, abgelaufen
|
|
- Angebotsversionen
|
|
- Angebotsvorlagen je Firma
|
|
- optionaler Freigabeprozess je nach Firmeneinstellung
|
|
- E-Mail-Versand aus dem System
|
|
- PDF-Erzeugung
|
|
- Umwandlung in Ausgangsrechnung
|
|
|
|
### Ausgangsrechnungen
|
|
|
|
Rechnungen an Kunden.
|
|
|
|
Geplante Funktionen:
|
|
|
|
- aus Angebot erzeugen
|
|
- direkt erstellen
|
|
- frei konfigurierbarer Rechnungsnummernkreis mit Pflicht-Platzhalter für Counter
|
|
- Positionen ausschließlich mit Artikelreferenz
|
|
- keine freien Text-/Freipositionen in Ausgangsrechnungen
|
|
- aktueller Artikelpreis wird beim Hinzufügen übernommen
|
|
- Einzelpreis und Rabatt pro Rechnungsposition dürfen überschrieben werden
|
|
- kundenbezogener Standardrabatt wird vorgeschlagen
|
|
- Skonto aus Firmeneinstellung oder Kundenregel wird vorgeschlagen
|
|
- Steuern passend für Deutschland
|
|
- Preisüberschreibungen mit Grund und User nachvollziehbar speichern
|
|
- Zahlungsstatus
|
|
- Mahnstatus
|
|
- PDF-Erzeugung
|
|
- revisionssichere Ablage nach Erstellung
|
|
- spätere Erweiterung für DATEV oder E-Rechnung möglich, aber nicht in der ersten Stufe
|
|
|
|
### Eingangsrechnungen
|
|
|
|
Rechnungen von Lieferanten.
|
|
|
|
Geplante Funktionen:
|
|
|
|
- Lieferant zuordnen
|
|
- Rechnungsdaten erfassen
|
|
- Positionen erfassen
|
|
- Dokument/PDF ablegen
|
|
- Zahlungsstatus
|
|
- Zahlungsziel und Skonto aus Lieferantenregel vorschlagen
|
|
- Kostenstellen oder Projekte, falls später benötigt
|
|
- Abgleich mit Artikeln und Lieferantenpreisen
|
|
- revisionssichere Ablage
|
|
|
|
### Kommunikation, Vorgänge, Aufgaben und Wiedervorlagen
|
|
|
|
Keine direkte E-Mail- oder Telefonie-Synchronisation in der ersten Stufe.
|
|
|
|
Begriffliche Entscheidung:
|
|
|
|
- UI-Hauptbegriff: `Vorgänge`
|
|
- technischer Name: `activities`
|
|
- `Aktivität` bleibt als Typ oder Kategorie möglich, ist aber als Hauptbegriff
|
|
zu unscharf
|
|
|
|
Ein Vorgang beschreibt etwas, das fachlich passiert ist, gerade passiert oder
|
|
geplant ist. Dadurch lassen sich Kommunikationsnotizen, Aufgaben, Termine,
|
|
Wiedervorlagen und Bearbeitungsschritte in einer gemeinsamen Timeline anzeigen.
|
|
|
|
Geplante Vorgangstypen:
|
|
|
|
- E-Mail-Notiz
|
|
- Telefonnotiz
|
|
- interne Notiz
|
|
- Aufgabe
|
|
- Wiedervorlage
|
|
- Kalendertermin
|
|
- Systemereignis, z.B. Angebot angenommen oder Rechnung erstellt
|
|
- manueller Bearbeitungsschritt, z.B. Rückfrage geklärt
|
|
|
|
Geplante Daten:
|
|
|
|
- Titel
|
|
- Beschreibung/Notiz
|
|
- Vorgangstyp
|
|
- Status: offen, in Bearbeitung, erledigt, storniert
|
|
- Priorität: niedrig, normal, hoch, kritisch
|
|
- Fälligkeitsdatum
|
|
- Start-/Endzeit für Termine
|
|
- zuständiger User
|
|
- erstellt von
|
|
- erledigt von
|
|
- Tags/Kategorien
|
|
- Sichtbarkeit, z.B. intern oder für alle berechtigten User
|
|
- Bezug auf Kunde, Lieferant, Kontakt, Angebot, Rechnung, Artikel, Dokument oder Importlauf
|
|
- versionierte Anhänge
|
|
- technische Systemquelle, falls automatisch erzeugt
|
|
|
|
Verknüpfungen:
|
|
|
|
- Ein Vorgang kann mehrere Bezüge haben, z.B. Kunde + Angebot + Dokument.
|
|
- Bezüge werden nicht als viele optionale Spalten modelliert, sondern über eine
|
|
Link-Tabelle.
|
|
- Beispiel: `activity_links(activity_id, entity_type, entity_id)`.
|
|
|
|
Live-Verhalten:
|
|
|
|
- neue oder geänderte Vorgänge erzeugen WebSocket-Events
|
|
- Dashboard zeigt offene/fällige Vorgänge
|
|
- Detailmasken zeigen eine Timeline der zugehörigen Vorgänge
|
|
- erledigte Vorgänge bleiben nachvollziehbar erhalten
|
|
|
|
Abgrenzung zu `communications`:
|
|
|
|
- `communications` speichert strukturierte Kommunikation, sobald echte
|
|
E-Mail-/Telefonie-Integration oder Nachrichtenarchivierung implementiert wird.
|
|
- `activities` bildet die nutzbare Timeline und Aufgaben-/Wiedervorlagefunktion
|
|
schon in der ersten Stufe ab.
|
|
- In der ersten Stufe können E-Mail- und Telefonnotizen direkt als Vorgänge
|
|
geführt werden.
|
|
|
|
### Dokumente und Vorlagen
|
|
|
|
Dokumente werden auf dem Filesystem gespeichert. Metadaten und Versionen liegen in
|
|
PostgreSQL.
|
|
|
|
Geplante Funktionen:
|
|
|
|
- PDF-Ablage
|
|
- Uploads
|
|
- versionierte Anhänge
|
|
- editierbare Dokumentvorlagen
|
|
- Vorlagen je Firma
|
|
- Verknüpfung mit Kunden, Lieferanten, Angeboten und Rechnungen
|
|
|
|
Keine Volltextsuche in Dokumenten in der ersten Stufe.
|
|
|
|
## Artikelpreislisten und APIs
|
|
|
|
### Dateiimport
|
|
|
|
Unterstützte erste Formate:
|
|
|
|
- CSV
|
|
- XML
|
|
- JSON
|
|
|
|
XLSX bleibt eine mögliche spätere Erweiterung.
|
|
|
|
Import-Pipeline:
|
|
|
|
1. Datei hochladen
|
|
2. Format erkennen oder manuell auswählen
|
|
3. Spalten/Felder zu Zielfeldern mappen
|
|
4. Vorschau und Validierung
|
|
5. Import ausführen
|
|
6. Importbericht speichern
|
|
7. Einkaufspreise aktualisieren
|
|
8. Verkaufspreise neu berechnen
|
|
9. betroffene Clients per WebSocket informieren
|
|
|
|
Preislistenimporte berücksichtigen optional:
|
|
|
|
- Hersteller-Code (`manufacturer_code`)
|
|
- Lieferantennummer (`supplier_number`)
|
|
- externe Lieferanten-Artikelnummer (`supplier_item_number`)
|
|
- Lieferanten-Einkaufspreis (`purchase_price`)
|
|
- Währung (`currency`)
|
|
|
|
Wenn Lieferantennummer und externe Artikelnummer im Import vorhanden sind, wird
|
|
neben dem internen Artikel eine Lieferantenpreis-Verknüpfung aktualisiert. Die
|
|
interne Artikelnummer bleibt unabhängig davon der primäre Objekt-Identifier.
|
|
|
|
### Frei konfigurierbare Preislisten
|
|
|
|
Da Lieferantenpreislisten frei konfigurierbar sein sollen, braucht das System
|
|
Mapping-Vorlagen:
|
|
|
|
- Lieferant
|
|
- Format
|
|
- Feldzuordnung
|
|
- Identifikationsfeld, z.B. EAN, Artikelnummer oder Herstellerartikelnummer
|
|
- Währungsfeld
|
|
- Preisfeld
|
|
- Mengen-/Staffelfeld
|
|
- Gültigkeitsdatum
|
|
- Konfliktregel
|
|
|
|
### Frei konfigurierbare API-Connectoren
|
|
|
|
Geplante Connector-Konfiguration:
|
|
|
|
- Lieferant
|
|
- Basis-URL
|
|
- Authentifizierung
|
|
- Header
|
|
- Query-Parameter
|
|
- Abrufintervall
|
|
- Request-Template
|
|
- Response-Mapping
|
|
- Fehlerprotokoll
|
|
- Testabruf
|
|
|
|
Komplexere APIs werden wahrscheinlich dennoch einzelne Spezialadapter brauchen.
|
|
Die freie Konfiguration sollte daher einfache REST/JSON- und REST/XML-Fälle
|
|
abdecken, aber nicht als vollständiger Ersatz für jede Lieferanten-API geplant
|
|
werden.
|
|
|
|
## Rechnungen, Steuern und Archivierung
|
|
|
|
Zielmarkt ist Deutschland.
|
|
|
|
Erste Stufe:
|
|
|
|
- deutsche Steuersätze
|
|
- Steuerwerte so erfassen, dass eine spätere Übertragung oder Auswertung für
|
|
Elster möglich ist
|
|
- frei konfigurierbare Nummernkreise
|
|
- Nummernkreis muss mindestens einen Counter-Platzhalter enthalten
|
|
- Rechnungen nach Erstellung unveränderlich behandeln
|
|
- Korrekturen über Storno/Korrekturrechnung statt Bearbeitung finaler Rechnung
|
|
|
|
Spätere Ausbaustufen:
|
|
|
|
- DATEV-Export
|
|
- XRechnung
|
|
- ZUGFeRD
|
|
- direkte Buchhaltungsintegration
|
|
|
|
## Backend-Architektur
|
|
|
|
Vorgeschlagene Hauptbereiche:
|
|
|
|
- Authentifizierung
|
|
- öffentliche Registrierung
|
|
- Admin-Freischaltung von Firmen
|
|
- SSO
|
|
- 2FA
|
|
- Firmenauswahl
|
|
- User-Einladungen
|
|
- E-Mail-Outbox
|
|
- Key-Management
|
|
- Session-Schlüsselverwaltung
|
|
- Payload-Verschlüsselung für REST und WebSocket
|
|
- Datenbankverschlüsselung auf Anwendungsebene
|
|
- Firmenschema-Auflösung
|
|
- rollenbasierte Autorisierung
|
|
- Migrationen für `public`
|
|
- Migrationen je Firmenschema
|
|
- REST-API
|
|
- WebSocket-Verbindungsverwaltung
|
|
- Event-Bus für Live-Updates
|
|
- Fachmodule
|
|
- Dateiimport
|
|
- API-Connectoren
|
|
- Dokumenterzeugung
|
|
- Dokumentablage im Filesystem
|
|
- Audit-Logging je Firmenschema
|
|
- Webhooks
|
|
|
|
## Socket-Kommunikation
|
|
|
|
Clients verbinden sich nach Login und Firmenauswahl mit dem Backend.
|
|
|
|
Das Socket-Protokoll ist versioniert. Jede Verbindung beginnt mit einer
|
|
`hello`-Nachricht. Ohne gültige Authentifizierung und bestätigte Protokollversion
|
|
werden keine Subscriptions angenommen.
|
|
|
|
Beispiele für Topics:
|
|
|
|
- `dashboard`
|
|
- `customers`
|
|
- `suppliers`
|
|
- `items`
|
|
- `stock`
|
|
- `quotes`
|
|
- `incoming_invoices`
|
|
- `outgoing_invoices`
|
|
- `communications`
|
|
- `tasks`
|
|
- `imports`
|
|
|
|
Beispiele für Server-Events:
|
|
|
|
- `snapshot`
|
|
- `created`
|
|
- `updated`
|
|
- `deleted`
|
|
- `price_recalculated`
|
|
- `stock_changed`
|
|
- `import_finished`
|
|
- `invoice_created_from_quote`
|
|
- `task_due`
|
|
|
|
Wichtig:
|
|
|
|
- jede WebSocket-Verbindung muss authentifiziert sein
|
|
- jede Verbindung hat genau eine aktive Firma
|
|
- jede Verbindung hat eine bestätigte Protokollversion
|
|
- jede Verbindung hat nach dem Handshake einen Session-Schlüssel für
|
|
verschlüsselte Payloads
|
|
- jede Nachricht wird gegen Rechte im Firmenschema geprüft
|
|
- Events gehen nur an berechtigte User derselben Firma
|
|
- Clients müssen verlorene Verbindungen erkennen und neu synchronisieren können
|
|
- für Offline-Desktopclients braucht es einen Änderungslog oder Sync-Endpunkt
|
|
- Events enthalten eine `sequence` für Reconnect und Offline-Sync
|
|
- schreibende Aktionen laufen bevorzugt über REST, Events informieren danach alle
|
|
betroffenen Clients
|
|
|
|
## REST-API und Webhooks
|
|
|
|
Zusätzlich zum WebSocket-Protokoll wird eine REST-API geplant.
|
|
|
|
Einsatz:
|
|
|
|
- öffentliche Registrierung im SaaS-Betrieb
|
|
- Admin-Freischaltung neuer Firmen
|
|
- Login
|
|
- Passwortwechsel beim ersten Login
|
|
- Firmenauswahl
|
|
- User-Einladungen
|
|
- CRUD-Operationen
|
|
- Dateiimport
|
|
- Dokumentdownload
|
|
- externe Integrationen
|
|
- Export vorhandener Daten
|
|
|
|
Webhooks:
|
|
|
|
- je Firma konfigurierbar
|
|
- Signatur je Webhook
|
|
- Retry-Strategie
|
|
- Ereignisse wie Angebot angenommen, Rechnung erstellt, Import abgeschlossen
|
|
|
|
Vorgeschlagene erste REST-Endpunkte:
|
|
|
|
- `POST /api/registration/organization`
|
|
- `GET /api/admin/organization-registrations`
|
|
- `GET /api/admin/organization-registrations/{id}`
|
|
- `POST /api/admin/organization-registrations/{id}/approve`
|
|
- `POST /api/admin/organization-registrations/{id}/reject`
|
|
- `POST /api/admin/organization-registrations/{id}/resend-initial-email`
|
|
- `POST /api/admin/organization-registrations/{id}/retry-provisioning`
|
|
- `POST /api/auth/login`
|
|
- `POST /api/auth/change-initial-password`
|
|
- `GET /api/auth/organizations`
|
|
- `POST /api/auth/select-organization`
|
|
- `GET /api/organizations/current/setup`
|
|
- `PUT /api/organizations/current/setup`
|
|
- `POST /api/organizations/current/invitations`
|
|
- `POST /api/organizations/current/invitations/{id}/resend`
|
|
- `GET /api/organizations/current/users`
|
|
- `PATCH /api/organizations/current/users/{user_id}/roles`
|
|
- `POST /api/organizations/current/users/{user_id}/disable`
|
|
|
|
Alle produktiven REST-Endpunkte werden versioniert, z.B. unter `/api/v1/...`.
|
|
Kritische POST-Endpunkte wie Rechnungserstellung oder Angebotsumwandlung sollen
|
|
Idempotency Keys unterstützen.
|
|
|
|
## Desktop-Offline-Konzept
|
|
|
|
Offline-Fähigkeit ist sinnvoll und wird eingeplant.
|
|
|
|
Vorgeschlagener Ansatz:
|
|
|
|
- lokaler Cache im Desktopclient
|
|
- lokale SQLite-Datenbank oder eingebetteter Speicher
|
|
- schreibende Offline-Aktionen zuerst nur für ausgewählte Bereiche erlauben
|
|
- Sync beim Wiederverbinden
|
|
- Konfliktstrategie pro Modul
|
|
|
|
Empfohlene erste Offline-Stufe:
|
|
|
|
- Lesen zuletzt synchronisierter Daten
|
|
- Vorgänge und Timelines anzeigen
|
|
- neue Notizen/Vorgangsentwürfe lokal speichern
|
|
- Aufgaben/Wiedervorlagen anzeigen
|
|
- Entwürfe lokal speichern
|
|
- keine finale Rechnungserstellung offline
|
|
|
|
Finale Rechnungen sollten wegen Nummernkreis, Revisionssicherheit und
|
|
Mehrbenutzerbetrieb nur online erstellt werden.
|
|
|
|
## Frontend-Planung
|
|
|
|
Webclient und Desktopclient sollen denselben fachlichen Umfang bekommen.
|
|
|
|
### Fensterbasiertes Arbeitsmodell
|
|
|
|
Die Anwendung wird fensterbasiert geplant. User sollen mehrere Arbeitskontexte
|
|
parallel offen halten können, z.B. Rechnung, Artikelliste, Kundendetail und
|
|
Vorgangstimeline.
|
|
|
|
Grundsätze:
|
|
|
|
- Listen, Detailfenster, Suchdialoge und Auswahlfelder sind eigene Fenster oder
|
|
fensterähnliche Arbeitsbereiche.
|
|
- Mehrere Fenster können gleichzeitig geöffnet sein.
|
|
- Änderungen in einem Fenster müssen andere offene Fenster sofort erreichen.
|
|
- Auswahlfelder und Suchdialoge dürfen keine veralteten Daten anzeigen.
|
|
- Ein Fenster darf lokale Eingaben nicht verlieren, wenn im Hintergrund Daten
|
|
aktualisiert werden.
|
|
- Bei Konflikten muss der User nachvollziehbar entscheiden können, ob lokale
|
|
Änderungen beibehalten, neu geladen oder zusammengeführt werden.
|
|
|
|
Beispiel:
|
|
|
|
1. User erstellt eine Ausgangsrechnung.
|
|
2. Beim Hinzufügen einer Position fehlt ein Artikel oder der Preis ist falsch.
|
|
3. User öffnet parallel Artikelfenster oder Vorgangsfenster.
|
|
4. User legt den Artikel/Vorgang an oder korrigiert den Preis.
|
|
5. Das Rechnungsfenster erhält sofort ein Live-Event.
|
|
6. Artikel und Vorgang sind ohne Neuladen in der Rechnung auswählbar.
|
|
|
|
Technische Anforderungen:
|
|
|
|
- Jede Datenänderung erzeugt ein fachliches Live-Event mit Entity-Typ, ID,
|
|
Änderungsart und Versions-/Sequenznummer.
|
|
- Frontends führen zentrale Stores/Caches je Firma, z.B. für Artikel, Kunden,
|
|
Vorgänge und Preisregeln.
|
|
- Fenster abonnieren die Store-Änderungen statt eigene isolierte Listen zu
|
|
halten.
|
|
- Auswahlkomponenten aktualisieren ihre Ergebnislisten bei Store-Updates.
|
|
- Auswahlkomponenten für große Stammdatenmengen, z.B. Kunden, Lieferanten und
|
|
Artikel, zeigen nicht pauschal alle Datensätze an. Sie bieten eine Suche nach
|
|
Nummer und Name und begrenzen die sichtbaren Treffer.
|
|
- Bei großen Listen werden nur betroffene Datensätze aktualisiert, nicht die
|
|
komplette Maske neu gerendert.
|
|
- Detailfenster vergleichen `updated_at` oder eine Versionsnummer, bevor lokale
|
|
Änderungen gespeichert werden.
|
|
- WebSocket-Reconnect nutzt `since_sequence`, damit verpasste Änderungen
|
|
nachgeladen werden.
|
|
|
|
Erste betroffene Bereiche:
|
|
|
|
- Artikel- und Preisauswahl in Rechnungen und Angeboten
|
|
- Vorgänge/Timelines in Kunden-, Lieferanten-, Artikel- und Belegfenstern
|
|
- Kunden- und Lieferantenauswahl in Belegen
|
|
- Benutzer- und Rechteänderungen in offenen Einstellungsfenstern
|
|
- Importfortschritt und Preisneuberechnung
|
|
|
|
Gemeinsame Kernmasken:
|
|
|
|
- öffentliche Registrierungsmaske, nur SaaS
|
|
- Admin-Maske für Firmenfreischaltungen, nur Betreiber/Admin
|
|
- Login
|
|
- erzwungener Passwortwechsel
|
|
- Firmenauswahl
|
|
- Dashboard
|
|
- Kunden
|
|
- Lieferanten
|
|
- Artikel
|
|
- Lager
|
|
- Angebote
|
|
- Ausgangsrechnungen
|
|
- Eingangsrechnungen
|
|
- Kommunikation
|
|
- Vorgänge
|
|
- Aufgaben/Wiedervorlagen
|
|
- Dokumente
|
|
- Einstellungen
|
|
- Benutzerverwaltung und Einladungen
|
|
- Importassistent
|
|
|
|
Benutzereinstellungen:
|
|
|
|
- Benutzereinstellungen werden je Firma und je Benutzer gespeichert.
|
|
- Die Werte werden wie andere fachliche Einstellungen verschlüsselt abgelegt.
|
|
- Erste Einstellung: Navigationsdarstellung mit den Modi `scroll` und
|
|
`groups`.
|
|
- Standard ist `scroll`, damit alle Menüpunkte auch bei kleinen Fenstern
|
|
erreichbar bleiben.
|
|
|
|
## Erste UI-Seiten: Organization und User
|
|
|
|
Die ersten UI-Seiten bilden den Onboarding-Flow ab. Fachlich geht es um eine
|
|
Firma, technisch um `organization`.
|
|
|
|
### Seite: Öffentliche Registrierung
|
|
|
|
Ziel:
|
|
|
|
- Eine neue Organization für den öffentlichen SaaS-Betrieb registrieren.
|
|
- Einen ersten User mit E-Mail-Adresse vormerken.
|
|
- Noch kein produktiver Zugriff vor Admin-Freischaltung.
|
|
|
|
Route:
|
|
|
|
- Web: `/register`
|
|
- Desktop: nicht erforderlich für öffentliche SaaS-Registrierung in der ersten
|
|
Stufe
|
|
|
|
Felder:
|
|
|
|
- `organization_name`, UI-Label `Firmenname`
|
|
- `email`, UI-Label `E-Mail-Adresse`
|
|
- `accept_terms`, UI-Label `Nutzungsbedingungen akzeptieren`, falls später nötig
|
|
|
|
Validierung:
|
|
|
|
- Firmenname ist Pflichtfeld
|
|
- Firmenname mindestens 2 Zeichen
|
|
- E-Mail ist Pflichtfeld
|
|
- E-Mail muss formal gültig sein
|
|
- Absenden mehrfach verhindern, solange Request läuft
|
|
|
|
Primäre Aktion:
|
|
|
|
- `Registrierung absenden`
|
|
|
|
API:
|
|
|
|
- `POST /api/v1/registration/organization`
|
|
|
|
Request:
|
|
|
|
```json
|
|
{
|
|
"organization_name": "Muster GmbH",
|
|
"email": "admin@example.com"
|
|
}
|
|
```
|
|
|
|
Erfolgszustand:
|
|
|
|
- UI zeigt, dass die Registrierung eingegangen ist.
|
|
- Hinweis: Freischaltung erfolgt durch Administrator.
|
|
- Kein Login-Link mit Zugriff suggerieren.
|
|
|
|
Fehlerzustände:
|
|
|
|
- E-Mail bereits registriert und bereits aktiver User
|
|
- Organization mit ähnlichem Namen existiert bereits
|
|
- Registrierung bereits offen
|
|
- Server nicht erreichbar
|
|
- Validierungsfehler
|
|
|
|
Sicherheits-/Betriebshinweise:
|
|
|
|
- Rate-Limit serverseitig
|
|
- keine Auskunft, ob eine E-Mail bereits bei einer anderen Firma aktiv ist
|
|
- Fehlermeldungen allgemein genug halten, um User Enumeration zu vermeiden
|
|
|
|
### Seite: Admin-Liste Registrierungen
|
|
|
|
Ziel:
|
|
|
|
- Betreiber-Admin sieht neue Organization-Registrierungen.
|
|
- Admin kann prüfen, freischalten oder ablehnen.
|
|
|
|
Route:
|
|
|
|
- Web: `/admin/organization-registrations`
|
|
- Desktop: später optional, erste Stufe Web
|
|
|
|
Tabelle:
|
|
|
|
- Firmenname
|
|
- E-Mail-Adresse
|
|
- Status
|
|
- Eingegangen am
|
|
- Entschieden am
|
|
- Entschieden von
|
|
|
|
Filter:
|
|
|
|
- offen
|
|
- freigeschaltet
|
|
- abgelehnt
|
|
- gesperrt
|
|
|
|
Aktionen:
|
|
|
|
- Details öffnen
|
|
- Freischalten
|
|
- Ablehnen
|
|
- E-Mail erneut senden, falls bereits freigeschaltet
|
|
- technischen Anlageversuch wiederholen
|
|
|
|
API:
|
|
|
|
- `GET /api/v1/admin/organization-registrations`
|
|
|
|
### Seite: Admin-Detail Registrierung
|
|
|
|
Ziel:
|
|
|
|
- Eine einzelne Registrierung prüfen und entscheiden.
|
|
|
|
Route:
|
|
|
|
- Web: `/admin/organization-registrations/:id`
|
|
|
|
Angezeigte Daten:
|
|
|
|
- Firmenname
|
|
- E-Mail-Adresse
|
|
- Status
|
|
- Zeitstempel
|
|
- technische Organization-ID, falls schon erzeugt
|
|
- Schema-Name, falls schon erzeugt
|
|
- Fehler der Schema-Erstellung, falls vorhanden
|
|
|
|
Aktionen:
|
|
|
|
- `Freischalten`
|
|
- `Ablehnen`
|
|
- `Zurück zur Liste`
|
|
- `Einladungs-E-Mail erneut senden`
|
|
- `Schema-Erstellung wiederholen`
|
|
|
|
Freischalten:
|
|
|
|
- erzeugt oder aktiviert `public.organizations`
|
|
- erzeugt `company_<organization_id>`
|
|
- erzeugt Basistabellen, Standardrollen und Standardrechte
|
|
- ordnet ersten User als `owner` zu
|
|
- erzeugt Initialpasswort
|
|
- setzt `must_change_password`
|
|
- legt E-Mail in `public.email_outbox`
|
|
|
|
Ablehnen:
|
|
|
|
- setzt Status `rejected`
|
|
- speichert optional internen Entscheidungsvermerk
|
|
- erzeugt kein Firmenschema
|
|
|
|
API:
|
|
|
|
- `GET /api/v1/admin/organization-registrations/{id}`
|
|
- `POST /api/v1/admin/organization-registrations/{id}/approve`
|
|
- `POST /api/v1/admin/organization-registrations/{id}/reject`
|
|
- `POST /api/v1/admin/organization-registrations/{id}/resend-initial-email`
|
|
- `POST /api/v1/admin/organization-registrations/{id}/retry-provisioning`
|
|
|
|
### Seite: Erster Login mit Initialpasswort
|
|
|
|
Ziel:
|
|
|
|
- Der erste User meldet sich mit E-Mail und Initialpasswort an.
|
|
- Danach muss sofort das Passwort geändert werden.
|
|
|
|
Route:
|
|
|
|
- Web: `/login`
|
|
- Desktop: Login-Dialog im nativen Client
|
|
|
|
Felder:
|
|
|
|
- `email`, UI-Label `E-Mail-Adresse`
|
|
- `password`, UI-Label `Passwort`
|
|
|
|
API:
|
|
|
|
- `POST /api/v1/auth/login`
|
|
|
|
Besondere Zustände:
|
|
|
|
- `must_change_password = true`: UI leitet direkt zur Passwortänderung weiter
|
|
- Organization noch nicht aktiv: UI zeigt neutralen Hinweis
|
|
- Initialpasswort abgelaufen: UI zeigt Hinweis auf erneute Einladung oder Support
|
|
|
|
### Seite: Passwort beim ersten Login ändern
|
|
|
|
Ziel:
|
|
|
|
- User ersetzt Initialpasswort durch eigenes Passwort.
|
|
|
|
Route:
|
|
|
|
- Web: `/change-initial-password`
|
|
- Desktop: eigener Dialog direkt nach Login
|
|
|
|
Felder:
|
|
|
|
- `current_password`, UI-Label `Aktuelles Passwort`
|
|
- `new_password`, UI-Label `Neues Passwort`
|
|
- `new_password_confirm`, UI-Label `Neues Passwort wiederholen`
|
|
|
|
Validierung:
|
|
|
|
- neues Passwort erfüllt Passwortrichtlinie
|
|
- Wiederholung stimmt überein
|
|
- neues Passwort darf nicht dem Initialpasswort entsprechen
|
|
|
|
API:
|
|
|
|
- `POST /api/v1/auth/change-initial-password`
|
|
|
|
Erfolgszustand:
|
|
|
|
- `must_change_password` wird entfernt
|
|
- Sessions mit altem Passwortstatus werden ungültig
|
|
- User wird zur Organization-Auswahl oder direkt ins Dashboard geleitet
|
|
|
|
### Seite: Organization-Grunddaten nach erstem Login
|
|
|
|
Ziel:
|
|
|
|
- Der erste User ergänzt nach Freischaltung die Stammdaten der Firma.
|
|
|
|
Route:
|
|
|
|
- Web: `/setup/organization`
|
|
- Desktop: Setup-Dialog nach Login
|
|
|
|
Felder erste Stufe:
|
|
|
|
- Firmenname
|
|
- Rechtsform
|
|
- Straße und Hausnummer
|
|
- PLZ
|
|
- Ort
|
|
- Land
|
|
- USt-IdNr., optional
|
|
- E-Mail der Firma
|
|
- Telefonnummer, optional
|
|
- Standard-Steuersatz
|
|
- Standard-Zahlungsziel
|
|
|
|
API:
|
|
|
|
- `GET /api/v1/organizations/current/setup`
|
|
- `PUT /api/v1/organizations/current/setup`
|
|
|
|
Erfolgszustand:
|
|
|
|
- Organization-Setup wird als abgeschlossen markiert
|
|
- User gelangt zum Dashboard
|
|
|
|
### Seite: User für Organization anlegen/einladen
|
|
|
|
Ziel:
|
|
|
|
- Ein berechtigter Firmen-User lädt weitere User ein.
|
|
|
|
Route:
|
|
|
|
- Web: `/settings/users`
|
|
- Desktop: `Benutzerrechte`
|
|
|
|
Felder Einladung:
|
|
|
|
- `email`, UI-Label `E-Mail-Adresse`
|
|
- `roles`, UI-Label `Rollen`
|
|
|
|
Aktionen:
|
|
|
|
- `Einladung senden`
|
|
- `Einladung erneut senden`
|
|
- `User deaktivieren`
|
|
- `Rollen ändern`
|
|
|
|
API:
|
|
|
|
- `GET /api/v1/organizations/current/users`
|
|
- `POST /api/v1/organizations/current/invitations`
|
|
- `PATCH /api/v1/organizations/current/users/{user_id}/roles`
|
|
- `POST /api/v1/organizations/current/invitations/{id}/resend`
|
|
- `PATCH /api/v1/organizations/current/users/{user_id}/roles`
|
|
- `POST /api/v1/organizations/current/users/{user_id}/disable`
|
|
|
|
Besonderheiten:
|
|
|
|
- Existierender globaler User bekommt keine Passwortüberschreibung.
|
|
- Neuer User bekommt Initialpasswort und `must_change_password`.
|
|
- Rollen werden im Firmenschema gespeichert.
|
|
|
|
### UI-Zustände und Komponenten
|
|
|
|
Gemeinsame Zustände:
|
|
|
|
- `idle`
|
|
- `validating`
|
|
- `submitting`
|
|
- `success`
|
|
- `error`
|
|
|
|
Wiederverwendbare Komponenten:
|
|
|
|
- Formularfeld mit Validierungsfehler
|
|
- Passwortfeld
|
|
- Statushinweis
|
|
- Tabellenliste mit Filter
|
|
- Bestätigungsdialog
|
|
- Fehlerbanner
|
|
|
|
Texte:
|
|
|
|
- UI-Texte deutsch
|
|
- technische IDs nur in Admin-Detailseiten anzeigen
|
|
- keine internen Schema- oder Security-Details auf öffentlichen Seiten
|
|
|
|
### Erste Implementierungsreihenfolge UI
|
|
|
|
1. Öffentliche Registrierungsseite
|
|
2. Login-Seite
|
|
3. Passwortänderung beim ersten Login
|
|
4. Admin-Liste Registrierungen
|
|
5. Admin-Detail mit Freischalten/Ablehnen
|
|
6. Organization-Grunddaten-Setup
|
|
7. Benutzerverwaltung mit Einladungen
|
|
|
|
## Erster Datenmodell-Entwurf
|
|
|
|
Dieser Abschnitt ist noch kein finales SQL-Schema, aber die Grundlage für die
|
|
ersten Migrationen.
|
|
|
|
### `public.users`
|
|
|
|
Globale Benutzeraccounts.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `email`
|
|
- `display_name`
|
|
- `password_hash`, nullable bei reinem SSO-Account
|
|
- `is_active`
|
|
- `must_change_password`
|
|
- `initial_password_expires_at`
|
|
- `created_at`
|
|
- `updated_at`
|
|
- `last_login_at`
|
|
|
|
### `public.organizations`
|
|
|
|
Globale Firmenliste zur Zuordnung von Usern zu Firmenschemas.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `display_name`
|
|
- `schema_name`
|
|
- `status`
|
|
- `registration_email`
|
|
- `approved_by_user_id`
|
|
- `approved_at`
|
|
- `rejected_by_user_id`
|
|
- `rejected_at`
|
|
- `rejection_reason`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Der `schema_name` muss eindeutig sein und darf nur vom Backend erzeugt werden.
|
|
Vor der Admin-Freischaltung kann `schema_name` leer bleiben, wenn das
|
|
Firmenschema erst nach Freigabe erzeugt wird.
|
|
|
|
### `public.user_organizations`
|
|
|
|
Zuordnung von Usern zu Firmen.
|
|
|
|
Felder:
|
|
|
|
- `user_id`
|
|
- `organization_id`
|
|
- `status`
|
|
- `invited_by_user_id`
|
|
- `invited_at`
|
|
- `accepted_at`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Rollen werden hier bewusst nicht gespeichert. Die eigentlichen Rollen liegen im
|
|
Firmenschema.
|
|
|
|
Mögliche Statuswerte:
|
|
|
|
- `pending_invitation`
|
|
- `active`
|
|
- `disabled`
|
|
|
|
### `public.auth_identities`
|
|
|
|
Verknüpfung von Usern mit Login-Methoden.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `user_id`
|
|
- `provider`
|
|
- `provider_subject`
|
|
- `email_at_provider`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Beispiele für `provider`:
|
|
|
|
- `password`
|
|
- `oidc`
|
|
- `saml`
|
|
- `microsoft`
|
|
- `google`
|
|
|
|
### `public.refresh_tokens`
|
|
|
|
Session-/Token-Verwaltung.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `user_id`
|
|
- `organization_id`, nullable solange keine Firma gewählt ist
|
|
- `token_hash`
|
|
- `expires_at`
|
|
- `revoked_at`
|
|
- `revoked_reason`
|
|
- `user_agent`
|
|
- `created_ip`
|
|
- `created_at`
|
|
|
|
### `public.socket_tokens`
|
|
|
|
Kurzlebige Tokens für WebSocket-Verbindungen. Diese Tabelle ist optional, wenn
|
|
Socket-Tokens als signierte, sehr kurzlebige Tokens ohne serverseitige Speicherung
|
|
umgesetzt werden.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `user_id`
|
|
- `organization_id`
|
|
- `token_hash`
|
|
- `expires_at`
|
|
- `used_at`
|
|
- `revoked_at`
|
|
- `created_at`
|
|
|
|
### `public.session_keys`
|
|
|
|
Metadaten zu aktiven Session-Schlüsseln. Rohes Schlüsselmaterial darf nicht im
|
|
Klartext in PostgreSQL gespeichert werden.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `user_id`
|
|
- `organization_id`
|
|
- `key_id`
|
|
- `wrapped_key`, falls serverseitige Wiederaufnahme nötig ist
|
|
- `algorithm`
|
|
- `created_at`
|
|
- `expires_at`
|
|
- `revoked_at`
|
|
|
|
Wenn Session-Schlüssel nur im Arbeitsspeicher gehalten werden, kann diese Tabelle
|
|
entfallen oder nur Widerrufs-/Audit-Metadaten enthalten.
|
|
|
|
### `public.idempotency_keys`
|
|
|
|
Schutz gegen doppelte Ausführung kritischer REST-Aktionen.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `user_id`
|
|
- `organization_id`
|
|
- `key`
|
|
- `request_hash`
|
|
- `response_status`
|
|
- `response_body_json`
|
|
- `expires_at`
|
|
- `created_at`
|
|
|
|
### Verschlüsselte Fachfelder
|
|
|
|
Für fachliche Tabellen wird ein einheitliches Muster verwendet.
|
|
|
|
Mögliche Feldstruktur:
|
|
|
|
- `*_ciphertext`
|
|
- `*_nonce`
|
|
- `*_tag`
|
|
- `*_key_id`
|
|
|
|
Alternativ kann ein JSONB- oder BYTEA-Container genutzt werden:
|
|
|
|
- `encrypted_payload`
|
|
- `encryption_meta`
|
|
|
|
Entscheidungskriterium:
|
|
|
|
- einzelne verschlüsselte Felder sind besser, wenn Teile eines Datensatzes
|
|
getrennt aktualisiert werden
|
|
- ein verschlüsselter Payload-Container ist einfacher, wenn Datensätze meistens
|
|
komplett gelesen und geschrieben werden
|
|
|
|
### `public.organization_registration_requests`
|
|
|
|
Optional separate Tabelle für SaaS-Registrierungen. Wenn die Registrierung direkt
|
|
in `public.organizations` abgebildet wird, ist diese Tabelle nicht zwingend nötig.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `organization_name`
|
|
- `email`
|
|
- `status`
|
|
- `organization_id`
|
|
- `requested_at`
|
|
- `decided_by_user_id`
|
|
- `decided_at`
|
|
- `decision_note`
|
|
|
|
Vorteil einer separaten Tabelle:
|
|
|
|
- abgelehnte Registrierungen bleiben nachvollziehbar
|
|
- `public.organizations` enthält nur Firmen, die technisch angelegt werden sollen
|
|
- Admin-Freischaltung ist sauberer auditierbar
|
|
|
|
### `public.user_invitations`
|
|
|
|
Einladungen von Firmen-Usern an neue oder bestehende User.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `organization_id`
|
|
- `email`
|
|
- `invited_by_user_id`
|
|
- `status`
|
|
- `expires_at`
|
|
- `accepted_at`
|
|
- `created_user_id`
|
|
- `created_at`
|
|
|
|
Mögliche Statuswerte:
|
|
|
|
- `pending`
|
|
- `accepted`
|
|
- `expired`
|
|
- `revoked`
|
|
|
|
### `public.email_outbox`
|
|
|
|
Queue für systemseitige E-Mails.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `recipient_email`
|
|
- `template`
|
|
- `payload_json`
|
|
- `status`
|
|
- `attempt_count`
|
|
- `last_error`
|
|
- `send_after`
|
|
- `sent_at`
|
|
- `created_at`
|
|
|
|
Verwendung:
|
|
|
|
- Initialpasswort nach Admin-Freischaltung
|
|
- Einladung neuer Firmen-User
|
|
- erneuter Versand durch Administrator
|
|
- spätere Passwort-Reset-Mails
|
|
|
|
### `company_*.settings`
|
|
|
|
Firmeneinstellungen.
|
|
|
|
Felder:
|
|
|
|
- `key`
|
|
- `value_json`
|
|
- `updated_by_user_id`
|
|
- `updated_at`
|
|
|
|
Beispiele:
|
|
|
|
- Freigabeprozess für Angebote aktiv
|
|
- Standard-Steuersatz
|
|
- Standard-Zahlungsbedingungen
|
|
- Dokumentvorlage für Angebote
|
|
- Dokumentvorlage für Rechnungen
|
|
|
|
### `company_*.roles`
|
|
|
|
Feste Standardrollen, aber je Firma gespeichert, damit Rechte später erweitert
|
|
oder angepasst werden können.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `code`
|
|
- `name`
|
|
- `description`
|
|
- `is_system_role`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
### `company_*.permissions`
|
|
|
|
Atomare Rechte.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `code`
|
|
- `description`
|
|
|
|
Beispiele:
|
|
|
|
- `customers.read`
|
|
- `customers.write`
|
|
- `customers.delete`
|
|
- `quotes.approve`
|
|
- `invoices.finalize`
|
|
- `settings.write`
|
|
|
|
### `company_*.role_permissions`
|
|
|
|
Zuordnung von Rollen zu Rechten.
|
|
|
|
Felder:
|
|
|
|
- `role_id`
|
|
- `permission_id`
|
|
|
|
### `company_*.user_roles`
|
|
|
|
Zuordnung globaler User zu Rollen in dieser Firma.
|
|
|
|
Felder:
|
|
|
|
- `user_id`
|
|
- `role_id`
|
|
- `created_at`
|
|
|
|
Der `user_id` verweist logisch auf `public.users.id`. Ein echter Foreign Key über
|
|
Schemas hinweg ist möglich, muss aber bewusst entschieden werden.
|
|
|
|
### `company_*.number_ranges`
|
|
|
|
Konfigurierbare Nummernkreise.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `code`
|
|
- `pattern`
|
|
- `counter_value`
|
|
- `counter_padding`
|
|
- `reset_rule`
|
|
- `is_active`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- `pattern` muss einen Counter-Platzhalter enthalten.
|
|
- Counter-Erhöhung muss transaktional und konkurrenzsicher erfolgen.
|
|
- Finale Rechnungsnummern dürfen nach Vergabe nicht wiederverwendet werden.
|
|
- Standardformat ist ein Präfix plus neunstelliger, gruppierter Zähler:
|
|
`K000.000.001`, `L000.000.001`, `I000.000.001`,
|
|
`A000.000.001`, `R000.000.001`.
|
|
- Standard-Nummernkreise:
|
|
`customers = K{counter}`, `suppliers = L{counter}`,
|
|
`items = I{counter}`, `activities = A{counter}`,
|
|
`outgoing_invoices = R{counter}`.
|
|
- Ergänzende Nummernkreise nach gleichem Prinzip:
|
|
`incoming_invoices = ER{counter}`, `quotes = AN{counter}`.
|
|
|
|
### `company_*.audit_log`
|
|
|
|
Revisions- und Nachvollziehbarkeitslog.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `actor_user_id`
|
|
- `action`
|
|
- `entity_type`
|
|
- `entity_id`
|
|
- `before_json`
|
|
- `after_json`
|
|
- `created_at`
|
|
|
|
Für sehr große Installationen kann dieses Log später partitioniert werden.
|
|
|
|
### `company_*.change_log`
|
|
|
|
Grundlage für Live-Updates und Offline-Synchronisation.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `sequence`
|
|
- `entity_type`
|
|
- `entity_id`
|
|
- `operation`
|
|
- `payload_json`
|
|
- `created_at`
|
|
- `created_by_user_id`
|
|
|
|
Nutzung:
|
|
|
|
- WebSocket-Events werden daraus erzeugt oder darauf gespiegelt.
|
|
- Desktopclients können seit ihrer letzten `sequence` Änderungen nachladen.
|
|
- Offline-Sync wird dadurch planbar.
|
|
|
|
### `company_*.documents` und `company_*.document_versions`
|
|
|
|
Metadaten für Dateien auf dem Filesystem.
|
|
|
|
`documents`:
|
|
|
|
- `id`
|
|
- `entity_type`
|
|
- `entity_id`
|
|
- `title`
|
|
- `current_version_id`
|
|
- `created_at`
|
|
- `created_by_user_id`
|
|
|
|
`document_versions`:
|
|
|
|
- `id`
|
|
- `document_id`
|
|
- `version_number`
|
|
- `storage_path`
|
|
- `mime_type`
|
|
- `sha256`
|
|
- `size_bytes`
|
|
- `created_at`
|
|
- `created_by_user_id`
|
|
|
|
## Implementierungsreihenfolge
|
|
|
|
Empfohlene Reihenfolge für die nächsten Entwicklungsschritte:
|
|
|
|
1. `public`-Migrationen für User, Firmen und Zuordnungen
|
|
2. SaaS-Registrierung mit Status `pending_approval`
|
|
3. Admin-Freischaltung und Backend-Service zur Firmenschema-Erstellung
|
|
4. Mailversand für Initialpasswort und Einladungen
|
|
5. Firmenschema-Basismigrationen für Rollen, Rechte, Settings, Nummernkreise,
|
|
Audit-Log und Change-Log
|
|
6. Login ohne SSO, aber mit Architektur für spätere Provider
|
|
7. erzwungener Passwortwechsel beim ersten Login
|
|
8. Firmenauswahl nach Login
|
|
9. User-Einladung in bestehende Firma
|
|
10. mandantenbewusste REST- und WebSocket-Basisschicht
|
|
11. erstes Fachmodul `customers`
|
|
12. Live-Update für `customers`
|
|
13. Dashboard-Grundlage
|
|
14. Artikelmodul und CSV-Import
|
|
|
|
SSO, 2FA, Desktop-Offline und automatische Updates sind wichtig, sollten aber nach
|
|
der mandantenfähigen Grundarchitektur kommen. Andernfalls müssten sie später
|
|
gegen eine noch instabile Daten- und Sessionstruktur gebaut werden.
|
|
|
|
## TODOs
|
|
|
|
### Erledigte Planungsentscheidungen
|
|
|
|
- [x] SaaS-Betrieb mit vielen Firmen auf einer Instanz
|
|
- [x] öffentlicher Server wird selbst betrieben
|
|
- [x] jede Firma bekommt eigenes PostgreSQL-Schema
|
|
- [x] `public` enthält nur globale User- und Firmenzuordnung
|
|
- [x] Rollen, Rechte und Einstellungen liegen im Firmenschema
|
|
- [x] ein User kann mehreren Firmen angehören
|
|
- [x] Zielmarkt erste Stufe: Deutschland
|
|
- [x] REST-API zusätzlich zum WebSocket-Protokoll
|
|
- [x] Webhooks einplanen
|
|
- [x] Desktopclient mit Offline-Fähigkeit
|
|
- [x] automatische Desktopclient-Updates einplanen
|
|
- [x] Dokumente im Filesystem, Metadaten in PostgreSQL
|
|
- [x] öffentliche Registrierung für SaaS
|
|
- [x] Admin-Freischaltung neuer Firmen
|
|
- [x] Registrierung mit Firmenname und E-Mail-Adresse
|
|
- [x] E-Mail-Adresse ist Username
|
|
- [x] Initialpasswort per E-Mail
|
|
- [x] Passwortwechsel beim ersten Login verpflichtend
|
|
- [x] neue Firmen-User werden per Einladung hinzugefügt
|
|
- [x] lokale Version erlaubt genau eine Firma
|
|
- [x] SaaS-Kommunikation ausschließlich über HTTPS/WSS
|
|
- [x] lokale produktive Kommunikation ebenfalls ausschließlich über HTTPS/WSS
|
|
- [x] Reverse Proxy übernimmt TLS-Terminierung
|
|
- [x] WebSocket-Protokoll wird versioniert
|
|
- [x] WebSocket-Verbindungen werden per Token authentifiziert
|
|
- [x] Webhooks werden signiert
|
|
- [x] fachliche Payloads werden zusätzlich zu HTTPS/WSS sessionbasiert verschlüsselt
|
|
- [x] fachliche Daten werden in PostgreSQL nicht im Klartext gespeichert
|
|
- [x] pro Firma wird ein eigener Datenschlüssel geplant
|
|
- [x] technischer Mandantenname ist `organization`
|
|
- [x] Schema-Namenskonvention ist `company_<organization_id>`
|
|
|
|
### Architektur
|
|
|
|
- [x] Alle tabellen/namen sind englisch, werden aber in der UI deutsch angezeigt
|
|
- [x] endgültigen Namen für Mandantenobjekt festlegen: `Firma`, `Organisation` oder `Company`: organization
|
|
- [x] Schema-Namenskonvention final festlegen: company_<organization_id>
|
|
- [ ] `public`-Tabellen detailliert entwerfen
|
|
- [ ] Firmenschema-Tabellen detailliert entwerfen
|
|
- [ ] Migrationsstrategie für neue und bestehende Firmenschemas festlegen: erst am schluss nötig
|
|
- [ ] sichere Kapselung für firmenschema-bewusste Datenbankzugriffe entwerfen
|
|
- [ ] Socket-Protokoll versionieren
|
|
- [ ] REST-API-Versionierung festlegen
|
|
- [ ] Event-Topics und Berechtigungsprüfung definieren
|
|
- [ ] Fehler- und Reconnect-Verhalten für Clients definieren
|
|
- [ ] fensterbasiertes Live-Refresh- und Store-Konzept final spezifizieren
|
|
- [ ] Offline-Sync-Strategie für Desktopclient spezifizieren
|
|
- [ ] SaaS-Onboarding-Flow final spezifizieren
|
|
- [ ] lokale Erstinstallation final spezifizieren
|
|
- [ ] E-Mail-Versandarchitektur festlegen
|
|
- [ ] Token-Modell final festlegen: JWT, opaque Tokens oder Hybrid
|
|
- [ ] WebSocket-Handshake final spezifizieren
|
|
- [ ] Idempotency-Key-Strategie für kritische REST-Aktionen festlegen
|
|
- [ ] Secret-Verschlüsselung für Lieferanten-API-Zugangsdaten spezifizieren
|
|
- [ ] Reconnect- und `since_sequence`-Sync formal spezifizieren
|
|
- [ ] lokale Zertifikatsstrategie final festlegen
|
|
- [ ] Debug-/Entwicklungsmodus klar vom produktiven Verschlüsselungsmodell trennen
|
|
- [ ] Session-Key-Aushandlung final spezifizieren
|
|
- [ ] AEAD-Algorithmus final auswählen
|
|
- [ ] Schlüsselhierarchie für Master Key, Firmenschlüssel und Session-Schlüssel spezifizieren
|
|
- [ ] Key-Rotation und Re-Encryption-Konzept planen
|
|
- [ ] Such-/Indexstrategie für verschlüsselte Daten planen
|
|
- [ ] festlegen, welche technischen Metadaten unverschlüsselt bleiben dürfen
|
|
|
|
### Backend
|
|
|
|
- [ ] öffentliche Registrierungs-API implementieren
|
|
- [ ] Admin-Freischaltungs-API implementieren
|
|
- [ ] Organization-Registration-Detail-API implementieren
|
|
- [ ] Initial-E-Mail erneut senden implementieren
|
|
- [ ] Provisioning-Retry implementieren
|
|
- [ ] Admin-Oberfläche für Registrierungsfreigaben anbinden
|
|
- [ ] Initialpasswort-Erzeugung implementieren
|
|
- [ ] E-Mail-Outbox implementieren
|
|
- [ ] Passwortwechsel beim ersten Login erzwingen
|
|
- [ ] User-Einladungen implementieren
|
|
- [ ] lokalen Setup-Modus für genau eine Firma implementieren
|
|
- [ ] Authentifizierung implementieren
|
|
- [ ] SSO-Provider-Konzept festlegen
|
|
- [ ] 2FA-Verfahren festlegen
|
|
- [ ] Firmenauswahl nach Login implementieren
|
|
- [ ] Rollen- und Rechtesystem im Firmenschema implementieren
|
|
- [ ] Firmenschema-Erstellung implementieren
|
|
- [ ] Public-Migrationen implementieren
|
|
- [ ] Firmenschema-Migrationen implementieren
|
|
- [ ] Live-Event-Bus je Firma einbauen
|
|
- [ ] Audit-Logging je Firmenschema ergänzen
|
|
- [ ] Datei-Upload und Dokumentablage implementieren
|
|
- [ ] PDF-Erzeugung evaluieren
|
|
- [ ] REST-API-Grundstruktur implementieren
|
|
- [ ] Webhook-Versand implementieren
|
|
- [ ] HTTPS/WSS-Betrieb hinter Reverse Proxy dokumentieren
|
|
- [ ] Access-/Refresh-Token-Handling implementieren
|
|
- [ ] WebSocket-Token oder WebSocket-Auth implementieren
|
|
- [ ] WebSocket-Handshake mit Protokollversion implementieren
|
|
- [ ] Live-Event-Typen je Entity definieren
|
|
- [ ] `since_sequence`-Nachlade-API für verpasste Live-Events implementieren
|
|
- [ ] Idempotency Keys für kritische POST-Endpunkte implementieren
|
|
- [ ] Verschlüsselung gespeicherter API-Secrets implementieren
|
|
- [ ] Session-Key-Verwaltung implementieren
|
|
- [ ] REST-Payload-Verschlüsselung implementieren
|
|
- [ ] WebSocket-Payload-Verschlüsselung implementieren
|
|
- [ ] Datenbank-Verschlüsselungsservice implementieren
|
|
- [ ] Firmenschlüssel-Erzeugung bei Firmenschema-Anlage implementieren
|
|
- [ ] Entschlüsselung in Logs, Audit-Log und Change-Log verhindern
|
|
|
|
### Datenmodell
|
|
|
|
- [ ] Firmenmodell in `public` detaillieren
|
|
- [ ] User- und Firmenzuordnung detaillieren
|
|
- [ ] Registrierungsanforderungen detaillieren
|
|
- [ ] User-Einladungen detaillieren
|
|
- [ ] E-Mail-Outbox detaillieren
|
|
- [ ] Passwortstatus und Initialpasswort-Ablauf detaillieren
|
|
- [ ] Refresh-Token-Modell detaillieren
|
|
- [ ] Socket-Token-Modell detaillieren
|
|
- [ ] Session-Key-Metadaten detaillieren
|
|
- [ ] Idempotency-Key-Modell detaillieren
|
|
- [ ] verschlüsselte Feldstruktur festlegen
|
|
- [ ] unverschlüsselte technische Metadaten je Tabelle festlegen
|
|
- [ ] Such-/Indexfelder für verschlüsselte Daten modellieren
|
|
- [ ] Rollenmodell detaillieren
|
|
- [ ] Rechtemodell detaillieren
|
|
- [ ] Kundenmodell detaillieren
|
|
- [ ] Lieferantenmodell detaillieren
|
|
- [ ] Artikelmodell detaillieren
|
|
- [ ] Kundenpreisbedingungsmodell (`customer_price_terms`) detaillieren
|
|
- [ ] Lieferantenpreisbedingungsmodell (`supplier_price_terms`) detaillieren
|
|
- [ ] Skonto-Regelmodell (`cash_discount_terms`) detaillieren
|
|
- [ ] Lagerbewegungsmodell detaillieren
|
|
- [ ] Angebotsmodell detaillieren
|
|
- [ ] Angebotsversionsmodell detaillieren
|
|
- [ ] Ausgangsrechnungsmodell detaillieren
|
|
- [ ] Rechnungspositionsmodell mit Artikelpflicht und Preisüberschreibung detaillieren
|
|
- [ ] Eingangsrechnungsmodell detaillieren
|
|
- [ ] Kommunikationsmodell detaillieren
|
|
- [ ] Vorgangsmodell (`activities`) detaillieren
|
|
- [ ] Vorgangsverknüpfungen (`activity_links`) detaillieren
|
|
- [ ] Aufgaben-/Wiedervorlagemodell detaillieren
|
|
- [ ] Dokument- und Dokumentversionsmodell detaillieren
|
|
- [ ] Preisquellen- und Preisregelmodell detaillieren
|
|
- [ ] Rabatt- und Skonto-Vererbungsregeln definieren
|
|
- [ ] Importmapping-Modell detaillieren
|
|
|
|
### Artikelimporte und Preise
|
|
|
|
- [ ] Beispielpreislisten sammeln
|
|
- [ ] CSV-Import als ersten Importweg implementieren
|
|
- [ ] XML-Import planen
|
|
- [ ] JSON-Import planen
|
|
- [ ] Spalten-/Feldmapping definieren
|
|
- [ ] Importvalidierung definieren
|
|
- [ ] Importhistorie definieren
|
|
- [ ] Preisneuberechnung nach Import spezifizieren
|
|
- [ ] Staffelpreise modellieren
|
|
- [ ] kundenspezifische Preise modellieren
|
|
- [ ] Projektpreise modellieren
|
|
- [ ] Lieferanten-API-Connector-Schnittstelle entwerfen
|
|
- [ ] Grenzen frei konfigurierbarer API-Connectoren dokumentieren
|
|
|
|
### Frontends
|
|
|
|
- [ ] gemeinsames UI-Konzept für Web und Desktop definieren
|
|
- [ ] Login-Flow für beide Clients planen
|
|
- [ ] Firmenauswahl planen
|
|
- [ ] öffentliche Registrierungsseite planen
|
|
- [ ] Admin-Liste für Organization-Registrierungen planen
|
|
- [ ] Admin-Detail für Organization-Freischaltung planen
|
|
- [ ] Passwortänderung beim ersten Login planen
|
|
- [ ] Organization-Grunddaten-Setup planen
|
|
- [ ] Benutzerverwaltung und Einladungen planen
|
|
- [ ] Dashboard entwerfen
|
|
- [ ] Kundenmaske entwerfen
|
|
- [ ] Lieferantenmaske entwerfen
|
|
- [ ] Artikelmaske entwerfen
|
|
- [ ] Lagermaske entwerfen
|
|
- [ ] Angebotsmaske mit Versionierung entwerfen
|
|
- [ ] Rechnungsmasken entwerfen
|
|
- [ ] Artikelpflicht und Preisüberschreibung in Rechnungsmasken entwerfen
|
|
- [ ] Kommunikationsverlauf entwerfen
|
|
- [ ] Vorgangstimeline entwerfen
|
|
- [ ] Vorgangsmaske mit Typen, Status, Priorität und Verknüpfungen entwerfen
|
|
- [ ] Aufgaben-/Wiedervorlagemaske entwerfen
|
|
- [ ] Dokumentverwaltung entwerfen
|
|
- [ ] Importassistent entwerfen
|
|
- [ ] Live-Refresh-Verhalten je Maske festlegen
|
|
- [ ] fensterübergreifende Aktualisierung von Listen, Suchdialogen und Auswahlfeldern planen
|
|
- [ ] Konfliktverhalten bei parallelen Fensteränderungen planen
|
|
- [ ] Offline-Verhalten im Desktopclient je Maske festlegen
|
|
- [ ] sichere Token-Speicherung im Desktopclient planen
|
|
- [ ] sichere Session-Key-Speicherung im Desktopclient planen
|
|
- [ ] Verschlüsselung des lokalen Offline-Caches planen
|
|
- [ ] Verbindungsstatus und TLS-Warnungen im Desktopclient planen
|
|
- [ ] Update-Mechanismus für Desktopclient auswählen
|
|
|
|
### Betrieb
|
|
|
|
- [ ] Docker-Setup für Backend und PostgreSQL erweitern
|
|
- [ ] TLS/Reverse-Proxy-Konzept für öffentlichen Betrieb definieren
|
|
- [ ] Zertifikatsstrategie festlegen
|
|
- [ ] Caddy, Traefik oder Nginx als Startoption auswählen
|
|
- [ ] lokale Zertifikatserzeugung für Installationsprogramm planen
|
|
- [ ] Security Header und HTTPS-Redirect konfigurieren
|
|
- [ ] Backup- und Restore-Konzept je Firma erstellen
|
|
- [ ] Logging und Monitoring planen
|
|
- [ ] Konfiguration für lokalen und öffentlichen Betrieb trennen
|
|
- [ ] Update-Strategie für Backend festlegen
|
|
- [ ] Update-Strategie für Desktopclient festlegen
|
|
- [ ] Dateisystem-Speicherlayout für Dokumente definieren
|
|
- [ ] Speicherquoten je Firma planen
|
|
|
|
### Rechtliches und Buchhaltung
|
|
|
|
- [ ] deutsche Steuerlogik detaillieren
|
|
- [ ] Elster-relevante Datenfelder identifizieren
|
|
- [ ] Revisionssichere Rechnungsablage konzipieren
|
|
- [ ] Storno-/Korrekturrechnungsprozess definieren
|
|
- [ ] Nummernkreisregeln und Platzhalter definieren
|
|
- [ ] spätere DATEV-Erweiterung vorbereiten
|
|
- [ ] spätere E-Rechnungs-Erweiterung vorbereiten
|
|
|
|
## Nächster sinnvoller Schritt
|
|
|
|
Als nächstes sollte das Datenmodell für `public` und das Firmenschema konkretisiert
|
|
werden. Besonders wichtig sind:
|
|
|
|
1. `users`, `organizations`, `user_organizations` und Login/SSO/2FA
|
|
2. Rollen und atomare Rechte im Firmenschema
|
|
3. Nummernkreise und revisionssichere Rechnungen
|
|
4. Änderungslog für Live-Updates und spätere Offline-Synchronisation
|