@fayz-ai/financial 0.0.0-stage → 0.1.0

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 (161) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -2
  3. package/dist/chunk-2XWEZXFT.js +2805 -0
  4. package/dist/chunk-2XWEZXFT.js.map +1 -0
  5. package/dist/chunk-4FWWKKZB.js +49 -0
  6. package/dist/chunk-4FWWKKZB.js.map +1 -0
  7. package/dist/chunk-4GXBOEI3.js +50 -0
  8. package/dist/chunk-4GXBOEI3.js.map +1 -0
  9. package/dist/chunk-4S5GK776.js +31 -0
  10. package/dist/chunk-4S5GK776.js.map +1 -0
  11. package/dist/chunk-56NQHYL7.js +20 -0
  12. package/dist/chunk-56NQHYL7.js.map +1 -0
  13. package/dist/chunk-7HISDBUG.js +84 -0
  14. package/dist/chunk-7HISDBUG.js.map +1 -0
  15. package/dist/chunk-FNXIAIUZ.js +81 -0
  16. package/dist/chunk-FNXIAIUZ.js.map +1 -0
  17. package/dist/chunk-HJPUHTI2.js +77 -0
  18. package/dist/chunk-HJPUHTI2.js.map +1 -0
  19. package/dist/chunk-IP5YH4S5.js +14 -0
  20. package/dist/chunk-IP5YH4S5.js.map +1 -0
  21. package/dist/chunk-J7BWATQU.js +10 -0
  22. package/dist/chunk-J7BWATQU.js.map +1 -0
  23. package/dist/chunk-MCSPCXQK.js +190 -0
  24. package/dist/chunk-MCSPCXQK.js.map +1 -0
  25. package/dist/chunk-T7WIM2V7.js +34 -0
  26. package/dist/chunk-T7WIM2V7.js.map +1 -0
  27. package/dist/chunk-TZZNVPBY.js +9 -0
  28. package/dist/chunk-TZZNVPBY.js.map +1 -0
  29. package/dist/chunk-UERJA7QH.js +893 -0
  30. package/dist/chunk-UERJA7QH.js.map +1 -0
  31. package/dist/chunk-XVELDCSL.js +11 -0
  32. package/dist/chunk-XVELDCSL.js.map +1 -0
  33. package/dist/chunk-YUNLQYOZ.js +26 -0
  34. package/dist/chunk-YUNLQYOZ.js.map +1 -0
  35. package/dist/chunk-ZFUQBYBG.js +124 -0
  36. package/dist/chunk-ZFUQBYBG.js.map +1 -0
  37. package/dist/data/fx-auto.d.ts +7 -0
  38. package/dist/data/fx-auto.d.ts.map +1 -0
  39. package/dist/data/fx-auto.js +3 -0
  40. package/dist/data/fx-auto.js.map +1 -0
  41. package/dist/data/idempotency.d.ts +2 -0
  42. package/dist/data/idempotency.d.ts.map +1 -0
  43. package/dist/data/idempotency.js +3 -0
  44. package/dist/data/idempotency.js.map +1 -0
  45. package/dist/data/mock.d.ts +29 -0
  46. package/dist/data/mock.d.ts.map +1 -0
  47. package/dist/data/mock.js +13 -0
  48. package/dist/data/mock.js.map +1 -0
  49. package/dist/data/provider.d.ts +265 -0
  50. package/dist/data/provider.d.ts.map +1 -0
  51. package/dist/data/provider.js +3 -0
  52. package/dist/data/provider.js.map +1 -0
  53. package/dist/data/reconcile.d.ts +37 -0
  54. package/dist/data/reconcile.d.ts.map +1 -0
  55. package/dist/data/reconcile.js +5 -0
  56. package/dist/data/reconcile.js.map +1 -0
  57. package/dist/data/supabase.d.ts +3 -0
  58. package/dist/data/supabase.d.ts.map +1 -0
  59. package/dist/data/supabase.js +16 -0
  60. package/dist/data/supabase.js.map +1 -0
  61. package/dist/data/tables.d.ts +39 -0
  62. package/dist/data/tables.d.ts.map +1 -0
  63. package/dist/data/tables.js +3 -0
  64. package/dist/data/tables.js.map +1 -0
  65. package/dist/domain/account.d.ts +66 -0
  66. package/dist/domain/account.d.ts.map +1 -0
  67. package/dist/domain/account.js +3 -0
  68. package/dist/domain/account.js.map +1 -0
  69. package/dist/domain/card.d.ts +33 -0
  70. package/dist/domain/card.d.ts.map +1 -0
  71. package/dist/domain/card.js +3 -0
  72. package/dist/domain/card.js.map +1 -0
  73. package/dist/domain/cash-session.d.ts +80 -0
  74. package/dist/domain/cash-session.d.ts.map +1 -0
  75. package/dist/domain/cash-session.js +4 -0
  76. package/dist/domain/cash-session.js.map +1 -0
  77. package/dist/domain/chart-of-accounts.d.ts +57 -0
  78. package/dist/domain/chart-of-accounts.d.ts.map +1 -0
  79. package/dist/domain/chart-of-accounts.js +114 -0
  80. package/dist/domain/chart-of-accounts.js.map +1 -0
  81. package/dist/domain/client-account.d.ts +28 -0
  82. package/dist/domain/client-account.d.ts.map +1 -0
  83. package/dist/domain/client-account.js +3 -0
  84. package/dist/domain/client-account.js.map +1 -0
  85. package/dist/domain/cnab240.d.ts +182 -0
  86. package/dist/domain/cnab240.d.ts.map +1 -0
  87. package/dist/domain/cnab240.js +265 -0
  88. package/dist/domain/cnab240.js.map +1 -0
  89. package/dist/domain/commission.d.ts +142 -0
  90. package/dist/domain/commission.d.ts.map +1 -0
  91. package/dist/domain/commission.js +3 -0
  92. package/dist/domain/commission.js.map +1 -0
  93. package/dist/domain/counterparty.d.ts +13 -0
  94. package/dist/domain/counterparty.d.ts.map +1 -0
  95. package/dist/domain/counterparty.js +48 -0
  96. package/dist/domain/counterparty.js.map +1 -0
  97. package/dist/domain/gift-card.d.ts +35 -0
  98. package/dist/domain/gift-card.d.ts.map +1 -0
  99. package/dist/domain/gift-card.js +3 -0
  100. package/dist/domain/gift-card.js.map +1 -0
  101. package/dist/domain/goal.d.ts +26 -0
  102. package/dist/domain/goal.d.ts.map +1 -0
  103. package/dist/domain/goal.js +3 -0
  104. package/dist/domain/goal.js.map +1 -0
  105. package/dist/domain/invoice.d.ts +228 -0
  106. package/dist/domain/invoice.d.ts.map +1 -0
  107. package/dist/domain/invoice.js +4 -0
  108. package/dist/domain/invoice.js.map +1 -0
  109. package/dist/domain/money.d.ts +5 -0
  110. package/dist/domain/money.d.ts.map +1 -0
  111. package/dist/domain/money.js +3 -0
  112. package/dist/domain/money.js.map +1 -0
  113. package/dist/domain/movement.d.ts +114 -0
  114. package/dist/domain/movement.d.ts.map +1 -0
  115. package/dist/domain/movement.js +4 -0
  116. package/dist/domain/movement.js.map +1 -0
  117. package/dist/domain/nota-fiscal.d.ts +47 -0
  118. package/dist/domain/nota-fiscal.d.ts.map +1 -0
  119. package/dist/domain/nota-fiscal.js +3 -0
  120. package/dist/domain/nota-fiscal.js.map +1 -0
  121. package/dist/domain/payment-method.d.ts +54 -0
  122. package/dist/domain/payment-method.d.ts.map +1 -0
  123. package/dist/domain/payment-method.js +25 -0
  124. package/dist/domain/payment-method.js.map +1 -0
  125. package/dist/domain/query.d.ts +9 -0
  126. package/dist/domain/query.d.ts.map +1 -0
  127. package/dist/domain/query.js +3 -0
  128. package/dist/domain/query.js.map +1 -0
  129. package/dist/domain/reconciliation.d.ts +277 -0
  130. package/dist/domain/reconciliation.d.ts.map +1 -0
  131. package/dist/domain/reconciliation.js +4 -0
  132. package/dist/domain/reconciliation.js.map +1 -0
  133. package/dist/domain/recurring-bill.d.ts +42 -0
  134. package/dist/domain/recurring-bill.d.ts.map +1 -0
  135. package/dist/domain/recurring-bill.js +3 -0
  136. package/dist/domain/recurring-bill.js.map +1 -0
  137. package/dist/domain/report.d.ts +92 -0
  138. package/dist/domain/report.d.ts.map +1 -0
  139. package/dist/domain/report.js +6 -0
  140. package/dist/domain/report.js.map +1 -0
  141. package/dist/domain/statement-match.d.ts +36 -0
  142. package/dist/domain/statement-match.d.ts.map +1 -0
  143. package/dist/domain/statement-match.js +3 -0
  144. package/dist/domain/statement-match.js.map +1 -0
  145. package/dist/domain/statement.d.ts +75 -0
  146. package/dist/domain/statement.d.ts.map +1 -0
  147. package/dist/domain/statement.js +4 -0
  148. package/dist/domain/statement.js.map +1 -0
  149. package/dist/domain/transfer.d.ts +29 -0
  150. package/dist/domain/transfer.d.ts.map +1 -0
  151. package/dist/domain/transfer.js +3 -0
  152. package/dist/domain/transfer.js.map +1 -0
  153. package/dist/store.d.ts +66 -0
  154. package/dist/store.d.ts.map +1 -0
  155. package/dist/store.js +287 -0
  156. package/dist/store.js.map +1 -0
  157. package/dist/views/zones.d.ts +40 -0
  158. package/dist/views/zones.d.ts.map +1 -0
  159. package/dist/views/zones.js +16 -0
  160. package/dist/views/zones.js.map +1 -0
  161. package/package.json +49 -3
