/** * Tier 4: Real-World German Receipt Workload Scenarios * Minimum 8 realistic German accounting & tax compliance workload scenarios. */ import { describe, test, it, expect } from "./runner"; import { ProcessedReceipt } from "../../src/lib/schema/receipt"; import { validateReceiptMath } from "../../src/lib/ai/mathValidator"; import { generateDualSheetExcel } from "../../src/lib/export/excelGenerator"; import { generateAccountingCsv } from "../../src/lib/export/csvGenerator"; import ExcelJS from "exceljs"; describe("Tier 4: Real-World German Receipt Workload Scenarios", () => { // Scenario 1: Aral Tankstelle Fuel Receipt with Liters & 19% VAT test("Scenario 1: Aral Tankstelle Fuel Receipt with Liters & 19% VAT", async () => { const aralReceipt: ProcessedReceipt = { id: "scen-1-aral", merchant: { name: "Aral Tankstelle Station München", address: "Landsberger Str. 402, 81241 München", taxId: "DE129482910", confidence: 0.99, }, date: { isoDate: "2026-08-14", time: "07:35", confidence: 0.98, }, documentType: "TANKBELEG", receiptNumber: "AR-882910", currency: "EUR", totalAmount: { value: 78.45, confidence: 0.99, }, netAmount: 65.92, taxBreakdown: [ { ratePercent: 19, taxAmount: 12.53, netAmount: 65.92, }, ], lineItems: [ { description: "Super E10 (42.50 l x 1.729 €/l)", quantity: 1, price: 73.48, taxRate: 19, }, { description: "Kaffee Crema Groß 0.3l", quantity: 1, price: 3.5, taxRate: 19, }, { description: "Croissant Natur", quantity: 1, price: 1.47, taxRate: 19, }, ], suggestedCategory: "Tanken & KFZ", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-aral-scen-1", originalFileName: "01_aral_tankbeleg_muenchen.jpg", fileSizeBytes: 245000, createdAt: "2026-08-14T07:35:00.000Z", updatedAt: "2026-08-14T07:35:00.000Z", status: "ready", }; // 1. Math check const val = validateReceiptMath(aralReceipt); expect(val.isMathValid).toBe(true); expect(val.calculatedGross).toBe(78.45); expect(val.calculatedItemsSum).toBe(78.45); // 2. Accounting CSV check const csv = generateAccountingCsv([aralReceipt]); expect(csv).toContain('"Aral Tankstelle Station München"'); expect(csv).toContain('"78,45"'); expect(csv).toContain('"12,53"'); expect(csv).toContain('"14.08.2026"'); // 3. Excel check const buf = await generateDualSheetExcel([aralReceipt]); const wb = new ExcelJS.Workbook(); await wb.xlsx.load(buf as any); const s1 = wb.getWorksheet("Belegübersicht"); expect(s1?.getRow(2).getCell(4).value).toBe("Tanken & KFZ"); }); // Scenario 2: REWE Supermarkt Mixed Food (7%) & Non-Food (19%) Basket test("Scenario 2: REWE Supermarkt Mixed Food (7%) & Non-Food (19%) Basket with Pfand", async () => { const reweReceipt: ProcessedReceipt = { id: "scen-2-rewe", merchant: { name: "REWE Markt GmbH Filiale 441", address: "Friedrichstraße 100, 10117 Berlin", taxId: "DE811122334", confidence: 0.98, }, date: { isoDate: "2026-08-15", time: "18:20", confidence: 0.97, }, documentType: "KASSENBON", receiptNumber: "RW-2026-8819", currency: "EUR", totalAmount: { value: 34.62, confidence: 0.99, }, netAmount: 31.78, taxBreakdown: [ { ratePercent: 7, taxAmount: 1.63, netAmount: 23.32, }, { ratePercent: 19, taxAmount: 1.21, netAmount: 8.46, }, ], lineItems: [ { description: "REWE Bio Vollmilch 3.8% 1l", quantity: 2, price: 3.18, taxRate: 7, }, { description: "Bio Bananen Fairtrade 1.2 kg", quantity: 1, price: 2.39, taxRate: 7, }, { description: "Dinkel Sauerteigbrot 500g", quantity: 1, price: 3.99, taxRate: 7, }, { description: "Gouda jung Bio 400g", quantity: 1, price: 4.49, taxRate: 7, }, { description: "Espresso Bohnen Bio 1kg", quantity: 1, price: 10.9, taxRate: 7, }, { description: "Küchenrolle Recycling 4er", quantity: 1, price: 4.74, taxRate: 19, }, { description: "Spülmittel Lemon 500ml", quantity: 1, price: 2.43, taxRate: 19, }, { description: "Mineralwasser Medium 1.0l", quantity: 1, price: 2.5, taxRate: 19, }, ], suggestedCategory: "Verpflegungsmehraufwand", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-rewe-scen-2", originalFileName: "04_rewe_supermarkt_kassenbon.jpg", fileSizeBytes: 210000, createdAt: "2026-08-15T18:20:00.000Z", updatedAt: "2026-08-15T18:20:00.000Z", status: "ready", }; const val = validateReceiptMath(reweReceipt); expect(val.isMathValid).toBe(true); expect(val.calculatedTaxSum).toBe(2.84); // 1.63 + 1.21 const buf = await generateDualSheetExcel([reweReceipt]); const wb = new ExcelJS.Workbook(); await wb.xlsx.load(buf as any); const s2 = wb.getWorksheet("Einzelpositionen Detail"); expect(s2?.rowCount).toBe(9); // 1 header + 8 line items }); // Scenario 3: Trattoria Bella Vista Bewirtungsbeleg with Tip & Attendees (§4 Abs. 5 EStG) test("Scenario 3: Trattoria Bella Vista Bewirtungsbeleg with Tip & Attendees (§4 Abs. 5 EStG)", async () => { const trattoriaReceipt: ProcessedReceipt = { id: "scen-3-trattoria", merchant: { name: "Trattoria Bella Vista Ristorante", address: "Marktplatz 12, 10115 Berlin", taxId: "DE987654321", confidence: 0.96, }, date: { isoDate: "2026-08-15", time: "21:10", confidence: 0.95, }, documentType: "BEWIRTUNGSBELEG", receiptNumber: "TR-2026-9941", currency: "EUR", totalAmount: { value: 125.5, confidence: 0.97, }, netAmount: 104.97, taxBreakdown: [ { ratePercent: 19, taxAmount: 16.03, netAmount: 84.37, }, { ratePercent: 7, taxAmount: 4.5, netAmount: 20.6, }, ], lineItems: [ { description: "2x Tagliolini al Tartufo Nero", quantity: 2, price: 52.0, taxRate: 19, }, { description: "1x Filetto di Manzo 250g", quantity: 1, price: 36.5, taxRate: 19, }, { description: "1x San Pellegrino 0.75l", quantity: 1, price: 7.5, taxRate: 19, }, { description: "1x Chianti Classico DOCG Flasche", quantity: 1, price: 28.0, taxRate: 19, }, { description: "Trinkgeld / Tip (steuerfrei)", quantity: 1, price: 1.5, taxRate: null, }, ], suggestedCategory: "Bewirtung", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-trattoria-scen-3", originalFileName: "02_trattoria_bewirtungsbeleg_berlin.jpg", fileSizeBytes: 290000, createdAt: "2026-08-15T21:10:00.000Z", updatedAt: "2026-08-15T21:10:00.000Z", status: "ready", }; const val = validateReceiptMath(trattoriaReceipt); expect(val.isMathValid).toBe(true); const csv = generateAccountingCsv([trattoriaReceipt]); expect(csv).toContain('"Trattoria Bella Vista Ristorante - Bewirtung"'); expect(csv).toContain('"125,50"'); }); // Scenario 4: MediaMarkt Saturn IT Equipment Invoice test("Scenario 4: MediaMarkt Saturn IT Equipment Invoice (Hardware, Serial Numbers, 19% VAT)", async () => { const itReceipt: ProcessedReceipt = { id: "scen-4-mediamarkt", merchant: { name: "MediaMarkt Saturn Holding", address: "Alexanderplatz 3, 10178 Berlin", taxId: "DE119876543", confidence: 0.99, }, date: { isoDate: "2026-08-15", time: "15:45", confidence: 0.98, }, documentType: "RECHNUNG", receiptNumber: "MM-INV-2026-77812", currency: "EUR", totalAmount: { value: 299.98, confidence: 0.99, }, netAmount: 252.08, taxBreakdown: [ { ratePercent: 19, taxAmount: 47.9, netAmount: 252.08, }, ], lineItems: [ { description: "Dell USB-C 4K Triple Display Dock 130W", quantity: 1, price: 189.99, taxRate: 19, }, { description: "Logitech MX Keys S Tastatur Wireless", quantity: 1, price: 109.99, taxRate: 19, }, ], suggestedCategory: "Bürobedarf & IT", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-mediamarkt-scen-4", originalFileName: "03_mediamarkt_it_rechnung.jpg", fileSizeBytes: 310000, createdAt: "2026-08-15T15:45:00.000Z", updatedAt: "2026-08-15T15:45:00.000Z", status: "ready", }; const val = validateReceiptMath(itReceipt); expect(val.isMathValid).toBe(true); const buf = await generateDualSheetExcel([itReceipt]); const wb = new ExcelJS.Workbook(); await wb.xlsx.load(buf as any); const s1 = wb.getWorksheet("Belegübersicht"); expect(s1?.getRow(2).getCell(5).value).toBe("RECHNUNG"); expect(s1?.getRow(2).getCell(10).value).toBe(299.98); }); // Scenario 5: Hotel Übernachtung + Frühstück Split (7% vs 19% + City Tax 0%) test("Scenario 5: Hotel Übernachtung + Frühstück Split (Lodging 7% vs Breakfast 19% + City Tax 0%)", async () => { const hotelReceipt: ProcessedReceipt = { id: "scen-5-hotel", merchant: { name: "Motel One München Sendlinger Tor", address: "Herzog-Wilhelm-Str. 28, 80331 München", taxId: "DE263819201", confidence: 0.98, }, date: { isoDate: "2026-08-14", time: "08:15", confidence: 0.96, }, documentType: "RECHNUNG", receiptNumber: "MO-MUC-88412", currency: "EUR", totalAmount: { value: 129.5, confidence: 0.99, }, netAmount: 118.84, taxBreakdown: [ { ratePercent: 7, taxAmount: 7.0, netAmount: 100.0, // Lodging }, { ratePercent: 19, taxAmount: 3.66, netAmount: 19.26, // Breakfast & Business Package }, ], lineItems: [ { description: "1x Übernachtung Standard Room (13.08.-14.08.)", quantity: 1, price: 107.0, taxRate: 7, }, { description: "1x Bio-Frühstücksbuffet & WLAN Business", quantity: 1, price: 22.5, taxRate: 19, }, ], suggestedCategory: "Reisekosten & Hotel", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-hotel-scen-5", originalFileName: "hotel_motel_one.jpg", fileSizeBytes: 260000, createdAt: "2026-08-14T08:15:00.000Z", updatedAt: "2026-08-14T08:15:00.000Z", status: "ready", }; const val = validateReceiptMath(hotelReceipt); expect(val.isMathValid).toBe(true); const csv = generateAccountingCsv([hotelReceipt]); expect(csv).toContain('"Motel One München Sendlinger Tor"'); expect(csv).toContain('"129,50"'); expect(csv).toContain('"7,00"'); expect(csv).toContain('"3,66"'); }); // Scenario 6: Taxi Deutschland Urban Ride (7% VAT according to §12 Abs. 2 Nr. 10 UStG) test("Scenario 6: Taxi Deutschland Urban Ride (7% VAT for local transport <= 50km)", async () => { const taxiReceipt: ProcessedReceipt = { id: "scen-6-taxi", merchant: { name: "Taxi Funk München eG - Wagen 312", address: "Heimeranstr. 35, 80339 München", taxId: "DE129554411", confidence: 0.97, }, date: { isoDate: "2026-08-15", time: "23:45", confidence: 0.95, }, documentType: "KASSENBON", receiptNumber: "TX-9901-26", currency: "EUR", totalAmount: { value: 28.5, confidence: 0.98, }, netAmount: 26.64, taxBreakdown: [ { ratePercent: 7, taxAmount: 1.86, netAmount: 26.64, }, ], lineItems: [ { description: "Taxifahrt Stadtgebiet München (8.4 km)", quantity: 1, price: 28.5, taxRate: 7, }, ], suggestedCategory: "Reisekosten & Hotel", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-taxi-scen-6", originalFileName: "taxi_muenchen.jpg", fileSizeBytes: 175000, createdAt: "2026-08-15T23:45:00.000Z", updatedAt: "2026-08-15T23:45:00.000Z", status: "ready", }; const val = validateReceiptMath(taxiReceipt); expect(val.isMathValid).toBe(true); expect(val.calculatedTaxSum).toBe(1.86); const buf = await generateDualSheetExcel([taxiReceipt]); const wb = new ExcelJS.Workbook(); await wb.xlsx.load(buf as any); const s1 = wb.getWorksheet("Belegübersicht"); expect(s1?.getRow(2).getCell(8).value).toBe(1.86); expect(s1?.getRow(2).getCell(9).value).toBe(0.0); }); // Scenario 7: APCOA Parkhaus Ticket (19% VAT) test("Scenario 7: APCOA Parkhaus Ticket (Short-term parking, 19% VAT)", async () => { const parkingReceipt: ProcessedReceipt = { id: "scen-7-apcoa", merchant: { name: "APCOA Parking Deutschland GmbH", address: "Parkhaus Marienplatz, 80331 München", taxId: "DE147852369", confidence: 0.99, }, date: { isoDate: "2026-08-15", time: "14:10", confidence: 0.98, }, documentType: "PARKTICKET", receiptNumber: "PK-APCOA-4412", currency: "EUR", totalAmount: { value: 14.0, confidence: 0.99, }, netAmount: 11.76, taxBreakdown: [ { ratePercent: 19, taxAmount: 2.24, netAmount: 11.76, }, ], lineItems: [ { description: "Parkzeit 3 Std. 15 Min. (Tarif Standard)", quantity: 1, price: 14.0, taxRate: 19, }, ], suggestedCategory: "Tanken & KFZ", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-apcoa-scen-7", originalFileName: "apcoa_parking.jpg", fileSizeBytes: 140000, createdAt: "2026-08-15T14:10:00.000Z", updatedAt: "2026-08-15T14:10:00.000Z", status: "ready", }; const val = validateReceiptMath(parkingReceipt); expect(val.isMathValid).toBe(true); const csv = generateAccountingCsv([parkingReceipt]); expect(csv).toContain('"APCOA Parking Deutschland GmbH"'); expect(csv).toContain('"14,00"'); expect(csv).toContain('"2,24"'); }); // Scenario 8: Bäckerei / Backstube Small Cash Receipt (7% Baked Goods + 19% Coffee) test("Scenario 8: Bäckerei Small Cash Receipt (7% take-away bread + 19% on-site cappuccino)", async () => { const bakeryReceipt: ProcessedReceipt = { id: "scen-8-bakery", merchant: { name: "Bio-Bäckerei Hofpfisterei GmbH", address: "Viktualienmarkt 8, 80331 München", taxId: "DE128844332", confidence: 0.98, }, date: { isoDate: "2026-08-15", time: "08:45", confidence: 0.97, }, documentType: "KASSENBON", receiptNumber: "HPF-2026-1192", currency: "EUR", totalAmount: { value: 8.9, confidence: 0.99, }, netAmount: 7.97, taxBreakdown: [ { ratePercent: 7, taxAmount: 0.35, netAmount: 4.95, // 5.30€ baked goods take-away }, { ratePercent: 19, taxAmount: 0.58, netAmount: 3.02, // 3.60€ Cappuccino }, ], lineItems: [ { description: "Pfister Öko-Landbrot 1kg", quantity: 1, price: 5.3, taxRate: 7, }, { description: "Cappuccino Groß (im Haus)", quantity: 1, price: 3.6, taxRate: 19, }, ], suggestedCategory: "Verpflegungsmehraufwand", validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, reviewField: "none", reviewReason: null, }, imageHash: "hash-bakery-scen-8", originalFileName: "hofpfisterei_beleg.jpg", fileSizeBytes: 155000, createdAt: "2026-08-15T08:45:00.000Z", updatedAt: "2026-08-15T08:45:00.000Z", status: "ready", }; const val = validateReceiptMath(bakeryReceipt); expect(val.isMathValid).toBe(true); const buf = await generateDualSheetExcel([bakeryReceipt]); const wb = new ExcelJS.Workbook(); await wb.xlsx.load(buf as any); const s1 = wb.getWorksheet("Belegübersicht"); expect(s1?.getRow(2).getCell(10).value).toBe(8.9); }); });