@7365admin1/module-hygiene 4.29.1-staging.12 → 4.29.1-staging.13
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/.changeset/checklist-photo-set-finish-approve-reject.md +9 -0
- package/.changeset/photo-required-only-on-completion.md +9 -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
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/module-hygiene": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Decide which approve finishes a checklist set from approve/reject, the same rule
|
|
6
|
+
the units list uses, instead of the unit `status` field. Units at rest carry
|
|
7
|
+
"open" or "closed", so the old check never saw a set as finished: a set holding a
|
|
8
|
+
rejected unit could not be completed, and the finishing tick was not recognised.
|
|
9
|
+
No change to when a photo is required, and no schema or data change.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/module-hygiene": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Require the completion photo only on the approve that FINISHES a set, not on
|
|
6
|
+
every unit tick. A task in this product is a set: both the web checklist and the
|
|
7
|
+
five field apps tick units one at a time and open the photographs dialog on the
|
|
8
|
+
tick that completes the set. The previous rule demanded a photo on every
|
|
9
|
+
approve, which refused every intermediate tick on both platforms.
|
package/dist/index.d.ts
CHANGED
|
@@ -388,6 +388,10 @@ declare function useAreaChecklistRepo(): {
|
|
|
388
388
|
updateAreaChecklist: (_id: string | ObjectId, value: TAreaChecklistUpdate, session?: ClientSession) => Promise<number>;
|
|
389
389
|
updateAreaChecklistStatus: (_id: string | ObjectId, status: (typeof allowedStatus)[number], session?: ClientSession) => Promise<number>;
|
|
390
390
|
updateAreaChecklistUnits: (_id: string | ObjectId, set: number, unitId: string | ObjectId, value: TAreaChecklistUnitsUpdate, session?: ClientSession) => Promise<number>;
|
|
391
|
+
readSetCompletionState: (_id: string | ObjectId, set: number, unitId: string) => Promise<{
|
|
392
|
+
completesSet: boolean;
|
|
393
|
+
hasEvidence: boolean;
|
|
394
|
+
}>;
|
|
391
395
|
getMaxSetNumberForArea: (areaId: string | ObjectId, session?: ClientSession) => Promise<any>;
|
|
392
396
|
closeExpiredAreaChecklists: () => Promise<number>;
|
|
393
397
|
pushScheduleTaskSets: (scheduleId: string | ObjectId, serviceType: TAppServiceType, areaId: string | ObjectId, scheduleTaskId: string | ObjectId, newSets: TAreaChecklist[]) => Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -3288,6 +3288,39 @@ var import_core10 = require("@7365admin1/core");
|
|
|
3288
3288
|
// src/repositories/hygiene-area-checklist.repository.ts
|
|
3289
3289
|
var import_mongodb9 = require("mongodb");
|
|
3290
3290
|
var import_node_server_utils17 = require("@7365admin1/node-server-utils");
|
|
3291
|
+
|
|
3292
|
+
// src/utils/completion-photo.util.ts
|
|
3293
|
+
var COMPLETION_PHOTO_MESSAGE = "A photo is required to complete this task. Take or upload at least one photo before marking it complete.";
|
|
3294
|
+
function readCompletion(units, unitId) {
|
|
3295
|
+
if (units.length === 0)
|
|
3296
|
+
return { completesSet: false, hasEvidence: false };
|
|
3297
|
+
const others = units.filter((unit) => String(unit.unit) !== String(unitId));
|
|
3298
|
+
return {
|
|
3299
|
+
completesSet: others.every(
|
|
3300
|
+
(unit) => unit.approve === true || unit.reject === true
|
|
3301
|
+
),
|
|
3302
|
+
hasEvidence: others.some(
|
|
3303
|
+
(unit) => Array.isArray(unit.attachment) && unit.attachment.length > 0
|
|
3304
|
+
)
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function requiresCompletionPhoto({
|
|
3308
|
+
decision,
|
|
3309
|
+
attachment,
|
|
3310
|
+
completesSet,
|
|
3311
|
+
hasEvidence
|
|
3312
|
+
}) {
|
|
3313
|
+
if (decision !== "approve")
|
|
3314
|
+
return false;
|
|
3315
|
+
if (!completesSet)
|
|
3316
|
+
return false;
|
|
3317
|
+
if (hasEvidence)
|
|
3318
|
+
return false;
|
|
3319
|
+
const carriesEvidence = Array.isArray(attachment) && attachment.some((id) => typeof id === "string" && id.trim().length > 0);
|
|
3320
|
+
return !carriesEvidence;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
// src/repositories/hygiene-area-checklist.repository.ts
|
|
3291
3324
|
function useAreaChecklistRepo() {
|
|
3292
3325
|
const db = import_node_server_utils17.useAtlas.getDb();
|
|
3293
3326
|
if (!db) {
|
|
@@ -3359,7 +3392,11 @@ function useAreaChecklistRepo() {
|
|
|
3359
3392
|
const parentChecklistId = new import_mongodb9.ObjectId(value.schedule);
|
|
3360
3393
|
const areaId = new import_mongodb9.ObjectId(value.area);
|
|
3361
3394
|
const existingChecklist = await collection.findOne(
|
|
3362
|
-
{
|
|
3395
|
+
{
|
|
3396
|
+
schedule: parentChecklistId,
|
|
3397
|
+
area: areaId,
|
|
3398
|
+
serviceType: value.serviceType
|
|
3399
|
+
},
|
|
3363
3400
|
{ session }
|
|
3364
3401
|
);
|
|
3365
3402
|
if (existingChecklist) {
|
|
@@ -4115,6 +4152,21 @@ function useAreaChecklistRepo() {
|
|
|
4115
4152
|
throw error;
|
|
4116
4153
|
}
|
|
4117
4154
|
}
|
|
4155
|
+
async function readSetCompletionState(_id, set, unitId) {
|
|
4156
|
+
try {
|
|
4157
|
+
_id = new import_mongodb9.ObjectId(_id);
|
|
4158
|
+
} catch (error) {
|
|
4159
|
+
throw new import_node_server_utils17.BadRequestError("Invalid area checklist ID format.");
|
|
4160
|
+
}
|
|
4161
|
+
const doc = await collection.findOne(
|
|
4162
|
+
{ _id, "checklist.set": set },
|
|
4163
|
+
{ projection: { checklist: 1 } }
|
|
4164
|
+
);
|
|
4165
|
+
const group = (doc?.checklist ?? []).find(
|
|
4166
|
+
(entry) => entry.set === set
|
|
4167
|
+
);
|
|
4168
|
+
return readCompletion(group?.units ?? [], unitId);
|
|
4169
|
+
}
|
|
4118
4170
|
async function updateAreaChecklist(_id, value, session) {
|
|
4119
4171
|
try {
|
|
4120
4172
|
_id = new import_mongodb9.ObjectId(_id);
|
|
@@ -4399,6 +4451,7 @@ function useAreaChecklistRepo() {
|
|
|
4399
4451
|
updateAreaChecklist,
|
|
4400
4452
|
updateAreaChecklistStatus,
|
|
4401
4453
|
updateAreaChecklistUnits,
|
|
4454
|
+
readSetCompletionState,
|
|
4402
4455
|
getMaxSetNumberForArea,
|
|
4403
4456
|
closeExpiredAreaChecklists,
|
|
4404
4457
|
pushScheduleTaskSets,
|
|
@@ -4870,7 +4923,8 @@ function useAreaChecklistController() {
|
|
|
4870
4923
|
getAllAreaChecklist: _getAllAreaChecklist,
|
|
4871
4924
|
getAreaChecklistHistory: _getAreaChecklistHistory,
|
|
4872
4925
|
getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
|
|
4873
|
-
getAreaChecklistUnits: _getAreaChecklistUnits
|
|
4926
|
+
getAreaChecklistUnits: _getAreaChecklistUnits,
|
|
4927
|
+
readSetCompletionState: _readSetCompletionState
|
|
4874
4928
|
} = useAreaChecklistRepo();
|
|
4875
4929
|
const {
|
|
4876
4930
|
createAreaChecklist: _createAreaChecklist,
|
|
@@ -5075,36 +5129,11 @@ function useAreaChecklistController() {
|
|
|
5075
5129
|
reject: import_joi9.default.boolean().optional(),
|
|
5076
5130
|
remarks: import_joi9.default.string().optional().allow("", null),
|
|
5077
5131
|
/*
|
|
5078
|
-
*
|
|
5079
|
-
*
|
|
5080
|
-
*
|
|
5081
|
-
* to take photo before completing the task ... all the services that has
|
|
5082
|
-
* this module must be the same process."*
|
|
5083
|
-
*
|
|
5084
|
-
* This endpoint is the one place every service and every client meets:
|
|
5085
|
-
* the five field apps (M&E, Cleaning, Landscape, Pest, Pool) all PATCH
|
|
5086
|
-
* `/api/hygiene-area-checklist/...` with a `serviceType`, and the web
|
|
5087
|
-
* checklist (`layer-common ManageChecklistMain`) posts here too. Requiring
|
|
5088
|
-
* it here is what makes "the same process" true rather than five client
|
|
5089
|
-
* guards that can drift apart — and a client guard alone is a suggestion.
|
|
5090
|
-
*
|
|
5091
|
-
* ONLY ON APPROVE. Completing a unit is the act the evidence is for.
|
|
5092
|
-
* A REJECT is the technician reporting a problem, and demanding a
|
|
5093
|
-
* photograph before somebody can report one would stop them reporting it.
|
|
5094
|
-
* `decision` carries which is which.
|
|
5095
|
-
*
|
|
5096
|
-
* At least one entry, and each must be a real id — an empty array or a
|
|
5097
|
-
* blank string would satisfy `.optional()` while carrying no evidence at
|
|
5098
|
-
* all, which is the shape a client sends when its uploader failed.
|
|
5132
|
+
* Shape only. WHETHER a photo is required depends on the set's current
|
|
5133
|
+
* state in the database, which Joi cannot see — see the check below.
|
|
5134
|
+
* Each entry must still be a real id: a blank string carries no evidence.
|
|
5099
5135
|
*/
|
|
5100
|
-
attachment: import_joi9.default.array().items(import_joi9.default.string().trim().min(1)).
|
|
5101
|
-
is: "approve",
|
|
5102
|
-
then: import_joi9.default.array().min(1).required().messages({
|
|
5103
|
-
"array.min": "A photo is required to complete this task. Take or upload at least one photo before marking it complete.",
|
|
5104
|
-
"any.required": "A photo is required to complete this task. Take or upload at least one photo before marking it complete."
|
|
5105
|
-
}),
|
|
5106
|
-
otherwise: import_joi9.default.array().optional()
|
|
5107
|
-
}),
|
|
5136
|
+
attachment: import_joi9.default.array().items(import_joi9.default.string().trim().min(1)).optional(),
|
|
5108
5137
|
completedBy: import_joi9.default.string().hex().required()
|
|
5109
5138
|
});
|
|
5110
5139
|
const { error } = validation.validate(payload);
|
|
@@ -5115,7 +5144,24 @@ function useAreaChecklistController() {
|
|
|
5115
5144
|
}
|
|
5116
5145
|
try {
|
|
5117
5146
|
const { id, set, unit, decision: decision2, ...value } = payload;
|
|
5118
|
-
|
|
5147
|
+
const setNumber = parseInt(set);
|
|
5148
|
+
if (decision2 === "approve") {
|
|
5149
|
+
const { completesSet, hasEvidence } = await _readSetCompletionState(
|
|
5150
|
+
id,
|
|
5151
|
+
setNumber,
|
|
5152
|
+
unit
|
|
5153
|
+
);
|
|
5154
|
+
if (requiresCompletionPhoto({
|
|
5155
|
+
decision: decision2,
|
|
5156
|
+
attachment: value.attachment,
|
|
5157
|
+
completesSet,
|
|
5158
|
+
hasEvidence
|
|
5159
|
+
})) {
|
|
5160
|
+
next(new import_node_server_utils20.BadRequestError(COMPLETION_PHOTO_MESSAGE));
|
|
5161
|
+
return;
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
await _updateAreaChecklistUnits(id, setNumber, unit, value);
|
|
5119
5165
|
res.json({ message: "Area checklist updated successfully." });
|
|
5120
5166
|
return;
|
|
5121
5167
|
} catch (error2) {
|