@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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +64 -2
- package/dist/index.js +732 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +736 -99
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -18339,7 +18339,8 @@ function useChartOfAccountRepo() {
|
|
|
18339
18339
|
{ key: { code: 1 } },
|
|
18340
18340
|
{ key: { path: 1 } },
|
|
18341
18341
|
{ key: { name: 1, org: 1 }, unique: true, name: "unique_name_per_org" },
|
|
18342
|
-
{ key: { code: 1, org: 1 }, unique: true, name: "unique_code_per_org" }
|
|
18342
|
+
{ key: { code: 1, org: 1 }, unique: true, name: "unique_code_per_org" },
|
|
18343
|
+
{ key: { name: "text", code: "text" }, name: "text_search" }
|
|
18343
18344
|
]);
|
|
18344
18345
|
} catch (error) {
|
|
18345
18346
|
throw new Error("Failed to create index on chart of account.");
|
|
@@ -19145,7 +19146,12 @@ function useAccountBalanceRepo() {
|
|
|
19145
19146
|
{ key: { account: 1 } },
|
|
19146
19147
|
{ key: { "period.fiscalYear": 1 } },
|
|
19147
19148
|
{
|
|
19148
|
-
key: {
|
|
19149
|
+
key: {
|
|
19150
|
+
org: 1,
|
|
19151
|
+
account: 1,
|
|
19152
|
+
"period.fiscalYear": 1,
|
|
19153
|
+
"period.month": 1
|
|
19154
|
+
},
|
|
19149
19155
|
unique: true,
|
|
19150
19156
|
name: "unique_balance_per_account_month"
|
|
19151
19157
|
}
|
|
@@ -19320,9 +19326,7 @@ function useAccountBalanceRepo() {
|
|
|
19320
19326
|
if (error instanceof AppError43) {
|
|
19321
19327
|
throw error;
|
|
19322
19328
|
}
|
|
19323
|
-
throw new InternalServerError39(
|
|
19324
|
-
"Failed to get account balance."
|
|
19325
|
-
);
|
|
19329
|
+
throw new InternalServerError39("Failed to get account balance.");
|
|
19326
19330
|
}
|
|
19327
19331
|
}
|
|
19328
19332
|
async function getYearBalancesByAccount(options) {
|
|
@@ -19347,9 +19351,7 @@ function useAccountBalanceRepo() {
|
|
|
19347
19351
|
if (error instanceof AppError43) {
|
|
19348
19352
|
throw error;
|
|
19349
19353
|
}
|
|
19350
|
-
throw new InternalServerError39(
|
|
19351
|
-
"Failed to get account balances by year."
|
|
19352
|
-
);
|
|
19354
|
+
throw new InternalServerError39("Failed to get account balances by year.");
|
|
19353
19355
|
}
|
|
19354
19356
|
}
|
|
19355
19357
|
async function updateById(account, org, fiscalYear, month, options, session) {
|
|
@@ -19435,7 +19437,7 @@ var journalTypes = [
|
|
|
19435
19437
|
var schemaJournalBase = {
|
|
19436
19438
|
date: Joi83.date().required(),
|
|
19437
19439
|
type: Joi83.string().allow(...journalTypes).required(),
|
|
19438
|
-
transaction: Joi83.string().required(),
|
|
19440
|
+
transaction: Joi83.string().hex().required(),
|
|
19439
19441
|
customer: Joi83.string().hex().optional().allow("", null),
|
|
19440
19442
|
description: Joi83.string().required(),
|
|
19441
19443
|
invoiceNumber: Joi83.string().optional().allow("", null)
|
|
@@ -19452,6 +19454,7 @@ var schemaJournalRepo = Joi83.object({
|
|
|
19452
19454
|
id: Joi83.string().required(),
|
|
19453
19455
|
createdBy: Joi83.string().hex().required(),
|
|
19454
19456
|
createdByName: Joi83.string().required(),
|
|
19457
|
+
transactionName: Joi83.string().required(),
|
|
19455
19458
|
customerName: Joi83.string().optional().allow("", null),
|
|
19456
19459
|
status: Joi83.string().valid(...JournalStatuses).required(),
|
|
19457
19460
|
reversedEntry: Joi83.string().hex().optional().allow("", null),
|
|
@@ -19463,6 +19466,7 @@ var schemaJournalRepoUpdate = Joi83.object({
|
|
|
19463
19466
|
date: Joi83.date().optional().allow("", null),
|
|
19464
19467
|
type: Joi83.string().allow(...journalTypes).optional().allow("", null),
|
|
19465
19468
|
transaction: Joi83.string().optional().allow("", null),
|
|
19469
|
+
transactionName: Joi83.string().optional().allow("", null),
|
|
19466
19470
|
description: Joi83.string().optional().allow("", null),
|
|
19467
19471
|
status: Joi83.string().valid(...JournalStatuses).optional().allow("", null),
|
|
19468
19472
|
customerName: Joi83.string().optional().allow("", null),
|
|
@@ -19491,6 +19495,11 @@ function modelJournal(data) {
|
|
|
19491
19495
|
} catch (error2) {
|
|
19492
19496
|
throw new BadRequestError93(`Invalid org ID: ${data.org}`);
|
|
19493
19497
|
}
|
|
19498
|
+
try {
|
|
19499
|
+
data.transaction = new ObjectId50(data.transaction);
|
|
19500
|
+
} catch (error2) {
|
|
19501
|
+
throw new BadRequestError93(`Invalid transaction ID: ${data.transaction}`);
|
|
19502
|
+
}
|
|
19494
19503
|
try {
|
|
19495
19504
|
data.createdBy = new ObjectId50(data.createdBy);
|
|
19496
19505
|
} catch (error2) {
|
|
@@ -19535,6 +19544,7 @@ function modelJournal(data) {
|
|
|
19535
19544
|
org: data.org,
|
|
19536
19545
|
type: data.type,
|
|
19537
19546
|
transaction: data.transaction,
|
|
19547
|
+
transactionName: data.transactionName ?? "",
|
|
19538
19548
|
date,
|
|
19539
19549
|
invoiceNumber: data.invoiceNumber ?? "",
|
|
19540
19550
|
customer: data.customer ?? "",
|
|
@@ -19839,6 +19849,24 @@ function useJournalRepo() {
|
|
|
19839
19849
|
throw new InternalServerError40("Failed to update journal status.");
|
|
19840
19850
|
}
|
|
19841
19851
|
}
|
|
19852
|
+
async function existsByTransactionId(org, transactionId) {
|
|
19853
|
+
try {
|
|
19854
|
+
org = new ObjectId51(org);
|
|
19855
|
+
} catch {
|
|
19856
|
+
throw new BadRequestError94("Invalid org ID.");
|
|
19857
|
+
}
|
|
19858
|
+
try {
|
|
19859
|
+
transactionId = new ObjectId51(transactionId);
|
|
19860
|
+
} catch {
|
|
19861
|
+
throw new BadRequestError94("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
|
+
}
|
|
19842
19870
|
return {
|
|
19843
19871
|
createIndexes,
|
|
19844
19872
|
delCachedData: repo.delCachedData,
|
|
@@ -19847,12 +19875,13 @@ function useJournalRepo() {
|
|
|
19847
19875
|
getById,
|
|
19848
19876
|
updateById,
|
|
19849
19877
|
deleteById,
|
|
19850
|
-
updateStatusById
|
|
19878
|
+
updateStatusById,
|
|
19879
|
+
existsByTransactionId
|
|
19851
19880
|
};
|
|
19852
19881
|
}
|
|
19853
19882
|
|
|
19854
19883
|
// src/resources/finance-journal/finance.journal.service.ts
|
|
19855
|
-
import
|
|
19884
|
+
import Joi93 from "joi";
|
|
19856
19885
|
|
|
19857
19886
|
// src/resources/finance-journal-line/finance.journal.line.model.ts
|
|
19858
19887
|
import { BadRequestError as BadRequestError95 } from "@goweekdays/utils";
|
|
@@ -20192,6 +20221,23 @@ function useJournalLineRepo() {
|
|
|
20192
20221
|
throw new InternalServerError41("Failed to delete journal line.");
|
|
20193
20222
|
}
|
|
20194
20223
|
}
|
|
20224
|
+
async function updateBalanceById(_id, balance, session) {
|
|
20225
|
+
try {
|
|
20226
|
+
_id = new ObjectId53(_id);
|
|
20227
|
+
} catch (error) {
|
|
20228
|
+
throw new BadRequestError96("Invalid Journal Line ID.");
|
|
20229
|
+
}
|
|
20230
|
+
try {
|
|
20231
|
+
await collection.updateOne(
|
|
20232
|
+
{ _id },
|
|
20233
|
+
{ $set: { balance, updatedAt: /* @__PURE__ */ new Date() } },
|
|
20234
|
+
{ session }
|
|
20235
|
+
);
|
|
20236
|
+
delCachedData();
|
|
20237
|
+
} catch (error) {
|
|
20238
|
+
throw new InternalServerError41("Failed to update journal line balance.");
|
|
20239
|
+
}
|
|
20240
|
+
}
|
|
20195
20241
|
async function updateStatusByJournal(journalEntry, status2, session) {
|
|
20196
20242
|
const validation = Joi86.object({
|
|
20197
20243
|
journalEntry: Joi86.string().hex().required(),
|
|
@@ -20232,6 +20278,7 @@ function useJournalLineRepo() {
|
|
|
20232
20278
|
getById,
|
|
20233
20279
|
getByEntry,
|
|
20234
20280
|
updateById,
|
|
20281
|
+
updateBalanceById,
|
|
20235
20282
|
deleteById,
|
|
20236
20283
|
updateStatusByJournal
|
|
20237
20284
|
};
|
|
@@ -20239,10 +20286,10 @@ function useJournalLineRepo() {
|
|
|
20239
20286
|
|
|
20240
20287
|
// src/resources/finance-journal/finance.journal.service.ts
|
|
20241
20288
|
import {
|
|
20242
|
-
AppError as
|
|
20243
|
-
BadRequestError as
|
|
20244
|
-
InternalServerError as
|
|
20245
|
-
NotFoundError as
|
|
20289
|
+
AppError as AppError49,
|
|
20290
|
+
BadRequestError as BadRequestError105,
|
|
20291
|
+
InternalServerError as InternalServerError45,
|
|
20292
|
+
NotFoundError as NotFoundError7,
|
|
20246
20293
|
useAtlas as useAtlas16
|
|
20247
20294
|
} from "@goweekdays/utils";
|
|
20248
20295
|
|
|
@@ -20661,6 +20708,542 @@ function useJournalLogRepo() {
|
|
|
20661
20708
|
import Joi89 from "joi";
|
|
20662
20709
|
import { BadRequestError as BadRequestError100, logger as logger51 } from "@goweekdays/utils";
|
|
20663
20710
|
|
|
20711
|
+
// src/resources/finance-journal-transaction/finance.journal.transaction.model.ts
|
|
20712
|
+
import { BadRequestError as BadRequestError101 } from "@goweekdays/utils";
|
|
20713
|
+
import Joi90 from "joi";
|
|
20714
|
+
import { ObjectId as ObjectId56 } from "mongodb";
|
|
20715
|
+
var schemaJournalTransactionAccount = Joi90.object({
|
|
20716
|
+
title: Joi90.string().required(),
|
|
20717
|
+
value: Joi90.string().hex().required()
|
|
20718
|
+
});
|
|
20719
|
+
var schemaJournalTransactionBase = {
|
|
20720
|
+
type: Joi90.string().valid(...journalTypes).required(),
|
|
20721
|
+
code: Joi90.string().required(),
|
|
20722
|
+
title: Joi90.string().required(),
|
|
20723
|
+
debits: Joi90.array().items(schemaJournalTransactionAccount).min(1).required(),
|
|
20724
|
+
credits: Joi90.array().items(schemaJournalTransactionAccount).min(1).required(),
|
|
20725
|
+
template: Joi90.boolean().required(),
|
|
20726
|
+
description: Joi90.string().optional().allow("", null)
|
|
20727
|
+
};
|
|
20728
|
+
var schemaJournalTransactionCtrl = Joi90.object({
|
|
20729
|
+
...schemaJournalTransactionBase,
|
|
20730
|
+
org: Joi90.string().hex().required(),
|
|
20731
|
+
createdBy: Joi90.string().hex().required()
|
|
20732
|
+
});
|
|
20733
|
+
var schemaJournalTransactionCtrlUpdate = Joi90.object({
|
|
20734
|
+
code: Joi90.string().optional(),
|
|
20735
|
+
title: Joi90.string().optional(),
|
|
20736
|
+
debits: Joi90.array().items(schemaJournalTransactionAccount).min(1).optional(),
|
|
20737
|
+
credits: Joi90.array().items(schemaJournalTransactionAccount).min(1).optional(),
|
|
20738
|
+
template: Joi90.boolean().optional(),
|
|
20739
|
+
description: Joi90.string().optional().allow("", null)
|
|
20740
|
+
});
|
|
20741
|
+
var schemaJournalTransactionRepo = Joi90.object({
|
|
20742
|
+
...schemaJournalTransactionBase,
|
|
20743
|
+
org: Joi90.string().hex().required(),
|
|
20744
|
+
createdBy: Joi90.string().hex().required(),
|
|
20745
|
+
createdByName: Joi90.string().optional().allow("", null)
|
|
20746
|
+
});
|
|
20747
|
+
var schemaJournalTransactionGetAll = Joi90.object({
|
|
20748
|
+
org: Joi90.string().hex().required(),
|
|
20749
|
+
type: Joi90.string().optional().allow("", null),
|
|
20750
|
+
search: Joi90.string().optional().allow("", null),
|
|
20751
|
+
page: Joi90.number().optional().allow("", null),
|
|
20752
|
+
limit: Joi90.number().max(100).optional().allow("", null)
|
|
20753
|
+
});
|
|
20754
|
+
function modelJournalTransaction(data) {
|
|
20755
|
+
const { error } = schemaJournalTransactionRepo.validate(data);
|
|
20756
|
+
if (error) {
|
|
20757
|
+
throw new BadRequestError101(
|
|
20758
|
+
`Invalid journal transaction data: ${error.message}`
|
|
20759
|
+
);
|
|
20760
|
+
}
|
|
20761
|
+
try {
|
|
20762
|
+
data.org = new ObjectId56(data.org);
|
|
20763
|
+
} catch (error2) {
|
|
20764
|
+
throw new BadRequestError101(`Invalid org ID: ${data.org}`);
|
|
20765
|
+
}
|
|
20766
|
+
try {
|
|
20767
|
+
data.createdBy = new ObjectId56(data.createdBy);
|
|
20768
|
+
} catch (error2) {
|
|
20769
|
+
throw new BadRequestError101(`Invalid createdBy ID: ${data.createdBy}`);
|
|
20770
|
+
}
|
|
20771
|
+
for (let i = 0; i < data.debits.length; i++) {
|
|
20772
|
+
try {
|
|
20773
|
+
data.debits[i].value = new ObjectId56(data.debits[i].value);
|
|
20774
|
+
} catch (error2) {
|
|
20775
|
+
throw new BadRequestError101(`Invalid debit account ID at index ${i}`);
|
|
20776
|
+
}
|
|
20777
|
+
}
|
|
20778
|
+
for (let i = 0; i < data.credits.length; i++) {
|
|
20779
|
+
try {
|
|
20780
|
+
data.credits[i].value = new ObjectId56(data.credits[i].value);
|
|
20781
|
+
} catch (error2) {
|
|
20782
|
+
throw new BadRequestError101(`Invalid credit account ID at index ${i}`);
|
|
20783
|
+
}
|
|
20784
|
+
}
|
|
20785
|
+
const now = /* @__PURE__ */ new Date();
|
|
20786
|
+
return {
|
|
20787
|
+
_id: data._id,
|
|
20788
|
+
org: data.org,
|
|
20789
|
+
type: data.type,
|
|
20790
|
+
code: data.code,
|
|
20791
|
+
title: data.title,
|
|
20792
|
+
debits: data.debits,
|
|
20793
|
+
credits: data.credits,
|
|
20794
|
+
template: data.template ?? false,
|
|
20795
|
+
description: data.description ?? "",
|
|
20796
|
+
createdBy: data.createdBy,
|
|
20797
|
+
createdByName: data.createdByName ?? "",
|
|
20798
|
+
createdAt: data.createdAt ?? now,
|
|
20799
|
+
updatedAt: data.updatedAt ?? ""
|
|
20800
|
+
};
|
|
20801
|
+
}
|
|
20802
|
+
|
|
20803
|
+
// src/resources/finance-journal-transaction/finance.journal.transaction.repository.ts
|
|
20804
|
+
import {
|
|
20805
|
+
AppError as AppError47,
|
|
20806
|
+
BadRequestError as BadRequestError102,
|
|
20807
|
+
makeCacheKey as makeCacheKey32,
|
|
20808
|
+
paginate as paginate28,
|
|
20809
|
+
logger as logger52,
|
|
20810
|
+
InternalServerError as InternalServerError43,
|
|
20811
|
+
useRepo as useRepo31
|
|
20812
|
+
} from "@goweekdays/utils";
|
|
20813
|
+
import { ObjectId as ObjectId57 } from "mongodb";
|
|
20814
|
+
function useJournalTransactionRepo() {
|
|
20815
|
+
const namespace_collection = "finance.journal.transactions";
|
|
20816
|
+
const repo = useRepo31(namespace_collection);
|
|
20817
|
+
async function createIndexes() {
|
|
20818
|
+
try {
|
|
20819
|
+
await repo.collection.createIndexes([
|
|
20820
|
+
{ key: { org: 1, type: 1, _id: 1 } },
|
|
20821
|
+
{ key: { org: 1 } },
|
|
20822
|
+
{ key: { type: 1 } },
|
|
20823
|
+
{ key: { code: 1 } },
|
|
20824
|
+
{ key: { org: 1, type: 1 } },
|
|
20825
|
+
{
|
|
20826
|
+
key: {
|
|
20827
|
+
title: "text",
|
|
20828
|
+
code: "text",
|
|
20829
|
+
description: "text"
|
|
20830
|
+
}
|
|
20831
|
+
},
|
|
20832
|
+
{
|
|
20833
|
+
key: { org: 1, type: 1, code: 1 },
|
|
20834
|
+
unique: true,
|
|
20835
|
+
name: "unique_org_type_code"
|
|
20836
|
+
}
|
|
20837
|
+
]);
|
|
20838
|
+
} catch (error) {
|
|
20839
|
+
throw new Error("Failed to create index on journal transactions.");
|
|
20840
|
+
}
|
|
20841
|
+
}
|
|
20842
|
+
async function add(value, session) {
|
|
20843
|
+
try {
|
|
20844
|
+
value = modelJournalTransaction(value);
|
|
20845
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
20846
|
+
repo.delCachedData();
|
|
20847
|
+
return res.insertedId;
|
|
20848
|
+
} catch (error) {
|
|
20849
|
+
logger52.log({ level: "error", message: error.message });
|
|
20850
|
+
throw new BadRequestError102(
|
|
20851
|
+
`Failed to create journal transaction: ${error.message}`
|
|
20852
|
+
);
|
|
20853
|
+
}
|
|
20854
|
+
}
|
|
20855
|
+
async function getAll(options) {
|
|
20856
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
20857
|
+
options.limit = options.limit ?? 10;
|
|
20858
|
+
options.search = options.search ?? "";
|
|
20859
|
+
const { error } = schemaJournalTransactionGetAll.validate(options);
|
|
20860
|
+
if (error) {
|
|
20861
|
+
throw new BadRequestError102(`Invalid query parameters: ${error.message}`);
|
|
20862
|
+
}
|
|
20863
|
+
const query = {};
|
|
20864
|
+
const cacheKeyOptions = {
|
|
20865
|
+
page: options.page,
|
|
20866
|
+
limit: options.limit
|
|
20867
|
+
};
|
|
20868
|
+
try {
|
|
20869
|
+
query.org = new ObjectId57(options.org);
|
|
20870
|
+
cacheKeyOptions.org = String(options.org);
|
|
20871
|
+
} catch (error2) {
|
|
20872
|
+
throw new BadRequestError102("Invalid organization ID.");
|
|
20873
|
+
}
|
|
20874
|
+
if (options.type) {
|
|
20875
|
+
query.type = options.type;
|
|
20876
|
+
cacheKeyOptions.type = options.type;
|
|
20877
|
+
}
|
|
20878
|
+
if (options.search) {
|
|
20879
|
+
query.$text = { $search: options.search };
|
|
20880
|
+
cacheKeyOptions.search = options.search;
|
|
20881
|
+
}
|
|
20882
|
+
const cacheKey = makeCacheKey32(namespace_collection, cacheKeyOptions);
|
|
20883
|
+
logger52.log({
|
|
20884
|
+
level: "info",
|
|
20885
|
+
message: `Cache key for getAll journal transactions: ${cacheKey}`
|
|
20886
|
+
});
|
|
20887
|
+
try {
|
|
20888
|
+
const cached = await repo.getCache(cacheKey);
|
|
20889
|
+
if (cached) {
|
|
20890
|
+
logger52.log({
|
|
20891
|
+
level: "info",
|
|
20892
|
+
message: `Cache hit for getAll journal transactions: ${cacheKey}`
|
|
20893
|
+
});
|
|
20894
|
+
return cached;
|
|
20895
|
+
}
|
|
20896
|
+
const items = await repo.collection.aggregate([
|
|
20897
|
+
{ $match: query },
|
|
20898
|
+
{ $sort: { title: 1 } },
|
|
20899
|
+
{ $skip: options.page * options.limit },
|
|
20900
|
+
{ $limit: options.limit }
|
|
20901
|
+
]).toArray();
|
|
20902
|
+
const length = await repo.collection.countDocuments(query);
|
|
20903
|
+
const data = paginate28(items, options.page, options.limit, length);
|
|
20904
|
+
repo.setCache(cacheKey, data, 600).then(() => {
|
|
20905
|
+
logger52.log({
|
|
20906
|
+
level: "info",
|
|
20907
|
+
message: `Cache set for getAll journal transactions: ${cacheKey}`
|
|
20908
|
+
});
|
|
20909
|
+
}).catch((err) => {
|
|
20910
|
+
logger52.log({
|
|
20911
|
+
level: "error",
|
|
20912
|
+
message: `Failed to set cache for getAll journal transactions: ${err.message}`
|
|
20913
|
+
});
|
|
20914
|
+
});
|
|
20915
|
+
return data;
|
|
20916
|
+
} catch (error2) {
|
|
20917
|
+
logger52.log({ level: "error", message: `${error2}` });
|
|
20918
|
+
throw error2;
|
|
20919
|
+
}
|
|
20920
|
+
}
|
|
20921
|
+
async function getByOrgTypeId(options) {
|
|
20922
|
+
try {
|
|
20923
|
+
options._id = new ObjectId57(options._id);
|
|
20924
|
+
} catch (error) {
|
|
20925
|
+
throw new BadRequestError102("Invalid ID.");
|
|
20926
|
+
}
|
|
20927
|
+
try {
|
|
20928
|
+
options.org = new ObjectId57(options.org);
|
|
20929
|
+
} catch (error) {
|
|
20930
|
+
throw new BadRequestError102("Invalid organization ID.");
|
|
20931
|
+
}
|
|
20932
|
+
const cacheKey = makeCacheKey32(namespace_collection, {
|
|
20933
|
+
org: String(options.org),
|
|
20934
|
+
type: options.type,
|
|
20935
|
+
_id: String(options._id),
|
|
20936
|
+
tag: "org_type_id"
|
|
20937
|
+
});
|
|
20938
|
+
try {
|
|
20939
|
+
const cached = await repo.getCache(cacheKey);
|
|
20940
|
+
if (cached) {
|
|
20941
|
+
logger52.log({
|
|
20942
|
+
level: "info",
|
|
20943
|
+
message: `Cache hit for get by org + type + id journal transaction: ${cacheKey}`
|
|
20944
|
+
});
|
|
20945
|
+
return cached;
|
|
20946
|
+
}
|
|
20947
|
+
const result = await repo.collection.findOne({
|
|
20948
|
+
_id: options._id,
|
|
20949
|
+
org: options.org,
|
|
20950
|
+
type: options.type
|
|
20951
|
+
});
|
|
20952
|
+
repo.setCache(cacheKey, result, 300).then(() => {
|
|
20953
|
+
logger52.log({
|
|
20954
|
+
level: "info",
|
|
20955
|
+
message: `Cache set for journal transaction by org + type + id: ${cacheKey}`
|
|
20956
|
+
});
|
|
20957
|
+
}).catch((err) => {
|
|
20958
|
+
logger52.log({
|
|
20959
|
+
level: "error",
|
|
20960
|
+
message: `Failed to set cache for journal transaction by org + type + id: ${err.message}`
|
|
20961
|
+
});
|
|
20962
|
+
});
|
|
20963
|
+
return result;
|
|
20964
|
+
} catch (error) {
|
|
20965
|
+
if (error instanceof AppError47) {
|
|
20966
|
+
throw error;
|
|
20967
|
+
} else {
|
|
20968
|
+
throw new InternalServerError43("Failed to get journal transaction.");
|
|
20969
|
+
}
|
|
20970
|
+
}
|
|
20971
|
+
}
|
|
20972
|
+
async function updateById(_id, options, session) {
|
|
20973
|
+
const { error } = schemaJournalTransactionCtrlUpdate.validate(options);
|
|
20974
|
+
if (error) {
|
|
20975
|
+
throw new BadRequestError102(
|
|
20976
|
+
`Invalid journal transaction update data: ${error.message}`
|
|
20977
|
+
);
|
|
20978
|
+
}
|
|
20979
|
+
try {
|
|
20980
|
+
_id = new ObjectId57(_id);
|
|
20981
|
+
} catch (error2) {
|
|
20982
|
+
throw new BadRequestError102("Invalid journal transaction ID.");
|
|
20983
|
+
}
|
|
20984
|
+
if (options.debits) {
|
|
20985
|
+
for (let i = 0; i < options.debits.length; i++) {
|
|
20986
|
+
try {
|
|
20987
|
+
options.debits[i].value = new ObjectId57(options.debits[i].value);
|
|
20988
|
+
} catch (error2) {
|
|
20989
|
+
throw new BadRequestError102(`Invalid debit account ID at index ${i}`);
|
|
20990
|
+
}
|
|
20991
|
+
}
|
|
20992
|
+
}
|
|
20993
|
+
if (options.credits) {
|
|
20994
|
+
for (let i = 0; i < options.credits.length; i++) {
|
|
20995
|
+
try {
|
|
20996
|
+
options.credits[i].value = new ObjectId57(options.credits[i].value);
|
|
20997
|
+
} catch (error2) {
|
|
20998
|
+
throw new BadRequestError102(`Invalid credit account ID at index ${i}`);
|
|
20999
|
+
}
|
|
21000
|
+
}
|
|
21001
|
+
}
|
|
21002
|
+
try {
|
|
21003
|
+
await repo.collection.updateOne(
|
|
21004
|
+
{ _id },
|
|
21005
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } },
|
|
21006
|
+
{ session }
|
|
21007
|
+
);
|
|
21008
|
+
repo.delCachedData();
|
|
21009
|
+
return "Successfully updated journal transaction.";
|
|
21010
|
+
} catch (error2) {
|
|
21011
|
+
throw new InternalServerError43("Failed to update journal transaction.");
|
|
21012
|
+
}
|
|
21013
|
+
}
|
|
21014
|
+
async function deleteById(_id, session) {
|
|
21015
|
+
try {
|
|
21016
|
+
_id = new ObjectId57(_id);
|
|
21017
|
+
} catch (error) {
|
|
21018
|
+
throw new BadRequestError102("Invalid ID.");
|
|
21019
|
+
}
|
|
21020
|
+
try {
|
|
21021
|
+
await repo.collection.deleteOne({ _id }, { session });
|
|
21022
|
+
repo.delCachedData();
|
|
21023
|
+
return "Successfully deleted journal transaction.";
|
|
21024
|
+
} catch (error) {
|
|
21025
|
+
throw new InternalServerError43("Failed to delete journal transaction.");
|
|
21026
|
+
}
|
|
21027
|
+
}
|
|
21028
|
+
return {
|
|
21029
|
+
createIndexes,
|
|
21030
|
+
add,
|
|
21031
|
+
getAll,
|
|
21032
|
+
getByOrgTypeId,
|
|
21033
|
+
updateById,
|
|
21034
|
+
deleteById
|
|
21035
|
+
};
|
|
21036
|
+
}
|
|
21037
|
+
|
|
21038
|
+
// src/resources/finance-journal-transaction/finance.journal.transaction.service.ts
|
|
21039
|
+
import {
|
|
21040
|
+
AppError as AppError48,
|
|
21041
|
+
BadRequestError as BadRequestError103,
|
|
21042
|
+
InternalServerError as InternalServerError44,
|
|
21043
|
+
NotFoundError as NotFoundError6
|
|
21044
|
+
} from "@goweekdays/utils";
|
|
21045
|
+
import Joi91 from "joi";
|
|
21046
|
+
function validateNoDuplicateAccounts(debits, credits) {
|
|
21047
|
+
const debitIds = debits.map((d) => String(d.value));
|
|
21048
|
+
const creditIds = credits.map((c) => String(c.value));
|
|
21049
|
+
const debitSet = new Set(debitIds);
|
|
21050
|
+
if (debitSet.size !== debitIds.length) {
|
|
21051
|
+
throw new BadRequestError103("Duplicate accounts found in debits.");
|
|
21052
|
+
}
|
|
21053
|
+
const creditSet = new Set(creditIds);
|
|
21054
|
+
if (creditSet.size !== creditIds.length) {
|
|
21055
|
+
throw new BadRequestError103("Duplicate accounts found in credits.");
|
|
21056
|
+
}
|
|
21057
|
+
}
|
|
21058
|
+
function useJournalTransactionService() {
|
|
21059
|
+
const {
|
|
21060
|
+
add: _add,
|
|
21061
|
+
updateById: _updateById,
|
|
21062
|
+
getByOrgTypeId
|
|
21063
|
+
} = useJournalTransactionRepo();
|
|
21064
|
+
const { getUserById } = useUserRepo();
|
|
21065
|
+
async function add(value) {
|
|
21066
|
+
const { error } = schemaJournalTransactionCtrl.validate(value);
|
|
21067
|
+
if (error) {
|
|
21068
|
+
throw new BadRequestError103(
|
|
21069
|
+
`Invalid journal transaction data: ${error.message}`
|
|
21070
|
+
);
|
|
21071
|
+
}
|
|
21072
|
+
validateNoDuplicateAccounts(value.debits, value.credits);
|
|
21073
|
+
const user = await getUserById(value.createdBy);
|
|
21074
|
+
if (!user) {
|
|
21075
|
+
throw new NotFoundError6("User not found.");
|
|
21076
|
+
}
|
|
21077
|
+
value.createdByName = `${user.firstName} ${user.lastName}`;
|
|
21078
|
+
try {
|
|
21079
|
+
const id = await _add(value);
|
|
21080
|
+
return id;
|
|
21081
|
+
} catch (error2) {
|
|
21082
|
+
if (error2 instanceof AppError48) {
|
|
21083
|
+
throw error2;
|
|
21084
|
+
}
|
|
21085
|
+
throw new InternalServerError44(
|
|
21086
|
+
`Failed to create journal transaction: ${error2.message}`
|
|
21087
|
+
);
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
async function updateById(org, type, _id, options) {
|
|
21091
|
+
const { error: idError } = Joi91.string().hex().required().validate(_id);
|
|
21092
|
+
if (idError) {
|
|
21093
|
+
throw new BadRequestError103("Invalid journal transaction ID.");
|
|
21094
|
+
}
|
|
21095
|
+
const { error } = schemaJournalTransactionCtrlUpdate.validate(options);
|
|
21096
|
+
if (error) {
|
|
21097
|
+
throw new BadRequestError103(`Invalid update data: ${error.message}`);
|
|
21098
|
+
}
|
|
21099
|
+
const existing = await getByOrgTypeId({ org, type, _id });
|
|
21100
|
+
if (!existing) {
|
|
21101
|
+
throw new NotFoundError6("Journal transaction not found.");
|
|
21102
|
+
}
|
|
21103
|
+
const debits = options.debits ?? existing.debits;
|
|
21104
|
+
const credits = options.credits ?? existing.credits;
|
|
21105
|
+
validateNoDuplicateAccounts(debits, credits);
|
|
21106
|
+
try {
|
|
21107
|
+
return await _updateById(_id, options);
|
|
21108
|
+
} catch (error2) {
|
|
21109
|
+
if (error2 instanceof AppError48) {
|
|
21110
|
+
throw error2;
|
|
21111
|
+
}
|
|
21112
|
+
throw new InternalServerError44(
|
|
21113
|
+
`Failed to update journal transaction: ${error2.message}`
|
|
21114
|
+
);
|
|
21115
|
+
}
|
|
21116
|
+
}
|
|
21117
|
+
return {
|
|
21118
|
+
add,
|
|
21119
|
+
updateById
|
|
21120
|
+
};
|
|
21121
|
+
}
|
|
21122
|
+
|
|
21123
|
+
// src/resources/finance-journal-transaction/finance.journal.transaction.controller.ts
|
|
21124
|
+
import Joi92 from "joi";
|
|
21125
|
+
import { BadRequestError as BadRequestError104 } from "@goweekdays/utils";
|
|
21126
|
+
function useJournalTransactionController() {
|
|
21127
|
+
const {
|
|
21128
|
+
getAll: _getAll,
|
|
21129
|
+
getByOrgTypeId: _getByOrgTypeId,
|
|
21130
|
+
deleteById: _deleteById
|
|
21131
|
+
} = useJournalTransactionRepo();
|
|
21132
|
+
const { add: _add, updateById: _updateById } = useJournalTransactionService();
|
|
21133
|
+
async function add(req, res, next) {
|
|
21134
|
+
const value = req.body;
|
|
21135
|
+
const { error } = schemaJournalTransactionCtrl.validate(value);
|
|
21136
|
+
if (error) {
|
|
21137
|
+
next(new BadRequestError104(error.message));
|
|
21138
|
+
return;
|
|
21139
|
+
}
|
|
21140
|
+
try {
|
|
21141
|
+
const id = await _add(value);
|
|
21142
|
+
res.json({ message: "Journal transaction created successfully.", id });
|
|
21143
|
+
return;
|
|
21144
|
+
} catch (error2) {
|
|
21145
|
+
next(error2);
|
|
21146
|
+
}
|
|
21147
|
+
}
|
|
21148
|
+
async function getAll(req, res, next) {
|
|
21149
|
+
const org = req.params.org ?? "";
|
|
21150
|
+
const type = req.params.type ?? "";
|
|
21151
|
+
const search = req.query.search ?? "";
|
|
21152
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
21153
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
21154
|
+
const { error } = schemaJournalTransactionGetAll.validate({
|
|
21155
|
+
...req.params,
|
|
21156
|
+
...req.query
|
|
21157
|
+
});
|
|
21158
|
+
if (!isFinite(page)) {
|
|
21159
|
+
next(new BadRequestError104("Invalid page number."));
|
|
21160
|
+
return;
|
|
21161
|
+
}
|
|
21162
|
+
if (!isFinite(limit)) {
|
|
21163
|
+
next(new BadRequestError104("Invalid limit number."));
|
|
21164
|
+
return;
|
|
21165
|
+
}
|
|
21166
|
+
if (error) {
|
|
21167
|
+
next(new BadRequestError104(error.message));
|
|
21168
|
+
return;
|
|
21169
|
+
}
|
|
21170
|
+
try {
|
|
21171
|
+
const data = await _getAll({ org, type, search, page, limit });
|
|
21172
|
+
res.json(data);
|
|
21173
|
+
return;
|
|
21174
|
+
} catch (error2) {
|
|
21175
|
+
next(error2);
|
|
21176
|
+
}
|
|
21177
|
+
}
|
|
21178
|
+
async function getByOrgTypeId(req, res, next) {
|
|
21179
|
+
const { error } = Joi92.object({
|
|
21180
|
+
org: Joi92.string().hex().required(),
|
|
21181
|
+
type: Joi92.string().required(),
|
|
21182
|
+
id: Joi92.string().hex().required()
|
|
21183
|
+
}).validate(req.params);
|
|
21184
|
+
if (error) {
|
|
21185
|
+
next(new BadRequestError104(error.message));
|
|
21186
|
+
return;
|
|
21187
|
+
}
|
|
21188
|
+
const _id = req.params.id;
|
|
21189
|
+
const org = req.params.org;
|
|
21190
|
+
const type = req.params.type;
|
|
21191
|
+
try {
|
|
21192
|
+
const data = await _getByOrgTypeId({ org, type, _id });
|
|
21193
|
+
res.json(data);
|
|
21194
|
+
return;
|
|
21195
|
+
} catch (error2) {
|
|
21196
|
+
next(error2);
|
|
21197
|
+
}
|
|
21198
|
+
}
|
|
21199
|
+
async function updateById(req, res, next) {
|
|
21200
|
+
const value = req.body;
|
|
21201
|
+
const { error } = Joi92.object({
|
|
21202
|
+
id: Joi92.string().hex().required(),
|
|
21203
|
+
org: Joi92.string().hex().required(),
|
|
21204
|
+
type: Joi92.string().allow(...journalTypes).required()
|
|
21205
|
+
}).concat(schemaJournalTransactionCtrlUpdate).validate({ ...req.params, ...value });
|
|
21206
|
+
if (error) {
|
|
21207
|
+
next(new BadRequestError104(error.message));
|
|
21208
|
+
return;
|
|
21209
|
+
}
|
|
21210
|
+
const id = req.params.id ?? "";
|
|
21211
|
+
const org = req.params.org ?? "";
|
|
21212
|
+
const type = req.params.type ?? "";
|
|
21213
|
+
try {
|
|
21214
|
+
const message = await _updateById(org, type, id, value);
|
|
21215
|
+
res.json({ message });
|
|
21216
|
+
return;
|
|
21217
|
+
} catch (error2) {
|
|
21218
|
+
next(error2);
|
|
21219
|
+
}
|
|
21220
|
+
}
|
|
21221
|
+
async function deleteById(req, res, next) {
|
|
21222
|
+
const id = req.params.id;
|
|
21223
|
+
const { error } = Joi92.object({
|
|
21224
|
+
id: Joi92.string().hex().required()
|
|
21225
|
+
}).validate({ id });
|
|
21226
|
+
if (error) {
|
|
21227
|
+
next(new BadRequestError104(error.message));
|
|
21228
|
+
return;
|
|
21229
|
+
}
|
|
21230
|
+
try {
|
|
21231
|
+
const message = await _deleteById(id);
|
|
21232
|
+
res.json({ message });
|
|
21233
|
+
return;
|
|
21234
|
+
} catch (error2) {
|
|
21235
|
+
next(error2);
|
|
21236
|
+
}
|
|
21237
|
+
}
|
|
21238
|
+
return {
|
|
21239
|
+
add,
|
|
21240
|
+
getAll,
|
|
21241
|
+
getByOrgTypeId,
|
|
21242
|
+
updateById,
|
|
21243
|
+
deleteById
|
|
21244
|
+
};
|
|
21245
|
+
}
|
|
21246
|
+
|
|
20664
21247
|
// src/resources/finance-journal/finance.journal.service.ts
|
|
20665
21248
|
function useJournalService() {
|
|
20666
21249
|
const {
|
|
@@ -20670,10 +21253,12 @@ function useJournalService() {
|
|
|
20670
21253
|
updateById: _updateById,
|
|
20671
21254
|
delCachedData: delJournalCache
|
|
20672
21255
|
} = useJournalRepo();
|
|
21256
|
+
const { getByOrgTypeId: getJournalTransaction } = useJournalTransactionRepo();
|
|
20673
21257
|
const {
|
|
20674
21258
|
add: addLine,
|
|
20675
21259
|
getByEntry: getJournalLinesByEntry,
|
|
20676
21260
|
updateById: updateJournalLineById,
|
|
21261
|
+
updateBalanceById: updateJournalLineBalance,
|
|
20677
21262
|
deleteById: deleteJournalLineById
|
|
20678
21263
|
} = useJournalLineRepo();
|
|
20679
21264
|
const { getUserById } = useUserRepo();
|
|
@@ -20687,21 +21272,21 @@ function useJournalService() {
|
|
|
20687
21272
|
const { add: addJournalLog } = useJournalLogRepo();
|
|
20688
21273
|
const { getById: getCustomerById } = useCustomerRepo();
|
|
20689
21274
|
async function add(entry, lines) {
|
|
20690
|
-
const { error } =
|
|
21275
|
+
const { error } = Joi93.object({
|
|
20691
21276
|
journalEntry: schemaJournalCtrl.required(),
|
|
20692
|
-
journalLines:
|
|
21277
|
+
journalLines: Joi93.array().items(schemaJournalLineCtrl).min(1).required()
|
|
20693
21278
|
}).validate({
|
|
20694
21279
|
journalEntry: entry,
|
|
20695
21280
|
journalLines: lines
|
|
20696
21281
|
});
|
|
20697
21282
|
if (error) {
|
|
20698
|
-
throw new
|
|
21283
|
+
throw new BadRequestError105(
|
|
20699
21284
|
`Invalid general journal data: ${error.message}`
|
|
20700
21285
|
);
|
|
20701
21286
|
}
|
|
20702
21287
|
const session = useAtlas16.getClient()?.startSession();
|
|
20703
21288
|
if (!session) {
|
|
20704
|
-
throw new
|
|
21289
|
+
throw new BadRequestError105("Unable to start database session.");
|
|
20705
21290
|
}
|
|
20706
21291
|
try {
|
|
20707
21292
|
session.startTransaction();
|
|
@@ -20716,16 +21301,25 @@ function useJournalService() {
|
|
|
20716
21301
|
await validateJournalLines(lines);
|
|
20717
21302
|
const user = await getUserById(entry.createdBy);
|
|
20718
21303
|
if (!user) {
|
|
20719
|
-
throw new
|
|
21304
|
+
throw new NotFoundError7("User not found.");
|
|
20720
21305
|
}
|
|
20721
21306
|
entry.createdByName = `${user.firstName} ${user.lastName}`;
|
|
20722
21307
|
if (entry.customer) {
|
|
20723
21308
|
const customer = await getCustomerById(String(entry.customer));
|
|
20724
21309
|
if (!customer) {
|
|
20725
|
-
throw new
|
|
21310
|
+
throw new NotFoundError7("Customer not found.");
|
|
20726
21311
|
}
|
|
20727
21312
|
entry.customerName = `${customer.firstName} ${customer.lastName}`;
|
|
20728
21313
|
}
|
|
21314
|
+
const journalTx = await getJournalTransaction({
|
|
21315
|
+
org: String(entry.org),
|
|
21316
|
+
type: entry.type,
|
|
21317
|
+
_id: String(entry.transaction)
|
|
21318
|
+
});
|
|
21319
|
+
if (!journalTx) {
|
|
21320
|
+
throw new NotFoundError7("Transaction not found.");
|
|
21321
|
+
}
|
|
21322
|
+
entry.transactionName = journalTx.title;
|
|
20729
21323
|
entry.status = "draft";
|
|
20730
21324
|
const entryId = await _add(entry, session);
|
|
20731
21325
|
for (const line of lines) {
|
|
@@ -20733,7 +21327,7 @@ function useJournalService() {
|
|
|
20733
21327
|
line.journalEntry = entryId.toString();
|
|
20734
21328
|
const account = await getAccountById(String(line.account));
|
|
20735
21329
|
if (!account) {
|
|
20736
|
-
throw new
|
|
21330
|
+
throw new NotFoundError7(`Account not found: ${line.account}`);
|
|
20737
21331
|
}
|
|
20738
21332
|
line.accountName = account.name;
|
|
20739
21333
|
line.accountCode = account.code;
|
|
@@ -20765,10 +21359,10 @@ function useJournalService() {
|
|
|
20765
21359
|
return "Journal entry created successfully.";
|
|
20766
21360
|
} catch (error2) {
|
|
20767
21361
|
await session.abortTransaction();
|
|
20768
|
-
if (error2 instanceof
|
|
21362
|
+
if (error2 instanceof AppError49) {
|
|
20769
21363
|
throw error2;
|
|
20770
21364
|
}
|
|
20771
|
-
throw new
|
|
21365
|
+
throw new InternalServerError45(
|
|
20772
21366
|
`Failed to create general journal: ${error2.message}`
|
|
20773
21367
|
);
|
|
20774
21368
|
} finally {
|
|
@@ -20782,18 +21376,19 @@ function useJournalService() {
|
|
|
20782
21376
|
getYearBalancesByAccount,
|
|
20783
21377
|
updateById: updateAccountBalance
|
|
20784
21378
|
} = useAccountBalanceRepo();
|
|
21379
|
+
const { getByOrg: getBusinessProfileByOrg } = useBusinessProfileRepo();
|
|
20785
21380
|
async function updateStatusById(_id, status2, user) {
|
|
20786
|
-
const validation =
|
|
20787
|
-
_id:
|
|
20788
|
-
status:
|
|
21381
|
+
const validation = Joi93.object({
|
|
21382
|
+
_id: Joi93.string().hex().required(),
|
|
21383
|
+
status: Joi93.string().valid("posted", "voided").required()
|
|
20789
21384
|
});
|
|
20790
21385
|
const { error } = validation.validate({ _id, status: status2 });
|
|
20791
21386
|
if (error) {
|
|
20792
|
-
throw new
|
|
21387
|
+
throw new BadRequestError105(`Invalid input: ${error.message}`);
|
|
20793
21388
|
}
|
|
20794
21389
|
const session = useAtlas16.getClient()?.startSession();
|
|
20795
21390
|
if (!session) {
|
|
20796
|
-
throw new
|
|
21391
|
+
throw new BadRequestError105("Unable to start database session.");
|
|
20797
21392
|
}
|
|
20798
21393
|
try {
|
|
20799
21394
|
let computeClosingBalance2 = function(openingDebit, openingCredit, movementDebit, movementCredit) {
|
|
@@ -20807,18 +21402,18 @@ function useJournalService() {
|
|
|
20807
21402
|
var computeClosingBalance = computeClosingBalance2;
|
|
20808
21403
|
const journalEntry = await getJournalById(_id);
|
|
20809
21404
|
if (!journalEntry) {
|
|
20810
|
-
throw new
|
|
21405
|
+
throw new NotFoundError7("Journal entry not found.");
|
|
20811
21406
|
}
|
|
20812
21407
|
if (journalEntry.status === "voided") {
|
|
20813
|
-
throw new
|
|
21408
|
+
throw new BadRequestError105("Cannot update a voided general journal.");
|
|
20814
21409
|
}
|
|
20815
21410
|
if (journalEntry.status === "posted" && status2 === "draft") {
|
|
20816
|
-
throw new
|
|
21411
|
+
throw new BadRequestError105(
|
|
20817
21412
|
"Cannot revert a posted general journal to draft."
|
|
20818
21413
|
);
|
|
20819
21414
|
}
|
|
20820
21415
|
if (status2 === "voided" && journalEntry.reversalDraft) {
|
|
20821
|
-
throw new
|
|
21416
|
+
throw new BadRequestError105(
|
|
20822
21417
|
"Cannot void a general journal with an existing reversal draft."
|
|
20823
21418
|
);
|
|
20824
21419
|
}
|
|
@@ -20840,9 +21435,17 @@ function useJournalService() {
|
|
|
20840
21435
|
if (status2 === "posted") {
|
|
20841
21436
|
await updateStatusByJournal(_id, status2, session);
|
|
20842
21437
|
await _updateStatusById(_id, status2, session);
|
|
21438
|
+
const businessProfile = await getBusinessProfileByOrg(
|
|
21439
|
+
String(journalEntry.org)
|
|
21440
|
+
);
|
|
21441
|
+
if (!businessProfile) {
|
|
21442
|
+
throw new NotFoundError7(
|
|
21443
|
+
"Business profile not found for organization."
|
|
21444
|
+
);
|
|
21445
|
+
}
|
|
20843
21446
|
const linesToPost = await getJournalLinesByEntry(_id);
|
|
20844
21447
|
const journalDate = new Date(journalEntry.date);
|
|
20845
|
-
const fiscalYear =
|
|
21448
|
+
const fiscalYear = Number(businessProfile.currentFiscalYear);
|
|
20846
21449
|
const month = journalDate.toLocaleString("en-US", { month: "long" });
|
|
20847
21450
|
for (const line of linesToPost) {
|
|
20848
21451
|
const existingBalance = await getAccountBalance({
|
|
@@ -20865,9 +21468,16 @@ function useJournalService() {
|
|
|
20865
21468
|
String(journalEntry.org),
|
|
20866
21469
|
fiscalYear,
|
|
20867
21470
|
month,
|
|
20868
|
-
{
|
|
21471
|
+
{
|
|
21472
|
+
movementDebit,
|
|
21473
|
+
movementCredit,
|
|
21474
|
+
closingDebit,
|
|
21475
|
+
closingCredit,
|
|
21476
|
+
netBalance
|
|
21477
|
+
},
|
|
20869
21478
|
session
|
|
20870
21479
|
);
|
|
21480
|
+
await updateJournalLineBalance(String(line._id), netBalance, session);
|
|
20871
21481
|
} else {
|
|
20872
21482
|
const allYearBalances = await getYearBalancesByAccount({
|
|
20873
21483
|
account: String(line.account),
|
|
@@ -20875,7 +21485,9 @@ function useJournalService() {
|
|
|
20875
21485
|
fiscalYear
|
|
20876
21486
|
});
|
|
20877
21487
|
const currentMonthIdx = MONTHS.indexOf(month);
|
|
20878
|
-
const previousRecord = allYearBalances.filter((r) => MONTHS.indexOf(r.period.month) < currentMonthIdx).sort(
|
|
21488
|
+
const previousRecord = allYearBalances.filter((r) => MONTHS.indexOf(r.period.month) < currentMonthIdx).sort(
|
|
21489
|
+
(a, b) => MONTHS.indexOf(b.period.month) - MONTHS.indexOf(a.period.month)
|
|
21490
|
+
)[0];
|
|
20879
21491
|
const openingDebit = previousRecord?.closingDebit ?? 0;
|
|
20880
21492
|
const openingCredit = previousRecord?.closingCredit ?? 0;
|
|
20881
21493
|
const movementDebit = line.debit || 0;
|
|
@@ -20904,6 +21516,7 @@ function useJournalService() {
|
|
|
20904
21516
|
},
|
|
20905
21517
|
session
|
|
20906
21518
|
);
|
|
21519
|
+
await updateJournalLineBalance(String(line._id), netBalance, session);
|
|
20907
21520
|
}
|
|
20908
21521
|
}
|
|
20909
21522
|
await addJournalLog(
|
|
@@ -20918,7 +21531,7 @@ function useJournalService() {
|
|
|
20918
21531
|
if (journalEntry.reversedEntry) {
|
|
20919
21532
|
const userData = await getUserById(user);
|
|
20920
21533
|
if (!userData) {
|
|
20921
|
-
throw new
|
|
21534
|
+
throw new NotFoundError7("User not found.");
|
|
20922
21535
|
}
|
|
20923
21536
|
await _updateById(
|
|
20924
21537
|
journalEntry.reversedEntry,
|
|
@@ -20930,6 +21543,7 @@ function useJournalService() {
|
|
|
20930
21543
|
},
|
|
20931
21544
|
session
|
|
20932
21545
|
);
|
|
21546
|
+
await updateStatusByJournal(String(journalEntry.reversedEntry), "voided", session);
|
|
20933
21547
|
await addJournalLog(
|
|
20934
21548
|
{
|
|
20935
21549
|
journalId: String(journalEntry._id),
|
|
@@ -20946,7 +21560,7 @@ function useJournalService() {
|
|
|
20946
21560
|
const counterName = `${journalEntry.org.toString()}-general-journal`;
|
|
20947
21561
|
const counter = await getCounterByType(counterName);
|
|
20948
21562
|
if (!counter) {
|
|
20949
|
-
throw new
|
|
21563
|
+
throw new NotFoundError7("Counter not found for general journal.");
|
|
20950
21564
|
}
|
|
20951
21565
|
const date = /* @__PURE__ */ new Date();
|
|
20952
21566
|
const entryNumber = counter ? counter.count + 1 : 1;
|
|
@@ -20955,7 +21569,8 @@ function useJournalService() {
|
|
|
20955
21569
|
org: String(journalEntry.org),
|
|
20956
21570
|
id: entryNumber.toString().padStart(6, "0"),
|
|
20957
21571
|
type: journalEntry.type,
|
|
20958
|
-
transaction: journalEntry.transaction,
|
|
21572
|
+
transaction: String(journalEntry.transaction),
|
|
21573
|
+
transactionName: journalEntry.transactionName,
|
|
20959
21574
|
date,
|
|
20960
21575
|
description: `Reversal of ${getPrefixByType(journalEntry.type)}-${journalEntry.id}`,
|
|
20961
21576
|
createdBy: String(journalEntry.createdBy),
|
|
@@ -20974,7 +21589,7 @@ function useJournalService() {
|
|
|
20974
21589
|
);
|
|
20975
21590
|
const lines = await getJournalLinesByEntry(String(journalEntry._id));
|
|
20976
21591
|
if (lines && lines.length === 0) {
|
|
20977
|
-
throw new
|
|
21592
|
+
throw new NotFoundError7("Journal lines not found for reversal.");
|
|
20978
21593
|
}
|
|
20979
21594
|
const reversedLines = [];
|
|
20980
21595
|
for (const line of lines) {
|
|
@@ -21019,10 +21634,10 @@ function useJournalService() {
|
|
|
21019
21634
|
return "Journal entry status updated successfully.";
|
|
21020
21635
|
} catch (error2) {
|
|
21021
21636
|
await session.abortTransaction();
|
|
21022
|
-
if (error2 instanceof
|
|
21637
|
+
if (error2 instanceof AppError49) {
|
|
21023
21638
|
throw error2;
|
|
21024
21639
|
}
|
|
21025
|
-
throw new
|
|
21640
|
+
throw new InternalServerError45(
|
|
21026
21641
|
`Failed to update general journal status: ${error2.message}`
|
|
21027
21642
|
);
|
|
21028
21643
|
} finally {
|
|
@@ -21030,11 +21645,11 @@ function useJournalService() {
|
|
|
21030
21645
|
}
|
|
21031
21646
|
}
|
|
21032
21647
|
async function updateById(id, entry, lines, user) {
|
|
21033
|
-
const { error } =
|
|
21034
|
-
id:
|
|
21648
|
+
const { error } = Joi93.object({
|
|
21649
|
+
id: Joi93.string().hex().required(),
|
|
21035
21650
|
journalEntry: schemaJournalCtrlUpdate.required(),
|
|
21036
|
-
journalLines:
|
|
21037
|
-
user:
|
|
21651
|
+
journalLines: Joi93.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
|
|
21652
|
+
user: Joi93.string().hex().required()
|
|
21038
21653
|
}).validate({
|
|
21039
21654
|
id,
|
|
21040
21655
|
journalEntry: entry,
|
|
@@ -21042,13 +21657,13 @@ function useJournalService() {
|
|
|
21042
21657
|
user
|
|
21043
21658
|
});
|
|
21044
21659
|
if (error) {
|
|
21045
|
-
throw new
|
|
21660
|
+
throw new BadRequestError105(
|
|
21046
21661
|
`Invalid general journal data: ${error.message}`
|
|
21047
21662
|
);
|
|
21048
21663
|
}
|
|
21049
21664
|
const session = useAtlas16.getClient()?.startSession();
|
|
21050
21665
|
if (!session) {
|
|
21051
|
-
throw new
|
|
21666
|
+
throw new BadRequestError105("Unable to start database session.");
|
|
21052
21667
|
}
|
|
21053
21668
|
try {
|
|
21054
21669
|
let hasLineChanged2 = function(existing, incoming) {
|
|
@@ -21058,24 +21673,37 @@ function useJournalService() {
|
|
|
21058
21673
|
session.startTransaction();
|
|
21059
21674
|
const existingEntry = await getJournalById(id);
|
|
21060
21675
|
if (!existingEntry) {
|
|
21061
|
-
throw new
|
|
21676
|
+
throw new NotFoundError7("Journal entry not found.");
|
|
21062
21677
|
}
|
|
21063
21678
|
if (existingEntry.status !== "draft") {
|
|
21064
|
-
throw new
|
|
21679
|
+
throw new BadRequestError105("Only draft journal entries can be updated.");
|
|
21065
21680
|
}
|
|
21066
21681
|
const updatedBy = await getUserById(user);
|
|
21067
21682
|
if (!updatedBy) {
|
|
21068
|
-
throw new
|
|
21683
|
+
throw new NotFoundError7("User not found.");
|
|
21069
21684
|
}
|
|
21070
21685
|
await validateJournalLines(lines);
|
|
21071
|
-
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;
|
|
21686
|
+
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);
|
|
21072
21687
|
if (entry.customer && String(entry.customer) !== String(existingEntry.customer)) {
|
|
21073
21688
|
const customer = await getCustomerById(String(entry.customer));
|
|
21074
21689
|
if (!customer) {
|
|
21075
|
-
throw new
|
|
21690
|
+
throw new NotFoundError7("Customer not found.");
|
|
21076
21691
|
}
|
|
21077
21692
|
entry.customerName = `${customer.firstName} ${customer.lastName}`;
|
|
21078
21693
|
}
|
|
21694
|
+
if (String(existingEntry.transaction) !== String(entry.transaction)) {
|
|
21695
|
+
const journalTx = await getJournalTransaction({
|
|
21696
|
+
org: String(existingEntry.org),
|
|
21697
|
+
type: entry.type,
|
|
21698
|
+
_id: String(entry.transaction)
|
|
21699
|
+
});
|
|
21700
|
+
if (!journalTx) {
|
|
21701
|
+
throw new NotFoundError7("Transaction not found.");
|
|
21702
|
+
}
|
|
21703
|
+
entry.transactionName = journalTx.title;
|
|
21704
|
+
} else {
|
|
21705
|
+
entry.transactionName = existingEntry.transactionName;
|
|
21706
|
+
}
|
|
21079
21707
|
const existingLines = await getJournalLinesByEntry(id);
|
|
21080
21708
|
const existingLineMap = new Map(
|
|
21081
21709
|
existingLines.map((line) => [String(line._id), line])
|
|
@@ -21090,7 +21718,7 @@ function useJournalService() {
|
|
|
21090
21718
|
(existingId) => !incomingIds.has(existingId)
|
|
21091
21719
|
);
|
|
21092
21720
|
if (existingLines.length > 0 && removedLineIds.length === existingLines.length && lines.length > 0) {
|
|
21093
|
-
throw new
|
|
21721
|
+
throw new BadRequestError105(
|
|
21094
21722
|
"All journal lines marked for deletion. Identity mismatch suspected."
|
|
21095
21723
|
);
|
|
21096
21724
|
}
|
|
@@ -21108,7 +21736,7 @@ function useJournalService() {
|
|
|
21108
21736
|
const newLines = lines.filter((line) => !line._id);
|
|
21109
21737
|
const nothingChanged = !headerChanged && removedLineIds.length === 0 && updatedLines.length === 0 && newLines.length === 0;
|
|
21110
21738
|
if (nothingChanged) {
|
|
21111
|
-
throw new
|
|
21739
|
+
throw new BadRequestError105("No changes detected.");
|
|
21112
21740
|
}
|
|
21113
21741
|
for (const lineId of removedLineIds) {
|
|
21114
21742
|
await deleteJournalLineById(lineId, session);
|
|
@@ -21123,7 +21751,7 @@ function useJournalService() {
|
|
|
21123
21751
|
if (existing && String(existing.account) !== String(line.account)) {
|
|
21124
21752
|
const account = await getAccountById(String(line.account));
|
|
21125
21753
|
if (!account) {
|
|
21126
|
-
throw new
|
|
21754
|
+
throw new NotFoundError7(`Account not found: ${line.account}`);
|
|
21127
21755
|
}
|
|
21128
21756
|
updatePayload.accountName = account.name;
|
|
21129
21757
|
updatePayload.accountCode = account.code;
|
|
@@ -21133,7 +21761,7 @@ function useJournalService() {
|
|
|
21133
21761
|
for (const line of newLines) {
|
|
21134
21762
|
const account = await getAccountById(String(line.account));
|
|
21135
21763
|
if (!account) {
|
|
21136
|
-
throw new
|
|
21764
|
+
throw new NotFoundError7(`Account not found: ${line.account}`);
|
|
21137
21765
|
}
|
|
21138
21766
|
await addLine(
|
|
21139
21767
|
{
|
|
@@ -21218,10 +21846,10 @@ function useJournalService() {
|
|
|
21218
21846
|
return "Journal entry updated successfully.";
|
|
21219
21847
|
} catch (error2) {
|
|
21220
21848
|
await session.abortTransaction();
|
|
21221
|
-
if (error2 instanceof
|
|
21849
|
+
if (error2 instanceof AppError49) {
|
|
21222
21850
|
throw error2;
|
|
21223
21851
|
}
|
|
21224
|
-
throw new
|
|
21852
|
+
throw new InternalServerError45(
|
|
21225
21853
|
`Failed to update general journal: ${error2.message}`
|
|
21226
21854
|
);
|
|
21227
21855
|
} finally {
|
|
@@ -21236,8 +21864,8 @@ function useJournalService() {
|
|
|
21236
21864
|
}
|
|
21237
21865
|
|
|
21238
21866
|
// src/resources/finance-journal/finance.journal.controller.ts
|
|
21239
|
-
import
|
|
21240
|
-
import { BadRequestError as
|
|
21867
|
+
import Joi94 from "joi";
|
|
21868
|
+
import { BadRequestError as BadRequestError106, logger as logger53 } from "@goweekdays/utils";
|
|
21241
21869
|
function useJournalController() {
|
|
21242
21870
|
const {
|
|
21243
21871
|
getAll: _getAll,
|
|
@@ -21251,13 +21879,13 @@ function useJournalController() {
|
|
|
21251
21879
|
} = useJournalService();
|
|
21252
21880
|
async function add(req, res, next) {
|
|
21253
21881
|
const value = req.body;
|
|
21254
|
-
const { error } =
|
|
21882
|
+
const { error } = Joi94.object({
|
|
21255
21883
|
journalEntry: schemaJournalCtrl.required(),
|
|
21256
|
-
journalLines:
|
|
21884
|
+
journalLines: Joi94.array().items(schemaJournalLineCtrl).min(1).required()
|
|
21257
21885
|
}).validate(value);
|
|
21258
21886
|
if (error) {
|
|
21259
|
-
next(new
|
|
21260
|
-
|
|
21887
|
+
next(new BadRequestError106(error.message));
|
|
21888
|
+
logger53.info(`Controller: ${error.message}`);
|
|
21261
21889
|
return;
|
|
21262
21890
|
}
|
|
21263
21891
|
try {
|
|
@@ -21278,15 +21906,15 @@ function useJournalController() {
|
|
|
21278
21906
|
const search = req.query.search ?? "";
|
|
21279
21907
|
const status2 = req.query.status ?? "all";
|
|
21280
21908
|
if (!isFinite(page)) {
|
|
21281
|
-
next(new
|
|
21909
|
+
next(new BadRequestError106("Invalid page number."));
|
|
21282
21910
|
return;
|
|
21283
21911
|
}
|
|
21284
21912
|
if (!isFinite(limit)) {
|
|
21285
|
-
next(new
|
|
21913
|
+
next(new BadRequestError106("Invalid limit number."));
|
|
21286
21914
|
return;
|
|
21287
21915
|
}
|
|
21288
21916
|
if (error) {
|
|
21289
|
-
next(new
|
|
21917
|
+
next(new BadRequestError106(error.message));
|
|
21290
21918
|
return;
|
|
21291
21919
|
}
|
|
21292
21920
|
try {
|
|
@@ -21306,12 +21934,12 @@ function useJournalController() {
|
|
|
21306
21934
|
}
|
|
21307
21935
|
async function getById(req, res, next) {
|
|
21308
21936
|
const id = req.params.id;
|
|
21309
|
-
const validation =
|
|
21310
|
-
id:
|
|
21937
|
+
const validation = Joi94.object({
|
|
21938
|
+
id: Joi94.string().hex().required()
|
|
21311
21939
|
});
|
|
21312
21940
|
const { error } = validation.validate({ id });
|
|
21313
21941
|
if (error) {
|
|
21314
|
-
next(new
|
|
21942
|
+
next(new BadRequestError106(error.message));
|
|
21315
21943
|
return;
|
|
21316
21944
|
}
|
|
21317
21945
|
try {
|
|
@@ -21326,15 +21954,15 @@ function useJournalController() {
|
|
|
21326
21954
|
const value = req.body;
|
|
21327
21955
|
const id = req.params.id ?? "";
|
|
21328
21956
|
const user = req.cookies.user;
|
|
21329
|
-
const { error } =
|
|
21330
|
-
id:
|
|
21957
|
+
const { error } = Joi94.object({
|
|
21958
|
+
id: Joi94.string().hex().required(),
|
|
21331
21959
|
journalEntry: schemaJournalCtrlUpdate.required(),
|
|
21332
|
-
journalLines:
|
|
21333
|
-
user:
|
|
21960
|
+
journalLines: Joi94.array().items(schemaJournalLineCtrlUpdate).min(1).required(),
|
|
21961
|
+
user: Joi94.string().hex().required()
|
|
21334
21962
|
}).validate({ id, user, ...value });
|
|
21335
21963
|
if (error) {
|
|
21336
|
-
next(new
|
|
21337
|
-
|
|
21964
|
+
next(new BadRequestError106(error.message));
|
|
21965
|
+
logger53.info(`Controller: ${error.message}`);
|
|
21338
21966
|
return;
|
|
21339
21967
|
}
|
|
21340
21968
|
try {
|
|
@@ -21353,7 +21981,7 @@ function useJournalController() {
|
|
|
21353
21981
|
async function deleteById(req, res, next) {
|
|
21354
21982
|
const id = req.params.id;
|
|
21355
21983
|
if (!id) {
|
|
21356
|
-
next(new
|
|
21984
|
+
next(new BadRequestError106("Journal Entry ID is required."));
|
|
21357
21985
|
return;
|
|
21358
21986
|
}
|
|
21359
21987
|
try {
|
|
@@ -21368,14 +21996,14 @@ function useJournalController() {
|
|
|
21368
21996
|
const id = req.params.id;
|
|
21369
21997
|
const status2 = req.params.status;
|
|
21370
21998
|
const user = req.cookies.user;
|
|
21371
|
-
const validation =
|
|
21372
|
-
id:
|
|
21373
|
-
status:
|
|
21374
|
-
user:
|
|
21999
|
+
const validation = Joi94.object({
|
|
22000
|
+
id: Joi94.string().hex().required(),
|
|
22001
|
+
status: Joi94.string().required(),
|
|
22002
|
+
user: Joi94.string().hex().required()
|
|
21375
22003
|
});
|
|
21376
22004
|
const { error } = validation.validate({ id, status: status2, user });
|
|
21377
22005
|
if (error) {
|
|
21378
|
-
next(new
|
|
22006
|
+
next(new BadRequestError106(error.message));
|
|
21379
22007
|
return;
|
|
21380
22008
|
}
|
|
21381
22009
|
try {
|
|
@@ -21397,8 +22025,8 @@ function useJournalController() {
|
|
|
21397
22025
|
}
|
|
21398
22026
|
|
|
21399
22027
|
// src/resources/finance-journal-line/finance.journal.line.controller.ts
|
|
21400
|
-
import
|
|
21401
|
-
import { BadRequestError as
|
|
22028
|
+
import Joi95 from "joi";
|
|
22029
|
+
import { BadRequestError as BadRequestError107 } from "@goweekdays/utils";
|
|
21402
22030
|
function useJournalLineController() {
|
|
21403
22031
|
const {
|
|
21404
22032
|
getAll: _getAll,
|
|
@@ -21409,14 +22037,14 @@ function useJournalLineController() {
|
|
|
21409
22037
|
} = useJournalLineRepo();
|
|
21410
22038
|
async function getAll(req, res, next) {
|
|
21411
22039
|
const query = req.query;
|
|
21412
|
-
const validation =
|
|
21413
|
-
org:
|
|
21414
|
-
JournalGeneral:
|
|
21415
|
-
page:
|
|
21416
|
-
limit:
|
|
21417
|
-
account:
|
|
21418
|
-
search:
|
|
21419
|
-
status:
|
|
22040
|
+
const validation = Joi95.object({
|
|
22041
|
+
org: Joi95.string().hex().required(),
|
|
22042
|
+
JournalGeneral: Joi95.string().hex().optional(),
|
|
22043
|
+
page: Joi95.number().min(1).optional().allow("", null),
|
|
22044
|
+
limit: Joi95.number().min(1).optional().allow("", null),
|
|
22045
|
+
account: Joi95.string().hex().optional().allow("", null),
|
|
22046
|
+
search: Joi95.string().optional().allow("", null),
|
|
22047
|
+
status: Joi95.string().valid("draft", "posted").optional().allow("", null)
|
|
21420
22048
|
});
|
|
21421
22049
|
const org = req.params.org ?? "";
|
|
21422
22050
|
const JournalGeneral = req.query.JournalGeneral;
|
|
@@ -21432,15 +22060,15 @@ function useJournalLineController() {
|
|
|
21432
22060
|
account
|
|
21433
22061
|
});
|
|
21434
22062
|
if (!isFinite(page)) {
|
|
21435
|
-
next(new
|
|
22063
|
+
next(new BadRequestError107("Invalid page number."));
|
|
21436
22064
|
return;
|
|
21437
22065
|
}
|
|
21438
22066
|
if (!isFinite(limit)) {
|
|
21439
|
-
next(new
|
|
22067
|
+
next(new BadRequestError107("Invalid limit number."));
|
|
21440
22068
|
return;
|
|
21441
22069
|
}
|
|
21442
22070
|
if (error) {
|
|
21443
|
-
next(new
|
|
22071
|
+
next(new BadRequestError107(error.message));
|
|
21444
22072
|
return;
|
|
21445
22073
|
}
|
|
21446
22074
|
try {
|
|
@@ -21461,12 +22089,12 @@ function useJournalLineController() {
|
|
|
21461
22089
|
}
|
|
21462
22090
|
async function getById(req, res, next) {
|
|
21463
22091
|
const id = req.params.id;
|
|
21464
|
-
const validation =
|
|
21465
|
-
id:
|
|
22092
|
+
const validation = Joi95.object({
|
|
22093
|
+
id: Joi95.string().hex().required()
|
|
21466
22094
|
});
|
|
21467
22095
|
const { error } = validation.validate({ id });
|
|
21468
22096
|
if (error) {
|
|
21469
|
-
next(new
|
|
22097
|
+
next(new BadRequestError107(error.message));
|
|
21470
22098
|
return;
|
|
21471
22099
|
}
|
|
21472
22100
|
try {
|
|
@@ -21555,6 +22183,7 @@ export {
|
|
|
21555
22183
|
modelJobSummary,
|
|
21556
22184
|
modelJournal,
|
|
21557
22185
|
modelJournalLine,
|
|
22186
|
+
modelJournalTransaction,
|
|
21558
22187
|
modelLedgerBill,
|
|
21559
22188
|
modelMember,
|
|
21560
22189
|
modelOption,
|
|
@@ -21624,6 +22253,11 @@ export {
|
|
|
21624
22253
|
schemaJournalLineRepo,
|
|
21625
22254
|
schemaJournalRepo,
|
|
21626
22255
|
schemaJournalRepoUpdate,
|
|
22256
|
+
schemaJournalTransactionAccount,
|
|
22257
|
+
schemaJournalTransactionCtrl,
|
|
22258
|
+
schemaJournalTransactionCtrlUpdate,
|
|
22259
|
+
schemaJournalTransactionGetAll,
|
|
22260
|
+
schemaJournalTransactionRepo,
|
|
21627
22261
|
schemaLanguage,
|
|
21628
22262
|
schemaLedgerBill,
|
|
21629
22263
|
schemaLedgerBillingSummary,
|
|
@@ -21705,6 +22339,9 @@ export {
|
|
|
21705
22339
|
useJournalLineRepo,
|
|
21706
22340
|
useJournalRepo,
|
|
21707
22341
|
useJournalService,
|
|
22342
|
+
useJournalTransactionController,
|
|
22343
|
+
useJournalTransactionRepo,
|
|
22344
|
+
useJournalTransactionService,
|
|
21708
22345
|
useLedgerBillingController,
|
|
21709
22346
|
useLedgerBillingRepo,
|
|
21710
22347
|
useMemberController,
|