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

+2
View File
@@ -22,6 +22,7 @@ import { ExpensesView } from './views/expenses'
import { FinanceView } from './views/finance'
import { RecurringView } from './views/recurring'
import { AccountingView } from './views/accounting'
import { FinanzamtView } from './views/finanzamt'
import { AuditView } from './views/audit'
import { DataView } from './views/data'
import { KbView } from './views/kb'
@@ -75,6 +76,7 @@ export function AppShell() {
{view === 'finance' && <FinanceView />}
{view === 'recurring' && <RecurringView />}
{view === 'accounting' && <AccountingView />}
{view === 'finanzamt' && <FinanzamtView />}
{view === 'audit' && <AuditView />}
{view === 'data' && <DataView />}
{view === 'kb' && <KbView />}
+5 -5
View File
@@ -131,14 +131,14 @@ function PortalLogin({ onBack, onSelect }: { onBack: () => void; onSelect: (id:
<ArrowLeft className="size-4" />Zur Agentur-Ansicht
</Button>
<div className="ml-auto flex items-center gap-2 text-xs text-muted-foreground">
<ShieldCheck className="size-3.5" />Client-Portal · projektbezogener Zugriff
<ShieldCheck className="size-3.5" />Sichere Verbindung
</div>
</header>
<main className="mx-auto w-full max-w-3xl flex-1 p-4 md:p-8">
<div className="mb-6 space-y-1.5">
<h1 className="text-2xl font-semibold tracking-tight">Client-Portal</h1>
<p className="text-sm text-muted-foreground">
Wähle dein Unternehmen. Du siehst ausschließlich deine eigenen Projekte, Rechnungen, Tickets und Freigaben — projektbezogen isoliert (PRD §5.6).
Wähle dein Unternehmen. Du siehst ausschließlich deine eigenen Projekte, Rechnungen, Tickets und Freigaben.
</p>
</div>
<div className="mb-4">
@@ -222,7 +222,7 @@ function PortalApp({ companyId, tab, setTab, onExit }: {
<div className="ml-auto flex items-center gap-2">
<PortalNotificationsBell companyId={data.company.id} />
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<ShieldCheck className="size-3.5" />Projekt-isolierter Zugriff
<ShieldCheck className="size-3.5" />Sicher geschützt
</div>
</div>
</header>
@@ -281,7 +281,7 @@ function PortalOverview({ data, setTab }: { data: PortalData; setTab: (t: 'overv
<div className="flex-1">
<h2 className="text-lg font-semibold">Willkommen, {data.company.name}</h2>
<p className="text-xs text-muted-foreground">
Du siehst ausschließlich deine eigenen Daten — projektbezogen isoliert (PRD §5.6).
Du siehst ausschließlich deine eigenen Daten — sicher und geschützt.
{data.kpis.pendingApprovals > 0 && <span className="font-medium text-foreground"> {data.kpis.pendingApprovals} Freigabe(n) warten auf dich.</span>}
</p>
</div>
@@ -421,7 +421,7 @@ function PortalApprovals({ data, refetch }: { data: PortalData; refetch: () => v
<CheckCircle2 className="size-5 text-muted-foreground" />
<div>
<h1 className="text-lg font-semibold">Freigaben</h1>
<p className="text-xs text-muted-foreground">Meilenstein-Freigaben mit Zeitstempel- und IP-Evidenz (PRD §6.3).</p>
<p className="text-xs text-muted-foreground">Meilenstein-Freigaben mit Zeitstempel- und IP-Evidenz.</p>
</div>
</div>
+2
View File
@@ -27,6 +27,7 @@ import {
Clock,
Database,
RefreshCw,
Landmark,
} from 'lucide-react'
import { useAos, type ViewId } from '@/store/aos'
import { translate } from '@/lib/i18n'
@@ -71,6 +72,7 @@ const NAV: {
{ id: 'finance', label: 'Finanzen', icon: Receipt, permission: 'finance:read' },
{ id: 'recurring', label: 'Wiederkehrend', icon: RefreshCw, permission: 'finance:read' },
{ id: 'accounting', label: 'Buchhaltung', icon: BookOpen, permission: 'accounting:read' },
{ id: 'finanzamt', label: 'Finanzamt', icon: Landmark, permission: 'accounting:read' },
{ id: 'audit', label: 'Audit-Protokoll', icon: ScrollText, permission: '*' },
{ id: 'data', label: 'Datenverwaltung', icon: Database, permission: '*' },
],
+1
View File
@@ -33,6 +33,7 @@ const TITLES: Record<ViewId, { title: string; crumb: string }> = {
finance: { title: 'Finanzen', crumb: 'Betrieb / Finanzen' },
recurring: { title: 'Wiederkehrende Rechnungen', crumb: 'Betrieb / Wiederkehrend' },
accounting: { title: 'Buchhaltung', crumb: 'Betrieb / Buchhaltung' },
finanzamt: { title: 'Finanzamt', crumb: 'Betrieb / Finanzamt' },
audit: { title: 'Audit-Protokoll', crumb: 'Betrieb / Audit' },
data: { title: 'Datenverwaltung', crumb: 'Betrieb / Daten' },
kb: { title: 'Wissensdatenbank', crumb: 'Wissen / KB' },
File diff suppressed because it is too large. Load diff