Added the organization filter in get project allocation API
This commit is contained in:
parent
05bfa48115
commit
9b59a4d6b6
@ -201,8 +201,8 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
|
|
||||||
#region =================================================================== Project Allocation APIs ===================================================================
|
#region =================================================================== Project Allocation APIs ===================================================================
|
||||||
|
|
||||||
[HttpGet("employees/get/{projectid?}/{includeInactive?}")]
|
[HttpGet("employees/get/{projectId}")]
|
||||||
public async Task<IActionResult> GetEmployeeByProjectId(Guid? projectId, bool includeInactive = false)
|
public async Task<IActionResult> GetEmployeeByProjectId(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive = false)
|
||||||
{
|
{
|
||||||
// --- Step 1: Input Validation ---
|
// --- Step 1: Input Validation ---
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
@ -214,12 +214,12 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
|
|
||||||
// --- Step 2: Prepare data without I/O ---
|
// --- Step 2: Prepare data without I/O ---
|
||||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var response = await _projectServices.GetEmployeeByProjectIdAsync(projectId, includeInactive, tenantId, loggedInEmployee);
|
var response = await _projectServices.GetEmployeeByProjectIdAsync(projectId, organizationId, includeInactive, tenantId, loggedInEmployee);
|
||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("allocation/{projectId}")]
|
[HttpGet("allocation/{projectId}")]
|
||||||
public async Task<IActionResult> GetProjectAllocation(Guid? projectId)
|
public async Task<IActionResult> GetProjectAllocation(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive = false)
|
||||||
{
|
{
|
||||||
// --- Step 1: Input Validation ---
|
// --- Step 1: Input Validation ---
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
@ -231,7 +231,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
|
|
||||||
// --- Step 2: Prepare data without I/O ---
|
// --- Step 2: Prepare data without I/O ---
|
||||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var response = await _projectServices.GetProjectAllocationAsync(projectId, tenantId, loggedInEmployee);
|
var response = await _projectServices.GetProjectAllocationAsync(projectId, organizationId, includeInactive, tenantId, loggedInEmployee);
|
||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,10 +480,10 @@ namespace Marco.Pms.Services.Service
|
|||||||
/// <param name="tenantId">The ID of the current tenant.</param>
|
/// <param name="tenantId">The ID of the current tenant.</param>
|
||||||
/// <param name="loggedInEmployee">The current authenticated employee (used for permission checks).</param>
|
/// <param name="loggedInEmployee">The current authenticated employee (used for permission checks).</param>
|
||||||
/// <returns>An ApiResponse containing a list of employees or an error.</returns>
|
/// <returns>An ApiResponse containing a list of employees or an error.</returns>
|
||||||
public async Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid? projectId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
|
public async Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
|
||||||
{
|
{
|
||||||
// --- Step 1: Input Validation ---
|
// --- Step 1: Input Validation ---
|
||||||
if (projectId == null)
|
if (projectId == Guid.Empty)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("GetEmployeeByProjectID called with a null projectId.");
|
_logger.LogWarning("GetEmployeeByProjectID called with a null projectId.");
|
||||||
// 400 Bad Request is more appropriate for invalid input than 404 Not Found.
|
// 400 Bad Request is more appropriate for invalid input than 404 Not Found.
|
||||||
@ -500,7 +500,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
// --- CRITICAL: Security Check ---
|
// --- CRITICAL: Security Check ---
|
||||||
// Before fetching data, you MUST verify the user has permission to see it.
|
// Before fetching data, you MUST verify the user has permission to see it.
|
||||||
// This is a placeholder for your actual permission logic.
|
// This is a placeholder for your actual permission logic.
|
||||||
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value);
|
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
|
||||||
var hasAllEmployeePermission = await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
|
var hasAllEmployeePermission = await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
|
||||||
var hasviewTeamPermission = await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id, projectId);
|
var hasviewTeamPermission = await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id, projectId);
|
||||||
|
|
||||||
@ -514,6 +514,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
// We start with the base query and conditionally add filters before executing it.
|
// We start with the base query and conditionally add filters before executing it.
|
||||||
// This avoids code duplication and is highly performant.
|
// This avoids code duplication and is highly performant.
|
||||||
var employeeQuery = _context.ProjectAllocations
|
var employeeQuery = _context.ProjectAllocations
|
||||||
|
.Include(pa => pa.Employee)
|
||||||
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId);
|
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId);
|
||||||
|
|
||||||
// Conditionally apply the filter for active allocations.
|
// Conditionally apply the filter for active allocations.
|
||||||
@ -522,16 +523,23 @@ namespace Marco.Pms.Services.Service
|
|||||||
employeeQuery = employeeQuery.Where(pa => pa.IsActive);
|
employeeQuery = employeeQuery.Where(pa => pa.IsActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conditionally apply the filter for organization ID.
|
||||||
|
if (organizationId.HasValue)
|
||||||
|
{
|
||||||
|
employeeQuery = employeeQuery.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Step 3: Project Directly to the ViewModel on the Database Server ---
|
// --- Step 3: Project Directly to the ViewModel on the Database Server ---
|
||||||
// This is the most significant performance optimization.
|
// This is the most significant performance optimization.
|
||||||
// Instead of fetching full Employee entities, we select only the data needed for the EmployeeVM.
|
// Instead of fetching full Employee entities, we select only the data needed for the EmployeeVM.
|
||||||
// AutoMapper's ProjectTo is perfect for this, as it translates the mapping configuration into an efficient SQL SELECT statement.
|
// AutoMapper's ProjectTo is perfect for this, as it translates the mapping configuration into an efficient SQL SELECT statement.
|
||||||
var resultVM = await employeeQuery
|
|
||||||
|
var projectAllocations = await employeeQuery
|
||||||
.Where(pa => pa.Employee != null) // Safety check for data integrity
|
.Where(pa => pa.Employee != null) // Safety check for data integrity
|
||||||
.Select(pa => pa.Employee) // Navigate to the Employee entity
|
|
||||||
.ProjectTo<EmployeeVM>(_mapper.ConfigurationProvider) // Let AutoMapper generate the SELECT
|
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
var resultVM = projectAllocations.Select(pa => _mapper.Map<EmployeeVM>(pa.Employee)).ToList();
|
||||||
|
|
||||||
_logger.LogInfo("Successfully fetched {EmployeeCount} employees for project {ProjectId}.", resultVM.Count, projectId);
|
_logger.LogInfo("Successfully fetched {EmployeeCount} employees for project {ProjectId}.", resultVM.Count, projectId);
|
||||||
|
|
||||||
// Note: The original mapping loop is now completely gone, replaced by the single efficient query above.
|
// Note: The original mapping loop is now completely gone, replaced by the single efficient query above.
|
||||||
@ -554,10 +562,10 @@ namespace Marco.Pms.Services.Service
|
|||||||
/// <param name="tenantId">The ID of the current tenant.</param>
|
/// <param name="tenantId">The ID of the current tenant.</param>
|
||||||
/// <param name="loggedInEmployee">The current authenticated employee for permission checks.</param>
|
/// <param name="loggedInEmployee">The current authenticated employee for permission checks.</param>
|
||||||
/// <returns>An ApiResponse containing allocation details or an appropriate error.</returns>
|
/// <returns>An ApiResponse containing allocation details or an appropriate error.</returns>
|
||||||
public async Task<ApiResponse<object>> GetProjectAllocationAsync(Guid? projectId, Guid tenantId, Employee loggedInEmployee)
|
public async Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
|
||||||
{
|
{
|
||||||
// --- Step 1: Input Validation ---
|
// --- Step 1: Input Validation ---
|
||||||
if (projectId == null)
|
if (projectId == Guid.Empty)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("GetProjectAllocation called with a null projectId.");
|
_logger.LogWarning("GetProjectAllocation called with a null projectId.");
|
||||||
return ApiResponse<object>.ErrorResponse("Project ID is required.", "Invalid Input Parameter", 400);
|
return ApiResponse<object>.ErrorResponse("Project ID is required.", "Invalid Input Parameter", 400);
|
||||||
@ -573,7 +581,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
// --- Step 2: Security and Existence Checks ---
|
// --- Step 2: Security and Existence Checks ---
|
||||||
// Before fetching data, you MUST verify the user has permission to see it.
|
// Before fetching data, you MUST verify the user has permission to see it.
|
||||||
// This is a placeholder for your actual permission logic.
|
// This is a placeholder for your actual permission logic.
|
||||||
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value);
|
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
|
||||||
if (!hasPermission)
|
if (!hasPermission)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId);
|
_logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId);
|
||||||
@ -582,11 +590,27 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
// --- Step 3: Execute a Single, Optimized Database Query ---
|
// --- Step 3: Execute a Single, Optimized Database Query ---
|
||||||
// This query projects directly to a new object on the database server, which is highly efficient.
|
// This query projects directly to a new object on the database server, which is highly efficient.
|
||||||
var allocations = await _context.ProjectAllocations
|
var projectAllocationQuery = _context.ProjectAllocations
|
||||||
// Filter down to the relevant records first.
|
.Include(pa => pa.Employee)
|
||||||
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId && pa.Employee != null)
|
.ThenInclude(e => e!.JobRole)
|
||||||
// Project directly to the final shape. This tells EF Core which columns to select.
|
.Include(pa => pa.Employee)
|
||||||
// The redundant .Include() is removed as EF Core infers the JOIN from this Select.
|
.ThenInclude(e => e!.Organization)
|
||||||
|
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId);
|
||||||
|
|
||||||
|
// Conditionally apply the filter for active allocations.
|
||||||
|
if (!includeInactive)
|
||||||
|
{
|
||||||
|
projectAllocationQuery = projectAllocationQuery.Where(pa => pa.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditionally apply the filter for organization ID.
|
||||||
|
if (organizationId.HasValue)
|
||||||
|
{
|
||||||
|
projectAllocationQuery = projectAllocationQuery
|
||||||
|
.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId.Value && pa.Employee.Organization != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var allocations = await projectAllocationQuery
|
||||||
.Select(pa => new
|
.Select(pa => new
|
||||||
{
|
{
|
||||||
// Fields from ProjectAllocation
|
// Fields from ProjectAllocation
|
||||||
@ -602,6 +626,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
LastName = pa.Employee.LastName,
|
LastName = pa.Employee.LastName,
|
||||||
MiddleName = pa.Employee.MiddleName,
|
MiddleName = pa.Employee.MiddleName,
|
||||||
|
|
||||||
|
OrganizationName = pa.Employee.Organization!.Name,
|
||||||
|
|
||||||
// Simplified JobRoleId logic: Use the allocation's role if it exists, otherwise fall back to the employee's default role.
|
// Simplified JobRoleId logic: Use the allocation's role if it exists, otherwise fall back to the employee's default role.
|
||||||
JobRoleId = pa.JobRoleId ?? pa.Employee.JobRoleId
|
JobRoleId = pa.JobRoleId ?? pa.Employee.JobRoleId
|
||||||
})
|
})
|
||||||
|
@ -17,8 +17,8 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
|||||||
Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<object>> CreateProjectAsync(CreateProjectDto projectDto, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> CreateProjectAsync(CreateProjectDto projectDto, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<object>> UpdateProjectAsync(Guid id, UpdateProjectDto updateProjectDto, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> UpdateProjectAsync(Guid id, UpdateProjectDto updateProjectDto, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid? projectId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<object>> GetProjectAllocationAsync(Guid? projectId, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<List<ProjectAllocationVM>>> ManageAllocationAsync(List<ProjectAllocationDot> projectAllocationDots, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<List<ProjectAllocationVM>>> ManageAllocationAsync(List<ProjectAllocationDot> projectAllocationDots, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<object>> GetProjectsByEmployeeAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<object>> GetProjectsByEmployeeAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee);
|
||||||
Task<ApiResponse<List<ProjectAllocationVM>>> AssigneProjectsToEmployeeAsync(List<ProjectsAllocationDto> projectAllocationDtos, Guid employeeId, Guid tenantId, Employee loggedInEmployee);
|
Task<ApiResponse<List<ProjectAllocationVM>>> AssigneProjectsToEmployeeAsync(List<ProjectsAllocationDto> projectAllocationDtos, Guid employeeId, Guid tenantId, Employee loggedInEmployee);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user