@classytic/ledger 0.7.0 → 0.9.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 (41) hide show
  1. package/README.md +221 -115
  2. package/dist/bridges/index.d.mts +2 -0
  3. package/dist/bridges/index.mjs +1 -0
  4. package/dist/constants/index.d.mts +1 -1
  5. package/dist/constants/index.mjs +2 -2
  6. package/dist/country/index.d.mts +1 -1
  7. package/dist/errors-BI5k4iak.mjs +121 -0
  8. package/dist/events/index.d.mts +2 -0
  9. package/dist/events/index.mjs +2 -0
  10. package/dist/exports/index.d.mts +1 -1
  11. package/dist/exports/index.mjs +1 -1
  12. package/dist/{fx-realization.plugin-CfYy1tB6.mjs → fx-realization.plugin-Bxlb8cIx.mjs} +45 -2
  13. package/dist/{index-BX8miYdu.d.mts → index-08IpHhrU.d.mts} +12 -1
  14. package/dist/{index-Bl0_ak5w.d.mts → index-Db0n_6Z8.d.mts} +1 -1
  15. package/dist/index-dqkjgpII.d.mts +104 -0
  16. package/dist/index.d.mts +344 -65
  17. package/dist/index.mjs +539 -110
  18. package/dist/{journals-C50E9mpo.d.mts → journals-DUpWwFt1.d.mts} +1 -1
  19. package/dist/opening-balance-1cixYh6Y.mjs +60 -0
  20. package/dist/outbox-store-DQbL-KYT.mjs +132 -0
  21. package/dist/outbox-store-UYC4eZpI.d.mts +249 -0
  22. package/dist/{partner-ledger-D9H5hegI.mjs → partner-ledger-BoebloHk.mjs} +2 -2
  23. package/dist/plugins/index.d.mts +1 -1
  24. package/dist/plugins/index.mjs +1 -1
  25. package/dist/reports/index.d.mts +1 -1
  26. package/dist/reports/index.mjs +1 -1
  27. package/dist/sync/index.d.mts +313 -0
  28. package/dist/sync/index.mjs +527 -0
  29. package/dist/sync-JvchM3FO.d.mts +152 -0
  30. package/dist/{trial-balance-DTc8kzTD.d.mts → trial-balance-DyNm5bFu.d.mts} +2 -2
  31. package/docs/country-packs.md +71 -47
  32. package/docs/engine.md +3 -2
  33. package/docs/subledger-integration.md +29 -8
  34. package/docs/sync.md +330 -0
  35. package/package.json +36 -14
  36. package/dist/errors-CSDQPNyt.mjs +0 -33
  37. /package/dist/{categories-BkKdv16V.mjs → categories-FJlrvzcl.mjs} +0 -0
  38. /package/dist/{core-BkGjuVZj.d.mts → core-DwjkrRkJ.d.mts} +0 -0
  39. /package/dist/{currencies-CsuBGfgs.mjs → currencies-Jo5oaM_4.mjs} +0 -0
  40. /package/dist/{exports-BP-0Ni5W.mjs → exports-C30yRapf.mjs} +0 -0
  41. /package/dist/{index-D1ZjgVxn.d.mts → index-J-XIbXH-.d.mts} +0 -0
