@classytic/ledger 0.1.3
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/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/account.repository-1C2sZvB2.d.mts +29 -0
- package/dist/account.repository-1C2sZvB2.d.mts.map +1 -0
- package/dist/account.repository-Crf5DGO4.mjs +393 -0
- package/dist/account.repository-Crf5DGO4.mjs.map +1 -0
- package/dist/categories-BNJBd4ze.mjs +70 -0
- package/dist/categories-BNJBd4ze.mjs.map +1 -0
- package/dist/constants/index.d.mts +2 -0
- package/dist/constants/index.mjs +5 -0
- package/dist/core-Cx0baosR.d.mts +104 -0
- package/dist/core-Cx0baosR.d.mts.map +1 -0
- package/dist/country/index.d.mts +105 -0
- package/dist/country/index.d.mts.map +1 -0
- package/dist/country/index.mjs +27 -0
- package/dist/country/index.mjs.map +1 -0
- package/dist/currencies-BBk3NwXn.mjs +82 -0
- package/dist/currencies-BBk3NwXn.mjs.map +1 -0
- package/dist/currencies-Bkn3FNkC.d.mts +38 -0
- package/dist/currencies-Bkn3FNkC.d.mts.map +1 -0
- package/dist/engine-Cd73EOT6.d.mts +72 -0
- package/dist/engine-Cd73EOT6.d.mts.map +1 -0
- package/dist/errors-CeqRahE-.mjs +28 -0
- package/dist/errors-CeqRahE-.mjs.map +1 -0
- package/dist/exports/index.d.mts +2 -0
- package/dist/exports/index.mjs +3 -0
- package/dist/fiscal-close-CNOwv_ud.mjs +934 -0
- package/dist/fiscal-close-CNOwv_ud.mjs.map +1 -0
- package/dist/fiscal-close-CzUzpnMg.d.mts +270 -0
- package/dist/fiscal-close-CzUzpnMg.d.mts.map +1 -0
- package/dist/fiscal-period.schema-CbALaaKl.mjs +477 -0
- package/dist/fiscal-period.schema-CbALaaKl.mjs.map +1 -0
- package/dist/fiscal-period.schema-DI2scngu.d.mts +38 -0
- package/dist/fiscal-period.schema-DI2scngu.d.mts.map +1 -0
- package/dist/idempotency.plugin-BESs9YPD.d.mts +58 -0
- package/dist/idempotency.plugin-BESs9YPD.d.mts.map +1 -0
- package/dist/idempotency.plugin-C6r8RI8d.mjs +165 -0
- package/dist/idempotency.plugin-C6r8RI8d.mjs.map +1 -0
- package/dist/index.d.mts +308 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +171 -0
- package/dist/index.mjs.map +1 -0
- package/dist/journals-CI3Wb4EF.mjs +92 -0
- package/dist/journals-CI3Wb4EF.mjs.map +1 -0
- package/dist/logger-Cv6VVc4r.d.mts +15 -0
- package/dist/logger-Cv6VVc4r.d.mts.map +1 -0
- package/dist/money.d.mts +129 -0
- package/dist/money.d.mts.map +1 -0
- package/dist/money.mjs +197 -0
- package/dist/money.mjs.map +1 -0
- package/dist/plugins/index.d.mts +2 -0
- package/dist/plugins/index.mjs +3 -0
- package/dist/reports/index.d.mts +2 -0
- package/dist/reports/index.mjs +3 -0
- package/dist/repositories/index.d.mts +2 -0
- package/dist/repositories/index.mjs +3 -0
- package/dist/schemas/index.d.mts +2 -0
- package/dist/schemas/index.mjs +3 -0
- package/dist/session-Dh0s6zG4.mjs +87 -0
- package/dist/session-Dh0s6zG4.mjs.map +1 -0
- package/dist/universal-CMfrZ2hG.mjs +257 -0
- package/dist/universal-CMfrZ2hG.mjs.map +1 -0
- package/dist/universal-x33ZJODp.d.mts +137 -0
- package/dist/universal-x33ZJODp.d.mts.map +1 -0
- package/docs/country-packs.md +117 -0
- package/docs/engine.md +147 -0
- package/docs/exports.md +81 -0
- package/docs/money.md +81 -0
- package/docs/plugins.md +136 -0
- package/docs/reports.md +154 -0
- package/docs/repositories.md +239 -0
- package/docs/schemas.md +146 -0
- package/docs/subledger-integration.md +287 -0
- package/package.json +116 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Subledger Integration
|
|
2
|
+
|
|
3
|
+
This guide explains how to integrate external subledgers (billing, inventory, payroll, etc.) with `@classytic/ledger`. The ledger provides **type-only posting contracts** — your application code is responsible for wiring subledgers to the journal entry repository.
|
|
4
|
+
|
|
5
|
+
## Responsibility Boundaries
|
|
6
|
+
|
|
7
|
+
| Concern | Ledger (`@classytic/ledger`) | App / Subledger Package |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Double-entry validation | Yes — plugin enforces `debits === credits` | — |
|
|
10
|
+
| Account existence check | Yes — plugin validates account ObjectIds | — |
|
|
11
|
+
| Cross-tenant integrity | Yes — plugin ensures accounts belong to same org | — |
|
|
12
|
+
| Fiscal period lock | Yes — plugin blocks posting into closed periods | — |
|
|
13
|
+
| Idempotency (duplicate guard) | Yes — plugin checks `idempotencyKey` uniqueness | Must generate deterministic keys |
|
|
14
|
+
| Posted-entry protection | Yes — plugin blocks field changes on posted entries; fully immutable when `strictness.immutable` enabled | Use `reverse()` for corrections; `unpost()` available when immutable mode is off |
|
|
15
|
+
| Account code → ObjectId resolution | No | Yes — look up account by `accountType` code |
|
|
16
|
+
| Tax calculation | No — country packs define tax codes, not tax logic | Yes — compute tax amounts before posting |
|
|
17
|
+
| Source document validation | No | Yes — implement `validate()` on the contract |
|
|
18
|
+
| Creating the journal entry | No — provides `repo.post()` | Yes — call `repo.create()` then `repo.post()` |
|
|
19
|
+
| Transaction coordination | No | Yes — wrap subledger + ledger writes in a session |
|
|
20
|
+
| Approval workflow | Enforces `approvedBy`/`approvedAt` if `requireApproval` is on | Yes — set these fields before calling `post()` |
|
|
21
|
+
| Reversal coordination | Provides `repo.reverse()` for the ledger side | Yes — update subledger state (void invoice, etc.) |
|
|
22
|
+
|
|
23
|
+
**In short:** the ledger validates and stores journal entries. Everything upstream — deciding _what_ to post, _when_ to post, and _how_ to resolve account codes — is the application's job.
|
|
24
|
+
|
|
25
|
+
## Posting Contracts
|
|
26
|
+
|
|
27
|
+
The ledger exports four TypeScript interfaces for structuring subledger integrations. These are **type-only** — they carry no runtime behavior or dependencies.
|
|
28
|
+
|
|
29
|
+
### `PostingContract<TSource>`
|
|
30
|
+
|
|
31
|
+
The top-level interface that a subledger adapter implements:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import type { PostingContract } from '@classytic/ledger';
|
|
35
|
+
|
|
36
|
+
interface PostingContract<TSource = unknown> {
|
|
37
|
+
readonly name: string; // e.g. 'billing', 'inventory'
|
|
38
|
+
toJournalEntries(source: TSource): SubledgerPostingInput[];
|
|
39
|
+
validate(source: TSource): void; // throws on failure
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### `SubledgerPostingInput`
|
|
44
|
+
|
|
45
|
+
The shape of a journal entry that the subledger produces:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
interface SubledgerPostingInput {
|
|
49
|
+
journalType: string; // e.g. 'SALE', 'PURCHASE', 'PAYROLL'
|
|
50
|
+
label: string; // human-readable description
|
|
51
|
+
date: Date; // posting date
|
|
52
|
+
journalItems: SubledgerJournalItem[]; // debit/credit lines
|
|
53
|
+
idempotencyKey?: string; // prevents duplicate postings on retry
|
|
54
|
+
metadata?: Record<string, unknown>; // arbitrary extra data
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### `SubledgerJournalItem`
|
|
59
|
+
|
|
60
|
+
A single debit or credit line:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
interface SubledgerJournalItem {
|
|
64
|
+
accountCode: string; // account type code (e.g. '1000', '4000')
|
|
65
|
+
debit: number; // integer cents
|
|
66
|
+
credit: number; // integer cents
|
|
67
|
+
label?: string; // line description
|
|
68
|
+
extraFields?: Record<string, unknown>; // dimension fields (departmentId, etc.)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `PostingResult`
|
|
73
|
+
|
|
74
|
+
Returned by your posting function after entries are created:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
interface PostingResult {
|
|
78
|
+
journalEntryIds: (string | ObjectId)[];
|
|
79
|
+
idempotencyKeys?: string[];
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Integration Pattern
|
|
84
|
+
|
|
85
|
+
A typical subledger integration has three layers:
|
|
86
|
+
|
|
87
|
+
1. **Contract** — maps source documents to journal entry inputs (pure logic, no DB calls)
|
|
88
|
+
2. **Resolver** — converts account type codes to ObjectIds for the current tenant
|
|
89
|
+
3. **Poster** — creates and posts journal entries via the ledger repository
|
|
90
|
+
|
|
91
|
+
### Step 1: Implement the Contract
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import type { PostingContract, SubledgerPostingInput } from '@classytic/ledger';
|
|
95
|
+
|
|
96
|
+
interface Invoice {
|
|
97
|
+
_id: string;
|
|
98
|
+
number: string;
|
|
99
|
+
date: Date;
|
|
100
|
+
lineItems: Array<{
|
|
101
|
+
description: string;
|
|
102
|
+
amount: number; // integer cents
|
|
103
|
+
taxAmount: number; // integer cents
|
|
104
|
+
departmentId?: string;
|
|
105
|
+
}>;
|
|
106
|
+
totalAmount: number;
|
|
107
|
+
totalTax: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const billingContract: PostingContract<Invoice> = {
|
|
111
|
+
name: 'billing',
|
|
112
|
+
|
|
113
|
+
validate(invoice) {
|
|
114
|
+
if (!invoice.lineItems.length) throw new Error('Invoice has no line items');
|
|
115
|
+
if (invoice.totalAmount <= 0) throw new Error('Invoice total must be positive');
|
|
116
|
+
// Add your business-specific validations here
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
toJournalEntries(invoice): SubledgerPostingInput[] {
|
|
120
|
+
const items = invoice.lineItems.flatMap((line) => [
|
|
121
|
+
{
|
|
122
|
+
accountCode: '4000', // Revenue
|
|
123
|
+
debit: 0,
|
|
124
|
+
credit: line.amount,
|
|
125
|
+
label: line.description,
|
|
126
|
+
extraFields: line.departmentId ? { departmentId: line.departmentId } : undefined,
|
|
127
|
+
},
|
|
128
|
+
...(line.taxAmount > 0
|
|
129
|
+
? [{
|
|
130
|
+
accountCode: '2100', // Tax Payable
|
|
131
|
+
debit: 0,
|
|
132
|
+
credit: line.taxAmount,
|
|
133
|
+
label: `Tax – ${line.description}`,
|
|
134
|
+
}]
|
|
135
|
+
: []),
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
items.push({
|
|
139
|
+
accountCode: '1200', // Accounts Receivable
|
|
140
|
+
debit: invoice.totalAmount + invoice.totalTax,
|
|
141
|
+
credit: 0,
|
|
142
|
+
label: `Invoice ${invoice.number}`,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return [{
|
|
146
|
+
journalType: 'SALE',
|
|
147
|
+
label: `Invoice ${invoice.number}`,
|
|
148
|
+
date: invoice.date,
|
|
149
|
+
journalItems: items,
|
|
150
|
+
idempotencyKey: `billing:invoice:${invoice._id}`,
|
|
151
|
+
}];
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Step 2: Resolve Account Codes
|
|
157
|
+
|
|
158
|
+
The contract produces account type codes (strings like `'4000'`). Your app must resolve these to real account ObjectIds for the tenant:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
async function resolveAccounts(
|
|
162
|
+
input: SubledgerPostingInput,
|
|
163
|
+
organizationId: string,
|
|
164
|
+
AccountModel: Model<any>,
|
|
165
|
+
): Promise<Array<{ account: ObjectId; debit: number; credit: number; label?: string; [key: string]: unknown }>> {
|
|
166
|
+
// Build a map of accountCode → ObjectId for this tenant
|
|
167
|
+
const codes = [...new Set(input.journalItems.map((i) => i.accountCode))];
|
|
168
|
+
const accounts = await AccountModel.find({
|
|
169
|
+
business: organizationId,
|
|
170
|
+
accountTypeCode: { $in: codes },
|
|
171
|
+
}).lean();
|
|
172
|
+
|
|
173
|
+
const codeToId = new Map(accounts.map((a) => [a.accountTypeCode, a._id]));
|
|
174
|
+
|
|
175
|
+
return input.journalItems.map((item) => {
|
|
176
|
+
const accountId = codeToId.get(item.accountCode);
|
|
177
|
+
if (!accountId) throw new Error(`No account found for type ${item.accountCode} in org ${organizationId}`);
|
|
178
|
+
return {
|
|
179
|
+
account: accountId,
|
|
180
|
+
debit: item.debit,
|
|
181
|
+
credit: item.credit,
|
|
182
|
+
label: item.label,
|
|
183
|
+
...item.extraFields,
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Step 3: Create and Post
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
async function postInvoice(
|
|
193
|
+
invoice: Invoice,
|
|
194
|
+
organizationId: string,
|
|
195
|
+
journalRepo: any,
|
|
196
|
+
AccountModel: Model<any>,
|
|
197
|
+
session?: ClientSession,
|
|
198
|
+
) {
|
|
199
|
+
// 1. Validate
|
|
200
|
+
billingContract.validate(invoice);
|
|
201
|
+
|
|
202
|
+
// 2. Map to journal inputs
|
|
203
|
+
const [input] = billingContract.toJournalEntries(invoice);
|
|
204
|
+
|
|
205
|
+
// 3. Resolve account codes → ObjectIds
|
|
206
|
+
const journalItems = await resolveAccounts(input, organizationId, AccountModel);
|
|
207
|
+
|
|
208
|
+
// 4. Create draft entry
|
|
209
|
+
const entry = await journalRepo.create({
|
|
210
|
+
business: organizationId,
|
|
211
|
+
journalType: input.journalType,
|
|
212
|
+
label: input.label,
|
|
213
|
+
date: input.date,
|
|
214
|
+
journalItems,
|
|
215
|
+
idempotencyKey: input.idempotencyKey,
|
|
216
|
+
}, { session });
|
|
217
|
+
|
|
218
|
+
// 5. Post (triggers double-entry validation, fiscal lock, idempotency)
|
|
219
|
+
await journalRepo.post(entry._id, organizationId, { session });
|
|
220
|
+
|
|
221
|
+
return { journalEntryIds: [entry._id], idempotencyKeys: [input.idempotencyKey!] };
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Idempotency
|
|
226
|
+
|
|
227
|
+
Subledger integrations should always set `idempotencyKey` to prevent duplicate postings from retries, queue redelivery, or webhook replays.
|
|
228
|
+
|
|
229
|
+
Key format convention: `{subledger}:{document-type}:{document-id}`
|
|
230
|
+
|
|
231
|
+
Examples:
|
|
232
|
+
- `billing:invoice:INV-001`
|
|
233
|
+
- `inventory:receipt:RCV-1234`
|
|
234
|
+
- `payroll:run:PR-2025-03`
|
|
235
|
+
|
|
236
|
+
The ledger's idempotency plugin returns a 409 Conflict if a journal entry with the same key already exists. Your posting code should catch this and treat it as a no-op (the entry was already posted).
|
|
237
|
+
|
|
238
|
+
**Prerequisite:** Enable `idempotency: true` in the engine config so the schema includes the `idempotencyKey` field with its unique sparse index.
|
|
239
|
+
|
|
240
|
+
## Dimension Fields
|
|
241
|
+
|
|
242
|
+
If your subledger items carry dimension fields (`departmentId`, `projectId`, etc.), these must be:
|
|
243
|
+
|
|
244
|
+
1. Declared in `extraItemFields` when creating the journal entry schema
|
|
245
|
+
2. Passed via `extraFields` on `SubledgerJournalItem` (contract level)
|
|
246
|
+
3. Spread onto journal items during account resolution (see Step 2 above)
|
|
247
|
+
|
|
248
|
+
Once posted, dimension fields are:
|
|
249
|
+
- Preserved through `duplicate()` and `reverse()` operations
|
|
250
|
+
- Available as filters on all report types (trial balance, balance sheet, income statement, general ledger, cash flow)
|
|
251
|
+
|
|
252
|
+
## Reversal Coordination
|
|
253
|
+
|
|
254
|
+
To void or reverse a subledger transaction:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// 1. Reverse the ledger entry
|
|
258
|
+
await journalRepo.reverse(journalEntryId, organizationId, {
|
|
259
|
+
actorId: userId,
|
|
260
|
+
session,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// 2. Update subledger state (your responsibility)
|
|
264
|
+
await Invoice.updateOne({ _id: invoiceId }, { status: 'voided' }, { session });
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The ledger's `reverse()` creates a new journal entry with debits and credits swapped, links it to the original via `reversedBy`, and marks the original as `reversed: true`. All dimension fields from the original entry are preserved on the reversal.
|
|
268
|
+
|
|
269
|
+
## Tax Handling
|
|
270
|
+
|
|
271
|
+
The ledger's country packs define **tax code metadata** (names, rates, regions) but do **not** compute tax amounts. Tax calculation is the application's responsibility:
|
|
272
|
+
|
|
273
|
+
1. Look up applicable tax codes from the country pack (`accounting.getTaxCodesForRegion('ON')`)
|
|
274
|
+
2. Compute tax amounts in your subledger / business logic
|
|
275
|
+
3. Include tax lines as separate `SubledgerJournalItem` entries with the appropriate tax liability account code
|
|
276
|
+
4. The ledger stores and reports on whatever you post — it does not validate tax arithmetic
|
|
277
|
+
|
|
278
|
+
## What the Ledger Does Not Do
|
|
279
|
+
|
|
280
|
+
To set clear expectations, the ledger intentionally does **not**:
|
|
281
|
+
|
|
282
|
+
- **Compute taxes** — country packs provide tax code catalogs, not calculation engines
|
|
283
|
+
- **Manage invoices, bills, or payments** — these are subledger concerns
|
|
284
|
+
- **Orchestrate multi-step workflows** — approval routing, email notifications, etc. are app-level
|
|
285
|
+
- **Resolve account codes to ObjectIds** — the app must map country-pack codes to tenant accounts
|
|
286
|
+
- **Coordinate distributed transactions** — the app must wrap cross-collection writes in a MongoDB session
|
|
287
|
+
- **Generate direct-method cash flow** — the cash flow report classifies by `cashFlowCategory` on account types (indirect method only)
|
package/package.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@classytic/ledger",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Production-grade double-entry accounting engine for MongoDB — schemas, reports, tax, multi-tenant",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.mjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.mts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"default": "./dist/index.mjs"
|
|
15
|
+
},
|
|
16
|
+
"./money": {
|
|
17
|
+
"types": "./dist/money.d.mts",
|
|
18
|
+
"import": "./dist/money.mjs",
|
|
19
|
+
"default": "./dist/money.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./schemas": {
|
|
22
|
+
"types": "./dist/schemas/index.d.mts",
|
|
23
|
+
"import": "./dist/schemas/index.mjs",
|
|
24
|
+
"default": "./dist/schemas/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./reports": {
|
|
27
|
+
"types": "./dist/reports/index.d.mts",
|
|
28
|
+
"import": "./dist/reports/index.mjs",
|
|
29
|
+
"default": "./dist/reports/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./plugins": {
|
|
32
|
+
"types": "./dist/plugins/index.d.mts",
|
|
33
|
+
"import": "./dist/plugins/index.mjs",
|
|
34
|
+
"default": "./dist/plugins/index.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./constants": {
|
|
37
|
+
"types": "./dist/constants/index.d.mts",
|
|
38
|
+
"import": "./dist/constants/index.mjs",
|
|
39
|
+
"default": "./dist/constants/index.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./country": {
|
|
42
|
+
"types": "./dist/country/index.d.mts",
|
|
43
|
+
"import": "./dist/country/index.mjs",
|
|
44
|
+
"default": "./dist/country/index.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./repositories": {
|
|
47
|
+
"types": "./dist/repositories/index.d.mts",
|
|
48
|
+
"import": "./dist/repositories/index.mjs",
|
|
49
|
+
"default": "./dist/repositories/index.mjs"
|
|
50
|
+
},
|
|
51
|
+
"./exports": {
|
|
52
|
+
"types": "./dist/exports/index.d.mts",
|
|
53
|
+
"import": "./dist/exports/index.mjs",
|
|
54
|
+
"default": "./dist/exports/index.mjs"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"dist",
|
|
59
|
+
"docs",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE"
|
|
62
|
+
],
|
|
63
|
+
"keywords": [
|
|
64
|
+
"accounting",
|
|
65
|
+
"double-entry",
|
|
66
|
+
"bookkeeping",
|
|
67
|
+
"ledger",
|
|
68
|
+
"journal",
|
|
69
|
+
"chart-of-accounts",
|
|
70
|
+
"financial-reports",
|
|
71
|
+
"balance-sheet",
|
|
72
|
+
"income-statement",
|
|
73
|
+
"trial-balance",
|
|
74
|
+
"general-ledger",
|
|
75
|
+
"tax",
|
|
76
|
+
"gst",
|
|
77
|
+
"hst",
|
|
78
|
+
"vat",
|
|
79
|
+
"multi-tenant",
|
|
80
|
+
"mongodb",
|
|
81
|
+
"mongoose",
|
|
82
|
+
"typescript"
|
|
83
|
+
],
|
|
84
|
+
"author": "Classytic (https://github.com/classytic)",
|
|
85
|
+
"license": "MIT",
|
|
86
|
+
"repository": {
|
|
87
|
+
"type": "git",
|
|
88
|
+
"url": "git+https://github.com/classytic/accounting.git"
|
|
89
|
+
},
|
|
90
|
+
"peerDependencies": {
|
|
91
|
+
"@classytic/mongokit": ">=3.3.2",
|
|
92
|
+
"mongoose": ">=9.0.0"
|
|
93
|
+
},
|
|
94
|
+
"engines": {
|
|
95
|
+
"node": ">=22"
|
|
96
|
+
},
|
|
97
|
+
"scripts": {
|
|
98
|
+
"build": "tsdown",
|
|
99
|
+
"dev": "tsdown --watch",
|
|
100
|
+
"test": "vitest run",
|
|
101
|
+
"test:watch": "vitest",
|
|
102
|
+
"test:coverage": "vitest run --coverage",
|
|
103
|
+
"typecheck": "tsc --noEmit",
|
|
104
|
+
"prepublishOnly": "npm run build && npm run typecheck",
|
|
105
|
+
"release": "npm run build && npm run typecheck && npm publish --access public"
|
|
106
|
+
},
|
|
107
|
+
"devDependencies": {
|
|
108
|
+
"@types/node": "^22.0.0",
|
|
109
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
110
|
+
"mongodb-memory-server": "^10.2.3",
|
|
111
|
+
"mongoose": "^9.3.1",
|
|
112
|
+
"tsdown": "^0.20.3",
|
|
113
|
+
"typescript": "^5.7.0",
|
|
114
|
+
"vitest": "^3.0.0"
|
|
115
|
+
}
|
|
116
|
+
}
|