diff --git a/Marco.Pms.Services/Service/DirectoryService.cs b/Marco.Pms.Services/Service/DirectoryService.cs index 9375cad..a672d75 100644 --- a/Marco.Pms.Services/Service/DirectoryService.cs +++ b/Marco.Pms.Services/Service/DirectoryService.cs @@ -1,4 +1,5 @@ using AutoMapper; +using FirebaseAdmin.Messaging; using Marco.Pms.DataAccess.Data; using Marco.Pms.Helpers.Utility; using Marco.Pms.Model.Directory; @@ -119,15 +120,18 @@ namespace Marco.Pms.Services.Service // --- Advanced Filtering from 'filter' parameter --- ContactFilterDto? contactFilter = TryDeserializeContactFilter(filter); - if (contactFilter?.BucketIds?.Any() ?? false) + if (contactFilter != null) { - // Note: Permission filtering is already applied. Here we further restrict by the user's filter choice. - contactQuery = contactQuery.Where(c => dbContext.ContactBucketMappings.Any(cbm => - cbm.ContactId == c.Id && contactFilter.BucketIds.Contains(cbm.BucketId))); - } - if (contactFilter?.CategoryIds?.Any() ?? false) - { - contactQuery = contactQuery.Where(c => c.ContactCategoryId.HasValue && contactFilter.CategoryIds.Contains(c.ContactCategoryId.Value)); + if (contactFilter.BucketIds?.Any() ?? false) + { + // Note: Permission filtering is already applied. Here we further restrict by the user's filter choice. + contactQuery = contactQuery.Where(c => dbContext.ContactBucketMappings.Any(cbm => + cbm.ContactId == c.Id && contactFilter.BucketIds.Contains(cbm.BucketId))); + } + if (contactFilter.CategoryIds?.Any() ?? false) + { + contactQuery = contactQuery.Where(c => c.ContactCategoryId.HasValue && contactFilter.CategoryIds.Contains(c.ContactCategoryId.Value)); + } } // --- Standard Filtering --- @@ -935,6 +939,7 @@ namespace Marco.Pms.Services.Service /// An ApiResponse containing the newly created contact's view model or an error. public async Task> CreateContactAsync(CreateContactDto createContact, Guid tenantId, Employee loggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); Guid loggedInEmployeeId = loggedInEmployee.Id; if (string.IsNullOrWhiteSpace(createContact.Name) || @@ -1007,6 +1012,25 @@ namespace Marco.Pms.Services.Service contactVM.BucketIds = contactBucketMappings.Select(cb => cb.BucketId).ToList(); contactVM.ProjectIds = projectMappings.Select(cp => cp.ProjectId).ToList(); + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = "New Contact Created", + Body = $"New Contact \"{contact.Name}\" is created by {name} in your bucket" + }; + + await _firebase.SendContactAsync(contactVM.BucketIds, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(contactVM, "Contact created successfully.", 201); } catch (Exception ex) @@ -1022,6 +1046,7 @@ namespace Marco.Pms.Services.Service public async Task> UpdateContactAsync(Guid id, UpdateContactDto updateContact, Guid tenantId, Employee loggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); if (updateContact == null) { _logger.LogWarning("Employee {EmployeeId} sent empty payload for updating contact.", loggedInEmployee.Id); @@ -1455,6 +1480,28 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Contact {ContactId} successfully updated by employee {EmployeeId}.", contact.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 _firebase = scope.ServiceProvider.GetRequiredService(); + + if (contactVM.BucketIds?.Any() ?? false) + { + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = $"Contact Updated - \"{contact.Name}\"", + Body = $"Contact \"{contact.Name}\" is updated by {name} in your bucket" + }; + + await _firebase.SendContactAsync(contactVM.BucketIds, notification, tenantId); + } + + }); + return ApiResponse.SuccessResponse(contactVM, "Contact Updated Successfully", 200); } @@ -1471,6 +1518,15 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Contact ID is empty", "Contact ID is empty", 400); } + var bucketIds = await _context.ContactBucketMappings.Where(cb => cb.ContactId == id).Select(cb => cb.BucketId).ToListAsync(); + var hasContactAccess = await _context.EmployeeBucketMappings.AnyAsync(eb => bucketIds.Contains(eb.BucketId) && eb.EmployeeId == loggedInEmployee.Id); + if (hasContactAccess) + { + _logger.LogWarning("Employee {EmployeeId} does not have permission to delete contact {ContactId}", + loggedInEmployee.Id, id); + return ApiResponse.ErrorResponse("Unauthorized", "You do not have permission", 403); + } + // Try to find the contact for the given tenant Contact? contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId); @@ -1505,10 +1561,45 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Contact ID {ContactId} has been {(DeletedOrActivated)} by Employee ID {EmployeeId}.", id, active ? "activated" : "deleted", loggedInEmployee.Id); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + if (bucketIds.Any()) + { + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + Notification notification; + if (active) + { + notification = new Notification + { + Title = $"Contact restored - \"{contact.Name}\"", + Body = $"Contact \"{contact.Name}\" is restored by {name} in your bucket" + }; + } + else + { + notification = new Notification + { + Title = $"Contact Deleted - \"{contact.Name}\"", + Body = $"Contact \"{contact.Name}\" is deleted by {name} in your bucket" + }; + } + + await _firebase.SendContactAsync(bucketIds, notification, tenantId); + } + + }); + return ApiResponse.SuccessResponse(new { }, active ? "Contact is activated successfully" : "Contact is deleted successfully", 200); } - #endregion #endregion @@ -1929,6 +2020,14 @@ namespace Marco.Pms.Services.Service try { + var bucketIds = await _context.ContactBucketMappings.Where(cb => cb.ContactId == noteDto.ContactId).Select(cb => cb.BucketId).ToListAsync(); + var hasContactAccess = await _context.EmployeeBucketMappings.AnyAsync(eb => bucketIds.Contains(eb.BucketId) && eb.EmployeeId == loggedInEmployee.Id); + if (hasContactAccess) + { + _logger.LogWarning("Employee {EmployeeId} does not have permission to delete contact {ContactId}", + loggedInEmployee.Id, noteDto.ContactId); + return ApiResponse.ErrorResponse("Unauthorized", "You do not have permission", 403); + } // Check if the contact exists and is active for this tenant Contact? contact = await _context.Contacts .AsNoTracking() // optimization for read-only query @@ -1959,6 +2058,27 @@ namespace Marco.Pms.Services.Service "Employee {EmployeeId} successfully added a note (NoteId: {NoteId}) to Contact {ContactId} for Tenant {TenantId}.", loggedInEmployee.Id, note.Id, contact.Id, tenantId); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = $"New note added at Contact - \"{contact.Name}\"", + Body = $"New note added at Contact \"{contact.Name}\" by {name} in your bucket" + }; + + await _firebase.SendContactNoteAsync(bucketIds, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(noteVM, "Note added successfully.", 200); } catch (Exception ex) @@ -1991,6 +2111,15 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Invalid or empty payload", "Invalid or empty payload", 400); } + var bucketIds = await _context.ContactBucketMappings.Where(cb => cb.ContactId == noteDto.ContactId).Select(cb => cb.BucketId).ToListAsync(); + var hasContactAccess = await _context.EmployeeBucketMappings.AnyAsync(eb => bucketIds.Contains(eb.BucketId) && eb.EmployeeId == loggedInEmployee.Id); + if (hasContactAccess) + { + _logger.LogWarning("Employee {EmployeeId} does not have permission to delete contact {ContactId}", + loggedInEmployee.Id, noteDto.ContactId); + return ApiResponse.ErrorResponse("Unauthorized", "You do not have permission", 403); + } + // Check if the contact belongs to this tenant Contact? contact = await _context.Contacts .AsNoTracking() @@ -2061,6 +2190,27 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Employee {EmployeeId} successfully updated Note {NoteId} for Contact {ContactId} at {UpdatedAt}", loggedInEmployee.Id, noteVM.Id, contact.Id, noteVM.UpdatedAt); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = $"Note updated at Contact - \"{contact.Name}\"", + Body = $"Note updated at Contact \"{contact.Name}\" by {name} in your bucket" + }; + + await _firebase.SendContactNoteAsync(bucketIds, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(noteVM, "Note updated successfully", 200); } catch (DbUpdateException ex) @@ -2103,6 +2253,28 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Note not found", "Note not found", 404); } + var bucketIds = await _context.ContactBucketMappings.Where(cb => cb.ContactId == note.ContactId).Select(cb => cb.BucketId).ToListAsync(); + var hasContactAccess = await _context.EmployeeBucketMappings.AnyAsync(eb => bucketIds.Contains(eb.BucketId) && eb.EmployeeId == loggedInEmployee.Id); + if (hasContactAccess) + { + _logger.LogWarning("Employee {EmployeeId} does not have permission to delete contact {ContactId}", + loggedInEmployee.Id, note.ContactId); + return ApiResponse.ErrorResponse("Unauthorized", "You do not have permission", 403); + } + + // Check if the contact belongs to this tenant + Contact? contact = await _context.Contacts + .AsNoTracking() + .FirstOrDefaultAsync(c => c.Id == note.ContactId && c.TenantId == tenantId); + + if (contact == null) + { + _logger.LogWarning("Employee {EmployeeId} attempted to update note {NoteId} for Contact {ContactId}, but the contact was not found in Tenant {TenantId}.", + loggedInEmployee.Id, note.Id, note.ContactId, tenantId); + + return ApiResponse.ErrorResponse("Contact not found", "Contact not found", 404); + } + // Capture old state for audit logging var oldObject = _updateLogsHelper.EntityToBsonDocument(note); @@ -2146,6 +2318,38 @@ namespace Marco.Pms.Services.Service loggedInEmployee.Id, id, currentTime); } + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + Notification notification; + if (active) + { + notification = new Notification + { + Title = $"Note restored at Contact - \"{contact.Name}\"", + Body = $"Note restored at Contact \"{contact.Name}\" by {name} in your bucket" + }; + } + else + { + notification = new Notification + { + Title = $"Note deleted at Contact - \"{contact.Name}\"", + Body = $"Note deleted at Contact \"{contact.Name}\" by {name} in your bucket" + }; + } + await _firebase.SendContactNoteAsync(bucketIds, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(new { }, active ? "Note restored successfully" : "Note deleted successfully", 200); @@ -2264,6 +2468,7 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Fetched {BucketCount} buckets for Employee {EmployeeId} successfully", bucketVMs.Count, loggedInEmployee.Id); + return ApiResponse.SuccessResponse(bucketVMs, $"{bucketVMs.Count} buckets fetched successfully", 200); } public async Task> CreateBucketAsync(CreateBucketDto bucketDto, Guid tenantId, Employee loggedInEmployee) @@ -2337,6 +2542,27 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Employee {EmployeeId} successfully created bucket {BucketId}", loggedInEmployee.Id, newBucket.Id); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = "New Bucket created", + Body = $"New Bucket created \"{newBucket.Name}\" by {name}" + }; + + await _firebase.SendBucketAsync(newBucket.Id, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(bucketVM, "Bucket created successfully", 200); } catch (Exception ex) @@ -2449,6 +2675,27 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Employee ID {LoggedInEmployeeId} successfully updated bucket ID {BucketId}.", loggedInEmployee.Id, bucket.Id); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = $"Bucket updated - \"{bucket.Name}\"", + Body = $"Bucket updated \"{bucket.Name}\" by {name}" + }; + + await _firebase.SendBucketAsync(bucket.Id, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(bucketVM, "Bucket updated successfully", 200); } catch (Exception ex) @@ -2519,6 +2766,9 @@ namespace Marco.Pms.Services.Service int assignedEmployeesCount = 0; int removedEmployeesCount = 0; + List assignedEmployeeIds = new List(); + List removedEmployeeIds = new List(); + // Process each assignment request foreach (var assignBucket in assignBuckets) { @@ -2541,6 +2791,7 @@ namespace Marco.Pms.Services.Service }; _context.EmployeeBucketMappings.Add(newMapping); assignedEmployeesCount++; + assignedEmployeeIds.Add(assignBucket.EmployeeId); } } else @@ -2549,6 +2800,7 @@ namespace Marco.Pms.Services.Service var existingMapping = employeeBuckets.FirstOrDefault(eb => eb.EmployeeId == assignBucket.EmployeeId); if (existingMapping != null && bucket.CreatedByID != assignBucket.EmployeeId) { + removedEmployeeIds.Add(existingMapping.EmployeeId); _context.EmployeeBucketMappings.Remove(existingMapping); removedEmployeesCount++; } @@ -2607,7 +2859,41 @@ namespace Marco.Pms.Services.Service { _logger.LogInfo("Employee {EmployeeId} removed {Count} employees from bucket {BucketId}.", loggedInEmployee.Id, removedEmployeesCount, bucketId); } + using var scope = _serviceScopeFactory.CreateScope(); + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + if (assignedEmployeeIds.Any()) + { + + var notification = new Notification + { + Title = "You have assigned to Bucket", + Body = $"You have assigned to bucket \"{bucket.Name}\" by {name}" + }; + + await _firebase.SendAssignBucketAsync(assignedEmployeeIds, notification, tenantId); + } + if (removedEmployeeIds.Any()) + { + + var notification = new Notification + { + Title = "You have removed from Bucket", + Body = $"You have removed from bucket \"{bucket.Name}\" by {name}" + }; + + await _firebase.SendAssignBucketAsync(removedEmployeeIds, notification, tenantId); + } + + }); return ApiResponse.SuccessResponse(bucketVm, "Bucket details updated successfully", 200); } public async Task> DeleteBucketAsync(Guid id, Guid tenantId, Employee loggedInEmployee) @@ -2720,6 +3006,27 @@ namespace Marco.Pms.Services.Service UpdatedAt = DateTime.UtcNow }, bucketCollection); + using var scope = _serviceScopeFactory.CreateScope(); + + _ = 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 _firebase = scope.ServiceProvider.GetRequiredService(); + + var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}"; + + var notification = new Notification + { + Title = $"Bucket deleted - \"{bucket.Name}\"", + Body = $"Bucket deleted \"{bucket.Name}\" by {name}" + }; + + await _firebase.SendBucketAsync(bucket.Id, notification, tenantId); + + }); + return ApiResponse.SuccessResponse(new { }, "Bucket deleted successfully", 200); } catch (Exception ex) diff --git a/Marco.Pms.Services/Service/FirebaseService.cs b/Marco.Pms.Services/Service/FirebaseService.cs index 1576ae9..ef7e1fd 100644 --- a/Marco.Pms.Services/Service/FirebaseService.cs +++ b/Marco.Pms.Services/Service/FirebaseService.cs @@ -31,6 +31,7 @@ namespace Marco.Pms.Services.Service _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); } + // Auth Controller public async Task SendLoginOnAnotherDeviceMessageAsync(Guid employeeId, string fcmToken, Guid tenentId) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); @@ -1622,6 +1623,152 @@ namespace Marco.Pms.Services.Service } } + // Directory Controller + public async Task SendContactAsync(List bucketIds, Notification notification, Guid tenantId) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + var assignedEmployeeIdsTask = Task.Run(async () => + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + return await _context.EmployeeBucketMappings.Where(eb => bucketIds.Contains(eb.BucketId)).Select(eb => eb.EmployeeId).ToListAsync(); + }); + var directoryAdminEmployeeIdsTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var roleIds = await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.DirectoryAdmin) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + var employeeIds = await dbContext.EmployeeRoleMappings + .Where(er => + roleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + return employeeIds; + }); + + await Task.WhenAll(assignedEmployeeIdsTask, directoryAdminEmployeeIdsTask); + + var assignedEmployeeIds = assignedEmployeeIdsTask.Result; + var directoryAdminEmployeeIds = directoryAdminEmployeeIdsTask.Result; + + assignedEmployeeIds.AddRange(directoryAdminEmployeeIds); + + assignedEmployeeIds = assignedEmployeeIds.Distinct().ToList(); + + var data = new Dictionary() + { + { "Keyword", "Contact_Modefied" } + }; + + var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => assignedEmployeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notification, data); + } + public async Task SendContactNoteAsync(List bucketIds, Notification notification, Guid tenantId) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + var assignedEmployeeIdsTask = Task.Run(async () => + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + return await _context.EmployeeBucketMappings.Where(eb => bucketIds.Contains(eb.BucketId)).Select(eb => eb.EmployeeId).ToListAsync(); + }); + var directoryAdminEmployeeIdsTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var roleIds = await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.DirectoryAdmin) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + var employeeIds = await dbContext.EmployeeRoleMappings + .Where(er => + roleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + return employeeIds; + }); + + await Task.WhenAll(assignedEmployeeIdsTask, directoryAdminEmployeeIdsTask); + + var assignedEmployeeIds = assignedEmployeeIdsTask.Result; + var directoryAdminEmployeeIds = directoryAdminEmployeeIdsTask.Result; + + assignedEmployeeIds.AddRange(directoryAdminEmployeeIds); + + assignedEmployeeIds = assignedEmployeeIds.Distinct().ToList(); + + var data = new Dictionary() + { + { "Keyword", "Contact_Note_Modefied" } + }; + + var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => assignedEmployeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notification, data); + } + public async Task SendBucketAsync(Guid bucketId, Notification notification, Guid tenantId) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + var assignedEmployeeIdsTask = Task.Run(async () => + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + return await _context.EmployeeBucketMappings.Where(eb => bucketId == eb.BucketId).Select(eb => eb.EmployeeId).ToListAsync(); + }); + var directoryAdminEmployeeIdsTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var roleIds = await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.DirectoryAdmin) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + var employeeIds = await dbContext.EmployeeRoleMappings + .Where(er => + roleIds.Contains(er.RoleId)) + .Select(er => er.EmployeeId) + .ToListAsync(); + return employeeIds; + }); + + await Task.WhenAll(assignedEmployeeIdsTask, directoryAdminEmployeeIdsTask); + + var assignedEmployeeIds = assignedEmployeeIdsTask.Result; + var directoryAdminEmployeeIds = directoryAdminEmployeeIdsTask.Result; + + assignedEmployeeIds.AddRange(directoryAdminEmployeeIds); + + assignedEmployeeIds = assignedEmployeeIds.Distinct().ToList(); + + var data = new Dictionary() + { + { "Keyword", "Bucket_Modefied" } + }; + + var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => assignedEmployeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notification, data); + } + public async Task SendAssignBucketAsync(List employeeIds, Notification notification, Guid tenantId) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + var _logger = scope.ServiceProvider.GetRequiredService(); + + var data = new Dictionary() + { + { "Keyword", "Bucket_Assigned" } + }; + + var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow).Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notification, data); + } + #region =================================================================== Helper Functions =================================================================== 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 30dfe2e..e0c69a1 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs @@ -1,4 +1,5 @@ -using Marco.Pms.Model.Dtos.Attendance; +using FirebaseAdmin.Messaging; +using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Expenses; using Marco.Pms.Model.Projects; @@ -22,5 +23,10 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task SendProjectAllocationMessageAsync(List projectAllocations, string name, Guid tenantId); Task SendModifyProjectMessageAsync(Project project, string name, bool IsExist, Guid tenantId); Task SendExpenseMessageAsync(Expenses expenses, string name, Guid tenantId); + + Task SendContactAsync(List bucketIds, Notification notification, Guid tenantId); + Task SendContactNoteAsync(List bucketIds, Notification notification, Guid tenantId); + Task SendBucketAsync(Guid bucketId, Notification notification, Guid tenantId); + Task SendAssignBucketAsync(List employeeIds, Notification notification, Guid tenantId); } }