@classytic/ledger 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -115
- package/dist/constants/index.d.mts +1 -1
- package/dist/country/index.d.mts +2 -2
- package/dist/country/index.mjs +0 -3
- package/dist/exports/index.d.mts +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/{fx-realization.plugin-CgQFDGv2.mjs → fx-realization.plugin-DDVK-oYO.mjs} +44 -37
- package/dist/{tax-hooks-BnVenul5.d.mts → index-BSsvrf3m.d.mts} +18 -67
- package/dist/index-RNZsX0Yo.d.mts +130 -0
- package/dist/index.d.mts +133 -68
- package/dist/index.mjs +166 -142
- package/dist/{journals-C50E9mpo.d.mts → journals-Dd4A9TN3.d.mts} +1 -1
- package/dist/opening-balance-DPXmAIzN.mjs +60 -0
- package/dist/plugins/index.d.mts +2 -16
- package/dist/plugins/index.mjs +2 -57
- package/dist/reports/index.d.mts +1 -1
- package/dist/sync/index.d.mts +313 -0
- package/dist/sync/index.mjs +527 -0
- package/dist/sync-CnuVf441.d.mts +152 -0
- package/dist/{trial-balance-s92GEvRR.d.mts → trial-balance-DTj-c21f.d.mts} +3 -34
- package/docs/country-packs.md +71 -47
- package/docs/engine.md +3 -2
- package/docs/subledger-integration.md +29 -8
- package/docs/sync.md +330 -0
- package/package.json +26 -14
- package/dist/index-BthGypsI.d.mts +0 -228
- /package/dist/{core-BkGjuVZj.d.mts → core-MpgjCqK0.d.mts} +0 -0
- /package/dist/{exports-BP-0Ni5W.mjs → exports-B3whucXe.mjs} +0 -0
- /package/dist/{index-D1ZjgVxn.d.mts → index-bCEeSzdO.d.mts} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -3,11 +3,12 @@ import { Money, add, allocate, format, formatPlain, fromDecimal, multiply, parse
|
|
|
3
3
|
import { n as Errors, t as AccountingError } from "./errors-CSDQPNyt.mjs";
|
|
4
4
|
import { C as isVirtualTaxAccount, E as requireOrgScope, S as computeEndingBalance, T as generateAgedBalance, _ as buildItemFilters, a as finalizeSession, b as buildAccountTypeMap, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, i as acquireSession, l as buildRevaluationEntry, m as generateCashFlow, n as closeFiscalPeriod, o as defaultLogger, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, u as computeRevaluation, v as getDateRange, w as DEFAULT_BUCKETS, x as calculateTotal, y as getFiscalYearStart } from "./partner-ledger-D9H5hegI.mjs";
|
|
5
5
|
import { c as getNormalBalance, d as isValidCategory, l as isBalanceSheet, n as CATEGORY_KEYS, t as CATEGORIES, u as isIncomeStatement } from "./categories-BkKdv16V.mjs";
|
|
6
|
-
import { a as
|
|
6
|
+
import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "./fx-realization.plugin-DDVK-oYO.mjs";
|
|
7
|
+
import { t as buildOpeningBalanceEntry } from "./opening-balance-DPXmAIzN.mjs";
|
|
7
8
|
import { defineCountryPack } from "./country/index.mjs";
|
|
8
|
-
import { a as exportToCsv, i as quickbooksFieldMap, r as universalFieldMap, t as flattenJournalEntries } from "./exports-
|
|
9
|
+
import { a as exportToCsv, i as quickbooksFieldMap, r as universalFieldMap, t as flattenJournalEntries } from "./exports-B3whucXe.mjs";
|
|
10
|
+
import { QueryParser, Repository } from "@classytic/mongokit";
|
|
9
11
|
import mongoose, { Schema } from "mongoose";
|
|
10
|
-
import { Repository } from "@classytic/mongokit";
|
|
11
12
|
//#region src/schemas/currency-field.ts
|
|
12
13
|
/**
|
|
13
14
|
* Build the Mongoose currency field definition.
|
|
@@ -919,11 +920,10 @@ function createModels(connection, config) {
|
|
|
919
920
|
* onto an existing mongokit Repository.
|
|
920
921
|
*
|
|
921
922
|
* @param repository - A mongokit Repository instance (already created)
|
|
922
|
-
* @param AccountModel - The Mongoose model for accounts
|
|
923
923
|
* @param country - The CountryPack for account type lookups
|
|
924
924
|
* @param orgField - The multi-tenant field name (e.g. 'business')
|
|
925
925
|
*/
|
|
926
|
-
function wireAccountMethods(repository,
|
|
926
|
+
function wireAccountMethods(repository, country, orgField) {
|
|
927
927
|
repository.on("before:create", (ctx) => {
|
|
928
928
|
const code = ctx.data?.accountTypeCode;
|
|
929
929
|
if (code && !country.isPostingAccount(code)) throw Errors.validation(`Cannot create account with type "${code}" — it is a structural group or calculated total, not a posting account.`);
|
|
@@ -936,7 +936,10 @@ function wireAccountMethods(repository, AccountModel, country, orgField) {
|
|
|
936
936
|
const postingTypes = country.getPostingAccountTypes();
|
|
937
937
|
const filter = {};
|
|
938
938
|
if (orgField && orgId != null) filter[orgField] = orgId;
|
|
939
|
-
const existing = await
|
|
939
|
+
const existing = await repository.findAll(filter, {
|
|
940
|
+
select: { accountNumber: 1 },
|
|
941
|
+
lean: true
|
|
942
|
+
});
|
|
940
943
|
const existingNumbers = new Set(existing.map((a) => a.accountNumber));
|
|
941
944
|
const toCreate = postingTypes.filter((at) => !existingNumbers.has(at.code)).map((at) => {
|
|
942
945
|
const doc = {
|
|
@@ -953,7 +956,7 @@ function wireAccountMethods(repository, AccountModel, country, orgField) {
|
|
|
953
956
|
};
|
|
954
957
|
try {
|
|
955
958
|
return {
|
|
956
|
-
created: (await
|
|
959
|
+
created: (await repository.createMany(toCreate, {
|
|
957
960
|
session: options.session ?? void 0,
|
|
958
961
|
ordered: false
|
|
959
962
|
})).length,
|
|
@@ -1034,7 +1037,10 @@ function wireAccountMethods(repository, AccountModel, country, orgField) {
|
|
|
1034
1037
|
};
|
|
1035
1038
|
const existsFilter = { accountNumber: { $in: validAccounts.map((a) => a.accountNumber) } };
|
|
1036
1039
|
if (orgField && orgId != null) existsFilter[orgField] = orgId;
|
|
1037
|
-
const existingDocs = await
|
|
1040
|
+
const existingDocs = await repository.findAll(existsFilter, {
|
|
1041
|
+
select: { accountNumber: 1 },
|
|
1042
|
+
lean: true
|
|
1043
|
+
});
|
|
1038
1044
|
const existingNumbers = new Set(existingDocs.map((d) => d.accountNumber));
|
|
1039
1045
|
const toCreate = [];
|
|
1040
1046
|
for (const item of validAccounts) if (existingNumbers.has(item.accountNumber)) results.skipped.push({
|
|
@@ -1056,7 +1062,7 @@ function wireAccountMethods(repository, AccountModel, country, orgField) {
|
|
|
1056
1062
|
return doc;
|
|
1057
1063
|
});
|
|
1058
1064
|
try {
|
|
1059
|
-
const inserted = await
|
|
1065
|
+
const inserted = await repository.createMany(docs, { ordered: false });
|
|
1060
1066
|
results.created = toCreate.map((item, idx) => ({
|
|
1061
1067
|
accountTypeCode: item.accountTypeCode,
|
|
1062
1068
|
active: item.active,
|
|
@@ -1541,6 +1547,7 @@ function wireReconciliationMethods(repository, ReconciliationModel, JournalEntry
|
|
|
1541
1547
|
const create = repository.create.bind(repository);
|
|
1542
1548
|
const deleteById = repository.delete.bind(repository);
|
|
1543
1549
|
const repoInstance = repository;
|
|
1550
|
+
const emitHook = repoInstance.emitAsync.bind(repoInstance);
|
|
1544
1551
|
repository.match = async (input) => {
|
|
1545
1552
|
const { account, items, note, reconciledBy, organizationId, session = null } = input;
|
|
1546
1553
|
let { matchingNumber } = input;
|
|
@@ -1585,6 +1592,17 @@ function wireReconciliationMethods(repository, ReconciliationModel, JournalEntry
|
|
|
1585
1592
|
const isFullReconcile = difference === 0;
|
|
1586
1593
|
const sharedCurrency = currencies.size === 1 ? Array.from(currencies)[0] : null;
|
|
1587
1594
|
if (!matchingNumber) matchingNumber = await nextMatchingNumber(ReconciliationModel, orgField, organizationId, session);
|
|
1595
|
+
const hookCtx = {
|
|
1596
|
+
input,
|
|
1597
|
+
items: itemSnapshots,
|
|
1598
|
+
sharedCurrency,
|
|
1599
|
+
matchingNumber,
|
|
1600
|
+
debitTotal,
|
|
1601
|
+
creditTotal,
|
|
1602
|
+
isFullReconcile,
|
|
1603
|
+
session
|
|
1604
|
+
};
|
|
1605
|
+
await emitHook("before:match", hookCtx);
|
|
1588
1606
|
const bulkOps = itemSnapshots.map((snap) => ({ updateOne: {
|
|
1589
1607
|
filter: { _id: snap.entry },
|
|
1590
1608
|
update: { $set: { [`journalItems.${snap.itemIndex}.matchingNumber`]: matchingNumber } }
|
|
@@ -1612,12 +1630,9 @@ function wireReconciliationMethods(repository, ReconciliationModel, JournalEntry
|
|
|
1612
1630
|
};
|
|
1613
1631
|
if (orgField && organizationId != null) reconciliationData[orgField] = organizationId;
|
|
1614
1632
|
const record = await create(reconciliationData);
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
reconciliation: record
|
|
1618
|
-
items: itemSnapshots,
|
|
1619
|
-
sharedCurrency,
|
|
1620
|
-
session
|
|
1633
|
+
await emitHook("after:match", {
|
|
1634
|
+
...hookCtx,
|
|
1635
|
+
reconciliation: record
|
|
1621
1636
|
});
|
|
1622
1637
|
return record;
|
|
1623
1638
|
};
|
|
@@ -1628,13 +1643,23 @@ function wireReconciliationMethods(repository, ReconciliationModel, JournalEntry
|
|
|
1628
1643
|
if (orgField && organizationId != null) query[orgField] = organizationId;
|
|
1629
1644
|
const existing = await ReconciliationModel.findOne(query).session(session).lean();
|
|
1630
1645
|
if (!existing) throw Errors.notFound(`Reconciliation ${matchingNumber} not found`);
|
|
1631
|
-
const
|
|
1646
|
+
const items = existing.items ?? [];
|
|
1647
|
+
const unmatchCtx = {
|
|
1648
|
+
matchingNumber,
|
|
1649
|
+
reconciliation: existing,
|
|
1650
|
+
items,
|
|
1651
|
+
organizationId,
|
|
1652
|
+
session
|
|
1653
|
+
};
|
|
1654
|
+
await emitHook("before:unmatch", unmatchCtx);
|
|
1655
|
+
const bulkOps = items.map((it) => ({ updateOne: {
|
|
1632
1656
|
filter: { _id: it.entry },
|
|
1633
1657
|
update: { $set: { [`journalItems.${it.itemIndex}.matchingNumber`]: null } }
|
|
1634
1658
|
} }));
|
|
1635
1659
|
if (bulkOps.length > 0) await JournalEntryModel.bulkWrite(bulkOps, { session: session ?? void 0 });
|
|
1636
1660
|
const result = await deleteById(String(existing._id));
|
|
1637
1661
|
if (!result.success) throw Errors.notFound("Failed to delete reconciliation record");
|
|
1662
|
+
await emitHook("after:unmatch", unmatchCtx);
|
|
1638
1663
|
return result;
|
|
1639
1664
|
};
|
|
1640
1665
|
repository.getOpenItems = async (params) => {
|
|
@@ -1727,7 +1752,7 @@ function createRepositories(models, config, plugins = {}, pagination = {}) {
|
|
|
1727
1752
|
const fpPagination = pagination.fiscalPeriod ?? {};
|
|
1728
1753
|
const budgetPagination = pagination.budget ?? {};
|
|
1729
1754
|
const reconPagination = pagination.reconciliation ?? {};
|
|
1730
|
-
const accounts = wireAccountMethods(new Repository(models.Account, plugins.account ?? [], accountPagination),
|
|
1755
|
+
const accounts = wireAccountMethods(new Repository(models.Account, plugins.account ?? [], accountPagination), country, orgField);
|
|
1731
1756
|
const jePlugins = [
|
|
1732
1757
|
...plugins.journalEntry ?? [],
|
|
1733
1758
|
doubleEntryPlugin({
|
|
@@ -2055,10 +2080,6 @@ function buildIntrospectAPI({ models, country, config }) {
|
|
|
2055
2080
|
return Object.freeze([...builtIn, ...custom]);
|
|
2056
2081
|
};
|
|
2057
2082
|
const reports = () => REPORT_CATALOG;
|
|
2058
|
-
const taxCodes = (region) => {
|
|
2059
|
-
if (region) return Object.freeze(country.getTaxCodesForRegion(region));
|
|
2060
|
-
return Object.freeze(Object.values(country.taxCodes));
|
|
2061
|
-
};
|
|
2062
2083
|
const fiscalPeriods = async (organizationId, session = null) => {
|
|
2063
2084
|
const filter = {};
|
|
2064
2085
|
if (orgField && organizationId != null) filter[orgField] = organizationId;
|
|
@@ -2076,32 +2097,21 @@ function buildIntrospectAPI({ models, country, config }) {
|
|
|
2076
2097
|
accounts: await accounts(organizationId),
|
|
2077
2098
|
journalTypes: journalTypes(),
|
|
2078
2099
|
reports: reports(),
|
|
2079
|
-
taxCodes: taxCodes(),
|
|
2080
2100
|
fiscalPeriods: await fiscalPeriods(organizationId)
|
|
2081
2101
|
});
|
|
2082
2102
|
return {
|
|
2083
2103
|
accounts,
|
|
2084
2104
|
journalTypes,
|
|
2085
2105
|
reports,
|
|
2086
|
-
taxCodes,
|
|
2087
2106
|
fiscalPeriods,
|
|
2088
2107
|
catalog
|
|
2089
2108
|
};
|
|
2090
2109
|
}
|
|
2091
2110
|
//#endregion
|
|
2092
2111
|
//#region src/semantic/record.ts
|
|
2093
|
-
function buildRecordAPI({ models, repositories,
|
|
2112
|
+
function buildRecordAPI({ models, repositories, config }) {
|
|
2094
2113
|
const AccountModel = models.Account;
|
|
2095
2114
|
const orgField = config.multiTenant?.orgField;
|
|
2096
|
-
const lookupTaxRate = (taxCode) => {
|
|
2097
|
-
const tc = country.taxCodes[taxCode];
|
|
2098
|
-
if (!tc) throw Errors.notFound(`Tax code '${taxCode}' not found in country pack.`, [{
|
|
2099
|
-
path: "tax.code",
|
|
2100
|
-
issue: "unknown tax code",
|
|
2101
|
-
value: taxCode
|
|
2102
|
-
}]);
|
|
2103
|
-
return tc.rate;
|
|
2104
|
-
};
|
|
2105
2115
|
const resolveAccounts = async (organizationId, codes, path, session) => {
|
|
2106
2116
|
const unique = Array.from(new Set(codes));
|
|
2107
2117
|
const filter = { accountTypeCode: { $in: unique } };
|
|
@@ -2155,26 +2165,8 @@ function buildRecordAPI({ models, repositories, country, config }) {
|
|
|
2155
2165
|
});
|
|
2156
2166
|
const sale = async (organizationId, input, options) => {
|
|
2157
2167
|
validateAmount(input.amount, "amount");
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
if (input.tax) {
|
|
2161
|
-
const rate = lookupTaxRate(input.tax.code);
|
|
2162
|
-
if (input.tax.inclusive) {
|
|
2163
|
-
const split = splitTaxInclusive(input.amount, rate);
|
|
2164
|
-
baseAmount = split.base;
|
|
2165
|
-
taxAmount = split.tax;
|
|
2166
|
-
} else {
|
|
2167
|
-
const split = splitTaxExclusive(input.amount, rate);
|
|
2168
|
-
baseAmount = split.base;
|
|
2169
|
-
taxAmount = split.tax;
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
const totalCharge = baseAmount + taxAmount;
|
|
2173
|
-
const codes = [input.receivableAccount, input.revenueAccount];
|
|
2174
|
-
if (input.tax) codes.push(input.tax.account);
|
|
2175
|
-
const acctMap = await resolveAccounts(organizationId, codes, "receivableAccount", options?.session ?? null);
|
|
2176
|
-
const items = [buildItem(acctMap.get(input.receivableAccount), totalCharge, 0, input.label, input.dimensions), buildItem(acctMap.get(input.revenueAccount), 0, baseAmount, input.label, input.dimensions)];
|
|
2177
|
-
if (input.tax) items.push(buildItem(acctMap.get(input.tax.account), 0, taxAmount, `${input.label ?? "Sale"} — ${input.tax.code}`, input.dimensions));
|
|
2168
|
+
const acctMap = await resolveAccounts(organizationId, [input.receivableAccount, input.revenueAccount], "receivableAccount", options?.session ?? null);
|
|
2169
|
+
const items = [buildItem(acctMap.get(input.receivableAccount), input.amount, 0, input.label, input.dimensions), buildItem(acctMap.get(input.revenueAccount), 0, input.amount, input.label, input.dimensions)];
|
|
2178
2170
|
return postEntry(organizationId, {
|
|
2179
2171
|
journalType: input.journalType ?? "SALES",
|
|
2180
2172
|
date: input.date,
|
|
@@ -2185,27 +2177,8 @@ function buildRecordAPI({ models, repositories, country, config }) {
|
|
|
2185
2177
|
};
|
|
2186
2178
|
const expense = async (organizationId, input, options) => {
|
|
2187
2179
|
validateAmount(input.amount, "amount");
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
if (input.tax) {
|
|
2191
|
-
const rate = lookupTaxRate(input.tax.code);
|
|
2192
|
-
if (input.tax.inclusive) {
|
|
2193
|
-
const split = splitTaxInclusive(input.amount, rate);
|
|
2194
|
-
baseAmount = split.base;
|
|
2195
|
-
taxAmount = split.tax;
|
|
2196
|
-
} else {
|
|
2197
|
-
const split = splitTaxExclusive(input.amount, rate);
|
|
2198
|
-
baseAmount = split.base;
|
|
2199
|
-
taxAmount = split.tax;
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
const totalPaid = baseAmount + taxAmount;
|
|
2203
|
-
const codes = [input.expenseAccount, input.paidFromAccount];
|
|
2204
|
-
if (input.tax) codes.push(input.tax.account);
|
|
2205
|
-
const acctMap = await resolveAccounts(organizationId, codes, "expenseAccount", options?.session ?? null);
|
|
2206
|
-
const items = [buildItem(acctMap.get(input.expenseAccount), baseAmount, 0, input.label, input.dimensions)];
|
|
2207
|
-
if (input.tax) items.push(buildItem(acctMap.get(input.tax.account), taxAmount, 0, `${input.label ?? "Expense"} — ${input.tax.code} ITC`, input.dimensions));
|
|
2208
|
-
items.push(buildItem(acctMap.get(input.paidFromAccount), 0, totalPaid, input.label, input.dimensions));
|
|
2180
|
+
const acctMap = await resolveAccounts(organizationId, [input.expenseAccount, input.paidFromAccount], "expenseAccount", options?.session ?? null);
|
|
2181
|
+
const items = [buildItem(acctMap.get(input.expenseAccount), input.amount, 0, input.label, input.dimensions), buildItem(acctMap.get(input.paidFromAccount), 0, input.amount, input.label, input.dimensions)];
|
|
2209
2182
|
return postEntry(organizationId, {
|
|
2210
2183
|
journalType: input.journalType ?? "PURCHASES",
|
|
2211
2184
|
date: input.date,
|
|
@@ -2307,16 +2280,80 @@ function buildRecordAPI({ models, repositories, country, config }) {
|
|
|
2307
2280
|
journalItems: items
|
|
2308
2281
|
}, options);
|
|
2309
2282
|
};
|
|
2283
|
+
const openingBalance = async (organizationId, input, options) => {
|
|
2284
|
+
if (!input.balances || input.balances.length === 0) throw Errors.validation("Opening balance requires at least one account balance.", [{
|
|
2285
|
+
path: "balances",
|
|
2286
|
+
issue: "must contain at least 1 entry",
|
|
2287
|
+
value: 0
|
|
2288
|
+
}]);
|
|
2289
|
+
const equityCode = input.equityAccount ?? config.country?.retainedEarningsAccountCode;
|
|
2290
|
+
if (!equityCode) throw Errors.validation("Equity contra account code is required. Pass equityAccount or configure retainedEarningsAccountCode in the country pack.", [{
|
|
2291
|
+
path: "equityAccount",
|
|
2292
|
+
issue: "required",
|
|
2293
|
+
value: void 0
|
|
2294
|
+
}]);
|
|
2295
|
+
const result = buildOpeningBalanceEntry({
|
|
2296
|
+
cutoverDate: input.cutoverDate,
|
|
2297
|
+
balances: input.balances.map((b) => ({
|
|
2298
|
+
accountCode: b.account,
|
|
2299
|
+
balance: b.balance
|
|
2300
|
+
})),
|
|
2301
|
+
equityAccountCode: equityCode,
|
|
2302
|
+
label: input.label
|
|
2303
|
+
});
|
|
2304
|
+
const acctMap = await resolveAccounts(organizationId, result.entry.journalItems.map((item) => item.account), "balances", options?.session ?? null);
|
|
2305
|
+
const items = result.entry.journalItems.map((item) => buildItem(acctMap.get(item.account), item.debit, item.credit, item.label));
|
|
2306
|
+
return postEntry(organizationId, {
|
|
2307
|
+
journalType: result.entry.journalType ?? "GENERAL",
|
|
2308
|
+
date: result.entry.date,
|
|
2309
|
+
label: result.entry.label,
|
|
2310
|
+
journalItems: items,
|
|
2311
|
+
...result.entry.extra
|
|
2312
|
+
}, options);
|
|
2313
|
+
};
|
|
2310
2314
|
return {
|
|
2311
2315
|
sale,
|
|
2312
2316
|
expense,
|
|
2313
2317
|
transfer,
|
|
2314
2318
|
payment,
|
|
2315
|
-
adjustment
|
|
2319
|
+
adjustment,
|
|
2320
|
+
openingBalance
|
|
2316
2321
|
};
|
|
2317
2322
|
}
|
|
2318
2323
|
//#endregion
|
|
2319
2324
|
//#region src/engine.ts
|
|
2325
|
+
/**
|
|
2326
|
+
* AccountingEngine — The main entry point for @classytic/ledger.
|
|
2327
|
+
*
|
|
2328
|
+
* The engine owns all models, repositories, and reports. Matches the
|
|
2329
|
+
* @classytic/flow and @classytic/promo pattern: pass a mongoose connection
|
|
2330
|
+
* in config, and everything is auto-wired.
|
|
2331
|
+
*
|
|
2332
|
+
* @example
|
|
2333
|
+
* ```typescript
|
|
2334
|
+
* import mongoose from 'mongoose';
|
|
2335
|
+
* import { createAccountingEngine } from '@classytic/ledger';
|
|
2336
|
+
* import { canadaPack } from '@classytic/ledger-ca';
|
|
2337
|
+
*
|
|
2338
|
+
* const engine = createAccountingEngine({
|
|
2339
|
+
* mongoose: mongoose.connection,
|
|
2340
|
+
* country: canadaPack,
|
|
2341
|
+
* currency: 'CAD',
|
|
2342
|
+
* multiTenant: { orgField: 'organizationId', orgRef: 'Organization' },
|
|
2343
|
+
* });
|
|
2344
|
+
*
|
|
2345
|
+
* // Models — auto-created Mongoose models
|
|
2346
|
+
* engine.models.Account
|
|
2347
|
+
* engine.models.JournalEntry
|
|
2348
|
+
*
|
|
2349
|
+
* // Repositories — plugins + domain methods pre-wired
|
|
2350
|
+
* await engine.repositories.accounts.seedAccounts(orgId);
|
|
2351
|
+
* await engine.repositories.journalEntries.post(entryId, orgId);
|
|
2352
|
+
*
|
|
2353
|
+
* // Reports — bound to owned models
|
|
2354
|
+
* const bs = await engine.reports.balanceSheet({ organizationId: orgId, dateOption: 'year', dateValue: 2025 });
|
|
2355
|
+
* ```
|
|
2356
|
+
*/
|
|
2320
2357
|
var AccountingEngine = class {
|
|
2321
2358
|
config;
|
|
2322
2359
|
country;
|
|
@@ -2337,7 +2374,6 @@ var AccountingEngine = class {
|
|
|
2337
2374
|
this.record = buildRecordAPI({
|
|
2338
2375
|
models: this.models,
|
|
2339
2376
|
repositories: this.repositories,
|
|
2340
|
-
country: this.country,
|
|
2341
2377
|
config: this.config
|
|
2342
2378
|
});
|
|
2343
2379
|
this.introspect = buildIntrospectAPI({
|
|
@@ -2366,9 +2402,59 @@ var AccountingEngine = class {
|
|
|
2366
2402
|
getAccountType(code) {
|
|
2367
2403
|
return this.country.getAccountType(code);
|
|
2368
2404
|
}
|
|
2369
|
-
/**
|
|
2370
|
-
|
|
2371
|
-
|
|
2405
|
+
/**
|
|
2406
|
+
* Create a pre-configured QueryParser for URL-driven queries against
|
|
2407
|
+
* ledger repositories. Returns a mongokit QueryParser with the correct
|
|
2408
|
+
* schema and pagination limits for the specified model.
|
|
2409
|
+
*
|
|
2410
|
+
* @param model - Which ledger model to parse queries for
|
|
2411
|
+
* @param overrides - Additional QueryParserOptions to merge
|
|
2412
|
+
*
|
|
2413
|
+
* @example
|
|
2414
|
+
* ```typescript
|
|
2415
|
+
* const parser = engine.createQueryParser('journalEntry');
|
|
2416
|
+
* const parsed = parser.parse(req.query);
|
|
2417
|
+
* const result = await engine.repositories.journalEntries.getAll({
|
|
2418
|
+
* ...parsed,
|
|
2419
|
+
* filters: { ...parsed.filters, organizationId },
|
|
2420
|
+
* });
|
|
2421
|
+
* ```
|
|
2422
|
+
*/
|
|
2423
|
+
createQueryParser(model, overrides) {
|
|
2424
|
+
const paginationConfig = this.config.pagination ?? {};
|
|
2425
|
+
const entry = {
|
|
2426
|
+
account: {
|
|
2427
|
+
model: this.models.Account,
|
|
2428
|
+
pagination: paginationConfig.account
|
|
2429
|
+
},
|
|
2430
|
+
journalEntry: {
|
|
2431
|
+
model: this.models.JournalEntry,
|
|
2432
|
+
pagination: paginationConfig.journalEntry
|
|
2433
|
+
},
|
|
2434
|
+
fiscalPeriod: {
|
|
2435
|
+
model: this.models.FiscalPeriod,
|
|
2436
|
+
pagination: paginationConfig.fiscalPeriod
|
|
2437
|
+
},
|
|
2438
|
+
budget: {
|
|
2439
|
+
model: this.models.Budget,
|
|
2440
|
+
pagination: paginationConfig.budget
|
|
2441
|
+
},
|
|
2442
|
+
reconciliation: {
|
|
2443
|
+
model: this.models.Reconciliation,
|
|
2444
|
+
pagination: paginationConfig.reconciliation
|
|
2445
|
+
},
|
|
2446
|
+
journal: {
|
|
2447
|
+
model: this.models.Journal,
|
|
2448
|
+
pagination: paginationConfig.journal
|
|
2449
|
+
}
|
|
2450
|
+
}[model];
|
|
2451
|
+
if (!entry) throw new Error(`createQueryParser: unknown model "${model}"`);
|
|
2452
|
+
return new QueryParser({
|
|
2453
|
+
schema: entry.model.schema,
|
|
2454
|
+
maxLimit: entry.pagination?.maxLimit ?? 100,
|
|
2455
|
+
searchMode: "regex",
|
|
2456
|
+
...overrides
|
|
2457
|
+
});
|
|
2372
2458
|
}
|
|
2373
2459
|
_buildReports() {
|
|
2374
2460
|
const AccountModel = this.models.Account;
|
|
@@ -2450,68 +2536,6 @@ function createAccountingEngine(config) {
|
|
|
2450
2536
|
return new AccountingEngine(config);
|
|
2451
2537
|
}
|
|
2452
2538
|
//#endregion
|
|
2453
|
-
//#region src/utils/repartition-tax.ts
|
|
2454
|
-
/**
|
|
2455
|
-
* Default role resolver used when the country pack doesn't override.
|
|
2456
|
-
* Walks `taxCodes` to find a code whose `direction` matches the role.
|
|
2457
|
-
*/
|
|
2458
|
-
function defaultResolveRoleCode(role, _tax, country) {
|
|
2459
|
-
const direction = role === "collected" ? "collected" : role === "recoverable" ? "recoverable" : null;
|
|
2460
|
-
if (!direction) return void 0;
|
|
2461
|
-
for (const tc of Object.values(country.taxCodes)) if (tc.direction === direction) return tc.code;
|
|
2462
|
-
}
|
|
2463
|
-
/**
|
|
2464
|
-
* Build a `TaxLineGenerator` that expands each hit `taxCode` into one
|
|
2465
|
-
* journal item per repartition line. Taxes without `repartition` fall
|
|
2466
|
-
* back to a single-line generator using the direction-implied account.
|
|
2467
|
-
*/
|
|
2468
|
-
function createRepartitionTaxGenerator(options) {
|
|
2469
|
-
const { country, resolveAccount, documentType = "invoice" } = options;
|
|
2470
|
-
return { generateTaxLines(input) {
|
|
2471
|
-
const code = input.taxCode;
|
|
2472
|
-
if (!code) return [];
|
|
2473
|
-
const tax = country.taxCodes[code];
|
|
2474
|
-
if (!tax) return [];
|
|
2475
|
-
const baseTax = Math.round(input.amount * tax.rate / 100);
|
|
2476
|
-
if (baseTax === 0) return [];
|
|
2477
|
-
const lines = tax.repartition && tax.repartition.length > 0 ? tax.repartition.filter((line) => !line.documentTypes || line.documentTypes.includes(documentType)) : [{
|
|
2478
|
-
factor: 1,
|
|
2479
|
-
accountRole: tax.direction === "recoverable" ? "recoverable" : "collected",
|
|
2480
|
-
gridCode: tax.reportLines?.[0]
|
|
2481
|
-
}];
|
|
2482
|
-
const generated = [];
|
|
2483
|
-
for (const rep of lines) {
|
|
2484
|
-
const signed = Math.round(baseTax * rep.factor);
|
|
2485
|
-
if (signed === 0) continue;
|
|
2486
|
-
const account = resolveAccount(rep.accountRole, tax, input);
|
|
2487
|
-
if (!account) throw new Error(`repartitionTax: cannot resolve account for role "${rep.accountRole}" on tax "${tax.code}"`);
|
|
2488
|
-
const absAmount = Math.abs(signed);
|
|
2489
|
-
let onCredit = rep.accountRole === "collected" || rep.accountRole === "transition";
|
|
2490
|
-
if (signed < 0) onCredit = !onCredit;
|
|
2491
|
-
generated.push({
|
|
2492
|
-
account,
|
|
2493
|
-
debit: onCredit ? 0 : absAmount,
|
|
2494
|
-
credit: onCredit ? absAmount : 0,
|
|
2495
|
-
label: rep.label ?? `${tax.name} ${rep.accountRole}`,
|
|
2496
|
-
taxDetails: [{
|
|
2497
|
-
taxCode: tax.code,
|
|
2498
|
-
taxName: tax.name,
|
|
2499
|
-
...rep.gridCode != null ? { gridCode: String(rep.gridCode) } : {}
|
|
2500
|
-
}]
|
|
2501
|
-
});
|
|
2502
|
-
}
|
|
2503
|
-
return generated;
|
|
2504
|
-
} };
|
|
2505
|
-
}
|
|
2506
|
-
/**
|
|
2507
|
-
* Helper for packs that want the standard "role → account-type code"
|
|
2508
|
-
* mapping without writing their own resolver. Returns the function you
|
|
2509
|
-
* stuff into `CountryPackInput.resolveTaxRepartitionAccountCode`.
|
|
2510
|
-
*/
|
|
2511
|
-
function defaultResolveTaxRepartitionAccountCode(country) {
|
|
2512
|
-
return (role, tax) => defaultResolveRoleCode(role, tax, country);
|
|
2513
|
-
}
|
|
2514
|
-
//#endregion
|
|
2515
2539
|
//#region src/utils/dimensions.ts
|
|
2516
2540
|
/**
|
|
2517
2541
|
* Analytic Dimensions — Helpers for defining analytic dimensions
|
|
@@ -2571,4 +2595,4 @@ function buildDimensionIndexes(dimensions, orgField) {
|
|
|
2571
2595
|
});
|
|
2572
2596
|
}
|
|
2573
2597
|
//#endregion
|
|
2574
|
-
export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, DEFAULT_BUCKETS, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountingEngine, createLockPlugin, createModels,
|
|
2598
|
+
export { AccountingEngine, AccountingError, CATEGORIES, CATEGORY_KEYS, CURRENCIES, DEFAULT_BUCKETS, Errors, JOURNAL_CODES, JOURNAL_TYPES, Money, acquireSession, add, allocate, buildAccountTypeMap, buildDimensionFields, buildDimensionIndexes, buildItemFilters, buildRevaluationEntry, calculateTotal, closeFiscalPeriod, computeEndingBalance, computeRevaluation, createAccountingEngine, createLockPlugin, createModels, createRepositories, creditLimitPlugin, dailyLockPlugin, defaultLogger, defineCountryPack, doubleEntryPlugin, exportToCsv, finalizeSession, fiscalLockPlugin, flattenJournalEntries, format, formatPlain, fromDecimal, fxRealizationPlugin, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, getCurrency, getCustomJournalTypes, getDateRange, getFiscalYearStart, getJournalType, getJournalTypeCodes, getMinorUnit, getNormalBalance, idempotencyPlugin, isBalanceSheet, isIncomeStatement, isValidCategory, isValidCurrency, isValidJournalType, isVirtualTaxAccount, multiply, parseCents, percentage, periodResolver, quickbooksFieldMap, registerJournalType, reopenFiscalPeriod, resolveModelNames, splitTaxExclusive, splitTaxInclusive, subtract, toDecimal, universalFieldMap, watermarkResolver };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as JournalType, f as MainType, h as StatementType, i as CategoryKey, o as Currency, r as Category } from "./core-
|
|
1
|
+
import { d as JournalType, f as MainType, h as StatementType, i as CategoryKey, o as Currency, r as Category } from "./core-MpgjCqK0.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/constants/categories.d.ts
|
|
4
4
|
/** All valid categories */
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/sync/builders/opening-balance.ts
|
|
2
|
+
function buildOpeningBalanceEntry(input) {
|
|
3
|
+
const { cutoverDate, balances, equityAccountCode } = input;
|
|
4
|
+
const dateStr = cutoverDate.toISOString().split("T")[0];
|
|
5
|
+
const label = input.label ?? `Opening Balance — Cutover ${dateStr}`;
|
|
6
|
+
const items = [];
|
|
7
|
+
let totalDebit = 0;
|
|
8
|
+
let totalCredit = 0;
|
|
9
|
+
for (const { accountCode, balance } of balances) {
|
|
10
|
+
if (balance === 0) continue;
|
|
11
|
+
if (balance > 0) {
|
|
12
|
+
items.push({
|
|
13
|
+
account: accountCode,
|
|
14
|
+
debit: balance,
|
|
15
|
+
credit: 0,
|
|
16
|
+
label: "Opening balance"
|
|
17
|
+
});
|
|
18
|
+
totalDebit += balance;
|
|
19
|
+
} else {
|
|
20
|
+
const absBalance = Math.abs(balance);
|
|
21
|
+
items.push({
|
|
22
|
+
account: accountCode,
|
|
23
|
+
debit: 0,
|
|
24
|
+
credit: absBalance,
|
|
25
|
+
label: "Opening balance"
|
|
26
|
+
});
|
|
27
|
+
totalCredit += absBalance;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const residual = totalDebit - totalCredit;
|
|
31
|
+
const lineCount = items.length;
|
|
32
|
+
if (residual > 0) items.push({
|
|
33
|
+
account: equityAccountCode,
|
|
34
|
+
debit: 0,
|
|
35
|
+
credit: residual,
|
|
36
|
+
label: "Opening balance equity (contra)"
|
|
37
|
+
});
|
|
38
|
+
else if (residual < 0) items.push({
|
|
39
|
+
account: equityAccountCode,
|
|
40
|
+
debit: Math.abs(residual),
|
|
41
|
+
credit: 0,
|
|
42
|
+
label: "Opening balance equity (contra)"
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
entry: {
|
|
46
|
+
date: cutoverDate,
|
|
47
|
+
label,
|
|
48
|
+
journalType: "GENERAL",
|
|
49
|
+
journalItems: items,
|
|
50
|
+
extra: {
|
|
51
|
+
_externalId: `opening-balance:${dateStr}`,
|
|
52
|
+
_importSource: "opening-balance"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
residual,
|
|
56
|
+
lineCount
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { buildOpeningBalanceEntry as t };
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,16 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
//#region src/plugins/tax-hook.plugin.d.ts
|
|
5
|
-
interface TaxHookPluginOptions {
|
|
6
|
-
/** Tax line generator — implements the tax calculation logic */
|
|
7
|
-
generator: TaxLineGenerator;
|
|
8
|
-
/** Only apply tax hooks on posted entries (default: true) */
|
|
9
|
-
onlyOnPost?: boolean;
|
|
10
|
-
}
|
|
11
|
-
declare function taxHookPlugin(options: TaxHookPluginOptions): {
|
|
12
|
-
name: string;
|
|
13
|
-
apply(repo: RepositoryInstance): void;
|
|
14
|
-
};
|
|
15
|
-
//#endregion
|
|
16
|
-
export { type CreateLockPluginOptions, type CreditLimitPluginOptions, type DailyLockPluginOptions, type DoubleEntryPluginOptions, type FiscalLockPluginOptions, type FxRealizationPluginOptions, type IdempotencyPluginOptions, type LockAccountSelector, type LockHit, type LockResolver, type LockResolverContext, type PeriodResolverOptions, type TaxHookPluginOptions, type TaxLockPluginOptions, type WatermarkResolverOptions, createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, taxHookPlugin, taxLockPlugin, watermarkResolver };
|
|
1
|
+
import { S as creditLimitPlugin, _ as FxRealizationPluginOptions, a as dailyLockPlugin, b as doubleEntryPlugin, c as periodResolver, d as LockAccountSelector, f as LockHit, g as idempotencyPlugin, h as IdempotencyPluginOptions, i as FiscalLockPluginOptions, l as createLockPlugin, m as LockResolverContext, n as watermarkResolver, o as fiscalLockPlugin, p as LockResolver, r as DailyLockPluginOptions, s as PeriodResolverOptions, t as WatermarkResolverOptions, u as CreateLockPluginOptions, v as fxRealizationPlugin, x as CreditLimitPluginOptions, y as DoubleEntryPluginOptions } from "../index-BSsvrf3m.mjs";
|
|
2
|
+
export { type CreateLockPluginOptions, type CreditLimitPluginOptions, type DailyLockPluginOptions, type DoubleEntryPluginOptions, type FiscalLockPluginOptions, type FxRealizationPluginOptions, type IdempotencyPluginOptions, type LockAccountSelector, type LockHit, type LockResolver, type LockResolverContext, type PeriodResolverOptions, type WatermarkResolverOptions, createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, watermarkResolver };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,57 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Apply a tax hook to journal items.
|
|
5
|
-
*
|
|
6
|
-
* Iterates each item that has a taxCode in taxDetails, calls
|
|
7
|
-
* `generator.generateTaxLines` for each, and appends the generated
|
|
8
|
-
* tax lines as new journal items.
|
|
9
|
-
*
|
|
10
|
-
* @returns The original items + generated tax items
|
|
11
|
-
*/
|
|
12
|
-
function applyTaxHook(items, generator) {
|
|
13
|
-
const taxLines = [];
|
|
14
|
-
for (const item of items) {
|
|
15
|
-
const taxDetails = item.taxDetails;
|
|
16
|
-
if (!taxDetails || taxDetails.length === 0) continue;
|
|
17
|
-
const taxCode = taxDetails.find((td) => td.taxCode != null)?.taxCode;
|
|
18
|
-
if (!taxCode) continue;
|
|
19
|
-
const side = item.debit > 0 ? "debit" : "credit";
|
|
20
|
-
const amount = item.debit > 0 ? item.debit : item.credit;
|
|
21
|
-
const input = {
|
|
22
|
-
account: item.account,
|
|
23
|
-
amount,
|
|
24
|
-
side,
|
|
25
|
-
taxCode
|
|
26
|
-
};
|
|
27
|
-
const generated = generator.generateTaxLines(input);
|
|
28
|
-
for (const line of generated) taxLines.push({
|
|
29
|
-
account: line.account,
|
|
30
|
-
debit: line.debit,
|
|
31
|
-
credit: line.credit,
|
|
32
|
-
label: line.label,
|
|
33
|
-
taxDetails: line.taxDetails
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return [...items, ...taxLines];
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/plugins/tax-hook.plugin.ts
|
|
40
|
-
function taxHookPlugin(options) {
|
|
41
|
-
const { generator, onlyOnPost = true } = options;
|
|
42
|
-
return {
|
|
43
|
-
name: "accounting:tax-hook",
|
|
44
|
-
apply(repo) {
|
|
45
|
-
repo.on("before:create", (context) => {
|
|
46
|
-
const data = context.data;
|
|
47
|
-
if (!data) return;
|
|
48
|
-
if (onlyOnPost && data.state !== "posted") return;
|
|
49
|
-
const items = data.journalItems;
|
|
50
|
-
if (!items || items.length === 0) return;
|
|
51
|
-
data.journalItems = applyTaxHook(items, generator);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, taxHookPlugin, taxLockPlugin, watermarkResolver };
|
|
1
|
+
import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "../fx-realization.plugin-DDVK-oYO.mjs";
|
|
2
|
+
export { createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, watermarkResolver };
|
package/dist/reports/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as generateDimensionBreakdown, C as FiscalReopenResult, D as DimensionBreakdownParams, E as DimensionBreakdownOptions, F as BudgetVsActualReport, I as BudgetVsActualRow, L as generateBudgetVsActual, M as generateCashFlow, N as BudgetVsActualOptions, O as DimensionBreakdownReport, P as BudgetVsActualParams, R as BalanceSheetOptions, S as FiscalCloseResult, T as reopenFiscalPeriod, _ as IncomeStatementOptions, a as RevaluationReport,
|
|
1
|
+
import { $ as AgedBalanceParams, A as generateDimensionBreakdown, C as FiscalReopenResult, D as DimensionBreakdownParams, E as DimensionBreakdownOptions, F as BudgetVsActualReport, I as BudgetVsActualRow, L as generateBudgetVsActual, M as generateCashFlow, N as BudgetVsActualOptions, O as DimensionBreakdownReport, P as BudgetVsActualParams, Q as AgedBalanceOptions, R as BalanceSheetOptions, S as FiscalCloseResult, T as reopenFiscalPeriod, _ as IncomeStatementOptions, a as RevaluationReport, b as generateGeneralLedger, et as AgedBalanceReport, f as PartnerLedgerLine, g as generatePartnerLedger, h as PartnerLedgerReport, i as RevaluationParams, it as generateAgedBalance, j as CashFlowOptions, k as DimensionBreakdownRow, m as PartnerLedgerParams, n as generateTrialBalance, nt as AgedBucketConfig, o as generateRevaluation, p as PartnerLedgerOptions, r as RevaluationOptions, rt as DEFAULT_BUCKETS, t as TrialBalanceOptions, tt as AgedBalanceRow, v as generateIncomeStatement, w as closeFiscalPeriod, x as FiscalCloseOptions, y as GeneralLedgerOptions, z as generateBalanceSheet } from "../trial-balance-DTj-c21f.mjs";
|
|
2
2
|
export { type AgedBalanceOptions, type AgedBalanceParams, type AgedBalanceReport, type AgedBalanceRow, type AgedBucketConfig, type BalanceSheetOptions, type BudgetVsActualOptions, type BudgetVsActualParams, type BudgetVsActualReport, type BudgetVsActualRow, type CashFlowOptions, DEFAULT_BUCKETS, type DimensionBreakdownOptions, type DimensionBreakdownParams, type DimensionBreakdownReport, type DimensionBreakdownRow, type FiscalCloseOptions, type FiscalCloseResult, type FiscalReopenResult, type GeneralLedgerOptions, type IncomeStatementOptions, type PartnerLedgerLine, type PartnerLedgerOptions, type PartnerLedgerParams, type PartnerLedgerReport, type RevaluationOptions, type RevaluationParams, type RevaluationReport, type TrialBalanceOptions, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
|