Firebase_Implementation #135
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 ===================================================================
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user