Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.api into Firebase_Implementation

This commit is contained in:
ashutosh.nehete 2025-08-14 14:45:13 +05:30
commit f4cb08f472
6 changed files with 196 additions and 2 deletions

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.ViewModels.Projects
{
public class ProjectHisteryVM
{
public string? ProjectName { get; set; }
public string? ProjectShortName { get; set; }
public DateTime AssignedDate { get; set; }
public DateTime? RemovedDate { get; set; }
public string? Designation { get; set; }
}
}

View File

@ -276,6 +276,23 @@ namespace MarcoBMS.Services.Controllers
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("allocation-histery/{employeeId}")]
public async Task<IActionResult> GetProjectByEmployeeBasic([FromRoute] Guid employeeId)
{
// --- Step 1: Input Validation ---
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
_logger.LogWarning("Get project list by employee Id called with invalid model state \n Errors: {Errors}", string.Join(", ", errors));
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request data provided.", errors, 400));
}
// --- Step 2: Prepare data without I/O ---
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetProjectByEmployeeBasicAsync(employeeId, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}
[HttpPost("assign-projects/{employeeId}")] [HttpPost("assign-projects/{employeeId}")]
public async Task<IActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId) public async Task<IActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId)
{ {
@ -338,6 +355,25 @@ namespace MarcoBMS.Services.Controllers
var response = await _projectServices.GetWorkItemsAsync(workAreaId, tenantId, loggedInEmployee); var response = await _projectServices.GetWorkItemsAsync(workAreaId, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("tasks-employee/{employeeId}")]
public async Task<IActionResult> GetTasksByEmployee(Guid employeeId, [FromQuery] DateTime? fromDate, DateTime? toDate)
{
// --- Step 1: Input Validation ---
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
_logger.LogWarning("Get Work Items by employeeId called with invalid model state \n Errors: {Errors}", string.Join(", ", errors));
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request data provided.", errors, 400));
}
if (!toDate.HasValue) toDate = DateTime.UtcNow;
if (!fromDate.HasValue) fromDate = toDate.Value.AddDays(-7);
// --- Step 2: Prepare data without I/O ---
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetTasksByEmployeeAsync(employeeId, fromDate.Value, toDate.Value, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}
#endregion #endregion

View File

@ -20,7 +20,7 @@ namespace MarcoBMS.Services.Helpers
public async Task<Employee> GetEmployeeByID(Guid EmployeeID) public async Task<Employee> GetEmployeeByID(Guid EmployeeID)
{ {
return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID && e.IsActive == true) ?? new Employee { }; return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { };
} }
public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID) public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID)

View File

