Files
permit-api/src/notification/notification.service.ts

315 lines
7.8 KiB
TypeScript

import mongoose from "mongoose";
import { AuthenticatedUser } from "../auth";
import { orgModel } from "../organization/organization.schema";
import {
getFilterObject,
getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter,
PageQueryParams,
} from "../pagination";
import { generateId } from "../utils/id";
import {
CreateNotificationInput,
notificationFields,
notificationModel,
UpdateNotificationInput,
} from "./notification.schema";
import { getUser } from "../user/user.service";
import { createNote } from "../note/note.service";
import { createAlert } from "../alert/alert.service";
import { arrayDiff } from "../utils/diff";
export async function createNotification(
input: CreateNotificationInput,
tenantId: string,
) {
const notification = await notificationModel.create({
...input,
pid: generateId(),
tenantId: tenantId,
createdAt: new Date(),
});
if (input.assignedToOrg) {
}
return await notificationModel
.findOne({ pid: notification.pid })
.populate({ path: "assignedTo", select: "pid name avatar" });
}
export async function getNotification(
notifId: string,
user: AuthenticatedUser,
) {
return await notificationModel
.findOne({
tenantId: user.tenantId,
pid: notifId,
})
.populate({ path: "assignedTo", select: "pid name avatar" });
}
export async function updateNotification(
notifId: string,
input: UpdateNotificationInput,
user: AuthenticatedUser,
) {
if (input.assignedToOrg && input.assignedTo) {
delete input.assignedTo;
}
const oldNotification = await notificationModel.findOne(
{ pid: notifId },
{ assignedTo: 1, assignedToOrg: 1 },
);
const updateNotificationResult = await notificationModel
.findOneAndUpdate(
{ $and: [{ tenantId: user.tenantId }, { pid: notifId }] },
{
...input,
updatedAt: new Date(),
},
{ new: true },
)
.populate({ path: "assignedTo", select: "pid name avatar" });
if (updateNotificationResult) {
for (const key in input) {
if (["status"].includes(key)) {
let msg = "";
if (input[key] === null) {
msg = `Cleared ${key}`;
} else {
msg = `Updated ${key} to '${input[key]}'`;
}
await createNote(
{
content: msg,
},
notifId,
"notifications",
user,
);
} else if (key == "assignedTo") {
const newAssignees = arrayDiff(
updateNotificationResult.assignedTo.map((item) => item._id),
oldNotification.assignedTo,
);
if (newAssignees.length > 0) {
let msg = "Assigned to:\n\n";
for (const assignee of newAssignees) {
const user = await getUser(assignee);
if (!user) continue;
msg += `${user.firstName + " " + user.lastName}\n`;
await createAlert(
user.tenantId,
`You are assigned to ${updateNotificationResult.permitNumber}`,
"user",
assignee,
updateNotificationResult.pid,
"permits",
{
client: updateNotificationResult.client.toString(),
county: updateNotificationResult.county.id.toString(),
},
);
}
await createNote(
{
content: msg,
},
notifId,
"notifications",
user,
);
}
} else if (key == "assignedToOrg") {
if (
oldNotification.assignedToOrg ==
updateNotificationResult.assignedToOrg
)
continue;
const orgName =
input.assignedToOrg == "agent"
? "Suncoast"
: updateNotificationResult.clientData.name;
await createAlert(
user.tenantId,
`${orgName} assigned to ${updateNotificationResult.permitNumber}`,
"team",
input.assignedToOrg == "client"
? updateNotificationResult.client.toString()
: process.env.SUNCOAST_ID,
updateNotificationResult.pid,
"permits",
{
client: updateNotificationResult.client.toString(),
county: updateNotificationResult.county.id.toString(),
},
);
}
}
}
return updateNotificationResult;
}
export async function listNotifications(
params: PageQueryParams,
user: AuthenticatedUser,
) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, notificationFields);
let filterObj = getFilterObject(params) || [];
filterObj.forEach((item) => {
if (item.changes) {
if (item.changes.$eq) {
item[`changes.${item.changes.$eq}`] = { $exists: true };
} else if (item.changes.$in) {
item.changes.$in.forEach((value) => {
item[`changes.${value}`] = { $exists: true };
});
}
delete item["changes"];
}
});
if (user.role == "client") {
filterObj.push({
client: {
$in: user.orgId.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj,
sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj,
sortObj,
);
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const pipeline: any = [
{
$match: { $and: [{ tenantId: user.tenantId }, ...filterObj] },
},
];
if (params.searchToken && params.searchToken != "") {
const regex = new RegExp(params.searchToken, "i");
pipeline.push({
$match: {
$or: [{ permitNumber: { $regex: regex } }, { link: { $regex: regex } }],
},
});
}
pipeline.push(
...[
...taggedUsersFilter,
...taggedOrgsFilter,
{
$lookup: {
from: "users",
localField: "assignedTo",
foreignField: "_id",
as: "assignedTo",
},
},
{
$project: {
_id: 1,
pid: 1,
permitId: 1,
tenantId: 1,
permitNumber: 1,
link: 1,
status: 1,
permitType: 1,
accelaStatus: 1,
changes: 1,
county: 1,
client: 1,
clientData: 1,
createdAt: 1,
updatedAt: 1,
taggedUsers: 1,
taggedOrgs: 1,
assignedToOrg: 1,
assignedTo: {
$map: {
input: "$assignedTo",
as: "user",
in: {
_id: "$$user._id",
pid: "$$user.pid",
name: "$$user.name",
avatar: "$$user.avatar",
},
},
},
},
},
{
$facet: {
metadata: [{ $count: "count" }],
data: [
{ $sort: sortObj },
{ $skip: (page - 1) * pageSize },
{ $limit: pageSize },
],
},
},
],
);
const notifications = await notificationModel.aggregate(pipeline);
if (notifications[0].data.length === 0)
return { notifications: [], metadata: { count: 0, page, pageSize } };
return {
notifications: notifications[0].data,
metadata: {
count: notifications[0].metadata[0].count,
page,
pageSize,
},
};
}
export async function deleteNotification(notifId: string, tenantId: string) {
return await notificationModel.deleteOne({
$and: [{ tenantId: tenantId }, { pid: notifId }],
});
}