From c881964ab1359d89437a4134c9da0ecc1f2aa431 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 24 Jul 2025 16:07:39 +0530 Subject: [PATCH] Added proper validation and logs in get expesne status, expenses type and payment mode APIs --- .../Controllers/MasterController.cs | 64 ++++++--- Marco.Pms.Services/Service/MasterService.cs | 130 +++++++++++++++--- .../ServiceInterfaces/IMasterService.cs | 9 +- 3 files changed, 161 insertions(+), 42 deletions(-) diff --git a/Marco.Pms.Services/Controllers/MasterController.cs b/Marco.Pms.Services/Controllers/MasterController.cs index 608caed..6b059b1 100644 --- a/Marco.Pms.Services/Controllers/MasterController.cs +++ b/Marco.Pms.Services/Controllers/MasterController.cs @@ -29,6 +29,7 @@ namespace Marco.Pms.Services.Controllers private readonly ILoggingService _logger; private readonly MasterHelper _masterHelper; private readonly IMasterService _masterService; + private readonly Guid tenantId; public MasterController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, MasterHelper masterHelper, IMasterService masterService) { _context = context; @@ -36,9 +37,11 @@ namespace Marco.Pms.Services.Controllers _logger = logger; _masterHelper = masterHelper; _masterService = masterService; + tenantId = userHelper.GetTenantId(); } - // -------------------------------- Activity -------------------------------- + #region =================================================================== Activity APIs =================================================================== + [HttpGet] [Route("activities")] public async Task GetActivitiesMaster() @@ -189,7 +192,9 @@ namespace Marco.Pms.Services.Controllers return Ok(ApiResponse.SuccessResponse(new { }, "Activity Deleted Successfully", 200)); } - // -------------------------------- Industry -------------------------------- + #endregion + + #region =================================================================== Industry APIs =================================================================== [HttpGet] [Route("industries")] @@ -202,7 +207,9 @@ namespace Marco.Pms.Services.Controllers return Ok(ApiResponse.SuccessResponse(industries, System.String.Format("{0} industry records fetched successfully", industries.Count), 200)); } - // -------------------------------- Ticket Status -------------------------------- + #endregion + + #region =================================================================== Ticket Status APIs =================================================================== [HttpGet("ticket-status")] public async Task GetTicketStatusMaster() @@ -289,7 +296,9 @@ namespace Marco.Pms.Services.Controllers } } - // -------------------------------- Ticket Type -------------------------------- + #endregion + + #region =================================================================== Ticket Type APIs =================================================================== [HttpGet("ticket-types")] public async Task GetTicketTypeMaster() @@ -377,7 +386,9 @@ namespace Marco.Pms.Services.Controllers } } - // -------------------------------- Ticket Priority -------------------------------- + #endregion + + #region =================================================================== Ticket Priority APIs =================================================================== [HttpGet("ticket-priorities")] public async Task GetTicketPriorityMaster() @@ -465,7 +476,9 @@ namespace Marco.Pms.Services.Controllers } } - // -------------------------------- Ticket Tag -------------------------------- + #endregion + + #region =================================================================== Ticket Tag APIs =================================================================== [HttpGet("ticket-tags")] public async Task GetTicketTagMaster() @@ -553,7 +566,9 @@ namespace Marco.Pms.Services.Controllers } } - // -------------------------------- Work Category -------------------------------- + #endregion + + #region =================================================================== Work Category APIs =================================================================== [HttpGet("work-categories")] public async Task GetWorkCategoryMasterList() @@ -674,7 +689,9 @@ namespace Marco.Pms.Services.Controllers } } - // -------------------------------- Work Status -------------------------------- + #endregion + + #region =================================================================== Work Status APIs =================================================================== [HttpGet("work-status")] public async Task GetWorkStatusMasterList() @@ -713,7 +730,9 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); } - // -------------------------------- Contact Category -------------------------------- + #endregion + + #region =================================================================== Contact Category APIs =================================================================== [HttpGet("contact-categories")] public async Task GetContactCategoryMasterList() @@ -782,7 +801,9 @@ namespace Marco.Pms.Services.Controllers return Ok(response); } - // -------------------------------- Contact Tag -------------------------------- + #endregion + + #region =================================================================== Contact Tag APIs =================================================================== [HttpGet("contact-tags")] public async Task GetContactTagMasterList() @@ -791,12 +812,6 @@ namespace Marco.Pms.Services.Controllers return Ok(response); } - //[HttpGet("contact-tag/{id}")] - //public async Task GetContactTagMaster(Guid id) - //{ - // return Ok(); - //} - [HttpPost("contact-tag")] public async Task CreateContactTagMaster([FromBody] CreateContactTagDto contactTagDto) { @@ -849,27 +864,34 @@ namespace Marco.Pms.Services.Controllers var response = await _masterHelper.DeleteContactTag(id); return Ok(response); } + #endregion + #region =================================================================== Expenses Type APIs =================================================================== + [HttpGet("expenses-types")] public async Task GetExpenseTypeList() { - var response = await _masterService.GetExpenseTypeListAsync(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _masterService.GetExpenseTypeListAsync(loggedInEmployee, tenantId); return StatusCode(response.StatusCode, response); } [HttpPost("expenses-type")] public async Task CreateExpenseType(ExpensesTypeMasterDto dto) { - var response = await _masterService.GetExpenseTypeListAsync(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _masterService.GetExpenseTypeListAsync(loggedInEmployee, tenantId); return StatusCode(response.StatusCode, response); } #endregion #region =================================================================== Expenses Status APIs =================================================================== + [HttpGet("expenses-status")] public async Task GetExpenseStatusList() { - var response = await _masterService.GetExpenseStatusListAsync(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _masterService.GetExpenseStatusListAsync(loggedInEmployee, tenantId); return StatusCode(response.StatusCode, response); } @@ -877,10 +899,12 @@ namespace Marco.Pms.Services.Controllers #endregion #region =================================================================== Payment mode APIs =================================================================== + [HttpGet("payment-modes")] public async Task GetPaymentModeList() { - var response = await _masterService.GetPaymentModeListAsync(); + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _masterService.GetPaymentModeListAsync(loggedInEmployee, tenantId); return StatusCode(response.StatusCode, response); } diff --git a/Marco.Pms.Services/Service/MasterService.cs b/Marco.Pms.Services/Service/MasterService.cs index bd74bce..2705e43 100644 --- a/Marco.Pms.Services/Service/MasterService.cs +++ b/Marco.Pms.Services/Service/MasterService.cs @@ -1,11 +1,12 @@ using AutoMapper; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Master; +using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Master; using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Services.Service.ServiceInterfaces; -using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; @@ -15,36 +16,50 @@ namespace Marco.Pms.Services.Service { private readonly ApplicationDbContext _context; private readonly ILoggingService _logger; - private readonly UserHelper _userHelper; private readonly PermissionServices _permission; private readonly IMapper _mapper; - private readonly Guid tenantId; public MasterService( ApplicationDbContext context, ILoggingService logger, - UserHelper userHelper, PermissionServices permission, IMapper mapper) { _context = context; _logger = logger; - _userHelper = userHelper; _permission = permission; _mapper = mapper; - tenantId = userHelper.GetTenantId(); } #region =================================================================== Expenses Type APIs =================================================================== - public async Task> GetExpenseTypeListAsync() + public async Task> GetExpenseTypeListAsync(Employee loggedInEmployee, Guid tenantId) { - var typeList = await _context.ExpensesTypeMaster.Where(et => et.TenantId == tenantId).ToListAsync(); - return ApiResponse.SuccessResponse(typeList); + try + { + // Validation if employee is taking action in same tenant + if (tenantId != loggedInEmployee.TenantId) + { + _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of expense type from different tenant", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Access Denied", "User do not have access for this information", 403); + } + + // Featching the list of Expenses Type. + var typeList = await _context.ExpensesTypeMaster.Where(et => et.TenantId == tenantId).ToListAsync(); + var response = _mapper.Map>(typeList); + + _logger.LogInfo("{Count} records of expense type have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id); + return ApiResponse.SuccessResponse(response, $"{response.Count} records of expense type have been fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occured while fetching list of expense type list by employee {EmployeeId}", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500); + } + } - public async Task> CreateExpenseTypeAsync(ExpensesTypeMasterDto dto) + public async Task> CreateExpenseTypeAsync(ExpensesTypeMasterDto dto, Employee loggedInEmployee, Guid tenantId) { - var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id); if (!hasManagePermission) { @@ -52,26 +67,105 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to Upload expenses for this project", 403); } var expensesType = _mapper.Map(dto); - return ApiResponse.SuccessResponse(expensesType); + _context.ExpensesTypeMaster.Add(expensesType); + await _context.SaveChangesAsync(); + _logger.LogInfo("New Expense Type {ExpensesTypeId} was added by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id); + + var response = _mapper.Map(expensesType); + return ApiResponse.SuccessResponse(response, "Expense type craeted Successfully", 201); } #endregion #region =================================================================== Expenses Status APIs =================================================================== - public async Task> GetExpenseStatusListAsync() + public async Task> GetExpenseStatusListAsync(Employee loggedInEmployee, Guid tenantId) { - var typeList = await _context.ExpensesStatusMaster.Where(et => et.TenantId == tenantId).ToListAsync(); - return ApiResponse.SuccessResponse(typeList); + + try + { + // Validation if employee is taking action in same tenant + if (tenantId != loggedInEmployee.TenantId) + { + _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of expense status from different tenant", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Access Denied", "User do not have access for this information", 403); + } + + // Featching the list of Expenses Status. + var statusList = await _context.ExpensesStatusMaster.Where(es => es.TenantId == tenantId).ToListAsync(); + var response = _mapper.Map>(statusList); + + var statusIds = statusList.Select(s => s.Id).ToList(); + var permissionStatusMapping = await _context.StatusPermissionMapping + .Where(ps => statusIds.Contains(ps.StatusId)) + .GroupBy(ps => ps.StatusId) + .Select(g => new + { + StatusId = g.Key, + PermissionIds = g.Select(ps => ps.PermissionId).ToList() + }).ToListAsync(); + + foreach (var status in response) + { + status.PermissionIds = permissionStatusMapping.Where(ps => ps.StatusId == status.Id).Select(ps => ps.PermissionIds).FirstOrDefault(); + } + + _logger.LogInfo("{Count} records of expense status have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id); + return ApiResponse.SuccessResponse(response, $"{response.Count} records of expense status have been fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occured while fetching list of expense sattus list by employee {EmployeeId}", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500); + } } #endregion #region =================================================================== Payment mode APIs =================================================================== - public async Task> GetPaymentModeListAsync() + public async Task> GetPaymentModeListAsync(Employee loggedInEmployee, Guid tenantId) { - var typeList = await _context.PaymentModeMatser.Where(et => et.TenantId == tenantId).ToListAsync(); - return ApiResponse.SuccessResponse(typeList); + try + { + // Validation if employee is taking action in same tenant + if (tenantId != loggedInEmployee.TenantId) + { + _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of payment modes from different tenant", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Access Denied", "User do not have access for this information", 403); + } + + // Featching the list of Payment Modes. + var paymentModes = await _context.PaymentModeMatser.Where(pm => pm.TenantId == tenantId).ToListAsync(); + var response = _mapper.Map>(paymentModes); + + _logger.LogInfo("{Count} records of payment modes have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id); + return ApiResponse.SuccessResponse(response, $"{response.Count} records of payment modes have been fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occured while featching list of payment modes list by employee {EmployeeId}", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Internal Error occured while featching list of payment modes list", ExceptionMapper(ex), 500); + } + } + + + #endregion + + #region =================================================================== Helper Function =================================================================== + private static object ExceptionMapper(Exception ex) + { + return new + { + Message = ex.Message, + StackTrace = ex.StackTrace, + Source = ex.Source, + InnerException = new + { + Message = ex.InnerException?.Message, + StackTrace = ex.InnerException?.StackTrace, + Source = ex.InnerException?.Source, + } + }; } diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs index 1b970ca..e427fc4 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs @@ -1,11 +1,12 @@ -using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.Employees; +using Marco.Pms.Model.Utilities; namespace Marco.Pms.Services.Service.ServiceInterfaces { public interface IMasterService { - Task> GetExpenseTypeListAsync(); - Task> GetExpenseStatusListAsync(); - Task> GetPaymentModeListAsync(); + Task> GetExpenseTypeListAsync(Employee loggedInEmployee, Guid tenantId); + Task> GetExpenseStatusListAsync(Employee loggedInEmployee, Guid tenantId); + Task> GetPaymentModeListAsync(Employee loggedInEmployee, Guid tenantId); } }