@classytic/ledger 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/country/index.d.mts +2 -2
- package/dist/{errors-BmRjW38t.mjs → errors-CSDQPNyt.mjs} +1 -1
- package/dist/fx-realization.plugin-CgQFDGv2.mjs +459 -0
- package/dist/index-BthGypsI.d.mts +228 -0
- package/dist/index.d.mts +50 -105
- package/dist/index.mjs +644 -124
- package/dist/{fiscal-close-Dk3yRT9i.mjs → partner-ledger-D9H5hegI.mjs} +143 -6
- package/dist/plugins/index.d.mts +2 -24
- package/dist/plugins/index.mjs +2 -2
- package/dist/reports/index.d.mts +2 -2
- package/dist/reports/index.mjs +2 -2
- package/dist/tax-hooks-BnVenul5.d.mts +513 -0
- package/dist/{trial-balance-BZ7yOOFD.d.mts → trial-balance-s92GEvRR.d.mts} +75 -2
- package/package.json +6 -5
- package/dist/date-lock.plugin-B2Jy0ukX.mjs +0 -253
- package/dist/idempotency.plugin-CK7LHnBn.d.mts +0 -60
- package/dist/index-GmfEFxVn.d.mts +0 -119
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { ClientSession, Model } from "mongoose";
|
|
2
|
+
import * as _classytic_mongokit0 from "@classytic/mongokit";
|
|
3
|
+
import { Repository, RepositoryContext, RepositoryInstance } from "@classytic/mongokit";
|
|
4
|
+
|
|
5
|
+
//#region src/types/repositories.d.ts
|
|
6
|
+
interface PostOptions {
|
|
7
|
+
session?: ClientSession | null;
|
|
8
|
+
/** Actor performing this operation (required when strictness.requireActor is enabled) */
|
|
9
|
+
actorId?: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface ReverseOptions extends PostOptions {
|
|
12
|
+
/** Date for the reversal entry (defaults to now) */
|
|
13
|
+
reversalDate?: Date;
|
|
14
|
+
}
|
|
15
|
+
interface SeedOptions {
|
|
16
|
+
session?: ClientSession | null;
|
|
17
|
+
}
|
|
18
|
+
interface SeedResult {
|
|
19
|
+
created: number;
|
|
20
|
+
skipped: number;
|
|
21
|
+
}
|
|
22
|
+
interface BulkCreateInput {
|
|
23
|
+
accountTypeCode?: string;
|
|
24
|
+
accountNumber?: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
active?: boolean;
|
|
27
|
+
isCashAccount?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generic over the account document type so consumers get real
|
|
31
|
+
* IntelliSense on `created[0].accountNumber` instead of `unknown`.
|
|
32
|
+
* Defaults to `Record<string, unknown>` for backwards-compatible call sites.
|
|
33
|
+
*/
|
|
34
|
+
interface BulkCreateResult<TAccount = Record<string, unknown>> {
|
|
35
|
+
summary: {
|
|
36
|
+
total: number;
|
|
37
|
+
created: number;
|
|
38
|
+
skipped: number;
|
|
39
|
+
errors: number;
|
|
40
|
+
};
|
|
41
|
+
created: TAccount[];
|
|
42
|
+
skipped: TAccount[];
|
|
43
|
+
errors: TAccount[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generic over the journal-entry document type — `{ original, reversal }`
|
|
47
|
+
* are the actual persisted shapes, so callers can access `._id`, `.state`,
|
|
48
|
+
* `.reversed`, etc. without casts.
|
|
49
|
+
*/
|
|
50
|
+
interface ReverseResult<TEntry = Record<string, unknown>> {
|
|
51
|
+
original: TEntry;
|
|
52
|
+
reversal: TEntry;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Journal Entry Repository — extends mongokit Repository with accounting domain methods.
|
|
56
|
+
*
|
|
57
|
+
* Inherits ALL Repository<TDoc> methods: create, getById, getAll, update,
|
|
58
|
+
* delete, count, exists, distinct, aggregate, withTransaction, etc.
|
|
59
|
+
*/
|
|
60
|
+
interface JournalEntryRepository<TDoc = Record<string, unknown>> extends Repository<TDoc> {
|
|
61
|
+
/** Post an entry (draft → posted). Validates items, balance, and accounts. */
|
|
62
|
+
post(id: unknown, orgId?: unknown, options?: PostOptions): Promise<TDoc>;
|
|
63
|
+
/** Unpost an entry (posted → draft). Resets state for re-editing. */
|
|
64
|
+
unpost(id: unknown, orgId?: unknown, options?: PostOptions): Promise<TDoc>;
|
|
65
|
+
/** Archive a draft entry (draft → archived). Preserves audit trail. */
|
|
66
|
+
archive(id: unknown, orgId?: unknown, options?: PostOptions): Promise<TDoc>;
|
|
67
|
+
/** Duplicate an entry as a new draft. Copies items, type, and label. */
|
|
68
|
+
duplicate(id: unknown, orgId?: unknown, options?: PostOptions): Promise<TDoc>;
|
|
69
|
+
/** Reverse a posted entry. Creates mirror entry with flipped debits/credits. */
|
|
70
|
+
reverse(id: unknown, orgId?: unknown, options?: ReverseOptions): Promise<ReverseResult<TDoc>>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Account Repository — extends mongokit Repository with seed and bulk operations.
|
|
74
|
+
*/
|
|
75
|
+
interface AccountRepository<TDoc = Record<string, unknown>> extends Repository<TDoc> {
|
|
76
|
+
/** Seed standard posting accounts for an org from the country pack. */
|
|
77
|
+
seedAccounts(orgId: unknown, options?: SeedOptions): Promise<SeedResult>;
|
|
78
|
+
/** Bulk create accounts with validation and skip-if-exists logic. */
|
|
79
|
+
bulkCreate(accounts: BulkCreateInput[], orgId: unknown): Promise<BulkCreateResult<TDoc>>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reference to a specific item inside a journal entry. Positional index
|
|
83
|
+
* because journal items are embedded sub-documents.
|
|
84
|
+
*/
|
|
85
|
+
interface JournalItemRef {
|
|
86
|
+
/** Journal entry id. */
|
|
87
|
+
entry: unknown;
|
|
88
|
+
/** Zero-based index into the entry's `journalItems` array. */
|
|
89
|
+
itemIndex: number;
|
|
90
|
+
}
|
|
91
|
+
interface MatchInput {
|
|
92
|
+
/** The account whose items are being matched (sanity checked). */
|
|
93
|
+
account: unknown;
|
|
94
|
+
/** Two or more items to match. Must share `account`. */
|
|
95
|
+
items: JournalItemRef[];
|
|
96
|
+
/**
|
|
97
|
+
* Optional caller-provided matching number. When omitted, the repository
|
|
98
|
+
* generates one via its counter. Must be unique per org.
|
|
99
|
+
*/
|
|
100
|
+
matchingNumber?: string;
|
|
101
|
+
note?: string;
|
|
102
|
+
reconciledBy?: string;
|
|
103
|
+
organizationId?: unknown;
|
|
104
|
+
session?: ClientSession | null;
|
|
105
|
+
}
|
|
106
|
+
interface OpenItem {
|
|
107
|
+
entry: unknown;
|
|
108
|
+
itemIndex: number;
|
|
109
|
+
debit: number;
|
|
110
|
+
credit: number;
|
|
111
|
+
date?: Date;
|
|
112
|
+
account: unknown;
|
|
113
|
+
[key: string]: unknown;
|
|
114
|
+
}
|
|
115
|
+
interface ReconciliationRepository<TDoc = Record<string, unknown>> extends Repository<TDoc> {
|
|
116
|
+
/**
|
|
117
|
+
* Match two or more journal items against each other. Stamps a
|
|
118
|
+
* shared `matchingNumber` onto every referenced item and creates a
|
|
119
|
+
* reconciliation document. When debit/credit totals balance, the
|
|
120
|
+
* reconciliation is flagged `isFullReconcile: true`.
|
|
121
|
+
*
|
|
122
|
+
* Fires `after:match` hook — the fxRealizationPlugin listens here.
|
|
123
|
+
*/
|
|
124
|
+
match(input: MatchInput): Promise<TDoc>;
|
|
125
|
+
/**
|
|
126
|
+
* Unwind a matching group. Clears `matchingNumber` on every referenced
|
|
127
|
+
* item and deletes the reconciliation document. The FX realization
|
|
128
|
+
* entry (if any) is reversed via `journalEntries.reverse`.
|
|
129
|
+
*/
|
|
130
|
+
unmatch(input: {
|
|
131
|
+
matchingNumber: string;
|
|
132
|
+
organizationId?: unknown;
|
|
133
|
+
session?: ClientSession | null;
|
|
134
|
+
}): Promise<{
|
|
135
|
+
success: boolean;
|
|
136
|
+
}>;
|
|
137
|
+
/**
|
|
138
|
+
* Find all posted journal items referencing the given account that
|
|
139
|
+
* do NOT yet have a matching number. Returns lean items with
|
|
140
|
+
* `{ entry, itemIndex, debit, credit, date }` plus any extra fields
|
|
141
|
+
* (dimension, maturityDate, currency).
|
|
142
|
+
*
|
|
143
|
+
* `filter` lets you narrow further by any journal-item field —
|
|
144
|
+
* commonly used to scope by `partnerId` for supplier/customer
|
|
145
|
+
* subsidiary ledger queries:
|
|
146
|
+
*
|
|
147
|
+
* `getOpenItems({ accountId: apControlId, filter: { partnerId: 'sup-1' } })`
|
|
148
|
+
*
|
|
149
|
+
* `asOfDate` restricts to items posted on or before that date,
|
|
150
|
+
* giving historical open-item snapshots — useful for aged-balance
|
|
151
|
+
* reports run as of a previous month-end.
|
|
152
|
+
*/
|
|
153
|
+
getOpenItems(params: {
|
|
154
|
+
accountId: unknown;
|
|
155
|
+
organizationId?: unknown; /** Extra equality filters on the journal item (e.g. `{ partnerId: 'X' }`). */
|
|
156
|
+
filter?: Record<string, unknown>; /** Only consider items from entries dated on or before this date. */
|
|
157
|
+
asOfDate?: Date;
|
|
158
|
+
limit?: number;
|
|
159
|
+
skip?: number;
|
|
160
|
+
}): Promise<OpenItem[]>;
|
|
161
|
+
}
|
|
162
|
+
interface JournalRepository<TDoc = Record<string, unknown>> extends Repository<TDoc> {
|
|
163
|
+
/**
|
|
164
|
+
* Seed the organization's default journals from the country pack's
|
|
165
|
+
* `journalTemplates`. Idempotent — skips journals that already exist.
|
|
166
|
+
*/
|
|
167
|
+
seedDefaults(orgId: unknown): Promise<SeedResult>;
|
|
168
|
+
/**
|
|
169
|
+
* Atomically increment the journal's sequence counter and return a
|
|
170
|
+
* formatted reference number (e.g. `'INV/2026/03/0042'`). Safe under
|
|
171
|
+
* concurrent posts — uses `$inc` inside `findOneAndUpdate`.
|
|
172
|
+
*/
|
|
173
|
+
nextSequenceNumber(journalId: unknown, orgId?: unknown): Promise<string>;
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/plugins/credit-limit.plugin.d.ts
|
|
177
|
+
interface CreditLimitPluginOptions {
|
|
178
|
+
/** The A/R control account that this limit guards. */
|
|
179
|
+
arControlAccountId: unknown;
|
|
180
|
+
/**
|
|
181
|
+
* Resolve the partner's credit limit in cents. Return `null` for
|
|
182
|
+
* "no limit" (skip the check), `0` for "cash-only customer".
|
|
183
|
+
*/
|
|
184
|
+
getCreditLimit: (partnerId: unknown, session: ClientSession | null) => Promise<number | null> | number | null;
|
|
185
|
+
/** Field name for the partner ref on each journal item. Default: `'partnerId'`. */
|
|
186
|
+
partnerField?: string;
|
|
187
|
+
/** JournalEntry model — required to sum existing exposure. */
|
|
188
|
+
JournalEntryModel: Model<unknown>;
|
|
189
|
+
/** Multi-tenant scope field. */
|
|
190
|
+
orgField?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Optional tolerance in cents — allow up to N cents over the limit.
|
|
193
|
+
* Defaults to 0 (strict).
|
|
194
|
+
*/
|
|
195
|
+
toleranceCents?: number;
|
|
196
|
+
}
|
|
197
|
+
declare function creditLimitPlugin(options: CreditLimitPluginOptions): {
|
|
198
|
+
name: string;
|
|
199
|
+
apply(repo: RepositoryInstance): void;
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/types/mongokit-augmentation.d.ts
|
|
203
|
+
/**
|
|
204
|
+
* Module augmentation for `@classytic/mongokit`.
|
|
205
|
+
*
|
|
206
|
+
* Ledger's state-transition methods (post, unpost, archive, reverseMark) tag
|
|
207
|
+
* their `repository.update()` call with a `_ledgerInternal` flag so the
|
|
208
|
+
* double-entry immutability guard can distinguish legitimate transitions from
|
|
209
|
+
* arbitrary edits. This file types that flag onto both `RepositoryContext`
|
|
210
|
+
* (what plugins observe) and `SessionOptions` (what callers pass) so consumers
|
|
211
|
+
* and plugin authors get full IntelliSense without casts.
|
|
212
|
+
*
|
|
213
|
+
* This file is side-effect only — importing it anywhere in the package is
|
|
214
|
+
* enough to activate the augmentation. `src/types/index.ts` re-exports it.
|
|
215
|
+
*/
|
|
216
|
+
type LedgerInternalOp = 'post' | 'unpost' | 'archive' | 'reverseMark' | 'fxRealize' | 'cashBasisRealize';
|
|
217
|
+
declare module '@classytic/mongokit' {
|
|
218
|
+
interface RepositoryContext {
|
|
219
|
+
/**
|
|
220
|
+
* Set by ledger's repository methods (post, unpost, archive, reverseMark)
|
|
221
|
+
* to signal a legitimate internal state transition. Plugins observing
|
|
222
|
+
* `before:update` can read this to distinguish from arbitrary edits.
|
|
223
|
+
*
|
|
224
|
+
* External `repository.update()` callers cannot spoof this flag because
|
|
225
|
+
* it is only set by ledger's own repo methods, never surfaced in the
|
|
226
|
+
* public API.
|
|
227
|
+
*/
|
|
228
|
+
_ledgerInternal?: LedgerInternalOp;
|
|
229
|
+
}
|
|
230
|
+
interface SessionOptions {
|
|
231
|
+
/**
|
|
232
|
+
* Ledger-internal flag — see `RepositoryContext._ledgerInternal`.
|
|
233
|
+
* Typed here so ledger's repo methods can pass it without casts.
|
|
234
|
+
* Consumers should never set this directly.
|
|
235
|
+
*/
|
|
236
|
+
_ledgerInternal?: LedgerInternalOp;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/plugins/double-entry.plugin.d.ts
|
|
241
|
+
interface DoubleEntryPluginOptions {
|
|
242
|
+
/** Only enforce on posted entries (default: true) */
|
|
243
|
+
onlyOnPost?: boolean;
|
|
244
|
+
/** Mongoose model — required to validate partial updates that only set state */
|
|
245
|
+
JournalEntryModel?: Model<unknown>;
|
|
246
|
+
/** Account model — when provided, posted creates verify account existence + tenant scoping */
|
|
247
|
+
AccountModel?: Model<unknown>;
|
|
248
|
+
/** Multi-tenant org field name (e.g. 'business'). Required for tenant-account integrity checks. */
|
|
249
|
+
orgField?: string;
|
|
250
|
+
}
|
|
251
|
+
declare function doubleEntryPlugin(options?: DoubleEntryPluginOptions): {
|
|
252
|
+
name: string;
|
|
253
|
+
apply(repo: RepositoryInstance): void;
|
|
254
|
+
};
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/plugins/fx-realization.plugin.d.ts
|
|
257
|
+
interface FxRealizationPluginOptions {
|
|
258
|
+
/** Repository used to create the balancing FX entry. */
|
|
259
|
+
journalEntries: JournalEntryRepository<Record<string, unknown>>;
|
|
260
|
+
/** Account id for realized FX gains (income). */
|
|
261
|
+
realizedGainAccount: unknown;
|
|
262
|
+
/** Account id for realized FX losses (expense). */
|
|
263
|
+
realizedLossAccount: unknown;
|
|
264
|
+
/** Base/functional currency — FX is computed relative to this. */
|
|
265
|
+
baseCurrency: string;
|
|
266
|
+
/** Multi-tenant org field. */
|
|
267
|
+
orgField?: string;
|
|
268
|
+
}
|
|
269
|
+
declare function fxRealizationPlugin(options: FxRealizationPluginOptions): {
|
|
270
|
+
name: string;
|
|
271
|
+
apply(repo: RepositoryInstance): void;
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/plugins/idempotency.plugin.d.ts
|
|
275
|
+
interface IdempotencyPluginOptions {
|
|
276
|
+
/** Mongoose model for journal entries */
|
|
277
|
+
JournalEntryModel: Model<unknown>;
|
|
278
|
+
/** Multi-tenant org field name */
|
|
279
|
+
orgField?: string;
|
|
280
|
+
}
|
|
281
|
+
declare function idempotencyPlugin(options: IdempotencyPluginOptions): {
|
|
282
|
+
name: string;
|
|
283
|
+
apply(repo: RepositoryInstance): void;
|
|
284
|
+
};
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region src/plugins/lock/types.d.ts
|
|
287
|
+
/**
|
|
288
|
+
* The resolved "who did what, when" for a locked slice of ledger.
|
|
289
|
+
* Returned by a `LockResolver` to describe why an entry is blocked.
|
|
290
|
+
*/
|
|
291
|
+
interface LockHit {
|
|
292
|
+
/** Scope that this hit belongs to — e.g. `'fiscal'`, `'tax'`. */
|
|
293
|
+
readonly scope: string;
|
|
294
|
+
/** Human-readable label used in error messages (e.g. `'Q1 2025'`). */
|
|
295
|
+
readonly label: string;
|
|
296
|
+
/** Optional sub-type inside the scope (e.g. `'VAT'`, `'TDS'`, `'GST'`). */
|
|
297
|
+
readonly subType?: string;
|
|
298
|
+
/** Optional external reference (filed return number, statement ID, …). */
|
|
299
|
+
readonly externalRef?: string;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Context given to a lock resolver. All fields except `entryDate` are
|
|
303
|
+
* pass-throughs from the `createLockPlugin` boilerplate — resolvers don't
|
|
304
|
+
* need to worry about where the date came from (payload vs persisted doc).
|
|
305
|
+
*/
|
|
306
|
+
interface LockResolverContext {
|
|
307
|
+
/** The effective date the entry will be posted on. */
|
|
308
|
+
readonly entryDate: Date;
|
|
309
|
+
/** The resolved multi-tenant scope value, or `undefined` if unscoped. */
|
|
310
|
+
readonly orgValue: unknown;
|
|
311
|
+
/** The mongoose session if the operation is running inside a transaction. */
|
|
312
|
+
readonly session: ClientSession | null;
|
|
313
|
+
/** The raw entry payload (for resolvers that need extra context). */
|
|
314
|
+
readonly data: Record<string, unknown>;
|
|
315
|
+
/** The full upstream repository context (advanced use). */
|
|
316
|
+
readonly repositoryContext: RepositoryContext;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Resolver signature. Return `null` if the entry is allowed, or a
|
|
320
|
+
* `LockHit` to block it with a 409 `PERIOD_LOCKED_{SCOPE}` error.
|
|
321
|
+
*/
|
|
322
|
+
type LockResolver = (ctx: LockResolverContext) => Promise<LockHit | null>;
|
|
323
|
+
/**
|
|
324
|
+
* Predicate used to narrow a lock to specific accounts. Called per
|
|
325
|
+
* journal item; if *any* item's account matches, the entry is subject
|
|
326
|
+
* to the lock. Receives the populated account document (lean).
|
|
327
|
+
*/
|
|
328
|
+
type LockAccountSelector = (account: Record<string, unknown>) => boolean;
|
|
329
|
+
/**
|
|
330
|
+
* Options accepted by `createLockPlugin`. All fields are type-safe —
|
|
331
|
+
* the factory handles date/org resolution, session propagation, and
|
|
332
|
+
* the `_ledgerInternal` skip path automatically.
|
|
333
|
+
*/
|
|
334
|
+
interface CreateLockPluginOptions {
|
|
335
|
+
/**
|
|
336
|
+
* Scope identifier — short, lowercase, stable. Used in the plugin name
|
|
337
|
+
* (`accounting:lock:{scope}`) and the error code
|
|
338
|
+
* (`PERIOD_LOCKED_{SCOPE}`).
|
|
339
|
+
*/
|
|
340
|
+
scope: string;
|
|
341
|
+
/**
|
|
342
|
+
* Resolver that decides whether a given entry is locked. Call one of
|
|
343
|
+
* the builtin resolvers (`periodResolver`, `watermarkResolver`) or
|
|
344
|
+
* implement your own.
|
|
345
|
+
*/
|
|
346
|
+
resolve: LockResolver;
|
|
347
|
+
/**
|
|
348
|
+
* Optional account-level narrowing. When provided, the lock only
|
|
349
|
+
* fires if at least one of the entry's journal items touches an
|
|
350
|
+
* account that matches the predicate.
|
|
351
|
+
*
|
|
352
|
+
* When this is set, the plugin must be able to load account docs, so
|
|
353
|
+
* `AccountModel` becomes required.
|
|
354
|
+
*/
|
|
355
|
+
accountSelector?: LockAccountSelector;
|
|
356
|
+
/** Required when `accountSelector` is set — used to hydrate accounts. */
|
|
357
|
+
AccountModel?: Model<unknown>;
|
|
358
|
+
/**
|
|
359
|
+
* Journal-entry model — required for partial updates (we need to look
|
|
360
|
+
* up the persisted `date` and `orgField` when the payload omits them).
|
|
361
|
+
*/
|
|
362
|
+
JournalEntryModel?: Model<unknown>;
|
|
363
|
+
/** Multi-tenant scope field name (e.g. `'organizationId'`). */
|
|
364
|
+
orgField?: string;
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/plugins/lock/create-lock-plugin.d.ts
|
|
368
|
+
declare function createLockPlugin(options: CreateLockPluginOptions): {
|
|
369
|
+
name: string;
|
|
370
|
+
apply(repo: RepositoryInstance): void;
|
|
371
|
+
};
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region src/plugins/lock/period-resolver.d.ts
|
|
374
|
+
interface PeriodResolverOptions {
|
|
375
|
+
/** Scope identifier, passed through to the resulting `LockHit`. */
|
|
376
|
+
scope: string;
|
|
377
|
+
/** Mongoose model holding closed-period rows. */
|
|
378
|
+
PeriodModel: Model<unknown>;
|
|
379
|
+
/** Field name for the start of the window. Default: `'startDate'`. */
|
|
380
|
+
startField?: string;
|
|
381
|
+
/** Field name for the end of the window. Default: `'endDate'`. */
|
|
382
|
+
endField?: string;
|
|
383
|
+
/**
|
|
384
|
+
* Field name that indicates "this window is closed". Default:
|
|
385
|
+
* `'closed'` (matches FiscalPeriod model).
|
|
386
|
+
*/
|
|
387
|
+
closedField?: string;
|
|
388
|
+
/** Value of the closed field that counts as closed. Default: `true`. */
|
|
389
|
+
closedValue?: unknown;
|
|
390
|
+
/** Field name to use as the display label in errors. Default: `'name'`. */
|
|
391
|
+
labelField?: string;
|
|
392
|
+
/** Field name to surface as `LockHit.subType`. Default: undefined. */
|
|
393
|
+
subTypeField?: string;
|
|
394
|
+
/** Field name to surface as `LockHit.externalRef`. Default: undefined. */
|
|
395
|
+
externalRefField?: string;
|
|
396
|
+
/** Multi-tenant scope field on the period doc. */
|
|
397
|
+
orgField?: string;
|
|
398
|
+
/**
|
|
399
|
+
* Optional additional query fragment merged into the lookup. Receives
|
|
400
|
+
* the resolver context so callers can derive dynamic filters from the
|
|
401
|
+
* entry payload (e.g. pick `taxType` from `data`).
|
|
402
|
+
*/
|
|
403
|
+
extraQuery?: (ctx: LockResolverContext) => Record<string, unknown> | undefined;
|
|
404
|
+
}
|
|
405
|
+
declare function periodResolver(options: PeriodResolverOptions): LockResolver;
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/plugins/lock/presets.d.ts
|
|
408
|
+
interface FiscalLockPluginOptions {
|
|
409
|
+
FiscalPeriodModel: Model<unknown>;
|
|
410
|
+
JournalEntryModel?: Model<unknown>;
|
|
411
|
+
orgField?: string;
|
|
412
|
+
}
|
|
413
|
+
declare function fiscalLockPlugin(options: FiscalLockPluginOptions): {
|
|
414
|
+
name: string;
|
|
415
|
+
apply(repo: _classytic_mongokit0.RepositoryInstance): void;
|
|
416
|
+
};
|
|
417
|
+
interface TaxLockPluginOptions {
|
|
418
|
+
/**
|
|
419
|
+
* Tax-period model. Expected shape (fields are overridable via the
|
|
420
|
+
* lower-level `createLockPlugin` if your schema differs):
|
|
421
|
+
*
|
|
422
|
+
* {
|
|
423
|
+
* periodStart: Date,
|
|
424
|
+
* periodEnd: Date,
|
|
425
|
+
* status: 'open' | 'filed' | 'amended',
|
|
426
|
+
* jurisdiction?: string,
|
|
427
|
+
* taxType?: string,
|
|
428
|
+
* returnRef?: string,
|
|
429
|
+
* [orgField]?: unknown,
|
|
430
|
+
* }
|
|
431
|
+
*
|
|
432
|
+
* `status !== 'open'` counts as locked.
|
|
433
|
+
*/
|
|
434
|
+
TaxPeriodModel: Model<unknown>;
|
|
435
|
+
AccountModel: Model<unknown>;
|
|
436
|
+
JournalEntryModel?: Model<unknown>;
|
|
437
|
+
orgField?: string;
|
|
438
|
+
/**
|
|
439
|
+
* Predicate deciding which accounts participate in tax returns.
|
|
440
|
+
* Defaults to `acc => acc.taxMetadata != null` — matches the
|
|
441
|
+
* convention used by country packs that populate `taxMetadata` on
|
|
442
|
+
* tax-payable / tax-recoverable account types.
|
|
443
|
+
*/
|
|
444
|
+
isTaxAffecting?: LockAccountSelector;
|
|
445
|
+
/**
|
|
446
|
+
* Derive `{ jurisdiction, taxType }` from the entry payload so each
|
|
447
|
+
* post can look up the right row. Default: use `data.jurisdiction`
|
|
448
|
+
* and `data.taxType` directly when present.
|
|
449
|
+
*/
|
|
450
|
+
deriveFilter?: (data: Record<string, unknown>) => {
|
|
451
|
+
jurisdiction?: string;
|
|
452
|
+
taxType?: string;
|
|
453
|
+
} | undefined;
|
|
454
|
+
}
|
|
455
|
+
declare function taxLockPlugin(options: TaxLockPluginOptions): {
|
|
456
|
+
name: string;
|
|
457
|
+
apply(repo: _classytic_mongokit0.RepositoryInstance): void;
|
|
458
|
+
};
|
|
459
|
+
interface DailyLockPluginOptions {
|
|
460
|
+
/**
|
|
461
|
+
* Return the `lastClosedDate` for the given org/branch — everything
|
|
462
|
+
* on or before this date is frozen. Return `null` if the branch
|
|
463
|
+
* has never been closed.
|
|
464
|
+
*/
|
|
465
|
+
getLastClosedDate: (orgValue: unknown, session: ClientSession | null) => Promise<Date | null> | Date | null;
|
|
466
|
+
JournalEntryModel?: Model<unknown>;
|
|
467
|
+
orgField?: string;
|
|
468
|
+
}
|
|
469
|
+
declare function dailyLockPlugin(options: DailyLockPluginOptions): {
|
|
470
|
+
name: string;
|
|
471
|
+
apply(repo: _classytic_mongokit0.RepositoryInstance): void;
|
|
472
|
+
};
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/plugins/lock/watermark-resolver.d.ts
|
|
475
|
+
interface WatermarkResolverOptions {
|
|
476
|
+
/** Scope identifier, passed through to the resulting `LockHit`. */
|
|
477
|
+
scope: string;
|
|
478
|
+
/**
|
|
479
|
+
* Resolve the current watermark for the given org. Return `null` to
|
|
480
|
+
* mean "no lock in place". The session is forwarded so the lookup
|
|
481
|
+
* can participate in the caller's transaction.
|
|
482
|
+
*/
|
|
483
|
+
getWatermark: (orgValue: unknown, session: ClientSession | null) => Promise<Date | null> | Date | null;
|
|
484
|
+
/**
|
|
485
|
+
* Optional label override. Defaults to `"through {ISO date}"`.
|
|
486
|
+
*/
|
|
487
|
+
formatLabel?: (watermark: Date, ctx: LockResolverContext) => string;
|
|
488
|
+
}
|
|
489
|
+
declare function watermarkResolver(options: WatermarkResolverOptions): LockResolver;
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region src/utils/tax-hooks.d.ts
|
|
492
|
+
interface TaxLineInput {
|
|
493
|
+
account: unknown;
|
|
494
|
+
amount: number;
|
|
495
|
+
side: 'debit' | 'credit';
|
|
496
|
+
taxCode?: string;
|
|
497
|
+
extraFields?: Record<string, unknown>;
|
|
498
|
+
}
|
|
499
|
+
interface GeneratedTaxLine {
|
|
500
|
+
account: unknown;
|
|
501
|
+
debit: number;
|
|
502
|
+
credit: number;
|
|
503
|
+
label?: string;
|
|
504
|
+
taxDetails?: Array<{
|
|
505
|
+
taxCode: string;
|
|
506
|
+
taxName?: string;
|
|
507
|
+
}>;
|
|
508
|
+
}
|
|
509
|
+
interface TaxLineGenerator {
|
|
510
|
+
generateTaxLines(input: TaxLineInput): GeneratedTaxLine[];
|
|
511
|
+
}
|
|
512
|
+
//#endregion
|
|
513
|
+
export { JournalEntryRepository as A, SeedResult as B, DoubleEntryPluginOptions as C, AccountRepository as D, creditLimitPlugin as E, PostOptions as F, ReconciliationRepository as I, ReverseOptions as L, JournalRepository as M, MatchInput as N, BulkCreateInput as O, OpenItem as P, ReverseResult as R, fxRealizationPlugin as S, CreditLimitPluginOptions as T, LockResolver as _, DailyLockPluginOptions as a, idempotencyPlugin as b, dailyLockPlugin as c, PeriodResolverOptions as d, periodResolver as f, LockHit as g, LockAccountSelector as h, watermarkResolver as i, JournalItemRef as j, BulkCreateResult as k, fiscalLockPlugin as l, CreateLockPluginOptions as m, TaxLineInput as n, FiscalLockPluginOptions as o, createLockPlugin as p, WatermarkResolverOptions as r, TaxLockPluginOptions as s, TaxLineGenerator as t, taxLockPlugin as u, LockResolverContext as v, doubleEntryPlugin as w, FxRealizationPluginOptions as x, IdempotencyPluginOptions as y, SeedOptions as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as DateRange } from "./core-BkGjuVZj.mjs";
|
|
2
|
-
import { t as CountryPack } from "./index-
|
|
2
|
+
import { t as CountryPack } from "./index-BthGypsI.mjs";
|
|
3
3
|
import { ClientSession, Model } from "mongoose";
|
|
4
4
|
|
|
5
5
|
//#region src/utils/logger.d.ts
|
|
@@ -403,6 +403,79 @@ declare function generateIncomeStatement(opts: IncomeStatementOptions, params: {
|
|
|
403
403
|
filters?: Record<string, unknown>;
|
|
404
404
|
}): Promise<IncomeStatementReport>;
|
|
405
405
|
//#endregion
|
|
406
|
+
//#region src/reports/partner-ledger.d.ts
|
|
407
|
+
interface PartnerLedgerOptions {
|
|
408
|
+
AccountModel: Model<unknown>;
|
|
409
|
+
JournalEntryModel: Model<unknown>;
|
|
410
|
+
orgField?: string;
|
|
411
|
+
}
|
|
412
|
+
interface PartnerLedgerParams {
|
|
413
|
+
organizationId?: unknown;
|
|
414
|
+
/**
|
|
415
|
+
* The control account being ledgered — typically `2111 A/P`
|
|
416
|
+
* (supplier statement) or `1141 A/R` (customer statement).
|
|
417
|
+
*/
|
|
418
|
+
controlAccountId: unknown;
|
|
419
|
+
/**
|
|
420
|
+
* Field name on each journal item that holds the partner reference.
|
|
421
|
+
* Default: `'partnerId'`. Whatever you declared in
|
|
422
|
+
* `schemaOptions.journalEntry.extraItemFields`.
|
|
423
|
+
*/
|
|
424
|
+
partnerField?: string;
|
|
425
|
+
/**
|
|
426
|
+
* The specific partner whose statement we're generating. Required —
|
|
427
|
+
* to get all partners use `generateAgedBalance` instead.
|
|
428
|
+
*/
|
|
429
|
+
partnerId: unknown;
|
|
430
|
+
startDate: Date;
|
|
431
|
+
endDate: Date;
|
|
432
|
+
/**
|
|
433
|
+
* If true, include items already matched (settled) inside the period.
|
|
434
|
+
* Default: true — statements show settled activity in the period.
|
|
435
|
+
*/
|
|
436
|
+
includeMatched?: boolean;
|
|
437
|
+
/** Custom aged buckets for the open-items summary. */
|
|
438
|
+
buckets?: AgedBucketConfig[];
|
|
439
|
+
}
|
|
440
|
+
interface PartnerLedgerLine {
|
|
441
|
+
date: Date;
|
|
442
|
+
entry: unknown;
|
|
443
|
+
itemIndex: number;
|
|
444
|
+
referenceNumber?: string;
|
|
445
|
+
label?: string;
|
|
446
|
+
debit: number;
|
|
447
|
+
credit: number;
|
|
448
|
+
/** Running balance (debit - credit, signed) including this row. */
|
|
449
|
+
balance: number;
|
|
450
|
+
matchingNumber: string | null;
|
|
451
|
+
maturityDate?: Date | null;
|
|
452
|
+
/** Days past `maturityDate` as of `endDate`; null if no maturity set. */
|
|
453
|
+
daysPastDue: number | null;
|
|
454
|
+
isMatched: boolean;
|
|
455
|
+
}
|
|
456
|
+
interface PartnerLedgerReport {
|
|
457
|
+
metadata: {
|
|
458
|
+
generatedAt: string;
|
|
459
|
+
partnerId: unknown;
|
|
460
|
+
controlAccount: {
|
|
461
|
+
id: unknown;
|
|
462
|
+
name?: string;
|
|
463
|
+
code?: string;
|
|
464
|
+
};
|
|
465
|
+
period: {
|
|
466
|
+
startDate: string;
|
|
467
|
+
endDate: string;
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
openingBalance: number;
|
|
471
|
+
closingBalance: number;
|
|
472
|
+
openItemsTotal: number;
|
|
473
|
+
matchedTotal: number;
|
|
474
|
+
lines: PartnerLedgerLine[];
|
|
475
|
+
agedBuckets: Record<string, number>;
|
|
476
|
+
}
|
|
477
|
+
declare function generatePartnerLedger(opts: PartnerLedgerOptions, params: PartnerLedgerParams): Promise<PartnerLedgerReport>;
|
|
478
|
+
//#endregion
|
|
406
479
|
//#region src/utils/revaluation.d.ts
|
|
407
480
|
/**
|
|
408
481
|
* Foreign Exchange Revaluation Utilities
|
|
@@ -539,4 +612,4 @@ declare function generateTrialBalance(opts: TrialBalanceOptions, params: {
|
|
|
539
612
|
filters?: Record<string, unknown>;
|
|
540
613
|
}): Promise<TrialBalanceReport>;
|
|
541
614
|
//#endregion
|
|
542
|
-
export {
|
|
615
|
+
export { TrialBalanceRow as $, generateDimensionBreakdown as A, BalanceSheetReport as B, FiscalReopenResult as C, DimensionBreakdownParams as D, DimensionBreakdownOptions as E, BudgetVsActualReport as F, IncomeStatementReport as G, CashFlowSection as H, BudgetVsActualRow as I, ReportCategory as J, LedgerEntry as K, generateBudgetVsActual as L, generateCashFlow as M, BudgetVsActualOptions as N, DimensionBreakdownReport as O, BudgetVsActualParams as P, TrialBalanceReport as Q, BalanceSheetOptions as R, FiscalCloseResult as S, reopenFiscalPeriod as T, GeneralLedgerAccount as U, CashFlowReport as V, GeneralLedgerReport as W, TaxReport as X, ReportGroup as Y, TaxReturnSummary as Z, IncomeStatementOptions as _, RevaluationReport as a, DEFAULT_BUCKETS as at, generateGeneralLedger as b, RevaluationRate as c, defaultLogger as ct, computeRevaluation as d, AgedBalanceOptions as et, PartnerLedgerLine as f, generatePartnerLedger as g, PartnerLedgerReport as h, RevaluationParams as i, AgedBucketConfig as it, CashFlowOptions as j, DimensionBreakdownRow as k, RevaluationResult as l, PartnerLedgerParams as m, generateTrialBalance as n, AgedBalanceReport as nt, generateRevaluation as o, generateAgedBalance as ot, PartnerLedgerOptions as p, ReportAccount as q, RevaluationOptions as r, AgedBalanceRow as rt, AccountForeignBalance as s, Logger as st, TrialBalanceOptions as t, AgedBalanceParams as tt, buildRevaluationEntry as u, generateIncomeStatement as v, closeFiscalPeriod as w, FiscalCloseOptions as x, GeneralLedgerOptions as y, generateBalanceSheet as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/ledger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Production-grade double-entry accounting engine for MongoDB — schemas, reports, tax, multi-tenant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"url": "git+https://github.com/classytic/accounting.git"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@classytic/mongokit": ">=3.5.
|
|
81
|
+
"@classytic/mongokit": ">=3.5.5",
|
|
82
82
|
"mongoose": ">=9.4.1"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
@@ -96,12 +96,13 @@
|
|
|
96
96
|
"format": "biome format src/ --write",
|
|
97
97
|
"check": "biome ci src/ --diagnostic-level=error",
|
|
98
98
|
"knip": "knip",
|
|
99
|
-
"
|
|
100
|
-
"
|
|
99
|
+
"smoke": "node scripts/smoke.mjs",
|
|
100
|
+
"prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test && npm run smoke",
|
|
101
|
+
"release": "npm run check && npm run build && npm run typecheck && npm test && npm run smoke && npm publish --access public"
|
|
101
102
|
},
|
|
102
103
|
"devDependencies": {
|
|
103
104
|
"@biomejs/biome": "^2.4.10",
|
|
104
|
-
"@classytic/mongokit": "^3.5.
|
|
105
|
+
"@classytic/mongokit": "^3.5.5",
|
|
105
106
|
"@types/node": "^22.0.0",
|
|
106
107
|
"@vitest/coverage-v8": "^3.2.4",
|
|
107
108
|
"knip": "^6.3.0",
|