package/dist/index.d.mts CHANGED
@@ -1,13 +1,128 @@
1
- import { _ as TaxMetadata, a as Cents, c as DateRange, d as JournalType, f as MainType, g as TaxDetail, h as StatementType, i as CategoryKey, l as EntryState, m as ObjectId, n as CashFlowCategory, o as Currency, p as NormalBalance, s as DateOption, t as AccountType, u as JournalItem, v as TotalAccountOp } from "./core-BkGjuVZj.mjs";
2
- import { S as isValidCategory, a as getJournalTypeCodes, b as isBalanceSheet, c as CURRENCIES, d as isValidCurrency, f as CATEGORIES, i as getJournalType, l as getCurrency, n as JOURNAL_TYPES, o as isValidJournalType, p as CATEGORY_KEYS, r as getCustomJournalTypes, s as registerJournalType, t as JOURNAL_CODES, u as getMinorUnit, x as isIncomeStatement, y as getNormalBalance } from "./journals-C50E9mpo.mjs";
3
- import { i as defineCountryPack, n as CountryPackInput, r as JournalTemplate, t as CountryPack } from "./index-BX8miYdu.mjs";
4
- import { _ as PopulatedJournalEntry, a as exportToCsv, h as FlatJournalRow, i as quickbooksFieldMap, m as ExportFieldMap, p as ExportField, r as universalFieldMap, t as flattenJournalEntries } from "./index-D1ZjgVxn.mjs";
1
+ import { a as EntryReversedNotification, c as PeriodLockedNotification, i as SourceRef, l as ReconciliationMismatchNotification, n as SourceBridge, o as NotificationBridge, r as SourceBridgeContext, s as NotificationBridgeContext, t as LedgerBridges } from "./index-dqkjgpII.mjs";
2
+ import { _ as TaxMetadata, a as Cents, c as DateRange, d as JournalType, f as MainType, g as TaxDetail, h as StatementType, i as CategoryKey, l as EntryState, m as ObjectId, n as CashFlowCategory, o as Currency, p as NormalBalance, s as DateOption, t as AccountType, u as JournalItem, v as TotalAccountOp } from "./core-DwjkrRkJ.mjs";
3
+ import { S as isValidCategory, a as getJournalTypeCodes, b as isBalanceSheet, c as CURRENCIES, d as isValidCurrency, f as CATEGORIES, i as getJournalType, l as getCurrency, n as JOURNAL_TYPES, o as isValidJournalType, p as CATEGORY_KEYS, r as getCustomJournalTypes, s as registerJournalType, t as JOURNAL_CODES, u as getMinorUnit, x as isIncomeStatement, y as getNormalBalance } from "./journals-DUpWwFt1.mjs";
4
+ import { i as defineCountryPack, n as CountryPackInput, r as JournalTemplate, t as CountryPack } from "./index-08IpHhrU.mjs";
5
+ import { C as EntryReversedPayload, D as ReconciliationUnmatchedPayload, E as ReconciliationMatchedPayload, O as LEDGER_EVENTS, S as EntryPostedPayload, T as JournalSeededPayload, _ as AccountBulkCreatedPayload, a as OutboxOwnershipError, b as EntryCreatedPayload, c as InProcessLedgerBus, d as createEvent, f as DomainEvent, g as PublishManyResult, h as EventTransport, i as OutboxFailOptions, k as LedgerEventName, l as InProcessLedgerBusOptions, m as EventLogger, n as OutboxClaimOptions, o as OutboxStore, p as EventHandler, r as OutboxErrorInfo, s as OutboxWriteOptions, t as OutboxAcknowledgeOptions, u as EventContext, v as AccountSeededPayload, w as EntryUnpostedPayload, x as EntryDuplicatedPayload, y as EntryArchivedPayload } from "./outbox-store-UYC4eZpI.mjs";
6
+ import { _ as PopulatedJournalEntry, a as exportToCsv, h as FlatJournalRow, i as quickbooksFieldMap, m as ExportFieldMap, p as ExportField, r as universalFieldMap, t as flattenJournalEntries } from "./index-J-XIbXH-.mjs";
5
7
  import { Money, abs, add, allocate, equals, format, formatPlain, fromDecimal, isNegative, isPositive, isValid, isZero, max, min, multiply, negate, parseCents, percentage, round, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
6
- import { $ as AgedBalanceParams, A as generateDimensionBreakdown, B as BalanceSheetReport, D as DimensionBreakdownParams, E as DimensionBreakdownOptions, F as BudgetVsActualReport, G as IncomeStatementReport, H as CashFlowSection, I as BudgetVsActualRow, J as ReportCategory, K as LedgerEntry, L as generateBudgetVsActual, M as generateCashFlow, N as BudgetVsActualOptions, O as DimensionBreakdownReport, P as BudgetVsActualParams, Q as AgedBalanceOptions, T as reopenFiscalPeriod, U as GeneralLedgerAccount, V as CashFlowReport, W as GeneralLedgerReport, X as TrialBalanceReport, Y as ReportGroup, Z as TrialBalanceRow, a as RevaluationReport, at as Logger, b as generateGeneralLedger, c as RevaluationRate, d as computeRevaluation, et as AgedBalanceReport, f as PartnerLedgerLine, g as generatePartnerLedger, h as PartnerLedgerReport, i as RevaluationParams, it as generateAgedBalance, k as DimensionBreakdownRow, l as RevaluationResult, m as PartnerLedgerParams, n as generateTrialBalance, nt as AgedBucketConfig, o as generateRevaluation, ot as defaultLogger, p as PartnerLedgerOptions, q as ReportAccount, r as RevaluationOptions, rt as DEFAULT_BUCKETS, s as AccountForeignBalance, tt as AgedBalanceRow, u as buildRevaluationEntry, v as generateIncomeStatement, w as closeFiscalPeriod, z as generateBalanceSheet } from "./trial-balance-DTc8kzTD.mjs";
7
- import { A as OpenItem, C as AccountRepository, D as JournalItemRef, E as JournalEntryRepository, F as SeedOptions, I as SeedResult, M as ReconciliationRepository, N as ReverseOptions, O as JournalRepository, P as ReverseResult, S as creditLimitPlugin, T as BulkCreateResult, _ as FxRealizationPluginOptions, a as dailyLockPlugin, b as doubleEntryPlugin, c as periodResolver, d as LockAccountSelector, f as LockHit, g as idempotencyPlugin, i as FiscalLockPluginOptions, j as PostOptions, k as MatchInput, l as createLockPlugin, m as LockResolverContext, n as watermarkResolver, o as fiscalLockPlugin, p as LockResolver, r as DailyLockPluginOptions, s as PeriodResolverOptions, t as WatermarkResolverOptions, u as CreateLockPluginOptions, v as fxRealizationPlugin, w as BulkCreateInput, x as CreditLimitPluginOptions } from "./index-Bl0_ak5w.mjs";
8
+ import { $ as AgedBalanceParams, A as generateDimensionBreakdown, B as BalanceSheetReport, D as DimensionBreakdownParams, E as DimensionBreakdownOptions, F as BudgetVsActualReport, G as IncomeStatementReport, H as CashFlowSection, I as BudgetVsActualRow, J as ReportCategory, K as LedgerEntry, L as generateBudgetVsActual, M as generateCashFlow, N as BudgetVsActualOptions, O as DimensionBreakdownReport, P as BudgetVsActualParams, Q as AgedBalanceOptions, T as reopenFiscalPeriod, U as GeneralLedgerAccount, V as CashFlowReport, W as GeneralLedgerReport, X as TrialBalanceReport, Y as ReportGroup, Z as TrialBalanceRow, a as RevaluationReport, at as Logger, b as generateGeneralLedger, c as RevaluationRate, d as computeRevaluation, et as AgedBalanceReport, f as PartnerLedgerLine, g as generatePartnerLedger, h as PartnerLedgerReport, i as RevaluationParams, it as generateAgedBalance, k as DimensionBreakdownRow, l as RevaluationResult, m as PartnerLedgerParams, n as generateTrialBalance, nt as AgedBucketConfig, o as generateRevaluation, ot as defaultLogger, p as PartnerLedgerOptions, q as ReportAccount, r as RevaluationOptions, rt as DEFAULT_BUCKETS, s as AccountForeignBalance, tt as AgedBalanceRow, u as buildRevaluationEntry, v as generateIncomeStatement, w as closeFiscalPeriod, z as generateBalanceSheet } from "./trial-balance-DyNm5bFu.mjs";
9
+ import { A as OpenItem, C as AccountRepository, D as JournalItemRef, E as JournalEntryRepository, F as SeedOptions, I as SeedResult, M as ReconciliationRepository, N as ReverseOptions, O as JournalRepository, P as ReverseResult, S as creditLimitPlugin, T as BulkCreateResult, _ as FxRealizationPluginOptions, a as dailyLockPlugin, b as doubleEntryPlugin, c as periodResolver, d as LockAccountSelector, f as LockHit, g as idempotencyPlugin, i as FiscalLockPluginOptions, j as PostOptions, k as MatchInput, l as createLockPlugin, m as LockResolverContext, n as watermarkResolver, o as fiscalLockPlugin, p as LockResolver, r as DailyLockPluginOptions, s as PeriodResolverOptions, t as WatermarkResolverOptions, u as CreateLockPluginOptions, v as fxRealizationPlugin, w as BulkCreateInput, x as CreditLimitPluginOptions } from "./index-Db0n_6Z8.mjs";
10
+ import { PaginationConfig, PluginFunction, PluginType, QueryParser, QueryParserOptions, Repository } from "@classytic/mongokit";
8
11
  import { ClientSession, Connection, Model } from "mongoose";
9
- import { PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
10
12
 
13
+ //#region src/plugins/immutable-guard.plugin.d.ts
14
+ interface ImmutableGuardOptions {
15
+ /** The JournalEntry Mongoose model, for looking up current state on update. */
16
+ JournalEntryModel: Model<unknown>;
17
+ /** Multi-tenant org field name (if enabled). */
18
+ orgField?: string;
19
+ }
20
+ /**
21
+ * Returns a mongokit plugin function. Install only when
22
+ * `config.strictness.immutable === true`.
23
+ */
24
+ declare function immutableGuardPlugin(options: ImmutableGuardOptions): PluginFunction;
25
+ //#endregion
26
+ //#region src/utils/errors.d.ts
27
+ /**
28
+ * Typed error for the accounting package.
29
+ * Carries HTTP status + machine-readable code + structured field errors.
30
+ *
31
+ * The `fields` array lets AI agents and API clients pinpoint exactly
32
+ * what failed in a multi-field validation:
33
+ *
34
+ * throw Errors.validationWithFields('Journal entry invalid', [
35
+ * { path: 'journalItems.2.account', issue: 'account does not exist', value: '...' },
36
+ * { path: 'journalItems', issue: 'debits must equal credits', value: { debit: 500, credit: 450 } },
37
+ * ]);
38
+ */
39
+ /** A single field-level validation error. */
40
+ interface FieldError {
41
+ /** Dot-notation path to the invalid field (e.g. `journalItems.2.account`) */
42
+ readonly path: string;
43
+ /** Human-readable description of what's wrong */
44
+ readonly issue: string;
45
+ /** The offending value (optional — omit for sensitive data) */
46
+ readonly value?: unknown;
47
+ }
48
+ declare class AccountingError extends Error {
49
+ readonly status: number;
50
+ readonly code: string;
51
+ readonly fields?: ReadonlyArray<FieldError>;
52
+ constructor(message: string, status?: number, code?: string, fields?: ReadonlyArray<FieldError>);
53
+ /** Serialize to a plain object for API responses and logs. */
54
+ toJSON(): {
55
+ name: string;
56
+ message: string;
57
+ status: number;
58
+ code: string;
59
+ fields?: ReadonlyArray<FieldError>;
60
+ };
61
+ }
62
+ interface ErrorsFactories {
63
+ validation: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
64
+ notFound: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
65
+ conflict: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
66
+ immutable: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
67
+ locked: (scope: string, msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
68
+ }
69
+ declare const Errors: ErrorsFactories;
70
+ /**
71
+ * Thrown when an idempotent create was attempted with a key that already
72
+ * resolved to a different winner (not the common "same winner, same payload"
73
+ * replay — that returns the existing doc without throwing).
74
+ *
75
+ * Typically surfaces when the host supplies an `idempotencyKey` that collides
76
+ * with a logically-different prior write.
77
+ */
78
+ declare class IdempotencyConflictError extends AccountingError {
79
+ readonly idempotencyKey: string;
80
+ readonly existingId: unknown;
81
+ constructor(idempotencyKey: string, existingId: unknown, message?: string);
82
+ }
83
+ /**
84
+ * Thrown when the unique `referenceNumber` index fires. With the new atomic
85
+ * counter this should be effectively impossible — if it ever throws, it
86
+ * indicates either a pre-atomic-counter doc that was hand-inserted OR a bug
87
+ * in the counter partitioning.
88
+ */
89
+ declare class DuplicateReferenceError extends AccountingError {
90
+ readonly referenceNumber: string;
91
+ constructor(referenceNumber: string, message?: string);
92
+ }
93
+ /**
94
+ * Thrown when an optimistic-concurrency FSM transition fails because another
95
+ * writer advanced the state or version between our read and our write.
96
+ *
97
+ * Callers should re-fetch the doc and decide whether to retry or surface.
98
+ */
99
+ declare class ConcurrencyError extends AccountingError {
100
+ readonly resource: string;
101
+ readonly resourceId: unknown;
102
+ constructor(resource: string, resourceId: unknown, message?: string);
103
+ }
104
+ /**
105
+ * Thrown when a mutation targets an entry that is protected by immutability —
106
+ * either `strictness.immutable` or the double-entry plugin's posted-entry
107
+ * guard. Factory `Errors.immutable(msg)` returns this subclass so callers
108
+ * can `instanceof`-match without sniffing the `code` field.
109
+ */
110
+ declare class ImmutableViolationError extends AccountingError {
111
+ readonly entryId: unknown;
112
+ constructor(entryId: unknown, message?: string, fields?: ReadonlyArray<FieldError>);
113
+ }
114
+ /**
115
+ * Detect a Mongo duplicate-key error (11000) and return the index name the
116
+ * conflict hit on, so callers can switch on which unique key fired.
117
+ *
118
+ * Handles both driver-style and mongoose-style error shapes. Safe to call
119
+ * with any `unknown` — returns `null` when the error is not a dup-key.
120
+ */
121
+ declare function classifyDuplicateKey(err: unknown): {
122
+ indexName: string;
123
+ keyPattern?: Record<string, unknown>;
124
+ } | null;
125
+ //#endregion
11
126
  //#region src/types/engine.d.ts
12
127
  /** Mongokit plugins to install per repository (composes with engine built-ins). */
13
128
  interface LedgerRepositoryPlugins {
@@ -33,6 +148,22 @@ interface MultiTenantConfig {
33
148
  orgField: string;
34
149
  /** Mongoose model name the org field references (e.g., 'Business', 'Organization') */
35
150
  orgRef: string;
151
+ /**
152
+ * Enable mongokit's `multiTenantPlugin` on every ledger repository. The plugin
153
+ * injects the tenant filter at POLICY priority (before cache/audit) whenever
154
+ * `ctx.organizationId` is present on a call. When `false` (default), only
155
+ * manual `orgField` filters inside domain verbs enforce scoping.
156
+ *
157
+ * Recommended: `true` for new hosts. Keep `false` if your app has not yet
158
+ * migrated to context-based scoping.
159
+ */
160
+ plugin?: boolean;
161
+ /**
162
+ * Fail closed when `ctx.organizationId` is missing on a plugin-scoped call.
163
+ * Only applies when `plugin: true`. Default: `false` (skip injection when
164
+ * context is empty — matches historical ledger behavior).
165
+ */
166
+ required?: boolean;
36
167
  }
37
168
  /** Options passed to schema factory functions */
38
169
  interface SchemaOptions {
@@ -148,12 +279,70 @@ interface AccountingEngineConfig {
148
279
  audit?: AuditConfig;
149
280
  /** Enable built-in idempotency key field on journal entries */
150
281
  idempotency?: boolean;
282
+ /**
283
+ * TTL in seconds for idempotency records — stale replay keys auto-expire
284
+ * via a partial TTL index so they don't collide with legitimate reuse
285
+ * after the window closes. Default: 86400 (24h). Matches Stripe / Saleor
286
+ * convention. Only applies when `idempotency: true`.
287
+ */
288
+ idempotencyTtlSeconds?: number;
289
+ /**
290
+ * Automatically call `Model.syncIndexes()` on every managed model right
291
+ * after the engine boots. Ensures new partial/TTL indexes (0.9.0+) are
292
+ * present in MongoDB before the first write. Default: `false` — hosts
293
+ * that run migrations themselves should leave this off.
294
+ */
295
+ syncIndexes?: boolean;
151
296
  /** Strictness rules for the ledger */
152
297
  strictness?: StrictnessConfig;
153
298
  /** Mongokit plugins to install per repository. */
154
299
  plugins?: LedgerRepositoryPlugins;
155
300
  /** Pagination caps per repository. */
156
301
  pagination?: LedgerPaginationConfig;
302
+ /**
303
+ * Mongoose type for the multi-tenant field on all ledger schemas.
304
+ *
305
+ * - `'string'` (default, back-compat): stores tenant IDs as strings.
306
+ * Accepts any external auth system (UUIDs, slugs, external identifiers).
307
+ * - `'objectId'`: stores tenant IDs as native MongoDB ObjectId with a
308
+ * Mongoose ref to the organization collection. Enables `$lookup` and
309
+ * `.populate()` against Better Auth's `organization` collection.
310
+ *
311
+ * New hosts wiring Better Auth should pass `'objectId'`. See
312
+ * PACKAGE_RULES §9.1 and §9.2.
313
+ *
314
+ * Note: this field is plumbed into `multiTenantPlugin` when
315
+ * `multiTenant.plugin: true`. Schema-level type switching is applied
316
+ * by the models factory when it supports dynamic type declaration.
317
+ */
318
+ tenantFieldType?: 'objectId' | 'string';
319
+ /**
320
+ * Optional event transport — structurally identical to `@classytic/arc`'s
321
+ * `EventTransport`. Drop in any arc transport (Memory, Redis, Kafka, BullMQ)
322
+ * or provide a custom one. When omitted, the engine uses an in-process bus
323
+ * (`InProcessLedgerBus`) that is NOT suitable for multi-instance deployments.
324
+ */
325
+ eventTransport?: EventTransport;
326
+ /**
327
+ * Optional host-owned outbox store — structurally identical to
328
+ * `@classytic/arc`'s `OutboxStore`. When provided, domain events are
329
+ * persisted via `outbox.save(event, { session })` inside the same
330
+ * mongoose session as the ledger write, giving at-least-once delivery
331
+ * guarantees. A host-side relay worker calls `store.claimPending` +
332
+ * `transport.publish` + `store.acknowledge` independently.
333
+ *
334
+ * Package-owned durable outbox storage is an anti-pattern per
335
+ * PACKAGE_RULES §5.5. Ledger does NOT ship a concrete store — the
336
+ * host picks `MongoOutboxStore` (arc), a SQL store, a Redis store,
337
+ * etc. See `src/events/outbox-store.ts` for the interface contract.
338
+ */
339
+ outboxStore?: OutboxStore;
340
+ /**
341
+ * Host-provided bridges for external integrations (source resolution,
342
+ * notifications). All bridges and all methods are optional — features
343
+ * degrade gracefully when a bridge is missing.
344
+ */
345
+ bridges?: LedgerBridges;
157
346
  }
158
347
  //#endregion
159
348
  //#region src/models/factory.d.ts
@@ -185,16 +374,20 @@ interface LedgerRepositories {
185
374
  reconciliations: ReconciliationRepository<unknown>;
186
375
  journals: JournalRepository<unknown>;
187
376
  }
377
+ /**
378
+ * Extra wiring context passed into wireXxxMethods so domain verbs can emit
379
+ * events and reach out through host-provided bridges. Always optional so
380
+ * consumers without events/bridges see no behavioral change.
381
+ */
382
+ interface LedgerRepositoryIntegrations {
383
+ events?: EventTransport;
384
+ bridges?: LedgerBridges;
385
+ outboxStore?: OutboxStore;
386
+ }
188
387
  /**
189
388
  * Build all ledger repositories with plugins + domain methods pre-wired.
190
- *
191
- * - `accounts` — has seedAccounts(), bulkCreate()
192
- * - `journalEntries` — has post(), unpost(), reverse(), duplicate() + double-entry + fiscal-lock (+ idempotency if enabled)
193
- * - `fiscalPeriods` — plain CRUD
194
- * - `budgets` — plain CRUD
195
- * - `reconciliations` — has reconcile(), unreconcile(), getUnreconciled()
196
389
  */
197
- declare function createRepositories(models: LedgerModels, config: AccountingEngineConfig, plugins?: LedgerRepositoryPlugins, pagination?: LedgerPaginationConfig): LedgerRepositories;
390
+ declare function createRepositories(models: LedgerModels, config: AccountingEngineConfig, plugins?: LedgerRepositoryPlugins, pagination?: LedgerPaginationConfig, integrations?: LedgerRepositoryIntegrations): LedgerRepositories;
198
391
  //#endregion
199
392
  //#region src/semantic/introspect.d.ts
200
393
  interface AccountSummary {
@@ -381,6 +574,28 @@ interface RecordAdjustmentInput {
381
574
  readonly dimensions?: Record<string, unknown>;
382
575
  readonly journalType?: string;
383
576
  }
577
+ interface RecordOpeningBalanceInput {
578
+ /** Cutover date — typically start of fiscal year. */
579
+ readonly cutoverDate: Date;
580
+ /**
581
+ * Account balances in integer cents, signed:
582
+ * - Positive = normal debit balance (assets)
583
+ * - Negative = normal credit balance (liabilities, equity)
584
+ *
585
+ * Should only contain balance sheet accounts. P&L cumulative effect
586
+ * belongs in retained earnings (the equity account).
587
+ */
588
+ readonly balances: ReadonlyArray<{
589
+ readonly account: AccountCode;
590
+ readonly balance: Cents$1;
591
+ }>;
592
+ /**
593
+ * Equity contra account code. Defaults to the country pack's
594
+ * `retainedEarningsAccountCode` (e.g. '3600' for CA, '3310' for BD).
595
+ */
596
+ readonly equityAccount?: AccountCode;
597
+ readonly label?: string;
598
+ }
384
599
  interface RecordAPI {
385
600
  /**
386
601
  * Record a sale. Debits cash/AR, credits revenue. Tax lines — if needed —
@@ -418,6 +633,31 @@ interface RecordAPI {
418
633
  * the other verbs. Amounts must balance (debits = credits).
419
634
  */
420
635
  adjustment(organizationId: unknown, input: RecordAdjustmentInput, options?: RecordOptions): Promise<unknown>;
636
+ /**
637
+ * Record opening balances for a cutover migration. Creates a single
638
+ * multi-line journal entry with each account's balance, contra'd against
639
+ * an equity account (retained earnings by default).
640
+ *
641
+ * Follows the Odoo convention: regular JE, not a special type. Only
642
+ * balance sheet accounts should be passed — P&L cumulative effect belongs
643
+ * in the equity contra account.
644
+ *
645
+ * Idempotent: uses `_externalId: 'opening-balance:{date}'` so re-calling
646
+ * with the same cutover date fails cleanly (duplicate key error).
647
+ *
648
+ * @example
649
+ * ```typescript
650
+ * await engine.record.openingBalance(orgId, {
651
+ * cutoverDate: new Date('2025-01-01'),
652
+ * balances: [
653
+ * { account: '1000', balance: 5000000 }, // $50k cash
654
+ * { account: '2620', balance: -1875000 }, // $18.75k AP
655
+ * { account: '3600', balance: -3125000 }, // $31.25k RE
656
+ * ],
657
+ * });
658
+ * ```
659
+ */
660
+ openingBalance(organizationId: unknown, input: RecordOpeningBalanceInput, options?: RecordOptions): Promise<unknown>;
421
661
  }
422
662
  //#endregion
423
663
  //#region src/engine.d.ts
@@ -453,6 +693,25 @@ declare class AccountingEngine {
453
693
  readonly repositories: LedgerRepositories;
454
694
  readonly record: RecordAPI;
455
695
  readonly introspect: IntrospectAPI;
696
+ /**
697
+ * Event transport — structurally matches `@classytic/arc`'s `EventTransport`.
698
+ * When the host does not inject one, the engine instantiates
699
+ * `InProcessLedgerBus` (suitable for single-instance deployments only).
700
+ * Subscribe with glob patterns: `ledger:entry.*`, `ledger:reconciliation.*`, `*`.
701
+ */
702
+ readonly events: EventTransport;
703
+ /**
704
+ * Host-provided bridges. Empty object when none supplied. Callers should
705
+ * optional-chain every method (`engine.bridges.source?.resolve?.(...)`).
706
+ */
707
+ readonly bridges: LedgerBridges;
708
+ /**
709
+ * Host-provided outbox store for durable event delivery (0.9.0). When
710
+ * present, every domain event is persisted to the outbox in the same
711
+ * mongoose session as the ledger write before the transport publish.
712
+ * Undefined when the host opts out of durable delivery.
713
+ */
714
+ readonly outboxStore: OutboxStore | undefined;
456
715
  private _reports?;
457
716
  constructor(config: AccountingEngineConfig);
458
717
  /**
@@ -540,10 +799,80 @@ declare class AccountingEngine {
540
799
  isValidAccountType(code: string): boolean;
541
800
  /** Get account type definition by code */
542
801
  getAccountType(code: string): AccountType | undefined;
802
+ /**
803
+ * Create a pre-configured QueryParser for URL-driven queries against
804
+ * ledger repositories. Returns a mongokit QueryParser with the correct
805
+ * schema and pagination limits for the specified model.
806
+ *
807
+ * @param model - Which ledger model to parse queries for
808
+ * @param overrides - Additional QueryParserOptions to merge
809
+ *
810
+ * @example
811
+ * ```typescript
812
+ * const parser = engine.createQueryParser('journalEntry');
813
+ * const parsed = parser.parse(req.query);
814
+ * const result = await engine.repositories.journalEntries.getAll({
815
+ * ...parsed,
816
+ * filters: { ...parsed.filters, organizationId },
817
+ * });
818
+ * ```
819
+ */
820
+ createQueryParser(model: 'account' | 'journalEntry' | 'fiscalPeriod' | 'budget' | 'reconciliation' | 'journal', overrides?: Partial<QueryParserOptions>): QueryParser;
543
821
  private _buildReports;
544
822
  }
545
823
  declare function createAccountingEngine(config: AccountingEngineConfig): AccountingEngine;
546
824
  //#endregion
825
+ //#region src/repositories/reconciliation.repository.d.ts
826
+ interface MatchHookItem {
827
+ entry: unknown;
828
+ itemIndex: number;
829
+ debit: number;
830
+ credit: number;
831
+ amountCurrency: number | null;
832
+ exchangeRate: number | null;
833
+ }
834
+ /**
835
+ * Context passed to `before:match` and `after:match` hooks.
836
+ *
837
+ * - `before:match`: fired after validation but before the matchingNumber is
838
+ * stamped and the reconciliation doc is created. Throw to abort.
839
+ * - `after:match`: fired after the reconciliation doc is persisted. Used by
840
+ * fxRealizationPlugin, and can be used by invoice packages to update
841
+ * payment state.
842
+ */
843
+ interface MatchHookContext {
844
+ /** The input that was passed to match(). */
845
+ input: MatchInput;
846
+ /** Validated + enriched item snapshots with debit/credit/currency info. */
847
+ items: MatchHookItem[];
848
+ /** Shared currency across all items, or null if mixed. */
849
+ sharedCurrency: string | null;
850
+ /** The matching number (auto-generated or caller-provided). */
851
+ matchingNumber: string;
852
+ /** Totals for the matched set. */
853
+ debitTotal: number;
854
+ creditTotal: number;
855
+ isFullReconcile: boolean;
856
+ /** The created reconciliation document (only in after:match). */
857
+ reconciliation?: unknown;
858
+ session: ClientSession | null;
859
+ }
860
+ /**
861
+ * Context passed to `before:unmatch` and `after:unmatch` hooks.
862
+ */
863
+ interface UnmatchHookContext {
864
+ matchingNumber: string;
865
+ /** The reconciliation document being removed. */
866
+ reconciliation: Record<string, unknown>;
867
+ /** The items that will be / were unmatched. */
868
+ items: Array<{
869
+ entry: unknown;
870
+ itemIndex: number;
871
+ }>;
872
+ organizationId?: unknown;
873
+ session: ClientSession | null;
874
+ }
875
+ //#endregion
547
876
  //#region src/utils/account-helpers.d.ts
548
877
  /**
549
878
  * Check if an account type is a virtual tax sub-account.
@@ -635,56 +964,6 @@ declare function buildDimensionIndexes(dimensions: DimensionDefinition[], orgFie
635
964
  options?: Record<string, unknown>;
636
965
  }>;
637
966
  //#endregion
638
- //#region src/utils/errors.d.ts
639
- /**
640
- * Typed error for the accounting package.
641
- * Carries HTTP status + machine-readable code + structured field errors.
642
- *
643
- * The `fields` array lets AI agents and API clients pinpoint exactly
644
- * what failed in a multi-field validation:
645
- *
646
- * throw Errors.validationWithFields('Journal entry invalid', [
647
- * { path: 'journalItems.2.account', issue: 'account does not exist', value: '...' },
648
- * { path: 'journalItems', issue: 'debits must equal credits', value: { debit: 500, credit: 450 } },
649
- * ]);
650
- */
651
- /** A single field-level validation error. */
652
- interface FieldError {
653
- /** Dot-notation path to the invalid field (e.g. `journalItems.2.account`) */
654
- readonly path: string;
655
- /** Human-readable description of what's wrong */
656
- readonly issue: string;
657
- /** The offending value (optional — omit for sensitive data) */
658
- readonly value?: unknown;
659
- }
660
- declare class AccountingError extends Error {
661
- readonly status: number;
662
- readonly code: string;
663
- readonly fields?: ReadonlyArray<FieldError>;
664
- constructor(message: string, status?: number, code?: string, fields?: ReadonlyArray<FieldError>);
665
- /** Serialize to a plain object for API responses and logs. */
666
- toJSON(): {
667
- name: string;
668
- message: string;
669
- status: number;
670
- code: string;
671
- fields?: ReadonlyArray<FieldError>;
672
- };
673
- }
674
- /** Convenience factory functions. */
675
- declare const Errors: {
676
- readonly validation: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
677
- readonly notFound: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
678
- readonly conflict: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
679
- readonly immutable: (msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
680
- /**
681
- * Period/scope lock violation. Replaces the old `fiscal` factory — use the
682
- * `scope` argument to distinguish fiscal / tax / daily / bank / etc. The
683
- * resulting error code is `PERIOD_LOCKED_{SCOPE}` (e.g. `PERIOD_LOCKED_FISCAL`).
684
- */
685
- readonly locked: (scope: string, msg: string, fields?: ReadonlyArray<FieldError>) => AccountingError;
686
- };
687
- //#endregion
688
967
  //#region src/utils/filter-builder.d.ts
689
968
  /**
690
969
  * Filter Builder — Sanitizes user-supplied dimension filters for aggregation pipelines.
@@ -764,4 +1043,4 @@ interface PostingResult {
764
1043
  idempotencyKeys?: string[];
765
1044
  }
766
1045
  //#endregion
767
- export { type AccountCode, type AccountForeignBalance, type AccountRepository, type AccountSummary, type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, type ActorContext, type AgedBalanceOptions, type AgedBalanceParams, type AgedBalanceReport, type AgedBalanceRow, type AgedBucketConfig, type AuditConfig, type BalanceSheetReport, type BudgetVsActualOptions, type BudgetVsActualParams, type BudgetVsActualReport, type BudgetVsActualRow, type BulkCreateInput, type BulkCreateResult, CATEGORIES, CATEGORY_KEYS, CURRENCIES, type CashFlowCategory, type CashFlowReport, type CashFlowSection, type CategoryKey, type Cents, type CountryPack, type CountryPackInput, type CreateLockPluginOptions, type CreditLimitPluginOptions, type Currency, DEFAULT_BUCKETS, type DailyLockPluginOptions, type DateOption, type DateRange, type DimensionBreakdownOptions, type DimensionBreakdownParams, type DimensionBreakdownReport, type DimensionBreakdownRow, type DimensionDefinition, type EntryState, Errors, type ExportField, type ExportFieldMap, type FieldError, type FiscalLockPluginOptions, type FiscalPeriodSummary, type FlatJournalRow, type FxRealizationPluginOptions, type GeneralLedgerAccount, type GeneralLedgerReport, type IncomeStatementReport, type IntrospectAPI, JOURNAL_CODES, JOURNAL_TYPES, type JournalEntryRepository, type JournalItem, type JournalItemRef, type JournalRepository, type JournalSchemaOptions, type JournalTemplate, type JournalType, type LedgerEntry, type LedgerModels, type LedgerPaginationConfig, type LedgerRepositories, type LedgerRepositoryPlugins, type LockAccountSelector, type LockHit, type LockResolver, type LockResolverContext, type Logger, type MainType, type MatchInput, type ModelNames, Money, type MultiCurrencyConfig, type MultiTenantConfig, type NormalBalance, type OpenItem, type PartnerLedgerLine, type PartnerLedgerOptions, type PartnerLedgerParams, type PartnerLedgerReport, type PeriodResolverOptions, type PopulatedJournalEntry, type PostOptions, type PostingContract, type PostingResult, type ReconciliationRepository, type RecordAPI, type RecordAdjustmentInput, type RecordAdjustmentLine, type RecordExpenseInput, type RecordOptions, type RecordPaymentInput, type RecordSaleInput, type RecordTransferInput, type ReportAccount, type ReportCategory, type ReportDescriptor, type ReportGroup, type ResolvedModelNames, type RevaluationOptions, type RevaluationParams, type RevaluationRate, type RevaluationReport, type RevaluationResult, type ReverseOptions, type ReverseResult, type SchemaOptions, type SeedOptions, type SeedResult, type Cents$1 as SemanticCents, type SessionResult, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxDetail, type TaxMetadata, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, type WatermarkResolverOptions, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountingEngine, createLockPlugin, createModels, createRepositories, creditLimitPlugin, dailyLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, fxRealizationPlugin, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, periodResolver, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, watermarkResolver };
1046
+ export { type AccountBulkCreatedPayload, type AccountCode, type AccountForeignBalance, type AccountRepository, type AccountSeededPayload, type AccountSummary, type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, type ActorContext, type AgedBalanceOptions, type AgedBalanceParams, type AgedBalanceReport, type AgedBalanceRow, type AgedBucketConfig, type AuditConfig, type BalanceSheetReport, type BudgetVsActualOptions, type BudgetVsActualParams, type BudgetVsActualReport, type BudgetVsActualRow, type BulkCreateInput, type BulkCreateResult, CATEGORIES, CATEGORY_KEYS, CURRENCIES, type CashFlowCategory, type CashFlowReport, type CashFlowSection, type CategoryKey, type Cents, ConcurrencyError, type CountryPack, type CountryPackInput, type CreateLockPluginOptions, type CreditLimitPluginOptions, type Currency, DEFAULT_BUCKETS, type DailyLockPluginOptions, type DateOption, type DateRange, type DimensionBreakdownOptions, type DimensionBreakdownParams, type DimensionBreakdownReport, type DimensionBreakdownRow, type DimensionDefinition, type DomainEvent, DuplicateReferenceError, type EntryArchivedPayload, type EntryCreatedPayload, type EntryDuplicatedPayload, type EntryPostedPayload, type EntryReversedNotification, type EntryReversedPayload, type EntryState, type EntryUnpostedPayload, Errors, type EventContext, type EventHandler, type EventLogger, type EventTransport, type ExportField, type ExportFieldMap, type FieldError, type FiscalLockPluginOptions, type FiscalPeriodSummary, type FlatJournalRow, type FxRealizationPluginOptions, type GeneralLedgerAccount, type GeneralLedgerReport, IdempotencyConflictError, type ImmutableGuardOptions, ImmutableViolationError, InProcessLedgerBus, type InProcessLedgerBusOptions, type IncomeStatementReport, type IntrospectAPI, JOURNAL_CODES, JOURNAL_TYPES, type JournalEntryRepository, type JournalItem, type JournalItemRef, type JournalRepository, type JournalSchemaOptions, type JournalSeededPayload, type JournalTemplate, type JournalType, LEDGER_EVENTS, type LedgerBridges, type LedgerEntry, type LedgerEventName, type LedgerModels, type LedgerPaginationConfig, type LedgerRepositories, type LedgerRepositoryPlugins, type LockAccountSelector, type LockHit, type LockResolver, type LockResolverContext, type Logger, type MainType, type MatchHookContext, type MatchHookItem, type MatchInput, type ModelNames, Money, type MultiCurrencyConfig, type MultiTenantConfig, type NormalBalance, type NotificationBridge, type NotificationBridgeContext, type OpenItem, type OutboxAcknowledgeOptions, type OutboxClaimOptions, type OutboxErrorInfo, type OutboxFailOptions, OutboxOwnershipError, type OutboxStore, type OutboxWriteOptions, type PartnerLedgerLine, type PartnerLedgerOptions, type PartnerLedgerParams, type PartnerLedgerReport, type PeriodLockedNotification, type PeriodResolverOptions, type PopulatedJournalEntry, type PostOptions, type PostingContract, type PostingResult, type PublishManyResult, type ReconciliationMatchedPayload, type ReconciliationMismatchNotification, type ReconciliationRepository, type ReconciliationUnmatchedPayload, type RecordAPI, type RecordAdjustmentInput, type RecordAdjustmentLine, type RecordExpenseInput, type RecordOptions, type RecordPaymentInput, type RecordSaleInput, type RecordTransferInput, type ReportAccount, type ReportCategory, type ReportDescriptor, type ReportGroup, type ResolvedModelNames, type RevaluationOptions, type RevaluationParams, type RevaluationRate, type RevaluationReport, type RevaluationResult, type ReverseOptions, type ReverseResult, type SchemaOptions, type SeedOptions, type SeedResult, type Cents$1 as SemanticCents, type SessionResult, type SourceBridge, type SourceBridgeContext, type SourceRef, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxDetail, type TaxMetadata, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, type UnmatchHookContext, type WatermarkResolverOptions, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, classifyDuplicateKey, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountingEngine, createEvent, createLockPlugin, createModels, createRepositories, creditLimitPlugin, dailyLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, fxRealizationPlugin, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, immutableGuardPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, periodResolver, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, watermarkResolver };