@classytic/ledger 0.5.1 → 0.7.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.
@@ -1,4 +1,4 @@
1
- import { n as Errors } from "./errors-BmRjW38t.mjs";
1
+ import { n as Errors } from "./errors-CSDQPNyt.mjs";
2
2
  import { i as extractMainType } from "./categories-BkKdv16V.mjs";
3
3
  import mongoose from "mongoose";
4
4
  //#region src/utils/tenant-guard.ts
@@ -1519,7 +1519,7 @@ async function closeFiscalPeriod(opts, params) {
1519
1519
  if (orgField && organizationId) periodQuery[orgField] = organizationId;
1520
1520
  const period = await FiscalPeriodModel.findOne(periodQuery, null, queryOpts).lean();
1521
1521
  if (!period) throw Errors.notFound("Fiscal period not found");
1522
- if (period.closed) throw Errors.fiscal("Fiscal period is already closed");
1522
+ if (period.closed) throw Errors.locked("fiscal", "Fiscal period is already closed");
1523
1523
  const startDate = period.startDate;
1524
1524
  const endDate = period.endDate;
1525
1525
  const accountQuery = { active: true };
@@ -1538,7 +1538,7 @@ async function closeFiscalPeriod(opts, params) {
1538
1538
  isIncome: at.category === "Income Statement-Income"
1539
1539
  });
1540
1540
  }
