@classytic/ledger 0.4.0 → 0.4.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @classytic/ledger
2
2
 
3
- Embeddable double-entry accounting engine for MongoDB. Integer-cents arithmetic, plugin-based, country-agnostic.
3
+ Embeddable double-entry accounting engine for MongoDB. Integer-cents arithmetic, plugin-based, country-agnostic. Extensible journal types, multi-tenant isolation at every layer.
4
4
 
5
5
  Build QuickBooks, Xero, or TaxCycle-grade apps — the engine handles the accounting, you handle the UX.
6
6
 
@@ -15,23 +15,49 @@ npm install @classytic/ledger-bd # Bangladesh (BFRS, VAT/TDS, Mushak)
15
15
  ## Quick Start
16
16
 
17
17
  ```typescript
18
+ import mongoose from 'mongoose';
18
19
  import { createAccountingEngine } from '@classytic/ledger';
19
20
  import { canadaPack } from '@classytic/ledger-ca';
20
21
 
21
- const accounting = createAccountingEngine({
22
+ // The engine owns the models — matches flow/promo pattern
23
+ const engine = createAccountingEngine({
24
+ mongoose: mongoose.connection,
22
25
  country: canadaPack,
23
26
  currency: 'CAD',
24
- multiTenant: { orgField: 'organization', orgRef: 'Organization' },
27
+ multiTenant: { orgField: 'organizationId', orgRef: 'Organization' },
25
28
  });
26
29
 
27
- // Schemas
28
- const Account = mongoose.model('Account', accounting.createAccountSchema());
29
- const JournalEntry = mongoose.model('JournalEntry', accounting.createJournalEntrySchema('Account'));
30
- const FiscalPeriod = mongoose.model('FiscalPeriod', accounting.createFiscalPeriodSchema());
30
+ // Models, repositories, and reports are auto-created
31
+ await engine.repositories.accounts.seedAccounts(orgId);
32
+ const entry = await engine.repositories.journalEntries.post(entryId, orgId);
33
+ const bs = await engine.reports.balanceSheet({ organizationId: orgId, dateOption: 'year', dateValue: 2025 });
34
+ ```
35
+
36
+ ### Engine-owned models (recommended)
37
+
38
+ Pass `mongoose: connection` in config and the engine creates and wires everything:
39
+
40
+ | Property | What it gives you |
41
+ |----------|-------------------|
42
+ | `engine.models.Account` / `JournalEntry` / `FiscalPeriod` / `Budget` / `Reconciliation` | Mongoose models, ready to query |
43
+ | `engine.repositories.accounts.seedAccounts()` / `bulkCreate()` | Account repo with domain methods |
44
+ | `engine.repositories.journalEntries.post()` / `reverse()` / `unpost()` / `duplicate()` | JE repo with plugins pre-wired (double-entry, fiscal-lock, idempotency) |
45
+ | `engine.repositories.fiscalPeriods` / `budgets` | Plain CRUD repos |
46
+ | `engine.repositories.reconciliations.reconcile()` / `unreconcile()` / `getUnreconciled()` | Reconciliation repo with domain methods |
47
+ | `engine.reports.trialBalance()` / `balanceSheet()` / etc. | All 10 reports, bound to the owned models |
48
+
49
+ This pattern unblocks framework auto-discovery (Arc `loadResources`, Fastify plugins, etc.) because resources can be defined at module top-level without factory wrappers.
50
+
51
+ ### Low-level (manual schema/model setup)
31
52
 
32
- // Reports
33
- const reports = accounting.createReports({ Account, JournalEntry });
34
- const bs = await reports.balanceSheet({ organizationId, dateOption: 'year', dateValue: 2025 });
53
+ If you need custom naming or don't want the engine to own models, omit `mongoose` from config:
54
+
55
+ ```typescript
56
+ const engine = createAccountingEngine({ country: canadaPack, currency: 'CAD' });
57
+ const Account = mongoose.model('GLAccount', engine.createAccountSchema());
58
+ const JournalEntry = mongoose.model('GLEntry', engine.createJournalEntrySchema('GLAccount'));
59
+ const accountRepo = engine.wireAccountRepository(new Repository(Account), Account);
60
+ const reports = engine.createReports({ Account, JournalEntry });
35
61
  ```
36
62
 
37
63
  ## Core Features
