Added the serviceId in project allocation

This commit is contained in:
ashutosh.nehete 2025-09-20 17:05:30 +05:30
parent 9b59a4d6b6
commit 7d85cb5f4c
13 changed files with 6246 additions and 17 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,68 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Service_FK_In_ProjectAllocation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "ServiceId",
table: "ProjectAllocations",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.AddColumn<string>(
name: "logoImage",
table: "Organizations",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.UpdateData(
table: "Organizations",
keyColumn: "Id",
keyValue: new Guid("4e3a6d31-c640-40f7-8d67-6c109fcdb9ea"),
column: "logoImage",
value: null);
migrationBuilder.CreateIndex(
name: "IX_ProjectAllocations_ServiceId",
table: "ProjectAllocations",
column: "ServiceId");
migrationBuilder.AddForeignKey(
name: "FK_ProjectAllocations_ServiceMasters_ServiceId",
table: "ProjectAllocations",
column: "ServiceId",
principalTable: "ServiceMasters",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ProjectAllocations_ServiceMasters_ServiceId",
table: "ProjectAllocations");
migrationBuilder.DropIndex(
name: "IX_ProjectAllocations_ServiceId",
table: "ProjectAllocations");
migrationBuilder.DropColumn(
name: "ServiceId",
table: "ProjectAllocations");
migrationBuilder.DropColumn(
name: "logoImage",
table: "Organizations");
}
}
}

View File

@ -3643,6 +3643,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.Property<string>("logoImage")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Organizations");
@ -3918,6 +3921,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<DateTime?>("ReAllocationDate")
.HasColumnType("datetime(6)");
b.Property<Guid?>("ServiceId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
@ -3927,6 +3933,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("ProjectId");
b.HasIndex("ServiceId");
b.HasIndex("TenantId");
b.ToTable("ProjectAllocations");
@ -5858,6 +5866,10 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Master.ServiceMaster", "Service")
.WithMany()
.HasForeignKey("ServiceId");
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
@ -5868,6 +5880,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Project");
b.Navigation("Service");
b.Navigation("Tenant");
});

View File

@ -7,6 +7,7 @@
public required string ContactPerson { get; set; }
public required string Address { get; set; }
public required string ContactNumber { get; set; }
public string? logoImage { get; set; }
public required List<Guid> ServiceIds { get; set; }
}
}

View File

@ -2,9 +2,10 @@
{
public class ProjectAllocationDot
{
public Guid EmpID { get; set; }
public Guid EmployeeId { get; set; }
public Guid JobRoleId { get; set; }
public Guid ProjectId { get; set; }
public Guid ServiceId { get; set; }
public bool Status { get; set; }
}
@ -13,6 +14,7 @@
{
public Guid ProjectId { get; set; }
public Guid JobRoleId { get; set; }
public Guid ServiceId { get; set; }
public bool Status { get; set; }
}
}

View File

