From b6dfb30f92f8cd3dc3a25dcaff31b2359547a93d Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 23 Jul 2025 17:22:19 +0530 Subject: [PATCH] Create API base for delete expense API --- .../Controllers/ExpenseController.cs | 13 +- Marco.Pms.Services/Service/ExpensesService.cs | 153 +++++++++++++----- .../ServiceInterfaces/IExpensesService.cs | 1 + 3 files changed, 117 insertions(+), 50 deletions(-) diff --git a/Marco.Pms.Services/Controllers/ExpenseController.cs b/Marco.Pms.Services/Controllers/ExpenseController.cs index 5a17d3d..a895125 100644 --- a/Marco.Pms.Services/Controllers/ExpenseController.cs +++ b/Marco.Pms.Services/Controllers/ExpenseController.cs @@ -54,14 +54,6 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); } - /// - /// Creates a new expense entry along with its bill attachments. - /// This operation is transactional and performs validations and file uploads concurrently for optimal performance - /// by leveraging async/await without unnecessary thread-pool switching via Task.Run. - /// - /// The data transfer object containing expense details and attachments. - /// An IActionResult indicating the result of the creation operation. - [HttpPost("create")] public async Task CreateExpense([FromBody] CreateExpensesDto model) { @@ -92,8 +84,11 @@ namespace Marco.Pms.Services.Controllers } [HttpDelete("delete/{id}")] - public void Delete(int id) + public async Task DeleteExpanse(Guid id) { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _expensesService.DeleteExpanseAsync(id, loggedInEmployee, tenantId); + return StatusCode(response.StatusCode, response); } } diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index d37142f..94f0cd7 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -702,47 +702,9 @@ namespace Marco.Pms.Services.Service var deleteBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId != null && !ba.IsActive).ToList(); if (deleteBillAttachments.Any()) { - var documentIds = deleteBillAttachments.Select(d => d.DocumentId).ToList(); - - var attachmentTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - var attachments = await dbContext.BillAttachments.AsNoTracking().Where(ba => documentIds.Contains(ba.DocumentId)).ToListAsync(); - - dbContext.BillAttachments.RemoveRange(attachments); - await dbContext.SaveChangesAsync(); - }); - var documentsTask = Task.Run(async () => - { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); - var documents = await dbContext.Documents.AsNoTracking().Where(ba => documentIds.Contains(ba.Id)).ToListAsync(); - - if (documents.Any()) - { - dbContext.Documents.RemoveRange(documents); - await dbContext.SaveChangesAsync(); - - List deletionObject = new List(); - foreach (var document in documents) - { - deletionObject.Add(new S3DeletionObject - { - Key = document.S3Key - }); - if (!string.IsNullOrWhiteSpace(document.ThumbS3Key) && document.ThumbS3Key != document.S3Key) - { - deletionObject.Add(new S3DeletionObject - { - Key = document.ThumbS3Key - }); - } - } - await _updateLogHelper.PushToS3DeletionAsync(deletionObject); - } - }); - - await Task.WhenAll(attachmentTask, documentsTask); + var documentIds = deleteBillAttachments.Select(d => d.DocumentId!.Value).ToList(); + await DeleteAttachemnts(documentIds); } } @@ -805,8 +767,75 @@ namespace Marco.Pms.Services.Service } } - public void Delete(int id) + 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); + + var hasAprrovePermissionTask = Task.Run(async () => + { + using var scope = _serviceScopeFactory.CreateScope(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id); + }); + + var hasAprrovePermission = await hasAprrovePermissionTask; + if (!hasAprrovePermission) + { + expenseQuery = expenseQuery.Where(e => e.CreatedById == loggedInEmployee.Id); + } + + var existingExpense = await expenseQuery.FirstOrDefaultAsync(); + if (existingExpense == null) + { + return ApiResponse.ErrorResponse("Expense cannot be deleted", "Expense cannot be deleted", 400); + } + var documentIds = await _context.BillAttachments + .Where(ba => ba.ExpensesId == existingExpense.Id) + .Select(ba => ba.DocumentId) + .ToListAsync(); + + var existingEntityBson = _updateLogHelper.EntityToBsonDocument(existingExpense); + + _context.Expenses.Remove(existingExpense); + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateException dbEx) + { + _logger.LogError(dbEx, "Databsae Exception occured while adding 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 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); + + await Task.WhenAll(attachmentDeletionTask, cacheTask, mongoDBTask); + return ApiResponse.SuccessResponse("Success", "Expense Deleted Successfully", 200); } #region =================================================================== Helper Functions =================================================================== @@ -1085,6 +1114,48 @@ namespace Marco.Pms.Services.Service return (document, billAttachment); } + private async Task DeleteAttachemnts(List documentIds) + { + var attachmentTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var attachments = await dbContext.BillAttachments.AsNoTracking().Where(ba => documentIds.Contains(ba.DocumentId)).ToListAsync(); + + dbContext.BillAttachments.RemoveRange(attachments); + await dbContext.SaveChangesAsync(); + }); + var documentsTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + var documents = await dbContext.Documents.AsNoTracking().Where(ba => documentIds.Contains(ba.Id)).ToListAsync(); + + if (documents.Any()) + { + dbContext.Documents.RemoveRange(documents); + await dbContext.SaveChangesAsync(); + + List deletionObject = new List(); + foreach (var document in documents) + { + deletionObject.Add(new S3DeletionObject + { + Key = document.S3Key + }); + if (!string.IsNullOrWhiteSpace(document.ThumbS3Key) && document.ThumbS3Key != document.S3Key) + { + deletionObject.Add(new S3DeletionObject + { + Key = document.ThumbS3Key + }); + } + } + await _updateLogHelper.PushToS3DeletionAsync(deletionObject); + } + }); + + await Task.WhenAll(attachmentTask, documentsTask); + } + #endregion } } diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs index 75d937a..5d2f7e4 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs @@ -11,5 +11,6 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> CreateExpenseAsync(CreateExpensesDto dto, Employee loggedInEmployee, Guid tenantId); Task> ChangeStatusAsync(ExpenseRecordDto model, Employee loggedInEmployee, Guid tenantId); Task> UpdateExpanseAsync(Guid id, UpdateExpensesDto model, Employee loggedInEmployee, Guid tenantId); + Task> DeleteExpanseAsync(Guid id, Employee loggedInEmployee, Guid tenantId); } }