@goweekdays/core 2.15.0 → 2.15.2

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