@classytic/ledger 0.1.5 → 0.3.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.
Files changed (70) hide show
  1. package/README.md +161 -64
  2. package/dist/{account.repository-Crf5DGO4.mjs → account.repository-BpkSd6q3.mjs} +190 -41
  3. package/dist/{categories-BNJBd4ze.mjs → categories-CclX7Q94.mjs} +0 -2
  4. package/dist/constants/index.d.mts +1 -1
  5. package/dist/constants/index.mjs +4 -5
  6. package/dist/{core-Cx0baosR.d.mts → core-8Xfnpn6g.d.mts} +1 -2
  7. package/dist/country/index.d.mts +2 -105
  8. package/dist/country/index.mjs +0 -2
  9. package/dist/{currencies-Bkn3FNkC.d.mts → currencies-4WAbFRlw.d.mts} +2 -3
  10. package/dist/{currencies-BBk3NwXn.mjs → currencies-W8kQAkm0.mjs} +0 -2
  11. package/dist/{idempotency.plugin-C6r8RI8d.mjs → date-lock.plugin-eYAJ9h_u.mjs} +50 -13
  12. package/dist/{engine-Cd73EOT6.d.mts → engine-Cn-9yerQ.d.mts} +38 -8
  13. package/dist/{errors-CeqRahE-.mjs → errors-B7yC-Jfw.mjs} +0 -2
  14. package/dist/exports/index.d.mts +2 -2
  15. package/dist/exports/index.mjs +2 -3
  16. package/dist/{universal-CMfrZ2hG.mjs → exports-I5Xkq-9_.mjs} +0 -7
  17. package/dist/{fiscal-close-DuXDgVvb.mjs → fiscal-close-B6LhQ10f.mjs} +742 -32
  18. package/dist/fiscal-period.schema-BMnlI9H5.d.mts +103 -0
  19. package/dist/{idempotency.plugin-BESs9YPD.d.mts → idempotency.plugin-B_CNsInz.d.mts} +19 -17
  20. package/dist/{universal-x33ZJODp.d.mts → index-BPukb3L8.d.mts} +1 -2
  21. package/dist/index-CxZqRaOU.d.mts +119 -0
  22. package/dist/index.d.mts +251 -29
  23. package/dist/index.mjs +124 -27
  24. package/dist/{journals-CI3Wb4EF.mjs → journals-oH-FK3g8.mjs} +0 -2
  25. package/dist/{logger-Cv6VVc4r.d.mts → logger-CbHWZl7v.d.mts} +1 -2
  26. package/dist/money.d.mts +1 -2
  27. package/dist/money.mjs +3 -3
  28. package/dist/plugins/index.d.mts +38 -2
  29. package/dist/plugins/index.mjs +57 -3
  30. package/dist/reconciliation.repository-CW4-8q90.d.mts +135 -0
  31. package/dist/{fiscal-period.schema-CbALaaKl.mjs → reconciliation.schema-BuetvZTd.mjs} +218 -30
  32. package/dist/reports/index.d.mts +2 -2
  33. package/dist/reports/index.mjs +2 -3
  34. package/dist/repositories/index.d.mts +2 -2
  35. package/dist/repositories/index.mjs +2 -3
  36. package/dist/revaluation-D9x0NE8w.d.mts +530 -0
  37. package/dist/schemas/index.d.mts +71 -2
  38. package/dist/schemas/index.mjs +2 -3
  39. package/dist/tenant-guard-Fm6AID_6.mjs +13 -0
  40. package/docs/reports.md +1 -1
  41. package/package.json +3 -3
  42. package/dist/account.repository-1C2sZvB2.d.mts +0 -29
  43. package/dist/account.repository-1C2sZvB2.d.mts.map +0 -1
  44. package/dist/account.repository-Crf5DGO4.mjs.map +0 -1
  45. package/dist/categories-BNJBd4ze.mjs.map +0 -1
  46. package/dist/core-Cx0baosR.d.mts.map +0 -1
  47. package/dist/country/index.d.mts.map +0 -1
  48. package/dist/country/index.mjs.map +0 -1
  49. package/dist/currencies-BBk3NwXn.mjs.map +0 -1
  50. package/dist/currencies-Bkn3FNkC.d.mts.map +0 -1
  51. package/dist/engine-Cd73EOT6.d.mts.map +0 -1
  52. package/dist/errors-CeqRahE-.mjs.map +0 -1
  53. package/dist/fiscal-close-CzUzpnMg.d.mts +0 -270
  54. package/dist/fiscal-close-CzUzpnMg.d.mts.map +0 -1
  55. package/dist/fiscal-close-DuXDgVvb.mjs.map +0 -1
  56. package/dist/fiscal-period.schema-CbALaaKl.mjs.map +0 -1
  57. package/dist/fiscal-period.schema-DI2scngu.d.mts +0 -38
  58. package/dist/fiscal-period.schema-DI2scngu.d.mts.map +0 -1
  59. package/dist/idempotency.plugin-BESs9YPD.d.mts.map +0 -1
  60. package/dist/idempotency.plugin-C6r8RI8d.mjs.map +0 -1
  61. package/dist/index.d.mts.map +0 -1
  62. package/dist/index.mjs.map +0 -1
  63. package/dist/journals-CI3Wb4EF.mjs.map +0 -1
  64. package/dist/logger-Cv6VVc4r.d.mts.map +0 -1
  65. package/dist/money.d.mts.map +0 -1
  66. package/dist/money.mjs.map +0 -1
  67. package/dist/session-Dh0s6zG4.mjs +0 -87
  68. package/dist/session-Dh0s6zG4.mjs.map +0 -1
  69. package/dist/universal-CMfrZ2hG.mjs.map +0 -1
  70. package/dist/universal-x33ZJODp.d.mts.map +0 -1
