@powerhousedao/contributor-billing 0.1.37 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/document-models/account-transactions/gen/schema/types.d.ts +3 -3
  2. package/dist/document-models/account-transactions/gen/schema/types.d.ts.map +1 -1
  3. package/dist/document-models/accounts/gen/schema/types.d.ts +7 -7
  4. package/dist/document-models/accounts/gen/schema/types.d.ts.map +1 -1
  5. package/dist/document-models/billing-statement/gen/document-schema.d.ts +16 -16
  6. package/dist/document-models/billing-statement/gen/document-schema.d.ts.map +1 -1
  7. package/dist/document-models/billing-statement/gen/schema/types.d.ts +5 -5
  8. package/dist/document-models/billing-statement/gen/schema/types.d.ts.map +1 -1
  9. package/dist/document-models/expense-report/gen/document-model.js +1 -1
  10. package/dist/document-models/expense-report/gen/document-schema.d.ts +16 -16
  11. package/dist/document-models/expense-report/gen/ph-factories.d.ts.map +1 -1
  12. package/dist/document-models/expense-report/gen/ph-factories.js +5 -0
  13. package/dist/document-models/expense-report/gen/schema/types.d.ts +2 -2
  14. package/dist/document-models/expense-report/gen/schema/types.d.ts.map +1 -1
  15. package/dist/document-models/expense-report/gen/schema/zod.d.ts.map +1 -1
  16. package/dist/document-models/expense-report/gen/schema/zod.js +10 -30
  17. package/dist/document-models/expense-report/gen/utils.d.ts.map +1 -1
  18. package/dist/document-models/expense-report/gen/utils.js +5 -0
  19. package/dist/document-models/expense-report/src/reducers/wallet.d.ts.map +1 -1
  20. package/dist/document-models/expense-report/src/reducers/wallet.js +61 -0
  21. package/dist/document-models/invoice/gen/document-schema.d.ts +32 -32
  22. package/dist/document-models/invoice/gen/document-schema.d.ts.map +1 -1
  23. package/dist/document-models/invoice/gen/schema/types.d.ts +10 -10
  24. package/dist/document-models/invoice/gen/schema/types.d.ts.map +1 -1
  25. package/dist/document-models/invoice/gen/schema/zod.d.ts.map +1 -1
  26. package/dist/document-models/service-offering/gen/document-schema.d.ts +16 -16
  27. package/dist/document-models/service-offering/gen/document-schema.d.ts.map +1 -1
  28. package/dist/document-models/service-offering/gen/schema/types.d.ts +11 -11
  29. package/dist/document-models/service-offering/gen/schema/types.d.ts.map +1 -1
  30. package/dist/document-models/service-subscriptions/gen/schema/types.d.ts +6 -6
  31. package/dist/document-models/service-subscriptions/gen/schema/types.d.ts.map +1 -1
  32. package/dist/document-models/snapshot-report/gen/document-schema.d.ts.map +1 -1
  33. package/dist/document-models/snapshot-report/gen/schema/types.d.ts +8 -8
  34. package/dist/document-models/snapshot-report/gen/schema/types.d.ts.map +1 -1
  35. package/dist/document-models/snapshot-report/src/reducers/configuration.d.ts.map +1 -1
  36. package/dist/document-models/snapshot-report/src/reducers/configuration.js +1 -1
  37. package/dist/editors/accounts-editor/components/AccountForm.d.ts.map +1 -1
  38. package/dist/editors/accounts-editor/components/AccountForm.js +3 -1
  39. package/dist/editors/builder-team-admin/components/FolderTree.d.ts.map +1 -1
  40. package/dist/editors/builder-team-admin/components/FolderTree.js +2 -2
  41. package/dist/editors/contributor-billing/components/AddMonthButton.d.ts +5 -0
  42. package/dist/editors/contributor-billing/components/AddMonthButton.d.ts.map +1 -0
  43. package/dist/editors/contributor-billing/components/AddMonthButton.js +56 -0
  44. package/dist/editors/contributor-billing/components/BillingOverview.d.ts +10 -0
  45. package/dist/editors/contributor-billing/components/BillingOverview.d.ts.map +1 -0
  46. package/dist/editors/contributor-billing/components/BillingOverview.js +117 -0
  47. package/dist/editors/contributor-billing/components/DashboardHome.d.ts +11 -0
  48. package/dist/editors/contributor-billing/components/DashboardHome.d.ts.map +1 -0
  49. package/dist/editors/contributor-billing/components/DashboardHome.js +51 -0
  50. package/dist/editors/contributor-billing/components/DriveContents.d.ts +8 -2
  51. package/dist/editors/contributor-billing/components/DriveContents.d.ts.map +1 -1
  52. package/dist/editors/contributor-billing/components/DriveContents.js +24 -10
  53. package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
  54. package/dist/editors/contributor-billing/components/DriveExplorer.js +18 -3
  55. package/dist/editors/contributor-billing/components/FolderTree.d.ts +19 -9
  56. package/dist/editors/contributor-billing/components/FolderTree.d.ts.map +1 -1
  57. package/dist/editors/contributor-billing/components/FolderTree.js +200 -103
  58. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts +8 -1
  59. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
  60. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +5 -3
  61. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts +6 -1
  62. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts.map +1 -1
  63. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.js +14 -6
  64. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts +6 -2
  65. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
  66. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +68 -19
  67. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.d.ts +10 -1
  68. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.d.ts.map +1 -1
  69. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.js +145 -32
  70. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.d.ts.map +1 -1
  71. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.js +6 -1
  72. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.d.ts +1 -1
  73. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.d.ts.map +1 -1
  74. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.js +33 -7
  75. package/dist/editors/contributor-billing/components/MonthOverview.d.ts +12 -0
  76. package/dist/editors/contributor-billing/components/MonthOverview.d.ts.map +1 -0
  77. package/dist/editors/contributor-billing/components/MonthOverview.js +35 -0
  78. package/dist/editors/contributor-billing/components/MonthlyReporting.d.ts +13 -0
  79. package/dist/editors/contributor-billing/components/MonthlyReporting.d.ts.map +1 -0
  80. package/dist/editors/contributor-billing/components/MonthlyReporting.js +90 -0
  81. package/dist/editors/contributor-billing/components/ReportingView.d.ts +10 -0
  82. package/dist/editors/contributor-billing/components/ReportingView.d.ts.map +1 -0
  83. package/dist/editors/contributor-billing/components/ReportingView.js +112 -0
  84. package/dist/editors/contributor-billing/config.js +1 -1
  85. package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.d.ts +54 -0
  86. package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.d.ts.map +1 -0
  87. package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.js +145 -0
  88. package/dist/editors/expense-report/components/AddBillingStatementModal.d.ts +3 -1
  89. package/dist/editors/expense-report/components/AddBillingStatementModal.d.ts.map +1 -1
  90. package/dist/editors/expense-report/components/AddBillingStatementModal.js +23 -7
  91. package/dist/editors/expense-report/components/AggregatedExpensesTable.js +2 -2
  92. package/dist/editors/expense-report/components/ExpenseReportPDF.js +2 -2
  93. package/dist/editors/expense-report/editor.d.ts.map +1 -1
  94. package/dist/editors/expense-report/editor.js +70 -14
  95. package/dist/editors/expense-report/hooks/useSyncWallet.js +9 -9
  96. package/dist/editors/invoice/ingestPDF.js +1 -1
  97. package/dist/editors/invoice/invoiceToGnosis.js +2 -2
  98. package/dist/editors/invoice/requestFinance.js +2 -2
  99. package/dist/editors/invoice/uploadPdfChunked.js +2 -2
  100. package/dist/editors/snapshot-report-editor/editor.d.ts.map +1 -1
  101. package/dist/editors/snapshot-report-editor/editor.js +26 -5
  102. package/dist/scripts/invoice/pdfToClaudeAI.d.ts.map +1 -1
  103. package/dist/scripts/invoice/pdfToClaudeAI.js +3 -1
  104. package/dist/style.css +85 -0
  105. package/dist/subgraphs/budget-statements/index.d.ts +11 -0
  106. package/dist/subgraphs/budget-statements/index.d.ts.map +1 -0
  107. package/dist/subgraphs/budget-statements/index.js +11 -0
  108. package/dist/subgraphs/budget-statements/resolvers.d.ts +3 -0
  109. package/dist/subgraphs/budget-statements/resolvers.d.ts.map +1 -0
  110. package/dist/subgraphs/budget-statements/resolvers.js +400 -0
  111. package/dist/subgraphs/budget-statements/schema.d.ts +3 -0
  112. package/dist/subgraphs/budget-statements/schema.d.ts.map +1 -0
  113. package/dist/subgraphs/budget-statements/schema.js +132 -0
  114. package/dist/subgraphs/index.d.ts +1 -0
  115. package/dist/subgraphs/index.d.ts.map +1 -1
  116. package/dist/subgraphs/index.js +1 -0
  117. package/package.json +1 -1
