Added the notification for expenses controller

This commit is contained in:
ashutosh.nehete 2025-08-20 16:07:00 +05:30
parent 2b6e8b7c8a
commit cf51d4f37c
4 changed files with 194 additions and 3 deletions

View File

@ -239,8 +239,17 @@ namespace MarcoBMS.Services.Controllers
}
else
{
var oldFCMToken = exsistingFCMMapping.FcmToken;
exsistingFCMMapping.FcmToken = loginDto.FcmToken;
_logger.LogInfo("Updating FCM Token for employee {EmployeeId}", emp.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.
await _firebase.SendLoginOnAnotherDeviceMessageAsync(oldFCMToken);
});
}
try
{

View File

@ -596,6 +596,9 @@ namespace Marco.Pms.Services.Service
/// <returns>An ApiResponse containing the updated expense details or an error.</returns>
public async Task<ApiResponse<object>> ChangeStatusAsync(ExpenseRecordDto model, Employee loggedInEmployee, Guid tenantId)
{
using var scope = _serviceScopeFactory.CreateScope();
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
// 1. Fetch Existing Expense with Related Entities (Single Query)
var expense = await _context.Expenses
.Include(e => e.ExpensesType)
@ -670,7 +673,6 @@ namespace Marco.Pms.Services.Service
}
else if (requiredPermissions.Any())
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
foreach (var permission in requiredPermissions)
{
@ -753,6 +755,18 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Expense was modified by another user. Please refresh and try again.", "Concurrency Error", 409);
}
_ = 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.SendExpenseMessageAsync(expense, name, tenantId);
});
// 10. Post-processing (audit log, cache, fetch next states)
try
{

View File

@ -6,7 +6,9 @@ using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.Projects;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Service;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using Project = Marco.Pms.Model.Projects.Project;
namespace Marco.Pms.Services.Service
{
@ -14,6 +16,14 @@ namespace Marco.Pms.Services.Service
{
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
private readonly IServiceScopeFactory _serviceScopeFactory;
private static readonly Guid Review = Guid.Parse("6537018f-f4e9-4cb3-a210-6c3b2da999d7");
private static readonly Guid RejectedByReviewer = Guid.Parse("965eda62-7907-4963-b4a1-657fb0b2724b");
private static readonly Guid Approve = Guid.Parse("4068007f-c92f-4f37-a907-bc15fe57d4d8");
private static readonly Guid RejectedByApprover = Guid.Parse("d1ee5eec-24b6-4364-8673-a8f859c60729");
private static readonly Guid ProcessPending = Guid.Parse("f18c5cfd-7815-4341-8da2-2c2d65778e27");
private static readonly Guid Processed = Guid.Parse("61578360-3a49-4c34-8604-7b35a3787b95");
public FirebaseService(IDbContextFactory<ApplicationDbContext> dbContextFactory,
IServiceScopeFactory serviceScopeFactory)
{
@ -21,6 +31,22 @@ namespace Marco.Pms.Services.Service
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
}
public async Task SendLoginOnAnotherDeviceMessageAsync(string FcmToken)
{
await using var _context = await _dbContextFactory.CreateDbContextAsync();
// List of device registration tokens to send the message to
var registrationTokens = new List<string> { FcmToken };
;
var notificationFirebase = new Notification
{
Title = "Login Alert",
Body = $"You has successfully logged in to the application from another device"
};
await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase);
}
public async Task SendLoginMessageAsync(string name)
{
await using var _context = await _dbContextFactory.CreateDbContextAsync();
@ -1444,10 +1470,149 @@ namespace Marco.Pms.Services.Service
}
}
//Contact Controller
public async Task SendReviewExpenseMessageAsync(Expenses expenses, string name, bool IsExist, Guid tenantId)
//Expenses Controller
public async Task SendExpenseMessageAsync(Expenses expenses, string name, Guid tenantId)
{
using var scope = _serviceScopeFactory.CreateScope();
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
try
{
await using var _context = await _dbContextFactory.CreateDbContextAsync();
var nextStatusIds = await _context.ExpensesStatusMapping.Where(sm => sm.StatusId == expenses.StatusId).Select(sm => sm.NextStatusId).ToListAsync();
var requriedPermissionIds = await _context.StatusPermissionMapping.Where(sp => nextStatusIds.Contains(sp.StatusId)).Select(sp => sp.PermissionId).ToListAsync();
requriedPermissionIds = requriedPermissionIds.Distinct().ToList();
var notificationEmployeeTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var roleIds = await dbContext.RolePermissionMappings
.Where(rp => requriedPermissionIds.Contains(rp.FeaturePermissionId))
.Select(rp => rp.ApplicationRoleId).ToListAsync();
var employeeIds = await dbContext.EmployeeRoleMappings
.Where(er =>
roleIds.Contains(er.RoleId))
.Select(er => er.EmployeeId)
.ToListAsync();
return employeeIds;
});
var dataEmployeeTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var roleIds = await dbContext.RolePermissionMappings
.Where(rp => rp.FeaturePermissionId == PermissionsMaster.ExpenseViewAll)
.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(notificationEmployeeTask, dataEmployeeTask);
var notificationEmployeeIds = notificationEmployeeTask.Result;
var dataEmployeeIds = dataEmployeeTask.Result;
Notification? notificationFirebase = null;
Notification? notificationCreatedBy = null;
if (expenses.StatusId == Review)
{
notificationFirebase = new Notification
{
Title = $"Expense Awaiting Your Review",
Body = $"An expense of amount \"{expenses.Amount}\" has been submitted by {name} and is awaiting your review."
};
}
else if (expenses.StatusId == Approve)
{
notificationFirebase = new Notification
{
Title = $"Expense Awaiting Your Approval",
Body = $"An expense of amount \"{expenses.Amount}\" has been reviewed by {name} and is awaiting your approval."
};
notificationCreatedBy = new Notification
{
Title = "Expense Reviewed",
Body = $"Your expense of amount \"{expenses.Amount}\" is reviewed by {name}."
};
}
else if (expenses.StatusId == ProcessPending)
{
notificationFirebase = new Notification
{
Title = $"Expense Awaiting Your Payment",
Body = $"Expense of amount \"{expenses.Amount}\" has been approved by {name}. Please complete the payment."
};
notificationCreatedBy = new Notification
{
Title = "Expense Approved",
Body = $"Your expense of amount \"{expenses.Amount}\" is approved by {name}."
};
}
else if (expenses.StatusId == Processed)
{
notificationCreatedBy = new Notification
{
Title = "Expense Paid",
Body = $"Your expense of amount \"{expenses.Amount}\" has been processed and paid by {name}."
};
}
else if (expenses.StatusId == RejectedByApprover || expenses.StatusId == RejectedByReviewer)
{
notificationCreatedBy = new Notification
{
Title = "Expense Rejected",
Body = $"Your expense of amount \"{expenses.Amount}\" has been rejected by {name}."
};
}
var data = new Dictionary<string, string>()
{
{ "Keyword", "Expenses_Modefied" },
{ "ExpenseId", expenses.Id.ToString() }
};
var registrationTokensForNotificationTask = Task.Run(async () =>
{
if (notificationFirebase != null)
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => notificationEmployeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync();
await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data);
}
});
var registrationTokensForDataTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => dataEmployeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync();
await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForNotification, data);
});
var registrationTokensForCreatedByTask = Task.Run(async () =>
{
if (notificationCreatedBy != null)
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var registrationTokensForemployee = await dbContext.FCMTokenMappings.Where(ft => ft.EmployeeId == expenses.CreatedById).Select(ft => ft.FcmToken).ToListAsync();
await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForemployee, notificationCreatedBy, data);
}
});
await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask, registrationTokensForCreatedByTask);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while get data for sending notification");
}
}
#region =================================================================== Helper Functions ===================================================================

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.Projects;
namespace Marco.Pms.Services.Service.ServiceInterfaces
@ -6,6 +7,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
public interface IFirebaseService
{
Task SendLoginMessageAsync(string name);
Task SendLoginOnAnotherDeviceMessageAsync(string FcmToken);
Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId);
Task SendAssignTaskMessageAsync(Guid workItemId, string name, List<Guid> teamMembers, Guid tenantId);
Task SendReportTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId);
@ -19,5 +21,6 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task SendProjectAllocationMessageAsync(List<ProjectAllocation> projectAllocations, string name, Guid tenantId);
Task SendModifyProjectMessageAsync(Project project, string name, bool IsExist, Guid tenantId);
Task SendExpenseMessageAsync(Expenses expenses, string name, Guid tenantId);
}
}