1541
- if (!retainedEarningsId) throw Errors.fiscal(`Retained earnings account (code: ${retainedEarningsAccountCode}) not found. Create this account before closing the fiscal period.`);
1541
+ if (!retainedEarningsId) throw Errors.locked("fiscal", `Retained earnings account (code: ${retainedEarningsAccountCode}) not found. Create this account before closing the fiscal period.`);
1542
1542
  const baseMatch = {
1543
1543
  state: "posted",
1544
1544
  date: {
@@ -1630,13 +1630,13 @@ async function reopenFiscalPeriod(opts, params) {
1630
1630
  if (orgField && organizationId) periodQuery[orgField] = organizationId;
1631
1631
  const period = await FiscalPeriodModel.findOne(periodQuery, null, queryOpts).lean();
1632
1632
  if (!period) throw Errors.notFound("Fiscal period not found");
1633
- if (!period.closed) throw Errors.fiscal("Fiscal period is not closed");
1633
+ if (!period.closed) throw Errors.locked("fiscal", "Fiscal period is not closed");
1634
1634
  const laterQuery = {
1635
1635
  closed: true,
1636
1636
  startDate: { $gt: period.endDate }
1637
1637
  };
1638
1638
  if (orgField && organizationId) laterQuery[orgField] = organizationId;
1639
- if (await FiscalPeriodModel.findOne(laterQuery, null, queryOpts).lean()) throw Errors.fiscal("Cannot reopen: a later fiscal period is already closed. Reopen later periods first.");
1639
+ if (await FiscalPeriodModel.findOne(laterQuery, null, queryOpts).lean()) throw Errors.locked("fiscal", "Cannot reopen: a later fiscal period is already closed. Reopen later periods first.");
1640
1640
  const closingEntryId = period.closingEntryId ?? null;
1641
1641
  if (closingEntryId) await JournalEntryModel.findByIdAndDelete(closingEntryId, queryOpts);
1642
1642
  const reopenedAt = /* @__PURE__ */ new Date();
@@ -1660,4 +1660,141 @@ async function reopenFiscalPeriod(opts, params) {
1660
1660
  }
1661
1661
  }
1662
1662
  //#endregion
1663
- export { DEFAULT_BUCKETS as C, isVirtualTaxAccount as S, requireOrgScope as T, getDateRange as _, defaultLogger as a, calculateTotal as b, buildRevaluationEntry as c, generateGeneralLedger as d, generateDimensionBreakdown as f, buildItemFilters as g, generateBalanceSheet as h, finalizeSession as i, computeRevaluation as l, generateBudgetVsActual as m, reopenFiscalPeriod as n, generateTrialBalance as o, generateCashFlow as p, acquireSession as r, generateRevaluation as s, closeFiscalPeriod as t, generateIncomeStatement as u, getFiscalYearStart as v, generateAgedBalance as w, computeEndingBalance as x, buildAccountTypeMap as y };
1663
+ //#region src/reports/partner-ledger.ts
1664
+ async function generatePartnerLedger(opts, params) {
1665
+ const { AccountModel, JournalEntryModel, orgField } = opts;
1666
+ const { controlAccountId, partnerField = "partnerId", partnerId, startDate, endDate, includeMatched = true, buckets = DEFAULT_BUCKETS } = params;
1667
+ requireOrgScope(orgField, params.organizationId);
1668
+ const accountDoc = await AccountModel.findById(controlAccountId).lean();
1669
+ const openingMatch = {
1670
+ state: "posted",
1671
+ date: { $lt: startDate }
1672
+ };
1673
+ if (orgField && params.organizationId) openingMatch[orgField] = params.organizationId;
1674
+ const openingPipeline = [
1675
+ { $match: openingMatch },
1676
+ { $unwind: "$journalItems" },
1677
+ { $match: {
1678
+ "journalItems.account": controlAccountId,
1679
+ [`journalItems.${partnerField}`]: partnerId
1680
+ } },
1681
+ { $group: {
1682
+ _id: null,
1683
+ debit: { $sum: { $ifNull: ["$journalItems.debit", 0] } },
1684
+ credit: { $sum: { $ifNull: ["$journalItems.credit", 0] } }
1685
+ } }
1686
+ ];
1687
+ const openingResult = await JournalEntryModel.aggregate(openingPipeline);
1688
+ const openingBalance = (openingResult[0]?.debit ?? 0) - (openingResult[0]?.credit ?? 0);
1689
+ const periodMatch = {
1690
+ state: "posted",
1691
+ date: {
1692
+ $gte: startDate,
1693
+ $lte: endDate
1694
+ }
1695
+ };
1696
+ if (orgField && params.organizationId) periodMatch[orgField] = params.organizationId;
1697
+ const itemMatch = {
1698
+ "journalItems.account": controlAccountId,
1699
+ [`journalItems.${partnerField}`]: partnerId
1700
+ };
1701
+ if (!includeMatched) itemMatch.$or = [{ "journalItems.matchingNumber": null }, { "journalItems.matchingNumber": { $exists: false } }];
1702
+ const linePipeline = [
1703
+ { $match: periodMatch },
1704
+ { $addFields: { journalItems: { $map: {
1705
+ input: { $range: [0, { $size: "$journalItems" }] },
1706
+ as: "idx",
1707
+ in: { $mergeObjects: [{ $arrayElemAt: ["$journalItems", "$$idx"] }, { _itemIndex: "$$idx" }] }
1708
+ } } } },
1709
+ { $unwind: "$journalItems" },
1710
+ { $match: itemMatch },
1711
+ { $project: {
1712
+ _id: 0,
1713
+ entry: "$_id",
1714
+ itemIndex: "$journalItems._itemIndex",
1715
+ date: { $ifNull: ["$journalItems.date", "$date"] },
1716
+ referenceNumber: 1,
1717
+ label: "$journalItems.label",
1718
+ debit: { $ifNull: ["$journalItems.debit", 0] },
1719
+ credit: { $ifNull: ["$journalItems.credit", 0] },
1720
+ signedDelta: { $subtract: [{ $ifNull: ["$journalItems.debit", 0] }, { $ifNull: ["$journalItems.credit", 0] }] },
1721
+ matchingNumber: "$journalItems.matchingNumber",
1722
+ maturityDate: "$journalItems.maturityDate"
1723
+ } },
1724
+ { $sort: {
1725
+ date: 1,
1726
+ entry: 1,
1727
+ itemIndex: 1
1728
+ } },
1729
+ { $setWindowFields: {
1730
+ sortBy: {
1731
+ date: 1,
1732
+ entry: 1,
1733
+ itemIndex: 1
1734
+ },
1735
+ output: { runningDelta: {
1736
+ $sum: "$signedDelta",
1737
+ window: { documents: ["unbounded", "current"] }
1738
+ } }
1739
+ } }
1740
+ ];
1741
+ const rawLines = await JournalEntryModel.aggregate(linePipeline);
1742
+ const endMs = endDate.getTime();
1743
+ const lines = rawLines.map((r) => {
1744
+ const matchingNumber = r.matchingNumber ?? null;
1745
+ const maturityDate = r.maturityDate ?? null;
1746
+ const daysPastDue = maturityDate ? Math.max(0, Math.floor((endMs - new Date(maturityDate).getTime()) / 864e5)) : null;
1747
+ return {
1748
+ date: r.date,
1749
+ entry: r.entry,
1750
+ itemIndex: r.itemIndex,
1751
+ referenceNumber: r.referenceNumber,
1752
+ label: r.label,
1753
+ debit: r.debit,
1754
+ credit: r.credit,
1755
+ balance: openingBalance + r.runningDelta,
1756
+ matchingNumber,
1757
+ maturityDate,
1758
+ daysPastDue,
1759
+ isMatched: matchingNumber != null
1760
+ };
1761
+ });
1762
+ const closingBalance = lines.length > 0 ? lines[lines.length - 1].balance : openingBalance;
1763
+ let openItemsTotal = 0;
1764
+ let matchedTotal = 0;
1765
+ for (const l of lines) {
1766
+ const delta = l.debit - l.credit;
1767
+ if (l.isMatched) matchedTotal += delta;
1768
+ else openItemsTotal += delta;
1769
+ }
1770
+ const agedBuckets = Object.fromEntries(buckets.map((b) => [b.label, 0]));
1771
+ for (const l of lines) {
1772
+ if (l.isMatched) continue;
1773
+ const days = l.daysPastDue ?? 0;
1774
+ const bucket = buckets.find((b) => days >= b.minDays && days < b.maxDays);
1775
+ if (bucket) agedBuckets[bucket.label] += l.debit - l.credit;
1776
+ }
1777
+ return {
1778
+ metadata: {
1779
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1780
+ partnerId,
1781
+ controlAccount: {
1782
+ id: controlAccountId,
1783
+ name: accountDoc?.name,
1784
+ code: accountDoc?.accountNumber
1785
+ },
1786
+ period: {
1787
+ startDate: startDate.toISOString().split("T")[0],
1788
+ endDate: endDate.toISOString().split("T")[0]
1789
+ }
1790
+ },
1791
+ openingBalance,
1792
+ closingBalance,
1793
+ openItemsTotal,
1794
+ matchedTotal,
1795
+ lines,
1796
+ agedBuckets
1797
+ };
1798
+ }
1799
+ //#endregion
1800
+ export { isVirtualTaxAccount as C, requireOrgScope as E, computeEndingBalance as S, generateAgedBalance as T, buildItemFilters as _, finalizeSession as a, buildAccountTypeMap as b, generateRevaluation as c, generateIncomeStatement as d, generateGeneralLedger as f, generateBalanceSheet as g, generateBudgetVsActual as h, acquireSession as i, buildRevaluationEntry as l, generateCashFlow as m, closeFiscalPeriod as n, defaultLogger as o, generateDimensionBreakdown as p, reopenFiscalPeriod as r, generateTrialBalance as s, generatePartnerLedger as t, computeRevaluation as u, getDateRange as v, DEFAULT_BUCKETS as w, calculateTotal as x, getFiscalYearStart as y };
@@ -1,38 +1,2 @@
1
- import { a as DoubleEntryPluginOptions, c as dateLockPlugin, i as fiscalLockPlugin, n as idempotencyPlugin, o as doubleEntryPlugin, r as FiscalLockPluginOptions, s as DateLockPluginOptions, t as IdempotencyPluginOptions } from "../idempotency.plugin-WcQLZU9n.mjs";
2
- import { RepositoryInstance } from "@classytic/mongokit";
3
-
4
- //#region src/utils/tax-hooks.d.ts
5
- interface TaxLineInput {
6
- account: unknown;
7
- amount: number;
8
- side: 'debit' | 'credit';
9
- taxCode?: string;
10
- extraFields?: Record<string, unknown>;
11
- }
12
- interface GeneratedTaxLine {
13
- account: unknown;
14
- debit: number;
15
- credit: number;
16
- label?: string;
17
- taxDetails?: Array<{
18
- taxCode: string;
19
- taxName?: string;
20
- }>;
21
- }
22
- interface TaxLineGenerator {
23
- generateTaxLines(input: TaxLineInput): GeneratedTaxLine[];
24
- }
25
- //#endregion
26
- //#region src/plugins/tax-hook.plugin.d.ts
27
- interface TaxHookPluginOptions {
28
- /** Tax line generator — implements the tax calculation logic */
29
- generator: TaxLineGenerator;
30
- /** Only apply tax hooks on posted entries (default: true) */
31
- onlyOnPost?: boolean;
32
- }
33
- declare function taxHookPlugin(options: TaxHookPluginOptions): {
34
- name: string;
35
- apply(repo: RepositoryInstance): void;
36
- };
37
- //#endregion
38
- export { type DateLockPluginOptions, type DoubleEntryPluginOptions, type FiscalLockPluginOptions, type IdempotencyPluginOptions, type TaxHookPluginOptions, dateLockPlugin, doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin, taxHookPlugin };
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-Bl0_ak5w.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 };
@@ -1,57 +1,2 @@
1
- import { i as doubleEntryPlugin, n as idempotencyPlugin, r as fiscalLockPlugin, t as dateLockPlugin } from "../date-lock.plugin-B6WyvqNG.mjs";
2
- //#region src/utils/tax-hooks.ts
3
- /**
4
- * Apply a tax hook to journal items.
5
- *
6
- * Iterates each item that has a taxCode in taxDetails, calls
7
- * `generator.generateTaxLines` for each, and appends the generated
8
- * tax lines as new journal items.
9
- *
10
- * @returns The original items + generated tax items
11
- */
12
- function applyTaxHook(items, generator) {
13
- const taxLines = [];
14
- for (const item of items) {
15
- const taxDetails = item.taxDetails;
16
- if (!taxDetails || taxDetails.length === 0) continue;
17
- const taxCode = taxDetails.find((td) => td.taxCode != null)?.taxCode;
18
- if (!taxCode) continue;
19
- const side = item.debit > 0 ? "debit" : "credit";
20
- const amount = item.debit > 0 ? item.debit : item.credit;
21
- const input = {
22
- account: item.account,
23
- amount,
24
- side,
25
- taxCode
26
- };
27
- const generated = generator.generateTaxLines(input);
28
- for (const line of generated) taxLines.push({
29
- account: line.account,
30
- debit: line.debit,
31
- credit: line.credit,
32
- label: line.label,
33
- taxDetails: line.taxDetails
34
- });
35
- }
36
- return [...items, ...taxLines];
37
- }
38
- //#endregion
39
- //#region src/plugins/tax-hook.plugin.ts
40
- function taxHookPlugin(options) {
41
- const { generator, onlyOnPost = true } = options;
42
- return {
43
- name: "accounting:tax-hook",
44
- apply(repo) {
45
- repo.on("before:create", (context) => {
46
- const data = context.data;
47
- if (!data) return;
48
- if (onlyOnPost && data.state !== "posted") return;
49
- const items = data.journalItems;
50
- if (!items || items.length === 0) return;
51
- data.journalItems = applyTaxHook(items, generator);
52
- });
53
- }
54
- };
55
- }
56
- //#endregion
57
- export { dateLockPlugin, doubleEntryPlugin, fiscalLockPlugin, idempotencyPlugin, taxHookPlugin };
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-CfYy1tB6.mjs";
2
+ export { createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, watermarkResolver };
@@ -1,2 +1,2 @@
1
- import { $ as AgedBucketConfig, A as BudgetVsActualReport, C as DimensionBreakdownReport, D as generateCashFlow, E as CashFlowOptions, M as generateBudgetVsActual, N as BalanceSheetOptions, O as BudgetVsActualOptions, P as generateBalanceSheet, Q as AgedBalanceRow, S as DimensionBreakdownParams, T as generateDimensionBreakdown, X as AgedBalanceParams, Y as AgedBalanceOptions, Z as AgedBalanceReport, _ as FiscalCloseResult, a as RevaluationReport, b as reopenFiscalPeriod, et as DEFAULT_BUCKETS, f as IncomeStatementOptions, g as FiscalCloseOptions, h as generateGeneralLedger, i as RevaluationParams, j as BudgetVsActualRow, k as BudgetVsActualParams, m as GeneralLedgerOptions, n as generateTrialBalance, o as generateRevaluation, p as generateIncomeStatement, r as RevaluationOptions, t as TrialBalanceOptions, tt as generateAgedBalance, v as FiscalReopenResult, w as DimensionBreakdownRow, x as DimensionBreakdownOptions, y as closeFiscalPeriod } from "../trial-balance-BZ7yOOFD.mjs";
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 RevaluationOptions, type RevaluationParams, type RevaluationReport, type TrialBalanceOptions, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
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-DTc8kzTD.mjs";
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 };
@@ -1,2 +1,2 @@
1
- import { C as DEFAULT_BUCKETS, d as generateGeneralLedger, f as generateDimensionBreakdown, h as generateBalanceSheet, m as generateBudgetVsActual, n as reopenFiscalPeriod, o as generateTrialBalance, p as generateCashFlow, s as generateRevaluation, t as closeFiscalPeriod, u as generateIncomeStatement, w as generateAgedBalance } from "../fiscal-close-Dk3yRT9i.mjs";
2
- export { DEFAULT_BUCKETS, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
1
+ import { T as generateAgedBalance, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, m as generateCashFlow, n as closeFiscalPeriod, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, w as DEFAULT_BUCKETS } from "../partner-ledger-D9H5hegI.mjs";
2
+ export { DEFAULT_BUCKETS, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
@@ -1,5 +1,5 @@
1
1
  import { c as DateRange } from "./core-BkGjuVZj.mjs";
2
- import { t as CountryPack } from "./index-GmfEFxVn.mjs";
2
+ import { t as CountryPack } from "./index-BX8miYdu.mjs";
3
3
  import { ClientSession, Model } from "mongoose";
4
4
 
5
5
  //#region src/utils/logger.d.ts
@@ -178,37 +178,6 @@ interface CashFlowReport {
178
178
  financing: CashFlowSection;
179
179
  netCashFlow: number;
180
180
  }
181
- interface TaxAccountBalance {
182
- code: string;
183
- name: string;
184
- balance: number;
185
- taxMetadata?: unknown;
186
- }
187
- interface TaxReturnSummary {
188
- totalSales: number;
189
- gstHstCollected: number;
190
- inputTaxCredits: number;
191
- netTax: number;
192
- finalAmount: number;
193
- isRefund: boolean;
194
- refundAmount: number;
195
- paymentAmount: number;
196
- }
197
- interface TaxReport {
198
- period: {
199
- startDate: string;
200
- endDate: string;
201
- province: string;
202
- };
203
- accountBalances: {
204
- collected: Record<string, TaxAccountBalance>;
205
- itc: Record<string, TaxAccountBalance>;
206
- instalments: Record<string, TaxAccountBalance>;
207
- };
208
- craLines: Record<string | number, number>;
209
- summary: TaxReturnSummary;
210
- calculatedAt: string;
211
- }
212
181
  //#endregion
213
182
  //#region src/reports/balance-sheet.d.ts
214
183
  interface BalanceSheetOptions {
@@ -403,6 +372,79 @@ declare function generateIncomeStatement(opts: IncomeStatementOptions, params: {
403
372
  filters?: Record<string, unknown>;
404
373
  }): Promise<IncomeStatementReport>;
405
374
  //#endregion
375
+ //#region src/reports/partner-ledger.d.ts
376
+ interface PartnerLedgerOptions {
377
+ AccountModel: Model<unknown>;
378
+ JournalEntryModel: Model<unknown>;
379
+ orgField?: string;
380
+ }
381
+ interface PartnerLedgerParams {
382
+ organizationId?: unknown;
383
+ /**
384
+ * The control account being ledgered — typically `2111 A/P`
385
+ * (supplier statement) or `1141 A/R` (customer statement).
386
+ */
387
+ controlAccountId: unknown;
388
+ /**
389
+ * Field name on each journal item that holds the partner reference.
390
+ * Default: `'partnerId'`. Whatever you declared in
391
+ * `schemaOptions.journalEntry.extraItemFields`.
392
+ */
393
+ partnerField?: string;
394
+ /**
395
+ * The specific partner whose statement we're generating. Required —
396
+ * to get all partners use `generateAgedBalance` instead.
397
+ */
398
+ partnerId: unknown;
399
+ startDate: Date;
400
+ endDate: Date;
401
+ /**
402
+ * If true, include items already matched (settled) inside the period.
403
+ * Default: true — statements show settled activity in the period.
404
+ */
405
+ includeMatched?: boolean;
406
+ /** Custom aged buckets for the open-items summary. */
407
+ buckets?: AgedBucketConfig[];
408
+ }
409
+ interface PartnerLedgerLine {
410
+ date: Date;
411
+ entry: unknown;
412
+ itemIndex: number;
413
+ referenceNumber?: string;
414
+ label?: string;
415
+ debit: number;
416
+ credit: number;
417
+ /** Running balance (debit - credit, signed) including this row. */
418
+ balance: number;
419
+ matchingNumber: string | null;
420
+ maturityDate?: Date | null;
421
+ /** Days past `maturityDate` as of `endDate`; null if no maturity set. */
422
+ daysPastDue: number | null;
423
+ isMatched: boolean;
424
+ }
425
+ interface PartnerLedgerReport {
426
+ metadata: {
427
+ generatedAt: string;
428
+ partnerId: unknown;
429
+ controlAccount: {
430
+ id: unknown;
431
+ name?: string;
432
+ code?: string;
433
+ };
434
+ period: {
435
+ startDate: string;
436
+ endDate: string;
437
+ };
438
+ };
439
+ openingBalance: number;
440
+ closingBalance: number;
441
+ openItemsTotal: number;
442
+ matchedTotal: number;
443
+ lines: PartnerLedgerLine[];
444
+ agedBuckets: Record<string, number>;
445
+ }
446
+ declare function generatePartnerLedger(opts: PartnerLedgerOptions, params: PartnerLedgerParams): Promise<PartnerLedgerReport>;
447
+ //#endregion
406
448
  //#region src/utils/revaluation.d.ts
407
449
  /**
408
450
  * Foreign Exchange Revaluation Utilities
@@ -539,4 +581,4 @@ declare function generateTrialBalance(opts: TrialBalanceOptions, params: {
539
581
  filters?: Record<string, unknown>;
540
582
  }): Promise<TrialBalanceReport>;
541
583
  //#endregion
542
- export { AgedBucketConfig as $, BudgetVsActualReport as A, IncomeStatementReport as B, DimensionBreakdownReport as C, generateCashFlow as D, CashFlowOptions as E, BalanceSheetReport as F, TaxReport as G, ReportAccount as H, CashFlowReport as I, TrialBalanceRow as J, TaxReturnSummary as K, CashFlowSection as L, generateBudgetVsActual as M, BalanceSheetOptions as N, BudgetVsActualOptions as O, generateBalanceSheet as P, AgedBalanceRow as Q, GeneralLedgerAccount as R, DimensionBreakdownParams as S, generateDimensionBreakdown as T, ReportCategory as U, LedgerEntry as V, ReportGroup as W, AgedBalanceParams as X, AgedBalanceOptions as Y, AgedBalanceReport as Z, FiscalCloseResult as _, RevaluationReport as a, reopenFiscalPeriod as b, RevaluationRate as c, computeRevaluation as d, DEFAULT_BUCKETS as et, IncomeStatementOptions as f, FiscalCloseOptions as g, generateGeneralLedger as h, RevaluationParams as i, BudgetVsActualRow as j, BudgetVsActualParams as k, RevaluationResult as l, GeneralLedgerOptions as m, generateTrialBalance as n, Logger as nt, generateRevaluation as o, generateIncomeStatement as p, TrialBalanceReport as q, RevaluationOptions as r, defaultLogger as rt, AccountForeignBalance as s, TrialBalanceOptions as t, generateAgedBalance as tt, buildRevaluationEntry as u, FiscalReopenResult as v, DimensionBreakdownRow as w, DimensionBreakdownOptions as x, closeFiscalPeriod as y, GeneralLedgerReport as z };
584
+ export { AgedBalanceParams as $, generateDimensionBreakdown as A, BalanceSheetReport as B, FiscalReopenResult as C, DimensionBreakdownParams as D, DimensionBreakdownOptions as E, BudgetVsActualReport as F, IncomeStatementReport as G, CashFlowSection as H, BudgetVsActualRow as I, ReportCategory as J, LedgerEntry as K, generateBudgetVsActual as L, generateCashFlow as M, BudgetVsActualOptions as N, DimensionBreakdownReport as O, BudgetVsActualParams as P, AgedBalanceOptions as Q, BalanceSheetOptions as R, FiscalCloseResult as S, reopenFiscalPeriod as T, GeneralLedgerAccount as U, CashFlowReport as V, GeneralLedgerReport as W, TrialBalanceReport as X, ReportGroup as Y, TrialBalanceRow as Z, IncomeStatementOptions as _, RevaluationReport as a, Logger as at, generateGeneralLedger as b, RevaluationRate as c, computeRevaluation as d, AgedBalanceReport as et, PartnerLedgerLine as f, generatePartnerLedger as g, PartnerLedgerReport as h, RevaluationParams as i, generateAgedBalance as it, CashFlowOptions as j, DimensionBreakdownRow as k, RevaluationResult as l, PartnerLedgerParams as m, generateTrialBalance as n, AgedBucketConfig as nt, generateRevaluation as o, defaultLogger as ot, PartnerLedgerOptions as p, ReportAccount as q, RevaluationOptions as r, DEFAULT_BUCKETS as rt, AccountForeignBalance as s, TrialBalanceOptions as t, AgedBalanceRow as tt, buildRevaluationEntry as u, generateIncomeStatement as v, closeFiscalPeriod as w, FiscalCloseOptions as x, GeneralLedgerOptions as y, generateBalanceSheet as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/ledger",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "Production-grade double-entry accounting engine for MongoDB — schemas, reports, tax, multi-tenant",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -78,7 +78,7 @@
78
78
  "url": "git+https://github.com/classytic/accounting.git"
79
79
  },
80
80
  "peerDependencies": {
81
- "@classytic/mongokit": ">=3.5.3",
81
+ "@classytic/mongokit": ">=3.5.5",
82
82
  "mongoose": ">=9.4.1"
83
83
  },
84
84
  "engines": {
@@ -96,12 +96,13 @@
96
96
  "format": "biome format src/ --write",
97
97
  "check": "biome ci src/ --diagnostic-level=error",
98
98
  "knip": "knip",
99
- "prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test",
100
- "release": "npm run check && npm run build && npm run typecheck && npm test && npm publish --access public"
99
+ "smoke": "node scripts/smoke.mjs",
100
+ "prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test && npm run smoke",
101
+ "release": "npm run check && npm run build && npm run typecheck && npm test && npm run smoke && npm publish --access public"
101
102
  },
102
103
  "devDependencies": {
103
104
  "@biomejs/biome": "^2.4.10",
104
- "@classytic/mongokit": "^3.5.3",
105
+ "@classytic/mongokit": "^3.5.5",
105
106
  "@types/node": "^22.0.0",
106
107
  "@vitest/coverage-v8": "^3.2.4",
107
108
  "knip": "^6.3.0",