feat: add taggedOrgs feature

This commit is contained in:
2026-01-26 11:16:30 +05:30
parent 9e3f0f8b07
commit ccde4f8356
15 changed files with 237 additions and 112 deletions

View File

@@ -16,6 +16,7 @@ export const alertsModel = mongoose.model(
county: { type: String, ref: "organization" }, county: { type: String, ref: "organization" },
permitType: String, permitType: String,
address: String, address: String,
createdBy: { type: String, ref: "user" },
}, },
recipientType: { recipientType: {
type: String, type: String,
@@ -28,7 +29,7 @@ export const alertsModel = mongoose.model(
type: [String], type: [String],
default: [], default: [],
}, },
}) }),
); );
const alertResponse = z.object({ const alertResponse = z.object({
@@ -43,6 +44,7 @@ const alertResponse = z.object({
county: z.any().optional(), county: z.any().optional(),
permitType: z.string().optional(), permitType: z.string().optional(),
address: z.string().optional(), address: z.string().optional(),
createdBy: z.any().optional(),
}) })
.optional(), .optional(),
recipientType: z.enum(["user", "team"]), recipientType: z.enum(["user", "team"]),
@@ -64,5 +66,5 @@ export const { schemas: alertSchemas, $ref: $alert } = buildJsonSchemas(
listAlertResponse, listAlertResponse,
pageQueryParams, pageQueryParams,
}, },
{ $id: "alert" } { $id: "alert" },
); );

View File

