Compare commits
No commits in common. "main" and "Ashutosh_Task#1225" have entirely different histories.
main
...
Ashutosh_T
@ -112,8 +112,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
public DbSet<StatusPermissionMapping> StatusPermissionMapping { get; set; }
|
||||
public DbSet<ExpensesStatusMapping> ExpensesStatusMapping { get; set; }
|
||||
|
||||
public DbSet<FCMTokenMapping> FCMTokenMappings { get; set; }
|
||||
|
||||
public DbSet<EntityTypeMaster> EntityTypeMasters { get; set; }
|
||||
public DbSet<DocumentTypeMaster> DocumentTypeMasters { get; set; }
|
||||
public DbSet<DocumentCategoryMaster> DocumentCategoryMasters { get; set; }
|
||||
@ -665,7 +663,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Materials, equipment and supplies purchased for site operations.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -675,7 +672,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Vehicle fuel, logistics services and delivery of goods or personnel.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = false,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -685,7 +681,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Delivery of personnel.",
|
||||
NoOfPersonsRequired = true,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = false,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -695,7 +690,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Site setup costs including equipment deployment and temporary infrastructure.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -705,7 +699,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.",
|
||||
NoOfPersonsRequired = true,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -715,7 +708,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Machinery servicing, electricity, water, and temporary office needs.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -725,7 +717,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Scheduled payments for external services or goods.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
},
|
||||
new ExpensesTypeMaster
|
||||
@ -735,7 +726,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Description = "Government fees, insurance, inspections and safety-related expenditures.",
|
||||
NoOfPersonsRequired = false,
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
}
|
||||
);
|
||||
@ -993,6 +983,11 @@ namespace Marco.Pms.DataAccess.Data
|
||||
);
|
||||
|
||||
modelBuilder.Entity<OrgTypeMaster>().HasData(
|
||||
new OrgTypeMaster
|
||||
{
|
||||
Id = Guid.Parse("743806fe-d991-4079-b223-e4e2da44f435"),
|
||||
Name = "Tenant"
|
||||
},
|
||||
new OrgTypeMaster
|
||||
{
|
||||
Id = Guid.Parse("5ee49bcd-b6d3-482f-9aaf-484afe04abec"),
|
||||
@ -1002,6 +997,11 @@ namespace Marco.Pms.DataAccess.Data
|
||||
{
|
||||
Id = Guid.Parse("a283356a-9b02-4029-afb7-e65c703efdd4"),
|
||||
Name = "Sub-Contractor"
|
||||
},
|
||||
new OrgTypeMaster
|
||||
{
|
||||
Id = Guid.Parse("b1877a3b-8832-47b1-bbe3-dc7e98672f49"),
|
||||
Name = "PMC"
|
||||
}
|
||||
);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_FCMTokenMApping_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "FCMTokenMappings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
EmployeeId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
FcmToken = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_FCMTokenMappings", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_FCMTokenMappings_Tenants_TenantId",
|
||||
column: x => x.TenantId,
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FCMTokenMappings_TenantId",
|
||||
table: "FCMTokenMappings",
|
||||
column: "TenantId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "FCMTokenMappings");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Expriy_Date_In_FCMMapping_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ExpiredAt",
|
||||
table: "FCMTokenMappings",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ExpiredAt",
|
||||
table: "FCMTokenMappings");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,268 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_IsAttachmentRequried_Parameter_In_ExpensesTypeMaster_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsAttachmentRequried",
|
||||
table: "ExpensesTypeMaster",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 12, 7, 6, 13, 429, DateTimeKind.Utc).AddTicks(3323));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 12, 7, 6, 13, 429, DateTimeKind.Utc).AddTicks(3316));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("07ca7182-9ac0-4407-b988-59901170cb86"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("260abd7e-c96d-4ae4-a29b-9b5bb5d24ebd"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5668de00-5d84-47f7-b9b5-7fefd1219f05"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("7cc41c91-23cb-442b-badd-f932138d149f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("846e89a9-5735-45ec-a21d-c97f85a94ada"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("a1a190ba-c4a8-432f-b26d-1231ca1d44bc"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: false);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("77013784-9324-4d8b-bd36-d6f928e68942"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: false);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "ExpensesTypeMaster",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"),
|
||||
column: "IsAttachmentRequried",
|
||||
value: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsAttachmentRequried",
|
||||
table: "ExpensesTypeMaster");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6233));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6226));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("07ca7182-9ac0-4407-b988-59901170cb86"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6307));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6290));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("260abd7e-c96d-4ae4-a29b-9b5bb5d24ebd"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6298));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6286));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6275));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5668de00-5d84-47f7-b9b5-7fefd1219f05"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6319));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6282));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("7cc41c91-23cb-442b-badd-f932138d149f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6314));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("846e89a9-5735-45ec-a21d-c97f85a94ada"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6311));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("a1a190ba-c4a8-432f-b26d-1231ca1d44bc"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6302));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 9, 3, 10, 46, 49, 955, DateTimeKind.Utc).AddTicks(6295));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,85 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_TenantId_From_MPIN_And_OTP : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MPINDetails_Tenants_TenantId",
|
||||
table: "MPINDetails");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_OTPDetails_Tenants_TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_OTPDetails_TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MPINDetails_TenantId",
|
||||
table: "MPINDetails");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TenantId",
|
||||
table: "MPINDetails");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "TenantId",
|
||||
table: "OTPDetails",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "TenantId",
|
||||
table: "MPINDetails",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OTPDetails_TenantId",
|
||||
table: "OTPDetails",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MPINDetails_TenantId",
|
||||
table: "MPINDetails",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MPINDetails_Tenants_TenantId",
|
||||
table: "MPINDetails",
|
||||
column: "TenantId",
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_OTPDetails_Tenants_TenantId",
|
||||
table: "OTPDetails",
|
||||
column: "TenantId",
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Service_FK_In_ProjectAllocation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "ServiceId",
|
||||
table: "ProjectAllocations",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "logoImage",
|
||||
table: "Organizations",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "Organizations",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("4e3a6d31-c640-40f7-8d67-6c109fcdb9ea"),
|
||||
column: "logoImage",
|
||||
value: null);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectAllocations_ServiceId",
|
||||
table: "ProjectAllocations",
|
||||
column: "ServiceId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ProjectAllocations_ServiceMasters_ServiceId",
|
||||
table: "ProjectAllocations",
|
||||
column: "ServiceId",
|
||||
principalTable: "ServiceMasters",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ProjectAllocations_ServiceMasters_ServiceId",
|
||||
table: "ProjectAllocations");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProjectAllocations_ServiceId",
|
||||
table: "ProjectAllocations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServiceId",
|
||||
table: "ProjectAllocations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logoImage",
|
||||
table: "Organizations");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,84 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Assigned_By_In_Mapping_Tables : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "AssignedById",
|
||||
table: "TenantOrgMappings",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("08dd8b35-d98b-44f1-896d-12aec3f035aa"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "AssignedById",
|
||||
table: "ProjectOrgMappings",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("08dd8b35-d98b-44f1-896d-12aec3f035aa"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TenantOrgMappings_AssignedById",
|
||||
table: "TenantOrgMappings",
|
||||
column: "AssignedById");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectOrgMappings_AssignedById",
|
||||
table: "ProjectOrgMappings",
|
||||
column: "AssignedById");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ProjectOrgMappings_Employees_AssignedById",
|
||||
table: "ProjectOrgMappings",
|
||||
column: "AssignedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_TenantOrgMappings_Employees_AssignedById",
|
||||
table: "TenantOrgMappings",
|
||||
column: "AssignedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ProjectOrgMappings_Employees_AssignedById",
|
||||
table: "ProjectOrgMappings");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_TenantOrgMappings_Employees_AssignedById",
|
||||
table: "TenantOrgMappings");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_TenantOrgMappings_AssignedById",
|
||||
table: "TenantOrgMappings");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProjectOrgMappings_AssignedById",
|
||||
table: "ProjectOrgMappings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AssignedById",
|
||||
table: "TenantOrgMappings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AssignedById",
|
||||
table: "ProjectOrgMappings");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Deleted_Organization_Types : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "OrgTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("743806fe-d991-4079-b223-e4e2da44f435"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "OrgTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("b1877a3b-8832-47b1-bbe3-dc7e98672f49"));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.InsertData(
|
||||
table: "OrgTypeMasters",
|
||||
columns: new[] { "Id", "Name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("743806fe-d991-4079-b223-e4e2da44f435"), "Tenant" },
|
||||
{ new Guid("b1877a3b-8832-47b1-bbe3-dc7e98672f49"), "PMC" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,92 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Forgin_Key_For_Approver : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Attendes_Employees_EmployeeID",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "EmployeeID",
|
||||
table: "Attendes",
|
||||
newName: "EmployeeId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ApprovedBy",
|
||||
table: "Attendes",
|
||||
newName: "ApprovedById");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Attendes_EmployeeID",
|
||||
table: "Attendes",
|
||||
newName: "IX_Attendes_EmployeeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Attendes_ApprovedById",
|
||||
table: "Attendes",
|
||||
column: "ApprovedById");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Attendes_Employees_ApprovedById",
|
||||
table: "Attendes",
|
||||
column: "ApprovedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Attendes_Employees_EmployeeId",
|
||||
table: "Attendes",
|
||||
column: "EmployeeId",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Attendes_Employees_ApprovedById",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Attendes_Employees_EmployeeId",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Attendes_ApprovedById",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "EmployeeId",
|
||||
table: "Attendes",
|
||||
newName: "EmployeeID");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ApprovedById",
|
||||
table: "Attendes",
|
||||
newName: "ApprovedBy");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_Attendes_EmployeeId",
|
||||
table: "Attendes",
|
||||
newName: "IX_Attendes_EmployeeID");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Attendes_Employees_EmployeeID",
|
||||
table: "Attendes",
|
||||
column: "EmployeeID",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
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_ExpenceUID_In_Expense_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ExpenseUId",
|
||||
table: "Expenses",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ExpenseUId",
|
||||
table: "Expenses");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Requested_In_Attendance_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ApprovedAt",
|
||||
table: "Attendes",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "RequestedAt",
|
||||
table: "Attendes",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "RequestedById",
|
||||
table: "Attendes",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Attendes_RequestedById",
|
||||
table: "Attendes",
|
||||
column: "RequestedById");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Attendes_Employees_RequestedById",
|
||||
table: "Attendes",
|
||||
column: "RequestedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Attendes_Employees_RequestedById",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Attendes_RequestedById",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ApprovedAt",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequestedAt",
|
||||
table: "Attendes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequestedById",
|
||||
table: "Attendes");
|
||||
}
|
||||
}
|
||||
}
|
@ -172,10 +172,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<int>("Activity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("ApprovedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("ApprovedById")
|
||||
b.Property<Guid?>("ApprovedBy")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("AttendanceDate")
|
||||
@ -188,7 +185,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid>("EmployeeId")
|
||||
b.Property<Guid>("EmployeeID")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime?>("InTime")
|
||||
@ -203,22 +200,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid>("ProjectID")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime?>("RequestedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("RequestedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApprovedById");
|
||||
|
||||
b.HasIndex("EmployeeId");
|
||||
|
||||
b.HasIndex("RequestedById");
|
||||
b.HasIndex("EmployeeID");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
@ -297,6 +284,9 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("TimeStamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -305,6 +295,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("MPINDetails");
|
||||
});
|
||||
|
||||
@ -324,6 +316,9 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("TimeStamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -332,6 +327,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("OTPDetails");
|
||||
});
|
||||
|
||||
@ -1843,10 +1840,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ExpenseUId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("ExpensesTypeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@ -2685,9 +2678,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsAttachmentRequried")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@ -2710,7 +2700,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"),
|
||||
Description = "Materials, equipment and supplies purchased for site operations.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Procurement",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2720,7 +2709,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"),
|
||||
Description = "Vehicle fuel, logistics services and delivery of goods or personnel.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = false,
|
||||
Name = "Transport",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2730,7 +2718,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"),
|
||||
Description = "Delivery of personnel.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = false,
|
||||
Name = "Travelling",
|
||||
NoOfPersonsRequired = true,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2740,7 +2727,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"),
|
||||
Description = "Site setup costs including equipment deployment and temporary infrastructure.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Mobilization",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2750,7 +2736,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"),
|
||||
Description = " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Employee Welfare",
|
||||
NoOfPersonsRequired = true,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2760,7 +2745,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("77013784-9324-4d8b-bd36-d6f928e68942"),
|
||||
Description = "Machinery servicing, electricity, water, and temporary office needs.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Maintenance & Utilities",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2770,7 +2754,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"),
|
||||
Description = "Scheduled payments for external services or goods.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Vendor/Supplier Payments",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -2780,7 +2763,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
Id = new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"),
|
||||
Description = "Government fees, insurance, inspections and safety-related expenditures.",
|
||||
IsActive = true,
|
||||
IsAttachmentRequried = true,
|
||||
Name = "Compliance & Safety",
|
||||
NoOfPersonsRequired = false,
|
||||
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
@ -3605,6 +3587,11 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.ToTable("OrgTypeMasters");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("743806fe-d991-4079-b223-e4e2da44f435"),
|
||||
Name = "Tenant"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("5ee49bcd-b6d3-482f-9aaf-484afe04abec"),
|
||||
@ -3614,6 +3601,11 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("a283356a-9b02-4029-afb7-e65c703efdd4"),
|
||||
Name = "Sub-Contractor"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("b1877a3b-8832-47b1-bbe3-dc7e98672f49"),
|
||||
Name = "PMC"
|
||||
});
|
||||
});
|
||||
|
||||
@ -3661,9 +3653,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid?>("UpdatedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("logoImage")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Organizations");
|
||||
@ -3689,9 +3678,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AssignedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("AssignedDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -3715,8 +3701,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssignedById");
|
||||
|
||||
b.HasIndex("OrganizationId");
|
||||
|
||||
b.HasIndex("OrganizationTypeId");
|
||||
@ -3777,9 +3761,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AssignedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("AssignedDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -3800,8 +3781,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssignedById");
|
||||
|
||||
b.HasIndex("OrganizationId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
@ -3949,9 +3928,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<DateTime?>("ReAllocationDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("ServiceId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@ -3961,8 +3937,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("ServiceId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ProjectAllocations");
|
||||
@ -4339,32 +4313,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.ToTable("TenantSubscriptions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Utilities.FCMTokenMapping", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("EmployeeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("ExpiredAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("FcmToken")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("FCMTokenMappings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Utilities.Inquiries", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -4718,18 +4666,10 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "Approver")
|
||||
.WithMany()
|
||||
.HasForeignKey("ApprovedById");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "Employee")
|
||||
.WithMany()
|
||||
.HasForeignKey("EmployeeId")
|
||||
.HasForeignKey("EmployeeID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "RequestedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedById");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
@ -4738,10 +4678,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("Approver");
|
||||
|
||||
b.Navigation("Employee");
|
||||
|
||||
b.Navigation("RequestedBy");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
@ -4784,6 +4720,28 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Navigation("UpdatedByEmployee");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.MPINDetails", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.OTPDetails", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
@ -5766,12 +5724,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.OrganizationModel.ProjectOrgMapping", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "AssignedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssignedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.OrganizationModel.Organization", "Organization")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrganizationId")
|
||||
@ -5802,8 +5754,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AssignedBy");
|
||||
|
||||
b.Navigation("Organization");
|
||||
|
||||
b.Navigation("OrganizationType");
|
||||
@ -5844,12 +5794,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.OrganizationModel.TenantOrgMapping", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "AssignedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssignedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.OrganizationModel.Organization", "Organization")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrganizationId")
|
||||
@ -5862,8 +5806,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AssignedBy");
|
||||
|
||||
b.Navigation("Organization");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
@ -5948,10 +5890,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Master.ServiceMaster", "Service")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServiceId");
|
||||
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
@ -5962,8 +5900,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("Service");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
@ -6146,17 +6082,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Utilities.FCMTokenMapping", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
|
@ -20,21 +20,18 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
|
||||
_collection = mongoDB.GetCollection<EmployeePermissionMongoDB>("EmployeeProfile");
|
||||
}
|
||||
public async Task<bool> AddApplicationRoleToCache(Guid employeeId, List<string> newRoleIds, List<string> newPermissionIds, Guid tenantId)
|
||||
public async Task<bool> AddApplicationRoleToCache(Guid employeeId, List<string> newRoleIds, List<string> newPermissionIds)
|
||||
{
|
||||
|
||||
// 2. Perform database queries concurrently for better performance.
|
||||
var employeeIdString = employeeId.ToString();
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
// 5. Build a single, efficient update operation.
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.AddToSetEach(e => e.ApplicationRoleIds, newRoleIds)
|
||||
.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
|
||||
.Set(r => r.TenantId, tenantIdString)
|
||||
.AddToSetEach(e => e.PermissionIds, newPermissionIds);
|
||||
|
||||
var options = new UpdateOptions { IsUpsert = true };
|
||||
@ -47,17 +44,14 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
// The operation is successful if an existing document was modified OR a new one was created.
|
||||
return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
|
||||
}
|
||||
public async Task<bool> AddProjectsToCache(Guid employeeId, List<Guid> projectIds, Guid tenantId)
|
||||
public async Task<bool> AddProjectsToCache(Guid employeeId, List<Guid> projectIds)
|
||||
{
|
||||
var newprojectIds = projectIds.Select(p => p.ToString()).ToList();
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
|
||||
.Set(r => r.TenantId, tenantIdString)
|
||||
.AddToSetEach(e => e.ProjectIds, newprojectIds);
|
||||
|
||||
var result = await _collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true });
|
||||
@ -68,12 +62,10 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
await InitializeCollectionAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId, Guid tenantId)
|
||||
public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
|
||||
var result = await _collection
|
||||
.Find(filter)
|
||||
@ -87,12 +79,10 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return projectIds;
|
||||
}
|
||||
public async Task<List<Guid>> GetPermissionsFromCache(Guid employeeId, Guid tenantId)
|
||||
public async Task<List<Guid>> GetPermissionsFromCache(Guid employeeId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
|
||||
var result = await _collection
|
||||
.Find(filter)
|
||||
@ -106,13 +96,10 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return permissionIds;
|
||||
}
|
||||
public async Task<bool> ClearAllProjectIdsFromCache(Guid employeeId, Guid tenantId)
|
||||
public async Task<bool> ClearAllProjectIdsFromCache(Guid employeeId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(e => e.ProjectIds, new List<string>());
|
||||
@ -138,25 +125,18 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllProjectIdsByPermissionIdFromCache(Guid permissionId, Guid tenantId)
|
||||
public async Task<bool> ClearAllProjectIdsByPermissionIdFromCache(Guid permissionId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.AnyEq(e => e.PermissionIds, permissionId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update.Set(e => e.ProjectIds, new List<string>());
|
||||
|
||||
var result = await _collection.UpdateManyAsync(filter, update).ConfigureAwait(false);
|
||||
return result.IsAcknowledged && result.ModifiedCount > 0;
|
||||
}
|
||||
public async Task<bool> RemoveRoleIdFromCache(Guid employeeId, Guid roleId, Guid tenantId)
|
||||
public async Task<bool> RemoveRoleIdFromCache(Guid employeeId, Guid roleId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Pull(e => e.ApplicationRoleIds, roleId.ToString());
|
||||
@ -171,13 +151,10 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllPermissionIdsByEmployeeIDFromCache(Guid employeeId, Guid tenantId)
|
||||
public async Task<bool> ClearAllPermissionIdsByEmployeeIDFromCache(Guid employeeId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(e => e.PermissionIds, new List<string>());
|
||||
@ -212,56 +189,11 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByOnlyEmployeeId(Guid employeeId)
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds)
|
||||
{
|
||||
var employeeIdString = employeeId.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);
|
||||
|
||||
var result = await _collection.DeleteManyAsync(filter);
|
||||
|
||||
if (result.DeletedCount == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error occured while deleting employee profile");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByTenantId(Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var result = await _collection.DeleteManyAsync(filter);
|
||||
|
||||
if (result.DeletedCount == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error occured while deleting employee profile");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.In(x => x.Id, employeeIds);
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var result = await _collection.DeleteManyAsync(filter);
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.MongoDBModels;
|
||||
using Marco.Pms.Model.MongoDBModels.Masters;
|
||||
using Marco.Pms.Model.MongoDBModels.Project;
|
||||
using Marco.Pms.Model.OrganizationModel;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -57,7 +55,7 @@ namespace Marco.Pms.Helpers
|
||||
var indexModel = new CreateIndexModel<ProjectMongoDB>(indexKeys, indexOptions);
|
||||
await _projectCollection.Indexes.CreateOneAsync(indexModel);
|
||||
}
|
||||
public async Task<bool> UpdateProjectDetailsOnlyToCache(Project project, StatusMaster projectStatus, Organization promotor, Organization pmc)
|
||||
public async Task<bool> UpdateProjectDetailsOnlyToCache(Project project, StatusMaster projectStatus)
|
||||
{
|
||||
// Build the update definition
|
||||
var updates = Builders<ProjectMongoDB>.Update.Combine(
|
||||
@ -69,26 +67,6 @@ namespace Marco.Pms.Helpers
|
||||
Id = projectStatus.Id.ToString(),
|
||||
Status = projectStatus.Status
|
||||
}),
|
||||
Builders<ProjectMongoDB>.Update.Set(r => r.Promoter, new OrganizationMongoDB
|
||||
{
|
||||
Id = promotor.Id.ToString(),
|
||||
Name = promotor.Name,
|
||||
ContactPerson = promotor.ContactPerson,
|
||||
Email = promotor.Email,
|
||||
Address = promotor.Address,
|
||||
ContactNumber = promotor.ContactNumber,
|
||||
SPRID = promotor.SPRID
|
||||
}),
|
||||
Builders<ProjectMongoDB>.Update.Set(r => r.PMC, new OrganizationMongoDB
|
||||
{
|
||||
Id = pmc.Id.ToString(),
|
||||
Name = pmc.Name,
|
||||
ContactPerson = pmc.ContactPerson,
|
||||
Email = pmc.Email,
|
||||
Address = promotor.Address,
|
||||
ContactNumber = promotor.ContactNumber,
|
||||
SPRID = promotor.SPRID
|
||||
}),
|
||||
Builders<ProjectMongoDB>.Update.Set(r => r.StartDate, project.StartDate),
|
||||
Builders<ProjectMongoDB>.Update.Set(r => r.EndDate, project.EndDate),
|
||||
Builders<ProjectMongoDB>.Update.Set(r => r.ContactPerson, project.ContactPerson)
|
||||
@ -429,19 +407,10 @@ namespace Marco.Pms.Helpers
|
||||
|
||||
#region=================================================================== WorkItem Cache Helper ===================================================================
|
||||
|
||||
public async Task<List<WorkItemMongoDB>> GetWorkItemsByWorkAreaIdsFromCache(List<Guid> workAreaIds, List<Guid> serviceIds)
|
||||
public async Task<List<WorkItemMongoDB>> GetWorkItemsByWorkAreaIdsFromCache(List<Guid> workAreaIds)
|
||||
{
|
||||
var stringWorkAreaIds = workAreaIds.Select(wa => wa.ToString()).ToList();
|
||||
|
||||
var filterBuilder = Builders<WorkItemMongoDB>.Filter;
|
||||
var filter = filterBuilder.Empty;
|
||||
|
||||
filter &= filterBuilder.In(w => w.WorkAreaId, stringWorkAreaIds);
|
||||
if (serviceIds.Any())
|
||||
{
|
||||
var stringServiceIds = serviceIds.Select(s => s.ToString()).ToList();
|
||||
filter &= filterBuilder.In(w => w.ActivityMaster!.ActivityGroupMaster!.Service!.Id, stringServiceIds);
|
||||
}
|
||||
var filter = Builders<WorkItemMongoDB>.Filter.In(w => w.WorkAreaId, stringWorkAreaIds);
|
||||
|
||||
var workItems = await _taskCollection // replace with your actual collection name
|
||||
.Find(filter)
|
||||
@ -480,17 +449,9 @@ namespace Marco.Pms.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
public async Task<List<WorkItemMongoDB>> GetWorkItemDetailsByWorkAreaFromCache(Guid workAreaId, List<Guid> serviceIds)
|
||||
public async Task<List<WorkItemMongoDB>> GetWorkItemDetailsByWorkAreaFromCache(Guid workAreaId)
|
||||
{
|
||||
var filterBuilder = Builders<WorkItemMongoDB>.Filter;
|
||||
var filter = filterBuilder.Empty;
|
||||
|
||||
filter &= filterBuilder.Eq(p => p.WorkAreaId, workAreaId.ToString());
|
||||
if (serviceIds.Any())
|
||||
{
|
||||
var stringServiceIds = serviceIds.Select(s => s.ToString()).ToList();
|
||||
filter &= filterBuilder.In(w => w.ActivityMaster!.ActivityGroupMaster!.Service!.Id, stringServiceIds);
|
||||
}
|
||||
var filter = Builders<WorkItemMongoDB>.Filter.Eq(p => p.WorkAreaId, workAreaId.ToString());
|
||||
|
||||
var options = new UpdateOptions { IsUpsert = true };
|
||||
var workItems = await _taskCollection
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.Model.MongoDBModels;
|
||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Bson;
|
||||
@ -147,86 +146,5 @@ namespace Marco.Pms.Helpers.Utility
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region =================================================================== NotificatioBody Helper Functions ===================================================================
|
||||
|
||||
public async Task AddNotificationAsync(NotificationMongoDB notification)
|
||||
{
|
||||
try
|
||||
{
|
||||
var collection = _mongoDatabase.GetCollection<NotificationMongoDB>("NotificatioBody");
|
||||
|
||||
var indexKeys = Builders<NotificationMongoDB>.IndexKeys
|
||||
.Ascending(doc => doc.TenantId)
|
||||
.Ascending(doc => doc.Name);
|
||||
|
||||
// Define index options with unique constraint
|
||||
var indexOptions = new CreateIndexOptions { Unique = true };
|
||||
|
||||
// Create the index model
|
||||
var indexModel = new CreateIndexModel<NotificationMongoDB>(indexKeys, indexOptions);
|
||||
|
||||
// Create the index on the collection (this operation is idempotent)
|
||||
collection.Indexes.CreateOne(indexModel);
|
||||
|
||||
await collection.InsertOneAsync(notification);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while adding the Notification body {NotificationName} for tenant {TenantId}", notification.Name, notification.TenantId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<NotificationMongoDB> GetNotificationBodyAsync(string name, Guid tenantId)
|
||||
{
|
||||
var rootTenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26");
|
||||
NotificationMongoDB? result = null;
|
||||
NotificationMongoDB? defaultNotification = new NotificationMongoDB
|
||||
{
|
||||
Name = "default",
|
||||
Title = "Error: Something Went Wrong",
|
||||
Body = " An unexpected error occurred. Please try again. If the problem persists, contact support",
|
||||
Parameters = "",
|
||||
TenantId = rootTenantId
|
||||
};
|
||||
|
||||
var collection = _mongoDatabase.GetCollection<NotificationMongoDB>("NotificatioBody");
|
||||
try
|
||||
{
|
||||
var filter = Builders<NotificationMongoDB>.Filter.And(
|
||||
Builders<NotificationMongoDB>.Filter.Eq(n => n.Name, name),
|
||||
Builders<NotificationMongoDB>.Filter.Eq(n => n.TenantId, tenantId)
|
||||
|
||||
);
|
||||
|
||||
result = await collection
|
||||
.Find(filter)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while fetching the Notification body {NotificationName} for tenant {TenantId}", name, tenantId);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<NotificationMongoDB>.Filter.And(
|
||||
Builders<NotificationMongoDB>.Filter.Eq(n => n.Name, name),
|
||||
Builders<NotificationMongoDB>.Filter.Eq(n => n.TenantId, rootTenantId)
|
||||
|
||||
);
|
||||
result = await collection.Find(filter).FirstOrDefaultAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while fetching the Notification body {NotificationName} for tenant {TenantId}", name, rootTenantId);
|
||||
return defaultNotification;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
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.AttendanceModule
|
||||
{
|
||||
@ -10,11 +10,9 @@ namespace Marco.Pms.Model.AttendanceModule
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Comment { get; set; } = string.Empty;
|
||||
public Guid EmployeeId { get; set; }
|
||||
public Guid EmployeeID { get; set; }
|
||||
|
||||
|
||||
[ForeignKey("EmployeeId")]
|
||||
[ValidateNever]
|
||||
public Employee? Employee { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public Guid ProjectID { get; set; }
|
||||
|
||||
@ -24,17 +22,9 @@ namespace Marco.Pms.Model.AttendanceModule
|
||||
public bool IsApproved { get; set; }
|
||||
public ATTENDANCE_MARK_TYPE Activity { get; set; }
|
||||
|
||||
public Guid? ApprovedById { get; set; }
|
||||
|
||||
[ForeignKey("ApprovedById")]
|
||||
public Guid? ApprovedBy { get; set; }
|
||||
[ForeignKey("EmployeeID")]
|
||||
[ValidateNever]
|
||||
public Employee? Approver { get; set; }
|
||||
public DateTime? RequestedAt { get; set; }
|
||||
public DateTime? ApprovedAt { get; set; }
|
||||
public Guid? RequestedById { get; set; }
|
||||
|
||||
[ForeignKey("RequestedById")]
|
||||
[ValidateNever]
|
||||
public Employee? RequestedBy { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
{
|
||||
public class MPINDetails
|
||||
public class MPINDetails : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
|
@ -1,6 +1,8 @@
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
{
|
||||
public class OTPDetails
|
||||
public class OTPDetails : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class CreateActivityMasterDto
|
||||
{
|
||||
public required Guid ActivityGroupId { get; set; }
|
||||
public required Guid ActitvityGroupId { get; set; }
|
||||
public required string ActivityName { get; set; }
|
||||
public required string UnitOfMeasurement { get; set; }
|
||||
public List<CreateCheckListDto>? CheckList { get; set; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class CreateWorkStatusMasterDto
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
public class UpdateWorkStatusMasterDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ namespace Marco.Pms.Model.Dtos.Attendance
|
||||
public ATTENDANCE_MARK_TYPE Action { get; set; }
|
||||
|
||||
public FileUploadModel? Image { get; set; }
|
||||
public string DeviceToken { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public enum ATTENDANCE_MARK_TYPE
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class LoginDto
|
||||
{
|
||||
public required string Username { get; set; }
|
||||
public required string Password { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
namespace Marco.Pms.Model.Dtos.Authentication
|
||||
{
|
||||
public class LogoutDto
|
||||
{
|
||||
public required string RefreshToken { get; set; }
|
||||
public string? FcmToken { get; set; }
|
||||
{ public string? RefreshToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class RefreshTokenDto
|
||||
{
|
||||
public required string Token { get; set; }
|
||||
public required string RefreshToken { get; set; }
|
||||
public string? Token { get; set; }
|
||||
public string? RefreshToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
{
|
||||
public class VerifyMPINDto
|
||||
{
|
||||
public required Guid EmployeeId { get; set; }
|
||||
public required string MPIN { get; set; }
|
||||
public required string MPINToken { get; set; }
|
||||
public Guid EmployeeId { get; set; }
|
||||
public string? MPIN { get; set; }
|
||||
public string? MPINToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,33 +8,27 @@
|
||||
public string? MiddleName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
|
||||
public required string Gender { get; set; }
|
||||
public required DateTime BirthDate { get; set; }
|
||||
public required DateTime JoiningDate { get; set; }
|
||||
public string? Gender { get; set; }
|
||||
public string? BirthDate { get; set; }
|
||||
public string? JoiningDate { get; set; }
|
||||
|
||||
public required string PermanentAddress { get; set; }
|
||||
public required string CurrentAddress { get; set; }
|
||||
public required string PhoneNumber { get; set; }
|
||||
public string? PermanentAddress { get; set; }
|
||||
public string? CurrentAddress { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
|
||||
public string? EmergencyPhoneNumber { get; set; }
|
||||
public string? EmergencyContactPerson { get; set; }
|
||||
public Guid JobRoleId { get; set; }
|
||||
public required Guid OrganizationId { get; set; }
|
||||
public required bool HasApplicationAccess { get; set; }
|
||||
}
|
||||
public class MobileUserManageDto
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public required string FirstName { get; set; }
|
||||
public required string LastName { get; set; }
|
||||
public required string PhoneNumber { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public required DateTime JoiningDate { get; set; }
|
||||
public required string Gender { get; set; }
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
public string? LastName { get; set; }
|
||||
public string PhoneNumber { get; set; } = string.Empty;
|
||||
public string? Gender { get; set; }
|
||||
public Guid JobRoleId { get; set; }
|
||||
public string? ProfileImage { get; set; }
|
||||
public required Guid OrganizationId { get; set; }
|
||||
public required bool HasApplicationAccess { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class CreateContactCategoryDto
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
public class CreateContactTagDto
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
public Guid? Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public required bool NoOfPersonsRequired { get; set; }
|
||||
public required bool IsAttachmentRequried { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
{
|
||||
public class UpdateContactCategoryDto
|
||||
{
|
||||
public required Guid Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
public class UpdateContactTagDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
public required string ContactPerson { get; set; }
|
||||
public required string Address { get; set; }
|
||||
public required string ContactNumber { get; set; }
|
||||
public string? logoImage { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
public required List<Guid> ServiceIds { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@
|
||||
public required string ContactPerson { get; set; }
|
||||
public required string Address { get; set; }
|
||||
public required string ContactNumber { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
public required List<Guid> ServiceIds { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,18 +7,17 @@ namespace Marco.Pms.Model.Dtos.Project
|
||||
{
|
||||
[Required(ErrorMessage = "Project Name is required!")]
|
||||
[DisplayName("Project Name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
[DisplayName("Short Name")]
|
||||
public string? ShortName { get; set; }
|
||||
|
||||
[DisplayName("Project Address")]
|
||||
[Required(ErrorMessage = "Project Address is required!")]
|
||||
public required string ProjectAddress { get; set; }
|
||||
public string? ProjectAddress { get; set; }
|
||||
|
||||
|
||||
[DisplayName("Contact Person")]
|
||||
public required string ContactPerson { get; set; }
|
||||
public string? ContactPerson { get; set; }
|
||||
|
||||
|
||||
public DateTime? StartDate { get; set; }
|
||||
@ -26,8 +25,6 @@ namespace Marco.Pms.Model.Dtos.Project
|
||||
|
||||
[DisplayName("Project Status")]
|
||||
[Required(ErrorMessage = "Project Status is required!")]
|
||||
public required Guid ProjectStatusId { get; set; }
|
||||
public required Guid PromoterId { get; set; }
|
||||
public required Guid PMCId { get; set; }
|
||||
public Guid ProjectStatusId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,9 @@
|
||||
{
|
||||
public class ProjectAllocationDot
|
||||
{
|
||||
public Guid EmployeeId { get; set; }
|
||||
public Guid EmpID { get; set; }
|
||||
public Guid JobRoleId { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
public Guid? ServiceId { get; set; }
|
||||
public bool Status { get; set; }
|
||||
}
|
||||
|
||||
@ -14,7 +13,6 @@
|
||||
{
|
||||
public Guid ProjectId { get; set; }
|
||||
public Guid JobRoleId { get; set; }
|
||||
public Guid? ServiceId { get; set; }
|
||||
public bool Status { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -5,20 +5,20 @@ namespace Marco.Pms.Model.Dtos.Project
|
||||
{
|
||||
public class UpdateProjectDto
|
||||
{
|
||||
public required Guid Id { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
[Required(ErrorMessage = "Project Name is required!")]
|
||||
[DisplayName("Project Name")]
|
||||
public required string Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
[DisplayName("Short Name")]
|
||||
public string? ShortName { get; set; }
|
||||
|
||||
[DisplayName("Project Address")]
|
||||
[Required(ErrorMessage = "Project Address is required!")]
|
||||
public required string ProjectAddress { get; set; }
|
||||
public string? ProjectAddress { get; set; }
|
||||
|
||||
|
||||
[DisplayName("Contact Person")]
|
||||
public required string ContactPerson { get; set; }
|
||||
public string? ContactPerson { get; set; }
|
||||
|
||||
|
||||
public DateTime? StartDate { get; set; }
|
||||
@ -26,8 +26,6 @@ namespace Marco.Pms.Model.Dtos.Project
|
||||
|
||||
[DisplayName("Project Status")]
|
||||
[Required(ErrorMessage = "Project Status is required!")]
|
||||
public required Guid ProjectStatusId { get; set; }
|
||||
public required Guid PromoterId { get; set; }
|
||||
public required Guid PMCId { get; set; }
|
||||
public Guid ProjectStatusId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,5 @@
|
||||
public required string OrganizationSize { get; set; }
|
||||
public required Guid IndustryId { get; set; }
|
||||
public required string Reference { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,224 +0,0 @@
|
||||
using Marco.Pms.Model.AppMenu;
|
||||
|
||||
namespace Marco.Pms.Model.Entitlements
|
||||
{
|
||||
public static class MenuStaticMaster
|
||||
{
|
||||
public static readonly MenuSection menu = new MenuSection
|
||||
{
|
||||
Header = "Main Navigation",
|
||||
Title = "Main Menu",
|
||||
Items = new List<MenuItem>
|
||||
{
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Dashboard",
|
||||
Icon = "bx bx-home",
|
||||
Available = true,
|
||||
Link = "/dashboard",
|
||||
PermissionIds = new List<string>(),
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Projects",
|
||||
Icon = "bx bx-building-house",
|
||||
Available = true,
|
||||
Link = "",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"6ea44136-987e-44ba-9e5d-1cf8f5837ebc",
|
||||
"172fc9b6-755b-4f62-ab26-55c34a330614",
|
||||
"b94802ce-0689-4643-9e1d-11c86950c35b",
|
||||
"8d7cc6e3-9147-41f7-aaa7-fa507e450bd4",
|
||||
"cf2825ad-453b-46aa-91d9-27c124d63373",
|
||||
"9fcc5f87-25e3-4846-90ac-67a71ab92e3c",
|
||||
"08752f33-3b29-4816-b76b-ea8a968ed3c5",
|
||||
"6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2",
|
||||
"db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>
|
||||
{
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Project List",
|
||||
Available = true,
|
||||
Link = "/projects",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"6ea44136-987e-44ba-9e5d-1cf8f5837ebc",
|
||||
"172fc9b6-755b-4f62-ab26-55c34a330614",
|
||||
"b94802ce-0689-4643-9e1d-11c86950c35b",
|
||||
"8d7cc6e3-9147-41f7-aaa7-fa507e450bd4",
|
||||
"cf2825ad-453b-46aa-91d9-27c124d63373",
|
||||
"9fcc5f87-25e3-4846-90ac-67a71ab92e3c",
|
||||
"08752f33-3b29-4816-b76b-ea8a968ed3c5"
|
||||
}
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Daily Task Planning",
|
||||
Available = true,
|
||||
Link = "/activities/task",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"8d7cc6e3-9147-41f7-aaa7-fa507e450bd4",
|
||||
"9fcc5f87-25e3-4846-90ac-67a71ab92e3c",
|
||||
"6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2"
|
||||
}
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Daily Progress Report",
|
||||
Available = true,
|
||||
Link = "/activities/records",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"9fcc5f87-25e3-4846-90ac-67a71ab92e3c",
|
||||
"6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2",
|
||||
"db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c"
|
||||
}
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Image Gallary",
|
||||
Available = true,
|
||||
Link = "/gallary",
|
||||
PermissionIds = new List<string>()
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Organizations",
|
||||
Available = true,
|
||||
Link = "/organizations",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"068cb3c1-49c5-4746-9f29-1fce16e820ac",
|
||||
"c1ae1363-ab8a-4bd9-a9d1-8c2c6083873a",
|
||||
"7a6cf830-0008-4e03-b31d-0d050cb634f4"
|
||||
}
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Project Report",
|
||||
Available = true,
|
||||
Link = "/activities/reports",
|
||||
PermissionIds = new List<string>()
|
||||
},
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Employees",
|
||||
Icon = "bx bx-user",
|
||||
Available = true,
|
||||
Link = "/employees",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"60611762-7f8a-4fb5-b53f-b1139918796b",
|
||||
"b82d2b7e-0d52-45f3-997b-c008ea460e7f",
|
||||
"a97d366a-c2bb-448d-be93-402bd2324566",
|
||||
"fbd213e0-0250-46f1-9f5f-4b2a1e6e76a3"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Attendance",
|
||||
Icon = "bx bx-list-ul",
|
||||
Available = true,
|
||||
Link = "/activities/Attendance",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"915e6bff-65f6-4e3f-aea8-3fd217d3ea9e",
|
||||
"57802c4a-00aa-4a1f-a048-fd2f70dd44b6",
|
||||
"ccb0589f-712b-43de-92ed-5b6088e7dc4e"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Directory",
|
||||
Icon = "bx bx-group",
|
||||
Available = true,
|
||||
Link = "/directory",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"4286a13b-bb40-4879-8c6d-18e9e393beda",
|
||||
"62668630-13ce-4f52-a0f0-db38af2230c5",
|
||||
"0f919170-92d4-4337-abd3-49b66fc871bb"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Expense",
|
||||
Icon = "bx bx-receipt",
|
||||
Available = true,
|
||||
Link = "/expenses",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"385be49f-8fde-440e-bdbc-3dffeb8dd116",
|
||||
"01e06444-9ca7-4df4-b900-8c3fa051b92f",
|
||||
"0f57885d-bcb2-4711-ac95-d841ace6d5a7",
|
||||
"1f4bda08-1873-449a-bb66-3e8222bd871b",
|
||||
"eaafdd76-8aac-45f9-a530-315589c6deca",
|
||||
"ea5a1529-4ee8-4828-80ea-0e23c9d4dd11",
|
||||
"ea5a1529-4ee8-4828-80ea-0e23c9d4dd11"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Administration",
|
||||
Icon = "bx bx-box",
|
||||
Available = true,
|
||||
Link = "",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d",
|
||||
"588a8824-f924-4955-82d8-fc51956cf323",
|
||||
"d032cb1a-3f30-462c-bef0-7ace73a71c0b",
|
||||
"00e20637-ce8d-4417-bec4-9b31b5e65092",
|
||||
"647145c6-2108-4c98-aab4-178602236e55"
|
||||
},
|
||||
Submenu = new List<SubMenuItem>
|
||||
{
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Tenant",
|
||||
Available = true,
|
||||
Link = "/tenants",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"d032cb1a-3f30-462c-bef0-7ace73a71c0b",
|
||||
"00e20637-ce8d-4417-bec4-9b31b5e65092",
|
||||
"647145c6-2108-4c98-aab4-178602236e55"
|
||||
}
|
||||
},
|
||||
new SubMenuItem
|
||||
{
|
||||
Text = "Masters",
|
||||
Available = true,
|
||||
Link = "/masters",
|
||||
PermissionIds = new List<string>
|
||||
{
|
||||
"5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d",
|
||||
"588a8824-f924-4955-82d8-fc51956cf323"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Text = "Inventory",
|
||||
Icon = "bx bx-store",
|
||||
Available = true,
|
||||
Link = "/inventory",
|
||||
PermissionIds = new List<string>(),
|
||||
Submenu = new List<SubMenuItem>()
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ namespace Marco.Pms.Model.Expenses
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string? TransactionId { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string ExpenseUId { get; set; } = string.Empty;
|
||||
public string? Location { get; set; }
|
||||
public string? GSTNumber { get; set; }
|
||||
public string SupplerName { get; set; } = string.Empty;
|
||||
|
@ -8,7 +8,6 @@
|
||||
public List<Guid>? WorkCategoryIds { get; set; }
|
||||
public List<Guid>? ActivityIds { get; set; }
|
||||
public List<Guid>? UploadedByIds { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
namespace Marco.Pms.Model.Filters
|
||||
{
|
||||
public class TaskFilter
|
||||
{
|
||||
public List<Guid>? BuildingIds { get; set; }
|
||||
public List<Guid>? FloorIds { get; set; }
|
||||
public List<Guid>? ActivityIds { get; set; }
|
||||
public List<Guid>? ServiceIds { get; set; }
|
||||
public DateTime? dateFrom { get; set; }
|
||||
public DateTime? dateTo { get; set; }
|
||||
}
|
||||
}
|
@ -36,9 +36,7 @@ namespace Marco.Pms.Model.Mapper
|
||||
IsRootUser = model.ApplicationUser?.IsRootUser ?? false,
|
||||
IsSystem = model.IsSystem,
|
||||
JoiningDate = model.JoiningDate,
|
||||
TenantId = model.TenantId ?? Guid.Empty,
|
||||
HasApplicationAccess = model.HasApplicationAccess,
|
||||
OrganizationId = model.OrganizationId
|
||||
TenantId = model.TenantId ?? Guid.Empty
|
||||
};
|
||||
}
|
||||
public static BasicEmployeeVM ToBasicEmployeeVMFromEmployee(this Employee employee)
|
||||
@ -72,7 +70,7 @@ namespace Marco.Pms.Model.Mapper
|
||||
PhoneNumber = model.PhoneNumber,
|
||||
Photo = image,
|
||||
JobRoleId = model.JobRoleId,
|
||||
JoiningDate = model.JoiningDate,
|
||||
JoiningDate = null,
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace Marco.Pms.Model.Mapper
|
||||
return new ProjectAllocation
|
||||
{
|
||||
AllocationDate = DateTime.Now,
|
||||
EmployeeId = model.EmployeeId,
|
||||
EmployeeId = model.EmpID,
|
||||
JobRoleId = model.JobRoleId,
|
||||
TenantId = TenantId,
|
||||
ProjectId = model.ProjectId
|
||||
@ -65,7 +65,7 @@ namespace Marco.Pms.Model.Mapper
|
||||
}
|
||||
|
||||
|
||||
public static ProjectAllocation ToProjectAllocationFromProjectsAllocationDto(this ProjectsAllocationDto model, Guid TenantId, Guid employeeId)
|
||||
public static ProjectAllocation ToProjectAllocationFromProjectsAllocationDto(this ProjectsAllocationDto model, Guid TenantId,Guid employeeId)
|
||||
{
|
||||
return new ProjectAllocation
|
||||
{
|
||||
|
@ -9,6 +9,5 @@ namespace Marco.Pms.Model.Master
|
||||
public bool NoOfPersonsRequired { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public bool IsAttachmentRequried { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,5 @@ namespace Marco.Pms.Model.MongoDBModels.Employees
|
||||
public List<string> PermissionIds { get; set; } = new List<string>();
|
||||
public List<string> ProjectIds { get; set; } = new List<string>();
|
||||
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
||||
public string TenantId { get; set; } = string.Empty; // Tenant ID
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ namespace Marco.Pms.Model.MongoDBModels.Expenses
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
||||
public string SupplerName { get; set; } = string.Empty;
|
||||
public string? ExpenseUId { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public ExpensesStatusMasterMongoDB Status { get; set; } = new ExpensesStatusMasterMongoDB();
|
||||
public List<ExpensesStatusMasterMongoDB> NextStatus { get; set; } = new List<ExpensesStatusMasterMongoDB>();
|
||||
|
@ -1,10 +0,0 @@
|
||||
namespace Marco.Pms.Model.MongoDBModels.Masters
|
||||
{
|
||||
public class ActivityGroupMasterMongoDB
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public ServiceMasterMongoDB? Service { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace Marco.Pms.Model.MongoDBModels.Masters
|
||||
{
|
||||
public class ServiceMasterMongoDB
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Marco.Pms.Model.MongoDBModels
|
||||
{
|
||||
public class NotificationMongoDB
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.String)]
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Body { get; set; } = string.Empty;
|
||||
public string Parameters { get; set; } = string.Empty; // Comma seprated variable needed for dynamic notifications
|
||||
|
||||
[BsonRepresentation(BsonType.String)]
|
||||
public Guid TenantId { get; set; }
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace Marco.Pms.Model.MongoDBModels
|
||||
{
|
||||
public class OrganizationMongoDB
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? ContactPerson { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? ContactNumber { get; set; }
|
||||
public double SPRID { get; set; }
|
||||
}
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
using Marco.Pms.Model.MongoDBModels.Masters;
|
||||
|
||||
namespace Marco.Pms.Model.MongoDBModels.Project
|
||||
namespace Marco.Pms.Model.MongoDBModels.Project
|
||||
{
|
||||
public class ActivityMasterMongoDB
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string? ActivityName { get; set; }
|
||||
public string? UnitOfMeasurement { get; set; }
|
||||
public ActivityGroupMasterMongoDB? ActivityGroupMaster { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ namespace Marco.Pms.Model.MongoDBModels.Project
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public StatusMasterMongoDB? ProjectStatus { get; set; }
|
||||
public OrganizationMongoDB? Promoter { get; set; }
|
||||
public OrganizationMongoDB? PMC { get; set; }
|
||||
public int TeamSize { get; set; }
|
||||
public double CompletedWork { get; set; }
|
||||
public double PlannedWork { get; set; }
|
||||
|
@ -9,7 +9,6 @@
|
||||
public string Address { get; set; } = string.Empty;
|
||||
public string ContactNumber { get; set; } = string.Empty;
|
||||
public double SPRID { get; set; }
|
||||
public string? logoImage { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public Guid? CreatedById { get; set; }
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
@ -28,12 +27,6 @@ namespace Marco.Pms.Model.OrganizationModel
|
||||
[ValidateNever]
|
||||
[ForeignKey("OrganizationTypeId")]
|
||||
public OrgTypeMaster? OrganizationType { get; set; }
|
||||
|
||||
public Guid AssignedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("AssignedById")]
|
||||
public Employee? AssignedBy { get; set; }
|
||||
public DateTime AssignedDate { get; set; }
|
||||
public DateTime? CompletionDate { get; set; }
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
@ -15,11 +14,6 @@ namespace Marco.Pms.Model.OrganizationModel
|
||||
public Organization? Organization { get; set; }
|
||||
public double SPRID { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public Guid AssignedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("AssignedById")]
|
||||
public Employee? AssignedBy { get; set; }
|
||||
public DateTime AssignedDate { get; set; }
|
||||
public DateTime? ReassignedDate { get; set; }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Projects
|
||||
{
|
||||
|
@ -1,8 +1,7 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Master;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
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.Projects
|
||||
{
|
||||
@ -22,15 +21,9 @@ namespace Marco.Pms.Model.Projects
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
[ForeignKey("ProjectId")]
|
||||
[ValidateNever]
|
||||
public Project? Project { get; set; }
|
||||
public Guid? ServiceId { get; set; }
|
||||
|
||||
[ForeignKey("ServiceId")]
|
||||
[ValidateNever]
|
||||
public ServiceMaster? Service { get; set; }
|
||||
|
||||
|
||||
public DateTime AllocationDate { get; set; }
|
||||
|
@ -1,7 +0,0 @@
|
||||
namespace Marco.Pms.Model.Utilities
|
||||
{
|
||||
public class FCMTokenDto
|
||||
{
|
||||
public required string FcmToken { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace Marco.Pms.Model.Utilities
|
||||
{
|
||||
public class FCMTokenMapping : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid EmployeeId { get; set; }
|
||||
public string FcmToken { get; set; } = string.Empty;
|
||||
public DateTime ExpiredAt { get; set; } = DateTime.UtcNow.AddDays(6);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
{
|
||||
public Guid? DocumentId { get; set; }
|
||||
public required string FileName { get; set; } // Name of the file (e.g., "image1.png")
|
||||
public string? Base64Data { get; set; } // Base64-encoded string of the file
|
||||
public required string Base64Data { get; set; } // Base64-encoded string of the file
|
||||
public required string ContentType { get; set; } // MIME type (e.g., "image/png", "application/pdf")
|
||||
public long FileSize { get; set; } // File size in bytes
|
||||
public string? Description { get; set; } // Optional: Description or purpose of the file
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.AttendanceVM
|
||||
{
|
||||
@ -7,20 +6,14 @@ namespace Marco.Pms.Model.ViewModels.AttendanceVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid EmployeeId { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? EmployeeAvatar { get; set; }
|
||||
public string? OrganizationName { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
public DateTime? CheckInTime { get; set; }
|
||||
public DateTime? CheckOutTime { get; set; }
|
||||
public DateTime? RequestedAt { get; set; }
|
||||
public DateTime? ApprovedAt { get; set; }
|
||||
public string? JobRoleName { get; set; }
|
||||
public ATTENDANCE_MARK_TYPE Activity { get; set; }
|
||||
public BasicEmployeeVM? Approver { get; set; }
|
||||
public BasicEmployeeVM? RequestedBy { get; set; }
|
||||
|
||||
public Guid? DocumentId { get; set; }
|
||||
public string? ThumbPreSignedUrl { get; set; }
|
||||
public string? PreSignedUrl { get; set; }
|
||||
|
@ -25,8 +25,6 @@
|
||||
public Guid TenantId { get; set; }
|
||||
public bool IsSystem { get; set; }
|
||||
public string? JobRole { get; set; }
|
||||
public bool HasApplicationAccess { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
||||
public DateTime TransactionDate { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string SupplerName { get; set; } = string.Empty;
|
||||
public string? ExpenseUId { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public ExpensesStatusMasterVM? Status { get; set; }
|
||||
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
|
||||
|
@ -18,7 +18,6 @@ namespace Marco.Pms.Model.ViewModels.Expanses
|
||||
public DateTime TransactionDate { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string SupplerName { get; set; } = string.Empty;
|
||||
public string? ExpenseUId { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string TransactionId { get; set; } = string.Empty;
|
||||
public double Amount { get; set; }
|
||||
|
@ -1,14 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Master
|
||||
{
|
||||
public class ActivityGroupDetailsListVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsSystem { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<ActivityVM>? Activities { get; set; }
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public bool NoOfPersonsRequired { get; set; }
|
||||
public bool IsAttachmentRequried { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
namespace Marco.Pms.Model.ViewModels.Master
|
||||
{
|
||||
public class ServiceDetailsListVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsSystem { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<ActivityGroupDetailsListVM>? ActivityGroups { get; set; }
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Organization
|
||||
{
|
||||
public class OrganizationDetailsVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? ContactPerson { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? ContactNumber { get; set; }
|
||||
public double SPRID { get; set; }
|
||||
public int ActiveEmployeeCount { get; set; }
|
||||
public int ActiveApplicationUserCount { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<ProjectServiceMappingVM>? Projects { get; set; }
|
||||
public List<GlobalServiceMaster>? Services { get; set; }
|
||||
public string? logoImage { get; set; }
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ namespace Marco.Pms.Model.ViewModels.Organization
|
||||
public string? Address { get; set; }
|
||||
public string? ContactNumber { get; set; }
|
||||
public double SPRID { get; set; }
|
||||
public string? logoImage { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||
|
@ -1,20 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Organization
|
||||
{
|
||||
public class ProjectOrganizationVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? ContactPerson { get; set; }
|
||||
public double SPRID { get; set; }
|
||||
public string? logoImage { get; set; }
|
||||
public string? OrganizationType { get; set; }
|
||||
public DateTime AssignedDate { get; set; }
|
||||
public BasicEmployeeVM? AssignedBy { get; set; }
|
||||
public ServiceMasterVM? Service { get; set; }
|
||||
public DateTime? CompletionDate { get; set; }
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Organization
|
||||
{
|
||||
public class ProjectServiceMappingVM
|
||||
{
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public ServiceMasterVM? Service { get; set; }
|
||||
public DateTime PlannedStartDate { get; set; }
|
||||
public DateTime PlannedEndDate { get; set; }
|
||||
public DateTime ActualStartDate { get; set; }
|
||||
public DateTime? ActualEndDate { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.ViewModels.Organization;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Projects
|
||||
{
|
||||
@ -13,8 +12,6 @@ namespace Marco.Pms.Model.ViewModels.Projects
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public StatusMaster? ProjectStatus { get; set; }
|
||||
public BasicOrganizationVm? Promoter { get; set; }
|
||||
public BasicOrganizationVm? PMC { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,18 +7,15 @@
|
||||
public required string TimeStamp { get; set; }
|
||||
public int TodaysAttendances { get; set; }
|
||||
public int TotalEmployees { get; set; }
|
||||
public double AttendancePercentage { get; set; }
|
||||
public int RegularizationPending { get; set; }
|
||||
public int CheckoutPending { get; set; }
|
||||
public double TotalPlannedWork { get; set; }
|
||||
public double TotalCompletedWork { get; set; }
|
||||
public double CompletionStatus { get; set; }
|
||||
public double TotalPlannedTask { get; set; }
|
||||
public double TotalCompletedTask { get; set; }
|
||||
public double TaskPercentage { get; set; }
|
||||
public double CompletionStatus { get; set; }
|
||||
public int ReportPending { get; set; }
|
||||
public int TodaysAssignTasks { get; set; }
|
||||
public int TodaysCompletedTasks { get; set; }
|
||||
public List<TeamOnSite> TeamOnSite { get; set; } = new List<TeamOnSite>();
|
||||
public List<PerformedTask> PerformedTasks { get; set; } = new List<PerformedTask>();
|
||||
public List<PerformedAttendance> PerformedAttendance { get; set; } = new List<PerformedAttendance>();
|
||||
|
@ -12,7 +12,6 @@ namespace Marco.Pms.Model.ViewModels.Tenant
|
||||
public string ContactNumber { get; set; } = string.Empty;
|
||||
public string? logoImage { get; set; } // Base64
|
||||
public string? OrganizationSize { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public Industry? Industry { get; set; }
|
||||
public TenantStatus? TenantStatus { get; set; }
|
||||
}
|
||||
|
@ -456,14 +456,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
// Step 2: Fetch all menu sections for the tenant
|
||||
var menus = await _sideBarMenuHelper.GetAllMenuSectionsAsync(tenantId);
|
||||
|
||||
if (!(menus?.Any() ?? false))
|
||||
{
|
||||
menus = new List<MenuSection>
|
||||
{
|
||||
MenuStaticMaster.menu
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var menu in menus)
|
||||
{
|
||||
var allowedItems = new List<MenuItem>();
|
||||
@ -575,23 +567,24 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
ProjectManagement, new List<MasterMenuVM>
|
||||
{
|
||||
new MasterMenuVM { Id = 3, Name = "Work Category" },
|
||||
new MasterMenuVM { Id = 8, Name = "Services" }
|
||||
new MasterMenuVM { Id = 3, Name = "Activity" },
|
||||
new MasterMenuVM { Id = 4, Name = "Work Category" },
|
||||
new MasterMenuVM { Id = 9, Name = "Services" }
|
||||
//new MasterMenuVM { Id = 10, Name = "Payment Mode" }
|
||||
}
|
||||
},
|
||||
{
|
||||
DirectoryManagement, new List<MasterMenuVM>
|
||||
{
|
||||
new MasterMenuVM { Id = 4, Name = "Contact Category" },
|
||||
new MasterMenuVM { Id = 5, Name = "Contact Tag" }
|
||||
new MasterMenuVM { Id = 5, Name = "Contact Category" },
|
||||
new MasterMenuVM { Id = 6, Name = "Contact Tag" }
|
||||
}
|
||||
},
|
||||
{
|
||||
ExpenseManagement, new List<MasterMenuVM>
|
||||
{
|
||||
new MasterMenuVM { Id = 6, Name = "Expense Type" },
|
||||
new MasterMenuVM { Id = 7, Name = "Payment Mode" }
|
||||
new MasterMenuVM { Id = 7, Name = "Expense Type" },
|
||||
new MasterMenuVM { Id = 8, Name = "Payment Mode" }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -640,13 +633,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
// Step 2: Fetch all menu sections for the tenant
|
||||
var menus = await _sideBarMenuHelper.GetAllMenuSectionsAsync(tenantId);
|
||||
if (!(menus?.Any() ?? false))
|
||||
{
|
||||
menus = new List<MenuSection>
|
||||
{
|
||||
MenuStaticMaster.menu
|
||||
};
|
||||
}
|
||||
List<MenuSectionApplicationVM> response = new List<MenuSectionApplicationVM>();
|
||||
|
||||
foreach (var menu in menus)
|
||||
@ -733,42 +719,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
menu.Items = allowedItems;
|
||||
}
|
||||
|
||||
var viewDocumentTask = Task.Run(async () =>
|
||||
{
|
||||
using var taskScope = _serviceScopeFactory.CreateScope();
|
||||
var permissions = taskScope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await permissions.HasPermission(PermissionsMaster.ViewDocument, employeeId);
|
||||
});
|
||||
|
||||
var uploadDocumentTask = Task.Run(async () =>
|
||||
{
|
||||
using var taskScope = _serviceScopeFactory.CreateScope();
|
||||
var permissions = taskScope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await permissions.HasPermission(PermissionsMaster.UploadDocument, employeeId);
|
||||
});
|
||||
|
||||
var verifyDocumentTask = Task.Run(async () =>
|
||||
{
|
||||
using var taskScope = _serviceScopeFactory.CreateScope();
|
||||
var permissions = taskScope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await permissions.HasPermission(PermissionsMaster.VerifyDocument, employeeId);
|
||||
});
|
||||
|
||||
var downloadDocumentTask = Task.Run(async () =>
|
||||
{
|
||||
using var taskScope = _serviceScopeFactory.CreateScope();
|
||||
var permissions = taskScope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await permissions.HasPermission(PermissionsMaster.DownloadDocument, employeeId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(viewDocumentTask, uploadDocumentTask, verifyDocumentTask, downloadDocumentTask);
|
||||
|
||||
var viewDocument = viewDocumentTask.Result;
|
||||
var uploadDocument = uploadDocumentTask.Result;
|
||||
var verifyDocument = verifyDocumentTask.Result;
|
||||
var downloadDocument = downloadDocumentTask.Result;
|
||||
|
||||
if (viewDocument || uploadDocument || verifyDocument || downloadDocument)
|
||||
if (await _permissions.HasPermission(PermissionsMaster.ViewDocument, employeeId))
|
||||
{
|
||||
response.Add(new MenuSectionApplicationVM
|
||||
{
|
||||
|
@ -1,5 +1,4 @@
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.AttendanceModule;
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
using Marco.Pms.Model.Employees;
|
||||
@ -7,7 +6,6 @@ using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.AttendanceVM;
|
||||
using Marco.Pms.Services.Hubs;
|
||||
using Marco.Pms.Services.Service;
|
||||
@ -30,41 +28,46 @@ namespace MarcoBMS.Services.Controllers
|
||||
public class AttendanceController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly EmployeeHelper _employeeHelper;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly S3UploadService _s3Service;
|
||||
private readonly PermissionServices _permission;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly Guid tenantId;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IHubContext<MarcoHub> _signalR;
|
||||
|
||||
|
||||
public AttendanceController(
|
||||
ApplicationDbContext context,
|
||||
UserHelper userHelper,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
ILoggingService logger,
|
||||
PermissionServices permission,
|
||||
IMapper mapper)
|
||||
ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR)
|
||||
{
|
||||
_context = context;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_employeeHelper = employeeHelper;
|
||||
_projectServices = projectServices;
|
||||
_userHelper = userHelper;
|
||||
_s3Service = s3Service;
|
||||
_logger = logger;
|
||||
_permission = permission;
|
||||
_mapper = mapper;
|
||||
tenantId = userHelper.GetTenantId();
|
||||
_signalR = signalR;
|
||||
}
|
||||
|
||||
private Guid GetTenantId()
|
||||
{
|
||||
return _userHelper.GetTenantId();
|
||||
//var tenant = User.FindFirst("TenantId")?.Value;
|
||||
//return (tenant != null ? Convert.ToInt32(tenant) : 1);
|
||||
}
|
||||
|
||||
[HttpGet("log/attendance/{attendanceid}")]
|
||||
|
||||
public async Task<IActionResult> GetAttendanceLogById(Guid attendanceid)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
|
||||
Guid TenantId = GetTenantId();
|
||||
|
||||
List<AttendanceLog> lstAttendance = await _context.AttendanceLogs
|
||||
.Include(a => a.Document)
|
||||
.Include(a => a.Employee)
|
||||
.Include(a => a.UpdatedByEmployee)
|
||||
.Where(c => c.AttendanceId == attendanceid && c.TenantId == tenantId)
|
||||
.Where(c => c.AttendanceId == attendanceid && c.TenantId == TenantId)
|
||||
.ToListAsync();
|
||||
|
||||
List<AttendanceLogVM> attendanceLogVMs = new List<AttendanceLogVM>();
|
||||
@ -78,44 +81,32 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(attendanceLogVMs, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200));
|
||||
|
||||
}
|
||||
|
||||
[HttpGet("log/employee/{employeeId}")]
|
||||
public async Task<IActionResult> GetAttendanceLogByEmployeeId(Guid employeeId, [FromQuery] DateTime? dateFrom = null, [FromQuery] DateTime? dateTo = null)
|
||||
|
||||
public async Task<IActionResult> GetAttendanceLogByEmployeeId(Guid employeeId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
{
|
||||
Guid TenantId = GetTenantId();
|
||||
DateTime fromDate = new DateTime();
|
||||
DateTime toDate = new DateTime();
|
||||
|
||||
if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
|
||||
{
|
||||
_logger.LogWarning("User sent Invalid from Date while featching attendance logs");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
|
||||
}
|
||||
if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
|
||||
{
|
||||
_logger.LogWarning("User sent Invalid to Date while featching attendance logs");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
|
||||
}
|
||||
|
||||
if (employeeId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("The employee Id sent by user is empty");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Employee ID is required and must not be Empty.", "Employee ID is required and must not be empty.", 400));
|
||||
}
|
||||
|
||||
Employee? employee = await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == employeeId && e.TenantId == tenantId);
|
||||
if (employee == null)
|
||||
{
|
||||
_logger.LogWarning("Employee {EmployeeId} not found", employeeId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found in database", 404));
|
||||
}
|
||||
|
||||
if (!dateFrom.HasValue)
|
||||
{
|
||||
dateFrom = DateTime.UtcNow;
|
||||
}
|
||||
if (!dateTo.HasValue)
|
||||
{
|
||||
var days = 0 - 7;
|
||||
dateTo = dateFrom.Value.AddDays(days);
|
||||
}
|
||||
|
||||
List<Attendance> attendances = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(c => c.EmployeeId == employeeId && c.TenantId == tenantId && c.AttendanceDate.Date >= dateFrom && c.AttendanceDate.Date <= dateTo).ToListAsync();
|
||||
|
||||
var projectIds = attendances.Select(a => a.ProjectID).Distinct().ToList();
|
||||
|
||||
var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync();
|
||||
|
||||
List<Attendance> attendances = await _context.Attendes.Where(c => c.EmployeeID == employeeId && c.TenantId == TenantId && c.AttendanceDate.Date >= fromDate && c.AttendanceDate.Date <= toDate).ToListAsync();
|
||||
Employee? employee = await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == employeeId && e.TenantId == TenantId && e.IsActive);
|
||||
List<EmployeeAttendanceVM> results = new List<EmployeeAttendanceVM>();
|
||||
|
||||
if (employee != null)
|
||||
@ -128,17 +119,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
EmployeeId = employee.Id,
|
||||
FirstName = employee.FirstName,
|
||||
LastName = employee.LastName,
|
||||
ProjectId = attendance.ProjectID,
|
||||
ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(),
|
||||
CheckInTime = attendance.InTime,
|
||||
CheckOutTime = attendance.OutTime,
|
||||
JobRoleName = employee.JobRole != null ? employee.JobRole.Name : "",
|
||||
Activity = attendance.Activity,
|
||||
EmployeeAvatar = null,
|
||||
RequestedAt = attendance.RequestedAt,
|
||||
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy),
|
||||
ApprovedAt = attendance.ApprovedAt,
|
||||
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver)
|
||||
EmployeeAvatar = null
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
@ -156,29 +141,19 @@ namespace MarcoBMS.Services.Controllers
|
||||
/// <param name="projectId">ProjectID</param>
|
||||
/// <param name="date">YYYY-MM-dd</param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpGet("project/log")]
|
||||
public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
|
||||
public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _projectServices = scope.ServiceProvider.GetRequiredService<IProjectServices>();
|
||||
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
|
||||
if (project == null)
|
||||
{
|
||||
_logger.LogWarning("Project {ProjectId} not found in database", projectId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project not found."));
|
||||
}
|
||||
|
||||
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, loggedInEmployee.Id);
|
||||
var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, loggedInEmployee.Id);
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
|
||||
Guid TenantId = GetTenantId();
|
||||
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id);
|
||||
var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id);
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);
|
||||
|
||||
if (!hasProjectPermission)
|
||||
{
|
||||
_logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", loggedInEmployee.Id, projectId);
|
||||
_logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
|
||||
}
|
||||
|
||||
@ -211,16 +186,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
if (hasTeamAttendancePermission)
|
||||
{
|
||||
List<Attendance> lstAttendance = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.Approver)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(tenantId, projectId, organizationId, true);
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
foreach (Attendance? attendance in lstAttendance)
|
||||
{
|
||||
@ -229,13 +198,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
Id = attendance.Id,
|
||||
CheckInTime = attendance.InTime,
|
||||
CheckOutTime = attendance.OutTime,
|
||||
Activity = attendance.Activity,
|
||||
ApprovedAt = attendance.ApprovedAt,
|
||||
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver),
|
||||
RequestedAt = attendance.RequestedAt,
|
||||
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy)
|
||||
Activity = attendance.Activity
|
||||
};
|
||||
teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeId);
|
||||
teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeID);
|
||||
if (teamMember != null)
|
||||
{
|
||||
result1.EmployeeAvatar = null;
|
||||
@ -245,16 +210,12 @@ namespace MarcoBMS.Services.Controllers
|
||||
result1.FirstName = teamMember.Employee.FirstName;
|
||||
result1.LastName = teamMember.Employee.LastName;
|
||||
result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null;
|
||||
result1.OrganizationName = teamMember.Employee.Organization?.Name;
|
||||
result1.ProjectId = projectId;
|
||||
result1.ProjectName = teamMember.Project?.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
result1.FirstName = null;
|
||||
result1.LastName = null;
|
||||
result1.JobRoleName = null;
|
||||
result1.OrganizationName = null;
|
||||
}
|
||||
|
||||
result.Add(result1);
|
||||
@ -264,32 +225,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
else if (hasSelfAttendancePermission)
|
||||
{
|
||||
List<Attendance> lstAttendances = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.Approver)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(c => c.ProjectID == projectId && c.EmployeeId == loggedInEmployee.Id && c.AttendanceDate.Date >= fromDate.Date &&
|
||||
c.AttendanceDate.Date <= toDate.Date && c.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var projectAllocationQuery = _context.ProjectAllocations
|
||||
.Include(pa => pa.Project)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.Organization)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(pa => pa.EmployeeId == loggedInEmployee.Id && pa.TenantId == tenantId && pa.IsActive &&
|
||||
pa.ProjectId == projectId && pa.Project != null &&
|
||||
pa.Employee != null && pa.Employee.Organization != null && pa.Employee.JobRole != null);
|
||||
|
||||
if (organizationId.HasValue)
|
||||
{
|
||||
projectAllocationQuery = projectAllocationQuery.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId);
|
||||
}
|
||||
|
||||
var projectAllocation = await projectAllocationQuery.FirstOrDefaultAsync();
|
||||
|
||||
List<Attendance> lstAttendances = await _context.Attendes.Where(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
|
||||
ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
|
||||
foreach (var attendance in lstAttendances)
|
||||
{
|
||||
if (projectAllocation != null)
|
||||
@ -302,16 +239,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
FirstName = projectAllocation.Employee?.FirstName,
|
||||
LastName = projectAllocation.Employee?.LastName,
|
||||
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
|
||||
OrganizationName = projectAllocation.Employee?.Organization?.Name,
|
||||
ProjectId = attendance.ProjectID,
|
||||
ProjectName = projectAllocation.Project?.Name,
|
||||
CheckInTime = attendance.InTime,
|
||||
CheckOutTime = attendance.OutTime,
|
||||
Activity = attendance.Activity,
|
||||
ApprovedAt = attendance.ApprovedAt,
|
||||
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver),
|
||||
RequestedAt = attendance.RequestedAt,
|
||||
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy)
|
||||
Activity = attendance.Activity
|
||||
};
|
||||
result.Add(result1);
|
||||
}
|
||||
@ -321,82 +251,122 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
||||
}
|
||||
|
||||
[HttpGet("project/team")]
|
||||
/// <summary>
|
||||
/// Retrieves employee attendance records for a specified project and date.
|
||||
/// The result is filtered based on the logged-in employee's permissions (Team or Self).
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the project.</param>
|
||||
/// <param name="organizationId">Optional. Filters attendance for employees of a specific organization.</param>
|
||||
/// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param>
|
||||
/// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param>
|
||||
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
|
||||
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
|
||||
/// <param name="projectId">ProjectID</param>
|
||||
/// <param name="date">YYYY-MM-dd</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("project/team")]
|
||||
|
||||
public async Task<IActionResult> EmployeeAttendanceByProject([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
Guid TenantId = GetTenantId();
|
||||
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id);
|
||||
var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id);
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);
|
||||
|
||||
// --- 1. Initial Validation and Permission Checks ---
|
||||
_logger.LogInfo("Fetching attendance for ProjectId: {ProjectId}, TenantId: {TenantId}", projectId, tenantId);
|
||||
|
||||
// Validate date format
|
||||
if (!DateTime.TryParse(date, out var forDate))
|
||||
if (!hasProjectPermission)
|
||||
{
|
||||
forDate = DateTime.UtcNow.Date; // Default to today's date
|
||||
_logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
|
||||
}
|
||||
|
||||
// Check if the project exists and if the employee has access
|
||||
var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
|
||||
if (project == null)
|
||||
DateTime forDate = new DateTime();
|
||||
|
||||
if (date != null && DateTime.TryParse(date, out forDate) == false)
|
||||
{
|
||||
_logger.LogWarning("Project {ProjectId} not found in database", projectId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project not found."));
|
||||
_logger.LogWarning("User sent Invalid Date while featching attendance logs");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
|
||||
|
||||
}
|
||||
if (projectId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("The project Id sent by user is less than or equal to zero");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400));
|
||||
}
|
||||
|
||||
if (!await _permission.HasProjectPermission(loggedInEmployee, projectId))
|
||||
{
|
||||
_logger.LogWarning("Unauthorized access attempt by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("You do not have permission to access this project."));
|
||||
}
|
||||
var result = new List<EmployeeAttendanceVM>();
|
||||
Attendance? attendance = null;
|
||||
|
||||
// --- 2. Delegate to Specific Logic Based on Permissions ---
|
||||
try
|
||||
if (date == null) forDate = DateTime.UtcNow.Date;
|
||||
if (hasTeamAttendancePermission)
|
||||
{
|
||||
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, loggedInEmployee.Id);
|
||||
List<EmployeeAttendanceVM> result;
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
if (hasTeamAttendancePermission)
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, IncludeInActive);
|
||||
var idList = projectteam.Select(p => p.EmployeeId).ToList();
|
||||
//var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
|
||||
foreach (ProjectAllocation teamMember in projectteam)
|
||||
{
|
||||
_logger.LogInfo("EmployeeId: {EmployeeId} has Team Attendance permission. Fetching team attendance.", loggedInEmployee.Id);
|
||||
result = await GetTeamAttendanceAsync(tenantId, projectId, organizationId, forDate, includeInactive);
|
||||
}
|
||||
else if (await _permission.HasPermission(PermissionsMaster.SelfAttendance, loggedInEmployee.Id))
|
||||
{
|
||||
_logger.LogInfo("EmployeeId: {EmployeeId} has Self Attendance permission. Fetching self attendance.", loggedInEmployee.Id);
|
||||
result = await GetSelfAttendanceAsync(tenantId, projectId, loggedInEmployee.Id, organizationId, forDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Access denied for EmployeeId: {EmployeeId}. No valid attendance permission found.", loggedInEmployee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You do not have permission to view attendance.", new { }, 403));
|
||||
if (teamMember.Employee != null && teamMember.Employee.JobRole != null)
|
||||
{
|
||||
var result1 = new EmployeeAttendanceVM()
|
||||
{
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = teamMember.EmployeeId,
|
||||
FirstName = teamMember.Employee.FirstName,
|
||||
LastName = teamMember.Employee.LastName,
|
||||
JobRoleName = teamMember.Employee.JobRole.Name,
|
||||
};
|
||||
|
||||
//var member = emp.Where(e => e.Id == teamMember.EmployeeId);
|
||||
|
||||
|
||||
attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance();
|
||||
if (attendance != null)
|
||||
{
|
||||
result1.Id = attendance.Id;
|
||||
result1.CheckInTime = attendance.InTime;
|
||||
result1.CheckOutTime = attendance.OutTime;
|
||||
result1.Activity = attendance.Activity;
|
||||
}
|
||||
|
||||
result.Add(result1);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInfo("Successfully fetched {Count} attendance records for ProjectId: {ProjectId}", result.Count, projectId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, $"{result.Count} attendance records fetched successfully."));
|
||||
result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
|
||||
{
|
||||
//return x.FirstName.CompareTo(y.FirstName);
|
||||
return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
else if (hasSelfAttendancePermission)
|
||||
{
|
||||
_logger.LogError(ex, "An error occurred while fetching attendance for ProjectId: {ProjectId}", projectId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An internal server error occurred."));
|
||||
Attendance lstAttendance = await _context.Attendes.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date == forDate && c.TenantId == TenantId) ?? new Attendance();
|
||||
ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
|
||||
if (projectAllocation != null)
|
||||
{
|
||||
EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
|
||||
{
|
||||
Id = lstAttendance.Id,
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = projectAllocation.EmployeeId,
|
||||
FirstName = projectAllocation.Employee?.FirstName,
|
||||
LastName = projectAllocation.Employee?.LastName,
|
||||
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
|
||||
CheckInTime = lstAttendance.InTime,
|
||||
CheckOutTime = lstAttendance.OutTime,
|
||||
Activity = lstAttendance.Activity
|
||||
};
|
||||
result.Add(result1);
|
||||
}
|
||||
}
|
||||
_logger.LogInfo("{count} Attendance records fetched successfully", result.Count);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("regularize")]
|
||||
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _projectServices = scope.ServiceProvider.GetRequiredService<IProjectServices>();
|
||||
|
||||
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive)
|
||||
{
|
||||
Guid TenantId = GetTenantId();
|
||||
Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var result = new List<EmployeeAttendanceVM>();
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);
|
||||
@ -407,45 +377,34 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
|
||||
}
|
||||
|
||||
List<Attendance> lstAttendance = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.Approver)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(tenantId, projectId, organizationId, true);
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
|
||||
var idList = projectteam.Select(p => p.EmployeeId).ToList();
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
|
||||
foreach (Attendance attende in lstAttendance)
|
||||
{
|
||||
var teamMember = projectteam.Find(m => m.EmployeeId == attende.EmployeeId);
|
||||
var result1 = new EmployeeAttendanceVM()
|
||||
{
|
||||
Id = attende.Id,
|
||||
CheckInTime = attende.InTime,
|
||||
CheckOutTime = attende.OutTime,
|
||||
Activity = attende.Activity,
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = attende.EmployeeID,
|
||||
|
||||
};
|
||||
|
||||
var teamMember = projectteam.Find(m => m.EmployeeId == attende.EmployeeID);
|
||||
if (teamMember != null && teamMember.Employee != null && teamMember.Employee.JobRole != null)
|
||||
{
|
||||
var result1 = new EmployeeAttendanceVM()
|
||||
{
|
||||
Id = attende.Id,
|
||||
CheckInTime = attende.InTime,
|
||||
CheckOutTime = attende.OutTime,
|
||||
Activity = attende.Activity,
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = attende.EmployeeId,
|
||||
FirstName = teamMember.Employee.FirstName,
|
||||
LastName = teamMember.Employee.LastName,
|
||||
JobRoleName = teamMember.Employee.JobRole.Name,
|
||||
OrganizationName = teamMember.Employee.Organization?.Name,
|
||||
ProjectId = projectId,
|
||||
ProjectName = teamMember.Project?.Name,
|
||||
ApprovedAt = attende.ApprovedAt,
|
||||
Approver = _mapper.Map<BasicEmployeeVM>(attende.Approver),
|
||||
RequestedAt = attende.RequestedAt,
|
||||
RequestedBy = _mapper.Map<BasicEmployeeVM>(attende.RequestedBy)
|
||||
};
|
||||
result.Add(result1);
|
||||
result1.FirstName = teamMember.Employee.FirstName;
|
||||
result1.LastName = teamMember.Employee.LastName;
|
||||
result1.JobRoleName = teamMember.Employee.JobRole.Name;
|
||||
}
|
||||
|
||||
|
||||
result.Add(result1);
|
||||
}
|
||||
|
||||
result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
|
||||
@ -456,6 +415,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[Route("record")]
|
||||
public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot)
|
||||
@ -470,17 +430,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
}
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _signalR = scope.ServiceProvider.GetRequiredService<IHubContext<MarcoHub>>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
Guid TenantId = GetTenantId();
|
||||
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == tenantId); ;
|
||||
Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ;
|
||||
|
||||
if (recordAttendanceDot.MarkTime == null)
|
||||
{
|
||||
@ -508,6 +464,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.IsApproved = true;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
|
||||
|
||||
|
||||
//string timeString = "10:30 PM"; // Format: "hh:mm tt"
|
||||
|
||||
attendance.OutTime = finalDateTime;
|
||||
}
|
||||
else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
|
||||
@ -518,8 +478,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.OutTime = finalDateTime;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
|
||||
attendance.RequestedById = currentEmployee.Id;
|
||||
attendance.RequestedAt = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -532,16 +490,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.IsApproved = true;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
|
||||
attendance.ApprovedById = currentEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
attendance.ApprovedBy = currentEmployee.Id;
|
||||
// do nothing
|
||||
}
|
||||
else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT)
|
||||
{
|
||||
attendance.IsApproved = false;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
|
||||
attendance.ApprovedById = currentEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
// do nothing
|
||||
}
|
||||
attendance.Date = DateTime.UtcNow;
|
||||
@ -552,11 +507,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
else
|
||||
{
|
||||
attendance = new Attendance();
|
||||
attendance.TenantId = tenantId;
|
||||
attendance.TenantId = TenantId;
|
||||
attendance.AttendanceDate = recordAttendanceDot.Date;
|
||||
// attendance.Activity = recordAttendanceDot.Action;
|
||||
attendance.Comment = recordAttendanceDot.Comment;
|
||||
attendance.EmployeeId = recordAttendanceDot.EmployeeID;
|
||||
attendance.EmployeeID = recordAttendanceDot.EmployeeID;
|
||||
attendance.ProjectID = recordAttendanceDot.ProjectID;
|
||||
attendance.Date = DateTime.UtcNow;
|
||||
|
||||
@ -584,7 +539,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
Latitude = recordAttendanceDot.Latitude,
|
||||
Longitude = recordAttendanceDot.Longitude,
|
||||
|
||||
TenantId = tenantId,
|
||||
TenantId = TenantId,
|
||||
UpdatedBy = currentEmployee.Id,
|
||||
UpdatedOn = recordAttendanceDot.Date
|
||||
};
|
||||
@ -622,19 +577,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
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);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{vm.FirstName} {vm.LastName}";
|
||||
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, tenantId);
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
|
||||
}
|
||||
_logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);
|
||||
@ -667,12 +609,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
}
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
|
||||
var _signalR = scope.ServiceProvider.GetRequiredService<IHubContext<MarcoHub>>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
Guid tenantId = GetTenantId();
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var batchId = Guid.NewGuid();
|
||||
|
||||
@ -704,7 +641,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
TenantId = tenantId,
|
||||
AttendanceDate = recordAttendanceDot.Date,
|
||||
Comment = recordAttendanceDot.Comment,
|
||||
EmployeeId = recordAttendanceDot.EmployeeID,
|
||||
EmployeeID = recordAttendanceDot.EmployeeID,
|
||||
ProjectID = recordAttendanceDot.ProjectID,
|
||||
Date = DateTime.UtcNow,
|
||||
InTime = finalDateTime,
|
||||
@ -736,8 +673,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
attendance.OutTime = finalDateTime;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
|
||||
attendance.RequestedById = loggedInEmployee.Id;
|
||||
attendance.RequestedAt = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -748,14 +683,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
case ATTENDANCE_MARK_TYPE.REGULARIZE:
|
||||
attendance.IsApproved = true;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
|
||||
attendance.ApprovedById = loggedInEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
break;
|
||||
case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
|
||||
attendance.IsApproved = false;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
|
||||
attendance.ApprovedById = loggedInEmployee.Id;
|
||||
attendance.ApprovedAt = DateTime.UtcNow;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -843,19 +774,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{vm.FirstName} {vm.LastName}";
|
||||
|
||||
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, tenantId);
|
||||
|
||||
});
|
||||
|
||||
_logger.LogInfo("Attendance recorded for employee: {FullName}", $"{employee.FirstName} {employee.LastName}");
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -879,136 +799,5 @@ namespace MarcoBMS.Services.Controllers
|
||||
DateTime finalDateTime = new DateTime(date.Year, date.Month, date.Day, parsedTime.Hour, parsedTime.Minute, 0);
|
||||
return finalDateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches attendance for an entire project team using a single, optimized database query.
|
||||
/// </summary>
|
||||
private async Task<List<EmployeeAttendanceVM>> GetTeamAttendanceAsync(Guid tenantId, Guid projectId, Guid? organizationId, DateTime forDate, bool includeInactive)
|
||||
{
|
||||
// This single query joins ProjectAllocations with Employees and performs a LEFT JOIN with Attendances.
|
||||
// This is far more efficient than fetching collections and joining them in memory.
|
||||
var query = _context.ProjectAllocations
|
||||
.Include(pa => pa.Project)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.Organization)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(pa => pa.TenantId == tenantId && pa.ProjectId == projectId);
|
||||
|
||||
// Apply filters based on optional parameters
|
||||
if (!includeInactive)
|
||||
{
|
||||
query = query.Where(pa => pa.IsActive);
|
||||
}
|
||||
if (organizationId.HasValue)
|
||||
{
|
||||
query = query.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId);
|
||||
}
|
||||
|
||||
List<Attendance> lstAttendance = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.Approver)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId).ToListAsync();
|
||||
|
||||
var teamAttendance = await query
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
var response = teamAttendance
|
||||
.Select(teamMember =>
|
||||
{
|
||||
var result1 = new EmployeeAttendanceVM()
|
||||
{
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = teamMember.EmployeeId,
|
||||
FirstName = teamMember.Employee?.FirstName,
|
||||
LastName = teamMember.Employee?.LastName,
|
||||
OrganizationName = teamMember.Employee?.Organization?.Name,
|
||||
JobRoleName = teamMember.Employee?.JobRole?.Name,
|
||||
ProjectId = projectId,
|
||||
ProjectName = teamMember.Project?.Name
|
||||
};
|
||||
|
||||
var attendance = lstAttendance.Find(x => x.EmployeeId == teamMember.EmployeeId) ?? new Attendance();
|
||||
if (attendance != null)
|
||||
{
|
||||
result1.Id = attendance.Id;
|
||||
result1.CheckInTime = attendance.InTime;
|
||||
result1.CheckOutTime = attendance.OutTime;
|
||||
result1.Activity = attendance.Activity;
|
||||
result1.ApprovedAt = attendance.ApprovedAt;
|
||||
result1.Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver);
|
||||
result1.RequestedAt = attendance.RequestedAt;
|
||||
result1.RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy);
|
||||
}
|
||||
return result1;
|
||||
})
|
||||
.OrderBy(vm => vm.FirstName) // Let the database handle sorting.
|
||||
.ThenBy(vm => vm.LastName).ToList();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a single attendance record for the logged-in employee.
|
||||
/// </summary>
|
||||
private async Task<List<EmployeeAttendanceVM>> GetSelfAttendanceAsync(Guid tenantId, Guid projectId, Guid employeeId, Guid? organizationId, DateTime forDate)
|
||||
{
|
||||
List<EmployeeAttendanceVM> result = new List<EmployeeAttendanceVM>();
|
||||
|
||||
// This query fetches the employee's project allocation and their attendance in a single trip.
|
||||
Attendance lstAttendance = await _context.Attendes
|
||||
.Include(a => a.RequestedBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Include(a => a.Approver)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId) ?? new Attendance();
|
||||
|
||||
var projectAllocationQuery = _context.ProjectAllocations
|
||||
.Include(pa => pa.Project)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.Organization)
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(pa => pa.EmployeeId == employeeId && pa.TenantId == tenantId && pa.IsActive &&
|
||||
pa.ProjectId == projectId && pa.Project != null &&
|
||||
pa.Employee != null && pa.Employee.Organization != null && pa.Employee.JobRole != null);
|
||||
|
||||
if (organizationId.HasValue)
|
||||
{
|
||||
projectAllocationQuery = projectAllocationQuery.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId);
|
||||
}
|
||||
|
||||
var projectAllocation = await projectAllocationQuery.FirstOrDefaultAsync();
|
||||
|
||||
if (projectAllocation != null)
|
||||
{
|
||||
EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
|
||||
{
|
||||
Id = lstAttendance.Id,
|
||||
EmployeeAvatar = null,
|
||||
EmployeeId = projectAllocation.EmployeeId,
|
||||
FirstName = projectAllocation.Employee?.FirstName,
|
||||
OrganizationName = projectAllocation.Employee?.Organization?.Name,
|
||||
LastName = projectAllocation.Employee?.LastName,
|
||||
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
|
||||
ProjectId = projectId,
|
||||
ProjectName = projectAllocation.Project?.Name,
|
||||
CheckInTime = lstAttendance.InTime,
|
||||
CheckOutTime = lstAttendance.OutTime,
|
||||
Activity = lstAttendance.Activity,
|
||||
ApprovedAt = lstAttendance.ApprovedAt,
|
||||
Approver = _mapper.Map<BasicEmployeeVM>(lstAttendance.Approver),
|
||||
RequestedAt = lstAttendance.RequestedAt,
|
||||
RequestedBy = _mapper.Map<BasicEmployeeVM>(lstAttendance.RequestedBy)
|
||||
};
|
||||
result.Add(result1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -235,7 +235,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
int inTodays = await _context.Attendes
|
||||
.Where(a => a.InTime >= today && a.InTime < tomorrow &&
|
||||
finalProjectIds.Contains(a.ProjectID))
|
||||
.Select(a => a.EmployeeId)
|
||||
.Select(a => a.EmployeeID)
|
||||
.Distinct()
|
||||
.CountAsync();
|
||||
|
||||
@ -354,7 +354,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var attendance = await _context.Attendes.Where(a => a.EmployeeId == LoggedInEmployee.Id && a.TenantId == tenantId).ToListAsync();
|
||||
var attendance = await _context.Attendes.Where(a => a.EmployeeID == LoggedInEmployee.Id && a.TenantId == tenantId).ToListAsync();
|
||||
if (attendance.Any())
|
||||
{
|
||||
var pendingRegularization = attendance.Where(a => a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE).ToList().Count;
|
||||
@ -395,12 +395,12 @@ namespace Marco.Pms.Services.Controllers
|
||||
var employeeIds = projectAllocation.Select(p => p.EmployeeId).Distinct().ToList();
|
||||
List<Employee>? employees = await _context.Employees.Where(e => employeeIds.Contains(e.Id)).ToListAsync();
|
||||
|
||||
var attendances = await _context.Attendes.Where(a => employeeIds.Contains(a.EmployeeId) && a.ProjectID == projectId && a.InTime.HasValue && a.InTime.Value.Date == currentDate.Date).ToListAsync();
|
||||
var attendances = await _context.Attendes.Where(a => employeeIds.Contains(a.EmployeeID) && a.ProjectID == projectId && a.InTime.HasValue && a.InTime.Value.Date == currentDate.Date).ToListAsync();
|
||||
List<EmployeeAttendanceVM> employeeAttendanceVMs = new List<EmployeeAttendanceVM>();
|
||||
foreach (var attendance in attendances)
|
||||
{
|
||||
|
||||
Employee? employee = employees.FirstOrDefault(e => e.Id == attendance.EmployeeId);
|
||||
Employee? employee = employees.FirstOrDefault(e => e.Id == attendance.EmployeeID);
|
||||
if (employee != null)
|
||||
{
|
||||
EmployeeAttendanceVM employeeAttendanceVM = new EmployeeAttendanceVM
|
||||
@ -579,7 +579,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
.ToList();
|
||||
|
||||
int presentCount = attendances
|
||||
.Count(a => employeeIds.Contains(a.EmployeeId) && a.InTime!.Value.Date == date);
|
||||
.Count(a => employeeIds.Contains(a.EmployeeID) && a.InTime!.Value.Date == date);
|
||||
|
||||
overviewList.Add(new AttendanceOverviewVM
|
||||
{
|
||||
|
@ -33,11 +33,11 @@ namespace Marco.Pms.Services.Controllers
|
||||
#region =================================================================== Contact Get APIs ===================================================================
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> GetContactList([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] Guid? projectId, [FromQuery] bool active = true,
|
||||
public async Task<IActionResult> GetContactList([FromQuery] string? search, [FromQuery] string? filter, [FromQuery] Guid? projectId, [FromQuery] bool active = true,
|
||||
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _directoryService.GetListOfContactsAsync(search: searchString, filter: filter, projectId: projectId, active: active, pageSize: pageSize, pageNumber: pageNumber, tenantId, loggedInEmployee);
|
||||
var response = await _directoryService.GetListOfContactsAsync(search: search, filter: filter, projectId: projectId, active: active, pageSize: pageSize, pageNumber: pageNumber, tenantId, loggedInEmployee);
|
||||
|
||||
return StatusCode(response.StatusCode, response);
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using AutoMapper;
|
||||
using FirebaseAdmin.Messaging;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Helpers.Utility;
|
||||
using Marco.Pms.Model.DocumentManager;
|
||||
@ -11,7 +10,6 @@ using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.DocumentManager;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -165,8 +163,9 @@ namespace Marco.Pms.Services.Controllers
|
||||
if (documentFilter.IsUploadedAt)
|
||||
{
|
||||
documentQuery = documentQuery.Where(da =>
|
||||
da.UploadedAt.Date >= documentFilter.StartDate.Value.Date &&
|
||||
da.UploadedAt.Date <= documentFilter.EndDate.Value.Date);
|
||||
da.UpdatedAt.HasValue &&
|
||||
da.UpdatedAt.Value.Date >= documentFilter.StartDate.Value.Date &&
|
||||
da.UpdatedAt.Value.Date <= documentFilter.EndDate.Value.Date);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -704,6 +703,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
return NotFound(ApiResponse<object>.ErrorResponse($"{(entityType == EmployeeEntity ? "Employee" : "Project")} Not Found", "Entity not found in database", 404));
|
||||
}
|
||||
|
||||
var isDocumentExist = await _context.DocumentAttachments.AnyAsync(da => da.DocumentId == model.DocumentId && da.TenantId == tenantId);
|
||||
if (isDocumentExist)
|
||||
{
|
||||
_logger.LogWarning("{DocumentId} is already existed in database", model.DocumentId ?? string.Empty);
|
||||
return StatusCode(409, ApiResponse<object>.ErrorResponse("Document already existed", "Document already existed in database", 409));
|
||||
}
|
||||
|
||||
// Map DTO to DB entity
|
||||
var attachment = _mapper.Map<DocumentAttachment>(model);
|
||||
attachment.UploadedAt = DateTime.UtcNow;
|
||||
@ -835,39 +841,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
response.ParentAttachmentId = versionMapping.ParentAttachmentId;
|
||||
response.Version = versionMapping.Version;
|
||||
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
if (EmployeeEntity == entityType && model.EntityId != loggedInEmployee.Id)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Document added to Employee.",
|
||||
Body = $"Document added to your profile of type \"{documentType.Name}\" by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendEmployeeDocumentMessageAsync(attachment.Id, model.EntityId, notification, tenantId);
|
||||
}
|
||||
if (ProjectEntity == entityType)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Document added to Project.",
|
||||
Body = $"Document added to your Project of type \"{documentType.Name}\" by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendProjectDocumentMessageAsync(attachment.Id, model.EntityId, notification, tenantId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Document added successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -894,6 +867,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
// Get current logged-in employee for authentication/auditing
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var tenantId = loggedInEmployee.TenantId;
|
||||
|
||||
_logger.LogInfo("Attempting to verify document. EmployeeId: {EmployeeId}, DocumentId: {DocumentId}, IsVerify: {IsVerify}",
|
||||
loggedInEmployee.Id, id, isVerify);
|
||||
@ -906,7 +880,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
if (documentAttachment == null)
|
||||
{
|
||||
_logger.LogWarning("Document attachment not found. Requested Id: {DocumentId}, TenantId: {TenantId}", id, tenantId);
|
||||
_logger.LogWarning("Document attachment not found. Requested Id: {DocumentId}, TenantId: {TenantId}", id, tenantId ?? Guid.Empty);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Attachment not found", "Attachment not found in database", 404));
|
||||
}
|
||||
|
||||
@ -958,41 +932,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
|
||||
_logger.LogInfo("Document verified successfully. DocumentId: {DocumentId}, VerifiedBy: {EmployeeId}", documentAttachment.Id, loggedInEmployee.Id);
|
||||
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
var entityType = documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId;
|
||||
var documentType = documentAttachment.DocumentType;
|
||||
|
||||
if (EmployeeEntity == entityType && documentAttachment.EntityId != loggedInEmployee.Id)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Your Document is Verified.",
|
||||
Body = $"Your Document of type \"{documentType?.Name}\" is Verified by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendEmployeeDocumentMessageAsync(documentAttachment.Id, documentAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
if (ProjectEntity == entityType)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Document for your project is verified",
|
||||
Body = $"Document for your Project of type \"{documentType?.Name}\" is Verified by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendProjectDocumentMessageAsync(documentAttachment.Id, documentAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
|
||||
});
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Document is verified successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1099,6 +1038,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
return NotFound(ApiResponse<object>.ErrorResponse($"{(entityType == EmployeeEntity ? "Employee" : "Project")} Not Found", "Entity not found in database", 404));
|
||||
}
|
||||
|
||||
var isDocumentExist = await _context.DocumentAttachments.AnyAsync(da => da.Id != model.Id && da.DocumentId == model.DocumentId && da.TenantId == tenantId);
|
||||
if (isDocumentExist)
|
||||
{
|
||||
_logger.LogWarning("{DocumentId} is already existed in database", model.DocumentId ?? string.Empty);
|
||||
return StatusCode(409, ApiResponse<object>.ErrorResponse("Document already existed", "Document already existed in database", 409));
|
||||
}
|
||||
|
||||
// Prepare for versioning
|
||||
var oldVersionMapping = await _context.AttachmentVersionMappings
|
||||
.FirstOrDefaultAsync(av => av.ChildAttachmentId == oldAttachment.Id && av.TenantId == tenantId);
|
||||
@ -1230,7 +1176,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
oldAttachment.DocumentId = model.DocumentId;
|
||||
oldAttachment.Description = model.Description;
|
||||
oldAttachment.DocumentDataId = document.Id;
|
||||
if (oldAttachment.IsVerified != null)
|
||||
if (oldAttachment.IsVerified == true)
|
||||
{
|
||||
oldAttachment.IsVerified = null;
|
||||
_logger.LogInfo("Reset verification flag for AttachmentId: {AttachmentId}", oldAttachment.Id);
|
||||
@ -1250,7 +1196,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
oldAttachment.Name = model.Name;
|
||||
oldAttachment.DocumentId = model.DocumentId;
|
||||
oldAttachment.Description = model.Description;
|
||||
if (oldAttachment.IsVerified != null)
|
||||
if (oldAttachment.IsVerified == true)
|
||||
{
|
||||
oldAttachment.IsVerified = null;
|
||||
_logger.LogInfo("Reset verification flag for AttachmentId: {AttachmentId}", oldAttachment.Id);
|
||||
@ -1342,39 +1288,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
response.Version = newVersionMapping.Version;
|
||||
_logger.LogInfo("API completed successfully for AttachmentId: {AttachmentId}", newAttachment.Id);
|
||||
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
if (EmployeeEntity == entityType && newAttachment.EntityId != loggedInEmployee.Id)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Your Document is updated.",
|
||||
Body = $"Your Document of type \"{documentType?.Name}\" is updated by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendEmployeeDocumentMessageAsync(newAttachment.Id, newAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
if (ProjectEntity == entityType)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = "Your Project Document is updated.",
|
||||
Body = $"Your Project Document of type \"{documentType?.Name}\" is updated by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendProjectDocumentMessageAsync(newAttachment.Id, newAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Document Updated successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1418,14 +1331,15 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
|
||||
// Check if the logged in employee has permission to delete OR is the owner of the document attachment
|
||||
var hasDeletePermission = false;
|
||||
var hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id);
|
||||
var hasViewPermission = false;
|
||||
if (ProjectEntity == documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId)
|
||||
{
|
||||
hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id, documentAttachment.EntityId);
|
||||
hasViewPermission = await _permission.HasPermission(PermissionsMaster.ViewDocument, loggedInEmployee.Id, documentAttachment.EntityId);
|
||||
}
|
||||
else if (EmployeeEntity == documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId)
|
||||
{
|
||||
hasDeletePermission = await _permission.HasPermission(PermissionsMaster.DeleteDocument, loggedInEmployee.Id);
|
||||
hasViewPermission = await _permission.HasPermission(PermissionsMaster.ViewDocument, loggedInEmployee.Id);
|
||||
}
|
||||
if (!hasDeletePermission && loggedInEmployee.Id != documentAttachment.EntityId)
|
||||
{
|
||||
@ -1457,42 +1371,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
// Log the successful completion of the operation
|
||||
_logger.LogInfo("DocumentAttachment ID: {DocumentId} has been {Message} by employee ID: {EmployeeId}", id, message, loggedInEmployee.Id);
|
||||
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 entityType = documentAttachment.DocumentType?.DocumentCategory?.EntityTypeId;
|
||||
var documentType = documentAttachment.DocumentType;
|
||||
|
||||
var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
if (EmployeeEntity == entityType && documentAttachment.EntityId != loggedInEmployee.Id)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = $"Your Document is {message}.",
|
||||
Body = $"Your Document of type \"{documentType?.Name}\" is {message} by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendEmployeeDocumentMessageAsync(documentAttachment.Id, documentAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
if (ProjectEntity == entityType)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = "Your Project Document is {message}.",
|
||||
Body = $"Your Project Document of type \"{documentType?.Name}\" is {message} by {name}"
|
||||
};
|
||||
|
||||
await _firebase.SendProjectDocumentMessageAsync(documentAttachment.Id, documentAttachment.EntityId, notification, tenantId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Return success response
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { }, $"Document attachment is {message}", 200));
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Data;
|
||||
using System.Net;
|
||||
@ -32,9 +31,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
public class EmployeeController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
private readonly EmployeeHelper _employeeHelper;
|
||||
@ -47,26 +45,12 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly Guid tenantId;
|
||||
private readonly Guid organizationId;
|
||||
|
||||
|
||||
public EmployeeController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IEmailSender emailSender,
|
||||
ApplicationDbContext context,
|
||||
EmployeeHelper employeeHelper,
|
||||
UserHelper userHelper,
|
||||
IConfiguration configuration,
|
||||
ILoggingService logger,
|
||||
IHubContext<MarcoHub> signalR,
|
||||
PermissionServices permission,
|
||||
IProjectServices projectServices,
|
||||
IMapper mapper,
|
||||
GeneralHelper generalHelper)
|
||||
public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
|
||||
ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
|
||||
IHubContext<MarcoHub> signalR, PermissionServices permission, IProjectServices projectServices, IMapper mapper, GeneralHelper generalHelper)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_context = context;
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
@ -80,7 +64,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
_projectServices = projectServices;
|
||||
_mapper = mapper;
|
||||
tenantId = _userHelper.GetTenantId();
|
||||
organizationId = _userHelper.GetCurrentOrganizationId();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -122,93 +105,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("list/organizations/{projectId}")]
|
||||
public async Task<IActionResult> GetEmployeesByProjectAsync(Guid projectId, [FromQuery] string searchString, [FromQuery] Guid? organizationId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the currently logged-in employee information
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var projectTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
|
||||
});
|
||||
|
||||
var tenantTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId);
|
||||
});
|
||||
|
||||
await Task.WhenAll(projectTask, tenantTask);
|
||||
|
||||
var project = projectTask.Result;
|
||||
var tenant = tenantTask.Result;
|
||||
|
||||
if (project == null || tenant == null)
|
||||
{
|
||||
_logger.LogWarning("Project {ProjectId} not found in database for tenant {TenantId}", projectId, tenantId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
|
||||
}
|
||||
// Check if the logged-in employee has permission for the requested project
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
|
||||
if (!hasProjectPermission)
|
||||
{
|
||||
_logger.LogWarning("User {EmployeeId} attempts to get employees for project {ProjectId} without permission", loggedInEmployee.Id, projectId);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User does not have access to view the employees for this project", 403));
|
||||
}
|
||||
|
||||
var organizationQuery = _context.ProjectOrgMappings
|
||||
.Include(po => po.ProjectService)
|
||||
.Where(po => po.ProjectService != null && po.ProjectService.ProjectId == projectId);
|
||||
|
||||
if (loggedInEmployee.OrganizationId != project.PMCId && loggedInEmployee.OrganizationId != project.PromoterId && loggedInEmployee.OrganizationId != tenant.OrganizationId)
|
||||
{
|
||||
organizationQuery = organizationQuery.Where(po => po.ParentOrganizationId == loggedInEmployee.OrganizationId || po.OrganizationId == loggedInEmployee.OrganizationId);
|
||||
}
|
||||
|
||||
var organizationIds = await organizationQuery.Select(po => po.OrganizationId).ToListAsync();
|
||||
|
||||
if (loggedInEmployee.OrganizationId == project.PMCId || loggedInEmployee.OrganizationId == project.PromoterId || loggedInEmployee.OrganizationId == tenant.OrganizationId)
|
||||
{
|
||||
organizationIds.Add(project.PMCId);
|
||||
organizationIds.Add(project.PromoterId);
|
||||
organizationIds.Add(tenant.OrganizationId);
|
||||
}
|
||||
|
||||
// Fetch employees allocated to the project matching the search criteria
|
||||
var employeesQuery = _context.Employees
|
||||
.AsNoTracking() // Improves performance by disabling change tracking for read-only query
|
||||
.Include(e => e.JobRole)
|
||||
.Where(e => (e.FirstName + " " + e.LastName).Contains(searchString) && organizationIds.Contains(e.OrganizationId));
|
||||
|
||||
if (organizationId.HasValue)
|
||||
{
|
||||
employeesQuery = employeesQuery.Where(e => e.OrganizationId == organizationId);
|
||||
}
|
||||
|
||||
var employees = await employeesQuery
|
||||
.ToListAsync();
|
||||
|
||||
var result = employees.Select(e => _mapper.Map<EmployeeVM>(e)).Distinct().ToList();
|
||||
_logger.LogInfo("Employees fetched for project {ProjectId} by user {EmployeeId}. Count: {Count}", projectId, loggedInEmployee.Id, employees.Count);
|
||||
|
||||
// Return the employee list wrapped in a successful API response
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, "Employee list fetched successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the exception and return a 500 status code with error message
|
||||
_logger.LogError(ex, "Error occurred while fetching employees for project {ProjectId}", projectId);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal server error", "An unexpected error occurred", 500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("list/{projectId?}")]
|
||||
public async Task<IActionResult> GetEmployeesByProjectAsync(Guid? projectId, [FromQuery] bool showInactive = false)
|
||||
[HttpGet]
|
||||
[Route("list/{projectid?}")]
|
||||
public async Task<IActionResult> GetEmployeesByProject(Guid? projectid, [FromQuery] bool ShowInactive)
|
||||
{
|
||||
// Step 1: Validate incoming request model state
|
||||
if (!ModelState.IsValid)
|
||||
@ -222,117 +121,61 @@ namespace MarcoBMS.Services.Controllers
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
}
|
||||
|
||||
List<EmployeeVM> result = new List<EmployeeVM>();
|
||||
try
|
||||
// Step 2: Get logged-in employee
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
_logger.LogInfo("GetEmployeesByProject called by EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, ShowInactive: {ShowInactive}",
|
||||
loggedInEmployee.Id, projectid ?? Guid.Empty, ShowInactive);
|
||||
|
||||
// Step 3: Fetch project access and permissions
|
||||
var projectIds = await _projectServices.GetMyProjectIdsAsync(tenantId, loggedInEmployee);
|
||||
|
||||
var hasViewAllEmployeesPermission = await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
|
||||
var hasViewTeamMembersPermission = await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id);
|
||||
|
||||
List<EmployeeVM> result = new();
|
||||
|
||||
// Step 4: Determine access level and fetch employees accordingly
|
||||
if (hasViewAllEmployeesPermission || projectid != null)
|
||||
{
|
||||
// Dependency injection scope for services
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
result = await _employeeHelper.GetEmployeeByProjectId(tenantId, projectid, ShowInactive);
|
||||
_logger.LogInfo("Employee list fetched using full access or specific project.");
|
||||
}
|
||||
else if (hasViewTeamMembersPermission && !ShowInactive)
|
||||
{
|
||||
var employeeIds = await _context.ProjectAllocations
|
||||
.Where(pa => projectIds.Contains(pa.ProjectId) && pa.IsActive && pa.TenantId == tenantId)
|
||||
.Select(pa => pa.EmployeeId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// Step 2: Get logged-in employee details
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
_logger.LogInfo("GetEmployeesByProject called. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, showInactive: {ShowInactive}",
|
||||
loggedInEmployee.Id, projectId ?? Guid.Empty, showInactive);
|
||||
|
||||
// Step 3: Fetch permissions concurrently
|
||||
var viewAllTask = Task.Run(async () =>
|
||||
{
|
||||
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
|
||||
});
|
||||
var viewTeamTask = Task.Run(async () =>
|
||||
{
|
||||
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
return await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id);
|
||||
});
|
||||
|
||||
await Task.WhenAll(viewAllTask, viewTeamTask);
|
||||
|
||||
var hasViewAllEmployeesPermission = viewAllTask.Result;
|
||||
var hasViewTeamMembersPermission = viewTeamTask.Result;
|
||||
|
||||
List<Employee> employees = new List<Employee>();
|
||||
|
||||
// Step 4: Query based on permission
|
||||
if (hasViewAllEmployeesPermission && !projectId.HasValue)
|
||||
{
|
||||
// OrganizationId needs to be retrieved from loggedInEmployee or context based on your app's structure
|
||||
var employeeQuery = _context.Employees
|
||||
.AsNoTracking() // Optimize EF query for read-only operation[web:1][web:13][web:18]
|
||||
.Include(e => e.JobRole)
|
||||
.Where(e => e.OrganizationId == organizationId);
|
||||
|
||||
employeeQuery = showInactive
|
||||
? employeeQuery.Where(e => !e.IsActive)
|
||||
: employeeQuery.Where(e => e.IsActive);
|
||||
|
||||
employees = await employeeQuery.ToListAsync();
|
||||
_logger.LogInfo("Employee list fetched with full access. Count: {Count}", employees.Count);
|
||||
}
|
||||
else if (hasViewTeamMembersPermission && !showInactive && !projectId.HasValue)
|
||||
{
|
||||
// Only active team members with limited permission
|
||||
var projectIds = await _projectServices.GetMyProjectIdsAsync(tenantId, loggedInEmployee);
|
||||
|
||||
employees = await _context.ProjectAllocations
|
||||
.AsNoTracking()
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(pa =>
|
||||
projectIds.Contains(pa.ProjectId)
|
||||
&& pa.IsActive
|
||||
&& pa.Employee != null
|
||||
&& pa.Employee.IsActive
|
||||
&& pa.TenantId == tenantId)
|
||||
.Select(pa => pa.Employee!)
|
||||
var employees = await _context.Employees
|
||||
.Include(fp => fp.JobRole)
|
||||
.Where(e => employeeIds.Contains(e.Id) && e.JobRole != null && e.IsActive && e.TenantId == tenantId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogInfo("Employee list fetched with limited access (active only). Count: {Count}", employees.Count);
|
||||
}
|
||||
|
||||
// If a specific projectId is provided, override employee fetching to ensure strict project context
|
||||
if (projectId.HasValue)
|
||||
{
|
||||
employees = await _context.ProjectAllocations
|
||||
.AsNoTracking()
|
||||
.Include(pa => pa.Employee)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
.Where(pa =>
|
||||
pa.ProjectId == projectId
|
||||
&& pa.IsActive
|
||||
&& pa.Employee != null
|
||||
&& pa.Employee.IsActive
|
||||
&& pa.TenantId == tenantId)
|
||||
.Select(pa => pa.Employee!)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogInfo("Employee list fetched for specific project. ProjectId: {ProjectId}. Count: {Count}",
|
||||
projectId, employees.Count);
|
||||
}
|
||||
|
||||
// Step 5: Map to view model
|
||||
result = employees.Select(e => _mapper.Map<EmployeeVM>(e)).Distinct().ToList();
|
||||
|
||||
_logger.LogInfo("Employees successfully fetched. EmployeeId: {EmployeeId} for ProjectId: {ProjectId}. Final Count: {Count}",
|
||||
loggedInEmployee.Id, projectId ?? Guid.Empty, result.Count);
|
||||
result = employees.Select(e => e.ToEmployeeVMFromEmployee()).ToList();
|
||||
|
||||
_logger.LogInfo("Employee list fetched using limited access (active only).");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Access denied for EmployeeId: {EmployeeId} - insufficient permissions.", loggedInEmployee.Id);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, "Filter applied.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Step 6: Error logging and response[web:6]
|
||||
_logger.LogError(ex, "Exception occurred while getting the list of employees");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal server error. Please try again later.", null, 500));
|
||||
}
|
||||
|
||||
// Step 5: Log and return results
|
||||
_logger.LogInfo("Employees fetched successfully by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}. Count: {Count}",
|
||||
loggedInEmployee.Id, projectid ?? Guid.Empty, result.Count);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result, "Filter applied.", 200));
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("basic")]
|
||||
public async Task<IActionResult> GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, [FromQuery] bool allEmployee)
|
||||
public async Task<IActionResult> GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var employeeQuery = _context.Employees.Where(e => e.IsActive);
|
||||
var employeeQuery = _context.Employees.Where(e => e.TenantId == tenantId);
|
||||
if (projectId != null && projectId != Guid.Empty)
|
||||
{
|
||||
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value);
|
||||
@ -344,26 +187,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
var employeeIds = await _context.ProjectAllocations.Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.TenantId == tenantId).Select(p => p.EmployeeId).ToListAsync();
|
||||
employeeQuery = employeeQuery.Where(e => employeeIds.Contains(e.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
employeeQuery = employeeQuery.Where(e => e.OrganizationId == organizationId);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
var searchStringLower = searchString.ToLower();
|
||||
employeeQuery = employeeQuery.Where(e => (e.FirstName + " " + e.LastName).ToLower().Contains(searchStringLower));
|
||||
}
|
||||
|
||||
var query = employeeQuery.OrderBy(e => e.FirstName);
|
||||
|
||||
if (!allEmployee)
|
||||
{
|
||||
query = (IOrderedQueryable<Employee>)query.Take(10);
|
||||
}
|
||||
|
||||
var response = await query
|
||||
.Select(e => _mapper.Map<BasicEmployeeVM>(e))
|
||||
.ToListAsync();
|
||||
var response = await employeeQuery.Take(10).Select(e => _mapper.Map<BasicEmployeeVM>(e)).ToListAsync();
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of employees fetched successfully", 200));
|
||||
}
|
||||
|
||||
@ -473,7 +303,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("old/manage")]
|
||||
[HttpPost("manage")]
|
||||
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
@ -606,203 +436,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse("Success.", responsemessage, 200));
|
||||
}
|
||||
|
||||
[HttpPost("manage")]
|
||||
public async Task<IActionResult> CreateEmployeeAsync([FromBody] CreateUserDto model)
|
||||
{
|
||||
// Correlation and context capture for logs
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
_logger.LogWarning("Model is null in CreateEmployeeAsync");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid payload", "Request body is required", 400));
|
||||
}
|
||||
|
||||
// Basic validation
|
||||
if (model.HasApplicationAccess && string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
_logger.LogWarning("Application access requested but email is missing");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid email", "Application users must have a valid email", 400));
|
||||
}
|
||||
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
// Load existing employee if updating, constrained by organization scope
|
||||
Employee? existingEmployee = null;
|
||||
if (model.Id.HasValue && model.Id.Value != Guid.Empty)
|
||||
{
|
||||
existingEmployee = await _context.Employees
|
||||
.FirstOrDefaultAsync(e => e.Id == model.Id);
|
||||
if (existingEmployee == null)
|
||||
{
|
||||
_logger.LogInfo("Employee not found for update. Id={EmployeeId}", model.Id);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found in database", 404));
|
||||
}
|
||||
}
|
||||
|
||||
// Identity user creation path (only if needed)
|
||||
ApplicationUser? identityUserToCreate = null;
|
||||
ApplicationUser? createdIdentityUser = null;
|
||||
|
||||
if (model.HasApplicationAccess)
|
||||
{
|
||||
// Only attempt identity resolution/creation if email supplied and either:
|
||||
// - Creating new employee, or
|
||||
// - Updating but existing employee does not have ApplicationUserId
|
||||
var needsIdentity = string.IsNullOrWhiteSpace(existingEmployee?.ApplicationUserId);
|
||||
if (needsIdentity && !string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser == null)
|
||||
{
|
||||
// Seat check only when provisioning a new identity user
|
||||
var isSeatsAvailable = await _generalHelper.CheckSeatsRemainingAsync(tenantId);
|
||||
if (!isSeatsAvailable)
|
||||
{
|
||||
_logger.LogWarning("Maximum users reached for Tenant {TenantId}. Cannot create identity user for {Email}", tenantId, model.Email);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||
"Maximum number of users reached. Cannot add new user",
|
||||
"Maximum number of users reached. Cannot add new user", 400));
|
||||
}
|
||||
|
||||
identityUserToCreate = new ApplicationUser
|
||||
{
|
||||
UserName = model.Email,
|
||||
Email = model.Email,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// If identity exists, re-use it; do not re-create
|
||||
createdIdentityUser = existingUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For create path: enforce uniqueness of employee email if applicable to business rules
|
||||
// Consider adding a unique filtered index: (OrganizationId, Email) WHERE Email IS NOT NULL
|
||||
if (!model.Id.HasValue || model.Id == Guid.Empty)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
var emailExists = await _context.Employees
|
||||
.AnyAsync(e => e.Email == model.Email);
|
||||
if (emailExists)
|
||||
{
|
||||
_logger.LogInfo("Employee email already exists. Email={Email}", model.Email);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse(
|
||||
"Employee with email already exists",
|
||||
"Employee with this email already exists", 403));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create identity user if needed
|
||||
if (identityUserToCreate != null && !string.IsNullOrWhiteSpace(identityUserToCreate.Email))
|
||||
{
|
||||
var createResult = await _userManager.CreateAsync(identityUserToCreate, "User@123");
|
||||
if (!createResult.Succeeded)
|
||||
{
|
||||
_logger.LogWarning("Failed to create identity user for {Email}. Errors={Errors}",
|
||||
identityUserToCreate.Email,
|
||||
string.Join(", ", createResult.Errors.Select(e => $"{e.Code}:{e.Description}")));
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", createResult.Errors, 400));
|
||||
}
|
||||
|
||||
createdIdentityUser = identityUserToCreate;
|
||||
_logger.LogInfo("Identity user created. IdentityUserId={UserId}, Email={Email}",
|
||||
createdIdentityUser.Id, createdIdentityUser.Email);
|
||||
}
|
||||
|
||||
|
||||
Guid employeeId;
|
||||
EmployeeVM employeeVM;
|
||||
string responseMessage;
|
||||
|
||||
if (existingEmployee != null)
|
||||
{
|
||||
// Update flow
|
||||
_mapper.Map(model, existingEmployee);
|
||||
|
||||
if (createdIdentityUser != null && !string.IsNullOrWhiteSpace(createdIdentityUser.Email))
|
||||
{
|
||||
existingEmployee.ApplicationUserId = createdIdentityUser.Id;
|
||||
await SendResetIfApplicableAsync(createdIdentityUser, existingEmployee.FirstName ?? "User");
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
employeeId = existingEmployee.Id;
|
||||
employeeVM = _mapper.Map<EmployeeVM>(existingEmployee);
|
||||
responseMessage = "Employee Updated Successfully";
|
||||
|
||||
_logger.LogInfo("Employee updated. EmployeeId={EmployeeId}, Org={OrgId}", employeeId, existingEmployee.OrganizationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create flow
|
||||
var newEmployee = _mapper.Map<Employee>(model);
|
||||
newEmployee.IsSystem = false;
|
||||
newEmployee.IsActive = true;
|
||||
newEmployee.IsPrimary = false;
|
||||
|
||||
if (createdIdentityUser != null && !string.IsNullOrWhiteSpace(createdIdentityUser.Email))
|
||||
{
|
||||
newEmployee.ApplicationUserId = createdIdentityUser.Id;
|
||||
await SendResetIfApplicableAsync(createdIdentityUser, newEmployee.FirstName ?? "User");
|
||||
}
|
||||
|
||||
await _context.Employees.AddAsync(newEmployee);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
employeeId = newEmployee.Id;
|
||||
employeeVM = _mapper.Map<EmployeeVM>(newEmployee);
|
||||
responseMessage = "Employee Created Successfully";
|
||||
|
||||
_logger.LogInfo("Employee created. EmployeeId={EmployeeId}, Org={OrgId}", employeeId, newEmployee.OrganizationId);
|
||||
}
|
||||
|
||||
await transaction.CommitAsync();
|
||||
|
||||
// SignalR notification
|
||||
var notification = new
|
||||
{
|
||||
LoggedInUserId = loggedInEmployee.Id,
|
||||
Keyword = "Employee",
|
||||
EmployeeId = employeeId
|
||||
};
|
||||
|
||||
// Consider broadcasting to tenant/organization group instead of Clients.All to avoid cross-tenant leaks:
|
||||
// await _signalR.Clients.Group($"org:{model.OrganizationId}").SendAsync("NotificationEventHandler", notification);
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
|
||||
_logger.LogInfo("Notification broadcasted for EmployeeId={EmployeeId}", employeeId);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(employeeVM, responseMessage, 200));
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(dbEx, "Database exception occurred while managing employee");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse(
|
||||
"Internal exception occurred",
|
||||
"Internal database exception has occurred", 500));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "Unhandled exception occurred while managing employee");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse(
|
||||
"Internal exception occurred",
|
||||
"Internal exception has occurred", 500));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("manage-mobile")]
|
||||
public async Task<IActionResult> CreateUserMoblie([FromBody] MobileUserManageDto model)
|
||||
{
|
||||
@ -869,7 +502,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
existingEmployee.LastName = model.LastName;
|
||||
existingEmployee.Gender = model.Gender;
|
||||
existingEmployee.PhoneNumber = model.PhoneNumber;
|
||||
existingEmployee.JoiningDate = model.JoiningDate;
|
||||
existingEmployee.JobRoleId = model.JobRoleId;
|
||||
existingEmployee.Photo = imageBytes;
|
||||
|
||||
@ -882,206 +514,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("app/manage")]
|
||||
public async Task<IActionResult> CreateUserMobileAsync([FromBody] MobileUserManageDto model)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
if (tenantId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("Tenant resolution failed in CreateUserMobile"); // structured warning
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Unauthorized tenant context", "Unauthorized", 403));
|
||||
}
|
||||
|
||||
if (model is null)
|
||||
{
|
||||
_logger.LogWarning("Null payload in CreateUserMobile for Tenant {TenantId}", tenantId); // validation log
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invalid data", 400));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.FirstName) || string.IsNullOrWhiteSpace(model.PhoneNumber))
|
||||
{
|
||||
_logger.LogWarning("Missing required fields FirstName/Phone for Tenant {TenantId}", tenantId); // validation log
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("First name and phone number are required.", "Required fields missing", 400));
|
||||
}
|
||||
|
||||
// Strict Base64 parse
|
||||
byte[]? imageBytes = null;
|
||||
if (!string.IsNullOrWhiteSpace(model.ProfileImage))
|
||||
{
|
||||
try
|
||||
{
|
||||
imageBytes = Convert.FromBase64String(model.ProfileImage);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Invalid base64 image in CreateUserMobile for Tenant {TenantId}", tenantId); // input issue
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid image format.", "Invalid image", 400));
|
||||
}
|
||||
}
|
||||
if (model.Id == null || model.Id == Guid.Empty)
|
||||
{
|
||||
var emailExists = await _context.Employees
|
||||
.AnyAsync(e => e.Email == model.Email && e.OrganizationId == model.OrganizationId);
|
||||
|
||||
if (emailExists && !string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
_logger.LogInfo("Employee email already exists in org. Email={Email}, Org={OrgId}", model.Email, model.OrganizationId);
|
||||
return StatusCode(409, ApiResponse<object>.ErrorResponse("Employee with email already exists", "Employee with this email already exists", 409));
|
||||
}
|
||||
|
||||
// Create path: map only allowed fields
|
||||
var employee = new Employee
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tenantId,
|
||||
FirstName = model.FirstName.Trim(),
|
||||
LastName = model.LastName?.Trim(),
|
||||
Email = model.Email,
|
||||
Gender = model.Gender,
|
||||
PhoneNumber = model.PhoneNumber,
|
||||
JoiningDate = model.JoiningDate,
|
||||
JobRoleId = model.JobRoleId,
|
||||
Photo = imageBytes,
|
||||
OrganizationId = model.OrganizationId,
|
||||
HasApplicationAccess = model.HasApplicationAccess,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(model.Email) && model.HasApplicationAccess)
|
||||
{
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser == null)
|
||||
{
|
||||
existingUser = new ApplicationUser
|
||||
{
|
||||
UserName = model.Email,
|
||||
Email = model.Email,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
var createResult = await _userManager.CreateAsync(existingUser, "User@123");
|
||||
if (!createResult.Succeeded)
|
||||
{
|
||||
_logger.LogWarning("Failed to create identity user for {Email}. Errors={Errors}",
|
||||
existingUser.Email,
|
||||
string.Join(", ", createResult.Errors.Select(e => $"{e.Code}:{e.Description}")));
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", createResult.Errors, 400));
|
||||
}
|
||||
|
||||
await SendResetIfApplicableAsync(existingUser, employee.FirstName ?? "User");
|
||||
employee.ApplicationUserId = existingUser.Id;
|
||||
}
|
||||
}
|
||||
|
||||
_context.Employees.Add(employee);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var employeeVM = _mapper.Map<EmployeeVM>(employee);
|
||||
|
||||
var notification = new
|
||||
{
|
||||
LoggedInUserId = loggedInEmployee?.Id,
|
||||
Keyword = "Employee",
|
||||
EmployeeId = employee.Id
|
||||
};
|
||||
|
||||
// Consider broadcasting to tenant/organization group instead of Clients.All to avoid cross-tenant leaks:
|
||||
// await _signalR.Clients.Group($"org:{model.OrganizationId}").SendAsync("NotificationEventHandler", notification);
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
|
||||
_logger.LogInfo("Employee {EmployeeId} created in Tenant {TenantId}", employee.Id, tenantId); // success
|
||||
return Ok(ApiResponse<object>.SuccessResponse(employeeVM, "Employee created successfully", 200));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update path: fetch scoped to tenant
|
||||
var employeeId = model.Id.Value;
|
||||
var existingEmployee = await _context.Employees
|
||||
.FirstOrDefaultAsync(e => e.Id == employeeId); // tenant-safe lookup
|
||||
|
||||
if (existingEmployee is null)
|
||||
{
|
||||
_logger.LogWarning("Update attempted for missing Employee {EmployeeId} in Tenant {TenantId}", employeeId, tenantId); // not found
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Not found", 404));
|
||||
}
|
||||
|
||||
// Update allowed fields only
|
||||
existingEmployee.FirstName = model.FirstName.Trim();
|
||||
existingEmployee.LastName = model.LastName?.Trim();
|
||||
existingEmployee.Gender = model.Gender;
|
||||
existingEmployee.PhoneNumber = model.PhoneNumber;
|
||||
existingEmployee.JoiningDate = model.JoiningDate;
|
||||
existingEmployee.JobRoleId = model.JobRoleId;
|
||||
existingEmployee.OrganizationId = model.OrganizationId;
|
||||
existingEmployee.HasApplicationAccess = model.HasApplicationAccess;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(existingEmployee.Email) && !string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
var emailExists = await _context.Employees
|
||||
.AnyAsync(e => e.Email == model.Email);
|
||||
|
||||
if (emailExists)
|
||||
{
|
||||
_logger.LogInfo("Employee email already exists in org. Email={Email}, Org={OrgId}", model.Email, model.OrganizationId);
|
||||
return StatusCode(409, ApiResponse<object>.ErrorResponse("Employee with email already exists", "Employee with this email already exists", 409));
|
||||
}
|
||||
existingEmployee.Email = model.Email;
|
||||
}
|
||||
|
||||
if (model.HasApplicationAccess && !string.IsNullOrWhiteSpace(model.Email) && string.IsNullOrWhiteSpace(existingEmployee.ApplicationUserId))
|
||||
{
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser == null)
|
||||
{
|
||||
existingUser = new ApplicationUser
|
||||
{
|
||||
UserName = model.Email,
|
||||
Email = model.Email,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
var createResult = await _userManager.CreateAsync(existingUser, "User@123");
|
||||
if (!createResult.Succeeded)
|
||||
{
|
||||
_logger.LogWarning("Failed to create identity user for {Email}. Errors={Errors}",
|
||||
existingUser.Email,
|
||||
string.Join(", ", createResult.Errors.Select(e => $"{e.Code}:{e.Description}")));
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", createResult.Errors, 400));
|
||||
}
|
||||
|
||||
await SendResetIfApplicableAsync(existingUser, existingEmployee.FirstName ?? "User");
|
||||
existingEmployee.ApplicationUserId = existingUser.Id;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageBytes != null)
|
||||
{
|
||||
existingEmployee.Photo = imageBytes;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var employeeVM = _mapper.Map<EmployeeVM>(existingEmployee);
|
||||
|
||||
var notification = new
|
||||
{
|
||||
LoggedInUserId = loggedInEmployee?.Id,
|
||||
Keyword = "Employee",
|
||||
EmployeeId = employeeId
|
||||
};
|
||||
|
||||
// Consider broadcasting to tenant/organization group instead of Clients.All to avoid cross-tenant leaks:
|
||||
// await _signalR.Clients.Group($"org:{model.OrganizationId}").SendAsync("NotificationEventHandler", notification);
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
|
||||
_logger.LogInfo("Employee {EmployeeId} updated in Tenant {TenantId}", existingEmployee.Id, tenantId); // success
|
||||
return Ok(ApiResponse<object>.SuccessResponse(employeeVM, "Employee updated successfully", 200));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> SuspendEmployee(Guid id, [FromQuery] bool active = false)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId);
|
||||
@ -1111,7 +546,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
var attendance = await _context.Attendes.Where(a => a.EmployeeId == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync();
|
||||
var attendance = await _context.Attendes.Where(a => a.EmployeeID == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync();
|
||||
if (attendance.Count != 0)
|
||||
{
|
||||
_logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id);
|
||||
@ -1165,19 +600,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
_logger.LogInfo("Application role mapping associated with employee ID {EmployeeId} has been removed.", employee.Id);
|
||||
}
|
||||
_logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id);
|
||||
|
||||
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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.
|
||||
|
||||
await _firebase.SendEmployeeSuspendMessageAsync(employee.Id, tenantId);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
try
|
||||
{
|
||||
@ -1253,14 +675,5 @@ namespace MarcoBMS.Services.Controllers
|
||||
return info;
|
||||
|
||||
}
|
||||
|
||||
// Prepare reset link sender helper
|
||||
private async Task SendResetIfApplicableAsync(ApplicationUser u, string firstName)
|
||||
{
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(u);
|
||||
var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}";
|
||||
await _emailSender.SendResetPasswordEmailOnRegister(u.Email ?? "", firstName, resetLink);
|
||||
_logger.LogInfo("Reset password email queued. Email={Email}", u.Email ?? "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Marco.Pms.Services.Controllers
|
||||
@ -23,32 +22,25 @@ namespace Marco.Pms.Services.Controllers
|
||||
[Authorize]
|
||||
public class ImageController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly S3UploadService _s3Service;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly PermissionServices _permission;
|
||||
private readonly Guid tenantId;
|
||||
public ImageController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
ApplicationDbContext context,
|
||||
S3UploadService s3Service,
|
||||
UserHelper userHelper,
|
||||
ILoggingService logger,
|
||||
PermissionServices permission)
|
||||
public ImageController(ApplicationDbContext context, S3UploadService s3Service, UserHelper userHelper, ILoggingService logger, PermissionServices permission)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_s3Service = s3Service ?? throw new ArgumentNullException(nameof(s3Service));
|
||||
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_permission = permission ?? throw new ArgumentNullException(nameof(permission));
|
||||
_context = context;
|
||||
_s3Service = s3Service;
|
||||
_userHelper = userHelper;
|
||||
_logger = logger;
|
||||
tenantId = userHelper.GetTenantId();
|
||||
_permission = permission;
|
||||
}
|
||||
|
||||
[HttpGet("images/{projectId}")]
|
||||
|
||||
public async Task<IActionResult> GetImageList(Guid projectId, [FromQuery] string? filter, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10)
|
||||
public async Task<IActionResult> GetImageList(Guid projectId, [FromQuery] string? filter, [FromQuery] int? pageNumber = 1, [FromQuery] int? pageSize = 10)
|
||||
{
|
||||
_logger.LogInfo("[GetImageList] Called by Employee for ProjectId: {ProjectId}", projectId);
|
||||
|
||||
@ -87,28 +79,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
var taskQuery = _context.TaskAllocations
|
||||
.Include(t => t.Employee)
|
||||
.Include(t => t.ReportedBy)
|
||||
.Include(t => t.ApprovedBy)
|
||||
.Include(t => t.WorkStatus)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.ActivityMaster)
|
||||
.ThenInclude(a => a!.ActivityGroup)
|
||||
.ThenInclude(ag => ag!.Service)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkArea)
|
||||
.ThenInclude(wa => wa!.Floor)
|
||||
.ThenInclude(f => f!.Building)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkCategoryMaster)
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId &&
|
||||
t.TenantId == tenantId);
|
||||
|
||||
// Step 4: Extract filter values
|
||||
var buildingIds = imageFilter?.BuildingIds;
|
||||
var floorIds = imageFilter?.FloorIds;
|
||||
@ -118,60 +88,71 @@ namespace Marco.Pms.Services.Controllers
|
||||
var startDate = imageFilter?.StartDate;
|
||||
var endDate = imageFilter?.EndDate;
|
||||
var uploadedByIds = imageFilter?.UploadedByIds;
|
||||
var serviceIds = imageFilter?.ServiceIds;
|
||||
|
||||
if (buildingIds?.Any() ?? false)
|
||||
// Step 5: Fetch building > floor > area > work item hierarchy
|
||||
List<Building>? buildings = null;
|
||||
List<Floor>? floors = null;
|
||||
List<WorkArea>? workAreas = null;
|
||||
|
||||
if (buildingIds != null && buildingIds.Count > 0)
|
||||
{
|
||||
|
||||
taskQuery = taskQuery
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
buildingIds.Contains(t.WorkItem.WorkArea.Floor.BuildingId));
|
||||
buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId && buildingIds.Contains(b.Id))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
if (floorIds?.Any() ?? false)
|
||||
else
|
||||
{
|
||||
taskQuery = taskQuery
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
floorIds.Contains(t.WorkItem.WorkArea.FloorId));
|
||||
buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId)
|
||||
.ToListAsync();
|
||||
|
||||
buildingIds = buildings.Select(b => b.Id).ToList();
|
||||
}
|
||||
|
||||
if (workAreaIds?.Any() ?? false)
|
||||
if (floorIds != null && floorIds.Count > 0)
|
||||
{
|
||||
taskQuery = taskQuery
|
||||
.Where(t => t.WorkItem != null &&
|
||||
workAreaIds.Contains(t.WorkItem.WorkAreaId));
|
||||
floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId) && floorIds.Contains(f.Id))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
if (activityIds?.Any() == true)
|
||||
else
|
||||
{
|
||||
taskQuery = taskQuery
|
||||
.Where(t => t.WorkItem != null &&
|
||||
activityIds.Contains(t.WorkItem.ActivityId));
|
||||
floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId))
|
||||
.ToListAsync();
|
||||
|
||||
floorIds = floors.Select(f => f.Id).ToList();
|
||||
}
|
||||
if (workAreaIds != null && workAreaIds.Count > 0)
|
||||
{
|
||||
workAreas = await _context.WorkAreas
|
||||
.Where(wa => floorIds.Contains(wa.FloorId) && workAreaIds.Contains(wa.Id))
|
||||
.ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
workAreas = await _context.WorkAreas
|
||||
.Where(wa => floorIds.Contains(wa.FloorId))
|
||||
.ToListAsync();
|
||||
|
||||
workAreaIds = workAreas.Select(wa => wa.Id).ToList();
|
||||
}
|
||||
|
||||
var workItemsQuery = _context.WorkItems.Include(w => w.ActivityMaster).Include(w => w.WorkCategoryMaster)
|
||||
.Where(wi => workAreaIds.Contains(wi.WorkAreaId));
|
||||
if (activityIds?.Any() == true) workItemsQuery = workItemsQuery.Where(wi => activityIds.Contains(wi.ActivityId));
|
||||
|
||||
if (workCategoryIds?.Any() == true)
|
||||
{
|
||||
taskQuery = taskQuery
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkCategoryId.HasValue &&
|
||||
workCategoryIds.Contains(t.WorkItem.WorkCategoryId.Value));
|
||||
}
|
||||
|
||||
if (serviceIds?.Any() ?? false)
|
||||
{
|
||||
taskQuery = taskQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
serviceIds.Contains(t.WorkItem.ActivityMaster.ActivityGroup.ServiceId));
|
||||
workItemsQuery = workItemsQuery.Where(wi => wi.WorkCategoryMaster != null && workCategoryIds.Contains(wi.WorkCategoryMaster.Id));
|
||||
}
|
||||
var workItems = await workItemsQuery.ToListAsync();
|
||||
var workItemIds = workItems.Select(wi => wi.Id).ToList();
|
||||
|
||||
// Step 6: Fetch task allocations and comments
|
||||
var tasks = await taskQuery.ToListAsync();
|
||||
|
||||
var tasks = await _context.TaskAllocations.Include(t => t.ReportedBy)
|
||||
.Where(t => workItemIds.Contains(t.WorkItemId)).ToListAsync();
|
||||
var taskIds = tasks.Select(t => t.Id).ToList();
|
||||
|
||||
var comments = await _context.TaskComments.Include(c => c.Employee)
|
||||
@ -191,21 +172,21 @@ namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
docQuery = docQuery.Where(d => d.UploadedAt.Date >= startDate.Value.Date && d.UploadedAt.Date <= endDate.Value.Date);
|
||||
}
|
||||
|
||||
int totalRecords = await docQuery.GroupBy(d => d.BatchId).CountAsync();
|
||||
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
|
||||
|
||||
documents = await docQuery
|
||||
.GroupBy(d => d.BatchId)
|
||||
.OrderByDescending(g => g.Max(d => d.UploadedAt))
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(g => new DocumentBatchDto
|
||||
if (pageNumber != null && pageSize != null)
|
||||
{
|
||||
BatchId = g.Key,
|
||||
Documents = g.ToList()
|
||||
})
|
||||
.ToListAsync();
|
||||
documents = await docQuery
|
||||
.GroupBy(d => d.BatchId)
|
||||
.OrderByDescending(g => g.Max(d => d.UploadedAt))
|
||||
.Skip((pageNumber.Value - 1) * pageSize.Value)
|
||||
.Take(pageSize.Value)
|
||||
.Select(g => new DocumentBatchDto
|
||||
{
|
||||
BatchId = g.Key,
|
||||
Documents = g.ToList()
|
||||
})
|
||||
.ToListAsync();
|
||||
Console.Write("Pagenation Success");
|
||||
}
|
||||
|
||||
|
||||
// Step 8: Build response
|
||||
@ -228,10 +209,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
comment = comments.OrderBy(c => c.CommentDate).FirstOrDefault(c => c.TaskAllocationId == task.Id);
|
||||
}
|
||||
|
||||
var workItem = task!.WorkItem;
|
||||
var workArea = task!.WorkItem!.WorkArea;
|
||||
var floor = task!.WorkItem!.WorkArea!.Floor;
|
||||
var building = task!.WorkItem!.WorkArea!.Floor!.Building;
|
||||
var workItem = workItems.FirstOrDefault(w => w.Id == task?.WorkItemId);
|
||||
var workArea = workAreas.FirstOrDefault(wa => wa.Id == workItem?.WorkAreaId);
|
||||
var floor = floors.FirstOrDefault(f => f.Id == workArea?.FloorId);
|
||||
var building = buildings.FirstOrDefault(b => b.Id == floor?.BuildingId);
|
||||
|
||||
return new
|
||||
{
|
||||
@ -268,17 +249,8 @@ namespace Marco.Pms.Services.Controllers
|
||||
documentVM = documentVM.Where(d => d.Documents != null && d.Documents.Any(x => uploadedByIds.Contains(x.UploadedBy?.Id ?? Guid.Empty))).ToList();
|
||||
}
|
||||
|
||||
var VM = new
|
||||
{
|
||||
TotalCount = totalRecords,
|
||||
TotalPages = totalPages,
|
||||
CurrentPage = pageNumber,
|
||||
PageSize = pageSize,
|
||||
Data = documentVM
|
||||
};
|
||||
|
||||
_logger.LogInfo("[GetImageList] Fetched {Count} documents for ProjectId: {ProjectId}", documentVM.Count, projectId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(VM, $"{documentVM.Count} image records fetched successfully", 200));
|
||||
return Ok(ApiResponse<object>.SuccessResponse(documentVM, $"{documentVM.Count} image records fetched successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("batch/{batchId}")]
|
||||
@ -390,150 +362,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Images for provided batchId fetched successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("filter/{projectId}")]
|
||||
public async Task<IActionResult> GetFilterObjectAsync(Guid projectId, CancellationToken ct)
|
||||
{
|
||||
_logger.LogInfo("GetFilterObject started for ProjectId {ProjectId}", projectId); // start log [memory:1]
|
||||
|
||||
// Validate input early
|
||||
if (projectId == Guid.Empty)
|
||||
{
|
||||
Log.Warning("GetFilterObject received empty ProjectId"); // input validation [memory:1]
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid project id", 400));
|
||||
}
|
||||
|
||||
// Load only what is needed for attachments; project scoping will be enforced downstream
|
||||
// NOTE: Select only the columns required to reduce payload
|
||||
var taskAttachments = await _context.TaskAttachments
|
||||
.AsNoTracking()
|
||||
.Select(ta => new { ta.ReferenceId, ta.DocumentId })
|
||||
.ToListAsync(ct); // I/O bound work [memory:10]
|
||||
|
||||
if (taskAttachments.Count == 0)
|
||||
{
|
||||
Log.Information("No task attachments found for ProjectId {ProjectId}", projectId); // early exit [memory:1]
|
||||
var emptyResponse = new
|
||||
{
|
||||
Buildings = Array.Empty<object>(),
|
||||
Floors = Array.Empty<object>(),
|
||||
WorkAreas = Array.Empty<object>(),
|
||||
WorkCategories = Array.Empty<object>(),
|
||||
Activities = Array.Empty<object>(),
|
||||
UploadedBys = Array.Empty<object>(),
|
||||
Services = Array.Empty<object>()
|
||||
};
|
||||
return Ok(ApiResponse<object>.SuccessResponse(emptyResponse, "No data found", 200));
|
||||
}
|
||||
|
||||
// HashSets for O(1) membership tests and to dedupe upfront
|
||||
var referenceIds = new HashSet<Guid>(taskAttachments.Select(x => x.ReferenceId));
|
||||
var documentIds = new HashSet<Guid>(taskAttachments.Select(x => x.DocumentId));
|
||||
|
||||
_logger.LogDebug("Collected {ReferenceCount} referenceIds and {DocumentCount} documentIds", referenceIds.Count, documentIds.Count); // metrics [memory:1]
|
||||
|
||||
// Load comments for the references under this tenant
|
||||
var comments = await _context.TaskComments
|
||||
.AsNoTracking()
|
||||
.Where(tc => referenceIds.Contains(tc.Id) && tc.TenantId == tenantId)
|
||||
.Select(tc => new { tc.Id, tc.TaskAllocationId })
|
||||
.ToListAsync(ct); // filtered selection [memory:10]
|
||||
|
||||
var taskIds = new HashSet<Guid>(comments.Select(c => c.TaskAllocationId));
|
||||
_logger.LogDebug("Resolved {CommentCount} comments mapping to {TaskIdCount} taskIds", comments.Count, taskIds.Count); // observation [memory:1]
|
||||
|
||||
// IMPORTANT: Correct project filter should be == not !=
|
||||
// Include graph tailored to fields needed in final projection (avoid over-inclusion)
|
||||
// Avoid Task.Run for async EF (it doesn’t add value and can harm thread pool)
|
||||
var tasks = await _context.TaskAllocations
|
||||
.AsNoTracking()
|
||||
.Where(t =>
|
||||
t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId && // fixed filter
|
||||
(taskIds.Contains(t.Id) || referenceIds.Contains(t.Id)))
|
||||
.Include(t => t.WorkItem!)
|
||||
.ThenInclude(wi => wi.ActivityMaster!)
|
||||
.ThenInclude(a => a.ActivityGroup!)
|
||||
.ThenInclude(ag => ag.Service)
|
||||
.Include(t => t.WorkItem!)
|
||||
.ThenInclude(wi => wi.WorkArea!)
|
||||
.ThenInclude(wa => wa.Floor!)
|
||||
.ThenInclude(f => f.Building)
|
||||
.Include(t => t.WorkItem!)
|
||||
.ThenInclude(wi => wi.WorkCategoryMaster)
|
||||
// Only select fields used later to reduce tracked object size in memory
|
||||
.Select(t => new
|
||||
{
|
||||
TaskId = t.Id,
|
||||
Building = new { Id = t.WorkItem!.WorkArea!.Floor!.Building!.Id, Name = t.WorkItem.WorkArea.Floor.Building.Name },
|
||||
Floor = new { Id = t.WorkItem.WorkArea.Floor.Id, Name = t.WorkItem.WorkArea.Floor.FloorName },
|
||||
WorkArea = new { Id = t.WorkItem.WorkArea.Id, Name = t.WorkItem.WorkArea.AreaName },
|
||||
Activity = t.WorkItem.ActivityMaster == null ? null : new { Id = t.WorkItem.ActivityMaster.Id, Name = t.WorkItem.ActivityMaster.ActivityName },
|
||||
WorkCategory = t.WorkItem.WorkCategoryMaster == null ? null : new { Id = t.WorkItem.WorkCategoryMaster.Id, Name = t.WorkItem.WorkCategoryMaster.Name },
|
||||
Service = t.WorkItem.ActivityMaster!.ActivityGroup!.Service == null ? null : new { Id = t.WorkItem.ActivityMaster.ActivityGroup.Service.Id, Name = t.WorkItem.ActivityMaster.ActivityGroup.Service.Name }
|
||||
})
|
||||
.ToListAsync(ct); // optimized projection [memory:10]
|
||||
|
||||
_logger.LogDebug("Fetched {TaskCount} tasks after filtering and projection", tasks.Count); // metrics [memory:1]
|
||||
|
||||
// Documents query in parallel with tasks is okay; here we’ve already awaited tasks, but both can run together if needed.
|
||||
// Only fetch uploader fields needed
|
||||
var documents = await _context.Documents
|
||||
.AsNoTracking()
|
||||
.Where(d => documentIds.Contains(d.Id))
|
||||
.Select(d => new
|
||||
{
|
||||
d.Id,
|
||||
UploadedBy = d.UploadedBy == null ? null : new
|
||||
{
|
||||
d.UploadedBy.Id,
|
||||
d.UploadedBy.FirstName,
|
||||
d.UploadedBy.LastName
|
||||
}
|
||||
})
|
||||
.ToListAsync(ct); // minimal shape [memory:10]
|
||||
|
||||
_logger.LogDebug("Fetched {DocumentCount} documents for UploadedBy resolution", documents.Count); // metrics [memory:1]
|
||||
|
||||
// Distinct projections via HashSet to avoid custom equality or anonymous Distinct pitfalls
|
||||
static List<T> DistinctBy<T, TKey>(IEnumerable<T> source, Func<T, TKey> keySelector)
|
||||
=> source.GroupBy(keySelector).Select(g => g.First()).ToList();
|
||||
|
||||
var buildings = DistinctBy(tasks.Select(t => t.Building), b => b.Id);
|
||||
var floors = DistinctBy(tasks.Select(t => t.Floor), f => f.Id);
|
||||
var workAreas = DistinctBy(tasks.Select(t => t.WorkArea), wa => wa.Id);
|
||||
var activities = DistinctBy(tasks.Where(t => t.Activity != null).Select(t => t.Activity!), a => a.Id);
|
||||
var workCategories = DistinctBy(tasks.Where(t => t.WorkCategory != null).Select(t => t.WorkCategory!), wc => wc.Id);
|
||||
var services = DistinctBy(tasks.Where(t => t.Service != null).Select(t => t.Service!), s => s.Id);
|
||||
|
||||
var uploadedBys = DistinctBy(
|
||||
documents.Where(d => d.UploadedBy != null)
|
||||
.Select(d => new
|
||||
{
|
||||
Id = d.UploadedBy!.Id,
|
||||
Name = string.Join(' ', new[] { d.UploadedBy!.FirstName, d.UploadedBy!.LastName }.Where(x => !string.IsNullOrWhiteSpace(x)))
|
||||
}),
|
||||
u => u.Id);
|
||||
|
||||
var response = new
|
||||
{
|
||||
Buildings = buildings,
|
||||
Floors = floors,
|
||||
WorkAreas = workAreas,
|
||||
WorkCategories = workCategories,
|
||||
Activities = activities,
|
||||
UploadedBys = uploadedBys,
|
||||
Services = services
|
||||
};
|
||||
|
||||
_logger.LogInfo("GetFilterObject succeeded for ProjectId {ProjectId}. Buildings={Buildings}, Floors={Floors}, WorkAreas={WorkAreas}, Activities={Activities}, WorkCategories={WorkCategories}, Services={Services}, UploadedBys={UploadedBys}",
|
||||
projectId, buildings.Count, floors.Count, workAreas.Count, activities.Count, workCategories.Count, services.Count, uploadedBys.Count); // success log [memory:1]
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Filter object for image gallery fetched successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("{documentId}")]
|
||||
public async Task<IActionResult> GetImage(Guid documentId)
|
||||
{
|
||||
|
@ -7,7 +7,6 @@ using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.TenantModels;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Tenant;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -131,65 +130,5 @@ namespace Marco.Pms.Services.Controllers
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An error occurred while fetching subscription plans."));
|
||||
}
|
||||
}
|
||||
|
||||
[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>
|
||||
[HttpGet("{projectId}/report")]
|
||||
public async Task<IActionResult> GetProjectReportAsync(Guid projectId)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _reportHelper = scope.ServiceProvider.GetRequiredService<ReportHelper>();
|
||||
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
// Use structured logging to include the projectId for better traceability.
|
||||
_logger.LogInfo("Attempting to fetch report for ProjectId: {ProjectId}", projectId);
|
||||
|
||||
try
|
||||
{
|
||||
// Call the helper service, which is now available as a class member.
|
||||
var response = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId);
|
||||
|
||||
// Check if the report data was found.
|
||||
if (response == null)
|
||||
{
|
||||
_logger.LogWarning("Project report not found for ProjectId: {ProjectId}", projectId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project report not found.", 404));
|
||||
}
|
||||
|
||||
// Log success and return the report.
|
||||
_logger.LogInfo("Successfully fetched report for ProjectId: {ProjectId}", projectId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Report for the project fetched successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the full exception if anything goes wrong during the process.
|
||||
_logger.LogError(ex, "An error occurred while generating the report for ProjectId: {ProjectId}", projectId);
|
||||
|
||||
// Return a standardized 500 Internal Server Error response to the client.
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An internal server error occurred while processing the report.", 500));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,12 @@
|
||||
using Marco.Pms.Model.Dtos.Activities;
|
||||
using Marco.Pms.Model.Dtos.DocumentManager;
|
||||
using Marco.Pms.Model.Dtos.Master;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Forum;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.Forum;
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
@ -36,18 +38,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
#region =================================================================== Organization Type APIs ===================================================================
|
||||
|
||||
[HttpGet("organization-type/list")]
|
||||
public async Task<IActionResult> GetOrganizationTypes()
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.GetOrganizationTypesAsync(loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Global Services APIs ===================================================================
|
||||
|
||||
[HttpGet("global-service/list")]
|
||||
@ -94,14 +84,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("service/all/list")]
|
||||
public async Task<IActionResult> GetServiceDetailsList([FromQuery] Guid? serviceId)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.GetServiceDetailsListAsync(serviceId, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPost("service/create")]
|
||||
public async Task<IActionResult> CreateService([FromBody] ServiceMasterDto serviceMasterDto)
|
||||
{
|
||||
@ -131,10 +113,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
#region =================================================================== Activity Group APIs ===================================================================
|
||||
|
||||
[HttpGet("activity-group/list")]
|
||||
public async Task<IActionResult> GetActivityGroups([FromQuery] Guid? serviceId)
|
||||
public async Task<IActionResult> GetActivityGroups()
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.GetActivityGroupsAsync(serviceId, loggedInEmployee, tenantId);
|
||||
var response = await _masterService.GetActivityGroupsAsync(loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
@ -168,35 +150,152 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
[HttpGet]
|
||||
[Route("activities")]
|
||||
public async Task<IActionResult> GetActivitiesMaster([FromQuery] Guid? activityGroupId)
|
||||
public async Task<IActionResult> GetActivitiesMaster()
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.GetActivitiesMasterAsync(activityGroupId, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var activities = await _context.ActivityMasters.Where(c => c.TenantId == tenantId && c.IsActive == true).ToListAsync();
|
||||
List<ActivityVM> activitiesVM = new List<ActivityVM>();
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
var checkList = await _context.ActivityCheckLists.Where(c => c.TenantId == tenantId && c.ActivityId == activity.Id).ToListAsync();
|
||||
List<CheckListVM> checkListVM = new List<CheckListVM>();
|
||||
if (checkList != null)
|
||||
{
|
||||
foreach (ActivityCheckList check in checkList)
|
||||
{
|
||||
var checkVM = check.ToCheckListVMFromActivityCheckList(activity.Id, false);
|
||||
checkListVM.Add(checkVM);
|
||||
}
|
||||
}
|
||||
|
||||
ActivityVM activityVM = activity.ToActivityVMFromActivityMaster(checkListVM);
|
||||
activitiesVM.Add(activityVM);
|
||||
}
|
||||
_logger.LogInfo("{count} activity records fetched successfully from tenant {tenantId}", activitiesVM.Count, tenantId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(activitiesVM, System.String.Format("{0} activity records fetched successfully", activitiesVM.Count), 200));
|
||||
}
|
||||
|
||||
[HttpPost("activity")]
|
||||
public async Task<IActionResult> CreateActivity([FromBody] CreateActivityMasterDto createActivity)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.CreateActivityAsync(createActivity, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
if (employee.TenantId != tenantId)
|
||||
{
|
||||
_logger.LogWarning("User from tenant {employeeTenantId} tries to access data from tenant {tenantId}", employee.TenantId ?? Guid.Empty, tenantId);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Current tenant did not match with user's tenant", "Current tenant did not match with user's tenant", 401));
|
||||
}
|
||||
var activityMaster = createActivity.ToActivityMasterFromCreateActivityMasterDto(tenantId);
|
||||
_context.ActivityMasters.Add(activityMaster);
|
||||
await _context.SaveChangesAsync();
|
||||
List<CheckListVM> checkListVM = new List<CheckListVM>();
|
||||
|
||||
if (createActivity.CheckList != null)
|
||||
{
|
||||
List<ActivityCheckList> activityCheckList = new List<ActivityCheckList>();
|
||||
foreach (var check in createActivity.CheckList)
|
||||
{
|
||||
ActivityCheckList checkList = check.ToActivityCheckListFromCreateCheckListDto(tenantId, activityMaster.Id);
|
||||
activityCheckList.Add(checkList);
|
||||
}
|
||||
_context.ActivityCheckLists.AddRange(activityCheckList);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
foreach (ActivityCheckList check in activityCheckList)
|
||||
{
|
||||
var checkVM = check.ToCheckListVMFromActivityCheckList(activityMaster.Id, false);
|
||||
checkListVM.Add(checkVM);
|
||||
}
|
||||
}
|
||||
ActivityVM activityVM = activityMaster.ToActivityVMFromActivityMaster(checkListVM);
|
||||
|
||||
_logger.LogInfo("activity created successfully from tenant {tenantId}", tenantId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(activityVM, "activity created successfully", 200));
|
||||
}
|
||||
|
||||
[HttpPost("activity/edit/{id}")]
|
||||
public async Task<IActionResult> UpdateActivity(Guid id, [FromBody] CreateActivityMasterDto createActivity)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.UpdateActivityAsync(id, createActivity, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
ActivityMaster? activity = await _context.ActivityMasters.FirstOrDefaultAsync(x => x.Id == id && x.IsActive == true && x.TenantId == tenantId);
|
||||
if (activity != null && createActivity.UnitOfMeasurement != null && createActivity.ActivityName != null)
|
||||
{
|
||||
|
||||
activity.ActivityName = createActivity.ActivityName;
|
||||
activity.UnitOfMeasurement = createActivity.UnitOfMeasurement;
|
||||
List<ActivityCheckList> activityCheckLists = await _context.ActivityCheckLists.AsNoTracking().Where(c => c.ActivityId == activity.Id).ToListAsync();
|
||||
List<CheckListVM> checkListVM = new List<CheckListVM>();
|
||||
|
||||
if (createActivity.CheckList != null)
|
||||
{
|
||||
|
||||
var newCheckIds = createActivity.CheckList.Select(c => c.Id);
|
||||
|
||||
List<ActivityCheckList> updateCheckList = new List<ActivityCheckList>();
|
||||
List<ActivityCheckList> deleteCheckList = new List<ActivityCheckList>();
|
||||
if (newCheckIds.Contains(null))
|
||||
{
|
||||
foreach (var check in createActivity.CheckList)
|
||||
{
|
||||
if (check.Id == null)
|
||||
{
|
||||
ActivityCheckList checkList = check.ToActivityCheckListFromCreateCheckListDto(tenantId, activity.Id);
|
||||
updateCheckList.Add(checkList);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var check in activityCheckLists)
|
||||
{
|
||||
if (newCheckIds.Contains(check.Id))
|
||||
{
|
||||
var updatedCheck = createActivity.CheckList.Find(c => c.Id == check.Id);
|
||||
ActivityCheckList checkList = updatedCheck != null ? updatedCheck.ToActivityCheckListFromCreateCheckListDto(tenantId, activity.Id) : new ActivityCheckList();
|
||||
updateCheckList.Add(checkList);
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteCheckList.Add(check);
|
||||
}
|
||||
}
|
||||
_context.ActivityCheckLists.UpdateRange(updateCheckList);
|
||||
if (deleteCheckList != null)
|
||||
{
|
||||
_context.ActivityCheckLists.RemoveRange(deleteCheckList);
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
foreach (ActivityCheckList check in updateCheckList)
|
||||
{
|
||||
var checkVM = check.ToCheckListVMFromActivityCheckList(activity.Id, false);
|
||||
checkListVM.Add(checkVM);
|
||||
}
|
||||
}
|
||||
else if (activityCheckLists != null)
|
||||
{
|
||||
_context.ActivityCheckLists.RemoveRange(activityCheckLists);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
ActivityVM activityVM = activity.ToActivityVMFromActivityMaster(checkListVM);
|
||||
_logger.LogInfo("activity updated successfully from tenant {tenantId}", tenantId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(activityVM, "activity updated successfully", 200));
|
||||
}
|
||||
_logger.LogWarning("Activity {ActivityId} not found", id);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found", 404));
|
||||
}
|
||||
|
||||
[HttpDelete("activity/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteActivity(Guid id, [FromQuery] bool active = false)
|
||||
public async Task<IActionResult> DeleteActivity(Guid Id)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _masterService.DeleteActivityAsync(id, active, loggedInEmployee, tenantId);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var activity = await _context.ActivityMasters.FirstOrDefaultAsync(a => a.Id == Id && a.TenantId == tenantId);
|
||||
if (activity != null)
|
||||
{
|
||||
activity.IsActive = false;
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Activity Deleted Successfully from tenant {tenantId}", tenantId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Activity Deleted Successfully", 200));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -201,8 +201,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
#region =================================================================== Project Allocation APIs ===================================================================
|
||||
|
||||
[HttpGet("employees/get/{projectId}")]
|
||||
public async Task<IActionResult> GetEmployeeByProjectId(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive = false)
|
||||
[HttpGet("employees/get/{projectid?}/{includeInactive?}")]
|
||||
public async Task<IActionResult> GetEmployeeByProjectId(Guid? projectId, bool includeInactive = false)
|
||||
{
|
||||
// --- Step 1: Input Validation ---
|
||||
if (!ModelState.IsValid)
|
||||
@ -214,12 +214,12 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// --- Step 2: Prepare data without I/O ---
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetEmployeeByProjectIdAsync(projectId, organizationId, includeInactive, tenantId, loggedInEmployee);
|
||||
var response = await _projectServices.GetEmployeeByProjectIdAsync(projectId, includeInactive, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("allocation/{projectId}")]
|
||||
public async Task<IActionResult> GetProjectAllocation(Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] Guid? serviceId, [FromQuery] bool includeInactive = false)
|
||||
public async Task<IActionResult> GetProjectAllocation(Guid? projectId)
|
||||
{
|
||||
// --- Step 1: Input Validation ---
|
||||
if (!ModelState.IsValid)
|
||||
@ -231,7 +231,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// --- Step 2: Prepare data without I/O ---
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetProjectAllocationAsync(projectId, organizationId, serviceId, includeInactive, tenantId, loggedInEmployee);
|
||||
var response = await _projectServices.GetProjectAllocationAsync(projectId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
@ -319,20 +319,12 @@ namespace MarcoBMS.Services.Controllers
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("get/task/team/{projectId}")]
|
||||
public async Task<IActionResult> GetProjectTeamByServiceAndOrganization(Guid projectId, [FromQuery] Guid? serviceId, [FromQuery] Guid? organizationId)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetProjectTeamByServiceAndOrganizationAsync(projectId, serviceId, organizationId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Project InfraStructure Get APIs ===================================================================
|
||||
|
||||
[HttpGet("infra-details/{projectId}")]
|
||||
public async Task<IActionResult> GetInfraDetails(Guid projectId, [FromQuery] Guid? serviceId)
|
||||
public async Task<IActionResult> GetInfraDetails(Guid projectId)
|
||||
{
|
||||
// --- Step 1: Input Validation ---
|
||||
if (!ModelState.IsValid)
|
||||
@ -344,13 +336,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// --- Step 2: Prepare data without I/O ---
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetInfraDetailsAsync(projectId, serviceId, tenantId, loggedInEmployee);
|
||||
var response = await _projectServices.GetInfraDetailsAsync(projectId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
|
||||
}
|
||||
|
||||
[HttpGet("tasks/{workAreaId}")]
|
||||
public async Task<IActionResult> GetWorkItems(Guid workAreaId, [FromQuery] Guid? serviceId)
|
||||
public async Task<IActionResult> GetWorkItems(Guid workAreaId)
|
||||
{
|
||||
// --- Step 1: Input Validation ---
|
||||
if (!ModelState.IsValid)
|
||||
@ -362,10 +354,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// --- Step 2: Prepare data without I/O ---
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetWorkItemsAsync(workAreaId, serviceId, tenantId, loggedInEmployee);
|
||||
var response = await _projectServices.GetWorkItemsAsync(workAreaId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpGet("tasks-employee/{employeeId}")]
|
||||
public async Task<IActionResult> GetTasksByEmployee(Guid employeeId, [FromQuery] DateTime? fromDate, DateTime? toDate)
|
||||
{
|
||||
@ -519,7 +510,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPost("assign/service")]
|
||||
public async Task<IActionResult> AssignServiceToProject([FromBody] AssignServiceDto model)
|
||||
{
|
||||
@ -550,24 +540,5 @@ namespace MarcoBMS.Services.Controllers
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region =================================================================== Assign Organization APIs ===================================================================
|
||||
|
||||
[HttpGet("get/assigned/organization/{projectId}")]
|
||||
public async Task<IActionResult> GetAssignedOrganizationsToProject(Guid projectId)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetAssignedOrganizationsToProjectAsync(projectId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
[HttpGet("get/assigned/organization/dropdown/{projectId}")]
|
||||
public async Task<IActionResult> GetAssignedOrganizationsToProjectForDropdownAsync(Guid projectId)
|
||||
{
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _projectServices.GetAssignedOrganizationsToProjectForDropdownAsync(projectId, tenantId, loggedInEmployee);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Dtos.Attendance;
|
||||
using Marco.Pms.Model.Dtos.Mail;
|
||||
using Marco.Pms.Model.Mail;
|
||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||
@ -11,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MongoDB.Driver;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Net.Mail;
|
||||
@ -22,25 +22,27 @@ namespace Marco.Pms.Services.Controllers
|
||||
[Authorize]
|
||||
public class ReportController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IEmailSender _emailSender;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly ReportHelper _reportHelper;
|
||||
private readonly IConfiguration _configuration;
|
||||
public ReportController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
ApplicationDbContext context,
|
||||
ILoggingService logger,
|
||||
UserHelper userHelper,
|
||||
IConfiguration configuration,
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
public ReportController(ApplicationDbContext context, IEmailSender emailSender, ILoggingService logger, UserHelper userHelper,
|
||||
IWebHostEnvironment env, ReportHelper reportHelper, IConfiguration configuration, CacheUpdateHelper cache, IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
_context = context;
|
||||
_emailSender = emailSender;
|
||||
_logger = logger;
|
||||
_userHelper = userHelper;
|
||||
_env = env;
|
||||
_reportHelper = reportHelper;
|
||||
_configuration = configuration;
|
||||
_cache = cache;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -433,12 +435,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
200));
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("report-mail")]
|
||||
public async Task<IActionResult> GetProjectStatisticsFromCache()
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
|
||||
|
||||
var mailList = await _cache.GetProjectReportMail(false);
|
||||
if (mailList == null)
|
||||
{
|
||||
@ -447,92 +447,5 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(mailList, "Fetched list of mail body successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("report-attendance")]
|
||||
public async Task<IActionResult> GetAttendanceReportAsync([FromQuery] bool isCurrentMonth = false)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
DateTime today = DateTime.Today;
|
||||
DateTime firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
|
||||
DateTime firstDayOfNextMonth = firstDayOfMonth.AddMonths(1);
|
||||
|
||||
if (!isCurrentMonth)
|
||||
{
|
||||
firstDayOfNextMonth = firstDayOfMonth;
|
||||
firstDayOfMonth = firstDayOfMonth.AddMonths(-1);
|
||||
}
|
||||
|
||||
// Generate list of all dates in the month
|
||||
var allDates = Enumerable.Range(0, (firstDayOfNextMonth - firstDayOfMonth).Days)
|
||||
.Select(offset => firstDayOfMonth.AddDays(offset))
|
||||
.ToList();
|
||||
|
||||
var attendancesTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.Attendes
|
||||
.Where(a => a.AttendanceDate >= firstDayOfMonth && a.AttendanceDate < firstDayOfNextMonth && a.Employee != null && a.TenantId == tenantId)
|
||||
.GroupBy(a => a.ProjectID)
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
var projectAllocationTask = Task.Run(async () =>
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await context.ProjectAllocations
|
||||
.Include(pa => pa.Employee)
|
||||
.Where(pa => pa.TenantId == tenantId && pa.IsActive)
|
||||
.ToListAsync();
|
||||
});
|
||||
|
||||
await Task.WhenAll(attendancesTask, projectAllocationTask);
|
||||
|
||||
var attendances = attendancesTask.Result;
|
||||
var projectAllocations = projectAllocationTask.Result;
|
||||
|
||||
var result = attendances.Select(g =>
|
||||
{
|
||||
var projectAllocation = projectAllocations.Where(pa => pa.ProjectId == g.Key && pa.Employee != null).ToList();
|
||||
var projectAttendance = projectAllocation.Select(pa =>
|
||||
{
|
||||
var attendances = g.Where(a => a.EmployeeId == pa.EmployeeId).ToList();
|
||||
var attendanceDate = attendances.Select(a => a.AttendanceDate.Date).ToList();
|
||||
return new
|
||||
{
|
||||
FirstName = pa.Employee!.FirstName,
|
||||
LastName = pa.Employee.LastName,
|
||||
Attendances = allDates.Select(d =>
|
||||
{
|
||||
var attendance = attendances.FirstOrDefault(a => a.AttendanceDate.Date == d);
|
||||
return new
|
||||
{
|
||||
AttendanceDate = d,
|
||||
CheckIn = attendance?.InTime,
|
||||
CheckOut = attendance?.OutTime,
|
||||
Activity = attendance?.Activity,
|
||||
IsApproved = attendance?.ApprovedById.HasValue,
|
||||
};
|
||||
}).ToList(),
|
||||
CheckInCheckOutDone = attendances.Where(a => a.InTime.HasValue && a.OutTime.HasValue && a.Activity == ATTENDANCE_MARK_TYPE.REGULARIZE).Count(),
|
||||
CheckInDone = attendances.Where(a => a.InTime.HasValue).Count(),
|
||||
CheckOutPending = attendances.Where(a => a.InTime.HasValue && !a.OutTime.HasValue).Count(),
|
||||
RejectedRegularize = attendances.Where(a => a.Activity == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT).Count(),
|
||||
AbsentAttendance = allDates.Where(d => !attendanceDate.Contains(d) && d.DayOfWeek != DayOfWeek.Sunday).Count()
|
||||
};
|
||||
}).OrderBy(ar => ar.FirstName).ThenBy(ar => ar.LastName).ToList();
|
||||
|
||||
return new
|
||||
{
|
||||
ProjectName = _context.Projects.Where(p => p.Id == g.Key && p.TenantId == tenantId).Select(p => p.Name).FirstOrDefault(),
|
||||
ProjectAttendance = projectAttendance
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
var response = result.OrderBy(r => r.ProjectName).ToList();
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Attendance Report fetched successfully", 200));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var employees = await _context.Employees.Where(e => employeesIds.Contains(e.Id)).ToListAsync();
|
||||
|
||||
Guid tenantId = GetTenantId();
|
||||
Guid TenantId = GetTenantId();
|
||||
try
|
||||
{
|
||||
foreach (EmployeeRoleDot role in employeeRoleDots)
|
||||
@ -435,7 +435,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
_logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to assign or remove the application role to System-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("System-defined employee cannot have application roles assigned or removed.", "System-defined employee cannot have application roles assigned or removed.", 400));
|
||||
}
|
||||
EmployeeRoleMapping mapping = role.ToEmployeeRoleMappingFromEmployeeRoleDot(tenantId);
|
||||
EmployeeRoleMapping mapping = role.ToEmployeeRoleMappingFromEmployeeRoleDot(TenantId);
|
||||
|
||||
var existingItem = await _context.EmployeeRoleMappings.AsNoTracking().SingleOrDefaultAsync(c => c.Id == mapping.Id);
|
||||
|
||||
@ -444,16 +444,16 @@ namespace MarcoBMS.Services.Controllers
|
||||
if (role.IsEnabled == true)
|
||||
{
|
||||
_context.EmployeeRoleMappings.Add(mapping);
|
||||
await _cache.AddApplicationRole(role.EmployeeId, [mapping.RoleId], tenantId);
|
||||
await _cache.AddApplicationRole(role.EmployeeId, [mapping.RoleId]);
|
||||
}
|
||||
}
|
||||
else if (role.IsEnabled == false)
|
||||
{
|
||||
_context.EmployeeRoleMappings.Remove(existingItem);
|
||||
await _cache.RemoveRoleId(existingItem.EmployeeId, existingItem.RoleId, tenantId);
|
||||
await _cache.ClearAllPermissionIdsByEmployeeID(existingItem.EmployeeId, tenantId);
|
||||
await _cache.RemoveRoleId(existingItem.EmployeeId, existingItem.RoleId);
|
||||
await _cache.ClearAllPermissionIdsByEmployeeID(existingItem.EmployeeId);
|
||||
}
|
||||
await _cache.ClearAllProjectIds(role.EmployeeId, tenantId);
|
||||
await _cache.ClearAllProjectIds(role.EmployeeId);
|
||||
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
@ -2,7 +2,6 @@
|
||||
using Marco.Pms.Model.Activities;
|
||||
using Marco.Pms.Model.Dtos.Activities;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Filters;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
@ -10,7 +9,6 @@ using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using Marco.Pms.Services.Hubs;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -18,7 +16,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using Document = Marco.Pms.Model.DocumentManager.Document;
|
||||
|
||||
namespace MarcoBMS.Services.Controllers
|
||||
@ -36,10 +33,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly IHubContext<MarcoHub> _signalR;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly PermissionServices _permissionServices;
|
||||
private readonly IFirebaseService _firebase;
|
||||
|
||||
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices,
|
||||
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache, IFirebaseService firebase)
|
||||
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache)
|
||||
{
|
||||
_context = context;
|
||||
_userHelper = userHelper;
|
||||
@ -48,7 +44,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
_signalR = signalR;
|
||||
_cache = cache;
|
||||
_permissionServices = permissionServices;
|
||||
_firebase = firebase;
|
||||
}
|
||||
|
||||
private Guid GetTenantId()
|
||||
@ -71,28 +66,28 @@ namespace MarcoBMS.Services.Controllers
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
}
|
||||
|
||||
// Retrieve tenant and loggedInEmployee context
|
||||
// Retrieve tenant and employee context
|
||||
var tenantId = GetTenantId();
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Check for permission to approve tasks
|
||||
var hasPermission = await _permissionServices.HasPermission(PermissionsMaster.AssignAndReportProgress, loggedInEmployee.Id);
|
||||
var hasPermission = await _permissionServices.HasPermission(PermissionsMaster.AssignAndReportProgress, employee.Id);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Employee {EmployeeId} attempted to assign Task without permission", loggedInEmployee.Id);
|
||||
_logger.LogWarning("Employee {EmployeeId} attempted to assign Task without permission", employee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to approve tasks", 403));
|
||||
}
|
||||
|
||||
_logger.LogInfo("Employee {EmployeeId} is assigning a new task", loggedInEmployee.Id);
|
||||
_logger.LogInfo("Employee {EmployeeId} is assigning a new task", employee.Id);
|
||||
|
||||
// Convert DTO to entity and save TaskAllocation
|
||||
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(loggedInEmployee.Id, tenantId);
|
||||
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(employee.Id, tenantId);
|
||||
_context.TaskAllocations.Add(taskAllocation);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
await _cache.UpdatePlannedAndCompleteWorksInWorkItem(taskAllocation.WorkItemId, todaysAssigned: taskAllocation.PlannedTask);
|
||||
|
||||
_logger.LogInfo("Task {TaskId} assigned by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id);
|
||||
_logger.LogInfo("Task {TaskId} assigned by Employee {EmployeeId}", taskAllocation.Id, employee.Id);
|
||||
|
||||
var response = taskAllocation.ToAssignTaskVMFromTaskAllocation();
|
||||
|
||||
@ -122,18 +117,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
var team = employees.Select(e => e.ToBasicEmployeeVMFromEmployee()).ToList();
|
||||
response.teamMembers = team;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
await _firebase.SendAssignTaskMessageAsync(taskAllocation.WorkItemId, name, employeeIds, tenantId);
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assigned successfully", 200));
|
||||
}
|
||||
|
||||
@ -163,14 +146,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
var taskAllocation = await _context.TaskAllocations
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkArea)
|
||||
.ThenInclude(wa => wa!.Floor)
|
||||
.ThenInclude(f => f!.Building)
|
||||
.FirstOrDefaultAsync(t => t.Id == reportTask.Id &&
|
||||
t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null);
|
||||
.FirstOrDefaultAsync(t => t.Id == reportTask.Id);
|
||||
|
||||
if (taskAllocation == null)
|
||||
{
|
||||
@ -302,18 +278,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
_logger.LogInfo("Task {TaskId} reported successfully by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
await _firebase.SendReportTaskMessageAsync(taskAllocation.Id, name, tenantId);
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task reported successfully", 200));
|
||||
}
|
||||
|
||||
@ -414,116 +378,88 @@ namespace MarcoBMS.Services.Controllers
|
||||
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Task_Comment", NumberOfImages = numberofImages, ProjectId = projectId };
|
||||
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
await _firebase.SendTaskCommentMessageAsync(taskAllocation.Id, name, tenantId);
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? filter, [FromQuery] Guid? serviceId,
|
||||
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
|
||||
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
{
|
||||
_logger.LogInfo("GetTasksList called for projectId: {ProjectId}", projectId);
|
||||
_logger.LogInfo("GetTasksList called for projectId: {ProjectId}, dateFrom: {DateFrom}, dateTo: {DateTo}", projectId, dateFrom ?? "", dateTo ?? "");
|
||||
|
||||
Guid tenantId = GetTenantId();
|
||||
DateTime fromDate = new DateTime();
|
||||
DateTime toDate = new DateTime();
|
||||
|
||||
// 1. Get task allocations in the specified date range
|
||||
var taskAllocationQuery = _context.TaskAllocations
|
||||
// Parse and validate dateFrom
|
||||
if (dateFrom != null && !DateTime.TryParse(dateFrom, out fromDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid starting date provided: {DateFrom}", dateFrom);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid starting date.", "Invalid starting date.", 400));
|
||||
}
|
||||
|
||||
// Parse and validate dateTo
|
||||
if (dateTo != null && !DateTime.TryParse(dateTo, out toDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid ending date provided: {DateTo}", dateTo);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid ending date.", "Invalid ending date.", 400));
|
||||
}
|
||||
|
||||
// Set default date range if not provided
|
||||
fromDate = dateFrom == null ? DateTime.UtcNow.Date : fromDate;
|
||||
toDate = dateTo == null ? fromDate.AddDays(1) : toDate;
|
||||
|
||||
// 1. Get all buildings under this project
|
||||
_logger.LogInfo("Fetching buildings for projectId: {ProjectId}", projectId);
|
||||
var buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId && b.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var buildingIds = buildings.Select(b => b.Id).ToList();
|
||||
|
||||
// 2. Get floors under the buildings
|
||||
var floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId) && f.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var floorIds = floors.Select(f => f.Id).ToList();
|
||||
|
||||
// 3. Get work areas under the floors
|
||||
var workAreas = await _context.WorkAreas
|
||||
.Where(a => floorIds.Contains(a.FloorId) && a.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var workAreaIds = workAreas.Select(a => a.Id).ToList();
|
||||
|
||||
// 4. Get work items under the work areas
|
||||
var workItems = await _context.WorkItems
|
||||
.Where(i => workAreaIds.Contains(i.WorkAreaId) && i.TenantId == tenantId)
|
||||
.Include(i => i.ActivityMaster)
|
||||
.ToListAsync();
|
||||
var workItemIds = workItems.Select(i => i.Id).ToList();
|
||||
|
||||
_logger.LogInfo("Fetching task allocations between {FromDate} and {ToDate}", fromDate, toDate);
|
||||
|
||||
// 5. Get task allocations in the specified date range
|
||||
var taskAllocations = await _context.TaskAllocations
|
||||
.Include(t => t.Employee)
|
||||
.Include(t => t.ReportedBy)
|
||||
.Include(t => t.ApprovedBy)
|
||||
.Include(t => t.WorkStatus)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.ActivityMaster)
|
||||
.ThenInclude(a => a!.ActivityGroup)
|
||||
.ThenInclude(ag => ag!.Service)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkCategoryMaster)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkArea)
|
||||
.ThenInclude(wa => wa!.Floor)
|
||||
.ThenInclude(f => f!.Building)
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId &&
|
||||
t.TenantId == tenantId);
|
||||
|
||||
var taskFilter = TryDeserializeFilter(filter);
|
||||
|
||||
if (taskFilter != null)
|
||||
{
|
||||
if (taskFilter.BuildingIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
taskFilter.BuildingIds.Contains(t.WorkItem.WorkArea.Floor.BuildingId));
|
||||
}
|
||||
if (taskFilter.FloorIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
taskFilter.FloorIds.Contains(t.WorkItem.WorkArea.FloorId));
|
||||
}
|
||||
if (taskFilter.ActivityIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
taskFilter.ActivityIds.Contains(t.WorkItem.ActivityId));
|
||||
}
|
||||
if (taskFilter.ServiceIds?.Any() ?? false)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
taskFilter.ServiceIds.Contains(t.WorkItem.ActivityMaster.ActivityGroup.ServiceId));
|
||||
}
|
||||
if (taskFilter.dateFrom.HasValue && taskFilter.dateTo.HasValue)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.AssignmentDate.Date >= taskFilter.dateFrom.Value.Date &&
|
||||
t.AssignmentDate.Date <= taskFilter.dateTo.Value.Date);
|
||||
}
|
||||
}
|
||||
|
||||
if (serviceId.HasValue)
|
||||
{
|
||||
taskAllocationQuery = taskAllocationQuery.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup.ServiceId == serviceId);
|
||||
}
|
||||
|
||||
|
||||
int totalRecords = await taskAllocationQuery.CountAsync();
|
||||
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
|
||||
|
||||
var taskAllocations = await taskAllocationQuery
|
||||
.OrderByDescending(t => t.AssignmentDate)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Where(t => workItemIds.Contains(t.WorkItemId) &&
|
||||
t.AssignmentDate.Date >= fromDate.Date &&
|
||||
t.AssignmentDate.Date <= toDate.Date &&
|
||||
t.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var taskIds = taskAllocations.Select(t => t.Id).ToList();
|
||||
|
||||
// 2. Load team members
|
||||
// 6. Load team members
|
||||
_logger.LogInfo("Loading task members and related employee data.");
|
||||
var teamMembers = await _context.TaskMembers
|
||||
.Include(t => t.Employee)
|
||||
.Where(t => taskIds.Contains(t.TaskAllocationId))
|
||||
.ToListAsync();
|
||||
|
||||
// 3. Load task comments
|
||||
// 7. Load task comments
|
||||
_logger.LogInfo("Fetching comments and attachments.");
|
||||
var allComments = await _context.TaskComments
|
||||
.Include(c => c.Employee)
|
||||
@ -531,14 +467,14 @@ namespace MarcoBMS.Services.Controllers
|
||||
.ToListAsync();
|
||||
var commentIds = allComments.Select(c => c.Id).ToList();
|
||||
|
||||
// 4. Load all attachments (task and comment)
|
||||
// 8. Load all attachments (task and comment)
|
||||
var attachments = await _context.TaskAttachments
|
||||
.Where(t => taskIds.Contains(t.ReferenceId) || commentIds.Contains(t.ReferenceId))
|
||||
.ToListAsync();
|
||||
|
||||
var documentIds = attachments.Select(t => t.DocumentId).ToList();
|
||||
|
||||
// 5. Load actual documents from attachment references
|
||||
// 9. Load actual documents from attachment references
|
||||
var documents = await _context.Documents
|
||||
.Where(d => documentIds.Contains(d.Id))
|
||||
.ToListAsync();
|
||||
@ -624,18 +560,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
tasks.Add(response);
|
||||
}
|
||||
|
||||
var VM = new
|
||||
{
|
||||
TotalCount = totalRecords,
|
||||
TotalPages = totalPages,
|
||||
CurrentPage = pageNumber,
|
||||
PageSize = pageSize,
|
||||
Data = tasks
|
||||
};
|
||||
|
||||
_logger.LogInfo("Task list constructed successfully. Returning {Count} tasks.", tasks.Count);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(VM, "Success", 200));
|
||||
return Ok(ApiResponse<object>.SuccessResponse(tasks, "Success", 200));
|
||||
}
|
||||
|
||||
[HttpGet("get/{taskId}")]
|
||||
@ -752,97 +679,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(taskVM, "Success", 200));
|
||||
}
|
||||
|
||||
[HttpGet("filter/{projectId}")]
|
||||
public async Task<IActionResult> GetTaskFilterObject(Guid projectId)
|
||||
{
|
||||
// Get the current tenant from claims/context
|
||||
Guid tenantId = GetTenantId();
|
||||
|
||||
// Log API invocation with the project and tenant for traceability
|
||||
_logger.LogInfo("Fetching filter objects for ProjectId={ProjectId}, TenantId={TenantId}", projectId, tenantId);
|
||||
|
||||
try
|
||||
{
|
||||
// AsNoTracking for improved performance—no intention to update these records
|
||||
// Only fetch & project properties actually required (DTO projection)
|
||||
var tasks = await _context.TaskAllocations
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.WorkArea)
|
||||
.ThenInclude(wa => wa!.Floor)
|
||||
.ThenInclude(f => f!.Building)
|
||||
.Include(t => t.WorkItem)
|
||||
.ThenInclude(wi => wi!.ActivityMaster)
|
||||
.ThenInclude(a => a!.ActivityGroup)
|
||||
.ThenInclude(ag => ag!.Service)
|
||||
.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.WorkArea != null &&
|
||||
t.WorkItem.WorkArea.Floor != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building != null &&
|
||||
t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId &&
|
||||
t.TenantId == tenantId).ToListAsync();
|
||||
|
||||
// Distinct by Id (since projection doesn't guarantee uniqueness across different allocations)
|
||||
var buildings = tasks.Where(t => t.WorkItem != null && t.WorkItem.WorkArea != null && t.WorkItem.WorkArea.Floor != null && t.WorkItem.WorkArea.Floor.Building != null)
|
||||
.Select(t => t.WorkItem!.WorkArea!.Floor!.Building!)
|
||||
.Select(b => new
|
||||
{
|
||||
Id = b.Id,
|
||||
Name = b.Name
|
||||
}).Distinct().ToList();
|
||||
|
||||
var floors = tasks.Where(t => t.WorkItem != null && t.WorkItem.WorkArea != null && t.WorkItem.WorkArea.Floor != null)
|
||||
.Select(t => t.WorkItem!.WorkArea!.Floor!)
|
||||
.Select(f => new
|
||||
{
|
||||
Id = f.Id,
|
||||
Name = f.FloorName,
|
||||
BuildingId = f.BuildingId
|
||||
}).Distinct().ToList();
|
||||
|
||||
var activities = tasks.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup.Service != null)
|
||||
.Select(t => t.WorkItem!.ActivityMaster!)
|
||||
.Select(a => new
|
||||
{
|
||||
Id = a.Id,
|
||||
Name = a.ActivityName
|
||||
}).Distinct().ToList();
|
||||
|
||||
var services = tasks.Where(t => t.WorkItem != null &&
|
||||
t.WorkItem.ActivityMaster != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup != null &&
|
||||
t.WorkItem.ActivityMaster.ActivityGroup.Service != null)
|
||||
.Select(t => t.WorkItem!.ActivityMaster!.ActivityGroup!.Service!)
|
||||
.Select(s => new
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name
|
||||
}).Distinct().ToList();
|
||||
|
||||
var response = new
|
||||
{
|
||||
Buildings = buildings,
|
||||
Floors = floors,
|
||||
Activities = activities,
|
||||
Services = services
|
||||
};
|
||||
|
||||
_logger.LogInfo("Successfully fetched filter objects for ProjectId={ProjectId}, TenantId={TenantId}", projectId, tenantId);
|
||||
|
||||
// Use DTO in API response for clarity and easier frontend usage
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Filter object for task fetched successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to fetch filter objects for ProjectId={ProjectId}, TenantId={TenantId}", projectId, tenantId);
|
||||
// Return a standard error result
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Failed to fetch filter object.", 500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Approves a reported task after validation, updates status, and stores attachments/comments.
|
||||
/// </summary>
|
||||
@ -888,6 +724,16 @@ namespace MarcoBMS.Services.Controllers
|
||||
"Approved tasks cannot be greater than completed tasks", 400));
|
||||
}
|
||||
|
||||
//// Update completed work in the associated work item, if it exists
|
||||
//if (taskAllocation.WorkItem != null && taskAllocation.CompletedTask != approveTask.ApprovedTask)
|
||||
//{
|
||||
// if (taskAllocation.CompletedTask > 0)
|
||||
// {
|
||||
// taskAllocation.WorkItem.CompletedWork -= taskAllocation.CompletedTask;
|
||||
// }
|
||||
// taskAllocation.WorkItem.CompletedWork += approveTask.ApprovedTask;
|
||||
//}
|
||||
|
||||
// Update task allocation details
|
||||
taskAllocation.ApprovedById = loggedInEmployee.Id;
|
||||
taskAllocation.ApprovedDate = DateTime.UtcNow;
|
||||
@ -973,58 +819,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
_logger.LogInfo("Task {TaskId} successfully approved by Employee {EmployeeId}", approveTask.Id, loggedInEmployee.Id);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// --- 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 name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||
|
||||
await _firebase.SendApproveTaskMessageAsync(taskAllocation.Id, name, tenantId);
|
||||
|
||||
});
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200));
|
||||
}
|
||||
|
||||
private TaskFilter? TryDeserializeFilter(string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
TaskFilter? expenseFilter = null;
|
||||
|
||||
try
|
||||
{
|
||||
// First, try to deserialize directly. This is the expected case (e.g., from a web client).
|
||||
expenseFilter = JsonSerializer.Deserialize<TaskFilter>(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))
|
||||
{
|
||||
expenseFilter = JsonSerializer.Deserialize<TaskFilter>(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 expenseFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user