Compare commits
No commits in common. "main" and "Service_Project_Management" have entirely different histories.
main
...
Service_Pr
@ -213,15 +213,9 @@ namespace Marco.Pms.DataAccess.Data
|
||||
public DbSet<ServiceProjectServiceMapping> ServiceProjectServiceMapping { get; set; }
|
||||
public DbSet<TeamRoleMaster> TeamRoleMasters { get; set; }
|
||||
public DbSet<ServiceProjectTag> ServiceProjectTags { get; set; }
|
||||
//public DbSet<TalkingPoint> TalkingPoints { get; set; }
|
||||
//public DbSet<TalkingPointAttachment> TalkingPointAttachments { get; set; }
|
||||
public DbSet<ServiceProjectTagMapping> ServiceProjectTagMappings { get; set; }
|
||||
public DbSet<ServiceProjectAllocation> ServiceProjectAllocations { get; set; }
|
||||
|
||||
#region ======================================================= Project Branch =======================================================
|
||||
public DbSet<ProjectBranch> ProjectBranches { get; set; }
|
||||
#endregion
|
||||
|
||||
#region ======================================================= Job =======================================================
|
||||
public DbSet<JobTicket> JobTickets { get; set; }
|
||||
public DbSet<JobStatus> JobStatus { get; set; }
|
||||
@ -1286,8 +1280,8 @@ namespace Marco.Pms.DataAccess.Data
|
||||
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 = "Work Done", DisplayName = "Work Done", Level = 4 },
|
||||
new JobStatus { Id = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"), Name = "Review Done", DisplayName = "Review Done", Level = 5 },
|
||||
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 }
|
||||
);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,122 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_ProjectBranches_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
table: "Projects",
|
||||
keyColumn: "ContactPerson",
|
||||
keyValue: null,
|
||||
column: "ContactPerson",
|
||||
value: "");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ContactPerson",
|
||||
table: "Projects",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProjectBranches",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
BranchName = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
ContactInformation = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Address = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
BranchType = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
GoogleMapUrl = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
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_ProjectBranches", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectBranches_Employees_CreatedById",
|
||||
column: x => x.CreatedById,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectBranches_Employees_UpdatedById",
|
||||
column: x => x.UpdatedById,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectBranches_ServiceProjects_ProjectId",
|
||||
column: x => x.ProjectId,
|
||||
principalTable: "ServiceProjects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectBranches_Tenants_TenantId",
|
||||
column: x => x.TenantId,
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectBranches_CreatedById",
|
||||
table: "ProjectBranches",
|
||||
column: "CreatedById");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectBranches_ProjectId",
|
||||
table: "ProjectBranches",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectBranches_TenantId",
|
||||
table: "ProjectBranches",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectBranches_UpdatedById",
|
||||
table: "ProjectBranches",
|
||||
column: "UpdatedById");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProjectBranches");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ContactPerson",
|
||||
table: "Projects",
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_ProjectBranch_As_ForignKey_In_JobTickets_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "ProjectBranchId",
|
||||
table: "JobTickets",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "JobStatus",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
|
||||
columns: new[] { "DisplayName", "Name" },
|
||||
values: new object[] { "Work Done", "Work Done" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "JobStatus",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
|
||||
columns: new[] { "DisplayName", "Name" },
|
||||
values: new object[] { "Review Done", "Review Done" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobTickets_ProjectBranchId",
|
||||
table: "JobTickets",
|
||||
column: "ProjectBranchId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_JobTickets_ProjectBranches_ProjectBranchId",
|
||||
table: "JobTickets",
|
||||
column: "ProjectBranchId",
|
||||
principalTable: "ProjectBranches",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_JobTickets_ProjectBranches_ProjectBranchId",
|
||||
table: "JobTickets");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_JobTickets_ProjectBranchId",
|
||||
table: "JobTickets");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ProjectBranchId",
|
||||
table: "JobTickets");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "JobStatus",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
|
||||
columns: new[] { "DisplayName", "Name" },
|
||||
values: new object[] { "Review", "Review" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "JobStatus",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
|
||||
columns: new[] { "DisplayName", "Name" },
|
||||
values: new object[] { "Done", "Done" });
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,29 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_IsArchive_In_JobTicket_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsArchive",
|
||||
table: "JobTickets",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsArchive",
|
||||
table: "JobTickets");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,39 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_Project_ForignKey_From_Expenses_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Expenses_Projects_ProjectId",
|
||||
table: "Expenses");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Expenses_ProjectId",
|
||||
table: "Expenses");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Expenses_ProjectId",
|
||||
table: "Expenses",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Expenses_Projects_ProjectId",
|
||||
table: "Expenses",
|
||||
column: "ProjectId",
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,38 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_Project_ForignKey_From_PaymentRequest_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_PaymentRequests_Projects_ProjectId",
|
||||
table: "PaymentRequests");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_PaymentRequests_ProjectId",
|
||||
table: "PaymentRequests");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PaymentRequests_ProjectId",
|
||||
table: "PaymentRequests",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_PaymentRequests_Projects_ProjectId",
|
||||
table: "PaymentRequests",
|
||||
column: "ProjectId",
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,38 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_Project_ForignKey_From_RecurringPayment_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_RecurringPayments_Projects_ProjectId",
|
||||
table: "RecurringPayments");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_RecurringPayments_ProjectId",
|
||||
table: "RecurringPayments");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecurringPayments_ProjectId",
|
||||
table: "RecurringPayments",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_RecurringPayments_Projects_ProjectId",
|
||||
table: "RecurringPayments",
|
||||
column: "ProjectId",
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,39 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_Project_ForignKey_From_ProjectContactMapping_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ContactProjectMappings_Projects_ProjectId",
|
||||
table: "ContactProjectMappings");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ContactProjectMappings_ProjectId",
|
||||
table: "ContactProjectMappings");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContactProjectMappings_ProjectId",
|
||||
table: "ContactProjectMappings",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ContactProjectMappings_Projects_ProjectId",
|
||||
table: "ContactProjectMappings",
|
||||
column: "ProjectId",
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,106 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Corrected_JobTicketId_Spelling_In_JobAttendance : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_JobAttendance_JobTickets_JobTcketId",
|
||||
table: "JobAttendance");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_JobAttendanceLogs_JobTickets_JobTcketId",
|
||||
table: "JobAttendanceLogs");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "JobTcketId",
|
||||
table: "JobAttendanceLogs",
|
||||
newName: "JobTicketId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_JobAttendanceLogs_JobTcketId",
|
||||
table: "JobAttendanceLogs",
|
||||
newName: "IX_JobAttendanceLogs_JobTicketId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "JobTcketId",
|
||||
table: "JobAttendance",
|
||||
newName: "JobTicketId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_JobAttendance_JobTcketId",
|
||||
table: "JobAttendance",
|
||||
newName: "IX_JobAttendance_JobTicketId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_JobAttendance_JobTickets_JobTicketId",
|
||||
table: "JobAttendance",
|
||||
column: "JobTicketId",
|
||||
principalTable: "JobTickets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_JobAttendanceLogs_JobTickets_JobTicketId",
|
||||
table: "JobAttendanceLogs",
|
||||
column: "JobTicketId",
|
||||
principalTable: "JobTickets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_JobAttendance_JobTickets_JobTicketId",
|
||||
table: "JobAttendance");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_JobAttendanceLogs_JobTickets_JobTicketId",
|
||||
table: "JobAttendanceLogs");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "JobTicketId",
|
||||
table: "JobAttendanceLogs",
|
||||
newName: "JobTcketId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_JobAttendanceLogs_JobTicketId",
|
||||
table: "JobAttendanceLogs",
|
||||
newName: "IX_JobAttendanceLogs_JobTcketId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "JobTicketId",
|
||||
table: "JobAttendance",
|
||||
newName: "JobTcketId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_JobAttendance_JobTicketId",
|
||||
table: "JobAttendance",
|
||||
newName: "IX_JobAttendance_JobTcketId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_JobAttendance_JobTickets_JobTcketId",
|
||||
table: "JobAttendance",
|
||||
column: "JobTcketId",
|
||||
principalTable: "JobTickets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_JobAttendanceLogs_JobTickets_JobTcketId",
|
||||
table: "JobAttendanceLogs",
|
||||
column: "JobTcketId",
|
||||
principalTable: "JobTickets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -898,6 +898,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("ContactId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ContactProjectMappings");
|
||||
@ -2327,6 +2329,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("ProcessedById");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("ReviewedById");
|
||||
|
||||
b.HasIndex("StatusId");
|
||||
@ -2794,6 +2798,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("PaidById");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("RecurringPaymentId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
@ -2920,6 +2926,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("ExpenseCategoryId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("StatusId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
@ -4791,7 +4799,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("ContactPerson")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
@ -5054,7 +5061,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid>("EmployeeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("JobTicketId")
|
||||
b.Property<Guid>("JobTcketId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("TaggedInAt")
|
||||
@ -5076,7 +5083,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("EmployeeId");
|
||||
|
||||
b.HasIndex("JobTicketId");
|
||||
b.HasIndex("JobTcketId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
@ -5104,7 +5111,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid>("JobAttendanceId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("JobTicketId")
|
||||
b.Property<Guid>("JobTcketId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Latitude")
|
||||
@ -5130,7 +5137,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("JobAttendanceId");
|
||||
|
||||
b.HasIndex("JobTicketId");
|
||||
b.HasIndex("JobTcketId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
@ -5253,16 +5260,16 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("aab71020-2fb8-44d9-9430-c9a7e9bf33b0"),
|
||||
DisplayName = "Work Done",
|
||||
DisplayName = "Review",
|
||||
Level = 4,
|
||||
Name = "Work Done"
|
||||
Name = "Review"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7"),
|
||||
DisplayName = "Review Done",
|
||||
DisplayName = "Done",
|
||||
Level = 5,
|
||||
Name = "Review Done"
|
||||
Name = "Done"
|
||||
},
|
||||
new
|
||||
{
|
||||
@ -5486,12 +5493,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsArchive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid?>("ProjectBranchId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@ -5525,8 +5526,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("ProjectBranchId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("StatusId");
|
||||
@ -5538,65 +5537,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.ToTable("JobTickets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ProjectBranch", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("BranchName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("BranchType")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ContactInformation")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid>("CreatedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("GoogleMapUrl")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.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");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("ProjectBranches");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProject", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -6863,6 +6803,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
@ -6871,6 +6817,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("Contact");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
@ -7362,6 +7310,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("ProcessedById");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "ReviewedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReviewedById");
|
||||
@ -7394,6 +7348,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("ProcessedBy");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("ReviewedBy");
|
||||
|
||||
b.Navigation("Status");
|
||||
@ -7524,6 +7480,10 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("PaidById");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Expenses.RecurringPayment", "RecurringPayment")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecurringPaymentId");
|
||||
@ -7548,6 +7508,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("PaidBy");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("RecurringPayment");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
@ -7600,6 +7562,10 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("ExpenseCategoryId");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Expenses.Masters.RecurringPaymentStatus", "Status")
|
||||
.WithMany()
|
||||
.HasForeignKey("StatusId")
|
||||
@ -7622,6 +7588,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("ExpenseCategory");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("Status");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
@ -8253,7 +8221,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasOne("Marco.Pms.Model.ServiceProject.JobTicket", "JobTicket")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobTicketId")
|
||||
.HasForeignKey("JobTcketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@ -8290,7 +8258,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasOne("Marco.Pms.Model.ServiceProject.JobTicket", "JobTicket")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobTicketId")
|
||||
.HasForeignKey("JobTcketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@ -8450,10 +8418,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.ServiceProject.ProjectBranch", "ProjectBranch")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectBranchId");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.ServiceProject.ServiceProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
@ -8480,8 +8444,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("ProjectBranch");
|
||||
|
||||
b.Navigation("Status");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
@ -8489,39 +8451,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ProjectBranch", b =>
|
||||
{
|
||||
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.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.ServiceProject.ServiceProject", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.OrganizationModel.Organization", "Client")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Marco.Pms.Model.Directory
|
||||
{
|
||||
@ -8,6 +9,9 @@ namespace Marco.Pms.Model.Directory
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public Guid ContactId { get; set; }
|
||||
[ValidateNever]
|
||||
[ForeignKey("ContactId")]
|
||||
|
||||
@ -8,7 +8,6 @@ namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
public required string Title { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public required Guid ProjectId { get; set; }
|
||||
public Guid? ProjectBranchId { get; set; }
|
||||
public List<BasicEmployeeDto>? Assignees { get; set; }
|
||||
public required DateTime StartDate { get; set; }
|
||||
public required DateTime DueDate { get; set; }
|
||||
|
||||
@ -5,7 +5,7 @@ namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
{
|
||||
public class JobAttendanceDto
|
||||
{
|
||||
public required Guid JobTicketId { get; set; }
|
||||
public required Guid JobTcketId { get; set; }
|
||||
public required TAGGING_MARK_TYPE Action { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
{
|
||||
public class ProjectBranchDto
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public required string BranchName { get; set; }
|
||||
public required Guid ProjectId { get; set; }
|
||||
public required string ContactInformation { get; set; }
|
||||
public required string Address { get; set; }
|
||||
public required string BranchType { get; set; } // HQ, ATMs, Bank Branches, Overcounter desk
|
||||
public string? GoogleMapUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
{
|
||||
public class TalkingPointDto
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public required Guid ServiceProjectId { get; set; }
|
||||
public required string Comment { get; set; }
|
||||
public List<FileUploadModel>? Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
@ -7,11 +7,11 @@ namespace Marco.Pms.Model.Dtos.ServiceProject
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
public Guid StatusId { get; set; }
|
||||
public List<BasicEmployeeDto>? Assignees { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime DueDate { get; set; }
|
||||
public List<TagDto>? Tags { get; set; }
|
||||
public bool IsArchive { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Expenses.Masters;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@ -13,6 +14,10 @@ namespace Marco.Pms.Model.Expenses
|
||||
public string UIDPrefix { get; set; } = default!;
|
||||
public int UIDPostfix { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public Guid ExpensesTypeId { get; set; }
|
||||
|
||||
//[ValidateNever]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Expenses.Masters;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@ -27,6 +28,10 @@ namespace Marco.Pms.Model.Expenses
|
||||
public double? TDSPercentage { get; set; }
|
||||
public DateTime DueDate { get; set; }
|
||||
public Guid? ProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public Guid? RecurringPaymentId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Expenses.Masters;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.TenantModels;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
@ -28,6 +29,10 @@ namespace Marco.Pms.Model.Expenses
|
||||
public DateTime? LatestPRGeneratedAt { get; set; }
|
||||
public DateTime? NextStrikeDate { get; set; }
|
||||
public Guid? ProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public int PaymentBufferDays { get; set; }
|
||||
public Guid? ExpenseCategoryId { get; set; }
|
||||
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class AdvanceFilter
|
||||
{
|
||||
// The dynamic filters from your JSON
|
||||
public List<SortItem>? SortFilters { get; set; }
|
||||
public List<SearchItem>? SearchFilters { get; set; }
|
||||
public List<AdvanceItem>? AdvanceFilters { get; set; }
|
||||
public string GroupByColumn { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class AdvanceItem
|
||||
{
|
||||
public string Column { get; set; } = string.Empty;
|
||||
public string Opration { get; set; } = string.Empty; // "greater than", "equal to", etc.
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class SearchItem
|
||||
{
|
||||
public string Column { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class SortItem
|
||||
{
|
||||
public string Column { get; set; } = string.Empty;
|
||||
public bool SortDescending { get; set; }
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ namespace Marco.Pms.Model.Projects
|
||||
public Guid Id { get; set; }
|
||||
[Required]
|
||||
[DisplayName("Project Name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? ShortName { get; set; }
|
||||
|
||||
[DisplayName("Project Address")]
|
||||
@ -22,7 +22,7 @@ namespace Marco.Pms.Model.Projects
|
||||
|
||||
[DisplayName("Contact Person")]
|
||||
|
||||
public string ContactPerson { get; set; } = string.Empty;
|
||||
public string? ContactPerson { get; set; }
|
||||
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
@ -8,10 +8,10 @@ namespace Marco.Pms.Model.ServiceProject
|
||||
public class JobAttendance : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid JobTicketId { get; set; }
|
||||
public Guid JobTcketId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("JobTicketId")]
|
||||
[ForeignKey("JobTcketId")]
|
||||
public JobTicket? JobTicket { get; set; }
|
||||
public TAGGING_MARK_TYPE Action { get; set; }
|
||||
public Guid EmployeeId { get; set; }
|
||||
|
||||
@ -14,10 +14,10 @@ namespace Marco.Pms.Model.ServiceProject
|
||||
[ValidateNever]
|
||||
[ForeignKey("JobAttendanceId")]
|
||||
public JobAttendance? JobAttendance { get; set; }
|
||||
public Guid JobTicketId { get; set; }
|
||||
public Guid JobTcketId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("JobTicketId")]
|
||||
[ForeignKey("JobTcketId")]
|
||||
public JobTicket? JobTicket { get; set; }
|
||||
public Guid? DocumentId { get; set; }
|
||||
|
||||
|
||||
@ -17,11 +17,6 @@ namespace Marco.Pms.Model.ServiceProject
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public ServiceProject? Project { get; set; }
|
||||
public Guid? ProjectBranchId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectBranchId")]
|
||||
public ProjectBranch? ProjectBranch { get; set; }
|
||||
public Guid StatusId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
@ -30,7 +25,6 @@ namespace Marco.Pms.Model.ServiceProject
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime DueDate { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public bool IsArchive { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public Guid CreatedById { get; set; }
|
||||
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
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 ProjectBranch : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string BranchName { get; set; } = string.Empty;
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public ServiceProject? Project { get; set; }
|
||||
public string ContactInformation { get; set; } = string.Empty; // Json string
|
||||
public string Address { get; set; } = string.Empty;
|
||||
public string BranchType { get; set; } = string.Empty; // HQ, ATMs, Bank Branches, Overcounter desk
|
||||
public string? GoogleMapUrl { get; set; }
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
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 TalkingPoint : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ServiceProjectId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ServiceProjectId")]
|
||||
public ServiceProject? ServiceProject { 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; }
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
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 TalkingPointAttachment : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid DocumentId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("DocumentId")]
|
||||
public Document? Document { get; set; }
|
||||
public Guid? TalkingPointId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("TalkingPointId")]
|
||||
public TalkingPoint? TalkingPoint { get; set; }
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
||||
public class ExpenseDetailsVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public ExpenseCategoryMasterVM? ExpenseCategory { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
|
||||
@ -9,7 +9,7 @@ namespace Marco.Pms.Model.ViewModels.Expanses
|
||||
public class ExpenseList
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public ExpenseCategoryMasterVM? ExpenseCategory { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
|
||||
@ -7,6 +7,5 @@
|
||||
public string? Description { get; set; }
|
||||
public string? JobTicketUId { get; set; }
|
||||
public string? StatusName { get; set; }
|
||||
public bool IsArchive { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
{
|
||||
public class BasicProjectBranchVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? BranchName { get; set; }
|
||||
public string? BranchType { get; set; } // HQ, ATMs, Bank Branches, Overcounter desk
|
||||
}
|
||||
}
|
||||
@ -6,5 +6,8 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,13 +11,11 @@ namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
public string? Description { get; set; }
|
||||
public string? JobTicketUId { get; set; }
|
||||
public BasicServiceProjectVM? Project { get; set; }
|
||||
public BasicProjectBranchVM? ProjectBranch { 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 bool IsArchive { get; set; }
|
||||
public Guid? AttendanceId { get; set; }
|
||||
public TAGGING_MARK_TYPE? TaggingAction { get; set; }
|
||||
public TAGGING_MARK_TYPE? NextTaggingAction { get; set; }
|
||||
|
||||
@ -16,7 +16,6 @@ namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime DueDate { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsArchive { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public List<TagVM>? Tags { get; set; }
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
{
|
||||
public class ProjectBranchDetailsVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? BranchName { get; set; }
|
||||
public BasicServiceProjectVM? Project { get; set; }
|
||||
public string? ContactInformation { get; set; } // Json string
|
||||
public string? Address { get; set; }
|
||||
public string? BranchType { get; set; } // HQ, ATMs, Bank Branches, Overcounter desk
|
||||
public string? GoogleMapUrl { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
{
|
||||
public class ProjectBranchVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? BranchName { get; set; }
|
||||
public BasicServiceProjectVM? Project { get; set; }
|
||||
public string? ContactInformation { get; set; } // Json string
|
||||
public string? Address { get; set; }
|
||||
public string? BranchType { get; set; } // HQ, ATMs, Bank Branches, Overcounter desk
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.DocumentManager;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.ServiceProject
|
||||
{
|
||||
public class TalkingPointVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public BasicServiceProjectVM? ServiceProject { 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>? Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
@ -474,7 +474,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
@ -517,7 +517,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.OutTime = finalDateTime;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
|
||||
attendance.RequestedById = loggedInEmployee.Id;
|
||||
attendance.RequestedById = currentEmployee.Id;
|
||||
attendance.RequestedAt = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
@ -531,7 +531,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.IsApproved = true;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
|
||||
attendance.ApprovedById = loggedInEmployee.Id;
|
||||
attendance.ApprovedById = currentEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
// do nothing
|
||||
}
|
||||
@ -539,7 +539,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.IsApproved = false;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
|
||||
attendance.ApprovedById = loggedInEmployee.Id;
|
||||
attendance.ApprovedById = currentEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
// do nothing
|
||||
}
|
||||
@ -584,7 +584,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
Longitude = recordAttendanceDot.Longitude,
|
||||
|
||||
TenantId = tenantId,
|
||||
UpdatedBy = loggedInEmployee.Id,
|
||||
UpdatedBy = currentEmployee.Id,
|
||||
UpdatedOn = recordAttendanceDot.Date
|
||||
};
|
||||
//if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.Count > 0)
|
||||
@ -619,7 +619,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
sendActivity = 1;
|
||||
}
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Attendance", Activity = sendActivity, ProjectId = attendance.ProjectID, Response = vm };
|
||||
var notification = new { LoggedInUserId = currentEmployee.Id, Keyword = "Attendance", Activity = sendActivity, ProjectId = attendance.ProjectID, Response = vm };
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
_logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);
|
||||
|
||||
@ -628,11 +628,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
// --- Push Notification Section ---
|
||||
// This section attempts to send a test push notification to the user's device.
|
||||
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||
var context = HttpContext;
|
||||
string origin = context.Request.Headers["Origin"].FirstOrDefault() ?? "";
|
||||
|
||||
var name = $"{vm.FirstName} {vm.LastName}";
|
||||
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, origin, loggedInEmployee.Id, tenantId);
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, tenantId);
|
||||
|
||||
});
|
||||
|
||||
@ -849,12 +848,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
// --- Push Notification Section ---
|
||||
// This section attempts to send a test push notification to the user's device.
|
||||
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||
var context = HttpContext;
|
||||
string origin = context.Request.Headers["Origin"].FirstOrDefault() ?? "";
|
||||
|
||||
var name = $"{vm.FirstName} {vm.LastName}";
|
||||
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, origin, loggedInEmployee.Id, tenantId);
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, tenantId);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ using Marco.Pms.Helpers.Utility;
|
||||
using Marco.Pms.Model.Collection;
|
||||
using Marco.Pms.Model.Dtos.Collection;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Filters;
|
||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||
using Marco.Pms.Model.OrganizationModel;
|
||||
using Marco.Pms.Model.Projects;
|
||||
@ -14,7 +13,6 @@ using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.Collection;
|
||||
using Marco.Pms.Model.ViewModels.Organization;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
using Marco.Pms.Services.Extensions;
|
||||
using Marco.Pms.Services.Service;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
@ -22,9 +20,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MongoDB.Driver;
|
||||
using System.Text.Json;
|
||||
using Document = Marco.Pms.Model.DocumentManager.Document;
|
||||
using Invoice = Marco.Pms.Model.Collection.Invoice;
|
||||
|
||||
namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
@ -63,8 +59,8 @@ namespace Marco.Pms.Services.Controllers
|
||||
/// </summary>
|
||||
|
||||
[HttpGet("invoice/list")]
|
||||
public async Task<IActionResult> GetInvoiceListAsync([FromQuery] Guid? projectId, [FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] DateTime? fromDate,
|
||||
[FromQuery] DateTime? toDate, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1, [FromQuery] bool isActive = true, [FromQuery] bool isPending = false)
|
||||
public async Task<IActionResult> GetInvoiceListAsync([FromQuery] Guid? projectId, [FromQuery] string? searchString, [FromQuery] DateTime? fromDate, [FromQuery] DateTime? toDate,
|
||||
[FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1, [FromQuery] bool isActive = true, [FromQuery] bool isPending = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -98,52 +94,16 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
_logger.LogInfo("Permissions validated for EmployeeId {EmployeeId}.", employee.Id);
|
||||
|
||||
var advanceFilter = TryDeserializeFilter(filter);
|
||||
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
// Fetch related project data asynchronously and in parallel
|
||||
var infraProjectsQuery = _context.Projects
|
||||
.Where(p => p.TenantId == tenantId);
|
||||
|
||||
var serviceProjectsQuery = context.ServiceProjects
|
||||
.Where(sp => sp.TenantId == tenantId);
|
||||
|
||||
if (advanceFilter?.SearchFilters != null && advanceFilter.SearchFilters.Any())
|
||||
{
|
||||
var projectSearchFilter = advanceFilter.SearchFilters
|
||||
.Where(f => f.Column == "ProjectName")
|
||||
.Select(f => new SearchItem { Column = "Name", Value = f.Value })
|
||||
.ToList();
|
||||
if (projectSearchFilter.Any())
|
||||
{
|
||||
infraProjectsQuery = infraProjectsQuery.ApplySearchFilters(projectSearchFilter);
|
||||
serviceProjectsQuery = serviceProjectsQuery.ApplySearchFilters(projectSearchFilter);
|
||||
}
|
||||
}
|
||||
|
||||
var infraProjectsTask = infraProjectsQuery
|
||||
.Select(p => _mapper.Map<BasicProjectVM>(p))
|
||||
.ToListAsync();
|
||||
var serviceProjectsTask = serviceProjectsQuery
|
||||
.Select(sp => _mapper.Map<BasicProjectVM>(sp))
|
||||
.ToListAsync();
|
||||
|
||||
await Task.WhenAll(infraProjectsTask, serviceProjectsTask);
|
||||
|
||||
var projects = infraProjectsTask.Result;
|
||||
projects.AddRange(serviceProjectsTask.Result);
|
||||
|
||||
var projIds = projects.Select(p => p.Id).Distinct().ToList();
|
||||
|
||||
// Build invoice query efficiently - always use AsNoTracking for reads
|
||||
var query = _context.Invoices
|
||||
.AsNoTracking()
|
||||
.Include(i => i.BilledTo)
|
||||
.Include(i => i.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||
.Include(i => i.UpdatedBy).ThenInclude(e => e!.JobRole)
|
||||
.Where(i => projIds.Contains(i.ProjectId) && i.IsActive == isActive && i.TenantId == tenantId);
|
||||
.Where(i => i.IsActive == isActive && i.TenantId == tenantId);
|
||||
|
||||
// Filter by date, ensuring date boundaries are correct
|
||||
if (fromDate.HasValue && toDate.HasValue)
|
||||
@ -166,22 +126,11 @@ namespace Marco.Pms.Services.Controllers
|
||||
_logger.LogDebug("Project filter applied: {ProjectId}", projectId.Value);
|
||||
}
|
||||
|
||||
query = query.ApplyCustomFilters(advanceFilter, "InvoiceDate");
|
||||
|
||||
if (advanceFilter?.SearchFilters != null)
|
||||
{
|
||||
var invoiceSearchFilter = advanceFilter.SearchFilters.Where(f => f.Column != "ProjectName").ToList();
|
||||
if (invoiceSearchFilter.Any())
|
||||
{
|
||||
query = query.ApplySearchFilters(invoiceSearchFilter);
|
||||
}
|
||||
}
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
var totalPages = (int)Math.Ceiling((double)totalItems / pageSize);
|
||||
_logger.LogInfo("Total invoices found: {TotalItems}", totalItems);
|
||||
|
||||
var pagedInvoices = await query
|
||||
.OrderByDescending(i => i.InvoiceDate)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
@ -206,6 +155,20 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
_logger.LogDebug("Received payment data for {Count} invoices.", paymentGroups.Count);
|
||||
|
||||
// Fetch related project data asynchronously and in parallel
|
||||
var projIds = pagedInvoices.Select(i => i.ProjectId).Distinct().ToList();
|
||||
var infraProjectsTask = _context.Projects
|
||||
.Where(p => projIds.Contains(p.Id) && p.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var serviceProjectsTask = context.ServiceProjects
|
||||
.Where(sp => projIds.Contains(sp.Id) && sp.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
await Task.WhenAll(infraProjectsTask, serviceProjectsTask);
|
||||
|
||||
var infraProjects = infraProjectsTask.Result;
|
||||
var serviceProjects = serviceProjectsTask.Result;
|
||||
|
||||
// Build results and compute balances in memory for tight control
|
||||
var results = new List<InvoiceListVM>();
|
||||
|
||||
@ -222,13 +185,16 @@ namespace Marco.Pms.Services.Controllers
|
||||
var vm = _mapper.Map<InvoiceListVM>(invoice);
|
||||
|
||||
// Project mapping logic - minimize nested object allocations
|
||||
vm.Project = projects.Where(sp => sp.Id == invoice.ProjectId).FirstOrDefault();
|
||||
vm.Project = serviceProjects.Where(sp => sp.Id == invoice.ProjectId).Select(p => _mapper.Map<BasicProjectVM>(p)).FirstOrDefault()
|
||||
?? infraProjects.Where(ip => ip.Id == invoice.ProjectId).Select(sp => _mapper.Map<BasicProjectVM>(sp)).FirstOrDefault();
|
||||
|
||||
|
||||
vm.BalanceAmount = balance;
|
||||
results.Add(vm);
|
||||
}
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)totalItems / pageSize);
|
||||
|
||||
_logger.LogInfo("Returning {Count} invoices (page {PageNumber} of {TotalPages}).", results.Count, pageNumber, totalPages);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(
|
||||
@ -1189,45 +1155,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
#region =================================================================== Helper Functions ===================================================================
|
||||
|
||||
private AdvanceFilter? TryDeserializeFilter(string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
AdvanceFilter? advanceFilter = null;
|
||||
|
||||
try
|
||||
{
|
||||
// First, try to deserialize directly. This is the expected case (e.g., from a web client).
|
||||
advanceFilter = JsonSerializer.Deserialize<AdvanceFilter>(filter, options);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
|
||||
// If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients).
|
||||
try
|
||||
{
|
||||
// Unescape the string first, then deserialize the result.
|
||||
string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
|
||||
if (!string.IsNullOrWhiteSpace(unescapedJsonString))
|
||||
{
|
||||
advanceFilter = JsonSerializer.Deserialize<AdvanceFilter>(unescapedJsonString, options);
|
||||
}
|
||||
}
|
||||
catch (JsonException ex1)
|
||||
{
|
||||
// If both attempts fail, log the final error and return null.
|
||||
_logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return advanceFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async permission check helper with scoped DI lifetime
|
||||
/// </summary>
|
||||
|
||||
@ -206,14 +206,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(dashboardVM, "Project counts fetched successfully.", 200));
|
||||
}
|
||||
|
||||
[HttpGet("project-completion-status")]
|
||||
public async Task<IActionResult> GetAllProjectsAsync()
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetAllProjectsAsync(string.Empty, 0, 0, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a dashboard summary of total employees and today's attendance.
|
||||
/// If a projectId is provided, it returns totals for that project; otherwise, for all accessible active projects.
|
||||
|
||||
@ -133,13 +133,30 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("get/project/report/{projectId}")]
|
||||
public async Task<IActionResult> GetProjectReport(Guid projectId)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _reportHelper = scope.ServiceProvider.GetRequiredService<ReportHelper>();
|
||||
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
var resonse = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId);
|
||||
|
||||
if (resonse == null)
|
||||
{
|
||||
_logger.LogWarning("Project report not found");
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project report not found", "Project report not found", 404));
|
||||
}
|
||||
_logger.LogInfo("Report for the project fetched successfully");
|
||||
return Ok(ApiResponse<object>.SuccessResponse(resonse, "Report for the project fetched successfully", 200));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a daily project report by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The GUID of the project for which to generate the report.</param>
|
||||
/// <returns>An IActionResult containing the project report or an appropriate error response.</returns>
|
||||
public async Task<IActionResult> GetProjectReportAsync(Guid projectId, [FromQuery] DateTime? date)
|
||||
[HttpGet("{projectId}/report")]
|
||||
public async Task<IActionResult> GetProjectReportAsync(Guid projectId)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _reportHelper = scope.ServiceProvider.GetRequiredService<ReportHelper>();
|
||||
@ -150,17 +167,8 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
DateTime reportDate;
|
||||
if (date.HasValue)
|
||||
{
|
||||
reportDate = date.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportDate = DateTime.UtcNow.AddDays(-1).Date;
|
||||
}
|
||||
// Call the helper service, which is now available as a class member.
|
||||
var response = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId, reportDate);
|
||||
var response = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId);
|
||||
|
||||
// Check if the report data was found.
|
||||
if (response == null)
|
||||
|
||||
@ -96,75 +96,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Project Branch Functions ===================================================================
|
||||
|
||||
[HttpGet("branch/list/{projectId}")]
|
||||
public async Task<IActionResult> GetProjectBranchListByProject(Guid projectId, [FromQuery] string? searchString, [FromQuery] bool isActive = true,
|
||||
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.GetProjectBranchListByProjectAsync(projectId, isActive, searchString, pageNumber, pageSize, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("branch/details/{id}")]
|
||||
public async Task<IActionResult> GetProjectBranchDetails(Guid id)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.GetProjectBranchDetailsAsync(id, loggedInEmployee, tenantId);
|
||||
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("branch-type/list")]
|
||||
public async Task<IActionResult> GetBranchTypeList([FromQuery] string? searchString)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.GetBranchTypeListAsync(searchString, loggedInEmployee, tenantId);
|
||||
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPost("branch/create")]
|
||||
public async Task<IActionResult> CreateProjectBranch([FromBody] ProjectBranchDto model)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.CreateProjectBranchAsync(model, loggedInEmployee, tenantId);
|
||||
if (response.Success)
|
||||
{
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Project_Branch", Response = response.Data };
|
||||
await _signalR.SendNotificationAsync(notification);
|
||||
}
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPut("branch/edit/{id}")]
|
||||
public async Task<IActionResult> UpdateProjectBranch(Guid id, ProjectBranchDto model)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.UpdateProjectBranchAsync(id, model, loggedInEmployee, tenantId);
|
||||
if (response.Success)
|
||||
{
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Project_Branch", Response = response.Data };
|
||||
await _signalR.SendNotificationAsync(notification);
|
||||
}
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpDelete("branch/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteProjectBranch(Guid id, [FromQuery] bool isActive = false)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.DeleteProjectBranchAsync(id, isActive, loggedInEmployee, tenantId);
|
||||
if (response.Success)
|
||||
{
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Project_Branch", Response = response.Data };
|
||||
await _signalR.SendNotificationAsync(notification);
|
||||
}
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Service Project Allocation Functions ===================================================================
|
||||
|
||||
[HttpGet("get/allocation/list")]
|
||||
@ -196,11 +127,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
#region =================================================================== Job Tickets Functions ===================================================================
|
||||
|
||||
[HttpGet("job/list")]
|
||||
public async Task<IActionResult> GetJobTicketsList([FromQuery] Guid? projectId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20, [FromQuery] bool isActive = true,
|
||||
[FromQuery] bool isArchive = false)
|
||||
public async Task<IActionResult> GetJobTicketsList([FromQuery] Guid? projectId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20, [FromQuery] bool isActive = true)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _serviceProject.GetJobTicketsListAsync(projectId, pageNumber, pageSize, isActive, isArchive, loggedInEmployee, tenantId);
|
||||
var response = await _serviceProject.GetJobTicketsListAsync(projectId, pageNumber, pageSize, isActive, loggedInEmployee, tenantId);
|
||||
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
using Marco.Pms.Model.Filters;
|
||||
using System.Linq.Dynamic.Core;
|
||||
|
||||
namespace Marco.Pms.Services.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enterprise-grade extension methods for applying dynamic filters and sorting to IQueryable sources.
|
||||
/// </summary>
|
||||
public static class QueryableExtensions
|
||||
{
|
||||
public static IQueryable<T> ApplyCustomFilters<T>(this IQueryable<T> query, AdvanceFilter? filter, string defaultSortColumn)
|
||||
{
|
||||
// 1. Apply Advanced Filters (Arithmetic/Logic)
|
||||
if (filter?.AdvanceFilters != null && filter.AdvanceFilters.Any())
|
||||
{
|
||||
foreach (var advanceFilter in filter.AdvanceFilters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(advanceFilter.Column)) continue;
|
||||
|
||||
string op = advanceFilter.Opration.ToLower().Trim();
|
||||
string predicate = "";
|
||||
|
||||
// Map your custom strings to Dynamic LINQ operators
|
||||
switch (op)
|
||||
{
|
||||
case "greater than": predicate = $"{advanceFilter.Column} > @0"; break;
|
||||
case "less than": predicate = $"{advanceFilter.Column} < @0"; break;
|
||||
case "equal to": predicate = $"{advanceFilter.Column} == @0"; break;
|
||||
case "not equal": predicate = $"{advanceFilter.Column} != @0"; break;
|
||||
case "greater or equal": predicate = $"{advanceFilter.Column} >= @0"; break;
|
||||
case "smaller or equal": predicate = $"{advanceFilter.Column} <= @0"; break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(predicate))
|
||||
{
|
||||
// Dynamic LINQ handles type conversion (string "100" to int 100) automatically
|
||||
query = query.Where(predicate, advanceFilter.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Apply Sorting
|
||||
if (filter?.SortFilters != null && filter.SortFilters.Any())
|
||||
{
|
||||
// Build a comma-separated sort string: "Column1 desc, Column2 asc"
|
||||
var sortExpressions = new List<string>();
|
||||
foreach (var sort in filter.SortFilters)
|
||||
{
|
||||
string direction = sort.SortDescending ? "desc" : "asc";
|
||||
sortExpressions.Add($"{sort.Column} {direction}");
|
||||
}
|
||||
|
||||
query = query.OrderBy(string.Join(", ", sortExpressions));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default sorting
|
||||
query = query.OrderBy($"{defaultSortColumn} desc");
|
||||
}
|
||||
|
||||
|
||||
// 3.Apply GroupBy
|
||||
if (!string.IsNullOrEmpty(filter?.GroupByColumn))
|
||||
{
|
||||
query.GroupBy(filter.GroupByColumn, "it")
|
||||
.Select("new (Key, it as Items)"); // Reshape to { Key: "Value", Items: [...] }
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
public static IQueryable<T> ApplySearchFilters<T>(this IQueryable<T> query, List<SearchItem> searchFilters)
|
||||
{
|
||||
// 1. Apply Search Filters (Contains/Text search)
|
||||
if (searchFilters.Any())
|
||||
{
|
||||
foreach (var search in searchFilters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(search.Column) || string.IsNullOrWhiteSpace(search.Value)) continue;
|
||||
|
||||
// Generates: x.Column.Contains("Value")
|
||||
// Case insensitive logic can be handled here if needed
|
||||
query = query.Where($"{search.Column}.Contains(@0)", search.Value);
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
public static IQueryable<T> ApplyGroupByFilters<T>(this IQueryable<T> query, string groupByColumn)
|
||||
{
|
||||
query.GroupBy(groupByColumn, "it")
|
||||
.Select("new (Key, it as Items)"); // Reshape to { Key: "Value", Items: [...] }
|
||||
return query;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1162,23 +1162,35 @@ namespace Marco.Pms.Services.Helpers
|
||||
var processedByIds = model.Select(m => m.ProcessedById).ToList();
|
||||
var paidByIds = model.Select(m => m.PaidById).ToList();
|
||||
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
var projectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<ProjectBasicMongoDB>(p))
|
||||
.ToListAsync();
|
||||
return await dbContext.Projects.AsNoTracking().Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => projectIds.Contains(sp.Id) && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<ProjectBasicMongoDB>(sp))
|
||||
.ToListAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => paidByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => reviewedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => approvedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => processedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var expenseCategoryTask = Task.Run(async () =>
|
||||
{
|
||||
@ -1190,15 +1202,6 @@ namespace Marco.Pms.Services.Helpers
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.PaymentModeMatser.AsNoTracking().Where(pm => paymentModeIds.Contains(pm.Id)).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask, expenseCategoryTask, paymentModeTask);
|
||||
|
||||
var projects = infraProjectTask.Result;
|
||||
projects.AddRange(serviceProjectTask.Result);
|
||||
|
||||
var expenseCategories = expenseCategoryTask.Result;
|
||||
var paymentModes = paymentModeTask.Result;
|
||||
|
||||
var statusMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1215,43 +1218,6 @@ namespace Marco.Pms.Services.Helpers
|
||||
NextStatus = g.Select(s => s.NextStatus).OrderBy(s => s!.Name).ToList()
|
||||
}).ToListAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => paidByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusMappingTask, paidByTask, createdByTask);
|
||||
var statusMappings = statusMappingTask.Result;
|
||||
var paidBys = paidByTask.Result;
|
||||
var createdBys = createdByTask.Result;
|
||||
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => reviewedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => approvedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().Where(e => processedByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(reviewedByTask, approvedByTask, processedByTask);
|
||||
var reviewedBys = reviewedByTask.Result;
|
||||
var approvedBys = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
|
||||
var statusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1283,17 +1249,26 @@ namespace Marco.Pms.Services.Helpers
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask);
|
||||
await Task.WhenAll(projectTask, expenseCategoryTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||
processedByTask, statusTask, billAttachmentsTask);
|
||||
|
||||
var projects = projectTask.Result;
|
||||
var expenseCategories = expenseCategoryTask.Result;
|
||||
var paymentModes = paymentModeTask.Result;
|
||||
var statusMappings = statusMappingTask.Result;
|
||||
var paidBys = paidByTask.Result;
|
||||
var createdBys = createdByTask.Result;
|
||||
var reviewedBys = reviewedByTask.Result;
|
||||
var approvedBys = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
var billAttachments = billAttachmentsTask.Result;
|
||||
|
||||
expenseList = model.Select(m =>
|
||||
{
|
||||
var response = _mapper.Map<ExpenseDetailsMongoDB>(m);
|
||||
|
||||
response.Project = projects.Where(p => Guid.Parse(p.Id) == m.ProjectId).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
||||
response.Project = projects.Where(p => p.Id == m.ProjectId).Select(p => _mapper.Map<ProjectBasicMongoDB>(p)).FirstOrDefault() ?? new ProjectBasicMongoDB();
|
||||
response.PaidBy = paidBys.Where(p => p.Id == m.PaidById).Select(p => _mapper.Map<BasicEmployeeMongoDB>(p)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||
response.CreatedBy = createdBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault() ?? new BasicEmployeeMongoDB();
|
||||
response.ReviewedBy = reviewedBys.Where(e => e.Id == m.CreatedById).Select(e => _mapper.Map<BasicEmployeeMongoDB>(e)).FirstOrDefault();
|
||||
@ -1317,23 +1292,35 @@ namespace Marco.Pms.Services.Helpers
|
||||
}
|
||||
private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, Guid tenantId)
|
||||
{
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
var projectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Projects
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == model.ProjectId && p.TenantId == tenantId)
|
||||
.Select(p => _mapper.Map<ProjectBasicMongoDB>(p))
|
||||
.FirstOrDefaultAsync();
|
||||
return await dbContext.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
|
||||
});
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.ServiceProjects
|
||||
.AsNoTracking()
|
||||
.Where(sp => sp.Id == model.ProjectId && sp.TenantId == tenantId)
|
||||
.Select(sp => _mapper.Map<ProjectBasicMongoDB>(sp))
|
||||
.FirstOrDefaultAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.PaidById && e.TenantId == tenantId);
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.CreatedById && e.TenantId == tenantId);
|
||||
});
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ReviewedById && e.TenantId == tenantId);
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ApprovedById && e.TenantId == tenantId);
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ProcessedById && e.TenantId == tenantId);
|
||||
});
|
||||
var expenseCategoryTask = Task.Run(async () =>
|
||||
{
|
||||
@ -1345,12 +1332,6 @@ namespace Marco.Pms.Services.Helpers
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == model.PaymentModeId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(infraProjectTask, serviceProjectTask, expenseCategoryTask, paymentModeTask);
|
||||
var project = infraProjectTask.Result ?? serviceProjectTask.Result ?? new ProjectBasicMongoDB();
|
||||
var expenseCategory = expenseCategoryTask.Result;
|
||||
var paymentMode = paymentModeTask.Result;
|
||||
|
||||
var statusMappingTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1367,43 +1348,6 @@ namespace Marco.Pms.Services.Helpers
|
||||
NextStatus = g.Select(s => s.NextStatus).OrderBy(s => s!.Name).ToList()
|
||||
}).FirstOrDefaultAsync();
|
||||
});
|
||||
var paidByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.PaidById && e.TenantId == tenantId);
|
||||
});
|
||||
var createdByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.CreatedById && e.TenantId == tenantId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusMappingTask, paidByTask, createdByTask);
|
||||
var statusMapping = statusMappingTask.Result;
|
||||
var paidBy = paidByTask.Result;
|
||||
var createdBy = createdByTask.Result;
|
||||
|
||||
var reviewedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ReviewedById && e.TenantId == tenantId);
|
||||
});
|
||||
var approvedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ApprovedById && e.TenantId == tenantId);
|
||||
});
|
||||
var processedByTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.Employees.Include(e => e.JobRole).AsNoTracking().FirstOrDefaultAsync(e => e.Id == model.ProcessedById && e.TenantId == tenantId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(reviewedByTask, approvedByTask, processedByTask);
|
||||
var reviewedBy = reviewedByTask.Result;
|
||||
var approvedBy = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
|
||||
var statusTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -1434,13 +1378,25 @@ namespace Marco.Pms.Services.Helpers
|
||||
.FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(statusTask, billAttachmentsTask);
|
||||
// Await all prerequisite checks at once.
|
||||
await Task.WhenAll(projectTask, expenseCategoryTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, reviewedByTask, approvedByTask,
|
||||
processedByTask, statusTask, billAttachmentsTask);
|
||||
|
||||
var project = projectTask.Result;
|
||||
var expenseCategory = expenseCategoryTask.Result;
|
||||
var paymentMode = paymentModeTask.Result;
|
||||
var statusMapping = statusMappingTask.Result;
|
||||
var paidBy = paidByTask.Result;
|
||||
var createdBy = createdByTask.Result;
|
||||
var reviewedBy = reviewedByTask.Result;
|
||||
var approvedBy = approvedByTask.Result;
|
||||
var processedBy = processedByTask.Result;
|
||||
var billAttachment = billAttachmentsTask.Result;
|
||||
|
||||
|
||||
var response = _mapper.Map<ExpenseDetailsMongoDB>(model);
|
||||
|
||||
response.Project = project;
|
||||
response.Project = _mapper.Map<ProjectBasicMongoDB>(project);
|
||||
response.PaidBy = _mapper.Map<BasicEmployeeMongoDB>(paidBy);
|
||||
response.CreatedBy = _mapper.Map<BasicEmployeeMongoDB>(createdBy);
|
||||
response.ReviewedBy = _mapper.Map<BasicEmployeeMongoDB>(reviewedBy);
|
||||
|
||||
@ -25,9 +25,10 @@ namespace Marco.Pms.Services.Helpers
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<ProjectStatisticReport?> GetDailyProjectReportWithOutTenant(Guid projectId, DateTime reportDate)
|
||||
public async Task<ProjectStatisticReport?> GetDailyProjectReportWithOutTenant(Guid projectId)
|
||||
{
|
||||
// await _cache.GetBuildingAndFloorByWorkAreaId();
|
||||
DateTime reportDate = DateTime.UtcNow.AddDays(-1).Date;
|
||||
var project = await _cache.GetProjectDetailsWithBuildings(projectId);
|
||||
if (project == null)
|
||||
{
|
||||
|
||||
@ -162,10 +162,6 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
dest => dest.ProjectStatusId,
|
||||
opt => opt.MapFrom(src => Guid.Empty)
|
||||
);
|
||||
CreateMap<ProjectBasicMongoDB, BasicProjectVM>()
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => new Guid(src.Id)));
|
||||
|
||||
CreateMap<ProjectMongoDB, Project>()
|
||||
.ForMember(
|
||||
@ -201,17 +197,9 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
CreateMap<ServiceProject, BasicProjectVM>();
|
||||
CreateMap<ServiceProject, ServiceProjectVM>();
|
||||
CreateMap<ServiceProject, BasicServiceProjectVM>();
|
||||
CreateMap<ServiceProject, ProjectBasicMongoDB>();
|
||||
CreateMap<ServiceProject, ServiceProjectDetailsVM>();
|
||||
CreateMap<ServiceProjectAllocation, ServiceProjectAllocationVM>();
|
||||
|
||||
#region ======================================================= Project Branch =======================================================
|
||||
CreateMap<ProjectBranchDto, ProjectBranch>();
|
||||
CreateMap<ProjectBranch, ProjectBranchVM>();
|
||||
CreateMap<ProjectBranch, ProjectBranchDetailsVM>();
|
||||
CreateMap<ProjectBranch, BasicProjectBranchVM>();
|
||||
#endregion
|
||||
|
||||
//#region ======================================================= Talking Points =======================================================
|
||||
//CreateMap<TalkingPointDto, TalkingPoint>();
|
||||
//CreateMap<TalkingPoint, TalkingPointVM>();
|
||||
|
||||
@ -42,7 +42,6 @@
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.MongoDB" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -474,12 +474,6 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
return await _dbContext.ContactsEmails.Where(e => contactIds.Contains(e.ContactId)).ToListAsync();
|
||||
});
|
||||
await Task.WhenAll(contactTask, phoneTask, emailTask);
|
||||
|
||||
var contacts = contactTask.Result;
|
||||
var phones = phoneTask.Result;
|
||||
var emails = emailTask.Result;
|
||||
|
||||
var tagTask = Task.Run(async () =>
|
||||
{
|
||||
await using var _dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -499,8 +493,11 @@ namespace Marco.Pms.Services.Service
|
||||
return await _dbContext.ContactBucketMappings.Where(cb => contactIds.Contains(cb.ContactId)).ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(tagTask, contactProjectTask, contactBucketTask);
|
||||
await Task.WhenAll(contactTask);
|
||||
|
||||
var contacts = contactTask.Result;
|
||||
var phones = phoneTask.Result;
|
||||
var emails = emailTask.Result;
|
||||
var tags = tagTask.Result;
|
||||
var contactProjects = contactProjectTask.Result;
|
||||
var contactBuckets = contactBucketTask.Result;
|
||||
@ -590,13 +587,6 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogWarning("Employee with ID {LoggedInEmployeeId} tries to update contact with ID {ContactId} is not found in database", loggedInEmployeeId);
|
||||
return ApiResponse<object>.ErrorResponse("Contact not found", "Contact not found", 404);
|
||||
}
|
||||
|
||||
var projectIds = await dbContext.ContactProjectMappings
|
||||
.AsNoTracking()
|
||||
.Where(cp => cp.ContactId == contact.Id && cp.TenantId == tenantId)
|
||||
.Select(cp => cp.ProjectId)
|
||||
.ToListAsync();
|
||||
|
||||
ContactProfileVM contactVM = _mapper.Map<ContactProfileVM>(contact);
|
||||
|
||||
var phonesTask = Task.Run(async () =>
|
||||
@ -619,26 +609,21 @@ namespace Marco.Pms.Services.Service
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
var infraProjectTask = Task.Run(async () =>
|
||||
var contactProjectsTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).Select(p => _mapper.Map<BasicProjectVM>(p)).ToListAsync();
|
||||
});
|
||||
|
||||
var serviceProjectTask = Task.Run(async () =>
|
||||
await using var taskDbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await taskDbContext.ContactProjectMappings
|
||||
.AsNoTracking()
|
||||
.Include(cp => cp.Project)
|
||||
.Where(cp => cp.ContactId == contact.Id && cp.Project != null && cp.Project.TenantId == tenantId)
|
||||
.Select(cp => new BasicProjectVM
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.ServiceProjects.Where(sp => projectIds.Contains(sp.Id) && sp.TenantId == tenantId).Select(sp => _mapper.Map<BasicProjectVM>(sp)).ToListAsync();
|
||||
Id = cp.Project!.Id,
|
||||
Name = cp.Project.Name
|
||||
})
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(phonesTask, emailsTask, infraProjectTask, serviceProjectTask);
|
||||
|
||||
contactVM.ContactPhones = phonesTask.Result;
|
||||
contactVM.ContactEmails = emailsTask.Result;
|
||||
var projects = infraProjectTask.Result;
|
||||
projects.AddRange(serviceProjectTask.Result);
|
||||
contactVM.Projects = projects;
|
||||
|
||||
var contactBucketsTask = Task.Run(async () =>
|
||||
{
|
||||
await using var taskDbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
@ -694,10 +679,12 @@ namespace Marco.Pms.Services.Service
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(contactBucketsTask, contactTagsTask, contactNotesTask);
|
||||
|
||||
await Task.WhenAll(phonesTask, emailsTask, contactProjectsTask, contactBucketsTask, contactTagsTask, contactNotesTask);
|
||||
contactVM.ContactPhones = phonesTask.Result;
|
||||
contactVM.ContactEmails = emailsTask.Result;
|
||||
contactVM.Tags = contactTagsTask.Result;
|
||||
contactVM.Buckets = contactBucketsTask.Result;
|
||||
contactVM.Projects = contactProjectsTask.Result;
|
||||
contactVM.Notes = contactNotesTask.Result;
|
||||
_logger.LogInfo("Employee ID {EmployeeId} fetched profile of contact {ContactId}", loggedInEmployeeId, contact.Id);
|
||||
return ApiResponse<object>.SuccessResponse(contactVM, "Contact profile fetched successfully");
|
||||
@ -3302,18 +3289,11 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
if (!(dto.ProjectIds?.Any() ?? false)) return new List<ContactProjectMapping>();
|
||||
|
||||
var infraProjectIds = await _context.Projects
|
||||
var validProjectIds = await _context.Projects
|
||||
.Where(p => dto.ProjectIds.Contains(p.Id) && p.TenantId == tenantId)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var serviceProjectIds = await _context.ServiceProjects
|
||||
.Where(sp => dto.ProjectIds.Contains(sp.Id) && sp.IsActive && sp.TenantId == tenantId)
|
||||
.Select(sp => sp.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var validProjectIds = infraProjectIds.Concat(serviceProjectIds).Distinct().Where(p => p != Guid.Empty).ToList();
|
||||
|
||||
var mappings = validProjectIds.Select(projectId => new ContactProjectMapping
|
||||
{
|
||||
ProjectId = projectId,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,6 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly ILoggingService _logger;
|
||||
|
||||
private static readonly Guid Review = Guid.Parse("6537018f-f4e9-4cb3-a210-6c3b2da999d7");
|
||||
private static readonly Guid RejectedByReviewer = Guid.Parse("965eda62-7907-4963-b4a1-657fb0b2724b");
|
||||
@ -26,12 +25,10 @@ namespace Marco.Pms.Services.Service
|
||||
private static readonly Guid Processed = Guid.Parse("61578360-3a49-4c34-8604-7b35a3787b95");
|
||||
|
||||
public FirebaseService(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
ILoggingService logger)
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
// Auth Controller
|
||||
@ -127,27 +124,14 @@ namespace Marco.Pms.Services.Service
|
||||
await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data);
|
||||
}
|
||||
|
||||
#region =================================================================== Attendance Functions ===================================================================
|
||||
|
||||
/// <summary>
|
||||
/// Sends attendance-related notifications for the specified project and employee attendance action.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The Id of the project where attendance is marked.</param>
|
||||
/// <param name="name">Name of the employee marking attendance.</param>
|
||||
/// <param name="markType">Type of attendance mark action.</param>
|
||||
/// <param name="employeeId">Employee for whom attendance is marked.</param>
|
||||
/// <param name="origin">Origin of the request (optional), used to filter notifications.</param>
|
||||
/// <param name="loggedInEmployeeId">Employee Id of the caller (to exclude self-notifications).</param>
|
||||
/// <param name="tenantId">Tenant identifier for multi-tenant setup.</param>
|
||||
/// <returns>Task representing the asynchronous operation.</returns>
|
||||
public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, string origin, Guid loggedInEmployeeId, Guid tenantId)
|
||||
// Attendance Controller
|
||||
public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
var projectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
// Fetch project details and assigned employees grouped by ProjectId
|
||||
var project = await dbContext.ProjectAllocations
|
||||
return await dbContext.ProjectAllocations
|
||||
.Include(pa => pa.Project)
|
||||
.Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null)
|
||||
.GroupBy(pa => pa.ProjectId)
|
||||
@ -155,146 +139,156 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(),
|
||||
EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList()
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
}).FirstOrDefaultAsync();
|
||||
});
|
||||
|
||||
if (project == null)
|
||||
var teamAttendanceRoleTask = Task.Run(async () =>
|
||||
{
|
||||
_logger.LogWarning("No active project allocations found for ProjectId: {ProjectId}", projectId);
|
||||
return; // or throw if critical
|
||||
}
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.RolePermissionMappings
|
||||
.Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance)
|
||||
.Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
});
|
||||
var regularizeAttendanceRoleTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.RolePermissionMappings
|
||||
.Where(rp => rp.FeaturePermissionId == PermissionsMaster.RegularizeAttendance)
|
||||
.Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
});
|
||||
var manageProjectsRoleTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.RolePermissionMappings
|
||||
.Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject)
|
||||
.Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
});
|
||||
|
||||
var projectAssignedEmployeeIds = project.EmployeeIds;
|
||||
await Task.WhenAll(projectTask, teamAttendanceRoleTask, manageProjectsRoleTask, regularizeAttendanceRoleTask);
|
||||
|
||||
// Load required role IDs in parallel for efficiency
|
||||
var teamAttendanceRoleIdsTask = dbContext.RolePermissionMappings.Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance).Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
var regularizeAttendanceRoleIdsTask = dbContext.RolePermissionMappings.Where(rp => rp.FeaturePermissionId == PermissionsMaster.RegularizeAttendance).Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
var manageProjectsRoleIdsTask = dbContext.RolePermissionMappings.Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject).Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||
var teamAttendanceRoleIds = teamAttendanceRoleTask.Result;
|
||||
var regularizeAttendanceRoleIds = regularizeAttendanceRoleTask.Result;
|
||||
var manageProjectsRoleIds = manageProjectsRoleTask.Result;
|
||||
var project = projectTask.Result;
|
||||
|
||||
await Task.WhenAll(teamAttendanceRoleIdsTask, regularizeAttendanceRoleIdsTask, manageProjectsRoleIdsTask);
|
||||
List<Guid> projectAssignedEmployeeIds = project?.EmployeeIds ?? new List<Guid>();
|
||||
|
||||
var teamAttendanceRoleIds = teamAttendanceRoleIdsTask.Result;
|
||||
var regularizeAttendanceRoleIds = regularizeAttendanceRoleIdsTask.Result;
|
||||
var manageProjectsRoleIds = manageProjectsRoleIdsTask.Result;
|
||||
|
||||
// Fetch employees eligible for attendance notifications
|
||||
var employeeIds = await dbContext.EmployeeRoleMappings
|
||||
.Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId))
|
||||
&& teamAttendanceRoleIds.Contains(er.RoleId))
|
||||
var employeeIdsTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.EmployeeRoleMappings
|
||||
.Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && teamAttendanceRoleIds.Contains(er.RoleId))
|
||||
.Select(er => er.EmployeeId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// Fetch team employees (for data-only notifications)
|
||||
var teamEmployeeIds = await dbContext.EmployeeRoleMappings
|
||||
});
|
||||
var teamEmployeeIdsTask = Task.Run(async () =>
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await dbContext.EmployeeRoleMappings
|
||||
.Where(er => projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId))
|
||||
.Select(er => er.EmployeeId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
List<Guid> messageNotificationIds = new();
|
||||
await Task.WhenAll(employeeIdsTask, teamEmployeeIdsTask);
|
||||
|
||||
// Construct notification content based on attendance mark type
|
||||
Notification notificationFirebase = markType switch
|
||||
var employeeIds = employeeIdsTask.Result;
|
||||
var teamEmployeeIds = teamEmployeeIdsTask.Result;
|
||||
|
||||
var mesaageNotificationIds = new List<Guid>();
|
||||
|
||||
Notification notificationFirebase;
|
||||
switch (markType)
|
||||
{
|
||||
ATTENDANCE_MARK_TYPE.CHECK_IN => new Notification
|
||||
case ATTENDANCE_MARK_TYPE.CHECK_IN:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Attendance Update",
|
||||
Body = $"{name} has checked in for project {project.ProjectName}."
|
||||
},
|
||||
ATTENDANCE_MARK_TYPE.CHECK_OUT => new Notification
|
||||
Body = $" {name} has checked in for project {project?.ProjectName ?? ""}."
|
||||
};
|
||||
mesaageNotificationIds.AddRange(employeeIds);
|
||||
break;
|
||||
case ATTENDANCE_MARK_TYPE.CHECK_OUT:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Attendance Update",
|
||||
Body = $"{name} has checked out for project {project.ProjectName}."
|
||||
},
|
||||
ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE => new Notification
|
||||
Body = $" {name} has checked out for project {project?.ProjectName ?? ""}."
|
||||
};
|
||||
mesaageNotificationIds.AddRange(employeeIds);
|
||||
break;
|
||||
case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Regularization Request",
|
||||
Body = $"{name} has submitted a regularization request for project {project.ProjectName}."
|
||||
},
|
||||
ATTENDANCE_MARK_TYPE.REGULARIZE => new Notification
|
||||
Body = $" {name} has submitted a regularization request for project {project?.ProjectName ?? ""}."
|
||||
};
|
||||
mesaageNotificationIds = await _context.EmployeeRoleMappings
|
||||
.Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && regularizeAttendanceRoleIds.Contains(er.RoleId))
|
||||
.Select(er => er.EmployeeId)
|
||||
.ToListAsync();
|
||||
break;
|
||||
case ATTENDANCE_MARK_TYPE.REGULARIZE:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Regularization Approved",
|
||||
Body = $"{name}'s regularization request for project {project.ProjectName} has been accepted."
|
||||
},
|
||||
ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT => new Notification
|
||||
Title = " Regularization Approved",
|
||||
Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been accepted."
|
||||
};
|
||||
mesaageNotificationIds.Add(employeeId);
|
||||
break;
|
||||
case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Regularization Denied",
|
||||
Body = $"{name}'s regularization request for project {project.ProjectName} has been rejected."
|
||||
},
|
||||
_ => new Notification
|
||||
Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been rejected."
|
||||
};
|
||||
mesaageNotificationIds.Add(employeeId);
|
||||
break;
|
||||
default:
|
||||
notificationFirebase = new Notification
|
||||
{
|
||||
Title = "Attendance Update",
|
||||
Body = $"{name} has updated attendance for project {project.ProjectName}."
|
||||
}
|
||||
Body = $" {name} has update his/her attendance for project {project?.ProjectName ?? ""}."
|
||||
};
|
||||
|
||||
// Set notification recipients based on type
|
||||
if (markType == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
|
||||
{
|
||||
messageNotificationIds = await dbContext.EmployeeRoleMappings
|
||||
.Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId))
|
||||
&& regularizeAttendanceRoleIds.Contains(er.RoleId))
|
||||
.Select(er => er.EmployeeId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
}
|
||||
else if (markType == ATTENDANCE_MARK_TYPE.REGULARIZE || markType == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT)
|
||||
{
|
||||
messageNotificationIds.Add(employeeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageNotificationIds.AddRange(employeeIds);
|
||||
break;
|
||||
}
|
||||
|
||||
var dataPayload = new Dictionary<string, string>
|
||||
// List of device registration tokens to send the message to
|
||||
|
||||
var data = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Keyword", "Attendance" },
|
||||
{ "ProjectId", projectId.ToString() },
|
||||
{ "Action", markType.ToString() }
|
||||
};
|
||||
|
||||
// Filter out current (logged-in) employee from notifications if origin is not provided (optional)
|
||||
if (string.IsNullOrWhiteSpace(origin))
|
||||
var registrationTokensForNotificationTask = Task.Run(async () =>
|
||||
{
|
||||
messageNotificationIds = messageNotificationIds.Where(e => e != Guid.Empty && e != loggedInEmployeeId).Distinct().ToList();
|
||||
teamEmployeeIds = teamEmployeeIds.Where(e => e != Guid.Empty && e != loggedInEmployeeId).Distinct().ToList();
|
||||
}
|
||||
|
||||
// Fetch FCM tokens for notification recipients
|
||||
var registrationTokensForNotification = messageNotificationIds.Count > 0
|
||||
? await dbContext.FCMTokenMappings.Where(ft => messageNotificationIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow && ft.TenantId == tenantId).Select(ft => ft.FcmToken).ToListAsync()
|
||||
: new List<string>();
|
||||
|
||||
// Fetch FCM tokens for data-only notification recipients
|
||||
var registrationTokensForData = teamEmployeeIds.Count > 0
|
||||
? await dbContext.FCMTokenMappings.Where(ft => teamEmployeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow && ft.TenantId == tenantId).Select(ft => ft.FcmToken).ToListAsync()
|
||||
: new List<string>();
|
||||
|
||||
// Send notifications concurrently
|
||||
var sendNotificationTask = registrationTokensForNotification.Count > 0
|
||||
? SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, dataPayload)
|
||||
: Task.CompletedTask;
|
||||
|
||||
var sendDataOnlyTask = registrationTokensForData.Count > 0
|
||||
? SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, dataPayload)
|
||||
: Task.CompletedTask;
|
||||
|
||||
await Task.WhenAll(sendNotificationTask, sendDataOnlyTask);
|
||||
|
||||
_logger.LogInfo("Attendance message sent successfully for ProjectId {ProjectId}, MarkType {MarkType}, EmployeeId {EmployeeId}, Origin {Origin}",
|
||||
projectId, markType, employeeId, origin);
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (mesaageNotificationIds.Any())
|
||||
{
|
||||
_logger.LogError(ex, "Error sending attendance message for ProjectId {ProjectId}, MarkType {MarkType}, EmployeeId {EmployeeId}, Origin {Origin}",
|
||||
projectId, markType, employeeId, origin);
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
var registrationTokensForNotification = await dbContext.FCMTokenMappings
|
||||
.Where(ft => mesaageNotificationIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow && ft.TenantId == tenantId)
|
||||
.Select(ft => ft.FcmToken).ToListAsync();
|
||||
|
||||
await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data);
|
||||
}
|
||||
});
|
||||
var registrationTokensForDataTask = Task.Run(async () =>
|
||||
{
|
||||
if (teamEmployeeIds.Any())
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
var registrationTokensForData = await dbContext.FCMTokenMappings
|
||||
.Where(ft => teamEmployeeIds.Contains(ft.EmployeeId) && ft.ExpiredAt >= DateTime.UtcNow && ft.TenantId == tenantId)
|
||||
.Select(ft => ft.FcmToken).ToListAsync();
|
||||
|
||||
await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data);
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
// Task Controller
|
||||
public async Task SendAssignTaskMessageAsync(Guid workItemId, string name, List<Guid> teamMembers, Guid tenantId)
|
||||
{
|
||||
|
||||
@ -3,7 +3,6 @@ using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Marco.Pms.Services.Service
|
||||
@ -13,18 +12,49 @@ namespace Marco.Pms.Services.Service
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly RolesHelper _rolesHelper;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly Guid tenantId;
|
||||
|
||||
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, ILoggingService logger, UserHelper userHelper)
|
||||
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, UserHelper userHelper)
|
||||
{
|
||||
_context = context;
|
||||
_rolesHelper = rolesHelper;
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
//public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
|
||||
//{
|
||||
// var featurePermissionIds = await _cache.GetPermissions(employeeId);
|
||||
// if (featurePermissionIds == null)
|
||||
// {
|
||||
// List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId);
|
||||
// featurePermissionIds = featurePermission.Select(fp => fp.Id).ToList();
|
||||
// }
|
||||
// if (projectId != null)
|
||||
// {
|
||||
// var projectLevelPerissionIds = await _context.ProjectLevelPermissionMappings
|
||||
// .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId).Select(pl => pl.PermissionId).ToListAsync();
|
||||
|
||||
// var projectLevelModuleIds = new HashSet<Guid>
|
||||
// {
|
||||
// Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"),
|
||||
// Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"),
|
||||
// Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"),
|
||||
// Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
|
||||
// Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462")
|
||||
// };
|
||||
|
||||
// var allProjectLevelPermissionIds = await _context.FeaturePermissions
|
||||
// .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && !projectLevelPerissionIds.Contains(fp.Id)).Select(fp => fp.Id).ToListAsync();
|
||||
// featurePermissionIds.RemoveRange(allProjectLevelPermissionIds);
|
||||
|
||||
// featurePermissionIds.AddRange(projectLevelPerissionIds);
|
||||
// featurePermissionIds = featurePermissionIds.Distinct().ToList();
|
||||
// }
|
||||
// var hasPermission = featurePermissionIds.Contains(featurePermissionId);
|
||||
// return hasPermission;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an employee has a specific feature permission, optionally within a project context.
|
||||
/// </summary>
|
||||
@ -126,78 +156,5 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
return projectIds.Contains(projectId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if an employee has permission to access a specific service project.
|
||||
/// Permission is granted if the user is directly allocated to the project OR
|
||||
/// assigned to any active job ticket within the project.
|
||||
/// </summary>
|
||||
/// <param name="loggedInEmployeeId">The ID of the user requesting access.</param>
|
||||
/// <param name="projectId">The ID of the project to access.</param>
|
||||
/// <returns>True if access is allowed, otherwise False.</returns>
|
||||
public async Task<bool> HasServiceProjectPermission(Guid loggedInEmployeeId, Guid projectId)
|
||||
{
|
||||
Guid ReviewDoneStatus = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7");
|
||||
Guid ClosedStatus = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69");
|
||||
// 1. Input Validation
|
||||
if (loggedInEmployeeId == Guid.Empty || projectId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("Permission check failed: Invalid input parameters. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployeeId, projectId);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInfo("Starting permission check for Employee: {EmployeeId} on Project: {ProjectId}", loggedInEmployeeId, projectId);
|
||||
|
||||
// 2. Check Level 1: Is the user a generic Team Member of the project?
|
||||
// This is usually the most common case, so checking this first saves complex query execution.
|
||||
bool isTeamMember = await _context.ServiceProjectAllocations
|
||||
.AsNoTracking() // Optimization: Read-only query does not need tracking
|
||||
.AnyAsync(spa => spa.ProjectId == projectId
|
||||
&& spa.EmployeeId == loggedInEmployeeId
|
||||
&& spa.IsActive
|
||||
&& spa.TenantId == tenantId);
|
||||
|
||||
if (isTeamMember)
|
||||
{
|
||||
_logger.LogInfo("Access Granted: User {EmployeeId} is a team member of Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Check Level 2: Is the user assigned to any ACTIVE specific Job Ticket?
|
||||
// Optimization: Combined the check for JobTicket and Mapping into a single Join query.
|
||||
// This prevents pulling a list of JobIds into memory (fixing memory bloat) and reduces DB roundtrips.
|
||||
bool hasActiveJobAssignment = await _context.JobTickets
|
||||
.AsNoTracking()
|
||||
.Where(jt => jt.ProjectId == projectId
|
||||
&& jt.StatusId != ReviewDoneStatus
|
||||
&& jt.StatusId != ClosedStatus
|
||||
&& jt.IsActive)
|
||||
.Join(_context.JobEmployeeMappings,
|
||||
ticket => ticket.Id,
|
||||
mapping => mapping.JobTicketId,
|
||||
(ticket, mapping) => mapping)
|
||||
.AnyAsync(mapping => mapping.AssigneeId == loggedInEmployeeId
|
||||
&& mapping.TenantId == tenantId);
|
||||
|
||||
if (hasActiveJobAssignment)
|
||||
{
|
||||
_logger.LogInfo("Access Granted: User {EmployeeId} is assigned active tickets in Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Default Deny
|
||||
_logger.LogWarning("Access Denied: User {EmployeeId} has no permissions for Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 5. Robust Error Handling
|
||||
// Log the full stack trace for debugging, but return false to maintain security (fail-closed).
|
||||
_logger.LogError(ex, "An error occurred while checking permissions for Employee: {EmployeeId} on Project: {ProjectId}", loggedInEmployeeId, projectId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,12 +220,6 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogInfo("Cache HIT. All {ProjectCount} projects found in cache.", projectIds.Count);
|
||||
}
|
||||
|
||||
if (pageNumber <= 0 || pageSize <= 0)
|
||||
{
|
||||
_logger.LogInfo("Successfully retrieved a total of {ProjectCount} projects.", responseVms.Count);
|
||||
return ApiResponse<object>.SuccessResponse(responseVms, "Projects retrieved successfully.", 200);
|
||||
}
|
||||
|
||||
var totalEntites = responseVms.Count;
|
||||
var totalPages = (int)Math.Ceiling((double)totalEntites / pageSize);
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
|
||||
Task SendEmployeeSuspendMessageAsync(Guid employeeId, Guid tenantId);
|
||||
|
||||
Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, string origin, Guid loggedInEmployeeId, Guid tenantId);
|
||||
Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId);
|
||||
Task SendAssignTaskMessageAsync(Guid workItemId, string name, List<Guid> teamMembers, Guid tenantId);
|
||||
Task SendReportTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId);
|
||||
Task SendTaskCommentMessageAsync(Guid taskAllocationId, string name, Guid tenantId);
|
||||
|
||||
@ -15,15 +15,6 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
Task<ApiResponse<object>> DeActivateServiceProjectAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Project Branch Functions ===================================================================
|
||||
Task<ApiResponse<object>> GetProjectBranchListByProjectAsync(Guid projectId, bool isActive, string? searchString, int pageNumber, int pageSize, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetProjectBranchDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetBranchTypeListAsync(string? searchString, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> CreateProjectBranchAsync(ProjectBranchDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> UpdateProjectBranchAsync(Guid id, ProjectBranchDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> DeleteProjectBranchAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Service Project Allocation Functions ===================================================================
|
||||
Task<ApiResponse<object>> GetServiceProjectAllocationListAsync(Guid? projectId, Guid? employeeId, bool isActive, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> ManageServiceProjectAllocationAsync(List<ServiceProjectAllocationDto> model, Employee loggedInEmployee, Guid tenantId);
|
||||
@ -34,7 +25,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Job Tickets Functions ===================================================================
|
||||
Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, bool isArchive, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetJobTicketDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> GetJobTagListAsync(Employee loggedInEmployee, Guid tenantId);
|
||||
Task<ApiResponse<object>> CreateJobTicketAsync(CreateJobTicketDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user