Added an API to get job status list

This commit is contained in:
ashutosh.nehete 2025-11-13 17:23:54 +05:30
parent b789b84be1
commit 611d7753bb
14 changed files with 17921 additions and 10 deletions

View File

@ -214,6 +214,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<TeamRoleMaster> TeamRoleMasters { get; set; }
public DbSet<ServiceProjectTag> ServiceProjectTags { get; set; }
public DbSet<ServiceProjectTagMapping> ServiceProjectTagMappings { get; set; }
public DbSet<ServiceProjectAllocation> ServiceProjectAllocations { get; set; }
#region ======================================================= Job =======================================================
public DbSet<JobTicket> JobTickets { get; set; }
@ -223,6 +224,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<JobEmployeeMapping> JobEmployeeMappings { get; set; }
public DbSet<JobTag> JobTags { get; set; }
public DbSet<JobTagMapping> JobTagMappings { get; set; }
public DbSet<JobAttachment> JobAttachments { get; set; }
#endregion
#endregion
@ -1272,13 +1274,13 @@ namespace Marco.Pms.DataAccess.Data
);
modelBuilder.Entity<JobStatus>().HasData(
new JobStatus { Id = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918"), Name = "New", DisplayName = "New" },
new JobStatus { Id = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), Name = "Assigned", DisplayName = "Assigned" },
new JobStatus { Id = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), Name = "In Progress", DisplayName = "In Progress" },
new JobStatus { Id = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), Name = "Resolved", DisplayName = "Resolved" },
new JobStatus { Id = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), Name = "Done", DisplayName = "Done" },
new JobStatus { Id = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), Name = "Closed", DisplayName = "Closed" },
new JobStatus { Id = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), Name = "On Hold", DisplayName = "On Hold" }
new JobStatus { Id = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918"), Name = "New", DisplayName = "New", Level = 1 },
new JobStatus { Id = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), Name = "Assigned", DisplayName = "Assigned", Level = 2 },
new JobStatus { Id = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), Name = "In Progress", DisplayName = "In Progress", Level = 3 },
new JobStatus { Id = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), Name = "Review", DisplayName = "Review", Level = 4 },
new JobStatus { Id = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), Name = "Done", DisplayName = "Done", Level = 5 },
new JobStatus { Id = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), Name = "Closed", DisplayName = "Closed", Level = 6 },
new JobStatus { Id = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), Name = "On Hold", DisplayName = "On Hold", Level = 7 }
);
modelBuilder.Entity<JobStatusMapping>().HasData(

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,222 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_JobAttcahments_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsActive",
table: "JobComments",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedAt",
table: "JobComments",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "UpdatedById",
table: "JobComments",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.CreateTable(
name: "JobAttachments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
DocumentId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
StatusId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
JobCommentId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobAttachments", x => x.Id);
table.ForeignKey(
name: "FK_JobAttachments_Documents_DocumentId",
column: x => x.DocumentId,
principalTable: "Documents",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobAttachments_JobComments_JobCommentId",
column: x => x.JobCommentId,
principalTable: "JobComments",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobAttachments_JobStatus_StatusId",
column: x => x.StatusId,
principalTable: "JobStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobAttachments_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ServiceProjectAllocations",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
EmployeeId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TeamRoleId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
AssignedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
AssignedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ReAssignedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
ReAssignedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceProjectAllocations", x => x.Id);
table.ForeignKey(
name: "FK_ServiceProjectAllocations_Employees_AssignedById",
column: x => x.AssignedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServiceProjectAllocations_Employees_EmployeeId",
column: x => x.EmployeeId,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServiceProjectAllocations_Employees_ReAssignedById",
column: x => x.ReAssignedById,
principalTable: "Employees",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ServiceProjectAllocations_ServiceProjects_ProjectId",
column: x => x.ProjectId,
principalTable: "ServiceProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServiceProjectAllocations_TeamRoleMasters_TeamRoleId",
column: x => x.TeamRoleId,
principalTable: "TeamRoleMasters",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServiceProjectAllocations_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_JobComments_UpdatedById",
table: "JobComments",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_JobAttachments_DocumentId",
table: "JobAttachments",
column: "DocumentId");
migrationBuilder.CreateIndex(
name: "IX_JobAttachments_JobCommentId",
table: "JobAttachments",
column: "JobCommentId");
migrationBuilder.CreateIndex(
name: "IX_JobAttachments_StatusId",
table: "JobAttachments",
column: "StatusId");
migrationBuilder.CreateIndex(
name: "IX_JobAttachments_TenantId",
table: "JobAttachments",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_AssignedById",
table: "ServiceProjectAllocations",
column: "AssignedById");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_EmployeeId",
table: "ServiceProjectAllocations",
column: "EmployeeId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_ProjectId",
table: "ServiceProjectAllocations",
column: "ProjectId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_ReAssignedById",
table: "ServiceProjectAllocations",
column: "ReAssignedById");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_TeamRoleId",
table: "ServiceProjectAllocations",
column: "TeamRoleId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectAllocations_TenantId",
table: "ServiceProjectAllocations",
column: "TenantId");
migrationBuilder.AddForeignKey(
name: "FK_JobComments_Employees_UpdatedById",
table: "JobComments",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_JobComments_Employees_UpdatedById",
table: "JobComments");
migrationBuilder.DropTable(
name: "JobAttachments");
migrationBuilder.DropTable(
name: "ServiceProjectAllocations");
migrationBuilder.DropIndex(
name: "IX_JobComments_UpdatedById",
table: "JobComments");
migrationBuilder.DropColumn(
name: "IsActive",
table: "JobComments");
migrationBuilder.DropColumn(
name: "UpdatedAt",
table: "JobComments");
migrationBuilder.DropColumn(
name: "UpdatedById",
table: "JobComments");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,85 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Level_In_JobStatus_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Level",
table: "JobStatus",
type: "int",
nullable: false,
defaultValue: 1);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"),
column: "Level",
value: 1);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
column: "Level",
value: 6);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
column: "Level",
value: 3);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
column: "Level",
value: 7);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
columns: new[] { "DisplayName", "Level", "Name" },
values: new object[] { "Review", 4, "Review" });
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
column: "Level",
value: 2);
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
column: "Level",
value: 5);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Level",
table: "JobStatus");
migrationBuilder.UpdateData(
table: "JobStatus",
keyColumn: "Id",
keyValue: new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
columns: new[] { "DisplayName", "Name" },
values: new object[] { "Resolved", "Resolved" });
}
}
}