@ -895,6 +895,73 @@ namespace Marco.Pms.Services.Service
return ApiResponse<List<ProjectAllocationVM>>.SuccessResponse(resultVm, "Assignments managed successfully.", 200); return ApiResponse<List<ProjectAllocationVM>>.SuccessResponse(resultVm, "Assignments managed successfully.", 200);
} }
public async Task<ApiResponse<object>> GetProjectByEmployeeBasicAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee)
{
// Log the start of the method execution with key input parameters
_logger.LogInfo("Fetching projects for EmployeeId: {EmployeeId}, TenantId: {TenantId} by User: {UserId}",
employeeId, tenantId, loggedInEmployee.Id);
try
{
// Retrieve project allocations linked to the specified employee and tenant
var projectAllocation = await _context.ProjectAllocations
.AsNoTracking() // Optimization: no tracking since entities are not updated
.Include(pa => pa.Project) // Include related Project data
.Include(pa => pa.Employee).ThenInclude(e => e!.JobRole) // Include related Employee and their JobRole
.Where(pa => pa.EmployeeId == employeeId
&& pa.TenantId == tenantId
&& pa.Project != null
&& pa.Employee != null)
.Select(pa => new
{
ProjectName = pa.Project!.Name,
ProjectShortName = pa.Project.ShortName,
AssignedDate = pa.AllocationDate,
RemovedDate = pa.ReAllocationDate,
Designation = pa.Employee!.JobRole!.Name,
DesignationId = pa.JobRoleId
})
.ToListAsync();
var designationIds = projectAllocation.Select(pa => pa.DesignationId).ToList();
var designations = await _context.JobRoles.Where(jr => designationIds.Contains(jr.Id)).ToListAsync();
var response = projectAllocation.Select(pa =>
{
var designation = designations.FirstOrDefault(jr => jr.Id == pa.DesignationId);
return new ProjectHisteryVM
{
ProjectName = pa.ProjectName,
ProjectShortName = pa.ProjectShortName,
AssignedDate = pa.AssignedDate,
RemovedDate = pa.RemovedDate,
Designation = designation?.Name
};
}).ToList();
// Log successful retrieval including count of records
_logger.LogInfo("Successfully fetched {Count} projects for EmployeeId: {EmployeeId}",
projectAllocation.Count, employeeId);
return ApiResponse<object>.SuccessResponse(
response,
$"{response.Count} project assignments fetched for employee.",
200);
}
catch (Exception ex)
{
// Log the exception with stack trace for debugging
_logger.LogError(ex, "Error occurred while fetching projects for EmployeeId: {EmployeeId}, TenantId: {TenantId}",
employeeId, tenantId);
return ApiResponse<object>.ErrorResponse(
"An error occurred while fetching project assignments.",
500);
}
}
#endregion #endregion
#region =================================================================== Project InfraStructure Get APIs =================================================================== #region =================================================================== Project InfraStructure Get APIs ===================================================================
@ -1025,6 +1092,83 @@ namespace Marco.Pms.Services.Service
} }
} }
/// <summary>
/// Retrieves tasks assigned to a specific employee within a date range for a tenant.
/// </summary>
/// <param name="employeeId">The ID of the employee to filter tasks.</param>
/// <param name="fromDate">The start date to filter task assignments.</param>
/// <param name="toDate">The end date to filter task assignments.</param>
/// <param name="tenantId">The tenant ID to filter tasks.</param>
/// <param name="loggedInEmployee">The employee requesting the data (for authorization/logging).</param>
/// <returns>An ApiResponse containing the task details.</returns>
public async Task<ApiResponse<object>> GetTasksByEmployeeAsync(Guid employeeId, DateTime fromDate, DateTime toDate, Guid tenantId, Employee loggedInEmployee)
{
_logger.LogInfo("Fetching tasks for EmployeeId: {EmployeeId} from {FromDate} to {ToDate} for TenantId: {TenantId}",
employeeId, fromDate, toDate, tenantId);
try
{
// Query TaskMembers with related necessary fields in one projection to minimize DB calls and data size
var taskData = await _context.TaskMembers
.Where(tm => tm.EmployeeId == employeeId &&
tm.TenantId == tenantId &&
tm.TaskAllocation != null &&
tm.TaskAllocation.AssignmentDate.Date >= fromDate.Date &&
tm.TaskAllocation.AssignmentDate.Date <= toDate.Date)
.Select(tm => new
{
AssignmentDate = tm.TaskAllocation!.AssignmentDate,
PlannedTask = tm.TaskAllocation.PlannedTask,
CompletedTask = tm.TaskAllocation.CompletedTask,
ProjectId = tm.TaskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId,
BuildingName = tm.TaskAllocation.WorkItem.WorkArea.Floor.Building!.Name,
FloorName = tm.TaskAllocation.WorkItem.WorkArea.Floor.FloorName,
AreaName = tm.TaskAllocation.WorkItem.WorkArea.AreaName,
ActivityName = tm.TaskAllocation.WorkItem.ActivityMaster!.ActivityName,
ActivityUnit = tm.TaskAllocation.WorkItem.ActivityMaster.UnitOfMeasurement
})
.OrderByDescending(t => t.AssignmentDate)
.ToListAsync();
_logger.LogInfo("Retrieved {TaskCount} tasks for EmployeeId: {EmployeeId}", taskData.Count, employeeId);
// Extract distinct project IDs to fetch project details efficiently
var distinctProjectIds = taskData.Select(t => t.ProjectId).Distinct().ToList();
var projects = await _context.Projects
.Where(p => distinctProjectIds.Contains(p.Id))
.Select(p => new { p.Id, p.Name })
.ToListAsync();
// Prepare the response
var response = taskData.Select(t =>
{
var project = projects.FirstOrDefault(p => p.Id == t.ProjectId);
return new
{
ProjectName = project?.Name ?? "Unknown Project",
t.AssignmentDate,
t.PlannedTask,
t.CompletedTask,
Location = $"{t.BuildingName} > {t.FloorName} > {t.AreaName}",
ActivityName = t.ActivityName,
ActivityUnit = t.ActivityUnit
};
}).ToList();
_logger.LogInfo("Successfully prepared task response for EmployeeId: {EmployeeId}", employeeId);
return ApiResponse<object>.SuccessResponse(response, "Task fetched successfully", 200);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while fetching tasks for EmployeeId: {EmployeeId}", employeeId);
return ApiResponse<object>.ErrorResponse("An error occurred while fetching the tasks.", 500);
}
}
#endregion #endregion
#region =================================================================== Project Infrastructre Manage APIs =================================================================== #region =================================================================== Project Infrastructre Manage APIs ===================================================================

View File

@ -20,8 +20,11 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
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);
Task<ApiResponse<object>> GetProjectByEmployeeBasicAsync(Guid employeeId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetInfraDetailsAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetInfraDetailsAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetWorkItemsAsync(Guid workAreaId, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetWorkItemsAsync(Guid workAreaId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetTasksByEmployeeAsync(Guid employeeId, DateTime fromDate, DateTime toDate, Guid tenantId, Employee loggedInEmployee);
Task<ServiceResponse> ManageProjectInfraAsync(List<InfraDto> infraDtos, Guid tenantId, Employee loggedInEmployee); Task<ServiceResponse> ManageProjectInfraAsync(List<InfraDto> infraDtos, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<List<WorkItemVM>>> CreateProjectTaskAsync(List<WorkItemDto> workItemDtos, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<List<WorkItemVM>>> CreateProjectTaskAsync(List<WorkItemDto> workItemDtos, Guid tenantId, Employee loggedInEmployee);
Task<ServiceResponse> DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee); Task<ServiceResponse> DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee);

View File

@ -49,6 +49,6 @@
"MongoDB": { "MongoDB": {
"SerilogDatabaseUrl": "mongodb://localhost:27017/DotNetLogs", "SerilogDatabaseUrl": "mongodb://localhost:27017/DotNetLogs",
"ConnectionString": "mongodb://localhost:27017/MarcoBMS_Caches?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500", "ConnectionString": "mongodb://localhost:27017/MarcoBMS_Caches?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500",
"ModificationConnectionString": "mongodb://localhost:27017/ModificationLog?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500" "ModificationConnectionString": "mongodb://localhost:27017/ModificationLog?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500&replicaSet=rs01&directConnection=true"
} }
} }