@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/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "./fiscal-period.schema-BQ5wsAq3.mjs";
|
|
2
|
-
import { a as isValidJournalType, i as getJournalTypeCodes, n as JOURNAL_TYPES, t as JOURNAL_CODES } from "./journals-oH-FK3g8.mjs";
|
|
3
|
-
import { a as generateIncomeStatement, c as calculateTotal, d as generateTrialBalance, f as buildItemFilters, i as generateGeneralLedger, l as computeEndingBalance, m as getFiscalYearStart, n as reopenFiscalPeriod, o as generateBalanceSheet, p as getDateRange, r as generateCashFlow, s as buildAccountTypeMap, t as closeFiscalPeriod, u as isVirtualTaxAccount } from "./fiscal-close-L631E3De.mjs";
|
|
4
|
-
import { n as Errors, t as AccountingError } from "./errors-B7yC-Jfw.mjs";
|
|
5
|
-
import { n as finalizeSession, r as defaultLogger, t as acquireSession } from "./session-Ba8E3Ufa.mjs";
|
|
6
|
-
import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-CclX7Q94.mjs";
|
|
7
1
|
import { Money, add, allocate, format, formatPlain, fromDecimal, multiply, parseCents, percentage, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
|
|
8
|
-
import { n as
|
|
9
|
-
import { n as
|
|
2
|
+
import { n as Errors, t as AccountingError } from "./errors-BoGUSUYL.mjs";
|
|
3
|
+
import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "./date-lock.plugin-C8kqPBjh.mjs";
|
|
4
|
+
import { C as DEFAULT_BUCKETS, S as isVirtualTaxAccount, _ as getDateRange, a as defaultLogger, b as calculateTotal, c as buildRevaluationEntry, d as generateGeneralLedger, f as generateDimensionBreakdown, g as buildItemFilters, h as generateBalanceSheet, i as finalizeSession, l as computeRevaluation, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, r as acquireSession, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, v as getFiscalYearStart, w as generateAgedBalance, x as computeEndingBalance, y as buildAccountTypeMap } from "./fiscal-close-DmPV82e4.mjs";
|
|
5
|
+
import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-FJlrvzcl.mjs";
|
|
6
|
+
import { n as wireJournalEntryMethods, r as wireAccountMethods, t as wireReconciliationMethods } from "./reconciliation.repository-DgJEDVS-.mjs";
|
|
7
|
+
import { a as createAccountSchema, i as createBudgetSchema, n as createJournalEntrySchema, r as createFiscalPeriodSchema, t as createReconciliationSchema } from "./reconciliation.schema-KScbsXbY.mjs";
|
|
8
|
+
import { a as getJournalType, c as registerJournalType, i as getCustomJournalTypes, n as JOURNAL_TYPES, o as getJournalTypeCodes, s as isValidJournalType, t as JOURNAL_CODES } from "./journals-BcMn71Cq.mjs";
|
|
10
9
|
import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "./currencies-W8kQAkm0.mjs";
|
|
11
10
|
import { defineCountryPack } from "./country/index.mjs";
|
|
12
|
-
import { a as exportToCsv,
|
|
11
|
+
import { a as exportToCsv, i as quickbooksFieldMap, r as universalFieldMap, t as flattenJournalEntries } from "./exports-DoGQQtMQ.mjs";
|
|
12
|
+
import { Schema } from "mongoose";
|
|
13
13
|
//#region src/engine.ts
|
|
14
14
|
var AccountingEngine = class {
|
|
15
15
|
config;
|
|
@@ -30,12 +30,19 @@ var AccountingEngine = class {
|
|
|
30
30
|
createFiscalPeriodSchema(options) {
|
|
31
31
|
return createFiscalPeriodSchema(this.config, options);
|
|
32
32
|
}
|
|
33
|
+
createBudgetSchema(options) {
|
|
34
|
+
return createBudgetSchema(this.config, options);
|
|
35
|
+
}
|
|
36
|
+
createReconciliationSchema(accountModelName, journalEntryModelName, options) {
|
|
37
|
+
return createReconciliationSchema(this.config, accountModelName, journalEntryModelName, options);
|
|
38
|
+
}
|
|
33
39
|
createReports(models) {
|
|
34
|
-
const { Account: AccountModel, JournalEntry: JournalEntryModel } = models;
|
|
40
|
+
const { Account: AccountModel, JournalEntry: JournalEntryModel, Budget: BudgetModel } = models;
|
|
35
41
|
const { country, config } = this;
|
|
36
42
|
const orgField = config.multiTenant?.orgField;
|
|
37
43
|
const fiscalYearStartMonth = config.fiscalYearStartMonth ?? 1;
|
|
38
|
-
const
|
|
44
|
+
const retainedEarningsAccountCode = config.retainedEarningsAccountCode;
|
|
45
|
+
const retainedEarningsDisplayCode = config.retainedEarningsDisplayCode;
|
|
39
46
|
const currentYearEarningsCode = config.currentYearEarningsCode;
|
|
40
47
|
return {
|
|
41
48
|
trialBalance: (params) => generateTrialBalance({
|
|
@@ -51,7 +58,8 @@ var AccountingEngine = class {
|
|
|
51
58
|
country,
|
|
52
59
|
orgField,
|
|
53
60
|
fiscalYearStartMonth,
|
|
54
|
-
|
|
61
|
+
retainedEarningsAccountCode,
|
|
62
|
+
retainedEarningsDisplayCode,
|
|
55
63
|
currentYearEarningsCode
|
|
56
64
|
}, params),
|
|
57
65
|
incomeStatement: (params) => generateIncomeStatement({
|
|
@@ -72,6 +80,35 @@ var AccountingEngine = class {
|
|
|
72
80
|
JournalEntryModel,
|
|
73
81
|
country,
|
|
74
82
|
orgField
|
|
83
|
+
}, params),
|
|
84
|
+
agedBalance: (params) => generateAgedBalance({
|
|
85
|
+
AccountModel,
|
|
86
|
+
JournalEntryModel,
|
|
87
|
+
country,
|
|
88
|
+
orgField
|
|
89
|
+
}, params),
|
|
90
|
+
dimensionBreakdown: (params) => generateDimensionBreakdown({
|
|
91
|
+
AccountModel,
|
|
92
|
+
JournalEntryModel,
|
|
93
|
+
country,
|
|
94
|
+
orgField
|
|
95
|
+
}, params),
|
|
96
|
+
budgetVsActual: (params) => {
|
|
97
|
+
if (!BudgetModel) throw new Error("Budget model required — pass Budget to createReports()");
|
|
98
|
+
return generateBudgetVsActual({
|
|
99
|
+
AccountModel,
|
|
100
|
+
JournalEntryModel,
|
|
101
|
+
BudgetModel,
|
|
102
|
+
country,
|
|
103
|
+
orgField
|
|
104
|
+
}, params);
|
|
105
|
+
},
|
|
106
|
+
revaluation: (params) => generateRevaluation({
|
|
107
|
+
AccountModel,
|
|
108
|
+
JournalEntryModel,
|
|
109
|
+
country,
|
|
110
|
+
orgField,
|
|
111
|
+
baseCurrency: this.currency
|
|
75
112
|
}, params)
|
|
76
113
|
};
|
|
77
114
|
}
|
|
@@ -110,23 +147,22 @@ var AccountingEngine = class {
|
|
|
110
147
|
createJournalEntryRepository(createRepository, models, additionalPlugins = []) {
|
|
111
148
|
const orgField = this.config.multiTenant?.orgField;
|
|
112
149
|
const { JournalEntryModel, AccountModel, FiscalPeriodModel } = models;
|
|
150
|
+
const jeModel = JournalEntryModel;
|
|
113
151
|
const plugins = [...additionalPlugins, doubleEntryPlugin({
|
|
114
|
-
JournalEntryModel,
|
|
152
|
+
JournalEntryModel: jeModel,
|
|
115
153
|
AccountModel,
|
|
116
154
|
orgField
|
|
117
155
|
})];
|
|
118
156
|
if (FiscalPeriodModel) plugins.push(fiscalLockPlugin({
|
|
119
157
|
FiscalPeriodModel,
|
|
120
|
-
JournalEntryModel,
|
|
158
|
+
JournalEntryModel: jeModel,
|
|
121
159
|
orgField
|
|
122
160
|
}));
|
|
123
161
|
if (this.config.idempotency) plugins.push(idempotencyPlugin({
|
|
124
|
-
JournalEntryModel,
|
|
162
|
+
JournalEntryModel: jeModel,
|
|
125
163
|
orgField
|
|
126
164
|
}));
|
|
127
|
-
|
|
128
|
-
wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);
|
|
129
|
-
return repository;
|
|
165
|
+
return wireJournalEntryMethods(createRepository(JournalEntryModel, plugins), JournalEntryModel, orgField, this.config.strictness);
|
|
130
166
|
}
|
|
131
167
|
/**
|
|
132
168
|
* Wire post/reverse domain methods onto a mongokit Repository
|
|
@@ -143,8 +179,7 @@ var AccountingEngine = class {
|
|
|
143
179
|
*/
|
|
144
180
|
wireJournalEntryRepository(repository, JournalEntryModel) {
|
|
145
181
|
const orgField = this.config.multiTenant?.orgField;
|
|
146
|
-
wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);
|
|
147
|
-
return repository;
|
|
182
|
+
return wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);
|
|
148
183
|
}
|
|
149
184
|
/**
|
|
150
185
|
* Wire seedAccounts/bulkCreate and posting-account validation onto a
|
|
@@ -157,14 +192,77 @@ var AccountingEngine = class {
|
|
|
157
192
|
*/
|
|
158
193
|
wireAccountRepository(repository, AccountModel) {
|
|
159
194
|
const orgField = this.config.multiTenant?.orgField;
|
|
160
|
-
wireAccountMethods(repository, AccountModel, this.country, orgField);
|
|
161
|
-
|
|
195
|
+
return wireAccountMethods(repository, AccountModel, this.country, orgField);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Wire reconcile/unreconcile/getUnreconciled methods onto a mongokit Repository.
|
|
199
|
+
*/
|
|
200
|
+
wireReconciliationRepository(repository, ReconciliationModel, JournalEntryModel) {
|
|
201
|
+
const orgField = this.config.multiTenant?.orgField;
|
|
202
|
+
return wireReconciliationMethods(repository, ReconciliationModel, JournalEntryModel, orgField);
|
|
162
203
|
}
|
|
163
204
|
};
|
|
164
205
|
function createAccountingEngine(config) {
|
|
165
206
|
return new AccountingEngine(config);
|
|
166
207
|
}
|
|
167
208
|
//#endregion
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
209
|
+
//#region src/utils/dimensions.ts
|
|
210
|
+
/**
|
|
211
|
+
* Analytic Dimensions — Helpers for defining analytic dimensions
|
|
212
|
+
* (department, project, cost center) on journal items.
|
|
213
|
+
*
|
|
214
|
+
* Generates Mongoose schema fields and indexes for dimension queries.
|
|
215
|
+
*/
|
|
216
|
+
/**
|
|
217
|
+
* Build extraItemFields schema definition for a set of dimensions.
|
|
218
|
+
*
|
|
219
|
+
* Returns a Mongoose schema-compatible object suitable for spreading into
|
|
220
|
+
* `extraItemFields` in JournalSchemaOptions.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* const fields = buildDimensionFields([
|
|
225
|
+
* { field: 'departmentId', label: 'Department', ref: 'Department' },
|
|
226
|
+
* { field: 'projectId', label: 'Project', ref: 'Project' },
|
|
227
|
+
* ]);
|
|
228
|
+
* // => { departmentId: { type: Schema.Types.ObjectId, ref: 'Department', required: false, default: null }, ... }
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
function buildDimensionFields(dimensions) {
|
|
232
|
+
const fields = {};
|
|
233
|
+
for (const dim of dimensions) {
|
|
234
|
+
const fieldDef = {
|
|
235
|
+
type: Schema.Types.ObjectId,
|
|
236
|
+
required: dim.required ?? false,
|
|
237
|
+
default: null
|
|
238
|
+
};
|
|
239
|
+
if (dim.ref) fieldDef.ref = dim.ref;
|
|
240
|
+
fields[dim.field] = fieldDef;
|
|
241
|
+
}
|
|
242
|
+
return fields;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Build extra indexes for dimension queries.
|
|
246
|
+
*
|
|
247
|
+
* Each dimension gets a compound index on `journalItems.{field}` + `date`
|
|
248
|
+
* for efficient filtered reporting. When `orgField` is provided, it is
|
|
249
|
+
* prepended to each index for multi-tenant scoping.
|
|
250
|
+
*
|
|
251
|
+
* @param dimensions - Array of dimension definitions
|
|
252
|
+
* @param orgField - Optional org-scoping field name (e.g. 'business')
|
|
253
|
+
* @returns Array of index specifications compatible with `extraIndexes` in JournalSchemaOptions
|
|
254
|
+
*/
|
|
255
|
+
function buildDimensionIndexes(dimensions, orgField) {
|
|
256
|
+
return dimensions.map((dim) => {
|
|
257
|
+
const fields = {};
|
|
258
|
+
if (orgField) fields[orgField] = 1;
|
|
259
|
+
fields[`journalItems.${dim.field}`] = 1;
|
|
260
|
+
fields.date = -1;
|
|
261
|
+
return {
|
|
262
|
+
fields,
|
|
263
|
+
options: {}
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, DEFAULT_BUCKETS, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, dateLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { o as SchemaOptions, r as JournalSchemaOptions, t as AccountingEngineConfig } from "./engine-DF-MtsEr.mjs";
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
|
|
4
|
+
//#region src/schemas/account.schema.d.ts
|
|
5
|
+
declare function createAccountSchema(config: AccountingEngineConfig, options?: SchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
6
|
+
timestamps: true;
|
|
7
|
+
}, {
|
|
8
|
+
[x: number]: any;
|
|
9
|
+
[x: string]: any;
|
|
10
|
+
} & mongoose.DefaultTimestampProps, mongoose.Document<unknown, {}, {
|
|
11
|
+
[x: number]: any;
|
|
12
|
+
[x: string]: any;
|
|
13
|
+
} & mongoose.DefaultTimestampProps, {
|
|
14
|
+
id: string;
|
|
15
|
+
}, Omit<mongoose.DefaultSchemaOptions, "timestamps"> & {
|
|
16
|
+
timestamps: true;
|
|
17
|
+
}> & Omit<{
|
|
18
|
+
[x: number]: any;
|
|
19
|
+
[x: string]: any;
|
|
20
|
+
} & mongoose.DefaultTimestampProps & {
|
|
21
|
+
_id: mongoose.Types.ObjectId;
|
|
22
|
+
} & {
|
|
23
|
+
__v: number;
|
|
24
|
+
}, "id"> & {
|
|
25
|
+
id: string;
|
|
26
|
+
}, unknown, {
|
|
27
|
+
[x: number]: any;
|
|
28
|
+
[x: string]: any;
|
|
29
|
+
createdAt: NativeDate;
|
|
30
|
+
updatedAt: NativeDate;
|
|
31
|
+
} & {
|
|
32
|
+
_id: mongoose.Types.ObjectId;
|
|
33
|
+
} & {
|
|
34
|
+
__v: number;
|
|
35
|
+
}>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/schemas/fiscal-period.schema.d.ts
|
|
38
|
+
declare function createFiscalPeriodSchema(config: AccountingEngineConfig, options?: SchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
39
|
+
timestamps: true;
|
|
40
|
+
}, {
|
|
41
|
+
[x: number]: any;
|
|
42
|
+
[x: string]: any;
|
|
43
|
+
} & mongoose.DefaultTimestampProps, mongoose.Document<unknown, {}, {
|
|
44
|
+
[x: number]: any;
|
|
45
|
+
[x: string]: any;
|
|
46
|
+
} & mongoose.DefaultTimestampProps, {
|
|
47
|
+
id: string;
|
|
48
|
+
}, Omit<mongoose.DefaultSchemaOptions, "timestamps"> & {
|
|
49
|
+
timestamps: true;
|
|
50
|
+
}> & Omit<{
|
|
51
|
+
[x: number]: any;
|
|
52
|
+
[x: string]: any;
|
|
53
|
+
} & mongoose.DefaultTimestampProps & {
|
|
54
|
+
_id: mongoose.Types.ObjectId;
|
|
55
|
+
} & {
|
|
56
|
+
__v: number;
|
|
57
|
+
}, "id"> & {
|
|
58
|
+
id: string;
|
|
59
|
+
}, unknown, {
|
|
60
|
+
[x: number]: any;
|
|
61
|
+
[x: string]: any;
|
|
62
|
+
createdAt: NativeDate;
|
|
63
|
+
updatedAt: NativeDate;
|
|
64
|
+
} & {
|
|
65
|
+
_id: mongoose.Types.ObjectId;
|
|
66
|
+
} & {
|
|
67
|
+
__v: number;
|
|
68
|
+
}>;
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/schemas/journal-entry.schema.d.ts
|
|
71
|
+
declare function createJournalEntrySchema(config: AccountingEngineConfig, accountModelName: string, options?: JournalSchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
72
|
+
timestamps: true;
|
|
73
|
+
}, {
|
|
74
|
+
[x: number]: any;
|
|
75
|
+
[x: string]: any;
|
|
76
|
+
} & mongoose.DefaultTimestampProps, mongoose.Document<unknown, {}, {
|
|
77
|
+
[x: number]: any;
|
|
78
|
+
[x: string]: any;
|
|
79
|
+
} & mongoose.DefaultTimestampProps, {
|
|
80
|
+
id: string;
|
|
81
|
+
}, Omit<mongoose.DefaultSchemaOptions, "timestamps"> & {
|
|
82
|
+
timestamps: true;
|
|
83
|
+
}> & Omit<{
|
|
84
|
+
[x: number]: any;
|
|
85
|
+
[x: string]: any;
|
|
86
|
+
} & mongoose.DefaultTimestampProps & {
|
|
87
|
+
_id: mongoose.Types.ObjectId;
|
|
88
|
+
} & {
|
|
89
|
+
__v: number;
|
|
90
|
+
}, "id"> & {
|
|
91
|
+
id: string;
|
|
92
|
+
}, unknown, {
|
|
93
|
+
[x: number]: any;
|
|
94
|
+
[x: string]: any;
|
|
95
|
+
createdAt: NativeDate;
|
|
96
|
+
updatedAt: NativeDate;
|
|
97
|
+
} & {
|
|
98
|
+
_id: mongoose.Types.ObjectId;
|
|
99
|
+
} & {
|
|
100
|
+
__v: number;
|
|
101
|
+
}>;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { createFiscalPeriodSchema as n, createAccountSchema as r, createJournalEntrySchema as t };
|
|
@@ -77,16 +77,37 @@ const JOURNAL_TYPES = Object.freeze({
|
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
const JOURNAL_CODES = Object.freeze(Object.fromEntries(Object.keys(JOURNAL_TYPES).map((k) => [k, k])));
|
|
80
|
+
const _customTypes = {};
|
|
81
|
+
let _frozen = false;
|
|
82
|
+
/**
|
|
83
|
+
* Register a custom journal type. Must be called **before** schema
|
|
84
|
+
* initialization (`createJournalEntrySchema`). Custom types are
|
|
85
|
+
* automatically included in Mongoose enum validation and all lookup
|
|
86
|
+
* functions.
|
|
87
|
+
*/
|
|
88
|
+
function registerJournalType(code, def) {
|
|
89
|
+
if (_frozen) throw new Error("Cannot register journal types after schema initialization");
|
|
90
|
+
if (code in JOURNAL_TYPES) throw new Error(`Cannot override built-in journal type: ${code}`);
|
|
91
|
+
if (def.code !== code) throw new Error(`Journal type code mismatch: key="${code}" but def.code="${def.code}"`);
|
|
92
|
+
if (!def.name || !def.description) throw new Error(`Journal type "${code}" requires non-empty name and description`);
|
|
93
|
+
_customTypes[code] = def;
|
|
94
|
+
}
|
|
95
|
+
/** Returns all custom (non-built-in) journal types. */
|
|
96
|
+
function getCustomJournalTypes() {
|
|
97
|
+
return Object.values(_customTypes);
|
|
98
|
+
}
|
|
99
|
+
/** @internal Lock the registry — called by `createJournalEntrySchema`. */
|
|
100
|
+
function _freezeJournalTypes() {
|
|
101
|
+
_frozen = true;
|
|
102
|
+
}
|
|
80
103
|
function getJournalTypeCodes() {
|
|
81
|
-
return Object.keys(JOURNAL_TYPES);
|
|
104
|
+
return [...Object.keys(JOURNAL_TYPES), ...Object.keys(_customTypes)];
|
|
82
105
|
}
|
|
83
106
|
function isValidJournalType(code) {
|
|
84
|
-
return code in JOURNAL_TYPES;
|
|
107
|
+
return code in JOURNAL_TYPES || code in _customTypes;
|
|
85
108
|
}
|
|
86
109
|
function getJournalType(code) {
|
|
87
|
-
return JOURNAL_TYPES[code] ?? null;
|
|
110
|
+
return JOURNAL_TYPES[code] ?? _customTypes[code] ?? null;
|
|
88
111
|
}
|
|
89
112
|
//#endregion
|
|
90
|
-
export {
|
|
91
|
-
|
|
92
|
-
//# sourceMappingURL=journals-oH-FK3g8.mjs.map
|
|
113
|
+
export { getJournalType as a, registerJournalType as c, getCustomJournalTypes as i, JOURNAL_TYPES as n, getJournalTypeCodes as o, _freezeJournalTypes as r, isValidJournalType as s, JOURNAL_CODES as t };
|
|
@@ -21,18 +21,26 @@ declare function extractMainType(key: string): MainType | null;
|
|
|
21
21
|
/** Extract statement type from a category key string */
|
|
22
22
|
declare function extractStatementType(key: string): StatementType | null;
|
|
23
23
|
//#endregion
|
|
24
|
+
//#region src/constants/currencies.d.ts
|
|
25
|
+
declare const CURRENCIES: Readonly<Record<string, Currency>>;
|
|
26
|
+
declare function getCurrency(code: string): Currency | null;
|
|
27
|
+
declare function isValidCurrency(code: string): boolean;
|
|
28
|
+
declare function getMinorUnit(code: string): number;
|
|
29
|
+
//#endregion
|
|
24
30
|
//#region src/constants/journals.d.ts
|
|
25
31
|
declare const JOURNAL_TYPES: Readonly<Record<string, JournalType>>;
|
|
26
32
|
declare const JOURNAL_CODES: Readonly<Record<string, string>>;
|
|
33
|
+
/**
|
|
34
|
+
* Register a custom journal type. Must be called **before** schema
|
|
35
|
+
* initialization (`createJournalEntrySchema`). Custom types are
|
|
36
|
+
* automatically included in Mongoose enum validation and all lookup
|
|
37
|
+
* functions.
|
|
38
|
+
*/
|
|
39
|
+
declare function registerJournalType(code: string, def: JournalType): void;
|
|
40
|
+
/** Returns all custom (non-built-in) journal types. */
|
|
41
|
+
declare function getCustomJournalTypes(): JournalType[];
|
|
27
42
|
declare function getJournalTypeCodes(): string[];
|
|
28
43
|
declare function isValidJournalType(code: string): boolean;
|
|
29
44
|
declare function getJournalType(code: string): JournalType | null;
|
|
30
45
|
//#endregion
|
|
31
|
-
|
|
32
|
-
declare const CURRENCIES: Readonly<Record<string, Currency>>;
|
|
33
|
-
declare function getCurrency(code: string): Currency | null;
|
|
34
|
-
declare function isValidCurrency(code: string): boolean;
|
|
35
|
-
declare function getMinorUnit(code: string): number;
|
|
36
|
-
//#endregion
|
|
37
|
-
export { getNormalBalance as _, JOURNAL_CODES as a, isValidCategory as b, getJournalTypeCodes as c, CATEGORY_KEYS as d, categoryKey as f, getCategoryStatementType as g, getCategoryMainType as h, isValidCurrency as i, isValidJournalType as l, extractStatementType as m, getCurrency as n, JOURNAL_TYPES as o, extractMainType as p, getMinorUnit as r, getJournalType as s, CURRENCIES as t, CATEGORIES as u, isBalanceSheet as v, isIncomeStatement as y };
|
|
38
|
-
//# sourceMappingURL=currencies-4WAbFRlw.d.mts.map
|
|
46
|
+
export { isValidCategory as S, getCategoryMainType as _, getJournalTypeCodes as a, isBalanceSheet as b, CURRENCIES as c, isValidCurrency as d, CATEGORIES as f, extractStatementType as g, extractMainType as h, getJournalType as i, getCurrency as l, categoryKey as m, JOURNAL_TYPES as n, isValidJournalType as o, CATEGORY_KEYS as p, getCustomJournalTypes as r, registerJournalType as s, JOURNAL_CODES as t, getMinorUnit as u, getCategoryStatementType as v, isIncomeStatement as x, getNormalBalance as y };
|
package/dist/money.d.mts
CHANGED
|
@@ -125,5 +125,4 @@ declare const Money: {
|
|
|
125
125
|
readonly parseCents: typeof parseCents;
|
|
126
126
|
};
|
|
127
127
|
//#endregion
|
|
128
|
-
export { Money, abs, add, allocate, equals, format, formatPlain, fromDecimal, isNegative, isPositive, isValid, isZero, max, min, multiply, negate, parseCents, percentage, round, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal };
|
|
129
|
-
//# sourceMappingURL=money.d.mts.map
|
|
128
|
+
export { Money, abs, add, allocate, equals, format, formatPlain, fromDecimal, isNegative, isPositive, isValid, isZero, max, min, multiply, negate, parseCents, percentage, round, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal };
|
package/dist/money.mjs
CHANGED
|
@@ -30,7 +30,9 @@ function round(amount) {
|
|
|
30
30
|
/** Convert a decimal dollar amount to integer cents: 10.50 → 1050 */
|
|
31
31
|
function fromDecimal(dollars, minorUnit = 2) {
|
|
32
32
|
const factor = 10 ** minorUnit;
|
|
33
|
-
|
|
33
|
+
const cents = Math.round(dollars * factor);
|
|
34
|
+
if (!Number.isSafeInteger(cents)) throw new Error(`Amount ${dollars} exceeds safe integer limit when converted to minor units. Max safe amount: ${Number.MAX_SAFE_INTEGER / factor}`);
|
|
35
|
+
return cents;
|
|
34
36
|
}
|
|
35
37
|
/** Convert integer cents to a decimal dollar amount: 1050 → 10.50 */
|
|
36
38
|
function toDecimal(cents, minorUnit = 2) {
|
|
@@ -91,7 +93,7 @@ function allocate(totalCents, ratios) {
|
|
|
91
93
|
const ratioSum = ratios.reduce((s, r) => s + r, 0);
|
|
92
94
|
if (ratioSum === 0) throw new Error("Sum of ratios must be > 0");
|
|
93
95
|
const allocations = ratios.map((r) => Math.floor(totalCents * r / ratioSum));
|
|
94
|
-
|
|
96
|
+
const remainder = totalCents - allocations.reduce((s, a) => s + a, 0);
|
|
95
97
|
const fractions = ratios.map((r, i) => ({
|
|
96
98
|
index: i,
|
|
97
99
|
frac: totalCents * r / ratioSum - allocations[i]
|
|
@@ -164,7 +166,7 @@ function parseCents(input, minorUnit = 2) {
|
|
|
164
166
|
if (typeof input === "number") return fromDecimal(input, minorUnit);
|
|
165
167
|
const cleaned = input.replace(/[$,\s]/g, "");
|
|
166
168
|
const parsed = parseFloat(cleaned);
|
|
167
|
-
if (isNaN(parsed)) throw new Error(`Cannot parse "${input}" as money`);
|
|
169
|
+
if (Number.isNaN(parsed)) throw new Error(`Cannot parse "${input}" as money`);
|
|
168
170
|
return fromDecimal(parsed, minorUnit);
|
|
169
171
|
}
|
|
170
172
|
const Money = {
|
|
@@ -193,5 +195,3 @@ const Money = {
|
|
|
193
195
|
};
|
|
194
196
|
//#endregion
|
|
195
197
|
export { Money, abs, add, allocate, equals, format, formatPlain, fromDecimal, isNegative, isPositive, isValid, isZero, max, min, multiply, negate, parseCents, percentage, round, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal };
|
|
196
|
-
|
|
197
|
-
//# sourceMappingURL=money.mjs.map
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,2 +1,38 @@
|
|
|
1
|
-
import { a as DoubleEntryPluginOptions, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin, r as FiscalLockPluginOptions, t as IdempotencyPluginOptions } from "../idempotency.plugin-
|
|
2
|
-
|
|
1
|
+
import { a as DoubleEntryPluginOptions, c as dateLockPlugin, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin, r as FiscalLockPluginOptions, s as DateLockPluginOptions, t as IdempotencyPluginOptions } from "../idempotency.plugin-zU-GKJ0-.mjs";
|
|
2
|
+
import { RepositoryInstance } from "@classytic/mongokit";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/tax-hooks.d.ts
|
|
5
|
+
interface TaxLineInput {
|
|
6
|
+
account: unknown;
|
|
7
|
+
amount: number;
|
|
8
|
+
side: 'debit' | 'credit';
|
|
9
|
+
taxCode?: string;
|
|
10
|
+
extraFields?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
interface GeneratedTaxLine {
|
|
13
|
+
account: unknown;
|
|
14
|
+
debit: number;
|
|
15
|
+
credit: number;
|
|
16
|
+
label?: string;
|
|
17
|
+
taxDetails?: Array<{
|
|
18
|
+
taxCode: string;
|
|
19
|
+
taxName?: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
interface TaxLineGenerator {
|
|
23
|
+
generateTaxLines(input: TaxLineInput): GeneratedTaxLine[];
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/plugins/tax-hook.plugin.d.ts
|
|
27
|
+
interface TaxHookPluginOptions {
|
|
28
|
+
/** Tax line generator — implements the tax calculation logic */
|
|
29
|
+
generator: TaxLineGenerator;
|
|
30
|
+
/** Only apply tax hooks on posted entries (default: true) */
|
|
31
|
+
onlyOnPost?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare function taxHookPlugin(options: TaxHookPluginOptions): {
|
|
34
|
+
name: string;
|
|
35
|
+
apply(repo: RepositoryInstance): void;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { type DateLockPluginOptions, type DoubleEntryPluginOptions, type FiscalLockPluginOptions, type IdempotencyPluginOptions, type TaxHookPluginOptions, dateLockPlugin, doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin, taxHookPlugin };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,2 +1,57 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
|
|
1
|
+
import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "../date-lock.plugin-C8kqPBjh.mjs";
|
|
2
|
+
//#region src/utils/tax-hooks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Apply a tax hook to journal items.
|
|
5
|
+
*
|
|
6
|
+
* Iterates each item that has a taxCode in taxDetails, calls
|
|
7
|
+
* `generator.generateTaxLines` for each, and appends the generated
|
|
8
|
+
* tax lines as new journal items.
|
|
9
|
+
*
|
|
10
|
+
* @returns The original items + generated tax items
|
|
11
|
+
*/
|
|
12
|
+
function applyTaxHook(items, generator) {
|
|
13
|
+
const taxLines = [];
|
|
14
|
+
for (const item of items) {
|
|
15
|
+
const taxDetails = item.taxDetails;
|
|
16
|
+
if (!taxDetails || taxDetails.length === 0) continue;
|
|
17
|
+
const taxCode = taxDetails.find((td) => td.taxCode != null)?.taxCode;
|
|
18
|
+
if (!taxCode) continue;
|
|
19
|
+
const side = item.debit > 0 ? "debit" : "credit";
|
|
20
|
+
const amount = item.debit > 0 ? item.debit : item.credit;
|
|
21
|
+
const input = {
|
|
22
|
+
account: item.account,
|
|
23
|
+
amount,
|
|
24
|
+
side,
|
|
25
|
+
taxCode
|
|
26
|
+
};
|
|
27
|
+
const generated = generator.generateTaxLines(input);
|
|
28
|
+
for (const line of generated) taxLines.push({
|
|
29
|
+
account: line.account,
|
|
30
|
+
debit: line.debit,
|
|
31
|
+
credit: line.credit,
|
|
32
|
+
label: line.label,
|
|
33
|
+
taxDetails: line.taxDetails
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return [...items, ...taxLines];
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/plugins/tax-hook.plugin.ts
|
|
40
|
+
function taxHookPlugin(options) {
|
|
41
|
+
const { generator, onlyOnPost = true } = options;
|
|
42
|
+
return {
|
|
43
|
+
name: "accounting:tax-hook",
|
|
44
|
+
apply(repo) {
|
|
45
|
+
repo.on("before:create", (context) => {
|
|
46
|
+
const data = context.data;
|
|
47
|
+
if (!data) return;
|
|
48
|
+
if (onlyOnPost && data.state !== "posted") return;
|
|
49
|
+
const items = data.journalItems;
|
|
50
|
+
if (!items || items.length === 0) return;
|
|
51
|
+
data.journalItems = applyTaxHook(items, generator);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { dateLockPlugin, doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin, taxHookPlugin };
|