@goweekdays/core 2.15.0 → 2.15.1

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/index.js CHANGED
@@ -102,6 +102,7 @@ __export(src_exports, {
102
102
  modelJobSummary: () => modelJobSummary,
103
103
  modelJournal: () => modelJournal,
104
104
  modelJournalLine: () => modelJournalLine,
105
+ modelJournalTransaction: () => modelJournalTransaction,
105
106
  modelLedgerBill: () => modelLedgerBill,
106
107
  modelMember: () => modelMember,
107
108
  modelOption: () => modelOption,
@@ -171,6 +172,11 @@ __export(src_exports, {
171
172
  schemaJournalLineRepo: () => schemaJournalLineRepo,
172
173
  schemaJournalRepo: () => schemaJournalRepo,
173
174
  schemaJournalRepoUpdate: () => schemaJournalRepoUpdate,
175
+ schemaJournalTransactionAccount: () => schemaJournalTransactionAccount,
176
+ schemaJournalTransactionCtrl: () => schemaJournalTransactionCtrl,
177
+ schemaJournalTransactionCtrlUpdate: () => schemaJournalTransactionCtrlUpdate,
178
+ schemaJournalTransactionGetAll: () => schemaJournalTransactionGetAll,
179
+ schemaJournalTransactionRepo: () => schemaJournalTransactionRepo,
174
180
  schemaLanguage: () => schemaLanguage,
175
181
  schemaLedgerBill: () => schemaLedgerBill,
176
182
  schemaLedgerBillingSummary: () => schemaLedgerBillingSummary,
@@ -252,6 +258,9 @@ __export(src_exports, {
252
258
  useJournalLineRepo: () => useJournalLineRepo,
253
259
  useJournalRepo: () => useJournalRepo,
254
260
  useJournalService: () => useJournalService,
261
+ useJournalTransactionController: () => useJournalTransactionController,
262
+ useJournalTransactionRepo: () => useJournalTransactionRepo,
263
+ useJournalTransactionService: () => useJournalTransactionService,
255
264
  useLedgerBillingController: () => useLedgerBillingController,
256
265
  useLedgerBillingRepo: () => useLedgerBillingRepo,
257
266
  useMemberController: () => useMemberController,
@@ -19145,7 +19154,12 @@ function useAccountBalanceRepo() {
19145
19154
  { key: { account: 1 } },
19146
19155
  { key: { "period.fiscalYear": 1 } },
19147
19156
  {
19148
- key: { org: 1, account: 1, "period.fiscalYear": 1, "period.month": 1 },
19157
+ key: {
19158
+ org: 1,
19159
+ account: 1,
19160
+ "period.fiscalYear": 1,
19161
+ "period.month": 1
19162
+ },
19149
19163
  unique: true,
19150
19164
  name: "unique_balance_per_account_month"
19151
19165
  }
@@ -19320,9 +19334,7 @@ function useAccountBalanceRepo() {
19320
19334
  if (error instanceof import_utils99.AppError) {
19321
19335
  throw error;
19322
19336
  }
19323
- throw new import_utils99.InternalServerError(
19324
- "Failed to get account balance."
19325
- );
19337
+ throw new import_utils99.InternalServerError("Failed to get account balance.");
19326
19338
  }
19327
19339
  }
19328
19340
  async function getYearBalancesByAccount(options) {
@@ -19347,9 +19359,7 @@ function useAccountBalanceRepo() {
19347
19359
  if (error instanceof import_utils99.AppError) {
19348
19360
  throw error;
19349
19361
  }
19350
- throw new import_utils99.InternalServerError(
19351
- "Failed to get account balances by year."
19352
- );
19362
+ throw new import_utils99.InternalServerError("Failed to get account balances by year.");
19353
19363
  }
19354
19364
  }
19355
19365
  async function updateById(account, org, fiscalYear, month, options, session) {
@@ -19435,7 +19445,7 @@ var journalTypes = [
19435
19445
  var schemaJournalBase = {
19436
19446
  date: import_joi83.default.date().required(),
19437
19447
  type: import_joi83.default.string().allow(...journalTypes).required(),
19438
- transaction: import_joi83.default.string().required(),
19448
+ transaction: import_joi83.default.string().hex().required(),
19439
19449
  customer: import_joi83.default.string().hex().optional().allow("", null),
19440
19450
  description: import_joi83.default.string().required(),
19441
19451
  invoiceNumber: import_joi83.default.string().optional().allow("", null)
@@ -19452,6 +19462,7 @@ var schemaJournalRepo = import_joi83.default.object({
19452
19462
  id: import_joi83.default.string().required(),
19453
19463
  createdBy: import_joi83.default.string().hex().required(),
19454
19464
  createdByName: import_joi83.default.string().required(),
19465
+ transactionName: import_joi83.default.string().required(),
19455
19466
  customerName: import_joi83.default.string().optional().allow("", null),
19456
19467
  status: import_joi83.default.string().valid(...JournalStatuses).required(),
19457
19468
  reversedEntry: import_joi83.default.string().hex().optional().allow("", null),
@@ -19463,6 +19474,7 @@ var schemaJournalRepoUpdate = import_joi83.default.object({
19463
19474
  date: import_joi83.default.date().optional().allow("", null),
19464
19475
  type: import_joi83.default.string().allow(...journalTypes).optional().allow("", null),
19465
19476
  transaction: import_joi83.default.string().optional().allow("", null),
19477
+ transactionName: import_joi83.default.string().optional().allow("", null),
19466
19478
  description: import_joi83.default.string().optional().allow("", null),
19467
19479
  status: import_joi83.default.string().valid(...JournalStatuses).optional().allow("", null),
19468
19480
  customerName: import_joi83.default.string().optional().allow("", null),
@@ -19491,6 +19503,11 @@ function modelJournal(data) {
19491
19503
  } catch (error2) {
19492
19504
  throw new import_utils100.BadRequestError(`Invalid org ID: ${data.org}`);
19493
19505
  }
19506
+ try {
19507
+ data.transaction = new import_mongodb50.ObjectId(data.transaction);
19508
+ } catch (error2) {
19509
+ throw new import_utils100.BadRequestError(`Invalid transaction ID: ${data.transaction}`);
19510
+ }
19494
19511
  try {
19495
19512
  data.createdBy = new import_mongodb50.ObjectId(data.createdBy);
19496
19513
  } catch (error2) {
@@ -19535,6 +19552,7 @@ function modelJournal(data) {
19535
19552
  org: data.org,
19536
19553
  type: data.type,
19537
19554
  transaction: data.transaction,
19555
+ transactionName: data.transactionName ?? "",
19538
19556
  date,
19539
19557
  invoiceNumber: data.invoiceNumber ?? "",
19540
19558
  customer: data.customer ?? "",
@@ -19831,6 +19849,24 @@ function useJournalRepo() {
19831
19849
  throw new import_utils101.InternalServerError("Failed to update journal status.");
19832
19850
  }
19833
19851
  }
19852
+ async function existsByTransactionId(org, transactionId) {
19853
+ try {
19854
+ org = new import_mongodb51.ObjectId(org);
19855
+ } catch {
19856
+ throw new import_utils101.BadRequestError("Invalid org ID.");
19857
+ }
19858
+ try {
19859
+ transactionId = new import_mongodb51.ObjectId(transactionId);
19860
+ } catch {
19861
+ throw new import_utils101.BadRequestError("Invalid transaction ID.");
19862
+ }
19863
+ const count = await repo.collection.countDocuments({
19864
+ org,
19865
+ transaction: transactionId,
19866
+ status: { $ne: "voided" }
19867
+ });
19868
+ return count > 0;
19869
+ }
19834
19870
  return {
19835
19871
  createIndexes,
19836
19872
  delCachedData: repo.delCachedData,
@@ -19839,12 +19875,13 @@ function useJournalRepo() {
19839
19875
  getById,
19840
19876
  updateById,
19841
19877
  deleteById,
19842
- updateStatusById
19878
+ updateStatusById,
19879
+ existsByTransactionId
19843
19880
  };
19844
19881
  }
19845
19882
 
19846
19883
  // src/resources/finance-journal/finance.journal.service.ts
19847
- var import_joi90 = __toESM(require("joi"));
19884
+ var import_joi93 = __toESM(require("joi"));
19848
19885
 
19849
19886
  // src/resources/finance-journal-line/finance.journal.line.model.ts
19850
19887
  var import_utils102 = require("@goweekdays/utils");
@@ -20175,6 +20212,23 @@ function useJournalLineRepo() {
20175
20212
  throw new import_utils103.InternalServerError("Failed to delete journal line.");
20176
20213
  }
20177
20214
  }
20215
+ async function updateBalanceById(_id, balance, session) {
20216
+ try {
20217
+ _id = new import_mongodb53.ObjectId(_id);
20218
+ } catch (error) {
20219
+ throw new import_utils103.BadRequestError("Invalid Journal Line ID.");
20220
+ }
20221
+ try {
20222
+ await collection.updateOne(
20223
+ { _id },
20224
+ { $set: { balance, updatedAt: /* @__PURE__ */ new Date() } },
20225
+ { session }
20226
+ );
20227
+ delCachedData();
20228
+ } catch (error) {
20229
+ throw new import_utils103.InternalServerError("Failed to update journal line balance.");
20230
+ }
20231
+ }
20178
20232
  async function updateStatusByJournal(journalEntry, status2, session) {
20179
20233
  const validation = import_joi86.default.object({
20180
20234
  journalEntry: import_joi86.default.string().hex().required(),
@@ -20215,13 +20269,14 @@ function useJournalLineRepo() {
20215
20269
  getById,
20216
20270
  getByEntry,
20217
20271
  updateById,
20272
+ updateBalanceById,
20218
20273
  deleteById,
20219
20274
  updateStatusByJournal
20220
20275
  };
20221
20276
  }
20222
20277
 
20223
20278
  // src/resources/finance-journal/finance.journal.service.ts
20224
- var import_utils108 = require("@goweekdays/utils");
20279
+ var import_utils112 = require("@goweekdays/utils");
20225
20280
 
20226
20281
  // src/resources/finance-journal/finance.journal.util.ts
20227
20282
  var import_utils104 = require("@goweekdays/utils");
@@ -20629,6 +20684,529 @@ function useJournalLogRepo() {
20629
20684
  var import_joi89 = __toESM(require("joi"));
20630
20685
  var import_utils107 = require("@goweekdays/utils");
20631
20686
 
20687
+ // src/resources/finance-journal-transaction/finance.journal.transaction.model.ts
20688
+ var import_utils108 = require("@goweekdays/utils");
20689
+ var import_joi90 = __toESM(require("joi"));
20690
+ var import_mongodb56 = require("mongodb");
20691
+ var schemaJournalTransactionAccount = import_joi90.default.object({
20692
+ title: import_joi90.default.string().required(),
20693
+ value: import_joi90.default.string().hex().required()
20694
+ });
20695
+ var schemaJournalTransactionBase = {
20696
+ type: import_joi90.default.string().valid(...journalTypes).required(),
20697
+ code: import_joi90.default.string().required(),
20698
+ title: import_joi90.default.string().required(),
20699
+ debits: import_joi90.default.array().items(schemaJournalTransactionAccount).min(1).required(),
20700
+ credits: import_joi90.default.array().items(schemaJournalTransactionAccount).min(1).required(),
20701
+ template: import_joi90.default.boolean().required(),
20702
+ description: import_joi90.default.string().optional().allow("", null)
20703
+ };
20704
+ var schemaJournalTransactionCtrl = import_joi90.default.object({
20705
+ ...schemaJournalTransactionBase,
20706
+ org: import_joi90.default.string().hex().required(),
20707
+ createdBy: import_joi90.default.string().hex().required()
20708
+ });
20709
+ var schemaJournalTransactionCtrlUpdate = import_joi90.default.object({
20710
+ code: import_joi90.default.string().optional(),
20711
+ title: import_joi90.default.string().optional(),
20712
+ debits: import_joi90.default.array().items(schemaJournalTransactionAccount).min(1).optional(),
20713
+ credits: import_joi90.default.array().items(schemaJournalTransactionAccount).min(1).optional(),
20714
+ template: import_joi90.default.boolean().optional(),
20715
+ description: import_joi90.default.string().optional().allow("", null)
20716
+ });
20717
+ var schemaJournalTransactionRepo = import_joi90.default.object({
20718
+ ...schemaJournalTransactionBase,
20719
+ org: import_joi90.default.string().hex().required(),
20720
+ createdBy: import_joi90.default.string().hex().required(),
20721
+ createdByName: import_joi90.default.string().optional().allow("", null)
20722
+ });
20723
+ var schemaJournalTransactionGetAll = import_joi90.default.object({
20724
+ org: import_joi90.default.string().hex().required(),
20725
+ type: import_joi90.default.string().optional().allow("", null),
20726
+ search: import_joi90.default.string().optional().allow("", null),
20727
+ page: import_joi90.default.number().optional().allow("", null),
20728
+ limit: import_joi90.default.number().max(100).optional().allow("", null)
20729
+ });
20730
+ function modelJournalTransaction(data) {
20731
+ const { error } = schemaJournalTransactionRepo.validate(data);
20732
+ if (error) {
20733
+ throw new import_utils108.BadRequestError(
20734
+ `Invalid journal transaction data: ${error.message}`
20735
+ );
20736
+ }
20737
+ try {
20738
+ data.org = new import_mongodb56.ObjectId(data.org);
20739
+ } catch (error2) {
20740
+ throw new import_utils108.BadRequestError(`Invalid org ID: ${data.org}`);
20741
+ }
20742
+ try {
20743
+ data.createdBy = new import_mongodb56.ObjectId(data.createdBy);
20744
+ } catch (error2) {
20745
+ throw new import_utils108.BadRequestError(`Invalid createdBy ID: ${data.createdBy}`);
20746
+ }
20747
+ for (let i = 0; i < data.debits.length; i++) {
20748
+ try {
20749
+ data.debits[i].value = new import_mongodb56.ObjectId(data.debits[i].value);
20750
+ } catch (error2) {
20751
+ throw new import_utils108.BadRequestError(`Invalid debit account ID at index ${i}`);
20752
+ }
20753
+ }
20754
+ for (let i = 0; i < data.credits.length; i++) {
20755
+ try {
20756
+ data.credits[i].value = new import_mongodb56.ObjectId(data.credits[i].value);
20757
+ } catch (error2) {
20758
+ throw new import_utils108.BadRequestError(`Invalid credit account ID at index ${i}`);
20759
+ }
20760
+ }
20761
+ const now = /* @__PURE__ */ new Date();
20762
+ return {
20763
+ _id: data._id,
20764
+ org: data.org,
20765
+ type: data.type,
20766
+ code: data.code,
20767
+ title: data.title,
20768
+ debits: data.debits,
20769
+ credits: data.credits,
20770
+ template: data.template ?? false,
20771
+ description: data.description ?? "",
20772
+ createdBy: data.createdBy,
20773
+ createdByName: data.createdByName ?? "",
20774
+ createdAt: data.createdAt ?? now,
20775
+ updatedAt: data.updatedAt ?? ""
20776
+ };
20777
+ }
20778
+
20779
+ // src/resources/finance-journal-transaction/finance.journal.transaction.repository.ts
20780
+ var import_utils109 = require("@goweekdays/utils");
20781
+ var import_mongodb57 = require("mongodb");
20782
+ function useJournalTransactionRepo() {
20783
+ const namespace_collection = "finance.journal.transactions";
20784
+ const repo = (0, import_utils109.useRepo)(namespace_collection);
20785
+ async function createIndexes() {
20786
+ try {
20787
+ await repo.collection.createIndexes([
20788
+ { key: { org: 1, type: 1, _id: 1 } },
20789
+ { key: { org: 1 } },
20790
+ { key: { type: 1 } },
20791
+ { key: { code: 1 } },
20792
+ { key: { org: 1, type: 1 } },
20793
+ {
20794
+ key: {
20795
+ title: "text",
20796
+ code: "text",
20797
+ description: "text"
20798
+ }
20799
+ },
20800
+ {
20801
+ key: { org: 1, type: 1, code: 1 },
20802
+ unique: true,
20803
+ name: "unique_org_type_code"
20804
+ }
20805
+ ]);
20806
+ } catch (error) {
20807
+ throw new Error("Failed to create index on journal transactions.");
20808
+ }
20809
+ }
20810
+ async function add(value, session) {
20811
+ try {
20812
+ value = modelJournalTransaction(value);
20813
+ const res = await repo.collection.insertOne(value, { session });
20814
+ repo.delCachedData();
20815
+ return res.insertedId;
20816
+ } catch (error) {
20817
+ import_utils109.logger.log({ level: "error", message: error.message });
20818
+ throw new import_utils109.BadRequestError(
20819
+ `Failed to create journal transaction: ${error.message}`
20820
+ );
20821
+ }
20822
+ }
20823
+ async function getAll(options) {
20824
+ options.page = options.page && options.page > 0 ? options.page - 1 : 0;
20825
+ options.limit = options.limit ?? 10;
20826
+ options.search = options.search ?? "";
20827
+ const { error } = schemaJournalTransactionGetAll.validate(options);
20828
+ if (error) {
20829
+ throw new import_utils109.BadRequestError(`Invalid query parameters: ${error.message}`);
20830
+ }
20831
+ const query = {};
20832
+ const cacheKeyOptions = {
20833
+ page: options.page,
20834
+ limit: options.limit
20835
+ };
20836
+ try {
20837
+ query.org = new import_mongodb57.ObjectId(options.org);
20838
+ cacheKeyOptions.org = String(options.org);
20839
+ } catch (error2) {
20840
+ throw new import_utils109.BadRequestError("Invalid organization ID.");
20841
+ }
20842
+ if (options.type) {
20843
+ query.type = options.type;
20844
+ cacheKeyOptions.type = options.type;
20845
+ }
20846
+ if (options.search) {
20847
+ query.$text = { $search: options.search };
20848
+ cacheKeyOptions.search = options.search;
20849
+ }
20850
+ const cacheKey = (0, import_utils109.makeCacheKey)(namespace_collection, cacheKeyOptions);
20851
+ import_utils109.logger.log({
20852
+ level: "info",
20853
+ message: `Cache key for getAll journal transactions: ${cacheKey}`
20854
+ });
20855
+ try {
20856
+ const cached = await repo.getCache(cacheKey);
20857
+ if (cached) {
20858
+ import_utils109.logger.log({
20859
+ level: "info",
20860
+ message: `Cache hit for getAll journal transactions: ${cacheKey}`
20861
+ });
20862
+ return cached;
20863
+ }
20864
+ const items = await repo.collection.aggregate([
20865
+ { $match: query },
20866
+ { $sort: { title: 1 } },
20867
+ { $skip: options.page * options.limit },
20868
+ { $limit: options.limit }
20869
+ ]).toArray();
20870
+ const length = await repo.collection.countDocuments(query);
20871
+ const data = (0, import_utils109.paginate)(items, options.page, options.limit, length);
20872
+ repo.setCache(cacheKey, data, 600).then(() => {
20873
+ import_utils109.logger.log({
20874
+ level: "info",
20875
+ message: `Cache set for getAll journal transactions: ${cacheKey}`
20876
+ });
20877
+ }).catch((err) => {
20878
+ import_utils109.logger.log({
20879
+ level: "error",
20880
+ message: `Failed to set cache for getAll journal transactions: ${err.message}`
20881
+ });
20882
+ });
20883
+ return data;
20884
+ } catch (error2) {
20885
+ import_utils109.logger.log({ level: "error", message: `${error2}` });
20886
+ throw error2;
20887
+ }
20888
+ }
20889
+ async function getByOrgTypeId(options) {
20890
+ try {
20891
+ options._id = new import_mongodb57.ObjectId(options._id);
20892
+ } catch (error) {
20893
+ throw new import_utils109.BadRequestError("Invalid ID.");
20894
+ }
20895
+ try {
20896
+ options.org = new import_mongodb57.ObjectId(options.org);
20897
+ } catch (error) {
20898
+ throw new import_utils109.BadRequestError("Invalid organization ID.");
20899
+ }
20900
+ const cacheKey = (0, import_utils109.makeCacheKey)(namespace_collection, {
20901
+ org: String(options.org),
20902
+ type: options.type,
20903
+ _id: String(options._id),
20904
+ tag: "org_type_id"
20905
+ });
20906
+ try {
20907
+ const cached = await repo.getCache(cacheKey);
20908
+ if (cached) {
20909
+ import_utils109.logger.log({
20910
+ level: "info",
20911
+ message: `Cache hit for get by org + type + id journal transaction: ${cacheKey}`
20912
+ });
20913
+ return cached;
20914
+ }
20915
+ const result = await repo.collection.findOne({
20916
+ _id: options._id,
20917
+ org: options.org,
20918
+ type: options.type
20919
+ });
20920
+ repo.setCache(cacheKey, result, 300).then(() => {
20921
+ import_utils109.logger.log({
20922
+ level: "info",
20923
+ message: `Cache set for journal transaction by org + type + id: ${cacheKey}`
20924
+ });
20925
+ }).catch((err) => {
20926
+ import_utils109.logger.log({
20927
+ level: "error",
20928
+ message: `Failed to set cache for journal transaction by org + type + id: ${err.message}`
20929
+ });
20930
+ });
20931
+ return result;
20932
+ } catch (error) {
20933
+ if (error instanceof import_utils109.AppError) {
20934
+ throw error;
20935
+ } else {
20936
+ throw new import_utils109.InternalServerError("Failed to get journal transaction.");
20937
+ }
20938
+ }
20939
+ }
20940
+ async function updateById(_id, options, session) {
20941
+ const { error } = schemaJournalTransactionCtrlUpdate.validate(options);
20942
+ if (error) {
20943
+ throw new import_utils109.BadRequestError(
20944
+ `Invalid journal transaction update data: ${error.message}`
20945
+ );
20946
+ }
20947
+ try {
20948
+ _id = new import_mongodb57.ObjectId(_id);
20949
+ } catch (error2) {
20950
+ throw new import_utils109.BadRequestError("Invalid journal transaction ID.");
20951
+ }
20952
+ if (options.debits) {
20953
+ for (let i = 0; i < options.debits.length; i++) {
20954
+ try {
20955
+ options.debits[i].value = new import_mongodb57.ObjectId(options.debits[i].value);
20956
+ } catch (error2) {
20957
+ throw new import_utils109.BadRequestError(`Invalid debit account ID at index ${i}`);
20958
+ }
20959
+ }
20960
+ }
20961
+ if (options.credits) {
20962
+ for (let i = 0; i < options.credits.length; i++) {
20963
+ try {
20964
+ options.credits[i].value = new import_mongodb57.ObjectId(options.credits[i].value);
20965
+ } catch (error2) {
20966
+ throw new import_utils109.BadRequestError(`Invalid credit account ID at index ${i}`);
20967
+ }
20968
+ }
20969
+ }
20970
+ try {
20971
+ await repo.collection.updateOne(
20972
+ { _id },
20973
+ { $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } },
20974
+ { session }
20975
+ );
20976
+ repo.delCachedData();
20977
+ return "Successfully updated journal transaction.";
20978
+ } catch (error2) {
20979
+ throw new import_utils109.InternalServerError("Failed to update journal transaction.");
20980
+ }
20981
+ }
20982
+ async function deleteById(_id, session) {
20983
+ try {
20984
+ _id = new import_mongodb57.ObjectId(_id);
20985
+ } catch (error) {
20986
+ throw new import_utils109.BadRequestError("Invalid ID.");
20987
+ }
20988
+ try {
20989
+ await repo.collection.deleteOne({ _id }, { session });
20990
+ repo.delCachedData();
20991
+ return "Successfully deleted journal transaction.";
20992
+ } catch (error) {
20993
+ throw new import_utils109.InternalServerError("Failed to delete journal transaction.");
20994
+ }
20995
+ }
20996
+ return {
20997
+ createIndexes,
20998
+ add,
20999
+ getAll,
21000
+ getByOrgTypeId,
21001
+ updateById,
21002
+ deleteById
21003
+ };
21004
+ }
21005
+
21006
+ // src/resources/finance-journal-transaction/finance.journal.transaction.service.ts
21007
+ var import_utils110 = require("@goweekdays/utils");
21008
+ var import_joi91 = __toESM(require("joi"));
21009
+ function validateNoDuplicateAccounts(debits, credits) {
21010
+ const debitIds = debits.map((d) => String(d.value));
21011
+ const creditIds = credits.map((c) => String(c.value));
21012
+ const debitSet = new Set(debitIds);
21013
+ if (debitSet.size !== debitIds.length) {
21014
+ throw new import_utils110.BadRequestError("Duplicate accounts found in debits.");
21015
+ }
21016
+ const creditSet = new Set(creditIds);
21017
+ if (creditSet.size !== creditIds.length) {
21018
+ throw new import_utils110.BadRequestError("Duplicate accounts found in credits.");
21019
+ }
21020
+ }
21021
+ function useJournalTransactionService() {
21022
+ const {
21023
+ add: _add,
21024
+ updateById: _updateById,
21025
+ getByOrgTypeId
21026
+ } = useJournalTransactionRepo();
21027
+ const { getUserById } = useUserRepo();
21028
+ async function add(value) {
21029
+ const { error } = schemaJournalTransactionCtrl.validate(value);
21030
+ if (error) {
21031
+ throw new import_utils110.BadRequestError(
21032
+ `Invalid journal transaction data: ${error.message}`
21033
+ );
21034
+ }
21035
+ validateNoDuplicateAccounts(value.debits, value.credits);
21036
+ const user = await getUserById(value.createdBy);
21037
+ if (!user) {
21038
+ throw new import_utils110.NotFoundError("User not found.");
21039
+ }
21040
+ value.createdByName = `${user.firstName} ${user.lastName}`;
21041
+ try {
21042
+ const id = await _add(value);
21043
+ return id;
21044
+ } catch (error2) {
21045
+ if (error2 instanceof import_utils110.AppError) {
21046
+ throw error2;
21047
+ }
21048
+ throw new import_utils110.InternalServerError(
21049
+ `Failed to create journal transaction: ${error2.message}`
21050
+ );
21051
+ }
21052
+ }
21053
+ async function updateById(org, type, _id, options) {
21054
+ const { error: idError } = import_joi91.default.string().hex().required().validate(_id);
21055
+ if (idError) {
21056
+ throw new import_utils110.BadRequestError("Invalid journal transaction ID.");
21057
+ }
21058
+ const { error } = schemaJournalTransactionCtrlUpdate.validate(options);
21059
+ if (error) {
21060
+ throw new import_utils110.BadRequestError(`Invalid update data: ${error.message}`);
21061
+ }
21062
+ const existing = await getByOrgTypeId({ org, type, _id });
21063
+ if (!existing) {
21064
+ throw new import_utils110.NotFoundError("Journal transaction not found.");
21065
+ }
21066
+ const debits = options.debits ?? existing.debits;
21067
+ const credits = options.credits ?? existing.credits;
21068
+ validateNoDuplicateAccounts(debits, credits);
21069
+ try {
21070
+ return await _updateById(_id, options);
21071
+ } catch (error2) {
21072
+ if (error2 instanceof import_utils110.AppError) {
21073
+ throw error2;
21074
+ }
21075
+ throw new import_utils110.InternalServerError(
21076
+ `Failed to update journal transaction: ${error2.message}`
21077
+ );
21078
+ }
21079
+ }
21080
+ return {
21081
+ add,
21082
+ updateById
21083
+ };
21084
+ }
21085
+
21086
+ // src/resources/finance-journal-transaction/finance.journal.transaction.controller.ts
21087
+ var import_joi92 = __toESM(require("joi"));
21088
+ var import_utils111 = require("@goweekdays/utils");
21089
+ function useJournalTransactionController() {
21090
+ const {
21091
+ getAll: _getAll,
21092
+ getByOrgTypeId: _getByOrgTypeId,
21093
+ deleteById: _deleteById
21094
+ } = useJournalTransactionRepo();
21095
+ const { add: _add, updateById: _updateById } = useJournalTransactionService();
21096
+ async function add(req, res, next) {
21097
+ const value = req.body;
21098
+ const { error } = schemaJournalTransactionCtrl.validate(value);
21099
+ if (error) {
21100
+ next(new import_utils111.BadRequestError(error.message));
21101
+ return;
21102
+ }
21103
+ try {
21104
+ const id = await _add(value);
21105
+ res.json({ message: "Journal transaction created successfully.", id });
21106
+ return;
21107
+ } catch (error2) {
21108
+ next(error2);
21109
+ }
21110
+ }
21111
+ async function getAll(req, res, next) {
21112
+ const org = req.params.org ?? "";
21113
+ const type = req.params.type ?? "";
21114
+ const search = req.query.search ?? "";
21115
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
21116
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
21117
+ const { error } = schemaJournalTransactionGetAll.validate({
21118
+ ...req.params,
21119
+ ...req.query
21120
+ });
21121
+ if (!isFinite(page)) {
21122
+ next(new import_utils111.BadRequestError("Invalid page number."));
21123
+ return;
21124
+ }
21125
+ if (!isFinite(limit)) {
21126
+ next(new import_utils111.BadRequestError("Invalid limit number."));
21127
+ return;
21128
+ }
21129
+ if (error) {
21130
+ next(new import_utils111.BadRequestError(error.message));
21131
+ return;
21132
+ }
21133
+ try {
21134
+ const data = await _getAll({ org, type, search, page, limit });
21135
+ res.json(data);
21136
+ return;
21137
+ } catch (error2) {
21138
+ next(error2);
21139
+ }
21140
+ }
21141
+ async function getByOrgTypeId(req, res, next) {
21142
+ const { error } = import_joi92.default.object({
21143
+ org: import_joi92.default.string().hex().required(),
21144
+ type: import_joi92.default.string().required(),
21145
+ id: import_joi92.default.string().hex().required()
21146
+ }).validate(req.params);
21147
+ if (error) {
21148
+ next(new import_utils111.BadRequestError(error.message));
21149
+ return;
21150
+ }
21151
+ const _id = req.params.id;
21152
+ const org = req.params.org;
21153
+ const type = req.params.type;
21154
+ try {
21155
+ const data = await _getByOrgTypeId({ org, type, _id });
21156
+ res.json(data);
21157
+ return;
21158
+ } catch (error2) {
21159
+ next(error2);
21160
+ }
21161
+ }
21162
+ async function updateById(req, res, next) {
21163
+ const value = req.body;
21164
+ const { error } = import_joi92.default.object({
21165
+ id: import_joi92.default.string().hex().required(),
21166
+ org: import_joi92.default.string().hex().required(),
21167
+ type: import_joi92.default.string().allow(...journalTypes).required()
21168
+ }).concat(schemaJournalTransactionCtrlUpdate).validate({ ...req.params, ...value });
21169
+ if (error) {
21170
+ next(new import_utils111.BadRequestError(error.message));
21171
+ return;
21172
+ }
21173
+ const id = req.params.id ?? "";
21174
+ const org = req.params.org ?? "";
21175
+ const type = req.params.type ?? "";
21176
+ try {
21177
+ const message = await _updateById(org, type, id, value);
21178
+ res.json({ message });
21179
+ return;
21180
+ } catch (error2) {
21181
+ next(error2);
21182
+ }
21183
+ }
21184
+ async function deleteById(req, res, next) {
21185
+ const id = req.params.id;
21186
+ const { error } = import_joi92.default.object({
21187
+ id: import_joi92.default.string().hex().required()
21188
+ }).validate({ id });
21189
+ if (error) {
21190
+ next(new import_utils111.BadRequestError(error.message));
21191
+ return;
21192
+ }
21193
+ try {
21194
+ const message = await _deleteById(id);
21195
+ res.json({ message });
21196
+ return;
21197
+ } catch (error2) {
21198
+ next(error2);
21199
+ }
21200
+ }
21201
+ return {
21202
+ add,
21203
+ getAll,
21204
+ getByOrgTypeId,
21205
+ updateById,
21206
+ deleteById
21207
+ };
21208
+ }
21209
+
20632
21210
  // src/resources/finance-journal/finance.journal.service.ts
20633
21211
  function useJournalService() {
20634
21212
  const {
@@ -20638,10 +21216,12 @@ function useJournalService() {
20638
21216
  updateById: _updateById,
20639
21217
  delCachedData: delJournalCache
20640
21218
  } = useJournalRepo();
21219
+ const { getByOrgTypeId: getJournalTransaction } = useJournalTransactionRepo();
20641
21220
  const {
20642
21221
  add: addLine,
20643
21222
  getByEntry: getJournalLinesByEntry,
20644
21223
  updateById: updateJournalLineById,
21224
+ updateBalanceById: updateJournalLineBalance,
20645
21225
  deleteById: deleteJournalLineById
20646
21226
  } = useJournalLineRepo();
20647
21227
  const { getUserById } = useUserRepo();
@@ -20655,21 +21235,21 @@ function useJournalService() {
20655
21235
  const { add: addJournalLog } = useJournalLogRepo();
20656
21236
  const { getById: getCustomerById } = useCustomerRepo();
20657
21237
  async function add(entry, lines) {
20658
- const { error } = import_joi90.default.object({
21238
+ const { error } = import_joi93.default.object({
20659
21239
  journalEntry: schemaJournalCtrl.required(),
20660
- journalLines: import_joi90.default.array().items(schemaJournalLineCtrl).min(1).required()
21240
+ journalLines: import_joi93.default.array().items(schemaJournalLineCtrl).min(1).required()
20661
21241
  }).validate({
20662
21242
  journalEntry: entry,
20663
21243
  journalLines: lines
20664
21244
  });
20665
21245
  if (error) {
20666
- throw new import_utils108.BadRequestError(
21246
+ throw new import_utils112.BadRequestError(
20667
21247
  `Invalid general journal data: ${error.message}`
20668
21248
  );
20669
21249
  }
20670
- const session = import_utils108.useAtlas.getClient()?.startSession();
21250
+ const session = import_utils112.useAtlas.getClient()?.startSession();
20671
21251
  if (!session) {
20672
- throw new import_utils108.BadRequestError("Unable to start database session.");
21252
+ throw new import_utils112.BadRequestError("Unable to start database session.");
20673
21253
  }
20674
21254
  try {
20675
21255
  session.startTransaction();
@@ -20684,16 +21264,25 @@ function useJournalService() {
20684
21264
  await validateJournalLines(lines);
20685
21265
  const user = await getUserById(entry.createdBy);
20686
21266
  if (!user) {
20687
- throw new import_utils108.NotFoundError("User not found.");
21267
+ throw new import_utils112.NotFoundError("User not found.");
20688
21268
  }
20689
21269
  entry.createdByName = `${user.firstName} ${user.lastName}`;
20690
21270
  if (entry.customer) {
20691
21271
  const customer = await getCustomerById(String(entry.customer));
20692
21272
  if (!customer) {
20693
- throw new import_utils108.NotFoundError("Customer not found.");
21273
+ throw new import_utils112.NotFoundError("Customer not found.");
20694
21274
  }
20695
21275
  entry.customerName = `${customer.firstName} ${customer.lastName}`;
20696
21276
  }
21277
+ const journalTx = await getJournalTransaction({
21278
+ org: String(entry.org),
21279
+ type: entry.type,
21280
+ _id: String(entry.transaction)
21281
+ });
21282
+ if (!journalTx) {
21283
+ throw new import_utils112.NotFoundError("Transaction not found.");
21284
+ }
21285
+ entry.transactionName = journalTx.title;
20697
21286
  entry.status = "draft";
20698
21287
  const entryId = await _add(entry, session);
20699
21288
  for (const line of lines) {
@@ -20701,7 +21290,7 @@ function useJournalService() {
20701
21290
  line.journalEntry = entryId.toString();
20702
21291
  const account = await getAccountById(String(line.account));
20703
21292
  if (!account) {
20704
- throw new import_utils108.NotFoundError(`Account not found: ${line.account}`);
21293
+ throw new import_utils112.NotFoundError(`Account not found: ${line.account}`);
20705
21294
  }
20706
21295
  line.accountName = account.name;
20707
21296
  line.accountCode = account.code;
@@ -20733,10 +21322,10 @@ function useJournalService() {
20733
21322
  return "Journal entry created successfully.";
20734
21323
  } catch (error2) {
20735
21324
  await session.abortTransaction();
20736
- if (error2 instanceof import_utils108.AppError) {
21325
+ if (error2 instanceof import_utils112.AppError) {
20737
21326
  throw error2;
20738
21327
  }
20739
- throw new import_utils108.InternalServerError(
21328
+ throw new import_utils112.InternalServerError(
20740
21329
  `Failed to create general journal: ${error2.message}`
20741
21330
  );
20742
21331
  } finally {
@@ -20750,18 +21339,19 @@ function useJournalService() {
20750
21339
  getYearBalancesByAccount,
20751
21340
  updateById: updateAccountBalance
20752
21341
  } = useAccountBalanceRepo();
21342
+ const { getByOrg: getBusinessProfileByOrg } = useBusinessProfileRepo();
20753
21343
  async function updateStatusById(_id, status2, user) {
20754
- const validation = import_joi90.default.object({
20755
- _id: import_joi90.default.string().hex().required(),
20756
- status: import_joi90.default.string().valid("posted", "voided").required()
21344
+ const validation = import_joi93.default.object({
21345
+ _id: import_joi93.default.string().hex().required(),
21346
+ status: import_joi93.default.string().valid("posted", "voided").required()
20757
21347
  });
20758
21348
  const { error } = validation.validate({ _id, status: status2 });
20759
21349
  if (error) {
20760
- throw new import_utils108.BadRequestError(`Invalid input: ${error.message}`);
21350
+ throw new import_utils112.BadRequestError(`Invalid input: ${error.message}`);
20761
21351
  }
20762
- const session = import_utils108.useAtlas.getClient()?.startSession();
21352
+ const session = import_utils112.useAtlas.getClient()?.startSession();
20763
21353
  if (!session) {
20764
- throw new import_utils108.BadRequestError("Unable to start database session.");
21354
+ throw new import_utils112.BadRequestError("Unable to start database session.");
20765
21355
  }
20766
21356
  try {
20767
21357
  let computeClosingBalance2 = function(openingDebit, openingCredit, movementDebit, movementCredit) {
@@ -20775,18 +21365,18 @@ function useJournalService() {
20775
21365
  var computeClosingBalance = computeClosingBalance2;
20776
21366
  const journalEntry = await getJournalById(_id);
20777
21367
  if (!journalEntry) {
20778
- throw new import_utils108.NotFoundError("Journal entry not found.");
21368
+ throw new import_utils112.NotFoundError("Journal entry not found.");
20779
21369
  }
20780
21370
  if (journalEntry.status === "voided") {
20781
- throw new import_utils108.BadRequestError("Cannot update a voided general journal.");
21371
+ throw new import_utils112.BadRequestError("Cannot update a voided general journal.");
20782
21372
  }
20783
21373
  if (journalEntry.status === "posted" && status2 === "draft") {
20784
- throw new import_utils108.BadRequestError(
21374
+ throw new import_utils112.BadRequestError(
20785
21375
  "Cannot revert a posted general journal to draft."
20786
21376
  );
20787
21377
  }
20788
21378
  if (status2 === "voided" && journalEntry.reversalDraft) {
20789
- throw new import_utils108.BadRequestError(
21379
+ throw new import_utils112.BadRequestError(
20790
21380
  "Cannot void a general journal with an existing reversal draft."
20791
21381
  );
20792
21382
  }
@@ -20808,9 +21398,17 @@ function useJournalService() {
20808
21398
  if (status2 === "posted") {
20809
21399
  await updateStatusByJournal(_id, status2, session);
20810
21400
  await _updateStatusById(_id, status2, session);
21401
+ const businessProfile = await getBusinessProfileByOrg(
21402
+ String(journalEntry.org)
21403
+ );
21404
+ if (!businessProfile) {
21405
+ throw new import_utils112.NotFoundError(
21406
+ "Business profile not found for organization."
21407
+ );
21408
+ }
20811
21409
  const linesToPost = await getJournalLinesByEntry(_id);
20812
21410
  const journalDate = new Date(journalEntry.date);
20813
- const fiscalYear = journalDate.getFullYear();
21411
+ const fiscalYear = Number(businessProfile.currentFiscalYear);
20814
21412
  const month = journalDate.toLocaleString("en-US", { month: "long" });
20815
21413
  for (const line of linesToPost) {
20816
21414
  const existingBalance = await getAccountBalance({
@@ -20833,9 +21431,16 @@ function useJournalService() {
20833
21431
  String(journalEntry.org),
20834
21432
  fiscalYear,
20835
21433
  month,
20836
- { movementDebit, movementCredit, closingDebit, closingCredit, netBalance },
21434
+ {
21435
+ movementDebit,
21436
+ movementCredit,
21437
+ closingDebit,
21438
+ closingCredit,
21439
+ netBalance
21440
+ },
20837
21441
  session
20838
21442
  );
21443
+ await updateJournalLineBalance(String(line._id), netBalance, session);
20839
21444
  } else {
20840
21445
  const allYearBalances = await getYearBalancesByAccount({
20841
21446
  account: String(line.account),
@@ -20843,7 +21448,9 @@ function useJournalService() {
20843
21448
  fiscalYear
20844
21449
  });
20845
21450
  const currentMonthIdx = MONTHS.indexOf(month);
20846
- const previousRecord = allYearBalances.filter((r) => MONTHS.indexOf(r.period.month) < currentMonthIdx).sort((a, b) => MONTHS.indexOf(b.period.month) - MONTHS.indexOf(a.period.month))[0];
21451
+ const previousRecord = allYearBalances.filter((r) => MONTHS.indexOf(r.period.month) < currentMonthIdx).sort(
21452
+ (a, b) => MONTHS.indexOf(b.period.month) - MONTHS.indexOf(a.period.month)
21453
+ )[0];
20847
21454
  const openingDebit = previousRecord?.closingDebit ?? 0;
20848
21455
  const openingCredit = previousRecord?.closingCredit ?? 0;
20849
21456
  const movementDebit = line.debit || 0;
@@ -20872,6 +21479,7 @@ function useJournalService() {
20872
21479
  },
20873
21480
  session
20874
21481
  );
21482
+ await updateJournalLineBalance(String(line._id), netBalance, session);
20875
21483
  }
20876
21484
  }
20877
21485
  await addJournalLog(
@@ -20886,7 +21494,7 @@ function useJournalService() {
20886
21494
  if (journalEntry.reversedEntry) {
20887
21495
  const userData = await getUserById(user);
20888
21496
  if (!userData) {
20889
- throw new import_utils108.NotFoundError("User not found.");
21497
+ throw new import_utils112.NotFoundError("User not found.");
20890
21498
  }
20891
21499
  await _updateById(
20892
21500
  journalEntry.reversedEntry,
@@ -20898,6 +21506,7 @@ function useJournalService() {
20898
21506
  },
20899
21507
  session
20900
21508
  );
21509
+ await updateStatusByJournal(String(journalEntry.reversedEntry), "voided", session);
20901
21510
  await addJournalLog(
20902
21511
  {
20903
21512
  journalId: String(journalEntry._id),
@@ -20914,7 +21523,7 @@ function useJournalService() {
20914
21523
  const counterName = `${journalEntry.org.toString()}-general-journal`;
20915
21524
  const counter = await getCounterByType(counterName);
20916
21525
  if (!counter) {
20917
- throw new import_utils108.NotFoundError("Counter not found for general journal.");
21526
+ throw new import_utils112.NotFoundError("Counter not found for general journal.");
20918
21527
  }
20919
21528
  const date = /* @__PURE__ */ new Date();
20920
21529
  const entryNumber = counter ? counter.count + 1 : 1;
@@ -20923,7 +21532,8 @@ function useJournalService() {
20923
21532
  org: String(journalEntry.org),
20924
21533
  id: entryNumber.toString().padStart(6, "0"),
20925
21534
  type: journalEntry.type,
20926
- transaction: journalEntry.transaction,
21535
+ transaction: String(journalEntry.transaction),
21536
+ transactionName: journalEntry.transactionName,
20927
21537
  date,
20928
21538
  description: `Reversal of ${getPrefixByType(journalEntry.type)}-${journalEntry.id}`,
20929
21539
  createdBy: String(journalEntry.createdBy),
@@ -20942,7 +21552,7 @@ function useJournalService() {
20942
21552
  );
20943
21553
  const lines = await getJournalLinesByEntry(String(journalEntry._id));
20944
21554
  if (lines && lines.length === 0) {
20945
- throw new import_utils108.NotFoundError("Journal lines not found for reversal.");
21555
+ throw new import_utils112.NotFoundError("Journal lines not found for reversal.");
20946
21556
  }
20947
21557
  const reversedLines = [];
20948
21558
  for (const line of lines) {
@@ -20987,10 +21597,10 @@ function useJournalService() {
20987
21597
  return "Journal entry status updated successfully.";
20988
21598
  } catch (error2) {
20989
21599
  await session.abortTransaction();
20990
- if (error2 instanceof import_utils108.AppError) {
21600
+ if (error2 instanceof import_utils112.AppError) {
20991
21601
  throw error2;
20992
21602
  }
20993
- throw new import_utils108.InternalServerError(
21603
+ throw new import_utils112.InternalServerError(
20994
21604
  `Failed to update general journal status: ${error2.message}`
20995
21605
  );
20996
21606
  } finally {
@@ -20998,11 +21608,11 @@ function useJournalService() {
20998
21608
  }
20999
21609
  }
21000
21610
  async function updateById(id, entry, lines, user) {
21001
- const { error } = import_joi90.default.object({
21002
- id: import_joi90.default.string().hex().required(),
21611
+ const { error } = import_joi93.default.object({
21612
+ id: import_joi93.default.string().hex().required(),
21003
21613
  journalEntry: schemaJournalCtrlUpdate.required(),
21004
- journalLines: import_joi90.default.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
21005
- user: import_joi90.default.string().hex().required()
21614
+ journalLines: import_joi93.default.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
21615
+ user: import_joi93.default.string().hex().required()
21006
21616
  }).validate({
21007
21617
  id,
21008
21618
  journalEntry: entry,
@@ -21010,13 +21620,13 @@ function useJournalService() {
21010
21620
  user
21011
21621
  });
21012
21622
  if (error) {
21013
- throw new import_utils108.BadRequestError(
21623
+ throw new import_utils112.BadRequestError(
21014
21624
  `Invalid general journal data: ${error.message}`
21015
21625
  );
21016
21626
  }
21017
- const session = import_utils108.useAtlas.getClient()?.startSession();
21627
+ const session = import_utils112.useAtlas.getClient()?.startSession();
21018
21628
  if (!session) {
21019
- throw new import_utils108.BadRequestError("Unable to start database session.");
21629
+ throw new import_utils112.BadRequestError("Unable to start database session.");
21020
21630
  }
21021
21631
  try {
21022
21632
  let hasLineChanged2 = function(existing, incoming) {
@@ -21026,24 +21636,37 @@ function useJournalService() {
21026
21636
  session.startTransaction();
21027
21637
  const existingEntry = await getJournalById(id);
21028
21638
  if (!existingEntry) {
21029
- throw new import_utils108.NotFoundError("Journal entry not found.");
21639
+ throw new import_utils112.NotFoundError("Journal entry not found.");
21030
21640
  }
21031
21641
  if (existingEntry.status !== "draft") {
21032
- throw new import_utils108.BadRequestError("Only draft journal entries can be updated.");
21642
+ throw new import_utils112.BadRequestError("Only draft journal entries can be updated.");
21033
21643
  }
21034
21644
  const updatedBy = await getUserById(user);
21035
21645
  if (!updatedBy) {
21036
- throw new import_utils108.NotFoundError("User not found.");
21646
+ throw new import_utils112.NotFoundError("User not found.");
21037
21647
  }
21038
21648
  await validateJournalLines(lines);
21039
- const headerChanged = new Date(existingEntry.date).getTime() !== new Date(entry.date).getTime() || existingEntry.description !== entry.description || String(existingEntry.customer || "") !== String(entry.customer || "") || existingEntry.transaction !== entry.transaction;
21649
+ const headerChanged = new Date(existingEntry.date).getTime() !== new Date(entry.date).getTime() || existingEntry.description !== entry.description || String(existingEntry.customer || "") !== String(entry.customer || "") || String(existingEntry.transaction) !== String(entry.transaction);
21040
21650
  if (entry.customer && String(entry.customer) !== String(existingEntry.customer)) {
21041
21651
  const customer = await getCustomerById(String(entry.customer));
21042
21652
  if (!customer) {
21043
- throw new import_utils108.NotFoundError("Customer not found.");
21653
+ throw new import_utils112.NotFoundError("Customer not found.");
21044
21654
  }
21045
21655
  entry.customerName = `${customer.firstName} ${customer.lastName}`;
21046
21656
  }
21657
+ if (String(existingEntry.transaction) !== String(entry.transaction)) {
21658
+ const journalTx = await getJournalTransaction({
21659
+ org: String(existingEntry.org),
21660
+ type: entry.type,
21661
+ _id: String(entry.transaction)
21662
+ });
21663
+ if (!journalTx) {
21664
+ throw new import_utils112.NotFoundError("Transaction not found.");
21665
+ }
21666
+ entry.transactionName = journalTx.title;
21667
+ } else {
21668
+ entry.transactionName = existingEntry.transactionName;
21669
+ }
21047
21670
  const existingLines = await getJournalLinesByEntry(id);
21048
21671
  const existingLineMap = new Map(
21049
21672
  existingLines.map((line) => [String(line._id), line])
@@ -21058,7 +21681,7 @@ function useJournalService() {
21058
21681
  (existingId) => !incomingIds.has(existingId)
21059
21682
  );
21060
21683
  if (existingLines.length > 0 && removedLineIds.length === existingLines.length && lines.length > 0) {
21061
- throw new import_utils108.BadRequestError(
21684
+ throw new import_utils112.BadRequestError(
21062
21685
  "All journal lines marked for deletion. Identity mismatch suspected."
21063
21686
  );
21064
21687
  }
@@ -21076,7 +21699,7 @@ function useJournalService() {
21076
21699
  const newLines = lines.filter((line) => !line._id);
21077
21700
  const nothingChanged = !headerChanged && removedLineIds.length === 0 && updatedLines.length === 0 && newLines.length === 0;
21078
21701
  if (nothingChanged) {
21079
- throw new import_utils108.BadRequestError("No changes detected.");
21702
+ throw new import_utils112.BadRequestError("No changes detected.");
21080
21703
  }
21081
21704
  for (const lineId of removedLineIds) {
21082
21705
  await deleteJournalLineById(lineId, session);
@@ -21091,7 +21714,7 @@ function useJournalService() {
21091
21714
  if (existing && String(existing.account) !== String(line.account)) {
21092
21715
  const account = await getAccountById(String(line.account));
21093
21716
  if (!account) {
21094
- throw new import_utils108.NotFoundError(`Account not found: ${line.account}`);
21717
+ throw new import_utils112.NotFoundError(`Account not found: ${line.account}`);
21095
21718
  }
21096
21719
  updatePayload.accountName = account.name;
21097
21720
  updatePayload.accountCode = account.code;
@@ -21101,7 +21724,7 @@ function useJournalService() {
21101
21724
  for (const line of newLines) {
21102
21725
  const account = await getAccountById(String(line.account));
21103
21726
  if (!account) {
21104
- throw new import_utils108.NotFoundError(`Account not found: ${line.account}`);
21727
+ throw new import_utils112.NotFoundError(`Account not found: ${line.account}`);
21105
21728
  }
21106
21729
  await addLine(
21107
21730
  {
@@ -21186,10 +21809,10 @@ function useJournalService() {
21186
21809
  return "Journal entry updated successfully.";
21187
21810
  } catch (error2) {
21188
21811
  await session.abortTransaction();
21189
- if (error2 instanceof import_utils108.AppError) {
21812
+ if (error2 instanceof import_utils112.AppError) {
21190
21813
  throw error2;
21191
21814
  }
21192
- throw new import_utils108.InternalServerError(
21815
+ throw new import_utils112.InternalServerError(
21193
21816
  `Failed to update general journal: ${error2.message}`
21194
21817
  );
21195
21818
  } finally {
@@ -21204,8 +21827,8 @@ function useJournalService() {
21204
21827
  }
21205
21828
 
21206
21829
  // src/resources/finance-journal/finance.journal.controller.ts
21207
- var import_joi91 = __toESM(require("joi"));
21208
- var import_utils109 = require("@goweekdays/utils");
21830
+ var import_joi94 = __toESM(require("joi"));
21831
+ var import_utils113 = require("@goweekdays/utils");
21209
21832
  function useJournalController() {
21210
21833
  const {
21211
21834
  getAll: _getAll,
@@ -21219,13 +21842,13 @@ function useJournalController() {
21219
21842
  } = useJournalService();
21220
21843
  async function add(req, res, next) {
21221
21844
  const value = req.body;
21222
- const { error } = import_joi91.default.object({
21845
+ const { error } = import_joi94.default.object({
21223
21846
  journalEntry: schemaJournalCtrl.required(),
21224
- journalLines: import_joi91.default.array().items(schemaJournalLineCtrl).min(1).required()
21847
+ journalLines: import_joi94.default.array().items(schemaJournalLineCtrl).min(1).required()
21225
21848
  }).validate(value);
21226
21849
  if (error) {
21227
- next(new import_utils109.BadRequestError(error.message));
21228
- import_utils109.logger.info(`Controller: ${error.message}`);
21850
+ next(new import_utils113.BadRequestError(error.message));
21851
+ import_utils113.logger.info(`Controller: ${error.message}`);
21229
21852
  return;
21230
21853
  }
21231
21854
  try {
@@ -21246,15 +21869,15 @@ function useJournalController() {
21246
21869
  const search = req.query.search ?? "";
21247
21870
  const status2 = req.query.status ?? "all";
21248
21871
  if (!isFinite(page)) {
21249
- next(new import_utils109.BadRequestError("Invalid page number."));
21872
+ next(new import_utils113.BadRequestError("Invalid page number."));
21250
21873
  return;
21251
21874
  }
21252
21875
  if (!isFinite(limit)) {
21253
- next(new import_utils109.BadRequestError("Invalid limit number."));
21876
+ next(new import_utils113.BadRequestError("Invalid limit number."));
21254
21877
  return;
21255
21878
  }
21256
21879
  if (error) {
21257
- next(new import_utils109.BadRequestError(error.message));
21880
+ next(new import_utils113.BadRequestError(error.message));
21258
21881
  return;
21259
21882
  }
21260
21883
  try {
@@ -21274,12 +21897,12 @@ function useJournalController() {
21274
21897
  }
21275
21898
  async function getById(req, res, next) {
21276
21899
  const id = req.params.id;
21277
- const validation = import_joi91.default.object({
21278
- id: import_joi91.default.string().hex().required()
21900
+ const validation = import_joi94.default.object({
21901
+ id: import_joi94.default.string().hex().required()
21279
21902
  });
21280
21903
  const { error } = validation.validate({ id });
21281
21904
  if (error) {
21282
- next(new import_utils109.BadRequestError(error.message));
21905
+ next(new import_utils113.BadRequestError(error.message));
21283
21906
  return;
21284
21907
  }
21285
21908
  try {
@@ -21294,15 +21917,15 @@ function useJournalController() {
21294
21917
  const value = req.body;
21295
21918
  const id = req.params.id ?? "";
21296
21919
  const user = req.cookies.user;
21297
- const { error } = import_joi91.default.object({
21298
- id: import_joi91.default.string().hex().required(),
21920
+ const { error } = import_joi94.default.object({
21921
+ id: import_joi94.default.string().hex().required(),
21299
21922
  journalEntry: schemaJournalCtrlUpdate.required(),
21300
- journalLines: import_joi91.default.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
21301
- user: import_joi91.default.string().hex().required()
21923
+ journalLines: import_joi94.default.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
21924
+ user: import_joi94.default.string().hex().required()
21302
21925
  }).validate({ id, user, ...value });
21303
21926
  if (error) {
21304
- next(new import_utils109.BadRequestError(error.message));
21305
- import_utils109.logger.info(`Controller: ${error.message}`);
21927
+ next(new import_utils113.BadRequestError(error.message));
21928
+ import_utils113.logger.info(`Controller: ${error.message}`);
21306
21929
  return;
21307
21930
  }
21308
21931
  try {
@@ -21321,7 +21944,7 @@ function useJournalController() {
21321
21944
  async function deleteById(req, res, next) {
21322
21945
  const id = req.params.id;
21323
21946
  if (!id) {
21324
- next(new import_utils109.BadRequestError("Journal Entry ID is required."));
21947
+ next(new import_utils113.BadRequestError("Journal Entry ID is required."));
21325
21948
  return;
21326
21949
  }
21327
21950
  try {
@@ -21336,14 +21959,14 @@ function useJournalController() {
21336
21959
  const id = req.params.id;
21337
21960
  const status2 = req.params.status;
21338
21961
  const user = req.cookies.user;
21339
- const validation = import_joi91.default.object({
21340
- id: import_joi91.default.string().hex().required(),
21341
- status: import_joi91.default.string().required(),
21342
- user: import_joi91.default.string().hex().required()
21962
+ const validation = import_joi94.default.object({
21963
+ id: import_joi94.default.string().hex().required(),
21964
+ status: import_joi94.default.string().required(),
21965
+ user: import_joi94.default.string().hex().required()
21343
21966
  });
21344
21967
  const { error } = validation.validate({ id, status: status2, user });
21345
21968
  if (error) {
21346
- next(new import_utils109.BadRequestError(error.message));
21969
+ next(new import_utils113.BadRequestError(error.message));
21347
21970
  return;
21348
21971
  }
21349
21972
  try {
@@ -21365,8 +21988,8 @@ function useJournalController() {
21365
21988
  }
21366
21989
 
21367
21990
  // src/resources/finance-journal-line/finance.journal.line.controller.ts
21368
- var import_joi92 = __toESM(require("joi"));
21369
- var import_utils110 = require("@goweekdays/utils");
21991
+ var import_joi95 = __toESM(require("joi"));
21992
+ var import_utils114 = require("@goweekdays/utils");
21370
21993
  function useJournalLineController() {
21371
21994
  const {
21372
21995
  getAll: _getAll,
@@ -21377,14 +22000,14 @@ function useJournalLineController() {
21377
22000
  } = useJournalLineRepo();
21378
22001
  async function getAll(req, res, next) {
21379
22002
  const query = req.query;
21380
- const validation = import_joi92.default.object({
21381
- org: import_joi92.default.string().hex().required(),
21382
- JournalGeneral: import_joi92.default.string().hex().optional(),
21383
- page: import_joi92.default.number().min(1).optional().allow("", null),
21384
- limit: import_joi92.default.number().min(1).optional().allow("", null),
21385
- account: import_joi92.default.string().hex().optional().allow("", null),
21386
- search: import_joi92.default.string().optional().allow("", null),
21387
- status: import_joi92.default.string().valid("draft", "posted").optional().allow("", null)
22003
+ const validation = import_joi95.default.object({
22004
+ org: import_joi95.default.string().hex().required(),
22005
+ JournalGeneral: import_joi95.default.string().hex().optional(),
22006
+ page: import_joi95.default.number().min(1).optional().allow("", null),
22007
+ limit: import_joi95.default.number().min(1).optional().allow("", null),
22008
+ account: import_joi95.default.string().hex().optional().allow("", null),
22009
+ search: import_joi95.default.string().optional().allow("", null),
22010
+ status: import_joi95.default.string().valid("draft", "posted").optional().allow("", null)
21388
22011
  });
21389
22012
  const org = req.params.org ?? "";
21390
22013
  const JournalGeneral = req.query.JournalGeneral;
@@ -21400,15 +22023,15 @@ function useJournalLineController() {
21400
22023
  account
21401
22024
  });
21402
22025
  if (!isFinite(page)) {
21403
- next(new import_utils110.BadRequestError("Invalid page number."));
22026
+ next(new import_utils114.BadRequestError("Invalid page number."));
21404
22027
  return;
21405
22028
  }
21406
22029
  if (!isFinite(limit)) {
21407
- next(new import_utils110.BadRequestError("Invalid limit number."));
22030
+ next(new import_utils114.BadRequestError("Invalid limit number."));
21408
22031
  return;
21409
22032
  }
21410
22033
  if (error) {
21411
- next(new import_utils110.BadRequestError(error.message));
22034
+ next(new import_utils114.BadRequestError(error.message));
21412
22035
  return;
21413
22036
  }
21414
22037
  try {
@@ -21429,12 +22052,12 @@ function useJournalLineController() {
21429
22052
  }
21430
22053
  async function getById(req, res, next) {
21431
22054
  const id = req.params.id;
21432
- const validation = import_joi92.default.object({
21433
- id: import_joi92.default.string().hex().required()
22055
+ const validation = import_joi95.default.object({
22056
+ id: import_joi95.default.string().hex().required()
21434
22057
  });
21435
22058
  const { error } = validation.validate({ id });
21436
22059
  if (error) {
21437
- next(new import_utils110.BadRequestError(error.message));
22060
+ next(new import_utils114.BadRequestError(error.message));
21438
22061
  return;
21439
22062
  }
21440
22063
  try {
@@ -21524,6 +22147,7 @@ function useJournalLineController() {
21524
22147
  modelJobSummary,
21525
22148
  modelJournal,
21526
22149
  modelJournalLine,
22150
+ modelJournalTransaction,
21527
22151
  modelLedgerBill,
21528
22152
  modelMember,
21529
22153
  modelOption,
@@ -21593,6 +22217,11 @@ function useJournalLineController() {
21593
22217
  schemaJournalLineRepo,
21594
22218
  schemaJournalRepo,
21595
22219
  schemaJournalRepoUpdate,
22220
+ schemaJournalTransactionAccount,
22221
+ schemaJournalTransactionCtrl,
22222
+ schemaJournalTransactionCtrlUpdate,
22223
+ schemaJournalTransactionGetAll,
22224
+ schemaJournalTransactionRepo,
21596
22225
  schemaLanguage,
21597
22226
  schemaLedgerBill,
21598
22227
  schemaLedgerBillingSummary,
@@ -21674,6 +22303,9 @@ function useJournalLineController() {
21674
22303
  useJournalLineRepo,
21675
22304
  useJournalRepo,
21676
22305
  useJournalService,
22306
+ useJournalTransactionController,
22307
+ useJournalTransactionRepo,
22308
+ useJournalTransactionService,
21677
22309
  useLedgerBillingController,
21678
22310
  useLedgerBillingRepo,
21679
22311
  useMemberController,