import Foundation @main struct ExpenseSummaryTests { static func match(_ id: String, category: String, amounts: [String]) -> MatchGroup { var ocr = OCRData() ocr.amounts = amounts let invoice = Material(id: id, name: id, path: "", type: "invoice", size: 0, matched: true, previewPath: "", ocr: ocr, displayAmount: 9999, sortDate: "", issueDate: "") return MatchGroup(id: id, invoices: [invoice], payments: [], category: category, matchType: "manual", score: 100, reasons: [], paymentTotal: 8888, expenseAmount: 7777) } static func main() throws { let traffic = (1...6).map { match("traffic-\($0)", category: "交通", amounts: ["100.10"]) } let hotel = match("hotel", category: "住宿", amounts: ["250.20"]) let excluded = match("excluded", category: "交通", amounts: ["999"]) var workspace = Workspace() workspace.matches = traffic + [hotel, excluded] let selection = MatchedPPTSelection(ids: Set((traffic + [hotel]).map(\.id))) let selected = try selection.exportWorkspace(from: workspace) let rows = ExpenseSummary.rows(for: selected.matches) precondition(rows.count == 2) precondition(rows.map(\.category) == ["交通", "住宿"]) precondition(rows[0].groupCount == 6 && rows[0].invoiceCount == 6) precondition(rows[0].amount == Decimal(string: "600.60")) precondition(rows[1].amount == Decimal(string: "250.20")) precondition(workspace.matches.count == 8) var multiple = match("multi", category: "交通", amounts: ["100", "-1,200.30", "5"]) multiple.invoices += [hotel.invoices[0]] let combined = ExpenseSummary.rows(for: [multiple]) precondition(combined[0].invoiceCount == 2) precondition(combined[0].amount == Decimal(string: "1450.50")) let emptyAmounts = ExpenseSummary.rows(for: [match("empty", category: "", amounts: [])]) precondition(emptyAmounts[0].category == "其他" && emptyAmounts[0].amount == .zero) precondition(ExpenseSummary.rows(for: []).isEmpty) let response = try JSONSerialization.data(withJSONObject: ["expenseGrouping": "category-v1", "expenseRowCount": 2]) try ExpenseSummary.validateExportResult(response, matches: selected.matches) for result: [String: Any] in [ ["destination": "/tmp/old-engine.xlsx"], ["expenseGrouping": "category-v1", "expenseRowCount": 7], ["expenseGrouping": "per-match", "expenseRowCount": 2] ] { let data = try JSONSerialization.data(withJSONObject: result) do { try ExpenseSummary.validateExportResult(data, matches: selected.matches) preconditionFailure("Outdated or inconsistent exports must be rejected") } catch {} } print("Selected expense grouping, counts and decimal totals tests passed") } }