@classytic/ledger 0.10.2 → 0.11.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 (31) hide show
  1. package/dist/bridges/index.d.mts +1 -1
  2. package/dist/constants/index.d.mts +1 -1
  3. package/dist/constants/index.mjs +2 -2
  4. package/dist/{core-DwjkrRkJ.d.mts → core-B7uVjqGS.d.mts} +25 -0
  5. package/dist/country/index.d.mts +1 -1
  6. package/dist/events/index.d.mts +1 -1
  7. package/dist/exports/index.d.mts +1 -1
  8. package/dist/exports/index.mjs +1 -1
  9. package/dist/{fx-realization.plugin-Dzqzi3u0.mjs → fx-realization.plugin-DY3pPxIi.mjs} +70 -1
  10. package/dist/{index-ClLwzNRF.d.mts → index-BFPFihTF.d.mts} +8 -0
  11. package/dist/{index-08IpHhrU.d.mts → index-Dd7HknPP.d.mts} +1 -1
  12. package/dist/index.d.mts +120 -24
  13. package/dist/index.mjs +375 -165
  14. package/dist/{journals-DUpWwFt1.d.mts → journals-CTrAuzdk.d.mts} +1 -1
  15. package/dist/{partner-ledger-CR0geilx.mjs → partner-ledger-B0eym6Ss.mjs} +951 -213
  16. package/dist/plugins/index.d.mts +1 -1
  17. package/dist/plugins/index.mjs +1 -1
  18. package/dist/reports/index.d.mts +2 -2
  19. package/dist/reports/index.mjs +2 -2
  20. package/dist/{trial-balance-DyNm5bFu.d.mts → trial-balance-UXV2PN6x.d.mts} +280 -75
  21. package/package.json +8 -20
  22. package/dist/opening-balance-1cixYh6Y.mjs +0 -60
  23. package/dist/sync/index.d.mts +0 -324
  24. package/dist/sync/index.mjs +0 -530
  25. package/dist/sync-JvchM3FO.d.mts +0 -152
  26. /package/dist/{categories-FJlrvzcl.mjs → categories-CclX7Q94.mjs} +0 -0
  27. /package/dist/{currencies-Jo5oaM_4.mjs → currencies-OuPHPyS2.mjs} +0 -0
  28. /package/dist/{exports-C30yRapf.mjs → exports-B3whucXe.mjs} +0 -0
  29. /package/dist/{index-Bl0gP9lD.d.mts → index-DygMrab0.d.mts} +0 -0
  30. /package/dist/{index-J-XIbXH-.d.mts → index-pRW5cZhF.d.mts} +0 -0
  31. /package/dist/{outbox-store-BcCiHMPw.d.mts → outbox-store-CPLeocPg.d.mts} +0 -0
