feat: Haben-clone + client portal privacy fix

Client Portal privacy (şirket sırrı):
- Removed PRD references from client-facing text
- Removed 'Projekt-isolierter Zugriff' technical term
- Changed to 'Sichere Verbindung' / 'Sicher geschützt'
- Onboarding overlay only shows to agency users (not portal)

Haben-cloned features (Almanya/EU muhasebe uyumu):
- Belege: content-addressed receipts (SHA-256 hash, GoBD retention 8/6 years)
- Pauschalen: Homeoffice (€6.92/day), Kilometer (€0.30/km), Verpflegung (€14/€24/€28)
  - EStG § 4 Abs. 5, § 9 Abs. 1 compliant rates (2026)
- Anlagen & AfA: Anlagenverzeichnis with linear depreciation
  - GWG (≤€800 Sofortabschreibung, § 6 Abs. 2 EStG)
  - Sammelposten (€251-€1000, § 6 Abs. 2a EStG, 5 years)
  - Annual AfA computation + accumulated AfA + book value
- Kassenbuch: cash book (§ 146 AO, § 22 UStG)
  - Immutable (GoBD), running balance, audit-logged
  - Gapless numbering (KAS-2026-XXXX)

Seed data: 5 Belege, 7 Pauschalen, 5 Anlagen, 5 Kassenbuch entries
TypeScript: 0 errors | ESLint: clean
This commit is contained in:
Z User committed 2026-10-27 17:40:47 +00:00
1 parent 3cb7532417
commit 1573931c0f
13 files changed
+2234 -5

No files matched your search

+100
View File
@@ -568,6 +568,106 @@ model Article {
updatedAt DateTime @updatedAt
}
// ---------------------------------------------------------------------------
// Haben-cloned features: Belege, Pauschalen, Anlagen/AfA, Kasse
// ---------------------------------------------------------------------------
// Belege (receipts/documents) — content-addressed storage (PRD §5.5)
model Beleg {
id String @id @default(cuid())
number String @unique // gapless, e.g. BEL-2026-0001
type String // rechnung-eingang|rechnung-ausgang|quittung|kassenbeleg|bankbeleg|sonstiges
// content-addressed (SHA-256 hash of the file content)
fileHash String @unique
fileName String
fileType String // application/pdf, image/jpeg, etc.
fileSizeBytes Int
// metadata extracted from the document
vendor String? // Rechnungssteller
invoiceNumber String? // Rechnungsnummer des Lieferanten
amountCents Int? // Bruttobetrag auf dem Beleg
amountNetCents Int? // Netto (falls extrahiert)
taxBp Int? // USt %
bookingDate DateTime?
// linking
expenseId String? // link to Expense if billable
invoiceId String? // link to Invoice if outgoing
// OCR / AI
ocrText String? // extracted text (optional, via Jina/AI)
// GoBD
archivedAt DateTime?
retentionUntil DateTime? // Aufbewahrung bis
createdAt DateTime @default(now())
}
// Pauschalen (flat-rate allowances) — Homeoffice, Kilometer, Verpflegung
model Pauschale {
id String @id @default(cuid())
type String // homeoffice|kilometer|verpflegung
// homeoffice: €4.17/day (2026: €6.92/day up to €1.260/year)
// kilometer: €0.30/km (car), €0.38/km (car w/ pass), or actual
// verpflegung: €14 (8-14h), €24 (>14h, same day), €28 (overnight)
date DateTime
userId String? // employee
amountCents Int // computed amount
// details per type
days Int? // homeoffice: 1; verpflegung: 1
km Int? // kilometer: distance
vehicleType String? // kilometer: auto|rad|fuss
tripPurpose String? // kilometer: geschäftlich|fortbildung|umzug
hoursAway Int? // verpflegung: hours away from office
overnight Boolean @default(false) // verpflegung: overnight stay
description String?
projectId String? // link to project if billable
expenseId String? // link to generated expense
createdAt DateTime @default(now())
}
// Anlagenverzeichnis & AfA (asset register + depreciation)
model Anlage {
id String @id @default(cuid())
number String @unique // e.g. ANL-2026-0001
name String // "MacBook Pro 16"
category String // hardware|software|mobiliar|fahrzeug|immobilie
// acquisition
purchaseDate DateTime
purchaseCostCents Int // Anschaffungskosten (netto)
taxBp Int @default(1900) // USt bei Anschaffung
// AfA (depreciation)
afaMethod String @default("linear") // linear|degressiv
usefulLifeYears Int @default(3) // Nutzungsdauer (AfA-Tabelle)
salvageValueCents Int @default(0) // Restwert
// GWG (Geringwertiger Wirtschaftsgüter ≤ €800 net → Sofortabschreibung)
isGWG Boolean @default(false) // GWG ≤ €800 → immediate expense
// Sammelposten (€251-€1000 → Sammelabschreibung über 5 Jahre)
isSammelposten Boolean @default(false)
// computed
annualAfaCents Int @default(0) // jährliche AfA
accumulatedAfaCents Int @default(0) // kumulierte AfA
bookValueCents Int @default(0) // Buchwert (purchase - accumulated AfA)
// status
status String @default("active") // active|sold|scrapped|fully-depreciated
soldDate DateTime?
soldPriceCents Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Kassenbuch (cash book for Barzahlungen — § 146 AO, § 22 UStG)
model KassenbuchEntry {
id String @id @default(cuid())
number String @unique // gapless, e.g. KAS-2026-0001
date DateTime
type String // einnahme|ausgabe
description String
amountCents Int // signed: + for einnahme, - for ausgabe
taxBp Int? // USt % (if applicable)
taxFree Boolean @default(false) // steuerfrei
// receipt link
belegId String? // link to Beleg (Kassenbeleg)
// balance after this entry (laufender Kassenstand)
balanceCents Int @default(0)
// GoBD: Kassenbuch ist unveränderlich (elektronische Aufzeichnung)
createdAt DateTime @default(now())
}