@ -57,7 +57,7 @@ namespace Marco.Pms.Model.Mapper
return new ProjectAllocation
{
AllocationDate = DateTime.Now,
EmployeeId = model.EmpID,
EmployeeId = model.EmployeeId,
JobRoleId = model.JobRoleId,
TenantId = TenantId,
ProjectId = model.ProjectId
@ -65,7 +65,7 @@ namespace Marco.Pms.Model.Mapper
}
public static ProjectAllocation ToProjectAllocationFromProjectsAllocationDto(this ProjectsAllocationDto model, Guid TenantId,Guid employeeId)
public static ProjectAllocation ToProjectAllocationFromProjectsAllocationDto(this ProjectsAllocationDto model, Guid TenantId, Guid employeeId)
{
return new ProjectAllocation
{

View File

@ -9,6 +9,7 @@
public string Address { get; set; } = string.Empty;
public string ContactNumber { get; set; } = string.Empty;
public double SPRID { get; set; }
public string? logoImage { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public Guid? CreatedById { get; set; }

View File

@ -1,7 +1,8 @@
using System.ComponentModel.DataAnnotations.Schema;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Projects
{
@ -21,9 +22,15 @@ namespace Marco.Pms.Model.Projects
public bool IsActive { get; set; } = true;
public Guid ProjectId { get; set; }
[ForeignKey("ProjectId")]
[ValidateNever]
public Project? Project { get; set; }
public Guid? ServiceId { get; set; }
[ForeignKey("ServiceId")]
[ValidateNever]
public ServiceMaster? Service { get; set; }
public DateTime AllocationDate { get; set; }

View File

@ -11,6 +11,7 @@ namespace Marco.Pms.Model.ViewModels.Organization
public string? Address { get; set; }
public string? ContactNumber { get; set; }
public double SPRID { get; set; }
public string? logoImage { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }

View File

@ -219,7 +219,7 @@ namespace MarcoBMS.Services.Controllers
}
[HttpGet("allocation/{projectId}")]
public async Task<IActionResult> GetProjectAllocation(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive = false)
public async Task<IActionResult> GetProjectAllocation(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] Guid? serviceId, [FromQuery] bool includeInactive = false)
{
// --- Step 1: Input Validation ---
if (!ModelState.IsValid)
@ -231,7 +231,7 @@ namespace MarcoBMS.Services.Controllers
// --- Step 2: Prepare data without I/O ---
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetProjectAllocationAsync(projectId, organizationId, includeInactive, tenantId, loggedInEmployee);
var response = await _projectServices.GetProjectAllocationAsync(projectId, organizationId, serviceId, includeInactive, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}

View File

@ -161,8 +161,7 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<ProjectAllocationDot, ProjectAllocation>()
.ForMember(
dest => dest.EmployeeId,
// Explicitly and safely convert string ProjectStatusId to Guid ProjectStatusId
opt => opt.MapFrom(src => src.EmpID));
opt => opt.MapFrom(src => src.EmployeeId));
CreateMap<ProjectsAllocationDto, ProjectAllocation>();
CreateMap<ProjectAllocation, ProjectAllocationVM>();

View File

@ -562,7 +562,7 @@ namespace Marco.Pms.Services.Service
/// <param name="tenantId">The ID of the current tenant.</param>
/// <param name="loggedInEmployee">The current authenticated employee for permission checks.</param>
/// <returns>An ApiResponse containing allocation details or an appropriate error.</returns>
public async Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
public async Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, Guid? serviceId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
{
// --- Step 1: Input Validation ---
if (projectId == Guid.Empty)
@ -595,7 +595,8 @@ namespace Marco.Pms.Services.Service
.ThenInclude(e => e!.JobRole)
.Include(pa => pa.Employee)
.ThenInclude(e => e!.Organization)
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId);
.Include(pa => pa.Service)
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId && pa.Service != null);
// Conditionally apply the filter for active allocations.
if (!includeInactive)
@ -628,6 +629,8 @@ namespace Marco.Pms.Services.Service
OrganizationName = pa.Employee.Organization!.Name,
ServiceName = pa.Service!.Name,
// 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
})
@ -679,8 +682,8 @@ namespace Marco.Pms.Services.Service
}
// --- Step 2: Fetch all relevant existing data in ONE database call ---
var employeeProjectPairs = allocationsDto.Select(a => new { a.EmpID, a.ProjectId }).ToList();
List<Guid> employeeIds = allocationsDto.Select(a => a.EmpID).Distinct().ToList();
var employeeProjectPairs = allocationsDto.Select(a => new { a.EmployeeId, a.ProjectId }).ToList();
List<Guid> employeeIds = allocationsDto.Select(a => a.EmployeeId).Distinct().ToList();
// Fetch all currently active allocations for the employees and projects in this batch.
// We use a dictionary for fast O(1) lookups inside the loop.
@ -695,7 +698,7 @@ namespace Marco.Pms.Services.Service
// --- Step 3: Process logic IN MEMORY ---
foreach (var dto in allocationsDto)
{
var key = (dto.EmpID, dto.ProjectId);
var key = (dto.EmployeeId, dto.ProjectId);
existingAllocations.TryGetValue(key, out var existingAllocation);
if (dto.Status == false) // User wants to DEACTIVATE the allocation
@ -726,8 +729,8 @@ namespace Marco.Pms.Services.Service
}
try
{
await _cache.ClearAllProjectIds(dto.EmpID);
_logger.LogInfo("Successfully completed cache invalidation for employee {EmployeeId}.", dto.EmpID);
await _cache.ClearAllProjectIds(dto.EmployeeId);
_logger.LogInfo("Successfully completed cache invalidation for employee {EmployeeId}.", dto.EmployeeId);
}
catch (Exception ex)
{

View File

@ -18,7 +18,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
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>> GetEmployeeByProjectIdAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetProjectAllocationAsync(Guid projectId, Guid? organizationId, Guid? serviceId, bool includeInactive, 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<List<ProjectAllocationVM>>> AssigneProjectsToEmployeeAsync(List<ProjectsAllocationDto> projectAllocationDtos, Guid employeeId, Guid tenantId, Employee loggedInEmployee);