diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index 6276df8..b7d8370 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -56,6 +56,7 @@ namespace Marco.Pms.Services.Service _mapper = mapper; } + #region =================================================================== Get Functions =================================================================== /// /// 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)); } - - - } // 4. --- Apply Ordering and Pagination --- @@ -247,11 +245,13 @@ namespace Marco.Pms.Services.Service expenseDetails = await _cache.AddExpenseByIdAsync(id, tenantId); 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.ErrorResponse("Expense Not Found", "Expense Not Found", 404); } } var vm = await GetAllExpnesRelatedTablesFromMongoDB(expenseDetails); + _logger.LogInfo("Employee {EmployeeId} successfully fetched expense details with ID {ExpenseId}", loggedInEmployee.Id, vm.Id); return ApiResponse.SuccessResponse(vm, "Successfully fetched the details of expense", 200); } @@ -272,13 +272,12 @@ namespace Marco.Pms.Services.Service }, 500); } } - public async Task> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId) { try { 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.SuccessResponse(supplerNameList, $"{supplerNameList.Count} records of suppler names fetched from expense", 200); } catch (DbUpdateException dbEx) @@ -299,6 +298,10 @@ namespace Marco.Pms.Services.Service } } + #endregion + + #region =================================================================== Post Functions =================================================================== + /// /// Creates a new expense entry along with its bill attachments. /// 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 }, Collection); + var cacheUpdateTask = _cache.ReplaceExpenseAsync(existingExpense); + // 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 => @@ -650,7 +655,7 @@ namespace Marco.Pms.Services.Service }); }).Unwrap(); - await Task.WhenAll(mongoDBTask, getNextStatusesTask); + await Task.WhenAll(mongoDBTask, getNextStatusesTask, cacheUpdateTask); var nextPossibleStatuses = await getNextStatusesTask; @@ -677,6 +682,11 @@ namespace Marco.Pms.Services.Service return ApiResponse.SuccessResponse(response, "Status updated, but a post-processing error occurred."); } } + + #endregion + + #region =================================================================== Put Functions =================================================================== + public async Task> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId) { var existingExpense = await _context.Expenses @@ -696,6 +706,7 @@ namespace Marco.Pms.Services.Service if (existingExpense == null) { + _logger.LogWarning("User attempted to update expense with ID {ExpenseId}, but not found in database", id); return ApiResponse.ErrorResponse("Expense not found", "Expense not found", 404); } @@ -722,14 +733,73 @@ namespace Marco.Pms.Services.Service if (newBillAttachments.Any()) { 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.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(); if (deleteBillAttachments.Any()) { var documentIds = deleteBillAttachments.Select(d => d.DocumentId!.Value).ToList(); - - await DeleteAttachemnts(documentIds); + try + { + 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.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.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.SuccessResponse(response, "Status updated, but a post-processing error occurred."); } } + + #endregion + + #region =================================================================== Delete Functions =================================================================== + public async Task> 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); @@ -811,7 +886,16 @@ namespace Marco.Pms.Services.Service var existingExpense = await expenseQuery.FirstOrDefaultAsync(); if (existingExpense == null) { - return ApiResponse.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.ErrorResponse(message, message, 400); } var documentIds = await _context.BillAttachments .Where(ba => ba.ExpensesId == existingExpense.Id) @@ -824,10 +908,11 @@ namespace Marco.Pms.Services.Service try { await _context.SaveChangesAsync(); + _logger.LogInfo("Employeee {EmployeeId} successfully deleted the expense {EmpenseId}", loggedInEmployee.Id, id); } catch (DbUpdateException dbEx) { - _logger.LogError(dbEx, "Databsae Exception occured while adding expense"); + _logger.LogError(dbEx, "Databsae Exception occured while deleting expense"); return ApiResponse.ErrorResponse("Databsae Exception", new { Message = dbEx.Message, @@ -841,27 +926,64 @@ namespace Marco.Pms.Services.Service } }, 500); } - var attachmentDeletionTask = Task.Run(async () => + try { - await DeleteAttachemnts(documentIds); - }); + var attachmentDeletionTask = Task.Run(async () => + { + await DeleteAttachemnts(documentIds); + }); - var cacheTask = Task.Run(async () => - { - await _cache.DeleteExpenseAsync(id, tenantId); - }); - var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject - { - EntityId = existingExpense.Id.ToString(), - UpdatedById = loggedInEmployee.Id.ToString(), - OldObject = existingEntityBson, - UpdatedAt = DateTime.UtcNow - }, Collection); + var cacheTask = Task.Run(async () => + { + await _cache.DeleteExpenseAsync(id, tenantId); + }); + var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject + { + EntityId = existingExpense.Id.ToString(), + UpdatedById = loggedInEmployee.Id.ToString(), + OldObject = existingEntityBson, + UpdatedAt = DateTime.UtcNow + }, 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.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.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.SuccessResponse("Success", "Expense Deleted Successfully", 200); } + #endregion + #region =================================================================== Helper Functions =================================================================== private async Task> GetAllExpnesRelatedTables(List model)