diff --git a/Marco.Pms.Services/Controllers/AttendanceController.cs b/Marco.Pms.Services/Controllers/AttendanceController.cs index 7545959..0c9a67d 100644 --- a/Marco.Pms.Services/Controllers/AttendanceController.cs +++ b/Marco.Pms.Services/Controllers/AttendanceController.cs @@ -586,7 +586,7 @@ namespace MarcoBMS.Services.Controllers var name = $"{vm.FirstName} {vm.LastName}"; - await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, TenantId); + await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeID, TenantId); }); @@ -795,7 +795,7 @@ namespace MarcoBMS.Services.Controllers var name = $"{vm.FirstName} {vm.LastName}"; - await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, tenantId); + await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeID, tenantId); }); diff --git a/Marco.Pms.Services/Controllers/TaskController.cs b/Marco.Pms.Services/Controllers/TaskController.cs index e8dc7a2..51660bb 100644 --- a/Marco.Pms.Services/Controllers/TaskController.cs +++ b/Marco.Pms.Services/Controllers/TaskController.cs @@ -161,7 +161,14 @@ namespace MarcoBMS.Services.Controllers var taskAllocation = await _context.TaskAllocations .Include(t => t.WorkItem) - .FirstOrDefaultAsync(t => t.Id == reportTask.Id); + .ThenInclude(wi => wi!.WorkArea) + .ThenInclude(wa => wa!.Floor) + .ThenInclude(f => f!.Building) + .FirstOrDefaultAsync(t => t.Id == reportTask.Id && + t.WorkItem != null && + t.WorkItem.WorkArea != null && + t.WorkItem.WorkArea.Floor != null && + t.WorkItem.WorkArea.Floor.Building != null); if (taskAllocation == null) { @@ -293,6 +300,18 @@ namespace MarcoBMS.Services.Controllers _logger.LogInfo("Task {TaskId} reported successfully by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id); + _ = Task.Run(async () => + { + // --- Push Notification Section --- + // This section attempts to send a test push notification to the user's device. + // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens. + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + await _firebase.SendReportTaskMessageAsync(taskAllocation.Id, name, tenantId); + + }); + return Ok(ApiResponse.SuccessResponse(response, "Task reported successfully", 200)); } @@ -393,6 +412,18 @@ namespace MarcoBMS.Services.Controllers var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Task_Comment", NumberOfImages = numberofImages, ProjectId = projectId }; await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); + _ = Task.Run(async () => + { + // --- Push Notification Section --- + // This section attempts to send a test push notification to the user's device. + // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens. + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + await _firebase.SendTaskCommentMessageAsync(taskAllocation.Id, name, tenantId); + + }); + return Ok(ApiResponse.SuccessResponse(response, "Comment saved successfully", 200)); } @@ -739,16 +770,6 @@ namespace MarcoBMS.Services.Controllers "Approved tasks cannot be greater than completed tasks", 400)); } - //// Update completed work in the associated work item, if it exists - //if (taskAllocation.WorkItem != null && taskAllocation.CompletedTask != approveTask.ApprovedTask) - //{ - // if (taskAllocation.CompletedTask > 0) - // { - // taskAllocation.WorkItem.CompletedWork -= taskAllocation.CompletedTask; - // } - // taskAllocation.WorkItem.CompletedWork += approveTask.ApprovedTask; - //} - // Update task allocation details taskAllocation.ApprovedById = loggedInEmployee.Id; taskAllocation.ApprovedDate = DateTime.UtcNow; @@ -834,6 +855,18 @@ namespace MarcoBMS.Services.Controllers _logger.LogInfo("Task {TaskId} successfully approved by Employee {EmployeeId}", approveTask.Id, loggedInEmployee.Id); + _ = Task.Run(async () => + { + // --- Push Notification Section --- + // This section attempts to send a test push notification to the user's device. + // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens. + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + await _firebase.SendApproveTaskMessageAsync(taskAllocation.Id, name, tenantId); + + }); + return Ok(ApiResponse.SuccessResponse("Task has been approved", "Task has been approved", 200)); } } diff --git a/Marco.Pms.Services/Service/FirebaseService.cs b/Marco.Pms.Services/Service/FirebaseService.cs index 7bd063a..1caf5d2 100644 --- a/Marco.Pms.Services/Service/FirebaseService.cs +++ b/Marco.Pms.Services/Service/FirebaseService.cs @@ -34,7 +34,9 @@ namespace Marco.Pms.Services.Service await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); } - public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid tenantId) + + // Attendance Controller + public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var projectTask = Task.Run(async () => @@ -58,6 +60,13 @@ namespace Marco.Pms.Services.Service .Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); + var regularizeAttendanceRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.RegularizeAttendance) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); @@ -66,9 +75,10 @@ namespace Marco.Pms.Services.Service .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); - await Task.WhenAll(projectTask, teamAttendanceRoleTask, manageProjectsRoleTask); + await Task.WhenAll(projectTask, teamAttendanceRoleTask, manageProjectsRoleTask, regularizeAttendanceRoleTask); var teamAttendanceRoleIds = teamAttendanceRoleTask.Result; + var regularizeAttendanceRoleIds = regularizeAttendanceRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; @@ -79,6 +89,9 @@ namespace Marco.Pms.Services.Service .Select(er => er.EmployeeId) .ToListAsync(); + var dataNotificationIds = new List(); + var mesaageNotificationIds = new List(); + Notification notificationFirebase; switch (markType) { @@ -88,6 +101,7 @@ namespace Marco.Pms.Services.Service Title = "Attendance Update", Body = $" {name} has checked in for project {project?.ProjectName ?? ""}." }; + mesaageNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.CHECK_OUT: notificationFirebase = new Notification @@ -95,6 +109,7 @@ namespace Marco.Pms.Services.Service Title = "Attendance Update", Body = $" {name} has checked out for project {project?.ProjectName ?? ""}." }; + mesaageNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE: notificationFirebase = new Notification @@ -102,6 +117,11 @@ namespace Marco.Pms.Services.Service Title = "Regularization Request", Body = $" {name} has submitted a regularization request for project {project?.ProjectName ?? ""}." }; + mesaageNotificationIds = await _context.EmployeeRoleMappings + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && regularizeAttendanceRoleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + dataNotificationIds = employeeIds; break; case ATTENDANCE_MARK_TYPE.REGULARIZE: notificationFirebase = new Notification @@ -109,6 +129,8 @@ namespace Marco.Pms.Services.Service Title = " Regularization Approved", Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been accepted." }; + mesaageNotificationIds.Add(employeeId); + dataNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT: notificationFirebase = new Notification @@ -116,6 +138,8 @@ namespace Marco.Pms.Services.Service Title = "Regularization Denied", Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been rejected." }; + mesaageNotificationIds.Add(employeeId); + dataNotificationIds.AddRange(employeeIds); break; default: notificationFirebase = new Notification @@ -127,9 +151,6 @@ namespace Marco.Pms.Services.Service } // List of device registration tokens to send the message to - var registrationTokens = await _context.FCMTokenMappings - .Where(ft => employeeIds.Contains(ft.EmployeeId) && ft.TenantId == tenantId) - .Select(ft => ft.FcmToken).ToListAsync(); var data = new Dictionary() { @@ -138,9 +159,31 @@ namespace Marco.Pms.Services.Service { "Action", markType.ToString() } }; - await SendMessageToMultipleDevicesWithDataAsync(registrationTokens, notificationFirebase, data); + var registrationTokensForNotificationTask = Task.Run(async () => + { + if (mesaageNotificationIds.Any()) + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => mesaageNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); + } + }); + var registrationTokensForDataTask = Task.Run(async () => + { + if (dataNotificationIds.Any()) + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => dataNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); + } + }); + + await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); } + // Task Controller public async Task SendAssignTaskMessageAsync(Guid workItemId, string name, List teamMembers, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); @@ -185,9 +228,18 @@ namespace Marco.Pms.Services.Service .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); - await Task.WhenAll(projectTask, viewTaskRoleTask); + var manageProjectsRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + await Task.WhenAll(projectTask, viewTaskRoleTask, manageProjectsRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; + var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; var buildingName = workItem.WorkArea.Floor.Building.Name; @@ -199,7 +251,7 @@ namespace Marco.Pms.Services.Service List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings - .Where(er => projectAssignedEmployeeIds.Contains(er.EmployeeId) && viewTaskRoleIds.Contains(er.RoleId)) + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); @@ -239,6 +291,384 @@ namespace Marco.Pms.Services.Service _logger.LogError(ex, "Exception occured while get data for sending notification"); } } + public async Task SendReportTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId) + { + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + try + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + var taskAllocation = await _context.TaskAllocations + .Include(t => t.WorkItem) + .ThenInclude(wi => wi!.WorkArea) + .ThenInclude(wa => wa!.Floor) + .ThenInclude(f => f!.Building) + .FirstOrDefaultAsync(t => t.Id == taskAllocationId && + t.TenantId == tenantId && + t.WorkItem != null && + t.WorkItem.WorkArea != null && + t.WorkItem.WorkArea.Floor != null && + t.WorkItem.WorkArea.Floor.Building != null); + + if (taskAllocation == null) + { + return; + } + + var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; + + var projectTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.ProjectAllocations + .Include(pa => pa.Project) + .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) + .GroupBy(pa => pa.ProjectId) + .Select(g => new + { + ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), + EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() + }).FirstOrDefaultAsync(); + }); + + var teamMemberTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + var viewTaskRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.TaskMembers + .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) + .Select(tm => tm.EmployeeId).ToListAsync(); + }); + var approveTaskRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ApproveTask) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + var manageProjectsRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + await Task.WhenAll(projectTask, teamMemberTask, viewTaskRoleTask, manageProjectsRoleTask, approveTaskRoleTask); + + var viewTaskRoleIds = viewTaskRoleTask.Result; + var teamMembers = teamMemberTask.Result; + var manageProjectsRoleIds = manageProjectsRoleTask.Result; + var approveTaskRoleIds = approveTaskRoleTask.Result; + var project = projectTask.Result; + + var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; + var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; + var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; + + var location = $"{buildingName} > {FloorName} > {AreaName}"; + + List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); + + var dataNotificationTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var employeeIds = await dbContext.EmployeeRoleMappings + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + + employeeIds.AddRange(teamMembers); + + return employeeIds; + }); + var mesaageNotificationTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.EmployeeRoleMappings + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && approveTaskRoleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + }); + + await Task.WhenAll(dataNotificationTask, mesaageNotificationTask); + + var dataNotificationIds = dataNotificationTask.Result; + var mesaageNotificationIds = mesaageNotificationTask.Result; + + var data = new Dictionary() + { + { "Keyword", "Report_Task" }, + { "ProjectId", projectId.ToString() }, + { "TaskAllocationId", taskAllocationId.ToString() } + }; + + + // List of device registration tokens to send the message to + + var registrationTokensForNotificationTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => mesaageNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + var notificationFirebase = new Notification + { + Title = $"New Task Reported - {project?.ProjectName}", + Body = $"{name} reported a task at {location}." + }; + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); + }); + var registrationTokensForDataTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => dataNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); + }); + + await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception occured while get data for sending notification"); + } + } + public async Task SendTaskCommentMessageAsync(Guid taskAllocationId, string name, Guid tenantId) + { + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + try + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + var taskAllocation = await _context.TaskAllocations + .Include(t => t.WorkItem) + .ThenInclude(wi => wi!.WorkArea) + .ThenInclude(wa => wa!.Floor) + .ThenInclude(f => f!.Building) + .FirstOrDefaultAsync(t => t.Id == taskAllocationId && + t.TenantId == tenantId && + t.WorkItem != null && + t.WorkItem.WorkArea != null && + t.WorkItem.WorkArea.Floor != null && + t.WorkItem.WorkArea.Floor.Building != null); + + if (taskAllocation == null) + { + return; + } + + var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; + + var projectTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.ProjectAllocations + .Include(pa => pa.Project) + .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) + .GroupBy(pa => pa.ProjectId) + .Select(g => new + { + ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), + EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() + }).FirstOrDefaultAsync(); + }); + var viewTaskRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.TaskMembers + .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) + .Select(tm => tm.EmployeeId).ToListAsync(); + }); + + var manageProjectsRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + await Task.WhenAll(projectTask, viewTaskRoleTask, manageProjectsRoleTask); + + var viewTaskRoleIds = viewTaskRoleTask.Result; + var manageProjectsRoleIds = manageProjectsRoleTask.Result; + var project = projectTask.Result; + + var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; + var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; + var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; + + var location = $"{buildingName} > {FloorName} > {AreaName}"; + + List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); + + var employeeIds = await _context.EmployeeRoleMappings + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + + employeeIds.Add(taskAllocation.AssignedBy); + + var data = new Dictionary() + { + { "Keyword", "Task_Comment" }, + { "ProjectId", projectId.ToString() }, + { "TaskAllocationId", taskAllocationId.ToString() } + }; + + + // List of device registration tokens to send the message to + var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + var notificationFirebase = new Notification + { + Title = $"New Comment on Task - {project?.ProjectName}", + Body = $"{name} added a comment on Task at {location}." + }; + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception occured while get data for sending notification"); + } + } + public async Task SendApproveTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId) + { + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + try + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + var taskAllocation = await _context.TaskAllocations + .Include(t => t.WorkItem) + .ThenInclude(wi => wi!.WorkArea) + .ThenInclude(wa => wa!.Floor) + .ThenInclude(f => f!.Building) + .FirstOrDefaultAsync(t => t.Id == taskAllocationId && + t.TenantId == tenantId && + t.WorkItem != null && + t.WorkItem.WorkArea != null && + t.WorkItem.WorkArea.Floor != null && + t.WorkItem.WorkArea.Floor.Building != null); + + if (taskAllocation == null) + { + return; + } + + var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; + + var projectTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.ProjectAllocations + .Include(pa => pa.Project) + .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) + .GroupBy(pa => pa.ProjectId) + .Select(g => new + { + ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), + EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() + }).FirstOrDefaultAsync(); + }); + + var teamMemberTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + var viewTaskRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.TaskMembers + .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) + .Select(tm => tm.EmployeeId).ToListAsync(); + }); + + var manageProjectsRoleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + await Task.WhenAll(projectTask, teamMemberTask, viewTaskRoleTask, manageProjectsRoleTask); + + var viewTaskRoleIds = viewTaskRoleTask.Result; + var teamMembers = teamMemberTask.Result; + var manageProjectsRoleIds = manageProjectsRoleTask.Result; + var project = projectTask.Result; + + var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; + var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; + var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; + + var location = $"{buildingName} > {FloorName} > {AreaName}"; + + List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); + + var employeeIds = await _context.EmployeeRoleMappings + .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + + teamMembers.Add(taskAllocation.AssignedBy); + + var data = new Dictionary() + { + { "Keyword", "Report_Task" }, + { "ProjectId", projectId.ToString() }, + { "TaskAllocationId", taskAllocationId.ToString() } + }; + + + // List of device registration tokens to send the message to + + var registrationTokensForNotificationTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => teamMembers.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + var notificationFirebase = new Notification + { + Title = $"Task Approved - {project?.ProjectName}", + Body = $"{name} approved your {taskAllocation.ReportedTask} of {taskAllocation.CompletedTask} tasks at {location}." + }; + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); + }); + var registrationTokensForDataTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); + }); + + await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception occured while get data for sending notification"); + } + } + + // Project Controller public async Task SendMessageToMultipleDevicesWithDataAsync(List registrationTokens, Notification notificationFirebase, Dictionary data) { diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs index bc5eb8f..e8175f3 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs @@ -5,7 +5,10 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces public interface IFirebaseService { Task SendLoginMessageAsync(string name); - Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId); + Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId); Task SendAssignTaskMessageAsync(Guid workItemId, string name, List teamMembers, Guid tenantId); + Task SendReportTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId); + Task SendTaskCommentMessageAsync(Guid taskAllocationId, string name, Guid tenantId); + Task SendApproveTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId); } }