@7365admin1/module-hygiene 4.30.0 → 4.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +78 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/require-photo-on-completion.test.mjs +161 -117
package/dist/index.mjs
CHANGED
|
@@ -3253,6 +3253,39 @@ import {
|
|
|
3253
3253
|
useAtlas as useAtlas6,
|
|
3254
3254
|
useCache as useCache5
|
|
3255
3255
|
} from "@7365admin1/node-server-utils";
|
|
3256
|
+
|
|
3257
|
+
// src/utils/completion-photo.util.ts
|
|
3258
|
+
var COMPLETION_PHOTO_MESSAGE = "A photo is required to complete this task. Take or upload at least one photo before marking it complete.";
|
|
3259
|
+
function readCompletion(units, unitId) {
|
|
3260
|
+
if (units.length === 0)
|
|
3261
|
+
return { completesSet: false, hasEvidence: false };
|
|
3262
|
+
const others = units.filter((unit) => String(unit.unit) !== String(unitId));
|
|
3263
|
+
return {
|
|
3264
|
+
completesSet: others.every(
|
|
3265
|
+
(unit) => unit.approve === true || unit.reject === true
|
|
3266
|
+
),
|
|
3267
|
+
hasEvidence: others.some(
|
|
3268
|
+
(unit) => Array.isArray(unit.attachment) && unit.attachment.length > 0
|
|
3269
|
+
)
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
function requiresCompletionPhoto({
|
|
3273
|
+
decision,
|
|
3274
|
+
attachment,
|
|
3275
|
+
completesSet,
|
|
3276
|
+
hasEvidence
|
|
3277
|
+
}) {
|
|
3278
|
+
if (decision !== "approve")
|
|
3279
|
+
return false;
|
|
3280
|
+
if (!completesSet)
|
|
3281
|
+
return false;
|
|
3282
|
+
if (hasEvidence)
|
|
3283
|
+
return false;
|
|
3284
|
+
const carriesEvidence = Array.isArray(attachment) && attachment.some((id) => typeof id === "string" && id.trim().length > 0);
|
|
3285
|
+
return !carriesEvidence;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// src/repositories/hygiene-area-checklist.repository.ts
|
|
3256
3289
|
function useAreaChecklistRepo() {
|
|
3257
3290
|
const db = useAtlas6.getDb();
|
|
3258
3291
|
if (!db) {
|
|
@@ -3324,7 +3357,11 @@ function useAreaChecklistRepo() {
|
|
|
3324
3357
|
const parentChecklistId = new ObjectId9(value.schedule);
|
|
3325
3358
|
const areaId = new ObjectId9(value.area);
|
|
3326
3359
|
const existingChecklist = await collection.findOne(
|
|
3327
|
-
{
|
|
3360
|
+
{
|
|
3361
|
+
schedule: parentChecklistId,
|
|
3362
|
+
area: areaId,
|
|
3363
|
+
serviceType: value.serviceType
|
|
3364
|
+
},
|
|
3328
3365
|
{ session }
|
|
3329
3366
|
);
|
|
3330
3367
|
if (existingChecklist) {
|
|
@@ -4080,6 +4117,21 @@ function useAreaChecklistRepo() {
|
|
|
4080
4117
|
throw error;
|
|
4081
4118
|
}
|
|
4082
4119
|
}
|
|
4120
|
+
async function readSetCompletionState(_id, set, unitId) {
|
|
4121
|
+
try {
|
|
4122
|
+
_id = new ObjectId9(_id);
|
|
4123
|
+
} catch (error) {
|
|
4124
|
+
throw new BadRequestError15("Invalid area checklist ID format.");
|
|
4125
|
+
}
|
|
4126
|
+
const doc = await collection.findOne(
|
|
4127
|
+
{ _id, "checklist.set": set },
|
|
4128
|
+
{ projection: { checklist: 1 } }
|
|
4129
|
+
);
|
|
4130
|
+
const group = (doc?.checklist ?? []).find(
|
|
4131
|
+
(entry) => entry.set === set
|
|
4132
|
+
);
|
|
4133
|
+
return readCompletion(group?.units ?? [], unitId);
|
|
4134
|
+
}
|
|
4083
4135
|
async function updateAreaChecklist(_id, value, session) {
|
|
4084
4136
|
try {
|
|
4085
4137
|
_id = new ObjectId9(_id);
|
|
@@ -4364,6 +4416,7 @@ function useAreaChecklistRepo() {
|
|
|
4364
4416
|
updateAreaChecklist,
|
|
4365
4417
|
updateAreaChecklistStatus,
|
|
4366
4418
|
updateAreaChecklistUnits,
|
|
4419
|
+
readSetCompletionState,
|
|
4367
4420
|
getMaxSetNumberForArea,
|
|
4368
4421
|
closeExpiredAreaChecklists,
|
|
4369
4422
|
pushScheduleTaskSets,
|
|
@@ -4839,7 +4892,8 @@ function useAreaChecklistController() {
|
|
|
4839
4892
|
getAllAreaChecklist: _getAllAreaChecklist,
|
|
4840
4893
|
getAreaChecklistHistory: _getAreaChecklistHistory,
|
|
4841
4894
|
getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
|
|
4842
|
-
getAreaChecklistUnits: _getAreaChecklistUnits
|
|
4895
|
+
getAreaChecklistUnits: _getAreaChecklistUnits,
|
|
4896
|
+
readSetCompletionState: _readSetCompletionState
|
|
4843
4897
|
} = useAreaChecklistRepo();
|
|
4844
4898
|
const {
|
|
4845
4899
|
createAreaChecklist: _createAreaChecklist,
|
|
@@ -5044,36 +5098,11 @@ function useAreaChecklistController() {
|
|
|
5044
5098
|
reject: Joi9.boolean().optional(),
|
|
5045
5099
|
remarks: Joi9.string().optional().allow("", null),
|
|
5046
5100
|
/*
|
|
5047
|
-
*
|
|
5048
|
-
*
|
|
5049
|
-
*
|
|
5050
|
-
* to take photo before completing the task ... all the services that has
|
|
5051
|
-
* this module must be the same process."*
|
|
5052
|
-
*
|
|
5053
|
-
* This endpoint is the one place every service and every client meets:
|
|
5054
|
-
* the five field apps (M&E, Cleaning, Landscape, Pest, Pool) all PATCH
|
|
5055
|
-
* `/api/hygiene-area-checklist/...` with a `serviceType`, and the web
|
|
5056
|
-
* checklist (`layer-common ManageChecklistMain`) posts here too. Requiring
|
|
5057
|
-
* it here is what makes "the same process" true rather than five client
|
|
5058
|
-
* guards that can drift apart — and a client guard alone is a suggestion.
|
|
5059
|
-
*
|
|
5060
|
-
* ONLY ON APPROVE. Completing a unit is the act the evidence is for.
|
|
5061
|
-
* A REJECT is the technician reporting a problem, and demanding a
|
|
5062
|
-
* photograph before somebody can report one would stop them reporting it.
|
|
5063
|
-
* `decision` carries which is which.
|
|
5064
|
-
*
|
|
5065
|
-
* At least one entry, and each must be a real id — an empty array or a
|
|
5066
|
-
* blank string would satisfy `.optional()` while carrying no evidence at
|
|
5067
|
-
* all, which is the shape a client sends when its uploader failed.
|
|
5101
|
+
* Shape only. WHETHER a photo is required depends on the set's current
|
|
5102
|
+
* state in the database, which Joi cannot see — see the check below.
|
|
5103
|
+
* Each entry must still be a real id: a blank string carries no evidence.
|
|
5068
5104
|
*/
|
|
5069
|
-
attachment: Joi9.array().items(Joi9.string().trim().min(1)).
|
|
5070
|
-
is: "approve",
|
|
5071
|
-
then: Joi9.array().min(1).required().messages({
|
|
5072
|
-
"array.min": "A photo is required to complete this task. Take or upload at least one photo before marking it complete.",
|
|
5073
|
-
"any.required": "A photo is required to complete this task. Take or upload at least one photo before marking it complete."
|
|
5074
|
-
}),
|
|
5075
|
-
otherwise: Joi9.array().optional()
|
|
5076
|
-
}),
|
|
5105
|
+
attachment: Joi9.array().items(Joi9.string().trim().min(1)).optional(),
|
|
5077
5106
|
completedBy: Joi9.string().hex().required()
|
|
5078
5107
|
});
|
|
5079
5108
|
const { error } = validation.validate(payload);
|
|
@@ -5084,7 +5113,24 @@ function useAreaChecklistController() {
|
|
|
5084
5113
|
}
|
|
5085
5114
|
try {
|
|
5086
5115
|
const { id, set, unit, decision: decision2, ...value } = payload;
|
|
5087
|
-
|
|
5116
|
+
const setNumber = parseInt(set);
|
|
5117
|
+
if (decision2 === "approve") {
|
|
5118
|
+
const { completesSet, hasEvidence } = await _readSetCompletionState(
|
|
5119
|
+
id,
|
|
5120
|
+
setNumber,
|
|
5121
|
+
unit
|
|
5122
|
+
);
|
|
5123
|
+
if (requiresCompletionPhoto({
|
|
5124
|
+
decision: decision2,
|
|
5125
|
+
attachment: value.attachment,
|
|
5126
|
+
completesSet,
|
|
5127
|
+
hasEvidence
|
|
5128
|
+
})) {
|
|
5129
|
+
next(new BadRequestError16(COMPLETION_PHOTO_MESSAGE));
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
await _updateAreaChecklistUnits(id, setNumber, unit, value);
|
|
5088
5134
|
res.json({ message: "Area checklist updated successfully." });
|
|
5089
5135
|
return;
|
|
5090
5136
|
} catch (error2) {
|