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
@@ -680,6 +680,58 @@ async function main() {
|
||||
await db.auditLog.create({ data: { actor: 'nora@nordlicht.digital', action: 'bank.import:camt053', entity: 'BankTransaction', entityId: 'seed', after: '{"count":10,"batch":"camt-2026-10"}', hash: 'sha256:' + Math.random().toString(36).slice(2) } })
|
||||
await db.auditLog.create({ data: { actor: 'nora@nordlicht.digital', action: 'bank.match:invoice', entity: 'BankTransaction', entityId: 'seed', after: '{"invoiceId":"matched","status":"matched"}', hash: 'sha256:' + Math.random().toString(36).slice(2) } })
|
||||
|
||||
// --- Haben-cloned: Belege, Pauschalen, Anlagen/AfA, Kassenbuch -----------
|
||||
|
||||
// Belege (receipts)
|
||||
const belege = [
|
||||
{ number: 'BEL-2026-0001', type: 'rechnung-eingang', fileName: 'jetbrains-2026.pdf', fileType: 'application/pdf', fileSizeBytes: 124000, fileHash: 'sha256:' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2), vendor: 'JetBrains s.r.o.', invoiceNumber: 'JB-2026-APPPACK', amountCents: CENTS(899), bookingDate: daysFromNow(-8), retentionUntil: daysFromNow(365 * 8) },
|
||||
{ number: 'BEL-2026-0002', type: 'rechnung-eingang', fileName: 'strato-hosting.pdf', fileType: 'application/pdf', fileSizeBytes: 89000, fileHash: 'sha256:' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2), vendor: 'Strato AG', invoiceNumber: 'STR-2026-Q4', amountCents: CENTS(348), bookingDate: daysFromNow(-10), retentionUntil: daysFromNow(365 * 8) },
|
||||
{ number: 'BEL-2026-0003', type: 'quittung', fileName: 'dbahn-hamburg-berlin.jpg', fileType: 'image/jpeg', fileSizeBytes: 45000, fileHash: 'sha256:' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2), vendor: 'Deutsche Bahn AG', amountCents: CENTS(142), bookingDate: daysFromNow(-4), retentionUntil: daysFromNow(365 * 6) },
|
||||
{ number: 'BEL-2026-0004', type: 'kassenbeleg', fileName: 'hotel-luebeck.pdf', fileType: 'application/pdf', fileSizeBytes: 67000, fileHash: 'sha256:' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2), vendor: 'Hotel am Markt', amountCents: CENTS(118), bookingDate: daysFromNow(-22), retentionUntil: daysFromNow(365 * 6) },
|
||||
{ number: 'BEL-2026-0005', type: 'rechnung-eingang', fileName: 'cloudflare-pro.pdf', fileType: 'application/pdf', fileSizeBytes: 34000, fileHash: 'sha256:' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2), vendor: 'Cloudflare Inc.', invoiceNumber: 'CF-2026-5512', amountCents: CENTS(89), bookingDate: daysFromNow(-2), retentionUntil: daysFromNow(365 * 8) },
|
||||
]
|
||||
for (const b of belege) {
|
||||
await db.beleg.create({ data: b })
|
||||
}
|
||||
|
||||
// Pauschalen
|
||||
const pauschalen = [
|
||||
{ type: 'homeoffice', date: daysFromNow(-1), amountCents: CENTS(6.92), days: 1, description: 'Homeoffice-Pauschale' },
|
||||
{ type: 'homeoffice', date: daysFromNow(-2), amountCents: CENTS(6.92), days: 1, description: 'Homeoffice-Pauschale' },
|
||||
{ type: 'homeoffice', date: daysFromNow(-3), amountCents: CENTS(6.92), days: 1, description: 'Homeoffice-Pauschale' },
|
||||
{ type: 'kilometer', date: daysFromNow(-4), amountCents: CENTS(42), km: 140, vehicleType: 'auto', tripPurpose: 'geschäftlich', description: 'Hamburg → Berlin (Projekt-Kickoff)' },
|
||||
{ type: 'kilometer', date: daysFromNow(-22), amountCents: CENTS(35.40), km: 118, vehicleType: 'auto', tripPurpose: 'geschäftlich', description: 'Lübeck Kunden-Termin' },
|
||||
{ type: 'verpflegung', date: daysFromNow(-4), amountCents: CENTS(24), hoursAway: 16, overnight: false, description: 'Reise Berlin (16h abwesend)' },
|
||||
{ type: 'verpflegung', date: daysFromNow(-22), amountCents: CENTS(14), hoursAway: 10, overnight: false, description: 'Reise Lübeck (10h abwesend)' },
|
||||
]
|
||||
for (const p of pauschalen) {
|
||||
await db.pauschale.create({ data: p })
|
||||
}
|
||||
|
||||
// Anlagen & AfA
|
||||
const anlagen = [
|
||||
{ number: 'ANL-2026-0001', name: 'MacBook Pro 16" M4 Max', category: 'hardware', purchaseDate: daysFromNow(-120), purchaseCostCents: CENTS(3499), usefulLifeYears: 3, isGWG: false, isSammelposten: false, annualAfaCents: CENTS(1166.33), accumulatedAfaCents: CENTS(1166.33), bookValueCents: CENTS(2332.67) },
|
||||
{ number: 'ANL-2026-0002', name: 'Dell U2723QE Monitor', category: 'hardware', purchaseDate: daysFromNow(-90), purchaseCostCents: CENTS(599), usefulLifeYears: 1, isGWG: true, isSammelposten: false, annualAfaCents: CENTS(599), accumulatedAfaCents: CENTS(599), bookValueCents: 0 },
|
||||
{ number: 'ANL-2026-0003', name: 'Bürostuhl Herman Miller', category: 'mobiliar', purchaseDate: daysFromNow(-60), purchaseCostCents: CENTS(1450), usefulLifeYears: 10, isGWG: false, isSammelposten: false, annualAfaCents: CENTS(145), accumulatedAfaCents: CENTS(145), bookValueCents: CENTS(1305) },
|
||||
{ number: 'ANL-2026-0004', name: 'Software-Lizenz JetBrains All Products', category: 'software', purchaseDate: daysFromNow(-30), purchaseCostCents: CENTS(899), usefulLifeYears: 1, isGWG: true, isSammelposten: false, annualAfaCents: CENTS(899), accumulatedAfaCents: CENTS(899), bookValueCents: 0 },
|
||||
{ number: 'ANL-2026-0005', name: 'Sammelposten Büroausstattung Q3', category: 'mobiliar', purchaseDate: daysFromNow(-45), purchaseCostCents: CENTS(650), usefulLifeYears: 5, isGWG: false, isSammelposten: true, annualAfaCents: CENTS(130), accumulatedAfaCents: CENTS(130), bookValueCents: CENTS(520) },
|
||||
]
|
||||
for (const a of anlagen) {
|
||||
await db.anlage.create({ data: a })
|
||||
}
|
||||
|
||||
// Kassenbuch
|
||||
const kassenbuch = [
|
||||
{ number: 'KAS-2026-0001', date: daysFromNow(-10), type: 'einnahme', description: 'Barverkauf Schulungsmaterial', amountCents: CENTS(150), taxBp: 1900, taxFree: false, balanceCents: CENTS(150) },
|
||||
{ number: 'KAS-2026-0002', date: daysFromNow(-8), type: 'ausgabe', description: 'Büromaterial (Stifte, Notizblöcke)', amountCents: -CENTS(34.50), taxBp: 1900, taxFree: false, balanceCents: CENTS(115.50) },
|
||||
{ number: 'KAS-2026-0003', date: daysFromNow(-5), type: 'ausgabe', description: 'Kaffee für Büro', amountCents: -CENTS(12.90), taxBp: 700, taxFree: false, balanceCents: CENTS(102.60) },
|
||||
{ number: 'KAS-2026-0004', date: daysFromNow(-3), type: 'einnahme', description: 'Workshop-Teilnahmegebühr (bar)', amountCents: CENTS(250), taxBp: 1900, taxFree: false, balanceCents: CENTS(352.60) },
|
||||
{ number: 'KAS-2026-0005', date: daysFromNow(-1), type: 'ausgabe', description: 'Taxi zum Kunden', amountCents: -CENTS(18.40), taxBp: 700, taxFree: false, balanceCents: CENTS(334.20) },
|
||||
]
|
||||
for (const k of kassenbuch) {
|
||||
await db.kassenbuchEntry.create({ data: k })
|
||||
}
|
||||
|
||||
// --- Portal approvals (client milestone approvals with evidence) ---------
|
||||
const portalApprovals = [
|
||||
{ project: 'SEO-Optimierung & BFGG-Check', title: 'SEO-Audit & Keyword-Analyse', description: 'Audit-Bericht mit Keyword-Strategie und Wettbewerbsanalyse.', status: 'approved', decidedAt: daysFromNow(-8), decidedBy: 'Dr. med. Iris Brandt', decidedIp: '91.64.224.12', decision: 'Sehr gute Analyse, bitte umsetzen.' },
|
||||
|
||||
Reference in new issue
Block a user