Renamed the CacheHelper To Helpers

This commit is contained in:
ashutosh.nehete 2025-07-22 10:53:55 +05:30
parent 5be154a9f1
commit cfbfbf2e2b
30 changed files with 220 additions and 97 deletions

View File

@ -1,9 +1,9 @@
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Employees;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using MongoDB.Driver; using MongoDB.Driver;
namespace Marco.Pms.CacheHelper namespace Marco.Pms.Helpers.CacheHelper
{ {
public class EmployeeCache public class EmployeeCache
{ {

View File

@ -0,0 +1,24 @@
using Marco.Pms.Model.MongoDBModels.Employees;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
namespace Marco.Pms.Helpers.CacheHelper
{
public class ExpenseCache
{
private readonly IMongoCollection<EmployeePermissionMongoDB> _collection;
public ExpenseCache(IConfiguration configuration)
{
var connectionString = configuration["MongoDB:ConnectionString"];
var mongoUrl = new MongoUrl(connectionString);
var client = new MongoClient(mongoUrl); // Your MongoDB connection string
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
_collection = mongoDB.GetCollection<EmployeePermissionMongoDB>("Expenses");
}
public async Task AddExpenseToCacheAsync()
{
}
}
}

View File

@ -1,13 +1,14 @@
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Masters;
using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
namespace Marco.Pms.CacheHelper namespace Marco.Pms.Helpers
{ {
public class ProjectCache public class ProjectCache
{ {

View File

@ -1,8 +1,8 @@
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Utility;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using MongoDB.Driver; using MongoDB.Driver;
namespace Marco.Pms.CacheHelper namespace Marco.Pms.Helpers.CacheHelper
{ {
public class ReportCache public class ReportCache
{ {

View File

@ -1,10 +1,10 @@
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Utility;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using System.Collections; using System.Collections;
namespace Marco.Pms.CacheHelper namespace Marco.Pms.Helpers
{ {
public class UpdateLogHelper public class UpdateLogHelper
{ {

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>

View File

@ -1,6 +1,6 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Employees
{ {
[BsonIgnoreExtraElements] [BsonIgnoreExtraElements]
public class EmployeePermissionMongoDB public class EmployeePermissionMongoDB

View File

@ -0,0 +1,6 @@
namespace Marco.Pms.Model.MongoDBModels.Expenses
{
public class ExpenseDetailsMongoDB
{
}
}

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Masters
{ {
public class StatusMasterMongoDB public class StatusMasterMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Masters
{ {
public class WorkCategoryMasterMongoDB public class WorkCategoryMasterMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class ActivityMasterMongoDB public class ActivityMasterMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class BuildingMongoDB public class BuildingMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class FloorMongoDB public class FloorMongoDB
{ {

View File

@ -1,4 +1,6 @@
namespace Marco.Pms.Model.MongoDBModels using Marco.Pms.Model.MongoDBModels.Masters;
namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class ProjectMongoDB public class ProjectMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class WorkAreaInfoMongoDB public class WorkAreaInfoMongoDB
{ {

View File

@ -1,4 +1,4 @@
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class WorkAreaMongoDB public class WorkAreaMongoDB
{ {

View File

@ -1,4 +1,6 @@
namespace Marco.Pms.Model.MongoDBModels using Marco.Pms.Model.MongoDBModels.Masters;
namespace Marco.Pms.Model.MongoDBModels.Project
{ {
public class WorkItemMongoDB public class WorkItemMongoDB
{ {

View File

@ -1,7 +1,7 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Utility
{ {
public class ProjectReportEmailMongoDB public class ProjectReportEmailMongoDB
{ {

View File

@ -1,7 +1,7 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.MongoDBModels namespace Marco.Pms.Model.MongoDBModels.Utility
{ {
public class UpdateLogsObject public class UpdateLogsObject
{ {

View File

@ -19,7 +19,7 @@ COPY ["Marco.Pms.Services/Marco.Pms.Services.csproj", "Marco.Pms.Services/"]
COPY ["Marco.Pms.DataAccess/Marco.Pms.DataAccess.csproj", "Marco.Pms.DataAccess/"] COPY ["Marco.Pms.DataAccess/Marco.Pms.DataAccess.csproj", "Marco.Pms.DataAccess/"]
COPY ["Marco.Pms.Model/Marco.Pms.Model.csproj", "Marco.Pms.Model/"] COPY ["Marco.Pms.Model/Marco.Pms.Model.csproj", "Marco.Pms.Model/"]
COPY ["Marco.Pms.Utility/Marco.Pms.Utility.csproj", "Marco.Pms.Utility/"] COPY ["Marco.Pms.Utility/Marco.Pms.Utility.csproj", "Marco.Pms.Utility/"]
COPY ["Marco.Pms.CacheHelper/Marco.Pms.CacheHelper.csproj", "Marco.Pms.CacheHelper/"] COPY ["Marco.Pms.Helpers/Marco.Pms.Helpers.csproj", "Marco.Pms.Helpers/"]
RUN dotnet restore "./Marco.Pms.Services/Marco.Pms.Services.csproj" RUN dotnet restore "./Marco.Pms.Services/Marco.Pms.Services.csproj"
COPY . . COPY . .
WORKDIR "/src/Marco.Pms.Services" WORKDIR "/src/Marco.Pms.Services"

View File

@ -1,7 +1,10 @@
using Marco.Pms.CacheHelper; using Marco.Pms.Helpers;
using Marco.Pms.Helpers.CacheHelper;
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Masters;
using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.MongoDBModels.Utility;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;

View File

@ -1,5 +1,6 @@
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels;
using Marco.Pms.Model.MongoDBModels.Project;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;

View File

@ -2,7 +2,7 @@
using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Mail; using Marco.Pms.Model.Mail;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Report; using Marco.Pms.Model.ViewModels.Report;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;

View File

@ -5,7 +5,8 @@ using Marco.Pms.Model.Dtos.Project;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Expenses; using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Masters;
using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;

View File

@ -45,7 +45,7 @@
<ProjectReference Include="..\Marco.Pms.DataAccess\Marco.Pms.DataAccess.csproj" /> <ProjectReference Include="..\Marco.Pms.DataAccess\Marco.Pms.DataAccess.csproj" />
<ProjectReference Include="..\Marco.Pms.Model\Marco.Pms.Model.csproj" /> <ProjectReference Include="..\Marco.Pms.Model\Marco.Pms.Model.csproj" />
<ProjectReference Include="..\Marco.Pms.Utility\Marco.Pms.Utility.csproj" /> <ProjectReference Include="..\Marco.Pms.Utility\Marco.Pms.Utility.csproj" />
<ProjectReference Include="..\Marco.Pms.CacheHelper\Marco.Pms.CacheHelper.csproj" /> <ProjectReference Include="..\Marco.Pms.Helpers\Marco.Pms.Helpers.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="EmailTemplates\**\*.html"> <Content Include="EmailTemplates\**\*.html">

View File

@ -1,4 +1,5 @@
using Marco.Pms.CacheHelper; using Marco.Pms.Helpers;
using Marco.Pms.Helpers.CacheHelper;
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;

View File

@ -1,12 +1,12 @@
using AutoMapper; using AutoMapper;
using Marco.Pms.CacheHelper; using Marco.Pms.Helpers;
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.DocumentManager; using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Dtos.Expenses; using Marco.Pms.Model.Dtos.Expenses;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Expenses; using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Utility;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Expanses; using Marco.Pms.Model.ViewModels.Expanses;
@ -29,6 +29,7 @@ namespace Marco.Pms.Services.Service
private readonly UpdateLogHelper _updateLogHelper; private readonly UpdateLogHelper _updateLogHelper;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private static readonly Guid Draft = Guid.Parse("297e0d8f-f668-41b5-bfea-e03b354251c8"); private static readonly Guid Draft = Guid.Parse("297e0d8f-f668-41b5-bfea-e03b354251c8");
private static readonly Guid Rejected = Guid.Parse("d1ee5eec-24b6-4364-8673-a8f859c60729");
private static readonly string Collection = "ExpensesModificationLog"; private static readonly string Collection = "ExpensesModificationLog";
public ExpensesService( public ExpensesService(
IDbContextFactory<ApplicationDbContext> dbContextFactory, IDbContextFactory<ApplicationDbContext> dbContextFactory,
@ -391,9 +392,25 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("Successfully created Expense {ExpenseId} for Project {ProjectId}.", expense.Id, expense.ProjectId); _logger.LogInfo("Successfully created Expense {ExpenseId} for Project {ProjectId}.", expense.Id, expense.ProjectId);
return ApiResponse<object>.SuccessResponse(response, "Expense created successfully.", 201); return ApiResponse<object>.SuccessResponse(response, "Expense created successfully.", 201);
} }
catch (ArgumentException ex) // Catches bad Base64 from attachment pre-validation catch (DbUpdateException dbEx)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError(dbEx, "Databsae Exception occured while adding expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
innerexcption = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
}
catch (ArgumentException ex) // Catches bad Base64 from attachment pre-validation
{
_logger.LogError(ex, "Invalid argument during expense creation for project {ProjectId}.", dto.ProjectId); _logger.LogError(ex, "Invalid argument during expense creation for project {ProjectId}.", dto.ProjectId);
return ApiResponse<object>.ErrorResponse("Invalid Request Data.", new return ApiResponse<object>.ErrorResponse("Invalid Request Data.", new
{ {
@ -410,7 +427,6 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) // General-purpose catch for unexpected errors (e.g., S3 or DB connection failure) catch (Exception ex) // General-purpose catch for unexpected errors (e.g., S3 or DB connection failure)
{ {
await transaction.RollbackAsync();
_logger.LogError(ex, "An unhandled exception occurred while creating an expense for project {ProjectId}.", dto.ProjectId); _logger.LogError(ex, "An unhandled exception occurred while creating an expense for project {ProjectId}.", dto.ProjectId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", new return ApiResponse<object>.ErrorResponse("An internal server error occurred.", new
{ {
@ -615,15 +631,29 @@ namespace Marco.Pms.Services.Service
} }
public async Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId)
{ {
var exsitingExpense = await _context.Expenses.FirstOrDefaultAsync(e => e.Id == model.Id && e.TenantId == tenantId); var existingExpense = await _context.Expenses
.Include(e => e.ExpensesType)
.Include(e => e.Project)
.Include(e => e.PaidBy)
.ThenInclude(e => e!.JobRole)
.Include(e => e.PaymentMode)
.Include(e => e.Status)
.Include(e => e.CreatedBy)
.FirstOrDefaultAsync(e =>
e.Id == model.Id &&
e.CreatedById == loggedInEmployee.Id &&
(e.StatusId == Draft || e.StatusId == Rejected) &&
e.TenantId == tenantId);
if (exsitingExpense == null) if (existingExpense == null)
{ {
return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404); return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404);
} }
_mapper.Map(model, exsitingExpense);
_context.Entry(exsitingExpense).State = EntityState.Modified; var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense); // Capture state for audit log BEFORE changes
_mapper.Map(model, existingExpense);
_context.Entry(existingExpense).State = EntityState.Modified;
try try
{ {
@ -637,9 +667,61 @@ namespace Marco.Pms.Services.Service
_logger.LogError(ex, "Concurrency conflict while updating project {ProjectId} ", id); _logger.LogError(ex, "Concurrency conflict while updating project {ProjectId} ", id);
return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409); return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409);
} }
var response = _mapper.Map<ExpenseList>(exsitingExpense); try
{
// Task to save the detailed audit log to a separate system (e.g., MongoDB).
var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
{
EntityId = existingExpense.Id.ToString(),
UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow
}, Collection);
// Task to get all possible next statuses from the *new* current state to help the UI.
// NOTE: This now fetches a list of all possible next states, which is more useful for a UI.
var getNextStatusesTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(t =>
{
var dbContext = t.Result;
return dbContext.ExpensesStatusMapping
.Include(s => s.NextStatus)
.Where(s => s.StatusId == existingExpense.StatusId && s.NextStatus != null && s.TenantId == tenantId)
.Select(s => s.NextStatus) // Select only the status object
.ToListAsync()
.ContinueWith(res =>
{
dbContext.Dispose(); // Ensure the context is disposed
return res.Result;
});
}).Unwrap();
await Task.WhenAll(mongoDBTask, getNextStatusesTask);
var nextPossibleStatuses = await getNextStatusesTask;
var response = _mapper.Map<ExpenseList>(existingExpense);
if (nextPossibleStatuses != null)
{
// The response DTO should have a property like: public List<ExpensesStatusMasterVM> NextAvailableStatuses { get; set; }
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(nextPossibleStatuses);
}
return ApiResponse<object>.SuccessResponse(response); return ApiResponse<object>.SuccessResponse(response);
} }
catch (Exception ex)
{
// This catch block handles errors from post-save operations like MongoDB logging.
// The primary update was successful, but we must log this failure.
_logger.LogError(ex, "Error occurred during post-save operations for ExpenseId: {ExpenseId} (e.g., audit logging). The primary status change was successful.", existingExpense.Id);
// We can still return a success response because the main operation succeeded,
// but we should not block the user for a failed audit log.
// Alternatively, if audit logging is critical, you could return an error.
// Here, we choose to return success but log the ancillary failure.
var response = _mapper.Map<ExpenseList>(existingExpense);
return ApiResponse<object>.SuccessResponse(response, "Status updated, but a post-processing error occurred.");
}
}
public void Delete(int id) public void Delete(int id)
{ {

View File

@ -5,7 +5,7 @@ using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.Project; using Marco.Pms.Model.Dtos.Project;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;

View File

@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marco.Pms.Utility", "Marco.
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marco.Pms.Services", "Marco.Pms.Services\Marco.Pms.Services.csproj", "{27A83653-5B7F-4135-9886-01594D54AFAE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marco.Pms.Services", "Marco.Pms.Services\Marco.Pms.Services.csproj", "{27A83653-5B7F-4135-9886-01594D54AFAE}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marco.Pms.CacheHelper", "Marco.Pms.CacheHelper\Marco.Pms.CacheHelper.csproj", "{1A105C22-4ED7-4F54-8834-6923DDD96852}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marco.Pms.Helpers", "Marco.Pms.Helpers\Marco.Pms.Helpers.csproj", "{1A105C22-4ED7-4F54-8834-6923DDD96852}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution