@goweekdays/core 2.12.3 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +232 -3
- package/dist/index.js +2379 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2393 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -11073,6 +11073,24 @@ function useOrgController() {
|
|
|
11073
11073
|
companySearch: _companySearch
|
|
11074
11074
|
} = useOrgRepo();
|
|
11075
11075
|
const { addPilot: _addPilot } = useOrgService();
|
|
11076
|
+
async function validOrg(req, res, next) {
|
|
11077
|
+
const org = req.params.org ?? "";
|
|
11078
|
+
const { error } = Joi49.string().hex().required().validate(org);
|
|
11079
|
+
if (error) {
|
|
11080
|
+
next(new BadRequestError53("Invalid organization ID."));
|
|
11081
|
+
return;
|
|
11082
|
+
}
|
|
11083
|
+
try {
|
|
11084
|
+
const organization = await _getById(org);
|
|
11085
|
+
if (!organization) {
|
|
11086
|
+
next(new BadRequestError53("Organization not found."));
|
|
11087
|
+
return;
|
|
11088
|
+
}
|
|
11089
|
+
next();
|
|
11090
|
+
} catch (error2) {
|
|
11091
|
+
next(error2);
|
|
11092
|
+
}
|
|
11093
|
+
}
|
|
11076
11094
|
async function add(req, res, next) {
|
|
11077
11095
|
const value = req.body;
|
|
11078
11096
|
const { error } = schemaOrgAdd.validate(value);
|
|
@@ -11246,6 +11264,7 @@ function useOrgController() {
|
|
|
11246
11264
|
}
|
|
11247
11265
|
}
|
|
11248
11266
|
return {
|
|
11267
|
+
validOrg,
|
|
11249
11268
|
add,
|
|
11250
11269
|
getOrgsByUserId,
|
|
11251
11270
|
getByName,
|
|
@@ -13798,10 +13817,10 @@ function useCounterRepo() {
|
|
|
13798
13817
|
throw new Error("Failed to create index.");
|
|
13799
13818
|
}
|
|
13800
13819
|
}
|
|
13801
|
-
async function add(type) {
|
|
13820
|
+
async function add(type, session) {
|
|
13802
13821
|
try {
|
|
13803
13822
|
const value = createCounter({ type });
|
|
13804
|
-
await collection.insertOne(value);
|
|
13823
|
+
await collection.insertOne(value, { session });
|
|
13805
13824
|
delCachedData();
|
|
13806
13825
|
} catch (error) {
|
|
13807
13826
|
throw new Error("Failed to add counter.");
|
|
@@ -13811,7 +13830,7 @@ function useCounterRepo() {
|
|
|
13811
13830
|
try {
|
|
13812
13831
|
const res = await collection.updateOne(
|
|
13813
13832
|
{ type },
|
|
13814
|
-
{ $inc: { count: 1 } },
|
|
13833
|
+
{ $inc: { count: 1 }, $set: { updatedAt: /* @__PURE__ */ new Date() } },
|
|
13815
13834
|
{ session }
|
|
13816
13835
|
);
|
|
13817
13836
|
delCachedData();
|
|
@@ -17045,6 +17064,2349 @@ function useJobSummaryCtrl() {
|
|
|
17045
17064
|
getByOrg
|
|
17046
17065
|
};
|
|
17047
17066
|
}
|
|
17067
|
+
|
|
17068
|
+
// src/resources/finance-tax/tax.model.ts
|
|
17069
|
+
import { BadRequestError as BadRequestError80 } from "@goweekdays/utils";
|
|
17070
|
+
import Joi69 from "joi";
|
|
17071
|
+
import { ObjectId as ObjectId40 } from "mongodb";
|
|
17072
|
+
var taxDirections = ["input", "output", "withholding"];
|
|
17073
|
+
var schemaTax = Joi69.object({
|
|
17074
|
+
org: Joi69.string().hex().required(),
|
|
17075
|
+
name: Joi69.string().required(),
|
|
17076
|
+
rate: Joi69.number().required(),
|
|
17077
|
+
direction: Joi69.string().valid(...taxDirections).required()
|
|
17078
|
+
});
|
|
17079
|
+
var schemaTaxUpdate = Joi69.object({
|
|
17080
|
+
name: Joi69.string().required(),
|
|
17081
|
+
rate: Joi69.number().required(),
|
|
17082
|
+
direction: Joi69.string().valid(...taxDirections).required()
|
|
17083
|
+
});
|
|
17084
|
+
function modelTax(data) {
|
|
17085
|
+
const { error } = schemaTax.validate(data);
|
|
17086
|
+
if (error) {
|
|
17087
|
+
throw new BadRequestError80(`Invalid tax data: ${error.message}`);
|
|
17088
|
+
}
|
|
17089
|
+
try {
|
|
17090
|
+
data.org = new ObjectId40(data.org);
|
|
17091
|
+
} catch (error2) {
|
|
17092
|
+
throw new BadRequestError80(`Invalid org ID: ${data.org}`);
|
|
17093
|
+
}
|
|
17094
|
+
return {
|
|
17095
|
+
_id: data._id,
|
|
17096
|
+
org: data.org,
|
|
17097
|
+
name: data.name,
|
|
17098
|
+
rate: data.rate,
|
|
17099
|
+
direction: data.direction,
|
|
17100
|
+
status: data.status ?? "active",
|
|
17101
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
17102
|
+
updatedAt: data.updatedAt ?? ""
|
|
17103
|
+
};
|
|
17104
|
+
}
|
|
17105
|
+
|
|
17106
|
+
// src/resources/finance-tax/tax.repository.ts
|
|
17107
|
+
import {
|
|
17108
|
+
AppError as AppError39,
|
|
17109
|
+
BadRequestError as BadRequestError81,
|
|
17110
|
+
useAtlas as useAtlas36,
|
|
17111
|
+
useCache as useCache27,
|
|
17112
|
+
makeCacheKey as makeCacheKey24,
|
|
17113
|
+
paginate as paginate21,
|
|
17114
|
+
logger as logger41,
|
|
17115
|
+
InternalServerError as InternalServerError36
|
|
17116
|
+
} from "@goweekdays/utils";
|
|
17117
|
+
import { ObjectId as ObjectId41 } from "mongodb";
|
|
17118
|
+
import Joi70 from "joi";
|
|
17119
|
+
function useTaxRepo() {
|
|
17120
|
+
const db = useAtlas36.getDb();
|
|
17121
|
+
if (!db) {
|
|
17122
|
+
throw new BadRequestError81("Unable to connect to server.");
|
|
17123
|
+
}
|
|
17124
|
+
const namespace_collection = "finance.taxes";
|
|
17125
|
+
const collection = db.collection(namespace_collection);
|
|
17126
|
+
const { getCache, setCache, delNamespace } = useCache27(namespace_collection);
|
|
17127
|
+
function delCachedData() {
|
|
17128
|
+
delNamespace().then(() => {
|
|
17129
|
+
logger41.log({
|
|
17130
|
+
level: "info",
|
|
17131
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
17132
|
+
});
|
|
17133
|
+
}).catch((err) => {
|
|
17134
|
+
logger41.log({
|
|
17135
|
+
level: "error",
|
|
17136
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
17137
|
+
});
|
|
17138
|
+
});
|
|
17139
|
+
}
|
|
17140
|
+
async function createIndexes() {
|
|
17141
|
+
try {
|
|
17142
|
+
await collection.createIndexes([
|
|
17143
|
+
{ key: { name: 1 } },
|
|
17144
|
+
{ key: { org: 1 } },
|
|
17145
|
+
{
|
|
17146
|
+
key: { name: 1, org: 1 },
|
|
17147
|
+
partialFilterExpression: { status: "active" },
|
|
17148
|
+
unique: true,
|
|
17149
|
+
name: "unique_name_per_org"
|
|
17150
|
+
}
|
|
17151
|
+
]);
|
|
17152
|
+
} catch (error) {
|
|
17153
|
+
throw new Error("Failed to create index on tax.");
|
|
17154
|
+
}
|
|
17155
|
+
}
|
|
17156
|
+
async function add(value, session) {
|
|
17157
|
+
try {
|
|
17158
|
+
value = modelTax(value);
|
|
17159
|
+
const res = await collection.insertOne(value, { session });
|
|
17160
|
+
delCachedData();
|
|
17161
|
+
return res.insertedId;
|
|
17162
|
+
} catch (error) {
|
|
17163
|
+
logger41.log({
|
|
17164
|
+
level: "error",
|
|
17165
|
+
message: error.message
|
|
17166
|
+
});
|
|
17167
|
+
throw new BadRequestError81(`Failed to create tax: ${error.message}`);
|
|
17168
|
+
}
|
|
17169
|
+
}
|
|
17170
|
+
async function getAll(options) {
|
|
17171
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
17172
|
+
options.status = options.status ?? "active";
|
|
17173
|
+
options.limit = options.limit ?? 10;
|
|
17174
|
+
const query = { status: options.status };
|
|
17175
|
+
try {
|
|
17176
|
+
query.org = new ObjectId41(options.org);
|
|
17177
|
+
} catch (error) {
|
|
17178
|
+
throw new BadRequestError81("Invalid organization ID.");
|
|
17179
|
+
}
|
|
17180
|
+
if (options.search) {
|
|
17181
|
+
query.$text = { $search: options.search };
|
|
17182
|
+
}
|
|
17183
|
+
const cacheKey = makeCacheKey24(namespace_collection, {
|
|
17184
|
+
search: options.search,
|
|
17185
|
+
page: options.page,
|
|
17186
|
+
limit: options.limit,
|
|
17187
|
+
status: options.status
|
|
17188
|
+
});
|
|
17189
|
+
logger41.log({
|
|
17190
|
+
level: "info",
|
|
17191
|
+
message: `Cache key for getAll taxes: ${cacheKey}`
|
|
17192
|
+
});
|
|
17193
|
+
try {
|
|
17194
|
+
const cached = await getCache(cacheKey);
|
|
17195
|
+
if (cached) {
|
|
17196
|
+
logger41.log({
|
|
17197
|
+
level: "info",
|
|
17198
|
+
message: `Cache hit for getAll taxes: ${cacheKey}`
|
|
17199
|
+
});
|
|
17200
|
+
return cached;
|
|
17201
|
+
}
|
|
17202
|
+
const items = await collection.aggregate([
|
|
17203
|
+
{ $match: query },
|
|
17204
|
+
{ $skip: options.page * options.limit },
|
|
17205
|
+
{ $limit: options.limit },
|
|
17206
|
+
{
|
|
17207
|
+
$project: {
|
|
17208
|
+
_id: 1,
|
|
17209
|
+
org: 1,
|
|
17210
|
+
name: 1,
|
|
17211
|
+
rate: 1,
|
|
17212
|
+
direction: 1,
|
|
17213
|
+
status: 1,
|
|
17214
|
+
createdAt: 1
|
|
17215
|
+
}
|
|
17216
|
+
}
|
|
17217
|
+
]).toArray();
|
|
17218
|
+
const length = await collection.countDocuments(query);
|
|
17219
|
+
const data = paginate21(items, options.page, options.limit, length);
|
|
17220
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
17221
|
+
logger41.log({
|
|
17222
|
+
level: "info",
|
|
17223
|
+
message: `Cache set for getAll taxes: ${cacheKey}`
|
|
17224
|
+
});
|
|
17225
|
+
}).catch((err) => {
|
|
17226
|
+
logger41.log({
|
|
17227
|
+
level: "error",
|
|
17228
|
+
message: `Failed to set cache for getAll taxes: ${err.message}`
|
|
17229
|
+
});
|
|
17230
|
+
});
|
|
17231
|
+
return data;
|
|
17232
|
+
} catch (error) {
|
|
17233
|
+
logger41.log({ level: "error", message: `${error}` });
|
|
17234
|
+
throw error;
|
|
17235
|
+
}
|
|
17236
|
+
}
|
|
17237
|
+
async function getTaxesByOrg({ search = "", page = 1, limit = 10, org = "", status: status2 = "active" } = {}) {
|
|
17238
|
+
page = page > 0 ? page - 1 : 0;
|
|
17239
|
+
try {
|
|
17240
|
+
org = new ObjectId41(org);
|
|
17241
|
+
} catch (error) {
|
|
17242
|
+
throw new BadRequestError81("Invalid organization ID.");
|
|
17243
|
+
}
|
|
17244
|
+
const query = { org, status: status2 };
|
|
17245
|
+
const cacheKeyOptions = {
|
|
17246
|
+
org: String(org),
|
|
17247
|
+
status: status2,
|
|
17248
|
+
limit,
|
|
17249
|
+
page
|
|
17250
|
+
};
|
|
17251
|
+
if (search) {
|
|
17252
|
+
cacheKeyOptions.search = search;
|
|
17253
|
+
query.$text = { $search: search };
|
|
17254
|
+
}
|
|
17255
|
+
try {
|
|
17256
|
+
const cacheKey = makeCacheKey24(namespace_collection, cacheKeyOptions);
|
|
17257
|
+
const cached = await getCache(
|
|
17258
|
+
cacheKey
|
|
17259
|
+
);
|
|
17260
|
+
if (cached) {
|
|
17261
|
+
logger41.log({
|
|
17262
|
+
level: "info",
|
|
17263
|
+
message: `Cache hit for getTaxesByOrg : ${cacheKey}`
|
|
17264
|
+
});
|
|
17265
|
+
return cached;
|
|
17266
|
+
}
|
|
17267
|
+
const items = await collection.aggregate([
|
|
17268
|
+
{ $match: query },
|
|
17269
|
+
{ $skip: page * limit },
|
|
17270
|
+
{ $limit: limit },
|
|
17271
|
+
{
|
|
17272
|
+
$project: {
|
|
17273
|
+
_id: 1,
|
|
17274
|
+
name: 1,
|
|
17275
|
+
rate: 1,
|
|
17276
|
+
direction: 1,
|
|
17277
|
+
status: 1,
|
|
17278
|
+
createdAt: 1
|
|
17279
|
+
}
|
|
17280
|
+
}
|
|
17281
|
+
]).toArray();
|
|
17282
|
+
const length = await collection.countDocuments(query);
|
|
17283
|
+
const data = paginate21(items, page, limit, length);
|
|
17284
|
+
setCache(cacheKey, data, 300).then(() => {
|
|
17285
|
+
logger41.log({
|
|
17286
|
+
level: "info",
|
|
17287
|
+
message: `Cache set for getTaxesByOrg: ${cacheKey}`
|
|
17288
|
+
});
|
|
17289
|
+
}).catch((err) => {
|
|
17290
|
+
logger41.log({
|
|
17291
|
+
level: "error",
|
|
17292
|
+
message: `Failed to set cache for getTaxesByOrg: ${err.message}`
|
|
17293
|
+
});
|
|
17294
|
+
});
|
|
17295
|
+
return data;
|
|
17296
|
+
} catch (error) {
|
|
17297
|
+
throw new InternalServerError36(
|
|
17298
|
+
"Internal server error, failed to retrieve taxes."
|
|
17299
|
+
);
|
|
17300
|
+
}
|
|
17301
|
+
}
|
|
17302
|
+
async function getById(_id) {
|
|
17303
|
+
try {
|
|
17304
|
+
_id = new ObjectId41(_id);
|
|
17305
|
+
} catch (error) {
|
|
17306
|
+
throw new BadRequestError81("Invalid ID.");
|
|
17307
|
+
}
|
|
17308
|
+
const cacheKey = makeCacheKey24(namespace_collection, { _id: String(_id) });
|
|
17309
|
+
try {
|
|
17310
|
+
const cached = await getCache(cacheKey);
|
|
17311
|
+
if (cached) {
|
|
17312
|
+
logger41.log({
|
|
17313
|
+
level: "info",
|
|
17314
|
+
message: `Cache hit for getById tax: ${cacheKey}`
|
|
17315
|
+
});
|
|
17316
|
+
return cached;
|
|
17317
|
+
}
|
|
17318
|
+
const result = await collection.findOne({ _id });
|
|
17319
|
+
if (!result) {
|
|
17320
|
+
throw new BadRequestError81("Tax not found.");
|
|
17321
|
+
}
|
|
17322
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
17323
|
+
logger41.log({
|
|
17324
|
+
level: "info",
|
|
17325
|
+
message: `Cache set for tax by id: ${cacheKey}`
|
|
17326
|
+
});
|
|
17327
|
+
}).catch((err) => {
|
|
17328
|
+
logger41.log({
|
|
17329
|
+
level: "error",
|
|
17330
|
+
message: `Failed to set cache for tax by id: ${err.message}`
|
|
17331
|
+
});
|
|
17332
|
+
});
|
|
17333
|
+
return result;
|
|
17334
|
+
} catch (error) {
|
|
17335
|
+
if (error instanceof AppError39) {
|
|
17336
|
+
throw error;
|
|
17337
|
+
} else {
|
|
17338
|
+
throw new InternalServerError36("Failed to get tax.");
|
|
17339
|
+
}
|
|
17340
|
+
}
|
|
17341
|
+
}
|
|
17342
|
+
async function updateById(_id, options) {
|
|
17343
|
+
const { error: errorId } = Joi70.string().hex().required().validate(String(_id));
|
|
17344
|
+
if (errorId) {
|
|
17345
|
+
throw new BadRequestError81("Invalid Tax ID.");
|
|
17346
|
+
}
|
|
17347
|
+
const { error } = schemaTaxUpdate.validate(options);
|
|
17348
|
+
if (error) {
|
|
17349
|
+
throw new BadRequestError81(`Invalid tax update data: ${error.message}`);
|
|
17350
|
+
}
|
|
17351
|
+
try {
|
|
17352
|
+
_id = new ObjectId41(_id);
|
|
17353
|
+
} catch (error2) {
|
|
17354
|
+
throw new BadRequestError81("Invalid Tax ID.");
|
|
17355
|
+
}
|
|
17356
|
+
try {
|
|
17357
|
+
await collection.updateOne(
|
|
17358
|
+
{ _id },
|
|
17359
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } }
|
|
17360
|
+
);
|
|
17361
|
+
delCachedData();
|
|
17362
|
+
return "Successfully updated tax.";
|
|
17363
|
+
} catch (error2) {
|
|
17364
|
+
throw new InternalServerError36("Failed to update tax.");
|
|
17365
|
+
}
|
|
17366
|
+
}
|
|
17367
|
+
async function deleteById(_id, session) {
|
|
17368
|
+
try {
|
|
17369
|
+
_id = new ObjectId41(_id);
|
|
17370
|
+
} catch (error) {
|
|
17371
|
+
throw new BadRequestError81("Invalid ID.");
|
|
17372
|
+
}
|
|
17373
|
+
try {
|
|
17374
|
+
await collection.updateOne(
|
|
17375
|
+
{ _id },
|
|
17376
|
+
{
|
|
17377
|
+
$set: {
|
|
17378
|
+
status: "deleted",
|
|
17379
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
17380
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
17381
|
+
}
|
|
17382
|
+
},
|
|
17383
|
+
{ session }
|
|
17384
|
+
);
|
|
17385
|
+
delCachedData();
|
|
17386
|
+
return "Successfully deleted tax.";
|
|
17387
|
+
} catch (error) {
|
|
17388
|
+
throw new InternalServerError36("Failed to delete tax.");
|
|
17389
|
+
}
|
|
17390
|
+
}
|
|
17391
|
+
async function updateStatusById(_id, status2) {
|
|
17392
|
+
try {
|
|
17393
|
+
_id = new ObjectId41(_id);
|
|
17394
|
+
} catch (error) {
|
|
17395
|
+
throw new BadRequestError81("Invalid ID.");
|
|
17396
|
+
}
|
|
17397
|
+
try {
|
|
17398
|
+
const result = await collection.updateOne(
|
|
17399
|
+
{ _id },
|
|
17400
|
+
{
|
|
17401
|
+
$set: {
|
|
17402
|
+
status: status2,
|
|
17403
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
17404
|
+
}
|
|
17405
|
+
}
|
|
17406
|
+
);
|
|
17407
|
+
if (result.matchedCount === 0) {
|
|
17408
|
+
throw new BadRequestError81("Tax not found.");
|
|
17409
|
+
}
|
|
17410
|
+
delCachedData();
|
|
17411
|
+
return "Successfully updated tax status.";
|
|
17412
|
+
} catch (error) {
|
|
17413
|
+
if (error instanceof AppError39) {
|
|
17414
|
+
throw error;
|
|
17415
|
+
}
|
|
17416
|
+
throw new InternalServerError36("Failed to update tax status.");
|
|
17417
|
+
}
|
|
17418
|
+
}
|
|
17419
|
+
return {
|
|
17420
|
+
createIndexes,
|
|
17421
|
+
add,
|
|
17422
|
+
getAll,
|
|
17423
|
+
getTaxesByOrg,
|
|
17424
|
+
getById,
|
|
17425
|
+
updateById,
|
|
17426
|
+
deleteById,
|
|
17427
|
+
updateStatusById
|
|
17428
|
+
};
|
|
17429
|
+
}
|
|
17430
|
+
|
|
17431
|
+
// src/resources/finance-tax/tax.controller.ts
|
|
17432
|
+
import Joi71 from "joi";
|
|
17433
|
+
import { BadRequestError as BadRequestError82, logger as logger42 } from "@goweekdays/utils";
|
|
17434
|
+
function useTaxController() {
|
|
17435
|
+
const {
|
|
17436
|
+
getAll: _getAll,
|
|
17437
|
+
getTaxesByOrg: _getTaxesByOrg,
|
|
17438
|
+
getById: _getById,
|
|
17439
|
+
updateById: _updateById,
|
|
17440
|
+
deleteById: _deleteById,
|
|
17441
|
+
updateStatusById: _updateStatusById,
|
|
17442
|
+
add: _add
|
|
17443
|
+
} = useTaxRepo();
|
|
17444
|
+
async function add(req, res, next) {
|
|
17445
|
+
const value = req.body;
|
|
17446
|
+
const { error } = schemaTax.validate(value);
|
|
17447
|
+
if (error) {
|
|
17448
|
+
next(new BadRequestError82(error.message));
|
|
17449
|
+
logger42.info(`Controller: ${error.message}`);
|
|
17450
|
+
return;
|
|
17451
|
+
}
|
|
17452
|
+
try {
|
|
17453
|
+
const message = await _add(value);
|
|
17454
|
+
res.json({ message });
|
|
17455
|
+
return;
|
|
17456
|
+
} catch (error2) {
|
|
17457
|
+
next(error2);
|
|
17458
|
+
}
|
|
17459
|
+
}
|
|
17460
|
+
async function getAll(req, res, next) {
|
|
17461
|
+
const query = req.query;
|
|
17462
|
+
const validation = Joi71.object({
|
|
17463
|
+
org: Joi71.string().hex().required(),
|
|
17464
|
+
page: Joi71.number().min(1).optional().allow("", null),
|
|
17465
|
+
limit: Joi71.number().min(1).optional().allow("", null),
|
|
17466
|
+
search: Joi71.string().optional().allow("", null),
|
|
17467
|
+
status: Joi71.string().optional()
|
|
17468
|
+
});
|
|
17469
|
+
const org = req.params.org ?? "";
|
|
17470
|
+
const { error } = validation.validate({ ...query, org });
|
|
17471
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
17472
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
17473
|
+
const search = req.query.search ?? "";
|
|
17474
|
+
const status2 = req.query.status ?? "active";
|
|
17475
|
+
const isPageNumber = isFinite(page);
|
|
17476
|
+
if (!isPageNumber) {
|
|
17477
|
+
next(new BadRequestError82("Invalid page number."));
|
|
17478
|
+
return;
|
|
17479
|
+
}
|
|
17480
|
+
const isLimitNumber = isFinite(limit);
|
|
17481
|
+
if (!isLimitNumber) {
|
|
17482
|
+
next(new BadRequestError82("Invalid limit number."));
|
|
17483
|
+
return;
|
|
17484
|
+
}
|
|
17485
|
+
if (error) {
|
|
17486
|
+
next(new BadRequestError82(error.message));
|
|
17487
|
+
return;
|
|
17488
|
+
}
|
|
17489
|
+
try {
|
|
17490
|
+
const taxes = await _getAll({ org, page, limit, search, status: status2 });
|
|
17491
|
+
res.json(taxes);
|
|
17492
|
+
return;
|
|
17493
|
+
} catch (error2) {
|
|
17494
|
+
next(error2);
|
|
17495
|
+
}
|
|
17496
|
+
}
|
|
17497
|
+
async function getTaxesByOrg(req, res, next) {
|
|
17498
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
17499
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
17500
|
+
const search = req.query.search ?? "";
|
|
17501
|
+
const status2 = req.query.status ?? "active";
|
|
17502
|
+
const org = req.params.org ?? "";
|
|
17503
|
+
const isPageNumber = isFinite(page);
|
|
17504
|
+
if (!isPageNumber) {
|
|
17505
|
+
next(new BadRequestError82("Invalid page number."));
|
|
17506
|
+
return;
|
|
17507
|
+
}
|
|
17508
|
+
const isLimitNumber = isFinite(limit);
|
|
17509
|
+
if (!isLimitNumber) {
|
|
17510
|
+
next(new BadRequestError82("Invalid limit number."));
|
|
17511
|
+
return;
|
|
17512
|
+
}
|
|
17513
|
+
const validation = Joi71.object({
|
|
17514
|
+
org: Joi71.string().hex().required(),
|
|
17515
|
+
page: Joi71.number().min(1).optional().allow("", null),
|
|
17516
|
+
limit: Joi71.number().min(1).optional().allow("", null),
|
|
17517
|
+
search: Joi71.string().optional().allow("", null),
|
|
17518
|
+
status: Joi71.string().optional()
|
|
17519
|
+
});
|
|
17520
|
+
const { error } = validation.validate({ org, page, limit, search, status: status2 });
|
|
17521
|
+
if (error) {
|
|
17522
|
+
next(new BadRequestError82(error.message));
|
|
17523
|
+
return;
|
|
17524
|
+
}
|
|
17525
|
+
try {
|
|
17526
|
+
const taxes = await _getTaxesByOrg({ org, page, limit, search, status: status2 });
|
|
17527
|
+
res.json(taxes);
|
|
17528
|
+
return;
|
|
17529
|
+
} catch (error2) {
|
|
17530
|
+
next(error2);
|
|
17531
|
+
}
|
|
17532
|
+
}
|
|
17533
|
+
async function getById(req, res, next) {
|
|
17534
|
+
const id = req.params.id;
|
|
17535
|
+
const validation = Joi71.object({
|
|
17536
|
+
id: Joi71.string().hex().required()
|
|
17537
|
+
});
|
|
17538
|
+
const { error } = validation.validate({ id });
|
|
17539
|
+
if (error) {
|
|
17540
|
+
next(new BadRequestError82(error.message));
|
|
17541
|
+
return;
|
|
17542
|
+
}
|
|
17543
|
+
try {
|
|
17544
|
+
const tax = await _getById(id);
|
|
17545
|
+
res.json(tax);
|
|
17546
|
+
return;
|
|
17547
|
+
} catch (error2) {
|
|
17548
|
+
next(error2);
|
|
17549
|
+
}
|
|
17550
|
+
}
|
|
17551
|
+
async function updateById(req, res, next) {
|
|
17552
|
+
const _id = req.params.id ?? "";
|
|
17553
|
+
const { error: errorId } = Joi71.string().hex().required().validate(_id);
|
|
17554
|
+
if (errorId) {
|
|
17555
|
+
next(new BadRequestError82("Invalid Tax ID."));
|
|
17556
|
+
return;
|
|
17557
|
+
}
|
|
17558
|
+
const payload = req.body;
|
|
17559
|
+
const { error } = schemaTaxUpdate.validate(payload);
|
|
17560
|
+
if (error) {
|
|
17561
|
+
next(new BadRequestError82(`Invalid tax update data: ${error.message}`));
|
|
17562
|
+
return;
|
|
17563
|
+
}
|
|
17564
|
+
try {
|
|
17565
|
+
const message = await _updateById(_id, payload);
|
|
17566
|
+
res.json({ message });
|
|
17567
|
+
return;
|
|
17568
|
+
} catch (error2) {
|
|
17569
|
+
next(error2);
|
|
17570
|
+
}
|
|
17571
|
+
}
|
|
17572
|
+
async function deleteById(req, res, next) {
|
|
17573
|
+
const id = req.params.id;
|
|
17574
|
+
if (!id) {
|
|
17575
|
+
next(new BadRequestError82("Tax ID is required."));
|
|
17576
|
+
return;
|
|
17577
|
+
}
|
|
17578
|
+
try {
|
|
17579
|
+
const message = await _deleteById(id);
|
|
17580
|
+
res.json(message);
|
|
17581
|
+
return;
|
|
17582
|
+
} catch (error) {
|
|
17583
|
+
next(error);
|
|
17584
|
+
}
|
|
17585
|
+
}
|
|
17586
|
+
async function updateStatusById(req, res, next) {
|
|
17587
|
+
const id = req.params.id;
|
|
17588
|
+
const status2 = req.params.status;
|
|
17589
|
+
const validation = Joi71.object({
|
|
17590
|
+
id: Joi71.string().hex().required(),
|
|
17591
|
+
status: Joi71.string().required()
|
|
17592
|
+
});
|
|
17593
|
+
const { error } = validation.validate({ id, status: status2 });
|
|
17594
|
+
if (error) {
|
|
17595
|
+
next(new BadRequestError82(error.message));
|
|
17596
|
+
return;
|
|
17597
|
+
}
|
|
17598
|
+
try {
|
|
17599
|
+
const message = await _updateStatusById(id, status2);
|
|
17600
|
+
res.json({ message });
|
|
17601
|
+
return;
|
|
17602
|
+
} catch (error2) {
|
|
17603
|
+
next(error2);
|
|
17604
|
+
}
|
|
17605
|
+
}
|
|
17606
|
+
return {
|
|
17607
|
+
add,
|
|
17608
|
+
getAll,
|
|
17609
|
+
getTaxesByOrg,
|
|
17610
|
+
getById,
|
|
17611
|
+
updateById,
|
|
17612
|
+
deleteById,
|
|
17613
|
+
updateStatusById
|
|
17614
|
+
};
|
|
17615
|
+
}
|
|
17616
|
+
|
|
17617
|
+
// src/resources/finance-account/chart-of-account.model.ts
|
|
17618
|
+
import Joi72 from "joi";
|
|
17619
|
+
import { ObjectId as ObjectId42 } from "mongodb";
|
|
17620
|
+
var chartOfAccountTypes = [
|
|
17621
|
+
"asset",
|
|
17622
|
+
"liability",
|
|
17623
|
+
"equity",
|
|
17624
|
+
"income",
|
|
17625
|
+
"expense"
|
|
17626
|
+
];
|
|
17627
|
+
var chartOfAccountNormalBalances = [
|
|
17628
|
+
"debit",
|
|
17629
|
+
"credit"
|
|
17630
|
+
];
|
|
17631
|
+
var schemaChartOfAccount = {
|
|
17632
|
+
type: Joi72.string().valid(...chartOfAccountTypes).required(),
|
|
17633
|
+
normalBalance: Joi72.string().valid(...chartOfAccountNormalBalances).required(),
|
|
17634
|
+
parent: Joi72.string().hex().optional().allow("", null),
|
|
17635
|
+
name: Joi72.string().required(),
|
|
17636
|
+
code: Joi72.string().required(),
|
|
17637
|
+
tax: Joi72.string().hex().optional().allow("", null)
|
|
17638
|
+
};
|
|
17639
|
+
var schemaChartOfAccountBase = Joi72.object({
|
|
17640
|
+
...schemaChartOfAccount,
|
|
17641
|
+
org: Joi72.string().hex().required(),
|
|
17642
|
+
isContra: Joi72.boolean().required()
|
|
17643
|
+
});
|
|
17644
|
+
var schemaChartOfAccountStd = Joi72.object({
|
|
17645
|
+
...schemaChartOfAccount,
|
|
17646
|
+
org: Joi72.string().hex().required(),
|
|
17647
|
+
isContra: Joi72.boolean().required(),
|
|
17648
|
+
path: Joi72.string().required(),
|
|
17649
|
+
parentName: Joi72.string().optional().allow("", null)
|
|
17650
|
+
});
|
|
17651
|
+
var schemaChartOfAccountUpdate = Joi72.object({
|
|
17652
|
+
...schemaChartOfAccount,
|
|
17653
|
+
isContra: Joi72.boolean().required()
|
|
17654
|
+
});
|
|
17655
|
+
function modelChartOfAccount(data) {
|
|
17656
|
+
const { error } = schemaChartOfAccountStd.validate(data);
|
|
17657
|
+
if (error) {
|
|
17658
|
+
throw new Error(`Invalid chart of account data: ${error.message}`);
|
|
17659
|
+
}
|
|
17660
|
+
try {
|
|
17661
|
+
data.org = new ObjectId42(data.org);
|
|
17662
|
+
} catch (error2) {
|
|
17663
|
+
throw new Error(`Invalid org ID: ${data.org}`);
|
|
17664
|
+
}
|
|
17665
|
+
if (data.parent) {
|
|
17666
|
+
try {
|
|
17667
|
+
data.parent = new ObjectId42(data.parent);
|
|
17668
|
+
} catch (error2) {
|
|
17669
|
+
throw new Error(`Invalid parent ID: ${data.parent}`);
|
|
17670
|
+
}
|
|
17671
|
+
}
|
|
17672
|
+
if (data.tax) {
|
|
17673
|
+
try {
|
|
17674
|
+
data.tax = new ObjectId42(data.tax);
|
|
17675
|
+
} catch (error2) {
|
|
17676
|
+
throw new Error(`Invalid tax ID: ${data.tax}`);
|
|
17677
|
+
}
|
|
17678
|
+
}
|
|
17679
|
+
return {
|
|
17680
|
+
_id: data._id,
|
|
17681
|
+
org: data.org,
|
|
17682
|
+
type: data.type,
|
|
17683
|
+
normalBalance: data.normalBalance,
|
|
17684
|
+
parent: data.parent,
|
|
17685
|
+
parentName: data.parentName ?? "",
|
|
17686
|
+
path: data.path,
|
|
17687
|
+
name: data.name,
|
|
17688
|
+
code: data.code,
|
|
17689
|
+
tax: data.tax,
|
|
17690
|
+
isContra: data.isContra,
|
|
17691
|
+
status: data.status ?? "active",
|
|
17692
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
17693
|
+
updatedAt: data.updatedAt ?? ""
|
|
17694
|
+
};
|
|
17695
|
+
}
|
|
17696
|
+
|
|
17697
|
+
// src/resources/finance-account/chart-of-account.repository.ts
|
|
17698
|
+
import {
|
|
17699
|
+
AppError as AppError40,
|
|
17700
|
+
BadRequestError as BadRequestError83,
|
|
17701
|
+
useAtlas as useAtlas37,
|
|
17702
|
+
useCache as useCache28,
|
|
17703
|
+
makeCacheKey as makeCacheKey25,
|
|
17704
|
+
paginate as paginate22,
|
|
17705
|
+
logger as logger43,
|
|
17706
|
+
InternalServerError as InternalServerError37
|
|
17707
|
+
} from "@goweekdays/utils";
|
|
17708
|
+
import { ObjectId as ObjectId43 } from "mongodb";
|
|
17709
|
+
import Joi73 from "joi";
|
|
17710
|
+
function useChartOfAccountRepo() {
|
|
17711
|
+
const db = useAtlas37.getDb();
|
|
17712
|
+
if (!db) {
|
|
17713
|
+
throw new BadRequestError83("Unable to connect to server.");
|
|
17714
|
+
}
|
|
17715
|
+
const namespace_collection = "finance.accounts";
|
|
17716
|
+
const collection = db.collection(namespace_collection);
|
|
17717
|
+
const { getCache, setCache, delNamespace } = useCache28(namespace_collection);
|
|
17718
|
+
function delCachedData() {
|
|
17719
|
+
delNamespace().then(() => {
|
|
17720
|
+
logger43.log({
|
|
17721
|
+
level: "info",
|
|
17722
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
17723
|
+
});
|
|
17724
|
+
}).catch((err) => {
|
|
17725
|
+
logger43.log({
|
|
17726
|
+
level: "error",
|
|
17727
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
17728
|
+
});
|
|
17729
|
+
});
|
|
17730
|
+
}
|
|
17731
|
+
async function createIndexes() {
|
|
17732
|
+
try {
|
|
17733
|
+
await collection.createIndexes([
|
|
17734
|
+
{ key: { name: 1 } },
|
|
17735
|
+
{ key: { org: 1 } },
|
|
17736
|
+
{ key: { code: 1 } },
|
|
17737
|
+
{ key: { path: 1 } },
|
|
17738
|
+
{ key: { name: 1, org: 1 }, unique: true, name: "unique_name_per_org" },
|
|
17739
|
+
{ key: { code: 1, org: 1 }, unique: true, name: "unique_code_per_org" }
|
|
17740
|
+
]);
|
|
17741
|
+
} catch (error) {
|
|
17742
|
+
throw new Error("Failed to create index on chart of account.");
|
|
17743
|
+
}
|
|
17744
|
+
}
|
|
17745
|
+
async function add(value, session) {
|
|
17746
|
+
try {
|
|
17747
|
+
value = modelChartOfAccount(value);
|
|
17748
|
+
const res = await collection.insertOne(value, { session });
|
|
17749
|
+
delCachedData();
|
|
17750
|
+
return res.insertedId;
|
|
17751
|
+
} catch (error) {
|
|
17752
|
+
logger43.log({
|
|
17753
|
+
level: "error",
|
|
17754
|
+
message: error.message
|
|
17755
|
+
});
|
|
17756
|
+
throw new BadRequestError83(
|
|
17757
|
+
`Failed to create chart of account: ${error.message}`
|
|
17758
|
+
);
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
17761
|
+
async function getAll(options) {
|
|
17762
|
+
const validation = Joi73.object({
|
|
17763
|
+
org: Joi73.string().hex().required(),
|
|
17764
|
+
page: Joi73.number().min(1).optional().allow("", null),
|
|
17765
|
+
limit: Joi73.number().min(1).optional().allow("", null),
|
|
17766
|
+
search: Joi73.string().optional().allow("", null),
|
|
17767
|
+
status: Joi73.string().optional().allow("", null)
|
|
17768
|
+
});
|
|
17769
|
+
const { error } = validation.validate(options);
|
|
17770
|
+
if (error) {
|
|
17771
|
+
throw new BadRequestError83(error.message);
|
|
17772
|
+
}
|
|
17773
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
17774
|
+
options.status = options.status ?? "active";
|
|
17775
|
+
options.limit = options.limit ?? 10;
|
|
17776
|
+
const query = { status: options.status };
|
|
17777
|
+
try {
|
|
17778
|
+
query.org = new ObjectId43(options.org);
|
|
17779
|
+
} catch (error2) {
|
|
17780
|
+
throw new BadRequestError83("Invalid organization ID.");
|
|
17781
|
+
}
|
|
17782
|
+
if (options.search) {
|
|
17783
|
+
query.$text = { $search: options.search };
|
|
17784
|
+
}
|
|
17785
|
+
const cacheKey = makeCacheKey25(namespace_collection, {
|
|
17786
|
+
search: options.search,
|
|
17787
|
+
page: options.page,
|
|
17788
|
+
limit: options.limit,
|
|
17789
|
+
status: options.status
|
|
17790
|
+
});
|
|
17791
|
+
logger43.log({
|
|
17792
|
+
level: "info",
|
|
17793
|
+
message: `Cache key for getAll chart of accounts: ${cacheKey}`
|
|
17794
|
+
});
|
|
17795
|
+
try {
|
|
17796
|
+
const cached = await getCache(cacheKey);
|
|
17797
|
+
if (cached) {
|
|
17798
|
+
logger43.log({
|
|
17799
|
+
level: "info",
|
|
17800
|
+
message: `Cache hit for getAll chart of accounts: ${cacheKey}`
|
|
17801
|
+
});
|
|
17802
|
+
return cached;
|
|
17803
|
+
}
|
|
17804
|
+
const items = await collection.aggregate([
|
|
17805
|
+
{ $match: query },
|
|
17806
|
+
{ $sort: { path: 1 } },
|
|
17807
|
+
{ $skip: options.page * options.limit },
|
|
17808
|
+
{ $limit: options.limit },
|
|
17809
|
+
{
|
|
17810
|
+
$project: {
|
|
17811
|
+
_id: 1,
|
|
17812
|
+
type: 1,
|
|
17813
|
+
normalBalance: 1,
|
|
17814
|
+
parent: 1,
|
|
17815
|
+
parentName: 1,
|
|
17816
|
+
path: 1,
|
|
17817
|
+
name: 1,
|
|
17818
|
+
code: 1,
|
|
17819
|
+
tax: 1,
|
|
17820
|
+
status: 1
|
|
17821
|
+
}
|
|
17822
|
+
}
|
|
17823
|
+
]).toArray();
|
|
17824
|
+
const length = await collection.countDocuments(query);
|
|
17825
|
+
const data = paginate22(items, options.page, options.limit, length);
|
|
17826
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
17827
|
+
logger43.log({
|
|
17828
|
+
level: "info",
|
|
17829
|
+
message: `Cache set for getAll chart of accounts: ${cacheKey}`
|
|
17830
|
+
});
|
|
17831
|
+
}).catch((err) => {
|
|
17832
|
+
logger43.log({
|
|
17833
|
+
level: "error",
|
|
17834
|
+
message: `Failed to set cache for getAll chart of accounts: ${err.message}`
|
|
17835
|
+
});
|
|
17836
|
+
});
|
|
17837
|
+
return data;
|
|
17838
|
+
} catch (error2) {
|
|
17839
|
+
logger43.log({ level: "error", message: `${error2}` });
|
|
17840
|
+
throw error2;
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17843
|
+
async function getByOrg({ search = "", page = 1, limit = 10, org = "", status: status2 = "active" } = {}) {
|
|
17844
|
+
page = page > 0 ? page - 1 : 0;
|
|
17845
|
+
try {
|
|
17846
|
+
org = new ObjectId43(org);
|
|
17847
|
+
} catch (error) {
|
|
17848
|
+
throw new BadRequestError83("Invalid organization ID.");
|
|
17849
|
+
}
|
|
17850
|
+
const query = { org, status: status2 };
|
|
17851
|
+
const cacheKeyOptions = {
|
|
17852
|
+
org: String(org),
|
|
17853
|
+
status: status2,
|
|
17854
|
+
limit,
|
|
17855
|
+
page
|
|
17856
|
+
};
|
|
17857
|
+
if (search) {
|
|
17858
|
+
cacheKeyOptions.search = search;
|
|
17859
|
+
query.$text = { $search: search };
|
|
17860
|
+
}
|
|
17861
|
+
try {
|
|
17862
|
+
const cacheKey = makeCacheKey25(namespace_collection, cacheKeyOptions);
|
|
17863
|
+
const cached = await getCache(
|
|
17864
|
+
cacheKey
|
|
17865
|
+
);
|
|
17866
|
+
if (cached) {
|
|
17867
|
+
logger43.log({
|
|
17868
|
+
level: "info",
|
|
17869
|
+
message: `Cache hit for getByOrg: ${cacheKey}`
|
|
17870
|
+
});
|
|
17871
|
+
return cached;
|
|
17872
|
+
}
|
|
17873
|
+
const items = await collection.aggregate([
|
|
17874
|
+
{ $match: query },
|
|
17875
|
+
{ $skip: page * limit },
|
|
17876
|
+
{ $limit: limit },
|
|
17877
|
+
{
|
|
17878
|
+
$project: {
|
|
17879
|
+
_id: 1,
|
|
17880
|
+
org: 1,
|
|
17881
|
+
type: 1,
|
|
17882
|
+
normalBalance: 1,
|
|
17883
|
+
parent: 1,
|
|
17884
|
+
name: 1,
|
|
17885
|
+
code: 1,
|
|
17886
|
+
tax: 1,
|
|
17887
|
+
status: 1,
|
|
17888
|
+
createdAt: 1,
|
|
17889
|
+
updatedAt: 1
|
|
17890
|
+
}
|
|
17891
|
+
}
|
|
17892
|
+
]).toArray();
|
|
17893
|
+
const length = await collection.countDocuments(query);
|
|
17894
|
+
const data = paginate22(items, page, limit, length);
|
|
17895
|
+
setCache(cacheKey, data, 300).then(() => {
|
|
17896
|
+
logger43.log({
|
|
17897
|
+
level: "info",
|
|
17898
|
+
message: `Cache set for getByOrg: ${cacheKey}`
|
|
17899
|
+
});
|
|
17900
|
+
}).catch((err) => {
|
|
17901
|
+
logger43.log({
|
|
17902
|
+
level: "error",
|
|
17903
|
+
message: `Failed to set cache for getByOrg: ${err.message}`
|
|
17904
|
+
});
|
|
17905
|
+
});
|
|
17906
|
+
return data;
|
|
17907
|
+
} catch (error) {
|
|
17908
|
+
throw new InternalServerError37(
|
|
17909
|
+
"Internal server error, failed to retrieve chart of accounts."
|
|
17910
|
+
);
|
|
17911
|
+
}
|
|
17912
|
+
}
|
|
17913
|
+
async function getById(_id) {
|
|
17914
|
+
try {
|
|
17915
|
+
_id = new ObjectId43(_id);
|
|
17916
|
+
} catch (error) {
|
|
17917
|
+
throw new BadRequestError83("Invalid ID.");
|
|
17918
|
+
}
|
|
17919
|
+
const cacheKey = makeCacheKey25(namespace_collection, { _id: String(_id) });
|
|
17920
|
+
try {
|
|
17921
|
+
const cached = await getCache(cacheKey);
|
|
17922
|
+
if (cached) {
|
|
17923
|
+
logger43.log({
|
|
17924
|
+
level: "info",
|
|
17925
|
+
message: `Cache hit for getById chart of account: ${cacheKey}`
|
|
17926
|
+
});
|
|
17927
|
+
return cached;
|
|
17928
|
+
}
|
|
17929
|
+
const result = await collection.findOne({ _id });
|
|
17930
|
+
if (!result) {
|
|
17931
|
+
throw new BadRequestError83("Chart of account not found.");
|
|
17932
|
+
}
|
|
17933
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
17934
|
+
logger43.log({
|
|
17935
|
+
level: "info",
|
|
17936
|
+
message: `Cache set for chart of account by id: ${cacheKey}`
|
|
17937
|
+
});
|
|
17938
|
+
}).catch((err) => {
|
|
17939
|
+
logger43.log({
|
|
17940
|
+
level: "error",
|
|
17941
|
+
message: `Failed to set cache for chart of account by id: ${err.message}`
|
|
17942
|
+
});
|
|
17943
|
+
});
|
|
17944
|
+
return result;
|
|
17945
|
+
} catch (error) {
|
|
17946
|
+
if (error instanceof AppError40) {
|
|
17947
|
+
throw error;
|
|
17948
|
+
} else {
|
|
17949
|
+
throw new InternalServerError37("Failed to get chart of account.");
|
|
17950
|
+
}
|
|
17951
|
+
}
|
|
17952
|
+
}
|
|
17953
|
+
async function updateById(_id, options, session) {
|
|
17954
|
+
try {
|
|
17955
|
+
_id = new ObjectId43(_id);
|
|
17956
|
+
} catch (error) {
|
|
17957
|
+
throw new BadRequestError83("Invalid Chart of Account ID.");
|
|
17958
|
+
}
|
|
17959
|
+
try {
|
|
17960
|
+
await collection.updateOne(
|
|
17961
|
+
{ _id },
|
|
17962
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } }
|
|
17963
|
+
);
|
|
17964
|
+
delCachedData();
|
|
17965
|
+
return "Successfully updated chart of account.";
|
|
17966
|
+
} catch (error) {
|
|
17967
|
+
throw new InternalServerError37("Failed to update chart of account.");
|
|
17968
|
+
}
|
|
17969
|
+
}
|
|
17970
|
+
async function deleteById(_id, session) {
|
|
17971
|
+
try {
|
|
17972
|
+
_id = new ObjectId43(_id);
|
|
17973
|
+
} catch (error) {
|
|
17974
|
+
throw new BadRequestError83("Invalid ID.");
|
|
17975
|
+
}
|
|
17976
|
+
try {
|
|
17977
|
+
await collection.updateOne(
|
|
17978
|
+
{ _id },
|
|
17979
|
+
{
|
|
17980
|
+
$set: {
|
|
17981
|
+
status: "archived",
|
|
17982
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
17983
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
17984
|
+
}
|
|
17985
|
+
},
|
|
17986
|
+
{ session }
|
|
17987
|
+
);
|
|
17988
|
+
delCachedData();
|
|
17989
|
+
return "Successfully archived chart of account.";
|
|
17990
|
+
} catch (error) {
|
|
17991
|
+
throw new InternalServerError37("Failed to archive chart of account.");
|
|
17992
|
+
}
|
|
17993
|
+
}
|
|
17994
|
+
async function updateStatusById(_id, status2) {
|
|
17995
|
+
try {
|
|
17996
|
+
_id = new ObjectId43(_id);
|
|
17997
|
+
} catch (error) {
|
|
17998
|
+
throw new BadRequestError83("Invalid ID.");
|
|
17999
|
+
}
|
|
18000
|
+
try {
|
|
18001
|
+
const result = await collection.updateOne(
|
|
18002
|
+
{ _id },
|
|
18003
|
+
{
|
|
18004
|
+
$set: {
|
|
18005
|
+
status: status2,
|
|
18006
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
18007
|
+
}
|
|
18008
|
+
}
|
|
18009
|
+
);
|
|
18010
|
+
if (result.matchedCount === 0) {
|
|
18011
|
+
throw new BadRequestError83("Chart of account not found.");
|
|
18012
|
+
}
|
|
18013
|
+
delCachedData();
|
|
18014
|
+
return "Successfully updated chart of account status.";
|
|
18015
|
+
} catch (error) {
|
|
18016
|
+
if (error instanceof AppError40) {
|
|
18017
|
+
throw error;
|
|
18018
|
+
}
|
|
18019
|
+
throw new InternalServerError37(
|
|
18020
|
+
"Failed to update chart of account status."
|
|
18021
|
+
);
|
|
18022
|
+
}
|
|
18023
|
+
}
|
|
18024
|
+
async function countByPath(path) {
|
|
18025
|
+
const { error } = Joi73.string().required().validate(path);
|
|
18026
|
+
if (error) {
|
|
18027
|
+
throw new BadRequestError83("Path is required and must be a string.");
|
|
18028
|
+
}
|
|
18029
|
+
try {
|
|
18030
|
+
const count = await collection.countDocuments({
|
|
18031
|
+
path: { $regex: `^${path}(-|$)` }
|
|
18032
|
+
});
|
|
18033
|
+
return count;
|
|
18034
|
+
} catch (error2) {
|
|
18035
|
+
throw new InternalServerError37(
|
|
18036
|
+
"Failed to count chart of accounts by path."
|
|
18037
|
+
);
|
|
18038
|
+
}
|
|
18039
|
+
}
|
|
18040
|
+
return {
|
|
18041
|
+
createIndexes,
|
|
18042
|
+
add,
|
|
18043
|
+
getAll,
|
|
18044
|
+
getByOrg,
|
|
18045
|
+
getById,
|
|
18046
|
+
updateById,
|
|
18047
|
+
deleteById,
|
|
18048
|
+
updateStatusById,
|
|
18049
|
+
countByPath
|
|
18050
|
+
};
|
|
18051
|
+
}
|
|
18052
|
+
|
|
18053
|
+
// src/resources/finance-account/chart-of-account.controller.ts
|
|
18054
|
+
import Joi75 from "joi";
|
|
18055
|
+
import { BadRequestError as BadRequestError85, logger as logger44 } from "@goweekdays/utils";
|
|
18056
|
+
|
|
18057
|
+
// src/resources/finance-account/chart-of-account.service.ts
|
|
18058
|
+
import { BadRequestError as BadRequestError84, useAtlas as useAtlas38 } from "@goweekdays/utils";
|
|
18059
|
+
import Joi74 from "joi";
|
|
18060
|
+
function useChartOfAccountService() {
|
|
18061
|
+
const {
|
|
18062
|
+
add: _add,
|
|
18063
|
+
getById: _getById,
|
|
18064
|
+
updateById: _updateById,
|
|
18065
|
+
deleteById: _deleteById,
|
|
18066
|
+
countByPath
|
|
18067
|
+
} = useChartOfAccountRepo();
|
|
18068
|
+
async function add(value) {
|
|
18069
|
+
const { error } = schemaChartOfAccountBase.validate(value);
|
|
18070
|
+
if (error) {
|
|
18071
|
+
throw new BadRequestError84(error.message);
|
|
18072
|
+
}
|
|
18073
|
+
const session = useAtlas38.getClient()?.startSession();
|
|
18074
|
+
if (!session) {
|
|
18075
|
+
throw new Error("Failed to start database session.");
|
|
18076
|
+
}
|
|
18077
|
+
try {
|
|
18078
|
+
session.startTransaction();
|
|
18079
|
+
const data = {
|
|
18080
|
+
org: value.org,
|
|
18081
|
+
name: value.name,
|
|
18082
|
+
code: value.code,
|
|
18083
|
+
type: value.type,
|
|
18084
|
+
normalBalance: value.normalBalance,
|
|
18085
|
+
parent: value.parent,
|
|
18086
|
+
path: value.code,
|
|
18087
|
+
tax: value.tax,
|
|
18088
|
+
isContra: value.isContra
|
|
18089
|
+
};
|
|
18090
|
+
let parentAccount = null;
|
|
18091
|
+
if (value.parent) {
|
|
18092
|
+
parentAccount = await _getById(value.parent);
|
|
18093
|
+
if (!parentAccount) {
|
|
18094
|
+
throw new BadRequestError84("Parent account not found.");
|
|
18095
|
+
}
|
|
18096
|
+
data.path = `${parentAccount.path}-${data.code}`;
|
|
18097
|
+
data.parentName = parentAccount.name;
|
|
18098
|
+
data.type = parentAccount.type;
|
|
18099
|
+
data.normalBalance = parentAccount.normalBalance;
|
|
18100
|
+
}
|
|
18101
|
+
const message = await _add(data, session);
|
|
18102
|
+
await session.commitTransaction();
|
|
18103
|
+
return message;
|
|
18104
|
+
} catch (error2) {
|
|
18105
|
+
await session.abortTransaction();
|
|
18106
|
+
throw error2;
|
|
18107
|
+
} finally {
|
|
18108
|
+
session.endSession();
|
|
18109
|
+
}
|
|
18110
|
+
}
|
|
18111
|
+
async function updateById(id, value) {
|
|
18112
|
+
const { error: errorId } = Joi74.string().hex().required().validate(id);
|
|
18113
|
+
if (errorId) {
|
|
18114
|
+
throw new BadRequestError84("Invalid Chart of Account ID.");
|
|
18115
|
+
}
|
|
18116
|
+
const { error } = schemaChartOfAccountUpdate.validate(value);
|
|
18117
|
+
if (error) {
|
|
18118
|
+
throw new BadRequestError84(
|
|
18119
|
+
`Invalid chart of account update data: ${error.message}`
|
|
18120
|
+
);
|
|
18121
|
+
}
|
|
18122
|
+
const session = useAtlas38.getClient()?.startSession();
|
|
18123
|
+
if (!session) {
|
|
18124
|
+
throw new Error("Failed to start database session.");
|
|
18125
|
+
}
|
|
18126
|
+
try {
|
|
18127
|
+
session.startTransaction();
|
|
18128
|
+
const existingAccount = await _getById(id);
|
|
18129
|
+
if (!existingAccount) {
|
|
18130
|
+
throw new BadRequestError84("Chart of Account not found.");
|
|
18131
|
+
}
|
|
18132
|
+
const updatedData = {
|
|
18133
|
+
org: existingAccount.org,
|
|
18134
|
+
name: value.name,
|
|
18135
|
+
code: value.code,
|
|
18136
|
+
type: value.type,
|
|
18137
|
+
normalBalance: value.normalBalance,
|
|
18138
|
+
parent: value.parent ?? "",
|
|
18139
|
+
parentName: "",
|
|
18140
|
+
tax: value.tax,
|
|
18141
|
+
path: existingAccount.path,
|
|
18142
|
+
isContra: value.isContra
|
|
18143
|
+
};
|
|
18144
|
+
if (value.parent) {
|
|
18145
|
+
const parentAccount = await _getById(value.parent);
|
|
18146
|
+
if (!parentAccount) {
|
|
18147
|
+
throw new BadRequestError84("Parent account not found.");
|
|
18148
|
+
}
|
|
18149
|
+
updatedData.path = `${parentAccount.path}-${value.code}`;
|
|
18150
|
+
updatedData.parentName = parentAccount.name;
|
|
18151
|
+
updatedData.type = parentAccount.type;
|
|
18152
|
+
updatedData.normalBalance = parentAccount.normalBalance;
|
|
18153
|
+
} else {
|
|
18154
|
+
updatedData.path = value.code;
|
|
18155
|
+
updatedData.parentName = "";
|
|
18156
|
+
}
|
|
18157
|
+
const message = await _updateById(id, updatedData, session);
|
|
18158
|
+
await session.commitTransaction();
|
|
18159
|
+
return message;
|
|
18160
|
+
} catch (error2) {
|
|
18161
|
+
await session.abortTransaction();
|
|
18162
|
+
throw error2;
|
|
18163
|
+
} finally {
|
|
18164
|
+
session.endSession();
|
|
18165
|
+
}
|
|
18166
|
+
}
|
|
18167
|
+
async function deleteById(id) {
|
|
18168
|
+
const { error } = Joi74.string().hex().required().validate(id);
|
|
18169
|
+
if (error) {
|
|
18170
|
+
throw new BadRequestError84("Invalid Chart of Account ID.");
|
|
18171
|
+
}
|
|
18172
|
+
const session = useAtlas38.getClient()?.startSession();
|
|
18173
|
+
if (!session) {
|
|
18174
|
+
throw new Error("Failed to start database session.");
|
|
18175
|
+
}
|
|
18176
|
+
try {
|
|
18177
|
+
session.startTransaction();
|
|
18178
|
+
const existingAccount = await _getById(id);
|
|
18179
|
+
if (!existingAccount) {
|
|
18180
|
+
throw new BadRequestError84("Chart of Account not found.");
|
|
18181
|
+
}
|
|
18182
|
+
const childCount = await countByPath(existingAccount.path);
|
|
18183
|
+
if (childCount > 1) {
|
|
18184
|
+
throw new BadRequestError84(
|
|
18185
|
+
"Cannot delete account with child accounts. Please delete child accounts first."
|
|
18186
|
+
);
|
|
18187
|
+
}
|
|
18188
|
+
const message = await _deleteById(id, session);
|
|
18189
|
+
await session.commitTransaction();
|
|
18190
|
+
return message;
|
|
18191
|
+
} catch (error2) {
|
|
18192
|
+
await session.abortTransaction();
|
|
18193
|
+
throw error2;
|
|
18194
|
+
} finally {
|
|
18195
|
+
session.endSession();
|
|
18196
|
+
}
|
|
18197
|
+
}
|
|
18198
|
+
return {
|
|
18199
|
+
add,
|
|
18200
|
+
updateById,
|
|
18201
|
+
deleteById
|
|
18202
|
+
};
|
|
18203
|
+
}
|
|
18204
|
+
|
|
18205
|
+
// src/resources/finance-account/chart-of-account.controller.ts
|
|
18206
|
+
function useChartOfAccountController() {
|
|
18207
|
+
const {
|
|
18208
|
+
getAll: _getAll,
|
|
18209
|
+
getByOrg: _getByOrg,
|
|
18210
|
+
getById: _getById,
|
|
18211
|
+
updateStatusById: _updateStatusById
|
|
18212
|
+
} = useChartOfAccountRepo();
|
|
18213
|
+
const {
|
|
18214
|
+
add: _add,
|
|
18215
|
+
updateById: _updateById,
|
|
18216
|
+
deleteById: _deleteById
|
|
18217
|
+
} = useChartOfAccountService();
|
|
18218
|
+
async function add(req, res, next) {
|
|
18219
|
+
const value = req.body;
|
|
18220
|
+
const { error } = schemaChartOfAccountBase.validate(value);
|
|
18221
|
+
if (error) {
|
|
18222
|
+
next(new BadRequestError85(error.message));
|
|
18223
|
+
logger44.info(`Controller: ${error.message}`);
|
|
18224
|
+
return;
|
|
18225
|
+
}
|
|
18226
|
+
try {
|
|
18227
|
+
const message = await _add(value);
|
|
18228
|
+
res.json({ message });
|
|
18229
|
+
return;
|
|
18230
|
+
} catch (error2) {
|
|
18231
|
+
next(error2);
|
|
18232
|
+
}
|
|
18233
|
+
}
|
|
18234
|
+
async function getAll(req, res, next) {
|
|
18235
|
+
const query = req.query;
|
|
18236
|
+
const validation = Joi75.object({
|
|
18237
|
+
org: Joi75.string().hex().required(),
|
|
18238
|
+
page: Joi75.number().min(1).optional().allow("", null),
|
|
18239
|
+
limit: Joi75.number().min(1).optional().allow("", null),
|
|
18240
|
+
search: Joi75.string().optional().allow("", null),
|
|
18241
|
+
status: Joi75.string().optional().allow("", null)
|
|
18242
|
+
});
|
|
18243
|
+
const org = req.params.org ?? "";
|
|
18244
|
+
const { error } = validation.validate({ ...query, org });
|
|
18245
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
18246
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
18247
|
+
const search = req.query.search ?? "";
|
|
18248
|
+
const status2 = req.query.status ?? "active";
|
|
18249
|
+
const isPageNumber = isFinite(page);
|
|
18250
|
+
if (!isPageNumber) {
|
|
18251
|
+
next(new BadRequestError85("Invalid page number."));
|
|
18252
|
+
return;
|
|
18253
|
+
}
|
|
18254
|
+
const isLimitNumber = isFinite(limit);
|
|
18255
|
+
if (!isLimitNumber) {
|
|
18256
|
+
next(new BadRequestError85("Invalid limit number."));
|
|
18257
|
+
return;
|
|
18258
|
+
}
|
|
18259
|
+
if (error) {
|
|
18260
|
+
next(new BadRequestError85(error.message));
|
|
18261
|
+
return;
|
|
18262
|
+
}
|
|
18263
|
+
try {
|
|
18264
|
+
const accounts = await _getAll({ org, page, limit, search, status: status2 });
|
|
18265
|
+
res.json(accounts);
|
|
18266
|
+
return;
|
|
18267
|
+
} catch (error2) {
|
|
18268
|
+
next(error2);
|
|
18269
|
+
}
|
|
18270
|
+
}
|
|
18271
|
+
async function getByOrg(req, res, next) {
|
|
18272
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
18273
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
18274
|
+
const search = req.query.search ?? "";
|
|
18275
|
+
const status2 = req.query.status ?? "active";
|
|
18276
|
+
const org = req.params.org ?? "";
|
|
18277
|
+
const isPageNumber = isFinite(page);
|
|
18278
|
+
if (!isPageNumber) {
|
|
18279
|
+
next(new BadRequestError85("Invalid page number."));
|
|
18280
|
+
return;
|
|
18281
|
+
}
|
|
18282
|
+
const isLimitNumber = isFinite(limit);
|
|
18283
|
+
if (!isLimitNumber) {
|
|
18284
|
+
next(new BadRequestError85("Invalid limit number."));
|
|
18285
|
+
return;
|
|
18286
|
+
}
|
|
18287
|
+
const validation = Joi75.object({
|
|
18288
|
+
org: Joi75.string().hex().required(),
|
|
18289
|
+
page: Joi75.number().min(1).optional().allow("", null),
|
|
18290
|
+
limit: Joi75.number().min(1).optional().allow("", null),
|
|
18291
|
+
search: Joi75.string().optional().allow("", null),
|
|
18292
|
+
status: Joi75.string().optional()
|
|
18293
|
+
});
|
|
18294
|
+
const { error } = validation.validate({ org, page, limit, search, status: status2 });
|
|
18295
|
+
if (error) {
|
|
18296
|
+
next(new BadRequestError85(error.message));
|
|
18297
|
+
return;
|
|
18298
|
+
}
|
|
18299
|
+
try {
|
|
18300
|
+
const accounts = await _getByOrg({ org, page, limit, search, status: status2 });
|
|
18301
|
+
res.json(accounts);
|
|
18302
|
+
return;
|
|
18303
|
+
} catch (error2) {
|
|
18304
|
+
next(error2);
|
|
18305
|
+
}
|
|
18306
|
+
}
|
|
18307
|
+
async function getById(req, res, next) {
|
|
18308
|
+
const id = req.params.id;
|
|
18309
|
+
const validation = Joi75.object({
|
|
18310
|
+
id: Joi75.string().hex().required()
|
|
18311
|
+
});
|
|
18312
|
+
const { error } = validation.validate({ id });
|
|
18313
|
+
if (error) {
|
|
18314
|
+
next(new BadRequestError85(error.message));
|
|
18315
|
+
return;
|
|
18316
|
+
}
|
|
18317
|
+
try {
|
|
18318
|
+
const account = await _getById(id);
|
|
18319
|
+
res.json(account);
|
|
18320
|
+
return;
|
|
18321
|
+
} catch (error2) {
|
|
18322
|
+
next(error2);
|
|
18323
|
+
}
|
|
18324
|
+
}
|
|
18325
|
+
async function updateById(req, res, next) {
|
|
18326
|
+
const _id = req.params.id;
|
|
18327
|
+
const { error: errorId } = Joi75.string().hex().required().validate(_id);
|
|
18328
|
+
if (errorId) {
|
|
18329
|
+
next(new BadRequestError85("Invalid Chart of Account ID."));
|
|
18330
|
+
return;
|
|
18331
|
+
}
|
|
18332
|
+
const payload = req.body;
|
|
18333
|
+
const { error } = schemaChartOfAccountUpdate.validate(payload);
|
|
18334
|
+
if (error) {
|
|
18335
|
+
next(new BadRequestError85(error.message));
|
|
18336
|
+
return;
|
|
18337
|
+
}
|
|
18338
|
+
try {
|
|
18339
|
+
const message = await _updateById(_id, payload);
|
|
18340
|
+
res.json({ message });
|
|
18341
|
+
return;
|
|
18342
|
+
} catch (error2) {
|
|
18343
|
+
next(error2);
|
|
18344
|
+
}
|
|
18345
|
+
}
|
|
18346
|
+
async function deleteById(req, res, next) {
|
|
18347
|
+
const id = req.params.id;
|
|
18348
|
+
if (!id) {
|
|
18349
|
+
next(new BadRequestError85("Chart of Account ID is required."));
|
|
18350
|
+
return;
|
|
18351
|
+
}
|
|
18352
|
+
try {
|
|
18353
|
+
const message = await _deleteById(id);
|
|
18354
|
+
res.json(message);
|
|
18355
|
+
return;
|
|
18356
|
+
} catch (error) {
|
|
18357
|
+
next(error);
|
|
18358
|
+
}
|
|
18359
|
+
}
|
|
18360
|
+
async function updateStatusById(req, res, next) {
|
|
18361
|
+
const id = req.params.id;
|
|
18362
|
+
const status2 = req.params.status;
|
|
18363
|
+
const validation = Joi75.object({
|
|
18364
|
+
id: Joi75.string().hex().required(),
|
|
18365
|
+
status: Joi75.string().required()
|
|
18366
|
+
});
|
|
18367
|
+
const { error } = validation.validate({ id, status: status2 });
|
|
18368
|
+
if (error) {
|
|
18369
|
+
next(new BadRequestError85(error.message));
|
|
18370
|
+
return;
|
|
18371
|
+
}
|
|
18372
|
+
try {
|
|
18373
|
+
const message = await _updateStatusById(id, status2);
|
|
18374
|
+
res.json({ message });
|
|
18375
|
+
return;
|
|
18376
|
+
} catch (error2) {
|
|
18377
|
+
next(error2);
|
|
18378
|
+
}
|
|
18379
|
+
}
|
|
18380
|
+
return {
|
|
18381
|
+
add,
|
|
18382
|
+
getAll,
|
|
18383
|
+
getByOrg,
|
|
18384
|
+
getById,
|
|
18385
|
+
updateById,
|
|
18386
|
+
deleteById,
|
|
18387
|
+
updateStatusById
|
|
18388
|
+
};
|
|
18389
|
+
}
|
|
18390
|
+
|
|
18391
|
+
// src/resources/finance-journal/finance.journal.entry.model.ts
|
|
18392
|
+
import { BadRequestError as BadRequestError86 } from "@goweekdays/utils";
|
|
18393
|
+
import Joi76 from "joi";
|
|
18394
|
+
import { ObjectId as ObjectId44 } from "mongodb";
|
|
18395
|
+
var journalEntryStatuses = [
|
|
18396
|
+
"draft",
|
|
18397
|
+
"posted",
|
|
18398
|
+
"voided"
|
|
18399
|
+
];
|
|
18400
|
+
var journalEntryTypes = [
|
|
18401
|
+
"general",
|
|
18402
|
+
"sales",
|
|
18403
|
+
"purchases",
|
|
18404
|
+
"cash-receipts",
|
|
18405
|
+
"cash-disbursements"
|
|
18406
|
+
];
|
|
18407
|
+
var schemaJournalEntryBase = {
|
|
18408
|
+
org: Joi76.string().hex().required(),
|
|
18409
|
+
book: Joi76.string().valid(...journalEntryTypes).required(),
|
|
18410
|
+
date: Joi76.date().required(),
|
|
18411
|
+
type: Joi76.string().required(),
|
|
18412
|
+
description: Joi76.string().required(),
|
|
18413
|
+
createdBy: Joi76.string().hex().required(),
|
|
18414
|
+
metadata: Joi76.object({
|
|
18415
|
+
customerId: Joi76.string().hex().optional().allow("", null),
|
|
18416
|
+
supplierId: Joi76.string().hex().optional().allow("", null),
|
|
18417
|
+
itemId: Joi76.string().hex().optional().allow("", null),
|
|
18418
|
+
assetId: Joi76.string().hex().optional().allow("", null),
|
|
18419
|
+
purchaseOrderId: Joi76.string().hex().optional().allow("", null),
|
|
18420
|
+
salesOrderId: Joi76.string().hex().optional().allow("", null)
|
|
18421
|
+
}).optional()
|
|
18422
|
+
};
|
|
18423
|
+
var schemaJournalEntryCtrl = Joi76.object(schemaJournalEntryBase);
|
|
18424
|
+
var schemaJournalEntryRepo = Joi76.object({
|
|
18425
|
+
...schemaJournalEntryBase,
|
|
18426
|
+
id: Joi76.string().required(),
|
|
18427
|
+
createdByName: Joi76.string().required(),
|
|
18428
|
+
status: Joi76.string().valid(...journalEntryStatuses).required()
|
|
18429
|
+
});
|
|
18430
|
+
var schemaJournalEntryGetAll = Joi76.object({
|
|
18431
|
+
org: Joi76.string().hex().required(),
|
|
18432
|
+
page: Joi76.number().optional().allow("", null),
|
|
18433
|
+
limit: Joi76.number().max(100).optional().allow("", null),
|
|
18434
|
+
book: Joi76.string().required().allow("", null),
|
|
18435
|
+
type: Joi76.string().optional().allow("", null),
|
|
18436
|
+
search: Joi76.string().optional().allow("", null),
|
|
18437
|
+
status: Joi76.string().optional().allow("", null)
|
|
18438
|
+
});
|
|
18439
|
+
function modelJournalEntry(data) {
|
|
18440
|
+
const { error } = schemaJournalEntryRepo.validate(data);
|
|
18441
|
+
if (error) {
|
|
18442
|
+
throw new Error(`Invalid journal entry data: ${error.message}`);
|
|
18443
|
+
}
|
|
18444
|
+
try {
|
|
18445
|
+
data.org = new ObjectId44(data.org);
|
|
18446
|
+
} catch (error2) {
|
|
18447
|
+
throw new BadRequestError86(`Invalid org ID: ${data.org}`);
|
|
18448
|
+
}
|
|
18449
|
+
try {
|
|
18450
|
+
data.createdBy = new ObjectId44(data.createdBy);
|
|
18451
|
+
} catch (error2) {
|
|
18452
|
+
throw new BadRequestError86(`Invalid createdBy ID: ${data.createdBy}`);
|
|
18453
|
+
}
|
|
18454
|
+
const date = /* @__PURE__ */ new Date();
|
|
18455
|
+
if (data.metadata) {
|
|
18456
|
+
if (data.metadata.customerId) {
|
|
18457
|
+
try {
|
|
18458
|
+
data.metadata.customerId = new ObjectId44(data.metadata.customerId);
|
|
18459
|
+
} catch (error2) {
|
|
18460
|
+
throw new BadRequestError86(
|
|
18461
|
+
`Invalid customer ID: ${data.metadata.customerId}`
|
|
18462
|
+
);
|
|
18463
|
+
}
|
|
18464
|
+
}
|
|
18465
|
+
if (data.metadata.supplierId) {
|
|
18466
|
+
try {
|
|
18467
|
+
data.metadata.supplierId = new ObjectId44(data.metadata.supplierId);
|
|
18468
|
+
} catch (error2) {
|
|
18469
|
+
throw new BadRequestError86(
|
|
18470
|
+
`Invalid supplier ID: ${data.metadata.supplierId}`
|
|
18471
|
+
);
|
|
18472
|
+
}
|
|
18473
|
+
}
|
|
18474
|
+
if (data.metadata.itemId) {
|
|
18475
|
+
try {
|
|
18476
|
+
data.metadata.itemId = new ObjectId44(data.metadata.itemId);
|
|
18477
|
+
} catch (error2) {
|
|
18478
|
+
throw new BadRequestError86(`Invalid item ID: ${data.metadata.itemId}`);
|
|
18479
|
+
}
|
|
18480
|
+
}
|
|
18481
|
+
if (data.metadata.assetId) {
|
|
18482
|
+
try {
|
|
18483
|
+
data.metadata.assetId = new ObjectId44(data.metadata.assetId);
|
|
18484
|
+
} catch (error2) {
|
|
18485
|
+
throw new BadRequestError86(`Invalid asset ID: ${data.metadata.assetId}`);
|
|
18486
|
+
}
|
|
18487
|
+
}
|
|
18488
|
+
if (data.metadata.purchaseOrderId) {
|
|
18489
|
+
try {
|
|
18490
|
+
data.metadata.purchaseOrderId = new ObjectId44(
|
|
18491
|
+
data.metadata.purchaseOrderId
|
|
18492
|
+
);
|
|
18493
|
+
} catch (error2) {
|
|
18494
|
+
throw new BadRequestError86(
|
|
18495
|
+
`Invalid purchase order ID: ${data.metadata.purchaseOrderId}`
|
|
18496
|
+
);
|
|
18497
|
+
}
|
|
18498
|
+
}
|
|
18499
|
+
if (data.metadata.salesOrderId) {
|
|
18500
|
+
try {
|
|
18501
|
+
data.metadata.salesOrderId = new ObjectId44(data.metadata.salesOrderId);
|
|
18502
|
+
} catch (error2) {
|
|
18503
|
+
throw new BadRequestError86(
|
|
18504
|
+
`Invalid sales order ID: ${data.metadata.salesOrderId}`
|
|
18505
|
+
);
|
|
18506
|
+
}
|
|
18507
|
+
}
|
|
18508
|
+
}
|
|
18509
|
+
return {
|
|
18510
|
+
_id: data._id,
|
|
18511
|
+
id: data.id,
|
|
18512
|
+
org: data.org,
|
|
18513
|
+
type: data.type,
|
|
18514
|
+
date,
|
|
18515
|
+
book: data.book,
|
|
18516
|
+
description: data.description,
|
|
18517
|
+
metadata: data.metadata ?? {
|
|
18518
|
+
customerId: "",
|
|
18519
|
+
supplierId: "",
|
|
18520
|
+
itemId: "",
|
|
18521
|
+
assetId: "",
|
|
18522
|
+
purchaseOrderId: "",
|
|
18523
|
+
salesOrderId: ""
|
|
18524
|
+
},
|
|
18525
|
+
status: data.status,
|
|
18526
|
+
postedAt: data.postedAt ?? "",
|
|
18527
|
+
createdBy: data.createdBy,
|
|
18528
|
+
createdByName: data.createdByName,
|
|
18529
|
+
createdAt: data.createdAt ?? date,
|
|
18530
|
+
updatedAt: data.updatedAt ?? ""
|
|
18531
|
+
};
|
|
18532
|
+
}
|
|
18533
|
+
|
|
18534
|
+
// src/resources/finance-journal/finance.journal.entry.repository.ts
|
|
18535
|
+
import {
|
|
18536
|
+
AppError as AppError41,
|
|
18537
|
+
BadRequestError as BadRequestError87,
|
|
18538
|
+
useAtlas as useAtlas39,
|
|
18539
|
+
useCache as useCache29,
|
|
18540
|
+
makeCacheKey as makeCacheKey26,
|
|
18541
|
+
paginate as paginate23,
|
|
18542
|
+
logger as logger45,
|
|
18543
|
+
InternalServerError as InternalServerError38
|
|
18544
|
+
} from "@goweekdays/utils";
|
|
18545
|
+
import { ObjectId as ObjectId45 } from "mongodb";
|
|
18546
|
+
import Joi77 from "joi";
|
|
18547
|
+
function useJournalEntryRepo() {
|
|
18548
|
+
const db = useAtlas39.getDb();
|
|
18549
|
+
if (!db) {
|
|
18550
|
+
throw new BadRequestError87("Unable to connect to server.");
|
|
18551
|
+
}
|
|
18552
|
+
const namespace_collection = "finance.journal.entries";
|
|
18553
|
+
const collection = db.collection(namespace_collection);
|
|
18554
|
+
const { getCache, setCache, delNamespace } = useCache29(namespace_collection);
|
|
18555
|
+
function delCachedData() {
|
|
18556
|
+
delNamespace().then(() => {
|
|
18557
|
+
logger45.log({
|
|
18558
|
+
level: "info",
|
|
18559
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
18560
|
+
});
|
|
18561
|
+
}).catch((err) => {
|
|
18562
|
+
logger45.log({
|
|
18563
|
+
level: "error",
|
|
18564
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
18565
|
+
});
|
|
18566
|
+
});
|
|
18567
|
+
}
|
|
18568
|
+
async function add(value, session) {
|
|
18569
|
+
try {
|
|
18570
|
+
value = modelJournalEntry(value);
|
|
18571
|
+
const res = await collection.insertOne(value, { session });
|
|
18572
|
+
delCachedData();
|
|
18573
|
+
return res.insertedId;
|
|
18574
|
+
} catch (error) {
|
|
18575
|
+
logger45.log({
|
|
18576
|
+
level: "error",
|
|
18577
|
+
message: error.message
|
|
18578
|
+
});
|
|
18579
|
+
throw new BadRequestError87(
|
|
18580
|
+
`Failed to create journal entry: ${error.message}`
|
|
18581
|
+
);
|
|
18582
|
+
}
|
|
18583
|
+
}
|
|
18584
|
+
async function getAll(options) {
|
|
18585
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
18586
|
+
options.status = options.status ?? "posted";
|
|
18587
|
+
options.limit = options.limit ?? 10;
|
|
18588
|
+
options.book = options.book ?? "general";
|
|
18589
|
+
const { error } = schemaJournalEntryGetAll.validate(options);
|
|
18590
|
+
if (error) {
|
|
18591
|
+
throw new BadRequestError87(`Invalid query parameters: ${error.message}`);
|
|
18592
|
+
}
|
|
18593
|
+
const query = {
|
|
18594
|
+
status: options.status,
|
|
18595
|
+
book: options.book
|
|
18596
|
+
};
|
|
18597
|
+
const cacheKeyOptions = {
|
|
18598
|
+
page: options.page,
|
|
18599
|
+
limit: options.limit,
|
|
18600
|
+
book: options.book,
|
|
18601
|
+
status: options.status
|
|
18602
|
+
};
|
|
18603
|
+
try {
|
|
18604
|
+
query.org = new ObjectId45(options.org);
|
|
18605
|
+
cacheKeyOptions.org = String(options.org);
|
|
18606
|
+
} catch (error2) {
|
|
18607
|
+
throw new BadRequestError87("Invalid organization ID.");
|
|
18608
|
+
}
|
|
18609
|
+
if (options.search) {
|
|
18610
|
+
query.$text = { $search: options.search };
|
|
18611
|
+
cacheKeyOptions.search = options.search;
|
|
18612
|
+
}
|
|
18613
|
+
if (options.type) {
|
|
18614
|
+
query.type = options.type;
|
|
18615
|
+
cacheKeyOptions.type = options.type;
|
|
18616
|
+
}
|
|
18617
|
+
const cacheKey = makeCacheKey26(namespace_collection, cacheKeyOptions);
|
|
18618
|
+
logger45.log({
|
|
18619
|
+
level: "info",
|
|
18620
|
+
message: `Cache key for getAll journal entries: ${cacheKey}`
|
|
18621
|
+
});
|
|
18622
|
+
try {
|
|
18623
|
+
const cached = await getCache(cacheKey);
|
|
18624
|
+
if (cached) {
|
|
18625
|
+
logger45.log({
|
|
18626
|
+
level: "info",
|
|
18627
|
+
message: `Cache hit for getAll journal entries: ${cacheKey}`
|
|
18628
|
+
});
|
|
18629
|
+
return cached;
|
|
18630
|
+
}
|
|
18631
|
+
const items = await collection.aggregate([
|
|
18632
|
+
{ $match: query },
|
|
18633
|
+
{ $skip: options.page * options.limit },
|
|
18634
|
+
{ $limit: options.limit },
|
|
18635
|
+
{
|
|
18636
|
+
$lookup: {
|
|
18637
|
+
from: "finance.journal.lines",
|
|
18638
|
+
let: { journalId: "$_id" },
|
|
18639
|
+
pipeline: [
|
|
18640
|
+
{
|
|
18641
|
+
$match: {
|
|
18642
|
+
$expr: { $eq: ["$journalEntry", "$$journalId"] }
|
|
18643
|
+
}
|
|
18644
|
+
},
|
|
18645
|
+
{
|
|
18646
|
+
$sort: { debit: -1 }
|
|
18647
|
+
}
|
|
18648
|
+
],
|
|
18649
|
+
as: "lines"
|
|
18650
|
+
}
|
|
18651
|
+
}
|
|
18652
|
+
]).toArray();
|
|
18653
|
+
const length = await collection.countDocuments(query);
|
|
18654
|
+
const data = paginate23(items, options.page, options.limit, length);
|
|
18655
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
18656
|
+
logger45.log({
|
|
18657
|
+
level: "info",
|
|
18658
|
+
message: `Cache set for getAll journal entries: ${cacheKey}`
|
|
18659
|
+
});
|
|
18660
|
+
}).catch((err) => {
|
|
18661
|
+
logger45.log({
|
|
18662
|
+
level: "error",
|
|
18663
|
+
message: `Failed to set cache for getAll journal entries: ${err.message}`
|
|
18664
|
+
});
|
|
18665
|
+
});
|
|
18666
|
+
return data;
|
|
18667
|
+
} catch (error2) {
|
|
18668
|
+
logger45.log({ level: "error", message: `${error2}` });
|
|
18669
|
+
throw error2;
|
|
18670
|
+
}
|
|
18671
|
+
}
|
|
18672
|
+
async function getById(_id) {
|
|
18673
|
+
try {
|
|
18674
|
+
_id = new ObjectId45(_id);
|
|
18675
|
+
} catch (error) {
|
|
18676
|
+
throw new BadRequestError87("Invalid ID.");
|
|
18677
|
+
}
|
|
18678
|
+
const cacheKey = makeCacheKey26(namespace_collection, { _id: String(_id) });
|
|
18679
|
+
try {
|
|
18680
|
+
const cached = await getCache(cacheKey);
|
|
18681
|
+
if (cached) {
|
|
18682
|
+
logger45.log({
|
|
18683
|
+
level: "info",
|
|
18684
|
+
message: `Cache hit for getById journal entry: ${cacheKey}`
|
|
18685
|
+
});
|
|
18686
|
+
return cached;
|
|
18687
|
+
}
|
|
18688
|
+
const result = await collection.findOne({ _id });
|
|
18689
|
+
if (!result) {
|
|
18690
|
+
throw new BadRequestError87("Journal entry not found.");
|
|
18691
|
+
}
|
|
18692
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
18693
|
+
logger45.log({
|
|
18694
|
+
level: "info",
|
|
18695
|
+
message: `Cache set for journal entry by id: ${cacheKey}`
|
|
18696
|
+
});
|
|
18697
|
+
}).catch((err) => {
|
|
18698
|
+
logger45.log({
|
|
18699
|
+
level: "error",
|
|
18700
|
+
message: `Failed to set cache for journal entry by id: ${err.message}`
|
|
18701
|
+
});
|
|
18702
|
+
});
|
|
18703
|
+
return result;
|
|
18704
|
+
} catch (error) {
|
|
18705
|
+
if (error instanceof AppError41) {
|
|
18706
|
+
throw error;
|
|
18707
|
+
} else {
|
|
18708
|
+
throw new InternalServerError38("Failed to get journal entry.");
|
|
18709
|
+
}
|
|
18710
|
+
}
|
|
18711
|
+
}
|
|
18712
|
+
async function updateById(_id, options) {
|
|
18713
|
+
const { error: errorId } = Joi77.string().hex().required().validate(String(_id));
|
|
18714
|
+
if (errorId) {
|
|
18715
|
+
throw new BadRequestError87("Invalid Journal Entry ID.");
|
|
18716
|
+
}
|
|
18717
|
+
const { error } = schemaJournalEntryRepo.validate(options);
|
|
18718
|
+
if (error) {
|
|
18719
|
+
throw new BadRequestError87(
|
|
18720
|
+
`Invalid journal entry update data: ${error.message}`
|
|
18721
|
+
);
|
|
18722
|
+
}
|
|
18723
|
+
try {
|
|
18724
|
+
_id = new ObjectId45(_id);
|
|
18725
|
+
} catch (error2) {
|
|
18726
|
+
throw new BadRequestError87("Invalid Journal Entry ID.");
|
|
18727
|
+
}
|
|
18728
|
+
try {
|
|
18729
|
+
await collection.updateOne(
|
|
18730
|
+
{ _id },
|
|
18731
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } }
|
|
18732
|
+
);
|
|
18733
|
+
delCachedData();
|
|
18734
|
+
return "Successfully updated journal entry.";
|
|
18735
|
+
} catch (error2) {
|
|
18736
|
+
throw new InternalServerError38("Failed to update journal entry.");
|
|
18737
|
+
}
|
|
18738
|
+
}
|
|
18739
|
+
async function deleteById(_id, session) {
|
|
18740
|
+
try {
|
|
18741
|
+
_id = new ObjectId45(_id);
|
|
18742
|
+
} catch (error) {
|
|
18743
|
+
throw new BadRequestError87("Invalid ID.");
|
|
18744
|
+
}
|
|
18745
|
+
try {
|
|
18746
|
+
await collection.updateOne(
|
|
18747
|
+
{ _id },
|
|
18748
|
+
{
|
|
18749
|
+
$set: {
|
|
18750
|
+
status: "voided",
|
|
18751
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
18752
|
+
}
|
|
18753
|
+
},
|
|
18754
|
+
{ session }
|
|
18755
|
+
);
|
|
18756
|
+
delCachedData();
|
|
18757
|
+
return "Successfully voided journal entry.";
|
|
18758
|
+
} catch (error) {
|
|
18759
|
+
throw new InternalServerError38("Failed to void journal entry.");
|
|
18760
|
+
}
|
|
18761
|
+
}
|
|
18762
|
+
async function updateStatusById(_id, status2) {
|
|
18763
|
+
try {
|
|
18764
|
+
_id = new ObjectId45(_id);
|
|
18765
|
+
} catch (error) {
|
|
18766
|
+
throw new BadRequestError87("Invalid ID.");
|
|
18767
|
+
}
|
|
18768
|
+
try {
|
|
18769
|
+
const result = await collection.updateOne(
|
|
18770
|
+
{ _id },
|
|
18771
|
+
{
|
|
18772
|
+
$set: {
|
|
18773
|
+
status: status2,
|
|
18774
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
18775
|
+
}
|
|
18776
|
+
}
|
|
18777
|
+
);
|
|
18778
|
+
if (result.matchedCount === 0) {
|
|
18779
|
+
throw new BadRequestError87("Journal entry not found.");
|
|
18780
|
+
}
|
|
18781
|
+
delCachedData();
|
|
18782
|
+
return "Successfully updated journal entry status.";
|
|
18783
|
+
} catch (error) {
|
|
18784
|
+
if (error instanceof AppError41) {
|
|
18785
|
+
throw error;
|
|
18786
|
+
}
|
|
18787
|
+
throw new InternalServerError38("Failed to update journal entry status.");
|
|
18788
|
+
}
|
|
18789
|
+
}
|
|
18790
|
+
return {
|
|
18791
|
+
add,
|
|
18792
|
+
getAll,
|
|
18793
|
+
getById,
|
|
18794
|
+
updateById,
|
|
18795
|
+
deleteById,
|
|
18796
|
+
updateStatusById
|
|
18797
|
+
};
|
|
18798
|
+
}
|
|
18799
|
+
|
|
18800
|
+
// src/resources/finance-journal/finance.journal.entry.service.ts
|
|
18801
|
+
import Joi80 from "joi";
|
|
18802
|
+
|
|
18803
|
+
// src/resources/finance-journal/finance.journal.line.model.ts
|
|
18804
|
+
import { BadRequestError as BadRequestError88 } from "@goweekdays/utils";
|
|
18805
|
+
import Joi78 from "joi";
|
|
18806
|
+
import { ObjectId as ObjectId46 } from "mongodb";
|
|
18807
|
+
var schemaJournalLineBase = {
|
|
18808
|
+
org: Joi78.string().hex().required(),
|
|
18809
|
+
account: Joi78.string().hex().required(),
|
|
18810
|
+
debit: Joi78.number().min(0).required(),
|
|
18811
|
+
credit: Joi78.number().min(0).required()
|
|
18812
|
+
};
|
|
18813
|
+
var schemaJournalLineCtrl = Joi78.object(schemaJournalLineBase);
|
|
18814
|
+
var schemaJournalLineRepo = Joi78.object({
|
|
18815
|
+
...schemaJournalLineBase,
|
|
18816
|
+
journalEntry: Joi78.string().hex().required(),
|
|
18817
|
+
accountName: Joi78.string().required()
|
|
18818
|
+
});
|
|
18819
|
+
function modelJournalLine(data) {
|
|
18820
|
+
const { error } = schemaJournalLineRepo.validate(data);
|
|
18821
|
+
if (error) {
|
|
18822
|
+
throw new BadRequestError88(`Invalid journal line data: ${error.message}`);
|
|
18823
|
+
}
|
|
18824
|
+
try {
|
|
18825
|
+
data.org = new ObjectId46(data.org);
|
|
18826
|
+
} catch (error2) {
|
|
18827
|
+
throw new BadRequestError88(`Invalid org ID: ${data.org}`);
|
|
18828
|
+
}
|
|
18829
|
+
try {
|
|
18830
|
+
data.journalEntry = new ObjectId46(data.journalEntry);
|
|
18831
|
+
} catch (error2) {
|
|
18832
|
+
throw new BadRequestError88(`Invalid journal entry ID: ${data.journalEntry}`);
|
|
18833
|
+
}
|
|
18834
|
+
try {
|
|
18835
|
+
data.account = new ObjectId46(data.account);
|
|
18836
|
+
} catch (error2) {
|
|
18837
|
+
throw new BadRequestError88(`Invalid account ID: ${data.account}`);
|
|
18838
|
+
}
|
|
18839
|
+
return {
|
|
18840
|
+
_id: data._id,
|
|
18841
|
+
org: data.org,
|
|
18842
|
+
journalEntry: data.journalEntry,
|
|
18843
|
+
account: data.account,
|
|
18844
|
+
accountName: data.accountName,
|
|
18845
|
+
debit: data.debit,
|
|
18846
|
+
credit: data.credit,
|
|
18847
|
+
postReference: data.postReference ?? "",
|
|
18848
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
18849
|
+
updatedAt: data.updatedAt ?? ""
|
|
18850
|
+
};
|
|
18851
|
+
}
|
|
18852
|
+
|
|
18853
|
+
// src/resources/finance-journal/finance.journal.line.repository.ts
|
|
18854
|
+
import {
|
|
18855
|
+
AppError as AppError42,
|
|
18856
|
+
BadRequestError as BadRequestError89,
|
|
18857
|
+
useAtlas as useAtlas40,
|
|
18858
|
+
useCache as useCache30,
|
|
18859
|
+
makeCacheKey as makeCacheKey27,
|
|
18860
|
+
paginate as paginate24,
|
|
18861
|
+
logger as logger46,
|
|
18862
|
+
InternalServerError as InternalServerError39
|
|
18863
|
+
} from "@goweekdays/utils";
|
|
18864
|
+
import { ObjectId as ObjectId47 } from "mongodb";
|
|
18865
|
+
import Joi79 from "joi";
|
|
18866
|
+
function useJournalLineRepo() {
|
|
18867
|
+
const db = useAtlas40.getDb();
|
|
18868
|
+
if (!db) {
|
|
18869
|
+
throw new BadRequestError89("Unable to connect to server.");
|
|
18870
|
+
}
|
|
18871
|
+
const namespace_collection = "finance.journal.lines";
|
|
18872
|
+
const collection = db.collection(namespace_collection);
|
|
18873
|
+
const { getCache, setCache, delNamespace } = useCache30(namespace_collection);
|
|
18874
|
+
function delCachedData() {
|
|
18875
|
+
delNamespace().then(() => {
|
|
18876
|
+
logger46.log({
|
|
18877
|
+
level: "info",
|
|
18878
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
18879
|
+
});
|
|
18880
|
+
}).catch((err) => {
|
|
18881
|
+
logger46.log({
|
|
18882
|
+
level: "error",
|
|
18883
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
18884
|
+
});
|
|
18885
|
+
});
|
|
18886
|
+
}
|
|
18887
|
+
async function add(value, session) {
|
|
18888
|
+
try {
|
|
18889
|
+
value = modelJournalLine(value);
|
|
18890
|
+
const res = await collection.insertOne(value, { session });
|
|
18891
|
+
delCachedData();
|
|
18892
|
+
return res.insertedId;
|
|
18893
|
+
} catch (error) {
|
|
18894
|
+
logger46.log({
|
|
18895
|
+
level: "error",
|
|
18896
|
+
message: error.message
|
|
18897
|
+
});
|
|
18898
|
+
throw new BadRequestError89(
|
|
18899
|
+
`Failed to create journal line: ${error.message}`
|
|
18900
|
+
);
|
|
18901
|
+
}
|
|
18902
|
+
}
|
|
18903
|
+
async function getAll(options) {
|
|
18904
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
18905
|
+
options.limit = options.limit ?? 10;
|
|
18906
|
+
const query = {};
|
|
18907
|
+
try {
|
|
18908
|
+
query.org = new ObjectId47(options.org);
|
|
18909
|
+
} catch (error) {
|
|
18910
|
+
throw new BadRequestError89("Invalid organization ID.");
|
|
18911
|
+
}
|
|
18912
|
+
if (options.journalEntry) {
|
|
18913
|
+
try {
|
|
18914
|
+
query.journalEntry = new ObjectId47(options.journalEntry);
|
|
18915
|
+
} catch (error) {
|
|
18916
|
+
throw new BadRequestError89("Invalid journal entry ID.");
|
|
18917
|
+
}
|
|
18918
|
+
}
|
|
18919
|
+
const cacheKey = makeCacheKey27(namespace_collection, {
|
|
18920
|
+
org: String(options.org),
|
|
18921
|
+
journalEntry: String(options.journalEntry ?? ""),
|
|
18922
|
+
page: options.page,
|
|
18923
|
+
limit: options.limit
|
|
18924
|
+
});
|
|
18925
|
+
logger46.log({
|
|
18926
|
+
level: "info",
|
|
18927
|
+
message: `Cache key for getAll journal lines: ${cacheKey}`
|
|
18928
|
+
});
|
|
18929
|
+
try {
|
|
18930
|
+
const cached = await getCache(cacheKey);
|
|
18931
|
+
if (cached) {
|
|
18932
|
+
logger46.log({
|
|
18933
|
+
level: "info",
|
|
18934
|
+
message: `Cache hit for getAll journal lines: ${cacheKey}`
|
|
18935
|
+
});
|
|
18936
|
+
return cached;
|
|
18937
|
+
}
|
|
18938
|
+
const items = await collection.aggregate([
|
|
18939
|
+
{ $match: query },
|
|
18940
|
+
{ $skip: options.page * options.limit },
|
|
18941
|
+
{ $limit: options.limit },
|
|
18942
|
+
{
|
|
18943
|
+
$project: {
|
|
18944
|
+
_id: 1,
|
|
18945
|
+
org: 1,
|
|
18946
|
+
journalEntry: 1,
|
|
18947
|
+
account: 1,
|
|
18948
|
+
debit: 1,
|
|
18949
|
+
credit: 1,
|
|
18950
|
+
description: 1,
|
|
18951
|
+
createdAt: 1
|
|
18952
|
+
}
|
|
18953
|
+
}
|
|
18954
|
+
]).toArray();
|
|
18955
|
+
const length = await collection.countDocuments(query);
|
|
18956
|
+
const data = paginate24(items, options.page, options.limit, length);
|
|
18957
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
18958
|
+
logger46.log({
|
|
18959
|
+
level: "info",
|
|
18960
|
+
message: `Cache set for getAll journal lines: ${cacheKey}`
|
|
18961
|
+
});
|
|
18962
|
+
}).catch((err) => {
|
|
18963
|
+
logger46.log({
|
|
18964
|
+
level: "error",
|
|
18965
|
+
message: `Failed to set cache for getAll journal lines: ${err.message}`
|
|
18966
|
+
});
|
|
18967
|
+
});
|
|
18968
|
+
return data;
|
|
18969
|
+
} catch (error) {
|
|
18970
|
+
logger46.log({ level: "error", message: `${error}` });
|
|
18971
|
+
throw error;
|
|
18972
|
+
}
|
|
18973
|
+
}
|
|
18974
|
+
async function getById(_id) {
|
|
18975
|
+
try {
|
|
18976
|
+
_id = new ObjectId47(_id);
|
|
18977
|
+
} catch (error) {
|
|
18978
|
+
throw new BadRequestError89("Invalid ID.");
|
|
18979
|
+
}
|
|
18980
|
+
const cacheKey = makeCacheKey27(namespace_collection, { _id: String(_id) });
|
|
18981
|
+
try {
|
|
18982
|
+
const cached = await getCache(cacheKey);
|
|
18983
|
+
if (cached) {
|
|
18984
|
+
logger46.log({
|
|
18985
|
+
level: "info",
|
|
18986
|
+
message: `Cache hit for getById journal line: ${cacheKey}`
|
|
18987
|
+
});
|
|
18988
|
+
return cached;
|
|
18989
|
+
}
|
|
18990
|
+
const result = await collection.findOne({ _id });
|
|
18991
|
+
if (!result) {
|
|
18992
|
+
throw new BadRequestError89("Journal line not found.");
|
|
18993
|
+
}
|
|
18994
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
18995
|
+
logger46.log({
|
|
18996
|
+
level: "info",
|
|
18997
|
+
message: `Cache set for journal line by id: ${cacheKey}`
|
|
18998
|
+
});
|
|
18999
|
+
}).catch((err) => {
|
|
19000
|
+
logger46.log({
|
|
19001
|
+
level: "error",
|
|
19002
|
+
message: `Failed to set cache for journal line by id: ${err.message}`
|
|
19003
|
+
});
|
|
19004
|
+
});
|
|
19005
|
+
return result;
|
|
19006
|
+
} catch (error) {
|
|
19007
|
+
if (error instanceof AppError42) {
|
|
19008
|
+
throw error;
|
|
19009
|
+
} else {
|
|
19010
|
+
throw new InternalServerError39("Failed to get journal line.");
|
|
19011
|
+
}
|
|
19012
|
+
}
|
|
19013
|
+
}
|
|
19014
|
+
async function updateById(_id, options) {
|
|
19015
|
+
const { error: errorId } = Joi79.string().hex().required().validate(String(_id));
|
|
19016
|
+
if (errorId) {
|
|
19017
|
+
throw new BadRequestError89("Invalid Journal Line ID.");
|
|
19018
|
+
}
|
|
19019
|
+
const { error } = schemaJournalLineRepo.validate(options);
|
|
19020
|
+
if (error) {
|
|
19021
|
+
throw new BadRequestError89(
|
|
19022
|
+
`Invalid journal line update data: ${error.message}`
|
|
19023
|
+
);
|
|
19024
|
+
}
|
|
19025
|
+
try {
|
|
19026
|
+
_id = new ObjectId47(_id);
|
|
19027
|
+
} catch (error2) {
|
|
19028
|
+
throw new BadRequestError89("Invalid Journal Line ID.");
|
|
19029
|
+
}
|
|
19030
|
+
try {
|
|
19031
|
+
await collection.updateOne(
|
|
19032
|
+
{ _id },
|
|
19033
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } }
|
|
19034
|
+
);
|
|
19035
|
+
delCachedData();
|
|
19036
|
+
return "Successfully updated journal line.";
|
|
19037
|
+
} catch (error2) {
|
|
19038
|
+
throw new InternalServerError39("Failed to update journal line.");
|
|
19039
|
+
}
|
|
19040
|
+
}
|
|
19041
|
+
async function deleteById(_id, session) {
|
|
19042
|
+
try {
|
|
19043
|
+
_id = new ObjectId47(_id);
|
|
19044
|
+
} catch (error) {
|
|
19045
|
+
throw new BadRequestError89("Invalid ID.");
|
|
19046
|
+
}
|
|
19047
|
+
try {
|
|
19048
|
+
await collection.deleteOne({ _id }, { session });
|
|
19049
|
+
delCachedData();
|
|
19050
|
+
return "Successfully deleted journal line.";
|
|
19051
|
+
} catch (error) {
|
|
19052
|
+
throw new InternalServerError39("Failed to delete journal line.");
|
|
19053
|
+
}
|
|
19054
|
+
}
|
|
19055
|
+
return {
|
|
19056
|
+
add,
|
|
19057
|
+
getAll,
|
|
19058
|
+
getById,
|
|
19059
|
+
updateById,
|
|
19060
|
+
deleteById
|
|
19061
|
+
};
|
|
19062
|
+
}
|
|
19063
|
+
|
|
19064
|
+
// src/resources/finance-journal/finance.journal.entry.service.ts
|
|
19065
|
+
import {
|
|
19066
|
+
AppError as AppError43,
|
|
19067
|
+
BadRequestError as BadRequestError90,
|
|
19068
|
+
InternalServerError as InternalServerError40,
|
|
19069
|
+
NotFoundError as NotFoundError6,
|
|
19070
|
+
useAtlas as useAtlas41
|
|
19071
|
+
} from "@goweekdays/utils";
|
|
19072
|
+
function useJournalEntryService() {
|
|
19073
|
+
const { add: _add } = useJournalEntryRepo();
|
|
19074
|
+
const { add: addLine } = useJournalLineRepo();
|
|
19075
|
+
const { getUserById } = useUserRepo();
|
|
19076
|
+
const { getById: getAccountById } = useChartOfAccountRepo();
|
|
19077
|
+
const {
|
|
19078
|
+
getByType: getCounterByType,
|
|
19079
|
+
add: addCounter,
|
|
19080
|
+
incrementByType
|
|
19081
|
+
} = useCounterRepo();
|
|
19082
|
+
async function add(entry, lines) {
|
|
19083
|
+
const { error } = Joi80.object({
|
|
19084
|
+
journalEntry: schemaJournalEntryCtrl.required(),
|
|
19085
|
+
journalLines: Joi80.array().items(schemaJournalLineCtrl).min(1).required()
|
|
19086
|
+
}).validate({ journalEntry: entry, journalLines: lines });
|
|
19087
|
+
if (error) {
|
|
19088
|
+
throw new BadRequestError90(`Invalid journal entry data: ${error.message}`);
|
|
19089
|
+
}
|
|
19090
|
+
const session = useAtlas41.getClient()?.startSession();
|
|
19091
|
+
if (!session) {
|
|
19092
|
+
throw new BadRequestError90("Unable to start database session.");
|
|
19093
|
+
}
|
|
19094
|
+
try {
|
|
19095
|
+
session.startTransaction();
|
|
19096
|
+
const counterName = `${entry.org.toString()}.journal.entry.${entry.book}`;
|
|
19097
|
+
const counter = await getCounterByType(counterName);
|
|
19098
|
+
if (!counter) {
|
|
19099
|
+
await addCounter(counterName, session);
|
|
19100
|
+
}
|
|
19101
|
+
const entryNumber = counter ? counter.count + 1 : 1;
|
|
19102
|
+
entry.id = entryNumber.toString().padStart(6, "0");
|
|
19103
|
+
await incrementByType(counterName, session);
|
|
19104
|
+
const totalDebit = lines.reduce((sum, line) => sum + line.debit, 0);
|
|
19105
|
+
const totalCredit = lines.reduce((sum, line) => sum + line.credit, 0);
|
|
19106
|
+
if (totalDebit !== totalCredit) {
|
|
19107
|
+
throw new BadRequestError90(
|
|
19108
|
+
`Total debit (${totalDebit}) must equal total credit (${totalCredit}).`
|
|
19109
|
+
);
|
|
19110
|
+
}
|
|
19111
|
+
const user = await getUserById(entry.createdBy);
|
|
19112
|
+
if (!user) {
|
|
19113
|
+
throw new NotFoundError6("User not found.");
|
|
19114
|
+
}
|
|
19115
|
+
entry.createdByName = `${user.firstName} ${user.lastName}`;
|
|
19116
|
+
entry.status = "draft";
|
|
19117
|
+
const entryId = await _add(entry, session);
|
|
19118
|
+
for (const line of lines) {
|
|
19119
|
+
line.journalEntry = entryId.toString();
|
|
19120
|
+
const account = await getAccountById(String(line.account));
|
|
19121
|
+
if (!account) {
|
|
19122
|
+
throw new NotFoundError6(`Account not found: ${line.account}`);
|
|
19123
|
+
}
|
|
19124
|
+
line.accountName = account.name;
|
|
19125
|
+
await addLine(line, session);
|
|
19126
|
+
}
|
|
19127
|
+
await session.commitTransaction();
|
|
19128
|
+
return "Journal entry created successfully.";
|
|
19129
|
+
} catch (error2) {
|
|
19130
|
+
await session.abortTransaction();
|
|
19131
|
+
if (error2 instanceof AppError43) {
|
|
19132
|
+
throw error2;
|
|
19133
|
+
}
|
|
19134
|
+
throw new InternalServerError40(
|
|
19135
|
+
`Failed to create journal entry: ${error2.message}`
|
|
19136
|
+
);
|
|
19137
|
+
} finally {
|
|
19138
|
+
session.endSession();
|
|
19139
|
+
}
|
|
19140
|
+
}
|
|
19141
|
+
return {
|
|
19142
|
+
add
|
|
19143
|
+
};
|
|
19144
|
+
}
|
|
19145
|
+
|
|
19146
|
+
// src/resources/finance-journal/finance.journal.entry.controller.ts
|
|
19147
|
+
import Joi81 from "joi";
|
|
19148
|
+
import { BadRequestError as BadRequestError91, logger as logger47 } from "@goweekdays/utils";
|
|
19149
|
+
function useJournalEntryController() {
|
|
19150
|
+
const {
|
|
19151
|
+
getAll: _getAll,
|
|
19152
|
+
getById: _getById,
|
|
19153
|
+
updateById: _updateById,
|
|
19154
|
+
deleteById: _deleteById,
|
|
19155
|
+
updateStatusById: _updateStatusById
|
|
19156
|
+
} = useJournalEntryRepo();
|
|
19157
|
+
const { add: _add } = useJournalEntryService();
|
|
19158
|
+
async function add(req, res, next) {
|
|
19159
|
+
const value = req.body;
|
|
19160
|
+
const { error } = Joi81.object({
|
|
19161
|
+
journalEntry: schemaJournalEntryCtrl.required(),
|
|
19162
|
+
journalLines: Joi81.array().items(schemaJournalLineCtrl).min(1).required()
|
|
19163
|
+
}).validate(value);
|
|
19164
|
+
if (error) {
|
|
19165
|
+
next(new BadRequestError91(error.message));
|
|
19166
|
+
logger47.info(`Controller: ${error.message}`);
|
|
19167
|
+
return;
|
|
19168
|
+
}
|
|
19169
|
+
try {
|
|
19170
|
+
const message = await _add(value.journalEntry, value.journalLines);
|
|
19171
|
+
res.json({ message });
|
|
19172
|
+
return;
|
|
19173
|
+
} catch (error2) {
|
|
19174
|
+
next(error2);
|
|
19175
|
+
}
|
|
19176
|
+
}
|
|
19177
|
+
async function getAll(req, res, next) {
|
|
19178
|
+
const query = req.query;
|
|
19179
|
+
const org = req.params.org ?? "";
|
|
19180
|
+
const { error } = schemaJournalEntryGetAll.validate({ ...query, org });
|
|
19181
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
19182
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
19183
|
+
const book = req.query.book ?? "general";
|
|
19184
|
+
const type = req.query.type ?? "";
|
|
19185
|
+
const search = req.query.search ?? "";
|
|
19186
|
+
const status2 = req.query.status ?? "posted";
|
|
19187
|
+
if (!isFinite(page)) {
|
|
19188
|
+
next(new BadRequestError91("Invalid page number."));
|
|
19189
|
+
return;
|
|
19190
|
+
}
|
|
19191
|
+
if (!isFinite(limit)) {
|
|
19192
|
+
next(new BadRequestError91("Invalid limit number."));
|
|
19193
|
+
return;
|
|
19194
|
+
}
|
|
19195
|
+
if (error) {
|
|
19196
|
+
next(new BadRequestError91(error.message));
|
|
19197
|
+
return;
|
|
19198
|
+
}
|
|
19199
|
+
try {
|
|
19200
|
+
const entries = await _getAll({
|
|
19201
|
+
org,
|
|
19202
|
+
page,
|
|
19203
|
+
limit,
|
|
19204
|
+
book,
|
|
19205
|
+
type,
|
|
19206
|
+
search,
|
|
19207
|
+
status: status2
|
|
19208
|
+
});
|
|
19209
|
+
res.json(entries);
|
|
19210
|
+
return;
|
|
19211
|
+
} catch (error2) {
|
|
19212
|
+
next(error2);
|
|
19213
|
+
}
|
|
19214
|
+
}
|
|
19215
|
+
async function getById(req, res, next) {
|
|
19216
|
+
const id = req.params.id;
|
|
19217
|
+
const validation = Joi81.object({
|
|
19218
|
+
id: Joi81.string().hex().required()
|
|
19219
|
+
});
|
|
19220
|
+
const { error } = validation.validate({ id });
|
|
19221
|
+
if (error) {
|
|
19222
|
+
next(new BadRequestError91(error.message));
|
|
19223
|
+
return;
|
|
19224
|
+
}
|
|
19225
|
+
try {
|
|
19226
|
+
const entry = await _getById(id);
|
|
19227
|
+
res.json(entry);
|
|
19228
|
+
return;
|
|
19229
|
+
} catch (error2) {
|
|
19230
|
+
next(error2);
|
|
19231
|
+
}
|
|
19232
|
+
}
|
|
19233
|
+
async function updateById(req, res, next) {
|
|
19234
|
+
const _id = req.params.id ?? "";
|
|
19235
|
+
const { error: errorId } = Joi81.string().hex().required().validate(_id);
|
|
19236
|
+
if (errorId) {
|
|
19237
|
+
next(new BadRequestError91("Invalid Journal Entry ID."));
|
|
19238
|
+
return;
|
|
19239
|
+
}
|
|
19240
|
+
const payload = req.body;
|
|
19241
|
+
const { error } = schemaJournalEntryCtrl.validate(payload);
|
|
19242
|
+
if (error) {
|
|
19243
|
+
next(
|
|
19244
|
+
new BadRequestError91(
|
|
19245
|
+
`Invalid journal entry update data: ${error.message}`
|
|
19246
|
+
)
|
|
19247
|
+
);
|
|
19248
|
+
return;
|
|
19249
|
+
}
|
|
19250
|
+
try {
|
|
19251
|
+
const message = await _updateById(_id, payload);
|
|
19252
|
+
res.json({ message });
|
|
19253
|
+
return;
|
|
19254
|
+
} catch (error2) {
|
|
19255
|
+
next(error2);
|
|
19256
|
+
}
|
|
19257
|
+
}
|
|
19258
|
+
async function deleteById(req, res, next) {
|
|
19259
|
+
const id = req.params.id;
|
|
19260
|
+
if (!id) {
|
|
19261
|
+
next(new BadRequestError91("Journal Entry ID is required."));
|
|
19262
|
+
return;
|
|
19263
|
+
}
|
|
19264
|
+
try {
|
|
19265
|
+
const message = await _deleteById(id);
|
|
19266
|
+
res.json(message);
|
|
19267
|
+
return;
|
|
19268
|
+
} catch (error) {
|
|
19269
|
+
next(error);
|
|
19270
|
+
}
|
|
19271
|
+
}
|
|
19272
|
+
async function updateStatusById(req, res, next) {
|
|
19273
|
+
const id = req.params.id;
|
|
19274
|
+
const status2 = req.params.status;
|
|
19275
|
+
const validation = Joi81.object({
|
|
19276
|
+
id: Joi81.string().hex().required(),
|
|
19277
|
+
status: Joi81.string().required()
|
|
19278
|
+
});
|
|
19279
|
+
const { error } = validation.validate({ id, status: status2 });
|
|
19280
|
+
if (error) {
|
|
19281
|
+
next(new BadRequestError91(error.message));
|
|
19282
|
+
return;
|
|
19283
|
+
}
|
|
19284
|
+
try {
|
|
19285
|
+
const message = await _updateStatusById(id, status2);
|
|
19286
|
+
res.json({ message });
|
|
19287
|
+
return;
|
|
19288
|
+
} catch (error2) {
|
|
19289
|
+
next(error2);
|
|
19290
|
+
}
|
|
19291
|
+
}
|
|
19292
|
+
return {
|
|
19293
|
+
add,
|
|
19294
|
+
getAll,
|
|
19295
|
+
getById,
|
|
19296
|
+
updateById,
|
|
19297
|
+
deleteById,
|
|
19298
|
+
updateStatusById
|
|
19299
|
+
};
|
|
19300
|
+
}
|
|
19301
|
+
|
|
19302
|
+
// src/resources/finance-journal/finance.journal.line.controller.ts
|
|
19303
|
+
import Joi82 from "joi";
|
|
19304
|
+
import { BadRequestError as BadRequestError92 } from "@goweekdays/utils";
|
|
19305
|
+
function useJournalLineController() {
|
|
19306
|
+
const {
|
|
19307
|
+
getAll: _getAll,
|
|
19308
|
+
getById: _getById,
|
|
19309
|
+
updateById: _updateById,
|
|
19310
|
+
deleteById: _deleteById,
|
|
19311
|
+
add: _add
|
|
19312
|
+
} = useJournalLineRepo();
|
|
19313
|
+
async function getAll(req, res, next) {
|
|
19314
|
+
const query = req.query;
|
|
19315
|
+
const validation = Joi82.object({
|
|
19316
|
+
org: Joi82.string().hex().required(),
|
|
19317
|
+
journalEntry: Joi82.string().hex().optional(),
|
|
19318
|
+
page: Joi82.number().min(1).optional().allow("", null),
|
|
19319
|
+
limit: Joi82.number().min(1).optional().allow("", null)
|
|
19320
|
+
});
|
|
19321
|
+
const org = req.params.org ?? "";
|
|
19322
|
+
const journalEntry = req.query.journalEntry;
|
|
19323
|
+
const { error } = validation.validate({ ...query, org, journalEntry });
|
|
19324
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
19325
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
19326
|
+
if (!isFinite(page)) {
|
|
19327
|
+
next(new BadRequestError92("Invalid page number."));
|
|
19328
|
+
return;
|
|
19329
|
+
}
|
|
19330
|
+
if (!isFinite(limit)) {
|
|
19331
|
+
next(new BadRequestError92("Invalid limit number."));
|
|
19332
|
+
return;
|
|
19333
|
+
}
|
|
19334
|
+
if (error) {
|
|
19335
|
+
next(new BadRequestError92(error.message));
|
|
19336
|
+
return;
|
|
19337
|
+
}
|
|
19338
|
+
try {
|
|
19339
|
+
const lines = await _getAll({ org, journalEntry, page, limit });
|
|
19340
|
+
res.json(lines);
|
|
19341
|
+
return;
|
|
19342
|
+
} catch (error2) {
|
|
19343
|
+
next(error2);
|
|
19344
|
+
}
|
|
19345
|
+
}
|
|
19346
|
+
async function getById(req, res, next) {
|
|
19347
|
+
const id = req.params.id;
|
|
19348
|
+
const validation = Joi82.object({
|
|
19349
|
+
id: Joi82.string().hex().required()
|
|
19350
|
+
});
|
|
19351
|
+
const { error } = validation.validate({ id });
|
|
19352
|
+
if (error) {
|
|
19353
|
+
next(new BadRequestError92(error.message));
|
|
19354
|
+
return;
|
|
19355
|
+
}
|
|
19356
|
+
try {
|
|
19357
|
+
const line = await _getById(id);
|
|
19358
|
+
res.json(line);
|
|
19359
|
+
return;
|
|
19360
|
+
} catch (error2) {
|
|
19361
|
+
next(error2);
|
|
19362
|
+
}
|
|
19363
|
+
}
|
|
19364
|
+
async function updateById(req, res, next) {
|
|
19365
|
+
const _id = req.params.id ?? "";
|
|
19366
|
+
const { error: errorId } = Joi82.string().hex().required().validate(_id);
|
|
19367
|
+
if (errorId) {
|
|
19368
|
+
next(new BadRequestError92("Invalid Journal Line ID."));
|
|
19369
|
+
return;
|
|
19370
|
+
}
|
|
19371
|
+
const payload = req.body;
|
|
19372
|
+
const { error } = schemaJournalLineCtrl.validate(payload);
|
|
19373
|
+
if (error) {
|
|
19374
|
+
next(
|
|
19375
|
+
new BadRequestError92(
|
|
19376
|
+
`Invalid journal line update data: ${error.message}`
|
|
19377
|
+
)
|
|
19378
|
+
);
|
|
19379
|
+
return;
|
|
19380
|
+
}
|
|
19381
|
+
try {
|
|
19382
|
+
const message = await _updateById(_id, payload);
|
|
19383
|
+
res.json({ message });
|
|
19384
|
+
return;
|
|
19385
|
+
} catch (error2) {
|
|
19386
|
+
next(error2);
|
|
19387
|
+
}
|
|
19388
|
+
}
|
|
19389
|
+
async function deleteById(req, res, next) {
|
|
19390
|
+
const id = req.params.id;
|
|
19391
|
+
if (!id) {
|
|
19392
|
+
next(new BadRequestError92("Journal Line ID is required."));
|
|
19393
|
+
return;
|
|
19394
|
+
}
|
|
19395
|
+
try {
|
|
19396
|
+
const message = await _deleteById(id);
|
|
19397
|
+
res.json(message);
|
|
19398
|
+
return;
|
|
19399
|
+
} catch (error) {
|
|
19400
|
+
next(error);
|
|
19401
|
+
}
|
|
19402
|
+
}
|
|
19403
|
+
return {
|
|
19404
|
+
getAll,
|
|
19405
|
+
getById,
|
|
19406
|
+
updateById,
|
|
19407
|
+
deleteById
|
|
19408
|
+
};
|
|
19409
|
+
}
|
|
17048
19410
|
export {
|
|
17049
19411
|
ACCESS_TOKEN_EXPIRY,
|
|
17050
19412
|
ACCESS_TOKEN_SECRET,
|
|
@@ -17087,12 +19449,17 @@ export {
|
|
|
17087
19449
|
VERIFICATION_USER_INVITE_DURATION,
|
|
17088
19450
|
XENDIT_BASE_URL,
|
|
17089
19451
|
XENDIT_SECRET_KEY,
|
|
19452
|
+
chartOfAccountNormalBalances,
|
|
19453
|
+
chartOfAccountTypes,
|
|
17090
19454
|
currencies,
|
|
17091
19455
|
isDev,
|
|
17092
19456
|
jobApplicationStatuses,
|
|
19457
|
+
journalEntryStatuses,
|
|
19458
|
+
journalEntryTypes,
|
|
17093
19459
|
ledgerBillStatuses,
|
|
17094
19460
|
ledgerBillTypes,
|
|
17095
19461
|
modelApp,
|
|
19462
|
+
modelChartOfAccount,
|
|
17096
19463
|
modelJobApplication,
|
|
17097
19464
|
modelJobPost,
|
|
17098
19465
|
modelJobProfile,
|
|
@@ -17104,6 +19471,8 @@ export {
|
|
|
17104
19471
|
modelJobStatusSetup,
|
|
17105
19472
|
modelJobStatusTrans,
|
|
17106
19473
|
modelJobSummary,
|
|
19474
|
+
modelJournalEntry,
|
|
19475
|
+
modelJournalLine,
|
|
17107
19476
|
modelLedgerBill,
|
|
17108
19477
|
modelMember,
|
|
17109
19478
|
modelOption,
|
|
@@ -17115,6 +19484,7 @@ export {
|
|
|
17115
19484
|
modelRole,
|
|
17116
19485
|
modelSubscription,
|
|
17117
19486
|
modelSubscriptionTransaction,
|
|
19487
|
+
modelTax,
|
|
17118
19488
|
modelUser,
|
|
17119
19489
|
modelVerification,
|
|
17120
19490
|
schemaApp,
|
|
@@ -17123,6 +19493,9 @@ export {
|
|
|
17123
19493
|
schemaBuilding,
|
|
17124
19494
|
schemaBuildingUnit,
|
|
17125
19495
|
schemaCertification,
|
|
19496
|
+
schemaChartOfAccountBase,
|
|
19497
|
+
schemaChartOfAccountStd,
|
|
19498
|
+
schemaChartOfAccountUpdate,
|
|
17126
19499
|
schemaEducation,
|
|
17127
19500
|
schemaGroup,
|
|
17128
19501
|
schemaInviteMember,
|
|
@@ -17149,6 +19522,11 @@ export {
|
|
|
17149
19522
|
schemaJobStatusTrans,
|
|
17150
19523
|
schemaJobSummary,
|
|
17151
19524
|
schemaJobSummaryInc,
|
|
19525
|
+
schemaJournalEntryCtrl,
|
|
19526
|
+
schemaJournalEntryGetAll,
|
|
19527
|
+
schemaJournalEntryRepo,
|
|
19528
|
+
schemaJournalLineCtrl,
|
|
19529
|
+
schemaJournalLineRepo,
|
|
17152
19530
|
schemaLanguage,
|
|
17153
19531
|
schemaLedgerBill,
|
|
17154
19532
|
schemaLedgerBillingSummary,
|
|
@@ -17179,11 +19557,14 @@ export {
|
|
|
17179
19557
|
schemaSubscriptionSeats,
|
|
17180
19558
|
schemaSubscriptionTransaction,
|
|
17181
19559
|
schemaSubscriptionUpdate,
|
|
19560
|
+
schemaTax,
|
|
19561
|
+
schemaTaxUpdate,
|
|
17182
19562
|
schemaUpdateOptions,
|
|
17183
19563
|
schemaUser,
|
|
17184
19564
|
schemaVerification,
|
|
17185
19565
|
schemaVerificationOrgInvite,
|
|
17186
19566
|
schemaWorkExp,
|
|
19567
|
+
taxDirections,
|
|
17187
19568
|
transactionSchema,
|
|
17188
19569
|
useAppController,
|
|
17189
19570
|
useAppRepo,
|
|
@@ -17196,6 +19577,8 @@ export {
|
|
|
17196
19577
|
useBuildingUnitController,
|
|
17197
19578
|
useBuildingUnitRepo,
|
|
17198
19579
|
useBuildingUnitService,
|
|
19580
|
+
useChartOfAccountController,
|
|
19581
|
+
useChartOfAccountRepo,
|
|
17199
19582
|
useCounterModel,
|
|
17200
19583
|
useCounterRepo,
|
|
17201
19584
|
useFileController,
|
|
@@ -17214,6 +19597,11 @@ export {
|
|
|
17214
19597
|
useJobSummaryCtrl,
|
|
17215
19598
|
useJobSummaryRepo,
|
|
17216
19599
|
useJobSummarySvc,
|
|
19600
|
+
useJournalEntryController,
|
|
19601
|
+
useJournalEntryRepo,
|
|
19602
|
+
useJournalEntryService,
|
|
19603
|
+
useJournalLineController,
|
|
19604
|
+
useJournalLineRepo,
|
|
17217
19605
|
useLedgerBillingController,
|
|
17218
19606
|
useLedgerBillingRepo,
|
|
17219
19607
|
useMemberController,
|
|
@@ -17244,6 +19632,8 @@ export {
|
|
|
17244
19632
|
useSubscriptionService,
|
|
17245
19633
|
useSubscriptionTransactionController,
|
|
17246
19634
|
useSubscriptionTransactionRepo,
|
|
19635
|
+
useTaxController,
|
|
19636
|
+
useTaxRepo,
|
|
17247
19637
|
useUserController,
|
|
17248
19638
|
useUserRepo,
|
|
17249
19639
|
useUserService,
|