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:
1 parent
3cb7532417
commit
1573931c0f
13 files changed
+2234
-5
No files matched your search
@@ -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 />}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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: '*' },
|
||||
],
|
||||
|
||||
@@ -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
Reference in new issue
Block a user