@classytic/ledger 0.1.3 → 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-CNOwv_ud.mjs → fiscal-close-L631E3De.mjs} +22 -17
- 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 +3 -3
- package/dist/country/index.d.mts.map +0 -1
- package/dist/engine-Cd73EOT6.d.mts.map +0 -1
- package/dist/fiscal-close-CNOwv_ud.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
1
|
import { Money } from "./money.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/exports/csv-serializer.ts
|
|
4
3
|
const NEEDS_QUOTING = /[",\r\n]/;
|
|
5
4
|
/** Escape a single CSV cell value per RFC 4180. */
|
|
@@ -17,7 +16,6 @@ function buildCsv(headers, dataRows, options = {}) {
|
|
|
17
16
|
const { includeHeaders = true } = options;
|
|
18
17
|
return serializeCsv(includeHeaders ? [headers, ...dataRows] : [...dataRows], options);
|
|
19
18
|
}
|
|
20
|
-
|
|
21
19
|
//#endregion
|
|
22
20
|
//#region src/exports/field-map.ts
|
|
23
21
|
/** Extract headers from a field map. */
|
|
@@ -36,7 +34,6 @@ function extractAllRows(fieldMap, rows) {
|
|
|
36
34
|
function exportToCsv(fieldMap, rows, options) {
|
|
37
35
|
return buildCsv(getHeaders(fieldMap), extractAllRows(fieldMap, rows), options);
|
|
38
36
|
}
|
|
39
|
-
|
|
40
37
|
//#endregion
|
|
41
38
|
//#region src/exports/flatten-journal.ts
|
|
42
39
|
function toDate(value) {
|
|
@@ -110,7 +107,6 @@ function flattenJournalEntries(entries) {
|
|
|
110
107
|
for (const entry of entries) rows.push(...flattenJournalEntry(entry));
|
|
111
108
|
return rows;
|
|
112
109
|
}
|
|
113
|
-
|
|
114
110
|
//#endregion
|
|
115
111
|
//#region src/exports/field-maps/quickbooks.ts
|
|
116
112
|
function formatQbDate(date) {
|
|
@@ -163,7 +159,6 @@ const quickbooksFieldMap = {
|
|
|
163
159
|
}
|
|
164
160
|
]
|
|
165
161
|
};
|
|
166
|
-
|
|
167
162
|
//#endregion
|
|
168
163
|
//#region src/exports/field-maps/universal.ts
|
|
169
164
|
function formatIsoDate(date) {
|
|
@@ -251,7 +246,7 @@ const universalFieldMap = {
|
|
|
251
246
|
}
|
|
252
247
|
]
|
|
253
248
|
};
|
|
254
|
-
|
|
255
249
|
//#endregion
|
|
256
250
|
export { exportToCsv as a, getHeaders as c, serializeCsv as d, flattenJournalEntry as i, buildCsv as l, quickbooksFieldMap as n, extractAllRows as o, flattenJournalEntries as r, extractRow as s, universalFieldMap as t, escapeCell as u };
|
|
257
|
-
|
|
251
|
+
|
|
252
|
+
//# sourceMappingURL=exports-I5Xkq-9_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exports-I5Xkq-9_.mjs","names":[],"sources":["../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"],"sourcesContent":["/**\n * CSV Serializer — RFC 4180 compliant CSV string builder.\n *\n * Pure function. No I/O, no side effects.\n *\n * @module @classytic/ledger/exports\n */\n\nimport type { CsvOptions } from './types.js';\n\nconst NEEDS_QUOTING = /[\",\\r\\n]/;\n\n/** Escape a single CSV cell value per RFC 4180. */\nexport function escapeCell(value: string): string {\n if (NEEDS_QUOTING.test(value)) {\n return '\"' + value.replace(/\"/g, '\"\"') + '\"';\n }\n return value;\n}\n\n/** Serialize a 2D array of strings into a CSV string. */\nexport function serializeCsv(\n rows: readonly (readonly string[])[],\n options: CsvOptions = {},\n): string {\n const { delimiter = ',', lineTerminator = '\\r\\n' } = options;\n\n return rows\n .map(row => row.map(escapeCell).join(delimiter))\n .join(lineTerminator);\n}\n\n/** Build a CSV string with optional header row. */\nexport function buildCsv(\n headers: readonly string[],\n dataRows: readonly (readonly string[])[],\n options: CsvOptions = {},\n): string {\n const { includeHeaders = true } = options;\n const allRows = includeHeaders ? [headers, ...dataRows] : [...dataRows];\n return serializeCsv(allRows, options);\n}\n","/**\n * Field Map Application — Applies an ExportFieldMap to data rows.\n *\n * @module @classytic/ledger/exports\n */\n\nimport type { ExportFieldMap, CsvOptions } from './types.js';\nimport { buildCsv } from './csv-serializer.js';\n\n/** Extract headers from a field map. */\nexport function getHeaders<TRow>(fieldMap: ExportFieldMap<TRow>): string[] {\n return fieldMap.fields.map(f => f.header);\n}\n\n/** Apply a field map to a single row, producing an array of cell strings. */\nexport function extractRow<TRow>(fieldMap: ExportFieldMap<TRow>, row: TRow): string[] {\n return fieldMap.fields.map(f => f.extract(row));\n}\n\n/** Apply a field map to an array of rows, producing a 2D string array. */\nexport function extractAllRows<TRow>(\n fieldMap: ExportFieldMap<TRow>,\n rows: readonly TRow[],\n): string[][] {\n return rows.map(row => extractRow(fieldMap, row));\n}\n\n/** One-shot: map + serialize to CSV string. */\nexport function exportToCsv<TRow>(\n fieldMap: ExportFieldMap<TRow>,\n rows: readonly TRow[],\n options?: CsvOptions,\n): string {\n const headers = getHeaders(fieldMap);\n const dataRows = extractAllRows(fieldMap, rows);\n return buildCsv(headers, dataRows, options);\n}\n","/**\n * Journal Entry Flattener — Denormalizes journal entries into flat rows.\n *\n * Each journal item becomes one row with entry-level fields repeated.\n * Monetary values stay as integer cents (matching DB storage).\n *\n * @module @classytic/ledger/exports\n */\n\nimport type {\n PopulatedJournalEntry,\n PopulatedAccount,\n FlatJournalRow,\n} from './types.js';\n\nfunction toDate(value: Date | string | undefined | null): Date {\n if (!value) return new Date(0);\n if (value instanceof Date) return value;\n return new Date(value);\n}\n\nfunction resolveAccount(\n account: PopulatedAccount | string | null | undefined,\n): { id: string; name: string; typeCode: string } {\n if (!account) return { id: '', name: '', typeCode: '' };\n if (typeof account === 'string') return { id: account, name: '', typeCode: '' };\n return {\n id: String(account._id ?? ''),\n name: account.name ?? '',\n typeCode: account.accountTypeCode ?? '',\n };\n}\n\n/** Flatten a single journal entry into one FlatJournalRow per journal item. */\nexport function flattenJournalEntry(entry: PopulatedJournalEntry): FlatJournalRow[] {\n const entryDate = toDate(entry.date);\n const items = entry.journalItems ?? [];\n const itemCount = items.length;\n\n // Known core item keys — everything else is an extra dimension field\n const KNOWN_ITEM_KEYS = new Set(['account', 'label', 'date', 'debit', 'credit', 'taxDetails']);\n\n return items.map((item, index) => {\n const acct = resolveAccount(item.account);\n const firstTax = item.taxDetails?.[0];\n\n // Collect extra item fields (dimensions like departmentId, projectId, etc.)\n const extraItemFields: Record<string, unknown> = {};\n for (const key of Object.keys(item)) {\n if (!KNOWN_ITEM_KEYS.has(key)) {\n extraItemFields[key] = (item as Record<string, unknown>)[key];\n }\n }\n\n return {\n entryId: String(entry._id ?? ''),\n journalType: entry.journalType ?? '',\n referenceNumber: entry.referenceNumber ?? '',\n entryLabel: entry.label ?? '',\n entryDate,\n state: entry.state,\n reversed: entry.reversed ?? false,\n totalDebit: entry.totalDebit ?? 0,\n totalCredit: entry.totalCredit ?? 0,\n\n accountId: acct.id,\n accountName: acct.name,\n accountTypeCode: acct.typeCode,\n itemLabel: item.label ?? '',\n itemDate: item.date ? toDate(item.date) : entryDate,\n debit: item.debit ?? 0,\n credit: item.credit ?? 0,\n taxCode: firstTax?.taxCode ?? '',\n taxName: firstTax?.taxName ?? '',\n\n itemIndex: index,\n itemCount,\n ...extraItemFields,\n };\n });\n}\n\n/** Flatten multiple journal entries into a single flat row array. */\nexport function flattenJournalEntries(\n entries: readonly PopulatedJournalEntry[],\n): FlatJournalRow[] {\n const rows: FlatJournalRow[] = [];\n for (const entry of entries) {\n rows.push(...flattenJournalEntry(entry));\n }\n return rows;\n}\n","/**\n * QuickBooks General Journal Import Field Map\n *\n * Produces CSV compatible with QuickBooks Desktop and Online\n * \"Import General Journal Entries\" feature.\n *\n * @module @classytic/ledger/exports\n */\n\nimport type { ExportFieldMap, FlatJournalRow } from '../types.js';\nimport { Money } from '../../money.js';\n\nfunction formatQbDate(date: Date): string {\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const y = date.getFullYear();\n return `${m}/${d}/${y}`;\n}\n\n/** Convert integer cents to dollar string, blank for zero. */\nfunction amountOrBlank(cents: number): string {\n if (cents === 0) return '';\n return Money.formatPlain(cents);\n}\n\nexport const quickbooksFieldMap: ExportFieldMap<FlatJournalRow> = {\n name: 'QuickBooks General Journal',\n target: 'quickbooks',\n fields: [\n { header: 'Date', extract: (row) => formatQbDate(row.entryDate) },\n { header: 'Transaction Type', extract: () => 'General Journal' },\n { header: 'Num', extract: (row) => row.referenceNumber },\n { header: 'Name', extract: () => '' },\n { header: 'Memo/Description', extract: (row) => row.itemLabel || row.entryLabel },\n { header: 'Account', extract: (row) => row.accountName || row.accountTypeCode || row.accountId },\n { header: 'Debit', extract: (row) => amountOrBlank(row.debit) },\n { header: 'Credit', extract: (row) => amountOrBlank(row.credit) },\n { header: 'Class', extract: () => '' },\n ],\n};\n","/**\n * Universal Export Field Map\n *\n * Comprehensive CSV export with all available fields.\n * Useful for data portability, auditing, or spreadsheet import.\n *\n * @module @classytic/ledger/exports\n */\n\nimport type { ExportFieldMap, FlatJournalRow } from '../types.js';\nimport { Money } from '../../money.js';\n\nfunction formatIsoDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n\n/** Convert integer cents to dollar string (e.g. 10050 → \"100.50\"). */\nfunction centsToDisplay(cents: number): string {\n return Money.formatPlain(cents);\n}\n\nexport const universalFieldMap: ExportFieldMap<FlatJournalRow> = {\n name: 'Universal Journal Export',\n target: 'universal',\n fields: [\n { header: 'Entry ID', extract: (row) => row.entryId },\n { header: 'Date', extract: (row) => formatIsoDate(row.entryDate) },\n { header: 'Journal Type', extract: (row) => row.journalType },\n { header: 'Reference Number', extract: (row) => row.referenceNumber },\n { header: 'Entry Description', extract: (row) => row.entryLabel },\n { header: 'State', extract: (row) => row.state },\n { header: 'Reversed', extract: (row) => row.reversed ? 'Yes' : 'No' },\n { header: 'Account Code', extract: (row) => row.accountTypeCode },\n { header: 'Account Name', extract: (row) => row.accountName },\n { header: 'Line Description', extract: (row) => row.itemLabel },\n { header: 'Debit', extract: (row) => centsToDisplay(row.debit) },\n { header: 'Credit', extract: (row) => centsToDisplay(row.credit) },\n { header: 'Tax Code', extract: (row) => row.taxCode },\n { header: 'Tax Name', extract: (row) => row.taxName },\n { header: 'Entry Total Debit', extract: (row) => centsToDisplay(row.totalDebit) },\n { header: 'Entry Total Credit',extract: (row) => centsToDisplay(row.totalCredit) },\n { header: 'Line', extract: (row) => String(row.itemIndex + 1) },\n { header: 'Line Count', extract: (row) => String(row.itemCount) },\n ],\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as Errors } from "./errors-
|
|
2
|
-
import { i as requireOrgScope, n as finalizeSession, r as defaultLogger, t as acquireSession } from "./session-
|
|
3
|
-
import { i as extractMainType } from "./categories-
|
|
4
|
-
|
|
1
|
+
import { n as Errors } from "./errors-B7yC-Jfw.mjs";
|
|
2
|
+
import { i as requireOrgScope, n as finalizeSession, r as defaultLogger, t as acquireSession } from "./session-Ba8E3Ufa.mjs";
|
|
3
|
+
import { i as extractMainType } from "./categories-CclX7Q94.mjs";
|
|
5
4
|
//#region src/utils/date-range.ts
|
|
6
5
|
/**
|
|
7
6
|
* Compute start/end dates from a date option + value.
|
|
@@ -70,7 +69,6 @@ function getFiscalYearStart(date, fiscalStartMonth = 1) {
|
|
|
70
69
|
const year = date.getMonth() < month ? date.getFullYear() - 1 : date.getFullYear();
|
|
71
70
|
return new Date(year, month, 1);
|
|
72
71
|
}
|
|
73
|
-
|
|
74
72
|
//#endregion
|
|
75
73
|
//#region src/utils/filter-builder.ts
|
|
76
74
|
/**
|
|
@@ -108,7 +106,6 @@ function buildItemFilters(filters) {
|
|
|
108
106
|
}
|
|
109
107
|
return result;
|
|
110
108
|
}
|
|
111
|
-
|
|
112
109
|
//#endregion
|
|
113
110
|
//#region src/reports/trial-balance.ts
|
|
114
111
|
async function generateTrialBalance(opts, params) {
|
|
@@ -210,7 +207,6 @@ async function generateTrialBalance(opts, params) {
|
|
|
210
207
|
}
|
|
211
208
|
};
|
|
212
209
|
}
|
|
213
|
-
|
|
214
210
|
//#endregion
|
|
215
211
|
//#region src/utils/account-helpers.ts
|
|
216
212
|
/**
|
|
@@ -255,7 +251,6 @@ function buildAccountTypeMap(accountTypes) {
|
|
|
255
251
|
for (const at of accountTypes) map.set(at.code, at);
|
|
256
252
|
return map;
|
|
257
253
|
}
|
|
258
|
-
|
|
259
254
|
//#endregion
|
|
260
255
|
//#region src/reports/balance-sheet.ts
|
|
261
256
|
async function generateBalanceSheet(opts, params) {
|
|
@@ -402,8 +397,12 @@ async function generateBalanceSheet(opts, params) {
|
|
|
402
397
|
groupsMap.Equity[reGroup.name].accounts.push(...reGroup.accounts);
|
|
403
398
|
groupsMap.Equity[reGroup.name].total += reGroup.total;
|
|
404
399
|
}
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
const pruneGroups = (groups) => Object.values(groups).map((g) => ({
|
|
401
|
+
...g,
|
|
402
|
+
accounts: g.accounts.filter((a) => a.balance !== 0 || a.isTotal || a.isCalculated)
|
|
403
|
+
})).filter((g) => g.accounts.length > 0 || g.total !== 0);
|
|
404
|
+
assets.groups = pruneGroups(groupsMap.Asset);
|
|
405
|
+
liabilities.groups = pruneGroups(groupsMap.Liability);
|
|
407
406
|
equity.groups = Object.values(groupsMap.Equity);
|
|
408
407
|
assets.total = assets.groups.reduce((s, g) => s + g.total, 0);
|
|
409
408
|
liabilities.total = liabilities.groups.reduce((s, g) => s + g.total, 0);
|
|
@@ -433,7 +432,6 @@ async function generateBalanceSheet(opts, params) {
|
|
|
433
432
|
}
|
|
434
433
|
};
|
|
435
434
|
}
|
|
436
|
-
|
|
437
435
|
//#endregion
|
|
438
436
|
//#region src/reports/income-statement.ts
|
|
439
437
|
async function generateIncomeStatement(opts, params) {
|
|
@@ -472,6 +470,16 @@ async function generateIncomeStatement(opts, params) {
|
|
|
472
470
|
const accountMap = new Map(allAccounts.map((a) => [String(a._id), a]));
|
|
473
471
|
const revenueGroups = {};
|
|
474
472
|
const expenseGroups = {};
|
|
473
|
+
const resolveGroupName = (at) => {
|
|
474
|
+
const visited = /* @__PURE__ */ new Set();
|
|
475
|
+
let current = at.parentCode ? country.getAccountType(at.parentCode) : void 0;
|
|
476
|
+
while (current && !visited.has(current.code)) {
|
|
477
|
+
if (current.isGroup) return current.name;
|
|
478
|
+
visited.add(current.code);
|
|
479
|
+
current = current.parentCode ? country.getAccountType(current.parentCode) : void 0;
|
|
480
|
+
}
|
|
481
|
+
return at.name;
|
|
482
|
+
};
|
|
475
483
|
for (const r of results) {
|
|
476
484
|
const acc = accountMap.get(String(r._id));
|
|
477
485
|
if (!acc) continue;
|
|
@@ -480,7 +488,7 @@ async function generateIncomeStatement(opts, params) {
|
|
|
480
488
|
const mainType = extractMainType(at.category);
|
|
481
489
|
const netAmount = mainType === "Income" ? r.c - r.d : r.d - r.c;
|
|
482
490
|
if (netAmount === 0) continue;
|
|
483
|
-
const groupName = (at
|
|
491
|
+
const groupName = resolveGroupName(at);
|
|
484
492
|
const groups = mainType === "Income" ? revenueGroups : expenseGroups;
|
|
485
493
|
if (!(groupName in groups)) groups[groupName] = {
|
|
486
494
|
name: groupName,
|
|
@@ -540,7 +548,6 @@ async function generateIncomeStatement(opts, params) {
|
|
|
540
548
|
netIncome
|
|
541
549
|
};
|
|
542
550
|
}
|
|
543
|
-
|
|
544
551
|
//#endregion
|
|
545
552
|
//#region src/reports/general-ledger.ts
|
|
546
553
|
async function generateGeneralLedger(opts, params) {
|
|
@@ -675,7 +682,6 @@ async function generateGeneralLedger(opts, params) {
|
|
|
675
682
|
}
|
|
676
683
|
};
|
|
677
684
|
}
|
|
678
|
-
|
|
679
685
|
//#endregion
|
|
680
686
|
//#region src/reports/cash-flow.ts
|
|
681
687
|
async function generateCashFlow(opts, params) {
|
|
@@ -773,7 +779,6 @@ async function generateCashFlow(opts, params) {
|
|
|
773
779
|
netCashFlow
|
|
774
780
|
};
|
|
775
781
|
}
|
|
776
|
-
|
|
777
782
|
//#endregion
|
|
778
783
|
//#region src/reports/fiscal-close.ts
|
|
779
784
|
async function closeFiscalPeriod(opts, params) {
|
|
@@ -928,7 +933,7 @@ async function reopenFiscalPeriod(opts, params) {
|
|
|
928
933
|
await finalizeSession(session, ownSession, success);
|
|
929
934
|
}
|
|
930
935
|
}
|
|
931
|
-
|
|
932
936
|
//#endregion
|
|
933
937
|
export { generateIncomeStatement as a, calculateTotal as c, generateTrialBalance as d, buildItemFilters as f, generateGeneralLedger as i, computeEndingBalance as l, getFiscalYearStart as m, reopenFiscalPeriod as n, generateBalanceSheet as o, getDateRange as p, generateCashFlow as r, buildAccountTypeMap as s, closeFiscalPeriod as t, isVirtualTaxAccount as u };
|
|
934
|
-
|
|
938
|
+
|
|
939
|
+
//# sourceMappingURL=fiscal-close-L631E3De.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fiscal-close-L631E3De.mjs","names":[],"sources":["../src/utils/date-range.ts","../src/utils/filter-builder.ts","../src/reports/trial-balance.ts","../src/utils/account-helpers.ts","../src/reports/balance-sheet.ts","../src/reports/income-statement.ts","../src/reports/general-ledger.ts","../src/reports/cash-flow.ts","../src/reports/fiscal-close.ts"],"sourcesContent":["/**\r\n * Date Range Utility — Compute period boundaries for reports.\r\n */\r\n\r\nimport type { DateOption, DateRange, QuarterValue, CustomDateRange } from '../types/core.js';\r\n\r\n/**\r\n * Compute start/end dates from a date option + value.\r\n *\r\n * Examples:\r\n * getDateRange('month', '2025-03') → Mar 1 – Mar 31\r\n * getDateRange('quarter', { quarter: 2, year: 2025 }) → Apr 1 – Jun 30\r\n * getDateRange('year', 2025) → Jan 1 – Dec 31\r\n * getDateRange('custom', { startDate, endDate })\r\n */\r\nexport function getDateRange(option: DateOption, value: unknown): DateRange {\r\n switch (option) {\r\n case 'month': {\r\n // Parse 'YYYY-MM' strings explicitly to avoid UTC-vs-local timezone shift\r\n let year: number;\r\n let month: number;\r\n const strVal = String(value);\r\n const match = strVal.match(/^(\\d{4})-(\\d{1,2})$/);\r\n if (match) {\r\n year = parseInt(match[1], 10);\r\n month = parseInt(match[2], 10) - 1; // 0-indexed\r\n } else {\r\n const date = new Date(value as string | number | Date);\r\n year = date.getFullYear();\r\n month = date.getMonth();\r\n }\r\n const startDate = new Date(year, month, 1);\r\n const endDate = new Date(year, month + 1, 0, 23, 59, 59, 999);\r\n return { startDate, endDate };\r\n }\r\n\r\n case 'quarter': {\r\n const { quarter, year } = value as QuarterValue;\r\n const startMonth = (quarter - 1) * 3;\r\n const startDate = new Date(year, startMonth, 1);\r\n const endDate = new Date(year, startMonth + 3, 0, 23, 59, 59, 999);\r\n return { startDate, endDate };\r\n }\r\n\r\n case 'year': {\r\n const year = typeof value === 'number' ? value : parseInt(String(value), 10);\r\n const startDate = new Date(year, 0, 1);\r\n const endDate = new Date(year, 11, 31, 23, 59, 59, 999);\r\n return { startDate, endDate };\r\n }\r\n\r\n case 'custom': {\r\n const { startDate, endDate } = value as CustomDateRange;\r\n const end = new Date(endDate);\r\n // Normalize end date to end-of-day if time is midnight (00:00:00)\r\n if (end.getHours() === 0 && end.getMinutes() === 0 && end.getSeconds() === 0 && end.getMilliseconds() === 0) {\r\n end.setHours(23, 59, 59, 999);\r\n }\r\n return {\r\n startDate: new Date(startDate),\r\n endDate: end,\r\n };\r\n }\r\n\r\n default: {\r\n // Default: current month\r\n const now = new Date();\r\n return {\r\n startDate: new Date(now.getFullYear(), now.getMonth(), 1),\r\n endDate: new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999),\r\n };\r\n }\r\n }\r\n}\r\n\r\n/** Get fiscal year start date for a given date and fiscal start month */\r\nexport function getFiscalYearStart(date: Date, fiscalStartMonth = 1): Date {\r\n const month = fiscalStartMonth - 1; // 0-indexed\r\n const year = date.getMonth() < month ? date.getFullYear() - 1 : date.getFullYear();\r\n return new Date(year, month, 1);\r\n}\r\n","/**\r\n * Filter Builder — Sanitizes user-supplied dimension filters for aggregation pipelines.\r\n *\r\n * Prevents injection of dangerous MongoDB operators while allowing\r\n * standard equality and comparison filters on custom dimension fields.\r\n */\r\n\r\nconst BLOCKED_OPERATORS = new Set([\r\n '$where', '$expr', '$function', '$accumulator',\r\n '$merge', '$out', '$unionWith',\r\n]);\r\n\r\n/**\r\n * Build a sanitized filter object from user-supplied dimension filters.\r\n * Blocks dangerous operators ($where, $expr, $function, etc.).\r\n *\r\n * @param filters - Key-value filters (e.g. { 'journalItems.departmentId': 'dept-1' })\r\n * @returns Sanitized filter object safe for $match stages\r\n * @throws Error if a blocked operator is used\r\n */\r\nexport function buildItemFilters(filters?: Record<string, unknown>): Record<string, unknown> {\r\n if (!filters || Object.keys(filters).length === 0) return {};\r\n\r\n const result: Record<string, unknown> = {};\r\n\r\n for (const [key, value] of Object.entries(filters)) {\r\n // Block operators at top level\r\n if (key.startsWith('$')) {\r\n throw new Error(`Filter key \"${key}\" is not allowed. Use field names, not operators.`);\r\n }\r\n\r\n // Check nested values for blocked operators\r\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\r\n for (const opKey of Object.keys(value as Record<string, unknown>)) {\r\n if (BLOCKED_OPERATORS.has(opKey)) {\r\n throw new Error(`Filter operator \"${opKey}\" is not allowed.`);\r\n }\r\n }\r\n }\r\n\r\n result[key] = value;\r\n }\r\n\r\n return result;\r\n}\r\n","/**\r\n * Trial Balance Report\r\n *\r\n * Three-column trial balance: Initial + Current Period + Ending Balance.\r\n * Pure aggregation pipeline — no cached balances.\r\n */\r\n\r\nimport type { Model, PipelineStage } from 'mongoose';\r\nimport type { AccountType, CategoryKey } from '../types/core.js';\r\nimport type { TrialBalanceRow, TrialBalanceReport } from '../types/report.js';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport { getDateRange, getFiscalYearStart } from '../utils/date-range.js';\r\nimport { computeEndingBalance } from '../utils/account-helpers.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { buildItemFilters } from '../utils/filter-builder.js';\r\n\r\nexport interface TrialBalanceOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n fiscalYearStartMonth?: number;\r\n}\r\n\r\nexport async function generateTrialBalance(\r\n opts: TrialBalanceOptions,\r\n params: {\r\n organizationId?: unknown;\r\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\r\n dateValue: unknown;\r\n accountId?: string;\r\n filters?: Record<string, unknown>;\r\n },\r\n): Promise<TrialBalanceReport> {\r\n const { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth = 1 } = opts;\r\n requireOrgScope(orgField, params.organizationId);\r\n const { startDate, endDate } = getDateRange(params.dateOption, params.dateValue);\r\n const fiscalYearStart = getFiscalYearStart(startDate, fiscalYearStartMonth);\r\n const itemFilters = buildItemFilters(params.filters);\r\n\r\n // Fetch all active accounts\r\n const accountQuery: Record<string, unknown> = { active: true };\r\n if (orgField && params.organizationId) accountQuery[orgField] = params.organizationId;\r\n\r\n const allAccounts = await AccountModel.find(accountQuery).lean() as Array<Record<string, unknown>>;\r\n\r\n // Split by statement type\r\n const bsIds: unknown[] = [];\r\n const isIds: unknown[] = [];\r\n\r\n for (const acc of allAccounts) {\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at || at.isGroup) continue;\r\n\r\n if (at.category.startsWith('Balance Sheet')) bsIds.push(acc._id);\r\n else if (at.category.startsWith('Income Statement')) isIds.push(acc._id);\r\n }\r\n\r\n const baseMatch: Record<string, unknown> = { state: 'posted' };\r\n if (orgField && params.organizationId) baseMatch[orgField] = params.organizationId;\r\n\r\n const accountFilter = params.accountId ? { 'journalItems.account': params.accountId } : {};\r\n\r\n // Build pipelines\r\n const buildPipeline = (ids: unknown[], dateFrom: Date, dateTo: Date): PipelineStage[] => [\r\n { $match: { ...baseMatch, date: { $gte: dateFrom, $lt: dateTo } } },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: ids }, ...accountFilter, ...itemFilters } },\r\n { $group: { _id: '$journalItems.account', d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ];\r\n\r\n // BS initial: all history before startDate\r\n // IS initial: fiscal year start → startDate\r\n // Current: startDate → endDate\r\n const [bsInitial, isInitial, current] = await Promise.all([\r\n bsIds.length ? JournalEntryModel.aggregate(buildPipeline(bsIds, new Date(0), startDate)) : [],\r\n isIds.length ? JournalEntryModel.aggregate(buildPipeline(isIds, fiscalYearStart, startDate)) : [],\r\n JournalEntryModel.aggregate(buildPipeline([...bsIds, ...isIds], startDate, new Date(endDate.getTime() + 1))),\r\n ]);\r\n\r\n // Merge\r\n const map = new Map<string, { iD: number; iC: number; cD: number; cC: number }>();\r\n\r\n for (const r of [...bsInitial, ...isInitial]) {\r\n const key = String(r._id);\r\n map.set(key, { iD: r.d, iC: r.c, cD: 0, cC: 0 });\r\n }\r\n for (const r of current) {\r\n const key = String(r._id);\r\n const existing = map.get(key) ?? { iD: 0, iC: 0, cD: 0, cC: 0 };\r\n existing.cD = r.d;\r\n existing.cC = r.c;\r\n map.set(key, existing);\r\n }\r\n\r\n // Build rows\r\n const accountLookup = new Map(allAccounts.map(a => [String(a._id), a]));\r\n\r\n const rows: TrialBalanceRow[] = [];\r\n for (const [id, bal] of map) {\r\n const acc = accountLookup.get(id);\r\n const totalD = bal.iD + bal.cD;\r\n const totalC = bal.iC + bal.cC;\r\n const net = totalD - totalC;\r\n\r\n rows.push({\r\n account: acc ?? id,\r\n initial: { debit: bal.iD, credit: bal.iC },\r\n current: { debit: bal.cD, credit: bal.cC },\r\n ending: net >= 0 ? { debit: net, credit: 0 } : { debit: 0, credit: Math.abs(net) },\r\n });\r\n }\r\n\r\n return { rows, period: { startDate, endDate } };\r\n}\r\n","/**\r\n * Account Helper Utilities\r\n */\r\n\r\nimport type { AccountType, TotalAccountOp, CategoryKey } from '../types/core.js';\r\nimport { extractMainType } from '../constants/categories.js';\r\n\r\n/**\r\n * Check if an account type is a virtual tax sub-account.\r\n * Returns true if the account's parent has `isVirtualTotal: true`.\r\n * Works for any country pack — no code format assumptions.\r\n */\r\nexport function isVirtualTaxAccount(accountType: AccountType, accountMap: Map<string, AccountType>): boolean {\r\n if (!accountType.parentCode) return false;\r\n const parent = accountMap.get(accountType.parentCode);\r\n return parent?.isVirtualTotal === true;\r\n}\r\n\r\n/** Check if an account type is a balance sheet account */\r\nexport function isBalanceSheetAccountType(accountType: AccountType): boolean {\r\n const { category } = accountType;\r\n return category.endsWith('-Asset') || category.endsWith('-Liability') || category.endsWith('-Equity');\r\n}\r\n\r\n/** Check if an account type is an income statement account */\r\nexport function isIncomeStatementAccountType(accountType: AccountType): boolean {\r\n const { category } = accountType;\r\n return category.endsWith('-Income') || category.endsWith('-Expense');\r\n}\r\n\r\n/**\r\n * Calculate a total from sub-accounts using the totalAccountTypes formula.\r\n * @param formula - Array of { account, operation } instructions\r\n * @param balanceMap - Map of account code → balance\r\n */\r\nexport function calculateTotal(\r\n formula: readonly TotalAccountOp[],\r\n balanceMap: Map<string, number>,\r\n): number {\r\n let total = 0;\r\n for (const item of formula) {\r\n const balance = balanceMap.get(item.account) ?? 0;\r\n total += item.operation === '+' ? balance : -balance;\r\n }\r\n return total;\r\n}\r\n\r\n/**\r\n * Compute the ending balance for an account given its debits and credits.\r\n * Uses the account's main type to determine normal balance direction.\r\n *\r\n * Assets & Expenses: debit - credit\r\n * Liabilities, Equity & Income: credit - debit\r\n */\r\nexport function computeEndingBalance(\r\n category: CategoryKey,\r\n totalDebit: number,\r\n totalCredit: number,\r\n): number {\r\n const mainType = extractMainType(category);\r\n if (mainType === 'Asset' || mainType === 'Expense') {\r\n return totalDebit - totalCredit;\r\n }\r\n return totalCredit - totalDebit;\r\n}\r\n\r\n/**\r\n * Build a lookup map from an array of account types.\r\n */\r\nexport function buildAccountTypeMap(accountTypes: readonly AccountType[]): Map<string, AccountType> {\r\n const map = new Map<string, AccountType>();\r\n for (const at of accountTypes) {\r\n map.set(at.code, at);\r\n }\r\n return map;\r\n}\r\n","/**\r\n * Balance Sheet Report\r\n *\r\n * Assets = Liabilities + Equity\r\n * Net income injected into retained earnings for the current fiscal year.\r\n */\r\n\r\nimport type { Model, PipelineStage } from 'mongoose';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport type { BalanceSheetReport, ReportCategory, ReportGroup, ReportAccount } from '../types/report.js';\r\nimport { getDateRange, getFiscalYearStart } from '../utils/date-range.js';\r\nimport { computeEndingBalance, calculateTotal, isVirtualTaxAccount, buildAccountTypeMap } from '../utils/account-helpers.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { buildItemFilters } from '../utils/filter-builder.js';\r\nimport { extractMainType } from '../constants/categories.js';\r\nimport type { CategoryKey } from '../types/core.js';\r\n\r\nexport interface BalanceSheetOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n fiscalYearStartMonth?: number;\r\n /** Display code for prior retained earnings (default: '3660') */\r\n retainedEarningsCode?: string;\r\n /** Display code for current year net income (default: '3680') */\r\n currentYearEarningsCode?: string;\r\n}\r\n\r\nexport async function generateBalanceSheet(\r\n opts: BalanceSheetOptions,\r\n params: {\r\n organizationId?: unknown;\r\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\r\n dateValue: unknown;\r\n businessName?: string;\r\n filters?: Record<string, unknown>;\r\n },\r\n): Promise<BalanceSheetReport> {\r\n const {\r\n AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth = 1,\r\n retainedEarningsCode = country.retainedEarningsCode ?? '3660',\r\n currentYearEarningsCode = country.currentYearEarningsCode ?? '3680',\r\n } = opts;\r\n requireOrgScope(orgField, params.organizationId);\r\n const { endDate } = getDateRange(params.dateOption, params.dateValue);\r\n const fiscalYearStart = getFiscalYearStart(endDate, fiscalYearStartMonth);\r\n const itemFilters = buildItemFilters(params.filters);\r\n\r\n // Fetch accounts\r\n const q: Record<string, unknown> = { active: true };\r\n if (orgField && params.organizationId) q[orgField] = params.organizationId;\r\n const allAccounts = await AccountModel.find(q).lean() as Array<Record<string, unknown>>;\r\n\r\n // Balance sheet account IDs\r\n const bsIds = allAccounts\r\n .filter(a => {\r\n const at = country.getAccountType(a.accountTypeCode as string);\r\n return at && !at.isGroup && at.category.startsWith('Balance Sheet');\r\n })\r\n .map(a => a._id);\r\n\r\n // Income statement account IDs (for net income calculation)\r\n const isIds = allAccounts\r\n .filter(a => {\r\n const at = country.getAccountType(a.accountTypeCode as string);\r\n return at && !at.isGroup && !at.isTotal && at.category.startsWith('Income Statement');\r\n })\r\n .map(a => a._id);\r\n\r\n const baseMatch: Record<string, unknown> = { state: 'posted' };\r\n if (orgField && params.organizationId) baseMatch[orgField] = params.organizationId;\r\n\r\n // Run pipelines in parallel\r\n const [bsResults, netIncomeResults, priorRetainedResults] = await Promise.all([\r\n // Balance sheet balances (all time up to endDate)\r\n JournalEntryModel.aggregate([\r\n { $match: { ...baseMatch, date: { $lte: endDate } } },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: bsIds }, ...itemFilters } },\r\n { $group: { _id: '$journalItems.account', d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ]) as Promise<Array<{ _id: unknown; d: number; c: number }>>,\r\n\r\n // Net income (fiscal year start → endDate)\r\n JournalEntryModel.aggregate([\r\n { $match: { ...baseMatch, date: { $gte: fiscalYearStart, $lte: endDate } } },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: isIds }, ...itemFilters } },\r\n { $group: { _id: null, d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ]) as Promise<Array<{ _id: unknown; d: number; c: number }>>,\r\n\r\n // Prior retained earnings (all income statement before fiscal year)\r\n JournalEntryModel.aggregate([\r\n { $match: { ...baseMatch, date: { $lt: fiscalYearStart } } },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: isIds }, ...itemFilters } },\r\n { $group: { _id: null, d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ]) as Promise<Array<{ _id: unknown; d: number; c: number }>>,\r\n ]);\r\n\r\n const netIncome = netIncomeResults.length > 0 ? netIncomeResults[0].c - netIncomeResults[0].d : 0;\r\n const priorRetained = priorRetainedResults.length > 0 ? priorRetainedResults[0].c - priorRetainedResults[0].d : 0;\r\n\r\n // Build categories\r\n const accountMap = new Map(allAccounts.map(a => [String(a._id), a]));\r\n const accountTypeMap = buildAccountTypeMap(country.accountTypes);\r\n const balanceMap = new Map<string, number>();\r\n\r\n const labels = country.reportLabels ?? {};\r\n const assets: ReportCategory = { name: labels.assets ?? 'Assets', total: 0, groups: [] };\r\n const liabilities: ReportCategory = { name: labels.liabilities ?? 'Liabilities', total: 0, groups: [] };\r\n const equity: ReportCategory = { name: labels.equity ?? 'Equity', total: 0, groups: [] };\r\n\r\n const groupsMap: Record<string, Record<string, ReportGroup>> = {\r\n Asset: {}, Liability: {}, Equity: {},\r\n };\r\n\r\n for (const r of bsResults) {\r\n const acc = accountMap.get(String(r._id));\r\n if (!acc) continue;\r\n\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at) continue;\r\n\r\n const mainType = extractMainType(at.category) ?? 'Asset';\r\n const balance = computeEndingBalance(at.category as CategoryKey, r.d, r.c);\r\n balanceMap.set(at.code, balance);\r\n\r\n const parentAt = at.parentCode ? country.getAccountType(at.parentCode) : undefined;\r\n const groupName = parentAt?.name ?? at.name;\r\n\r\n if (!(groupName in groupsMap[mainType])) {\r\n groupsMap[mainType][groupName] = { name: groupName, total: 0, accounts: [] };\r\n }\r\n\r\n const group = groupsMap[mainType][groupName];\r\n\r\n // Skip virtual tax sub-accounts from display but include in calculation\r\n if (!isVirtualTaxAccount(at, accountTypeMap)) {\r\n group.accounts.push({\r\n id: acc._id,\r\n name: (acc.name as string) ?? at.name,\r\n code: (acc.accountNumber as string) ?? at.code,\r\n balance,\r\n isTotal: at.isTotal,\r\n isVirtualTotal: at.isVirtualTotal,\r\n });\r\n }\r\n\r\n if (!at.isTotal) {\r\n group.total += balance;\r\n }\r\n }\r\n\r\n // Add retained earnings to equity\r\n const reGroup: ReportGroup = {\r\n name: 'Retained Earnings',\r\n total: priorRetained + netIncome,\r\n accounts: [\r\n { id: 'prior-retained', name: 'Previous Years Retained Earnings', code: retainedEarningsCode, balance: priorRetained },\r\n { id: 'current-year', name: `Current Year Net Income (${endDate.getFullYear()})`, code: currentYearEarningsCode, balance: netIncome, isCalculated: true },\r\n ],\r\n };\r\n\r\n if (!(reGroup.name in groupsMap.Equity)) {\r\n groupsMap.Equity[reGroup.name] = reGroup;\r\n } else {\r\n groupsMap.Equity[reGroup.name].accounts.push(...reGroup.accounts);\r\n groupsMap.Equity[reGroup.name].total += reGroup.total;\r\n }\r\n\r\n // Convert groups maps to arrays, filtering out zero-balance accounts and empty groups\r\n const pruneGroups = (groups: Record<string, ReportGroup>) =>\r\n Object.values(groups)\r\n .map(g => ({\r\n ...g,\r\n accounts: g.accounts.filter(a => a.balance !== 0 || a.isTotal || a.isCalculated),\r\n }))\r\n .filter(g => g.accounts.length > 0 || g.total !== 0);\r\n\r\n assets.groups = pruneGroups(groupsMap.Asset);\r\n liabilities.groups = pruneGroups(groupsMap.Liability);\r\n equity.groups = Object.values(groupsMap.Equity); // Keep equity as-is (retained earnings always shown)\r\n\r\n // Sum totals\r\n assets.total = assets.groups.reduce((s, g) => s + g.total, 0);\r\n liabilities.total = liabilities.groups.reduce((s, g) => s + g.total, 0);\r\n equity.total = equity.groups.reduce((s, g) => s + g.total, 0);\r\n\r\n const liabilitiesAndEquity = liabilities.total + equity.total;\r\n\r\n return {\r\n metadata: {\r\n businessName: params.businessName,\r\n generatedAt: new Date().toISOString(),\r\n asOfDate: endDate.toISOString().split('T')[0],\r\n displayDate: `As of ${endDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`,\r\n },\r\n assets,\r\n liabilities,\r\n equity,\r\n summary: {\r\n totalAssets: assets.total,\r\n totalLiabilities: liabilities.total,\r\n totalEquity: equity.total,\r\n liabilitiesAndEquity,\r\n difference: assets.total - liabilitiesAndEquity,\r\n isBalanced: assets.total === liabilitiesAndEquity,\r\n },\r\n };\r\n}\r\n","/**\r\n * Income Statement (Profit & Loss) Report\r\n *\r\n * Revenue - COGS = Gross Profit\r\n * Gross Profit - Operating Expenses = Operating Income\r\n * Operating Income ± Other = Net Income\r\n */\r\n\r\nimport type { Model } from 'mongoose';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport type { IncomeStatementReport, ReportCategory, ReportGroup } from '../types/report.js';\r\nimport { getDateRange } from '../utils/date-range.js';\r\nimport { extractMainType } from '../constants/categories.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { buildItemFilters } from '../utils/filter-builder.js';\r\n\r\nexport interface IncomeStatementOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n}\r\n\r\nexport async function generateIncomeStatement(\r\n opts: IncomeStatementOptions,\r\n params: {\r\n organizationId?: unknown;\r\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\r\n dateValue: unknown;\r\n businessName?: string;\r\n filters?: Record<string, unknown>;\r\n },\r\n): Promise<IncomeStatementReport> {\r\n const { AccountModel, JournalEntryModel, country, orgField } = opts;\r\n requireOrgScope(orgField, params.organizationId);\r\n const { startDate, endDate } = getDateRange(params.dateOption, params.dateValue);\r\n const itemFilters = buildItemFilters(params.filters);\r\n\r\n // Fetch accounts\r\n const q: Record<string, unknown> = { active: true };\r\n if (orgField && params.organizationId) q[orgField] = params.organizationId;\r\n const allAccounts = await AccountModel.find(q).lean() as Array<Record<string, unknown>>;\r\n\r\n // Income statement posting accounts only\r\n const isAccounts = allAccounts.filter(a => {\r\n const at = country.getAccountType(a.accountTypeCode as string);\r\n return at && !at.isGroup && !at.isTotal && at.category.startsWith('Income Statement');\r\n });\r\n const isIds = isAccounts.map(a => a._id);\r\n\r\n const baseMatch: Record<string, unknown> = {\r\n state: 'posted',\r\n date: { $gte: startDate, $lte: endDate },\r\n };\r\n if (orgField && params.organizationId) baseMatch[orgField] = params.organizationId;\r\n\r\n const results = await JournalEntryModel.aggregate([\r\n { $match: baseMatch },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: isIds }, ...itemFilters } },\r\n { $group: { _id: '$journalItems.account', d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ]) as Array<{ _id: unknown; d: number; c: number }>;\r\n\r\n const accountMap = new Map(allAccounts.map(a => [String(a._id), a]));\r\n\r\n // Organize into revenue and expenses\r\n const revenueGroups: Record<string, ReportGroup> = {};\r\n const expenseGroups: Record<string, ReportGroup> = {};\r\n\r\n // Resolve the top-level IS group (Revenue, Cost of Sales, Operating Expenses)\r\n // by walking up the parent chain until hitting a group-label account type.\r\n const resolveGroupName = (at: { parentCode: string | null; name: string }) => {\r\n const visited = new Set<string>();\r\n let current = at.parentCode ? country.getAccountType(at.parentCode) : undefined;\r\n while (current && !visited.has(current.code)) {\r\n if (current.isGroup) return current.name;\r\n visited.add(current.code);\r\n current = current.parentCode ? country.getAccountType(current.parentCode) : undefined;\r\n }\r\n return at.name;\r\n };\r\n\r\n for (const r of results) {\r\n const acc = accountMap.get(String(r._id));\r\n if (!acc) continue;\r\n\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at) continue;\r\n\r\n const mainType = extractMainType(at.category);\r\n const netAmount = mainType === 'Income' ? r.c - r.d : r.d - r.c;\r\n if (netAmount === 0) continue;\r\n\r\n const groupName = resolveGroupName(at);\r\n\r\n const groups = mainType === 'Income' ? revenueGroups : expenseGroups;\r\n\r\n if (!(groupName in groups)) {\r\n groups[groupName] = { name: groupName, total: 0, accounts: [] };\r\n }\r\n\r\n groups[groupName].accounts.push({\r\n id: acc._id,\r\n name: (acc.name as string) ?? at.name,\r\n code: (acc.accountNumber as string) ?? at.code,\r\n balance: netAmount,\r\n });\r\n groups[groupName].total += netAmount;\r\n }\r\n\r\n const labels = country.reportLabels ?? {};\r\n const revenue: ReportCategory = {\r\n name: labels.revenue ?? 'Revenue',\r\n total: Object.values(revenueGroups).reduce((s, g) => s + g.total, 0),\r\n groups: Object.values(revenueGroups),\r\n };\r\n\r\n const expenses: ReportCategory = {\r\n name: labels.expenses ?? 'Expenses',\r\n total: Object.values(expenseGroups).reduce((s, g) => s + g.total, 0),\r\n groups: Object.values(expenseGroups),\r\n };\r\n\r\n // Calculate COGS — use pack-declared group code, fall back to common names\r\n const cogsCode = country.cogsGroupCode;\r\n const isCogs = (name: string) =>\r\n cogsCode\r\n ? name === cogsCode\r\n : name === 'Cost of Sales' || name === 'Cost of Goods Sold';\r\n\r\n const cogsGroup = expenses.groups.find(g => isCogs(g.name));\r\n const costOfSales = cogsGroup?.total ?? 0;\r\n const grossProfit = revenue.total - costOfSales;\r\n const operatingExpenses = expenses.groups\r\n .filter(g => !isCogs(g.name))\r\n .reduce((s, g) => s + g.total, 0);\r\n const operatingIncome = grossProfit - operatingExpenses;\r\n const netIncome = revenue.total - expenses.total;\r\n\r\n const periodDisplay =\r\n params.dateOption === 'year'\r\n ? `For the year ended ${endDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`\r\n : `${startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} – ${endDate.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}`;\r\n\r\n return {\r\n metadata: {\r\n businessName: params.businessName,\r\n generatedAt: new Date().toISOString(),\r\n periodStart: startDate.toISOString().split('T')[0],\r\n periodEnd: endDate.toISOString().split('T')[0],\r\n displayPeriod: periodDisplay,\r\n },\r\n revenue,\r\n costOfSales,\r\n grossProfit,\r\n expenses,\r\n operatingIncome,\r\n netIncome,\r\n };\r\n}\r\n","/**\r\n * General Ledger Report\r\n *\r\n * Shows every posted entry for selected accounts with running balances.\r\n * Uses batched queries (3 max) instead of per-account loops.\r\n */\r\n\r\nimport type { Model } from 'mongoose';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport type { GeneralLedgerReport, GeneralLedgerAccount, LedgerEntry } from '../types/report.js';\r\nimport type { AccountType } from '../types/core.js';\r\nimport type { CategoryKey } from '../types/core.js';\r\nimport { getDateRange, getFiscalYearStart } from '../utils/date-range.js';\r\nimport { computeEndingBalance } from '../utils/account-helpers.js';\r\nimport { extractMainType } from '../constants/categories.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { buildItemFilters } from '../utils/filter-builder.js';\r\n\r\nexport interface GeneralLedgerOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n fiscalYearStartMonth?: number;\r\n}\r\n\r\nexport async function generateGeneralLedger(\r\n opts: GeneralLedgerOptions,\r\n params: {\r\n organizationId?: unknown;\r\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\r\n dateValue: unknown;\r\n accountId?: string;\r\n filters?: Record<string, unknown>;\r\n },\r\n): Promise<GeneralLedgerReport> {\r\n const { AccountModel, JournalEntryModel, country, orgField, fiscalYearStartMonth = 1 } = opts;\r\n requireOrgScope(orgField, params.organizationId);\r\n const { startDate, endDate } = getDateRange(params.dateOption, params.dateValue);\r\n const fiscalYearStart = getFiscalYearStart(startDate, fiscalYearStartMonth);\r\n const itemFilters = buildItemFilters(params.filters);\r\n\r\n // Get target accounts\r\n const acctQuery: Record<string, unknown> = { active: true };\r\n if (orgField && params.organizationId) acctQuery[orgField] = params.organizationId;\r\n if (params.accountId) acctQuery._id = params.accountId;\r\n\r\n const allAccounts = await AccountModel.find(acctQuery).lean() as Array<Record<string, unknown>>;\r\n\r\n // Filter to postable accounts (no groups, no totals)\r\n const filtered: Array<{ acc: Record<string, unknown>; at: AccountType }> = [];\r\n for (const acc of allAccounts) {\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at || at.isGroup || at.isTotal) continue;\r\n filtered.push({ acc, at });\r\n }\r\n\r\n if (filtered.length === 0) {\r\n return { accounts: [], period: { startDate, endDate } };\r\n }\r\n\r\n // Separate BS vs IS account IDs (different opening-balance date ranges)\r\n const bsAccountIds: unknown[] = [];\r\n const isAccountIds: unknown[] = [];\r\n const allAccountIds: unknown[] = [];\r\n\r\n for (const { acc, at } of filtered) {\r\n allAccountIds.push(acc._id);\r\n if (at.category.startsWith('Balance Sheet')) {\r\n bsAccountIds.push(acc._id);\r\n } else {\r\n isAccountIds.push(acc._id);\r\n }\r\n }\r\n\r\n // Org scope helper\r\n const orgScope: Record<string, unknown> = {};\r\n if (orgField && params.organizationId) orgScope[orgField] = params.organizationId;\r\n\r\n // ── Batch queries (3 max, run in parallel) ──────────────────────────────────\r\n\r\n const openingBalancePipeline = (\r\n accountIds: unknown[],\r\n dateFilter: Record<string, unknown>,\r\n ) =>\r\n accountIds.length > 0\r\n ? JournalEntryModel.aggregate([\r\n { $match: { state: 'posted', date: dateFilter, ...orgScope } },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: accountIds }, ...itemFilters } },\r\n {\r\n $group: {\r\n _id: '$journalItems.account',\r\n d: { $sum: '$journalItems.debit' },\r\n c: { $sum: '$journalItems.credit' },\r\n },\r\n },\r\n ])\r\n : Promise.resolve([]);\r\n\r\n const [bsOpenResults, isOpenResults, periodEntries] = await Promise.all([\r\n // BS opening: all posted entries before startDate\r\n openingBalancePipeline(bsAccountIds, { $lt: startDate }),\r\n // IS opening: posted entries from fiscal year start to before startDate\r\n openingBalancePipeline(isAccountIds, { $gte: fiscalYearStart, $lt: startDate }),\r\n // Period entries: all posted entries for any target account in the period\r\n JournalEntryModel.find({\r\n state: 'posted',\r\n date: { $gte: startDate, $lte: endDate },\r\n 'journalItems.account': { $in: allAccountIds },\r\n ...orgScope,\r\n ...itemFilters,\r\n })\r\n .select('date referenceNumber label journalItems')\r\n .sort({ date: 1 })\r\n .lean() as Promise<Array<Record<string, unknown>>>,\r\n ]);\r\n\r\n // ── Build lookup maps ───────────────────────────────────────────────────────\r\n\r\n // Opening balance by account ID\r\n const openBalMap = new Map<string, { d: number; c: number }>();\r\n for (const r of [...(bsOpenResults as Array<{ _id: unknown; d: number; c: number }>),\r\n ...(isOpenResults as Array<{ _id: unknown; d: number; c: number }>)]) {\r\n openBalMap.set(String(r._id), { d: r.d, c: r.c });\r\n }\r\n\r\n // ── Pre-index period entries by account ID (O(entries × items) once) ────────\r\n\r\n const entryItemsByAccount = new Map<string, Array<{\r\n date: Date; referenceNumber: string; label: string; debit: number; credit: number;\r\n }>>();\r\n\r\n for (const entry of periodEntries) {\r\n const items = (entry.journalItems as Array<Record<string, unknown>>) ?? [];\r\n for (const item of items) {\r\n const accId = String(item.account);\r\n const debit = (item.debit as number) ?? 0;\r\n const credit = (item.credit as number) ?? 0;\r\n\r\n let list = entryItemsByAccount.get(accId);\r\n if (!list) {\r\n list = [];\r\n entryItemsByAccount.set(accId, list);\r\n }\r\n list.push({\r\n date: entry.date as Date,\r\n referenceNumber: (entry.referenceNumber as string) ?? '',\r\n label: (entry.label as string) ?? '',\r\n debit,\r\n credit,\r\n });\r\n }\r\n }\r\n\r\n // ── Assemble per-account results (O(accounts + total items)) ──────────────\r\n\r\n const glAccounts: GeneralLedgerAccount[] = [];\r\n\r\n for (const { acc, at } of filtered) {\r\n const accIdStr = String(acc._id);\r\n const openData = openBalMap.get(accIdStr);\r\n const openingBalance = openData\r\n ? computeEndingBalance(at.category as CategoryKey, openData.d, openData.c)\r\n : 0;\r\n\r\n let runningBalance = openingBalance;\r\n const entries: LedgerEntry[] = [];\r\n const mainType = extractMainType(at.category as CategoryKey);\r\n\r\n const accountItems = entryItemsByAccount.get(accIdStr) ?? [];\r\n for (const item of accountItems) {\r\n const delta =\r\n mainType === 'Asset' || mainType === 'Expense'\r\n ? item.debit - item.credit\r\n : item.credit - item.debit;\r\n\r\n runningBalance += delta;\r\n\r\n entries.push({\r\n date: item.date,\r\n referenceNumber: item.referenceNumber,\r\n label: item.label,\r\n debit: item.debit,\r\n credit: item.credit,\r\n runningBalance,\r\n });\r\n }\r\n\r\n glAccounts.push({\r\n account: acc,\r\n openingBalance,\r\n entries,\r\n closingBalance: runningBalance,\r\n });\r\n }\r\n\r\n return { accounts: glAccounts, period: { startDate, endDate } };\r\n}\r\n","/**\r\n * Cash Flow Statement\r\n *\r\n * Groups transactions by cashFlowCategory from account type definitions:\r\n * Operating, Investing, Financing.\r\n * Uses aggregation pipeline — no in-memory processing.\r\n */\r\n\r\nimport type { Model } from 'mongoose';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport type { CashFlowCategory, CategoryKey } from '../types/core.js';\r\nimport type { CashFlowReport } from '../types/report.js';\r\nimport { getDateRange } from '../utils/date-range.js';\r\nimport { computeEndingBalance } from '../utils/account-helpers.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { buildItemFilters } from '../utils/filter-builder.js';\r\n\r\nexport interface CashFlowOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n}\r\n\r\nexport async function generateCashFlow(\r\n opts: CashFlowOptions,\r\n params: {\r\n organizationId?: unknown;\r\n dateOption: 'month' | 'quarter' | 'year' | 'custom';\r\n dateValue: unknown;\r\n businessName?: string;\r\n filters?: Record<string, unknown>;\r\n },\r\n): Promise<CashFlowReport> {\r\n const { AccountModel, JournalEntryModel, country, orgField } = opts;\r\n requireOrgScope(orgField, params.organizationId);\r\n const { startDate, endDate } = getDateRange(params.dateOption, params.dateValue);\r\n const itemFilters = buildItemFilters(params.filters);\r\n\r\n // Fetch accounts\r\n const q: Record<string, unknown> = { active: true };\r\n if (orgField && params.organizationId) q[orgField] = params.organizationId;\r\n const allAccounts = await AccountModel.find(q).lean() as Array<Record<string, unknown>>;\r\n\r\n // Build maps: accountId -> metadata, accountId -> raw account doc\r\n const accountCfMap = new Map<string, { category: CategoryKey; cfCategory: CashFlowCategory }>();\r\n const accountMap = new Map(allAccounts.map(a => [String(a._id), a]));\r\n const cfAccountIds: unknown[] = [];\r\n\r\n for (const acc of allAccounts) {\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at || at.isGroup || at.isTotal) continue;\r\n\r\n const cf = at.cashFlowCategory;\r\n if (!cf) continue;\r\n\r\n // Normalize case: 'operating' → 'Operating'\r\n const normalized = (cf.charAt(0).toUpperCase() + cf.slice(1)) as CashFlowCategory;\r\n accountCfMap.set(String(acc._id), { category: at.category, cfCategory: normalized });\r\n cfAccountIds.push(acc._id);\r\n }\r\n\r\n // Aggregate journal items for accounts with cashFlowCategory\r\n const baseMatch: Record<string, unknown> = {\r\n state: 'posted',\r\n date: { $gte: startDate, $lte: endDate },\r\n };\r\n if (orgField && params.organizationId) baseMatch[orgField] = params.organizationId;\r\n\r\n const results = cfAccountIds.length > 0\r\n ? await JournalEntryModel.aggregate([\r\n { $match: baseMatch },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: cfAccountIds }, ...itemFilters } },\r\n { $group: { _id: '$journalItems.account', d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ]) as Array<{ _id: unknown; d: number; c: number }>\r\n : [];\r\n\r\n // Accumulate by category\r\n const flows: Record<CashFlowCategory, { total: number; accounts: Array<{ name: string; code: string; amount: number }> }> = {\r\n Operating: { total: 0, accounts: [] },\r\n Investing: { total: 0, accounts: [] },\r\n Financing: { total: 0, accounts: [] },\r\n };\r\n\r\n for (const r of results) {\r\n const accIdStr = String(r._id);\r\n const meta = accountCfMap.get(accIdStr);\r\n if (!meta) continue;\r\n\r\n // Net cash flow: for assets/expenses, debit increases (cash out), credit decreases (cash in)\r\n // For liabilities/equity/income, credit increases, debit decreases\r\n const amount = computeEndingBalance(meta.category, r.d, r.c);\r\n const acc = accountMap.get(accIdStr);\r\n const at = country.getAccountType(acc?.accountTypeCode as string);\r\n\r\n flows[meta.cfCategory].accounts.push({\r\n name: (acc?.name as string) ?? at?.name ?? '',\r\n code: (acc?.accountNumber as string) ?? at?.code ?? '',\r\n amount,\r\n });\r\n flows[meta.cfCategory].total += amount;\r\n }\r\n\r\n const netCashFlow = flows.Operating.total + flows.Investing.total + flows.Financing.total;\r\n\r\n const periodDisplay = `${startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} – ${endDate.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}`;\r\n\r\n return {\r\n metadata: {\r\n businessName: params.businessName,\r\n generatedAt: new Date().toISOString(),\r\n periodStart: startDate.toISOString().split('T')[0],\r\n periodEnd: endDate.toISOString().split('T')[0],\r\n displayPeriod: periodDisplay,\r\n },\r\n operating: flows.Operating,\r\n investing: flows.Investing,\r\n financing: flows.Financing,\r\n netCashFlow,\r\n };\r\n}\r\n","/**\r\n * Fiscal Year Closing & Reopening\r\n *\r\n * Close: zeroes income/expense accounts, transfers net income to retained\r\n * earnings via a YEAR_END journal entry, marks the period closed.\r\n *\r\n * Reopen: validates no later period is closed, deletes the closing entry,\r\n * marks the period open again with an audit trail.\r\n *\r\n * Session management: creates an internal transaction by default.\r\n * Pass an external session to join a caller-managed transaction instead.\r\n */\r\n\r\nimport type { Model, ClientSession } from 'mongoose';\r\nimport type { CountryPack } from '../country/index.js';\r\nimport { requireOrgScope } from '../utils/tenant-guard.js';\r\nimport { Errors } from '../utils/errors.js';\r\nimport type { Logger } from '../utils/logger.js';\r\nimport { defaultLogger } from '../utils/logger.js';\r\nimport { acquireSession, finalizeSession } from '../utils/session.js';\r\n\r\nexport interface FiscalCloseOptions {\r\n AccountModel: Model<unknown>;\r\n JournalEntryModel: Model<unknown>;\r\n FiscalPeriodModel: Model<unknown>;\r\n country: CountryPack;\r\n orgField?: string;\r\n retainedEarningsCode?: string;\r\n logger?: Logger;\r\n}\r\n\r\nexport interface FiscalCloseResult {\r\n periodId: unknown;\r\n netIncome: number;\r\n closingEntryId: unknown | null;\r\n accountsClosed: number;\r\n closedAt: Date;\r\n}\r\n\r\nexport async function closeFiscalPeriod(\r\n opts: FiscalCloseOptions,\r\n params: {\r\n periodId: unknown;\r\n organizationId?: unknown;\r\n closedBy?: string;\r\n session?: ClientSession;\r\n },\r\n): Promise<FiscalCloseResult> {\r\n const {\r\n AccountModel,\r\n JournalEntryModel,\r\n FiscalPeriodModel,\r\n country,\r\n orgField,\r\n retainedEarningsCode = country.retainedEarningsCode ?? '3660',\r\n logger = defaultLogger,\r\n } = opts;\r\n const { periodId, organizationId, closedBy } = params;\r\n requireOrgScope(orgField, organizationId);\r\n\r\n const { session, ownSession } = await acquireSession(\r\n AccountModel.db,\r\n params.session,\r\n logger,\r\n );\r\n let success = false;\r\n\r\n try {\r\n const queryOpts = session ? { session } : {};\r\n\r\n // 1. Fetch and validate the fiscal period (org-scoped)\r\n const periodQuery: Record<string, unknown> = { _id: periodId };\r\n if (orgField && organizationId) periodQuery[orgField] = organizationId;\r\n const period = await FiscalPeriodModel.findOne(periodQuery, null, queryOpts).lean() as Record<string, unknown> | null;\r\n if (!period) throw Errors.notFound('Fiscal period not found');\r\n if (period.closed) throw Errors.fiscal('Fiscal period is already closed');\r\n\r\n const startDate = period.startDate as Date;\r\n const endDate = period.endDate as Date;\r\n\r\n // 2. Find all income statement accounts for this org\r\n const accountQuery: Record<string, unknown> = { active: true };\r\n if (orgField && organizationId) accountQuery[orgField] = organizationId;\r\n const allAccounts = await AccountModel.find(accountQuery, null, queryOpts).lean() as Array<Record<string, unknown>>;\r\n\r\n const isAccounts: Array<{ id: unknown; code: string; isIncome: boolean }> = [];\r\n let retainedEarningsId: unknown = null;\r\n\r\n for (const acc of allAccounts) {\r\n const at = country.getAccountType(acc.accountTypeCode as string);\r\n if (!at) continue;\r\n\r\n if (acc.accountTypeCode === retainedEarningsCode) {\r\n retainedEarningsId = acc._id;\r\n }\r\n\r\n if (at.isGroup || at.isTotal) continue;\r\n if (at.category.startsWith('Income Statement')) {\r\n isAccounts.push({\r\n id: acc._id,\r\n code: at.code,\r\n isIncome: at.category === 'Income Statement-Income',\r\n });\r\n }\r\n }\r\n\r\n if (!retainedEarningsId) {\r\n throw Errors.fiscal(\r\n `Retained earnings account (code: ${retainedEarningsCode}) not found. ` +\r\n 'Create this account before closing the fiscal period.',\r\n );\r\n }\r\n\r\n // 3. Aggregate balances for all IS accounts in the period\r\n const baseMatch: Record<string, unknown> = {\r\n state: 'posted',\r\n date: { $gte: startDate, $lte: endDate },\r\n };\r\n if (orgField && organizationId) baseMatch[orgField] = organizationId;\r\n\r\n const isIds = isAccounts.map(a => a.id);\r\n const balances = isIds.length > 0\r\n ? await JournalEntryModel.aggregate([\r\n { $match: baseMatch },\r\n { $unwind: '$journalItems' },\r\n { $match: { 'journalItems.account': { $in: isIds } } },\r\n { $group: { _id: '$journalItems.account', d: { $sum: '$journalItems.debit' }, c: { $sum: '$journalItems.credit' } } },\r\n ], queryOpts) as Array<{ _id: unknown; d: number; c: number }>\r\n : [];\r\n\r\n // 4. Build closing journal entry items\r\n const closingItems: Array<{ account: unknown; debit: number; credit: number; label: string }> = [];\r\n let netIncome = 0;\r\n\r\n const balMap = new Map(balances.map(b => [String(b._id), b]));\r\n\r\n for (const acc of isAccounts) {\r\n const bal = balMap.get(String(acc.id));\r\n if (!bal) continue;\r\n\r\n const net = bal.c - bal.d;\r\n if (net === 0) continue;\r\n\r\n closingItems.push({\r\n account: acc.id,\r\n debit: net > 0 ? net : 0,\r\n credit: net < 0 ? Math.abs(net) : 0,\r\n label: `Close ${acc.code}`,\r\n });\r\n\r\n netIncome += net;\r\n }\r\n\r\n let closingEntryId: unknown = null;\r\n\r\n if (closingItems.length > 0) {\r\n closingItems.push({\r\n account: retainedEarningsId,\r\n debit: netIncome < 0 ? Math.abs(netIncome) : 0,\r\n credit: netIncome > 0 ? netIncome : 0,\r\n label: 'Transfer net income to retained earnings',\r\n });\r\n\r\n const totalDebit = closingItems.reduce((s, i) => s + i.debit, 0);\r\n const totalCredit = closingItems.reduce((s, i) => s + i.credit, 0);\r\n\r\n const closingEntryData: Record<string, unknown> = {\r\n journalType: 'YEAR_END',\r\n state: 'posted',\r\n date: endDate,\r\n label: `Fiscal year closing – ${(period.name as string) ?? 'Period'}`,\r\n journalItems: closingItems,\r\n totalDebit,\r\n totalCredit,\r\n };\r\n if (orgField && organizationId) closingEntryData[orgField] = organizationId;\r\n\r\n const [closingEntry] = await JournalEntryModel.create([closingEntryData], queryOpts);\r\n closingEntryId = closingEntry._id;\r\n }\r\n\r\n // 5. Mark the period as closed (org-scoped)\r\n const closedAt = new Date();\r\n await FiscalPeriodModel.findOneAndUpdate(\r\n periodQuery,\r\n { closed: true, closedAt, closedBy: closedBy ?? null, closingEntryId },\r\n queryOpts,\r\n );\r\n\r\n const result: FiscalCloseResult = {\r\n periodId,\r\n netIncome,\r\n closingEntryId,\r\n accountsClosed: closingItems.length - (closingItems.length > 0 ? 1 : 0),\r\n closedAt,\r\n };\r\n\r\n success = true;\r\n return result;\r\n } finally {\r\n await finalizeSession(session, ownSession, success);\r\n }\r\n}\r\n\r\n// ── Reopen ────────────────────────────────────────────────────────────────\r\n\r\nexport interface FiscalReopenResult {\r\n periodId: unknown;\r\n deletedEntryId: unknown | null;\r\n reopenedAt: Date;\r\n}\r\n\r\nexport async function reopenFiscalPeriod(\r\n opts: Pick<FiscalCloseOptions, 'JournalEntryModel' | 'FiscalPeriodModel'> & {\r\n orgField?: string;\r\n logger?: Logger;\r\n /** Any model on the same connection — used to start sessions */\r\n AccountModel?: Model<unknown>;\r\n },\r\n params: {\r\n periodId: unknown;\r\n organizationId?: unknown;\r\n reopenedBy?: string;\r\n session?: ClientSession;\r\n },\r\n): Promise<FiscalReopenResult> {\r\n const { JournalEntryModel, FiscalPeriodModel, orgField, logger = defaultLogger } = opts;\r\n const { periodId, organizationId, reopenedBy } = params;\r\n requireOrgScope(orgField, organizationId);\r\n\r\n // Use any available model's db connection for session creation\r\n const db = (opts.AccountModel ?? FiscalPeriodModel).db;\r\n const { session, ownSession } = await acquireSession(db, params.session, logger);\r\n let success = false;\r\n\r\n try {\r\n const queryOpts = session ? { session } : {};\r\n\r\n // 1. Fetch and validate the period (org-scoped)\r\n const periodQuery: Record<string, unknown> = { _id: periodId };\r\n if (orgField && organizationId) periodQuery[orgField] = organizationId;\r\n const period = await FiscalPeriodModel.findOne(periodQuery, null, queryOpts).lean() as Record<string, unknown> | null;\r\n if (!period) throw Errors.notFound('Fiscal period not found');\r\n if (!period.closed) throw Errors.fiscal('Fiscal period is not closed');\r\n\r\n // 2. Block if a later period is already closed (prevents cascade corruption)\r\n const laterQuery: Record<string, unknown> = {\r\n closed: true,\r\n startDate: { $gt: period.endDate },\r\n };\r\n if (orgField && organizationId) laterQuery[orgField] = organizationId;\r\n\r\n const laterClosed = await FiscalPeriodModel.findOne(laterQuery, null, queryOpts).lean();\r\n if (laterClosed) {\r\n throw Errors.fiscal(\r\n 'Cannot reopen: a later fiscal period is already closed. Reopen later periods first.',\r\n );\r\n }\r\n\r\n // 3. Delete the closing journal entry (if one was created)\r\n const closingEntryId = period.closingEntryId ?? null;\r\n if (closingEntryId) {\r\n await JournalEntryModel.findByIdAndDelete(closingEntryId, queryOpts);\r\n }\r\n\r\n // 4. Mark the period as reopened (org-scoped)\r\n const reopenedAt = new Date();\r\n await FiscalPeriodModel.findOneAndUpdate(\r\n periodQuery,\r\n {\r\n closed: false,\r\n closedAt: null,\r\n closedBy: null,\r\n closingEntryId: null,\r\n reopenedAt,\r\n reopenedBy: reopenedBy ?? null,\r\n },\r\n queryOpts,\r\n );\r\n\r\n const result: FiscalReopenResult = {\r\n periodId,\r\n deletedEntryId: closingEntryId,\r\n reopenedAt,\r\n };\r\n\r\n success = true;\r\n return result;\r\n } finally {\r\n await finalizeSession(session, ownSession, success);\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as DateRange } from "./core-
|
|
2
|
-
import { CountryPack } from "./
|
|
3
|
-
import { t as Logger } from "./logger-
|
|
1
|
+
import { c as DateRange } from "./core-8Xfnpn6g.mjs";
|
|
2
|
+
import { t as CountryPack } from "./index-ZnSiqHYV.mjs";
|
|
3
|
+
import { t as Logger } from "./logger-UbTdBb1x.mjs";
|
|
4
4
|
import { ClientSession, Model } from "mongoose";
|
|
5
5
|
|
|
6
6
|
//#region src/types/report.d.ts
|
|
@@ -267,4 +267,4 @@ declare function reopenFiscalPeriod(opts: Pick<FiscalCloseOptions, 'JournalEntry
|
|
|
267
267
|
}): Promise<FiscalReopenResult>;
|
|
268
268
|
//#endregion
|
|
269
269
|
export { ReportAccount as C, TaxReturnSummary as D, TaxReport as E, TrialBalanceReport as O, LedgerEntry as S, ReportGroup as T, CashFlowReport as _, reopenFiscalPeriod as a, GeneralLedgerReport as b, GeneralLedgerOptions as c, generateIncomeStatement as d, BalanceSheetOptions as f, BalanceSheetReport as g, generateTrialBalance as h, closeFiscalPeriod as i, TrialBalanceRow as k, generateGeneralLedger as l, TrialBalanceOptions as m, FiscalCloseResult as n, CashFlowOptions as o, generateBalanceSheet as p, FiscalReopenResult as r, generateCashFlow as s, FiscalCloseOptions as t, IncomeStatementOptions as u, CashFlowSection as v, ReportCategory as w, IncomeStatementReport as x, GeneralLedgerAccount as y };
|
|
270
|
-
//# sourceMappingURL=fiscal-close-
|
|
270
|
+
//# sourceMappingURL=fiscal-close-dNlzB37y.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiscal-close-
|
|
1
|
+
{"version":3,"file":"fiscal-close-dNlzB37y.d.mts","names":[],"sources":["../src/types/report.ts","../src/reports/trial-balance.ts","../src/reports/balance-sheet.ts","../src/reports/income-statement.ts","../src/reports/general-ledger.ts","../src/reports/cash-flow.ts","../src/reports/fiscal-close.ts"],"mappings":"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"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
import { i as getJournalTypeCodes, t as JOURNAL_CODES } from "./journals-
|
|
1
|
+
import { i as getJournalTypeCodes, t as JOURNAL_CODES } from "./journals-oH-FK3g8.mjs";
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
|
-
|
|
3
|
+
//#region src/schemas/currency-field.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the Mongoose currency field definition.
|
|
6
|
+
* Returns `null` if multi-currency is not enabled.
|
|
7
|
+
*/
|
|
8
|
+
function buildCurrencyField(config) {
|
|
9
|
+
if (!config.multiCurrency?.enabled) return null;
|
|
10
|
+
const allowed = config.multiCurrency.currencies;
|
|
11
|
+
return {
|
|
12
|
+
type: String,
|
|
13
|
+
default: null,
|
|
14
|
+
...allowed?.length ? { enum: [
|
|
15
|
+
null,
|
|
16
|
+
config.currency,
|
|
17
|
+
...allowed
|
|
18
|
+
] } : {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
4
22
|
//#region src/schemas/account.schema.ts
|
|
5
23
|
/**
|
|
6
24
|
* Account Schema Factory
|
|
@@ -40,9 +58,11 @@ function createAccountSchema(config, options = {}) {
|
|
|
40
58
|
isCashAccount: {
|
|
41
59
|
type: Boolean,
|
|
42
60
|
default: false
|
|
43
|
-
}
|
|
44
|
-
...extraFields
|
|
61
|
+
}
|
|
45
62
|
};
|
|
63
|
+
const currencyField = buildCurrencyField(config);
|
|
64
|
+
if (currencyField) fields.currency = currencyField;
|
|
65
|
+
Object.assign(fields, extraFields);
|
|
46
66
|
if (multiTenant) fields[multiTenant.orgField] = {
|
|
47
67
|
type: mongoose.Schema.Types.ObjectId,
|
|
48
68
|
ref: multiTenant.orgRef,
|
|
@@ -76,7 +96,6 @@ function createAccountSchema(config, options = {}) {
|
|
|
76
96
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
77
97
|
return schema;
|
|
78
98
|
}
|
|
79
|
-
|
|
80
99
|
//#endregion
|
|
81
100
|
//#region src/schemas/journal-entry.schema.ts
|
|
82
101
|
/**
|
|
@@ -101,6 +120,31 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
101
120
|
validator: (v) => Number.isInteger(v) && v >= 0,
|
|
102
121
|
message: "{PATH} must be a non-negative integer (cents), got {VALUE}"
|
|
103
122
|
};
|
|
123
|
+
const currencyItemFields = {};
|
|
124
|
+
const currencyField = buildCurrencyField(config);
|
|
125
|
+
if (currencyField) {
|
|
126
|
+
currencyItemFields.currency = currencyField;
|
|
127
|
+
currencyItemFields.exchangeRate = {
|
|
128
|
+
type: Number,
|
|
129
|
+
default: null,
|
|
130
|
+
validate: {
|
|
131
|
+
validator: (v) => v === null || v > 0,
|
|
132
|
+
message: "exchangeRate must be greater than zero when set, got {VALUE}"
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
currencyItemFields.originalDebit = {
|
|
136
|
+
type: Number,
|
|
137
|
+
default: null,
|
|
138
|
+
min: 0,
|
|
139
|
+
validate: amountValidator
|
|
140
|
+
};
|
|
141
|
+
currencyItemFields.originalCredit = {
|
|
142
|
+
type: Number,
|
|
143
|
+
default: null,
|
|
144
|
+
min: 0,
|
|
145
|
+
validate: amountValidator
|
|
146
|
+
};
|
|
147
|
+
}
|
|
104
148
|
const JournalItemSchema = new mongoose.Schema({
|
|
105
149
|
account: {
|
|
106
150
|
type: mongoose.Schema.Types.ObjectId,
|
|
@@ -125,6 +169,7 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
125
169
|
type: [TaxDetailSchema],
|
|
126
170
|
default: []
|
|
127
171
|
},
|
|
172
|
+
...currencyItemFields,
|
|
128
173
|
...extraItemFields
|
|
129
174
|
}, { _id: false });
|
|
130
175
|
const fields = {
|
|
@@ -378,7 +423,6 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
|
|
|
378
423
|
for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
|
|
379
424
|
return schema;
|
|
380
425
|
}
|
|
381
|
-
|
|
382
426
|
//#endregion
|
|
383
427
|
//#region src/schemas/fiscal-period.schema.ts
|
|
384
428
|
/**
|
|
@@ -471,7 +515,7 @@ function createFiscalPeriodSchema(config, options = {}) {
|
|
|
471
515
|
});
|
|
472
516
|
return schema;
|
|
473
517
|
}
|
|
474
|
-
|
|
475
518
|
//#endregion
|
|
476
519
|
export { createJournalEntrySchema as n, createAccountSchema as r, createFiscalPeriodSchema as t };
|
|
477
|
-
|
|
520
|
+
|
|
521
|
+
//# sourceMappingURL=fiscal-period.schema-BQ5wsAq3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fiscal-period.schema-BQ5wsAq3.mjs","names":[],"sources":["../src/schemas/currency-field.ts","../src/schemas/account.schema.ts","../src/schemas/journal-entry.schema.ts","../src/schemas/fiscal-period.schema.ts"],"sourcesContent":["/**\n * Shared Mongoose field definition for multi-currency support.\n * Used by both Account and JournalItem schemas when multiCurrency is enabled.\n */\n\nimport type { AccountingEngineConfig } from '../types/engine.js';\n\n/**\n * Build the Mongoose currency field definition.\n * Returns `null` if multi-currency is not enabled.\n */\nexport function buildCurrencyField(config: AccountingEngineConfig): Record<string, unknown> | null {\n if (!config.multiCurrency?.enabled) return null;\n\n const allowed = config.multiCurrency.currencies;\n return {\n type: String,\n default: null,\n ...(allowed?.length ? { enum: [null, config.currency, ...allowed] } : {}),\n };\n}\n","/**\r\n * Account Schema Factory\r\n *\r\n * Creates a Mongoose schema for Chart of Accounts that is:\r\n * - Multi-tenant aware (adds org field + compound indexes when configured)\r\n * - Validates accountTypeCode against the country pack\r\n * - Supports accountNumber (unique per org) and name (user-facing display)\r\n * - Lean: no cached balances — always computed from journal entries\r\n */\r\n\r\nimport mongoose from 'mongoose';\r\nimport type { AccountingEngineConfig, SchemaOptions } from '../types/engine.js';\r\nimport { buildCurrencyField } from './currency-field.js';\r\n\r\nexport function createAccountSchema(\r\n config: AccountingEngineConfig,\r\n options: SchemaOptions = {},\r\n) {\r\n const { multiTenant, country } = config;\r\n const { indexes = true, extraFields = {}, extraIndexes = [] } = options;\r\n\r\n // ── Base fields ──────────────────────────────────────────────────────────\r\n\r\n const fields: Record<string, unknown> = {\r\n accountTypeCode: {\r\n type: String,\r\n required: true,\r\n validate: {\r\n validator: (code: string) => country.isValidAccountType(code),\r\n message: (props: { value: string }) =>\r\n `\"${props.value}\" is not a valid account type code for ${country.name}.`,\r\n },\r\n },\r\n accountNumber: {\r\n type: String,\r\n required: true,\r\n trim: true,\r\n },\r\n name: {\r\n type: String,\r\n required: true,\r\n trim: true,\r\n },\r\n active: { type: Boolean, default: true },\r\n isCashAccount: { type: Boolean, default: false },\r\n };\r\n\r\n // ── Multi-currency account field (opt-in) ──────────────────────────────\r\n const currencyField = buildCurrencyField(config);\r\n if (currencyField) fields.currency = currencyField;\r\n\r\n Object.assign(fields, extraFields);\r\n\r\n // ── Multi-tenant field ───────────────────────────────────────────────────\r\n\r\n if (multiTenant) {\r\n fields[multiTenant.orgField] = {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: multiTenant.orgRef,\r\n required: true,\r\n };\r\n }\r\n\r\n // ── Schema ───────────────────────────────────────────────────────────────\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const schema = new mongoose.Schema(fields as any, { timestamps: true });\r\n\r\n // ── Pre-validate: auto-default accountNumber and name ──────────────────\r\n\r\n schema.pre('validate', function () {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const doc = this as any;\r\n if (!doc.accountNumber && doc.accountTypeCode) {\r\n doc.accountNumber = doc.accountTypeCode;\r\n }\r\n if (!doc.name && doc.accountTypeCode) {\r\n const at = country.getAccountType(doc.accountTypeCode);\r\n doc.name = at?.name ?? doc.accountTypeCode;\r\n }\r\n });\r\n\r\n // ── Indexes ──────────────────────────────────────────────────────────────\r\n\r\n if (indexes) {\r\n if (multiTenant) {\r\n const org = multiTenant.orgField;\r\n schema.index({ [org]: 1, active: 1 });\r\n // accountNumber is the unique identity per org\r\n schema.index({ [org]: 1, accountNumber: 1 }, { unique: true });\r\n // accountTypeCode is non-unique — multiple accounts can share a classification\r\n schema.index({ [org]: 1, accountTypeCode: 1 });\r\n } else {\r\n schema.index({ active: 1 });\r\n schema.index({ accountNumber: 1 }, { unique: true });\r\n schema.index({ accountTypeCode: 1 });\r\n }\r\n }\r\n\r\n for (const idx of extraIndexes) {\r\n schema.index(idx.fields, idx.options);\r\n }\r\n\r\n return schema;\r\n}\r\n","/**\r\n * Journal Entry Schema Factory\r\n *\r\n * Creates a Mongoose schema for double-entry journal entries.\r\n * - Multi-tenant aware\r\n * - Embedded journal items with account refs\r\n * - State machine: draft → posted, draft → archived\r\n * - Auto-generated reference numbers\r\n * - Double-entry validation on post\r\n * - Optimized indexes for high-load reporting\r\n */\r\n\r\nimport mongoose from 'mongoose';\r\nimport type { AccountingEngineConfig, JournalSchemaOptions } from '../types/engine.js';\r\nimport { getJournalTypeCodes, JOURNAL_CODES } from '../constants/journals.js';\r\nimport { buildCurrencyField } from './currency-field.js';\r\n\r\nexport function createJournalEntrySchema(\r\n config: AccountingEngineConfig,\r\n accountModelName: string,\r\n options: JournalSchemaOptions = {},\r\n) {\r\n const { multiTenant } = config;\r\n const {\r\n indexes = true,\r\n autoReference = true,\r\n textSearch = true,\r\n extraFields = {},\r\n extraIndexes = [],\r\n extraItemFields = {},\r\n } = options;\r\n\r\n // ── Tax Detail (audit reference only) ────────────────────────────────────\r\n\r\n const TaxDetailSchema = new mongoose.Schema(\r\n {\r\n taxCode: { type: String },\r\n taxName: { type: String },\r\n },\r\n { _id: false },\r\n );\r\n\r\n // ── Journal Item ─────────────────────────────────────────────────────────\r\n\r\n const amountValidator = {\r\n validator: (v: number) => Number.isInteger(v) && v >= 0,\r\n message: '{PATH} must be a non-negative integer (cents), got {VALUE}',\r\n };\r\n\r\n // ── Multi-currency item fields (opt-in) ──────────────────────────────────\r\n const currencyItemFields: Record<string, unknown> = {};\r\n const currencyField = buildCurrencyField(config);\r\n if (currencyField) {\r\n currencyItemFields.currency = currencyField;\r\n currencyItemFields.exchangeRate = {\r\n type: Number, default: null,\r\n validate: { validator: (v: number | null) => v === null || v > 0, message: 'exchangeRate must be greater than zero when set, got {VALUE}' },\r\n };\r\n currencyItemFields.originalDebit = { type: Number, default: null, min: 0, validate: amountValidator };\r\n currencyItemFields.originalCredit = { type: Number, default: null, min: 0, validate: amountValidator };\r\n }\r\n\r\n const JournalItemSchema = new mongoose.Schema(\r\n {\r\n account: {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: accountModelName,\r\n required: true,\r\n },\r\n label: { type: String },\r\n date: { type: Date },\r\n debit: { type: Number, default: 0, min: 0, validate: amountValidator },\r\n credit: { type: Number, default: 0, min: 0, validate: amountValidator },\r\n taxDetails: { type: [TaxDetailSchema], default: [] },\r\n ...currencyItemFields,\r\n ...extraItemFields,\r\n },\r\n { _id: false },\r\n );\r\n\r\n // ── Main fields ──────────────────────────────────────────────────────────\r\n\r\n const fields: Record<string, unknown> = {\r\n journalType: {\r\n type: String,\r\n enum: getJournalTypeCodes(),\r\n default: JOURNAL_CODES['MISC'],\r\n required: true,\r\n },\r\n referenceNumber: { type: String },\r\n label: { type: String },\r\n date: {\r\n type: Date,\r\n default: Date.now,\r\n required: function (this: { state?: string }) {\r\n return this.state !== 'draft';\r\n },\r\n },\r\n journalItems: { type: [JournalItemSchema], default: [] },\r\n totalDebit: { type: Number, required: true, min: 0, validate: { validator: Number.isInteger, message: 'totalDebit must be an integer (cents)' } },\r\n totalCredit: { type: Number, required: true, min: 0, validate: { validator: Number.isInteger, message: 'totalCredit must be an integer (cents)' } },\r\n state: {\r\n type: String,\r\n enum: ['draft', 'posted', 'archived'],\r\n default: 'draft',\r\n required: true,\r\n },\r\n stateChangedAt: { type: Date, default: Date.now },\r\n reversed: { type: Boolean, default: false },\r\n reversedBy: {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: 'JournalEntry',\r\n default: null,\r\n },\r\n reversalOf: {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: 'JournalEntry',\r\n default: null,\r\n },\r\n ...extraFields,\r\n };\r\n\r\n // ── Audit fields (conditional) ─────────────────────────────────────────\r\n\r\n if (config.audit?.trackActor) {\r\n fields.createdBy = { type: mongoose.Schema.Types.ObjectId, default: null };\r\n fields.postedBy = { type: mongoose.Schema.Types.ObjectId, default: null };\r\n fields.reversedByUser = { type: mongoose.Schema.Types.ObjectId, default: null };\r\n }\r\n\r\n // ── Approval fields (conditional) ──────────────────────────────────────\r\n\r\n if (config.strictness?.requireApproval || config.audit?.trackActor) {\r\n fields.approvedBy = { type: mongoose.Schema.Types.ObjectId, default: null };\r\n fields.approvedAt = { type: Date, default: null };\r\n }\r\n\r\n // ── Idempotency key (conditional) ──────────────────────────────────────\r\n\r\n if (config.idempotency) {\r\n fields.idempotencyKey = { type: String, default: null };\r\n }\r\n\r\n // ── Multi-tenant field ───────────────────────────────────────────────────\r\n\r\n if (multiTenant) {\r\n fields[multiTenant.orgField] = {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: multiTenant.orgRef,\r\n required: true,\r\n };\r\n }\r\n\r\n // ── Schema ───────────────────────────────────────────────────────────────\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const schema = new mongoose.Schema(fields as any, { timestamps: true });\r\n\r\n // ── Pre-validate: double-entry enforcement ───────────────────────────────\r\n\r\n schema.pre('validate', function () {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const doc = this as any;\r\n\r\n // Propagate entry date to items without a date\r\n for (const item of doc.journalItems) {\r\n if (!item.date) item.date = doc.date;\r\n }\r\n\r\n // Each line must be debit OR credit (not both), and posted entries cannot have zero-value lines\r\n for (let i = 0; i < doc.journalItems.length; i++) {\r\n const d = doc.journalItems[i].debit || 0;\r\n const c = doc.journalItems[i].credit || 0;\r\n if (d > 0 && c > 0) {\r\n throw new Error(\r\n `Journal item at index ${i}: cannot have both debit (${d}) and credit (${c}) greater than zero`,\r\n );\r\n }\r\n if (doc.state === 'posted' && d === 0 && c === 0) {\r\n throw new Error(\r\n `Journal item at index ${i}: posted entries cannot have zero-value lines (both debit and credit are 0)`,\r\n );\r\n }\r\n }\r\n\r\n // Calculate totals\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const totalDebit = doc.journalItems.reduce((s: number, i: any) => s + (i.debit || 0), 0);\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const totalCredit = doc.journalItems.reduce((s: number, i: any) => s + (i.credit || 0), 0);\r\n\r\n // Enforce minimum items and balance for posted entries\r\n if (doc.state === 'posted') {\r\n if (doc.journalItems.length < 2) {\r\n throw new Error('Posted entries must have at least 2 journal items');\r\n }\r\n if (totalDebit !== totalCredit) {\r\n throw new Error('Total debit must equal total credit for posted entries');\r\n }\r\n }\r\n\r\n doc.totalDebit = totalDebit;\r\n doc.totalCredit = totalCredit;\r\n });\r\n\r\n // ── Pre-save: auto-generate reference number ─────────────────────────────\r\n\r\n if (autoReference) {\r\n // Helper: compute next reference number from DB\r\n // Uses aggregation pipeline to extract & sort the numeric suffix,\r\n // avoiding lexicographic sort issues beyond sequence 9999.\r\n const generateReferenceNumber = async (doc: Record<string, unknown>, Model: mongoose.Model<unknown>, session: unknown) => {\r\n const jt = (doc.journalType as string) || 'MISC';\r\n const d = new Date(doc.date as string | number | Date);\r\n const year = d.getFullYear();\r\n const month = String(d.getMonth() + 1).padStart(2, '0');\r\n const prefix = `${jt}/${year}/${month}/`;\r\n\r\n // Build match filter\r\n const matchFilter: Record<string, unknown> = {\r\n referenceNumber: { $regex: `^${prefix.replace(/\\//g, '\\\\/')}` },\r\n };\r\n\r\n // Add org field to query for multi-tenant\r\n if (multiTenant) {\r\n matchFilter[multiTenant.orgField] = doc[multiTenant.orgField];\r\n }\r\n\r\n // Extract numeric suffix via $split and sort numerically\r\n const pipeline: mongoose.PipelineStage[] = [\r\n { $match: matchFilter },\r\n {\r\n $addFields: {\r\n _refSeq: {\r\n $toInt: {\r\n $arrayElemAt: [{ $split: ['$referenceNumber', '/'] }, -1],\r\n },\r\n },\r\n },\r\n },\r\n { $sort: { _refSeq: -1 as const } },\r\n { $limit: 1 },\r\n { $project: { _refSeq: 1 } },\r\n ];\r\n\r\n const results = await Model.aggregate(pipeline)\r\n .session(session as mongoose.mongo.ClientSession | null);\r\n\r\n let seq = 1;\r\n if (results.length > 0 && typeof results[0]._refSeq === 'number') {\r\n seq = results[0]._refSeq + 1;\r\n }\r\n\r\n return `${prefix}${String(seq).padStart(4, '0')}`;\r\n };\r\n\r\n schema.pre('save', async function () {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const doc = this as any;\r\n\r\n if (doc.isModified('journalType')) {\r\n doc.referenceNumber = undefined;\r\n }\r\n\r\n if (!doc.referenceNumber) {\r\n const session = doc.$session?.() ?? null;\r\n const Model = doc.constructor as mongoose.Model<unknown>;\r\n doc.referenceNumber = await generateReferenceNumber(doc, Model, session);\r\n }\r\n });\r\n\r\n // Retry on duplicate key error (race condition between concurrent inserts)\r\n const MAX_REF_RETRIES = 3;\r\n schema.post('save', async function (error: Error, doc: unknown, next: (err?: Error) => void) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const mongoError = error as any;\r\n // 11000 = MongoDB duplicate key error\r\n if (mongoError.code === 11000 && mongoError.keyPattern?.referenceNumber) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const entry = doc as any;\r\n const retryCount: number = entry.__refRetries ?? 0;\r\n if (retryCount >= MAX_REF_RETRIES) {\r\n next(new Error(\r\n `Failed to generate unique reference number after ${MAX_REF_RETRIES} retries. ` +\r\n 'Too many concurrent inserts for this period.',\r\n ));\r\n return;\r\n }\r\n entry.__refRetries = retryCount + 1;\r\n const session = entry.$session?.() ?? null;\r\n const Model = entry.constructor as mongoose.Model<unknown>;\r\n entry.referenceNumber = await generateReferenceNumber(entry, Model, session);\r\n try {\r\n await entry.save({ session });\r\n next();\r\n } catch (retryError) {\r\n next(retryError as Error);\r\n }\r\n } else {\r\n next(error);\r\n }\r\n });\r\n }\r\n\r\n // ── Indexes ──────────────────────────────────────────────────────────────\r\n\r\n if (indexes) {\r\n const org = multiTenant?.orgField;\r\n\r\n // Partial filter: unique constraint only applies to docs with a string\r\n // referenceNumber — allows multiple entries without a ref when autoReference is off.\r\n const refPartial = { partialFilterExpression: { referenceNumber: { $exists: true, $type: 'string' } } };\r\n\r\n if (org) {\r\n schema.index({ [org]: 1, referenceNumber: 1 }, { unique: true, ...refPartial });\r\n schema.index({ [org]: 1, state: 1, date: 1 });\r\n schema.index({ [org]: 1, date: -1 });\r\n schema.index({ [org]: 1, journalType: 1 });\r\n schema.index({ 'journalItems.account': 1, state: 1 });\r\n schema.index({ [org]: 1, 'journalItems.account': 1, date: 1, state: 1 });\r\n } else {\r\n schema.index({ referenceNumber: 1 }, { unique: true, ...refPartial });\r\n schema.index({ state: 1, date: 1 });\r\n schema.index({ date: -1 });\r\n schema.index({ journalType: 1 });\r\n schema.index({ 'journalItems.account': 1, state: 1 });\r\n }\r\n\r\n schema.index({ reversed: 1 });\r\n\r\n // Idempotency key: unique sparse index (only when enabled)\r\n if (config.idempotency) {\r\n const idempotencyIdx: Record<string, 1 | -1> = {};\r\n if (org) idempotencyIdx[org] = 1;\r\n idempotencyIdx.idempotencyKey = 1;\r\n schema.index(idempotencyIdx, {\r\n unique: true,\r\n partialFilterExpression: { idempotencyKey: { $exists: true, $ne: null } },\r\n });\r\n }\r\n }\r\n\r\n if (textSearch) {\r\n schema.index(\r\n { referenceNumber: 'text', label: 'text' },\r\n { weights: { referenceNumber: 10, label: 5 }, name: 'journal_text_idx' },\r\n );\r\n }\r\n\r\n for (const idx of extraIndexes) {\r\n schema.index(idx.fields, idx.options);\r\n }\r\n\r\n return schema;\r\n}\r\n","/**\r\n * Fiscal Period Schema Factory\r\n *\r\n * Creates a Mongoose schema for tracking fiscal periods (months, quarters, years).\r\n * Supports closing periods to lock entries.\r\n */\r\n\r\nimport mongoose from 'mongoose';\r\nimport type { AccountingEngineConfig, SchemaOptions } from '../types/engine.js';\r\n\r\nexport function createFiscalPeriodSchema(\r\n config: AccountingEngineConfig,\r\n options: SchemaOptions = {},\r\n) {\r\n const { multiTenant } = config;\r\n const { indexes = true, extraFields = {}, extraIndexes = [] } = options;\r\n\r\n const fields: Record<string, unknown> = {\r\n name: { type: String, required: true },\r\n startDate: { type: Date, required: true },\r\n endDate: { type: Date, required: true },\r\n closed: { type: Boolean, default: false },\r\n closedAt: { type: Date, default: null },\r\n closedBy: { type: String, default: null },\r\n closingEntryId: { type: mongoose.Schema.Types.ObjectId, default: null },\r\n reopenedAt: { type: Date, default: null },\r\n reopenedBy: { type: String, default: null },\r\n ...extraFields,\r\n };\r\n\r\n if (multiTenant) {\r\n fields[multiTenant.orgField] = {\r\n type: mongoose.Schema.Types.ObjectId,\r\n ref: multiTenant.orgRef,\r\n required: true,\r\n };\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const schema = new mongoose.Schema(fields as any, { timestamps: true });\r\n\r\n if (indexes) {\r\n if (multiTenant) {\r\n const org = multiTenant.orgField;\r\n schema.index({ [org]: 1, startDate: 1, endDate: 1 }, { unique: true });\r\n schema.index({ [org]: 1, closed: 1 });\r\n } else {\r\n schema.index({ startDate: 1, endDate: 1 }, { unique: true });\r\n schema.index({ closed: 1 });\r\n }\r\n }\r\n\r\n for (const idx of extraIndexes) {\r\n schema.index(idx.fields, idx.options);\r\n }\r\n\r\n // ── Overlap guard: prevent overlapping date ranges within a tenant ─────\r\n schema.pre('validate', async function () {\r\n const doc = this as mongoose.Document & { startDate: Date; endDate: Date; [key: string]: unknown };\r\n if (!doc.startDate || !doc.endDate) return;\r\n\r\n // A period overlaps if: existing.startDate < this.endDate AND existing.endDate > this.startDate\r\n const overlapQuery: Record<string, unknown> = {\r\n _id: { $ne: doc._id },\r\n startDate: { $lt: doc.endDate },\r\n endDate: { $gt: doc.startDate },\r\n };\r\n\r\n if (multiTenant) {\r\n overlapQuery[multiTenant.orgField] = doc[multiTenant.orgField];\r\n }\r\n\r\n const overlap = await doc.collection.findOne(overlapQuery);\r\n if (overlap) {\r\n const msg = `Fiscal period overlaps with existing period \"${overlap.name}\" (${new Date(overlap.startDate).toISOString().split('T')[0]} – ${new Date(overlap.endDate).toISOString().split('T')[0]}).`;\r\n doc.invalidate('startDate', msg, doc.startDate, 'overlap');\r\n }\r\n });\r\n\r\n return schema;\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as SchemaOptions, r as JournalSchemaOptions, t as AccountingEngineConfig } from "./engine-BzBMpWuy.mjs";
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/schemas/account.schema.d.ts
|
|
@@ -35,4 +35,4 @@ declare function createFiscalPeriodSchema(config: AccountingEngineConfig, option
|
|
|
35
35
|
}>;
|
|
36
36
|
//#endregion
|
|
37
37
|
export { createJournalEntrySchema as n, createAccountSchema as r, createFiscalPeriodSchema as t };
|
|
38
|
-
//# sourceMappingURL=fiscal-period.schema-
|
|
38
|
+
//# sourceMappingURL=fiscal-period.schema-BRdKAjrr.d.mts.map
|
package/dist/{fiscal-period.schema-DI2scngu.d.mts.map → fiscal-period.schema-BRdKAjrr.d.mts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiscal-period.schema-
|
|
1
|
+
{"version":3,"file":"fiscal-period.schema-BRdKAjrr.d.mts","names":[],"sources":["../src/schemas/account.schema.ts","../src/schemas/journal-entry.schema.ts","../src/schemas/fiscal-period.schema.ts"],"mappings":";;;;iBAcgB,mBAAA,CACd,MAAA,EAAQ,sBAAA,EACR,OAAA,GAAS,aAAA,GAAkB,QAAA,CAAA,MAAA,MAAA,QAAA,CAAA,KAAA;;;;;;;;;;;iBCCb,wBAAA,CACd,MAAA,EAAQ,sBAAA,EACR,gBAAA,UACA,OAAA,GAAS,oBAAA,GAAyB,QAAA,CAAA,MAAA,MAAA,QAAA,CAAA,KAAA;;;;;;;;;;;iBCVpB,wBAAA,CACd,MAAA,EAAQ,sBAAA,EACR,OAAA,GAAS,aAAA,GAAkB,QAAA,CAAA,MAAA,MAAA,QAAA,CAAA,KAAA"}
|