@@ -0,0 +1,893 @@
1
+ import { summarizeLedger, trailingWindow } from './chunk-T7WIM2V7.js';
2
+ import { matchStatementLines } from './chunk-ZFUQBYBG.js';
3
+ import { emptyStatement, isRealized, buildStatement, ledgerBalance } from './chunk-HJPUHTI2.js';
4
+ import { transferLegs } from './chunk-YUNLQYOZ.js';
5
+ import { cancellationReason, assertReschedule, lineTotal, invoiceTotal, itemsDescription, invoiceStatusOf } from './chunk-7HISDBUG.js';
6
+ import { openAmount, paymentFee } from './chunk-4GXBOEI3.js';
7
+ import { sessionCashFlow, closingDifference } from './chunk-56NQHYL7.js';
8
+ import { runReconcileAsBill, runReconcileLink } from './chunk-FNXIAIUZ.js';
9
+ import { toReconcileLine, transferForStatementLine, statementLegOf, candidateTolerance, candidateScore, statementLinesPage, pageTotals } from './chunk-MCSPCXQK.js';
10
+ import { round2 } from './chunk-XVELDCSL.js';
11
+
12
+ // src/data/mock.ts
13
+ var nextId = 1;
14
+ function uid() {
15
+ return String(nextId++);
16
+ }
17
+ function now() {
18
+ return (/* @__PURE__ */ new Date()).toISOString();
19
+ }
20
+ function today() {
21
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
22
+ }
23
+ function matchesDateRange(dateStr, range) {
24
+ if (!range || !dateStr) return true;
25
+ return dateStr >= range.from && dateStr <= range.to;
26
+ }
27
+ function matchesStatus(status, filter) {
28
+ if (!filter) return true;
29
+ if (Array.isArray(filter)) return filter.includes(status);
30
+ return status === filter;
31
+ }
32
+ function paginate(items, page, pageSize) {
33
+ const p = page ?? 1;
34
+ const ps = pageSize ?? 50;
35
+ const start = (p - 1) * ps;
36
+ return { data: items.slice(start, start + ps), total: items.length };
37
+ }
38
+ function createStore(options) {
39
+ const tenantId = "mock-tenant";
40
+ const seed = options?.seed;
41
+ const defaultPMTypes = (options?.paymentMethodTypes ?? [
42
+ { name: "Cash", transactionType: "cash" },
43
+ { name: "Credit Card", transactionType: "credit_card" },
44
+ { name: "Debit Card", transactionType: "debit_card" },
45
+ { name: "PIX", transactionType: "pix" },
46
+ { name: "Bank Transfer", transactionType: "transfer" }
47
+ ]).map((t) => ({
48
+ id: uid(),
49
+ name: t.name,
50
+ transactionType: t.transactionType,
51
+ isActive: true,
52
+ tenantId,
53
+ createdAt: now()
54
+ }));
55
+ const cashAccount = {
56
+ id: uid(),
57
+ name: "Main Cash Register",
58
+ accountType: "cash_physical",
59
+ currentBalance: 0,
60
+ initialBalance: 0,
61
+ isActive: true,
62
+ tenantId,
63
+ createdAt: now(),
64
+ updatedAt: now()
65
+ };
66
+ const bankAccount = {
67
+ id: uid(),
68
+ name: "Business Checking",
69
+ accountType: "bank",
70
+ bankName: "Main Bank",
71
+ currentBalance: 0,
72
+ initialBalance: 0,
73
+ isActive: true,
74
+ tenantId,
75
+ createdAt: now(),
76
+ updatedAt: now()
77
+ };
78
+ return {
79
+ // Seeded data (when provided) makes the module render as if the user already
80
+ // has accounts + transactions — a populated Summary/Statements instead of the
81
+ // empty onboarding state. Seed arrays are copied so the caller's data is not mutated.
82
+ invoices: seed?.invoices ? [...seed.invoices] : [],
83
+ invoiceItems: seed?.invoiceItems ? [...seed.invoiceItems] : [],
84
+ movements: seed?.movements ? [...seed.movements] : [],
85
+ statementLines: [],
86
+ statementSources: [],
87
+ bankAccounts: seed?.bankAccounts ? [...seed.bankAccounts] : [cashAccount, bankAccount],
88
+ cashSessions: [],
89
+ paymentMethods: seed?.paymentMethods ? [...seed.paymentMethods] : [],
90
+ paymentMethodTypes: defaultPMTypes,
91
+ chartOfAccounts: [],
92
+ costCenters: [],
93
+ cardTransactions: [],
94
+ notas: [],
95
+ recurringBills: seed?.recurringBills ? [...seed.recurringBills] : []
96
+ };
97
+ }
98
+ function createMockFinancialProvider(options) {
99
+ const store = createStore(options);
100
+ const tenantId = "mock-tenant";
101
+ function recalcInvoiceStatus(invoice) {
102
+ const bills = store.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
103
+ invoice.status = invoiceStatusOf(invoice, bills, today());
104
+ invoice.updatedAt = now();
105
+ }
106
+ function markReconciled(statementLineId, matchedMovementId) {
107
+ const line = store.statementLines.find((l) => l.id === statementLineId);
108
+ const internal = matchedMovementId ? store.movements.find((m) => m.id === matchedMovementId) : void 0;
109
+ if (line) {
110
+ line.reconciledAt = now();
111
+ line.matchedMovementId = matchedMovementId;
112
+ line.matchedInvoiceId = internal?.invoiceId;
113
+ line.matchedCounterpartyName = internal?.invoiceId ? store.invoices.find((i) => i.id === internal.invoiceId)?.contactName : void 0;
114
+ line.updatedAt = now();
115
+ }
116
+ if (internal) {
117
+ internal.reconciledAt = now();
118
+ internal.updatedAt = now();
119
+ }
120
+ }
121
+ function reconcileFacade() {
122
+ return {
123
+ createInvoice: (input) => provider.createInvoice(input),
124
+ getMovements: (query) => provider.getMovements(query),
125
+ payMovement: (input) => provider.payMovement(input),
126
+ reconcileMovement: async ({ bankMovementId, matchedMovementId }) => {
127
+ markReconciled(bankMovementId, matchedMovementId);
128
+ }
129
+ };
130
+ }
131
+ const provider = {
132
+ // --- Invoices ---
133
+ async getInvoices(query) {
134
+ let results = [...store.invoices];
135
+ if (query.direction) results = results.filter((i) => i.direction === query.direction);
136
+ if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
137
+ if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
138
+ if (query.unitId) results = results.filter((i) => i.unitId === query.unitId);
139
+ if (query.accountId) {
140
+ const invoiceIds = new Set(store.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
141
+ results = results.filter((invoice) => invoiceIds.has(invoice.id));
142
+ }
143
+ if (query.costCenterId) {
144
+ const invoiceIds = new Set(store.invoiceItems.filter((item) => item.costCenterId === query.costCenterId).map((item) => item.invoiceId));
145
+ results = results.filter((invoice) => invoiceIds.has(invoice.id));
146
+ }
147
+ if (query.fiscalNumber) {
148
+ const nf = query.fiscalNumber.toLowerCase();
149
+ results = results.filter((i) => i.fiscalNumber?.toLowerCase().includes(nf));
150
+ }
151
+ if (query.bankAccountId) {
152
+ const pagas = new Set(
153
+ store.movements.filter((m) => m.bankAccountId === query.bankAccountId).map((m) => m.invoiceId).filter(Boolean)
154
+ );
155
+ results = results.filter((i) => pagas.has(i.id));
156
+ }
157
+ if (query.dateRange) results = results.filter((i) => matchesDateRange(i.invoiceDate, query.dateRange));
158
+ if (query.search) {
159
+ const s = query.search.toLowerCase();
160
+ results = results.filter(
161
+ (i) => i.contactName?.toLowerCase().includes(s) || i.observations?.toLowerCase().includes(s) || i.fiscalNumber?.toLowerCase().includes(s)
162
+ );
163
+ }
164
+ results.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
165
+ return paginate(results, query.page, query.pageSize);
166
+ },
167
+ async getInvoiceById(id) {
168
+ return store.invoices.find((i) => i.id === id) ?? null;
169
+ },
170
+ async getInvoiceItems(invoiceId) {
171
+ return store.invoiceItems.filter((i) => i.invoiceId === invoiceId);
172
+ },
173
+ async createInvoice(input) {
174
+ const invoiceId = uid();
175
+ const totalAmount = invoiceTotal(input.items);
176
+ const itemsSummary = itemsDescription(input.items);
177
+ const invoice = {
178
+ id: invoiceId,
179
+ direction: input.direction,
180
+ invoiceDate: input.invoiceDate,
181
+ fiscalNumber: input.fiscalNumber,
182
+ totalAmount,
183
+ paidAmount: 0,
184
+ status: "open",
185
+ totalInstallments: input.installments.length || 1,
186
+ contactId: input.contactId,
187
+ contactName: input.contactName,
188
+ cashSessionId: input.cashSessionId,
189
+ unitId: input.unitId,
190
+ observations: input.observations,
191
+ itemsSummary: itemsSummary || void 0,
192
+ metadata: input.metadata,
193
+ tenantId,
194
+ createdAt: now(),
195
+ updatedAt: now()
196
+ };
197
+ store.invoices.push(invoice);
198
+ for (const itemInput of input.items) {
199
+ const itemTotal = lineTotal(itemInput);
200
+ const item = {
201
+ id: uid(),
202
+ invoiceId,
203
+ itemKind: itemInput.itemKind,
204
+ referenceId: itemInput.referenceId,
205
+ description: itemInput.description,
206
+ quantity: itemInput.quantity,
207
+ unitPrice: itemInput.unitPrice,
208
+ totalAmount: itemTotal,
209
+ discount: itemInput.discount ?? 0,
210
+ surcharge: itemInput.surcharge ?? 0,
211
+ accountId: itemInput.accountId,
212
+ costCenterId: itemInput.costCenterId,
213
+ metadata: itemInput.metadata,
214
+ createdAt: now()
215
+ };
216
+ store.invoiceItems.push(item);
217
+ }
218
+ for (const movInput of input.installments) {
219
+ const movement = {
220
+ id: uid(),
221
+ invoiceId,
222
+ direction: input.direction,
223
+ movementKind: movInput.movementKind,
224
+ amount: movInput.amount,
225
+ paidAmount: 0,
226
+ status: "pending",
227
+ dueDate: movInput.dueDate,
228
+ installmentNumber: movInput.installmentNumber,
229
+ paymentMethodId: movInput.paymentMethodId,
230
+ bankAccountId: movInput.bankAccountId,
231
+ notes: movInput.notes,
232
+ metadata: movInput.metadata,
233
+ tenantId,
234
+ createdAt: now(),
235
+ updatedAt: now()
236
+ };
237
+ store.movements.push(movement);
238
+ }
239
+ return invoice;
240
+ },
241
+ async updateInvoice(id, data) {
242
+ const invoice = store.invoices.find((i) => i.id === id);
243
+ if (!invoice) throw new Error(`Invoice ${id} not found`);
244
+ Object.assign(invoice, data, { updatedAt: now() });
245
+ return invoice;
246
+ },
247
+ async updateInstallment(id, data) {
248
+ const mov = store.movements.find((mv) => mv.id === id);
249
+ if (!mov) return;
250
+ if (data.dueDate !== void 0) mov.dueDate = data.dueDate;
251
+ if (data.amount !== void 0) mov.amount = data.amount;
252
+ if (data.metadata !== void 0) mov.metadata = data.metadata;
253
+ },
254
+ async replaceInvoiceItems(invoiceId, items) {
255
+ const invoice = store.invoices.find((i) => i.id === invoiceId);
256
+ if (!invoice) throw new Error(`Invoice ${invoiceId} not found`);
257
+ store.invoiceItems = store.invoiceItems.filter((it) => it.invoiceId !== invoiceId);
258
+ for (const item of items) {
259
+ store.invoiceItems.push({
260
+ id: uid(),
261
+ invoiceId,
262
+ itemKind: item.itemKind,
263
+ referenceId: item.referenceId,
264
+ description: item.description,
265
+ quantity: item.quantity,
266
+ unitPrice: item.unitPrice,
267
+ totalAmount: lineTotal(item),
268
+ discount: item.discount ?? 0,
269
+ surcharge: item.surcharge ?? 0,
270
+ accountId: item.accountId,
271
+ costCenterId: item.costCenterId,
272
+ metadata: item.metadata,
273
+ createdAt: now()
274
+ });
275
+ }
276
+ invoice.totalAmount = invoiceTotal(items);
277
+ invoice.updatedAt = now();
278
+ },
279
+ async rescheduleInvoice(invoiceId, installments) {
280
+ const invoice = store.invoices.find((i) => i.id === invoiceId);
281
+ if (!invoice) throw new Error(`Invoice ${invoiceId} not found`);
282
+ const old = store.movements.filter((mv) => mv.invoiceId === invoiceId && mv.movementKind === "bill");
283
+ assertReschedule(invoice, old, installments);
284
+ const carried = new Map(old.map((mv) => [mv.installmentNumber ?? 1, mv.metadata]));
285
+ store.movements = store.movements.filter((mv) => !old.includes(mv));
286
+ installments.forEach((inst, i) => {
287
+ store.movements.push({
288
+ id: uid(),
289
+ invoiceId,
290
+ direction: invoice.direction,
291
+ movementKind: "bill",
292
+ amount: inst.amount,
293
+ paidAmount: 0,
294
+ status: "pending",
295
+ dueDate: inst.dueDate,
296
+ installmentNumber: i + 1,
297
+ metadata: inst.metadata ?? carried.get(i + 1),
298
+ tenantId,
299
+ createdAt: now(),
300
+ updatedAt: now()
301
+ });
302
+ });
303
+ },
304
+ async cancelInvoice(input) {
305
+ cancellationReason(input);
306
+ const invoice = store.invoices.find((i) => i.id === input.invoiceId);
307
+ if (!invoice) throw new Error(`Invoice ${input.invoiceId} not found`);
308
+ invoice.status = "cancelled";
309
+ invoice.updatedAt = now();
310
+ store.movements.filter((m) => m.invoiceId === input.invoiceId && m.status === "pending").forEach((m) => {
311
+ m.status = "cancelled";
312
+ m.updatedAt = now();
313
+ });
314
+ },
315
+ // --- Movements ---
316
+ async getMovements(query) {
317
+ let results = [...store.movements];
318
+ if (query.invoiceId) results = results.filter((m) => m.invoiceId === query.invoiceId);
319
+ if (query.direction) results = results.filter((m) => m.direction === query.direction);
320
+ if (query.status) results = results.filter((m) => matchesStatus(m.status, query.status));
321
+ if (query.bankAccountId) results = results.filter((m) => m.bankAccountId === query.bankAccountId);
322
+ if (query.cashSessionId) results = results.filter((m) => m.cashSessionId === query.cashSessionId);
323
+ if (query.dateRange) results = results.filter((m) => matchesDateRange(m.dueDate, query.dateRange));
324
+ results.sort((a, b) => a.dueDate.localeCompare(b.dueDate));
325
+ return paginate(results, query.page, query.pageSize);
326
+ },
327
+ // O mock não guarda faturas no formato do read model; devolve a conta vazia
328
+ // em vez de inventar números que a tela apresentaria como saldo do cliente.
329
+ async getClientAccount(personId) {
330
+ return { personId, contracted: 0, paid: 0, open: 0, invoices: [] };
331
+ },
332
+ async getCardReceivables() {
333
+ return [];
334
+ },
335
+ async reverseMovement(movementId, reason) {
336
+ const m = store.movements.find((x) => x.id === movementId);
337
+ if (m) {
338
+ m.status = "cancelled";
339
+ m.notes = [m.notes, `Estorno: ${reason}`].filter(Boolean).join(" \xB7 ");
340
+ }
341
+ },
342
+ async payMovement(input) {
343
+ const bill = store.movements.find((m) => m.id === input.movementId);
344
+ if (!bill) throw new Error(`Movement ${input.movementId} not found`);
345
+ const method = input.paymentMethodId ? store.paymentMethods.find((pm) => pm.id === input.paymentMethodId) : void 0;
346
+ const thisFee = paymentFee(input.amount, method?.discountValue ?? 0, bill.direction);
347
+ const payment = {
348
+ id: uid(),
349
+ invoiceId: bill.invoiceId,
350
+ direction: bill.direction,
351
+ movementKind: "payment",
352
+ amount: input.amount,
353
+ paidAmount: input.amount,
354
+ feeAmount: thisFee,
355
+ status: "paid",
356
+ settlesMovementId: bill.id,
357
+ dueDate: bill.dueDate,
358
+ paymentDate: input.paymentDate,
359
+ installmentNumber: bill.installmentNumber,
360
+ paymentMethodId: input.paymentMethodId,
361
+ paymentMethodTypeId: input.paymentMethodTypeId,
362
+ bankAccountId: input.bankAccountId,
363
+ cashSessionId: input.cashSessionId,
364
+ cardBrand: input.cardBrand,
365
+ cardInstallments: input.cardInstallments,
366
+ notes: input.notes,
367
+ metadata: input.metadata,
368
+ tenantId,
369
+ createdAt: now(),
370
+ updatedAt: now()
371
+ };
372
+ store.movements.push(payment);
373
+ bill.paidAmount = round2(bill.paidAmount + input.amount);
374
+ bill.status = bill.paidAmount >= bill.amount ? "paid" : "partial";
375
+ bill.paymentDate = void 0;
376
+ bill.bankAccountId = void 0;
377
+ bill.updatedAt = now();
378
+ if (input.bankAccountId) {
379
+ const account = store.bankAccounts.find((a) => a.id === input.bankAccountId);
380
+ if (account) {
381
+ if (bill.direction === "credit") {
382
+ account.currentBalance = round2(account.currentBalance + (input.amount - thisFee));
383
+ } else {
384
+ account.currentBalance = round2(account.currentBalance - input.amount);
385
+ }
386
+ account.updatedAt = now();
387
+ }
388
+ }
389
+ if (bill.invoiceId) {
390
+ const invoice = store.invoices.find((i) => i.id === bill.invoiceId);
391
+ if (invoice) {
392
+ const bills = store.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
393
+ invoice.paidAmount = bills.reduce((sum, m) => sum + m.paidAmount, 0);
394
+ recalcInvoiceStatus(invoice);
395
+ }
396
+ }
397
+ return payment;
398
+ },
399
+ async cancelMovement(id) {
400
+ const movement = store.movements.find((m) => m.id === id);
401
+ if (!movement) throw new Error(`Movement ${id} not found`);
402
+ movement.status = "cancelled";
403
+ movement.updatedAt = now();
404
+ },
405
+ // --- Transfers (between accounts) ---
406
+ async createTransfer(input) {
407
+ const transferId = uid();
408
+ const [out, into] = transferLegs(input, transferId);
409
+ const debitMovementId = uid();
410
+ const creditMovementId = uid();
411
+ const base = {
412
+ movementKind: "transfer",
413
+ amount: input.amount,
414
+ paidAmount: input.amount,
415
+ feeAmount: 0,
416
+ status: "paid",
417
+ dueDate: input.date,
418
+ paymentDate: input.date,
419
+ notes: input.notes,
420
+ tenantId,
421
+ createdAt: now(),
422
+ updatedAt: now()
423
+ };
424
+ store.movements.push({ ...base, id: debitMovementId, ...out });
425
+ store.movements.push({ ...base, id: creditMovementId, ...into });
426
+ const from = store.bankAccounts.find((a) => a.id === input.fromAccountId);
427
+ const to = store.bankAccounts.find((a) => a.id === input.toAccountId);
428
+ if (from) {
429
+ from.currentBalance = round2(from.currentBalance - input.amount);
430
+ from.updatedAt = now();
431
+ }
432
+ if (to) {
433
+ to.currentBalance = round2(to.currentBalance + input.amount);
434
+ to.updatedAt = now();
435
+ }
436
+ return { transferId, debitMovementId, creditMovementId };
437
+ },
438
+ // --- Bank Accounts ---
439
+ // Espelha a view da 113: só conta com ORIGEM ativa. Sem isso o mock diria
440
+ // que o filtro funciona quando ele não filtra nada.
441
+ async getStatementAccounts() {
442
+ const comOrigem = new Set(store.statementSources.filter((o) => o.isActive).map((o) => o.accountId));
443
+ return store.bankAccounts.filter((a) => comOrigem.has(a.id));
444
+ },
445
+ async getBankAccounts() {
446
+ return store.bankAccounts.filter((a) => a.isActive);
447
+ },
448
+ async getAccountsByIds(ids) {
449
+ return store.bankAccounts.filter((a) => ids.includes(a.id));
450
+ },
451
+ async searchFinancialAccounts(query, limit = 20) {
452
+ const term = query.trim().toLocaleLowerCase("pt-BR");
453
+ if (term.length < 2) return [];
454
+ return store.bankAccounts.filter((account) => account.isActive && account.name.toLocaleLowerCase("pt-BR").includes(term)).sort((a, b) => a.name.localeCompare(b.name, "pt-BR")).slice(0, limit);
455
+ },
456
+ async createBankAccount(data) {
457
+ const account = {
458
+ id: uid(),
459
+ name: data.name,
460
+ accountType: data.accountType,
461
+ bankName: data.bankName,
462
+ accountNumber: data.accountNumber,
463
+ agencyNumber: data.agencyNumber,
464
+ currentBalance: data.initialBalance ?? 0,
465
+ initialBalance: data.initialBalance ?? 0,
466
+ creditLimit: data.creditLimit,
467
+ dueDay: data.dueDay,
468
+ closingDay: data.closingDay,
469
+ isActive: true,
470
+ unitId: data.unitId,
471
+ tenantId,
472
+ createdAt: now(),
473
+ updatedAt: now()
474
+ };
475
+ store.bankAccounts.push(account);
476
+ return account;
477
+ },
478
+ async updateBankAccount(id, data) {
479
+ const account = store.bankAccounts.find((a) => a.id === id);
480
+ if (!account) throw new Error(`Bank account ${id} not found`);
481
+ Object.assign(account, data, { updatedAt: now() });
482
+ return account;
483
+ },
484
+ // --- Cash Sessions ---
485
+ async getCashSessions(bankAccountId) {
486
+ let results = [...store.cashSessions];
487
+ if (bankAccountId) results = results.filter((s) => s.bankAccountId === bankAccountId);
488
+ return results.sort((a, b) => b.openedAt.localeCompare(a.openedAt));
489
+ },
490
+ async getOpenSession(bankAccountId) {
491
+ return store.cashSessions.find((s) => s.bankAccountId === bankAccountId && s.status === "open") ?? null;
492
+ },
493
+ async openCashSession(input) {
494
+ const account = store.bankAccounts.find((a) => a.id === input.bankAccountId);
495
+ const session = {
496
+ id: uid(),
497
+ bankAccountId: input.bankAccountId,
498
+ bankAccountName: account?.name,
499
+ status: "open",
500
+ openedAt: now(),
501
+ openedByUserId: input.openedByUserId,
502
+ openedByName: input.openedByName,
503
+ openingBalance: input.openingBalance,
504
+ unitId: input.unitId,
505
+ tenantId,
506
+ createdAt: now()
507
+ };
508
+ store.cashSessions.push(session);
509
+ return session;
510
+ },
511
+ async closeCashSession(input) {
512
+ const session = store.cashSessions.find((s) => s.id === input.sessionId);
513
+ if (!session) throw new Error(`Cash session ${input.sessionId} not found`);
514
+ const flow = sessionCashFlow(store.movements.filter((m) => m.cashSessionId === session.id && m.status === "paid"));
515
+ const { expectedBalance, difference } = closingDifference(session.openingBalance, flow, input.closingBalance);
516
+ session.status = "closed";
517
+ session.closedAt = now();
518
+ session.closedByUserId = input.closedByUserId;
519
+ session.closedByName = input.closedByName;
520
+ session.closingBalance = input.closingBalance;
521
+ session.expectedBalance = expectedBalance;
522
+ session.difference = difference;
523
+ session.notes = input.notes;
524
+ return session;
525
+ },
526
+ async getCashSessionSummary(sessionId) {
527
+ const session = store.cashSessions.find((s) => s.id === sessionId);
528
+ if (!session) throw new Error(`Cash session ${sessionId} not found`);
529
+ return { session, ...sessionCashFlow(store.movements.filter((m) => m.cashSessionId === sessionId && m.status === "paid")) };
530
+ },
531
+ // --- Payment Methods ---
532
+ async getPaymentMethods() {
533
+ return store.paymentMethods.filter((pm) => pm.isActive);
534
+ },
535
+ async getPaymentMethodTypes() {
536
+ return store.paymentMethodTypes.filter((t) => t.isActive);
537
+ },
538
+ // --- Chart of Accounts ---
539
+ async getChartOfAccounts() {
540
+ return store.chartOfAccounts.filter((n) => n.isActive);
541
+ },
542
+ async resolveChartAccountRefs(refs) {
543
+ const ids = new Set(store.chartOfAccounts.map((account) => account.id));
544
+ return Object.fromEntries(refs.filter((ref) => ids.has(ref)).map((ref) => [ref, ref]));
545
+ },
546
+ async getCostCenters() {
547
+ return store.costCenters.filter((c) => c.isActive);
548
+ },
549
+ // --- Card Transactions ---
550
+ async getCardTransactions(dateRange) {
551
+ let results = [...store.cardTransactions];
552
+ if (dateRange) results = results.filter((t) => matchesDateRange(t.expectedDate, dateRange));
553
+ return results.sort((a, b) => a.expectedDate.localeCompare(b.expectedDate));
554
+ },
555
+ // --- Statements (ERP extract: opening balance + realized cash + per-row net/fee) ---
556
+ async getStatement(query) {
557
+ const consolidated = !query.bankAccountId;
558
+ const scopeAccounts = consolidated ? store.bankAccounts.filter((a) => a.isActive) : store.bankAccounts.filter((a) => a.id === query.bankAccountId);
559
+ if (!consolidated && scopeAccounts.length === 0) return emptyStatement(query.bankAccountId);
560
+ const accountIds = new Set(scopeAccounts.map((a) => a.id));
561
+ const accountNameById = new Map(store.bankAccounts.map((a) => [a.id, a.name]));
562
+ const initialSum = scopeAccounts.reduce((s, a) => s + a.initialBalance, 0);
563
+ const invoiceById = (id) => id ? store.invoices.find((i) => i.id === id) : void 0;
564
+ const inScope = store.movements.filter((m) => isRealized(m) && (consolidated || m.bankAccountId && accountIds.has(m.bankAccountId)));
565
+ const statement = buildStatement({
566
+ openingBalance: ledgerBalance(initialSum, inScope.filter((m) => (m.paymentDate ?? "") < query.dateRange.from)),
567
+ movements: inScope.filter((m) => matchesDateRange(m.paymentDate, query.dateRange)).sort((a, b) => (a.paymentDate ?? "").localeCompare(b.paymentDate ?? "") || a.createdAt.localeCompare(b.createdAt)),
568
+ consolidated,
569
+ accountNameOf: (id) => accountNameById.get(id),
570
+ invoiceOf: (movement) => invoiceById(movement.invoiceId)
571
+ });
572
+ for (const e of statement.entries) {
573
+ const mid = e.movement.matchedMovementId;
574
+ const mm = mid ? store.movements.find((m) => m.id === mid) : void 0;
575
+ if (!mm) continue;
576
+ const inv = invoiceById(mm.invoiceId);
577
+ e.reconciledWith = {
578
+ movementId: mm.id,
579
+ description: mm.notes ?? void 0,
580
+ invoiceId: mm.invoiceId,
581
+ fiscalNumber: inv?.fiscalNumber,
582
+ contactName: inv?.contactName
583
+ };
584
+ }
585
+ return {
586
+ ...statement,
587
+ accountId: query.bankAccountId,
588
+ accountCurrentBalance: ledgerBalance(initialSum, inScope.filter((m) => (m.paymentDate ?? "") <= today()))
589
+ };
590
+ },
591
+ async getDre(query) {
592
+ const rows = [];
593
+ for (const account of store.chartOfAccounts.filter((candidate) => candidate.accountType === "revenue" || candidate.accountType === "expense")) {
594
+ rows.push({
595
+ accountId: account.id,
596
+ accountCode: account.code,
597
+ accountName: account.name,
598
+ accountType: account.accountType,
599
+ parentAccountId: account.parentId,
600
+ statementCode: account.code,
601
+ statementName: account.name,
602
+ unclassified: false,
603
+ amount: 0,
604
+ discount: 0,
605
+ lineCount: 0
606
+ });
607
+ }
608
+ for (const invoice of store.invoices.filter((candidate) => candidate.invoiceDate.startsWith(String(query.year)) && (!query.unitId || candidate.unitId === query.unitId))) {
609
+ for (const item of store.invoiceItems.filter((candidate) => candidate.invoiceId === invoice.id)) {
610
+ const account = store.chartOfAccounts.find((candidate) => candidate.id === item.accountId);
611
+ const accountType = invoice.direction === "credit" ? "revenue" : "expense";
612
+ rows.push({
613
+ unitId: invoice.unitId,
614
+ competenceMonth: `${invoice.invoiceDate.slice(0, 7)}-01`,
615
+ accountId: account?.id,
616
+ accountCode: account?.code,
617
+ accountName: account?.name,
618
+ accountType,
619
+ parentAccountId: account?.parentId,
620
+ statementCode: account?.accountType === accountType ? account.code : void 0,
621
+ statementName: account?.accountType === accountType ? account.name : void 0,
622
+ unclassified: !account || account.accountType !== accountType,
623
+ amount: item.totalAmount,
624
+ discount: item.discount,
625
+ lineCount: 1
626
+ });
627
+ }
628
+ }
629
+ return rows;
630
+ },
631
+ // --- Summary ---
632
+ async getSummary(dateRange) {
633
+ const todayStr = today();
634
+ return summarizeLedger({
635
+ accounts: store.bankAccounts,
636
+ movements: store.movements,
637
+ today: todayStr,
638
+ window: dateRange ?? trailingWindow(todayStr)
639
+ });
640
+ },
641
+ // --- Recurring payables and receivables ---
642
+ async getRecurringBills(direction) {
643
+ return store.recurringBills.filter((bill) => bill.direction === direction).sort((a, b) => Number(b.isActive) - Number(a.isActive) || a.description.localeCompare(b.description));
644
+ },
645
+ async saveRecurringBill(input) {
646
+ const timestamp = now();
647
+ const existing = input.id ? store.recurringBills.find((bill2) => bill2.id === input.id) : void 0;
648
+ const bill = {
649
+ id: existing?.id ?? uid(),
650
+ direction: input.direction,
651
+ description: input.description.trim(),
652
+ counterpartyPersonId: input.counterpartyPersonId,
653
+ counterpartyName: existing?.counterpartyName,
654
+ amount: round2(input.amount),
655
+ items: input.items.map((item) => ({ ...item })),
656
+ unitId: input.unitId || null,
657
+ chartAccountId: input.chartAccountId || null,
658
+ costCenterId: input.costCenterId || null,
659
+ frequency: input.frequency,
660
+ dueDay: input.dueDay,
661
+ startsOn: input.startsOn,
662
+ endsOn: input.endsOn || null,
663
+ isActive: input.isActive ?? existing?.isActive ?? true,
664
+ notes: input.notes?.trim() || null,
665
+ createdAt: existing?.createdAt ?? timestamp,
666
+ updatedAt: timestamp
667
+ };
668
+ if (existing) Object.assign(existing, bill);
669
+ else store.recurringBills.push(bill);
670
+ return bill;
671
+ },
672
+ async updateRecurringBill(id, patch) {
673
+ const bill = store.recurringBills.find((candidate) => candidate.id === id);
674
+ if (!bill) throw new Error("Recurring bill not found");
675
+ if (patch.isActive !== void 0) bill.isActive = patch.isActive;
676
+ if (patch.endsOn !== void 0) bill.endsOn = patch.endsOn;
677
+ bill.updatedAt = now();
678
+ },
679
+ async runRecurring() {
680
+ return { created: 0, total: 0 };
681
+ },
682
+ // --- Conciliação Bancária ---
683
+ // Espelha o provider real: a linha do extrato mora em `store.statementLines`,
684
+ // não em `store.movements`. O mock tinha a linha como movimento e por isso
685
+ // passava verde sobre o defeito que o banco recusava (migration 112).
686
+ async importBankTransactions(input) {
687
+ let imported = 0, duplicates = 0;
688
+ for (const line of input.lines) {
689
+ const key = `${line.externalSource}:${line.externalId}`;
690
+ if (store.statementLines.some((l) => `${l.externalSource}:${l.externalId}` === key)) {
691
+ duplicates++;
692
+ continue;
693
+ }
694
+ store.statementLines.push({
695
+ ...line,
696
+ id: uid(),
697
+ amount: Math.abs(line.amount),
698
+ bankAccountId: line.bankAccountId ?? input.bankAccountId,
699
+ providerDeleted: false,
700
+ createdAt: now(),
701
+ updatedAt: now()
702
+ });
703
+ imported++;
704
+ }
705
+ return { imported, duplicates };
706
+ },
707
+ async getStatementLines(query) {
708
+ let results = store.statementLines.filter((l) => !l.providerDeleted);
709
+ if (query?.bankAccountId) results = results.filter((l) => l.bankAccountId === query.bankAccountId);
710
+ if (query?.dateRange) results = results.filter((l) => matchesDateRange(l.date, query.dateRange));
711
+ if (query?.direction) results = results.filter((l) => l.direction === query.direction);
712
+ if (query?.status === "reconciled") results = results.filter((l) => !!l.reconciledAt);
713
+ else if (query?.status === "pending") results = results.filter((l) => !l.reconciledAt);
714
+ const term = (query?.search ?? "").trim().toLocaleLowerCase("pt-BR");
715
+ if (term) {
716
+ const casa = (v) => (v ?? "").toLocaleLowerCase("pt-BR").includes(term);
717
+ results = results.filter((l) => casa(l.description) || casa(l.counterpartyName) || casa(l.counterpartyDocument) || casa(l.document) || casa(l.code) || casa(l.category) || casa(l.transactionType) || casa(l.externalId));
718
+ }
719
+ results = [...results].sort((a, b) => b.date.localeCompare(a.date));
720
+ const { page, pageSize } = statementLinesPage(query);
721
+ const rows = results.slice((page - 1) * pageSize, page * pageSize);
722
+ return { rows, total: results.length, page, pageSize, ...pageTotals(rows) };
723
+ },
724
+ async getUnreconciled(query) {
725
+ let results = store.statementLines.filter((l) => !l.reconciledAt && !l.providerDeleted);
726
+ if (query?.bankAccountId) results = results.filter((l) => l.bankAccountId === query.bankAccountId);
727
+ if (query?.dateRange) results = results.filter((l) => matchesDateRange(l.date, query.dateRange));
728
+ return [...results].sort((a, b) => b.date.localeCompare(a.date));
729
+ },
730
+ async suggestReconciliation(statementLineId) {
731
+ const line = store.statementLines.find((l) => l.id === statementLineId);
732
+ if (!line) return [];
733
+ const tol = candidateTolerance(line.amount);
734
+ return store.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction && Math.abs(m.amount - line.amount) <= tol).map((m) => ({
735
+ movement: m,
736
+ invoice: m.invoiceId ? store.invoices.find((i) => i.id === m.invoiceId) : void 0,
737
+ score: candidateScore(line, m)
738
+ })).sort((a, b) => b.score - a.score).slice(0, 25);
739
+ },
740
+ async searchReconciliationTargets(statementLineId, query) {
741
+ const line = store.statementLines.find((l) => l.id === statementLineId);
742
+ if (!line) return { data: [], hasMore: false };
743
+ const status = query.status ?? "all";
744
+ const termo = query.search?.trim().toLocaleLowerCase("pt-BR");
745
+ const rows = store.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction).filter((m) => m.status !== "cancelled").filter((m) => {
746
+ const paga = m.movementKind === "payment" || m.status === "paid";
747
+ return status === "all" || (status === "paid" ? paga : !paga);
748
+ }).filter((m) => !query.dueFrom || (m.dueDate ?? "") >= query.dueFrom).filter((m) => !query.dueTo || (m.dueDate ?? "") <= query.dueTo).filter((m) => query.amountMin == null || m.amount >= query.amountMin).filter((m) => query.amountMax == null || m.amount <= query.amountMax).map((m) => ({ movement: m, invoice: m.invoiceId ? store.invoices.find((i) => i.id === m.invoiceId) : void 0 })).filter(({ movement, invoice }) => !termo || `${invoice?.contactName ?? ""} ${invoice?.fiscalNumber ?? ""} ${movement.notes ?? ""}`.toLocaleLowerCase("pt-BR").includes(termo)).sort((a, b) => (a.movement.dueDate ?? "").localeCompare(b.movement.dueDate ?? ""));
749
+ const pageSize = query.pageSize ?? 50;
750
+ const start = ((query.page ?? 1) - 1) * pageSize;
751
+ return {
752
+ data: rows.slice(start, start + pageSize).map((c) => ({ ...c, score: 0 })),
753
+ hasMore: rows.length > start + pageSize
754
+ };
755
+ },
756
+ // A mesma cabeça da conciliação de verdade (match-statement), sobre a
757
+ // memória: valor, nome e data, e um lançamento por linha.
758
+ async suggestReconciliationBatch(lines) {
759
+ const contexto = /* @__PURE__ */ new Map();
760
+ for (const movement of store.movements) {
761
+ const invoice = movement.invoiceId ? store.invoices.find((i) => i.id === movement.invoiceId) : void 0;
762
+ if (invoice) contexto.set(movement.id, { contactName: invoice.contactName, invoice });
763
+ }
764
+ const candidatos = store.movements.filter((m) => !m.externalSource && !m.reconciledAt);
765
+ return matchStatementLines(lines, candidatos, contexto);
766
+ },
767
+ async reconcileMovement(input) {
768
+ if (input.mode === "settle" && input.matchedMovementId) {
769
+ const line = store.statementLines.find((l) => l.id === input.bankMovementId);
770
+ if (!line) throw new Error("Linha do extrato n\xE3o encontrada");
771
+ const alvo = store.movements.find((m) => m.id === input.matchedMovementId);
772
+ if (!alvo) throw new Error("Lan\xE7amento n\xE3o encontrado");
773
+ await runReconcileLink(
774
+ reconcileFacade(),
775
+ toReconcileLine(line),
776
+ {
777
+ movementId: alvo.id,
778
+ mode: "settle",
779
+ openAmount: openAmount(alvo)
780
+ },
781
+ { payment: input.payment, adjustments: input.adjustments, partial: input.partial }
782
+ );
783
+ return;
784
+ }
785
+ markReconciled(input.bankMovementId, input.matchedMovementId);
786
+ },
787
+ async reconcileAsTransfer(input) {
788
+ const line = store.statementLines.find((l) => l.id === input.bankMovementId);
789
+ if (!line) throw new Error("Linha do extrato n\xE3o encontrada");
790
+ const transfer = await provider.createTransfer(transferForStatementLine(toReconcileLine(line), input));
791
+ await provider.reconcileMovement({ bankMovementId: input.bankMovementId, matchedMovementId: statementLegOf(line, transfer) });
792
+ return transfer;
793
+ },
794
+ // «Lançar como conta». A conta nasce classificada, é quitada contra a conta
795
+ // do extrato, e a linha passa a ser a prova dela. É o caminho que faz o
796
+ // extrato BATER: sem ele, uma linha sem contrapartida fica marcada como
797
+ // conciliada e o dinheiro continua fora do livro.
798
+ async reconcileAsBill(input) {
799
+ const line = store.statementLines.find((l) => l.id === input.bankMovementId);
800
+ if (!line) throw new Error("Linha do extrato n\xE3o encontrada");
801
+ const { invoiceId } = await runReconcileAsBill(reconcileFacade(), toReconcileLine(line), input);
802
+ const invoice = store.invoices.find((i) => i.id === invoiceId);
803
+ if (!invoice) throw new Error("A conta n\xE3o foi criada");
804
+ return invoice;
805
+ },
806
+ async unreconcileMovement(statementLineId) {
807
+ const line = store.statementLines.find((l) => l.id === statementLineId);
808
+ if (!line) return;
809
+ const matchedId = line.matchedMovementId;
810
+ line.reconciledAt = void 0;
811
+ line.matchedMovementId = void 0;
812
+ line.updatedAt = now();
813
+ if (matchedId) {
814
+ const internal = store.movements.find((m) => m.id === matchedId);
815
+ if (internal) {
816
+ internal.reconciledAt = void 0;
817
+ internal.updatedAt = now();
818
+ }
819
+ }
820
+ },
821
+ // --- Notas fiscais (NF-e / NFC-e / NFS-e) ---
822
+ // Os vales-presente moram na tabela da loja, que não existe em memória.
823
+ // Responder "não instalado" faz a tela se explicar; deixar o método fora
824
+ // faria ela explodir com um TypeError que não diz nada a ninguém.
825
+ async giftCardsEnabled() {
826
+ return false;
827
+ },
828
+ async getNotas(query) {
829
+ let list = [...store.notas].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
830
+ if (query.status) list = list.filter((n) => matchesStatus(n.status, query.status));
831
+ if (query.model) list = list.filter((n) => n.model === query.model);
832
+ if (query.orderId) list = list.filter((n) => n.orderId === query.orderId);
833
+ if (query.search) {
834
+ const q = query.search.toLowerCase();
835
+ list = list.filter((n) => (n.number ?? "").toLowerCase().includes(q) || (n.contactName ?? "").toLowerCase().includes(q));
836
+ }
837
+ return paginate(list, query.page, query.pageSize);
838
+ },
839
+ async getNotaById(id) {
840
+ return store.notas.find((n) => n.id === id) ?? null;
841
+ },
842
+ async createNota(input) {
843
+ const nota = {
844
+ id: uid(),
845
+ tenantId: "mock-tenant",
846
+ model: input.model ?? "nfe",
847
+ status: "draft",
848
+ number: null,
849
+ series: input.series ?? null,
850
+ orderId: input.orderId,
851
+ contactId: input.contactId,
852
+ contactName: input.contactName,
853
+ totalAmount: input.totalAmount,
854
+ observations: input.observations,
855
+ createdAt: now(),
856
+ updatedAt: now()
857
+ };
858
+ store.notas.unshift(nota);
859
+ return nota;
860
+ },
861
+ async updateNota(id, patch) {
862
+ const nota = store.notas.find((n) => n.id === id);
863
+ if (!nota) throw new Error("Nota fiscal not found");
864
+ Object.assign(nota, patch, { updatedAt: now() });
865
+ return nota;
866
+ },
867
+ async issueNota(id) {
868
+ const nota = store.notas.find((n) => n.id === id);
869
+ if (!nota) throw new Error("Nota fiscal not found");
870
+ const issuedCount = store.notas.filter((n) => n.status === "issued").length;
871
+ nota.status = "issued";
872
+ nota.number = String(issuedCount + 1).padStart(6, "0");
873
+ nota.series = nota.series ?? "1";
874
+ nota.issuedAt = now();
875
+ nota.emissor = "manual";
876
+ nota.updatedAt = now();
877
+ return nota;
878
+ },
879
+ async cancelNota(id, reason) {
880
+ const nota = store.notas.find((n) => n.id === id);
881
+ if (!nota) throw new Error("Nota fiscal not found");
882
+ nota.status = "cancelled";
883
+ nota.errorMessage = reason;
884
+ nota.updatedAt = now();
885
+ return nota;
886
+ }
887
+ };
888
+ return provider;
889
+ }
890
+
891
+ export { createMockFinancialProvider };
892
+ //# sourceMappingURL=chunk-UERJA7QH.js.map
893
+ //# sourceMappingURL=chunk-UERJA7QH.js.map