@@ -41,8 +67,9 @@ const bs = await reports.balanceSheet({ organizationId, dateOption: 'year', date
41
67
  - Integer-cents storage — zero floating-point drift
42
68
  - Draft → Posted → Reversed state machine
43
69
  - Configurable immutability (corrections only via reversal)
44
- - Multi-tenant isolation at every layer
70
+ - Multi-tenant isolation at every layer (reports, schemas, repositories)
45
71
  - Country packs for localized charts of accounts and tax codes
72
+ - Extensible journal type registry — add domain-specific types (POS, E-Commerce, Payroll) at startup
46
73
 
47
74
  **10 Reports**
48
75
  - Trial Balance (3-column: initial + period + ending)
@@ -155,7 +182,36 @@ taxHookPlugin({
155
182
  | `@classytic/ledger/repositories` | Repository wiring |
156
183
  | `@classytic/ledger/exports` | CSV export + QuickBooks field maps |
157
184
  | `@classytic/ledger/country` | `defineCountryPack`, `CountryPack` interface |
158
- | `@classytic/ledger/constants` | Categories, journal types, currencies |
185
+ | `@classytic/ledger/constants` | Categories, journal types (+ registry), currencies |
186
+
187
+ ## Extensible Journal Types
188
+
189
+ The 15 built-in journal types (SALES, PURCHASES, GENERAL, PAYROLL, etc.) cover standard accounting. For domain-specific needs, register custom types **before** schema creation:
190
+
191
+ ```typescript
192
+ import { registerJournalType, getJournalTypeCodes, isValidJournalType } from '@classytic/ledger';
193
+
194
+ // Register at startup, before createJournalEntrySchema()
195
+ registerJournalType('POS_SALES', {
196
+ code: 'POS_SALES',
197
+ name: 'POS Sales Journal',
198
+ description: 'Daily aggregated point-of-sale transactions',
199
+ });
200
+
201
+ registerJournalType('ECOM_SALES', {
202
+ code: 'ECOM_SALES',
203
+ name: 'E-Commerce Sales Journal',
204
+ description: 'Per-order online transactions',
205
+ });
206
+
207
+ // Custom types pass Mongoose enum validation, appear in all lookups
208
+ isValidJournalType('POS_SALES'); // true
209
+ getJournalTypeCodes(); // [...15 built-in, 'POS_SALES', 'ECOM_SALES']
210
+
211
+ // Reference numbers use the custom type prefix: POS_SALES/2025/03/0001
212
+ ```
213
+
214
+ The registry freezes when `createJournalEntrySchema()` is called. Late registration throws. Built-in types cannot be overridden.
159
215
 
160
216
  ## Country Packs
161
217
 
@@ -180,25 +236,31 @@ Available packs: `@classytic/ledger-ca` (Canada), `@classytic/ledger-bd` (Bangla
180
236
 
181
237
  ## Testing
182
238
 
183
- 949 tests covering unit, integration, and end-to-end scenarios:
184
-
185
239
  ```bash
186
- npm test # run all
187
- npx vitest run tests/e2e/ # e2e scenarios only
240
+ npm test # run all
241
+ npx vitest run tests/e2e/ # e2e scenarios only
242
+ npx vitest run tests/scenarios/ # integration scenarios
243
+ npx vitest run tests/hardening/ # edge cases & invariants
188
244
  ```
189
245
 
190
- E2E suites include:
246
+ Test suites cover:
191
247
  - Canadian small business full-year lifecycle
192
248
  - Multi-currency trading with FX revaluation
193
- - All plugins + dimensions + budgets + fiscal close
249
+ - Multi-tenant report isolation (org A cannot see org B)
250
+ - Posting pipeline → Trial Balance → Income Statement → Balance Sheet
251
+ - Reversal & correction workflows with audit trail
252
+ - Custom journal type registry → schema → posting pipeline
253
+ - Double-entry conservation law (debit = credit across all entries)
254
+ - Money arithmetic hardening (overflow, penny-leak, float traps)
255
+ - Public API surface & subpath export verification
194
256
  - O-Level / A-Level / university textbook accounting problems
195
257
 
196
258
  ## Requirements
197
259
 
198
260
  - Node.js >= 22
199
261
  - MongoDB (replica set recommended for transactions)
200
- - Mongoose >= 9
201
- - @classytic/mongokit >= 3
262
+ - Mongoose >= 9.4.1
263
+ - @classytic/mongokit >= 3.5.3
202
264
 
203
265
  ## License
204
266
 
@@ -1,4 +1,4 @@
1
- import { a as extractStatementType, c as getNormalBalance, d as isValidCategory, i as extractMainType, l as isBalanceSheet, n as CATEGORY_KEYS, o as getCategoryMainType, r as categoryKey, s as getCategoryStatementType, t as CATEGORIES, u as isIncomeStatement } from "../categories-FJlrvzcl.mjs";
2
- import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "../journals-BcMn71Cq.mjs";
1
+ import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "../journals-BfwnCFam.mjs";
2
+ import { a as extractStatementType, c as getNormalBalance, d as isValidCategory, i as extractMainType, l as isBalanceSheet, n as CATEGORY_KEYS, o as getCategoryMainType, r as categoryKey, s as getCategoryStatementType, t as CATEGORIES, u as isIncomeStatement } from "../categories-DWogBUgQ.mjs";
3
3
  import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "../currencies-W8kQAkm0.mjs";
4
4
  export { CATEGORIES, CATEGORY_KEYS, CURRENCIES, JOURNAL_CODES, JOURNAL_TYPES, categoryKey, extractMainType, extractStatementType, getCategoryMainType, getCategoryStatementType, getCurrency, getCustomJournalTypes, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, registerJournalType };
@@ -1,4 +1,4 @@
1
- import { n as Errors } from "./errors-BoGUSUYL.mjs";
1
+ import { n as Errors } from "./errors-B_dyYZc_.mjs";
2
2
  //#region src/plugins/double-entry.plugin.ts
3
3
  function doubleEntryPlugin(options = {}) {
4
4
  const { onlyOnPost = true, JournalEntryModel, AccountModel, orgField } = options;
@@ -1,5 +1,6 @@
1
1
  import { t as CountryPack } from "./index-CxZqRaOU.mjs";
2
2
  import { t as Logger } from "./logger-UbTdBb1x.mjs";
3
+ import { Connection } from "mongoose";
3
4
 
4
5
  //#region src/types/engine.d.ts
5
6
  /** Multi-tenant configuration */
@@ -68,8 +69,35 @@ interface MultiCurrencyConfig {
68
69
  /** Allowed foreign currency codes. If omitted, any ISO 4217 code is accepted. */
69
70
  currencies?: readonly string[];
70
71
  }
72
+ /**
73
+ * Override default model names. Useful when you want to avoid collisions
74
+ * with existing models or use custom naming conventions.
75
+ */
76
+ interface ModelNames {
77
+ account?: string;
78
+ journalEntry?: string;
79
+ fiscalPeriod?: string;
80
+ budget?: string;
81
+ reconciliation?: string;
82
+ }
71
83
  /** Main engine configuration */
72
84
  interface AccountingEngineConfig {
85
+ /**
86
+ * Mongoose connection. Required for `engine.models` and `engine.repositories`
87
+ * to be auto-populated. If omitted, you must use the low-level schema
88
+ * factories (`engine.createAccountSchema()`) and register models yourself.
89
+ */
90
+ mongoose?: Connection;
91
+ /** Override default model names (e.g. 'Account' → 'GLAccount') */
92
+ modelNames?: ModelNames;
93
+ /** Extra fields / indexes per model */
94
+ schemaOptions?: {
95
+ account?: SchemaOptions;
96
+ journalEntry?: JournalSchemaOptions;
97
+ fiscalPeriod?: SchemaOptions;
98
+ budget?: SchemaOptions;
99
+ reconciliation?: SchemaOptions;
100
+ };
73
101
  /** Country pack providing account types, tax codes, and templates */
74
102
  country: CountryPack;
75
103
  /** Default ISO 4217 currency code — the functional/base currency (e.g., 'CAD', 'BDT') */
@@ -99,4 +127,4 @@ interface AccountingEngineConfig {
99
127
  strictness?: StrictnessConfig;
100
128
  }
101
129
  //#endregion
102
- export { MultiTenantConfig as a, MultiCurrencyConfig as i, AuditConfig as n, SchemaOptions as o, JournalSchemaOptions as r, StrictnessConfig as s, AccountingEngineConfig as t };
130
+ export { MultiCurrencyConfig as a, StrictnessConfig as c, ModelNames as i, AuditConfig as n, MultiTenantConfig as o, JournalSchemaOptions as r, SchemaOptions as s, AccountingEngineConfig as t };
@@ -1,6 +1,6 @@
1
- import { n as Errors } from "./errors-BoGUSUYL.mjs";
2
- import { t as requireOrgScope } from "./tenant-guard-CAxXoWuS.mjs";
3
- import { i as extractMainType } from "./categories-FJlrvzcl.mjs";
1
+ import { n as Errors } from "./errors-B_dyYZc_.mjs";
2
+ import { t as requireOrgScope } from "./tenant-guard-r17Se3Bb.mjs";
3
+ import { i as extractMainType } from "./categories-DWogBUgQ.mjs";
4
4
  import mongoose from "mongoose";
5
5
  //#region src/reports/aged-balance.ts
6
6
  const DEFAULT_BUCKETS = [
package/dist/index.d.mts CHANGED
@@ -4,15 +4,77 @@ import { a as TaxReportLine, i as TaxCodesByRegion, n as CountryPackInput, o as
4
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-J-XIbXH-.mjs";
5
5
  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
6
  import { n as defaultLogger, t as Logger } from "./logger-UbTdBb1x.mjs";
7
- import { a as MultiTenantConfig, i as MultiCurrencyConfig, n as AuditConfig, o as SchemaOptions, r as JournalSchemaOptions, s as StrictnessConfig, t as AccountingEngineConfig } from "./engine-DF-MtsEr.mjs";
8
- import { a as BulkCreateInput, c as PostOptions, d as ReverseOptions, f as ReverseResult, i as AccountRepository, l as ReconcileParams, m as SeedResult, n as wireJournalEntryMethods, o as BulkCreateResult, p as SeedOptions, r as wireAccountMethods, s as JournalEntryRepository, t as wireReconciliationMethods, u as ReconciliationRepository } from "./reconciliation.repository-DEybU_Ok.mjs";
9
- import { n as createFiscalPeriodSchema, r as createAccountSchema, t as createJournalEntrySchema } from "./journal-entry.schema-B1CzLwC3.mjs";
7
+ import { a as MultiCurrencyConfig, c as StrictnessConfig, i as ModelNames, n as AuditConfig, o as MultiTenantConfig, r as JournalSchemaOptions, s as SchemaOptions, t as AccountingEngineConfig } from "./engine-scgOvxHJ.mjs";
8
+ import { a as BulkCreateInput, c as PostOptions, d as ReverseOptions, f as ReverseResult, i as AccountRepository, l as ReconcileParams, m as SeedResult, n as wireJournalEntryMethods, o as BulkCreateResult, p as SeedOptions, r as wireAccountMethods, s as JournalEntryRepository, t as wireReconciliationMethods, u as ReconciliationRepository } from "./reconciliation.repository-D-D_ITL-.mjs";
9
+ import { n as createFiscalPeriodSchema, r as createAccountSchema, t as createJournalEntrySchema } from "./journal-entry.schema-JqrfbvB4.mjs";
10
10
  import { c as dateLockPlugin, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin } from "./idempotency.plugin-zU-GKJ0-.mjs";
11
11
  import { $ as AgedBucketConfig, A as BudgetVsActualReport, B as IncomeStatementReport, C as DimensionBreakdownReport, D as generateCashFlow, F as BalanceSheetReport, G as TaxReport, H as ReportAccount, I as CashFlowReport, J as TrialBalanceRow, K as TaxReturnSummary, L as CashFlowSection, M as generateBudgetVsActual, O as BudgetVsActualOptions, P as generateBalanceSheet, Q as AgedBalanceRow, R as GeneralLedgerAccount, S as DimensionBreakdownParams, T as generateDimensionBreakdown, U as ReportCategory, V as LedgerEntry, W as ReportGroup, X as AgedBalanceParams, Y as AgedBalanceOptions, Z as AgedBalanceReport, a as RevaluationReport, b as reopenFiscalPeriod, c as RevaluationRate, d as computeRevaluation, et as DEFAULT_BUCKETS, h as generateGeneralLedger, i as RevaluationParams, j as BudgetVsActualRow, k as BudgetVsActualParams, l as RevaluationResult, n as generateTrialBalance, o as generateRevaluation, p as generateIncomeStatement, q as TrialBalanceReport, r as RevaluationOptions, s as AccountForeignBalance, tt as generateAgedBalance, u as buildRevaluationEntry, w as DimensionBreakdownRow, x as DimensionBreakdownOptions, y as closeFiscalPeriod, z as GeneralLedgerReport } from "./trial-balance-DcQ0xj_4.mjs";
12
12
  import * as mongoose$1 from "mongoose";
13
13
  import { ClientSession, Connection, Model } from "mongoose";
14
- import { PluginType, Repository } from "@classytic/mongokit";
14
+ import { PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
15
15
 
16
+ //#region src/models/factory.d.ts
17
+ interface LedgerModels {
18
+ Account: Model<unknown>;
19
+ JournalEntry: Model<unknown>;
20
+ FiscalPeriod: Model<unknown>;
21
+ Budget: Model<unknown>;
22
+ Reconciliation: Model<unknown>;
23
+ }
24
+ interface ResolvedModelNames {
25
+ account: string;
26
+ journalEntry: string;
27
+ fiscalPeriod: string;
28
+ budget: string;
29
+ reconciliation: string;
30
+ }
31
+ declare function resolveModelNames(overrides?: ModelNames): ResolvedModelNames;
32
+ /**
33
+ * Create (or reuse) all ledger models on the given connection.
34
+ *
35
+ * If a model with the same name is already registered on the connection,
36
+ * the existing model is reused — this allows multiple engine instances
37
+ * to share models and prevents "OverwriteModelError".
38
+ */
39
+ declare function createModels(connection: Connection, config: AccountingEngineConfig): LedgerModels;
40
+ //#endregion
41
+ //#region src/repositories/factory.d.ts
42
+ interface LedgerRepositoryPlugins {
43
+ account?: PluginType[];
44
+ journalEntry?: PluginType[];
45
+ fiscalPeriod?: PluginType[];
46
+ budget?: PluginType[];
47
+ reconciliation?: PluginType[];
48
+ }
49
+ /**
50
+ * Pagination config per repository. Defaults to `{ maxLimit: 1000 }` for accounts
51
+ * (typical chart of accounts size), 100 for everything else.
52
+ */
53
+ interface LedgerPaginationConfig {
54
+ account?: PaginationConfig;
55
+ journalEntry?: PaginationConfig;
56
+ fiscalPeriod?: PaginationConfig;
57
+ budget?: PaginationConfig;
58
+ reconciliation?: PaginationConfig;
59
+ }
60
+ interface LedgerRepositories {
61
+ accounts: AccountRepository<unknown>;
62
+ journalEntries: JournalEntryRepository<unknown>;
63
+ fiscalPeriods: Repository<unknown>;
64
+ budgets: Repository<unknown>;
65
+ reconciliations: ReconciliationRepository<unknown>;
66
+ }
67
+ /**
68
+ * Build all ledger repositories with plugins + domain methods pre-wired.
69
+ *
70
+ * - `accounts` — has seedAccounts(), bulkCreate()
71
+ * - `journalEntries` — has post(), unpost(), reverse(), duplicate() + double-entry + fiscal-lock (+ idempotency if enabled)
72
+ * - `fiscalPeriods` — plain CRUD
73
+ * - `budgets` — plain CRUD
74
+ * - `reconciliations` — has reconcile(), unreconcile(), getUnreconciled()
75
+ */
76
+ declare function createRepositories(models: LedgerModels, config: AccountingEngineConfig, plugins?: LedgerRepositoryPlugins, pagination?: LedgerPaginationConfig): LedgerRepositories;
77
+ //#endregion
16
78
  //#region src/engine.d.ts
17
79
  declare class AccountingEngine {
18
80
  readonly config: AccountingEngineConfig;
@@ -42,7 +104,102 @@ declare class AccountingEngine {
42
104
  readonly isValid: typeof isValid;
43
105
  readonly parseCents: typeof parseCents;
44
106
  };
45
- constructor(config: AccountingEngineConfig);
107
+ private _models?;
108
+ private _repositories?;
109
+ private _reports?;
110
+ constructor(config: AccountingEngineConfig, plugins?: LedgerRepositoryPlugins, pagination?: LedgerPaginationConfig);
111
+ /**
112
+ * Auto-created Mongoose models. Requires `mongoose` in config.
113
+ *
114
+ * @throws if `mongoose` was not provided in config
115
+ */
116
+ get models(): LedgerModels;
117
+ /**
118
+ * Auto-wired repositories with plugins + domain methods (post, reverse, etc.).
119
+ * Requires `mongoose` in config.
120
+ *
121
+ * @throws if `mongoose` was not provided in config
122
+ */
123
+ get repositories(): LedgerRepositories;
124
+ /**
125
+ * Pre-built reports bound to auto-created models. Requires `mongoose` in config.
126
+ * For custom models, use `engine.createReports({ Account, JournalEntry, Budget })`.
127
+ */
128
+ get reports(): {
129
+ trialBalance: (params: {
130
+ organizationId?: unknown;
131
+ dateOption: "month" | "quarter" | "year" | "custom";
132
+ dateValue: unknown;
133
+ accountId?: string;
134
+ filters?: Record<string, unknown>;
135
+ }) => Promise<TrialBalanceReport>;
136
+ balanceSheet: (params: {
137
+ organizationId?: unknown;
138
+ dateOption: "month" | "quarter" | "year" | "custom";
139
+ dateValue: unknown;
140
+ businessName?: string;
141
+ filters?: Record<string, unknown>;
142
+ }) => Promise<BalanceSheetReport>;
143
+ incomeStatement: (params: {
144
+ organizationId?: unknown;
145
+ dateOption: "month" | "quarter" | "year" | "custom";
146
+ dateValue: unknown;
147
+ businessName?: string;
148
+ filters?: Record<string, unknown>;
149
+ }) => Promise<IncomeStatementReport>;
150
+ generalLedger: (params: {
151
+ organizationId?: unknown;
152
+ dateOption: "month" | "quarter" | "year" | "custom";
153
+ dateValue: unknown;
154
+ accountId?: string;
155
+ filters?: Record<string, unknown>;
156
+ }) => Promise<GeneralLedgerReport>;
157
+ cashFlow: (params: {
158
+ organizationId?: unknown;
159
+ dateOption: "month" | "quarter" | "year" | "custom";
160
+ dateValue: unknown;
161
+ businessName?: string;
162
+ filters?: Record<string, unknown>;
163
+ }) => Promise<CashFlowReport>;
164
+ agedBalance: (params: {
165
+ organizationId?: unknown;
166
+ asOfDate?: Date;
167
+ type: "receivable" | "payable";
168
+ accountIds?: unknown[];
169
+ dueDateField?: string;
170
+ contactField?: string;
171
+ buckets?: {
172
+ label: string;
173
+ minDays: number;
174
+ maxDays: number;
175
+ }[] | undefined;
176
+ }) => Promise<AgedBalanceReport>;
177
+ dimensionBreakdown: (params: {
178
+ organizationId?: unknown;
179
+ dateOption: "month" | "quarter" | "year" | "custom";
180
+ dateValue: unknown;
181
+ dimension: string;
182
+ accountCategory?: string;
183
+ filters?: Record<string, unknown>;
184
+ }) => Promise<DimensionBreakdownReport>;
185
+ budgetVsActual: (params: {
186
+ organizationId?: unknown;
187
+ dateOption: "month" | "quarter" | "year" | "custom";
188
+ dateValue: unknown;
189
+ accountIds?: unknown[];
190
+ filters?: Record<string, unknown>;
191
+ }) => Promise<BudgetVsActualReport>;
192
+ revaluation: (params: {
193
+ organizationId?: unknown;
194
+ asOfDate: Date;
195
+ rates: {
196
+ currency: string;
197
+ rate: number;
198
+ }[];
199
+ unrealizedGainLossAccountId: unknown;
200
+ generateEntry?: boolean;
201
+ }) => Promise<RevaluationReport>;
202
+ };
46
203
  createAccountSchema(options?: SchemaOptions): mongoose$1.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
47
204
  timestamps: true;
48
205
  }, {
@@ -198,6 +355,11 @@ declare class AccountingEngine {
198
355
  } & {
199
356
  __v: number;
200
357
  }>;
358
+ /**
359
+ * Build a reports object bound to the given models. Use this when you
360
+ * need custom/external models. Prefer `engine.reports` when the engine
361
+ * owns the models (via `mongoose` in config).
362
+ */
201
363
  createReports(models: {
202
364
  Account: Model<unknown>;
203
365
  JournalEntry: Model<unknown>;
@@ -277,6 +439,7 @@ declare class AccountingEngine {
277
439
  generateEntry?: boolean;
278
440
  }) => Promise<RevaluationReport>;
279
441
  };
442
+ private _buildReports;
280
443
  /** Get all posting account types (accounts you can post transactions to) */
281
444
  getPostingAccountTypes(): readonly AccountType[];
282
445
  /** Validate an account type code */
@@ -335,7 +498,7 @@ declare class AccountingEngine {
335
498
  */
336
499
  wireReconciliationRepository<TDoc = unknown>(repository: Repository<TDoc>, ReconciliationModel: Model<unknown>, JournalEntryModel: Model<unknown>): ReconciliationRepository<TDoc>;
337
500
  }
338
- declare function createAccountingEngine(config: AccountingEngineConfig): AccountingEngine;
501
+ declare function createAccountingEngine(config: AccountingEngineConfig, plugins?: LedgerRepositoryPlugins, pagination?: LedgerPaginationConfig): AccountingEngine;
339
502
  //#endregion
340
503
  //#region src/utils/account-helpers.d.ts
341
504
  /**
@@ -527,4 +690,4 @@ interface PostingResult {
527
690
  idempotencyKeys?: string[];
528
691
  }
529
692
  //#endregion
530
- export { type AccountForeignBalance, type AccountRepository, type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, 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 Currency, DEFAULT_BUCKETS, type DateOption, type DateRange, type DimensionBreakdownOptions, type DimensionBreakdownParams, type DimensionBreakdownReport, type DimensionBreakdownRow, type DimensionDefinition, type EntryState, Errors, type ExportField, type ExportFieldMap, type FlatJournalRow, type GeneralLedgerAccount, type GeneralLedgerReport, type IncomeStatementReport, JOURNAL_CODES, JOURNAL_TYPES, type JournalEntryRepository, type JournalItem, type JournalSchemaOptions, type JournalType, type LedgerEntry, type Logger, type MainType, Money, type MultiCurrencyConfig, type MultiTenantConfig, type NormalBalance, type PopulatedJournalEntry, type PostOptions, type PostingContract, type PostingResult, type ReconcileParams, type ReconciliationRepository, type ReportAccount, type ReportCategory, type ReportGroup, type RevaluationOptions, type RevaluationParams, type RevaluationRate, type RevaluationReport, type RevaluationResult, type ReverseOptions, type ReverseResult, type SchemaOptions, type SeedOptions, type SeedResult, type SessionResult, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxCode, type TaxCodesByRegion, type TaxDetail, type TaxMetadata, type TaxReport, type TaxReportLine, type TaxReportTemplate, type TaxReturnSummary, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, dateLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
693
+ export { type AccountForeignBalance, type AccountRepository, type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, 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 Currency, DEFAULT_BUCKETS, type DateOption, type DateRange, type DimensionBreakdownOptions, type DimensionBreakdownParams, type DimensionBreakdownReport, type DimensionBreakdownRow, type DimensionDefinition, type EntryState, Errors, type ExportField, type ExportFieldMap, type FlatJournalRow, type GeneralLedgerAccount, type GeneralLedgerReport, type IncomeStatementReport, JOURNAL_CODES, JOURNAL_TYPES, type JournalEntryRepository, type JournalItem, type JournalSchemaOptions, type JournalType, type LedgerEntry, type LedgerModels, type LedgerPaginationConfig, type LedgerRepositories, type LedgerRepositoryPlugins, type Logger, type MainType, type ModelNames, Money, type MultiCurrencyConfig, type MultiTenantConfig, type NormalBalance, type PopulatedJournalEntry, type PostOptions, type PostingContract, type PostingResult, type ReconcileParams, type ReconciliationRepository, type ReportAccount, type ReportCategory, 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 SessionResult, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxCode, type TaxCodesByRegion, type TaxDetail, type TaxMetadata, type TaxReport, type TaxReportLine, type TaxReportTemplate, type TaxReturnSummary, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, createModels, createRepositories, dateLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
package/dist/index.mjs CHANGED
@@ -1,25 +1,159 @@
1
+ import { a as createAccountSchema, i as createBudgetSchema, n as createJournalEntrySchema, r as createFiscalPeriodSchema, t as createReconciliationSchema } from "./reconciliation.schema-BA1lPv4t.mjs";
2
+ import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "./journals-BfwnCFam.mjs";
1
3
  import { Money, add, allocate, format, formatPlain, fromDecimal, multiply, parseCents, percentage, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
2
- import { n as Errors, t as AccountingError } from "./errors-BoGUSUYL.mjs";
3
- import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "./date-lock.plugin-C8kqPBjh.mjs";
4
- import { C as DEFAULT_BUCKETS, S as isVirtualTaxAccount, _ as getDateRange, a as defaultLogger, b as calculateTotal, c as buildRevaluationEntry, d as generateGeneralLedger, f as generateDimensionBreakdown, g as buildItemFilters, h as generateBalanceSheet, i as finalizeSession, l as computeRevaluation, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, r as acquireSession, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, v as getFiscalYearStart, w as generateAgedBalance, x as computeEndingBalance, y as buildAccountTypeMap } from "./fiscal-close-DmPV82e4.mjs";
5
- import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-FJlrvzcl.mjs";
6
- import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "./reconciliation.repository-DgJEDVS-.mjs";
7
- import { a as createAccountSchema, i as createBudgetSchema, n as createJournalEntrySchema, r as createFiscalPeriodSchema, t as createReconciliationSchema } from "./reconciliation.schema-KScbsXbY.mjs";
8
- import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "./journals-BcMn71Cq.mjs";
4
+ import { n as Errors, t as AccountingError } from "./errors-B_dyYZc_.mjs";
5
+ import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "./date-lock.plugin-DL6pe24p.mjs";
6
+ import { C as DEFAULT_BUCKETS, S as isVirtualTaxAccount, _ as getDateRange, a as defaultLogger, b as calculateTotal, c as buildRevaluationEntry, d as generateGeneralLedger, f as generateDimensionBreakdown, g as buildItemFilters, h as generateBalanceSheet, i as finalizeSession, l as computeRevaluation, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, r as acquireSession, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, v as getFiscalYearStart, w as generateAgedBalance, x as computeEndingBalance, y as buildAccountTypeMap } from "./fiscal-close-B2_7WMTe.mjs";
7
+ import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-DWogBUgQ.mjs";
8
+ import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "./reconciliation.repository-fPwFKvrk.mjs";
9
9
  import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "./currencies-W8kQAkm0.mjs";
10
10
  import { defineCountryPack } from "./country/index.mjs";
11
11
  import { a as exportToCsv, i as quickbooksFieldMap, r as universalFieldMap, t as flattenJournalEntries } from "./exports-DoGQQtMQ.mjs";
12
12
  import { Schema } from "mongoose";
13
+ import { Repository } from "@classytic/mongokit";
14
+ //#region src/models/factory.ts
15
+ function resolveModelNames(overrides) {
16
+ return {
17
+ account: overrides?.account ?? "Account",
18
+ journalEntry: overrides?.journalEntry ?? "JournalEntry",
19
+ fiscalPeriod: overrides?.fiscalPeriod ?? "FiscalPeriod",
20
+ budget: overrides?.budget ?? "Budget",
21
+ reconciliation: overrides?.reconciliation ?? "Reconciliation"
22
+ };
23
+ }
24
+ /**
25
+ * Create (or reuse) all ledger models on the given connection.
26
+ *
27
+ * If a model with the same name is already registered on the connection,
28
+ * the existing model is reused — this allows multiple engine instances
29
+ * to share models and prevents "OverwriteModelError".
30
+ */
31
+ function createModels(connection, config) {
32
+ const names = resolveModelNames(config.modelNames);
33
+ const so = config.schemaOptions ?? {};
34
+ const existing = connection.models;
35
+ if (existing[names.account] && existing[names.journalEntry] && existing[names.fiscalPeriod] && existing[names.budget] && existing[names.reconciliation]) return {
36
+ Account: existing[names.account],
37
+ JournalEntry: existing[names.journalEntry],
38
+ FiscalPeriod: existing[names.fiscalPeriod],
39
+ Budget: existing[names.budget],
40
+ Reconciliation: existing[names.reconciliation]
41
+ };
42
+ return {
43
+ Account: existing[names.account] ?? connection.model(names.account, createAccountSchema(config, so.account)),
44
+ JournalEntry: existing[names.journalEntry] ?? connection.model(names.journalEntry, createJournalEntrySchema(config, names.account, so.journalEntry)),
45
+ FiscalPeriod: existing[names.fiscalPeriod] ?? connection.model(names.fiscalPeriod, createFiscalPeriodSchema(config, so.fiscalPeriod)),
46
+ Budget: existing[names.budget] ?? connection.model(names.budget, createBudgetSchema(config, so.budget)),
47
+ Reconciliation: existing[names.reconciliation] ?? connection.model(names.reconciliation, createReconciliationSchema(config, names.account, names.journalEntry, so.reconciliation))
48
+ };
49
+ }
50
+ //#endregion
51
+ //#region src/repositories/factory.ts
52
+ /**
53
+ * Repositories Factory — wires fully-configured repositories.
54
+ *
55
+ * Matches the flow/promo pattern: engine owns the repositories with all
56
+ * plugins (double-entry, fiscal-lock, idempotency) pre-wired.
57
+ *
58
+ * Consumer never constructs Repository, never calls wireXxx methods.
59
+ * Just uses `engine.repositories.accounts.seedAccounts(orgId)`.
60
+ */
61
+ /**
62
+ * Build all ledger repositories with plugins + domain methods pre-wired.
63
+ *
64
+ * - `accounts` — has seedAccounts(), bulkCreate()
65
+ * - `journalEntries` — has post(), unpost(), reverse(), duplicate() + double-entry + fiscal-lock (+ idempotency if enabled)
66
+ * - `fiscalPeriods` — plain CRUD
67
+ * - `budgets` — plain CRUD
68
+ * - `reconciliations` — has reconcile(), unreconcile(), getUnreconciled()
69
+ */
70
+ function createRepositories(models, config, plugins = {}, pagination = {}) {
71
+ const orgField = config.multiTenant?.orgField;
72
+ const strictness = config.strictness;
73
+ const country = config.country;
74
+ const accountPagination = pagination.account ?? { maxLimit: 1e3 };
75
+ const jePagination = pagination.journalEntry ?? {};
76
+ const fpPagination = pagination.fiscalPeriod ?? {};
77
+ const budgetPagination = pagination.budget ?? {};
78
+ const reconPagination = pagination.reconciliation ?? {};
79
+ const accounts = wireAccountMethods(new Repository(models.Account, plugins.account ?? [], accountPagination), models.Account, country, orgField);
80
+ const jePlugins = [
81
+ ...plugins.journalEntry ?? [],
82
+ doubleEntryPlugin({
83
+ JournalEntryModel: models.JournalEntry,
84
+ AccountModel: models.Account,
85
+ orgField
86
+ }),
87
+ fiscalLockPlugin({
88
+ FiscalPeriodModel: models.FiscalPeriod,
89
+ JournalEntryModel: models.JournalEntry,
90
+ orgField
91
+ })
92
+ ];
93
+ if (config.idempotency) jePlugins.push(idempotencyPlugin({
94
+ JournalEntryModel: models.JournalEntry,
95
+ orgField
96
+ }));
97
+ return {
98
+ accounts,
99
+ journalEntries: wireJournalEntryMethods(new Repository(models.JournalEntry, jePlugins, jePagination), models.JournalEntry, orgField, strictness),
100
+ fiscalPeriods: new Repository(models.FiscalPeriod, plugins.fiscalPeriod ?? [], fpPagination),
101
+ budgets: new Repository(models.Budget, plugins.budget ?? [], budgetPagination),
102
+ reconciliations: wireReconciliationMethods(new Repository(models.Reconciliation, plugins.reconciliation ?? [], reconPagination), models.Reconciliation, models.JournalEntry, orgField)
103
+ };
104
+ }
105
+ //#endregion
13
106
  //#region src/engine.ts
14
107
  var AccountingEngine = class {
15
108
  config;
16
109
  country;
17
110
  currency;
18
111
  money = Money;
19
- constructor(config) {
112
+ _models;
113
+ _repositories;
114
+ _reports;
115
+ constructor(config, plugins = {}, pagination = {}) {
20
116
  this.config = config;
21
117
  this.country = config.country;
22
118
  this.currency = config.currency;
119
+ if (config.mongoose) {
120
+ this._models = createModels(config.mongoose, config);
121
+ this._repositories = createRepositories(this._models, config, plugins, pagination);
122
+ }
123
+ }
124
+ /**
125
+ * Auto-created Mongoose models. Requires `mongoose` in config.
126
+ *
127
+ * @throws if `mongoose` was not provided in config
128
+ */
129
+ get models() {
130
+ if (!this._models) throw new Error("engine.models requires a Mongoose connection. Pass `mongoose: connection` in createAccountingEngine config.");
131
+ return this._models;
132
+ }
133
+ /**
134
+ * Auto-wired repositories with plugins + domain methods (post, reverse, etc.).
135
+ * Requires `mongoose` in config.
136
+ *
137
+ * @throws if `mongoose` was not provided in config
138
+ */
139
+ get repositories() {
140
+ if (!this._repositories) throw new Error("engine.repositories requires a Mongoose connection. Pass `mongoose: connection` in createAccountingEngine config.");
141
+ return this._repositories;
142
+ }
143
+ /**
144
+ * Pre-built reports bound to auto-created models. Requires `mongoose` in config.
145
+ * For custom models, use `engine.createReports({ Account, JournalEntry, Budget })`.
146
+ */
147
+ get reports() {
148
+ if (!this._reports) {
149
+ if (!this._models) throw new Error("engine.reports requires a Mongoose connection. Pass `mongoose: connection` in createAccountingEngine config, or use engine.createReports({ Account, JournalEntry }) for manual models.");
150
+ this._reports = this._buildReports({
151
+ Account: this._models.Account,
152
+ JournalEntry: this._models.JournalEntry,
153
+ Budget: this._models.Budget
154
+ });
155
+ }
156
+ return this._reports;
23
157
  }
24
158
  createAccountSchema(options) {
25
159
  return createAccountSchema(this.config, options);
@@ -36,7 +170,15 @@ var AccountingEngine = class {
36
170
  createReconciliationSchema(accountModelName, journalEntryModelName, options) {
37
171
  return createReconciliationSchema(this.config, accountModelName, journalEntryModelName, options);
38
172
  }
173
+ /**
174
+ * Build a reports object bound to the given models. Use this when you
175
+ * need custom/external models. Prefer `engine.reports` when the engine
176
+ * owns the models (via `mongoose` in config).
177
+ */
39
178
  createReports(models) {
179
+ return this._buildReports(models);
180
+ }
181
+ _buildReports(models) {
40
182
  const { Account: AccountModel, JournalEntry: JournalEntryModel, Budget: BudgetModel } = models;
41
183
  const { country, config } = this;
42
184
  const orgField = config.multiTenant?.orgField;
@@ -202,8 +344,8 @@ var AccountingEngine = class {
202
344
  return wireReconciliationMethods(repository, ReconciliationModel, JournalEntryModel, orgField);
203
345
  }
204
346
  };
205
- function createAccountingEngine(config) {
206
- return new AccountingEngine(config);
347
+ function createAccountingEngine(config, plugins = {}, pagination = {}) {
348
+ return new AccountingEngine(config, plugins, pagination);
207
349
  }
208
350
  //#endregion
209
351
  //#region src/utils/dimensions.ts
@@ -265,4 +407,4 @@ function buildDimensionIndexes(dimensions, orgField) {
265
407
  });
266
408
  }
267
409
  //#endregion
268
- export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, DEFAULT_BUCKETS, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, dateLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
410
+ export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, DEFAULT_BUCKETS, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, createModels, createRepositories, dateLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
@@ -1,4 +1,4 @@
1
- import { o as SchemaOptions, r as JournalSchemaOptions, t as AccountingEngineConfig } from "./engine-DF-MtsEr.mjs";
1
+ import { r as JournalSchemaOptions, s as SchemaOptions, t as AccountingEngineConfig } from "./engine-scgOvxHJ.mjs";
2
2
  import mongoose from "mongoose";
3
3
 
4
4
  //#region src/schemas/account.schema.d.ts
@@ -1,4 +1,4 @@
1
- import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "../date-lock.plugin-C8kqPBjh.mjs";
1
+ import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "../date-lock.plugin-DL6pe24p.mjs";
2
2
  //#region src/utils/tax-hooks.ts
3
3
  /**
4
4
  * Apply a tax hook to journal items.
@@ -1,5 +1,5 @@
1
1
  import { t as CountryPack } from "./index-CxZqRaOU.mjs";
2
- import { s as StrictnessConfig } from "./engine-DF-MtsEr.mjs";
2
+ import { c as StrictnessConfig } from "./engine-scgOvxHJ.mjs";
3
3
  import { ClientSession, Model } from "mongoose";
4
4
  import { Repository } from "@classytic/mongokit";
5
5
 
@@ -1,5 +1,5 @@
1
- import { n as Errors } from "./errors-BoGUSUYL.mjs";
2
- import { t as requireOrgScope } from "./tenant-guard-CAxXoWuS.mjs";
1
+ import { n as Errors } from "./errors-B_dyYZc_.mjs";
2
+ import { t as requireOrgScope } from "./tenant-guard-r17Se3Bb.mjs";
3
3
  //#region src/repositories/account.repository.ts
4
4
  /**
5
5
  * Wire seedAccounts, bulkCreate and posting-account validation
@@ -1,4 +1,4 @@
1
- import { o as getJournalTypeCodes, r as _freezeJournalTypes, t as JOURNAL_CODES } from "./journals-BcMn71Cq.mjs";
1
+ import { o as getJournalTypeCodes, r as _freezeJournalTypes, t as JOURNAL_CODES } from "./journals-BfwnCFam.mjs";
2
2
  import mongoose from "mongoose";
3
3
  //#region src/schemas/currency-field.ts
4
4
  /**
@@ -1,2 +1,2 @@
1
- import { C as DEFAULT_BUCKETS, d as generateGeneralLedger, f as generateDimensionBreakdown, h as generateBalanceSheet, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, w as generateAgedBalance } from "../fiscal-close-DmPV82e4.mjs";
1
+ import { C as DEFAULT_BUCKETS, d as generateGeneralLedger, f as generateDimensionBreakdown, h as generateBalanceSheet, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, w as generateAgedBalance } from "../fiscal-close-B2_7WMTe.mjs";
2
2
  export { DEFAULT_BUCKETS, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
@@ -1,2 +1,2 @@
1
- import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "../reconciliation.repository-DEybU_Ok.mjs";
1
+ import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "../reconciliation.repository-D-D_ITL-.mjs";
2
2
  export { wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
@@ -1,2 +1,2 @@
1
- import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "../reconciliation.repository-DgJEDVS-.mjs";
1
+ import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "../reconciliation.repository-fPwFKvrk.mjs";
2
2
  export { wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
@@ -1,5 +1,5 @@
1
- import { o as SchemaOptions, t as AccountingEngineConfig } from "../engine-DF-MtsEr.mjs";
2
- import { n as createFiscalPeriodSchema, r as createAccountSchema, t as createJournalEntrySchema } from "../journal-entry.schema-B1CzLwC3.mjs";
1
+ import { s as SchemaOptions, t as AccountingEngineConfig } from "../engine-scgOvxHJ.mjs";
2
+ import { n as createFiscalPeriodSchema, r as createAccountSchema, t as createJournalEntrySchema } from "../journal-entry.schema-JqrfbvB4.mjs";
3
3
  import mongoose from "mongoose";
4
4
 
5
5
  //#region src/schemas/budget.schema.d.ts
@@ -1,2 +1,2 @@
1
- import { a as createAccountSchema, i as createBudgetSchema, n as createJournalEntrySchema, r as createFiscalPeriodSchema, t as createReconciliationSchema } from "../reconciliation.schema-KScbsXbY.mjs";
1
+ import { a as createAccountSchema, i as createBudgetSchema, n as createJournalEntrySchema, r as createFiscalPeriodSchema, t as createReconciliationSchema } from "../reconciliation.schema-BA1lPv4t.mjs";
2
2
  export { createAccountSchema, createBudgetSchema, createFiscalPeriodSchema, createJournalEntrySchema, createReconciliationSchema };
@@ -1,4 +1,4 @@
1
- import { n as Errors } from "./errors-BoGUSUYL.mjs";
1
+ import { n as Errors } from "./errors-B_dyYZc_.mjs";
2
2
  //#region src/utils/tenant-guard.ts
3
3
  /**
4
4
  * Multi-tenant scope guard.
package/docs/schemas.md CHANGED
@@ -52,8 +52,8 @@ const JournalEntry = mongoose.model('JournalEntry', JournalEntrySchema);
52
52
 
53
53
  | Field | Type | Required | Description |
54
54
  |---|---|---|---|
55
- | `journalType` | String | Yes | One of: `GENERAL`, `SALES`, `PURCHASE`, `CASH_RECEIPT`, `CASH_DISBURSEMENT`, `PAYROLL`, `ADJUSTMENT`, `YEAR_END`, `MISC` |
56
- | `referenceNumber` | String | Auto | Auto-generated (e.g. `GJ-0001`). Unique per org |
55
+ | `journalType` | String | Yes | Built-in: `SALES`, `PURCHASES`, `CASH_RECEIPTS`, `CASH_PAYMENTS`, `PAYROLL`, `GENERAL`, `INVENTORY`, `FIXED_ASSETS`, `BANK_RECONCILIATION`, `DEPRECIATION`, `YEAR_END`, `ACCOUNTS_RECEIVABLE`, `ACCOUNTS_PAYABLE`, `TAX`, `MISC`. Extensible via `registerJournalType()` before schema creation. |
56
+ | `referenceNumber` | String | Auto | Auto-generated as `{JOURNAL_TYPE}/{YYYY}/{MM}/{NNNN}` (e.g. `SALES/2025/03/0001`). Unique per org. Sequence increments per journal type per month. |
57
57
  | `label` | String | No | Description/memo |
58
58
  | `date` | Date | No | Defaults to `new Date()` |
59
59
  | `state` | String | No | `draft` (default), `posted`, or `archived` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/ledger",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Production-grade double-entry accounting engine for MongoDB — schemas, reports, tax, multi-tenant",
5
5
  "type": "module",
6
6
  "sideEffects": false,