@7365admin1/module-hygiene 4.30.0 → 4.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +74 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/require-photo-on-completion.test.mjs +101 -120
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @7365admin1/module-hygiene
|
|
2
2
|
|
|
3
|
+
## 4.30.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b4ed69f: Require the completion photo only on the approve that FINISHES a set, not on
|
|
8
|
+
every unit tick. A task in this product is a set: both the web checklist and the
|
|
9
|
+
five field apps tick units one at a time and open the photographs dialog on the
|
|
10
|
+
tick that completes the set. The previous rule demanded a photo on every
|
|
11
|
+
approve, which refused every intermediate tick on both platforms.
|
|
12
|
+
|
|
3
13
|
## 4.30.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
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
|
@@ -3359,7 +3359,11 @@ function useAreaChecklistRepo() {
|
|
|
3359
3359
|
const parentChecklistId = new import_mongodb9.ObjectId(value.schedule);
|
|
3360
3360
|
const areaId = new import_mongodb9.ObjectId(value.area);
|
|
3361
3361
|
const existingChecklist = await collection.findOne(
|
|
3362
|
-
{
|
|
3362
|
+
{
|
|
3363
|
+
schedule: parentChecklistId,
|
|
3364
|
+
area: areaId,
|
|
3365
|
+
serviceType: value.serviceType
|
|
3366
|
+
},
|
|
3363
3367
|
{ session }
|
|
3364
3368
|
);
|
|
3365
3369
|
if (existingChecklist) {
|
|
@@ -4115,6 +4119,32 @@ function useAreaChecklistRepo() {
|
|
|
4115
4119
|
throw error;
|
|
4116
4120
|
}
|
|
4117
4121
|
}
|
|
4122
|
+
async function readSetCompletionState(_id, set, unitId) {
|
|
4123
|
+
try {
|
|
4124
|
+
_id = new import_mongodb9.ObjectId(_id);
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
throw new import_node_server_utils17.BadRequestError("Invalid area checklist ID format.");
|
|
4127
|
+
}
|
|
4128
|
+
const doc = await collection.findOne(
|
|
4129
|
+
{ _id, "checklist.set": set },
|
|
4130
|
+
{ projection: { checklist: 1 } }
|
|
4131
|
+
);
|
|
4132
|
+
const group = (doc?.checklist ?? []).find(
|
|
4133
|
+
(entry) => entry.set === set
|
|
4134
|
+
);
|
|
4135
|
+
const units = group?.units ?? [];
|
|
4136
|
+
if (units.length === 0)
|
|
4137
|
+
return { completesSet: false, hasEvidence: false };
|
|
4138
|
+
const others = units.filter(
|
|
4139
|
+
(unit) => String(unit.unit) !== String(unitId)
|
|
4140
|
+
);
|
|
4141
|
+
return {
|
|
4142
|
+
completesSet: others.every((unit) => unit.status === "completed"),
|
|
4143
|
+
hasEvidence: others.some(
|
|
4144
|
+
(unit) => Array.isArray(unit.attachment) && unit.attachment.length > 0
|
|
4145
|
+
)
|
|
4146
|
+
};
|
|
4147
|
+
}
|
|
4118
4148
|
async function updateAreaChecklist(_id, value, session) {
|
|
4119
4149
|
try {
|
|
4120
4150
|
_id = new import_mongodb9.ObjectId(_id);
|
|
@@ -4399,6 +4429,7 @@ function useAreaChecklistRepo() {
|
|
|
4399
4429
|
updateAreaChecklist,
|
|
4400
4430
|
updateAreaChecklistStatus,
|
|
4401
4431
|
updateAreaChecklistUnits,
|
|
4432
|
+
readSetCompletionState,
|
|
4402
4433
|
getMaxSetNumberForArea,
|
|
4403
4434
|
closeExpiredAreaChecklists,
|
|
4404
4435
|
pushScheduleTaskSets,
|
|
@@ -4650,6 +4681,24 @@ var import_joi9 = __toESM(require("joi"));
|
|
|
4650
4681
|
var import_node_server_utils20 = require("@7365admin1/node-server-utils");
|
|
4651
4682
|
var import_core11 = require("@7365admin1/core");
|
|
4652
4683
|
|
|
4684
|
+
// src/utils/completion-photo.util.ts
|
|
4685
|
+
var COMPLETION_PHOTO_MESSAGE = "A photo is required to complete this task. Take or upload at least one photo before marking it complete.";
|
|
4686
|
+
function requiresCompletionPhoto({
|
|
4687
|
+
decision,
|
|
4688
|
+
attachment,
|
|
4689
|
+
completesSet,
|
|
4690
|
+
hasEvidence
|
|
4691
|
+
}) {
|
|
4692
|
+
if (decision !== "approve")
|
|
4693
|
+
return false;
|
|
4694
|
+
if (!completesSet)
|
|
4695
|
+
return false;
|
|
4696
|
+
if (hasEvidence)
|
|
4697
|
+
return false;
|
|
4698
|
+
const carriesEvidence = Array.isArray(attachment) && attachment.some((id) => typeof id === "string" && id.trim().length > 0);
|
|
4699
|
+
return !carriesEvidence;
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4653
4702
|
// src/services/hygiene-checklist-pdf.service.ts
|
|
4654
4703
|
var import_puppeteer = require("puppeteer");
|
|
4655
4704
|
var import_node_server_utils19 = require("@7365admin1/node-server-utils");
|
|
@@ -4870,7 +4919,8 @@ function useAreaChecklistController() {
|
|
|
4870
4919
|
getAllAreaChecklist: _getAllAreaChecklist,
|
|
4871
4920
|
getAreaChecklistHistory: _getAreaChecklistHistory,
|
|
4872
4921
|
getAreaChecklistHistoryDetails: _getAreaChecklistHistoryDetails,
|
|
4873
|
-
getAreaChecklistUnits: _getAreaChecklistUnits
|
|
4922
|
+
getAreaChecklistUnits: _getAreaChecklistUnits,
|
|
4923
|
+
readSetCompletionState: _readSetCompletionState
|
|
4874
4924
|
} = useAreaChecklistRepo();
|
|
4875
4925
|
const {
|
|
4876
4926
|
createAreaChecklist: _createAreaChecklist,
|
|
@@ -5075,36 +5125,11 @@ function useAreaChecklistController() {
|
|
|
5075
5125
|
reject: import_joi9.default.boolean().optional(),
|
|
5076
5126
|
remarks: import_joi9.default.string().optional().allow("", null),
|
|
5077
5127
|
/*
|
|
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.
|
|
5128
|
+
* Shape only. WHETHER a photo is required depends on the set's current
|
|
5129
|
+
* state in the database, which Joi cannot see — see the check below.
|
|
5130
|
+
* Each entry must still be a real id: a blank string carries no evidence.
|
|
5099
5131
|
*/
|
|
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
|
-
}),
|
|
5132
|
+
attachment: import_joi9.default.array().items(import_joi9.default.string().trim().min(1)).optional(),
|
|
5108
5133
|
completedBy: import_joi9.default.string().hex().required()
|
|
5109
5134
|
});
|
|
5110
5135
|
const { error } = validation.validate(payload);
|
|
@@ -5115,7 +5140,24 @@ function useAreaChecklistController() {
|
|
|
5115
5140
|
}
|
|
5116
5141
|
try {
|
|
5117
5142
|
const { id, set, unit, decision: decision2, ...value } = payload;
|
|
5118
|
-
|
|
5143
|
+
const setNumber = parseInt(set);
|
|
5144
|
+
if (decision2 === "approve") {
|
|
5145
|
+
const { completesSet, hasEvidence } = await _readSetCompletionState(
|
|
5146
|
+
id,
|
|
5147
|
+
setNumber,
|
|
5148
|
+
unit
|
|
5149
|
+
);
|
|
5150
|
+
if (requiresCompletionPhoto({
|
|
5151
|
+
decision: decision2,
|
|
5152
|
+
attachment: value.attachment,
|
|
5153
|
+
completesSet,
|
|
5154
|
+
hasEvidence
|
|
5155
|
+
})) {
|
|
5156
|
+
next(new import_node_server_utils20.BadRequestError(COMPLETION_PHOTO_MESSAGE));
|
|
5157
|
+
return;
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
await _updateAreaChecklistUnits(id, setNumber, unit, value);
|
|
5119
5161
|
res.json({ message: "Area checklist updated successfully." });
|
|
5120
5162
|
return;
|
|
5121
5163
|
} catch (error2) {
|