Merge pull request 'Added the filter and pagenation in get task list API' (#136) from Ashutosh_Enhancement_#1293 into Organization_Management
Reviewed-on: #136
This commit is contained in:
commit
0c6f5e0df0
10
Marco.Pms.Model/Filters/TaskFilter.cs
Normal file
10
Marco.Pms.Model/Filters/TaskFilter.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class TaskFilter
|
||||
{
|
||||
public List<Guid>? BuildingIds { get; set; }
|
||||
public List<Guid>? FloorIds { get; set; }
|
||||
public List<Guid>? ActivityIds { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using Marco.Pms.Model.Activities;
|
||||
using Marco.Pms.Model.Dtos.Activities;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Filters;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
@ -17,6 +18,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using Document = Marco.Pms.Model.DocumentManager.Document;
|
||||
|
||||
namespace MarcoBMS.Services.Controllers
|
||||
@ -428,7 +430,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? filter, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20,
|
||||
[FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
{
|
||||
_logger.LogInfo("GetTasksList called for projectId: {ProjectId}, dateFrom: {DateFrom}, dateTo: {DateTo}", projectId, dateFrom ?? "", dateTo ?? "");
|
||||
|
||||
@ -436,76 +439,103 @@ namespace MarcoBMS.Services.Controllers
|
||||
DateTime fromDate = new DateTime();
|
||||
DateTime toDate = new DateTime();
|
||||
|
||||
// Parse and validate dateFrom
|
||||
// 1. Parse and validate dateFrom
|
||||
if (dateFrom != null && !DateTime.TryParse(dateFrom, out fromDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid starting date provided: {DateFrom}", dateFrom);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid starting date.", "Invalid starting date.", 400));
|
||||
}
|
||||
|
||||
// Parse and validate dateTo
|
||||
// 2. Parse and validate dateTo
|
||||
if (dateTo != null && !DateTime.TryParse(dateTo, out toDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid ending date provided: {DateTo}", dateTo);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid ending date.", "Invalid ending date.", 400));
|
||||
}
|
||||
|
||||
// Set default date range if not provided
|
||||
// 3. Set default date range if not provided
|
||||
fromDate = dateFrom == null ? DateTime.UtcNow.Date : fromDate;
|
||||
toDate = dateTo == null ? fromDate.AddDays(1) : toDate;
|
||||
|
||||
// 1. Get all buildings under this project
|
||||
_logger.LogInfo("Fetching buildings for projectId: {ProjectId}", projectId);
|
||||
var buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId && b.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var buildingIds = buildings.Select(b => b.Id).ToList();
|
||||
|
||||
// 2. Get floors under the buildings
|
||||
var floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId) && f.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var floorIds = floors.Select(f => f.Id).ToList();
|
||||
|
||||
// 3. Get work areas under the floors
|
||||
var workAreas = await _context.WorkAreas
|
||||
.Where(a => floorIds.Contains(a.FloorId) && a.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var workAreaIds = workAreas.Select(a => a.Id).ToList();
|
||||
|
||||
// 4. Get work items under the work areas
|
||||
var workItems = await _context.WorkItems
|
||||
.Where(i => workAreaIds.Contains(i.WorkAreaId) && i.TenantId == tenantId)
|
||||
.Include(i => i.ActivityMaster)
|
||||
.ToListAsync();
|
||||
var workItemIds = workItems.Select(i => i.Id).ToList();
|
||||
|
||||
_logger.LogInfo("Fetching task allocations between {FromDate} and {ToDate}", fromDate, toDate);
|
||||
|
||||
// 5. Get task allocations in the specified date range
|
||||
var taskAllocations = await _context.TaskAllocations
|
||||
// 4. Get task allocations in the specified date range
|
||||
var taskAllocationQuery = _context.TaskAllocations
|
||||
.Include(t => t.Employee)
|
||||
.Include(t => t.ReportedBy)
|
||||
.Include(t => t.ApprovedBy)
|
||||
.Include(t => t.WorkStatus)
|
||||
.Include(t => t.WorkItem)
|
||||
.Where(t => workItemIds.Contains(t.WorkItemId) &&
|
||||
.ThenInclude(wi => wi!.ActivityMaster)
|
||||
.ThenInclude(a => a!.ActivityGroup)
|
||||
.ThenInclude(ag => ag!.Service)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkCategoryMaster)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkArea)
|
||||
.ThenInclude(wa => wa!.Floor)
|
||||
.ThenInclude(f => f!.Building)
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building.ProjectId != projectId &&
|
||||
t.AssignmentDate.Date >= fromDate.Date &&
|
||||
t.AssignmentDate.Date <= toDate.Date &&
|
||||
t.TenantId == tenantId)
|
||||
t.TenantId == tenantId);
|
||||
|
||||
var taskFilter = TryDeserializeFilter(filter);
|
||||
|
||||
if (taskFilter != null)
|
||||
{
|
||||
if (taskFilter.BuildingIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
taskFilter.BuildingIds.Contains(t.WorkItem.WorkArea.Floor.BuildingId));
|
||||
}
|
||||
if (taskFilter.FloorIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
taskFilter.FloorIds.Contains(t.WorkItem.WorkArea.FloorId));
|
||||
}
|
||||
if (taskFilter.ActivityIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
taskFilter.ActivityIds.Contains(t.WorkItem.ActivityId));
|
||||
}
|
||||
if (taskFilter.ServiceIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
taskFilter.ServiceIds.Contains(t.WorkItem.ActivityMaster.ActivityGroup.ServiceId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int totalRecords = await taskAllocationQuery.CountAsync();
|
||||
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
|
||||
|
||||
var taskAllocations = await taskAllocationQuery
|
||||
.OrderByDescending(t => t.AssignmentDate)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
var taskIds = taskAllocations.Select(t => t.Id).ToList();
|
||||
|
||||
// 6. Load team members
|
||||
// 5. Load team members
|
||||
_logger.LogInfo("Loading task members and related employee data.");
|
||||
var teamMembers = await _context.TaskMembers
|
||||
.Include(t => t.Employee)
|
||||
.Where(t => taskIds.Contains(t.TaskAllocationId))
|
||||
.ToListAsync();
|
||||
|
||||
// 7. Load task comments
|
||||
// 6. Load task comments
|
||||
_logger.LogInfo("Fetching comments and attachments.");
|
||||
var allComments = await _context.TaskComments
|
||||
.Include(c => c.Employee)
|
||||
@ -513,14 +543,14 @@ namespace MarcoBMS.Services.Controllers
|
||||
.ToListAsync();
|
||||
var commentIds = allComments.Select(c => c.Id).ToList();
|
||||
|
||||
// 8. Load all attachments (task and comment)
|
||||
// 7. Load all attachments (task and comment)
|
||||
var attachments = await _context.TaskAttachments
|
||||
.Where(t => taskIds.Contains(t.ReferenceId) || commentIds.Contains(t.ReferenceId))
|
||||
.ToListAsync();
|
||||
|
||||
var documentIds = attachments.Select(t => t.DocumentId).ToList();
|
||||
|
||||
// 9. Load actual documents from attachment references
|
||||
// 8. Load actual documents from attachment references
|
||||
var documents = await _context.Documents
|
||||
.Where(d => documentIds.Contains(d.Id))
|
||||
.ToListAsync();
|
||||
@ -606,9 +636,18 @@ namespace MarcoBMS.Services.Controllers
|
||||
tasks.Add(response);
|
||||
}
|
||||
|
||||
var VM = new
|
||||
{
|
||||
TotalCount = totalRecords,
|
||||
TotalPages = totalPages,
|
||||
CurrentPage = pageNumber,
|
||||
PageSize = pageSize,
|
||||
Data = tasks
|
||||
};
|
||||
|
||||
_logger.LogInfo("Task list constructed successfully. Returning {Count} tasks.", tasks.Count);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(tasks, "Success", 200));
|
||||
return Ok(ApiResponse<object>.SuccessResponse(VM, "Success", 200));
|
||||
}
|
||||
|
||||
[HttpGet("get/{taskId}")]
|
||||
@ -869,5 +908,44 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200));
|
||||
}
|
||||
|
||||
private TaskFilter? TryDeserializeFilter(string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
TaskFilter? expenseFilter = null;
|
||||
|
||||
try
|
||||
{
|
||||
// First, try to deserialize directly. This is the expected case (e.g., from a web client).
|
||||
expenseFilter = JsonSerializer.Deserialize<TaskFilter>(filter, options);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
|
||||
// If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients).
|
||||
try
|
||||
{
|
||||
// Unescape the string first, then deserialize the result.
|
||||
string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
|
||||
if (!string.IsNullOrWhiteSpace(unescapedJsonString))
|
||||
{
|
||||
expenseFilter = JsonSerializer.Deserialize<TaskFilter>(unescapedJsonString, options);
|
||||
}
|
||||
}
|
||||
catch (JsonException ex1)
|
||||
{
|
||||
// If both attempts fail, log the final error and return null.
|
||||
_logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return expenseFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user