Added proper logs to all Expesne APIs

This commit is contained in:
ashutosh.nehete 2025-07-23 18:02:12 +05:30
parent ae1222bb96
commit 8b5b0aed4c

View File

@ -56,6 +56,7 @@ namespace Marco.Pms.Services.Service
_mapper = mapper; _mapper = mapper;
} }
#region =================================================================== Get Functions ===================================================================
/// <summary> /// <summary>
/// Retrieves a paginated list of expenses based on user permissions and optional filters. /// Retrieves a paginated list of expenses based on user permissions and optional filters.
@ -159,9 +160,6 @@ namespace Marco.Pms.Services.Service
{ {
expensesQuery = expensesQuery.Where(e => expenseFilter.CreatedByIds.Contains(e.CreatedById)); expensesQuery = expensesQuery.Where(e => expenseFilter.CreatedByIds.Contains(e.CreatedById));
} }
} }
// 4. --- Apply Ordering and Pagination --- // 4. --- Apply Ordering and Pagination ---
@ -247,11 +245,13 @@ namespace Marco.Pms.Services.Service
expenseDetails = await _cache.AddExpenseByIdAsync(id, tenantId); expenseDetails = await _cache.AddExpenseByIdAsync(id, tenantId);
if (expenseDetails == null) if (expenseDetails == null)
{ {
_logger.LogWarning("User attempted to fetch expense details with ID {ExpenseId}, but not found in both database and cache", id);
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404); return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
} }
} }
var vm = await GetAllExpnesRelatedTablesFromMongoDB(expenseDetails); var vm = await GetAllExpnesRelatedTablesFromMongoDB(expenseDetails);
_logger.LogInfo("Employee {EmployeeId} successfully fetched expense details with ID {ExpenseId}", loggedInEmployee.Id, vm.Id);
return ApiResponse<object>.SuccessResponse(vm, "Successfully fetched the details of expense", 200); return ApiResponse<object>.SuccessResponse(vm, "Successfully fetched the details of expense", 200);
} }
@ -272,13 +272,12 @@ namespace Marco.Pms.Services.Service
}, 500); }, 500);
} }
} }
public async Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId)
{ {
try try
{ {
var supplerNameList = await _context.Expenses.Where(e => e.TenantId == tenantId).Select(e => e.SupplerName).Distinct().ToListAsync(); var supplerNameList = await _context.Expenses.Where(e => e.TenantId == tenantId).Select(e => e.SupplerName).Distinct().ToListAsync();
_logger.LogInfo("Employee {EmployeeId} fetched list of organizations in a tenant {TenantId}", loggedInEmployee.Id, tenantId); _logger.LogInfo("Employee {EmployeeId} fetched list of suppler names from expenses in a tenant {TenantId}", loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(supplerNameList, $"{supplerNameList.Count} records of suppler names fetched from expense", 200); return ApiResponse<object>.SuccessResponse(supplerNameList, $"{supplerNameList.Count} records of suppler names fetched from expense", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
@ -299,6 +298,10 @@ namespace Marco.Pms.Services.Service
} }
} }
#endregion
#region =================================================================== Post Functions ===================================================================
/// <summary> /// <summary>
/// Creates a new expense entry along with its bill attachments. /// Creates a new expense entry along with its bill attachments.
/// This operation is transactional and performs validations and file uploads concurrently for optimal performance /// This operation is transactional and performs validations and file uploads concurrently for optimal performance
@ -633,6 +636,8 @@ namespace Marco.Pms.Services.Service
UpdatedAt = DateTime.UtcNow UpdatedAt = DateTime.UtcNow
}, Collection); }, Collection);
var cacheUpdateTask = _cache.ReplaceExpenseAsync(existingExpense);
// Task to get all possible next statuses from the *new* current state to help the UI. // 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. // 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 getNextStatusesTask = _dbContextFactory.CreateDbContextAsync().ContinueWith(t =>
@ -650,7 +655,7 @@ namespace Marco.Pms.Services.Service
}); });
}).Unwrap(); }).Unwrap();
await Task.WhenAll(mongoDBTask, getNextStatusesTask); await Task.WhenAll(mongoDBTask, getNextStatusesTask, cacheUpdateTask);
var nextPossibleStatuses = await getNextStatusesTask; var nextPossibleStatuses = await getNextStatusesTask;
@ -677,6 +682,11 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.SuccessResponse(response, "Status updated, but a post-processing error occurred."); return ApiResponse<object>.SuccessResponse(response, "Status updated, but a post-processing error occurred.");
} }
} }
#endregion
#region =================================================================== Put Functions ===================================================================
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 existingExpense = await _context.Expenses var existingExpense = await _context.Expenses
@ -696,6 +706,7 @@ namespace Marco.Pms.Services.Service
if (existingExpense == null) if (existingExpense == null)
{ {
_logger.LogWarning("User attempted to update expense with ID {ExpenseId}, but not found in database", id);
return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404); return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404);
} }
@ -722,14 +733,73 @@ namespace Marco.Pms.Services.Service
if (newBillAttachments.Any()) if (newBillAttachments.Any())
{ {
await ProcessAndUploadAttachmentsAsync(newBillAttachments, existingExpense, loggedInEmployee.Id, tenantId); await ProcessAndUploadAttachmentsAsync(newBillAttachments, existingExpense, loggedInEmployee.Id, tenantId);
await _context.SaveChangesAsync(); try
{
await _context.SaveChangesAsync();
_logger.LogInfo("{Count} New attachments added while updating expense {ExpenseId} by employee {EmployeeId}",
newBillAttachments.Count, existingExpense.Id, loggedInEmployee.Id);
}
catch (DbUpdateException dbEx)
{
_logger.LogError(dbEx, "Databsae Exception occured while adding new attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
}
} }
var deleteBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId != null && !ba.IsActive).ToList(); var deleteBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId != null && !ba.IsActive).ToList();
if (deleteBillAttachments.Any()) if (deleteBillAttachments.Any())
{ {
var documentIds = deleteBillAttachments.Select(d => d.DocumentId!.Value).ToList(); var documentIds = deleteBillAttachments.Select(d => d.DocumentId!.Value).ToList();
try
await DeleteAttachemnts(documentIds); {
await DeleteAttachemnts(documentIds);
_logger.LogInfo("{Count} Attachments deleted while updating expense {ExpenseId} by employee {EmployeeId}",
deleteBillAttachments.Count, existingExpense.Id, loggedInEmployee.Id);
}
catch (DbUpdateException dbEx)
{
_logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", new
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
}
} }
} }
@ -791,6 +861,11 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.SuccessResponse(response, "Status updated, but a post-processing error occurred."); return ApiResponse<object>.SuccessResponse(response, "Status updated, but a post-processing error occurred.");
} }
} }
#endregion
#region =================================================================== Delete Functions ===================================================================
public async Task<ApiResponse<object>> DeleteExpanseAsync(Guid id, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> DeleteExpanseAsync(Guid id, Employee loggedInEmployee, Guid tenantId)
{ {
var expenseQuery = _context.Expenses.Where(e => e.Id == id && e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id && e.TenantId == tenantId); var expenseQuery = _context.Expenses.Where(e => e.Id == id && e.StatusId == Draft && e.CreatedById == loggedInEmployee.Id && e.TenantId == tenantId);
@ -811,7 +886,16 @@ namespace Marco.Pms.Services.Service
var existingExpense = await expenseQuery.FirstOrDefaultAsync(); var existingExpense = await expenseQuery.FirstOrDefaultAsync();
if (existingExpense == null) if (existingExpense == null)
{ {
return ApiResponse<object>.ErrorResponse("Expense cannot be deleted", "Expense cannot be deleted", 400); var message = hasAprrovePermission ? "Expenses not found" : "Expense cannot be deleted";
if (hasAprrovePermission)
{
_logger.LogWarning("Employee {EmployeeId} attempted to delete expense {ExpenseId}, but not found in database", loggedInEmployee.Id, id);
}
else
{
_logger.LogWarning("Employee {EmployeeId} attempted to delete expense {ExpenseId}, Which is created by another employee", loggedInEmployee.Id, id);
}
return ApiResponse<object>.ErrorResponse(message, message, 400);
} }
var documentIds = await _context.BillAttachments var documentIds = await _context.BillAttachments
.Where(ba => ba.ExpensesId == existingExpense.Id) .Where(ba => ba.ExpensesId == existingExpense.Id)
@ -824,10 +908,11 @@ namespace Marco.Pms.Services.Service
try try
{ {
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogInfo("Employeee {EmployeeId} successfully deleted the expense {EmpenseId}", loggedInEmployee.Id, id);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while adding expense"); _logger.LogError(dbEx, "Databsae Exception occured while deleting expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", new
{ {
Message = dbEx.Message, Message = dbEx.Message,
@ -841,27 +926,64 @@ namespace Marco.Pms.Services.Service
} }
}, 500); }, 500);
} }
var attachmentDeletionTask = Task.Run(async () => try
{ {
await DeleteAttachemnts(documentIds); var attachmentDeletionTask = Task.Run(async () =>
}); {
await DeleteAttachemnts(documentIds);
});
var cacheTask = Task.Run(async () => var cacheTask = Task.Run(async () =>
{ {
await _cache.DeleteExpenseAsync(id, tenantId); await _cache.DeleteExpenseAsync(id, tenantId);
}); });
var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
{ {
EntityId = existingExpense.Id.ToString(), EntityId = existingExpense.Id.ToString(),
UpdatedById = loggedInEmployee.Id.ToString(), UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson, OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow UpdatedAt = DateTime.UtcNow
}, Collection); }, Collection);
await Task.WhenAll(attachmentDeletionTask, cacheTask, mongoDBTask); await Task.WhenAll(attachmentDeletionTask, cacheTask, mongoDBTask);
}
catch (DbUpdateException dbEx)
{
_logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", new
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
}
return ApiResponse<object>.SuccessResponse("Success", "Expense Deleted Successfully", 200); return ApiResponse<object>.SuccessResponse("Success", "Expense Deleted Successfully", 200);
} }
#endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
private async Task<List<ExpenseList>> GetAllExpnesRelatedTables(List<Expenses> model) private async Task<List<ExpenseList>> GetAllExpnesRelatedTables(List<Expenses> model)