@7365admin1/core 3.17.0 → 3.19.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 +12 -0
- package/dist/index.d.ts +20 -10
- package/dist/index.js +908 -612
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +899 -603
- package/dist/index.mjs.map +1 -1
- package/dist/public/handlebars/service-provider-create-org.hbs +123 -85
- package/dist/public/handlebars/service-provider-invite.hbs +175 -89
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12092,7 +12092,8 @@ function useVerificationService() {
|
|
|
12092
12092
|
email,
|
|
12093
12093
|
orgId,
|
|
12094
12094
|
siteId,
|
|
12095
|
-
siteName
|
|
12095
|
+
siteName,
|
|
12096
|
+
inviteType
|
|
12096
12097
|
}) {
|
|
12097
12098
|
const schema2 = Joi11.object({
|
|
12098
12099
|
email: Joi11.string().email().lowercase().required(),
|
|
@@ -12129,11 +12130,11 @@ function useVerificationService() {
|
|
|
12129
12130
|
};
|
|
12130
12131
|
try {
|
|
12131
12132
|
const org = await getOrgById(orgId);
|
|
12132
|
-
if (
|
|
12133
|
-
value.type = "service-provider-
|
|
12133
|
+
if (inviteType === "organization-invite") {
|
|
12134
|
+
value.type = "service-provider-invite" /* SERVICE_PROVIDER_INVITE */;
|
|
12134
12135
|
subject = "Service Provider Organization Invite" /* _SERVICE_PROVIDER_ORGANIZATION_INVITE */;
|
|
12135
12136
|
} else {
|
|
12136
|
-
value.type = "service-provider-
|
|
12137
|
+
value.type = "service-provider-create-org" /* SERVICE_PROVIDER_CREATE_ORG */;
|
|
12137
12138
|
subject = "Service Provider Invite" /* _SERVICE_PROVIDER_INVITE */;
|
|
12138
12139
|
}
|
|
12139
12140
|
const res = await _add(value);
|
|
@@ -14728,25 +14729,47 @@ function useVerificationController() {
|
|
|
14728
14729
|
email: Joi16.string().email().required(),
|
|
14729
14730
|
orgId: Joi16.string().hex().required(),
|
|
14730
14731
|
siteId: Joi16.string().hex().required(),
|
|
14731
|
-
siteName: Joi16.string().required()
|
|
14732
|
+
siteName: Joi16.string().required(),
|
|
14733
|
+
inviteType: Joi16.string().valid("create-org", "organization-invite").required()
|
|
14732
14734
|
});
|
|
14733
14735
|
const { error } = validation.validate(payload);
|
|
14734
14736
|
if (error) {
|
|
14735
|
-
logger22.log({
|
|
14737
|
+
logger22.log({
|
|
14738
|
+
level: "error",
|
|
14739
|
+
message: `controller - ${error.message}`
|
|
14740
|
+
});
|
|
14736
14741
|
next(new BadRequestError31(`Invalid input: ${error.message}`));
|
|
14737
14742
|
return;
|
|
14738
14743
|
}
|
|
14744
|
+
const {
|
|
14745
|
+
email,
|
|
14746
|
+
orgId,
|
|
14747
|
+
siteId,
|
|
14748
|
+
siteName,
|
|
14749
|
+
inviteType
|
|
14750
|
+
} = payload;
|
|
14739
14751
|
try {
|
|
14740
|
-
await _createServiceProviderInvite(
|
|
14752
|
+
await _createServiceProviderInvite({
|
|
14753
|
+
email,
|
|
14754
|
+
orgId,
|
|
14755
|
+
siteId,
|
|
14756
|
+
siteName,
|
|
14757
|
+
inviteType
|
|
14758
|
+
});
|
|
14741
14759
|
const cookieOptions = {
|
|
14742
14760
|
domain: DOMAIN,
|
|
14743
14761
|
secure: true,
|
|
14744
14762
|
maxAge: 4 * 60 * 60 * 1e3
|
|
14745
14763
|
};
|
|
14746
|
-
res.cookie("service-provider-email",
|
|
14764
|
+
res.cookie("service-provider-email", email, cookieOptions).json({
|
|
14765
|
+
message: "Successfully invited service provider."
|
|
14766
|
+
});
|
|
14747
14767
|
return;
|
|
14748
14768
|
} catch (error2) {
|
|
14749
|
-
logger22.log({
|
|
14769
|
+
logger22.log({
|
|
14770
|
+
level: "error",
|
|
14771
|
+
message: `controller - ${error2.message}`
|
|
14772
|
+
});
|
|
14750
14773
|
next(error2);
|
|
14751
14774
|
return;
|
|
14752
14775
|
}
|
|
@@ -15106,421 +15129,77 @@ function useFileController() {
|
|
|
15106
15129
|
|
|
15107
15130
|
// src/controllers/organization.controller.ts
|
|
15108
15131
|
import {
|
|
15109
|
-
BadRequestError as
|
|
15110
|
-
logger as
|
|
15132
|
+
BadRequestError as BadRequestError35,
|
|
15133
|
+
logger as logger26,
|
|
15111
15134
|
NotFoundError as NotFoundError11
|
|
15112
15135
|
} from "@7365admin1/node-server-utils";
|
|
15113
|
-
import
|
|
15114
|
-
function useOrgController() {
|
|
15115
|
-
const { getOrgsByMembership } = useMemberRepo();
|
|
15116
|
-
const {
|
|
15117
|
-
getByName: _getByName,
|
|
15118
|
-
getById: _getById,
|
|
15119
|
-
getByEmail: _getByEmail,
|
|
15120
|
-
getAll: _getAll,
|
|
15121
|
-
add: _add,
|
|
15122
|
-
update: _update,
|
|
15123
|
-
getOrgsByEmail: _getOrgsByEmail,
|
|
15124
|
-
getAdminOrgForResident: _getAdminOrgForResident,
|
|
15125
|
-
completeOnboardingById: _completeOnboardingById
|
|
15126
|
-
} = useOrgRepo();
|
|
15127
|
-
async function add(req, res, next) {
|
|
15128
|
-
const validation = Joi18.object({
|
|
15129
|
-
name: Joi18.string().required(),
|
|
15130
|
-
type: Joi18.string().required(),
|
|
15131
|
-
nature: Joi18.string().valid(...allowedNatures).required(),
|
|
15132
|
-
email: Joi18.string().email().optional().allow("", null),
|
|
15133
|
-
contact: Joi18.string().optional().allow("", null),
|
|
15134
|
-
terms: Joi18.string().optional().allow("", null),
|
|
15135
|
-
policies: Joi18.string().optional().allow("", null)
|
|
15136
|
-
});
|
|
15137
|
-
const { error } = validation.validate(req.body);
|
|
15138
|
-
if (error) {
|
|
15139
|
-
logger25.log({ level: "error", message: error.message });
|
|
15140
|
-
next(new BadRequestError33(error.message));
|
|
15141
|
-
return;
|
|
15142
|
-
}
|
|
15143
|
-
try {
|
|
15144
|
-
await _add(req.body);
|
|
15145
|
-
res.status(201).json({ message: "Successfully created organization." });
|
|
15146
|
-
return;
|
|
15147
|
-
} catch (error2) {
|
|
15148
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15149
|
-
next(error2);
|
|
15150
|
-
return;
|
|
15151
|
-
}
|
|
15152
|
-
}
|
|
15153
|
-
async function getAll(req, res, next) {
|
|
15154
|
-
const validation = Joi18.object({
|
|
15155
|
-
search: Joi18.string().optional().allow("", null),
|
|
15156
|
-
page: Joi18.number().integer().min(1).allow("", null).default(1),
|
|
15157
|
-
limit: Joi18.number().integer().min(1).max(100).allow("", null).default(10),
|
|
15158
|
-
nature: Joi18.string().valid(...allowedNatures).optional().allow("", null),
|
|
15159
|
-
sort: Joi18.string().optional().allow("", null)
|
|
15160
|
-
});
|
|
15161
|
-
const query = { ...req.query };
|
|
15162
|
-
const { error } = validation.validate(query);
|
|
15163
|
-
if (error) {
|
|
15164
|
-
logger25.log({ level: "error", message: error.message });
|
|
15165
|
-
next(new BadRequestError33(error.message));
|
|
15166
|
-
return;
|
|
15167
|
-
}
|
|
15168
|
-
const search = req.query.search ?? "";
|
|
15169
|
-
const page = parseInt(req.query.page ?? "1");
|
|
15170
|
-
const limit = parseInt(req.query.limit ?? "10");
|
|
15171
|
-
const nature = req.query.nature ?? "";
|
|
15172
|
-
try {
|
|
15173
|
-
const data = await _getAll({
|
|
15174
|
-
search,
|
|
15175
|
-
page,
|
|
15176
|
-
limit,
|
|
15177
|
-
nature
|
|
15178
|
-
});
|
|
15179
|
-
res.json(data);
|
|
15180
|
-
return;
|
|
15181
|
-
} catch (error2) {
|
|
15182
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15183
|
-
next(error2);
|
|
15184
|
-
return;
|
|
15185
|
-
}
|
|
15186
|
-
}
|
|
15187
|
-
async function addOnboardingOrg(req, res, next) {
|
|
15188
|
-
const validation = Joi18.object({
|
|
15189
|
-
name: Joi18.string().required(),
|
|
15190
|
-
type: Joi18.string().required(),
|
|
15191
|
-
nature: Joi18.string().valid(...allowedNatures).required(),
|
|
15192
|
-
email: Joi18.string().email().optional().allow("", null),
|
|
15193
|
-
contact: Joi18.string().optional().allow("", null)
|
|
15194
|
-
});
|
|
15195
|
-
const { error } = validation.validate(req.body);
|
|
15196
|
-
if (error) {
|
|
15197
|
-
logger25.log({ level: "error", message: error.message });
|
|
15198
|
-
next(new BadRequestError33(error.message));
|
|
15199
|
-
return;
|
|
15200
|
-
}
|
|
15201
|
-
try {
|
|
15202
|
-
const _id = await _add(req.body);
|
|
15203
|
-
const data = await _getById(_id);
|
|
15204
|
-
res.status(201).json({
|
|
15205
|
-
message: "Successfully created organization.",
|
|
15206
|
-
data
|
|
15207
|
-
});
|
|
15208
|
-
return;
|
|
15209
|
-
} catch (error2) {
|
|
15210
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15211
|
-
next(error2);
|
|
15212
|
-
return;
|
|
15213
|
-
}
|
|
15214
|
-
}
|
|
15215
|
-
async function getOrgsByUserId(req, res, next) {
|
|
15216
|
-
const validation = Joi18.object({
|
|
15217
|
-
search: Joi18.string().optional().allow("", null),
|
|
15218
|
-
page: Joi18.number().integer().min(1).allow("", null).default(1),
|
|
15219
|
-
limit: Joi18.number().integer().min(1).max(100).allow("", null).default(10),
|
|
15220
|
-
user: Joi18.string().hex().required(),
|
|
15221
|
-
type: Joi18.string().optional().allow("", null)
|
|
15222
|
-
});
|
|
15223
|
-
const query = { ...req.query };
|
|
15224
|
-
query.user = req.params.user;
|
|
15225
|
-
const { error } = validation.validate(query);
|
|
15226
|
-
if (error) {
|
|
15227
|
-
logger25.log({ level: "error", message: error.message });
|
|
15228
|
-
next(new BadRequestError33(error.message));
|
|
15229
|
-
return;
|
|
15230
|
-
}
|
|
15231
|
-
const search = req.query.search ?? "";
|
|
15232
|
-
const page = parseInt(req.query.page ?? "1");
|
|
15233
|
-
const limit = parseInt(req.query.limit ?? "10");
|
|
15234
|
-
const user = req.params.user;
|
|
15235
|
-
const type = req.query.type ?? "";
|
|
15236
|
-
try {
|
|
15237
|
-
const data = await getOrgsByMembership({
|
|
15238
|
-
search,
|
|
15239
|
-
page,
|
|
15240
|
-
limit,
|
|
15241
|
-
user,
|
|
15242
|
-
type
|
|
15243
|
-
});
|
|
15244
|
-
res.json(data);
|
|
15245
|
-
return;
|
|
15246
|
-
} catch (error2) {
|
|
15247
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15248
|
-
next(error2);
|
|
15249
|
-
return;
|
|
15250
|
-
}
|
|
15251
|
-
}
|
|
15252
|
-
async function getByName(req, res, next) {
|
|
15253
|
-
const validation = Joi18.string().required();
|
|
15254
|
-
const name = req.params.name;
|
|
15255
|
-
const { error } = validation.validate(name);
|
|
15256
|
-
if (error) {
|
|
15257
|
-
logger25.log({ level: "error", message: error.message });
|
|
15258
|
-
next(new BadRequestError33(error.message));
|
|
15259
|
-
return;
|
|
15260
|
-
}
|
|
15261
|
-
try {
|
|
15262
|
-
const data = await _getByName(name);
|
|
15263
|
-
res.json(data);
|
|
15264
|
-
return;
|
|
15265
|
-
} catch (error2) {
|
|
15266
|
-
next(error2);
|
|
15267
|
-
return;
|
|
15268
|
-
}
|
|
15269
|
-
}
|
|
15270
|
-
async function getById(req, res, next) {
|
|
15271
|
-
const validation = Joi18.string().hex().required();
|
|
15272
|
-
const _id = req.params.id;
|
|
15273
|
-
const { error } = validation.validate(_id);
|
|
15274
|
-
if (error) {
|
|
15275
|
-
logger25.log({ level: "error", message: error.message });
|
|
15276
|
-
next(new BadRequestError33(error.message));
|
|
15277
|
-
return;
|
|
15278
|
-
}
|
|
15279
|
-
try {
|
|
15280
|
-
const data = await _getById(_id);
|
|
15281
|
-
res.json(data);
|
|
15282
|
-
return;
|
|
15283
|
-
} catch (error2) {
|
|
15284
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15285
|
-
next(error2);
|
|
15286
|
-
return;
|
|
15287
|
-
}
|
|
15288
|
-
}
|
|
15289
|
-
async function getByEmail(req, res, next) {
|
|
15290
|
-
const validation = Joi18.string().required();
|
|
15291
|
-
const email = req.params.email;
|
|
15292
|
-
const { error } = validation.validate(email);
|
|
15293
|
-
if (error) {
|
|
15294
|
-
logger25.log({ level: "error", message: error.message });
|
|
15295
|
-
next(new BadRequestError33(error.message));
|
|
15296
|
-
return;
|
|
15297
|
-
}
|
|
15298
|
-
try {
|
|
15299
|
-
const data = await _getByEmail(email);
|
|
15300
|
-
if (!data) {
|
|
15301
|
-
next(new NotFoundError11("Organization not found."));
|
|
15302
|
-
return;
|
|
15303
|
-
}
|
|
15304
|
-
res.json(data);
|
|
15305
|
-
return;
|
|
15306
|
-
} catch (error2) {
|
|
15307
|
-
next(error2);
|
|
15308
|
-
return;
|
|
15309
|
-
}
|
|
15310
|
-
}
|
|
15311
|
-
async function getOrgsByEmail(req, res, next) {
|
|
15312
|
-
const validation = Joi18.object({
|
|
15313
|
-
email: Joi18.string().email().required()
|
|
15314
|
-
});
|
|
15315
|
-
const query = {
|
|
15316
|
-
email: req.params.email
|
|
15317
|
-
};
|
|
15318
|
-
const { error } = validation.validate(query);
|
|
15319
|
-
if (error) {
|
|
15320
|
-
logger25.log({ level: "error", message: error.message });
|
|
15321
|
-
next(new BadRequestError33(error.message));
|
|
15322
|
-
return;
|
|
15323
|
-
}
|
|
15324
|
-
const email = req.params.email;
|
|
15325
|
-
try {
|
|
15326
|
-
const data = await _getOrgsByEmail(email);
|
|
15327
|
-
res.json(data);
|
|
15328
|
-
return;
|
|
15329
|
-
} catch (error2) {
|
|
15330
|
-
logger25.log({ level: "error", message: error2.message });
|
|
15331
|
-
next(error2);
|
|
15332
|
-
return;
|
|
15333
|
-
}
|
|
15334
|
-
}
|
|
15335
|
-
async function update(req, res, next) {
|
|
15336
|
-
const validation = Joi18.object({
|
|
15337
|
-
name: Joi18.string().optional(),
|
|
15338
|
-
type: Joi18.string().optional(),
|
|
15339
|
-
nature: Joi18.string().valid(...allowedNatures).optional(),
|
|
15340
|
-
email: Joi18.string().email().allow("", null).optional(),
|
|
15341
|
-
contact: Joi18.string().allow("", null).optional(),
|
|
15342
|
-
terms: Joi18.string().optional().allow("", null),
|
|
15343
|
-
policies: Joi18.string().optional().allow("", null)
|
|
15344
|
-
});
|
|
15345
|
-
const { error } = validation.validate(req.body);
|
|
15346
|
-
if (error) {
|
|
15347
|
-
next(new BadRequestError33(error.message));
|
|
15348
|
-
return;
|
|
15349
|
-
}
|
|
15350
|
-
const id = req.params.id;
|
|
15351
|
-
try {
|
|
15352
|
-
await _update(id, req.body);
|
|
15353
|
-
const data = await _getById(id);
|
|
15354
|
-
res.json({
|
|
15355
|
-
message: "Organization updated successfully",
|
|
15356
|
-
data
|
|
15357
|
-
});
|
|
15358
|
-
} catch (err) {
|
|
15359
|
-
next(err);
|
|
15360
|
-
}
|
|
15361
|
-
}
|
|
15362
|
-
async function getAdminOrgForResident(_req, res, next) {
|
|
15363
|
-
try {
|
|
15364
|
-
const data = await _getAdminOrgForResident();
|
|
15365
|
-
res.status(200).json(data);
|
|
15366
|
-
} catch (error) {
|
|
15367
|
-
logger25.log({ level: "error", message: error.message });
|
|
15368
|
-
next(error);
|
|
15369
|
-
}
|
|
15370
|
-
}
|
|
15371
|
-
return {
|
|
15372
|
-
add,
|
|
15373
|
-
addOnboardingOrg,
|
|
15374
|
-
getAll,
|
|
15375
|
-
getOrgsByUserId,
|
|
15376
|
-
getByName,
|
|
15377
|
-
getById,
|
|
15378
|
-
getByEmail,
|
|
15379
|
-
update,
|
|
15380
|
-
getOrgsByEmail,
|
|
15381
|
-
getAdminOrgForResident
|
|
15382
|
-
};
|
|
15383
|
-
}
|
|
15136
|
+
import Joi20 from "joi";
|
|
15384
15137
|
|
|
15385
|
-
// src/
|
|
15386
|
-
import
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
limit: Joi19.number().integer().min(1).max(100).allow("", null).default(10),
|
|
15395
|
-
nature: Joi19.string().valid(...allowedNatures).optional().allow("", null),
|
|
15396
|
-
status: Joi19.string().trim().valid("active", "suspended", "deleted").optional().empty("").default("active")
|
|
15397
|
-
});
|
|
15398
|
-
const query = { ...req.query };
|
|
15399
|
-
const { error, value } = validation.validate(query, {
|
|
15400
|
-
convert: true,
|
|
15401
|
-
stripUnknown: true
|
|
15402
|
-
});
|
|
15403
|
-
if (error) {
|
|
15404
|
-
logger26.log({ level: "error", message: error.message });
|
|
15405
|
-
next(new BadRequestError34(error.message));
|
|
15406
|
-
return;
|
|
15407
|
-
}
|
|
15408
|
-
const search = value.search ?? "";
|
|
15409
|
-
const page = typeof value.page === "number" ? value.page : parseInt(String(value.page ?? "1"), 10);
|
|
15410
|
-
const limit = typeof value.limit === "number" ? value.limit : parseInt(String(value.limit ?? "10"), 10);
|
|
15411
|
-
const nature = value.nature ?? "";
|
|
15412
|
-
const status = value.status;
|
|
15413
|
-
try {
|
|
15414
|
-
const data = await _getAll({
|
|
15415
|
-
search,
|
|
15416
|
-
page,
|
|
15417
|
-
limit,
|
|
15418
|
-
nature,
|
|
15419
|
-
status
|
|
15420
|
-
});
|
|
15421
|
-
res.json(data);
|
|
15422
|
-
return;
|
|
15423
|
-
} catch (error2) {
|
|
15424
|
-
logger26.log({ level: "error", message: error2.message });
|
|
15425
|
-
next(error2);
|
|
15426
|
-
return;
|
|
15427
|
-
}
|
|
15428
|
-
}
|
|
15429
|
-
async function getOrganizationsWithSubscription(req, res, next) {
|
|
15430
|
-
const validation = Joi19.object({
|
|
15431
|
-
search: Joi19.string().optional().allow("", null),
|
|
15432
|
-
page: Joi19.number().integer().min(1).default(1),
|
|
15433
|
-
limit: Joi19.number().integer().min(1).max(100).default(10),
|
|
15434
|
-
status: Joi19.string().trim().valid("active", "suspended", "deleted").default("active"),
|
|
15435
|
-
type: Joi19.string().optional().allow("", null),
|
|
15436
|
-
billingCycle: Joi19.string().optional().allow("", null)
|
|
15437
|
-
});
|
|
15438
|
-
const { error, value } = validation.validate(
|
|
15439
|
-
req.query,
|
|
15440
|
-
{
|
|
15441
|
-
convert: true,
|
|
15442
|
-
stripUnknown: true
|
|
15443
|
-
}
|
|
15444
|
-
);
|
|
15445
|
-
if (error) {
|
|
15446
|
-
next(new BadRequestError34(error.message));
|
|
15447
|
-
return;
|
|
15448
|
-
}
|
|
15449
|
-
try {
|
|
15450
|
-
const data = await _getOrganizationsWithSubscription({
|
|
15451
|
-
search: value.search ?? "",
|
|
15452
|
-
page: value.page ?? 1,
|
|
15453
|
-
limit: value.limit ?? 10,
|
|
15454
|
-
status: value.status ?? "active",
|
|
15455
|
-
type: value.type ?? "",
|
|
15456
|
-
billingCycle: value.billingCycle ?? ""
|
|
15457
|
-
});
|
|
15458
|
-
res.json(data);
|
|
15459
|
-
} catch (error2) {
|
|
15460
|
-
next(error2);
|
|
15461
|
-
}
|
|
15462
|
-
}
|
|
15463
|
-
return {
|
|
15464
|
-
getAll,
|
|
15465
|
-
getOrganizationsWithSubscription
|
|
15466
|
-
};
|
|
15467
|
-
}
|
|
15138
|
+
// src/repositories/subscription.repo.ts
|
|
15139
|
+
import {
|
|
15140
|
+
BadRequestError as BadRequestError34,
|
|
15141
|
+
logger as logger25,
|
|
15142
|
+
makeCacheKey as makeCacheKey13,
|
|
15143
|
+
paginate as paginate11,
|
|
15144
|
+
useAtlas as useAtlas17,
|
|
15145
|
+
useCache as useCache14
|
|
15146
|
+
} from "@7365admin1/node-server-utils";
|
|
15468
15147
|
|
|
15469
15148
|
// src/models/subscription.model.ts
|
|
15470
|
-
import { BadRequestError as
|
|
15471
|
-
import
|
|
15149
|
+
import { BadRequestError as BadRequestError33 } from "@7365admin1/node-server-utils";
|
|
15150
|
+
import Joi18 from "joi";
|
|
15472
15151
|
import { ObjectId as ObjectId22 } from "mongodb";
|
|
15473
15152
|
var SubscriptionType = /* @__PURE__ */ ((SubscriptionType2) => {
|
|
15474
15153
|
SubscriptionType2["ORGANIZATION"] = "organization";
|
|
15475
15154
|
SubscriptionType2["AFFILIATE"] = "affiliate";
|
|
15476
15155
|
return SubscriptionType2;
|
|
15477
15156
|
})(SubscriptionType || {});
|
|
15478
|
-
var schema =
|
|
15479
|
-
user:
|
|
15480
|
-
amount:
|
|
15481
|
-
payment_method_card_number:
|
|
15482
|
-
payment_method_cardholder_name:
|
|
15483
|
-
payment_method_expiry_month:
|
|
15484
|
-
payment_method_expiry_year:
|
|
15485
|
-
payment_method_cvv:
|
|
15486
|
-
payment_method_type:
|
|
15487
|
-
currency:
|
|
15488
|
-
seats:
|
|
15157
|
+
var schema = Joi18.object({
|
|
15158
|
+
user: Joi18.string().hex().required(),
|
|
15159
|
+
amount: Joi18.number().min(0).required(),
|
|
15160
|
+
payment_method_card_number: Joi18.string().optional().allow("", null),
|
|
15161
|
+
payment_method_cardholder_name: Joi18.string().optional().allow("", null),
|
|
15162
|
+
payment_method_expiry_month: Joi18.string().optional().allow("", null),
|
|
15163
|
+
payment_method_expiry_year: Joi18.string().optional().allow("", null),
|
|
15164
|
+
payment_method_cvv: Joi18.string().optional().allow("", null),
|
|
15165
|
+
payment_method_type: Joi18.string().optional().allow("", null),
|
|
15166
|
+
currency: Joi18.string().optional().allow("", null),
|
|
15167
|
+
seats: Joi18.number().optional().min(0).allow(null),
|
|
15489
15168
|
organization: orgSchema.optional().allow({}),
|
|
15490
|
-
billingAddress:
|
|
15491
|
-
type:
|
|
15492
|
-
country:
|
|
15493
|
-
address:
|
|
15494
|
-
continuedAddress:
|
|
15495
|
-
city:
|
|
15496
|
-
province:
|
|
15497
|
-
postalCode:
|
|
15498
|
-
taxId:
|
|
15169
|
+
billingAddress: Joi18.object({
|
|
15170
|
+
type: Joi18.string().required(),
|
|
15171
|
+
country: Joi18.string().required(),
|
|
15172
|
+
address: Joi18.string().required(),
|
|
15173
|
+
continuedAddress: Joi18.string().optional().allow("", null),
|
|
15174
|
+
city: Joi18.string().required(),
|
|
15175
|
+
province: Joi18.string().optional().allow("", null),
|
|
15176
|
+
postalCode: Joi18.string().required(),
|
|
15177
|
+
taxId: Joi18.string().optional().allow("", null)
|
|
15499
15178
|
}).required(),
|
|
15500
|
-
promoCode:
|
|
15179
|
+
promoCode: Joi18.string().optional().allow("", null)
|
|
15501
15180
|
});
|
|
15502
15181
|
function MSubscription(value) {
|
|
15503
|
-
const schema2 =
|
|
15504
|
-
_id:
|
|
15505
|
-
user:
|
|
15506
|
-
org:
|
|
15507
|
-
amount:
|
|
15508
|
-
currency:
|
|
15509
|
-
description:
|
|
15510
|
-
promoCode:
|
|
15511
|
-
type:
|
|
15512
|
-
paidSeats:
|
|
15513
|
-
currentSeats:
|
|
15514
|
-
maxSeats:
|
|
15515
|
-
status:
|
|
15516
|
-
billingCycle:
|
|
15182
|
+
const schema2 = Joi18.object({
|
|
15183
|
+
_id: Joi18.string().hex().optional().allow("", null),
|
|
15184
|
+
user: Joi18.string().hex().optional().allow("", null),
|
|
15185
|
+
org: Joi18.string().hex().optional().allow("", null),
|
|
15186
|
+
amount: Joi18.number().min(0).required(),
|
|
15187
|
+
currency: Joi18.string().required(),
|
|
15188
|
+
description: Joi18.string().optional().allow("", null),
|
|
15189
|
+
promoCode: Joi18.string().optional().allow("", null),
|
|
15190
|
+
type: Joi18.string().valid(...Object.values(SubscriptionType)).optional().allow(null, ""),
|
|
15191
|
+
paidSeats: Joi18.number().optional().min(0).allow("", null),
|
|
15192
|
+
currentSeats: Joi18.number().optional().min(0).allow("", null),
|
|
15193
|
+
maxSeats: Joi18.number().optional().min(0).allow("", null),
|
|
15194
|
+
status: Joi18.string().optional().allow("", null),
|
|
15195
|
+
billingCycle: Joi18.string().valid("monthly", "yearly").required(),
|
|
15517
15196
|
// Ensure valid values
|
|
15518
|
-
nextBillingDate:
|
|
15519
|
-
lastPaymentStatus:
|
|
15520
|
-
failedAttempts:
|
|
15521
|
-
createdAt:
|
|
15522
|
-
updatedAt:
|
|
15523
|
-
deletedAt:
|
|
15197
|
+
nextBillingDate: Joi18.date().optional(),
|
|
15198
|
+
lastPaymentStatus: Joi18.string().optional().allow("", null),
|
|
15199
|
+
failedAttempts: Joi18.number().optional().allow("", null),
|
|
15200
|
+
createdAt: Joi18.date().optional(),
|
|
15201
|
+
updatedAt: Joi18.string().optional().allow("", null),
|
|
15202
|
+
deletedAt: Joi18.string().optional().allow("", null)
|
|
15524
15203
|
}).custom((value2, helpers) => {
|
|
15525
15204
|
if (!value2.user && !value2.org) {
|
|
15526
15205
|
return helpers.error("any.invalid", {
|
|
@@ -15531,7 +15210,7 @@ function MSubscription(value) {
|
|
|
15531
15210
|
});
|
|
15532
15211
|
const { error } = schema2.validate(value);
|
|
15533
15212
|
if (error) {
|
|
15534
|
-
throw new
|
|
15213
|
+
throw new BadRequestError33(error.details[0].message);
|
|
15535
15214
|
}
|
|
15536
15215
|
if (value._id)
|
|
15537
15216
|
value._id = new ObjectId22(value._id);
|
|
@@ -15571,20 +15250,12 @@ function MSubscription(value) {
|
|
|
15571
15250
|
}
|
|
15572
15251
|
|
|
15573
15252
|
// src/repositories/subscription.repo.ts
|
|
15574
|
-
import {
|
|
15575
|
-
BadRequestError as BadRequestError36,
|
|
15576
|
-
logger as logger27,
|
|
15577
|
-
makeCacheKey as makeCacheKey13,
|
|
15578
|
-
paginate as paginate11,
|
|
15579
|
-
useAtlas as useAtlas17,
|
|
15580
|
-
useCache as useCache14
|
|
15581
|
-
} from "@7365admin1/node-server-utils";
|
|
15582
15253
|
import { ObjectId as ObjectId23 } from "mongodb";
|
|
15583
|
-
import
|
|
15254
|
+
import Joi19 from "joi";
|
|
15584
15255
|
function useSubscriptionRepo() {
|
|
15585
15256
|
const db = useAtlas17.getDb();
|
|
15586
15257
|
if (!db) {
|
|
15587
|
-
throw new
|
|
15258
|
+
throw new BadRequestError34("Unable to connect to server.");
|
|
15588
15259
|
}
|
|
15589
15260
|
const namespace_collection = "subscriptions";
|
|
15590
15261
|
const collection = db.collection(namespace_collection);
|
|
@@ -15599,7 +15270,7 @@ function useSubscriptionRepo() {
|
|
|
15599
15270
|
{ key: { failedAttempts: 1 } }
|
|
15600
15271
|
]);
|
|
15601
15272
|
} catch (error) {
|
|
15602
|
-
throw new
|
|
15273
|
+
throw new BadRequestError34("Failed to create index on subscription.");
|
|
15603
15274
|
}
|
|
15604
15275
|
}
|
|
15605
15276
|
async function createUniqueIndex() {
|
|
@@ -15609,7 +15280,7 @@ function useSubscriptionRepo() {
|
|
|
15609
15280
|
{ unique: true }
|
|
15610
15281
|
);
|
|
15611
15282
|
} catch (error) {
|
|
15612
|
-
throw new
|
|
15283
|
+
throw new BadRequestError34(
|
|
15613
15284
|
"Failed to create unique index on subscription."
|
|
15614
15285
|
);
|
|
15615
15286
|
}
|
|
@@ -15620,76 +15291,76 @@ function useSubscriptionRepo() {
|
|
|
15620
15291
|
value = MSubscription(value);
|
|
15621
15292
|
const res = await collection.insertOne(value, { session });
|
|
15622
15293
|
delNamespace().then(() => {
|
|
15623
|
-
|
|
15294
|
+
logger25.info(`Cache cleared for namespace: ${namespace_collection}`);
|
|
15624
15295
|
}).catch((err) => {
|
|
15625
|
-
|
|
15296
|
+
logger25.error(
|
|
15626
15297
|
`Failed to clear cache for namespace: ${namespace_collection}`,
|
|
15627
15298
|
err
|
|
15628
15299
|
);
|
|
15629
15300
|
});
|
|
15630
15301
|
return res.insertedId;
|
|
15631
15302
|
} catch (error) {
|
|
15632
|
-
|
|
15303
|
+
logger25.log({ level: "error", message: `${error}` });
|
|
15633
15304
|
const isDuplicated = error.message.includes("duplicate");
|
|
15634
15305
|
if (isDuplicated) {
|
|
15635
|
-
throw new
|
|
15306
|
+
throw new BadRequestError34("Subscription already exists.");
|
|
15636
15307
|
}
|
|
15637
|
-
throw new
|
|
15308
|
+
throw new BadRequestError34("Failed to create subscription.");
|
|
15638
15309
|
}
|
|
15639
15310
|
}
|
|
15640
15311
|
async function getById(_id) {
|
|
15641
15312
|
try {
|
|
15642
15313
|
_id = new ObjectId23(_id);
|
|
15643
15314
|
} catch (error) {
|
|
15644
|
-
throw new
|
|
15315
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
15645
15316
|
}
|
|
15646
15317
|
try {
|
|
15647
15318
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
15648
15319
|
const cachedData = await getCache(cacheKey);
|
|
15649
15320
|
if (cachedData) {
|
|
15650
|
-
|
|
15321
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15651
15322
|
return cachedData;
|
|
15652
15323
|
}
|
|
15653
15324
|
const data = await collection.findOne({ _id });
|
|
15654
15325
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15655
|
-
|
|
15326
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15656
15327
|
}).catch((err) => {
|
|
15657
|
-
|
|
15328
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15658
15329
|
});
|
|
15659
15330
|
return data;
|
|
15660
15331
|
} catch (error) {
|
|
15661
|
-
throw new
|
|
15332
|
+
throw new BadRequestError34("Failed to get subscription by ID.");
|
|
15662
15333
|
}
|
|
15663
15334
|
}
|
|
15664
15335
|
async function getByUserId(user) {
|
|
15665
15336
|
try {
|
|
15666
15337
|
user = new ObjectId23(user);
|
|
15667
15338
|
} catch (error) {
|
|
15668
|
-
throw new
|
|
15339
|
+
throw new BadRequestError34("Invalid user ID format.");
|
|
15669
15340
|
}
|
|
15670
15341
|
try {
|
|
15671
15342
|
const cacheKey = makeCacheKey13(namespace_collection, { user });
|
|
15672
15343
|
const cachedData = await getCache(cacheKey);
|
|
15673
15344
|
if (cachedData) {
|
|
15674
|
-
|
|
15345
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15675
15346
|
return cachedData;
|
|
15676
15347
|
}
|
|
15677
15348
|
const data = await collection.findOne({ user });
|
|
15678
15349
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15679
|
-
|
|
15350
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15680
15351
|
}).catch((err) => {
|
|
15681
|
-
|
|
15352
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15682
15353
|
});
|
|
15683
15354
|
return data;
|
|
15684
15355
|
} catch (error) {
|
|
15685
|
-
throw new
|
|
15356
|
+
throw new BadRequestError34("Failed to get subscription by ID.");
|
|
15686
15357
|
}
|
|
15687
15358
|
}
|
|
15688
15359
|
async function getByAffiliateUserId(user) {
|
|
15689
15360
|
try {
|
|
15690
15361
|
user = new ObjectId23(user);
|
|
15691
15362
|
} catch (error) {
|
|
15692
|
-
throw new
|
|
15363
|
+
throw new BadRequestError34("Invalid user ID format.");
|
|
15693
15364
|
}
|
|
15694
15365
|
try {
|
|
15695
15366
|
const cacheKey = makeCacheKey13(namespace_collection, {
|
|
@@ -15698,7 +15369,7 @@ function useSubscriptionRepo() {
|
|
|
15698
15369
|
});
|
|
15699
15370
|
const cachedData = await getCache(cacheKey);
|
|
15700
15371
|
if (cachedData) {
|
|
15701
|
-
|
|
15372
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15702
15373
|
return cachedData;
|
|
15703
15374
|
}
|
|
15704
15375
|
const data = await collection.findOne({
|
|
@@ -15706,20 +15377,20 @@ function useSubscriptionRepo() {
|
|
|
15706
15377
|
type: "affiliate"
|
|
15707
15378
|
});
|
|
15708
15379
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15709
|
-
|
|
15380
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15710
15381
|
}).catch((err) => {
|
|
15711
|
-
|
|
15382
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15712
15383
|
});
|
|
15713
15384
|
return data;
|
|
15714
15385
|
} catch (error) {
|
|
15715
|
-
throw new
|
|
15386
|
+
throw new BadRequestError34("Failed to get subscription by ID.");
|
|
15716
15387
|
}
|
|
15717
15388
|
}
|
|
15718
15389
|
async function getByOrgId(org) {
|
|
15719
15390
|
try {
|
|
15720
15391
|
org = new ObjectId23(org);
|
|
15721
15392
|
} catch (error) {
|
|
15722
|
-
throw new
|
|
15393
|
+
throw new BadRequestError34("Invalid org ID format.");
|
|
15723
15394
|
}
|
|
15724
15395
|
try {
|
|
15725
15396
|
const cacheKey = makeCacheKey13(namespace_collection, {
|
|
@@ -15728,7 +15399,7 @@ function useSubscriptionRepo() {
|
|
|
15728
15399
|
});
|
|
15729
15400
|
const cachedData = await getCache(cacheKey);
|
|
15730
15401
|
if (cachedData) {
|
|
15731
|
-
|
|
15402
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15732
15403
|
return cachedData;
|
|
15733
15404
|
}
|
|
15734
15405
|
const data = await collection.findOne({
|
|
@@ -15736,13 +15407,13 @@ function useSubscriptionRepo() {
|
|
|
15736
15407
|
type: "organization"
|
|
15737
15408
|
});
|
|
15738
15409
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15739
|
-
|
|
15410
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15740
15411
|
}).catch((err) => {
|
|
15741
|
-
|
|
15412
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15742
15413
|
});
|
|
15743
15414
|
return data;
|
|
15744
15415
|
} catch (error) {
|
|
15745
|
-
throw new
|
|
15416
|
+
throw new BadRequestError34("Failed to get subscription by ID.");
|
|
15746
15417
|
}
|
|
15747
15418
|
}
|
|
15748
15419
|
async function getBySubscriptionId(subscriptionId) {
|
|
@@ -15750,18 +15421,18 @@ function useSubscriptionRepo() {
|
|
|
15750
15421
|
const cacheKey = makeCacheKey13(namespace_collection, { subscriptionId });
|
|
15751
15422
|
const cachedData = await getCache(cacheKey);
|
|
15752
15423
|
if (cachedData) {
|
|
15753
|
-
|
|
15424
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15754
15425
|
return cachedData;
|
|
15755
15426
|
}
|
|
15756
15427
|
const data = await collection.findOne({ subscriptionId });
|
|
15757
15428
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15758
|
-
|
|
15429
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15759
15430
|
}).catch((err) => {
|
|
15760
|
-
|
|
15431
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15761
15432
|
});
|
|
15762
15433
|
return data;
|
|
15763
15434
|
} catch (error) {
|
|
15764
|
-
throw new
|
|
15435
|
+
throw new BadRequestError34(
|
|
15765
15436
|
"Failed to get subscription by subscription ID."
|
|
15766
15437
|
);
|
|
15767
15438
|
}
|
|
@@ -15785,7 +15456,7 @@ function useSubscriptionRepo() {
|
|
|
15785
15456
|
const cacheKey = makeCacheKey13(namespace_collection, cacheOptions);
|
|
15786
15457
|
const cachedData = await getCache(cacheKey);
|
|
15787
15458
|
if (cachedData) {
|
|
15788
|
-
|
|
15459
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15789
15460
|
return cachedData;
|
|
15790
15461
|
}
|
|
15791
15462
|
try {
|
|
@@ -15798,13 +15469,13 @@ function useSubscriptionRepo() {
|
|
|
15798
15469
|
const length = await collection.countDocuments(query);
|
|
15799
15470
|
const data = paginate11(items, page, limit, length);
|
|
15800
15471
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15801
|
-
|
|
15472
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15802
15473
|
}).catch((err) => {
|
|
15803
|
-
|
|
15474
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15804
15475
|
});
|
|
15805
15476
|
return data;
|
|
15806
15477
|
} catch (error) {
|
|
15807
|
-
|
|
15478
|
+
logger25.log({ level: "error", message: `${error}` });
|
|
15808
15479
|
throw error;
|
|
15809
15480
|
}
|
|
15810
15481
|
}
|
|
@@ -15812,19 +15483,19 @@ function useSubscriptionRepo() {
|
|
|
15812
15483
|
try {
|
|
15813
15484
|
_id = new ObjectId23(_id);
|
|
15814
15485
|
} catch (error) {
|
|
15815
|
-
throw new
|
|
15486
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
15816
15487
|
}
|
|
15817
15488
|
try {
|
|
15818
15489
|
await collection.updateOne({ _id }, { $set: { status } });
|
|
15819
15490
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
15820
15491
|
delCache(cacheKey).then(() => {
|
|
15821
|
-
|
|
15492
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
15822
15493
|
}).catch((err) => {
|
|
15823
|
-
|
|
15494
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
15824
15495
|
});
|
|
15825
15496
|
return "Successfully updated subscription status.";
|
|
15826
15497
|
} catch (error) {
|
|
15827
|
-
throw new
|
|
15498
|
+
throw new BadRequestError34("Failed to update subscription status.");
|
|
15828
15499
|
}
|
|
15829
15500
|
}
|
|
15830
15501
|
async function getDueSubscriptions(BATCH_SIZE = 100) {
|
|
@@ -15851,7 +15522,7 @@ function useSubscriptionRepo() {
|
|
|
15851
15522
|
const cacheKey = makeCacheKey13(namespace_collection, cacheOptions);
|
|
15852
15523
|
const cachedData = await getCache(cacheKey);
|
|
15853
15524
|
if (cachedData) {
|
|
15854
|
-
|
|
15525
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15855
15526
|
return cachedData;
|
|
15856
15527
|
}
|
|
15857
15528
|
try {
|
|
@@ -15873,13 +15544,13 @@ function useSubscriptionRepo() {
|
|
|
15873
15544
|
]
|
|
15874
15545
|
}).sort({ nextBillingDate: 1 }).limit(BATCH_SIZE).toArray();
|
|
15875
15546
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15876
|
-
|
|
15547
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15877
15548
|
}).catch((err) => {
|
|
15878
|
-
|
|
15549
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15879
15550
|
});
|
|
15880
15551
|
return data;
|
|
15881
15552
|
} catch (error) {
|
|
15882
|
-
throw new
|
|
15553
|
+
throw new BadRequestError34("Failed to get due subscriptions.");
|
|
15883
15554
|
}
|
|
15884
15555
|
}
|
|
15885
15556
|
async function getFailedSubscriptions(BATCH_SIZE = 100) {
|
|
@@ -15890,7 +15561,7 @@ function useSubscriptionRepo() {
|
|
|
15890
15561
|
const cacheKey = makeCacheKey13(namespace_collection, cacheOptions);
|
|
15891
15562
|
const cachedData = await getCache(cacheKey);
|
|
15892
15563
|
if (cachedData) {
|
|
15893
|
-
|
|
15564
|
+
logger25.info(`Cache hit for key: ${cacheKey}`);
|
|
15894
15565
|
return cachedData;
|
|
15895
15566
|
}
|
|
15896
15567
|
try {
|
|
@@ -15899,13 +15570,13 @@ function useSubscriptionRepo() {
|
|
|
15899
15570
|
status: "active"
|
|
15900
15571
|
}).limit(BATCH_SIZE).toArray();
|
|
15901
15572
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
15902
|
-
|
|
15573
|
+
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
15903
15574
|
}).catch((err) => {
|
|
15904
|
-
|
|
15575
|
+
logger25.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
15905
15576
|
});
|
|
15906
15577
|
return data;
|
|
15907
15578
|
} catch (error) {
|
|
15908
|
-
throw new
|
|
15579
|
+
throw new BadRequestError34("Failed to get failed subscriptions.");
|
|
15909
15580
|
}
|
|
15910
15581
|
}
|
|
15911
15582
|
async function findOrgSubscriptionsForStatusSync() {
|
|
@@ -15919,7 +15590,7 @@ function useSubscriptionRepo() {
|
|
|
15919
15590
|
).toArray();
|
|
15920
15591
|
return data;
|
|
15921
15592
|
} catch (error) {
|
|
15922
|
-
throw new
|
|
15593
|
+
throw new BadRequestError34(
|
|
15923
15594
|
"Failed to list organization subscriptions for status sync."
|
|
15924
15595
|
);
|
|
15925
15596
|
}
|
|
@@ -15938,24 +15609,24 @@ function useSubscriptionRepo() {
|
|
|
15938
15609
|
).toArray();
|
|
15939
15610
|
return data;
|
|
15940
15611
|
} catch (error) {
|
|
15941
|
-
throw new
|
|
15612
|
+
throw new BadRequestError34(
|
|
15942
15613
|
"Failed to list organization subscriptions with overdue next billing date."
|
|
15943
15614
|
);
|
|
15944
15615
|
}
|
|
15945
15616
|
}
|
|
15946
15617
|
async function processSuccessfulPayment(value, session) {
|
|
15947
|
-
const schema2 =
|
|
15948
|
-
_id:
|
|
15949
|
-
nextBillingDate:
|
|
15618
|
+
const schema2 = Joi19.object({
|
|
15619
|
+
_id: Joi19.string().hex().required(),
|
|
15620
|
+
nextBillingDate: Joi19.date().required()
|
|
15950
15621
|
});
|
|
15951
15622
|
const { error } = schema2.validate(value);
|
|
15952
15623
|
if (error) {
|
|
15953
|
-
throw new
|
|
15624
|
+
throw new BadRequestError34(error.message);
|
|
15954
15625
|
}
|
|
15955
15626
|
try {
|
|
15956
15627
|
value._id = new ObjectId23(value._id);
|
|
15957
15628
|
} catch (error2) {
|
|
15958
|
-
throw new
|
|
15629
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
15959
15630
|
}
|
|
15960
15631
|
const date = value.nextBillingDate;
|
|
15961
15632
|
try {
|
|
@@ -15973,31 +15644,31 @@ function useSubscriptionRepo() {
|
|
|
15973
15644
|
},
|
|
15974
15645
|
{ session }
|
|
15975
15646
|
);
|
|
15976
|
-
|
|
15647
|
+
logger25.info(`${res.modifiedCount} subscription updated.`);
|
|
15977
15648
|
const cacheKey = makeCacheKey13(namespace_collection, { _id: value._id });
|
|
15978
15649
|
delCache(cacheKey).then(() => {
|
|
15979
|
-
|
|
15650
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
15980
15651
|
}).catch((err) => {
|
|
15981
|
-
|
|
15652
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
15982
15653
|
});
|
|
15983
15654
|
return "Successfully updated subscription.";
|
|
15984
15655
|
} catch (error2) {
|
|
15985
|
-
|
|
15986
|
-
throw new
|
|
15656
|
+
logger25.error(`${error2}`);
|
|
15657
|
+
throw new BadRequestError34("Failed to update subscription.");
|
|
15987
15658
|
}
|
|
15988
15659
|
}
|
|
15989
15660
|
async function markSubscriptionAsFailed({ _id, failed }, session) {
|
|
15990
|
-
const schema2 =
|
|
15991
|
-
_id:
|
|
15661
|
+
const schema2 = Joi19.object({
|
|
15662
|
+
_id: Joi19.string().hex().required()
|
|
15992
15663
|
});
|
|
15993
15664
|
const { error } = schema2.validate({ _id });
|
|
15994
15665
|
if (error) {
|
|
15995
|
-
throw new
|
|
15666
|
+
throw new BadRequestError34(error.message);
|
|
15996
15667
|
}
|
|
15997
15668
|
try {
|
|
15998
15669
|
_id = new ObjectId23(_id);
|
|
15999
15670
|
} catch (error2) {
|
|
16000
|
-
throw new
|
|
15671
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
16001
15672
|
}
|
|
16002
15673
|
const updateOptions = {
|
|
16003
15674
|
$inc: { failedAttempts: 1 }
|
|
@@ -16011,27 +15682,27 @@ function useSubscriptionRepo() {
|
|
|
16011
15682
|
});
|
|
16012
15683
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
16013
15684
|
delCache(cacheKey).then(() => {
|
|
16014
|
-
|
|
15685
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
16015
15686
|
}).catch((err) => {
|
|
16016
|
-
|
|
15687
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
16017
15688
|
});
|
|
16018
15689
|
return result;
|
|
16019
15690
|
} catch (error2) {
|
|
16020
|
-
throw new
|
|
15691
|
+
throw new BadRequestError34("Failed to update subscription.");
|
|
16021
15692
|
}
|
|
16022
15693
|
}
|
|
16023
15694
|
async function markSubscriptionAsCanceled(_id, session) {
|
|
16024
|
-
const schema2 =
|
|
16025
|
-
_id:
|
|
15695
|
+
const schema2 = Joi19.object({
|
|
15696
|
+
_id: Joi19.string().hex().required()
|
|
16026
15697
|
});
|
|
16027
15698
|
const { error } = schema2.validate({ _id });
|
|
16028
15699
|
if (error) {
|
|
16029
|
-
throw new
|
|
15700
|
+
throw new BadRequestError34(error.message);
|
|
16030
15701
|
}
|
|
16031
15702
|
try {
|
|
16032
15703
|
_id = new ObjectId23(_id);
|
|
16033
15704
|
} catch (error2) {
|
|
16034
|
-
throw new
|
|
15705
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
16035
15706
|
}
|
|
16036
15707
|
try {
|
|
16037
15708
|
const result = await collection.updateOne(
|
|
@@ -16041,27 +15712,27 @@ function useSubscriptionRepo() {
|
|
|
16041
15712
|
);
|
|
16042
15713
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
16043
15714
|
delCache(cacheKey).then(() => {
|
|
16044
|
-
|
|
15715
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
16045
15716
|
}).catch((err) => {
|
|
16046
|
-
|
|
15717
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
16047
15718
|
});
|
|
16048
15719
|
return result;
|
|
16049
15720
|
} catch (error2) {
|
|
16050
|
-
throw new
|
|
15721
|
+
throw new BadRequestError34("Failed to update subscription.");
|
|
16051
15722
|
}
|
|
16052
15723
|
}
|
|
16053
15724
|
async function updateStatusById(_id, status, session) {
|
|
16054
|
-
const schema2 =
|
|
16055
|
-
_id:
|
|
15725
|
+
const schema2 = Joi19.object({
|
|
15726
|
+
_id: Joi19.string().hex().required()
|
|
16056
15727
|
});
|
|
16057
15728
|
const { error } = schema2.validate({ _id });
|
|
16058
15729
|
if (error) {
|
|
16059
|
-
throw new
|
|
15730
|
+
throw new BadRequestError34(error.message);
|
|
16060
15731
|
}
|
|
16061
15732
|
try {
|
|
16062
15733
|
_id = new ObjectId23(_id);
|
|
16063
15734
|
} catch (error2) {
|
|
16064
|
-
throw new
|
|
15735
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
16065
15736
|
}
|
|
16066
15737
|
try {
|
|
16067
15738
|
const result = await collection.updateOne(
|
|
@@ -16071,13 +15742,13 @@ function useSubscriptionRepo() {
|
|
|
16071
15742
|
);
|
|
16072
15743
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
16073
15744
|
delCache(cacheKey).then(() => {
|
|
16074
|
-
|
|
15745
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
16075
15746
|
}).catch((err) => {
|
|
16076
|
-
|
|
15747
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
16077
15748
|
});
|
|
16078
15749
|
return result;
|
|
16079
15750
|
} catch (error2) {
|
|
16080
|
-
throw new
|
|
15751
|
+
throw new BadRequestError34("Failed to update subscription status.");
|
|
16081
15752
|
}
|
|
16082
15753
|
}
|
|
16083
15754
|
async function updateSeatsById({
|
|
@@ -16088,13 +15759,13 @@ function useSubscriptionRepo() {
|
|
|
16088
15759
|
amount,
|
|
16089
15760
|
promoCode
|
|
16090
15761
|
}, session) {
|
|
16091
|
-
const schema2 =
|
|
16092
|
-
_id:
|
|
16093
|
-
currentSeats:
|
|
16094
|
-
maxSeats:
|
|
16095
|
-
paidSeats:
|
|
16096
|
-
amount:
|
|
16097
|
-
promoCode:
|
|
15762
|
+
const schema2 = Joi19.object({
|
|
15763
|
+
_id: Joi19.string().hex().required(),
|
|
15764
|
+
currentSeats: Joi19.number().required().min(1),
|
|
15765
|
+
maxSeats: Joi19.number().required().min(0),
|
|
15766
|
+
paidSeats: Joi19.number().optional().min(0),
|
|
15767
|
+
amount: Joi19.number().required().min(0),
|
|
15768
|
+
promoCode: Joi19.string().optional().allow("", null)
|
|
16098
15769
|
});
|
|
16099
15770
|
const { error } = schema2.validate({
|
|
16100
15771
|
_id,
|
|
@@ -16105,12 +15776,12 @@ function useSubscriptionRepo() {
|
|
|
16105
15776
|
promoCode
|
|
16106
15777
|
});
|
|
16107
15778
|
if (error) {
|
|
16108
|
-
throw new
|
|
15779
|
+
throw new BadRequestError34(error.message);
|
|
16109
15780
|
}
|
|
16110
15781
|
try {
|
|
16111
15782
|
_id = new ObjectId23(_id);
|
|
16112
15783
|
} catch (error2) {
|
|
16113
|
-
throw new
|
|
15784
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
16114
15785
|
}
|
|
16115
15786
|
const data = {
|
|
16116
15787
|
currentSeats,
|
|
@@ -16131,31 +15802,31 @@ function useSubscriptionRepo() {
|
|
|
16131
15802
|
);
|
|
16132
15803
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
16133
15804
|
delCache(cacheKey).then(() => {
|
|
16134
|
-
|
|
15805
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
16135
15806
|
}).catch((err) => {
|
|
16136
|
-
|
|
15807
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
16137
15808
|
});
|
|
16138
15809
|
return result;
|
|
16139
15810
|
} catch (error2) {
|
|
16140
|
-
throw new
|
|
15811
|
+
throw new BadRequestError34("Failed to update subscription seats.");
|
|
16141
15812
|
}
|
|
16142
15813
|
}
|
|
16143
15814
|
async function updateMaxSeatsById({
|
|
16144
15815
|
_id,
|
|
16145
15816
|
seats
|
|
16146
15817
|
}, session) {
|
|
16147
|
-
const schema2 =
|
|
16148
|
-
_id:
|
|
16149
|
-
seats:
|
|
15818
|
+
const schema2 = Joi19.object({
|
|
15819
|
+
_id: Joi19.string().hex().required(),
|
|
15820
|
+
seats: Joi19.number().required().min(1)
|
|
16150
15821
|
});
|
|
16151
15822
|
const { error } = schema2.validate({ _id, seats });
|
|
16152
15823
|
if (error) {
|
|
16153
|
-
throw new
|
|
15824
|
+
throw new BadRequestError34(error.message);
|
|
16154
15825
|
}
|
|
16155
15826
|
try {
|
|
16156
15827
|
_id = new ObjectId23(_id);
|
|
16157
15828
|
} catch (error2) {
|
|
16158
|
-
throw new
|
|
15829
|
+
throw new BadRequestError34("Invalid subscription ID format.");
|
|
16159
15830
|
}
|
|
16160
15831
|
try {
|
|
16161
15832
|
const result = await collection.updateOne(
|
|
@@ -16165,13 +15836,13 @@ function useSubscriptionRepo() {
|
|
|
16165
15836
|
);
|
|
16166
15837
|
const cacheKey = makeCacheKey13(namespace_collection, { _id });
|
|
16167
15838
|
delCache(cacheKey).then(() => {
|
|
16168
|
-
|
|
15839
|
+
logger25.info(`Cache deleted for key: ${cacheKey}`);
|
|
16169
15840
|
}).catch((err) => {
|
|
16170
|
-
|
|
15841
|
+
logger25.error(`Failed to delete cache for key: ${cacheKey}`, err);
|
|
16171
15842
|
});
|
|
16172
15843
|
return result;
|
|
16173
15844
|
} catch (error2) {
|
|
16174
|
-
throw new
|
|
15845
|
+
throw new BadRequestError34("Failed to update subscription paid seats.");
|
|
16175
15846
|
}
|
|
16176
15847
|
}
|
|
16177
15848
|
return {
|
|
@@ -16198,6 +15869,397 @@ function useSubscriptionRepo() {
|
|
|
16198
15869
|
};
|
|
16199
15870
|
}
|
|
16200
15871
|
|
|
15872
|
+
// src/controllers/organization.controller.ts
|
|
15873
|
+
function useOrgController() {
|
|
15874
|
+
const { getOrgsByMembership } = useMemberRepo();
|
|
15875
|
+
const {
|
|
15876
|
+
getByName: _getByName,
|
|
15877
|
+
getById: _getById,
|
|
15878
|
+
getByEmail: _getByEmail,
|
|
15879
|
+
getAll: _getAll,
|
|
15880
|
+
add: _add,
|
|
15881
|
+
update: _update,
|
|
15882
|
+
getOrgsByEmail: _getOrgsByEmail,
|
|
15883
|
+
getAdminOrgForResident: _getAdminOrgForResident,
|
|
15884
|
+
completeOnboardingById: _completeOnboardingById,
|
|
15885
|
+
updateStatusById: _updateStatusById
|
|
15886
|
+
} = useOrgRepo();
|
|
15887
|
+
const {
|
|
15888
|
+
getByOrgId: _getSubscriptionByOrgId,
|
|
15889
|
+
updateStatusById: _updateSubscriptionStatusById
|
|
15890
|
+
} = useSubscriptionRepo();
|
|
15891
|
+
async function add(req, res, next) {
|
|
15892
|
+
const validation = Joi20.object({
|
|
15893
|
+
name: Joi20.string().required(),
|
|
15894
|
+
type: Joi20.string().required(),
|
|
15895
|
+
nature: Joi20.string().valid(...allowedNatures).required(),
|
|
15896
|
+
email: Joi20.string().email().optional().allow("", null),
|
|
15897
|
+
contact: Joi20.string().optional().allow("", null),
|
|
15898
|
+
terms: Joi20.string().optional().allow("", null),
|
|
15899
|
+
policies: Joi20.string().optional().allow("", null)
|
|
15900
|
+
});
|
|
15901
|
+
const { error } = validation.validate(req.body);
|
|
15902
|
+
if (error) {
|
|
15903
|
+
logger26.log({ level: "error", message: error.message });
|
|
15904
|
+
next(new BadRequestError35(error.message));
|
|
15905
|
+
return;
|
|
15906
|
+
}
|
|
15907
|
+
try {
|
|
15908
|
+
await _add(req.body);
|
|
15909
|
+
res.status(201).json({ message: "Successfully created organization." });
|
|
15910
|
+
return;
|
|
15911
|
+
} catch (error2) {
|
|
15912
|
+
logger26.log({ level: "error", message: error2.message });
|
|
15913
|
+
next(error2);
|
|
15914
|
+
return;
|
|
15915
|
+
}
|
|
15916
|
+
}
|
|
15917
|
+
async function getAll(req, res, next) {
|
|
15918
|
+
const validation = Joi20.object({
|
|
15919
|
+
search: Joi20.string().optional().allow("", null),
|
|
15920
|
+
page: Joi20.number().integer().min(1).allow("", null).default(1),
|
|
15921
|
+
limit: Joi20.number().integer().min(1).max(100).allow("", null).default(10),
|
|
15922
|
+
nature: Joi20.string().valid(...allowedNatures).optional().allow("", null),
|
|
15923
|
+
sort: Joi20.string().optional().allow("", null)
|
|
15924
|
+
});
|
|
15925
|
+
const query = { ...req.query };
|
|
15926
|
+
const { error } = validation.validate(query);
|
|
15927
|
+
if (error) {
|
|
15928
|
+
logger26.log({ level: "error", message: error.message });
|
|
15929
|
+
next(new BadRequestError35(error.message));
|
|
15930
|
+
return;
|
|
15931
|
+
}
|
|
15932
|
+
const search = req.query.search ?? "";
|
|
15933
|
+
const page = parseInt(req.query.page ?? "1");
|
|
15934
|
+
const limit = parseInt(req.query.limit ?? "10");
|
|
15935
|
+
const nature = req.query.nature ?? "";
|
|
15936
|
+
try {
|
|
15937
|
+
const data = await _getAll({
|
|
15938
|
+
search,
|
|
15939
|
+
page,
|
|
15940
|
+
limit,
|
|
15941
|
+
nature
|
|
15942
|
+
});
|
|
15943
|
+
res.json(data);
|
|
15944
|
+
return;
|
|
15945
|
+
} catch (error2) {
|
|
15946
|
+
logger26.log({ level: "error", message: error2.message });
|
|
15947
|
+
next(error2);
|
|
15948
|
+
return;
|
|
15949
|
+
}
|
|
15950
|
+
}
|
|
15951
|
+
async function addOnboardingOrg(req, res, next) {
|
|
15952
|
+
const validation = Joi20.object({
|
|
15953
|
+
name: Joi20.string().required(),
|
|
15954
|
+
type: Joi20.string().required(),
|
|
15955
|
+
nature: Joi20.string().valid(...allowedNatures).required(),
|
|
15956
|
+
email: Joi20.string().email().optional().allow("", null),
|
|
15957
|
+
contact: Joi20.string().optional().allow("", null)
|
|
15958
|
+
});
|
|
15959
|
+
const { error } = validation.validate(req.body);
|
|
15960
|
+
if (error) {
|
|
15961
|
+
logger26.log({ level: "error", message: error.message });
|
|
15962
|
+
next(new BadRequestError35(error.message));
|
|
15963
|
+
return;
|
|
15964
|
+
}
|
|
15965
|
+
try {
|
|
15966
|
+
const _id = await _add(req.body);
|
|
15967
|
+
const data = await _getById(_id);
|
|
15968
|
+
res.status(201).json({
|
|
15969
|
+
message: "Successfully created organization.",
|
|
15970
|
+
data
|
|
15971
|
+
});
|
|
15972
|
+
return;
|
|
15973
|
+
} catch (error2) {
|
|
15974
|
+
logger26.log({ level: "error", message: error2.message });
|
|
15975
|
+
next(error2);
|
|
15976
|
+
return;
|
|
15977
|
+
}
|
|
15978
|
+
}
|
|
15979
|
+
async function getOrgsByUserId(req, res, next) {
|
|
15980
|
+
const validation = Joi20.object({
|
|
15981
|
+
search: Joi20.string().optional().allow("", null),
|
|
15982
|
+
page: Joi20.number().integer().min(1).allow("", null).default(1),
|
|
15983
|
+
limit: Joi20.number().integer().min(1).max(100).allow("", null).default(10),
|
|
15984
|
+
user: Joi20.string().hex().required(),
|
|
15985
|
+
type: Joi20.string().optional().allow("", null)
|
|
15986
|
+
});
|
|
15987
|
+
const query = { ...req.query };
|
|
15988
|
+
query.user = req.params.user;
|
|
15989
|
+
const { error } = validation.validate(query);
|
|
15990
|
+
if (error) {
|
|
15991
|
+
logger26.log({ level: "error", message: error.message });
|
|
15992
|
+
next(new BadRequestError35(error.message));
|
|
15993
|
+
return;
|
|
15994
|
+
}
|
|
15995
|
+
const search = req.query.search ?? "";
|
|
15996
|
+
const page = parseInt(req.query.page ?? "1");
|
|
15997
|
+
const limit = parseInt(req.query.limit ?? "10");
|
|
15998
|
+
const user = req.params.user;
|
|
15999
|
+
const type = req.query.type ?? "";
|
|
16000
|
+
try {
|
|
16001
|
+
const data = await getOrgsByMembership({
|
|
16002
|
+
search,
|
|
16003
|
+
page,
|
|
16004
|
+
limit,
|
|
16005
|
+
user,
|
|
16006
|
+
type
|
|
16007
|
+
});
|
|
16008
|
+
res.json(data);
|
|
16009
|
+
return;
|
|
16010
|
+
} catch (error2) {
|
|
16011
|
+
logger26.log({ level: "error", message: error2.message });
|
|
16012
|
+
next(error2);
|
|
16013
|
+
return;
|
|
16014
|
+
}
|
|
16015
|
+
}
|
|
16016
|
+
async function getByName(req, res, next) {
|
|
16017
|
+
const validation = Joi20.string().required();
|
|
16018
|
+
const name = req.params.name;
|
|
16019
|
+
const { error } = validation.validate(name);
|
|
16020
|
+
if (error) {
|
|
16021
|
+
logger26.log({ level: "error", message: error.message });
|
|
16022
|
+
next(new BadRequestError35(error.message));
|
|
16023
|
+
return;
|
|
16024
|
+
}
|
|
16025
|
+
try {
|
|
16026
|
+
const data = await _getByName(name);
|
|
16027
|
+
res.json(data);
|
|
16028
|
+
return;
|
|
16029
|
+
} catch (error2) {
|
|
16030
|
+
next(error2);
|
|
16031
|
+
return;
|
|
16032
|
+
}
|
|
16033
|
+
}
|
|
16034
|
+
async function getById(req, res, next) {
|
|
16035
|
+
const validation = Joi20.string().hex().required();
|
|
16036
|
+
const _id = req.params.id;
|
|
16037
|
+
const { error } = validation.validate(_id);
|
|
16038
|
+
if (error) {
|
|
16039
|
+
logger26.log({ level: "error", message: error.message });
|
|
16040
|
+
next(new BadRequestError35(error.message));
|
|
16041
|
+
return;
|
|
16042
|
+
}
|
|
16043
|
+
try {
|
|
16044
|
+
const data = await _getById(_id);
|
|
16045
|
+
res.json(data);
|
|
16046
|
+
return;
|
|
16047
|
+
} catch (error2) {
|
|
16048
|
+
logger26.log({ level: "error", message: error2.message });
|
|
16049
|
+
next(error2);
|
|
16050
|
+
return;
|
|
16051
|
+
}
|
|
16052
|
+
}
|
|
16053
|
+
async function getByEmail(req, res, next) {
|
|
16054
|
+
const validation = Joi20.string().required();
|
|
16055
|
+
const email = req.params.email;
|
|
16056
|
+
const { error } = validation.validate(email);
|
|
16057
|
+
if (error) {
|
|
16058
|
+
logger26.log({ level: "error", message: error.message });
|
|
16059
|
+
next(new BadRequestError35(error.message));
|
|
16060
|
+
return;
|
|
16061
|
+
}
|
|
16062
|
+
try {
|
|
16063
|
+
const data = await _getByEmail(email);
|
|
16064
|
+
if (!data) {
|
|
16065
|
+
next(new NotFoundError11("Organization not found."));
|
|
16066
|
+
return;
|
|
16067
|
+
}
|
|
16068
|
+
res.json(data);
|
|
16069
|
+
return;
|
|
16070
|
+
} catch (error2) {
|
|
16071
|
+
next(error2);
|
|
16072
|
+
return;
|
|
16073
|
+
}
|
|
16074
|
+
}
|
|
16075
|
+
async function getOrgsByEmail(req, res, next) {
|
|
16076
|
+
const validation = Joi20.object({
|
|
16077
|
+
email: Joi20.string().email().required()
|
|
16078
|
+
});
|
|
16079
|
+
const query = {
|
|
16080
|
+
email: req.params.email
|
|
16081
|
+
};
|
|
16082
|
+
const { error } = validation.validate(query);
|
|
16083
|
+
if (error) {
|
|
16084
|
+
logger26.log({ level: "error", message: error.message });
|
|
16085
|
+
next(new BadRequestError35(error.message));
|
|
16086
|
+
return;
|
|
16087
|
+
}
|
|
16088
|
+
const email = req.params.email;
|
|
16089
|
+
try {
|
|
16090
|
+
const data = await _getOrgsByEmail(email);
|
|
16091
|
+
res.json(data);
|
|
16092
|
+
return;
|
|
16093
|
+
} catch (error2) {
|
|
16094
|
+
logger26.log({ level: "error", message: error2.message });
|
|
16095
|
+
next(error2);
|
|
16096
|
+
return;
|
|
16097
|
+
}
|
|
16098
|
+
}
|
|
16099
|
+
async function update(req, res, next) {
|
|
16100
|
+
const validation = Joi20.object({
|
|
16101
|
+
name: Joi20.string().optional(),
|
|
16102
|
+
type: Joi20.string().optional(),
|
|
16103
|
+
nature: Joi20.string().valid(...allowedNatures).optional(),
|
|
16104
|
+
email: Joi20.string().email().allow("", null).optional(),
|
|
16105
|
+
contact: Joi20.string().allow("", null).optional(),
|
|
16106
|
+
terms: Joi20.string().optional().allow("", null),
|
|
16107
|
+
policies: Joi20.string().optional().allow("", null)
|
|
16108
|
+
});
|
|
16109
|
+
const { error } = validation.validate(req.body);
|
|
16110
|
+
if (error) {
|
|
16111
|
+
next(new BadRequestError35(error.message));
|
|
16112
|
+
return;
|
|
16113
|
+
}
|
|
16114
|
+
const id = req.params.id;
|
|
16115
|
+
try {
|
|
16116
|
+
await _update(id, req.body);
|
|
16117
|
+
const data = await _getById(id);
|
|
16118
|
+
res.json({
|
|
16119
|
+
message: "Organization updated successfully",
|
|
16120
|
+
data
|
|
16121
|
+
});
|
|
16122
|
+
} catch (err) {
|
|
16123
|
+
next(err);
|
|
16124
|
+
}
|
|
16125
|
+
}
|
|
16126
|
+
async function getAdminOrgForResident(_req, res, next) {
|
|
16127
|
+
try {
|
|
16128
|
+
const data = await _getAdminOrgForResident();
|
|
16129
|
+
res.status(200).json(data);
|
|
16130
|
+
} catch (error) {
|
|
16131
|
+
logger26.log({ level: "error", message: error.message });
|
|
16132
|
+
next(error);
|
|
16133
|
+
}
|
|
16134
|
+
}
|
|
16135
|
+
async function updateStatus(req, res, next) {
|
|
16136
|
+
const validation = Joi20.object({
|
|
16137
|
+
status: Joi20.string().valid("active", "suspended").required()
|
|
16138
|
+
});
|
|
16139
|
+
const { error } = validation.validate(req.body);
|
|
16140
|
+
if (error) {
|
|
16141
|
+
next(new BadRequestError35(error.message));
|
|
16142
|
+
return;
|
|
16143
|
+
}
|
|
16144
|
+
const orgId = req.params.id;
|
|
16145
|
+
const { status } = req.body;
|
|
16146
|
+
try {
|
|
16147
|
+
await _updateStatusById(orgId, status);
|
|
16148
|
+
const subscription = await _getSubscriptionByOrgId(orgId);
|
|
16149
|
+
if (subscription) {
|
|
16150
|
+
await _updateSubscriptionStatusById(
|
|
16151
|
+
subscription._id,
|
|
16152
|
+
status
|
|
16153
|
+
);
|
|
16154
|
+
}
|
|
16155
|
+
const data = await _getById(orgId);
|
|
16156
|
+
res.json({
|
|
16157
|
+
message: "Organization status updated successfully.",
|
|
16158
|
+
data
|
|
16159
|
+
});
|
|
16160
|
+
} catch (err) {
|
|
16161
|
+
next(err);
|
|
16162
|
+
}
|
|
16163
|
+
}
|
|
16164
|
+
return {
|
|
16165
|
+
add,
|
|
16166
|
+
addOnboardingOrg,
|
|
16167
|
+
getAll,
|
|
16168
|
+
getOrgsByUserId,
|
|
16169
|
+
getByName,
|
|
16170
|
+
getById,
|
|
16171
|
+
getByEmail,
|
|
16172
|
+
update,
|
|
16173
|
+
getOrgsByEmail,
|
|
16174
|
+
getAdminOrgForResident,
|
|
16175
|
+
updateStatus
|
|
16176
|
+
};
|
|
16177
|
+
}
|
|
16178
|
+
|
|
16179
|
+
// src/controllers/organization-v2.controller.ts
|
|
16180
|
+
import Joi21 from "joi";
|
|
16181
|
+
import { BadRequestError as BadRequestError36, logger as logger27 } from "@7365admin1/node-server-utils";
|
|
16182
|
+
function useOrgControllerV2() {
|
|
16183
|
+
const { getAll: _getAll, getOrganizationsWithSubscription: _getOrganizationsWithSubscription } = useOrgRepo();
|
|
16184
|
+
async function getAll(req, res, next) {
|
|
16185
|
+
const validation = Joi21.object({
|
|
16186
|
+
search: Joi21.string().optional().allow("", null),
|
|
16187
|
+
page: Joi21.number().integer().min(1).allow("", null).default(1),
|
|
16188
|
+
limit: Joi21.number().integer().min(1).max(100).allow("", null).default(10),
|
|
16189
|
+
nature: Joi21.string().valid(...allowedNatures).optional().allow("", null),
|
|
16190
|
+
status: Joi21.string().trim().valid("active", "suspended", "deleted").optional().empty("").default("active")
|
|
16191
|
+
});
|
|
16192
|
+
const query = { ...req.query };
|
|
16193
|
+
const { error, value } = validation.validate(query, {
|
|
16194
|
+
convert: true,
|
|
16195
|
+
stripUnknown: true
|
|
16196
|
+
});
|
|
16197
|
+
if (error) {
|
|
16198
|
+
logger27.log({ level: "error", message: error.message });
|
|
16199
|
+
next(new BadRequestError36(error.message));
|
|
16200
|
+
return;
|
|
16201
|
+
}
|
|
16202
|
+
const search = value.search ?? "";
|
|
16203
|
+
const page = typeof value.page === "number" ? value.page : parseInt(String(value.page ?? "1"), 10);
|
|
16204
|
+
const limit = typeof value.limit === "number" ? value.limit : parseInt(String(value.limit ?? "10"), 10);
|
|
16205
|
+
const nature = value.nature ?? "";
|
|
16206
|
+
const status = value.status;
|
|
16207
|
+
try {
|
|
16208
|
+
const data = await _getAll({
|
|
16209
|
+
search,
|
|
16210
|
+
page,
|
|
16211
|
+
limit,
|
|
16212
|
+
nature,
|
|
16213
|
+
status
|
|
16214
|
+
});
|
|
16215
|
+
res.json(data);
|
|
16216
|
+
return;
|
|
16217
|
+
} catch (error2) {
|
|
16218
|
+
logger27.log({ level: "error", message: error2.message });
|
|
16219
|
+
next(error2);
|
|
16220
|
+
return;
|
|
16221
|
+
}
|
|
16222
|
+
}
|
|
16223
|
+
async function getOrganizationsWithSubscription(req, res, next) {
|
|
16224
|
+
const validation = Joi21.object({
|
|
16225
|
+
search: Joi21.string().optional().allow("", null),
|
|
16226
|
+
page: Joi21.number().integer().min(1).default(1),
|
|
16227
|
+
limit: Joi21.number().integer().min(1).max(100).default(10),
|
|
16228
|
+
status: Joi21.string().trim().valid("active", "suspended", "deleted").default("active"),
|
|
16229
|
+
type: Joi21.string().optional().allow("", null),
|
|
16230
|
+
billingCycle: Joi21.string().optional().allow("", null)
|
|
16231
|
+
});
|
|
16232
|
+
const { error, value } = validation.validate(
|
|
16233
|
+
req.query,
|
|
16234
|
+
{
|
|
16235
|
+
convert: true,
|
|
16236
|
+
stripUnknown: true
|
|
16237
|
+
}
|
|
16238
|
+
);
|
|
16239
|
+
if (error) {
|
|
16240
|
+
next(new BadRequestError36(error.message));
|
|
16241
|
+
return;
|
|
16242
|
+
}
|
|
16243
|
+
try {
|
|
16244
|
+
const data = await _getOrganizationsWithSubscription({
|
|
16245
|
+
search: value.search ?? "",
|
|
16246
|
+
page: value.page ?? 1,
|
|
16247
|
+
limit: value.limit ?? 10,
|
|
16248
|
+
status: value.status ?? "active",
|
|
16249
|
+
type: value.type ?? "",
|
|
16250
|
+
billingCycle: value.billingCycle ?? ""
|
|
16251
|
+
});
|
|
16252
|
+
res.json(data);
|
|
16253
|
+
} catch (error2) {
|
|
16254
|
+
next(error2);
|
|
16255
|
+
}
|
|
16256
|
+
}
|
|
16257
|
+
return {
|
|
16258
|
+
getAll,
|
|
16259
|
+
getOrganizationsWithSubscription
|
|
16260
|
+
};
|
|
16261
|
+
}
|
|
16262
|
+
|
|
16201
16263
|
// src/services/subscription.service.ts
|
|
16202
16264
|
import {
|
|
16203
16265
|
AppError as AppError7,
|
|
@@ -25378,6 +25440,7 @@ var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
|
|
|
25378
25440
|
var BuildingStatus = /* @__PURE__ */ ((BuildingStatus2) => {
|
|
25379
25441
|
BuildingStatus2["ACTIVE"] = "active";
|
|
25380
25442
|
BuildingStatus2["PENDING"] = "pending";
|
|
25443
|
+
BuildingStatus2["DELETED"] = "deleted";
|
|
25381
25444
|
return BuildingStatus2;
|
|
25382
25445
|
})(BuildingStatus || {});
|
|
25383
25446
|
var objectIdSchema = Joi41.alternatives().try(
|
|
@@ -26738,12 +26801,6 @@ function useVehicleService() {
|
|
|
26738
26801
|
const org = await _getById(orgId);
|
|
26739
26802
|
if (!org)
|
|
26740
26803
|
throw new BadRequestError75("Org not found");
|
|
26741
|
-
const allowedNatures2 = "property_management_agency" /* PROPERTY_MANAGEMENT_AGENCY */;
|
|
26742
|
-
if (!allowedNatures2.includes(org.nature)) {
|
|
26743
|
-
throw new BadRequestError75(
|
|
26744
|
-
"Only property management can approve vehicles."
|
|
26745
|
-
);
|
|
26746
|
-
}
|
|
26747
26804
|
const vehicle = await _getVehicleById(id);
|
|
26748
26805
|
const plate = vehicle.plates.find((p) => p._id.toString() === id);
|
|
26749
26806
|
const _plateNumber = plate?.plateNumber;
|
|
@@ -27966,6 +28023,21 @@ function useBuildingRepo() {
|
|
|
27966
28023
|
);
|
|
27967
28024
|
async function createIndexes() {
|
|
27968
28025
|
try {
|
|
28026
|
+
const indexes = await collection.indexes();
|
|
28027
|
+
const legacyUniqueIndexKeys = [
|
|
28028
|
+
JSON.stringify({ name: 1 }),
|
|
28029
|
+
JSON.stringify({ site: 1, name: 1 }),
|
|
28030
|
+
JSON.stringify({ site: 1, block: 1 })
|
|
28031
|
+
];
|
|
28032
|
+
await Promise.all(
|
|
28033
|
+
indexes.map(async (index) => {
|
|
28034
|
+
const key = JSON.stringify(index.key);
|
|
28035
|
+
const isLegacyUniqueIndex = index.unique && legacyUniqueIndexKeys.includes(key) && index.partialFilterExpression?.status !== "active" /* ACTIVE */;
|
|
28036
|
+
if (index.name && isLegacyUniqueIndex) {
|
|
28037
|
+
await collection.dropIndex(index.name);
|
|
28038
|
+
}
|
|
28039
|
+
})
|
|
28040
|
+
);
|
|
27969
28041
|
await collection.createIndexes([
|
|
27970
28042
|
{ key: { name: "text" }, name: "text-index" },
|
|
27971
28043
|
// { key: { name: 1 }, unique: true, name: "unique-name-index" },
|
|
@@ -27979,6 +28051,14 @@ function useBuildingRepo() {
|
|
|
27979
28051
|
partialFilterExpression: {
|
|
27980
28052
|
status: "active" /* ACTIVE */
|
|
27981
28053
|
}
|
|
28054
|
+
},
|
|
28055
|
+
{
|
|
28056
|
+
key: { site: 1, name: 1 },
|
|
28057
|
+
unique: true,
|
|
28058
|
+
name: "unique-site-name-active",
|
|
28059
|
+
partialFilterExpression: {
|
|
28060
|
+
status: "active" /* ACTIVE */
|
|
28061
|
+
}
|
|
27982
28062
|
}
|
|
27983
28063
|
]);
|
|
27984
28064
|
} catch (error) {
|
|
@@ -27986,9 +28066,45 @@ function useBuildingRepo() {
|
|
|
27986
28066
|
}
|
|
27987
28067
|
}
|
|
27988
28068
|
const { getBySiteBuildingLevel: _getBySiteBuildingLevel } = useBuildingUnitRepo();
|
|
28069
|
+
async function assertBuildingDoesNotExist({
|
|
28070
|
+
site,
|
|
28071
|
+
name,
|
|
28072
|
+
block,
|
|
28073
|
+
excludeId,
|
|
28074
|
+
session
|
|
28075
|
+
}) {
|
|
28076
|
+
const duplicates = [];
|
|
28077
|
+
if (name) {
|
|
28078
|
+
duplicates.push({ name });
|
|
28079
|
+
}
|
|
28080
|
+
if (block) {
|
|
28081
|
+
duplicates.push({ block });
|
|
28082
|
+
}
|
|
28083
|
+
if (!duplicates.length) {
|
|
28084
|
+
return;
|
|
28085
|
+
}
|
|
28086
|
+
const existing = await collection.findOne(
|
|
28087
|
+
{
|
|
28088
|
+
site,
|
|
28089
|
+
status: "active" /* ACTIVE */,
|
|
28090
|
+
...excludeId && { _id: { $ne: excludeId } },
|
|
28091
|
+
$or: duplicates
|
|
28092
|
+
},
|
|
28093
|
+
{ session }
|
|
28094
|
+
);
|
|
28095
|
+
if (existing) {
|
|
28096
|
+
throw new BadRequestError80("Building already exists.");
|
|
28097
|
+
}
|
|
28098
|
+
}
|
|
27989
28099
|
async function add(value, session) {
|
|
27990
28100
|
try {
|
|
27991
28101
|
value = MBuilding(value);
|
|
28102
|
+
await assertBuildingDoesNotExist({
|
|
28103
|
+
site: value.site,
|
|
28104
|
+
name: value.name,
|
|
28105
|
+
block: value.block,
|
|
28106
|
+
session
|
|
28107
|
+
});
|
|
27992
28108
|
const res = await collection.insertOne(value, { session });
|
|
27993
28109
|
delCachedData();
|
|
27994
28110
|
return res.insertedId;
|
|
@@ -28027,6 +28143,21 @@ function useBuildingRepo() {
|
|
|
28027
28143
|
throw new BadRequestError80("Invalid level ID format.");
|
|
28028
28144
|
}
|
|
28029
28145
|
}
|
|
28146
|
+
if (value.name || value.block) {
|
|
28147
|
+
const currentBuilding = await collection.findOne(
|
|
28148
|
+
{ _id },
|
|
28149
|
+
{ session }
|
|
28150
|
+
);
|
|
28151
|
+
if (currentBuilding) {
|
|
28152
|
+
await assertBuildingDoesNotExist({
|
|
28153
|
+
site: currentBuilding.site,
|
|
28154
|
+
name: value.name,
|
|
28155
|
+
block: value.block,
|
|
28156
|
+
excludeId: _id,
|
|
28157
|
+
session
|
|
28158
|
+
});
|
|
28159
|
+
}
|
|
28160
|
+
}
|
|
28030
28161
|
if (value.name)
|
|
28031
28162
|
await buildingUnitCollection.updateMany(
|
|
28032
28163
|
{ building: _id, status: "active" /* ACTIVE */ },
|
|
@@ -28256,7 +28387,8 @@ function useBuildingRepo() {
|
|
|
28256
28387
|
try {
|
|
28257
28388
|
const res = await collection.updateOne(
|
|
28258
28389
|
{ _id },
|
|
28259
|
-
{ $set: { status: "deleted"
|
|
28390
|
+
{ $set: { status: "deleted" /* DELETED */, deletedAt: /* @__PURE__ */ new Date() } },
|
|
28391
|
+
{ session }
|
|
28260
28392
|
);
|
|
28261
28393
|
delCachedData();
|
|
28262
28394
|
return res;
|
|
@@ -29165,18 +29297,19 @@ function useBuildingService() {
|
|
|
29165
29297
|
}
|
|
29166
29298
|
}
|
|
29167
29299
|
const buildingUnitData = {};
|
|
29168
|
-
if (building.name !== data.name) {
|
|
29300
|
+
if (data.name && building.name !== data.name) {
|
|
29169
29301
|
buildingUnitData.buildingName = data.name;
|
|
29170
29302
|
}
|
|
29171
|
-
if (building.block !== data.block) {
|
|
29303
|
+
if (data.block && building.block !== data.block) {
|
|
29172
29304
|
buildingUnitData.block = data.block;
|
|
29173
29305
|
}
|
|
29174
|
-
await updateByBuildingId(id, buildingUnitData, session);
|
|
29175
29306
|
if (building.name === data.name) {
|
|
29176
29307
|
delete data.name;
|
|
29177
29308
|
}
|
|
29178
|
-
console.log("data", data);
|
|
29179
29309
|
const result = await _updateById(id, data, session);
|
|
29310
|
+
if (Object.keys(buildingUnitData).length) {
|
|
29311
|
+
await updateByBuildingId(id, buildingUnitData, session);
|
|
29312
|
+
}
|
|
29180
29313
|
await session.commitTransaction();
|
|
29181
29314
|
return result;
|
|
29182
29315
|
} catch (error) {
|
|
@@ -57331,7 +57464,7 @@ function useNewDashboardRepo() {
|
|
|
57331
57464
|
const periodRange = getDateRange(period);
|
|
57332
57465
|
const incidentCollection = db.collection(incidents_namespace_collection);
|
|
57333
57466
|
const visitorCollection = db.collection(visitors_namespace_collection);
|
|
57334
|
-
const
|
|
57467
|
+
const patrolLogCollection = db.collection("patrol.logs");
|
|
57335
57468
|
const [
|
|
57336
57469
|
workOrderReport,
|
|
57337
57470
|
yesterdayWorkOrderReport,
|
|
@@ -57458,7 +57591,7 @@ function useNewDashboardRepo() {
|
|
|
57458
57591
|
checkOut: null,
|
|
57459
57592
|
status: { $ne: "deleted" }
|
|
57460
57593
|
}),
|
|
57461
|
-
|
|
57594
|
+
patrolLogCollection.aggregate([
|
|
57462
57595
|
{
|
|
57463
57596
|
$match: {
|
|
57464
57597
|
site: { $in: [siteIdObj, siteId] },
|
|
@@ -57466,23 +57599,23 @@ function useNewDashboardRepo() {
|
|
|
57466
57599
|
}
|
|
57467
57600
|
},
|
|
57468
57601
|
{
|
|
57469
|
-
$unwind: "$
|
|
57602
|
+
$unwind: "$cameras"
|
|
57470
57603
|
},
|
|
57471
57604
|
{
|
|
57472
57605
|
$facet: {
|
|
57473
57606
|
total: [{ $count: "count" }],
|
|
57474
57607
|
completed: [
|
|
57475
|
-
{ $match: { "
|
|
57608
|
+
{ $match: { "cameras.status": "Completed" } },
|
|
57476
57609
|
{ $count: "count" }
|
|
57477
57610
|
],
|
|
57478
57611
|
skipped: [
|
|
57479
|
-
{ $match: { "
|
|
57612
|
+
{ $match: { "cameras.status": "Skipped" } },
|
|
57480
57613
|
{ $count: "count" }
|
|
57481
57614
|
]
|
|
57482
57615
|
}
|
|
57483
57616
|
}
|
|
57484
57617
|
]).toArray(),
|
|
57485
|
-
|
|
57618
|
+
patrolLogCollection.aggregate([
|
|
57486
57619
|
{
|
|
57487
57620
|
$match: {
|
|
57488
57621
|
site: { $in: [siteIdObj, siteId] },
|
|
@@ -57490,23 +57623,23 @@ function useNewDashboardRepo() {
|
|
|
57490
57623
|
}
|
|
57491
57624
|
},
|
|
57492
57625
|
{
|
|
57493
|
-
$unwind: "$
|
|
57626
|
+
$unwind: "$cameras"
|
|
57494
57627
|
},
|
|
57495
57628
|
{
|
|
57496
57629
|
$facet: {
|
|
57497
57630
|
total: [{ $count: "count" }],
|
|
57498
57631
|
completed: [
|
|
57499
|
-
{ $match: { "
|
|
57632
|
+
{ $match: { "cameras.status": "Completed" } },
|
|
57500
57633
|
{ $count: "count" }
|
|
57501
57634
|
],
|
|
57502
57635
|
skipped: [
|
|
57503
|
-
{ $match: { "
|
|
57636
|
+
{ $match: { "cameras.status": "Skipped" } },
|
|
57504
57637
|
{ $count: "count" }
|
|
57505
57638
|
]
|
|
57506
57639
|
}
|
|
57507
57640
|
}
|
|
57508
57641
|
]).toArray(),
|
|
57509
|
-
|
|
57642
|
+
patrolLogCollection.aggregate([
|
|
57510
57643
|
{
|
|
57511
57644
|
$match: {
|
|
57512
57645
|
site: { $in: [siteIdObj, siteId] },
|
|
@@ -57514,17 +57647,17 @@ function useNewDashboardRepo() {
|
|
|
57514
57647
|
}
|
|
57515
57648
|
},
|
|
57516
57649
|
{
|
|
57517
|
-
$unwind: "$
|
|
57650
|
+
$unwind: "$cameras"
|
|
57518
57651
|
},
|
|
57519
57652
|
{
|
|
57520
57653
|
$facet: {
|
|
57521
57654
|
total: [{ $count: "count" }],
|
|
57522
57655
|
completed: [
|
|
57523
|
-
{ $match: { "
|
|
57656
|
+
{ $match: { "cameras.status": "Completed" } },
|
|
57524
57657
|
{ $count: "count" }
|
|
57525
57658
|
],
|
|
57526
57659
|
skipped: [
|
|
57527
|
-
{ $match: { "
|
|
57660
|
+
{ $match: { "cameras.status": "Skipped" } },
|
|
57528
57661
|
{ $count: "count" }
|
|
57529
57662
|
]
|
|
57530
57663
|
}
|
|
@@ -57555,78 +57688,127 @@ function useNewDashboardRepo() {
|
|
|
57555
57688
|
const tTotal = tPatrolFacet.total[0]?.count ?? 0;
|
|
57556
57689
|
const tCompleted = tPatrolFacet.completed[0]?.count ?? 0;
|
|
57557
57690
|
const todayCompliance = tTotal > 0 ? tCompleted / tTotal * 100 : 0;
|
|
57558
|
-
|
|
57559
|
-
|
|
57560
|
-
|
|
57561
|
-
|
|
57562
|
-
|
|
57563
|
-
|
|
57564
|
-
|
|
57565
|
-
|
|
57566
|
-
|
|
57567
|
-
|
|
57568
|
-
|
|
57691
|
+
let activePatrolItems = [];
|
|
57692
|
+
if (period === "today" /* TODAY */) {
|
|
57693
|
+
const todayString = moment2.tz("Asia/Singapore").format("YYYY-MM-DD");
|
|
57694
|
+
const dayIndex = moment2.tz("Asia/Singapore").day();
|
|
57695
|
+
const repeatDay = dayIndex === 0 ? 7 : dayIndex;
|
|
57696
|
+
const routes = await db.collection("patrol.route").find({
|
|
57697
|
+
site: { $in: [siteIdObj, siteId] },
|
|
57698
|
+
repeat: { $in: [repeatDay, String(repeatDay)] },
|
|
57699
|
+
status: { $ne: "deleted" }
|
|
57700
|
+
}).toArray();
|
|
57701
|
+
const expandedRoutes = [];
|
|
57702
|
+
for (const route of routes) {
|
|
57703
|
+
if (route.start) {
|
|
57569
57704
|
expandedRoutes.push({
|
|
57570
57705
|
route,
|
|
57571
|
-
startTime,
|
|
57706
|
+
startTime: route.start,
|
|
57572
57707
|
itemIndex: 0
|
|
57573
57708
|
});
|
|
57574
57709
|
}
|
|
57575
57710
|
}
|
|
57576
|
-
|
|
57577
|
-
|
|
57578
|
-
|
|
57579
|
-
|
|
57580
|
-
|
|
57581
|
-
|
|
57582
|
-
|
|
57583
|
-
|
|
57584
|
-
|
|
57585
|
-
|
|
57586
|
-
|
|
57587
|
-
"route._id": { $in: [route._id, route._id.toString()] },
|
|
57588
|
-
"route.startTime": startTime
|
|
57589
|
-
});
|
|
57590
|
-
let person = "Unassigned";
|
|
57591
|
-
if (log && log.createdBy) {
|
|
57592
|
-
const userDoc = await db.collection("users").findOne({
|
|
57593
|
-
_id: toObjectId16(log.createdBy)
|
|
57711
|
+
expandedRoutes.sort((a, b) => a.startTime.localeCompare(b.startTime));
|
|
57712
|
+
const limitedRoutes = expandedRoutes.slice(0, 4);
|
|
57713
|
+
limitedRoutes.forEach((item, idx) => {
|
|
57714
|
+
item.itemIndex = idx + 1;
|
|
57715
|
+
});
|
|
57716
|
+
activePatrolItems = await Promise.all(
|
|
57717
|
+
limitedRoutes.map(async ({ route, startTime, itemIndex }) => {
|
|
57718
|
+
const log = await db.collection("patrol.logs").findOne({
|
|
57719
|
+
site: { $in: [siteIdObj, siteId] },
|
|
57720
|
+
route: { $in: [route._id, route._id.toString()] },
|
|
57721
|
+
createdAt: { $gte: today, $lte: todayEnd }
|
|
57594
57722
|
});
|
|
57595
|
-
|
|
57596
|
-
|
|
57723
|
+
let person = "Unassigned";
|
|
57724
|
+
if (log && log.assignee && log.assignee.length > 0) {
|
|
57725
|
+
const userIds = log.assignee.map((id) => toObjectId16(id));
|
|
57726
|
+
const userDocs = await db.collection("members").find({ _id: { $in: userIds } }).toArray();
|
|
57727
|
+
if (userDocs && userDocs.length > 0) {
|
|
57728
|
+
person = userDocs.map((u) => u.name || u.email || "").filter(Boolean).join(", ");
|
|
57729
|
+
}
|
|
57730
|
+
} else if (log && log.createdBy) {
|
|
57731
|
+
const userDoc = await db.collection("members").findOne({
|
|
57732
|
+
_id: toObjectId16(log.createdBy)
|
|
57733
|
+
});
|
|
57734
|
+
if (userDoc && userDoc.name) {
|
|
57735
|
+
person = userDoc.name;
|
|
57736
|
+
}
|
|
57597
57737
|
}
|
|
57598
|
-
|
|
57599
|
-
|
|
57600
|
-
|
|
57601
|
-
|
|
57602
|
-
} else {
|
|
57603
|
-
const routeTime = moment2.tz(
|
|
57604
|
-
`${todayString} ${startTime}`,
|
|
57605
|
-
"YYYY-MM-DD HH:mm",
|
|
57606
|
-
"Asia/Singapore"
|
|
57607
|
-
);
|
|
57608
|
-
const nowSg = moment2.tz("Asia/Singapore");
|
|
57609
|
-
const diffMinutes = nowSg.diff(routeTime, "minutes");
|
|
57610
|
-
if (diffMinutes >= 0) {
|
|
57611
|
-
if (diffMinutes <= 120) {
|
|
57612
|
-
status = "on paused";
|
|
57738
|
+
let status = "pending";
|
|
57739
|
+
if (log) {
|
|
57740
|
+
if (log.status) {
|
|
57741
|
+
status = Array.isArray(log.status) ? log.status.join(", ") : log.status;
|
|
57613
57742
|
} else {
|
|
57614
|
-
status = "
|
|
57743
|
+
status = "completed";
|
|
57615
57744
|
}
|
|
57616
57745
|
} else {
|
|
57617
|
-
|
|
57746
|
+
const routeTime = moment2.tz(
|
|
57747
|
+
`${todayString} ${startTime}`,
|
|
57748
|
+
"YYYY-MM-DD HH:mm",
|
|
57749
|
+
"Asia/Singapore"
|
|
57750
|
+
);
|
|
57751
|
+
const nowSg = moment2.tz("Asia/Singapore");
|
|
57752
|
+
const diffMinutes = nowSg.diff(routeTime, "minutes");
|
|
57753
|
+
if (diffMinutes >= 0) {
|
|
57754
|
+
if (diffMinutes <= 120) {
|
|
57755
|
+
status = "on paused";
|
|
57756
|
+
} else {
|
|
57757
|
+
status = "incomplete";
|
|
57758
|
+
}
|
|
57759
|
+
} else {
|
|
57760
|
+
status = "pending";
|
|
57761
|
+
}
|
|
57618
57762
|
}
|
|
57619
|
-
|
|
57620
|
-
|
|
57621
|
-
|
|
57622
|
-
|
|
57623
|
-
|
|
57624
|
-
|
|
57625
|
-
|
|
57626
|
-
|
|
57627
|
-
}
|
|
57628
|
-
|
|
57629
|
-
|
|
57763
|
+
return {
|
|
57764
|
+
id: `${route._id.toString()}_${startTime}`,
|
|
57765
|
+
title: route.name,
|
|
57766
|
+
subtitle: `ID${String(itemIndex).padStart(3, "0")}`,
|
|
57767
|
+
person,
|
|
57768
|
+
status,
|
|
57769
|
+
time: startTime
|
|
57770
|
+
};
|
|
57771
|
+
})
|
|
57772
|
+
);
|
|
57773
|
+
} else {
|
|
57774
|
+
const logs = await db.collection("patrol.logs").find({
|
|
57775
|
+
site: { $in: [siteIdObj, siteId] },
|
|
57776
|
+
createdAt: periodRange
|
|
57777
|
+
}).sort({ createdAt: -1 }).limit(4).toArray();
|
|
57778
|
+
activePatrolItems = await Promise.all(
|
|
57779
|
+
logs.map(async (log) => {
|
|
57780
|
+
let person = "Unassigned";
|
|
57781
|
+
if (log.assignee && log.assignee.length > 0) {
|
|
57782
|
+
const userIds = log.assignee.map((id) => toObjectId16(id));
|
|
57783
|
+
const userDocs = await db.collection("members").find({ _id: { $in: userIds } }).toArray();
|
|
57784
|
+
if (userDocs && userDocs.length > 0) {
|
|
57785
|
+
person = userDocs.map((u) => u.name || u.email || "").filter(Boolean).join(", ");
|
|
57786
|
+
}
|
|
57787
|
+
} else if (log.createdBy) {
|
|
57788
|
+
const userDoc = await db.collection("members").findOne({
|
|
57789
|
+
_id: toObjectId16(log.createdBy)
|
|
57790
|
+
});
|
|
57791
|
+
if (userDoc && userDoc.name) {
|
|
57792
|
+
person = userDoc.name;
|
|
57793
|
+
}
|
|
57794
|
+
}
|
|
57795
|
+
let status = "completed";
|
|
57796
|
+
if (log.status) {
|
|
57797
|
+
status = Array.isArray(log.status) ? log.status.join(", ") : log.status;
|
|
57798
|
+
}
|
|
57799
|
+
const logTime = moment2(log.createdAt).tz("Asia/Singapore").format("HH:mm");
|
|
57800
|
+
const logDate = moment2(log.createdAt).tz("Asia/Singapore").format("DD/MM/YYYY");
|
|
57801
|
+
return {
|
|
57802
|
+
id: log._id.toString(),
|
|
57803
|
+
title: log.name,
|
|
57804
|
+
subtitle: logDate,
|
|
57805
|
+
person,
|
|
57806
|
+
status,
|
|
57807
|
+
time: logTime
|
|
57808
|
+
};
|
|
57809
|
+
})
|
|
57810
|
+
);
|
|
57811
|
+
}
|
|
57630
57812
|
const data = {
|
|
57631
57813
|
openWorkOrder: {
|
|
57632
57814
|
count: wFacet.total[0]?.count ?? 0,
|
|
@@ -65591,6 +65773,74 @@ function usePostPrelovedRepo() {
|
|
|
65591
65773
|
throw error;
|
|
65592
65774
|
}
|
|
65593
65775
|
}
|
|
65776
|
+
async function getByOwnerId(ownerId, {
|
|
65777
|
+
search = "",
|
|
65778
|
+
page = 1,
|
|
65779
|
+
limit = 10,
|
|
65780
|
+
filter,
|
|
65781
|
+
site,
|
|
65782
|
+
status,
|
|
65783
|
+
category
|
|
65784
|
+
}, session) {
|
|
65785
|
+
page = page > 0 ? page - 1 : 0;
|
|
65786
|
+
let ownerObjectId;
|
|
65787
|
+
try {
|
|
65788
|
+
ownerObjectId = new ObjectId137(ownerId);
|
|
65789
|
+
} catch {
|
|
65790
|
+
throw new BadRequestError212("Invalid user ID format.");
|
|
65791
|
+
}
|
|
65792
|
+
if (site) {
|
|
65793
|
+
try {
|
|
65794
|
+
site = new ObjectId137(site);
|
|
65795
|
+
} catch {
|
|
65796
|
+
throw new BadRequestError212("Invalid site ID format.");
|
|
65797
|
+
}
|
|
65798
|
+
}
|
|
65799
|
+
let categoryIds = null;
|
|
65800
|
+
if (Array.isArray(category) && category.length > 0) {
|
|
65801
|
+
categoryIds = category.map((cat) => {
|
|
65802
|
+
try {
|
|
65803
|
+
return new ObjectId137(cat);
|
|
65804
|
+
} catch {
|
|
65805
|
+
throw new BadRequestError212("Invalid category ID format.");
|
|
65806
|
+
}
|
|
65807
|
+
});
|
|
65808
|
+
}
|
|
65809
|
+
const query = {
|
|
65810
|
+
status: { $ne: "deleted" /* DELETED */ },
|
|
65811
|
+
createdBy: ownerObjectId,
|
|
65812
|
+
...site && { site },
|
|
65813
|
+
...search && {
|
|
65814
|
+
$or: [
|
|
65815
|
+
{ title: { $regex: search, $options: "i" } },
|
|
65816
|
+
{ description: { $regex: search, $options: "i" } }
|
|
65817
|
+
]
|
|
65818
|
+
},
|
|
65819
|
+
...status && {
|
|
65820
|
+
status: { $in: Array.isArray(status) ? status : [status] }
|
|
65821
|
+
},
|
|
65822
|
+
...categoryIds && { category: { $in: categoryIds } }
|
|
65823
|
+
};
|
|
65824
|
+
const sortObj = buildSortObj(filter);
|
|
65825
|
+
try {
|
|
65826
|
+
const items = await collection.aggregate(
|
|
65827
|
+
[
|
|
65828
|
+
{ $match: query },
|
|
65829
|
+
USER_LOOKUP,
|
|
65830
|
+
USER_UNWIND,
|
|
65831
|
+
CATEGORY_LOOKUP,
|
|
65832
|
+
{ $sort: sortObj },
|
|
65833
|
+
{ $skip: page * limit },
|
|
65834
|
+
{ $limit: limit }
|
|
65835
|
+
],
|
|
65836
|
+
{ session }
|
|
65837
|
+
).toArray();
|
|
65838
|
+
const length = await collection.countDocuments(query, { session });
|
|
65839
|
+
return paginate61(items, page, limit, length);
|
|
65840
|
+
} catch (error) {
|
|
65841
|
+
throw error;
|
|
65842
|
+
}
|
|
65843
|
+
}
|
|
65594
65844
|
async function updateById(_id, value, session) {
|
|
65595
65845
|
try {
|
|
65596
65846
|
_id = new ObjectId137(_id);
|
|
@@ -65661,6 +65911,7 @@ function usePostPrelovedRepo() {
|
|
|
65661
65911
|
add,
|
|
65662
65912
|
getById,
|
|
65663
65913
|
getAll,
|
|
65914
|
+
getByOwnerId,
|
|
65664
65915
|
updateById,
|
|
65665
65916
|
deleteById,
|
|
65666
65917
|
updateStatus
|
|
@@ -65875,6 +66126,7 @@ function usePostPrelovedController() {
|
|
|
65875
66126
|
const {
|
|
65876
66127
|
getById: _getById,
|
|
65877
66128
|
getAll: _getAll,
|
|
66129
|
+
getByOwnerId: _getByOwnerId,
|
|
65878
66130
|
updateById: _updateById,
|
|
65879
66131
|
deleteById: _deleteById,
|
|
65880
66132
|
updateStatus: _updateStatus
|
|
@@ -65978,6 +66230,49 @@ function usePostPrelovedController() {
|
|
|
65978
66230
|
return;
|
|
65979
66231
|
}
|
|
65980
66232
|
}
|
|
66233
|
+
async function getByOwnerId(req, res, next) {
|
|
66234
|
+
const validation = Joi135.object({
|
|
66235
|
+
ownerId: Joi135.string().hex().length(24).required(),
|
|
66236
|
+
page: Joi135.number().integer().min(1).allow("", null).default(1),
|
|
66237
|
+
limit: Joi135.number().integer().min(1).max(100).allow("", null).default(10),
|
|
66238
|
+
search: Joi135.string().optional().allow("", null),
|
|
66239
|
+
filter: Joi135.string().valid("recent", "price-low-high", "price-high-low").optional().allow("", null),
|
|
66240
|
+
site: Joi135.string().hex().length(24).optional().allow("", null),
|
|
66241
|
+
status: Joi135.alternatives().try(
|
|
66242
|
+
Joi135.array().items(Joi135.string().valid(...Object.values(PostStatus))),
|
|
66243
|
+
Joi135.string().valid(...Object.values(PostStatus))
|
|
66244
|
+
).optional().allow(null),
|
|
66245
|
+
category: Joi135.array().items(Joi135.string().hex().length(24)).single().optional().allow(null)
|
|
66246
|
+
});
|
|
66247
|
+
const { error, value } = validation.validate(
|
|
66248
|
+
{ ...req.query, ownerId: req.params.ownerId },
|
|
66249
|
+
{ abortEarly: false }
|
|
66250
|
+
);
|
|
66251
|
+
if (error) {
|
|
66252
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
66253
|
+
logger188.log({ level: "error", message: messages });
|
|
66254
|
+
next(new BadRequestError214(messages));
|
|
66255
|
+
return;
|
|
66256
|
+
}
|
|
66257
|
+
const { ownerId, page, limit, search, filter, site, status, category } = value;
|
|
66258
|
+
try {
|
|
66259
|
+
const data = await _getByOwnerId(ownerId, {
|
|
66260
|
+
page,
|
|
66261
|
+
limit,
|
|
66262
|
+
search,
|
|
66263
|
+
filter,
|
|
66264
|
+
site,
|
|
66265
|
+
status: status ? Array.isArray(status) ? status : [status] : void 0,
|
|
66266
|
+
category: category ?? void 0
|
|
66267
|
+
});
|
|
66268
|
+
res.status(200).json(data);
|
|
66269
|
+
return;
|
|
66270
|
+
} catch (error2) {
|
|
66271
|
+
logger188.log({ level: "error", message: error2.message });
|
|
66272
|
+
next(error2);
|
|
66273
|
+
return;
|
|
66274
|
+
}
|
|
66275
|
+
}
|
|
65981
66276
|
async function updateById(req, res, next) {
|
|
65982
66277
|
const _id = req.params.id;
|
|
65983
66278
|
const payload = { _id, ...req.body };
|
|
@@ -66048,6 +66343,7 @@ function usePostPrelovedController() {
|
|
|
66048
66343
|
add,
|
|
66049
66344
|
getById,
|
|
66050
66345
|
getAll,
|
|
66346
|
+
getByOwnerId,
|
|
66051
66347
|
updateById,
|
|
66052
66348
|
deleteById,
|
|
66053
66349
|
updateStatus
|