Added the Create Job ticket API

This commit is contained in:
ashutosh.nehete 2025-11-13 09:58:42 +05:30
parent 9af4c2279a
commit 5a97cd472c
24 changed files with 10112 additions and 106 deletions

View File

@ -209,12 +209,20 @@ namespace Marco.Pms.DataAccess.Data
#region ======================================================= Service Project =======================================================
public DbSet<ServiceProject> ServiceProjects { get; set; }
public DbSet<ServiceProjectServiceMapping> ServiceProjectServiceMapping { get; set; }
public DbSet<TeamRoleMaster> TeamRoleMasters { get; set; }
public DbSet<ServiceProjectTag> ServiceProjectTags { get; set; }
public DbSet<ServiceProjectTagMapping> ServiceProjectTagMappings { get; set; }
//#region ======================================================= Job =======================================================
//public DbSet<JobTicket> JobTickets { get; set; }
//public DbSet<JobStatus> JobStatus { get; set; }
#region ======================================================= Job =======================================================
public DbSet<JobTicket> JobTickets { get; set; }
public DbSet<JobStatus> JobStatus { get; set; }
public DbSet<JobStatusMapping> JobStatusMappings { get; set; }
public DbSet<JobUpdateLog> JobUpdateLogs { get; set; }
public DbSet<JobEmployeeMapping> JobEmployeeMappings { get; set; }
public DbSet<JobTag> JobTags { get; set; }
public DbSet<JobTagMapping> JobTagMappings { get; set; }
//#endregion
#endregion
#endregion
@ -542,6 +550,7 @@ namespace Marco.Pms.DataAccess.Data
.HasOne<Tenant>() // No navigation property in ApplicationRole
.WithMany()
.HasForeignKey(ar => ar.TenantId);
// Configure the relationship between ApplicationRole and FeaturePermission via a join table
modelBuilder.Entity<RolePermissionMappings>(entity =>
{
@ -1248,84 +1257,68 @@ namespace Marco.Pms.DataAccess.Data
);
modelBuilder.Entity<RecurringPaymentStatus>().HasData(
new RecurringPaymentStatus
{
Id = Guid.Parse("da462422-13b2-45cc-a175-910a225f6fc8"),
Name = "Active"
},
new RecurringPaymentStatus
{
Id = Guid.Parse("3ec864d2-8bf5-42fb-ba70-5090301dd816"),
Name = "De-Activated"
},
new RecurringPaymentStatus
{
Id = Guid.Parse("306856fb-5655-42eb-bf8b-808bb5e84725"),
Name = "Completed"
},
new RecurringPaymentStatus
{
Id = Guid.Parse("8bfc9346-e092-4a80-acbf-515ae1ef6868"),
Name = "Paused"
}
);
new RecurringPaymentStatus { Id = Guid.Parse("da462422-13b2-45cc-a175-910a225f6fc8"), Name = "Active" },
new RecurringPaymentStatus { Id = Guid.Parse("3ec864d2-8bf5-42fb-ba70-5090301dd816"), Name = "De-Activated" },
new RecurringPaymentStatus { Id = Guid.Parse("306856fb-5655-42eb-bf8b-808bb5e84725"), Name = "Completed" },
new RecurringPaymentStatus { Id = Guid.Parse("8bfc9346-e092-4a80-acbf-515ae1ef6868"), Name = "Paused" }
);
//modelBuilder.Entity<JobStatus>().HasData(
// new JobStatus
// {
// Id = Guid.Parse(""),
// Name = "",
// DisplayName = ""
// },
// new JobStatus
// {
// Id = Guid.Parse(""),
// Name = "",
// DisplayName = ""
// },
// new JobStatus
// {
// Id = Guid.Parse(""),
// Name = "",
// DisplayName = ""
// });
modelBuilder.Entity<TeamRoleMaster>().HasData(
new TeamRoleMaster { Id = Guid.Parse("8cfbf16f-7d3b-4c29-af5b-18935f20aa7b"), Name = "Support", Description = "A Support role involves assisting users or customers by resolving technical or service-related issues, answering inquiries, and ensuring a positive experience with products or services." },
new TeamRoleMaster { Id = Guid.Parse("145d7222-408b-4733-8a17-f417e070b8b8"), Name = "Service Engineer", Description = "A Service Engineer installs, maintains, repairs, and troubleshoots equipment to ensure optimal operation and customer satisfaction." },
new TeamRoleMaster { Id = Guid.Parse("03bf5853-5e0b-4eb8-9f37-33bd999a05b3"), Name = "Manager", Description = "A Manager oversees and coordinates a team or department to achieve organizational goals through planning, decision-making, and leadership." }
);
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" }
);
modelBuilder.Entity<JobStatusMapping>().HasData(
// New to Assigned
new JobStatusMapping { Id = Guid.Parse("024e1810-6a57-4a0d-8d2e-be88da79fcd4"), StatusId = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918"), NextStatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Assinged to In Progress
new JobStatusMapping { Id = Guid.Parse("cb0db140-87fa-4a6f-812d-2834bd0f53a9"), StatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), NextStatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Assinged to On Hold
new JobStatusMapping { Id = Guid.Parse("42f24930-387e-4f51-9c2d-3e29ffaaf02a"), StatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), NextStatusId = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Assigned to closed
new JobStatusMapping { Id = Guid.Parse("16c83c23-09be-40fd-9d05-f44795d8dee8"), StatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), NextStatusId = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// In Progress to Resolved
new JobStatusMapping { Id = Guid.Parse("8c4ecdae-7435-4475-8389-15bc453561a1"), StatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), NextStatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// In Progress to Assigned
new JobStatusMapping { Id = Guid.Parse("a44b0a66-ee33-47e7-a01f-6b8d9b621543"), StatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), NextStatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// In Progress to Closed
new JobStatusMapping { Id = Guid.Parse("7165ecee-10e3-4fc0-85d2-6d93d5b11776"), StatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), NextStatusId = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// In Progress to On Hold
new JobStatusMapping { Id = Guid.Parse("87891499-e45d-406b-bf22-722db1beedc9"), StatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), NextStatusId = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Resolved to Done
new JobStatusMapping { Id = Guid.Parse("dc986ec4-858e-4c98-b330-4a5c98c91f07"), StatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), NextStatusId = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Resolved to Assigned
new JobStatusMapping { Id = Guid.Parse("ca8b4358-3122-4aaa-bcf8-0b66e4ab313a"), StatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), NextStatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Resolved to In Progress
new JobStatusMapping { Id = Guid.Parse("5602d32c-290e-48a3-83dd-91af6d12ed46"), StatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), NextStatusId = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Resolved to Closed
new JobStatusMapping { Id = Guid.Parse("9c2918be-b3c1-46fb-a03b-14dd613e1021"), StatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), NextStatusId = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Resolved to On Hold
new JobStatusMapping { Id = Guid.Parse("ab974bdb-2d8f-4ddc-9b71-bd6d198bae75"), StatusId = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), NextStatusId = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// On Hold to Assigned
new JobStatusMapping { Id = Guid.Parse("2787c903-1b39-4e7d-a0f2-3bb2309bb341"), StatusId = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), NextStatusId = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") },
// Done to Close
new JobStatusMapping { Id = Guid.Parse("76bc5551-8f80-469d-ba23-95d7e746c9a9"), StatusId = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), NextStatusId = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") }
);
modelBuilder.Entity<Module>().HasData(
new Module
{
Id = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Project",
Description = "Project Module",
Key = "b04da7e9-0406-409c-ac7f-b97256e6ea02"
},
new Module
{
Id = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"),
Name = "Employee",
Description = "Employee Module",
Key = "0971c7fb-6ce1-458a-ae3f-8d3205893637"
},
new Module
{
Id = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"),
Name = "Masters",
Description = "Masters Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
},
new Module
{
Id = new Guid("f482a079-4dec-4f2d-9867-6baf2a4f23d9"),
Name = "Tenant",
Description = "Tenant Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
},
new Module
{
Id = new Guid("0a79687a-86d7-430d-a2d7-8b8603cc76a1"),
Name = "Finance",
Description = "Finance Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
});
new Module { Id = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), Name = "Project", Description = "Project Module", Key = "b04da7e9-0406-409c-ac7f-b97256e6ea02" },
new Module { Id = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"), Name = "Employee", Description = "Employee Module", Key = "0971c7fb-6ce1-458a-ae3f-8d3205893637" },
new Module { Id = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"), Name = "Masters", Description = "Masters Module", Key = "504ec132-e6a9-422f-8f85-050602cfce05" },
new Module { Id = new Guid("f482a079-4dec-4f2d-9867-6baf2a4f23d9"), Name = "Tenant", Description = "Tenant Module", Key = "504ec132-e6a9-422f-8f85-050602cfce05" },
new Module { Id = new Guid("0a79687a-86d7-430d-a2d7-8b8603cc76a1"), Name = "Finance", Description = "Finance Module", Key = "504ec132-e6a9-422f-8f85-050602cfce05" }
);
modelBuilder.Entity<Feature>().HasData(
// Project Module

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,538 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_JobTicket_Related_Tables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "JobStatus",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DisplayName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_JobStatus", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobTags",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobTags", x => x.Id);
table.ForeignKey(
name: "FK_JobTags_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ServiceProjectTags",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceProjectTags", x => x.Id);
table.ForeignKey(
name: "FK_ServiceProjectTags_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "TeamRoleMasters",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_TeamRoleMasters", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobTickets",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Title = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
AssigneeId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
StatusId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
StartDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
DueDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
UpdatedById = 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_JobTickets", x => x.Id);
table.ForeignKey(
name: "FK_JobTickets_Employees_AssigneeId",
column: x => x.AssigneeId,
principalTable: "Employees",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobTickets_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobTickets_Employees_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Employees",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobTickets_JobStatus_StatusId",
column: x => x.StatusId,
principalTable: "JobStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobTickets_ServiceProjects_ProjectId",
column: x => x.ProjectId,
principalTable: "ServiceProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobTickets_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ServiceProjectTagMappings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ServiceProjectTagId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ServiceProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceProjectTagMappings", x => x.Id);
table.ForeignKey(
name: "FK_ServiceProjectTagMappings_ServiceProjectTags_ServiceProjectT~",
column: x => x.ServiceProjectTagId,
principalTable: "ServiceProjectTags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServiceProjectTagMappings_ServiceProjects_ServiceProjectId",
column: x => x.ServiceProjectId,
principalTable: "ServiceProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobStatusMappings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TeamRoleId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
StatusId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
NextStatusId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobStatusMappings", x => x.Id);
table.ForeignKey(
name: "FK_JobStatusMappings_JobStatus_NextStatusId",
column: x => x.NextStatusId,
principalTable: "JobStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobStatusMappings_JobStatus_StatusId",
column: x => x.StatusId,
principalTable: "JobStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobStatusMappings_TeamRoleMasters_TeamRoleId",
column: x => x.TeamRoleId,
principalTable: "TeamRoleMasters",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobStatusMappings_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobEmployeeMappings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
AssigneeId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
JobTicketId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobEmployeeMappings", x => x.Id);
table.ForeignKey(
name: "FK_JobEmployeeMappings_Employees_AssigneeId",
column: x => x.AssigneeId,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobEmployeeMappings_JobTickets_JobTicketId",
column: x => x.JobTicketId,
principalTable: "JobTickets",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobEmployeeMappings_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobTagMappings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
JobTagId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
JobTicketId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobTagMappings", x => x.Id);
table.ForeignKey(
name: "FK_JobTagMappings_JobTags_JobTagId",
column: x => x.JobTagId,
principalTable: "JobTags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobTagMappings_JobTickets_JobTicketId",
column: x => x.JobTicketId,
principalTable: "JobTickets",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobTagMappings_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "JobUpdateLogs",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
JobTicketId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
StatusId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
NextStatusId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
Comment = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_JobUpdateLogs", x => x.Id);
table.ForeignKey(
name: "FK_JobUpdateLogs_Employees_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobUpdateLogs_JobStatus_NextStatusId",
column: x => x.NextStatusId,
principalTable: "JobStatus",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobUpdateLogs_JobStatus_StatusId",
column: x => x.StatusId,
principalTable: "JobStatus",
principalColumn: "Id");
table.ForeignKey(
name: "FK_JobUpdateLogs_JobTickets_JobTicketId",
column: x => x.JobTicketId,
principalTable: "JobTickets",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_JobUpdateLogs_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.InsertData(
table: "JobStatus",
columns: new[] { "Id", "DisplayName", "Name" },
values: new object[,]
{
{ new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"), "New", "New" },
{ new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), "Closed", "Closed" },
{ new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), "In Progress", "In Progress" },
{ new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), "On Hold", "On Hold" },
{ new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), "Resolved", "Resolved" },
{ new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), "Assigned", "Assigned" },
{ new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), "Done", "Done" }
});
migrationBuilder.InsertData(
table: "TeamRoleMasters",
columns: new[] { "Id", "Description", "Name" },
values: new object[,]
{
{ new Guid("03bf5853-5e0b-4eb8-9f37-33bd999a05b3"), "A Manager oversees and coordinates a team or department to achieve organizational goals through planning, decision-making, and leadership.", "Manager" },
{ new Guid("145d7222-408b-4733-8a17-f417e070b8b8"), "A Service Engineer installs, maintains, repairs, and troubleshoots equipment to ensure optimal operation and customer satisfaction.", "Service Engineer" },
{ new Guid("8cfbf16f-7d3b-4c29-af5b-18935f20aa7b"), "A Support role involves assisting users or customers by resolving technical or service-related issues, answering inquiries, and ensuring a positive experience with products or services.", "Support" }
});
migrationBuilder.InsertData(
table: "JobStatusMappings",
columns: new[] { "Id", "NextStatusId", "StatusId", "TeamRoleId", "TenantId" },
values: new object[,]
{
{ new Guid("024e1810-6a57-4a0d-8d2e-be88da79fcd4"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("16c83c23-09be-40fd-9d05-f44795d8dee8"), new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("2787c903-1b39-4e7d-a0f2-3bb2309bb341"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("42f24930-387e-4f51-9c2d-3e29ffaaf02a"), new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("5602d32c-290e-48a3-83dd-91af6d12ed46"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("7165ecee-10e3-4fc0-85d2-6d93d5b11776"), new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("76bc5551-8f80-469d-ba23-95d7e746c9a9"), new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("87891499-e45d-406b-bf22-722db1beedc9"), new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("8c4ecdae-7435-4475-8389-15bc453561a1"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("9c2918be-b3c1-46fb-a03b-14dd613e1021"), new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("a44b0a66-ee33-47e7-a01f-6b8d9b621543"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("ab974bdb-2d8f-4ddc-9b71-bd6d198bae75"), new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("ca8b4358-3122-4aaa-bcf8-0b66e4ab313a"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("cb0db140-87fa-4a6f-812d-2834bd0f53a9"), new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"), new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("dc986ec4-858e-4c98-b330-4a5c98c91f07"), new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"), null, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") }
});
migrationBuilder.CreateIndex(
name: "IX_JobEmployeeMappings_AssigneeId",
table: "JobEmployeeMappings",
column: "AssigneeId");
migrationBuilder.CreateIndex(
name: "IX_JobEmployeeMappings_JobTicketId",
table: "JobEmployeeMappings",
column: "JobTicketId");
migrationBuilder.CreateIndex(
name: "IX_JobEmployeeMappings_TenantId",
table: "JobEmployeeMappings",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobStatusMappings_NextStatusId",
table: "JobStatusMappings",
column: "NextStatusId");
migrationBuilder.CreateIndex(
name: "IX_JobStatusMappings_StatusId",
table: "JobStatusMappings",
column: "StatusId");
migrationBuilder.CreateIndex(
name: "IX_JobStatusMappings_TeamRoleId",
table: "JobStatusMappings",
column: "TeamRoleId");
migrationBuilder.CreateIndex(
name: "IX_JobStatusMappings_TenantId",
table: "JobStatusMappings",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobTagMappings_JobTagId",
table: "JobTagMappings",
column: "JobTagId");
migrationBuilder.CreateIndex(
name: "IX_JobTagMappings_JobTicketId",
table: "JobTagMappings",
column: "JobTicketId");
migrationBuilder.CreateIndex(
name: "IX_JobTagMappings_TenantId",
table: "JobTagMappings",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobTags_TenantId",
table: "JobTags",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_AssigneeId",
table: "JobTickets",
column: "AssigneeId");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_CreatedById",
table: "JobTickets",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_ProjectId",
table: "JobTickets",
column: "ProjectId");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_StatusId",
table: "JobTickets",
column: "StatusId");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_TenantId",
table: "JobTickets",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobTickets_UpdatedById",
table: "JobTickets",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_JobUpdateLogs_JobTicketId",
table: "JobUpdateLogs",
column: "JobTicketId");
migrationBuilder.CreateIndex(
name: "IX_JobUpdateLogs_NextStatusId",
table: "JobUpdateLogs",
column: "NextStatusId");
migrationBuilder.CreateIndex(
name: "IX_JobUpdateLogs_StatusId",
table: "JobUpdateLogs",
column: "StatusId");
migrationBuilder.CreateIndex(
name: "IX_JobUpdateLogs_TenantId",
table: "JobUpdateLogs",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_JobUpdateLogs_UpdatedById",
table: "JobUpdateLogs",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectTagMappings_ServiceProjectId",
table: "ServiceProjectTagMappings",
column: "ServiceProjectId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectTagMappings_ServiceProjectTagId",
table: "ServiceProjectTagMappings",
column: "ServiceProjectTagId");
migrationBuilder.CreateIndex(
name: "IX_ServiceProjectTags_TenantId",
table: "ServiceProjectTags",
column: "TenantId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "JobEmployeeMappings");
migrationBuilder.DropTable(
name: "JobStatusMappings");
migrationBuilder.DropTable(
name: "JobTagMappings");
migrationBuilder.DropTable(
name: "JobUpdateLogs");
migrationBuilder.DropTable(
name: "ServiceProjectTagMappings");
migrationBuilder.DropTable(
name: "TeamRoleMasters");
migrationBuilder.DropTable(
name: "JobTags");
migrationBuilder.DropTable(
name: "JobTickets");
migrationBuilder.DropTable(
name: "ServiceProjectTags");
migrationBuilder.DropTable(
name: "JobStatus");
}
}
}

View File

@ -4943,6 +4943,390 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("JobRoles");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobEmployeeMapping", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("AssigneeId")
.HasColumnType("char(36)");
b.Property<Guid>("JobTicketId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("AssigneeId");
b.HasIndex("JobTicketId");
b.HasIndex("TenantId");
b.ToTable("JobEmployeeMappings");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobStatus", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("JobStatus");
b.HasData(
new
{
Id = new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"),
DisplayName = "New",
Name = "New"
},
new
{
Id = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
DisplayName = "Assigned",
Name = "Assigned"
},
new
{
Id = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
DisplayName = "In Progress",
Name = "In Progress"
},
new
{
Id = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
DisplayName = "Resolved",
Name = "Resolved"
},
new
{
Id = new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
DisplayName = "Done",
Name = "Done"
},
new
{
Id = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
DisplayName = "Closed",
Name = "Closed"
},
new
{
Id = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
DisplayName = "On Hold",
Name = "On Hold"
});
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobStatusMapping", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("NextStatusId")
.HasColumnType("char(36)");
b.Property<Guid>("StatusId")
.HasColumnType("char(36)");
b.Property<Guid?>("TeamRoleId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("NextStatusId");
b.HasIndex("StatusId");
b.HasIndex("TeamRoleId");
b.HasIndex("TenantId");
b.ToTable("JobStatusMappings");
b.HasData(
new
{
Id = new Guid("024e1810-6a57-4a0d-8d2e-be88da79fcd4"),
NextStatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
StatusId = new Guid("32d76a02-8f44-4aa0-9b66-c3716c45a918"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("cb0db140-87fa-4a6f-812d-2834bd0f53a9"),
NextStatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
StatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("42f24930-387e-4f51-9c2d-3e29ffaaf02a"),
NextStatusId = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
StatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("16c83c23-09be-40fd-9d05-f44795d8dee8"),
NextStatusId = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
StatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("8c4ecdae-7435-4475-8389-15bc453561a1"),
NextStatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
StatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("a44b0a66-ee33-47e7-a01f-6b8d9b621543"),
NextStatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
StatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("7165ecee-10e3-4fc0-85d2-6d93d5b11776"),
NextStatusId = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
StatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("87891499-e45d-406b-bf22-722db1beedc9"),
NextStatusId = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
StatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("dc986ec4-858e-4c98-b330-4a5c98c91f07"),
NextStatusId = new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
StatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("ca8b4358-3122-4aaa-bcf8-0b66e4ab313a"),
NextStatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
StatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("5602d32c-290e-48a3-83dd-91af6d12ed46"),
NextStatusId = new Guid("5a6873a5-fed7-4745-a52f-8f61bf3bd72d"),
StatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("9c2918be-b3c1-46fb-a03b-14dd613e1021"),
NextStatusId = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
StatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("ab974bdb-2d8f-4ddc-9b71-bd6d198bae75"),
NextStatusId = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
StatusId = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("2787c903-1b39-4e7d-a0f2-3bb2309bb341"),
NextStatusId = new Guid("cfa1886d-055f-4ded-84c6-42a2a8a14a66"),
StatusId = new Guid("75a0c8b8-9c6a-41af-80bf-b35bab722eb2"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("76bc5551-8f80-469d-ba23-95d7e746c9a9"),
NextStatusId = new Guid("3ddeefb5-ae3c-4e10-a922-35e0a452bb69"),
StatusId = new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
});
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("JobTags");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTagMapping", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("JobTagId")
.HasColumnType("char(36)");
b.Property<Guid>("JobTicketId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("JobTagId");
b.HasIndex("JobTicketId");
b.HasIndex("TenantId");
b.ToTable("JobTagMappings");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTicket", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid?>("AssigneeId")
.HasColumnType("char(36)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("DueDate")
.HasColumnType("datetime(6)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<DateTime>("StartDate")
.HasColumnType("datetime(6)");
b.Property<Guid>("StatusId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("AssigneeId");
b.HasIndex("CreatedById");
b.HasIndex("ProjectId");
b.HasIndex("StatusId");
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("JobTickets");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobUpdateLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Comment")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("JobTicketId")
.HasColumnType("char(36)");
b.Property<Guid?>("NextStatusId")
.HasColumnType("char(36)");
b.Property<Guid?>("StatusId")
.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("JobTicketId");
b.HasIndex("NextStatusId");
b.HasIndex("StatusId");
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("JobUpdateLogs");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProject", b =>
{
b.Property<Guid>("Id")
@ -5041,6 +5425,86 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("ServiceProjectServiceMapping");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectTag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("ServiceProjectTags");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectTagMapping", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("ServiceProjectId")
.HasColumnType("char(36)");
b.Property<Guid>("ServiceProjectTagId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ServiceProjectId");
b.HasIndex("ServiceProjectTagId");
b.ToTable("ServiceProjectTagMappings");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.TeamRoleMaster", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("TeamRoleMasters");
b.HasData(
new
{
Id = new Guid("8cfbf16f-7d3b-4c29-af5b-18935f20aa7b"),
Description = "A Support role involves assisting users or customers by resolving technical or service-related issues, answering inquiries, and ensuring a positive experience with products or services.",
Name = "Support"
},
new
{
Id = new Guid("145d7222-408b-4733-8a17-f417e070b8b8"),
Description = "A Service Engineer installs, maintains, repairs, and troubleshoots equipment to ensure optimal operation and customer satisfaction.",
Name = "Service Engineer"
},
new
{
Id = new Guid("03bf5853-5e0b-4eb8-9f37-33bd999a05b3"),
Description = "A Manager oversees and coordinates a team or department to achieve organizational goals through planning, decision-making, and leadership.",
Name = "Manager"
});
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.SubscriptionPlan", b =>
{
b.Property<Guid>("Id")
@ -7391,6 +7855,190 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobEmployeeMapping", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "Assignee")
.WithMany()
.HasForeignKey("AssigneeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.JobTicket", "JobTicket")
.WithMany()
.HasForeignKey("JobTicketId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Assignee");
b.Navigation("JobTicket");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobStatusMapping", b =>
{
b.HasOne("Marco.Pms.Model.ServiceProject.JobStatus", "NextStatus")
.WithMany()
.HasForeignKey("NextStatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.TeamRoleMaster", "TeamRole")
.WithMany()
.HasForeignKey("TeamRoleId");
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("NextStatus");
b.Navigation("Status");
b.Navigation("TeamRole");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTag", b =>
{
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTagMapping", b =>
{
b.HasOne("Marco.Pms.Model.ServiceProject.JobTag", "JobTag")
.WithMany()
.HasForeignKey("JobTagId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.JobTicket", "JobTicket")
.WithMany()
.HasForeignKey("JobTicketId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("JobTag");
b.Navigation("JobTicket");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobTicket", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "Assignee")
.WithMany()
.HasForeignKey("AssigneeId");
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
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.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("Assignee");
b.Navigation("CreatedBy");
b.Navigation("Project");
b.Navigation("Status");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.JobUpdateLog", b =>
{
b.HasOne("Marco.Pms.Model.ServiceProject.JobTicket", "JobTicket")
.WithMany()
.HasForeignKey("JobTicketId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.JobStatus", "NextStatus")
.WithMany()
.HasForeignKey("NextStatusId");
b.HasOne("Marco.Pms.Model.ServiceProject.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId");
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("JobTicket");
b.Navigation("NextStatus");
b.Navigation("Status");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProject", b =>
{
b.HasOne("Marco.Pms.Model.OrganizationModel.Organization", "Client")
@ -7459,6 +8107,36 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectTag", b =>
{
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProjectTagMapping", b =>
{
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProject", "ServiceProject")
.WithMany()
.HasForeignKey("ServiceProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProjectTag", "ServiceProjectTag")
.WithMany()
.HasForeignKey("ServiceProjectTagId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ServiceProject");
b.Navigation("ServiceProjectTag");
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.SubscriptionPlanDetails", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")

View File

@ -0,0 +1,8 @@
namespace Marco.Pms.Model.Dtos.Employees
{
public class BasicEmployeeDto
{
public Guid EmployeeId { get; set; }
public bool IsActive { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Master
{
public class TagDto
{
public Guid? Id { get; set; }
public required string Name { get; set; }
public bool IsActive { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.ViewModels.Master;
namespace Marco.Pms.Model.Dtos.ServiceProject
{
@ -12,7 +13,7 @@ namespace Marco.Pms.Model.Dtos.ServiceProject
public required string Address { get; set; }
public required DateTime AssignedDate { get; set; }
public required Guid StatusId { get; set; }
public List<TagDto>? Tags { get; set; }
public required string ContactName { get; set; }
public required string ContactPhone { get; set; }
public required string ContactEmail { get; set; }

View File

@ -0,0 +1,22 @@
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 JobEmployeeMapping : TenantRelation
{
public Guid Id { get; set; }
public Guid AssigneeId { get; set; }
[ValidateNever]
[ForeignKey("AssigneeId")]
public Employee? Assignee { get; set; }
public Guid JobTicketId { get; set; }
[ValidateNever]
[ForeignKey("JobTicketId")]
public JobTicket? JobTicket { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.ServiceProject
{
public class JobStatusMapping : TenantRelation
{
public Guid Id { get; set; }
public Guid? TeamRoleId { get; set; }
[ValidateNever]
[ForeignKey("TeamRoleId")]
public TeamRoleMaster? TeamRole { get; set; }
public Guid StatusId { get; set; }
[ValidateNever]
[ForeignKey("StatusId")]
public JobStatus? Status { get; set; }
public Guid NextStatusId { get; set; }
[ValidateNever]
[ForeignKey("NextStatusId")]
public JobStatus? NextStatus { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.ServiceProject
{
public class JobTag : TenantRelation
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,21 @@
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.ServiceProject
{
public class JobTagMapping : TenantRelation
{
public Guid Id { get; set; }
public Guid JobTagId { get; set; }
[ValidateNever]
[ForeignKey("JobTagId")]
public JobTag? JobTag { get; set; }
public Guid JobTicketId { get; set; }
[ValidateNever]
[ForeignKey("JobTicketId")]
public JobTicket? JobTicket { get; set; }
}
}

View File

@ -15,6 +15,11 @@ namespace Marco.Pms.Model.ServiceProject
[ValidateNever]
[ForeignKey("ProjectId")]
public ServiceProject? Project { get; set; }
public Guid? AssigneeId { get; set; }
[ValidateNever]
[ForeignKey("AssigneeId")]
public Employee? Assignee { get; set; }
public Guid StatusId { get; set; }
[ValidateNever]
@ -22,7 +27,7 @@ namespace Marco.Pms.Model.ServiceProject
public JobStatus? Status { get; set; }
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public bool IsActive { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }

View File

@ -0,0 +1,35 @@
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 JobUpdateLog : TenantRelation
{
public Guid Id { get; set; }
public Guid JobTicketId { get; set; }
[ValidateNever]
[ForeignKey("JobTicketId")]
public JobTicket? JobTicket { get; set; }
public Guid? StatusId { get; set; }
[ValidateNever]
[ForeignKey("StatusId")]
public JobStatus? Status { get; set; }
public Guid? NextStatusId { get; set; } // Current status
[ValidateNever]
[ForeignKey("NextStatusId")]
public JobStatus? NextStatus { get; set; }
public string Comment { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime UpdatedAt { get; set; }
public Guid UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]
public Employee? UpdatedBy { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.ServiceProject
{
public class ServiceProjectTag : TenantRelation
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.ServiceProject
{
public class ServiceProjectTagMapping
{
public Guid Id { get; set; }
public Guid ServiceProjectTagId { get; set; }
[ValidateNever]
[ForeignKey("ServiceProjectTagId")]
public ServiceProjectTag? ServiceProjectTag { get; set; }
public Guid ServiceProjectId { get; set; }
[ValidateNever]
[ForeignKey("ServiceProjectId")]
public ServiceProject? ServiceProject { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.ServiceProject
{
public class TeamRoleMaster
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,8 @@
namespace Marco.Pms.Model.ViewModels.Master
{
public class TagVM
{
public Guid Id { get; set; }
public string? Name { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Marco.Pms.Model.ViewModels.ServiceProject
{
public class BasicServiceProjectVM
{
public Guid Id { get; set; }
public string? Name { get; set; }
public string? ShortName { get; set; }
public DateTime AssignedDate { get; set; }
public string? ContactName { get; set; }
public string? ContactPhone { get; set; }
public string? ContactEmail { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using Marco.Pms.Model.Dtos.Employees;
using Marco.Pms.Model.Dtos.Master;
namespace Marco.Pms.Model.ViewModels.ServiceProject
{
public class CreateJobTicketDto
{
public required string Title { get; set; }
public required string Description { get; set; }
public required Guid ProjectId { get; set; }
public List<BasicEmployeeDto>? Assignees { get; set; }
public required DateTime StartDate { get; set; }
public required DateTime DueDate { get; set; }
public List<TagDto>? Tags { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using Marco.Pms.Model.ServiceProject;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Master;
namespace Marco.Pms.Model.ViewModels.ServiceProject
{
public class JobTicketVM
{
public Guid Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public BasicServiceProjectVM? Project { get; set; }
public List<BasicEmployeeVM>? Assignees { get; set; }
public JobStatus? Status { get; set; }
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public List<TagVM>? Tags { get; set; }
}
}

View File

@ -1,6 +1,7 @@
using Marco.Pms.Model.Dtos.ServiceProject;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.ServiceProject;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
@ -29,26 +30,7 @@ namespace Marco.Pms.Services.Controllers
}
[HttpPost("create")]
public async Task<IActionResult> CreateProject([FromBody] ServiceProjectDto serviceProject)
{
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));
}
Employee loggedInEmploee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _serviceProject.CreateServiceProjectAsync(serviceProject, loggedInEmploee, tenantId);
if (response.Success)
{
var notification = new { LoggedInUserId = loggedInEmploee.Id, Keyword = "Service_Project", Response = response.Data };
await _signalR.SendNotificationAsync(notification);
}
return StatusCode(response.StatusCode, response);
}
#region =================================================================== Service Project Functions ===================================================================
[HttpGet("list")]
public async Task<IActionResult> GetServiceProjectList([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
@ -78,6 +60,26 @@ namespace Marco.Pms.Services.Controllers
}
[HttpPost("create")]
public async Task<IActionResult> CreateProject([FromBody] ServiceProjectDto serviceProject)
{
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));
}
Employee loggedInEmploee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _serviceProject.CreateServiceProjectAsync(serviceProject, loggedInEmploee, tenantId);
if (response.Success)
{
var notification = new { LoggedInUserId = loggedInEmploee.Id, Keyword = "Service_Project", Response = response.Data };
await _signalR.SendNotificationAsync(notification);
}
return StatusCode(response.StatusCode, response);
}
[HttpPut("edit/{id}")]
public async Task<IActionResult> UpdateServicecProject(Guid id, [FromBody] ServiceProjectDto serviceProject)
{
@ -114,6 +116,27 @@ namespace Marco.Pms.Services.Controllers
}
return StatusCode(response.StatusCode, response);
}
#endregion
#region =================================================================== Job Tickets Functions ===================================================================
[HttpPost("job/create")]
public async Task<IActionResult> CreateJobTicket(CreateJobTicketDto model)
{
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));
}
Employee loggedInEmploee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _serviceProject.CreateJobTicketAsync(model, loggedInEmploee, tenantId);
if (response.Success)
{
var notification = new { LoggedInUserId = loggedInEmploee.Id, Keyword = "Job_Ticket", Response = response.Data };
await _signalR.SendNotificationAsync(notification);
}
return StatusCode(response.StatusCode, response);
}
#endregion
}
}

View File

@ -195,7 +195,13 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<ServiceProjectDto, ServiceProject>();
CreateMap<ServiceProject, ServiceProjectVM>();
CreateMap<ServiceProject, BasicServiceProjectVM>();
CreateMap<ServiceProject, ServiceProjectDetailsVM>();
#region ======================================================= Job Ticket =======================================================
CreateMap<CreateJobTicketDto, JobTicket>();
CreateMap<JobTicket, JobTicketVM>();
#endregion
#endregion
#region ======================================================= Employee =======================================================
@ -468,7 +474,8 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<PaymentAdjustmentHead, PaymentAdjustmentHeadVM>();
#endregion
#region ======================================================= Expenses Status Master =======================================================
#region ======================================================= Tags =======================================================
CreateMap<JobTag, TagVM>();
#endregion
#region ======================================================= Expenses Status Master =======================================================

View File

@ -1,18 +1,23 @@
using Marco.Pms.Model.Dtos.ServiceProject;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.ServiceProject;
namespace Marco.Pms.Services.Service.ServiceInterfaces
{
public interface IServiceProject
{
#region =================================================================== Service Project Functions ===================================================================
Task<ApiResponse<object>> GetServiceProjectListAsync(int pageNumber, int pageSize, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetServiceProjectDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreateServiceProjectAsync(ServiceProjectDto serviceProject, Employee loggedInEmployee, Guid TenantId);
Task<ApiResponse<object>> UpdateServiceProjectAsync(Guid id, ServiceProjectDto serviceProject, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> DeActivateServiceProjectAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
#endregion
#region =================================================================== Job Tickets Functions ===================================================================
Task<ApiResponse<object>> CreateJobTicketAsync(CreateJobTicketDto model, Employee loggedInEmployee, Guid tenantId);
#endregion
}
}

View File

@ -25,6 +25,9 @@ namespace Marco.Pms.Services.Service
private readonly CacheUpdateHelper _cache;
private readonly IMapper _mapper;
private readonly Guid NewStatus = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918");
private readonly Guid AssignedStatus = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66");
public ServiceProjectService(IDbContextFactory<ApplicationDbContext> dbContextFactory,
IServiceScopeFactory serviceScopeFactory,
ApplicationDbContext context,
@ -357,7 +360,189 @@ namespace Marco.Pms.Services.Service
#region =================================================================== Expense Functions ===================================================================
#endregion
#region =================================================================== Expense Functions ===================================================================
#region =================================================================== Job Tickets Functions ===================================================================
/// <summary>
/// Creates a new job ticket with optional assignees and tags within a transactional scope.
/// </summary>
/// <param name="model">Data transfer object containing job ticket details.</param>
/// <param name="loggedInEmployee">Employee initiating the creation.</param>
/// <param name="tenantId">Tenant identifier for multi-tenant context.</param>
/// <returns>ApiResponse containing the created job ticket view or error details.</returns>
public async Task<ApiResponse<object>> CreateJobTicketAsync(CreateJobTicketDto model, Employee loggedInEmployee, Guid tenantId)
{
await using var transaction = await _context.Database.BeginTransactionAsync();
try
{
_logger.LogInfo("Starting job ticket creation for project {ProjectId} by employee {EmployeeId} in tenant {TenantId}",
model.ProjectId, loggedInEmployee.Id, tenantId);
// Load project and relevant statuses in parallel with separate DbContext instances for concurrency efficiency
var serviceProjectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ServiceProjects.FirstOrDefaultAsync(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId && sp.IsActive);
});
var statusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.JobStatus.Where(js => js.Id == NewStatus || js.Id == AssignedStatus).ToListAsync();
});
await Task.WhenAll(serviceProjectTask, statusTask);
var serviceProject = serviceProjectTask.Result;
var statusList = statusTask.Result;
if (serviceProject == null)
{
_logger.LogWarning("Service project with ID {ProjectId} not found or inactive in tenant {TenantId}", model.ProjectId, tenantId);
return ApiResponse<object>.ErrorResponse("Service project not found", "Service project not found or inactive", 404);
}
var hasAssignees = model.Assignees?.Any(a => a.IsActive) ?? false;
// Map DTO to entity
var jobTicket = _mapper.Map<JobTicket>(model);
jobTicket.Id = Guid.NewGuid();
jobTicket.StatusId = hasAssignees ? AssignedStatus : NewStatus;
jobTicket.CreatedAt = DateTime.UtcNow;
jobTicket.CreatedById = loggedInEmployee.Id;
jobTicket.TenantId = tenantId;
_context.JobTickets.Add(jobTicket);
await _context.SaveChangesAsync();
// Handle assignees if any
List<BasicEmployeeVM> assigneeVMs = new();
if (hasAssignees)
{
var activeAssigneeIds = model.Assignees!.Where(a => a.IsActive).Select(a => a.EmployeeId).ToList();
var assignees = await _context.Employees
.Include(e => e.JobRole)
.Where(e => activeAssigneeIds.Contains(e.Id) && e.IsActive)
.ToListAsync();
var jobEmployeeMappings = assignees.Select(e => new JobEmployeeMapping
{
AssigneeId = e.Id,
JobTicketId = jobTicket.Id,
TenantId = tenantId
}).ToList();
_context.JobEmployeeMappings.AddRange(jobEmployeeMappings);
assigneeVMs = _mapper.Map<List<BasicEmployeeVM>>(assignees);
}
// Handle tags if any
var jobTags = new List<JobTag>();
if (model.Tags?.Any(t => t.IsActive) ?? false)
{
var activeTagNames = model.Tags.Where(t => t.IsActive).Select(t => t.Name).Distinct().ToList();
var existingTags = await _context.JobTags.Where(jt => activeTagNames.Contains(jt.Name) && jt.TenantId == tenantId).ToListAsync();
var newTags = new List<JobTag>();
var tagMappings = new List<JobTagMapping>();
foreach (var tagDto in model.Tags.Where(t => t.IsActive).DistinctBy(t => t.Name))
{
var tag = existingTags.FirstOrDefault(jt => jt.Name == tagDto.Name);
if (tag == null)
{
tag = new JobTag
{
Id = Guid.NewGuid(),
Name = tagDto.Name,
TenantId = tenantId
};
newTags.Add(tag);
}
tagMappings.Add(new JobTagMapping
{
Id = Guid.NewGuid(),
JobTagId = tag.Id,
JobTicketId = jobTicket.Id,
TenantId = tenantId
});
}
if (newTags.Any()) _context.JobTags.AddRange(newTags);
_context.JobTagMappings.AddRange(tagMappings);
jobTags.AddRange(existingTags);
jobTags.AddRange(newTags);
}
// Create job update logs for status change
var updateLogs = new List<JobUpdateLog>();
const string creationMessage = "A new job has been successfully created and added to the system. It is now ready for assignment and further processing.";
updateLogs.Add(new JobUpdateLog
{
JobTicketId = jobTicket.Id,
NextStatusId = NewStatus,
Description = creationMessage,
Comment = creationMessage,
UpdatedAt = DateTime.UtcNow,
UpdatedById = loggedInEmployee.Id,
TenantId = tenantId
});
if (hasAssignees)
{
const string assignmentMessage = "The designated assignee(s) have been successfully assigned to the job and are now responsible for managing and completing the associated tasks.";
updateLogs.Add(new JobUpdateLog
{
JobTicketId = jobTicket.Id,
StatusId = NewStatus,
NextStatusId = AssignedStatus,
Description = assignmentMessage,
Comment = assignmentMessage,
UpdatedAt = DateTime.UtcNow.AddTicks(10), // Small offset to preserve time order
UpdatedById = loggedInEmployee.Id,
TenantId = tenantId
});
}
_context.JobUpdateLogs.AddRange(updateLogs);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
// Prepare response VM
var currentStatus = statusList.FirstOrDefault(js => js.Id == jobTicket.StatusId);
var vm = _mapper.Map<JobTicketVM>(jobTicket);
vm.Status = currentStatus;
vm.Project = _mapper.Map<BasicServiceProjectVM>(serviceProject);
vm.Assignees = assigneeVMs;
vm.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
vm.Tags = _mapper.Map<List<TagVM>>(jobTags.Distinct().ToList());
_logger.LogInfo("Job ticket {JobTicketId} successfully created with status {StatusId} by employee {EmployeeId} in tenant {TenantId}",
jobTicket.Id, jobTicket.StatusId, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(vm, "Job created successfully", 201);
}
catch (DbUpdateException dbEx)
{
await transaction.RollbackAsync();
_logger.LogError(dbEx, "Database error while creating job ticket for project {ProjectId} by employee {EmployeeId} in tenant {TenantId}",
model.ProjectId, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Database Error", "An error occurred while saving data to the database.", 500);
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "Unhandled exception while creating job ticket for project {ProjectId} by employee {EmployeeId} in tenant {TenantId}",
model.ProjectId, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500);
}
}
#endregion
}
}