View File

@ -5012,6 +5012,37 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("JobRoles");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobAttachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("DocumentId")
.HasColumnType("char(36)");
b.Property<Guid?>("JobCommentId")
.HasColumnType("char(36)");
b.Property<Guid>("StatusId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("DocumentId");
b.HasIndex("JobCommentId");
b.HasIndex("StatusId");
b.HasIndex("TenantId");
b.ToTable("JobAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobComment", b =>
{
b.Property<Guid>("Id")
@ -5028,12 +5059,21 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<Guid>("JobTicketId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("CreatedById");
@ -5042,6 +5082,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("JobComments");
});
@ -5081,6 +5123,9 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Level")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
@ -5094,42 +5139,49 @@ namespace Marco.Pms.DataAccess.Migrations
{
Id = new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"),
DisplayName = "New",
Level = 1,
Name = "New"
},
new
{
Id = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
DisplayName = "Assigned",
Level = 2,
Name = "Assigned"
},
new
{
Id = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
DisplayName = "In Progress",
Level = 3,
Name = "In Progress"
},
new
{
Id = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
DisplayName = "Resolved",
Name = "Resolved"
DisplayName = "Review",
Level = 4,
Name = "Review"
},
new
{
Id = new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
DisplayName = "Done",
Level = 5,
Name = "Done"
},
new
{
Id = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
DisplayName = "Closed",
Level = 6,
Name = "Closed"
},
new
{
Id = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
DisplayName = "On Hold",
Level = 7,
Name = "On Hold"
});
});
@ -5449,6 +5501,56 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("ServiceProjects");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectAllocation", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("AssignedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("AssignedById")
.HasColumnType("char(36)");
b.Property<Guid>("EmployeeId")
.HasColumnType("char(36)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<DateTime?>("ReAssignedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("ReAssignedById")
.HasColumnType("char(36)");
b.Property<Guid>("TeamRoleId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("AssignedById");
b.HasIndex("EmployeeId");
b.HasIndex("ProjectId");
b.HasIndex("ReAssignedById");
b.HasIndex("TeamRoleId");
b.HasIndex("TenantId");
b.ToTable("ServiceProjectAllocations");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectServiceMapping", b =>
{
b.Property<Guid>("Id")
@ -7967,6 +8069,39 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobAttachment", b =>
{
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
.WithMany()
.HasForeignKey("DocumentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.JobComment", "JobComment")
.WithMany()
.HasForeignKey("JobCommentId");
b.HasOne("Marco.Pms.Model.ServiceProject.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Document");
b.Navigation("JobComment");
b.Navigation("Status");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobComment", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
@ -7987,11 +8122,17 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");
b.Navigation("JobTicket");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobEmployeeMapping", b =>
@ -8174,6 +8315,55 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectAllocation", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "AssignedBy")
.WithMany()
.HasForeignKey("AssignedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "Employee")
.WithMany()
.HasForeignKey("EmployeeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "ReAssignedBy")
.WithMany()
.HasForeignKey("ReAssignedById");
b.HasOne("Marco.Pms.Model.ServiceProject.TeamRoleMaster", "TeamRole")
.WithMany()
.HasForeignKey("TeamRoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AssignedBy");
b.Navigation("Employee");
b.Navigation("Project");
b.Navigation("ReAssignedBy");
b.Navigation("TeamRole");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectServiceMapping", b =>
{
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProject", "Project")

View File

@ -0,0 +1,27 @@
using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.ServiceProject
{
public class JobAttachment : TenantRelation
{
public Guid Id { get; set; }
public Guid DocumentId { get; set; }
[ValidateNever]
[ForeignKey("DocumentId")]
public Document? Document { get; set; }
public Guid StatusId { get; set; }
[ValidateNever]
[ForeignKey("StatusId")]
public JobStatus? Status { get; set; }
public Guid? JobCommentId { get; set; }
[ValidateNever]
[ForeignKey("JobCommentId")]
public JobComment? JobComment { get; set; }
}
}

View File

@ -14,11 +14,18 @@ namespace Marco.Pms.Model.ServiceProject
[ForeignKey("JobTicketId")]
public JobTicket? JobTicket { get; set; }
public string Comment { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]
public Employee? UpdatedBy { get; set; }
}
}

View File

@ -5,5 +5,6 @@
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public int Level { get; set; }
}
}

View File

@ -0,0 +1,40 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.ServiceProject
{
public class ServiceProjectAllocation : TenantRelation
{
public Guid Id { get; set; }
public Guid ProjectId { get; set; }
[ValidateNever]
[ForeignKey("ProjectId")]
public ServiceProject? Project { get; set; }
public Guid EmployeeId { get; set; }
[ValidateNever]
[ForeignKey("EmployeeId")]
public Employee? Employee { get; set; }
public Guid TeamRoleId { get; set; }
[ValidateNever]
[ForeignKey("TeamRoleId")]
public TeamRoleMaster? TeamRole { get; set; }
public bool IsActive { get; set; } = true;
public DateTime AssignedAt { get; set; }
public Guid AssignedById { get; set; }
[ValidateNever]
[ForeignKey("AssignedById")]
public Employee? AssignedBy { get; set; }
public DateTime? ReAssignedAt { get; set; }
public Guid? ReAssignedById { get; set; }
[ValidateNever]
[ForeignKey("ReAssignedById")]
public Employee? ReAssignedBy { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.DocumentManager;
namespace Marco.Pms.Model.ViewModels.ServiceProject
{
@ -7,7 +8,11 @@ namespace Marco.Pms.Model.ViewModels.ServiceProject
public Guid Id { get; set; }
public BasicJobTicketVM? JobTicket { get; set; }
public string? Comment { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }
public List<DocumentVM>? Documents { get; set; }
}
}

View File

@ -38,6 +38,16 @@ namespace Marco.Pms.Services.Controllers
tenantId = userHelper.GetTenantId();
}
#region =================================================================== Job Status APIs ===================================================================
[HttpGet("job-status/list")]
public async Task<IActionResult> GetJobStatusAsync([FromQuery] Guid? statusId, [FromQuery] Guid? projectId)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.GetJobStatusAsync(statusId, projectId, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
#endregion
#region =================================================================== Recurring Payment Status APIs ===================================================================
[HttpGet("recurring-status/list")]

View File

@ -14,6 +14,7 @@ using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Expenses.Masters;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels.Utility;
using Marco.Pms.Model.ServiceProject;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Collection;
@ -54,6 +55,84 @@ namespace Marco.Pms.Services.Service
_updateLogHelper = updateLogHelper ?? throw new ArgumentNullException(nameof(updateLogHelper));
}
#region =================================================================== Job Status APIs ===================================================================
/// <summary>
/// Retrieves job status records filtered by optional statusId and projectId within tenant context.
/// If both statusId and projectId are specified, returns next possible statuses based on team role mappings.
/// Otherwise, returns all global statuses ordered by name.
/// </summary>
/// <param name="statusId">Optional current job status ID to find next possible statuses.</param>
/// <param name="projectId">Optional project ID for filtering based on employee's allocation.</param>
/// <param name="loggedInEmployee">Employee requesting the statuses.</param>
/// <param name="tenantId">Tenant identifier for multi-tenant scope.</param>
/// <returns>ApiResponse containing list of job statuses or error details.</returns>
public async Task<ApiResponse<object>> GetJobStatusAsync(
Guid? statusId,
Guid? projectId,
Employee loggedInEmployee,
Guid tenantId)
{
_logger.LogDebug("GetJobStatusAsync called by employee {EmployeeId} in tenant {TenantId}", loggedInEmployee.Id, tenantId);
if (tenantId == Guid.Empty)
{
_logger.LogWarning("TenantId is missing in GetJobStatusAsync request by employee {EmployeeId}", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Access Denied", "Invalid tenant context.", 403);
}
try
{
List<JobStatus> statuses;
if (statusId.HasValue && projectId.HasValue)
{
var statusMappingQuery = _context.JobStatusMappings
.Include(jsm => jsm.NextStatus)
.Where(jsm => jsm.StatusId == statusId && jsm.NextStatus != null && jsm.TenantId == tenantId);
// Find employee's latest project allocation to determine team role
var projectAllocation = await _context.ServiceProjectAllocations
.Where(spa => spa.ProjectId == projectId && spa.EmployeeId == loggedInEmployee.Id && spa.TenantId == tenantId && spa.IsActive)
.OrderByDescending(spa => spa.AssignedAt)
.FirstOrDefaultAsync();
var teamRoleId = projectAllocation?.TeamRoleId;
// Check if mappings exist for the employee's team role; fallback to null team role mappings
var hasTeamStatusMapping = projectAllocation != null &&
await statusMappingQuery.AnyAsync(jsm => jsm.TeamRoleId == teamRoleId);
if (hasTeamStatusMapping)
{
statusMappingQuery = statusMappingQuery.Where(jsm => jsm.TeamRoleId == teamRoleId);
}
else
{
statusMappingQuery = statusMappingQuery.Where(jsm => jsm.TeamRoleId == null);
}
statuses = await statusMappingQuery.Select(jsm => jsm.NextStatus!).OrderBy(js => js.Level).ToListAsync();
}
else
{
// No specific filters - return all job statuses ordered by name
statuses = await _context.JobStatus.OrderBy(js => js.Level).ToListAsync();
}
_logger.LogInfo("Fetched {Count} job status records for tenant {TenantId}", statuses.Count, tenantId);
return ApiResponse<object>.SuccessResponse(statuses, $"{statuses.Count} job status record(s) fetched successfully", 200);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching job status for employee {EmployeeId} in tenant {TenantId}", loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while fetching job statuses. Please try again later.", 500);
}
}
#endregion
#region =================================================================== Recurring Payment Status APIs ===================================================================
public async Task<ApiResponse<object>> GetRecurringPaymentStatusAsync(Employee loggedInEmployee, Guid tenantId)

View File

@ -10,9 +10,12 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
{
public interface IMasterService
{
#region =================================================================== Job Status APIs ===================================================================
Task<ApiResponse<object>> GetJobStatusAsync(Guid? statusId, Guid? projectId, Employee loggedInEmployee, Guid tenantId);
#endregion
#region =================================================================== Recurring Payment Status APIs ===================================================================
Task<ApiResponse<object>> GetRecurringPaymentStatusAsync(Employee loggedInEmployee, Guid tenantId);
#endregion
#region =================================================================== Currency APIs ===================================================================