@classytic/ledger 0.1.5 → 0.2.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/dist/{account.repository-1C2sZvB2.d.mts → account.repository-C7gwFLfM.d.mts} +3 -3
- package/dist/{account.repository-1C2sZvB2.d.mts.map → account.repository-C7gwFLfM.d.mts.map} +1 -1
- package/dist/{account.repository-Crf5DGO4.mjs → account.repository-kDKwDt0I.mjs} +4 -6
- package/dist/{account.repository-Crf5DGO4.mjs.map → account.repository-kDKwDt0I.mjs.map} +1 -1
- package/dist/{categories-BNJBd4ze.mjs → categories-CclX7Q94.mjs} +2 -2
- package/dist/{categories-BNJBd4ze.mjs.map → categories-CclX7Q94.mjs.map} +1 -1
- package/dist/constants/index.d.mts +1 -1
- package/dist/constants/index.mjs +4 -5
- package/dist/{core-Cx0baosR.d.mts → core-8Xfnpn6g.d.mts} +1 -1
- package/dist/{core-Cx0baosR.d.mts.map → core-8Xfnpn6g.d.mts.map} +1 -1
- package/dist/country/index.d.mts +2 -105
- package/dist/country/index.mjs +1 -1
- package/dist/country/index.mjs.map +1 -1
- package/dist/{currencies-Bkn3FNkC.d.mts → currencies-4WAbFRlw.d.mts} +2 -2
- package/dist/{currencies-Bkn3FNkC.d.mts.map → currencies-4WAbFRlw.d.mts.map} +1 -1
- package/dist/{currencies-BBk3NwXn.mjs → currencies-W8kQAkm0.mjs} +2 -2
- package/dist/{currencies-BBk3NwXn.mjs.map → currencies-W8kQAkm0.mjs.map} +1 -1
- package/dist/{engine-Cd73EOT6.d.mts → engine-BzBMpWuy.d.mts} +31 -5
- package/dist/engine-BzBMpWuy.d.mts.map +1 -0
- package/dist/{errors-CeqRahE-.mjs → errors-B7yC-Jfw.mjs} +2 -2
- package/dist/{errors-CeqRahE-.mjs.map → errors-B7yC-Jfw.mjs.map} +1 -1
- package/dist/exports/index.d.mts +2 -2
- package/dist/exports/index.mjs +2 -3
- package/dist/{universal-CMfrZ2hG.mjs → exports-I5Xkq-9_.mjs} +2 -7
- package/dist/exports-I5Xkq-9_.mjs.map +1 -0
- package/dist/{fiscal-close-DuXDgVvb.mjs → fiscal-close-L631E3De.mjs} +8 -15
- package/dist/fiscal-close-L631E3De.mjs.map +1 -0
- package/dist/{fiscal-close-CzUzpnMg.d.mts → fiscal-close-dNlzB37y.d.mts} +4 -4
- package/dist/{fiscal-close-CzUzpnMg.d.mts.map → fiscal-close-dNlzB37y.d.mts.map} +1 -1
- package/dist/{fiscal-period.schema-CbALaaKl.mjs → fiscal-period.schema-BQ5wsAq3.mjs} +52 -8
- package/dist/fiscal-period.schema-BQ5wsAq3.mjs.map +1 -0
- package/dist/{fiscal-period.schema-DI2scngu.d.mts → fiscal-period.schema-BRdKAjrr.d.mts} +2 -2
- package/dist/{fiscal-period.schema-DI2scngu.d.mts.map → fiscal-period.schema-BRdKAjrr.d.mts.map} +1 -1
- package/dist/{idempotency.plugin-BESs9YPD.d.mts → idempotency.plugin-CPxPt4vX.d.mts} +1 -1
- package/dist/{idempotency.plugin-BESs9YPD.d.mts.map → idempotency.plugin-CPxPt4vX.d.mts.map} +1 -1
- package/dist/{idempotency.plugin-C6r8RI8d.mjs → idempotency.plugin-v9NQ_ta-.mjs} +3 -6
- package/dist/{idempotency.plugin-C6r8RI8d.mjs.map → idempotency.plugin-v9NQ_ta-.mjs.map} +1 -1
- package/dist/{universal-x33ZJODp.d.mts → index-BPukb3L8.d.mts} +1 -1
- package/dist/index-BPukb3L8.d.mts.map +1 -0
- package/dist/index-ZnSiqHYV.d.mts +105 -0
- package/dist/index-ZnSiqHYV.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +11 -12
- package/dist/index.mjs.map +1 -1
- package/dist/{journals-CI3Wb4EF.mjs → journals-oH-FK3g8.mjs} +2 -2
- package/dist/{journals-CI3Wb4EF.mjs.map → journals-oH-FK3g8.mjs.map} +1 -1
- package/dist/{logger-Cv6VVc4r.d.mts → logger-UbTdBb1x.d.mts} +1 -1
- package/dist/{logger-Cv6VVc4r.d.mts.map → logger-UbTdBb1x.d.mts.map} +1 -1
- package/dist/money.mjs +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +2 -3
- package/dist/reports/index.d.mts +1 -1
- package/dist/reports/index.mjs +2 -3
- package/dist/repositories/index.d.mts +1 -1
- package/dist/repositories/index.mjs +2 -3
- package/dist/schemas/index.d.mts +1 -1
- package/dist/schemas/index.mjs +2 -3
- package/dist/{session-Dh0s6zG4.mjs → session-Ba8E3Ufa.mjs} +3 -6
- package/dist/{session-Dh0s6zG4.mjs.map → session-Ba8E3Ufa.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/country/index.d.mts.map +0 -1
- package/dist/engine-Cd73EOT6.d.mts.map +0 -1
- package/dist/fiscal-close-DuXDgVvb.mjs.map +0 -1
- package/dist/fiscal-period.schema-CbALaaKl.mjs.map +0 -1
- package/dist/universal-CMfrZ2hG.mjs.map +0 -1
- package/dist/universal-x33ZJODp.d.mts.map +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { n as Errors } from "./errors-
|
|
2
|
-
|
|
1
|
+
import { n as Errors } from "./errors-B7yC-Jfw.mjs";
|
|
3
2
|
//#region src/plugins/double-entry.plugin.ts
|
|
4
3
|
function doubleEntryPlugin(options = {}) {
|
|
5
4
|
const { onlyOnPost = true, JournalEntryModel, AccountModel, orgField } = options;
|
|
@@ -86,7 +85,6 @@ function doubleEntryPlugin(options = {}) {
|
|
|
86
85
|
}
|
|
87
86
|
};
|
|
88
87
|
}
|
|
89
|
-
|
|
90
88
|
//#endregion
|
|
91
89
|
//#region src/plugins/fiscal-lock.plugin.ts
|
|
92
90
|
function fiscalLockPlugin(options) {
|
|
@@ -139,7 +137,6 @@ function fiscalLockPlugin(options) {
|
|
|
139
137
|
}
|
|
140
138
|
};
|
|
141
139
|
}
|
|
142
|
-
|
|
143
140
|
//#endregion
|
|
144
141
|
//#region src/plugins/idempotency.plugin.ts
|
|
145
142
|
function idempotencyPlugin(options) {
|
|
@@ -159,7 +156,7 @@ function idempotencyPlugin(options) {
|
|
|
159
156
|
}
|
|
160
157
|
};
|
|
161
158
|
}
|
|
162
|
-
|
|
163
159
|
//#endregion
|
|
164
160
|
export { fiscalLockPlugin as n, doubleEntryPlugin as r, idempotencyPlugin as t };
|
|
165
|
-
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=idempotency.plugin-v9NQ_ta-.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.plugin-C6r8RI8d.mjs","names":[],"sources":["../src/plugins/double-entry.plugin.ts","../src/plugins/fiscal-lock.plugin.ts","../src/plugins/idempotency.plugin.ts"],"sourcesContent":["/**\r\n * Double-Entry Validation Plugin for @classytic/mongokit\r\n *\r\n * Ensures every journal entry posted via the repository satisfies:\r\n * sum(debits) === sum(credits)\r\n *\r\n * Plugs into the before:create and before:update hooks.\r\n */\r\n\r\nimport type { Model } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface DoubleEntryPluginOptions {\r\n /** Only enforce on posted entries (default: true) */\r\n onlyOnPost?: boolean;\r\n /** Mongoose model — required to validate partial updates that only set state */\r\n JournalEntryModel?: Model<unknown>;\r\n /** Account model — when provided, posted creates verify account existence + tenant scoping */\r\n AccountModel?: Model<unknown>;\r\n /** Multi-tenant org field name (e.g. 'business'). Required for tenant-account integrity checks. */\r\n orgField?: string;\r\n}\r\n\r\nexport function doubleEntryPlugin(options: DoubleEntryPluginOptions = {}) {\r\n const { onlyOnPost = true, JournalEntryModel, AccountModel, orgField } = options;\r\n\r\n function validateItems(\r\n items: Array<{ debit?: number; credit?: number }>,\r\n data: Record<string, unknown>,\r\n ): void {\r\n // Each line must be debit OR credit (not both), and cannot be zero-value\r\n for (let i = 0; i < items.length; i++) {\r\n const d = items[i].debit ?? 0;\r\n const c = items[i].credit ?? 0;\r\n if (d > 0 && c > 0) {\r\n throw Errors.validation(\r\n `Invalid journal item at index ${i}: a line cannot have both debit (${d}) and credit (${c}) greater than zero.`,\r\n );\r\n }\r\n if (d === 0 && c === 0) {\r\n throw Errors.validation(\r\n `Invalid journal item at index ${i}: a line cannot have both debit and credit equal to zero.`,\r\n );\r\n }\r\n }\r\n\r\n const totalDebit = items.reduce((s, i) => s + (i.debit ?? 0), 0);\r\n const totalCredit = items.reduce((s, i) => s + (i.credit ?? 0), 0);\r\n\r\n // Integer cents — exact comparison, no floating-point drift possible.\r\n if (totalDebit !== totalCredit) {\r\n throw Errors.validation(\r\n `Double-entry violation: debits (${totalDebit}) ≠ credits (${totalCredit}). ` +\r\n `Difference: ${Math.abs(totalDebit - totalCredit)}`,\r\n );\r\n }\r\n\r\n // Sync totals onto the data object\r\n data.totalDebit = totalDebit;\r\n data.totalCredit = totalCredit;\r\n }\r\n\r\n return {\r\n name: 'accounting:double-entry',\r\n apply(repo: RepositoryInstance) {\r\n const validate = async (context: Record<string, unknown>) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // Skip draft entries if configured\r\n if (onlyOnPost && data.state !== 'posted') return;\r\n\r\n const items = data.journalItems as Array<{ debit?: number; credit?: number; account?: unknown }> | undefined;\r\n\r\n // Posted entries must have at least 2 journal items\r\n if (data.state === 'posted' && (!items || items.length < 2)) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${items?.length ?? 0}.`,\r\n );\r\n }\r\n\r\n if (!items || items.length === 0) return;\r\n\r\n validateItems(items, data);\r\n\r\n // Account existence + tenant-account integrity (fail-closed for posted creates)\r\n if (data.state === 'posted') {\r\n if (!AccountModel) {\r\n throw new Error(\r\n 'doubleEntryPlugin: AccountModel is required to validate posted entries. ' +\r\n 'Pass AccountModel in plugin options to enable account existence and tenant integrity checks.',\r\n );\r\n }\r\n await validateAccounts(items, data, context);\r\n }\r\n };\r\n\r\n /** Verify all journal item accounts exist and belong to the same org */\r\n const validateAccounts = async (\r\n items: Array<{ account?: unknown }>,\r\n data: Record<string, unknown>,\r\n context: Record<string, unknown>,\r\n ) => {\r\n const accountIds = items\r\n .map(i => i.account)\r\n .filter(a => a != null && a !== '');\r\n\r\n if (accountIds.length === 0) {\r\n throw Errors.validation('Posted entry has items with missing accounts.');\r\n }\r\n\r\n const selectFields = orgField ? `_id ${orgField}` : '_id';\r\n const accounts = await AccountModel!.find({ _id: { $in: accountIds } })\r\n .select(selectFields)\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Array<Record<string, unknown>>;\r\n\r\n // Check all accounts exist\r\n const foundIds = new Set(accounts.map(a => String(a._id)));\r\n const missingCount = accountIds.filter(id => !foundIds.has(String(id))).length;\r\n if (missingCount > 0) {\r\n throw Errors.validation(\r\n `${missingCount} item(s) reference non-existent accounts.`,\r\n );\r\n }\r\n\r\n // Check tenant scoping\r\n if (orgField && data[orgField] != null) {\r\n const dataOrg = String(data[orgField]);\r\n const crossTenant = accounts.filter(a => String(a[orgField]) !== dataOrg);\r\n if (crossTenant.length > 0) {\r\n throw Errors.validation(\r\n `${crossTenant.length} item(s) reference accounts from another organization.`,\r\n );\r\n }\r\n }\r\n };\r\n\r\n const validateUpdate = async (context: Record<string, unknown>) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // ── Immutability guard: block modifications to posted entries ──────\r\n // Allow: idempotent state re-set (state: 'posted')\r\n // Block: everything else — including reversed/reversedBy (only settable via\r\n // reverse() which uses entry.save() directly, bypassing this hook)\r\n if (JournalEntryModel) {\r\n const id = context.id;\r\n if (id) {\r\n // Check if target entry is already posted\r\n const target = await JournalEntryModel.findById(id)\r\n .select('state')\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (target?.state === 'posted') {\r\n // Block any state transition away from 'posted' (immutable ledger)\r\n if (data.state !== undefined && data.state !== 'posted') {\r\n throw Errors.immutable(\r\n 'Cannot change state of a posted journal entry. Posted entries are immutable.',\r\n );\r\n }\r\n\r\n // Only allow idempotent state re-set on posted entries.\r\n // reversed/reversedBy are NOT allowed through repository.update() —\r\n // reverse() uses entry.save() directly to bypass the plugin, so any\r\n // attempt to set these flags through the generic update path is illegitimate.\r\n const allowedKeys = new Set(['state']);\r\n const dataKeys = Object.keys(data);\r\n const hasDisallowedKeys = dataKeys.some(k => !allowedKeys.has(k));\r\n\r\n if (hasDisallowedKeys) {\r\n throw Errors.immutable(\r\n 'Cannot modify a posted journal entry. Use reverse() to create a correcting entry instead.',\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (onlyOnPost && data.state !== 'posted') return;\r\n\r\n const items = data.journalItems as Array<{ debit?: number; credit?: number }> | undefined;\r\n\r\n if (items !== undefined) {\r\n // Items present in payload — validate directly\r\n if (items.length < 2) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${items.length}.`,\r\n );\r\n }\r\n validateItems(items, data);\r\n\r\n // Account existence + tenant-account integrity (when AccountModel provided)\r\n if (AccountModel) {\r\n await validateAccounts(items as Array<{ account?: unknown }>, data, context);\r\n }\r\n return;\r\n }\r\n\r\n // state → posted but no journalItems in payload: fetch the persisted doc\r\n if (!JournalEntryModel) {\r\n throw new Error(\r\n 'doubleEntryPlugin: JournalEntryModel is required to validate partial updates that set state to \"posted\". ' +\r\n 'Pass JournalEntryModel in plugin options.',\r\n );\r\n }\r\n\r\n const id = context.id;\r\n if (!id) {\r\n throw new Error(\r\n 'doubleEntryPlugin: update context is missing \"id\". Cannot validate partial post without document ID.',\r\n );\r\n }\r\n\r\n const existing = await JournalEntryModel.findById(id)\r\n .select('journalItems')\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (!existing) return; // will 404 downstream\r\n\r\n const persistedItems = existing.journalItems as Array<{ debit?: number; credit?: number; account?: unknown }> | undefined;\r\n if (!persistedItems || persistedItems.length < 2) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${persistedItems?.length ?? 0}.`,\r\n );\r\n }\r\n\r\n validateItems(persistedItems, data);\r\n\r\n // Account existence + tenant-account integrity (when AccountModel provided)\r\n if (AccountModel) {\r\n await validateAccounts(persistedItems, { ...data, ...existing }, context);\r\n }\r\n };\r\n\r\n repo.on('before:create', (payload: unknown) => validate(payload as Record<string, unknown>));\r\n repo.on('before:update', (payload: unknown) => validateUpdate(payload as Record<string, unknown>));\r\n },\r\n };\r\n}\r\n","/**\r\n * Fiscal Lock Plugin for @classytic/mongokit\r\n *\r\n * Prevents journal entries from being created or posted\r\n * in a closed fiscal period.\r\n *\r\n * Requires a FiscalPeriod model to check against.\r\n */\r\n\r\nimport type { Model, ClientSession } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface FiscalLockPluginOptions {\r\n /** Mongoose model for fiscal periods */\r\n FiscalPeriodModel: Model<unknown>;\r\n /** Mongoose model for journal entries — needed to look up persisted date on partial updates */\r\n JournalEntryModel?: Model<unknown>;\r\n /** Organization field name (for multi-tenant) */\r\n orgField?: string;\r\n}\r\n\r\nexport function fiscalLockPlugin(options: FiscalLockPluginOptions) {\r\n const { FiscalPeriodModel, JournalEntryModel, orgField } = options;\r\n\r\n return {\r\n name: 'accounting:fiscal-lock',\r\n apply(repo: RepositoryInstance) {\r\n const checkPeriod = async (context: Record<string, unknown>, isUpdate: boolean) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // Only check when posting or creating posted entries\r\n if (data.state !== 'posted') return;\r\n\r\n const session = (context.session as ClientSession) ?? null;\r\n\r\n // Resolve the entry date (and org field from persisted doc if needed)\r\n let entryDate: Date | undefined;\r\n let persistedDoc: Record<string, unknown> | null = null;\r\n\r\n if (data.date) {\r\n entryDate = new Date(data.date as string | number | Date);\r\n } else if (!isUpdate) {\r\n // Create without explicit date — schema will default to now, so check against now\r\n entryDate = new Date();\r\n } else {\r\n // Partial update without date — fetch the persisted doc\r\n if (!context.id) {\r\n throw new Error(\r\n 'fiscalLockPlugin: update context is missing \"id\". Cannot validate fiscal lock without document ID.',\r\n );\r\n }\r\n if (!JournalEntryModel) {\r\n throw new Error(\r\n 'fiscalLockPlugin: JournalEntryModel is required to validate partial updates that set state to \"posted\". ' +\r\n 'Pass JournalEntryModel in plugin options.',\r\n );\r\n }\r\n const selectFields = orgField ? `date ${orgField}` : 'date';\r\n persistedDoc = await JournalEntryModel.findById(context.id)\r\n .select(selectFields)\r\n .session(session)\r\n .lean() as Record<string, unknown> | null;\r\n if (persistedDoc?.date) {\r\n entryDate = new Date(persistedDoc.date as string | number | Date);\r\n }\r\n }\r\n\r\n if (!entryDate) return; // No date to check against (new entry without date defaults to draft)\r\n\r\n // Build query\r\n const query: Record<string, unknown> = {\r\n startDate: { $lte: entryDate },\r\n endDate: { $gte: entryDate },\r\n closed: true,\r\n };\r\n\r\n // Multi-tenant scope — check payload, context, then persisted doc\r\n if (orgField) {\r\n let orgValue = data[orgField] ?? context[orgField];\r\n\r\n if (!orgValue && isUpdate) {\r\n // Org field not in payload or context — resolve from persisted doc\r\n if (persistedDoc) {\r\n orgValue = persistedDoc[orgField];\r\n } else if (context.id && JournalEntryModel) {\r\n const persisted = await JournalEntryModel.findById(context.id)\r\n .select(orgField)\r\n .session(session)\r\n .lean() as Record<string, unknown> | null;\r\n if (persisted) orgValue = persisted[orgField];\r\n }\r\n }\r\n\r\n if (!orgValue) {\r\n throw new Error(\r\n `fiscalLockPlugin: orgField \"${orgField}\" is configured but could not be resolved from ` +\r\n 'payload, context, or persisted document. Refusing to run unscoped fiscal period query.',\r\n );\r\n }\r\n\r\n query[orgField] = orgValue;\r\n }\r\n\r\n const closedPeriod = await FiscalPeriodModel.findOne(query).session(session).lean();\r\n\r\n if (closedPeriod) {\r\n const period = closedPeriod as Record<string, unknown>;\r\n throw Errors.fiscal(\r\n `Cannot post entry dated ${entryDate.toISOString().split('T')[0]}: ` +\r\n `fiscal period \"${period.name}\" is closed.`,\r\n );\r\n }\r\n };\r\n\r\n repo.on('before:create', (payload: unknown) => checkPeriod(payload as Record<string, unknown>, false));\r\n repo.on('before:update', (payload: unknown) => checkPeriod(payload as Record<string, unknown>, true));\r\n },\r\n };\r\n}\r\n","/**\r\n * Idempotency Plugin for @classytic/mongokit\r\n *\r\n * Prevents duplicate journal entries by checking for existing entries\r\n * with the same idempotency key before creation.\r\n */\r\n\r\nimport type { Model, ClientSession } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface IdempotencyPluginOptions {\r\n /** Mongoose model for journal entries */\r\n JournalEntryModel: Model<unknown>;\r\n /** Multi-tenant org field name */\r\n orgField?: string;\r\n}\r\n\r\nexport function idempotencyPlugin(options: IdempotencyPluginOptions) {\r\n const { JournalEntryModel, orgField } = options;\r\n\r\n return {\r\n name: 'accounting:idempotency',\r\n apply(repo: RepositoryInstance) {\r\n repo.on('before:create', async (raw: unknown) => {\r\n const context = raw as Record<string, unknown>;\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data?.idempotencyKey) return;\r\n\r\n const query: Record<string, unknown> = {\r\n idempotencyKey: data.idempotencyKey,\r\n };\r\n if (orgField && data[orgField]) {\r\n query[orgField] = data[orgField];\r\n }\r\n\r\n const existing = await JournalEntryModel.findOne(query)\r\n .select('_id')\r\n .session((context.session as ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (existing) {\r\n throw Errors.conflict(\r\n `Duplicate idempotency key: \"${data.idempotencyKey}\". Existing entry: ${existing._id}`,\r\n );\r\n }\r\n });\r\n },\r\n };\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"idempotency.plugin-v9NQ_ta-.mjs","names":[],"sources":["../src/plugins/double-entry.plugin.ts","../src/plugins/fiscal-lock.plugin.ts","../src/plugins/idempotency.plugin.ts"],"sourcesContent":["/**\r\n * Double-Entry Validation Plugin for @classytic/mongokit\r\n *\r\n * Ensures every journal entry posted via the repository satisfies:\r\n * sum(debits) === sum(credits)\r\n *\r\n * Plugs into the before:create and before:update hooks.\r\n */\r\n\r\nimport type { Model } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface DoubleEntryPluginOptions {\r\n /** Only enforce on posted entries (default: true) */\r\n onlyOnPost?: boolean;\r\n /** Mongoose model — required to validate partial updates that only set state */\r\n JournalEntryModel?: Model<unknown>;\r\n /** Account model — when provided, posted creates verify account existence + tenant scoping */\r\n AccountModel?: Model<unknown>;\r\n /** Multi-tenant org field name (e.g. 'business'). Required for tenant-account integrity checks. */\r\n orgField?: string;\r\n}\r\n\r\nexport function doubleEntryPlugin(options: DoubleEntryPluginOptions = {}) {\r\n const { onlyOnPost = true, JournalEntryModel, AccountModel, orgField } = options;\r\n\r\n function validateItems(\r\n items: Array<{ debit?: number; credit?: number }>,\r\n data: Record<string, unknown>,\r\n ): void {\r\n // Each line must be debit OR credit (not both), and cannot be zero-value\r\n for (let i = 0; i < items.length; i++) {\r\n const d = items[i].debit ?? 0;\r\n const c = items[i].credit ?? 0;\r\n if (d > 0 && c > 0) {\r\n throw Errors.validation(\r\n `Invalid journal item at index ${i}: a line cannot have both debit (${d}) and credit (${c}) greater than zero.`,\r\n );\r\n }\r\n if (d === 0 && c === 0) {\r\n throw Errors.validation(\r\n `Invalid journal item at index ${i}: a line cannot have both debit and credit equal to zero.`,\r\n );\r\n }\r\n }\r\n\r\n const totalDebit = items.reduce((s, i) => s + (i.debit ?? 0), 0);\r\n const totalCredit = items.reduce((s, i) => s + (i.credit ?? 0), 0);\r\n\r\n // Integer cents — exact comparison, no floating-point drift possible.\r\n if (totalDebit !== totalCredit) {\r\n throw Errors.validation(\r\n `Double-entry violation: debits (${totalDebit}) ≠ credits (${totalCredit}). ` +\r\n `Difference: ${Math.abs(totalDebit - totalCredit)}`,\r\n );\r\n }\r\n\r\n // Sync totals onto the data object\r\n data.totalDebit = totalDebit;\r\n data.totalCredit = totalCredit;\r\n }\r\n\r\n return {\r\n name: 'accounting:double-entry',\r\n apply(repo: RepositoryInstance) {\r\n const validate = async (context: Record<string, unknown>) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // Skip draft entries if configured\r\n if (onlyOnPost && data.state !== 'posted') return;\r\n\r\n const items = data.journalItems as Array<{ debit?: number; credit?: number; account?: unknown }> | undefined;\r\n\r\n // Posted entries must have at least 2 journal items\r\n if (data.state === 'posted' && (!items || items.length < 2)) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${items?.length ?? 0}.`,\r\n );\r\n }\r\n\r\n if (!items || items.length === 0) return;\r\n\r\n validateItems(items, data);\r\n\r\n // Account existence + tenant-account integrity (fail-closed for posted creates)\r\n if (data.state === 'posted') {\r\n if (!AccountModel) {\r\n throw new Error(\r\n 'doubleEntryPlugin: AccountModel is required to validate posted entries. ' +\r\n 'Pass AccountModel in plugin options to enable account existence and tenant integrity checks.',\r\n );\r\n }\r\n await validateAccounts(items, data, context);\r\n }\r\n };\r\n\r\n /** Verify all journal item accounts exist and belong to the same org */\r\n const validateAccounts = async (\r\n items: Array<{ account?: unknown }>,\r\n data: Record<string, unknown>,\r\n context: Record<string, unknown>,\r\n ) => {\r\n const accountIds = items\r\n .map(i => i.account)\r\n .filter(a => a != null && a !== '');\r\n\r\n if (accountIds.length === 0) {\r\n throw Errors.validation('Posted entry has items with missing accounts.');\r\n }\r\n\r\n const selectFields = orgField ? `_id ${orgField}` : '_id';\r\n const accounts = await AccountModel!.find({ _id: { $in: accountIds } })\r\n .select(selectFields)\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Array<Record<string, unknown>>;\r\n\r\n // Check all accounts exist\r\n const foundIds = new Set(accounts.map(a => String(a._id)));\r\n const missingCount = accountIds.filter(id => !foundIds.has(String(id))).length;\r\n if (missingCount > 0) {\r\n throw Errors.validation(\r\n `${missingCount} item(s) reference non-existent accounts.`,\r\n );\r\n }\r\n\r\n // Check tenant scoping\r\n if (orgField && data[orgField] != null) {\r\n const dataOrg = String(data[orgField]);\r\n const crossTenant = accounts.filter(a => String(a[orgField]) !== dataOrg);\r\n if (crossTenant.length > 0) {\r\n throw Errors.validation(\r\n `${crossTenant.length} item(s) reference accounts from another organization.`,\r\n );\r\n }\r\n }\r\n };\r\n\r\n const validateUpdate = async (context: Record<string, unknown>) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // ── Immutability guard: block modifications to posted entries ──────\r\n // Allow: idempotent state re-set (state: 'posted')\r\n // Block: everything else — including reversed/reversedBy (only settable via\r\n // reverse() which uses entry.save() directly, bypassing this hook)\r\n if (JournalEntryModel) {\r\n const id = context.id;\r\n if (id) {\r\n // Check if target entry is already posted\r\n const target = await JournalEntryModel.findById(id)\r\n .select('state')\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (target?.state === 'posted') {\r\n // Block any state transition away from 'posted' (immutable ledger)\r\n if (data.state !== undefined && data.state !== 'posted') {\r\n throw Errors.immutable(\r\n 'Cannot change state of a posted journal entry. Posted entries are immutable.',\r\n );\r\n }\r\n\r\n // Only allow idempotent state re-set on posted entries.\r\n // reversed/reversedBy are NOT allowed through repository.update() —\r\n // reverse() uses entry.save() directly to bypass the plugin, so any\r\n // attempt to set these flags through the generic update path is illegitimate.\r\n const allowedKeys = new Set(['state']);\r\n const dataKeys = Object.keys(data);\r\n const hasDisallowedKeys = dataKeys.some(k => !allowedKeys.has(k));\r\n\r\n if (hasDisallowedKeys) {\r\n throw Errors.immutable(\r\n 'Cannot modify a posted journal entry. Use reverse() to create a correcting entry instead.',\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (onlyOnPost && data.state !== 'posted') return;\r\n\r\n const items = data.journalItems as Array<{ debit?: number; credit?: number }> | undefined;\r\n\r\n if (items !== undefined) {\r\n // Items present in payload — validate directly\r\n if (items.length < 2) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${items.length}.`,\r\n );\r\n }\r\n validateItems(items, data);\r\n\r\n // Account existence + tenant-account integrity (when AccountModel provided)\r\n if (AccountModel) {\r\n await validateAccounts(items as Array<{ account?: unknown }>, data, context);\r\n }\r\n return;\r\n }\r\n\r\n // state → posted but no journalItems in payload: fetch the persisted doc\r\n if (!JournalEntryModel) {\r\n throw new Error(\r\n 'doubleEntryPlugin: JournalEntryModel is required to validate partial updates that set state to \"posted\". ' +\r\n 'Pass JournalEntryModel in plugin options.',\r\n );\r\n }\r\n\r\n const id = context.id;\r\n if (!id) {\r\n throw new Error(\r\n 'doubleEntryPlugin: update context is missing \"id\". Cannot validate partial post without document ID.',\r\n );\r\n }\r\n\r\n const existing = await JournalEntryModel.findById(id)\r\n .select('journalItems')\r\n .session((context.session as import('mongoose').ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (!existing) return; // will 404 downstream\r\n\r\n const persistedItems = existing.journalItems as Array<{ debit?: number; credit?: number; account?: unknown }> | undefined;\r\n if (!persistedItems || persistedItems.length < 2) {\r\n throw Errors.validation(\r\n `Cannot post entry: at least 2 journal items required, got ${persistedItems?.length ?? 0}.`,\r\n );\r\n }\r\n\r\n validateItems(persistedItems, data);\r\n\r\n // Account existence + tenant-account integrity (when AccountModel provided)\r\n if (AccountModel) {\r\n await validateAccounts(persistedItems, { ...data, ...existing }, context);\r\n }\r\n };\r\n\r\n repo.on('before:create', (payload: unknown) => validate(payload as Record<string, unknown>));\r\n repo.on('before:update', (payload: unknown) => validateUpdate(payload as Record<string, unknown>));\r\n },\r\n };\r\n}\r\n","/**\r\n * Fiscal Lock Plugin for @classytic/mongokit\r\n *\r\n * Prevents journal entries from being created or posted\r\n * in a closed fiscal period.\r\n *\r\n * Requires a FiscalPeriod model to check against.\r\n */\r\n\r\nimport type { Model, ClientSession } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface FiscalLockPluginOptions {\r\n /** Mongoose model for fiscal periods */\r\n FiscalPeriodModel: Model<unknown>;\r\n /** Mongoose model for journal entries — needed to look up persisted date on partial updates */\r\n JournalEntryModel?: Model<unknown>;\r\n /** Organization field name (for multi-tenant) */\r\n orgField?: string;\r\n}\r\n\r\nexport function fiscalLockPlugin(options: FiscalLockPluginOptions) {\r\n const { FiscalPeriodModel, JournalEntryModel, orgField } = options;\r\n\r\n return {\r\n name: 'accounting:fiscal-lock',\r\n apply(repo: RepositoryInstance) {\r\n const checkPeriod = async (context: Record<string, unknown>, isUpdate: boolean) => {\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data) return;\r\n\r\n // Only check when posting or creating posted entries\r\n if (data.state !== 'posted') return;\r\n\r\n const session = (context.session as ClientSession) ?? null;\r\n\r\n // Resolve the entry date (and org field from persisted doc if needed)\r\n let entryDate: Date | undefined;\r\n let persistedDoc: Record<string, unknown> | null = null;\r\n\r\n if (data.date) {\r\n entryDate = new Date(data.date as string | number | Date);\r\n } else if (!isUpdate) {\r\n // Create without explicit date — schema will default to now, so check against now\r\n entryDate = new Date();\r\n } else {\r\n // Partial update without date — fetch the persisted doc\r\n if (!context.id) {\r\n throw new Error(\r\n 'fiscalLockPlugin: update context is missing \"id\". Cannot validate fiscal lock without document ID.',\r\n );\r\n }\r\n if (!JournalEntryModel) {\r\n throw new Error(\r\n 'fiscalLockPlugin: JournalEntryModel is required to validate partial updates that set state to \"posted\". ' +\r\n 'Pass JournalEntryModel in plugin options.',\r\n );\r\n }\r\n const selectFields = orgField ? `date ${orgField}` : 'date';\r\n persistedDoc = await JournalEntryModel.findById(context.id)\r\n .select(selectFields)\r\n .session(session)\r\n .lean() as Record<string, unknown> | null;\r\n if (persistedDoc?.date) {\r\n entryDate = new Date(persistedDoc.date as string | number | Date);\r\n }\r\n }\r\n\r\n if (!entryDate) return; // No date to check against (new entry without date defaults to draft)\r\n\r\n // Build query\r\n const query: Record<string, unknown> = {\r\n startDate: { $lte: entryDate },\r\n endDate: { $gte: entryDate },\r\n closed: true,\r\n };\r\n\r\n // Multi-tenant scope — check payload, context, then persisted doc\r\n if (orgField) {\r\n let orgValue = data[orgField] ?? context[orgField];\r\n\r\n if (!orgValue && isUpdate) {\r\n // Org field not in payload or context — resolve from persisted doc\r\n if (persistedDoc) {\r\n orgValue = persistedDoc[orgField];\r\n } else if (context.id && JournalEntryModel) {\r\n const persisted = await JournalEntryModel.findById(context.id)\r\n .select(orgField)\r\n .session(session)\r\n .lean() as Record<string, unknown> | null;\r\n if (persisted) orgValue = persisted[orgField];\r\n }\r\n }\r\n\r\n if (!orgValue) {\r\n throw new Error(\r\n `fiscalLockPlugin: orgField \"${orgField}\" is configured but could not be resolved from ` +\r\n 'payload, context, or persisted document. Refusing to run unscoped fiscal period query.',\r\n );\r\n }\r\n\r\n query[orgField] = orgValue;\r\n }\r\n\r\n const closedPeriod = await FiscalPeriodModel.findOne(query).session(session).lean();\r\n\r\n if (closedPeriod) {\r\n const period = closedPeriod as Record<string, unknown>;\r\n throw Errors.fiscal(\r\n `Cannot post entry dated ${entryDate.toISOString().split('T')[0]}: ` +\r\n `fiscal period \"${period.name}\" is closed.`,\r\n );\r\n }\r\n };\r\n\r\n repo.on('before:create', (payload: unknown) => checkPeriod(payload as Record<string, unknown>, false));\r\n repo.on('before:update', (payload: unknown) => checkPeriod(payload as Record<string, unknown>, true));\r\n },\r\n };\r\n}\r\n","/**\r\n * Idempotency Plugin for @classytic/mongokit\r\n *\r\n * Prevents duplicate journal entries by checking for existing entries\r\n * with the same idempotency key before creation.\r\n */\r\n\r\nimport type { Model, ClientSession } from 'mongoose';\r\nimport { Errors } from '../utils/errors.js';\r\n\r\n/** Minimal interface matching @classytic/mongokit RepositoryInstance */\r\ninterface RepositoryInstance {\r\n on(event: string, listener: (data: unknown) => void | Promise<void>): unknown;\r\n}\r\n\r\nexport interface IdempotencyPluginOptions {\r\n /** Mongoose model for journal entries */\r\n JournalEntryModel: Model<unknown>;\r\n /** Multi-tenant org field name */\r\n orgField?: string;\r\n}\r\n\r\nexport function idempotencyPlugin(options: IdempotencyPluginOptions) {\r\n const { JournalEntryModel, orgField } = options;\r\n\r\n return {\r\n name: 'accounting:idempotency',\r\n apply(repo: RepositoryInstance) {\r\n repo.on('before:create', async (raw: unknown) => {\r\n const context = raw as Record<string, unknown>;\r\n const data = context.data as Record<string, unknown> | undefined;\r\n if (!data?.idempotencyKey) return;\r\n\r\n const query: Record<string, unknown> = {\r\n idempotencyKey: data.idempotencyKey,\r\n };\r\n if (orgField && data[orgField]) {\r\n query[orgField] = data[orgField];\r\n }\r\n\r\n const existing = await JournalEntryModel.findOne(query)\r\n .select('_id')\r\n .session((context.session as ClientSession) ?? null)\r\n .lean() as Record<string, unknown> | null;\r\n\r\n if (existing) {\r\n throw Errors.conflict(\r\n `Duplicate idempotency key: \"${data.idempotencyKey}\". Existing entry: ${existing._id}`,\r\n );\r\n }\r\n });\r\n },\r\n };\r\n}\r\n"],"mappings":"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"}
|
|
@@ -134,4 +134,4 @@ declare const quickbooksFieldMap: ExportFieldMap<FlatJournalRow>;
|
|
|
134
134
|
declare const universalFieldMap: ExportFieldMap<FlatJournalRow>;
|
|
135
135
|
//#endregion
|
|
136
136
|
export { PopulatedJournalEntry as _, exportToCsv as a, getHeaders as c, serializeCsv as d, CsvOptions as f, PopulatedAccount as g, FlatJournalRow as h, flattenJournalEntry as i, buildCsv as l, ExportFieldMap as m, quickbooksFieldMap as n, extractAllRows as o, ExportField as p, flattenJournalEntries as r, extractRow as s, universalFieldMap as t, escapeCell as u, PopulatedJournalItem as v };
|
|
137
|
-
//# sourceMappingURL=
|
|
137
|
+
//# sourceMappingURL=index-BPukb3L8.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BPukb3L8.d.mts","names":[],"sources":["../src/exports/types.ts","../src/exports/csv-serializer.ts","../src/exports/field-map.ts","../src/exports/flatten-journal.ts","../src/exports/field-maps/quickbooks.ts","../src/exports/field-maps/universal.ts"],"mappings":";;AAeA;;;;;;;;;;;UAAiB,gBAAA;EACf,GAAA;EACA,eAAA;EACA,IAAA;EACA,MAAA;EACA,aAAA;AAAA;;;;UAMe,oBAAA;EACf,OAAA,EAAS,gBAAA;EACT,KAAA;EACA,IAAA,GAAO,IAAA;EACP,KAAA;EACA,MAAA;EACA,UAAA,GAAa,KAAA;IAAQ,OAAA;IAAkB,OAAA;EAAA;EAE3B;EAAA,CAAX,GAAA;AAAA;;;;;UAOc,qBAAA;EACf,GAAA;EACA,WAAA;EACA,eAAA;EACA,KAAA;EACA,IAAA,EAAM,IAAA;EACN,YAAA,EAAc,oBAAA;EACd,UAAA;EACA,WAAA;EACA,KAAA;EACA,QAAA;EACA,SAAA,GAAY,IAAA;EACZ,SAAA,GAAY,IAAA;EAAA,CACX,GAAA;AAAA;;;;;UASc,cAAA;EACf,OAAA;EACA,WAAA;EACA,eAAA;EACA,UAAA;EACA,SAAA,EAAW,IAAA;EACX,KAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EAEA,SAAA;EACA,WAAA;EACA,eAAA;EACA,SAAA;EACA,QAAA,EAAU,IAAA;EACV,KAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA;EAEA,SAAA;EACA,SAAA;EARA;EAAA,CAWC,GAAA;AAAA;;;;;UASc,WAAA,QAAmB,cAAA;EAAA,SACzB,MAAA;EAAA,SACA,OAAA,GAAU,GAAA,EAAK,IAAA;AAAA;;AAF1B;;UAQiB,cAAA,QAAsB,cAAA;EAAA,SAC5B,IAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA,WAAiB,WAAA,CAAY,IAAA;AAAA;AAAA,UAKvB,UAAA;EACf,SAAA;EACA,cAAA;EACA,cAAA;AAAA;;;;iBCvGc,UAAA,CAAW,KAAA;;iBAQX,YAAA,CACd,IAAA,kCACA,OAAA,GAAS,UAAA;ADGX;AAAA,iBCOgB,QAAA,CACd,OAAA,qBACA,QAAA,kCACA,OAAA,GAAS,UAAA;;;;iBC1BK,UAAA,MAAA,CAAiB,QAAA,EAAU,cAAA,CAAe,IAAA;;iBAK1C,UAAA,MAAA,CAAiB,QAAA,EAAU,cAAA,CAAe,IAAA,GAAO,GAAA,EAAK,IAAA;;iBAKtD,cAAA,MAAA,CACd,QAAA,EAAU,cAAA,CAAe,IAAA,GACzB,IAAA,WAAe,IAAA;AFIjB;AAAA,iBEEgB,WAAA,MAAA,CACd,QAAA,EAAU,cAAA,CAAe,IAAA,GACzB,IAAA,WAAe,IAAA,IACf,OAAA,GAAU,UAAA;;;;iBCGI,mBAAA,CAAoB,KAAA,EAAO,qBAAA,GAAwB,cAAA;;iBAiDnD,qBAAA,CACd,OAAA,WAAkB,qBAAA,KACjB,cAAA;;;cC5DU,kBAAA,EAAoB,cAAA,CAAe,cAAA;;;cCJnC,iBAAA,EAAmB,cAAA,CAAe,cAAA"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { t as AccountType } from "./core-8Xfnpn6g.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/country/index.d.ts
|
|
4
|
+
interface TaxCode {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly taxType: string;
|
|
8
|
+
readonly rate: number;
|
|
9
|
+
readonly direction: 'collected' | 'recoverable' | 'paid';
|
|
10
|
+
readonly province?: string;
|
|
11
|
+
readonly reportLines?: readonly number[];
|
|
12
|
+
readonly description: string;
|
|
13
|
+
readonly active: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface TaxCodesByRegion {
|
|
16
|
+
readonly [region: string]: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
interface TaxReportLine {
|
|
19
|
+
readonly line: number | string;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly type: 'input' | 'calculated' | 'manual';
|
|
23
|
+
readonly calculate?: (data: Record<string | number, number>) => number;
|
|
24
|
+
readonly section: string;
|
|
25
|
+
}
|
|
26
|
+
interface TaxReportTemplate {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly lines: Readonly<Record<string | number, TaxReportLine>>;
|
|
29
|
+
calculate(inputData: Record<string | number, number>, manualData?: Record<string | number, number>): Record<string | number, number>;
|
|
30
|
+
summarize(calculated: Record<string | number, number>): Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
interface CountryPack {
|
|
33
|
+
/** ISO 3166-1 alpha-2 code (e.g., 'CA', 'US', 'GB') */
|
|
34
|
+
readonly code: string;
|
|
35
|
+
/** Country name */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Default currency code */
|
|
38
|
+
readonly defaultCurrency: string;
|
|
39
|
+
/**
|
|
40
|
+
* Full chart of accounts template — flat array of account type definitions.
|
|
41
|
+
* Includes both regular accounts and virtual tax sub-accounts.
|
|
42
|
+
*/
|
|
43
|
+
readonly accountTypes: readonly AccountType[];
|
|
44
|
+
/** Tax codes indexed by code string */
|
|
45
|
+
readonly taxCodes: Readonly<Record<string, TaxCode>>;
|
|
46
|
+
/** Tax codes grouped by region/province/state */
|
|
47
|
+
readonly taxCodesByRegion: TaxCodesByRegion;
|
|
48
|
+
/** Available regions (provinces/states) */
|
|
49
|
+
readonly regions: readonly string[];
|
|
50
|
+
/** Tax report template (e.g., CRA GST/HST return) */
|
|
51
|
+
readonly taxReport?: TaxReportTemplate;
|
|
52
|
+
/** Account code for prior retained earnings (e.g. '3660' CA, '3200' US) */
|
|
53
|
+
readonly retainedEarningsCode?: string;
|
|
54
|
+
/** Account code for current year net income (e.g. '3680' CA, '3210' US) */
|
|
55
|
+
readonly currentYearEarningsCode?: string;
|
|
56
|
+
/** Group label code used to identify Cost of Sales in the income statement */
|
|
57
|
+
readonly cogsGroupCode?: string;
|
|
58
|
+
/** Override default English report section names */
|
|
59
|
+
readonly reportLabels?: {
|
|
60
|
+
readonly assets?: string;
|
|
61
|
+
readonly liabilities?: string;
|
|
62
|
+
readonly equity?: string;
|
|
63
|
+
readonly revenue?: string;
|
|
64
|
+
readonly expenses?: string;
|
|
65
|
+
};
|
|
66
|
+
/** Get all account types that can be posted to (not groups, not totals) */
|
|
67
|
+
getPostingAccountTypes(): readonly AccountType[];
|
|
68
|
+
/** Get account type by code */
|
|
69
|
+
getAccountType(code: string): AccountType | undefined;
|
|
70
|
+
/** Validate an account type code exists */
|
|
71
|
+
isValidAccountType(code: string): boolean;
|
|
72
|
+
/** Check if an account type can receive postings */
|
|
73
|
+
isPostingAccount(code: string): boolean;
|
|
74
|
+
/** Get tax codes for a specific region */
|
|
75
|
+
getTaxCodesForRegion(region: string): TaxCode[];
|
|
76
|
+
/** Flatten hierarchical accounts (if needed) */
|
|
77
|
+
flattenAccountTypes(): readonly AccountType[];
|
|
78
|
+
}
|
|
79
|
+
interface CountryPackInput {
|
|
80
|
+
code: string;
|
|
81
|
+
name: string;
|
|
82
|
+
defaultCurrency: string;
|
|
83
|
+
accountTypes: readonly AccountType[];
|
|
84
|
+
taxCodes: Readonly<Record<string, TaxCode>>;
|
|
85
|
+
taxCodesByRegion: TaxCodesByRegion;
|
|
86
|
+
regions: readonly string[];
|
|
87
|
+
taxReport?: TaxReportTemplate;
|
|
88
|
+
retainedEarningsCode?: string;
|
|
89
|
+
currentYearEarningsCode?: string;
|
|
90
|
+
cogsGroupCode?: string;
|
|
91
|
+
reportLabels?: {
|
|
92
|
+
readonly assets?: string;
|
|
93
|
+
readonly liabilities?: string;
|
|
94
|
+
readonly equity?: string;
|
|
95
|
+
readonly revenue?: string;
|
|
96
|
+
readonly expenses?: string;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Factory to create a CountryPack with auto-generated helper methods.
|
|
101
|
+
*/
|
|
102
|
+
declare function defineCountryPack(input: CountryPackInput): CountryPack;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { TaxReportLine as a, TaxCodesByRegion as i, CountryPackInput as n, TaxReportTemplate as o, TaxCode as r, defineCountryPack as s, CountryPack as t };
|
|
105
|
+
//# sourceMappingURL=index-ZnSiqHYV.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-ZnSiqHYV.d.mts","names":[],"sources":["../src/country/index.ts"],"mappings":";;;UAgBiB,OAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAA;EAAA,SACA,OAAA;EAAA,SACA,IAAA;EAAA,SACA,SAAA;EAAA,SACA,QAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,MAAA;AAAA;AAAA,UAGM,gBAAA;EAAA,UACL,MAAA;AAAA;AAAA,UAKK,aAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA;EAAA,SACA,SAAA,IAAa,IAAA,EAAM,MAAA;EAAA,SACnB,OAAA;AAAA;AAAA,UAGM,iBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA,EAAO,QAAA,CAAS,MAAA,kBAAwB,aAAA;EACjD,SAAA,CAAU,SAAA,EAAW,MAAA,2BAAiC,UAAA,GAAa,MAAA,4BAAkC,MAAA;EACrG,SAAA,CAAU,UAAA,EAAY,MAAA,4BAAkC,MAAA;AAAA;AAAA,UAKzC,WAAA;EAPU;EAAA,SAShB,IAAA;EARY;EAAA,SAUZ,IAAA;EAV4F;EAAA,SAY5F,eAAA;EAX+C;;;;EAAA,SAiB/C,YAAA,WAAuB,WAAA;EAnBhB;EAAA,SAsBP,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,OAAA;EAtBM;EAAA,SAyBxC,gBAAA,EAAkB,gBAAA;EAxBN;EAAA,SA2BZ,OAAA;EA3B0D;EAAA,SA8B1D,SAAA,GAAY,iBAAA;EA9BgF;EAAA,SAmC5F,oBAAA;EAlCa;EAAA,SAoCb,uBAAA;EApC+C;EAAA,SAsC/C,aAAA;EAtCqD;EAAA,SAwCrD,YAAA;IAAA,SACE,MAAA;IAAA,SACA,WAAA;IAAA,SACA,MAAA;IAAA,SACA,OAAA;IAAA,SACA,QAAA;EAAA;EAtBgB;EA4B3B,sBAAA,aAAmC,WAAA;EAAA;EAGnC,cAAA,CAAe,IAAA,WAAe,WAAA;EASQ;EANtC,kBAAA,CAAmB,IAAA;EASwB;EAN3C,gBAAA,CAAiB,IAAA;EArDR;EAwDT,oBAAA,CAAqB,MAAA,WAAiB,OAAA;EApD7B;EAuDT,mBAAA,aAAgC,WAAA;AAAA;AAAA,UAKjB,gBAAA;EACf,IAAA;EACA,IAAA;EACA,eAAA;EACA,YAAA,WAAuB,WAAA;EACvB,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,OAAA;EAClC,gBAAA,EAAkB,gBAAA;EAClB,OAAA;EACA,SAAA,GAAY,iBAAA;EACZ,oBAAA;EACA,uBAAA;EACA,aAAA;EACA,YAAA;IAAA,SACW,MAAA;IAAA,SACA,WAAA;IAAA,SACA,MAAA;IAAA,SACA,OAAA;IAAA,SACA,QAAA;EAAA;AAAA;;;;iBAOG,iBAAA,CAAkB,KAAA,EAAO,gBAAA,GAAmB,WAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { _ as TaxMetadata, a as Cents, c as DateRange, d as JournalType, f as MainType, g as TaxDetail, h as StatementType, i as CategoryKey, l as EntryState, m as ObjectId, n as CashFlowCategory, o as Currency, p as NormalBalance, s as DateOption, t as AccountType, u as JournalItem, v as TotalAccountOp } from "./core-
|
|
2
|
-
import { _ as getNormalBalance, a as JOURNAL_CODES, b as isValidCategory, c as getJournalTypeCodes, d as CATEGORY_KEYS, i as isValidCurrency, l as isValidJournalType, n as getCurrency, o as JOURNAL_TYPES, r as getMinorUnit, t as CURRENCIES, u as CATEGORIES, v as isBalanceSheet, y as isIncomeStatement } from "./currencies-
|
|
3
|
-
import {
|
|
4
|
-
import { _ as PopulatedJournalEntry, a as exportToCsv, h as FlatJournalRow, m as ExportFieldMap, n as quickbooksFieldMap, p as ExportField, r as flattenJournalEntries, t as universalFieldMap } from "./
|
|
1
|
+
import { _ as TaxMetadata, a as Cents, c as DateRange, d as JournalType, f as MainType, g as TaxDetail, h as StatementType, i as CategoryKey, l as EntryState, m as ObjectId, n as CashFlowCategory, o as Currency, p as NormalBalance, s as DateOption, t as AccountType, u as JournalItem, v as TotalAccountOp } from "./core-8Xfnpn6g.mjs";
|
|
2
|
+
import { _ as getNormalBalance, a as JOURNAL_CODES, b as isValidCategory, c as getJournalTypeCodes, d as CATEGORY_KEYS, i as isValidCurrency, l as isValidJournalType, n as getCurrency, o as JOURNAL_TYPES, r as getMinorUnit, t as CURRENCIES, u as CATEGORIES, v as isBalanceSheet, y as isIncomeStatement } from "./currencies-4WAbFRlw.mjs";
|
|
3
|
+
import { a as TaxReportLine, i as TaxCodesByRegion, n as CountryPackInput, o as TaxReportTemplate, r as TaxCode, s as defineCountryPack, t as CountryPack } from "./index-ZnSiqHYV.mjs";
|
|
4
|
+
import { _ as PopulatedJournalEntry, a as exportToCsv, h as FlatJournalRow, m as ExportFieldMap, n as quickbooksFieldMap, p as ExportField, r as flattenJournalEntries, t as universalFieldMap } from "./index-BPukb3L8.mjs";
|
|
5
5
|
import { Money, abs, add, allocate, equals, format, formatPlain, fromDecimal, isNegative, isPositive, isValid, isZero, max, min, multiply, negate, parseCents, percentage, round, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
|
|
6
|
-
import { n as defaultLogger, t as Logger } from "./logger-
|
|
7
|
-
import { a as
|
|
8
|
-
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "./fiscal-period.schema-
|
|
9
|
-
import { i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin } from "./idempotency.plugin-
|
|
10
|
-
import { C as ReportAccount, D as TaxReturnSummary, E as TaxReport, O as TrialBalanceReport, S as LedgerEntry, T as ReportGroup, _ as CashFlowReport, a as reopenFiscalPeriod, b as GeneralLedgerReport, d as generateIncomeStatement, g as BalanceSheetReport, h as generateTrialBalance, i as closeFiscalPeriod, k as TrialBalanceRow, l as generateGeneralLedger, p as generateBalanceSheet, s as generateCashFlow, v as CashFlowSection, w as ReportCategory, x as IncomeStatementReport, y as GeneralLedgerAccount } from "./fiscal-close-
|
|
11
|
-
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "./account.repository-
|
|
6
|
+
import { n as defaultLogger, t as Logger } from "./logger-UbTdBb1x.mjs";
|
|
7
|
+
import { a as MultiTenantConfig, i as MultiCurrencyConfig, n as AuditConfig, o as SchemaOptions, r as JournalSchemaOptions, s as StrictnessConfig, t as AccountingEngineConfig } from "./engine-BzBMpWuy.mjs";
|
|
8
|
+
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "./fiscal-period.schema-BRdKAjrr.mjs";
|
|
9
|
+
import { i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin } from "./idempotency.plugin-CPxPt4vX.mjs";
|
|
10
|
+
import { C as ReportAccount, D as TaxReturnSummary, E as TaxReport, O as TrialBalanceReport, S as LedgerEntry, T as ReportGroup, _ as CashFlowReport, a as reopenFiscalPeriod, b as GeneralLedgerReport, d as generateIncomeStatement, g as BalanceSheetReport, h as generateTrialBalance, i as closeFiscalPeriod, k as TrialBalanceRow, l as generateGeneralLedger, p as generateBalanceSheet, s as generateCashFlow, v as CashFlowSection, w as ReportCategory, x as IncomeStatementReport, y as GeneralLedgerAccount } from "./fiscal-close-dNlzB37y.mjs";
|
|
11
|
+
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "./account.repository-C7gwFLfM.mjs";
|
|
12
12
|
import * as mongoose$1 from "mongoose";
|
|
13
13
|
import { ClientSession, Connection, Model } from "mongoose";
|
|
14
14
|
|
|
@@ -304,5 +304,5 @@ interface PostingResult {
|
|
|
304
304
|
idempotencyKeys?: string[];
|
|
305
305
|
}
|
|
306
306
|
//#endregion
|
|
307
|
-
export { type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, type AuditConfig, type BalanceSheetReport, CATEGORIES, CATEGORY_KEYS, CURRENCIES, type CashFlowCategory, type CashFlowReport, type CashFlowSection, type CategoryKey, type Cents, type CountryPack, type CountryPackInput, type Currency, type DateOption, type DateRange, type EntryState, Errors, type ExportField, type ExportFieldMap, type FlatJournalRow, type GeneralLedgerAccount, type GeneralLedgerReport, type IncomeStatementReport, JOURNAL_CODES, JOURNAL_TYPES, type JournalItem, type JournalSchemaOptions, type JournalType, type LedgerEntry, type Logger, type MainType, Money, type MultiTenantConfig, type NormalBalance, type PopulatedJournalEntry, type PostingContract, type PostingResult, type ReportAccount, type ReportCategory, type ReportGroup, type SchemaOptions, type SessionResult, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxCode, type TaxCodesByRegion, type TaxDetail, type TaxMetadata, type TaxReport, type TaxReportLine, type TaxReportTemplate, type TaxReturnSummary, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, acquireSession, add, allocate, buildAccountTypeMap, buildItemFilters, calculateTotal, closeFiscalPeriod, computeEndingBalance, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, getCurrency, getDateRange, getFiscalYearStart, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods };
|
|
307
|
+
export { type AccountType, AccountingEngine, type AccountingEngineConfig, AccountingError, type AuditConfig, type BalanceSheetReport, CATEGORIES, CATEGORY_KEYS, CURRENCIES, type CashFlowCategory, type CashFlowReport, type CashFlowSection, type CategoryKey, type Cents, type CountryPack, type CountryPackInput, type Currency, type DateOption, type DateRange, type EntryState, Errors, type ExportField, type ExportFieldMap, type FlatJournalRow, type GeneralLedgerAccount, type GeneralLedgerReport, type IncomeStatementReport, JOURNAL_CODES, JOURNAL_TYPES, type JournalItem, type JournalSchemaOptions, type JournalType, type LedgerEntry, type Logger, type MainType, Money, type MultiCurrencyConfig, type MultiTenantConfig, type NormalBalance, type PopulatedJournalEntry, type PostingContract, type PostingResult, type ReportAccount, type ReportCategory, type ReportGroup, type SchemaOptions, type SessionResult, type StatementType, type StrictnessConfig, type SubledgerJournalItem, type SubledgerPostingInput, type TaxCode, type TaxCodesByRegion, type TaxDetail, type TaxMetadata, type TaxReport, type TaxReportLine, type TaxReportTemplate, type TaxReturnSummary, type TotalAccountOp, type TrialBalanceReport, type TrialBalanceRow, acquireSession, add, allocate, buildAccountTypeMap, buildItemFilters, calculateTotal, closeFiscalPeriod, computeEndingBalance, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, getCurrency, getDateRange, getFiscalYearStart, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods };
|
|
308
308
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "./fiscal-period.schema-
|
|
2
|
-
import { a as isValidJournalType, i as getJournalTypeCodes, n as JOURNAL_TYPES, t as JOURNAL_CODES } from "./journals-
|
|
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-
|
|
4
|
-
import { n as Errors, t as AccountingError } from "./errors-
|
|
5
|
-
import { n as finalizeSession, r as defaultLogger, t as acquireSession } from "./session-
|
|
6
|
-
import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-
|
|
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
7
|
import { Money, add, allocate, format, formatPlain, fromDecimal, multiply, parseCents, percentage, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal } from "./money.mjs";
|
|
8
|
-
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "./account.repository-
|
|
9
|
-
import { n as fiscalLockPlugin, r as doubleEntryPlugin, t as idempotencyPlugin } from "./idempotency.plugin-
|
|
10
|
-
import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "./currencies-
|
|
8
|
+
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "./account.repository-kDKwDt0I.mjs";
|
|
9
|
+
import { n as fiscalLockPlugin, r as doubleEntryPlugin, t as idempotencyPlugin } from "./idempotency.plugin-v9NQ_ta-.mjs";
|
|
10
|
+
import { i as isValidCurrency, n as getCurrency, r as getMinorUnit, t as CURRENCIES } from "./currencies-W8kQAkm0.mjs";
|
|
11
11
|
import { defineCountryPack } from "./country/index.mjs";
|
|
12
|
-
import { a as exportToCsv, n as quickbooksFieldMap, r as flattenJournalEntries, t as universalFieldMap } from "./
|
|
13
|
-
|
|
12
|
+
import { a as exportToCsv, n as quickbooksFieldMap, r as flattenJournalEntries, t as universalFieldMap } from "./exports-I5Xkq-9_.mjs";
|
|
14
13
|
//#region src/engine.ts
|
|
15
14
|
var AccountingEngine = class {
|
|
16
15
|
config;
|
|
@@ -165,7 +164,7 @@ var AccountingEngine = class {
|
|
|
165
164
|
function createAccountingEngine(config) {
|
|
166
165
|
return new AccountingEngine(config);
|
|
167
166
|
}
|
|
168
|
-
|
|
169
167
|
//#endregion
|
|
170
168
|
export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildItemFilters, calculateTotal, closeFiscalPeriod, computeEndingBalance, createAccountSchema, createAccountingEngine, createFiscalPeriodSchema, createJournalEntrySchema, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, getCurrency, getDateRange, getFiscalYearStart, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, quickbooksFieldMap, reopenFiscalPeriod, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, wireAccountMethods, wireJournalEntryMethods };
|
|
169
|
+
|
|
171
170
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/engine.ts"],"sourcesContent":["/**\n * AccountingEngine — The main entry point for @classytic/ledger.\n *\n * Usage:\n * const accounting = createAccountingEngine({\n * country: canadaPack,\n * currency: 'CAD',\n * multiTenant: { orgField: 'business', orgRef: 'Business' },\n * });\n *\n * const AccountSchema = accounting.createAccountSchema();\n * const JournalEntrySchema = accounting.createJournalEntrySchema('Account');\n * const FiscalPeriodSchema = accounting.createFiscalPeriodSchema();\n *\n * // Register models\n * const Account = mongoose.model('Account', AccountSchema);\n * const JournalEntry = mongoose.model('JournalEntry', JournalEntrySchema);\n * const FiscalPeriod = mongoose.model('FiscalPeriod', FiscalPeriodSchema);\n *\n * // Reports\n * const reports = accounting.createReports({ Account, JournalEntry });\n * const bs = await reports.balanceSheet({ dateOption: 'year', dateValue: 2025, organizationId: '...' });\n */\n\nimport type { Model } from 'mongoose';\nimport type { AccountingEngineConfig, SchemaOptions, JournalSchemaOptions } from './types/engine.js';\nimport type { CountryPack } from './country/index.js';\nimport { createAccountSchema } from './schemas/account.schema.js';\nimport { createJournalEntrySchema } from './schemas/journal-entry.schema.js';\nimport { createFiscalPeriodSchema } from './schemas/fiscal-period.schema.js';\nimport { generateTrialBalance } from './reports/trial-balance.js';\nimport { generateBalanceSheet } from './reports/balance-sheet.js';\nimport { generateIncomeStatement } from './reports/income-statement.js';\nimport { generateGeneralLedger } from './reports/general-ledger.js';\nimport { generateCashFlow } from './reports/cash-flow.js';\nimport { Money } from './money.js';\nimport { wireJournalEntryMethods } from './repositories/journal-entry.repository.js';\nimport { wireAccountMethods } from './repositories/account.repository.js';\nimport { doubleEntryPlugin } from './plugins/double-entry.plugin.js';\nimport { fiscalLockPlugin } from './plugins/fiscal-lock.plugin.js';\nimport { idempotencyPlugin } from './plugins/idempotency.plugin.js';\n\nexport class AccountingEngine {\n readonly config: AccountingEngineConfig;\n readonly country: CountryPack;\n readonly currency: string;\n readonly money = Money;\n\n constructor(config: AccountingEngineConfig) {\n this.config = config;\n this.country = config.country;\n this.currency = config.currency;\n }\n\n // ── Schema Factories ───────────────────────────────────────────────────────\n\n createAccountSchema(options?: SchemaOptions) {\n return createAccountSchema(this.config, options);\n }\n\n createJournalEntrySchema(accountModelName: string, options?: JournalSchemaOptions) {\n return createJournalEntrySchema(this.config, accountModelName, options);\n }\n\n createFiscalPeriodSchema(options?: SchemaOptions) {\n return createFiscalPeriodSchema(this.config, options);\n }\n\n // ── Report Engine ──────────────────────────────────────────────────────────\n\n createReports(models: {\n Account: Model<unknown>;\n JournalEntry: Model<unknown>;\n }) {\n const { Account: AccountModel, JournalEntry: JournalEntryModel } = models;\n const { country, config } = this;\n const orgField = config.multiTenant?.orgField;\n const fiscalYearStartMonth = config.fiscalYearStartMonth ?? 1;\n const retainedEarningsCode = config.retainedEarningsCode;\n const currentYearEarningsCode = config.currentYearEarningsCode;\n\n return {\n trialBalance: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n accountId?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateTrialBalance(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth },\n params,\n ),\n\n balanceSheet: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateBalanceSheet(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth, retainedEarningsCode, currentYearEarningsCode },\n params,\n ),\n\n incomeStatement: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateIncomeStatement(\n { AccountModel, JournalEntryModel, country, orgField },\n params,\n ),\n\n generalLedger: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n accountId?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateGeneralLedger(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth },\n params,\n ),\n\n cashFlow: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateCashFlow(\n { AccountModel, JournalEntryModel, country, orgField },\n params,\n ),\n };\n }\n\n // ── Account Type Helpers ───────────────────────────────────────────────────\n\n /** Get all posting account types (accounts you can post transactions to) */\n getPostingAccountTypes() {\n return this.country.getPostingAccountTypes();\n }\n\n /** Validate an account type code */\n isValidAccountType(code: string) {\n return this.country.isValidAccountType(code);\n }\n\n /** Get account type definition by code */\n getAccountType(code: string) {\n return this.country.getAccountType(code);\n }\n\n /** Get tax codes for a region */\n getTaxCodesForRegion(region: string) {\n return this.country.getTaxCodesForRegion(region);\n }\n\n // ── Repository Factories ─────────────────────────────────────────────────\n\n /**\n * Create a fully-configured journal entry repository with secure plugin wiring.\n * This is the **recommended** way to set up journal entry repositories.\n *\n * Includes:\n * - Double-entry plugin with account existence + tenant integrity validation\n * - Fiscal lock plugin (when FiscalPeriodModel is provided)\n * - post(), unpost(), reverse(), and duplicate() domain methods\n *\n * @param createRepository - The `createRepository` function from @classytic/mongokit\n * @param models.JournalEntryModel - Mongoose model for journal entries\n * @param models.AccountModel - Mongoose model for accounts (required for secure posted-create validation)\n * @param models.FiscalPeriodModel - Mongoose model for fiscal periods (optional, enables fiscal lock)\n * @param additionalPlugins - Extra plugins to include (e.g. timestampPlugin)\n * @returns A wired repository with post(), unpost(), reverse(), duplicate(), and all plugins configured\n */\n createJournalEntryRepository(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n createRepository: (model: Model<unknown>, plugins: any[]) => any,\n models: {\n JournalEntryModel: Model<unknown>;\n AccountModel: Model<unknown>;\n FiscalPeriodModel?: Model<unknown>;\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n additionalPlugins: any[] = [],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): any {\n const orgField = this.config.multiTenant?.orgField;\n const { JournalEntryModel, AccountModel, FiscalPeriodModel } = models;\n\n const plugins = [\n ...additionalPlugins,\n doubleEntryPlugin({\n JournalEntryModel,\n AccountModel,\n orgField,\n }),\n ];\n\n if (FiscalPeriodModel) {\n plugins.push(\n fiscalLockPlugin({\n FiscalPeriodModel,\n JournalEntryModel,\n orgField,\n }),\n );\n }\n\n if (this.config.idempotency) {\n plugins.push(\n idempotencyPlugin({\n JournalEntryModel,\n orgField,\n }),\n );\n }\n\n const repository = createRepository(JournalEntryModel, plugins);\n wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);\n return repository;\n }\n\n /**\n * Wire post/reverse domain methods onto a mongokit Repository\n * for journal entries. The repository must already be created via\n * `createRepository(Model, plugins)` from @classytic/mongokit.\n *\n * **Note:** Prefer `createJournalEntryRepository()` which guarantees\n * secure plugin wiring. This method only adds domain methods and does\n * not validate plugin configuration.\n *\n * @param repository - An existing mongokit Repository instance\n * @param JournalEntryModel - The Mongoose model for journal entries\n * @returns The same repository, now with `.post()` and `.reverse()`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wireJournalEntryRepository(repository: any, JournalEntryModel: Model<unknown>): any {\n const orgField = this.config.multiTenant?.orgField;\n wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);\n return repository;\n }\n\n /**\n * Wire seedAccounts/bulkCreate and posting-account validation onto a\n * mongokit Repository for accounts. The repository must already be\n * created via `createRepository(Model, plugins)` from @classytic/mongokit.\n *\n * @param repository - An existing mongokit Repository instance\n * @param AccountModel - The Mongoose model for accounts\n * @returns The same repository, now with `.seedAccounts()` and `.bulkCreate()`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wireAccountRepository(repository: any, AccountModel: Model<unknown>): any {\n const orgField = this.config.multiTenant?.orgField;\n wireAccountMethods(repository, AccountModel, this.country, orgField);\n return repository;\n }\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────\n\nexport function createAccountingEngine(config: AccountingEngineConfig): AccountingEngine {\n return new AccountingEngine(config);\n}\n"],"mappings":";;;;;;;;;;;;;;AA0CA,IAAa,mBAAb,MAA8B;CAC5B,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS,QAAQ;CAEjB,YAAY,QAAgC;AAC1C,OAAK,SAAS;AACd,OAAK,UAAU,OAAO;AACtB,OAAK,WAAW,OAAO;;CAKzB,oBAAoB,SAAyB;AAC3C,SAAO,oBAAoB,KAAK,QAAQ,QAAQ;;CAGlD,yBAAyB,kBAA0B,SAAgC;AACjF,SAAO,yBAAyB,KAAK,QAAQ,kBAAkB,QAAQ;;CAGzE,yBAAyB,SAAyB;AAChD,SAAO,yBAAyB,KAAK,QAAQ,QAAQ;;CAKvD,cAAc,QAGX;EACD,MAAM,EAAE,SAAS,cAAc,cAAc,sBAAsB;EACnE,MAAM,EAAE,SAAS,WAAW;EAC5B,MAAM,WAAW,OAAO,aAAa;EACrC,MAAM,uBAAuB,OAAO,wBAAwB;EAC5D,MAAM,uBAAuB,OAAO;EACpC,MAAM,0BAA0B,OAAO;AAEvC,SAAO;GACL,eAAe,WAOb,qBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB,EAC5E,OACD;GAEH,eAAe,WAOb,qBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB;IAAsB;IAAyB,EAC3H,OACD;GAEH,kBAAkB,WAOhB,wBACE;IAAE;IAAc;IAAmB;IAAS;IAAU,EACtD,OACD;GAEH,gBAAgB,WAOd,sBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB,EAC5E,OACD;GAEH,WAAW,WAOT,iBACE;IAAE;IAAc;IAAmB;IAAS;IAAU,EACtD,OACD;GACJ;;;CAMH,yBAAyB;AACvB,SAAO,KAAK,QAAQ,wBAAwB;;;CAI9C,mBAAmB,MAAc;AAC/B,SAAO,KAAK,QAAQ,mBAAmB,KAAK;;;CAI9C,eAAe,MAAc;AAC3B,SAAO,KAAK,QAAQ,eAAe,KAAK;;;CAI1C,qBAAqB,QAAgB;AACnC,SAAO,KAAK,QAAQ,qBAAqB,OAAO;;;;;;;;;;;;;;;;;;CAqBlD,6BAEE,kBACA,QAMA,oBAA2B,EAAE,EAExB;EACL,MAAM,WAAW,KAAK,OAAO,aAAa;EAC1C,MAAM,EAAE,mBAAmB,cAAc,sBAAsB;EAE/D,MAAM,UAAU,CACd,GAAG,mBACH,kBAAkB;GAChB;GACA;GACA;GACD,CAAC,CACH;AAED,MAAI,kBACF,SAAQ,KACN,iBAAiB;GACf;GACA;GACA;GACD,CAAC,CACH;AAGH,MAAI,KAAK,OAAO,YACd,SAAQ,KACN,kBAAkB;GAChB;GACA;GACD,CAAC,CACH;EAGH,MAAM,aAAa,iBAAiB,mBAAmB,QAAQ;AAC/D,0BAAwB,YAAY,mBAAmB,UAAU,KAAK,OAAO,WAAW;AACxF,SAAO;;;;;;;;;;;;;;;CAiBT,2BAA2B,YAAiB,mBAAwC;EAClF,MAAM,WAAW,KAAK,OAAO,aAAa;AAC1C,0BAAwB,YAAY,mBAAmB,UAAU,KAAK,OAAO,WAAW;AACxF,SAAO;;;;;;;;;;;CAaT,sBAAsB,YAAiB,cAAmC;EACxE,MAAM,WAAW,KAAK,OAAO,aAAa;AAC1C,qBAAmB,YAAY,cAAc,KAAK,SAAS,SAAS;AACpE,SAAO;;;AAMX,SAAgB,uBAAuB,QAAkD;AACvF,QAAO,IAAI,iBAAiB,OAAO"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/engine.ts"],"sourcesContent":["/**\n * AccountingEngine — The main entry point for @classytic/ledger.\n *\n * Usage:\n * const accounting = createAccountingEngine({\n * country: canadaPack,\n * currency: 'CAD',\n * multiTenant: { orgField: 'business', orgRef: 'Business' },\n * });\n *\n * const AccountSchema = accounting.createAccountSchema();\n * const JournalEntrySchema = accounting.createJournalEntrySchema('Account');\n * const FiscalPeriodSchema = accounting.createFiscalPeriodSchema();\n *\n * // Register models\n * const Account = mongoose.model('Account', AccountSchema);\n * const JournalEntry = mongoose.model('JournalEntry', JournalEntrySchema);\n * const FiscalPeriod = mongoose.model('FiscalPeriod', FiscalPeriodSchema);\n *\n * // Reports\n * const reports = accounting.createReports({ Account, JournalEntry });\n * const bs = await reports.balanceSheet({ dateOption: 'year', dateValue: 2025, organizationId: '...' });\n */\n\nimport type { Model } from 'mongoose';\nimport type { AccountingEngineConfig, SchemaOptions, JournalSchemaOptions } from './types/engine.js';\nimport type { CountryPack } from './country/index.js';\nimport { createAccountSchema } from './schemas/account.schema.js';\nimport { createJournalEntrySchema } from './schemas/journal-entry.schema.js';\nimport { createFiscalPeriodSchema } from './schemas/fiscal-period.schema.js';\nimport { generateTrialBalance } from './reports/trial-balance.js';\nimport { generateBalanceSheet } from './reports/balance-sheet.js';\nimport { generateIncomeStatement } from './reports/income-statement.js';\nimport { generateGeneralLedger } from './reports/general-ledger.js';\nimport { generateCashFlow } from './reports/cash-flow.js';\nimport { Money } from './money.js';\nimport { wireJournalEntryMethods } from './repositories/journal-entry.repository.js';\nimport { wireAccountMethods } from './repositories/account.repository.js';\nimport { doubleEntryPlugin } from './plugins/double-entry.plugin.js';\nimport { fiscalLockPlugin } from './plugins/fiscal-lock.plugin.js';\nimport { idempotencyPlugin } from './plugins/idempotency.plugin.js';\n\nexport class AccountingEngine {\n readonly config: AccountingEngineConfig;\n readonly country: CountryPack;\n readonly currency: string;\n readonly money = Money;\n\n constructor(config: AccountingEngineConfig) {\n this.config = config;\n this.country = config.country;\n this.currency = config.currency;\n }\n\n // ── Schema Factories ───────────────────────────────────────────────────────\n\n createAccountSchema(options?: SchemaOptions) {\n return createAccountSchema(this.config, options);\n }\n\n createJournalEntrySchema(accountModelName: string, options?: JournalSchemaOptions) {\n return createJournalEntrySchema(this.config, accountModelName, options);\n }\n\n createFiscalPeriodSchema(options?: SchemaOptions) {\n return createFiscalPeriodSchema(this.config, options);\n }\n\n // ── Report Engine ──────────────────────────────────────────────────────────\n\n createReports(models: {\n Account: Model<unknown>;\n JournalEntry: Model<unknown>;\n }) {\n const { Account: AccountModel, JournalEntry: JournalEntryModel } = models;\n const { country, config } = this;\n const orgField = config.multiTenant?.orgField;\n const fiscalYearStartMonth = config.fiscalYearStartMonth ?? 1;\n const retainedEarningsCode = config.retainedEarningsCode;\n const currentYearEarningsCode = config.currentYearEarningsCode;\n\n return {\n trialBalance: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n accountId?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateTrialBalance(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth },\n params,\n ),\n\n balanceSheet: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateBalanceSheet(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth, retainedEarningsCode, currentYearEarningsCode },\n params,\n ),\n\n incomeStatement: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateIncomeStatement(\n { AccountModel, JournalEntryModel, country, orgField },\n params,\n ),\n\n generalLedger: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n accountId?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateGeneralLedger(\n { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth },\n params,\n ),\n\n cashFlow: (params: {\n organizationId?: unknown;\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\n dateValue: unknown;\n businessName?: string;\n filters?: Record<string, unknown>;\n }) =>\n generateCashFlow(\n { AccountModel, JournalEntryModel, country, orgField },\n params,\n ),\n };\n }\n\n // ── Account Type Helpers ───────────────────────────────────────────────────\n\n /** Get all posting account types (accounts you can post transactions to) */\n getPostingAccountTypes() {\n return this.country.getPostingAccountTypes();\n }\n\n /** Validate an account type code */\n isValidAccountType(code: string) {\n return this.country.isValidAccountType(code);\n }\n\n /** Get account type definition by code */\n getAccountType(code: string) {\n return this.country.getAccountType(code);\n }\n\n /** Get tax codes for a region */\n getTaxCodesForRegion(region: string) {\n return this.country.getTaxCodesForRegion(region);\n }\n\n // ── Repository Factories ─────────────────────────────────────────────────\n\n /**\n * Create a fully-configured journal entry repository with secure plugin wiring.\n * This is the **recommended** way to set up journal entry repositories.\n *\n * Includes:\n * - Double-entry plugin with account existence + tenant integrity validation\n * - Fiscal lock plugin (when FiscalPeriodModel is provided)\n * - post(), unpost(), reverse(), and duplicate() domain methods\n *\n * @param createRepository - The `createRepository` function from @classytic/mongokit\n * @param models.JournalEntryModel - Mongoose model for journal entries\n * @param models.AccountModel - Mongoose model for accounts (required for secure posted-create validation)\n * @param models.FiscalPeriodModel - Mongoose model for fiscal periods (optional, enables fiscal lock)\n * @param additionalPlugins - Extra plugins to include (e.g. timestampPlugin)\n * @returns A wired repository with post(), unpost(), reverse(), duplicate(), and all plugins configured\n */\n createJournalEntryRepository(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n createRepository: (model: Model<unknown>, plugins: any[]) => any,\n models: {\n JournalEntryModel: Model<unknown>;\n AccountModel: Model<unknown>;\n FiscalPeriodModel?: Model<unknown>;\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n additionalPlugins: any[] = [],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): any {\n const orgField = this.config.multiTenant?.orgField;\n const { JournalEntryModel, AccountModel, FiscalPeriodModel } = models;\n\n const plugins = [\n ...additionalPlugins,\n doubleEntryPlugin({\n JournalEntryModel,\n AccountModel,\n orgField,\n }),\n ];\n\n if (FiscalPeriodModel) {\n plugins.push(\n fiscalLockPlugin({\n FiscalPeriodModel,\n JournalEntryModel,\n orgField,\n }),\n );\n }\n\n if (this.config.idempotency) {\n plugins.push(\n idempotencyPlugin({\n JournalEntryModel,\n orgField,\n }),\n );\n }\n\n const repository = createRepository(JournalEntryModel, plugins);\n wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);\n return repository;\n }\n\n /**\n * Wire post/reverse domain methods onto a mongokit Repository\n * for journal entries. The repository must already be created via\n * `createRepository(Model, plugins)` from @classytic/mongokit.\n *\n * **Note:** Prefer `createJournalEntryRepository()` which guarantees\n * secure plugin wiring. This method only adds domain methods and does\n * not validate plugin configuration.\n *\n * @param repository - An existing mongokit Repository instance\n * @param JournalEntryModel - The Mongoose model for journal entries\n * @returns The same repository, now with `.post()` and `.reverse()`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wireJournalEntryRepository(repository: any, JournalEntryModel: Model<unknown>): any {\n const orgField = this.config.multiTenant?.orgField;\n wireJournalEntryMethods(repository, JournalEntryModel, orgField, this.config.strictness);\n return repository;\n }\n\n /**\n * Wire seedAccounts/bulkCreate and posting-account validation onto a\n * mongokit Repository for accounts. The repository must already be\n * created via `createRepository(Model, plugins)` from @classytic/mongokit.\n *\n * @param repository - An existing mongokit Repository instance\n * @param AccountModel - The Mongoose model for accounts\n * @returns The same repository, now with `.seedAccounts()` and `.bulkCreate()`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wireAccountRepository(repository: any, AccountModel: Model<unknown>): any {\n const orgField = this.config.multiTenant?.orgField;\n wireAccountMethods(repository, AccountModel, this.country, orgField);\n return repository;\n }\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────\n\nexport function createAccountingEngine(config: AccountingEngineConfig): AccountingEngine {\n return new AccountingEngine(config);\n}\n"],"mappings":";;;;;;;;;;;;;AA0CA,IAAa,mBAAb,MAA8B;CAC5B;CACA;CACA;CACA,QAAiB;CAEjB,YAAY,QAAgC;AAC1C,OAAK,SAAS;AACd,OAAK,UAAU,OAAO;AACtB,OAAK,WAAW,OAAO;;CAKzB,oBAAoB,SAAyB;AAC3C,SAAO,oBAAoB,KAAK,QAAQ,QAAQ;;CAGlD,yBAAyB,kBAA0B,SAAgC;AACjF,SAAO,yBAAyB,KAAK,QAAQ,kBAAkB,QAAQ;;CAGzE,yBAAyB,SAAyB;AAChD,SAAO,yBAAyB,KAAK,QAAQ,QAAQ;;CAKvD,cAAc,QAGX;EACD,MAAM,EAAE,SAAS,cAAc,cAAc,sBAAsB;EACnE,MAAM,EAAE,SAAS,WAAW;EAC5B,MAAM,WAAW,OAAO,aAAa;EACrC,MAAM,uBAAuB,OAAO,wBAAwB;EAC5D,MAAM,uBAAuB,OAAO;EACpC,MAAM,0BAA0B,OAAO;AAEvC,SAAO;GACL,eAAe,WAOb,qBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB,EAC5E,OACD;GAEH,eAAe,WAOb,qBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB;IAAsB;IAAyB,EAC3H,OACD;GAEH,kBAAkB,WAOhB,wBACE;IAAE;IAAc;IAAmB;IAAS;IAAU,EACtD,OACD;GAEH,gBAAgB,WAOd,sBACE;IAAE;IAAc;IAAmB;IAAS;IAAU;IAAsB,EAC5E,OACD;GAEH,WAAW,WAOT,iBACE;IAAE;IAAc;IAAmB;IAAS;IAAU,EACtD,OACD;GACJ;;;CAMH,yBAAyB;AACvB,SAAO,KAAK,QAAQ,wBAAwB;;;CAI9C,mBAAmB,MAAc;AAC/B,SAAO,KAAK,QAAQ,mBAAmB,KAAK;;;CAI9C,eAAe,MAAc;AAC3B,SAAO,KAAK,QAAQ,eAAe,KAAK;;;CAI1C,qBAAqB,QAAgB;AACnC,SAAO,KAAK,QAAQ,qBAAqB,OAAO;;;;;;;;;;;;;;;;;;CAqBlD,6BAEE,kBACA,QAMA,oBAA2B,EAAE,EAExB;EACL,MAAM,WAAW,KAAK,OAAO,aAAa;EAC1C,MAAM,EAAE,mBAAmB,cAAc,sBAAsB;EAE/D,MAAM,UAAU,CACd,GAAG,mBACH,kBAAkB;GAChB;GACA;GACA;GACD,CAAC,CACH;AAED,MAAI,kBACF,SAAQ,KACN,iBAAiB;GACf;GACA;GACA;GACD,CAAC,CACH;AAGH,MAAI,KAAK,OAAO,YACd,SAAQ,KACN,kBAAkB;GAChB;GACA;GACD,CAAC,CACH;EAGH,MAAM,aAAa,iBAAiB,mBAAmB,QAAQ;AAC/D,0BAAwB,YAAY,mBAAmB,UAAU,KAAK,OAAO,WAAW;AACxF,SAAO;;;;;;;;;;;;;;;CAiBT,2BAA2B,YAAiB,mBAAwC;EAClF,MAAM,WAAW,KAAK,OAAO,aAAa;AAC1C,0BAAwB,YAAY,mBAAmB,UAAU,KAAK,OAAO,WAAW;AACxF,SAAO;;;;;;;;;;;CAaT,sBAAsB,YAAiB,cAAmC;EACxE,MAAM,WAAW,KAAK,OAAO,aAAa;AAC1C,qBAAmB,YAAY,cAAc,KAAK,SAAS,SAAS;AACpE,SAAO;;;AAMX,SAAgB,uBAAuB,QAAkD;AACvF,QAAO,IAAI,iBAAiB,OAAO"}
|
|
@@ -86,7 +86,7 @@ function isValidJournalType(code) {
|
|
|
86
86
|
function getJournalType(code) {
|
|
87
87
|
return JOURNAL_TYPES[code] ?? null;
|
|
88
88
|
}
|
|
89
|
-
|
|
90
89
|
//#endregion
|
|
91
90
|
export { isValidJournalType as a, getJournalTypeCodes as i, JOURNAL_TYPES as n, getJournalType as r, JOURNAL_CODES as t };
|
|
92
|
-
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=journals-oH-FK3g8.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journals-
|
|
1
|
+
{"version":3,"file":"journals-oH-FK3g8.mjs","names":[],"sources":["../src/constants/journals.ts"],"sourcesContent":["/**\r\n * Journal Types — Standard journal classifications.\r\n * Extensible: country packs can add custom journal types.\r\n */\r\n\r\nimport type { JournalType } from '../types/core.js';\r\n\r\nexport const JOURNAL_TYPES: Readonly<Record<string, JournalType>> = Object.freeze({\r\n SALES: { code: 'SALES', name: 'Sales Journal', description: 'Sales transactions and revenue' },\r\n PURCHASES: { code: 'PURCHASES', name: 'Purchases Journal', description: 'Purchase transactions and expenses' },\r\n CASH_RECEIPTS: { code: 'CASH_RECEIPTS', name: 'Cash Receipts Journal', description: 'Cash and bank deposits received' },\r\n CASH_PAYMENTS: { code: 'CASH_PAYMENTS', name: 'Cash Payments Journal', description: 'Cash and bank payments made' },\r\n PAYROLL: { code: 'PAYROLL', name: 'Payroll Journal', description: 'Employee wages, salaries, and related expenses' },\r\n GENERAL: { code: 'GENERAL', name: 'General Journal', description: 'Adjusting entries, corrections, and misc transactions' },\r\n INVENTORY: { code: 'INVENTORY', name: 'Inventory Journal', description: 'Inventory adjustments and movements' },\r\n FIXED_ASSETS: { code: 'FIXED_ASSETS', name: 'Fixed Assets Journal', description: 'Asset purchases, disposals, and depreciation' },\r\n BANK_RECONCILIATION:{ code: 'BANK_RECONCILIATION', name: 'Bank Reconciliation', description: 'Bank reconciliation adjustments' },\r\n DEPRECIATION: { code: 'DEPRECIATION', name: 'Depreciation Journal', description: 'Periodic depreciation expenses' },\r\n YEAR_END: { code: 'YEAR_END', name: 'Year-End Adjustments', description: 'Year-end closing and adjustment entries' },\r\n ACCOUNTS_RECEIVABLE:{ code: 'ACCOUNTS_RECEIVABLE', name: 'Accounts Receivable', description: 'Customer invoices and receivable transactions' },\r\n ACCOUNTS_PAYABLE: { code: 'ACCOUNTS_PAYABLE', name: 'Accounts Payable', description: 'Vendor bills and payable transactions' },\r\n TAX: { code: 'TAX', name: 'Tax Journal', description: 'GST/HST/PST and other tax-related entries' },\r\n MISC: { code: 'MISC', name: 'Miscellaneous', description: 'Transactions that don\\'t fit other categories' },\r\n});\r\n\r\nexport const JOURNAL_CODES = Object.freeze(\r\n Object.fromEntries(Object.keys(JOURNAL_TYPES).map(k => [k, k])) as Record<string, string>,\r\n);\r\n\r\nexport function getJournalTypeCodes(): string[] {\r\n return Object.keys(JOURNAL_TYPES);\r\n}\r\n\r\nexport function isValidJournalType(code: string): boolean {\r\n return code in JOURNAL_TYPES;\r\n}\r\n\r\nexport function getJournalType(code: string): JournalType | null {\r\n return JOURNAL_TYPES[code] ?? null;\r\n}\r\n"],"mappings":";AAOA,MAAa,gBAAuD,OAAO,OAAO;CAChF,OAAoB;EAAE,MAAM;EAAS,MAAM;EAAiB,aAAa;EAAkC;CAC3G,WAAoB;EAAE,MAAM;EAAa,MAAM;EAAqB,aAAa;EAAsC;CACvH,eAAoB;EAAE,MAAM;EAAiB,MAAM;EAAyB,aAAa;EAAmC;CAC5H,eAAoB;EAAE,MAAM;EAAiB,MAAM;EAAyB,aAAa;EAA+B;CACxH,SAAoB;EAAE,MAAM;EAAW,MAAM;EAAmB,aAAa;EAAkD;CAC/H,SAAoB;EAAE,MAAM;EAAW,MAAM;EAAmB,aAAa;EAAyD;CACtI,WAAoB;EAAE,MAAM;EAAa,MAAM;EAAqB,aAAa;EAAuC;CACxH,cAAoB;EAAE,MAAM;EAAgB,MAAM;EAAwB,aAAa;EAAgD;CACvI,qBAAoB;EAAE,MAAM;EAAuB,MAAM;EAAuB,aAAa;EAAmC;CAChI,cAAoB;EAAE,MAAM;EAAgB,MAAM;EAAwB,aAAa;EAAkC;CACzH,UAAoB;EAAE,MAAM;EAAY,MAAM;EAAwB,aAAa;EAA2C;CAC9H,qBAAoB;EAAE,MAAM;EAAuB,MAAM;EAAuB,aAAa;EAAiD;CAC9I,kBAAoB;EAAE,MAAM;EAAoB,MAAM;EAAoB,aAAa;EAAyC;CAChI,KAAoB;EAAE,MAAM;EAAO,MAAM;EAAe,aAAa;EAA6C;CAClH,MAAoB;EAAE,MAAM;EAAQ,MAAM;EAAiB,aAAa;EAAiD;CAC1H,CAAC;AAEF,MAAa,gBAAgB,OAAO,OAClC,OAAO,YAAY,OAAO,KAAK,cAAc,CAAC,KAAI,MAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAChE;AAED,SAAgB,sBAAgC;AAC9C,QAAO,OAAO,KAAK,cAAc;;AAGnC,SAAgB,mBAAmB,MAAuB;AACxD,QAAO,QAAQ;;AAGjB,SAAgB,eAAe,MAAkC;AAC/D,QAAO,cAAc,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger-
|
|
1
|
+
{"version":3,"file":"logger-UbTdBb1x.d.mts","names":[],"sources":["../src/utils/logger.ts"],"mappings":";;AAIA;;;UAAiB,MAAA;EACf,IAAA,CAAK,OAAA,UAAiB,IAAA,GAAO,MAAA;EAC7B,KAAA,CAAM,OAAA,UAAiB,IAAA,GAAO,MAAA;EAC9B,IAAA,CAAK,OAAA,UAAiB,IAAA,GAAO,MAAA;AAAA;;cAIlB,aAAA,EAAe,MAAA"}
|
package/dist/money.mjs
CHANGED
|
@@ -191,7 +191,7 @@ const Money = {
|
|
|
191
191
|
isValid,
|
|
192
192
|
parseCents
|
|
193
193
|
};
|
|
194
|
-
|
|
195
194
|
//#endregion
|
|
196
195
|
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
197
|
//# sourceMappingURL=money.mjs.map
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as DoubleEntryPluginOptions, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin, r as FiscalLockPluginOptions, t as IdempotencyPluginOptions } from "../idempotency.plugin-
|
|
1
|
+
import { a as DoubleEntryPluginOptions, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin, r as FiscalLockPluginOptions, t as IdempotencyPluginOptions } from "../idempotency.plugin-CPxPt4vX.mjs";
|
|
2
2
|
export { type DoubleEntryPluginOptions, type FiscalLockPluginOptions, type IdempotencyPluginOptions, doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { n as fiscalLockPlugin, r as doubleEntryPlugin, t as idempotencyPlugin } from "../idempotency.plugin-
|
|
2
|
-
|
|
3
|
-
export { doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin };
|
|
1
|
+
import { n as fiscalLockPlugin, r as doubleEntryPlugin, t as idempotencyPlugin } from "../idempotency.plugin-v9NQ_ta-.mjs";
|
|
2
|
+
export { doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin };
|
package/dist/reports/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as reopenFiscalPeriod, c as GeneralLedgerOptions, d as generateIncomeStatement, f as BalanceSheetOptions, h as generateTrialBalance, i as closeFiscalPeriod, l as generateGeneralLedger, m as TrialBalanceOptions, n as FiscalCloseResult, o as CashFlowOptions, p as generateBalanceSheet, r as FiscalReopenResult, s as generateCashFlow, t as FiscalCloseOptions, u as IncomeStatementOptions } from "../fiscal-close-
|
|
1
|
+
import { a as reopenFiscalPeriod, c as GeneralLedgerOptions, d as generateIncomeStatement, f as BalanceSheetOptions, h as generateTrialBalance, i as closeFiscalPeriod, l as generateGeneralLedger, m as TrialBalanceOptions, n as FiscalCloseResult, o as CashFlowOptions, p as generateBalanceSheet, r as FiscalReopenResult, s as generateCashFlow, t as FiscalCloseOptions, u as IncomeStatementOptions } from "../fiscal-close-dNlzB37y.mjs";
|
|
2
2
|
export { type BalanceSheetOptions, type CashFlowOptions, type FiscalCloseOptions, type FiscalCloseResult, type FiscalReopenResult, type GeneralLedgerOptions, type IncomeStatementOptions, type TrialBalanceOptions, closeFiscalPeriod, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, reopenFiscalPeriod };
|
package/dist/reports/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { a as generateIncomeStatement, d as generateTrialBalance, i as generateGeneralLedger, n as reopenFiscalPeriod, o as generateBalanceSheet, r as generateCashFlow, t as closeFiscalPeriod } from "../fiscal-close-
|
|
2
|
-
|
|
3
|
-
export { closeFiscalPeriod, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, reopenFiscalPeriod };
|
|
1
|
+
import { a as generateIncomeStatement, d as generateTrialBalance, i as generateGeneralLedger, n as reopenFiscalPeriod, o as generateBalanceSheet, r as generateCashFlow, t as closeFiscalPeriod } from "../fiscal-close-L631E3De.mjs";
|
|
2
|
+
export { closeFiscalPeriod, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, reopenFiscalPeriod };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-
|
|
1
|
+
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-C7gwFLfM.mjs";
|
|
2
2
|
export { wireAccountMethods, wireJournalEntryMethods };
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-
|
|
2
|
-
|
|
3
|
-
export { wireAccountMethods, wireJournalEntryMethods };
|
|
1
|
+
import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-kDKwDt0I.mjs";
|
|
2
|
+
export { wireAccountMethods, wireJournalEntryMethods };
|
package/dist/schemas/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "../fiscal-period.schema-
|
|
1
|
+
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "../fiscal-period.schema-BRdKAjrr.mjs";
|
|
2
2
|
export { createAccountSchema, createFiscalPeriodSchema, createJournalEntrySchema };
|
package/dist/schemas/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "../fiscal-period.schema-
|
|
2
|
-
|
|
3
|
-
export { createAccountSchema, createFiscalPeriodSchema, createJournalEntrySchema };
|
|
1
|
+
import { n as createJournalEntrySchema, r as createAccountSchema, t as createFiscalPeriodSchema } from "../fiscal-period.schema-BQ5wsAq3.mjs";
|
|
2
|
+
export { createAccountSchema, createFiscalPeriodSchema, createJournalEntrySchema };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { n as Errors } from "./errors-
|
|
2
|
-
|
|
1
|
+
import { n as Errors } from "./errors-B7yC-Jfw.mjs";
|
|
3
2
|
//#region src/utils/tenant-guard.ts
|
|
4
3
|
/**
|
|
5
4
|
* Multi-tenant scope guard.
|
|
@@ -10,7 +9,6 @@ import { n as Errors } from "./errors-CeqRahE-.mjs";
|
|
|
10
9
|
function requireOrgScope(orgField, organizationId) {
|
|
11
10
|
if (orgField && !organizationId) throw Errors.validation("organizationId is required when multi-tenant mode is configured (orgField: \"" + orgField + "\"). Refusing to run unscoped query.");
|
|
12
11
|
}
|
|
13
|
-
|
|
14
12
|
//#endregion
|
|
15
13
|
//#region src/utils/logger.ts
|
|
16
14
|
/** Default console-based implementation */
|
|
@@ -19,7 +17,6 @@ const defaultLogger = {
|
|
|
19
17
|
error: (msg, meta) => console.error(`[accounting] ${msg}`, meta ?? ""),
|
|
20
18
|
info: (msg, meta) => console.info(`[accounting] ${msg}`, meta ?? "")
|
|
21
19
|
};
|
|
22
|
-
|
|
23
20
|
//#endregion
|
|
24
21
|
//#region src/utils/session.ts
|
|
25
22
|
/**
|
|
@@ -81,7 +78,7 @@ async function finalizeSession(session, ownSession, success) {
|
|
|
81
78
|
session.endSession();
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
|
-
|
|
85
81
|
//#endregion
|
|
86
82
|
export { requireOrgScope as i, finalizeSession as n, defaultLogger as r, acquireSession as t };
|
|
87
|
-
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=session-Ba8E3Ufa.mjs.map
|