@classytic/ledger 0.2.0 → 0.4.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 +161 -64
- package/dist/{categories-CclX7Q94.mjs → categories-FJlrvzcl.mjs} +0 -2
- package/dist/constants/index.d.mts +2 -2
- package/dist/constants/index.mjs +3 -3
- package/dist/core-8Xfnpn6g.d.mts +1 -2
- package/dist/country/index.d.mts +1 -1
- package/dist/country/index.mjs +0 -2
- package/dist/currencies-W8kQAkm0.mjs +0 -2
- package/dist/{idempotency.plugin-v9NQ_ta-.mjs → date-lock.plugin-C8kqPBjh.mjs} +51 -11
- package/dist/{engine-BzBMpWuy.d.mts → engine-DF-MtsEr.d.mts} +10 -6
- package/dist/{errors-B7yC-Jfw.mjs → errors-BoGUSUYL.mjs} +0 -2
- package/dist/exports/index.d.mts +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/{exports-I5Xkq-9_.mjs → exports-DoGQQtMQ.mjs} +96 -77
- package/dist/{fiscal-close-L631E3De.mjs → fiscal-close-DmPV82e4.mjs} +1000 -286
- package/dist/{idempotency.plugin-CPxPt4vX.d.mts → idempotency.plugin-zU-GKJ0-.d.mts} +19 -17
- package/dist/{index-ZnSiqHYV.d.mts → index-CxZqRaOU.d.mts} +20 -6
- package/dist/{index-BPukb3L8.d.mts → index-J-XIbXH-.d.mts} +7 -8
- package/dist/index.d.mts +280 -58
- package/dist/index.mjs +123 -25
- package/dist/journal-entry.schema-B1CzLwC3.d.mts +103 -0
- package/dist/{journals-oH-FK3g8.mjs → journals-BcMn71Cq.mjs} +27 -6
- package/dist/{currencies-4WAbFRlw.d.mts → journals-DTipb_rz.d.mts} +16 -8
- package/dist/logger-UbTdBb1x.d.mts +1 -2
- package/dist/money.d.mts +1 -2
- package/dist/money.mjs +5 -5
- package/dist/plugins/index.d.mts +38 -2
- package/dist/plugins/index.mjs +57 -2
- package/dist/reconciliation.repository-DEybU_Ok.d.mts +135 -0
- package/dist/{account.repository-kDKwDt0I.mjs → reconciliation.repository-DgJEDVS-.mjs} +361 -210
- package/dist/{fiscal-period.schema-BQ5wsAq3.mjs → reconciliation.schema-KScbsXbY.mjs} +235 -90
- package/dist/reports/index.d.mts +2 -2
- package/dist/reports/index.mjs +2 -2
- package/dist/repositories/index.d.mts +2 -2
- package/dist/repositories/index.mjs +2 -2
- package/dist/schemas/index.d.mts +71 -2
- package/dist/schemas/index.mjs +2 -2
- package/dist/tenant-guard-CAxXoWuS.mjs +13 -0
- package/dist/trial-balance-DcQ0xj_4.d.mts +530 -0
- package/docs/reports.md +1 -1
- package/package.json +14 -6
- package/dist/account.repository-C7gwFLfM.d.mts +0 -29
- package/dist/account.repository-C7gwFLfM.d.mts.map +0 -1
- package/dist/account.repository-kDKwDt0I.mjs.map +0 -1
- package/dist/categories-CclX7Q94.mjs.map +0 -1
- package/dist/core-8Xfnpn6g.d.mts.map +0 -1
- package/dist/country/index.mjs.map +0 -1
- package/dist/currencies-4WAbFRlw.d.mts.map +0 -1
- package/dist/currencies-W8kQAkm0.mjs.map +0 -1
- package/dist/engine-BzBMpWuy.d.mts.map +0 -1
- package/dist/errors-B7yC-Jfw.mjs.map +0 -1
- package/dist/exports-I5Xkq-9_.mjs.map +0 -1
- package/dist/fiscal-close-L631E3De.mjs.map +0 -1
- package/dist/fiscal-close-dNlzB37y.d.mts +0 -270
- package/dist/fiscal-close-dNlzB37y.d.mts.map +0 -1
- package/dist/fiscal-period.schema-BQ5wsAq3.mjs.map +0 -1
- package/dist/fiscal-period.schema-BRdKAjrr.d.mts +0 -38
- package/dist/fiscal-period.schema-BRdKAjrr.d.mts.map +0 -1
- package/dist/idempotency.plugin-CPxPt4vX.d.mts.map +0 -1
- package/dist/idempotency.plugin-v9NQ_ta-.mjs.map +0 -1
- package/dist/index-BPukb3L8.d.mts.map +0 -1
- package/dist/index-ZnSiqHYV.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/journals-oH-FK3g8.mjs.map +0 -1
- package/dist/logger-UbTdBb1x.d.mts.map +0 -1
- package/dist/money.d.mts.map +0 -1
- package/dist/money.mjs.map +0 -1
- package/dist/session-Ba8E3Ufa.mjs +0 -84
- package/dist/session-Ba8E3Ufa.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
# @classytic/ledger
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **Double-entry bookkeeping** with balance validation and posted-entry protection (optionally immutable via strictness config)
|
|
8
|
-
- **Multi-tenant** isolation via configurable org field
|
|
9
|
-
- **Country packs** for localized chart of accounts and tax codes
|
|
10
|
-
- **Financial reports** — trial balance, balance sheet, income statement, general ledger, cash flow
|
|
11
|
-
- **Fiscal period management** — close and reopen with automatic year-end entries, overlap protection
|
|
12
|
-
- **CSV export** — QuickBooks-compatible and universal field maps
|
|
13
|
-
- **Cents-based Money** arithmetic for precision
|
|
14
|
-
- **Plugin system** — fiscal lock, double-entry validation, idempotency (via mongokit hooks)
|
|
15
|
-
- **Dimension fields** — custom fields on journal items (departmentId, projectId, etc.) preserved through all workflows
|
|
16
|
-
- **Dimension filters** — filter all reports by custom journal item fields
|
|
17
|
-
- **Strictness controls** — configurable immutability, actor tracking, and approval requirements
|
|
18
|
-
- **Subledger contracts** — typed interfaces for integrating billing, inventory, payroll, and other subledgers
|
|
3
|
+
Embeddable double-entry accounting engine for MongoDB. Integer-cents arithmetic, plugin-based, country-agnostic.
|
|
4
|
+
|
|
5
|
+
Build QuickBooks, Xero, or TaxCycle-grade apps — the engine handles the accounting, you handle the UX.
|
|
19
6
|
|
|
20
7
|
## Install
|
|
21
8
|
|
|
22
9
|
```bash
|
|
23
10
|
npm install @classytic/ledger @classytic/mongokit mongoose
|
|
11
|
+
npm install @classytic/ledger-ca # Canada (GIFI, GST/HST, CRA)
|
|
12
|
+
npm install @classytic/ledger-bd # Bangladesh (BFRS, VAT/TDS, Mushak)
|
|
24
13
|
```
|
|
25
14
|
|
|
26
15
|
## Quick Start
|
|
@@ -28,80 +17,188 @@ npm install @classytic/ledger @classytic/mongokit mongoose
|
|
|
28
17
|
```typescript
|
|
29
18
|
import { createAccountingEngine } from '@classytic/ledger';
|
|
30
19
|
import { canadaPack } from '@classytic/ledger-ca';
|
|
31
|
-
import mongoose from 'mongoose';
|
|
32
20
|
|
|
33
|
-
// 1. Create engine
|
|
34
21
|
const accounting = createAccountingEngine({
|
|
35
22
|
country: canadaPack,
|
|
36
23
|
currency: 'CAD',
|
|
37
|
-
multiTenant: { orgField: '
|
|
38
|
-
audit: { trackActor: true },
|
|
39
|
-
idempotency: true,
|
|
40
|
-
strictness: { immutable: true, requireActor: true },
|
|
24
|
+
multiTenant: { orgField: 'organization', orgRef: 'Organization' },
|
|
41
25
|
});
|
|
42
26
|
|
|
43
|
-
//
|
|
27
|
+
// Schemas
|
|
44
28
|
const Account = mongoose.model('Account', accounting.createAccountSchema());
|
|
45
|
-
const JournalEntry = mongoose.model('JournalEntry', accounting.createJournalEntrySchema('Account'
|
|
46
|
-
extraItemFields: {
|
|
47
|
-
departmentId: { type: mongoose.Schema.Types.ObjectId, ref: 'Department' },
|
|
48
|
-
},
|
|
49
|
-
}));
|
|
29
|
+
const JournalEntry = mongoose.model('JournalEntry', accounting.createJournalEntrySchema('Account'));
|
|
50
30
|
const FiscalPeriod = mongoose.model('FiscalPeriod', accounting.createFiscalPeriodSchema());
|
|
51
31
|
|
|
52
|
-
//
|
|
53
|
-
|
|
32
|
+
// Reports
|
|
33
|
+
const reports = accounting.createReports({ Account, JournalEntry });
|
|
34
|
+
const bs = await reports.balanceSheet({ organizationId, dateOption: 'year', dateValue: 2025 });
|
|
35
|
+
```
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
## Core Features
|
|
38
|
+
|
|
39
|
+
**Accounting Engine**
|
|
40
|
+
- Double-entry validation with balance enforcement
|
|
41
|
+
- Integer-cents storage — zero floating-point drift
|
|
42
|
+
- Draft → Posted → Reversed state machine
|
|
43
|
+
- Configurable immutability (corrections only via reversal)
|
|
44
|
+
- Multi-tenant isolation at every layer
|
|
45
|
+
- Country packs for localized charts of accounts and tax codes
|
|
46
|
+
|
|
47
|
+
**10 Reports**
|
|
48
|
+
- Trial Balance (3-column: initial + period + ending)
|
|
49
|
+
- Balance Sheet (with computed retained earnings)
|
|
50
|
+
- Income Statement (revenue, COGS, gross profit, operating expenses, net income)
|
|
51
|
+
- General Ledger (per-account with running balances)
|
|
52
|
+
- Cash Flow (Operating / Investing / Financing)
|
|
53
|
+
- Aged Receivable / Payable (configurable buckets: current, 30, 60, 90+)
|
|
54
|
+
- Budget vs Actual (variance analysis)
|
|
55
|
+
- Dimension Breakdown (by department, project, cost center)
|
|
56
|
+
- Foreign Exchange Revaluation (unrealized gain/loss computation)
|
|
57
|
+
- Fiscal Year Close / Reopen (automatic closing entries)
|
|
58
|
+
|
|
59
|
+
**Plugins**
|
|
60
|
+
- `doubleEntryPlugin` — validates debits = credits, account existence, tenant integrity
|
|
61
|
+
- `fiscalLockPlugin` — prevents posting to closed fiscal periods
|
|
62
|
+
- `dateLockPlugin` — blocks entries before a configurable lock date
|
|
63
|
+
- `taxHookPlugin` — auto-generates tax lines via user-defined `TaxLineGenerator`
|
|
64
|
+
- `idempotencyPlugin` — prevents duplicate entries by key
|
|
65
|
+
|
|
66
|
+
**Utilities**
|
|
67
|
+
- `Money` — cents arithmetic, tax splitting, allocation with zero-sum guarantee
|
|
68
|
+
- `buildDimensionFields` — schema helpers for analytic dimensions
|
|
69
|
+
- `suggestMatches` — reconciliation matching suggestions
|
|
70
|
+
- `computeRevaluation` — FX gain/loss computation
|
|
71
|
+
|
|
72
|
+
## Engine Configuration
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
```typescript
|
|
75
|
+
createAccountingEngine({
|
|
76
|
+
country: canadaPack, // required
|
|
77
|
+
currency: 'CAD', // required — base/functional currency
|
|
78
|
+
multiTenant: { orgField, orgRef }, // optional — multi-tenant scoping
|
|
79
|
+
multiCurrency: { enabled: true, currencies: ['USD', 'EUR'] },
|
|
80
|
+
fiscalYearStartMonth: 1, // 1=Jan (default), 4=Apr, 7=Jul
|
|
81
|
+
retainedEarningsAccountCode: '3600', // overrides country pack
|
|
82
|
+
audit: { trackActor: true },
|
|
83
|
+
idempotency: true,
|
|
84
|
+
strictness: {
|
|
85
|
+
immutable: true, // disable unpost, corrections via reverse only
|
|
86
|
+
requireActor: true, // actorId required on post/reverse
|
|
87
|
+
requireApproval: true // entries must be approved before posting
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
62
91
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
## Reports API
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const reports = accounting.createReports({ Account, JournalEntry, Budget });
|
|
96
|
+
|
|
97
|
+
// All reports accept: { organizationId, dateOption, dateValue, filters? }
|
|
98
|
+
await reports.trialBalance({ ... });
|
|
99
|
+
await reports.balanceSheet({ ... });
|
|
100
|
+
await reports.incomeStatement({ ... });
|
|
101
|
+
await reports.generalLedger({ ... });
|
|
102
|
+
await reports.cashFlow({ ... });
|
|
103
|
+
await reports.agedBalance({ type: 'receivable', asOfDate: new Date() });
|
|
104
|
+
await reports.budgetVsActual({ ... }); // requires Budget model
|
|
105
|
+
await reports.dimensionBreakdown({ dimension: 'departmentId', ... });
|
|
106
|
+
await reports.revaluation({ rates: [{ currency: 'USD', rate: 1.40 }], ... });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
All report data is sorted by account code. All monetary values are integer cents — use `Money.toDecimal()` at your API boundary.
|
|
110
|
+
|
|
111
|
+
## Schemas
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
accounting.createAccountSchema(options?)
|
|
115
|
+
accounting.createJournalEntrySchema(accountModelName, {
|
|
116
|
+
extraItemFields: { departmentId: { type: ObjectId, ref: 'Department' } },
|
|
117
|
+
})
|
|
118
|
+
accounting.createFiscalPeriodSchema(options?)
|
|
119
|
+
accounting.createBudgetSchema(options?)
|
|
120
|
+
accounting.createReconciliationSchema(accountModelName, journalEntryModelName, options?)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Plugins
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { dateLockPlugin, taxHookPlugin } from '@classytic/ledger';
|
|
127
|
+
|
|
128
|
+
// Date lock — block posting before a date
|
|
129
|
+
dateLockPlugin({
|
|
130
|
+
getLockDate: async (orgId) => db.getOrgLockDate(orgId),
|
|
131
|
+
JournalEntryModel,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Tax hook — auto-generate tax lines
|
|
135
|
+
taxHookPlugin({
|
|
136
|
+
generator: {
|
|
137
|
+
generateTaxLines(input) {
|
|
138
|
+
if (!input.taxCode) return [];
|
|
139
|
+
const tax = Money.percentage(input.amount, 1300); // 13%
|
|
140
|
+
return [{ account: hstAccountId, debit: 0, credit: tax, taxDetails: [{ taxCode: 'HST' }] }];
|
|
141
|
+
},
|
|
142
|
+
},
|
|
70
143
|
});
|
|
71
144
|
```
|
|
72
145
|
|
|
73
146
|
## Subpath Exports
|
|
74
147
|
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
| `@classytic/ledger` | Engine, Money, schemas, plugins, reports,
|
|
78
|
-
| `@classytic/ledger/money` | `Money` class
|
|
79
|
-
| `@classytic/ledger/schemas` |
|
|
80
|
-
| `@classytic/ledger/reports` | Report generators
|
|
81
|
-
| `@classytic/ledger/plugins` |
|
|
82
|
-
| `@classytic/ledger/repositories` |
|
|
83
|
-
| `@classytic/ledger/exports` | CSV export
|
|
84
|
-
| `@classytic/ledger/constants` | Categories, journal types, currencies |
|
|
148
|
+
| Path | Contents |
|
|
149
|
+
|------|----------|
|
|
150
|
+
| `@classytic/ledger` | Engine, Money, all schemas, plugins, reports, types |
|
|
151
|
+
| `@classytic/ledger/money` | `Money` class |
|
|
152
|
+
| `@classytic/ledger/schemas` | Schema factories |
|
|
153
|
+
| `@classytic/ledger/reports` | Report generators |
|
|
154
|
+
| `@classytic/ledger/plugins` | All plugins |
|
|
155
|
+
| `@classytic/ledger/repositories` | Repository wiring |
|
|
156
|
+
| `@classytic/ledger/exports` | CSV export + QuickBooks field maps |
|
|
85
157
|
| `@classytic/ledger/country` | `defineCountryPack`, `CountryPack` interface |
|
|
158
|
+
| `@classytic/ledger/constants` | Categories, journal types, currencies |
|
|
159
|
+
|
|
160
|
+
## Country Packs
|
|
86
161
|
|
|
87
|
-
|
|
162
|
+
Build your own or use an existing one:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { defineCountryPack } from '@classytic/ledger';
|
|
166
|
+
|
|
167
|
+
export const myPack = defineCountryPack({
|
|
168
|
+
code: 'US',
|
|
169
|
+
name: 'United States',
|
|
170
|
+
defaultCurrency: 'USD',
|
|
171
|
+
retainedEarningsAccountCode: '3200',
|
|
172
|
+
accountTypes: [ /* your chart of accounts */ ],
|
|
173
|
+
taxCodes: { /* your tax codes */ },
|
|
174
|
+
taxCodesByRegion: {},
|
|
175
|
+
regions: [],
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Available packs: `@classytic/ledger-ca` (Canada), `@classytic/ledger-bd` (Bangladesh).
|
|
180
|
+
|
|
181
|
+
## Testing
|
|
182
|
+
|
|
183
|
+
949 tests covering unit, integration, and end-to-end scenarios:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm test # run all
|
|
187
|
+
npx vitest run tests/e2e/ # e2e scenarios only
|
|
188
|
+
```
|
|
88
189
|
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
- [Exports](docs/exports.md)
|
|
95
|
-
- [Country Packs](docs/country-packs.md)
|
|
96
|
-
- [Money](docs/money.md)
|
|
97
|
-
- [Subledger Integration](docs/subledger-integration.md)
|
|
190
|
+
E2E suites include:
|
|
191
|
+
- Canadian small business full-year lifecycle
|
|
192
|
+
- Multi-currency trading with FX revaluation
|
|
193
|
+
- All plugins + dimensions + budgets + fiscal close
|
|
194
|
+
- O-Level / A-Level / university textbook accounting problems
|
|
98
195
|
|
|
99
196
|
## Requirements
|
|
100
197
|
|
|
101
198
|
- Node.js >= 22
|
|
102
199
|
- MongoDB (replica set recommended for transactions)
|
|
103
200
|
- Mongoose >= 9
|
|
104
|
-
- @classytic/mongokit >= 3
|
|
201
|
+
- @classytic/mongokit >= 3
|
|
105
202
|
|
|
106
203
|
## License
|
|
107
204
|
|
|
@@ -66,5 +66,3 @@ function extractStatementType(key) {
|
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
68
68
|
export { extractStatementType as a, getNormalBalance as c, isValidCategory as d, extractMainType as i, isBalanceSheet as l, CATEGORY_KEYS as n, getCategoryMainType as o, categoryKey as r, getCategoryStatementType as s, CATEGORIES as t, isIncomeStatement as u };
|
|
69
|
-
|
|
70
|
-
//# sourceMappingURL=categories-CclX7Q94.mjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
export { CATEGORIES, CATEGORY_KEYS, CURRENCIES, JOURNAL_CODES, JOURNAL_TYPES, categoryKey, extractMainType, extractStatementType, getCategoryMainType, getCategoryStatementType, getCurrency, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType };
|
|
1
|
+
import { S as isValidCategory, _ as getCategoryMainType, a as getJournalTypeCodes, b as isBalanceSheet, c as CURRENCIES, d as isValidCurrency, f as CATEGORIES, g as extractStatementType, h as extractMainType, i as getJournalType, l as getCurrency, m as categoryKey, n as JOURNAL_TYPES, o as isValidJournalType, p as CATEGORY_KEYS, r as getCustomJournalTypes, s as registerJournalType, t as JOURNAL_CODES, u as getMinorUnit, v as getCategoryStatementType, x as isIncomeStatement, y as getNormalBalance } from "../journals-DTipb_rz.mjs";
|
|
2
|
+
export { CATEGORIES, CATEGORY_KEYS, CURRENCIES, JOURNAL_CODES, JOURNAL_TYPES, categoryKey, extractMainType, extractStatementType, getCategoryMainType, getCategoryStatementType, getCurrency, getCustomJournalTypes, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, registerJournalType };
|
package/dist/constants/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as
|
|
1
|
+
import { a as extractStatementType, c as getNormalBalance, d as isValidCategory, i as extractMainType, l as isBalanceSheet, n as CATEGORY_KEYS, o as getCategoryMainType, r as categoryKey, s as getCategoryStatementType, t as CATEGORIES, u as isIncomeStatement } from "../categories-FJlrvzcl.mjs";
|
|
2
|
+
import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "../journals-BcMn71Cq.mjs";
|
|
3
3
|
import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "../currencies-W8kQAkm0.mjs";
|
|
4
|
-
export { CATEGORIES, CATEGORY_KEYS, CURRENCIES, JOURNAL_CODES, JOURNAL_TYPES, categoryKey, extractMainType, extractStatementType, getCategoryMainType, getCategoryStatementType, getCurrency, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType };
|
|
4
|
+
export { CATEGORIES, CATEGORY_KEYS, CURRENCIES, JOURNAL_CODES, JOURNAL_TYPES, categoryKey, extractMainType, extractStatementType, getCategoryMainType, getCategoryStatementType, getCurrency, getCustomJournalTypes, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, registerJournalType };
|
package/dist/core-8Xfnpn6g.d.mts
CHANGED
|
@@ -100,5 +100,4 @@ interface DateRange {
|
|
|
100
100
|
endDate: Date;
|
|
101
101
|
}
|
|
102
102
|
//#endregion
|
|
103
|
-
export { TaxMetadata as _, Cents as a, DateRange as c, JournalType as d, MainType as f, TaxDetail as g, StatementType as h, CategoryKey as i, EntryState as l, ObjectId as m, CashFlowCategory as n, Currency as o, NormalBalance as p, Category as r, DateOption as s, AccountType as t, JournalItem as u, TotalAccountOp as v };
|
|
104
|
-
//# sourceMappingURL=core-8Xfnpn6g.d.mts.map
|
|
103
|
+
export { TaxMetadata as _, Cents as a, DateRange as c, JournalType as d, MainType as f, TaxDetail as g, StatementType as h, CategoryKey as i, EntryState as l, ObjectId as m, CashFlowCategory as n, Currency as o, NormalBalance as p, Category as r, DateOption as s, AccountType as t, JournalItem as u, TotalAccountOp as v };
|
package/dist/country/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TaxReportLine, i as TaxCodesByRegion, n as CountryPackInput, o as TaxReportTemplate, r as TaxCode, s as defineCountryPack, t as CountryPack } from "../index-
|
|
1
|
+
import { a as TaxReportLine, i as TaxCodesByRegion, n as CountryPackInput, o as TaxReportTemplate, r as TaxCode, s as defineCountryPack, t as CountryPack } from "../index-CxZqRaOU.mjs";
|
|
2
2
|
export { CountryPack, CountryPackInput, TaxCode, TaxCodesByRegion, TaxReportLine, TaxReportTemplate, defineCountryPack };
|
package/dist/country/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as Errors } from "./errors-
|
|
1
|
+
import { n as Errors } from "./errors-BoGUSUYL.mjs";
|
|
2
2
|
//#region src/plugins/double-entry.plugin.ts
|
|
3
3
|
function doubleEntryPlugin(options = {}) {
|
|
4
4
|
const { onlyOnPost = true, JournalEntryModel, AccountModel, orgField } = options;
|
|
@@ -36,7 +36,7 @@ function doubleEntryPlugin(options = {}) {
|
|
|
36
36
|
const accountIds = items.map((i) => i.account).filter((a) => a != null && a !== "");
|
|
37
37
|
if (accountIds.length === 0) throw Errors.validation("Posted entry has items with missing accounts.");
|
|
38
38
|
const selectFields = orgField ? `_id ${orgField}` : "_id";
|
|
39
|
-
const accounts = await AccountModel
|
|
39
|
+
const accounts = await AccountModel?.find({ _id: { $in: accountIds } }).select(selectFields).session(context.session ?? null).lean();
|
|
40
40
|
const foundIds = new Set(accounts.map((a) => String(a._id)));
|
|
41
41
|
const missingCount = accountIds.filter((id) => !foundIds.has(String(id))).length;
|
|
42
42
|
if (missingCount > 0) throw Errors.validation(`${missingCount} item(s) reference non-existent accounts.`);
|
|
@@ -80,8 +80,8 @@ function doubleEntryPlugin(options = {}) {
|
|
|
80
80
|
...existing
|
|
81
81
|
}, context);
|
|
82
82
|
};
|
|
83
|
-
repo.on("before:create",
|
|
84
|
-
repo.on("before:update",
|
|
83
|
+
repo.on("before:create", validate);
|
|
84
|
+
repo.on("before:update", validateUpdate);
|
|
85
85
|
}
|
|
86
86
|
};
|
|
87
87
|
}
|
|
@@ -132,8 +132,8 @@ function fiscalLockPlugin(options) {
|
|
|
132
132
|
throw Errors.fiscal(`Cannot post entry dated ${entryDate.toISOString().split("T")[0]}: fiscal period "${period.name}" is closed.`);
|
|
133
133
|
}
|
|
134
134
|
};
|
|
135
|
-
repo.on("before:create", (
|
|
136
|
-
repo.on("before:update", (
|
|
135
|
+
repo.on("before:create", (ctx) => checkPeriod(ctx, false));
|
|
136
|
+
repo.on("before:update", (ctx) => checkPeriod(ctx, true));
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
}
|
|
@@ -144,8 +144,7 @@ function idempotencyPlugin(options) {
|
|
|
144
144
|
return {
|
|
145
145
|
name: "accounting:idempotency",
|
|
146
146
|
apply(repo) {
|
|
147
|
-
repo.on("before:create", async (
|
|
148
|
-
const context = raw;
|
|
147
|
+
repo.on("before:create", async (context) => {
|
|
149
148
|
const data = context.data;
|
|
150
149
|
if (!data?.idempotencyKey) return;
|
|
151
150
|
const query = { idempotencyKey: data.idempotencyKey };
|
|
@@ -157,6 +156,47 @@ function idempotencyPlugin(options) {
|
|
|
157
156
|
};
|
|
158
157
|
}
|
|
159
158
|
//#endregion
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
//#region src/plugins/date-lock.plugin.ts
|
|
160
|
+
function dateLockPlugin(options) {
|
|
161
|
+
const { getLockDate, JournalEntryModel, orgField } = options;
|
|
162
|
+
return {
|
|
163
|
+
name: "accounting:date-lock",
|
|
164
|
+
apply(repo) {
|
|
165
|
+
const checkLock = async (context, isUpdate) => {
|
|
166
|
+
const data = context.data;
|
|
167
|
+
if (!data) return;
|
|
168
|
+
if (data.state !== "posted") return;
|
|
169
|
+
const session = context.session ?? null;
|
|
170
|
+
let entryDate;
|
|
171
|
+
let persistedDoc = null;
|
|
172
|
+
if (data.date) entryDate = new Date(data.date);
|
|
173
|
+
else if (!isUpdate) entryDate = /* @__PURE__ */ new Date();
|
|
174
|
+
else {
|
|
175
|
+
if (!context.id) throw new Error("dateLockPlugin: update context is missing \"id\". Cannot validate date lock without document ID.");
|
|
176
|
+
const selectFields = orgField ? `date ${orgField}` : "date";
|
|
177
|
+
persistedDoc = await JournalEntryModel.findById(context.id).select(selectFields).session(session).lean();
|
|
178
|
+
if (persistedDoc?.date) entryDate = new Date(persistedDoc.date);
|
|
179
|
+
}
|
|
180
|
+
if (!entryDate) return;
|
|
181
|
+
let orgValue;
|
|
182
|
+
if (orgField) {
|
|
183
|
+
orgValue = data[orgField] ?? context[orgField];
|
|
184
|
+
if (!orgValue && isUpdate) {
|
|
185
|
+
if (persistedDoc) orgValue = persistedDoc[orgField];
|
|
186
|
+
else if (context.id) {
|
|
187
|
+
const persisted = await JournalEntryModel.findById(context.id).select(orgField).session(session).lean();
|
|
188
|
+
if (persisted) orgValue = persisted[orgField];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const lockDate = await getLockDate(orgValue, session ?? void 0);
|
|
193
|
+
if (!lockDate) return;
|
|
194
|
+
if (entryDate < lockDate) throw Errors.fiscal(`Cannot post entry dated ${entryDate.toISOString().split("T")[0]}: date is before lock date ${lockDate.toISOString().split("T")[0]}.`);
|
|
195
|
+
};
|
|
196
|
+
repo.on("before:create", (ctx) => checkLock(ctx, false));
|
|
197
|
+
repo.on("before:update", (ctx) => checkLock(ctx, true));
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
export { doubleEntryPlugin as i, idempotencyPlugin as n, fiscalLockPlugin as r, dateLockPlugin as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CountryPack } from "./index-
|
|
1
|
+
import { t as CountryPack } from "./index-CxZqRaOU.mjs";
|
|
2
2
|
import { t as Logger } from "./logger-UbTdBb1x.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/types/engine.d.ts
|
|
@@ -80,9 +80,14 @@ interface AccountingEngineConfig {
|
|
|
80
80
|
multiCurrency?: MultiCurrencyConfig;
|
|
81
81
|
/** Fiscal year start month (1-12, default: 1 = January) */
|
|
82
82
|
fiscalYearStartMonth?: number;
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The retained earnings account code (e.g. '3600' CA, '3310' BD).
|
|
85
|
+
* Overrides the country pack value. See CountryPack.retainedEarningsAccountCode.
|
|
86
|
+
*/
|
|
87
|
+
retainedEarningsAccountCode?: string;
|
|
88
|
+
/** Display code for the "Previous Years Retained Earnings" line. Overrides country pack. */
|
|
89
|
+
retainedEarningsDisplayCode?: string;
|
|
90
|
+
/** Display code for current year net income line. Overrides country pack. */
|
|
86
91
|
currentYearEarningsCode?: string;
|
|
87
92
|
/** Logger instance. Defaults to console-based logger. */
|
|
88
93
|
logger?: Logger;
|
|
@@ -94,5 +99,4 @@ interface AccountingEngineConfig {
|
|
|
94
99
|
strictness?: StrictnessConfig;
|
|
95
100
|
}
|
|
96
101
|
//#endregion
|
|
97
|
-
export { MultiTenantConfig as a, MultiCurrencyConfig as i, AuditConfig as n, SchemaOptions as o, JournalSchemaOptions as r, StrictnessConfig as s, AccountingEngineConfig as t };
|
|
98
|
-
//# sourceMappingURL=engine-BzBMpWuy.d.mts.map
|
|
102
|
+
export { MultiTenantConfig as a, MultiCurrencyConfig as i, AuditConfig as n, SchemaOptions as o, JournalSchemaOptions as r, StrictnessConfig as s, AccountingEngineConfig as t };
|
package/dist/exports/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as PopulatedJournalEntry, a as exportToCsv, c as getHeaders, d as serializeCsv, f as CsvOptions, g as PopulatedAccount, h as FlatJournalRow, i as
|
|
1
|
+
import { _ as PopulatedJournalEntry, a as exportToCsv, c as getHeaders, d as serializeCsv, f as CsvOptions, g as PopulatedAccount, h as FlatJournalRow, i as quickbooksFieldMap, l as buildCsv, m as ExportFieldMap, n as flattenJournalEntry, o as extractAllRows, p as ExportField, r as universalFieldMap, s as extractRow, t as flattenJournalEntries, u as escapeCell, v as PopulatedJournalItem } from "../index-J-XIbXH-.mjs";
|
|
2
2
|
export { CsvOptions, ExportField, ExportFieldMap, FlatJournalRow, PopulatedAccount, PopulatedJournalEntry, PopulatedJournalItem, buildCsv, escapeCell, exportToCsv, extractAllRows, extractRow, flattenJournalEntries, flattenJournalEntry, getHeaders, quickbooksFieldMap, serializeCsv, universalFieldMap };
|
package/dist/exports/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as exportToCsv, c as getHeaders, d as serializeCsv, i as
|
|
1
|
+
import { a as exportToCsv, c as getHeaders, d as serializeCsv, i as quickbooksFieldMap, l as buildCsv, n as flattenJournalEntry, o as extractAllRows, r as universalFieldMap, s as extractRow, t as flattenJournalEntries, u as escapeCell } from "../exports-DoGQQtMQ.mjs";
|
|
2
2
|
export { buildCsv, escapeCell, exportToCsv, extractAllRows, extractRow, flattenJournalEntries, flattenJournalEntry, getHeaders, quickbooksFieldMap, serializeCsv, universalFieldMap };
|