@@ -0,0 +1,400 @@
1
+ import {} from "@powerhousedao/reactor-api";
2
+ // Helper to create a period key from start and end dates
3
+ const getPeriodKey = (periodStart, periodEnd) => {
4
+ if (!periodStart || !periodEnd)
5
+ return null;
6
+ // Normalize dates to YYYY-MM-DD format for consistent matching
7
+ const startDate = new Date(periodStart);
8
+ const endDate = new Date(periodEnd);
9
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime()))
10
+ return null;
11
+ const formatDate = (d) => d.toISOString().split("T")[0];
12
+ return `${formatDate(startDate)}_${formatDate(endDate)}`;
13
+ };
14
+ // Helper to extract month key from date (format: "JAN2026")
15
+ const getMonthKey = (dateStr) => {
16
+ if (!dateStr)
17
+ return null;
18
+ const date = new Date(dateStr);
19
+ if (isNaN(date.getTime()))
20
+ return null;
21
+ const months = [
22
+ "JAN",
23
+ "FEB",
24
+ "MAR",
25
+ "APR",
26
+ "MAY",
27
+ "JUN",
28
+ "JUL",
29
+ "AUG",
30
+ "SEP",
31
+ "OCT",
32
+ "NOV",
33
+ "DEC",
34
+ ];
35
+ return `${months[date.getMonth()]}${date.getFullYear()}`;
36
+ };
37
+ // Helper to sort budget statements by month (most recent first)
38
+ const sortByMonth = (a, b) => {
39
+ const parseMonth = (m) => {
40
+ const months = {
41
+ JAN: 0,
42
+ FEB: 1,
43
+ MAR: 2,
44
+ APR: 3,
45
+ MAY: 4,
46
+ JUN: 5,
47
+ JUL: 6,
48
+ AUG: 7,
49
+ SEP: 8,
50
+ OCT: 9,
51
+ NOV: 10,
52
+ DEC: 11,
53
+ };
54
+ const monthStr = m.substring(0, 3);
55
+ const year = parseInt(m.substring(3), 10);
56
+ return new Date(year, months[monthStr] || 0).getTime();
57
+ };
58
+ return parseMonth(b.month) - parseMonth(a.month);
59
+ };
60
+ export const getResolvers = (subgraph) => {
61
+ const reactor = subgraph.reactor;
62
+ return {
63
+ Query: {
64
+ budgetStatements: async (_, args) => {
65
+ const { teamId, networkSlug } = args.filter || {};
66
+ const drives = await reactor.getDrives();
67
+ // Step 1: Collect all documents from all drives (or filtered by networkSlug)
68
+ const snapshotReportDocs = [];
69
+ const expenseReportDocs = [];
70
+ const accountTransactionsDocs = new Map();
71
+ const builderProfileDocs = new Map();
72
+ // If networkSlug is provided, find the network drive and get valid builder PHIDs
73
+ let allowedBuilderPhids = null;
74
+ if (networkSlug) {
75
+ const targetNetworkSlug = networkSlug.toLowerCase().trim();
76
+ // Find the network drive matching the slug
77
+ for (const driveId of drives) {
78
+ try {
79
+ const docIds = await reactor.getDocuments(driveId);
80
+ const docs = await Promise.all(docIds.map(async (docId) => {
81
+ try {
82
+ return await reactor.getDocument(docId);
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }));
88
+ const networkDoc = docs.find((doc) => {
89
+ if (!doc ||
90
+ doc.header.documentType !== "powerhouse/network-profile")
91
+ return false;
92
+ const state = doc.state
93
+ .global;
94
+ if (!state?.name)
95
+ return false;
96
+ const slug = state.name
97
+ .toLowerCase()
98
+ .trim()
99
+ .split(/\s+/)
100
+ .join("-");
101
+ return slug === targetNetworkSlug;
102
+ });
103
+ if (networkDoc) {
104
+ // Get the builders list from this drive
105
+ const buildersDoc = docs.find((doc) => doc && doc.header.documentType === "powerhouse/builders");
106
+ if (buildersDoc) {
107
+ const state = buildersDoc.state.global;
108
+ if (Array.isArray(state?.builders)) {
109
+ allowedBuilderPhids = new Set(state.builders.filter((id) => typeof id === "string"));
110
+ }
111
+ }
112
+ break;
113
+ }
114
+ }
115
+ catch (error) {
116
+ console.warn(`Failed to inspect drive ${driveId}:`, error);
117
+ }
118
+ }
119
+ // If no network found or no builders list, return empty results
120
+ if (!allowedBuilderPhids) {
121
+ return [];
122
+ }
123
+ }
124
+ // Always scan all drives - reports may be in different drives than the network drive
125
+ // Filter is applied by ownerId matching allowedBuilderPhids
126
+ const drivesToScan = drives;
127
+ for (const driveId of drivesToScan) {
128
+ const docsIds = await reactor.getDocuments(driveId);
129
+ const docs = await Promise.all(docsIds.map(async (docId) => reactor.getDocument(docId)));
130
+ for (const doc of docs) {
131
+ const docType = doc.header.documentType;
132
+ if (docType === "powerhouse/snapshot-report") {
133
+ const snapshotDoc = doc;
134
+ const ownerId = snapshotDoc.state.global.ownerId;
135
+ // Apply filters: teamId takes precedence, then networkSlug (via allowedBuilderPhids)
136
+ if (teamId && ownerId !== teamId)
137
+ continue;
138
+ if (allowedBuilderPhids &&
139
+ ownerId &&
140
+ !allowedBuilderPhids.has(ownerId))
141
+ continue;
142
+ snapshotReportDocs.push(snapshotDoc);
143
+ }
144
+ else if (docType === "powerhouse/expense-report") {
145
+ const expenseDoc = doc;
146
+ const ownerId = expenseDoc.state.global.ownerId;
147
+ // Apply filters: teamId takes precedence, then networkSlug (via allowedBuilderPhids)
148
+ if (teamId && ownerId !== teamId)
149
+ continue;
150
+ if (allowedBuilderPhids &&
151
+ ownerId &&
152
+ !allowedBuilderPhids.has(ownerId))
153
+ continue;
154
+ expenseReportDocs.push(expenseDoc);
155
+ }
156
+ else if (docType === "powerhouse/account-transactions") {
157
+ const txDoc = doc;
158
+ accountTransactionsDocs.set(doc.header.id, txDoc);
159
+ }
160
+ else if (docType === "powerhouse/builder-profile") {
161
+ builderProfileDocs.set(doc.header.id, doc);
162
+ }
163
+ }
164
+ }
165
+ // Step 2: Group reports by ownerId AND period
166
+ // Key format: "ownerId_periodStart_periodEnd"
167
+ const budgetStatementsByOwnerAndPeriod = new Map();
168
+ // Group snapshot reports
169
+ for (const snapshotDoc of snapshotReportDocs) {
170
+ const state = snapshotDoc.state.global;
171
+ const ownerId = state.ownerId;
172
+ if (!ownerId)
173
+ continue;
174
+ const periodKey = getPeriodKey(state.reportPeriodStart, state.reportPeriodEnd);
175
+ if (!periodKey)
176
+ continue;
177
+ const key = `${ownerId}_${periodKey}`;
178
+ if (!budgetStatementsByOwnerAndPeriod.has(key)) {
179
+ budgetStatementsByOwnerAndPeriod.set(key, {
180
+ ownerId,
181
+ periodKey,
182
+ snapshotReport: null,
183
+ expenseReport: null,
184
+ });
185
+ }
186
+ budgetStatementsByOwnerAndPeriod.get(key).snapshotReport =
187
+ snapshotDoc;
188
+ }
189
+ // Group expense reports and match with snapshot reports
190
+ for (const expenseDoc of expenseReportDocs) {
191
+ const state = expenseDoc.state.global;
192
+ const ownerId = state.ownerId;
193
+ if (!ownerId)
194
+ continue;
195
+ const periodKey = getPeriodKey(state.periodStart, state.periodEnd);
196
+ if (!periodKey)
197
+ continue;
198
+ const key = `${ownerId}_${periodKey}`;
199
+ if (!budgetStatementsByOwnerAndPeriod.has(key)) {
200
+ budgetStatementsByOwnerAndPeriod.set(key, {
201
+ ownerId,
202
+ periodKey,
203
+ snapshotReport: null,
204
+ expenseReport: null,
205
+ });
206
+ }
207
+ budgetStatementsByOwnerAndPeriod.get(key).expenseReport = expenseDoc;
208
+ }
209
+ // Step 3: Build the budget statements
210
+ const budgetStatements = [];
211
+ for (const [key, { ownerId, periodKey, snapshotReport, expenseReport },] of budgetStatementsByOwnerAndPeriod.entries()) {
212
+ // Get the builder profile for this owner
213
+ let builderProfileDoc = builderProfileDocs.get(ownerId) || null;
214
+ // Try to fetch directly if not found
215
+ if (!builderProfileDoc) {
216
+ try {
217
+ builderProfileDoc =
218
+ await reactor.getDocument(ownerId);
219
+ }
220
+ catch {
221
+ // Ignore errors - profile may not exist
222
+ }
223
+ }
224
+ // Build owner object
225
+ const ownerState = builderProfileDoc
226
+ ? (builderProfileDoc.state?.global ?? null)
227
+ : null;
228
+ const owner = {
229
+ id: ownerId,
230
+ name: ownerState?.name || "Unknown",
231
+ code: ownerState?.code || "",
232
+ logo: ownerState?.icon || "",
233
+ };
234
+ // Derive the month from the period start date
235
+ const periodStartDate = snapshotReport?.state.global.reportPeriodStart ||
236
+ expenseReport?.state.global.periodStart ||
237
+ null;
238
+ const month = getMonthKey(periodStartDate) || periodKey;
239
+ // Build snapshot report data
240
+ const snapshotReportData = snapshotReport
241
+ ? buildSnapshotReportData(snapshotReport, accountTransactionsDocs)
242
+ : {
243
+ startDate: "",
244
+ endDate: "",
245
+ accounts: [],
246
+ };
247
+ // Build expense report data
248
+ const expenseReportData = expenseReport
249
+ ? buildExpenseReportData(expenseReport)
250
+ : {
251
+ periodStart: "",
252
+ periodEnd: "",
253
+ groups: [],
254
+ wallets: [],
255
+ };
256
+ budgetStatements.push({
257
+ id: key,
258
+ owner,
259
+ month,
260
+ snapshotReport: snapshotReportData,
261
+ expenseReport: expenseReportData,
262
+ });
263
+ }
264
+ // Sort by month (most recent first)
265
+ budgetStatements.sort(sortByMonth);
266
+ return budgetStatements;
267
+ },
268
+ },
269
+ };
270
+ };
271
+ /**
272
+ * Build snapshot report data from a SnapshotReportDocument
273
+ */
274
+ function buildSnapshotReportData(doc, accountTransactionsDocs) {
275
+ const state = doc.state.global;
276
+ return {
277
+ startDate: state.reportPeriodStart || state.startDate || "",
278
+ endDate: state.reportPeriodEnd || state.endDate || "",
279
+ accounts: state.snapshotAccounts.map((account) => {
280
+ // Build balances from startingBalances and endingBalances
281
+ const balances = account.startingBalances.map((startBal, index) => {
282
+ const endBal = account.endingBalances[index] || {
283
+ amount: { unit: startBal.token, value: "0" },
284
+ token: startBal.token,
285
+ };
286
+ return {
287
+ startingBalance: startBal.amount,
288
+ endingBalance: endBal.amount,
289
+ token: {
290
+ symbol: startBal.token,
291
+ contractAddress: "", // Not available in the document model
292
+ },
293
+ };
294
+ });
295
+ // Build transactions from snapshot account transactions
296
+ const transactions = account.transactions.map((tx) => ({
297
+ id: tx.id,
298
+ datetime: tx.datetime,
299
+ txHash: tx.txHash,
300
+ counterParty: tx.counterParty || "",
301
+ counterPartyName: getCounterPartyName(tx.counterPartyAccountId, accountTransactionsDocs),
302
+ amount: {
303
+ value: tx.amount,
304
+ unit: tx.token,
305
+ },
306
+ direction: tx.direction,
307
+ flowType: tx.flowType || "External",
308
+ }));
309
+ return {
310
+ id: account.id,
311
+ name: account.accountName,
312
+ address: account.accountAddress,
313
+ type: account.type,
314
+ balances,
315
+ transactions,
316
+ };
317
+ }),
318
+ };
319
+ }
320
+ /**
321
+ * Build expense report data from an ExpenseReportDocument
322
+ */
323
+ function buildExpenseReportData(doc) {
324
+ const state = doc.state.global;
325
+ // Build groups
326
+ const groups = state.groups.map((group) => ({
327
+ id: group.id,
328
+ label: group.label || "",
329
+ parentId: group.parentId || "",
330
+ }));
331
+ // Create a map for quick group label lookup
332
+ const groupLabelMap = new Map();
333
+ for (const group of state.groups) {
334
+ groupLabelMap.set(group.id, group.label || "");
335
+ }
336
+ // Build wallets
337
+ const wallets = state.wallets.map((wallet) => {
338
+ // Build totals from wallet totals
339
+ const totals = (wallet.totals || [])
340
+ .filter((t) => t !== null)
341
+ .map((total) => ({
342
+ group: total.group || "",
343
+ groupLabel: groupLabelMap.get(total.group || "") || "",
344
+ totalBudget: { unit: "USDS", value: String(total.totalBudget || 0) },
345
+ totalForecast: {
346
+ unit: "USDS",
347
+ value: String(total.totalForecast || 0),
348
+ },
349
+ totalActuals: { unit: "USDS", value: String(total.totalActuals || 0) },
350
+ totalPayments: {
351
+ unit: "USDS",
352
+ value: String(total.totalPayments || 0),
353
+ },
354
+ }));
355
+ // Build line items
356
+ const lineItems = (wallet.lineItems || [])
357
+ .filter((li) => li !== null)
358
+ .map((item) => ({
359
+ id: item.id || "",
360
+ label: item.label || "",
361
+ groupId: item.group || "",
362
+ groupLabel: groupLabelMap.get(item.group || "") || "",
363
+ budget: { unit: "USDS", value: String(item.budget || 0) },
364
+ forecast: { unit: "USDS", value: String(item.forecast || 0) },
365
+ actuals: { unit: "USDS", value: String(item.actuals || 0) },
366
+ payments: { unit: "USDS", value: String(item.payments || 0) },
367
+ comments: item.comments || null,
368
+ }));
369
+ // Collect billing statement IDs
370
+ const billingStatementIds = (wallet.billingStatements || []).filter((id) => id !== null);
371
+ return {
372
+ name: wallet.name || null,
373
+ address: wallet.wallet || null,
374
+ totals,
375
+ lineItems,
376
+ billingStatementIds,
377
+ };
378
+ });
379
+ return {
380
+ periodStart: state.periodStart || "",
381
+ periodEnd: state.periodEnd || "",
382
+ groups,
383
+ wallets,
384
+ };
385
+ }
386
+ /**
387
+ * Get counter party name from account-transactions document
388
+ */
389
+ function getCounterPartyName(counterPartyAccountId, accountTransactionsDocs) {
390
+ if (!counterPartyAccountId)
391
+ return "";
392
+ // Search through all account-transactions docs to find the account name
393
+ for (const txDoc of accountTransactionsDocs.values()) {
394
+ const account = txDoc.state.global.account;
395
+ if (account && account.id === counterPartyAccountId) {
396
+ return account.name || "";
397
+ }
398
+ }
399
+ return "";
400
+ }
@@ -0,0 +1,3 @@
1
+ import type { DocumentNode } from "graphql";
2
+ export declare const schema: DocumentNode;
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/budget-statements/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,YAkIpB,CAAC"}
@@ -0,0 +1,132 @@
1
+ import { gql } from "graphql-tag";
2
+ export const schema = gql `
3
+ """
4
+ Subgraph definition
5
+ """
6
+ type Query {
7
+ budgetStatements(filter: budgetStatementsFilter): [BudgetStatement!]!
8
+ }
9
+
10
+ input budgetStatementsFilter {
11
+ teamId: PHID
12
+ networkSlug: String
13
+ }
14
+
15
+ type BudgetStatement {
16
+ id: OID!
17
+ owner: BudgetStatementOwner!
18
+ month: String! ## JAN2026
19
+ snapshotReport: BudgetStatementSnapshotReport!
20
+ expenseReport: BudgetStatementExpenseReport!
21
+ }
22
+
23
+ type BudgetStatementOwner {
24
+ id: PHID!
25
+ name: String!
26
+ code: String!
27
+ logo: URL!
28
+ }
29
+
30
+ type BudgetStatementSnapshotReport {
31
+ startDate: DateTime!
32
+ endDate: DateTime!
33
+ accounts: [SnapshotAccount!]!
34
+ }
35
+
36
+ type SnapshotAccount {
37
+ id: ID!
38
+ name: String!
39
+ address: String!
40
+ type: SnapAccountType!
41
+ balances: [SnapshotAccountBalance!]!
42
+ transactions: [SnapshotAccountTransaction!]!
43
+ }
44
+
45
+ type SnapshotAccountBalance {
46
+ startingBalance: Amount_Currency!
47
+ endingBalance: Amount_Currency!
48
+ token: Token!
49
+ }
50
+
51
+ type Token {
52
+ symbol: String!
53
+ contractAddress: EthereumAddress!
54
+ }
55
+
56
+ enum SnapAccountType {
57
+ Source
58
+ Internal
59
+ Destination
60
+ External
61
+ }
62
+
63
+ type SnapshotAccountTransaction {
64
+ id: ID!
65
+ datetime: DateTime!
66
+ txHash: String!
67
+ counterParty: EthereumAddress!
68
+ counterPartyName: String!
69
+ amount: TxAmount!
70
+ direction: AccountTransactionDirection!
71
+ flowType: AccountTransactionFlowType!
72
+ }
73
+
74
+ type TxAmount {
75
+ value: Amount_Currency!
76
+ unit: String!
77
+ }
78
+
79
+ enum AccountTransactionDirection {
80
+ INFLOW
81
+ OUTFLOW
82
+ }
83
+
84
+ enum AccountTransactionFlowType {
85
+ TopUp
86
+ Return
87
+ Internal
88
+ External
89
+ }
90
+
91
+ type BudgetStatementExpenseReport {
92
+ periodStart: DateTime!
93
+ periodEnd: DateTime!
94
+ groups: [ExpenseReportGroup!]!
95
+ wallets: [ExpenseReportWallet!]!
96
+ }
97
+
98
+ type ExpenseReportGroup {
99
+ id: ID!
100
+ label: String!
101
+ parentId: ID!
102
+ }
103
+
104
+ type ExpenseReportWallet {
105
+ name: String
106
+ address: EthereumAddress
107
+ totals: [ExpenseReportGroupTotals!]!
108
+ lineItems: [ExpenseReportLineItem!]!
109
+ billingStatementIds: [PHID!]!
110
+ }
111
+
112
+ type ExpenseReportGroupTotals {
113
+ group: ID!
114
+ groupLabel: String!
115
+ totalBudget: Amount_Currency!
116
+ totalForecast: Amount_Currency!
117
+ totalActuals: Amount_Currency!
118
+ totalPayments: Amount_Currency!
119
+ }
120
+
121
+ type ExpenseReportLineItem {
122
+ id: ID!
123
+ label: String!
124
+ groupId: ID!
125
+ groupLabel: String! # resolved from groups
126
+ budget: Amount_Currency!
127
+ forecast: Amount_Currency!
128
+ actuals: Amount_Currency!
129
+ payments: Amount_Currency!
130
+ comments: String
131
+ }
132
+ `;
@@ -8,4 +8,5 @@ export * as SnapshotReportSubgraph from "./snapshot-report/index.js";
8
8
  export * as AccTxsAddonSubgraph from "./acc-txs-addon/index.js";
9
9
  export * as ServiceSubscriptionsSubgraph from "./service-subscriptions/index.js";
10
10
  export * as ServiceOfferingSubgraph from "./service-offering/index.js";
11
+ export * as BudgetStatementsSubgraph from "./budget-statements/index.js";
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,2BAA2B,MAAM,iCAAiC,CAAC;AAC/E,OAAO,KAAK,eAAe,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,oBAAoB,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,wBAAwB,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,qBAAqB,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,sBAAsB,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,4BAA4B,MAAM,kCAAkC,CAAC;AACjF,OAAO,KAAK,uBAAuB,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,2BAA2B,MAAM,iCAAiC,CAAC;AAC/E,OAAO,KAAK,eAAe,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,oBAAoB,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,wBAAwB,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,qBAAqB,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,sBAAsB,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,4BAA4B,MAAM,kCAAkC,CAAC;AACjF,OAAO,KAAK,uBAAuB,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,wBAAwB,MAAM,8BAA8B,CAAC"}
@@ -8,3 +8,4 @@ export * as SnapshotReportSubgraph from "./snapshot-report/index.js";
8
8
  export * as AccTxsAddonSubgraph from "./acc-txs-addon/index.js";
9
9
  export * as ServiceSubscriptionsSubgraph from "./service-subscriptions/index.js";
10
10
  export * as ServiceOfferingSubgraph from "./service-offering/index.js";
11
+ export * as BudgetStatementsSubgraph from "./budget-statements/index.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/contributor-billing",
3
3
  "description": "Document models that help contributors of open organisations get paid anonymously for their work on a monthly basis.",
4
- "version": "0.1.37",
4
+ "version": "0.1.40",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [