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