@iservice365/module-hygiene 1.1.0 → 1.4.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 +24 -0
- package/dist/index.d.ts +83 -2
- package/dist/index.js +737 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +744 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -790,8 +790,8 @@ function MUnit(value) {
|
|
|
790
790
|
return {
|
|
791
791
|
site: value.site,
|
|
792
792
|
name: value.name,
|
|
793
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
794
793
|
status: "active",
|
|
794
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
795
795
|
updatedAt: "",
|
|
796
796
|
deletedAt: ""
|
|
797
797
|
};
|
|
@@ -1503,6 +1503,9 @@ function useParentChecklistRepo() {
|
|
|
1503
1503
|
status,
|
|
1504
1504
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1505
1505
|
};
|
|
1506
|
+
if (status === "completed") {
|
|
1507
|
+
updateValue.completedAt = /* @__PURE__ */ new Date();
|
|
1508
|
+
}
|
|
1506
1509
|
const res = await collection.updateOne(
|
|
1507
1510
|
{ _id },
|
|
1508
1511
|
{ $set: updateValue },
|
|
@@ -2932,7 +2935,8 @@ function useSupplyRepository() {
|
|
|
2932
2935
|
{
|
|
2933
2936
|
$project: {
|
|
2934
2937
|
name: 1,
|
|
2935
|
-
qty: 1
|
|
2938
|
+
qty: 1,
|
|
2939
|
+
status: 1
|
|
2936
2940
|
}
|
|
2937
2941
|
},
|
|
2938
2942
|
{ $sort: { _id: -1 } },
|
|
@@ -3025,7 +3029,7 @@ function useSupplyRepository() {
|
|
|
3025
3029
|
} catch (error) {
|
|
3026
3030
|
const isDuplicated = error.message.includes("duplicate");
|
|
3027
3031
|
if (isDuplicated) {
|
|
3028
|
-
throw new BadRequestError16("
|
|
3032
|
+
throw new BadRequestError16("Supply already exists.");
|
|
3029
3033
|
}
|
|
3030
3034
|
throw error;
|
|
3031
3035
|
}
|
|
@@ -3567,7 +3571,8 @@ import {
|
|
|
3567
3571
|
logger as logger23,
|
|
3568
3572
|
makeCacheKey as makeCacheKey7,
|
|
3569
3573
|
paginate as paginate7,
|
|
3570
|
-
BadRequestError as BadRequestError23
|
|
3574
|
+
BadRequestError as BadRequestError23,
|
|
3575
|
+
NotFoundError as NotFoundError6
|
|
3571
3576
|
} from "@iservice365/node-server-utils";
|
|
3572
3577
|
function useRequestItemRepository() {
|
|
3573
3578
|
const db = useAtlas10.getDb();
|
|
@@ -3656,7 +3661,7 @@ function useRequestItemRepository() {
|
|
|
3656
3661
|
status: 1
|
|
3657
3662
|
}
|
|
3658
3663
|
},
|
|
3659
|
-
{ $sort: { _id: 1 } },
|
|
3664
|
+
{ $sort: { _id: -1 } },
|
|
3660
3665
|
{ $skip: page * limit },
|
|
3661
3666
|
{ $limit: limit }
|
|
3662
3667
|
]).toArray();
|
|
@@ -3672,21 +3677,156 @@ function useRequestItemRepository() {
|
|
|
3672
3677
|
throw error;
|
|
3673
3678
|
}
|
|
3674
3679
|
}
|
|
3680
|
+
async function getRequestItemById(_id, session) {
|
|
3681
|
+
try {
|
|
3682
|
+
_id = new ObjectId14(_id);
|
|
3683
|
+
} catch (error) {
|
|
3684
|
+
throw new BadRequestError23("Invalid request item ID format.");
|
|
3685
|
+
}
|
|
3686
|
+
const query = { _id };
|
|
3687
|
+
const cacheKey = makeCacheKey7(namespace_collection, {
|
|
3688
|
+
_id: _id.toString()
|
|
3689
|
+
});
|
|
3690
|
+
if (!session) {
|
|
3691
|
+
const cachedData = await getCache(cacheKey);
|
|
3692
|
+
if (cachedData) {
|
|
3693
|
+
logger23.info(`Cache hit for key: ${cacheKey}`);
|
|
3694
|
+
return cachedData;
|
|
3695
|
+
}
|
|
3696
|
+
} else {
|
|
3697
|
+
logger23.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
3698
|
+
}
|
|
3699
|
+
try {
|
|
3700
|
+
const data = await collection.aggregate([
|
|
3701
|
+
{ $match: query },
|
|
3702
|
+
{
|
|
3703
|
+
$lookup: {
|
|
3704
|
+
from: "site.supply.items",
|
|
3705
|
+
localField: "supply",
|
|
3706
|
+
foreignField: "_id",
|
|
3707
|
+
as: "supplyDetails"
|
|
3708
|
+
}
|
|
3709
|
+
},
|
|
3710
|
+
{
|
|
3711
|
+
$unwind: {
|
|
3712
|
+
path: "$supplyDetails",
|
|
3713
|
+
preserveNullAndEmptyArrays: true
|
|
3714
|
+
}
|
|
3715
|
+
},
|
|
3716
|
+
{
|
|
3717
|
+
$project: {
|
|
3718
|
+
site: 1,
|
|
3719
|
+
supply: 1,
|
|
3720
|
+
supplyName: 1,
|
|
3721
|
+
qty: 1,
|
|
3722
|
+
status: 1,
|
|
3723
|
+
unitOfMeasurement: "$supplyDetails.unitOfMeasurement"
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
]).toArray();
|
|
3727
|
+
if (!data || data.length === 0) {
|
|
3728
|
+
throw new NotFoundError6("Request item not found.");
|
|
3729
|
+
}
|
|
3730
|
+
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
3731
|
+
logger23.info(`Cache set for key: ${cacheKey}`);
|
|
3732
|
+
}).catch((err) => {
|
|
3733
|
+
logger23.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
3734
|
+
});
|
|
3735
|
+
return data[0];
|
|
3736
|
+
} catch (error) {
|
|
3737
|
+
throw error;
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
async function approveRequestItem(_id, remarks, session) {
|
|
3741
|
+
try {
|
|
3742
|
+
_id = new ObjectId14(_id);
|
|
3743
|
+
} catch (error) {
|
|
3744
|
+
throw new BadRequestError23("Invalid request item ID format.");
|
|
3745
|
+
}
|
|
3746
|
+
try {
|
|
3747
|
+
const updateValue = {
|
|
3748
|
+
status: "approved",
|
|
3749
|
+
remarks: remarks || "",
|
|
3750
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3751
|
+
};
|
|
3752
|
+
const res = await collection.updateOne(
|
|
3753
|
+
{ _id },
|
|
3754
|
+
{ $set: updateValue },
|
|
3755
|
+
{ session }
|
|
3756
|
+
);
|
|
3757
|
+
if (res.modifiedCount === 0) {
|
|
3758
|
+
throw new InternalServerError7("Unable to approve request item.");
|
|
3759
|
+
}
|
|
3760
|
+
delNamespace().then(() => {
|
|
3761
|
+
logger23.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3762
|
+
}).catch((err) => {
|
|
3763
|
+
logger23.error(
|
|
3764
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3765
|
+
err
|
|
3766
|
+
);
|
|
3767
|
+
});
|
|
3768
|
+
return res.modifiedCount;
|
|
3769
|
+
} catch (error) {
|
|
3770
|
+
throw error;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
async function disapproveRequestItem(_id, remarks, session) {
|
|
3774
|
+
try {
|
|
3775
|
+
_id = new ObjectId14(_id);
|
|
3776
|
+
} catch (error) {
|
|
3777
|
+
throw new BadRequestError23("Invalid request item ID format.");
|
|
3778
|
+
}
|
|
3779
|
+
try {
|
|
3780
|
+
const updateValue = {
|
|
3781
|
+
status: "disapproved",
|
|
3782
|
+
remarks: remarks || "",
|
|
3783
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3784
|
+
};
|
|
3785
|
+
const res = await collection.updateOne(
|
|
3786
|
+
{ _id },
|
|
3787
|
+
{ $set: updateValue },
|
|
3788
|
+
{ session }
|
|
3789
|
+
);
|
|
3790
|
+
if (res.modifiedCount === 0) {
|
|
3791
|
+
throw new InternalServerError7("Unable to disapprove request item.");
|
|
3792
|
+
}
|
|
3793
|
+
delNamespace().then(() => {
|
|
3794
|
+
logger23.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
3795
|
+
}).catch((err) => {
|
|
3796
|
+
logger23.error(
|
|
3797
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
3798
|
+
err
|
|
3799
|
+
);
|
|
3800
|
+
});
|
|
3801
|
+
return res.modifiedCount;
|
|
3802
|
+
} catch (error) {
|
|
3803
|
+
throw error;
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3675
3806
|
return {
|
|
3676
3807
|
createIndex,
|
|
3677
3808
|
createTextIndex,
|
|
3678
3809
|
createRequestItem,
|
|
3679
|
-
getRequestItems
|
|
3810
|
+
getRequestItems,
|
|
3811
|
+
getRequestItemById,
|
|
3812
|
+
approveRequestItem,
|
|
3813
|
+
disapproveRequestItem
|
|
3680
3814
|
};
|
|
3681
3815
|
}
|
|
3682
3816
|
|
|
3683
3817
|
// src/services/hygiene-request-item.service.ts
|
|
3684
|
-
import { useAtlas as useAtlas11 } from "@iservice365/node-server-utils";
|
|
3818
|
+
import { BadRequestError as BadRequestError24, useAtlas as useAtlas11 } from "@iservice365/node-server-utils";
|
|
3685
3819
|
import { useUserRepo } from "@iservice365/core";
|
|
3686
3820
|
function useRequestItemService() {
|
|
3687
|
-
const {
|
|
3821
|
+
const {
|
|
3822
|
+
createRequestItem: _createRequestItem,
|
|
3823
|
+
getRequestItemById: _getRequestItemById,
|
|
3824
|
+
approveRequestItem: _approveRequestItem,
|
|
3825
|
+
disapproveRequestItem: _disapproveRequestItem
|
|
3826
|
+
} = useRequestItemRepository();
|
|
3688
3827
|
const { getSupplyById } = useSupplyRepository();
|
|
3689
3828
|
const { getUserById } = useUserRepo();
|
|
3829
|
+
const { createStock } = useStockService();
|
|
3690
3830
|
async function createRequestItem(value) {
|
|
3691
3831
|
try {
|
|
3692
3832
|
const { supply, createdBy } = value;
|
|
@@ -3733,17 +3873,76 @@ function useRequestItemService() {
|
|
|
3733
3873
|
await session?.endSession();
|
|
3734
3874
|
}
|
|
3735
3875
|
}
|
|
3736
|
-
|
|
3876
|
+
async function approveRequestItem(id, remarks) {
|
|
3877
|
+
const session = useAtlas11.getClient()?.startSession();
|
|
3878
|
+
try {
|
|
3879
|
+
session?.startTransaction();
|
|
3880
|
+
await _approveRequestItem(id, remarks, session);
|
|
3881
|
+
const requestItem = await _getRequestItemById(id, session);
|
|
3882
|
+
if (requestItem.status !== "pending") {
|
|
3883
|
+
throw new BadRequestError24(
|
|
3884
|
+
"Only 'pending' request items can be approved."
|
|
3885
|
+
);
|
|
3886
|
+
}
|
|
3887
|
+
const createdStocks = await createStock(
|
|
3888
|
+
{
|
|
3889
|
+
site: requestItem.site.toString(),
|
|
3890
|
+
supply: requestItem.supply.toString(),
|
|
3891
|
+
qty: requestItem.qty,
|
|
3892
|
+
remarks
|
|
3893
|
+
},
|
|
3894
|
+
true
|
|
3895
|
+
);
|
|
3896
|
+
await session?.commitTransaction();
|
|
3897
|
+
return createdStocks;
|
|
3898
|
+
} catch (error) {
|
|
3899
|
+
await session?.abortTransaction();
|
|
3900
|
+
throw error;
|
|
3901
|
+
} finally {
|
|
3902
|
+
await session?.endSession();
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
async function disapproveRequestItem(id, remarks) {
|
|
3906
|
+
const session = useAtlas11.getClient()?.startSession();
|
|
3907
|
+
try {
|
|
3908
|
+
session?.startTransaction();
|
|
3909
|
+
const result = await _disapproveRequestItem(id, remarks, session);
|
|
3910
|
+
const requestItem = await _getRequestItemById(id, session);
|
|
3911
|
+
if (requestItem.status !== "pending") {
|
|
3912
|
+
throw new BadRequestError24(
|
|
3913
|
+
"Only 'pending' request items can be disapproved."
|
|
3914
|
+
);
|
|
3915
|
+
}
|
|
3916
|
+
await session?.commitTransaction();
|
|
3917
|
+
return result;
|
|
3918
|
+
} catch (error) {
|
|
3919
|
+
await session?.abortTransaction();
|
|
3920
|
+
throw error;
|
|
3921
|
+
} finally {
|
|
3922
|
+
await session?.endSession();
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
return {
|
|
3926
|
+
createRequestItem,
|
|
3927
|
+
createRequestItemByBatch,
|
|
3928
|
+
approveRequestItem,
|
|
3929
|
+
disapproveRequestItem
|
|
3930
|
+
};
|
|
3737
3931
|
}
|
|
3738
3932
|
|
|
3739
3933
|
// src/controllers/hygiene-request-item.controller.ts
|
|
3740
|
-
import { BadRequestError as
|
|
3934
|
+
import { BadRequestError as BadRequestError25, logger as logger24 } from "@iservice365/node-server-utils";
|
|
3741
3935
|
import Joi14 from "joi";
|
|
3742
3936
|
function useRequestItemController() {
|
|
3743
|
-
const {
|
|
3937
|
+
const {
|
|
3938
|
+
getRequestItems: _getRequestItems,
|
|
3939
|
+
getRequestItemById: _getRequestItemById
|
|
3940
|
+
} = useRequestItemRepository();
|
|
3744
3941
|
const {
|
|
3745
3942
|
createRequestItem: _createRequestItem,
|
|
3746
|
-
createRequestItemByBatch: _createRequestItemByBatch
|
|
3943
|
+
createRequestItemByBatch: _createRequestItemByBatch,
|
|
3944
|
+
approveRequestItem: _approveRequestItem,
|
|
3945
|
+
disapproveRequestItem: _disapproveRequestItem
|
|
3747
3946
|
} = useRequestItemService();
|
|
3748
3947
|
async function createRequestItem(req, res, next) {
|
|
3749
3948
|
const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
|
|
@@ -3765,7 +3964,7 @@ function useRequestItemController() {
|
|
|
3765
3964
|
const { error } = validation.validate(payload);
|
|
3766
3965
|
if (error) {
|
|
3767
3966
|
logger24.log({ level: "error", message: error.message });
|
|
3768
|
-
next(new
|
|
3967
|
+
next(new BadRequestError25(error.message));
|
|
3769
3968
|
return;
|
|
3770
3969
|
}
|
|
3771
3970
|
try {
|
|
@@ -3802,7 +4001,7 @@ function useRequestItemController() {
|
|
|
3802
4001
|
const { error } = validation.validate(payload);
|
|
3803
4002
|
if (error) {
|
|
3804
4003
|
logger24.log({ level: "error", message: error.message });
|
|
3805
|
-
next(new
|
|
4004
|
+
next(new BadRequestError25(error.message));
|
|
3806
4005
|
return;
|
|
3807
4006
|
}
|
|
3808
4007
|
try {
|
|
@@ -3826,7 +4025,7 @@ function useRequestItemController() {
|
|
|
3826
4025
|
const { error } = validation.validate(query);
|
|
3827
4026
|
if (error) {
|
|
3828
4027
|
logger24.log({ level: "error", message: error.message });
|
|
3829
|
-
next(new
|
|
4028
|
+
next(new BadRequestError25(error.message));
|
|
3830
4029
|
return;
|
|
3831
4030
|
}
|
|
3832
4031
|
const page = parseInt(req.query.page) ?? 1;
|
|
@@ -3848,10 +4047,530 @@ function useRequestItemController() {
|
|
|
3848
4047
|
return;
|
|
3849
4048
|
}
|
|
3850
4049
|
}
|
|
4050
|
+
async function getRequestItemById(req, res, next) {
|
|
4051
|
+
const validation = Joi14.string().hex().required();
|
|
4052
|
+
const _id = req.params.id;
|
|
4053
|
+
const { error } = validation.validate(_id);
|
|
4054
|
+
if (error) {
|
|
4055
|
+
logger24.log({ level: "error", message: error.message });
|
|
4056
|
+
next(new BadRequestError25(error.message));
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
try {
|
|
4060
|
+
const data = await _getRequestItemById(_id);
|
|
4061
|
+
res.json(data);
|
|
4062
|
+
return;
|
|
4063
|
+
} catch (error2) {
|
|
4064
|
+
logger24.log({ level: "error", message: error2.message });
|
|
4065
|
+
next(error2);
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
async function approveRequestItem(req, res, next) {
|
|
4070
|
+
const payload = { ...req.params, ...req.body };
|
|
4071
|
+
const validation = Joi14.object({
|
|
4072
|
+
id: Joi14.string().hex().required(),
|
|
4073
|
+
remarks: Joi14.string().optional().allow("", null)
|
|
4074
|
+
});
|
|
4075
|
+
const { error } = validation.validate(payload);
|
|
4076
|
+
if (error) {
|
|
4077
|
+
logger24.log({ level: "error", message: error.message });
|
|
4078
|
+
next(new BadRequestError25(error.message));
|
|
4079
|
+
return;
|
|
4080
|
+
}
|
|
4081
|
+
try {
|
|
4082
|
+
await _approveRequestItem(payload.id, payload.remarks);
|
|
4083
|
+
res.json({ message: "Request item approved successfully." });
|
|
4084
|
+
return;
|
|
4085
|
+
} catch (error2) {
|
|
4086
|
+
logger24.log({ level: "error", message: error2.message });
|
|
4087
|
+
next(error2);
|
|
4088
|
+
return;
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
async function disapproveRequestItem(req, res, next) {
|
|
4092
|
+
const payload = { ...req.params, ...req.body };
|
|
4093
|
+
const validation = Joi14.object({
|
|
4094
|
+
id: Joi14.string().hex().required(),
|
|
4095
|
+
remarks: Joi14.string().optional().allow("", null)
|
|
4096
|
+
});
|
|
4097
|
+
const { error } = validation.validate(payload);
|
|
4098
|
+
if (error) {
|
|
4099
|
+
logger24.log({ level: "error", message: error.message });
|
|
4100
|
+
next(new BadRequestError25(error.message));
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
try {
|
|
4104
|
+
await _disapproveRequestItem(payload.id, payload.remarks);
|
|
4105
|
+
res.json({ message: "Request item disapproved successfully." });
|
|
4106
|
+
return;
|
|
4107
|
+
} catch (error2) {
|
|
4108
|
+
logger24.log({ level: "error", message: error2.message });
|
|
4109
|
+
next(error2);
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
3851
4113
|
return {
|
|
3852
4114
|
createRequestItem,
|
|
3853
4115
|
createRequestItemByBatch,
|
|
3854
|
-
getRequestItems
|
|
4116
|
+
getRequestItems,
|
|
4117
|
+
getRequestItemById,
|
|
4118
|
+
approveRequestItem,
|
|
4119
|
+
disapproveRequestItem
|
|
4120
|
+
};
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
// src/models/hygiene-schedule-task.model.ts
|
|
4124
|
+
import { BadRequestError as BadRequestError26, logger as logger25 } from "@iservice365/node-server-utils";
|
|
4125
|
+
import Joi15 from "joi";
|
|
4126
|
+
import { ObjectId as ObjectId15 } from "mongodb";
|
|
4127
|
+
var allowedFrequency = ["week", "month", "quarter", "year"];
|
|
4128
|
+
var allowedDays = [
|
|
4129
|
+
"Mon",
|
|
4130
|
+
"Tue",
|
|
4131
|
+
"Wed",
|
|
4132
|
+
"Thu",
|
|
4133
|
+
"Fri",
|
|
4134
|
+
"Sat",
|
|
4135
|
+
"Sun"
|
|
4136
|
+
];
|
|
4137
|
+
var allowedQuarter = ["1st", "2nd", "3rd", "4th"];
|
|
4138
|
+
var allowedWeekOfMonth = [
|
|
4139
|
+
"1st",
|
|
4140
|
+
"2nd",
|
|
4141
|
+
"3rd",
|
|
4142
|
+
"4th",
|
|
4143
|
+
"last"
|
|
4144
|
+
];
|
|
4145
|
+
var allowedMonths = [
|
|
4146
|
+
"January",
|
|
4147
|
+
"February",
|
|
4148
|
+
"March",
|
|
4149
|
+
"April",
|
|
4150
|
+
"May",
|
|
4151
|
+
"June",
|
|
4152
|
+
"July",
|
|
4153
|
+
"August",
|
|
4154
|
+
"September",
|
|
4155
|
+
"October",
|
|
4156
|
+
"November",
|
|
4157
|
+
"December"
|
|
4158
|
+
];
|
|
4159
|
+
var scheduleTaskSchema = Joi15.object({
|
|
4160
|
+
site: Joi15.string().hex().required(),
|
|
4161
|
+
title: Joi15.string().required(),
|
|
4162
|
+
frequency: Joi15.string().valid(...allowedFrequency).required(),
|
|
4163
|
+
time: Joi15.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
|
|
4164
|
+
day: Joi15.string().valid(...allowedDays).required(),
|
|
4165
|
+
weekOfMonth: Joi15.string().valid(...allowedWeekOfMonth).when("frequency", {
|
|
4166
|
+
is: Joi15.string().valid("month", "quarter", "year"),
|
|
4167
|
+
then: Joi15.required(),
|
|
4168
|
+
otherwise: Joi15.optional().allow("", null)
|
|
4169
|
+
}),
|
|
4170
|
+
quarter: Joi15.string().valid(...allowedQuarter).when("frequency", {
|
|
4171
|
+
is: "quarter",
|
|
4172
|
+
then: Joi15.required(),
|
|
4173
|
+
otherwise: Joi15.optional().allow("", null)
|
|
4174
|
+
}),
|
|
4175
|
+
month: Joi15.string().valid(...allowedMonths).when("frequency", {
|
|
4176
|
+
is: Joi15.string().valid("quarter", "year"),
|
|
4177
|
+
then: Joi15.required(),
|
|
4178
|
+
otherwise: Joi15.optional().allow("", null)
|
|
4179
|
+
}),
|
|
4180
|
+
description: Joi15.string().optional().allow("", null),
|
|
4181
|
+
areas: Joi15.array().min(1).items(
|
|
4182
|
+
Joi15.object({
|
|
4183
|
+
name: Joi15.string().required(),
|
|
4184
|
+
value: Joi15.any().required()
|
|
4185
|
+
})
|
|
4186
|
+
).required()
|
|
4187
|
+
});
|
|
4188
|
+
function MScheduleTask(value) {
|
|
4189
|
+
const { error } = scheduleTaskSchema.validate(value);
|
|
4190
|
+
if (error) {
|
|
4191
|
+
logger25.info(`Hygiene Schedule Task Model: ${error.message}`);
|
|
4192
|
+
throw new BadRequestError26(error.message);
|
|
4193
|
+
}
|
|
4194
|
+
if (value.site) {
|
|
4195
|
+
try {
|
|
4196
|
+
value.site = new ObjectId15(value.site);
|
|
4197
|
+
} catch (error2) {
|
|
4198
|
+
throw new BadRequestError26("Invalid site ID format.");
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
if (value.areas && Array.isArray(value.areas)) {
|
|
4202
|
+
value.areas = value.areas.map((area) => {
|
|
4203
|
+
try {
|
|
4204
|
+
return {
|
|
4205
|
+
name: area.name,
|
|
4206
|
+
value: new ObjectId15(area.value.toString())
|
|
4207
|
+
};
|
|
4208
|
+
} catch (error2) {
|
|
4209
|
+
throw new BadRequestError26(`Invalid area value format: ${area.name}`);
|
|
4210
|
+
}
|
|
4211
|
+
});
|
|
4212
|
+
}
|
|
4213
|
+
return {
|
|
4214
|
+
site: value.site,
|
|
4215
|
+
title: value.title,
|
|
4216
|
+
frequency: value.frequency,
|
|
4217
|
+
time: value.time,
|
|
4218
|
+
day: value.day,
|
|
4219
|
+
weekOfMonth: value.weekOfMonth,
|
|
4220
|
+
quarter: value.quarter,
|
|
4221
|
+
month: value.month,
|
|
4222
|
+
description: value.description,
|
|
4223
|
+
areas: value.areas,
|
|
4224
|
+
status: "active",
|
|
4225
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4226
|
+
updatedAt: "",
|
|
4227
|
+
deletedAt: ""
|
|
4228
|
+
};
|
|
4229
|
+
}
|
|
4230
|
+
|
|
4231
|
+
// src/repositories/hygiene-schedule-task.repository.ts
|
|
4232
|
+
import { ObjectId as ObjectId16 } from "mongodb";
|
|
4233
|
+
import {
|
|
4234
|
+
useAtlas as useAtlas12,
|
|
4235
|
+
InternalServerError as InternalServerError8,
|
|
4236
|
+
paginate as paginate8,
|
|
4237
|
+
BadRequestError as BadRequestError27,
|
|
4238
|
+
useCache as useCache8,
|
|
4239
|
+
logger as logger26,
|
|
4240
|
+
makeCacheKey as makeCacheKey8,
|
|
4241
|
+
NotFoundError as NotFoundError7
|
|
4242
|
+
} from "@iservice365/node-server-utils";
|
|
4243
|
+
function useScheduleTaskRepository() {
|
|
4244
|
+
const db = useAtlas12.getDb();
|
|
4245
|
+
if (!db) {
|
|
4246
|
+
throw new InternalServerError8("Unable to connect to server.");
|
|
4247
|
+
}
|
|
4248
|
+
const namespace_collection = "site.schedule-tasks";
|
|
4249
|
+
const collection = db.collection(namespace_collection);
|
|
4250
|
+
const { delNamespace, setCache, getCache } = useCache8(namespace_collection);
|
|
4251
|
+
async function createIndex() {
|
|
4252
|
+
try {
|
|
4253
|
+
await collection.createIndexes([
|
|
4254
|
+
{ key: { site: 1 } },
|
|
4255
|
+
{ key: { status: 1 } }
|
|
4256
|
+
]);
|
|
4257
|
+
} catch (error) {
|
|
4258
|
+
throw new InternalServerError8(
|
|
4259
|
+
"Failed to create index on hygiene schedule task."
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
async function createTextIndex() {
|
|
4264
|
+
try {
|
|
4265
|
+
await collection.createIndex({ title: "text", description: "text" });
|
|
4266
|
+
} catch (error) {
|
|
4267
|
+
throw new InternalServerError8(
|
|
4268
|
+
"Failed to create text index on hygiene schedule task."
|
|
4269
|
+
);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
async function createScheduleTask(value, session) {
|
|
4273
|
+
try {
|
|
4274
|
+
value = MScheduleTask(value);
|
|
4275
|
+
const res = await collection.insertOne(value, { session });
|
|
4276
|
+
delNamespace().then(() => {
|
|
4277
|
+
logger26.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4278
|
+
}).catch((err) => {
|
|
4279
|
+
logger26.error(
|
|
4280
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4281
|
+
err
|
|
4282
|
+
);
|
|
4283
|
+
});
|
|
4284
|
+
return res.insertedId;
|
|
4285
|
+
} catch (error) {
|
|
4286
|
+
throw error;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
async function getScheduleTasks({
|
|
4290
|
+
page = 1,
|
|
4291
|
+
limit = 10,
|
|
4292
|
+
search = "",
|
|
4293
|
+
site
|
|
4294
|
+
}) {
|
|
4295
|
+
page = page > 0 ? page - 1 : 0;
|
|
4296
|
+
const query = {
|
|
4297
|
+
status: { $ne: "deleted" }
|
|
4298
|
+
};
|
|
4299
|
+
const cacheOptions = {
|
|
4300
|
+
page,
|
|
4301
|
+
limit
|
|
4302
|
+
};
|
|
4303
|
+
try {
|
|
4304
|
+
site = new ObjectId16(site);
|
|
4305
|
+
query.site = site;
|
|
4306
|
+
cacheOptions.site = site.toString();
|
|
4307
|
+
} catch (error) {
|
|
4308
|
+
throw new BadRequestError27("Invalid site ID format.");
|
|
4309
|
+
}
|
|
4310
|
+
if (search) {
|
|
4311
|
+
query.$or = [{ name: { $regex: search, $options: "i" } }];
|
|
4312
|
+
cacheOptions.search = search;
|
|
4313
|
+
}
|
|
4314
|
+
const cacheKey = makeCacheKey8(namespace_collection, cacheOptions);
|
|
4315
|
+
const cachedData = await getCache(cacheKey);
|
|
4316
|
+
if (cachedData) {
|
|
4317
|
+
logger26.info(`Cache hit for key: ${cacheKey}`);
|
|
4318
|
+
return cachedData;
|
|
4319
|
+
}
|
|
4320
|
+
try {
|
|
4321
|
+
const items = await collection.aggregate([
|
|
4322
|
+
{ $match: query },
|
|
4323
|
+
{
|
|
4324
|
+
$project: {
|
|
4325
|
+
title: 1,
|
|
4326
|
+
areas: 1,
|
|
4327
|
+
status: 1
|
|
4328
|
+
}
|
|
4329
|
+
},
|
|
4330
|
+
{ $sort: { _id: -1 } },
|
|
4331
|
+
{ $skip: page * limit },
|
|
4332
|
+
{ $limit: limit }
|
|
4333
|
+
]).toArray();
|
|
4334
|
+
const length = await collection.countDocuments(query);
|
|
4335
|
+
const data = paginate8(items, page, limit, length);
|
|
4336
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4337
|
+
logger26.info(`Cache set for key: ${cacheKey}`);
|
|
4338
|
+
}).catch((err) => {
|
|
4339
|
+
logger26.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4340
|
+
});
|
|
4341
|
+
return data;
|
|
4342
|
+
} catch (error) {
|
|
4343
|
+
throw error;
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
async function getScheduleTaskById(_id, session) {
|
|
4347
|
+
try {
|
|
4348
|
+
_id = new ObjectId16(_id);
|
|
4349
|
+
} catch (error) {
|
|
4350
|
+
throw new BadRequestError27("Invalid schedule task ID format.");
|
|
4351
|
+
}
|
|
4352
|
+
const query = {
|
|
4353
|
+
_id,
|
|
4354
|
+
status: { $ne: "deleted" }
|
|
4355
|
+
};
|
|
4356
|
+
const cacheKey = makeCacheKey8(namespace_collection, {
|
|
4357
|
+
_id: _id.toString()
|
|
4358
|
+
});
|
|
4359
|
+
if (!session) {
|
|
4360
|
+
const cachedData = await getCache(cacheKey);
|
|
4361
|
+
if (cachedData) {
|
|
4362
|
+
logger26.info(`Cache hit for key: ${cacheKey}`);
|
|
4363
|
+
return cachedData;
|
|
4364
|
+
}
|
|
4365
|
+
} else {
|
|
4366
|
+
logger26.info(`Skipping cache during transaction for key: ${cacheKey}`);
|
|
4367
|
+
}
|
|
4368
|
+
try {
|
|
4369
|
+
const data = await collection.aggregate([
|
|
4370
|
+
{ $match: query },
|
|
4371
|
+
{
|
|
4372
|
+
$project: {
|
|
4373
|
+
title: 1,
|
|
4374
|
+
frequency: 1,
|
|
4375
|
+
time: 1,
|
|
4376
|
+
day: 1,
|
|
4377
|
+
weekOfMonth: 1,
|
|
4378
|
+
quarter: 1,
|
|
4379
|
+
month: 1,
|
|
4380
|
+
description: 1,
|
|
4381
|
+
areas: 1,
|
|
4382
|
+
status: 1,
|
|
4383
|
+
createdAt: 1
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
]).toArray();
|
|
4387
|
+
if (!data || data.length === 0) {
|
|
4388
|
+
throw new NotFoundError7("Schedule task not found.");
|
|
4389
|
+
}
|
|
4390
|
+
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
4391
|
+
logger26.info(`Cache set for key: ${cacheKey}`);
|
|
4392
|
+
}).catch((err) => {
|
|
4393
|
+
logger26.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
4394
|
+
});
|
|
4395
|
+
return data[0];
|
|
4396
|
+
} catch (error) {
|
|
4397
|
+
throw error;
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
async function updateScheduleTask(_id, value, session) {
|
|
4401
|
+
try {
|
|
4402
|
+
_id = new ObjectId16(_id);
|
|
4403
|
+
} catch (error) {
|
|
4404
|
+
throw new BadRequestError27("Invalid schedule task ID format.");
|
|
4405
|
+
}
|
|
4406
|
+
if (value.areas && Array.isArray(value.areas)) {
|
|
4407
|
+
value.areas = value.areas.map((area) => {
|
|
4408
|
+
try {
|
|
4409
|
+
return {
|
|
4410
|
+
name: area.name,
|
|
4411
|
+
value: new ObjectId16(area.value.toString())
|
|
4412
|
+
};
|
|
4413
|
+
} catch (error) {
|
|
4414
|
+
throw new BadRequestError27(`Invalid area value format: ${area.name}`);
|
|
4415
|
+
}
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
try {
|
|
4419
|
+
const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
|
|
4420
|
+
const res = await collection.updateOne(
|
|
4421
|
+
{ _id },
|
|
4422
|
+
{ $set: updateValue },
|
|
4423
|
+
{ session }
|
|
4424
|
+
);
|
|
4425
|
+
if (res.modifiedCount === 0) {
|
|
4426
|
+
throw new InternalServerError8(
|
|
4427
|
+
"Unable to update hygiene schedule task."
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
delNamespace().then(() => {
|
|
4431
|
+
logger26.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
4432
|
+
}).catch((err) => {
|
|
4433
|
+
logger26.error(
|
|
4434
|
+
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
4435
|
+
err
|
|
4436
|
+
);
|
|
4437
|
+
});
|
|
4438
|
+
return res.modifiedCount;
|
|
4439
|
+
} catch (error) {
|
|
4440
|
+
throw error;
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
return {
|
|
4444
|
+
createIndex,
|
|
4445
|
+
createTextIndex,
|
|
4446
|
+
createScheduleTask,
|
|
4447
|
+
getScheduleTasks,
|
|
4448
|
+
getScheduleTaskById,
|
|
4449
|
+
updateScheduleTask
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4453
|
+
// src/controllers/hygiene-schedule-task.controller.ts
|
|
4454
|
+
import { BadRequestError as BadRequestError28, logger as logger27 } from "@iservice365/node-server-utils";
|
|
4455
|
+
import Joi16 from "joi";
|
|
4456
|
+
function useScheduleTaskController() {
|
|
4457
|
+
const {
|
|
4458
|
+
createScheduleTask: _createScheduleTask,
|
|
4459
|
+
getScheduleTasks: _getScheduleTasks,
|
|
4460
|
+
getScheduleTaskById: _getScheduleTaskById,
|
|
4461
|
+
updateScheduleTask: _updateScheduleTask
|
|
4462
|
+
} = useScheduleTaskRepository();
|
|
4463
|
+
async function createScheduleTask(req, res, next) {
|
|
4464
|
+
const payload = { ...req.body, ...req.params };
|
|
4465
|
+
const { error } = scheduleTaskSchema.validate(payload);
|
|
4466
|
+
if (error) {
|
|
4467
|
+
logger27.log({ level: "error", message: error.message });
|
|
4468
|
+
next(new BadRequestError28(error.message));
|
|
4469
|
+
return;
|
|
4470
|
+
}
|
|
4471
|
+
try {
|
|
4472
|
+
const id = await _createScheduleTask(payload);
|
|
4473
|
+
res.status(201).json({ message: "Schedule task created successfully.", id });
|
|
4474
|
+
return;
|
|
4475
|
+
} catch (error2) {
|
|
4476
|
+
logger27.log({ level: "error", message: error2.message });
|
|
4477
|
+
next(error2);
|
|
4478
|
+
return;
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
async function getScheduleTasks(req, res, next) {
|
|
4482
|
+
const query = { ...req.query, ...req.params };
|
|
4483
|
+
const validation = Joi16.object({
|
|
4484
|
+
page: Joi16.number().min(1).optional().allow("", null),
|
|
4485
|
+
limit: Joi16.number().min(1).optional().allow("", null),
|
|
4486
|
+
search: Joi16.string().optional().allow("", null),
|
|
4487
|
+
site: Joi16.string().hex().required()
|
|
4488
|
+
});
|
|
4489
|
+
const { error } = validation.validate(query);
|
|
4490
|
+
if (error) {
|
|
4491
|
+
logger27.log({ level: "error", message: error.message });
|
|
4492
|
+
next(new BadRequestError28(error.message));
|
|
4493
|
+
return;
|
|
4494
|
+
}
|
|
4495
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
4496
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
4497
|
+
const search = req.query.search ?? "";
|
|
4498
|
+
const site = req.params.site ?? "";
|
|
4499
|
+
try {
|
|
4500
|
+
const data = await _getScheduleTasks({
|
|
4501
|
+
page,
|
|
4502
|
+
limit,
|
|
4503
|
+
search,
|
|
4504
|
+
site
|
|
4505
|
+
});
|
|
4506
|
+
res.json(data);
|
|
4507
|
+
return;
|
|
4508
|
+
} catch (error2) {
|
|
4509
|
+
logger27.log({ level: "error", message: error2.message });
|
|
4510
|
+
next(error2);
|
|
4511
|
+
return;
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
async function getScheduleTaskById(req, res, next) {
|
|
4515
|
+
const validation = Joi16.string().hex().required();
|
|
4516
|
+
const _id = req.params.id;
|
|
4517
|
+
const { error } = validation.validate(_id);
|
|
4518
|
+
if (error) {
|
|
4519
|
+
logger27.log({ level: "error", message: error.message });
|
|
4520
|
+
next(new BadRequestError28(error.message));
|
|
4521
|
+
return;
|
|
4522
|
+
}
|
|
4523
|
+
try {
|
|
4524
|
+
const data = await _getScheduleTaskById(_id);
|
|
4525
|
+
res.json(data);
|
|
4526
|
+
return;
|
|
4527
|
+
} catch (error2) {
|
|
4528
|
+
logger27.log({ level: "error", message: error2.message });
|
|
4529
|
+
next(error2);
|
|
4530
|
+
return;
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
async function updateScheduleTask(req, res, next) {
|
|
4534
|
+
const payload = { id: req.params.id, ...req.body };
|
|
4535
|
+
const validation = Joi16.object({
|
|
4536
|
+
id: Joi16.string().hex().required(),
|
|
4537
|
+
title: Joi16.string().optional().allow("", null),
|
|
4538
|
+
frequency: Joi16.string().valid(...allowedFrequency).optional().allow("", null),
|
|
4539
|
+
time: Joi16.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
|
|
4540
|
+
day: Joi16.string().valid(...allowedDays).optional().allow("", null),
|
|
4541
|
+
weekOfMonth: Joi16.string().valid(...allowedWeekOfMonth).optional().allow("", null),
|
|
4542
|
+
quarter: Joi16.string().valid(...allowedQuarter).optional().allow("", null),
|
|
4543
|
+
month: Joi16.string().valid(...allowedMonths).optional().allow("", null),
|
|
4544
|
+
description: Joi16.string().optional().allow("", null),
|
|
4545
|
+
areas: Joi16.array().min(1).items(
|
|
4546
|
+
Joi16.object({
|
|
4547
|
+
name: Joi16.string().required(),
|
|
4548
|
+
value: Joi16.any().required()
|
|
4549
|
+
})
|
|
4550
|
+
).optional()
|
|
4551
|
+
});
|
|
4552
|
+
const { error } = validation.validate(payload);
|
|
4553
|
+
if (error) {
|
|
4554
|
+
logger27.log({ level: "error", message: error.message });
|
|
4555
|
+
next(new BadRequestError28(error.message));
|
|
4556
|
+
return;
|
|
4557
|
+
}
|
|
4558
|
+
try {
|
|
4559
|
+
const { id, ...value } = payload;
|
|
4560
|
+
await _updateScheduleTask(id, value);
|
|
4561
|
+
res.json({ message: "Schedule task updated successfully." });
|
|
4562
|
+
return;
|
|
4563
|
+
} catch (error2) {
|
|
4564
|
+
logger27.log({ level: "error", message: error2.message });
|
|
4565
|
+
next(error2);
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
return {
|
|
4570
|
+
createScheduleTask,
|
|
4571
|
+
getScheduleTasks,
|
|
4572
|
+
getScheduleTaskById,
|
|
4573
|
+
updateScheduleTask
|
|
3855
4574
|
};
|
|
3856
4575
|
}
|
|
3857
4576
|
export {
|
|
@@ -3859,17 +4578,24 @@ export {
|
|
|
3859
4578
|
MAreaChecklist,
|
|
3860
4579
|
MParentChecklist,
|
|
3861
4580
|
MRequestItem,
|
|
4581
|
+
MScheduleTask,
|
|
3862
4582
|
MStock,
|
|
3863
4583
|
MSupply,
|
|
3864
4584
|
MUnit,
|
|
3865
4585
|
allowedChecklistStatus,
|
|
4586
|
+
allowedDays,
|
|
4587
|
+
allowedFrequency,
|
|
4588
|
+
allowedMonths,
|
|
4589
|
+
allowedQuarter,
|
|
3866
4590
|
allowedRequestItemStatus,
|
|
3867
4591
|
allowedStatus,
|
|
3868
4592
|
allowedTypes,
|
|
4593
|
+
allowedWeekOfMonth,
|
|
3869
4594
|
areaChecklistSchema,
|
|
3870
4595
|
areaSchema,
|
|
3871
4596
|
parentChecklistSchema,
|
|
3872
4597
|
requestItemSchema,
|
|
4598
|
+
scheduleTaskSchema,
|
|
3873
4599
|
stockSchema,
|
|
3874
4600
|
supplySchema,
|
|
3875
4601
|
unitSchema,
|
|
@@ -3884,6 +4610,8 @@ export {
|
|
|
3884
4610
|
useRequestItemController,
|
|
3885
4611
|
useRequestItemRepository,
|
|
3886
4612
|
useRequestItemService,
|
|
4613
|
+
useScheduleTaskController,
|
|
4614
|
+
useScheduleTaskRepository,
|
|
3887
4615
|
useStockController,
|
|
3888
4616
|
useStockRepository,
|
|
3889
4617
|
useStockService,
|