forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcsv-generator.service.ts
More file actions
113 lines (105 loc) · 3.44 KB
/
Copy pathcsv-generator.service.ts
File metadata and controls
113 lines (105 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Injectable } from "@nestjs/common";
import { ExportJob } from "./entities/export-job.entity";
import * as XLSX from "xlsx";
@Injectable()
export class CsvGeneratorService {
async generateCsv(data: any, job: ExportJob): Promise<Buffer> {
const rows = this.flattenDataToRows(data, job);
const header = Object.keys(rows[0] ?? {}).join(",");
const body = rows.map((row) =>
Object.values(row)
.map((v) => `"${String(v ?? "").replace(/"/g, '""')}"`)
.join(","),
);
return Buffer.from([header, ...body].join("\n"), "utf-8");
}
async generateXlsx(data: any, job: ExportJob): Promise<Buffer> {
const rows = this.flattenDataToRows(data, job);
const worksheet = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Export");
return Buffer.from(
XLSX.write(workbook, { type: "buffer", bookType: "xlsx" }),
);
}
private flattenDataToRows(data: any, _job: ExportJob): Record<string, any>[] {
// Monthly summary
if (data.monthlyData) {
return data.monthlyData.map((m: any) => ({
month: m.month,
expense_count: m.expenseCount,
total_amount: m.totalAmount,
settlements: m.settlements,
settlement_amount: m.settlementAmount,
}));
}
// Category breakdown
if (data.categories) {
return data.categories.map((c: any) => ({
category: c.category,
total_amount: c.totalAmount,
expense_count: c.expenseCount,
average_amount: c.averageAmount.toFixed(2),
percentage: c.percentage.toFixed(2),
}));
}
// Partner summary
if (data.partners) {
return data.partners.map((p: any) => ({
partner_name: p.partnerName,
owed_to_you: p.totalOwedToYou,
you_owe: p.totalYouOwe,
net_balance: p.netBalance,
expense_count: p.expenseCount,
}));
}
// Payment history / timeline
if (data.timeline) {
return data.timeline.map((t: any) => ({
date: new Date(t.date).toLocaleDateString(),
type: t.type,
description: t.description,
amount: t.amount,
currency: t.currency,
direction: t.direction ?? "N/A",
}));
}
// Tax report
if (data.businessExpenses) {
return [
...data.businessExpenses.map((e: any) => ({
type: "BUSINESS",
date: new Date(e.createdAt).toLocaleDateString(),
description: e.description,
category: e.category,
amount: e.amount,
})),
...data.personalExpenses.map((e: any) => ({
type: "PERSONAL",
date: new Date(e.createdAt).toLocaleDateString(),
description: e.description,
category: e.category,
amount: e.amount,
})),
];
}
// Fallback: raw expenses + settlements
const expenses = (data.expenses ?? []).map((e: any) => ({
type: "EXPENSE",
date: new Date(e.createdAt).toLocaleDateString(),
description: e.description,
category: e.category,
amount: e.amount,
currency: e.currency,
}));
const settlements = (data.settlements ?? []).map((s: any) => ({
type: "SETTLEMENT",
date: new Date(s.createdAt).toLocaleDateString(),
description: s.description ?? "Payment settlement",
category: "",
amount: s.amount,
currency: s.currency,
}));
return [...expenses, ...settlements];
}
}