@@ -1,530 +0,0 @@
1
- import { t as buildOpeningBalanceEntry } from "../opening-balance-1cixYh6Y.mjs";
2
- //#region src/sync/ledger-bridge.ts
3
- const isCustomerSide = (t) => t === "out_invoice" || t === "out_refund" || t === "receipt";
4
- const isRefund = (t) => t === "out_refund" || t === "in_refund";
5
- const JOURNAL_TYPE_MAP = {
6
- out_invoice: "SALES",
7
- in_invoice: "PURCHASES",
8
- out_refund: "SALES",
9
- in_refund: "PURCHASES",
10
- receipt: "CASH_RECEIPTS"
11
- };
12
- /**
13
- * Create a LedgerBridge implementation backed by a @classytic/ledger engine.
14
- *
15
- * @param engine - The accounting engine (or any object matching the minimal
16
- * shape: `{ record: { adjustment, payment }, repositories: { journalEntries: { reverse } } }`)
17
- * @param config - Account mapping configuration
18
- * @returns A LedgerBridge that the invoice engine can use
19
- */
20
- function createLedgerBridge(engine, config) {
21
- const { accounts } = config;
22
- return {
23
- async createJournalEntry(input) {
24
- const customerSide = isCustomerSide(input.moveType);
25
- const refund = isRefund(input.moveType);
26
- const isReceipt = input.moveType === "receipt";
27
- const balanceSheetAccount = customerSide ? isReceipt && config.receiptAccount ? config.receiptAccount : accounts.receivable : accounts.payable;
28
- const incomeOrExpenseAccount = customerSide ? accounts.revenue : accounts.expense;
29
- const taxAccount = customerSide ? accounts.taxPayable : accounts.taxReceivable;
30
- const lines = [];
31
- if (customerSide && !refund) {
32
- lines.push({
33
- account: balanceSheetAccount,
34
- debit: input.totalAmount,
35
- label: `Invoice ${input.invoiceId}`
36
- });
37
- for (const line of input.lines) lines.push({
38
- account: incomeOrExpenseAccount,
39
- credit: line.amount,
40
- label: line.description
41
- });
42
- if (input.taxAmount > 0) lines.push({
43
- account: taxAccount,
44
- credit: input.taxAmount,
45
- label: "Tax"
46
- });
47
- } else if (customerSide && refund) {
48
- lines.push({
49
- account: balanceSheetAccount,
50
- credit: input.totalAmount,
51
- label: `Credit Note ${input.invoiceId}`
52
- });
53
- for (const line of input.lines) lines.push({
54
- account: incomeOrExpenseAccount,
55
- debit: line.amount,
56
- label: line.description
57
- });
58
- if (input.taxAmount > 0) lines.push({
59
- account: taxAccount,
60
- debit: input.taxAmount,
61
- label: "Tax reversal"
62
- });
63
- } else if (!customerSide && !refund) {
64
- for (const line of input.lines) lines.push({
65
- account: incomeOrExpenseAccount,
66
- debit: line.amount,
67
- label: line.description
68
- });
69
- if (input.taxAmount > 0) lines.push({
70
- account: taxAccount,
71
- debit: input.taxAmount,
72
- label: "Tax"
73
- });
74
- lines.push({
75
- account: balanceSheetAccount,
76
- credit: input.totalAmount,
77
- label: `Bill ${input.invoiceId}`
78
- });
79
- } else {
80
- lines.push({
81
- account: balanceSheetAccount,
82
- debit: input.totalAmount,
83
- label: `Vendor Credit ${input.invoiceId}`
84
- });
85
- for (const line of input.lines) lines.push({
86
- account: incomeOrExpenseAccount,
87
- credit: line.amount,
88
- label: line.description
89
- });
90
- if (input.taxAmount > 0) lines.push({
91
- account: taxAccount,
92
- credit: input.taxAmount,
93
- label: "Tax reversal"
94
- });
95
- }
96
- const label = input.notes ? `${input.moveType} ${input.invoiceId} — ${input.notes}` : `${input.moveType} ${input.invoiceId}`;
97
- const result = await engine.record.adjustment(input.organizationId, {
98
- date: input.date,
99
- lines,
100
- label,
101
- journalType: JOURNAL_TYPE_MAP[input.moveType] ?? "GENERAL"
102
- }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {});
103
- return String(result._id);
104
- },
105
- async reverseJournalEntry(journalEntryId, _reason, ctx) {
106
- const options = {};
107
- if (ctx.actorId) options.actorId = ctx.actorId;
108
- if (ctx.session) options.session = ctx.session;
109
- const result = await engine.repositories.journalEntries.reverse(journalEntryId, ctx.organizationId, options);
110
- return String(result.reversal._id);
111
- },
112
- async recordPayment(input) {
113
- let fromAccount;
114
- let toAccount;
115
- if (config.resolvePaymentAccounts) {
116
- const resolved = config.resolvePaymentAccounts(input);
117
- fromAccount = resolved.receivableOrPayable;
118
- toAccount = resolved.cash;
119
- } else {
120
- fromAccount = accounts.receivable;
121
- toAccount = accounts.cash;
122
- }
123
- const result = await engine.record.payment(input.organizationId, {
124
- date: input.date,
125
- amount: input.amount,
126
- fromReceivableAccount: fromAccount,
127
- toCashAccount: toAccount,
128
- label: `Payment ${input.paymentId} for ${input.invoiceId}`
129
- }, { idempotencyKey: `payment:${input.paymentId}` });
130
- return String(result._id);
131
- }
132
- };
133
- }
134
- //#endregion
135
- //#region src/sync/mappers/bank-statement.ts
136
- function bankStatementMapper(config) {
137
- return {
138
- externalId: (txn) => txn.externalId,
139
- toJournalEntry: (txn, _ctx) => {
140
- const cents = Number(txn.amount.amount);
141
- const absCents = Math.abs(cents);
142
- const isInflow = cents > 0;
143
- const counterAccount = config.categorize?.(txn) ?? config.suspenseAccountId;
144
- const label = [
145
- config.labelPrefix ?? "Import",
146
- txn.description,
147
- txn.counterparty?.name
148
- ].filter(Boolean).join(" — ");
149
- return {
150
- date: txn.postedDate,
151
- label,
152
- referenceNumber: txn.externalId,
153
- journalItems: isInflow ? [{
154
- account: config.bankAccountId,
155
- debit: absCents,
156
- credit: 0
157
- }, {
158
- account: counterAccount,
159
- debit: 0,
160
- credit: absCents
161
- }] : [{
162
- account: counterAccount,
163
- debit: absCents,
164
- credit: 0
165
- }, {
166
- account: config.bankAccountId,
167
- debit: 0,
168
- credit: absCents
169
- }],
170
- extra: {
171
- _importSource: txn.type ?? "bank-import",
172
- _importCounterparty: txn.counterparty?.name,
173
- _importReference: txn.reference
174
- }
175
- };
176
- }
177
- };
178
- }
179
- //#endregion
180
- //#region src/sync/mappers/invoice.ts
181
- function invoiceMapper(config) {
182
- return {
183
- externalId: (inv) => inv.externalId,
184
- toJournalEntry: (inv) => {
185
- const isSales = inv.type === "sales";
186
- const total = Number(inv.total.amount);
187
- const taxTotal = Number(inv.taxTotal.amount);
188
- const items = [];
189
- if (isSales) items.push({
190
- account: config.receivablesAccountId,
191
- debit: total,
192
- credit: 0
193
- });
194
- else items.push({
195
- account: config.payablesAccountId,
196
- debit: 0,
197
- credit: total
198
- });
199
- for (const line of inv.lines) {
200
- const lineAmount = Number(line.amount.amount);
201
- const account = (line.accountCode ? config.resolveAccountCode?.(line.accountCode) : void 0) ?? (isSales ? config.defaultRevenueAccountId : config.defaultExpenseAccountId);
202
- if (isSales) items.push({
203
- account,
204
- debit: 0,
205
- credit: lineAmount,
206
- label: line.description
207
- });
208
- else items.push({
209
- account,
210
- debit: lineAmount,
211
- credit: 0,
212
- label: line.description
213
- });
214
- }
215
- if (taxTotal > 0) {
216
- const taxAccount = isSales ? config.taxLiabilityAccountId : config.taxReceivableAccountId;
217
- if (taxAccount) if (isSales) items.push({
218
- account: taxAccount,
219
- debit: 0,
220
- credit: taxTotal
221
- });
222
- else items.push({
223
- account: taxAccount,
224
- debit: taxTotal,
225
- credit: 0
226
- });
227
- }
228
- return {
229
- date: inv.issueDate,
230
- label: `${isSales ? "Sales" : "Purchase"} Invoice — ${inv.contact.name ?? inv.externalId}`,
231
- referenceNumber: inv.externalId,
232
- journalItems: items,
233
- extra: {
234
- _importSource: "invoice-import",
235
- _importContactName: inv.contact.name,
236
- _importContactRef: inv.contact.reference
237
- }
238
- };
239
- }
240
- };
241
- }
242
- //#endregion
243
- //#region src/sync/mappers/journal-entry.ts
244
- function journalEntryMapper(config) {
245
- return {
246
- externalId: (je) => je.externalId,
247
- toJournalEntry: (je) => ({
248
- date: je.date,
249
- label: je.narration ?? `Imported JE ${je.externalId}`,
250
- referenceNumber: je.externalId,
251
- journalItems: je.lines.map((line) => ({
252
- account: config.resolveAccountCode(line.accountCode),
253
- debit: line.debit ? Number(line.debit.amount) : 0,
254
- credit: line.credit ? Number(line.credit.amount) : 0,
255
- label: line.description
256
- })),
257
- extra: { _importSource: "journal-entry-import" }
258
- })
259
- };
260
- }
261
- //#endregion
262
- //#region src/sync/mappers/opening-balance.ts
263
- function openingBalanceMapper(config) {
264
- return {
265
- externalId: (_tb) => {
266
- return `opening-balance:${config.cutoverDate.toISOString().split("T")[0]}`;
267
- },
268
- toJournalEntry: (tb, _ctx) => {
269
- const balances = [];
270
- for (const line of tb.lines) {
271
- const net = (line.debit ? Number(line.debit.amount) : 0) - (line.credit ? Number(line.credit.amount) : 0);
272
- if (net === 0) continue;
273
- const resolved = config.resolveAccountCode(line.accountCode);
274
- if (!resolved) continue;
275
- balances.push({
276
- accountCode: String(resolved),
277
- balance: net
278
- });
279
- }
280
- if (balances.length === 0) return null;
281
- return buildOpeningBalanceEntry({
282
- cutoverDate: config.cutoverDate,
283
- balances,
284
- equityAccountCode: String(config.equityAccountId)
285
- }).entry;
286
- }
287
- };
288
- }
289
- //#endregion
290
- //#region src/sync/wire-export.ts
291
- function wireExport(args) {
292
- const batchSize = args.options?.batchSize ?? 100;
293
- const onProgress = args.options?.onProgress;
294
- return { async run() {
295
- const start = performance.now();
296
- let emitted = 0;
297
- const errors = [];
298
- const entries = await args.journalEntries.getAll(args.query);
299
- let batch = [];
300
- for (const entry of entries) {
301
- try {
302
- const out = args.sink.fromJournalEntry(entry);
303
- batch.push(out);
304
- } catch (err) {
305
- const id = entry?._id;
306
- errors.push({
307
- entryId: id ? String(id) : void 0,
308
- message: err.message
309
- });
310
- continue;
311
- }
312
- if (batch.length >= batchSize) {
313
- const count = batch.length;
314
- await args.sink.emit(batch);
315
- batch = [];
316
- emitted += count;
317
- onProgress?.({ emitted });
318
- }
319
- }
320
- if (batch.length > 0) {
321
- const count = batch.length;
322
- await args.sink.emit(batch);
323
- batch = [];
324
- emitted += count;
325
- onProgress?.({ emitted });
326
- }
327
- if (args.sink.flush) await args.sink.flush();
328
- return {
329
- ok: errors.length === 0,
330
- emitted,
331
- errors,
332
- durationMs: performance.now() - start
333
- };
334
- } };
335
- }
336
- //#endregion
337
- //#region src/sync/wire-import.ts
338
- function wireImport(args) {
339
- const batchSize = args.options?.batchSize ?? 100;
340
- const strict = args.options?.strict ?? false;
341
- const dryRun = args.options?.dryRun ?? false;
342
- const journalType = args.options?.journalType ?? "GENERAL";
343
- const onProgress = args.options?.onProgress;
344
- const useBulk = !dryRun && typeof args.journalEntries.createMany === "function";
345
- return { async run() {
346
- const start = performance.now();
347
- const ctx = {
348
- organizationId: args.context.organizationId,
349
- importedAt: /* @__PURE__ */ new Date(),
350
- importRunId: args.context.importRunId
351
- };
352
- let inserted = 0;
353
- let skipped = 0;
354
- let failed = 0;
355
- const errors = [];
356
- let processed = 0;
357
- const batch = [];
358
- for await (const raw of toAsyncIterable(args.source)) {
359
- batch.push(raw);
360
- if (batch.length >= batchSize) {
361
- const result = await processBatch(batch.splice(0));
362
- inserted += result.inserted;
363
- skipped += result.skipped;
364
- failed += result.failed;
365
- errors.push(...result.errors);
366
- processed += result.processed;
367
- if (strict && result.failed > 0) break;
368
- onProgress?.({ processed });
369
- }
370
- }
371
- if (batch.length > 0) {
372
- const result = await processBatch(batch);
373
- inserted += result.inserted;
374
- skipped += result.skipped;
375
- failed += result.failed;
376
- errors.push(...result.errors);
377
- processed += result.processed;
378
- onProgress?.({ processed });
379
- }
380
- return {
381
- ok: failed === 0,
382
- inserted,
383
- skipped,
384
- failed,
385
- errors,
386
- durationMs: performance.now() - start
387
- };
388
- async function processBatch(records) {
389
- let batchInserted = 0;
390
- let batchSkipped = 0;
391
- let batchFailed = 0;
392
- const batchErrors = [];
393
- const externalIds = [];
394
- for (const raw of records) try {
395
- externalIds.push(args.mapper.externalId(raw));
396
- } catch (err) {
397
- externalIds.push("");
398
- batchErrors.push({
399
- externalId: void 0,
400
- message: `externalId() threw: ${err.message}`,
401
- cause: err
402
- });
403
- batchFailed += 1;
404
- }
405
- const existingSet = /* @__PURE__ */ new Set();
406
- const validIds = externalIds.filter((id) => id.length > 0);
407
- if (validIds.length > 0 && args.findExisting) try {
408
- const found = await args.findExisting(validIds, ctx.organizationId);
409
- for (const id of found) existingSet.add(id);
410
- } catch {}
411
- const pendingDocs = [];
412
- for (let i = 0; i < records.length; i++) {
413
- const raw = records[i];
414
- const externalId = externalIds[i];
415
- if (!externalId) continue;
416
- if (existingSet.has(externalId)) {
417
- batchSkipped += 1;
418
- continue;
419
- }
420
- let inputs;
421
- try {
422
- inputs = args.mapper.toJournalEntry(raw, ctx);
423
- } catch (err) {
424
- batchErrors.push({
425
- externalId,
426
- message: `toJournalEntry() threw: ${err.message}`,
427
- cause: err
428
- });
429
- batchFailed += 1;
430
- continue;
431
- }
432
- if (inputs === null) {
433
- batchSkipped += 1;
434
- continue;
435
- }
436
- const inputArray = Array.isArray(inputs) ? inputs : [inputs];
437
- for (const input of inputArray) {
438
- if (dryRun) {
439
- batchInserted += 1;
440
- continue;
441
- }
442
- const doc = {
443
- journalType: input.journalType ?? journalType,
444
- journal: input.journal,
445
- label: input.label ?? "Import",
446
- date: input.date,
447
- journalItems: input.journalItems.map((item) => ({
448
- account: item.account,
449
- debit: item.debit,
450
- credit: item.credit,
451
- label: item.label,
452
- currency: item.currency,
453
- exchangeRate: item.exchangeRate,
454
- originalDebit: item.originalDebit,
455
- originalCredit: item.originalCredit,
456
- matchingNumber: item.matchingNumber,
457
- maturityDate: item.maturityDate
458
- })),
459
- _externalId: externalId,
460
- state: "posted",
461
- ...ctx.organizationId !== void 0 ? { organizationId: ctx.organizationId } : {},
462
- ...ctx.importRunId ? { _importRunId: ctx.importRunId } : {},
463
- ...input.extra
464
- };
465
- pendingDocs.push({
466
- externalId,
467
- doc
468
- });
469
- }
470
- }
471
- if (dryRun || pendingDocs.length === 0) return {
472
- inserted: batchInserted,
473
- skipped: batchSkipped,
474
- failed: batchFailed,
475
- errors: batchErrors,
476
- processed: records.length
477
- };
478
- if (useBulk) try {
479
- await args.journalEntries.createMany?.(pendingDocs.map((p) => p.doc));
480
- batchInserted += pendingDocs.length;
481
- } catch (_err) {
482
- for (const { externalId, doc } of pendingDocs) try {
483
- await args.journalEntries.create(doc);
484
- batchInserted += 1;
485
- } catch (innerErr) {
486
- const innerMsg = innerErr.message;
487
- if (innerMsg.includes("idempotency") || innerMsg.includes("duplicate")) batchSkipped += 1;
488
- else {
489
- batchErrors.push({
490
- externalId,
491
- message: innerMsg,
492
- cause: innerErr
493
- });
494
- batchFailed += 1;
495
- }
496
- }
497
- }
498
- else for (const { externalId, doc } of pendingDocs) try {
499
- await args.journalEntries.create(doc);
500
- batchInserted += 1;
501
- } catch (err) {
502
- const msg = err.message;
503
- if (msg.includes("idempotency") || msg.includes("duplicate")) batchSkipped += 1;
504
- else {
505
- batchErrors.push({
506
- externalId,
507
- message: msg,
508
- cause: err
509
- });
510
- batchFailed += 1;
511
- }
512
- }
513
- return {
514
- inserted: batchInserted,
515
- skipped: batchSkipped,
516
- failed: batchFailed,
517
- errors: batchErrors,
518
- processed: records.length
519
- };
520
- }
521
- } };
522
- }
523
- function toAsyncIterable(source) {
524
- if (Symbol.asyncIterator in Object(source)) return source;
525
- return { async *[Symbol.asyncIterator]() {
526
- for (const item of source) yield item;
527
- } };
528
- }
529
- //#endregion
530
- export { bankStatementMapper, buildOpeningBalanceEntry, createLedgerBridge, invoiceMapper, journalEntryMapper, openingBalanceMapper, wireExport, wireImport };
@@ -1,152 +0,0 @@
1
- import { a as Cents } from "./core-DwjkrRkJ.mjs";
2
-
3
- //#region src/types/sync.d.ts
4
- /**
5
- * Maps a raw record (from any source) into a JournalEntry creation payload.
6
- * The consumer passes this to wireImport. Reference implementations ship
7
- * for every fin-io canonical shape: ofxBankMapper, camtBankMapper, etc.
8
- */
9
- interface ImportMapper<TRaw> {
10
- /**
11
- * Transform one raw record into zero, one, or many JournalEntry inputs.
12
- * Return null to skip a record (e.g. opening-balance entries).
13
- */
14
- toJournalEntry(raw: TRaw, ctx: ImportContext): JournalEntryInput | JournalEntryInput[] | null;
15
- /**
16
- * Stable, source-assigned unique ID for the raw record. Used for
17
- * idempotent re-imports — wireImport checks for an existing entry
18
- * with this idempotencyKey before creating.
19
- */
20
- externalId(raw: TRaw): string;
21
- }
22
- interface ImportContext {
23
- organizationId: unknown;
24
- /** When the import job started, for audit. */
25
- importedAt: Date;
26
- /** Optional run-scoped tag (e.g. 'monthly-bank-import-2026-04'). */
27
- importRunId?: string;
28
- }
29
- /**
30
- * Minimal JournalEntry creation payload. This is what the mapper produces
31
- * and wireImport passes to `journalEntries.create()`. The shape matches
32
- * the AccountingEngine's expected create input.
33
- */
34
- interface JournalEntryInput {
35
- journalType?: string;
36
- journal?: unknown;
37
- referenceNumber?: string;
38
- label?: string;
39
- date: Date;
40
- journalItems: JournalItemInput[];
41
- /** Extra fields injected into the entry doc (dimension fields, tags, etc.). */
42
- extra?: Record<string, unknown>;
43
- }
44
- interface JournalItemInput {
45
- account: unknown;
46
- debit: Cents;
47
- credit: Cents;
48
- label?: string;
49
- currency?: string;
50
- exchangeRate?: number;
51
- originalDebit?: Cents;
52
- originalCredit?: Cents;
53
- matchingNumber?: string;
54
- maturityDate?: Date;
55
- }
56
- /**
57
- * Result of an import run. Always returned — never thrown. Errors on
58
- * individual records do NOT abort the run unless `strict: true`.
59
- */
60
- interface ImportReport {
61
- ok: boolean;
62
- inserted: number;
63
- skipped: number;
64
- failed: number;
65
- errors: ImportError[];
66
- durationMs: number;
67
- }
68
- interface ImportError {
69
- externalId?: string;
70
- message: string;
71
- cause?: unknown;
72
- }
73
- /**
74
- * Maps a JournalEntry into an external format record. Used by wireExport.
75
- */
76
- interface ExportSink<TOut> {
77
- fromJournalEntry(entry: unknown): TOut;
78
- emit(records: TOut[]): Promise<void>;
79
- flush?(): Promise<void>;
80
- }
81
- interface ExportReport {
82
- ok: boolean;
83
- emitted: number;
84
- errors: Array<{
85
- entryId?: string;
86
- message: string;
87
- }>;
88
- durationMs: number;
89
- }
90
- interface WireImportArgs<TRaw> {
91
- /** The raw records to import. */
92
- source: Iterable<TRaw> | AsyncIterable<TRaw>;
93
- /** Maps raw → JournalEntry input. */
94
- mapper: ImportMapper<TRaw>;
95
- /** The ledger's JournalEntry repository (from createRepositories()). */
96
- journalEntries: {
97
- create(data: Record<string, unknown>): Promise<unknown>;
98
- /**
99
- * Bulk create journal entries. When provided, wireImport uses this for
100
- * batch inserts instead of per-record create() — dramatically faster
101
- * for large imports (single round-trip per batch instead of N).
102
- *
103
- * Falls back to sequential create() if not provided.
104
- */
105
- createMany?(data: Record<string, unknown>[]): Promise<unknown[]>;
106
- getAll(query: Record<string, unknown>): Promise<unknown[]>;
107
- };
108
- /** Organizational context. */
109
- context: Pick<ImportContext, 'organizationId' | 'importRunId'>;
110
- /**
111
- * Optional callback that checks whether entries with the given
112
- * referenceNumbers already exist. Returns the set of existing ones.
113
- * If not provided, wireImport skips the pre-check and relies on
114
- * create() errors (or the idempotency plugin) for dedup.
115
- *
116
- * Example using Mongoose Model directly:
117
- * findExisting: async (refNums, orgId) => {
118
- * const docs = await JournalEntry.find({
119
- * organizationId: orgId,
120
- * referenceNumber: { $in: refNums },
121
- * }).select('referenceNumber').lean();
122
- * return new Set(docs.map(d => d.referenceNumber));
123
- * }
124
- */
125
- findExisting?: (referenceNumbers: string[], organizationId: unknown) => Promise<Set<string>>;
126
- options?: {
127
- /** First error aborts the run. Default: false. */strict?: boolean; /** Entries per batch. Default: 100. */
128
- batchSize?: number; /** Dry-run: do everything except persist. Default: false. */
129
- dryRun?: boolean; /** Progress callback. */
130
- onProgress?: (p: {
131
- processed: number;
132
- total?: number;
133
- }) => void; /** Journal type for imported entries. Default: 'GENERAL'. */
134
- journalType?: string;
135
- };
136
- }
137
- interface WireExportArgs<TOut> {
138
- /** Query to select entries for export. */
139
- query: Record<string, unknown>;
140
- sink: ExportSink<TOut>;
141
- journalEntries: {
142
- getAll(query: Record<string, unknown>): Promise<unknown[]>;
143
- };
144
- options?: {
145
- batchSize?: number;
146
- onProgress?: (p: {
147
- emitted: number;
148
- }) => void;
149
- };
150
- }
151
- //#endregion
152
- export { ImportMapper as a, JournalItemInput as c, ImportError as i, WireExportArgs as l, ExportSink as n, ImportReport as o, ImportContext as r, JournalEntryInput as s, ExportReport as t, WireImportArgs as u };