@classytic/ledger 0.9.1 → 0.10.1
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/dist/bridges/index.d.mts +1 -1
- package/dist/{errors-BI5k4iak.mjs → errors-vXd932rB.mjs} +1 -1
- package/dist/events/index.d.mts +3 -2
- package/dist/events/index.mjs +2 -2
- package/dist/{fx-realization.plugin-Bxlb8cIx.mjs → fx-realization.plugin-Dzqzi3u0.mjs} +1 -1
- package/dist/{index-Dih0lM65.d.mts → index-Bl0gP9lD.d.mts} +3 -3
- package/dist/index.d.mts +93 -73
- package/dist/index.mjs +222 -214
- package/dist/outbox-store-BbKdQ2eT.mjs +253 -0
- package/dist/outbox-store-BcCiHMPw.d.mts +305 -0
- package/dist/{partner-ledger-BoebloHk.mjs → partner-ledger-CR0geilx.mjs} +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/reports/index.mjs +1 -1
- package/dist/sync/index.d.mts +13 -2
- package/dist/sync/index.mjs +6 -3
- package/package.json +12 -6
- package/dist/outbox-store-DQbL-KYT.mjs +0 -132
- package/dist/outbox-store-UYC4eZpI.d.mts +0 -249
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as IdempotencyConflictError, i as Errors, n as ConcurrencyError, o as ImmutableViolationError, r as DuplicateReferenceError, s as classifyDuplicateKey, t as AccountingError } from "./errors-
|
|
1
|
+
import { _ as LEDGER_EVENTS, a as EntryArchived, c as EntryPosted, d as JournalSeeded, f as ReconciliationMatched, g as createEvent, h as InProcessLedgerBus, i as AccountSeeded, l as EntryReversed, m as ledgerEventDefinitions, n as OutboxOwnershipError, o as EntryCreated, p as ReconciliationUnmatched, r as AccountBulkCreated, s as EntryDuplicated, t as InvalidOutboxEventError, u as EntryUnposted } from "./outbox-store-BbKdQ2eT.mjs";
|
|
2
|
+
import { a as IdempotencyConflictError, i as Errors, n as ConcurrencyError, o as ImmutableViolationError, r as DuplicateReferenceError, s as classifyDuplicateKey, t as AccountingError } from "./errors-vXd932rB.mjs";
|
|
3
3
|
import { a as JOURNAL_CODES, c as getCustomJournalTypes, d as isValidJournalType, f as registerJournalType, i as isValidCurrency, l as getJournalType, n as getCurrency, o as JOURNAL_TYPES, r as getMinorUnit, s as _freezeJournalTypes, t as CURRENCIES, u as getJournalTypeCodes } from "./currencies-Jo5oaM_4.mjs";
|
|
4
4
|
import { Money, add, allocate, format, formatPlain, fromDecimal, multiply, parseCents, percentage, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
|
|
5
|
-
import { C as isVirtualTaxAccount, E as requireOrgScope, S as computeEndingBalance, T as generateAgedBalance, _ as buildItemFilters, a as finalizeSession, b as buildAccountTypeMap, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, i as acquireSession, l as buildRevaluationEntry, m as generateCashFlow, n as closeFiscalPeriod, o as defaultLogger, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, u as computeRevaluation, v as getDateRange, w as DEFAULT_BUCKETS, x as calculateTotal, y as getFiscalYearStart } from "./partner-ledger-
|
|
5
|
+
import { C as isVirtualTaxAccount, E as requireOrgScope, S as computeEndingBalance, T as generateAgedBalance, _ as buildItemFilters, a as finalizeSession, b as buildAccountTypeMap, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, i as acquireSession, l as buildRevaluationEntry, m as generateCashFlow, n as closeFiscalPeriod, o as defaultLogger, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, u as computeRevaluation, v as getDateRange, w as DEFAULT_BUCKETS, x as calculateTotal, y as getFiscalYearStart } from "./partner-ledger-CR0geilx.mjs";
|
|
6
6
|
import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-FJlrvzcl.mjs";
|
|
7
|
-
import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "./fx-realization.plugin-
|
|
7
|
+
import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "./fx-realization.plugin-Dzqzi3u0.mjs";
|
|
8
8
|
import { t as buildOpeningBalanceEntry } from "./opening-balance-1cixYh6Y.mjs";
|
|
9
9
|
import { defineCountryPack } from "./country/index.mjs";
|
|
10
10
|
import { a as exportToCsv, i as quickbooksFieldMap, r as universalFieldMap, t as flattenJournalEntries } from "./exports-C30yRapf.mjs";
|
|
11
|
-
import { QueryParser, Repository, getNextSequence, multiTenantPlugin } from "@classytic/mongokit";
|
|
11
|
+
import { QueryParser, Repository, getNextSequence, multiTenantPlugin, withTransaction } from "@classytic/mongokit";
|
|
12
12
|
import mongoose, { Schema } from "mongoose";
|
|
13
|
+
import { resolveTenantConfig } from "@classytic/primitives/tenant";
|
|
13
14
|
//#region src/plugins/immutable-guard.plugin.ts
|
|
14
15
|
/**
|
|
15
16
|
* Returns a mongokit plugin function. Install only when
|
|
@@ -37,6 +38,77 @@ function immutableGuardPlugin(options) {
|
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
//#endregion
|
|
41
|
+
//#region src/models/inject-tenant.ts
|
|
42
|
+
/**
|
|
43
|
+
* Mongoose-specific adapter around `resolveTenantConfig()` from
|
|
44
|
+
* `@classytic/primitives/tenant`. The pure resolution lives in primitives
|
|
45
|
+
* (zero runtime deps) — this file only handles the Mongoose schema
|
|
46
|
+
* mutations (add field, prepend tenant onto compound indexes) that
|
|
47
|
+
* primitives can't own without a mongoose dependency.
|
|
48
|
+
*
|
|
49
|
+
* Prior to consolidation, each ledger schema (account, budget,
|
|
50
|
+
* fiscal-period, journal, journal-entry, reconciliation) inlined the same
|
|
51
|
+
* `if (multiTenant) { fields[multiTenant.tenantField] = { type: ObjectId,
|
|
52
|
+
* ref, required: true } }` block. That logic now lives here — schemas call
|
|
53
|
+
* `injectTenantField(schema, scope)` where `scope` is a
|
|
54
|
+
* `ResolvedTenantConfig` produced by `resolveLedgerTenant()`.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Translate ledger's `AccountingEngineConfig` into a `ResolvedTenantConfig`.
|
|
58
|
+
*
|
|
59
|
+
* Ledger's `MultiTenantConfig` tightens primitives' optionals (`tenantField`
|
|
60
|
+
* and `ref` are required in ledger — no default org-collection name) and
|
|
61
|
+
* carries a ledger-specific `plugin` knob for `multiTenantPlugin` adoption
|
|
62
|
+
* that primitives doesn't own. We merge those with `config.tenantFieldType`
|
|
63
|
+
* (engine-level field-type override) and produce a pure
|
|
64
|
+
* `ResolvedTenantConfig` suitable for `injectTenantField()`.
|
|
65
|
+
*
|
|
66
|
+
* When `config.multiTenant` is absent, scoping is disabled (strategy =
|
|
67
|
+
* `'none'`) and no tenant field is added to schemas.
|
|
68
|
+
*/
|
|
69
|
+
function resolveLedgerTenant(config) {
|
|
70
|
+
const mt = config.multiTenant;
|
|
71
|
+
if (!mt) return resolveTenantConfig(false);
|
|
72
|
+
return resolveTenantConfig({
|
|
73
|
+
enabled: true,
|
|
74
|
+
strategy: "field",
|
|
75
|
+
tenantField: mt.tenantField,
|
|
76
|
+
ref: mt.ref,
|
|
77
|
+
fieldType: config.tenantFieldType ?? "objectId",
|
|
78
|
+
required: true
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Inject the tenant field into a Mongoose schema and (when
|
|
83
|
+
* `strategy === 'field'` is active) prepend the tenant field onto every
|
|
84
|
+
* existing compound index so queries are index-efficient under multi-tenant
|
|
85
|
+
* scoping. Matches the order/people/revenue pattern (PACKAGE_RULES §9.2).
|
|
86
|
+
*
|
|
87
|
+
* Schemas should declare their compound indexes WITHOUT a tenant prefix
|
|
88
|
+
* and then call this helper once — the prefix is added here.
|
|
89
|
+
*/
|
|
90
|
+
function injectTenantField(schema, scope) {
|
|
91
|
+
const isFieldStrategy = scope.strategy === "field";
|
|
92
|
+
const isObjectId = scope.fieldType === "objectId";
|
|
93
|
+
if (isFieldStrategy) {
|
|
94
|
+
const fieldDef = {
|
|
95
|
+
type: isObjectId ? mongoose.Schema.Types.ObjectId : String,
|
|
96
|
+
...scope.enabled && scope.required ? { required: true } : {}
|
|
97
|
+
};
|
|
98
|
+
if (isObjectId && scope.ref) fieldDef.ref = scope.ref;
|
|
99
|
+
schema.add({ [scope.tenantField]: fieldDef });
|
|
100
|
+
}
|
|
101
|
+
if (!scope.enabled || !isFieldStrategy) return;
|
|
102
|
+
const existingIndexes = schema._indexes;
|
|
103
|
+
if (existingIndexes && existingIndexes.length > 0) for (const indexEntry of existingIndexes) {
|
|
104
|
+
const fields = indexEntry[0];
|
|
105
|
+
if (fields[scope.tenantField] !== void 0) continue;
|
|
106
|
+
const newFields = { [scope.tenantField]: 1 };
|
|
107
|
+
for (const [key, val] of Object.entries(fields)) newFields[key] = val;
|
|
108
|
+
indexEntry[0] = newFields;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
40
112
|
//#region src/schemas/currency-field.ts
|
|
41
113
|
/**
|
|
42
114
|
* Build the Mongoose currency field definition.
|
|
@@ -67,7 +139,8 @@ function buildCurrencyField(config) {
|
|
|
67
139
|
* - Lean: no cached balances — always computed from journal entries
|
|
68
140
|
*/
|
|
69
141
|
function createAccountSchema(config, options = {}) {
|
|
70
|
-
const {
|
|
142
|
+
const { country } = config;
|
|
143
|
+
const scope = resolveLedgerTenant(config);
|
|
71
144
|
const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
|
|
72
145
|
const fields = {
|
|
73
146
|
accountTypeCode: {
|
|
@@ -100,36 +173,21 @@ function createAccountSchema(config, options = {}) {
|
|
|
100
173
|
const currencyField = buildCurrencyField(config);
|
|
101
174
|
if (currencyField) fields.currency = currencyField;
|
|
102
175
|
Object.assign(fields, extraFields);
|
|
103
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
104
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
105
|
-
ref: multiTenant.orgRef,
|
|
106
|
-
required: true
|
|
107
|
-
};
|
|
108
176
|
const schema = new mongoose.Schema(fields, { timestamps: true });
|
|
109
177
|
schema.pre("validate", function() {
|
|
110
178
|
if (!this.accountNumber && this.accountTypeCode) this.accountNumber = this.accountTypeCode;
|
|
111
|
-
if (!this.name && this.accountTypeCode)
|
|
179
|
+
if (!this.name && this.accountTypeCode) {
|
|
180
|
+
const at = country.getAccountType(this.accountTypeCode);
|
|
181
|
+
this.name = at?.name ?? this.accountTypeCode;
|
|
182
|
+
}
|
|
112
183
|
});
|
|
113
|
-
if (indexes)
|
|
114
|
-
const org = multiTenant.orgField;
|
|
115
|
-
schema.index({
|
|
116
|
-
[org]: 1,
|
|
117
|
-
active: 1
|
|
118
|
-
});
|
|
119
|
-
schema.index({
|
|
120
|
-
[org]: 1,
|
|
121
|
-
accountNumber: 1
|
|
122
|
-
}, { unique: true });
|
|
123
|
-
schema.index({
|
|
124
|
-
[org]: 1,
|
|
125
|
-
accountTypeCode: 1
|
|
126
|
-
});
|
|
127
|
-
} else {
|
|
184
|
+
if (indexes) {
|
|
128
185
|
schema.index({ active: 1 });
|
|
129
186
|
schema.index({ accountNumber: 1 }, { unique: true });
|
|
130
187
|
schema.index({ accountTypeCode: 1 });
|
|
131
188
|
}
|
|
132
189
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
190
|
+
injectTenantField(schema, scope);
|
|
133
191
|
return schema;
|
|
134
192
|
}
|
|
135
193
|
//#endregion
|
|
@@ -142,7 +200,7 @@ function createAccountSchema(config, options = {}) {
|
|
|
142
200
|
* All monetary amounts are in integer cents.
|
|
143
201
|
*/
|
|
144
202
|
function createBudgetSchema(config, options = {}) {
|
|
145
|
-
const
|
|
203
|
+
const scope = resolveLedgerTenant(config);
|
|
146
204
|
const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
|
|
147
205
|
const fields = {
|
|
148
206
|
account: {
|
|
@@ -172,30 +230,12 @@ function createBudgetSchema(config, options = {}) {
|
|
|
172
230
|
},
|
|
173
231
|
...extraFields
|
|
174
232
|
};
|
|
175
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
176
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
177
|
-
ref: multiTenant.orgRef,
|
|
178
|
-
required: true
|
|
179
|
-
};
|
|
180
233
|
const schema = new mongoose.Schema(fields, { timestamps: true });
|
|
181
234
|
schema.pre("validate", function() {
|
|
182
235
|
const doc = this;
|
|
183
236
|
if (doc.periodStart && doc.periodEnd && doc.periodEnd <= doc.periodStart) doc.invalidate("periodEnd", "periodEnd must be after periodStart.", doc.periodEnd, "periodEnd");
|
|
184
237
|
});
|
|
185
|
-
if (indexes)
|
|
186
|
-
const org = multiTenant.orgField;
|
|
187
|
-
schema.index({
|
|
188
|
-
[org]: 1,
|
|
189
|
-
account: 1,
|
|
190
|
-
periodStart: 1,
|
|
191
|
-
periodEnd: 1
|
|
192
|
-
}, { unique: true });
|
|
193
|
-
schema.index({
|
|
194
|
-
[org]: 1,
|
|
195
|
-
periodStart: 1,
|
|
196
|
-
periodEnd: 1
|
|
197
|
-
});
|
|
198
|
-
} else {
|
|
238
|
+
if (indexes) {
|
|
199
239
|
schema.index({
|
|
200
240
|
account: 1,
|
|
201
241
|
periodStart: 1,
|
|
@@ -207,6 +247,7 @@ function createBudgetSchema(config, options = {}) {
|
|
|
207
247
|
});
|
|
208
248
|
}
|
|
209
249
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
250
|
+
injectTenantField(schema, scope);
|
|
210
251
|
return schema;
|
|
211
252
|
}
|
|
212
253
|
//#endregion
|
|
@@ -219,6 +260,7 @@ function createBudgetSchema(config, options = {}) {
|
|
|
219
260
|
*/
|
|
220
261
|
function createFiscalPeriodSchema(config, options = {}) {
|
|
221
262
|
const { multiTenant } = config;
|
|
263
|
+
const scope = resolveLedgerTenant(config);
|
|
222
264
|
const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
|
|
223
265
|
const fields = {
|
|
224
266
|
name: {
|
|
@@ -259,24 +301,8 @@ function createFiscalPeriodSchema(config, options = {}) {
|
|
|
259
301
|
},
|
|
260
302
|
...extraFields
|
|
261
303
|
};
|
|
262
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
263
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
264
|
-
ref: multiTenant.orgRef,
|
|
265
|
-
required: true
|
|
266
|
-
};
|
|
267
304
|
const schema = new mongoose.Schema(fields, { timestamps: true });
|
|
268
|
-
if (indexes)
|
|
269
|
-
const org = multiTenant.orgField;
|
|
270
|
-
schema.index({
|
|
271
|
-
[org]: 1,
|
|
272
|
-
startDate: 1,
|
|
273
|
-
endDate: 1
|
|
274
|
-
}, { unique: true });
|
|
275
|
-
schema.index({
|
|
276
|
-
[org]: 1,
|
|
277
|
-
closed: 1
|
|
278
|
-
});
|
|
279
|
-
} else {
|
|
305
|
+
if (indexes) {
|
|
280
306
|
schema.index({
|
|
281
307
|
startDate: 1,
|
|
282
308
|
endDate: 1
|
|
@@ -284,6 +310,7 @@ function createFiscalPeriodSchema(config, options = {}) {
|
|
|
284
310
|
schema.index({ closed: 1 });
|
|
285
311
|
}
|
|
286
312
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
313
|
+
injectTenantField(schema, scope);
|
|
287
314
|
schema.pre("validate", async function() {
|
|
288
315
|
const doc = this;
|
|
289
316
|
if (!doc.startDate || !doc.endDate) return;
|
|
@@ -292,7 +319,7 @@ function createFiscalPeriodSchema(config, options = {}) {
|
|
|
292
319
|
startDate: { $lt: doc.endDate },
|
|
293
320
|
endDate: { $gt: doc.startDate }
|
|
294
321
|
};
|
|
295
|
-
if (multiTenant) overlapQuery[multiTenant.
|
|
322
|
+
if (multiTenant) overlapQuery[multiTenant.tenantField] = doc[multiTenant.tenantField];
|
|
296
323
|
const overlap = await doc.collection.findOne(overlapQuery);
|
|
297
324
|
if (overlap) {
|
|
298
325
|
const msg = `Fiscal period overlaps with existing period "${overlap.name}" (${new Date(overlap.startDate).toISOString().split("T")[0]} – ${new Date(overlap.endDate).toISOString().split("T")[0]}).`;
|
|
@@ -324,23 +351,33 @@ function createFiscalPeriodSchema(config, options = {}) {
|
|
|
324
351
|
* `engine.repositories.journals.seedDefaults(orgId)`.
|
|
325
352
|
*/
|
|
326
353
|
function createJournalSchema(config, accountModelName, options = {}) {
|
|
327
|
-
const
|
|
354
|
+
const scope = resolveLedgerTenant(config);
|
|
328
355
|
const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
|
|
329
356
|
const fields = {
|
|
357
|
+
/** Short stable identifier — e.g. `'SALES'`, `'BANK'`. */
|
|
330
358
|
code: {
|
|
331
359
|
type: String,
|
|
332
360
|
required: true,
|
|
333
361
|
trim: true
|
|
334
362
|
},
|
|
363
|
+
/** Display name. */
|
|
335
364
|
name: {
|
|
336
365
|
type: String,
|
|
337
366
|
required: true,
|
|
338
367
|
trim: true
|
|
339
368
|
},
|
|
369
|
+
/**
|
|
370
|
+
* One of the registered `JOURNAL_TYPES` codes. Connects this journal
|
|
371
|
+
* to the engine's posting contracts and reference-number generator.
|
|
372
|
+
*/
|
|
340
373
|
journalType: {
|
|
341
374
|
type: String,
|
|
342
375
|
required: true
|
|
343
376
|
},
|
|
377
|
+
/**
|
|
378
|
+
* Logical source — drives future lock-date buckets (sale-lock-date,
|
|
379
|
+
* purchase-lock-date) and bank-statement import wiring.
|
|
380
|
+
*/
|
|
344
381
|
kind: {
|
|
345
382
|
type: String,
|
|
346
383
|
enum: [
|
|
@@ -354,15 +391,18 @@ function createJournalSchema(config, accountModelName, options = {}) {
|
|
|
354
391
|
default: "general",
|
|
355
392
|
required: true
|
|
356
393
|
},
|
|
394
|
+
/** Reference-number prefix — defaults to `code` when omitted. */
|
|
357
395
|
sequencePrefix: {
|
|
358
396
|
type: String,
|
|
359
397
|
default: null
|
|
360
398
|
},
|
|
399
|
+
/** Next sequence number (monotonic within this journal). */
|
|
361
400
|
sequenceNextNum: {
|
|
362
401
|
type: Number,
|
|
363
402
|
default: 1,
|
|
364
403
|
min: 1
|
|
365
404
|
},
|
|
405
|
+
/** Optional default debit/credit account for quick data entry. */
|
|
366
406
|
defaultDebitAccount: {
|
|
367
407
|
type: mongoose.Schema.Types.ObjectId,
|
|
368
408
|
ref: accountModelName,
|
|
@@ -373,10 +413,15 @@ function createJournalSchema(config, accountModelName, options = {}) {
|
|
|
373
413
|
ref: accountModelName,
|
|
374
414
|
default: null
|
|
375
415
|
},
|
|
416
|
+
/** Free-form payment-method identifiers allowed on entries in this journal. */
|
|
376
417
|
allowedPaymentMethods: {
|
|
377
418
|
type: [String],
|
|
378
419
|
default: []
|
|
379
420
|
},
|
|
421
|
+
/**
|
|
422
|
+
* Opaque source id — `'manual'`, `'stripe'`, bank connector id, etc.
|
|
423
|
+
* Used by bank-statement and external-integration plugins.
|
|
424
|
+
*/
|
|
380
425
|
source: {
|
|
381
426
|
type: String,
|
|
382
427
|
default: "manual"
|
|
@@ -387,33 +432,16 @@ function createJournalSchema(config, accountModelName, options = {}) {
|
|
|
387
432
|
},
|
|
388
433
|
...extraFields
|
|
389
434
|
};
|
|
390
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
391
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
392
|
-
ref: multiTenant.orgRef,
|
|
393
|
-
required: true
|
|
394
|
-
};
|
|
395
435
|
const schema = new mongoose.Schema(fields, { timestamps: true });
|
|
396
436
|
if (indexes) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}, { unique: true });
|
|
403
|
-
schema.index({
|
|
404
|
-
[org]: 1,
|
|
405
|
-
kind: 1,
|
|
406
|
-
active: 1
|
|
407
|
-
});
|
|
408
|
-
} else {
|
|
409
|
-
schema.index({ code: 1 }, { unique: true });
|
|
410
|
-
schema.index({
|
|
411
|
-
kind: 1,
|
|
412
|
-
active: 1
|
|
413
|
-
});
|
|
414
|
-
}
|
|
437
|
+
schema.index({ code: 1 }, { unique: true });
|
|
438
|
+
schema.index({
|
|
439
|
+
kind: 1,
|
|
440
|
+
active: 1
|
|
441
|
+
});
|
|
415
442
|
}
|
|
416
443
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
444
|
+
injectTenantField(schema, scope);
|
|
417
445
|
return schema;
|
|
418
446
|
}
|
|
419
447
|
//#endregion
|
|
@@ -431,6 +459,7 @@ function createJournalSchema(config, accountModelName, options = {}) {
|
|
|
431
459
|
*/
|
|
432
460
|
function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
433
461
|
const { multiTenant } = config;
|
|
462
|
+
const scope = resolveLedgerTenant(config);
|
|
434
463
|
const { indexes = true, autoReference = true, textSearch = true, extraFields = {}, extraIndexes = [], extraItemFields = {} } = options;
|
|
435
464
|
const TaxDetailSchema = new mongoose.Schema({
|
|
436
465
|
taxCode: { type: String },
|
|
@@ -603,11 +632,6 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
603
632
|
type: String,
|
|
604
633
|
default: null
|
|
605
634
|
};
|
|
606
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
607
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
608
|
-
ref: multiTenant.orgRef,
|
|
609
|
-
required: true
|
|
610
|
-
};
|
|
611
635
|
const schema = new mongoose.Schema(fields, {
|
|
612
636
|
timestamps: true,
|
|
613
637
|
optimisticConcurrency: true
|
|
@@ -640,7 +664,7 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
640
664
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
641
665
|
let orgScope = "global";
|
|
642
666
|
if (multiTenant) {
|
|
643
|
-
const raw = this.get(multiTenant.
|
|
667
|
+
const raw = this.get(multiTenant.tenantField);
|
|
644
668
|
if (raw != null) orgScope = typeof raw.toHexString === "function" ? raw.toHexString() : String(raw);
|
|
645
669
|
else orgScope = "unscoped";
|
|
646
670
|
}
|
|
@@ -649,72 +673,38 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
649
673
|
}
|
|
650
674
|
});
|
|
651
675
|
if (indexes) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
unique: true,
|
|
663
|
-
...refPartial
|
|
664
|
-
});
|
|
665
|
-
schema.index({
|
|
666
|
-
[org]: 1,
|
|
667
|
-
state: 1,
|
|
668
|
-
date: 1
|
|
669
|
-
});
|
|
670
|
-
schema.index({
|
|
671
|
-
[org]: 1,
|
|
672
|
-
date: -1
|
|
673
|
-
});
|
|
674
|
-
schema.index({
|
|
675
|
-
[org]: 1,
|
|
676
|
-
journalType: 1
|
|
677
|
-
});
|
|
678
|
-
schema.index({
|
|
679
|
-
"journalItems.account": 1,
|
|
680
|
-
state: 1
|
|
681
|
-
});
|
|
682
|
-
schema.index({
|
|
683
|
-
[org]: 1,
|
|
684
|
-
"journalItems.account": 1,
|
|
685
|
-
date: 1,
|
|
686
|
-
state: 1
|
|
687
|
-
});
|
|
688
|
-
} else {
|
|
689
|
-
schema.index({ referenceNumber: 1 }, {
|
|
690
|
-
unique: true,
|
|
691
|
-
...refPartial
|
|
692
|
-
});
|
|
693
|
-
schema.index({
|
|
694
|
-
state: 1,
|
|
695
|
-
date: 1
|
|
696
|
-
});
|
|
697
|
-
schema.index({ date: -1 });
|
|
698
|
-
schema.index({ journalType: 1 });
|
|
699
|
-
schema.index({
|
|
700
|
-
"journalItems.account": 1,
|
|
701
|
-
state: 1
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
schema.index({ reversed: 1 });
|
|
705
|
-
if (org) schema.index({
|
|
706
|
-
[org]: 1,
|
|
707
|
-
"journalItems.matchingNumber": 1
|
|
676
|
+
schema.index({ referenceNumber: 1 }, {
|
|
677
|
+
unique: true,
|
|
678
|
+
partialFilterExpression: { referenceNumber: {
|
|
679
|
+
$exists: true,
|
|
680
|
+
$type: "string"
|
|
681
|
+
} }
|
|
682
|
+
});
|
|
683
|
+
schema.index({
|
|
684
|
+
state: 1,
|
|
685
|
+
date: 1
|
|
708
686
|
});
|
|
709
|
-
|
|
687
|
+
schema.index({ date: -1 });
|
|
688
|
+
schema.index({ journalType: 1 });
|
|
689
|
+
if (scope.enabled) schema.index({
|
|
690
|
+
"journalItems.account": 1,
|
|
691
|
+
date: 1,
|
|
692
|
+
state: 1
|
|
693
|
+
});
|
|
694
|
+
schema.index({ "journalItems.matchingNumber": 1 });
|
|
695
|
+
if (config.idempotency) schema.index({ idempotencyKey: 1 }, {
|
|
696
|
+
unique: true,
|
|
697
|
+
partialFilterExpression: { idempotencyKey: { $type: "string" } }
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
injectTenantField(schema, scope);
|
|
701
|
+
if (indexes) {
|
|
702
|
+
schema.index({
|
|
703
|
+
"journalItems.account": 1,
|
|
704
|
+
state: 1
|
|
705
|
+
});
|
|
706
|
+
schema.index({ reversed: 1 });
|
|
710
707
|
if (config.idempotency) {
|
|
711
|
-
const idempotencyIdx = {};
|
|
712
|
-
if (org) idempotencyIdx[org] = 1;
|
|
713
|
-
idempotencyIdx.idempotencyKey = 1;
|
|
714
|
-
schema.index(idempotencyIdx, {
|
|
715
|
-
unique: true,
|
|
716
|
-
partialFilterExpression: { idempotencyKey: { $type: "string" } }
|
|
717
|
-
});
|
|
718
708
|
const ttlSeconds = typeof config.idempotencyTtlSeconds === "number" && config.idempotencyTtlSeconds > 0 ? config.idempotencyTtlSeconds : 86400;
|
|
719
709
|
schema.index({ createdAt: 1 }, {
|
|
720
710
|
name: "idempotency_ttl_idx",
|
|
@@ -758,7 +748,7 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
758
748
|
* `updatedAt` timestamps.
|
|
759
749
|
*/
|
|
760
750
|
function createReconciliationSchema(config, accountModelName, journalEntryModelName, options = {}) {
|
|
761
|
-
const
|
|
751
|
+
const scope = resolveLedgerTenant(config);
|
|
762
752
|
const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
|
|
763
753
|
const MatchedItemRefSchema = new mongoose.Schema({
|
|
764
754
|
entry: {
|
|
@@ -775,16 +765,19 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
775
765
|
message: "itemIndex must be a non-negative integer"
|
|
776
766
|
}
|
|
777
767
|
},
|
|
768
|
+
/** Snapshot of the debit side of the item in cents, for audit. */
|
|
778
769
|
debit: {
|
|
779
770
|
type: Number,
|
|
780
771
|
default: 0,
|
|
781
772
|
min: 0
|
|
782
773
|
},
|
|
774
|
+
/** Snapshot of the credit side of the item in cents, for audit. */
|
|
783
775
|
credit: {
|
|
784
776
|
type: Number,
|
|
785
777
|
default: 0,
|
|
786
778
|
min: 0
|
|
787
779
|
},
|
|
780
|
+
/** Optional snapshot of the item's foreign amount for FX realization. */
|
|
788
781
|
amountCurrency: {
|
|
789
782
|
type: Number,
|
|
790
783
|
default: null
|
|
@@ -795,6 +788,10 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
795
788
|
}
|
|
796
789
|
}, { _id: false });
|
|
797
790
|
const fields = {
|
|
791
|
+
/**
|
|
792
|
+
* Stable identifier shared by every matched item, also stamped onto
|
|
793
|
+
* `journalItems[i].matchingNumber` for cheap open-item lookups.
|
|
794
|
+
*/
|
|
798
795
|
matchingNumber: {
|
|
799
796
|
type: String,
|
|
800
797
|
required: true
|
|
@@ -822,6 +819,7 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
822
819
|
required: true,
|
|
823
820
|
min: 0
|
|
824
821
|
},
|
|
822
|
+
/** `debitTotal - creditTotal` in cents — zero ⇒ full reconcile. */
|
|
825
823
|
difference: {
|
|
826
824
|
type: Number,
|
|
827
825
|
default: 0
|
|
@@ -830,6 +828,11 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
830
828
|
type: Boolean,
|
|
831
829
|
default: false
|
|
832
830
|
},
|
|
831
|
+
/**
|
|
832
|
+
* Optional currency stamp — when all matched items share a single
|
|
833
|
+
* foreign currency, this records it so the FX realization plugin can
|
|
834
|
+
* compute realized gain/loss against the base currency rates.
|
|
835
|
+
*/
|
|
833
836
|
currency: {
|
|
834
837
|
type: String,
|
|
835
838
|
default: null
|
|
@@ -840,6 +843,7 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
840
843
|
type: Date,
|
|
841
844
|
default: Date.now
|
|
842
845
|
},
|
|
846
|
+
/** Audit ref to the FX realization entry when the plugin fires. */
|
|
843
847
|
fxRealizationEntry: {
|
|
844
848
|
type: mongoose.Schema.Types.ObjectId,
|
|
845
849
|
ref: journalEntryModelName,
|
|
@@ -847,40 +851,18 @@ function createReconciliationSchema(config, accountModelName, journalEntryModelN
|
|
|
847
851
|
},
|
|
848
852
|
...extraFields
|
|
849
853
|
};
|
|
850
|
-
if (multiTenant) fields[multiTenant.orgField] = {
|
|
851
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
852
|
-
ref: multiTenant.orgRef,
|
|
853
|
-
required: true
|
|
854
|
-
};
|
|
855
854
|
const schema = new mongoose.Schema(fields, { timestamps: true });
|
|
856
855
|
if (indexes) {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
[org]: 1,
|
|
865
|
-
account: 1,
|
|
866
|
-
isFullReconcile: 1,
|
|
867
|
-
reconciledAt: 1
|
|
868
|
-
});
|
|
869
|
-
schema.index({
|
|
870
|
-
[org]: 1,
|
|
871
|
-
"items.entry": 1
|
|
872
|
-
});
|
|
873
|
-
} else {
|
|
874
|
-
schema.index({ matchingNumber: 1 }, { unique: true });
|
|
875
|
-
schema.index({
|
|
876
|
-
account: 1,
|
|
877
|
-
isFullReconcile: 1,
|
|
878
|
-
reconciledAt: 1
|
|
879
|
-
});
|
|
880
|
-
schema.index({ "items.entry": 1 });
|
|
881
|
-
}
|
|
856
|
+
schema.index({ matchingNumber: 1 }, { unique: true });
|
|
857
|
+
schema.index({
|
|
858
|
+
account: 1,
|
|
859
|
+
isFullReconcile: 1,
|
|
860
|
+
reconciledAt: 1
|
|
861
|
+
});
|
|
862
|
+
schema.index({ "items.entry": 1 });
|
|
882
863
|
}
|
|
883
864
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
865
|
+
injectTenantField(schema, scope);
|
|
884
866
|
return schema;
|
|
885
867
|
}
|
|
886
868
|
//#endregion
|
|
@@ -927,6 +909,14 @@ async function safePublish$3(events, outboxStore, type, payload, ctx) {
|
|
|
927
909
|
await events.publish(event);
|
|
928
910
|
} catch {}
|
|
929
911
|
}
|
|
912
|
+
function isDuplicateKeyBulkError(err) {
|
|
913
|
+
if (!err || typeof err !== "object") return false;
|
|
914
|
+
const e = err;
|
|
915
|
+
if (e.code === 11e3) return true;
|
|
916
|
+
if (Array.isArray(e.writeErrors) && e.writeErrors.length > 0) return true;
|
|
917
|
+
if (e.status === 409 && e.duplicate !== void 0) return true;
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
930
920
|
/**
|
|
931
921
|
* Wire seedAccounts, bulkCreate and posting-account validation
|
|
932
922
|
* onto an existing mongokit Repository.
|
|
@@ -1093,24 +1083,42 @@ function wireAccountMethods(repository, country, orgField, integrations = {}) {
|
|
|
1093
1083
|
_id: inserted[idx]._id
|
|
1094
1084
|
}));
|
|
1095
1085
|
} catch (err) {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
_id:
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1086
|
+
if (!isDuplicateKeyBulkError(err)) throw err;
|
|
1087
|
+
const insertedDocs = err.insertedDocs ?? [];
|
|
1088
|
+
const insertedNumbers = new Set(insertedDocs.map((d) => d.accountNumber));
|
|
1089
|
+
const stillUnknown = toCreate.filter((t) => !insertedNumbers.has(t.accountNumber));
|
|
1090
|
+
const concurrentlyPersistedById = /* @__PURE__ */ new Map();
|
|
1091
|
+
if (stillUnknown.length > 0) {
|
|
1092
|
+
const concurrentFilter = { accountNumber: { $in: stillUnknown.map((t) => t.accountNumber) } };
|
|
1093
|
+
if (orgField && orgId != null) concurrentFilter[orgField] = orgId;
|
|
1094
|
+
const persisted = await repository.findAll(concurrentFilter, {
|
|
1095
|
+
select: {
|
|
1096
|
+
_id: 1,
|
|
1097
|
+
accountNumber: 1
|
|
1098
|
+
},
|
|
1099
|
+
lean: true
|
|
1100
|
+
});
|
|
1101
|
+
for (const p of persisted) concurrentlyPersistedById.set(p.accountNumber, p._id);
|
|
1102
|
+
}
|
|
1103
|
+
for (const item of toCreate) if (insertedNumbers.has(item.accountNumber)) {
|
|
1104
|
+
const iDoc = insertedDocs.find((d) => d.accountNumber === item.accountNumber);
|
|
1105
|
+
results.created.push({
|
|
1110
1106
|
accountTypeCode: item.accountTypeCode,
|
|
1111
|
-
|
|
1107
|
+
active: item.active,
|
|
1108
|
+
isCashAccount: item.isCashAccount,
|
|
1109
|
+
_id: iDoc?._id
|
|
1112
1110
|
});
|
|
1113
|
-
} else
|
|
1111
|
+
} else if (concurrentlyPersistedById.has(item.accountNumber)) results.skipped.push({
|
|
1112
|
+
index: item.index,
|
|
1113
|
+
accountTypeCode: item.accountTypeCode,
|
|
1114
|
+
reason: "Already exists (concurrent insert)",
|
|
1115
|
+
_id: concurrentlyPersistedById.get(item.accountNumber)
|
|
1116
|
+
});
|
|
1117
|
+
else results.skipped.push({
|
|
1118
|
+
index: item.index,
|
|
1119
|
+
accountTypeCode: item.accountTypeCode,
|
|
1120
|
+
reason: "Already exists (concurrent insert)"
|
|
1121
|
+
});
|
|
1114
1122
|
}
|
|
1115
1123
|
}
|
|
1116
1124
|
const summary = {
|
|
@@ -1304,7 +1312,7 @@ function wireJournalEntryMethods(repository, _JournalEntryModel, orgField, stric
|
|
|
1304
1312
|
const getByQuery = repository.getByQuery.bind(repository);
|
|
1305
1313
|
const baseCreate = repository.create.bind(repository);
|
|
1306
1314
|
const update = repository.update.bind(repository);
|
|
1307
|
-
const withTransaction = repository.
|
|
1315
|
+
const withTransaction$1 = (fn, opts) => withTransaction(repository.Model.db, fn, opts);
|
|
1308
1316
|
const raceSafeCreate = async (data, options) => {
|
|
1309
1317
|
const input = data;
|
|
1310
1318
|
const idempotencyKey = typeof input.idempotencyKey === "string" && input.idempotencyKey.length > 0 ? input.idempotencyKey : void 0;
|
|
@@ -1672,7 +1680,7 @@ function wireJournalEntryMethods(repository, _JournalEntryModel, orgField, stric
|
|
|
1672
1680
|
};
|
|
1673
1681
|
};
|
|
1674
1682
|
if (options.session) return await doReverse(options.session);
|
|
1675
|
-
if (withTransaction) return await withTransaction((session) => doReverse(session), { allowFallback: true });
|
|
1683
|
+
if (withTransaction$1) return await withTransaction$1((session) => doReverse(session), { allowFallback: true });
|
|
1676
1684
|
return await doReverse();
|
|
1677
1685
|
};
|
|
1678
1686
|
const methodNames = [
|
|
@@ -1967,7 +1975,7 @@ function wireReconciliationMethods(repository, ReconciliationModel, JournalEntry
|
|
|
1967
1975
|
* Build all ledger repositories with plugins + domain methods pre-wired.
|
|
1968
1976
|
*/
|
|
1969
1977
|
function createRepositories(models, config, plugins = {}, pagination = {}, integrations = {}) {
|
|
1970
|
-
const orgField = config.multiTenant?.
|
|
1978
|
+
const orgField = config.multiTenant?.tenantField;
|
|
1971
1979
|
const strictness = config.strictness;
|
|
1972
1980
|
const country = config.country;
|
|
1973
1981
|
const { events, bridges, outboxStore } = integrations;
|
|
@@ -2301,7 +2309,7 @@ const REPORT_CATALOG = Object.freeze([
|
|
|
2301
2309
|
function buildIntrospectAPI({ models, country, config }) {
|
|
2302
2310
|
const AccountModel = models.Account;
|
|
2303
2311
|
const FiscalPeriodModel = models.FiscalPeriod;
|
|
2304
|
-
const orgField = config.multiTenant?.
|
|
2312
|
+
const orgField = config.multiTenant?.tenantField;
|
|
2305
2313
|
const normalBalanceFor = (category) => {
|
|
2306
2314
|
if (category.endsWith("Asset") || category.endsWith("Expense")) return "debit";
|
|
2307
2315
|
return "credit";
|
|
@@ -2363,7 +2371,7 @@ function buildIntrospectAPI({ models, country, config }) {
|
|
|
2363
2371
|
//#region src/semantic/record.ts
|
|
2364
2372
|
function buildRecordAPI({ models, repositories, config }) {
|
|
2365
2373
|
const AccountModel = models.Account;
|
|
2366
|
-
const orgField = config.multiTenant?.
|
|
2374
|
+
const orgField = config.multiTenant?.tenantField;
|
|
2367
2375
|
const resolveAccounts = async (organizationId, codes, path, session) => {
|
|
2368
2376
|
const unique = Array.from(new Set(codes));
|
|
2369
2377
|
const filter = { accountTypeCode: { $in: unique } };
|
|
@@ -2591,7 +2599,7 @@ function buildRecordAPI({ models, repositories, config }) {
|
|
|
2591
2599
|
* mongoose: mongoose.connection,
|
|
2592
2600
|
* country: canadaPack,
|
|
2593
2601
|
* currency: 'CAD',
|
|
2594
|
-
* multiTenant: {
|
|
2602
|
+
* multiTenant: { tenantField: 'organizationId', ref: 'Organization' },
|
|
2595
2603
|
* });
|
|
2596
2604
|
*
|
|
2597
2605
|
* // Models — auto-created Mongoose models
|
|
@@ -2762,7 +2770,7 @@ var AccountingEngine = class {
|
|
|
2762
2770
|
const JournalEntryModel = this.models.JournalEntry;
|
|
2763
2771
|
const BudgetModel = this.models.Budget;
|
|
2764
2772
|
const { country, config } = this;
|
|
2765
|
-
const orgField = config.multiTenant?.
|
|
2773
|
+
const orgField = config.multiTenant?.tenantField;
|
|
2766
2774
|
const fiscalYearStartMonth = config.fiscalYearStartMonth ?? 1;
|
|
2767
2775
|
const retainedEarningsAccountCode = config.retainedEarningsAccountCode;
|
|
2768
2776
|
const retainedEarningsDisplayCode = config.retainedEarningsDisplayCode;
|
|
@@ -2896,4 +2904,4 @@ function buildDimensionIndexes(dimensions, orgField) {
|
|
|
2896
2904
|
});
|
|
2897
2905
|
}
|
|
2898
2906
|
//#endregion
|
|
2899
|
-
export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, ConcurrencyError, DEFAULT_BUCKETS, DuplicateReferenceError, Errors, IdempotencyConflictError, ImmutableViolationError, InProcessLedgerBus, JOURNAL_CODES, JOURNAL_TYPES, LEDGER_EVENTS, Money, OutboxOwnershipError, 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 };
|
|
2907
|
+
export { AccountBulkCreated, AccountSeeded, AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, ConcurrencyError, DEFAULT_BUCKETS, DuplicateReferenceError, EntryArchived, EntryCreated, EntryDuplicated, EntryPosted, EntryReversed, EntryUnposted, Errors, IdempotencyConflictError, ImmutableViolationError, InProcessLedgerBus, InvalidOutboxEventError, JOURNAL_CODES, JOURNAL_TYPES, JournalSeeded, LEDGER_EVENTS, Money, OutboxOwnershipError, ReconciliationMatched, ReconciliationUnmatched, 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, ledgerEventDefinitions, multiply, parseCents, percentage, periodResolver, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, watermarkResolver };
|