Added proper reponse message in expense APIs

This commit is contained in:
ashutosh.nehete 2025-07-28 18:00:37 +05:30
parent 5b091a8d6f
commit 0b2883af0f
2 changed files with 68 additions and 167 deletions

View File

@ -206,34 +206,12 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while fetching list expenses"); _logger.LogError(dbEx, "Databsae Exception occured while fetching list expenses");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
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) catch (Exception ex)
{ {
_logger.LogError(ex, "Error occured while fetching list expenses"); _logger.LogError(ex, "Error occured while fetching list expenses");
return ApiResponse<object>.ErrorResponse("Error Occured", new return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500);
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
} }
} }
public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId)
@ -259,18 +237,7 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "An unhandled exception occurred while fetching an expense details {ExpenseId}.", id); _logger.LogError(ex, "An unhandled exception occurred while fetching an expense details {ExpenseId}.", id);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", new return ApiResponse<object>.ErrorResponse("An internal server error occurred.", ExceptionMapper(ex), 500);
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
} }
} }
public async Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId)
@ -284,18 +251,7 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while fetching suppler name list from expense"); _logger.LogError(dbEx, "Databsae Exception occured while fetching suppler name list from expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
} }
} }
@ -448,50 +404,17 @@ namespace Marco.Pms.Services.Service
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError(dbEx, "Databsae Exception occured while adding expense"); _logger.LogError(dbEx, "Databsae Exception occured while adding expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
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 (ArgumentException ex) // Catches bad Base64 from attachment pre-validation 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.", ExceptionMapper(ex), 400);
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 400);
} }
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)
{ {
_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.", ExceptionMapper(ex), 500);
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
} }
} }
@ -705,8 +628,6 @@ namespace Marco.Pms.Services.Service
.Include(e => e.CreatedBy) .Include(e => e.CreatedBy)
.FirstOrDefaultAsync(e => .FirstOrDefaultAsync(e =>
e.Id == model.Id && e.Id == model.Id &&
e.CreatedById == loggedInEmployee.Id &&
(e.StatusId == Draft || e.StatusId == Rejected) &&
e.TenantId == tenantId); e.TenantId == tenantId);
@ -716,6 +637,17 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404); return ApiResponse<object>.ErrorResponse("Expense not found", "Expense not found", 404);
} }
if (existingExpense.StatusId != Draft && existingExpense.StatusId != Rejected)
{
_logger.LogWarning("User attempted to update expense with ID {ExpenseId}, but donot have status of DRAFT or REJECTED", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Expense connot be updated", "Expense connot be updated", 400);
}
if (existingExpense.CreatedById != loggedInEmployee.Id)
{
_logger.LogWarning("User attempted to update expense with ID {ExpenseId} which not created by them", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You donot have access to update this expense", "You donot have access to update this expense", 400);
}
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense); // Capture state for audit log BEFORE changes var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense); // Capture state for audit log BEFORE changes
_mapper.Map(model, existingExpense); _mapper.Map(model, existingExpense);
_context.Entry(existingExpense).State = EntityState.Modified; _context.Entry(existingExpense).State = EntityState.Modified;
@ -748,18 +680,7 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while adding new attachments during updating expense"); _logger.LogError(dbEx, "Databsae Exception occured while adding new attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
} }
} }
@ -777,34 +698,12 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense"); _logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
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) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception occured while deleting attachments during updating expense"); _logger.LogError(ex, "Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", new return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", ExceptionMapper(ex), 500);
{
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = new
{
Message = ex.InnerException?.Message,
StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source,
}
}, 500);
} }
} }
} }
@ -874,7 +773,11 @@ namespace Marco.Pms.Services.Service
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.TenantId == tenantId); var expenseTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.Expenses.Where(e => e.Id == id && e.StatusId == Draft && e.TenantId == tenantId).FirstOrDefaultAsync();
});
var hasAprrovePermissionTask = Task.Run(async () => var hasAprrovePermissionTask = Task.Run(async () =>
{ {
@ -883,13 +786,11 @@ namespace Marco.Pms.Services.Service
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id); return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
}); });
var hasAprrovePermission = await hasAprrovePermissionTask; await Task.WhenAll(expenseTask, hasAprrovePermissionTask);
if (!hasAprrovePermission)
{ var hasAprrovePermission = hasAprrovePermissionTask.Result;
expenseQuery = expenseQuery.Where(e => e.CreatedById == loggedInEmployee.Id); var existingExpense = expenseTask.Result;
}
var existingExpense = await expenseQuery.FirstOrDefaultAsync();
if (existingExpense == null) if (existingExpense == null)
{ {
var message = hasAprrovePermission ? "Expenses not found" : "Expense cannot be deleted"; var message = hasAprrovePermission ? "Expenses not found" : "Expense cannot be deleted";
@ -903,6 +804,19 @@ namespace Marco.Pms.Services.Service
} }
return ApiResponse<object>.ErrorResponse(message, message, 400); return ApiResponse<object>.ErrorResponse(message, message, 400);
} }
if (existingExpense.StatusId != Draft)
{
_logger.LogWarning("User attempted to delete expense with ID {ExpenseId}, but donot have status of DRAFT or REJECTED", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Expense connot be deleted", "Expense connot be deleted", 400);
}
if (!hasAprrovePermission && existingExpense.CreatedById != loggedInEmployee.Id)
{
_logger.LogWarning("User attempted to delete expense with ID {ExpenseId} which not created by them", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You donot have access to delete this expense", "You donot have access to delete this expense", 400);
}
var documentIds = await _context.BillAttachments var documentIds = await _context.BillAttachments
.Where(ba => ba.ExpensesId == existingExpense.Id) .Where(ba => ba.ExpensesId == existingExpense.Id)
.Select(ba => ba.DocumentId) .Select(ba => ba.DocumentId)
@ -919,18 +833,7 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while deleting expense"); _logger.LogError(dbEx, "Databsae Exception occured while deleting expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
Message = dbEx.Message,
StackTrace = dbEx.StackTrace,
Source = dbEx.Source,
InnerException = new
{
Message = dbEx.InnerException?.Message,
StackTrace = dbEx.InnerException?.StackTrace,
Source = dbEx.InnerException?.Source,
}
}, 500);
} }
try try
{ {
@ -956,23 +859,22 @@ namespace Marco.Pms.Services.Service
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense"); _logger.LogError(dbEx, "Databsae Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Databsae Exception", new return ApiResponse<object>.ErrorResponse("Databsae Exception", ExceptionMapper(dbEx), 500);
{
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) catch (Exception ex)
{ {
_logger.LogError(ex, "Exception occured while deleting attachments during updating expense"); _logger.LogError(ex, "Exception occured while deleting attachments during updating expense");
return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", new return ApiResponse<object>.ErrorResponse("Exception occured while deleting attachments during updating expense ", ExceptionMapper(ex), 500);
}
return ApiResponse<object>.SuccessResponse("Success", "Expense Deleted Successfully", 200);
}
#endregion
#region =================================================================== Helper Functions ===================================================================
private static object ExceptionMapper(Exception ex)
{
return new
{ {
Message = ex.Message, Message = ex.Message,
StackTrace = ex.StackTrace, StackTrace = ex.StackTrace,
@ -983,15 +885,8 @@ namespace Marco.Pms.Services.Service
StackTrace = ex.InnerException?.StackTrace, StackTrace = ex.InnerException?.StackTrace,
Source = ex.InnerException?.Source, Source = ex.InnerException?.Source,
} }
}, 500); };
} }
return ApiResponse<object>.SuccessResponse("Success", "Expense Deleted Successfully", 200);
}
#endregion
#region =================================================================== Helper Functions ===================================================================
private async Task<List<ExpenseList>> GetAllExpnesRelatedTables(List<Expenses> model, Guid tenantId) private async Task<List<ExpenseList>> GetAllExpnesRelatedTables(List<Expenses> model, Guid tenantId)
{ {
List<ExpenseList> expenseList = new List<ExpenseList>(); List<ExpenseList> expenseList = new List<ExpenseList>();

View File

@ -455,7 +455,7 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403); return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
} }
var expensesStatus = await _context.ExpensesStatusMaster.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId && !et.IsSystem); var expensesStatus = await _context.ExpensesStatusMaster.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);
// Checking if Expense Status exists // Checking if Expense Status exists
if (expensesStatus == null) if (expensesStatus == null)
@ -464,6 +464,12 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Expense Status not found", "Expense Status not found", 404); return ApiResponse<object>.ErrorResponse("Expense Status not found", "Expense Status not found", 404);
} }
if (expensesStatus.IsSystem)
{
_logger.LogWarning("Employee {Employee} attempts to {Action} Expense status, but status is system defined", loggedInEmployee.Id, action);
return ApiResponse<object>.ErrorResponse($"Expense Status is system defined cannot able to {action}", $"Expense Status is system defined cannot able to {action}", 400);
}
// Mapping ExpensesStatusMaster to BsonDocument // Mapping ExpensesStatusMaster to BsonDocument
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expensesStatus); var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expensesStatus);