394 lines
19 KiB
C#

using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace MarcoBMS.Services.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class TaskController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly UserHelper _userHelper;
private readonly ILoggingService _logger;
private readonly PermissionServices _permissionServices;
private readonly Guid Approve_Task;
public TaskController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices)
{
_context = context;
_userHelper = userHelper;
_logger = logger;
_permissionServices = permissionServices;
Approve_Task = Guid.Parse("db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c");
}
private Guid GetTenantId()
{
return _userHelper.GetTenantId();
}
[HttpPost("assign")]
public async Task<IActionResult> AssignTask([FromBody] AssignTaskDto assignTask)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
var tenantId = GetTenantId();
var Employee = await _userHelper.GetCurrentEmployeeAsync();
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(Employee.Id, tenantId);
_context.TaskAllocations.Add(taskAllocation);
await _context.SaveChangesAsync();
var response = taskAllocation.ToAssignTaskVMFromTaskAllocation();
var teamMembers = new List<TaskMembers> { };
if (assignTask.TaskTeam != null)
{
foreach (var teamMember in assignTask.TaskTeam)
{
var result = new TaskMembers
{
TaskAllocationId = taskAllocation.Id,
EmployeeId = teamMember,
TenantId = tenantId,
};
teamMembers.Add(result);
}
}
_context.TaskMembers.AddRange(teamMembers);
await _context.SaveChangesAsync();
var idList = teamMembers.Select(m => m.EmployeeId);
List<Employee> employees = await _context.Employees.Where(e => idList.Contains(e.Id)).ToListAsync();
List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
foreach (var employee in employees)
{
team.Add(employee.ToBasicEmployeeVMFromEmployee());
}
response.teamMembers = team;
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assignned successfully", 200));
}
/// <summary>
/// Reports progress on a specific task allocation including checklist updates and comments.
/// </summary>
/// <param name="reportTask">Task progress details submitted by the employee.</param>
/// <returns>Returns the updated task report along with checklist and comments.</returns>
[HttpPost("report")]
public async Task<IActionResult> ReportTaskProgress([FromBody] ReportTaskDto reportTask)
{
// Validate model
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogWarning("Invalid model state while reporting task. Errors: {@Errors}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
var tenantId = GetTenantId();
var employee = await _userHelper.GetCurrentEmployeeAsync();
// Get task allocation and associated work item
var taskAllocation = await _context.TaskAllocations
.Include(t => t.WorkItem)
.FirstOrDefaultAsync(t => t.Id == reportTask.Id);
if (taskAllocation == null)
{
_logger.LogWarning("TaskAllocation not found. TaskId: {TaskId}, EmployeeId: {EmployeeId}", reportTask.Id, employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400));
}
// Update completed work in the work item if applicable
if (taskAllocation.WorkItem != null)
{
if (taskAllocation.CompletedTask != 0)
{
taskAllocation.WorkItem.CompletedWork -= taskAllocation.CompletedTask;
}
taskAllocation.WorkItem.CompletedWork += reportTask.CompletedTask;
}
// Update task progress
taskAllocation.ReportedDate = reportTask.ReportedDate;
taskAllocation.ReportedById = employee.Id;
taskAllocation.CompletedTask = reportTask.CompletedTask;
// Process checklist
List<CheckListMappings> checkListMappings = new List<CheckListMappings>();
List<CheckListVM> checkListVMs = new List<CheckListVM>();
if (reportTask.CheckList != null && reportTask.CheckList.Any())
{
var checkListIds = reportTask.CheckList.Select(c => c.Id).ToList();
var checkList = await _context.ActivityCheckLists
.Where(c => checkListIds.Contains(c.Id))
.ToListAsync();
foreach (var checkDto in reportTask.CheckList)
{
// Map checklist DTO to ViewModel
var activityId = taskAllocation.WorkItem?.ActivityId ?? Guid.Empty;
checkListVMs.Add(checkDto.ToCheckListVMFromReportCheckListDto(activityId));
// If checked, prepare mapping
if (checkDto.IsChecked)
{
var check = checkList.FirstOrDefault(c => c.Id == checkDto.Id);
if (check != null)
{
checkListMappings.Add(new CheckListMappings
{
CheckListId = check.Id,
TaskAllocationId = reportTask.Id
});
}
}
}
_context.CheckListMappings.AddRange(checkListMappings);
}
// Add task comment
var comment = reportTask.ToCommentFromReportTaskDto(tenantId, employee.Id);
_context.TaskComments.Add(comment);
await _context.SaveChangesAsync();
_logger.LogInfo("Task {TaskId} progress reported by Employee {EmployeeId}", reportTask.Id, employee.Id);
// Prepare response
var response = taskAllocation.ToReportTaskVMFromTaskAllocation();
var comments = await _context.TaskComments
.Where(c => c.TaskAllocationId == taskAllocation.Id)
.ToListAsync();
response.Comments = comments.Select(c => c.ToCommentVMFromTaskComment()).ToList();
response.checkList = checkListVMs;
return Ok(ApiResponse<object>.SuccessResponse(response, "Task reported successfully", 200));
}
[HttpPost("comment")]
public async Task<IActionResult> AddCommentForTask([FromBody] CreateCommentDto createComment)
{
var tenantId = GetTenantId();
var Employee = await _userHelper.GetCurrentEmployeeAsync();
var comment = createComment.ToCommentFromCommentDto(tenantId, Employee.Id);
_context.TaskComments.Add(comment);
await _context.SaveChangesAsync();
CommentVM response = comment.ToCommentVMFromTaskComment();
return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
}
[HttpGet("list")]
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
{
Guid tenantId = GetTenantId();
DateTime fromDate = new DateTime();
DateTime toDate = new DateTime();
if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
{
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid starting date.", "Invalid starting date.", 400));
}
if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
{
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid ending date.", "Invalid ending date.", 400));
}
if (dateFrom == null) fromDate = DateTime.UtcNow.Date;
if (dateTo == null) toDate = fromDate.AddDays(1);
var jobroles = await _context.JobRoles.Where(r => r.TenantId == tenantId).ToListAsync();
//var taskAllocations = await _context.TaskAllocations.Where(t => t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId && t.AssignmentDate >= fromDate && t.AssignmentDate <= toDate && t.TenantId == tenantId).Include(t => t.WorkItemId).ToListAsync();
List<Building> buildings = await _context.Buildings.Where(b => b.ProjectId == projectId && b.TenantId == tenantId).ToListAsync();
List<Guid> idList = buildings.Select(b => b.Id).ToList();
List<Floor> floors = await _context.Floor.Where(f => idList.Contains(f.BuildingId) && f.TenantId == tenantId).ToListAsync();
idList = floors.Select(f => f.Id).ToList();
List<WorkArea> workAreas = await _context.WorkAreas.Where(a => idList.Contains(a.FloorId) && a.TenantId == tenantId).ToListAsync();
idList = workAreas.Select(a => a.Id).ToList();
List<WorkItem> workItems = await _context.WorkItems.Where(i => idList.Contains(i.WorkAreaId) && i.TenantId == tenantId).Include(i => i.ActivityMaster).ToListAsync();
idList = workItems.Select(i => i.Id).ToList();
var activityIdList = workItems.Select(i => i.ActivityId).ToList();
List<TaskAllocation> taskAllocations = await _context.TaskAllocations.Where(t => idList.Contains(t.WorkItemId) && t.AssignmentDate.Date >= fromDate.Date && t.AssignmentDate.Date <= toDate.Date && t.TenantId == tenantId).Include(t => t.WorkItem).Include(t => t.Employee).ToListAsync();
var taskIdList = taskAllocations.Select(t => t.Id).ToList();
List<TaskMembers> teamMembers = await _context.TaskMembers.Where(t => taskIdList.Contains(t.TaskAllocationId)).ToListAsync();
var employeeIdList = teamMembers.Select(e => e.EmployeeId).ToList();
List<Employee> employees = await _context.Employees.Where(e => employeeIdList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
List<ListTaskVM> tasks = new List<ListTaskVM>();
//foreach (var workItem in workItems)
//{
foreach (var taskAllocation in taskAllocations)
{
var response = taskAllocation.ToListTaskVMFromTaskAllocation();
List<TaskComment> comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
List<TaskMembers> taskMembers = teamMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).ToList();
foreach (var taskMember in taskMembers)
{
var teamMember = employees.Find(e => e.Id == taskMember.EmployeeId);
if (teamMember != null)
{
team.Add(teamMember.ToBasicEmployeeVMFromEmployee());
}
}
List<CommentVM> commentVM = new List<CommentVM> { };
foreach (var comment in comments)
{
commentVM.Add(comment.ToCommentVMFromTaskComment());
}
List<ActivityCheckList> checkLists = await _context.ActivityCheckLists.Where(x => x.ActivityId == (taskAllocation.WorkItem != null ? taskAllocation.WorkItem.ActivityId : Guid.Empty)).ToListAsync();
List<CheckListMappings> checkListMappings = await _context.CheckListMappings.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
List<CheckListVM> checkList = new List<CheckListVM>();
foreach (var check in checkLists)
{
var checkListMapping = checkListMappings.Find(c => c.CheckListId == check.Id);
if (checkListMapping != null)
{
checkList.Add(check.ToCheckListVMFromActivityCheckList(check.ActivityId, true));
}
else
{
checkList.Add(check.ToCheckListVMFromActivityCheckList(check.ActivityId, false));
}
}
response.comments = commentVM;
response.teamMembers = team;
response.CheckList = checkList;
tasks.Add(response);
}
//}
return Ok(ApiResponse<object>.SuccessResponse(tasks, "Success", 200));
}
[HttpGet("get/{taskId}")]
public async Task<IActionResult> GetTask(Guid taskId)
{
if (taskId == Guid.Empty) return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invalid data", 400));
var taskAllocation = await _context.TaskAllocations.Include(t => t.Tenant).Include(t => t.Employee).Include(t => t.WorkItem).FirstOrDefaultAsync(t => t.Id == taskId);
if (taskAllocation != null && taskAllocation.Employee != null && taskAllocation.Tenant != null)
{
//var employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == taskAllocation.AssignedBy);
string employeeName = System.String.Format("{0} {1}", taskAllocation.Employee.FirstName, taskAllocation.Employee.LastName);
string tenantName = taskAllocation.Tenant.ContactName ?? string.Empty;
if (taskAllocation == null) return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
var taskVM = taskAllocation.TaskAllocationToTaskVM(employeeName);
var comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
var team = await _context.TaskMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).Include(m => m.Employee).ToListAsync();
var teamMembers = new List<EmployeeVM> { };
foreach (var member in team)
{
var result = member.Employee != null ? member.Employee.ToEmployeeVMFromEmployee() : new EmployeeVM();
teamMembers.Add(result);
}
List<CommentVM> Comments = new List<CommentVM> { };
foreach (var comment in comments)
{
Comments.Add(comment.ToCommentVMFromTaskComment());
}
taskVM.Comments = Comments;
taskVM.TeamMembers = teamMembers;
return Ok(ApiResponse<object>.SuccessResponse(taskVM, "Success", 200));
}
return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not Found", 404));
}
/// <summary>
/// Approves a task allocation by the logged-in employee.
/// </summary>
/// <param name="id">The ID of the task to approve.</param>
/// <returns>Returns success or error response based on operation outcome.</returns>
[HttpPost("approve/{id}")]
public async Task<IActionResult> ApproveTask(Guid id)
{
// Get the current tenant ID from the logged-in user context
Guid tenantId = _userHelper.GetTenantId();
// Get the currently logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
_logger.LogInfo("Employee {EmployeeId} is attempting to approve Task {TaskId}", loggedInEmployee.Id, id);
// Fetch the task allocation by ID and tenant
var taskAllocation = await _context.TaskAllocations
.FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId && t.ReportedDate != null);
// If the task allocation does not exist
if (taskAllocation == null)
{
_logger.LogWarning("Task {TaskId} not found for Tenant {TenantId} by Employee {EmployeeId}", id, tenantId, loggedInEmployee.Id);
return NotFound(ApiResponse<object>.ErrorResponse("Task not found", "Task not found", 404));
}
// Check if the employee has permission to approve the task
var hasPermission = await _permissionServices.HasPermission(Approve_Task, loggedInEmployee.Id);
if (!hasPermission)
{
_logger.LogWarning("Employee {EmployeeId} attempted to approve Task {TaskId} without proper permissions", loggedInEmployee.Id, id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403));
}
// Update task allocation with approval details
taskAllocation.ApprovedById = loggedInEmployee.Id;
taskAllocation.ApprovedDate = DateTime.UtcNow;
taskAllocation.TaskStatus = TASK_STATUS.APPROVED;
// Save changes to the database
await _context.SaveChangesAsync();
_logger.LogInfo("Employee {EmployeeId} successfully approved Task {TaskId}", loggedInEmployee.Id, id);
// Return success response
return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200));
}
}
}