@plusscommunities/pluss-maintenance-aws 2.1.45 → 2.1.46
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/createJob.js +201 -114
- package/createJobType.js +49 -49
- package/db/maintenance/addMaintenanceJob.js +100 -90
- package/db/maintenance/editMaintenanceJob.js +22 -12
- package/db/maintenance/getJobEmail.js +22 -22
- package/deleteJob.js +58 -58
- package/deleteJobType.js +40 -40
- package/editJob.js +126 -65
- package/editJobStatus.js +57 -57
- package/editJobType.js +61 -61
- package/editNote.js +117 -117
- package/feature.config.js +279 -270
- package/getData.js +48 -41
- package/getJob.js +4 -4
- package/getJobType.js +35 -35
- package/getJobTypes.js +66 -66
- package/getJobs.js +48 -48
- package/integration/IntegrationStrategy.js +52 -52
- package/integration/archibus/ArchibusStrategy.js +42 -28
- package/integration/index.js +19 -19
- package/jobChanged.js +88 -89
- package/jobTypesChanged.js +52 -39
- package/package.json +3 -3
- package/requests/assignRequest.js +81 -81
- package/requests/getAssignees.js +27 -27
- package/requests/getExternalSync.js +100 -100
- package/requests/getPropertyRequests.js +89 -0
- package/requests/getRequest.js +77 -77
- package/requests/helper/hasRequestPermission.js +12 -12
- package/requests/helper/isValidAssignee.js +10 -10
- package/requests/retrySync.js +90 -90
- package/requests/setExternalJobId.js +113 -113
- package/requests/updatePriority.js +33 -33
- package/scheduleJobImport.js +63 -63
- package/sendJobEmail.js +105 -104
- package/updateData.js +34 -34
- package/values.config.a.js +26 -25
- package/values.config.default.js +30 -28
- package/values.config.enquiry.js +222 -221
- package/values.config.feedback.js +194 -193
- package/values.config.food.js +29 -28
- package/values.config.forms.js +29 -28
- package/values.config.js +30 -28
- package/watchJobs.js +154 -154
package/createJob.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _ = require("lodash");
|
|
2
2
|
const config = require("./config.json");
|
|
3
3
|
const { init } = require("@plusscommunities/pluss-core-aws/config");
|
|
4
|
-
const { getBody } = require("@plusscommunities/pluss-core-aws/helper");
|
|
4
|
+
const { getBody, log } = require("@plusscommunities/pluss-core-aws/helper");
|
|
5
5
|
const generateJsonResponse = require("@plusscommunities/pluss-core-aws/helper/generateJsonResponse");
|
|
6
6
|
const addMaintenanceJob = require("./db/maintenance/addMaintenanceJob");
|
|
7
7
|
const validateSiteAccess = require("@plusscommunities/pluss-core-aws/helper/auth/validateSiteAccess");
|
|
@@ -11,126 +11,213 @@ const getUserPreview = require("@plusscommunities/pluss-core-aws/helper/getUserP
|
|
|
11
11
|
const publishActivity = require("@plusscommunities/pluss-core-aws/db/activity/publishActivity");
|
|
12
12
|
const sendJobEmail = require("./sendJobEmail");
|
|
13
13
|
const logAnalyticsActivity = require("@plusscommunities/pluss-core-aws/db/analytics/logAnalyticsActivity");
|
|
14
|
+
const {
|
|
15
|
+
PropertyRepository,
|
|
16
|
+
} = require("@plusscommunities/pluss-core-aws/db/property/PropertyRepository");
|
|
14
17
|
const { values } = require("./values.config");
|
|
15
18
|
|
|
19
|
+
const properties = new PropertyRepository();
|
|
20
|
+
|
|
16
21
|
module.exports.createJob = (event, context, callback) => {
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
// Avoids waiting on the pooled DB connection to close before returning.
|
|
23
|
+
context.callbackWaitsForEmptyEventLoop = false;
|
|
24
|
+
init(config);
|
|
25
|
+
const data = getBody(event);
|
|
26
|
+
|
|
27
|
+
validateSiteAccess(event, data.location)
|
|
28
|
+
.then(async (authorised) => {
|
|
29
|
+
if (!authorised) {
|
|
30
|
+
return callback(
|
|
31
|
+
null,
|
|
32
|
+
generateJsonResponse(403, { fail: true, error: "not authorised" }),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
// With linking on, location is resolved later; without it, room is required now.
|
|
36
|
+
const hasLocation = values.linkToProperty || !_.isUndefined(data.room);
|
|
37
|
+
if (
|
|
38
|
+
!data.userID ||
|
|
39
|
+
_.isUndefined(data.userName) ||
|
|
40
|
+
!hasLocation ||
|
|
41
|
+
_.isUndefined(data.title)
|
|
42
|
+
) {
|
|
43
|
+
log("createJob", "InsufficientInput", data.userID);
|
|
44
|
+
return callback(
|
|
45
|
+
null,
|
|
46
|
+
generateJsonResponse(422, { error: "Insufficient input" }),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
let user = await getUserPreviewFromReq(event);
|
|
50
|
+
// Capture submitter before `user` is reassigned to the subject below.
|
|
51
|
+
const submittingUserId = user.id;
|
|
52
|
+
const isOnBehalf = data.userID !== "undefined" && user.id !== data.userID;
|
|
53
|
+
if (isOnBehalf) {
|
|
54
|
+
const canRequest = await validateMasterAuth(
|
|
55
|
+
event,
|
|
56
|
+
values.permissionMaintenanceTracking,
|
|
57
|
+
data.location,
|
|
58
|
+
);
|
|
59
|
+
if (!canRequest) {
|
|
60
|
+
return callback(
|
|
61
|
+
null,
|
|
62
|
+
generateJsonResponse(422, {
|
|
63
|
+
fail: true,
|
|
64
|
+
error: "cannot request on behalf",
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
19
68
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!authorised) {
|
|
23
|
-
return callback(
|
|
24
|
-
null,
|
|
25
|
-
generateJsonResponse(403, { fail: true, error: "not authorised" })
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
if (
|
|
29
|
-
!data.userID ||
|
|
30
|
-
_.isUndefined(data.userName) ||
|
|
31
|
-
_.isUndefined(data.room) ||
|
|
32
|
-
_.isUndefined(data.title)
|
|
33
|
-
) {
|
|
34
|
-
console.error("insufficient input", data.userID);
|
|
35
|
-
return callback(
|
|
36
|
-
null,
|
|
37
|
-
generateJsonResponse(422, { error: "Insufficient input" })
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
let user = await getUserPreviewFromReq(event);
|
|
41
|
-
if (data.userID !== "undefined" && user.id !== data.userID) {
|
|
42
|
-
const canRequest = await validateMasterAuth(
|
|
43
|
-
event,
|
|
44
|
-
values.permissionMaintenanceTracking,
|
|
45
|
-
data.location
|
|
46
|
-
);
|
|
47
|
-
if (!canRequest) {
|
|
48
|
-
return callback(
|
|
49
|
-
null,
|
|
50
|
-
generateJsonResponse(422, {
|
|
51
|
-
fail: true,
|
|
52
|
-
error: "cannot request on behalf",
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
}
|
|
69
|
+
user = await getUserPreview(data.userID);
|
|
70
|
+
}
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
// Resolved server-side, never taken from the request, so a resident cannot spoof a property.
|
|
73
|
+
let resolvedProperty = null;
|
|
74
|
+
if (values.linkToProperty) {
|
|
75
|
+
// An explicit propertyId is honoured for any actor holding the tracking
|
|
76
|
+
// permission (staff can also submit as themselves); it is client input,
|
|
77
|
+
// so resolution fails closed.
|
|
78
|
+
const picksProperty =
|
|
79
|
+
!!data.propertyId &&
|
|
80
|
+
(isOnBehalf ||
|
|
81
|
+
(await validateMasterAuth(
|
|
82
|
+
event,
|
|
83
|
+
values.permissionMaintenanceTracking,
|
|
84
|
+
data.location,
|
|
85
|
+
)));
|
|
86
|
+
if (picksProperty) {
|
|
87
|
+
try {
|
|
88
|
+
resolvedProperty = await properties.byId({
|
|
89
|
+
site: data.location,
|
|
90
|
+
propertyId: data.propertyId,
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// A resolver outage is not invalid input.
|
|
94
|
+
log("createJob", "PropertyResolveError", error.message);
|
|
95
|
+
return callback(
|
|
96
|
+
null,
|
|
97
|
+
generateJsonResponse(500, { error: "Internal Error" }),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (!resolvedProperty) {
|
|
101
|
+
return callback(
|
|
102
|
+
null,
|
|
103
|
+
generateJsonResponse(422, {
|
|
104
|
+
error: "Invalid property for this site",
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
} else if (!isOnBehalf) {
|
|
109
|
+
// Self-submission: resolve the submitter's own link, failing open so a
|
|
110
|
+
// resolver outage cannot block residents from reporting.
|
|
111
|
+
try {
|
|
112
|
+
resolvedProperty = await properties.forUser({
|
|
113
|
+
site: data.location,
|
|
114
|
+
userId: submittingUserId,
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
log("createJob", "PropertyResolveError", error.message);
|
|
118
|
+
resolvedProperty = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
59
122
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
data.phone,
|
|
65
|
-
data.room,
|
|
66
|
-
data.title,
|
|
67
|
-
data.description,
|
|
68
|
-
data.type,
|
|
69
|
-
data.isHome,
|
|
70
|
-
data.homeText,
|
|
71
|
-
data.images || data.image,
|
|
72
|
-
data.location,
|
|
73
|
-
data.audience,
|
|
74
|
-
data.customFields
|
|
75
|
-
).then((id) => {
|
|
76
|
-
sendJobEmail({
|
|
77
|
-
userID: user.id,
|
|
78
|
-
userName: user.displayName,
|
|
79
|
-
phone: data.phone,
|
|
80
|
-
room: data.room,
|
|
81
|
-
title: data.title,
|
|
82
|
-
description: data.description,
|
|
83
|
-
type: data.type,
|
|
84
|
-
isHome: data.isHome,
|
|
85
|
-
homeText: data.homeText,
|
|
86
|
-
image: data.image,
|
|
87
|
-
images: data.images,
|
|
88
|
-
site: data.location,
|
|
89
|
-
audience: data.audience,
|
|
90
|
-
id,
|
|
91
|
-
customFields: data.customFields,
|
|
92
|
-
});
|
|
123
|
+
// Record and email use the same resolved name so they never disagree.
|
|
124
|
+
const propertyId = resolvedProperty ? resolvedProperty.id : undefined;
|
|
125
|
+
const propertyName = resolvedProperty ? resolvedProperty.name : undefined;
|
|
126
|
+
const room = propertyName || data.room;
|
|
93
127
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
);
|
|
128
|
+
// No property link and no room means no location.
|
|
129
|
+
if (values.linkToProperty && !room) {
|
|
130
|
+
log("createJob", "InsufficientInput", data.userID);
|
|
131
|
+
return callback(
|
|
132
|
+
null,
|
|
133
|
+
generateJsonResponse(422, { error: "Insufficient input" }),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
104
136
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
return addMaintenanceJob(
|
|
138
|
+
user.id,
|
|
139
|
+
user.displayName,
|
|
140
|
+
user,
|
|
141
|
+
data.phone,
|
|
142
|
+
room,
|
|
143
|
+
data.title,
|
|
144
|
+
data.description,
|
|
145
|
+
data.type,
|
|
146
|
+
data.isHome,
|
|
147
|
+
data.homeText,
|
|
148
|
+
data.images || data.image,
|
|
149
|
+
data.location,
|
|
150
|
+
data.audience,
|
|
151
|
+
data.customFields,
|
|
152
|
+
propertyId,
|
|
153
|
+
propertyName,
|
|
154
|
+
).then(async (id) => {
|
|
155
|
+
// Awaited (allSettled) — the frozen event loop would drop fire-and-forget
|
|
156
|
+
// work, one failure must not strand the others, and a failed side effect
|
|
157
|
+
// must not fail the create.
|
|
158
|
+
const settled = await Promise.allSettled([
|
|
159
|
+
sendJobEmail({
|
|
160
|
+
userID: user.id,
|
|
161
|
+
userName: user.displayName,
|
|
162
|
+
phone: data.phone,
|
|
163
|
+
room,
|
|
164
|
+
title: data.title,
|
|
165
|
+
description: data.description,
|
|
166
|
+
type: data.type,
|
|
167
|
+
isHome: data.isHome,
|
|
168
|
+
homeText: data.homeText,
|
|
169
|
+
image: data.image,
|
|
170
|
+
images: data.images,
|
|
171
|
+
site: data.location,
|
|
172
|
+
audience: data.audience,
|
|
173
|
+
id,
|
|
174
|
+
customFields: data.customFields,
|
|
175
|
+
}),
|
|
176
|
+
publishActivity(
|
|
177
|
+
values.activityAddMaintenanceJob,
|
|
178
|
+
data.location,
|
|
179
|
+
id,
|
|
180
|
+
user,
|
|
181
|
+
{
|
|
182
|
+
title: data.title,
|
|
183
|
+
description: data.description,
|
|
184
|
+
},
|
|
185
|
+
),
|
|
186
|
+
logAnalyticsActivity(
|
|
187
|
+
`Request`,
|
|
188
|
+
values.entityKey,
|
|
189
|
+
data.location,
|
|
190
|
+
user,
|
|
191
|
+
id,
|
|
192
|
+
),
|
|
193
|
+
]);
|
|
194
|
+
settled
|
|
195
|
+
.filter((result) => result.status === "rejected")
|
|
196
|
+
.forEach((result) => {
|
|
197
|
+
log("createJob", "SideEffectError", result.reason);
|
|
198
|
+
});
|
|
112
199
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
200
|
+
return callback(
|
|
201
|
+
null,
|
|
202
|
+
generateJsonResponse(200, {
|
|
203
|
+
success: true,
|
|
204
|
+
id,
|
|
205
|
+
searchResult: id,
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
})
|
|
210
|
+
.catch((error) => {
|
|
211
|
+
console.error(
|
|
212
|
+
"Failed to save maintenance node --> email successful",
|
|
213
|
+
data.userID,
|
|
214
|
+
);
|
|
215
|
+
console.log(error);
|
|
216
|
+
return callback(
|
|
217
|
+
null,
|
|
218
|
+
generateJsonResponse(422, {
|
|
219
|
+
error,
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
});
|
|
136
223
|
};
|
package/createJobType.js
CHANGED
|
@@ -8,57 +8,57 @@ const updateRef = require("@plusscommunities/pluss-core-aws/db/common/updateRef"
|
|
|
8
8
|
const { values } = require("./values.config");
|
|
9
9
|
|
|
10
10
|
module.exports.createJobType = (event, context, callback) => {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
init(config);
|
|
12
|
+
const data = getBody(event);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
if (!data.site || !data.name || !data.email || !data.description) {
|
|
15
|
+
return callback(
|
|
16
|
+
null,
|
|
17
|
+
generateJsonResponse(422, { error: "No site, type or email attached" }),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
validateMasterAuth(event, values.permissionMaintenanceTypes).then(
|
|
22
|
+
(authorised) => {
|
|
23
|
+
if (!authorised) {
|
|
24
|
+
console.error("Authorization not valid");
|
|
25
|
+
return callback(
|
|
26
|
+
null,
|
|
27
|
+
generateJsonResponse(422, {
|
|
28
|
+
error: { message: "Admin - Create User - not authorized." },
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const jobType = {
|
|
34
|
+
id: createGuid().substring(0, 8),
|
|
35
|
+
email: data.email,
|
|
36
|
+
typeName: data.name,
|
|
37
|
+
description: data.description,
|
|
38
|
+
level: data.level,
|
|
39
|
+
site: data.site,
|
|
40
|
+
hasCustomFields: data.hasCustomFields ?? false,
|
|
41
|
+
customFields: data.customFields ?? [],
|
|
42
|
+
};
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
44
|
+
updateRef(values.tableNameJobTypes, jobType)
|
|
45
|
+
.then((result) => {
|
|
46
|
+
return callback(
|
|
47
|
+
null,
|
|
48
|
+
generateJsonResponse(200, {
|
|
49
|
+
success: true,
|
|
50
|
+
jobType: result,
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
})
|
|
54
|
+
.catch((error) => {
|
|
55
|
+
return callback(
|
|
56
|
+
null,
|
|
57
|
+
generateJsonResponse(422, {
|
|
58
|
+
error,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
64
|
};
|
|
@@ -7,100 +7,110 @@ const { getConfig } = require("@plusscommunities/pluss-core-aws/config");
|
|
|
7
7
|
const { values } = require("../../values.config");
|
|
8
8
|
|
|
9
9
|
module.exports = async (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
userID,
|
|
11
|
+
userName,
|
|
12
|
+
user,
|
|
13
|
+
phone,
|
|
14
|
+
room,
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
type,
|
|
18
|
+
isHome,
|
|
19
|
+
homeText,
|
|
20
|
+
images,
|
|
21
|
+
location,
|
|
22
|
+
audience,
|
|
23
|
+
customFields,
|
|
24
|
+
propertyId,
|
|
25
|
+
propertyName,
|
|
24
26
|
) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
userName,
|
|
28
|
-
userProfilePic: user ? user.profilePic : null,
|
|
29
|
-
phone,
|
|
30
|
-
room,
|
|
31
|
-
title,
|
|
32
|
-
description,
|
|
33
|
-
type,
|
|
34
|
-
isHome,
|
|
35
|
-
homeText,
|
|
36
|
-
images,
|
|
37
|
-
site: location,
|
|
38
|
-
audience,
|
|
39
|
-
customFields,
|
|
40
|
-
};
|
|
27
|
+
// Property name overrides room so existing readers of room keep working.
|
|
28
|
+
const resolvedRoom = propertyName || room;
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
requestToSave.history = [
|
|
59
|
-
{
|
|
60
|
-
timestamp: moment.utc().valueOf(),
|
|
61
|
-
status: requestToSave.status,
|
|
62
|
-
user,
|
|
63
|
-
},
|
|
64
|
-
];
|
|
30
|
+
const requestToSave = {
|
|
31
|
+
userID,
|
|
32
|
+
userName,
|
|
33
|
+
userProfilePic: user ? user.profilePic : null,
|
|
34
|
+
phone,
|
|
35
|
+
room: resolvedRoom,
|
|
36
|
+
title,
|
|
37
|
+
description,
|
|
38
|
+
type,
|
|
39
|
+
isHome,
|
|
40
|
+
homeText,
|
|
41
|
+
images,
|
|
42
|
+
site: location,
|
|
43
|
+
audience,
|
|
44
|
+
customFields,
|
|
45
|
+
};
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const jobNoQ = {
|
|
71
|
-
IndexName: "MaintenanceSiteJobNoIndex",
|
|
72
|
-
KeyConditionExpression: "site = :site",
|
|
73
|
-
ExpressionAttributeValues: {
|
|
74
|
-
":site": requestToSave.site,
|
|
75
|
-
},
|
|
76
|
-
Limit: 1,
|
|
77
|
-
ScanIndexForward: false,
|
|
78
|
-
};
|
|
47
|
+
// Omit propertyId when absent so the property index stays sparse (linked rows only).
|
|
48
|
+
if (propertyId) {
|
|
49
|
+
requestToSave.propertyId = propertyId;
|
|
50
|
+
}
|
|
79
51
|
|
|
80
|
-
|
|
52
|
+
if (requestToSave.images) {
|
|
53
|
+
const thumbSource = Array.isArray(requestToSave.images)
|
|
54
|
+
? requestToSave.images[0]
|
|
55
|
+
: requestToSave.images;
|
|
56
|
+
if (thumbSource) {
|
|
57
|
+
requestToSave.thumbnail = thumbSource
|
|
58
|
+
.replace("/general1400/", "/general300/")
|
|
59
|
+
.replace("/uploads1400/", "/uploads300/");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (_.isEmpty(requestToSave.audience)) {
|
|
63
|
+
requestToSave.audience = [];
|
|
64
|
+
}
|
|
65
|
+
requestToSave.status = getConfig().maintenanceInstantComplete
|
|
66
|
+
? "Completed"
|
|
67
|
+
: "Unassigned";
|
|
68
|
+
requestToSave.history = [
|
|
69
|
+
{
|
|
70
|
+
timestamp: moment.utc().valueOf(),
|
|
71
|
+
status: requestToSave.status,
|
|
72
|
+
user,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
requestToSave.jobNo =
|
|
96
|
-
!_.isEmpty(Items) && Items[0].jobId
|
|
97
|
-
? Number.parseInt(Items[0].jobId) + 1
|
|
98
|
-
: 0;
|
|
99
|
-
}
|
|
100
|
-
requestToSave.jobId = (requestToSave.jobNo || 0) + "";
|
|
101
|
-
}
|
|
76
|
+
requestToSave.id = uuid.v1();
|
|
77
|
+
requestToSave.createdTime = moment.utc().toISOString();
|
|
78
|
+
requestToSave.createdUnix = moment.utc().valueOf();
|
|
79
|
+
if (!requestToSave.jobId) {
|
|
80
|
+
const jobNoQ = {
|
|
81
|
+
IndexName: "MaintenanceSiteJobNoIndex",
|
|
82
|
+
KeyConditionExpression: "site = :site",
|
|
83
|
+
ExpressionAttributeValues: {
|
|
84
|
+
":site": requestToSave.site,
|
|
85
|
+
},
|
|
86
|
+
Limit: 1,
|
|
87
|
+
ScanIndexForward: false,
|
|
88
|
+
};
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
90
|
+
const queryRes = await indexQuery(values.tableNameMaintenance, jobNoQ);
|
|
91
|
+
|
|
92
|
+
if (!_.isEmpty(queryRes.Items)) {
|
|
93
|
+
requestToSave.jobNo = queryRes.Items[0].jobNo + 1;
|
|
94
|
+
} else {
|
|
95
|
+
const query = {
|
|
96
|
+
IndexName: "MaintenanceSiteJobIdIndex",
|
|
97
|
+
KeyConditionExpression: "site = :site",
|
|
98
|
+
ExpressionAttributeValues: {
|
|
99
|
+
":site": requestToSave.site,
|
|
100
|
+
},
|
|
101
|
+
Limit: 1,
|
|
102
|
+
ScanIndexForward: false,
|
|
103
|
+
};
|
|
104
|
+
const { Items } = await indexQuery(values.tableNameMaintenance, query);
|
|
105
|
+
requestToSave.jobNo =
|
|
106
|
+
!_.isEmpty(Items) && Items[0].jobId
|
|
107
|
+
? Number.parseInt(Items[0].jobId) + 1
|
|
108
|
+
: 0;
|
|
109
|
+
}
|
|
110
|
+
requestToSave.jobId = (requestToSave.jobNo || 0) + "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
requestToSave.priority = "Low";
|
|
114
|
+
await updateRef(values.tableNameMaintenance, requestToSave);
|
|
115
|
+
return requestToSave.id;
|
|
106
116
|
};
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
const moment = require("moment");
|
|
2
|
+
const _ = require("lodash");
|
|
2
3
|
const editRef = require("@plusscommunities/pluss-core-aws/db/common/editRef");
|
|
4
|
+
const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
|
|
5
|
+
const updateRef = require("@plusscommunities/pluss-core-aws/db/common/updateRef");
|
|
3
6
|
const { values } = require("../../values.config");
|
|
4
7
|
|
|
5
|
-
module.exports = (job) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
job.lastActivityUnix = moment.utc().valueOf();
|
|
8
|
+
module.exports = async (job) => {
|
|
9
|
+
job.lastActivity = moment.utc().toISOString();
|
|
10
|
+
job.lastActivityUnix = moment.utc().valueOf();
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const clearsProperty =
|
|
13
|
+
!_.isUndefined(job.propertyId) && job.propertyId == null;
|
|
14
|
+
if (!clearsProperty) {
|
|
15
|
+
return editRef(values.tableNameMaintenance, "id", job.id, job);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const item = await getRef(values.tableNameMaintenance, "id", job.id);
|
|
19
|
+
if (!item) {
|
|
20
|
+
throw new Error(`Maintenance job not found: ${job.id}`);
|
|
21
|
+
}
|
|
22
|
+
// GSI hash keys can't be null; drop propertyId/propertyName in the same
|
|
23
|
+
// single put as the edit so a partial failure can't leave the pair split.
|
|
24
|
+
const merged = { ...item, ...job, id: job.id };
|
|
25
|
+
delete merged.propertyId;
|
|
26
|
+
delete merged.propertyName;
|
|
27
|
+
return updateRef(values.tableNameMaintenance, merged);
|
|
18
28
|
};
|