@@ -1,6 +1,24 @@
1
- import { i as getJournalTypeCodes, t as JOURNAL_CODES } from "./journals-CI3Wb4EF.mjs";
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,
@@ -50,9 +70,8 @@ function createAccountSchema(config, options = {}) {
50
70
  };
51
71
  const schema = new mongoose.Schema(fields, { timestamps: true });
52
72
  schema.pre("validate", function() {
53
- const doc = this;
54
- if (!doc.accountNumber && doc.accountTypeCode) doc.accountNumber = doc.accountTypeCode;
55
- if (!doc.name && doc.accountTypeCode) doc.name = country.getAccountType(doc.accountTypeCode)?.name ?? doc.accountTypeCode;
73
+ if (!this.accountNumber && this.accountTypeCode) this.accountNumber = this.accountTypeCode;
74
+ if (!this.name && this.accountTypeCode) this.name = country.getAccountType(this.accountTypeCode)?.name ?? this.accountTypeCode;
56
75
  });
57
76
  if (indexes) if (multiTenant) {
58
77
  const org = multiTenant.orgField;
@@ -76,7 +95,6 @@ function createAccountSchema(config, options = {}) {
76
95
  for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
77
96
  return schema;
78
97
  }
79
-
80
98
  //#endregion
81
99
  //#region src/schemas/journal-entry.schema.ts
82
100
  /**
@@ -101,6 +119,31 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
101
119
  validator: (v) => Number.isInteger(v) && v >= 0,
102
120
  message: "{PATH} must be a non-negative integer (cents), got {VALUE}"
103
121
  };
122
+ const currencyItemFields = {};
123
+ const currencyField = buildCurrencyField(config);
124
+ if (currencyField) {
125
+ currencyItemFields.currency = currencyField;
126
+ currencyItemFields.exchangeRate = {
127
+ type: Number,
128
+ default: null,
129
+ validate: {
130
+ validator: (v) => v === null || v > 0,
131
+ message: "exchangeRate must be greater than zero when set, got {VALUE}"
132
+ }
133
+ };
134
+ currencyItemFields.originalDebit = {
135
+ type: Number,
136
+ default: null,
137
+ min: 0,
138
+ validate: amountValidator
139
+ };
140
+ currencyItemFields.originalCredit = {
141
+ type: Number,
142
+ default: null,
143
+ min: 0,
144
+ validate: amountValidator
145
+ };
146
+ }
104
147
  const JournalItemSchema = new mongoose.Schema({
105
148
  account: {
106
149
  type: mongoose.Schema.Types.ObjectId,
@@ -125,6 +168,7 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
125
168
  type: [TaxDetailSchema],
126
169
  default: []
127
170
  },
171
+ ...currencyItemFields,
128
172
  ...extraItemFields
129
173
  }, { _id: false });
130
174
  const fields = {
@@ -230,22 +274,21 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
230
274
  };
231
275
  const schema = new mongoose.Schema(fields, { timestamps: true });
232
276
  schema.pre("validate", function() {
233
- const doc = this;
234
- for (const item of doc.journalItems) if (!item.date) item.date = doc.date;
235
- for (let i = 0; i < doc.journalItems.length; i++) {
236
- const d = doc.journalItems[i].debit || 0;
237
- const c = doc.journalItems[i].credit || 0;
277
+ for (const item of this.journalItems) if (!item.date) item.date = this.date;
278
+ for (let i = 0; i < this.journalItems.length; i++) {
279
+ const d = this.journalItems[i].debit ?? 0;
280
+ const c = this.journalItems[i].credit ?? 0;
238
281
  if (d > 0 && c > 0) throw new Error(`Journal item at index ${i}: cannot have both debit (${d}) and credit (${c}) greater than zero`);
239
- if (doc.state === "posted" && d === 0 && c === 0) throw new Error(`Journal item at index ${i}: posted entries cannot have zero-value lines (both debit and credit are 0)`);
282
+ if (this.state === "posted" && d === 0 && c === 0) throw new Error(`Journal item at index ${i}: posted entries cannot have zero-value lines (both debit and credit are 0)`);
240
283
  }
241
- const totalDebit = doc.journalItems.reduce((s, i) => s + (i.debit || 0), 0);
242
- const totalCredit = doc.journalItems.reduce((s, i) => s + (i.credit || 0), 0);
243
- if (doc.state === "posted") {
244
- if (doc.journalItems.length < 2) throw new Error("Posted entries must have at least 2 journal items");
284
+ const totalDebit = this.journalItems.reduce((s, item) => s + (item.debit ?? 0), 0);
285
+ const totalCredit = this.journalItems.reduce((s, item) => s + (item.credit ?? 0), 0);
286
+ if (this.state === "posted") {
287
+ if (this.journalItems.length < 2) throw new Error("Posted entries must have at least 2 journal items");
245
288
  if (totalDebit !== totalCredit) throw new Error("Total debit must equal total credit for posted entries");
246
289
  }
247
- doc.totalDebit = totalDebit;
248
- doc.totalCredit = totalCredit;
290
+ this.totalDebit = totalDebit;
291
+ this.totalCredit = totalCredit;
249
292
  });
250
293
  if (autoReference) {
251
294
  const generateReferenceNumber = async (doc, Model, session) => {
@@ -267,12 +310,11 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
267
310
  return `${prefix}${String(seq).padStart(4, "0")}`;
268
311
  };
269
312
  schema.pre("save", async function() {
270
- const doc = this;
271
- if (doc.isModified("journalType")) doc.referenceNumber = void 0;
272
- if (!doc.referenceNumber) {
273
- const session = doc.$session?.() ?? null;
274
- const Model = doc.constructor;
275
- doc.referenceNumber = await generateReferenceNumber(doc, Model, session);
313
+ if (this.isModified("journalType")) this.referenceNumber = void 0;
314
+ if (!this.referenceNumber) {
315
+ const session = this.$session?.() ?? null;
316
+ const Model = this.constructor;
317
+ this.referenceNumber = await generateReferenceNumber(this, Model, session);
276
318
  }
277
319
  });
278
320
  const MAX_REF_RETRIES = 3;
@@ -378,7 +420,6 @@ function createJournalEntrySchema(config, accountModelName, options = {}) {
378
420
  for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
379
421
  return schema;
380
422
  }
381
-
382
423
  //#endregion
383
424
  //#region src/schemas/fiscal-period.schema.ts
384
425
  /**
@@ -471,7 +512,154 @@ function createFiscalPeriodSchema(config, options = {}) {
471
512
  });
472
513
  return schema;
473
514
  }
474
-
475
515
  //#endregion
476
- export { createJournalEntrySchema as n, createAccountSchema as r, createFiscalPeriodSchema as t };
477
- //# sourceMappingURL=fiscal-period.schema-CbALaaKl.mjs.map
516
+ //#region src/schemas/budget.schema.ts
517
+ /**
518
+ * Budget Schema Factory
519
+ *
520
+ * Creates a Mongoose schema for budget records.
521
+ * Each record represents a budgeted amount for an account over a specific period.
522
+ * All monetary amounts are in integer cents.
523
+ */
524
+ function createBudgetSchema(config, options = {}) {
525
+ const { multiTenant } = config;
526
+ const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
527
+ const fields = {
528
+ account: {
529
+ type: mongoose.Schema.Types.ObjectId,
530
+ ref: "Account",
531
+ required: true
532
+ },
533
+ periodStart: {
534
+ type: Date,
535
+ required: true
536
+ },
537
+ periodEnd: {
538
+ type: Date,
539
+ required: true
540
+ },
541
+ amount: {
542
+ type: Number,
543
+ required: true,
544
+ validate: {
545
+ validator: (v) => Number.isInteger(v),
546
+ message: "amount must be an integer (cents)."
547
+ }
548
+ },
549
+ label: {
550
+ type: String,
551
+ default: null
552
+ },
553
+ ...extraFields
554
+ };
555
+ if (multiTenant) fields[multiTenant.orgField] = {
556
+ type: mongoose.Schema.Types.ObjectId,
557
+ ref: multiTenant.orgRef,
558
+ required: true
559
+ };
560
+ const schema = new mongoose.Schema(fields, { timestamps: true });
561
+ schema.pre("validate", function() {
562
+ const doc = this;
563
+ if (doc.periodStart && doc.periodEnd && doc.periodEnd <= doc.periodStart) doc.invalidate("periodEnd", "periodEnd must be after periodStart.", doc.periodEnd, "periodEnd");
564
+ });
565
+ if (indexes) if (multiTenant) {
566
+ const org = multiTenant.orgField;
567
+ schema.index({
568
+ [org]: 1,
569
+ account: 1,
570
+ periodStart: 1,
571
+ periodEnd: 1
572
+ }, { unique: true });
573
+ schema.index({
574
+ [org]: 1,
575
+ periodStart: 1,
576
+ periodEnd: 1
577
+ });
578
+ } else {
579
+ schema.index({
580
+ account: 1,
581
+ periodStart: 1,
582
+ periodEnd: 1
583
+ }, { unique: true });
584
+ schema.index({
585
+ periodStart: 1,
586
+ periodEnd: 1
587
+ });
588
+ }
589
+ for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
590
+ return schema;
591
+ }
592
+ //#endregion
593
+ //#region src/schemas/reconciliation.schema.ts
594
+ /**
595
+ * Reconciliation Schema Factory
596
+ *
597
+ * Creates a Mongoose schema for reconciliation records that link matched
598
+ * debit/credit journal items. Used to track which journal entries have been
599
+ * reconciled against each other for a given account.
600
+ */
601
+ function createReconciliationSchema(config, accountModelName, journalEntryModelName, options = {}) {
602
+ const { multiTenant } = config;
603
+ const { indexes = true, extraFields = {}, extraIndexes = [] } = options;
604
+ const fields = {
605
+ account: {
606
+ type: mongoose.Schema.Types.ObjectId,
607
+ ref: accountModelName,
608
+ required: true
609
+ },
610
+ journalEntryIds: {
611
+ type: [{
612
+ type: mongoose.Schema.Types.ObjectId,
613
+ ref: journalEntryModelName
614
+ }],
615
+ required: true,
616
+ validate: {
617
+ validator: (v) => Array.isArray(v) && v.length > 0,
618
+ message: "journalEntryIds must contain at least one entry."
619
+ }
620
+ },
621
+ debitTotal: {
622
+ type: Number,
623
+ required: true
624
+ },
625
+ creditTotal: {
626
+ type: Number,
627
+ required: true
628
+ },
629
+ difference: {
630
+ type: Number,
631
+ default: 0
632
+ },
633
+ note: { type: String },
634
+ reconciledBy: { type: String },
635
+ reconciledAt: {
636
+ type: Date,
637
+ default: Date.now
638
+ },
639
+ ...extraFields
640
+ };
641
+ if (multiTenant) fields[multiTenant.orgField] = {
642
+ type: mongoose.Schema.Types.ObjectId,
643
+ ref: multiTenant.orgRef,
644
+ required: true
645
+ };
646
+ const schema = new mongoose.Schema(fields, { timestamps: true });
647
+ if (indexes) {
648
+ if (multiTenant) {
649
+ const org = multiTenant.orgField;
650
+ schema.index({
651
+ [org]: 1,
652
+ account: 1,
653
+ reconciledAt: 1
654
+ });
655
+ } else schema.index({
656
+ account: 1,
657
+ reconciledAt: 1
658
+ });
659
+ schema.index({ journalEntryIds: 1 });
660
+ }
661
+ for (const idx of extraIndexes) schema.index(idx.fields, idx.options);
662
+ return schema;
663
+ }
664
+ //#endregion
665
+ export { createAccountSchema as a, createJournalEntrySchema as i, createBudgetSchema as n, createFiscalPeriodSchema as r, createReconciliationSchema as t };
@@ -1,2 +1,2 @@
1
- import { a as reopenFiscalPeriod, c as GeneralLedgerOptions, d as generateIncomeStatement, f as BalanceSheetOptions, h as generateTrialBalance, i as closeFiscalPeriod, l as generateGeneralLedger, m as TrialBalanceOptions, n as FiscalCloseResult, o as CashFlowOptions, p as generateBalanceSheet, r as FiscalReopenResult, s as generateCashFlow, t as FiscalCloseOptions, u as IncomeStatementOptions } from "../fiscal-close-CzUzpnMg.mjs";
2
- export { type BalanceSheetOptions, type CashFlowOptions, type FiscalCloseOptions, type FiscalCloseResult, type FiscalReopenResult, type GeneralLedgerOptions, type IncomeStatementOptions, type TrialBalanceOptions, closeFiscalPeriod, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, reopenFiscalPeriod };
1
+ import { $ as BudgetVsActualReport, A as generateGeneralLedger, C as FiscalCloseResult, D as CashFlowOptions, E as reopenFiscalPeriod, F as TrialBalanceOptions, I as generateTrialBalance, M as generateIncomeStatement, N as BalanceSheetOptions, O as generateCashFlow, P as generateBalanceSheet, Q as BudgetVsActualParams, S as FiscalCloseOptions, T as closeFiscalPeriod, Z as BudgetVsActualOptions, _ as DimensionBreakdownOptions, b as DimensionBreakdownRow, d as AgedBalanceParams, et as BudgetVsActualRow, f as AgedBalanceReport, g as generateAgedBalance, h as DEFAULT_BUCKETS, i as generateRevaluation, j as IncomeStatementOptions, k as GeneralLedgerOptions, m as AgedBucketConfig, n as RevaluationParams, p as AgedBalanceRow, r as RevaluationReport, t as RevaluationOptions, tt as generateBudgetVsActual, u as AgedBalanceOptions, v as DimensionBreakdownParams, w as FiscalReopenResult, x as generateDimensionBreakdown, y as DimensionBreakdownReport } from "../revaluation-D9x0NE8w.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,3 +1,2 @@
1
- import { a as generateIncomeStatement, d as generateTrialBalance, i as generateGeneralLedger, n as reopenFiscalPeriod, o as generateBalanceSheet, r as generateCashFlow, t as closeFiscalPeriod } from "../fiscal-close-DuXDgVvb.mjs";
2
-
3
- export { closeFiscalPeriod, generateBalanceSheet, generateCashFlow, generateGeneralLedger, generateIncomeStatement, generateTrialBalance, reopenFiscalPeriod };
1
+ import { d as DEFAULT_BUCKETS, f as generateAgedBalance, g as generateBalanceSheet, h as generateIncomeStatement, l as generateBudgetVsActual, m as generateGeneralLedger, n as reopenFiscalPeriod, o as generateRevaluation, p as generateCashFlow, t as closeFiscalPeriod, u as generateDimensionBreakdown, x as generateTrialBalance } from "../fiscal-close-B6LhQ10f.mjs";
2
+ export { DEFAULT_BUCKETS, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
@@ -1,2 +1,2 @@
1
- import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-1C2sZvB2.mjs";
2
- export { wireAccountMethods, wireJournalEntryMethods };
1
+ import { n as wireAccountMethods, r as wireJournalEntryMethods, t as wireReconciliationMethods } from "../reconciliation.repository-CW4-8q90.mjs";
2
+ export { wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };
@@ -1,3 +1,2 @@
1
- import { n as wireJournalEntryMethods, t as wireAccountMethods } from "../account.repository-Crf5DGO4.mjs";
2
-
3
- export { wireAccountMethods, wireJournalEntryMethods };
1
+ import { n as wireJournalEntryMethods, r as wireReconciliationMethods, t as wireAccountMethods } from "../account.repository-BpkSd6q3.mjs";
2
+ export { wireAccountMethods, wireJournalEntryMethods, wireReconciliationMethods };