@@ -16,7 +16,8 @@ export async function createAlert(
county?: String; county?: String;
permitType?: String; permitType?: String;
address?: String; address?: String;
} createdBy?: String;
},
) { ) {
const newAlert = await alertsModel.create({ const newAlert = await alertsModel.create({
tenantId, tenantId,
@@ -44,13 +45,13 @@ export async function createAlert(
createdAt: new Date(), createdAt: new Date(),
}, },
}, },
["alert:read"] ["alert:read"],
); );
} }
export async function getUserAlerts( export async function getUserAlerts(
user: AuthenticatedUser, user: AuthenticatedUser,
params: PageQueryParams params: PageQueryParams,
) { ) {
const page = params.page || 1; const page = params.page || 1;
const pageSize = params.pageSize || 10; const pageSize = params.pageSize || 10;
@@ -71,7 +72,8 @@ export async function getUserAlerts(
.limit(pageSize) .limit(pageSize)
.skip((page - 1) * pageSize) .skip((page - 1) * pageSize)
.populate({ path: "metaFields.client", select: "pid name avatar" }) .populate({ path: "metaFields.client", select: "pid name avatar" })
.populate({ path: "metaFields.county", select: "pid name avatar" }); .populate({ path: "metaFields.county", select: "pid name avatar" })
.populate({ path: "metaFields.createdBy", select: "pid name avatar" });
const modifiedAlerts = alerts.map((alert) => { const modifiedAlerts = alerts.map((alert) => {
return { return {
@@ -89,7 +91,7 @@ export async function markAsRead(alertId: string, user: AuthenticatedUser) {
const updatedAlert = await alertsModel.findOneAndUpdate( const updatedAlert = await alertsModel.findOneAndUpdate(
{ tenantId: user.tenantId, pid: alertId }, { tenantId: user.tenantId, pid: alertId },
{ $addToSet: { readBy: user.userId } }, { $addToSet: { readBy: user.userId } },
{ new: true } { new: true },
); );
if (!updatedAlert) return null; if (!updatedAlert) return null;
@@ -116,7 +118,7 @@ export async function markAllRead(user: AuthenticatedUser) {
const updatedResult = await alertsModel.updateMany( const updatedResult = await alertsModel.updateMany(
{ $or: filters }, { $or: filters },
{ $addToSet: { readBy: user.userId } } { $addToSet: { readBy: user.userId } },
); );
return updatedResult; return updatedResult;

View File

@@ -8,17 +8,10 @@ export async function createNote(
input: CreateNoteInput, input: CreateNoteInput,
resourceId: string, resourceId: string,
resourceType: string, resourceType: string,
user: AuthenticatedUser user: AuthenticatedUser,
) { ) {
const userIds = extractExpressions(input.content); const userIds = extractExpressions(input.content);
const taggedUsers = userIds.map((item) => {
return {
userId: item,
taggedAt: new Date(),
};
});
const newNote = await noteModel.create({ const newNote = await noteModel.create({
tenantId: user.tenantId, tenantId: user.tenantId,
pid: generateId(), pid: generateId(),
@@ -43,44 +36,86 @@ export async function createNote(
const model = modelMap[resourceType]; const model = modelMap[resourceType];
const obj = await model.findOne({ pid: resourceId }); const obj = await model.findOne({ pid: resourceId });
if (!obj.taggedUsers) { const orgs = [];
await model.updateOne( userIds.forEach((item) => {
{ pid: resourceId }, if (item == "client" && obj.client)
{ orgs.push({ orgId: obj.client.toString(), taggedAt: new Date() });
$set: { taggedUsers: taggedUsers }, if (item == "agent")
$addToSet: { assignedTo: userIds[0] }, orgs.push({ orgId: process.env.SUNCOAST_ID, taggedAt: new Date() });
} });
const taggedUsers = userIds
.filter((item) => !["client", "agent"].includes(item))
.map((item) => {
return {
userId: item,
taggedAt: new Date(),
};
});
for (const org of orgs) {
if (!obj.taggedOrgs) obj.taggedOrgs = [];
const orgIndex = obj.taggedOrgs.findIndex(
(item) => item.orgId == org.orgId,
); );
} else { if (orgIndex != -1) obj.taggedOrgs[orgIndex].taggedAt = new Date();
for (const user of taggedUsers) { else obj.taggedOrgs.push(org);
const userIndex = obj.taggedUsers.findIndex(
(item) => item.userId == user.userId
);
if (userIndex != -1) obj.taggedUsers[userIndex].taggedAt = new Date();
else obj.taggedUsers.push(user);
}
const assignee = obj.assignedTo.find(
(item) => item.toString() == userIds[0]
);
if (!assignee) obj.assignedTo.push(userIds[0]);
obj.markModified("taggedUsers", "assignedTo");
await obj.save();
} }
for (const id of userIds) { for (const user of taggedUsers) {
if (id == user.userId) continue; if (!obj.taggedUsers) obj.taggedUsers = [];
await createAlert( const userIndex = obj.taggedUsers.findIndex(
user.tenantId, (item) => item.userId == user.userId,
"You are tagged in a note",
"user",
id,
resourceId,
resourceType
); );
if (userIndex != -1) obj.taggedUsers[userIndex].taggedAt = new Date();
else obj.taggedUsers.push(user);
}
if (taggedUsers.length > 0) {
const assignee = obj.assignedTo.find(
(item) => item.toString() == taggedUsers[0].userId,
);
if (!assignee) obj.assignedTo.push(taggedUsers[0].userId);
}
obj.markModified("taggedUsers", "assignedTo", "taggedOrgs");
await obj.save();
if (taggedUsers.length > 0) {
for (const taggedUser of taggedUsers) {
if (taggedUser.userId == user.userId) continue;
await createAlert(
user.tenantId,
"You are tagged in a note",
"user",
taggedUser.userId,
resourceId,
resourceType,
{
createdBy: user.userId,
},
);
}
}
if (orgs.length > 0) {
for (const org of orgs) {
await createAlert(
user.tenantId,
`Your organization is tagged in a note`,
"team",
org.orgId,
resourceId,
resourceType,
{
createdBy: user.userId,
},
);
}
} }
} }
@@ -91,7 +126,7 @@ export async function updateNote(
input: CreateNoteInput, input: CreateNoteInput,
resourceId: string, resourceId: string,
noteId: string, noteId: string,
tenantId: string tenantId: string,
) { ) {
return await noteModel return await noteModel
.findOneAndUpdate( .findOneAndUpdate(
@@ -103,7 +138,7 @@ export async function updateNote(
], ],
}, },
{ ...input }, { ...input },
{ new: true } { new: true },
) )
.populate({ path: "createdBy", select: "pid name avatar" }); .populate({ path: "createdBy", select: "pid name avatar" });
} }
@@ -147,7 +182,7 @@ export async function listNotes(resourceId: string, tenantId: string) {
export async function deleteNote( export async function deleteNote(
resourceId: string, resourceId: string,
noteId: string, noteId: string,
tenantId: string tenantId: string,
) { ) {
return await noteModel.deleteOne({ return await noteModel.deleteOne({
$and: [{ pid: noteId }, { tenantId: tenantId }, { resourceId: resourceId }], $and: [{ pid: noteId }, { tenantId: tenantId }, { resourceId: resourceId }],

View File

@@ -32,15 +32,16 @@ const notificationSchema = new mongoose.Schema({
}, },
assignedToOrg: String, assignedToOrg: String,
taggedUsers: Array, taggedUsers: Array,
taggedOrgs: Array,
}); });
export const notificationFields = Object.keys(notificationSchema.paths).filter( export const notificationFields = Object.keys(notificationSchema.paths).filter(
(path) => path !== "__v" (path) => path !== "__v",
); );
export const notificationModel = mongoose.model( export const notificationModel = mongoose.model(
"notification", "notification",
notificationSchema notificationSchema,
); );
const createNotificationInput = z.object({ const createNotificationInput = z.object({
@@ -73,5 +74,5 @@ export const { schemas: notificationSchemas, $ref: $notification } =
updateNotificationInput, updateNotificationInput,
pageQueryParams, pageQueryParams,
}, },
{ $id: "notification" } { $id: "notification" },
); );

View File

@@ -4,6 +4,7 @@ import { orgModel } from "../organization/organization.schema";
import { import {
getFilterObject, getFilterObject,
getSortObject, getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter, getTaggedUsersFilter,
PageQueryParams, PageQueryParams,
} from "../pagination"; } from "../pagination";
@@ -56,11 +57,7 @@ export async function updateNotification(
user: AuthenticatedUser, user: AuthenticatedUser,
) { ) {
if (input.assignedToOrg && input.assignedTo) { if (input.assignedToOrg && input.assignedTo) {
input.assignedTo = []; delete input.assignedTo;
} else if (input.assignedToOrg) {
input.assignedTo = [];
} else if (input.assignedTo) {
input.assignedToOrg = null;
} }
const oldNotification = await notificationModel.findOne( const oldNotification = await notificationModel.findOne(
@@ -208,12 +205,18 @@ export async function listNotifications(
}); });
} }
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj,
sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj, filterObj,
sortObj, sortObj,
); );
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1); if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const pipeline: any = [ const pipeline: any = [
{ {
@@ -232,7 +235,8 @@ export async function listNotifications(
pipeline.push( pipeline.push(
...[ ...[
...taggedFilter, ...taggedUsersFilter,
...taggedOrgsFilter,
{ {
$lookup: { $lookup: {
from: "users", from: "users",
@@ -259,6 +263,7 @@ export async function listNotifications(
createdAt: 1, createdAt: 1,
updatedAt: 1, updatedAt: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
assignedToOrg: 1, assignedToOrg: 1,
assignedTo: { assignedTo: {
$map: { $map: {

View File

@@ -19,7 +19,7 @@ export type PageQueryParams = z.infer<typeof pageQueryParams>;
export function getSortObject( export function getSortObject(
params: PageQueryParams, params: PageQueryParams,
validFields: Array<string> validFields: Array<string>,
) { ) {
const sortObj: Record<string, 1 | -1> = {}; const sortObj: Record<string, 1 | -1> = {};
@@ -41,7 +41,7 @@ export function getSortObject(
export function getFilterObject(params: PageQueryParams) { export function getFilterObject(params: PageQueryParams) {
if (params.filter && params.filter != "") { if (params.filter && params.filter != "") {
const parsedQuery = parse(params.filter.split("|")).filter( const parsedQuery = parse(params.filter.split("|")).filter(
(query) => Object.keys(query).length > 0 (query) => Object.keys(query).length > 0,
); );
return parsedQuery; return parsedQuery;
} }
@@ -49,13 +49,13 @@ export function getFilterObject(params: PageQueryParams) {
export function getTaggedUsersFilter( export function getTaggedUsersFilter(
filterObj: MongoFilter[], filterObj: MongoFilter[],
sortObj: Record<string, 1 | -1> sortObj: Record<string, 1 | -1>,
) { ) {
const taggedUserFilterIndex = filterObj.findIndex((item) => const taggedUserFilterIndex = filterObj.findIndex((item) =>
Object.keys(item).includes("taggedUsers") Object.keys(item).includes("taggedUsers"),
); );
let taggedFilter = []; let taggedUsersFilter = [];
if (taggedUserFilterIndex != -1) { if (taggedUserFilterIndex != -1) {
const filterItem = filterObj[taggedUserFilterIndex].taggedUsers; const filterItem = filterObj[taggedUserFilterIndex].taggedUsers;
let filterValues = null; let filterValues = null;
@@ -75,7 +75,7 @@ export function getTaggedUsersFilter(
const condition = notFlag ? { $exists: false } : { $exists: true }; const condition = notFlag ? { $exists: false } : { $exists: true };
taggedFilter = [ taggedUsersFilter = [
{ {
$addFields: { $addFields: {
taggedUsersFilter: { taggedUsersFilter: {
@@ -92,5 +92,53 @@ export function getTaggedUsersFilter(
]; ];
} }
return { taggedFilter, taggedUserFilterIndex }; return { taggedUsersFilter, taggedUserFilterIndex };
}
export function getTaggedOrgsFilter(
filterObj: MongoFilter[],
sortObj: Record<string, 1 | -1>,
) {
const taggedOrgsFilterIndex = filterObj.findIndex((item) =>
Object.keys(item).includes("taggedOrgs"),
);
let taggedOrgsFilter = [];
if (taggedOrgsFilterIndex != -1) {
const filterItem = filterObj[taggedOrgsFilterIndex].taggedOrgs;
let filterValues = null;
let notFlag = false;
if (filterItem["$eq"]) {
filterValues = [filterItem["$eq"]];
} else if (filterItem["$ne"]) {
filterValues = [filterItem["$ne"]];
notFlag = true;
} else if (filterItem["$in"]) {
filterValues = filterItem["$in"];
} else {
filterValues = filterItem["$nin"];
notFlag = true;
}
const condition = notFlag ? { $exists: false } : { $exists: true };
taggedOrgsFilter = [
{
$addFields: {
taggedOrgsFilter: {
$filter: {
input: "$taggedOrgs",
as: "org",
cond: { $in: ["$$org.orgId", filterValues] },
},
},
},
},
{ $match: { "taggedOrgsFilter.0": condition } },
{ $sort: { "taggedOrgsFilter.taggedAt": sortObj.taggedOrgs ?? -1 } },
];
}
return { taggedOrgsFilter, taggedOrgsFilterIndex };
} }

View File

@@ -30,7 +30,7 @@ const permitSchema = new mongoose.Schema({
pipeline: Array, pipeline: Array,
currentStage: Number, currentStage: Number,
}, },
{ _id: false } { _id: false },
), ),
status: String, status: String,
manualStatus: String, manualStatus: String,
@@ -103,6 +103,7 @@ const permitSchema = new mongoose.Schema({
startDate: Date, startDate: Date,
history: Array, history: Array,
taggedUsers: Array, taggedUsers: Array,
taggedOrgs: Array,
noc: String, noc: String,
deed: String, deed: String,
requests: [String], requests: [String],
@@ -118,7 +119,7 @@ permitSchema.index({ tenantId: 1, permitNumber: 1 }, { unique: true });
permitSchema.index({ "address.full_address": "text" }); permitSchema.index({ "address.full_address": "text" });
export const permitFields = Object.keys(permitSchema.paths).filter( export const permitFields = Object.keys(permitSchema.paths).filter(
(path) => path !== "__v" (path) => path !== "__v",
); );
export const permitModel = mongoose.model("permit", permitSchema); export const permitModel = mongoose.model("permit", permitSchema);
@@ -150,7 +151,7 @@ const permitCore = {
date: z.date().nullable().optional(), date: z.date().nullable().optional(),
description: z.string().optional(), description: z.string().optional(),
comment: z.string().optional(), comment: z.string().optional(),
}) }),
), ),
currentStage: z.number(), currentStage: z.number(),
}) })
@@ -182,7 +183,7 @@ const permitCore = {
due_date: z.date().optional(), due_date: z.date().optional(),
is_completed: z.string().optional(), is_completed: z.string().optional(),
comment: z.string().optional(), comment: z.string().optional(),
}) }),
) )
.optional(), .optional(),
newPayment: z newPayment: z
@@ -194,7 +195,7 @@ const permitCore = {
balance_due: z.number().optional(), balance_due: z.number().optional(),
code_text: z.string().optional(), code_text: z.string().optional(),
status: z.string().optional(), status: z.string().optional(),
}) }),
) )
.optional(), .optional(),
newConditions: z newConditions: z
@@ -204,7 +205,7 @@ const permitCore = {
status_value: z.string().optional(), status_value: z.string().optional(),
short_comments: z.string().optional(), short_comments: z.string().optional(),
name: z.string().optional(), name: z.string().optional(),
}) }),
) )
.optional(), .optional(),
professionals: z.record(z.any()).optional(), professionals: z.record(z.any()).optional(),
@@ -297,5 +298,5 @@ export const { schemas: permitSchemas, $ref: $permit } = buildJsonSchemas(
updatePermitInput, updatePermitInput,
pageQueryParams, pageQueryParams,
}, },
{ $id: "permit" } { $id: "permit" },
); );

View File

@@ -1,6 +1,7 @@
import { import {
getFilterObject, getFilterObject,
getSortObject, getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter, getTaggedUsersFilter,
PageQueryParams, PageQueryParams,
} from "../pagination"; } from "../pagination";
@@ -153,18 +154,25 @@ export async function listPermits(
}); });
} }
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj,
sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj, filterObj,
sortObj, sortObj,
); );
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1); if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const permitsList = await permitModel.aggregate([ const permitsList = await permitModel.aggregate([
{ {
$match: { $and: [{ tenantId: user.tenantId }, ...filterObj] }, $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] },
}, },
...taggedFilter, ...taggedUsersFilter,
...taggedOrgsFilter,
{ {
$lookup: { $lookup: {
from: "users", from: "users",
@@ -219,6 +227,7 @@ export async function listPermits(
startDate: 1, startDate: 1,
history: 1, history: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
noc: 1, noc: 1,
deed: 1, deed: 1,
requests: 1, requests: 1,
@@ -268,11 +277,7 @@ export async function updatePermit(
user: AuthenticatedUser, user: AuthenticatedUser,
) { ) {
if (input.assignedToOrg && input.assignedTo) { if (input.assignedToOrg && input.assignedTo) {
input.assignedTo = []; delete input.assignedTo;
} else if (input.assignedToOrg) {
input.assignedTo = [];
} else if (input.assignedTo) {
input.assignedToOrg = null;
} }
const oldPermitResult = await permitModel.findOne( const oldPermitResult = await permitModel.findOne(
@@ -562,6 +567,7 @@ export async function searchPermit(
startDate: 1, startDate: 1,
history: 1, history: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
noc: 1, noc: 1,
deed: 1, deed: 1,
requests: 1, requests: 1,

View File

@@ -103,6 +103,7 @@ const processedSchema = new mongoose.Schema({
startDate: Date, startDate: Date,
history: Array, history: Array,
taggedUsers: Array, taggedUsers: Array,
taggedOrgs: Array,
noc: String, noc: String,
deed: String, deed: String,
requests: [String], requests: [String],

View File

@@ -3,6 +3,7 @@ import { AuthenticatedUser } from "../auth";
import { import {
getFilterObject, getFilterObject,
getSortObject, getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter, getTaggedUsersFilter,
PageQueryParams, PageQueryParams,
} from "../pagination"; } from "../pagination";
@@ -44,11 +45,7 @@ export async function updateProcessed(
user: AuthenticatedUser, user: AuthenticatedUser,
) { ) {
if (input.assignedToOrg && input.assignedTo) { if (input.assignedToOrg && input.assignedTo) {
input.assignedTo = []; delete input.assignedTo;
} else if (input.assignedToOrg) {
input.assignedTo = [];
} else if (input.assignedTo) {
input.assignedToOrg = null;
} }
const oldPermitResult = await processedModel.findOne( const oldPermitResult = await processedModel.findOne(
@@ -205,12 +202,18 @@ export async function listProcessedPermits(
}); });
} }
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj,
sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj, filterObj,
sortObj, sortObj,
); );
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1); if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const pipeline: any = [ const pipeline: any = [
{ {
@@ -233,7 +236,8 @@ export async function listProcessedPermits(
pipeline.push( pipeline.push(
...[ ...[
...taggedFilter, ...taggedUsersFilter,
...taggedOrgsFilter,
{ {
$lookup: { $lookup: {
from: "users", from: "users",
@@ -288,6 +292,7 @@ export async function listProcessedPermits(
block: 1, block: 1,
jobNumber: 1, jobNumber: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
noc: 1, noc: 1,
deed: 1, deed: 1,
requests: 1, requests: 1,

View File

@@ -56,6 +56,7 @@ const rtsSchema = new mongoose.Schema({
}, },
assignedToOrg: String, assignedToOrg: String,
taggedUsers: Array, taggedUsers: Array,
taggedOrgs: Array,
fileValidationStatus: String, fileValidationStatus: String,
permitNumber: [String], permitNumber: [String],
lot: [String], lot: [String],

View File

@@ -10,6 +10,7 @@ import { generateId } from "../utils/id";
import { import {
getFilterObject, getFilterObject,
getSortObject, getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter, getTaggedUsersFilter,
PageQueryParams, PageQueryParams,
} from "../pagination"; } from "../pagination";
@@ -99,18 +100,25 @@ export async function listRts(
}); });
} }
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj,
sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj, filterObj,
sortObj, sortObj,
); );
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1); if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const rtsList = await rtsModel.aggregate([ const rtsList = await rtsModel.aggregate([
{ {
$match: { $and: [{ tenantId: user.tenantId }, ...filterObj] }, $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] },
}, },
...taggedFilter, ...taggedUsersFilter,
...taggedOrgsFilter,
{ {
$lookup: { $lookup: {
from: "organizations", from: "organizations",
@@ -241,11 +249,7 @@ export async function updateRts(
user: AuthenticatedUser, user: AuthenticatedUser,
) { ) {
if (input.assignedToOrg && input.assignedTo) { if (input.assignedToOrg && input.assignedTo) {
input.assignedTo = []; delete input.assignedTo;
} else if (input.assignedToOrg) {
input.assignedTo = [];
} else if (input.assignedTo) {
input.assignedToOrg = null;
} }
const oldRts = await rtsModel.findOne( const oldRts = await rtsModel.findOne(

View File

@@ -4,6 +4,7 @@ import { createNote } from "../note/note.service";
import { import {
getFilterObject, getFilterObject,
getSortObject, getSortObject,
getTaggedOrgsFilter,
getTaggedUsersFilter, getTaggedUsersFilter,
PageQueryParams, PageQueryParams,
} from "../pagination"; } from "../pagination";
@@ -20,7 +21,7 @@ import {
export async function createTask( export async function createTask(
input: CreateTaskInput, input: CreateTaskInput,
user: AuthenticatedUser user: AuthenticatedUser,
) { ) {
if (!input.stage) { if (!input.stage) {
input.stage = { input.stage = {
@@ -44,7 +45,7 @@ export async function createTask(
}, },
task.pid, task.pid,
"tasks", "tasks",
user user,
); );
} }
@@ -57,7 +58,7 @@ export async function createTask(
export async function updateTask( export async function updateTask(
taskId: string, taskId: string,
input: UpdateTaskInput, input: UpdateTaskInput,
user: AuthenticatedUser user: AuthenticatedUser,
) { ) {
const oldTask = await taskModel.findOne({ pid: taskId }, { assignedTo: 1 }); const oldTask = await taskModel.findOne({ pid: taskId }, { assignedTo: 1 });
const updatedTask = await taskModel const updatedTask = await taskModel
@@ -70,7 +71,7 @@ export async function updateTask(
if (updatedTask && input.assignedTo) { if (updatedTask && input.assignedTo) {
const newAssignees = arrayDiff( const newAssignees = arrayDiff(
updatedTask.assignedTo.map((item) => item._id), updatedTask.assignedTo.map((item) => item._id),
oldTask.assignedTo oldTask.assignedTo,
); );
if (newAssignees.length > 0) { if (newAssignees.length > 0) {
@@ -81,7 +82,7 @@ export async function updateTask(
"user", "user",
assignee, assignee,
updatedTask.pid, updatedTask.pid,
"tasks" "tasks",
); );
} }
} }
@@ -99,25 +100,32 @@ export async function getTask(taskId: string, tenantId: string) {
export async function listTasks( export async function listTasks(
params: PageQueryParams, params: PageQueryParams,
user: AuthenticatedUser user: AuthenticatedUser,
) { ) {
const page = params.page || 1; const page = params.page || 1;
const pageSize = params.pageSize || 10; const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, taskFields); const sortObj = getSortObject(params, taskFields);
let filterObj = getFilterObject(params) || []; let filterObj = getFilterObject(params) || [];
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj, filterObj,
sortObj sortObj,
);
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
filterObj,
sortObj,
); );
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1); if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
const taskList = await taskModel.aggregate([ const taskList = await taskModel.aggregate([
{ {
$match: { $and: [{ tenantId: user.tenantId }, ...filterObj] }, $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] },
}, },
...taggedFilter, ...taggedUsersFilter,
...taggedOrgsFilter,
{ {
$lookup: { $lookup: {
from: "users", from: "users",
@@ -146,6 +154,7 @@ export async function listTasks(
stage: 1, stage: 1,
createdAt: 1, createdAt: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
createdBy: { createdBy: {
$let: { $let: {
vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } }, vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } },
@@ -244,6 +253,7 @@ export async function searchTasks(params: PageQueryParams, tenantId: string) {
stage: 1, stage: 1,
createdAt: 1, createdAt: 1,
taggedUsers: 1, taggedUsers: 1,
taggedOrgs: 1,
createdBy: { createdBy: {
$let: { $let: {
vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } }, vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } },
@@ -296,7 +306,7 @@ export async function searchTasks(params: PageQueryParams, tenantId: string) {
export async function newUpload( export async function newUpload(
id: string, id: string,
newUpload: UploadTaskInput, newUpload: UploadTaskInput,
user: AuthenticatedUser user: AuthenticatedUser,
) { ) {
return await taskModel.findOneAndUpdate( return await taskModel.findOneAndUpdate(
{ pid: id, tenantId: user.tenantId }, { pid: id, tenantId: user.tenantId },
@@ -308,6 +318,6 @@ export async function newUpload(
createdBy: user.userId, createdBy: user.userId,
}, },
}, },
} },
); );
} }

View File

@@ -2,7 +2,7 @@ import mongoose from "mongoose";
export type MongoFilter = Record<string, any>; export type MongoFilter = Record<string, any>;
const ignoreObjectIdConversion = ["taggedUsers"]; const ignoreObjectIdConversion = ["taggedUsers", "taggedOrgs"];
const ignoreNumberConversion = ["jobNumber"]; const ignoreNumberConversion = ["jobNumber"];
const ignoreDateConversion = ["permitNumber"]; const ignoreDateConversion = ["permitNumber"];
@@ -46,7 +46,7 @@ function formulaToMongoFilter(formula: string): MongoFilter {
// Convert each value to appropriate type // Convert each value to appropriate type
const parsedValues = valueArray.map((value) => const parsedValues = valueArray.map((value) =>
convertValue(trimmedField, value) convertValue(trimmedField, value),
); );
// If no operator or equals operator, use $in // If no operator or equals operator, use $in
@@ -98,7 +98,7 @@ function formulaToMongoFilter(formula: string): MongoFilter {
export function parse(formulas: string[]): Array<MongoFilter> { export function parse(formulas: string[]): Array<MongoFilter> {
const parsedQuery: Array<MongoFilter> = formulas.map((formula) => const parsedQuery: Array<MongoFilter> = formulas.map((formula) =>
formulaToMongoFilter(formula) formulaToMongoFilter(formula),
); );
return parsedQuery; return parsedQuery;
} }

View File

@@ -12,7 +12,11 @@ import { paymentModel } from "../payments/payment.schema";
export function extractExpressions(input: string) { export function extractExpressions(input: string) {
return [...input.matchAll(/{{(.*?)}}/g)] return [...input.matchAll(/{{(.*?)}}/g)]
.map((match) => match[1].trim()) .map((match) => match[1].trim())
.filter((item) => mongoose.Types.ObjectId.isValid(item)); .filter(
(item) =>
mongoose.Types.ObjectId.isValid(item) ||
["client", "team"].includes(item),
);
} }
export const modelMap = { export const modelMap = {