Compare commits
	
		
			No commits in common. "70c1c6da9dccf925c665343e283f91e6c1aa3823" and "2ed0e6e5b6608765bd297350f6e8251178c80355" have entirely different histories.
		
	
	
		
			70c1c6da9d
			...
			2ed0e6e5b6
		
	
		
@ -69,7 +69,6 @@ namespace Marco.Pms.DataAccess.Data
 | 
			
		||||
        public DbSet<Document> Documents { get; set; }
 | 
			
		||||
        public DbSet<TicketTag> TicketTags { get; set; }
 | 
			
		||||
        public DbSet<WorkCategoryMaster> WorkCategoryMasters { get; set; }
 | 
			
		||||
        public DbSet<WorkStatusMaster> WorkStatusMasters { get; set; }
 | 
			
		||||
        public DbSet<Contact> Contacts { get; set; }
 | 
			
		||||
        public DbSet<ContactCategoryMaster> ContactCategoryMasters { get; set; }
 | 
			
		||||
        public DbSet<ContactEmail> ContactsEmails { get; set; }
 | 
			
		||||
@ -435,32 +434,6 @@ namespace Marco.Pms.DataAccess.Data
 | 
			
		||||
                    TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
            modelBuilder.Entity<WorkStatusMaster>().HasData(
 | 
			
		||||
                new WorkStatusMaster
 | 
			
		||||
                {
 | 
			
		||||
                    Id = new Guid("030bb085-e230-4370-aec7-9a74d652864e"),
 | 
			
		||||
                    Name = "Approve",
 | 
			
		||||
                    Description = "Confirm the tasks are actually finished as reported",
 | 
			
		||||
                    IsSystem = true,
 | 
			
		||||
                    TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                },
 | 
			
		||||
                new WorkStatusMaster
 | 
			
		||||
                {
 | 
			
		||||
                    Id = new Guid("2a1a5b96-cf93-4111-b4b1-76c19d6333b4"),
 | 
			
		||||
                    Name = "Partially Approve",
 | 
			
		||||
                    Description = "Not all tasks are actually finished as reported",
 | 
			
		||||
                    IsSystem = true,
 | 
			
		||||
                    TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                },
 | 
			
		||||
                new WorkStatusMaster
 | 
			
		||||
                {
 | 
			
		||||
                    Id = new Guid("00a062e6-62e6-42c5-b6b1-024328651b72"),
 | 
			
		||||
                    Name = "NCR",
 | 
			
		||||
                    Description = "Tasks are not finished as reported or have any issues in al the tasks",
 | 
			
		||||
                    IsSystem = true,
 | 
			
		||||
                    TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,188 +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 Added_Apporved_By_In_TaskAllocation_Table : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.AddColumn<Guid>(
 | 
			
		||||
                name: "ApprovedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "char(36)",
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                collation: "ascii_general_ci");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<DateTime>(
 | 
			
		||||
                name: "ApprovedDate",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "datetime(6)",
 | 
			
		||||
                nullable: true);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<Guid>(
 | 
			
		||||
                name: "ParentTaskId",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "char(36)",
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                collation: "ascii_general_ci");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<Guid>(
 | 
			
		||||
                name: "ReportedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "char(36)",
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                collation: "ascii_general_ci");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<double>(
 | 
			
		||||
                name: "ReportedTask",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "double",
 | 
			
		||||
                nullable: false,
 | 
			
		||||
                defaultValue: 0.0);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<Guid>(
 | 
			
		||||
                name: "WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                type: "char(36)",
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                collation: "ascii_general_ci");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "WorkStatusMasters",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
 | 
			
		||||
                    Name = table.Column<string>(type: "longtext", nullable: false)
 | 
			
		||||
                        .Annotation("MySql:CharSet", "utf8mb4"),
 | 
			
		||||
                    Description = table.Column<string>(type: "longtext", nullable: false)
 | 
			
		||||
                        .Annotation("MySql:CharSet", "utf8mb4"),
 | 
			
		||||
                    IsSystem = table.Column<bool>(type: "tinyint(1)", nullable: false),
 | 
			
		||||
                    TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_WorkStatusMasters", x => x.Id);
 | 
			
		||||
                    table.ForeignKey(
 | 
			
		||||
                        name: "FK_WorkStatusMasters_Tenants_TenantId",
 | 
			
		||||
                        column: x => x.TenantId,
 | 
			
		||||
                        principalTable: "Tenants",
 | 
			
		||||
                        principalColumn: "Id",
 | 
			
		||||
                        onDelete: ReferentialAction.Cascade);
 | 
			
		||||
                })
 | 
			
		||||
                .Annotation("MySql:CharSet", "utf8mb4");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "WorkStatusMasters",
 | 
			
		||||
                columns: new[] { "Id", "Description", "IsSystem", "Name", "TenantId" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("00a062e6-62e6-42c5-b6b1-024328651b72"), "Tasks are not finished as reported or have any issues in al the tasks", true, "NCR", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
 | 
			
		||||
                    { new Guid("030bb085-e230-4370-aec7-9a74d652864e"), "Confirm the tasks are actually finished as reported", true, "Approve", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
 | 
			
		||||
                    { new Guid("2a1a5b96-cf93-4111-b4b1-76c19d6333b4"), "Not all tasks are actually finished as reported", true, "Partially Approve", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_ApprovedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "ApprovedById");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_ReportedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "ReportedById");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "WorkStatusId");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_WorkStatusMasters_TenantId",
 | 
			
		||||
                table: "WorkStatusMasters",
 | 
			
		||||
                column: "TenantId");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_Employees_ApprovedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "ApprovedById",
 | 
			
		||||
                principalTable: "Employees",
 | 
			
		||||
                principalColumn: "Id");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_Employees_ReportedById",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "ReportedById",
 | 
			
		||||
                principalTable: "Employees",
 | 
			
		||||
                principalColumn: "Id");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_WorkStatusMasters_WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations",
 | 
			
		||||
                column: "WorkStatusId",
 | 
			
		||||
                principalTable: "WorkStatusMasters",
 | 
			
		||||
                principalColumn: "Id");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_Employees_ApprovedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_Employees_ReportedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropForeignKey(
 | 
			
		||||
                name: "FK_TaskAllocations_WorkStatusMasters_WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "WorkStatusMasters");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_ApprovedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_ReportedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropIndex(
 | 
			
		||||
                name: "IX_TaskAllocations_WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ApprovedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ApprovedDate",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ParentTaskId",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ReportedById",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ReportedTask",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "WorkStatusId",
 | 
			
		||||
                table: "TaskAllocations");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,41 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public partial class EnhancedWorkItemForParentId_Description : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.AddColumn<string>(
 | 
			
		||||
                name: "Description",
 | 
			
		||||
                table: "WorkItems",
 | 
			
		||||
                type: "longtext",
 | 
			
		||||
                nullable: true)
 | 
			
		||||
                .Annotation("MySql:CharSet", "utf8mb4");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<Guid>(
 | 
			
		||||
                name: "ParentTaskId",
 | 
			
		||||
                table: "WorkItems",
 | 
			
		||||
                type: "char(36)",
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                collation: "ascii_general_ci");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "Description",
 | 
			
		||||
                table: "WorkItems");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "ParentTaskId",
 | 
			
		||||
                table: "WorkItems");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -28,12 +28,6 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("ApprovedById")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("ApprovedDate")
 | 
			
		||||
                        .HasColumnType("datetime(6)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid>("AssignedBy")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
@ -46,44 +40,26 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                    b.Property<string>("Description")
 | 
			
		||||
                        .HasColumnType("longtext");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("ParentTaskId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<double>("PlannedTask")
 | 
			
		||||
                        .HasColumnType("double");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("ReportedById")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("ReportedDate")
 | 
			
		||||
                        .HasColumnType("datetime(6)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<double>("ReportedTask")
 | 
			
		||||
                        .HasColumnType("double");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid>("TenantId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid>("WorkItemId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("WorkStatusId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("ApprovedById");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("AssignedBy");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("ReportedById");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("TenantId");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("WorkItemId");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("WorkStatusId");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("TaskAllocations");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
@ -1966,59 +1942,6 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                        });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Master.WorkStatusMaster", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<Guid>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Description")
 | 
			
		||||
                        .IsRequired()
 | 
			
		||||
                        .HasColumnType("longtext");
 | 
			
		||||
 | 
			
		||||
                    b.Property<bool>("IsSystem")
 | 
			
		||||
                        .HasColumnType("tinyint(1)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Name")
 | 
			
		||||
                        .IsRequired()
 | 
			
		||||
                        .HasColumnType("longtext");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid>("TenantId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("TenantId");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("WorkStatusMasters");
 | 
			
		||||
 | 
			
		||||
                    b.HasData(
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("030bb085-e230-4370-aec7-9a74d652864e"),
 | 
			
		||||
                            Description = "Confirm the tasks are actually finished as reported",
 | 
			
		||||
                            IsSystem = true,
 | 
			
		||||
                            Name = "Approve",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("2a1a5b96-cf93-4111-b4b1-76c19d6333b4"),
 | 
			
		||||
                            Description = "Not all tasks are actually finished as reported",
 | 
			
		||||
                            IsSystem = true,
 | 
			
		||||
                            Name = "Partially Approve",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("00a062e6-62e6-42c5-b6b1-024328651b72"),
 | 
			
		||||
                            Description = "Tasks are not finished as reported or have any issues in al the tasks",
 | 
			
		||||
                            IsSystem = true,
 | 
			
		||||
                            Name = "NCR",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Projects.Building", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<Guid>("Id")
 | 
			
		||||
@ -2198,12 +2121,6 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                    b.Property<double>("CompletedWork")
 | 
			
		||||
                        .HasColumnType("double");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Description")
 | 
			
		||||
                        .HasColumnType("longtext");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("ParentTaskId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<double>("PlannedWork")
 | 
			
		||||
                        .HasColumnType("double");
 | 
			
		||||
 | 
			
		||||
@ -2534,20 +2451,12 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Activities.TaskAllocation", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Employees.Employee", "ApprovedBy")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("ApprovedById");
 | 
			
		||||
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Employees.Employee", "Employee")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("AssignedBy")
 | 
			
		||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
			
		||||
                        .IsRequired();
 | 
			
		||||
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Employees.Employee", "ReportedBy")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("ReportedById");
 | 
			
		||||
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("TenantId")
 | 
			
		||||
@ -2560,21 +2469,11 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
			
		||||
                        .IsRequired();
 | 
			
		||||
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Master.WorkStatusMaster", "WorkStatus")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("WorkStatusId");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("ApprovedBy");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("Employee");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("ReportedBy");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("Tenant");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("WorkItem");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("WorkStatus");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Activities.TaskComment", b =>
 | 
			
		||||
@ -3187,17 +3086,6 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                    b.Navigation("Tenant");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Master.WorkStatusMaster", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("TenantId")
 | 
			
		||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
			
		||||
                        .IsRequired();
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("Tenant");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Projects.Building", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant")
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using Marco.Pms.Model.Employees;
 | 
			
		||||
using Marco.Pms.Model.Master;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
 | 
			
		||||
@ -11,43 +10,29 @@ namespace Marco.Pms.Model.Activities
 | 
			
		||||
    public class TaskAllocation : TenantRelation
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid? ParentTaskId { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public DateTime AssignmentDate { get; set; }
 | 
			
		||||
        public double PlannedTask { get; set; }
 | 
			
		||||
        public double CompletedTask { get; set; }
 | 
			
		||||
        public double ReportedTask { get; set; }
 | 
			
		||||
        public DateTime? ReportedDate { get; set; }
 | 
			
		||||
        public DateTime? ApprovedDate { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid AssignedBy { get; set; }   //Employee Id 
 | 
			
		||||
        //public int? WorkItemMappingId { get; set; }
 | 
			
		||||
        //[ForeignKey("WorkItemMappingId")]
 | 
			
		||||
        //[ValidateNever]
 | 
			
		||||
        //public WorkItemMapping? WorkItemMapping { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid AssignedBy { get; set; }   //Employee Id
 | 
			
		||||
        [ForeignKey("AssignedBy")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public Employee? Employee { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid? ReportedById { get; set; }   //Employee Id
 | 
			
		||||
 | 
			
		||||
        [ForeignKey("ReportedById")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public Employee? ReportedBy { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid? ApprovedById { get; set; }   //Employee Id
 | 
			
		||||
 | 
			
		||||
        [ForeignKey("ApprovedById")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public Employee? ApprovedBy { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid WorkItemId { get; set; }
 | 
			
		||||
 | 
			
		||||
        [ForeignKey("WorkItemId")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public WorkItem? WorkItem { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid? WorkStatusId { get; set; }
 | 
			
		||||
 | 
			
		||||
        [ForeignKey("WorkStatusId")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public WorkStatusMaster? WorkStatus { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.Master
 | 
			
		||||
{
 | 
			
		||||
    public class WorkStatusMaster : TenantRelation
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public string Name { get; set; } = string.Empty;
 | 
			
		||||
        public string Description { get; set; } = string.Empty;
 | 
			
		||||
        public bool IsSystem { get; set; } = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,13 +0,0 @@
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
{
 | 
			
		||||
    public class ApproveTaskDto
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid WorkStatus { get; set; }
 | 
			
		||||
        public long ApprovedTask { get; set; }
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
        public List<FileUploadModel>? Images { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
    public class AssignTaskDto
 | 
			
		||||
    {
 | 
			
		||||
        public DateTime AssignmentDate { get; set; }
 | 
			
		||||
        public Guid? ParentTaskId { get; set; }
 | 
			
		||||
        public double PlannedTask { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public List<Guid>? TaskTeam { get; set; }   //Employee Ids
 | 
			
		||||
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Master
 | 
			
		||||
{
 | 
			
		||||
    public class CreateWorkStatusMasterDto
 | 
			
		||||
    {
 | 
			
		||||
        public string? Name { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -5,7 +5,6 @@ namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
    public class ReportTaskDto
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid? ParentTaskId { get; set; }
 | 
			
		||||
        public double CompletedTask { get; set; }
 | 
			
		||||
        public DateTime ReportedDate { get; set; }
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Master
 | 
			
		||||
{
 | 
			
		||||
    public class UpdateWorkStatusMasterDto
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public string? Name { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,7 +11,5 @@ namespace Marco.Pms.Model.Dtos.Project
 | 
			
		||||
        public Guid ActivityID { get; set; }
 | 
			
		||||
        public int PlannedWork { get; set; }
 | 
			
		||||
        public int CompletedWork { get; set; }
 | 
			
		||||
        public Guid? ParentTaskId { get; set; }
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ namespace Marco.Pms.Model.Mapper
 | 
			
		||||
            return new TaskAllocation
 | 
			
		||||
            {
 | 
			
		||||
                AssignmentDate = assignTask.AssignmentDate,
 | 
			
		||||
                ParentTaskId = assignTask.ParentTaskId,
 | 
			
		||||
                PlannedTask = assignTask.PlannedTask,
 | 
			
		||||
                CompletedTask = 0,
 | 
			
		||||
                Description = assignTask.Description,
 | 
			
		||||
@ -44,23 +43,18 @@ namespace Marco.Pms.Model.Mapper
 | 
			
		||||
                TenantId = tenantId
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        public static TaskVM TaskAllocationToTaskVM(this TaskAllocation taskAllocation)
 | 
			
		||||
        public static TaskVM TaskAllocationToTaskVM(this TaskAllocation taskAllocation, string employeeName)
 | 
			
		||||
        {
 | 
			
		||||
            return new TaskVM
 | 
			
		||||
            {
 | 
			
		||||
                Id = taskAllocation.Id,
 | 
			
		||||
                AssignmentDate = taskAllocation.AssignmentDate,
 | 
			
		||||
                ReportedDate = taskAllocation.ReportedDate,
 | 
			
		||||
                ApprovedDate = taskAllocation.ApprovedDate,
 | 
			
		||||
                PlannedTask = taskAllocation.PlannedTask,
 | 
			
		||||
                CompletedTask = taskAllocation.CompletedTask,
 | 
			
		||||
                NotApprovedTask = taskAllocation.ApprovedById == null ? taskAllocation.CompletedTask : (taskAllocation.CompletedTask - taskAllocation.ReportedTask),
 | 
			
		||||
                ReportedDate = taskAllocation.ReportedDate,
 | 
			
		||||
                Description = taskAllocation.Description,
 | 
			
		||||
                AssignedBy = taskAllocation.Employee?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                ReportedBy = taskAllocation.ReportedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                ApprovedBy = taskAllocation.ApprovedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                WorkItem = taskAllocation.WorkItem,
 | 
			
		||||
                WorkStatus = taskAllocation.WorkStatus
 | 
			
		||||
                AssignBy = employeeName,
 | 
			
		||||
                WorkItem = taskAllocation.WorkItem
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        public static AssignedTaskVM ToAssignTaskVMFromTaskAllocation(this TaskAllocation taskAllocation)
 | 
			
		||||
@ -106,18 +100,12 @@ namespace Marco.Pms.Model.Mapper
 | 
			
		||||
            return new ListTaskVM
 | 
			
		||||
            {
 | 
			
		||||
                Id = taskAllocation.Id,
 | 
			
		||||
                ParentTaskId = taskAllocation.ParentTaskId,
 | 
			
		||||
                AssignmentDate = taskAllocation.AssignmentDate,
 | 
			
		||||
                ApprovedDate = taskAllocation.ApprovedDate,
 | 
			
		||||
                Description = taskAllocation.Description,
 | 
			
		||||
                PlannedTask = taskAllocation.PlannedTask,
 | 
			
		||||
                ReportedDate = taskAllocation.ReportedDate,
 | 
			
		||||
                WorkStatus = taskAllocation.WorkStatus,
 | 
			
		||||
                CompletedTask = taskAllocation.CompletedTask,
 | 
			
		||||
                NotApprovedTask = taskAllocation.ApprovedById == null ? taskAllocation.CompletedTask : (taskAllocation.CompletedTask - taskAllocation.ReportedTask),
 | 
			
		||||
                AssignedBy = taskAllocation.Employee?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                ReportedBy = taskAllocation.ReportedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                ApprovedBy = taskAllocation.ApprovedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                AssignedBy = taskAllocation.Employee != null ? taskAllocation.Employee.ToBasicEmployeeVMFromEmployee() : new BasicEmployeeVM(),
 | 
			
		||||
                WorkItemId = taskAllocation.WorkItemId,
 | 
			
		||||
                WorkItem = taskAllocation.WorkItem
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@ -59,9 +59,7 @@ namespace Marco.Pms.Model.Mapper
 | 
			
		||||
                WorkCategoryId = model.WorkCategoryId,
 | 
			
		||||
                TaskDate = DateTime.Now,
 | 
			
		||||
                TenantId = tenantId,
 | 
			
		||||
                WorkAreaId = model.WorkAreaID,
 | 
			
		||||
                ParentTaskId = model.ParentTaskId,
 | 
			
		||||
                Description = model.Comment
 | 
			
		||||
                WorkAreaId = model.WorkAreaID
 | 
			
		||||
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -20,17 +20,11 @@ namespace Marco.Pms.Model.Projects
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public ActivityMaster? ActivityMaster { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [ForeignKey("WorkCategoryId")]
 | 
			
		||||
        [ValidateNever]
 | 
			
		||||
        public WorkCategoryMaster? WorkCategoryMaster { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid? ParentTaskId { get; set; } 
 | 
			
		||||
 | 
			
		||||
        public double PlannedWork { get; set; }
 | 
			
		||||
        public double CompletedWork { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string? Description { get; set; } 
 | 
			
		||||
        public DateTime TaskDate { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,22 +1,15 @@
 | 
			
		||||
using Marco.Pms.Model.Master;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.ViewModels.Activities
 | 
			
		||||
{
 | 
			
		||||
    public class ListTaskVM
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid? ParentTaskId { get; set; }
 | 
			
		||||
        public DateTime AssignmentDate { get; set; }
 | 
			
		||||
        public DateTime? ReportedDate { get; set; }
 | 
			
		||||
        public DateTime? ApprovedDate { get; set; }
 | 
			
		||||
        public double PlannedTask { get; set; }
 | 
			
		||||
        public double CompletedTask { get; set; }
 | 
			
		||||
        public double NotApprovedTask { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? AssignedBy { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? ReportedBy { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? ApprovedBy { get; set; }
 | 
			
		||||
        public WorkStatusMaster? WorkStatus { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public Guid WorkItemId { get; set; }
 | 
			
		||||
        public List<string>? ReportedPreSignedUrls { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
using Marco.Pms.Model.Master;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Employee;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.ViewModels.Activities
 | 
			
		||||
{
 | 
			
		||||
@ -7,19 +7,14 @@ namespace Marco.Pms.Model.ViewModels.Activities
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public DateTime AssignmentDate { get; set; }
 | 
			
		||||
        public DateTime? ReportedDate { get; set; }
 | 
			
		||||
        public DateTime? ApprovedDate { get; set; }
 | 
			
		||||
        public double PlannedTask { get; set; }
 | 
			
		||||
        public double CompletedTask { get; set; }
 | 
			
		||||
        public double NotApprovedTask { get; set; }
 | 
			
		||||
        public DateTime? ReportedDate { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? AssignedBy { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? ReportedBy { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? ApprovedBy { get; set; }
 | 
			
		||||
        public WorkStatusMaster? WorkStatus { get; set; }
 | 
			
		||||
        public string? AssignBy { get; set; }
 | 
			
		||||
        public WorkItem? WorkItem { get; set; }
 | 
			
		||||
        public List<string>? PreSignedUrls { get; set; }
 | 
			
		||||
        public List<CommentVM>? Comments { get; set; }
 | 
			
		||||
        public List<BasicEmployeeVM>? TeamMembers { get; set; }
 | 
			
		||||
        public List<EmployeeVM>? TeamMembers { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
namespace Marco.Pms.Model.ViewModels.DashBoard
 | 
			
		||||
{
 | 
			
		||||
    public class AttendanceOverviewVM
 | 
			
		||||
    {
 | 
			
		||||
        public string? Role { get; set; }
 | 
			
		||||
        public string? Date { get; set; }
 | 
			
		||||
        public int Present { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -597,185 +597,257 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        {
 | 
			
		||||
            if (!ModelState.IsValid)
 | 
			
		||||
            {
 | 
			
		||||
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
 | 
			
		||||
                _logger.LogError("Invalid attendance model received.");
 | 
			
		||||
                var errors = ModelState.Values
 | 
			
		||||
                    .SelectMany(v => v.Errors)
 | 
			
		||||
                    .Select(e => e.ErrorMessage)
 | 
			
		||||
                    .ToList();
 | 
			
		||||
                _logger.LogError("User sent Invalid Date while marking attendance");
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = GetTenantId();
 | 
			
		||||
            var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            Guid TenantId = GetTenantId();
 | 
			
		||||
 | 
			
		||||
            using var transaction = await _context.Database.BeginTransactionAsync();
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Validate mark time
 | 
			
		||||
                Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ;
 | 
			
		||||
 | 
			
		||||
                if (recordAttendanceDot.MarkTime == null)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Null mark time provided.");
 | 
			
		||||
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Mark time is required", 400));
 | 
			
		||||
                    _logger.LogError("User sent Invalid Mark Time while marking attendance");
 | 
			
		||||
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Invalid Mark Time", 400));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (string.IsNullOrWhiteSpace(recordAttendanceDot.Comment))
 | 
			
		||||
                DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
 | 
			
		||||
                if (recordAttendanceDot.Comment == null)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Empty comment provided.");
 | 
			
		||||
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Comment is required", 400));
 | 
			
		||||
                    _logger.LogError("User sent Invalid comment while marking attendance");
 | 
			
		||||
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Invalid Comment", 400));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
 | 
			
		||||
                var attendance = await _context.Attendes
 | 
			
		||||
                    .FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == tenantId);
 | 
			
		||||
 | 
			
		||||
                // Create or update attendance
 | 
			
		||||
                if (attendance == null)
 | 
			
		||||
                if (attendance != null)
 | 
			
		||||
                {
 | 
			
		||||
                    attendance = new Attendance
 | 
			
		||||
                    attendance.Comment = recordAttendanceDot.Comment;
 | 
			
		||||
                    if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_IN)
 | 
			
		||||
                    {
 | 
			
		||||
                        TenantId = tenantId,
 | 
			
		||||
                        AttendanceDate = recordAttendanceDot.Date,
 | 
			
		||||
                        Comment = recordAttendanceDot.Comment,
 | 
			
		||||
                        EmployeeID = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                        ProjectID = recordAttendanceDot.ProjectID,
 | 
			
		||||
                        Date = DateTime.UtcNow,
 | 
			
		||||
                        InTime = finalDateTime,
 | 
			
		||||
                        Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT
 | 
			
		||||
                    };
 | 
			
		||||
                    _context.Attendes.Add(attendance);
 | 
			
		||||
                        attendance.InTime = finalDateTime;
 | 
			
		||||
                        attendance.OutTime = null;
 | 
			
		||||
                        attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_OUT)
 | 
			
		||||
                    {
 | 
			
		||||
                        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)
 | 
			
		||||
                    {
 | 
			
		||||
                        DateTime date = attendance.AttendanceDate;
 | 
			
		||||
                        finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime);
 | 
			
		||||
                        if (attendance.InTime < finalDateTime)
 | 
			
		||||
                        {
 | 
			
		||||
                            attendance.OutTime = finalDateTime;
 | 
			
		||||
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            _logger.LogError("Employee {EmployeeId} sent regularization request but it check-out time is earlier than check-out");
 | 
			
		||||
                            return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Check-out time must be later than check-in time", 400));
 | 
			
		||||
                        }
 | 
			
		||||
                        // do nothing
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE)
 | 
			
		||||
                    {
 | 
			
		||||
                        attendance.IsApproved = true;
 | 
			
		||||
                        attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
 | 
			
		||||
                        // do nothing
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT)
 | 
			
		||||
                    {
 | 
			
		||||
                        attendance.IsApproved = false;
 | 
			
		||||
                        attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
 | 
			
		||||
                        // do nothing
 | 
			
		||||
                    }
 | 
			
		||||
                    attendance.Date = DateTime.UtcNow;
 | 
			
		||||
 | 
			
		||||
                    // update code
 | 
			
		||||
                    _context.Attendes.Update(attendance);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    attendance = new Attendance();
 | 
			
		||||
                    attendance.TenantId = TenantId;
 | 
			
		||||
                    attendance.AttendanceDate = recordAttendanceDot.Date;
 | 
			
		||||
                    // attendance.Activity = recordAttendanceDot.Action;
 | 
			
		||||
                    attendance.Comment = recordAttendanceDot.Comment;
 | 
			
		||||
                    attendance.EmployeeID = recordAttendanceDot.EmployeeID;
 | 
			
		||||
                    attendance.ProjectID = recordAttendanceDot.ProjectID;
 | 
			
		||||
                    attendance.Date = DateTime.UtcNow;
 | 
			
		||||
 | 
			
		||||
                    switch (recordAttendanceDot.Action)
 | 
			
		||||
                    {
 | 
			
		||||
                        case ATTENDANCE_MARK_TYPE.CHECK_IN:
 | 
			
		||||
                            attendance.InTime = finalDateTime;
 | 
			
		||||
                            attendance.OutTime = null;
 | 
			
		||||
                            attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case ATTENDANCE_MARK_TYPE.CHECK_OUT:
 | 
			
		||||
                            attendance.OutTime = finalDateTime;
 | 
			
		||||
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
 | 
			
		||||
                            attendance.IsApproved = true;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE:
 | 
			
		||||
                            DateTime date = attendance.InTime ?? recordAttendanceDot.Date;
 | 
			
		||||
                            finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime);
 | 
			
		||||
                            if (attendance.InTime < finalDateTime)
 | 
			
		||||
                            {
 | 
			
		||||
                                attendance.OutTime = finalDateTime;
 | 
			
		||||
                                attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                _logger.LogWarning("Regularization check-out time is before check-in.");
 | 
			
		||||
                                return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Invalid regularization", 400));
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case ATTENDANCE_MARK_TYPE.REGULARIZE:
 | 
			
		||||
                            attendance.IsApproved = true;
 | 
			
		||||
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
 | 
			
		||||
                            attendance.IsApproved = false;
 | 
			
		||||
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    _context.Attendes.Update(attendance);
 | 
			
		||||
 | 
			
		||||
                    attendance.InTime = finalDateTime;
 | 
			
		||||
                    attendance.OutTime = null;
 | 
			
		||||
                    attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
 | 
			
		||||
 | 
			
		||||
                    _context.Attendes.Add(attendance);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Upload image if present
 | 
			
		||||
                Document? document = null;
 | 
			
		||||
                string? preSignedUrl = null;
 | 
			
		||||
                var Image = recordAttendanceDot.Image;
 | 
			
		||||
                var preSignedUrl = string.Empty;
 | 
			
		||||
 | 
			
		||||
                if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.ContentType != null)
 | 
			
		||||
                if (Image != null && Image.ContentType != null)
 | 
			
		||||
                {
 | 
			
		||||
                    string base64 = recordAttendanceDot.Image.Base64Data?.Split(',').LastOrDefault() ?? "";
 | 
			
		||||
                    if (string.IsNullOrWhiteSpace(base64))
 | 
			
		||||
                        return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Image data missing", 400));
 | 
			
		||||
 | 
			
		||||
                    var fileType = _s3Service.GetContentTypeFromBase64(base64);
 | 
			
		||||
                    var fileName = _s3Service.GenerateFileName(fileType, tenantId, "attendance");
 | 
			
		||||
                    var objectKey = $"tenant-{tenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
 | 
			
		||||
                    if (string.IsNullOrEmpty(Image.Base64Data))
 | 
			
		||||
                        return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
 | 
			
		||||
 | 
			
		||||
                    //If base64 has a data URI prefix, strip it
 | 
			
		||||
                    var base64 = Image.Base64Data.Contains(",")
 | 
			
		||||
                        ? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
 | 
			
		||||
                        : Image.Base64Data;
 | 
			
		||||
 | 
			
		||||
                    string fileType = _s3Service.GetContentTypeFromBase64(base64);
 | 
			
		||||
                    string fileName = _s3Service.GenerateFileName(fileType, TenantId, "attendance");
 | 
			
		||||
 | 
			
		||||
                    string objectKey = $"tenant-{TenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
 | 
			
		||||
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                    preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
 | 
			
		||||
 | 
			
		||||
                    document = new Document
 | 
			
		||||
                    {
 | 
			
		||||
                        FileName = recordAttendanceDot.Image.FileName ?? "",
 | 
			
		||||
                        ContentType = recordAttendanceDot.Image.ContentType,
 | 
			
		||||
                        FileName = Image.FileName ?? "",
 | 
			
		||||
                        ContentType = Image.ContentType,
 | 
			
		||||
                        S3Key = objectKey,
 | 
			
		||||
                        Base64Data = recordAttendanceDot.Image.Base64Data,
 | 
			
		||||
                        FileSize = recordAttendanceDot.Image.FileSize,
 | 
			
		||||
                        Base64Data = Image.Base64Data,
 | 
			
		||||
                        FileSize = Image.FileSize,
 | 
			
		||||
                        UploadedAt = recordAttendanceDot.Date,
 | 
			
		||||
                        TenantId = tenantId
 | 
			
		||||
                        TenantId = TenantId
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    await _context.SaveChangesAsync();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Log attendance
 | 
			
		||||
                var attendanceLog = new AttendanceLog
 | 
			
		||||
                {
 | 
			
		||||
                    AttendanceId = attendance.Id,
 | 
			
		||||
                    Activity = attendance.Activity,
 | 
			
		||||
                    ActivityTime = finalDateTime,
 | 
			
		||||
                    Comment = recordAttendanceDot.Comment,
 | 
			
		||||
                    EmployeeID = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                    Latitude = recordAttendanceDot.Latitude,
 | 
			
		||||
                    Longitude = recordAttendanceDot.Longitude,
 | 
			
		||||
                    DocumentId = document?.Id,
 | 
			
		||||
                    TenantId = tenantId,
 | 
			
		||||
                    UpdatedBy = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                    UpdatedOn = recordAttendanceDot.Date
 | 
			
		||||
                };
 | 
			
		||||
                _context.AttendanceLogs.Add(attendanceLog);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // Step 3: Always insert a new log entry
 | 
			
		||||
                if (document != null)
 | 
			
		||||
                {
 | 
			
		||||
                    var attendanceLog = new AttendanceLog
 | 
			
		||||
                    {
 | 
			
		||||
                        AttendanceId = attendance.Id, // Use existing or new AttendanceId
 | 
			
		||||
                        Activity = attendance.Activity,
 | 
			
		||||
 | 
			
		||||
                        ActivityTime = finalDateTime,
 | 
			
		||||
                        Comment = recordAttendanceDot.Comment,
 | 
			
		||||
                        EmployeeID = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                        Latitude = recordAttendanceDot.Latitude,
 | 
			
		||||
                        Longitude = recordAttendanceDot.Longitude,
 | 
			
		||||
                        DocumentId = document.Id,
 | 
			
		||||
                        TenantId = TenantId,
 | 
			
		||||
                        UpdatedBy = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                        UpdatedOn = recordAttendanceDot.Date
 | 
			
		||||
                    };
 | 
			
		||||
                    _context.AttendanceLogs.Add(attendanceLog);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    var attendanceLog = new AttendanceLog
 | 
			
		||||
                    {
 | 
			
		||||
                        AttendanceId = attendance.Id, // Use existing or new AttendanceId
 | 
			
		||||
                        Activity = attendance.Activity,
 | 
			
		||||
 | 
			
		||||
                        ActivityTime = finalDateTime,
 | 
			
		||||
                        Comment = recordAttendanceDot.Comment,
 | 
			
		||||
                        EmployeeID = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                        Latitude = recordAttendanceDot.Latitude,
 | 
			
		||||
                        Longitude = recordAttendanceDot.Longitude,
 | 
			
		||||
                        DocumentId = document != null ? document.Id : null,
 | 
			
		||||
                        TenantId = TenantId,
 | 
			
		||||
                        UpdatedBy = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                        UpdatedOn = recordAttendanceDot.Date
 | 
			
		||||
                    };
 | 
			
		||||
                    _context.AttendanceLogs.Add(attendanceLog);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                //if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.Count > 0)
 | 
			
		||||
                //{
 | 
			
		||||
                //    attendanceLog.Photo = recordAttendanceDot.Image[0].Base64Data;
 | 
			
		||||
                //}
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
                await transaction.CommitAsync();
 | 
			
		||||
 | 
			
		||||
                // Construct view model
 | 
			
		||||
                var employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
 | 
			
		||||
                var vm = new EmployeeAttendanceVM
 | 
			
		||||
                await transaction.CommitAsync(); // Commit transaction
 | 
			
		||||
 | 
			
		||||
                Employee employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
 | 
			
		||||
                if (employee.JobRole != null)
 | 
			
		||||
                {
 | 
			
		||||
                    Id = attendance.Id,
 | 
			
		||||
                    EmployeeId = employee.Id,
 | 
			
		||||
                    FirstName = employee.FirstName,
 | 
			
		||||
                    LastName = employee.LastName,
 | 
			
		||||
                    CheckInTime = attendance.InTime,
 | 
			
		||||
                    CheckOutTime = attendance.OutTime,
 | 
			
		||||
                    Activity = attendance.Activity,
 | 
			
		||||
                    JobRoleName = employee.JobRole?.Name,
 | 
			
		||||
                    DocumentId = document?.Id ?? Guid.Empty,
 | 
			
		||||
                    ThumbPreSignedUrl = preSignedUrl ?? "",
 | 
			
		||||
                    PreSignedUrl = preSignedUrl ?? ""
 | 
			
		||||
                };
 | 
			
		||||
                    EmployeeAttendanceVM vm = new EmployeeAttendanceVM();
 | 
			
		||||
                    if (document != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        vm = new EmployeeAttendanceVM()
 | 
			
		||||
                        {
 | 
			
		||||
                            CheckInTime = attendance.InTime,
 | 
			
		||||
                            CheckOutTime = attendance.OutTime,
 | 
			
		||||
                            EmployeeAvatar = null,
 | 
			
		||||
                            EmployeeId = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                            FirstName = employee.FirstName,
 | 
			
		||||
                            LastName = employee.LastName,
 | 
			
		||||
                            Id = attendance.Id,
 | 
			
		||||
                            Activity = attendance.Activity,
 | 
			
		||||
                            JobRoleName = employee.JobRole.Name,
 | 
			
		||||
                            DocumentId = document.Id,
 | 
			
		||||
                            ThumbPreSignedUrl = preSignedUrl,
 | 
			
		||||
                            PreSignedUrl = preSignedUrl
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        vm = new EmployeeAttendanceVM()
 | 
			
		||||
                        {
 | 
			
		||||
                            CheckInTime = attendance.InTime,
 | 
			
		||||
                            CheckOutTime = attendance.OutTime,
 | 
			
		||||
                            EmployeeAvatar = null,
 | 
			
		||||
                            EmployeeId = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                            FirstName = employee.FirstName,
 | 
			
		||||
                            LastName = employee.LastName,
 | 
			
		||||
                            Id = attendance.Id,
 | 
			
		||||
                            Activity = attendance.Activity,
 | 
			
		||||
                            JobRoleName = employee.JobRole.Name,
 | 
			
		||||
                            DocumentId = Guid.Empty,
 | 
			
		||||
                            ThumbPreSignedUrl = string.Empty,
 | 
			
		||||
                            PreSignedUrl = string.Empty
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                var notification = new
 | 
			
		||||
                {
 | 
			
		||||
                    LoggedInUserId = currentEmployee.Id,
 | 
			
		||||
                    Keyword = "Attendance",
 | 
			
		||||
                    Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0,
 | 
			
		||||
                    ProjectId = attendance.ProjectID,
 | 
			
		||||
                    Response = vm
 | 
			
		||||
                };
 | 
			
		||||
                    _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);
 | 
			
		||||
                    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);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(new EmployeeAttendanceVM(), "Attendance marked successfully.", 200));
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
 | 
			
		||||
                _logger.LogInfo("Attendance recorded for employee: {FullName}", $"{employee.FirstName} {employee.LastName}");
 | 
			
		||||
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                await transaction.RollbackAsync();
 | 
			
		||||
                _logger.LogError("Error while recording attendance : {Error}", ex.Message);
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Something went wrong", ex.Message, 500));
 | 
			
		||||
                await transaction.RollbackAsync(); // Rollback on failure
 | 
			
		||||
                _logger.LogError("{Error} while marking attendance", ex.Message);
 | 
			
		||||
                var response = new
 | 
			
		||||
                {
 | 
			
		||||
                    message = ex.Message,
 | 
			
		||||
                    detail = ex.StackTrace,
 | 
			
		||||
                    statusCode = StatusCodes.Status500InternalServerError
 | 
			
		||||
                };
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, response, 400));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        private static DateTime GetDateFromTimeStamp(DateTime date, string timeString)
 | 
			
		||||
        {
 | 
			
		||||
            //DateTime date = recordAttendanceDot.Date;
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ using Marco.Pms.Model.Employees;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.DashBoard;
 | 
			
		||||
using Marco.Pms.Services.Service;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
@ -22,13 +21,11 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
        private readonly ApplicationDbContext _context;
 | 
			
		||||
        private readonly UserHelper _userHelper;
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        private readonly PermissionServices _permissionServices;
 | 
			
		||||
        public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices)
 | 
			
		||||
        public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _userHelper = userHelper;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _permissionServices = permissionServices;
 | 
			
		||||
        }
 | 
			
		||||
        [HttpGet("progression")]
 | 
			
		||||
        public async Task<IActionResult> GetGraph([FromQuery] double days, [FromQuery] string FromDate, [FromQuery] Guid? projectId)
 | 
			
		||||
@ -357,102 +354,5 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            _logger.LogInfo($"Record of performed activities for project {projectId} for date {currentDate.Date} by employee {LoggedInEmployee.Id}");
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(report, $"Record of performed activities for project {project.Name} for date {currentDate.Date}", 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet("attendance-overview/{projectId}")]
 | 
			
		||||
        public async Task<IActionResult> GetAttendanceOverView(Guid projectId, [FromQuery] string days)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("GetAttendanceOverView called for ProjectId: {ProjectId}, Days: {Days}", projectId, days);
 | 
			
		||||
 | 
			
		||||
            // Step 1: Validate project existence
 | 
			
		||||
            var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId);
 | 
			
		||||
            if (project == null)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("Project not found for ProjectId: {ProjectId}", projectId);
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 400));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 2: Permission check
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            bool hasAssigned = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.ToString());
 | 
			
		||||
 | 
			
		||||
            if (!hasAssigned)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("Unauthorized access attempt. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
 | 
			
		||||
                return StatusCode(403, ApiResponse<object>.ErrorResponse(
 | 
			
		||||
                    "You don't have permission to access this feature",
 | 
			
		||||
                    "You don't have permission to access this feature", 403));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 3: Validate and parse days input
 | 
			
		||||
            if (!int.TryParse(days, out int dayCount) || dayCount <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("Invalid days input received: {Days}", days);
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid number of days", "Days must be a positive integer", 400));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 4: Define date range
 | 
			
		||||
            DateTime today = DateTime.UtcNow.Date;
 | 
			
		||||
            DateTime startDate = today.AddDays(-dayCount);
 | 
			
		||||
 | 
			
		||||
            // Step 5: Load project allocations and related job roles
 | 
			
		||||
            var allocations = await _context.ProjectAllocations
 | 
			
		||||
                .Where(pa => pa.ProjectId == projectId)
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            if (!allocations.Any())
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInfo("No employee allocations found for project: {ProjectId}", projectId);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(new List<AttendanceOverviewVM>(), "No allocations found", 200));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var jobRoleIds = allocations.Select(pa => pa.JobRoleId).Distinct().ToList();
 | 
			
		||||
 | 
			
		||||
            var jobRoles = await _context.JobRoles
 | 
			
		||||
                .Where(jr => jobRoleIds.Contains(jr.Id))
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            // Step 6: Load attendance records for given date range
 | 
			
		||||
            var attendances = await _context.Attendes
 | 
			
		||||
                .Where(a =>
 | 
			
		||||
                    a.ProjectID == projectId &&
 | 
			
		||||
                    a.InTime.HasValue &&
 | 
			
		||||
                    a.InTime.Value.Date >= startDate &&
 | 
			
		||||
                    a.InTime.Value.Date <= today)
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var overviewList = new List<AttendanceOverviewVM>();
 | 
			
		||||
 | 
			
		||||
            // Step 7: Process attendance per date per role
 | 
			
		||||
            for (DateTime date = today; date > startDate; date = date.AddDays(-1))
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var jobRole in jobRoles)
 | 
			
		||||
                {
 | 
			
		||||
                    var employeeIds = allocations
 | 
			
		||||
                        .Where(pa => pa.JobRoleId == jobRole.Id)
 | 
			
		||||
                        .Select(pa => pa.EmployeeId)
 | 
			
		||||
                        .ToList();
 | 
			
		||||
 | 
			
		||||
                    int presentCount = attendances
 | 
			
		||||
                        .Count(a => employeeIds.Contains(a.EmployeeID) && a.InTime!.Value.Date == date);
 | 
			
		||||
 | 
			
		||||
                    overviewList.Add(new AttendanceOverviewVM
 | 
			
		||||
                    {
 | 
			
		||||
                        Role = jobRole.Name,
 | 
			
		||||
                        Date = date.ToString("yyyy-MM-dd"),
 | 
			
		||||
                        Present = presentCount
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 8: Order result for consistent presentation
 | 
			
		||||
            var sortedResult = overviewList
 | 
			
		||||
                .OrderByDescending(r => r.Date)
 | 
			
		||||
                .ThenByDescending(r => r.Present)
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
            _logger.LogInfo("Attendance overview fetched. ProjectId: {ProjectId}, Records: {Count}", projectId, sortedResult.Count);
 | 
			
		||||
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(sortedResult, $"{sortedResult.Count} records fetched for attendance overview", 200));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -671,45 +671,6 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // -------------------------------- Work Status  --------------------------------
 | 
			
		||||
 | 
			
		||||
        [HttpGet("work-status")]
 | 
			
		||||
        public async Task<IActionResult> GetWorkStatusMasterList()
 | 
			
		||||
        {
 | 
			
		||||
            var response = await _masterHelper.GetWorkStatusList();
 | 
			
		||||
            return StatusCode(response.StatusCode, response);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost("work-status")]
 | 
			
		||||
        public async Task<IActionResult> CreateWorkStatusMaster([FromBody] CreateWorkStatusMasterDto createWorkStatusDto)
 | 
			
		||||
        {
 | 
			
		||||
            if (!ModelState.IsValid)
 | 
			
		||||
            {
 | 
			
		||||
                var errors = ModelState.Values
 | 
			
		||||
                    .SelectMany(v => v.Errors)
 | 
			
		||||
                    .Select(e => e.ErrorMessage)
 | 
			
		||||
                    .ToList();
 | 
			
		||||
                _logger.LogError("User sent Invalid Date while marking attendance");
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
 | 
			
		||||
            }
 | 
			
		||||
            var response = await _masterHelper.CreateWorkStatus(createWorkStatusDto);
 | 
			
		||||
            return StatusCode(response.StatusCode, response);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost("work-status/edit/{id}")]
 | 
			
		||||
        public async Task<IActionResult> UpdateWorkStatusMaster(Guid id, [FromBody] UpdateWorkStatusMasterDto updateWorkStatusDto)
 | 
			
		||||
        {
 | 
			
		||||
            var response = await _masterHelper.UpdateWorkStatus(id, updateWorkStatusDto);
 | 
			
		||||
            return StatusCode(response.StatusCode, response);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpDelete("work-status/{id}")]
 | 
			
		||||
        public async Task<IActionResult> DeleteWorkStatusMaster(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            var response = await _masterHelper.DeleteWorkStatus(id);
 | 
			
		||||
            return StatusCode(response.StatusCode, response);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // -------------------------------- Contact Category  --------------------------------
 | 
			
		||||
 | 
			
		||||
        [HttpGet("contact-categories")]
 | 
			
		||||
 | 
			
		||||
@ -578,6 +578,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            projectIds.Add(projectAllocation.ProjectId);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
@ -595,82 +596,59 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost("task")]
 | 
			
		||||
        public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDtos)
 | 
			
		||||
        public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDot)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("CreateProjectTask called with {Count} items", workItemDtos?.Count ?? 0);
 | 
			
		||||
 | 
			
		||||
            // Validate request
 | 
			
		||||
            if (workItemDtos == null || !workItemDtos.Any())
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("No work items provided in the request.");
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Work Item details are not valid.", 400));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = GetTenantId();
 | 
			
		||||
            var workItemsToCreate = new List<WorkItem>();
 | 
			
		||||
            var workItemsToUpdate = new List<WorkItem>();
 | 
			
		||||
            var responseList = new List<WorkItemVM>();
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            string message = "";
 | 
			
		||||
            List<WorkItemVM> workItems = new List<WorkItemVM> { };
 | 
			
		||||
            List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
 | 
			
		||||
            foreach (var itemDto in workItemDtos)
 | 
			
		||||
            {
 | 
			
		||||
                var workItem = itemDto.ToWorkItemFromWorkItemDto(tenantId);
 | 
			
		||||
                var workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == workItem.WorkAreaId) ?? new WorkArea();
 | 
			
		||||
 | 
			
		||||
                Building building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty)) ?? new Building();
 | 
			
		||||
 | 
			
		||||
                if (itemDto.Id != null && itemDto.Id != Guid.Empty)
 | 
			
		||||
                {
 | 
			
		||||
                    // Update existing
 | 
			
		||||
                    workItemsToUpdate.Add(workItem);
 | 
			
		||||
                    message = $"Task Updated in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // Create new
 | 
			
		||||
                    workItem.Id = Guid.NewGuid();
 | 
			
		||||
                    workItemsToCreate.Add(workItem);
 | 
			
		||||
                    message = $"Task Added in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                responseList.Add(new WorkItemVM
 | 
			
		||||
                {
 | 
			
		||||
                    WorkItemId = workItem.Id,
 | 
			
		||||
                    WorkItem = workItem
 | 
			
		||||
                });
 | 
			
		||||
                projectIds.Add(building.ProjectId);
 | 
			
		||||
            }
 | 
			
		||||
            string message = "";
 | 
			
		||||
            string responseMessage = "";
 | 
			
		||||
            // Apply DB changes
 | 
			
		||||
            if (workItemsToCreate.Any())
 | 
			
		||||
            if (workItemDot != null)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInfo("Adding {Count} new work items", workItemsToCreate.Count);
 | 
			
		||||
                await _context.WorkItems.AddRangeAsync(workItemsToCreate);
 | 
			
		||||
                responseMessage = "Task Added Successfully";
 | 
			
		||||
                foreach (var item in workItemDot)
 | 
			
		||||
                {
 | 
			
		||||
                    WorkItem workItem = item.ToWorkItemFromWorkItemDto(tenantId);
 | 
			
		||||
 | 
			
		||||
                    var workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == workItem.WorkAreaId) ?? new WorkArea();
 | 
			
		||||
 | 
			
		||||
                    Building building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty)) ?? new Building();
 | 
			
		||||
 | 
			
		||||
                    if (item.Id != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        //update
 | 
			
		||||
                        _context.WorkItems.Update(workItem);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        responseMessage = "Task Updated Successfully";
 | 
			
		||||
                        message = $"Task Updated in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //create
 | 
			
		||||
                        _context.WorkItems.Add(workItem);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        responseMessage = "Task Added Successfully";
 | 
			
		||||
                        message = $"Task Added in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                    }
 | 
			
		||||
                    var result = new WorkItemVM
 | 
			
		||||
                    {
 | 
			
		||||
                        WorkItemId = workItem.Id,
 | 
			
		||||
                        WorkItem = workItem
 | 
			
		||||
                    };
 | 
			
		||||
                    workItems.Add(result);
 | 
			
		||||
                    projectIds.Add(building.ProjectId);
 | 
			
		||||
                }
 | 
			
		||||
                var activity = await _context.ActivityMasters.ToListAsync();
 | 
			
		||||
                var category = await _context.WorkCategoryMasters.ToListAsync();
 | 
			
		||||
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(workItems, responseMessage, 200));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (workItemsToUpdate.Any())
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInfo("Updating {Count} existing work items", workItemsToUpdate.Count);
 | 
			
		||||
                _context.WorkItems.UpdateRange(workItemsToUpdate);
 | 
			
		||||
                responseMessage = "Task Updated Successfully";
 | 
			
		||||
            return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Work Item Details are not valid.", 400));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
            _logger.LogInfo("CreateProjectTask completed successfully. Created: {Created}, Updated: {Updated}", workItemsToCreate.Count, workItemsToUpdate.Count);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message };
 | 
			
		||||
 | 
			
		||||
            await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(responseList, responseMessage, 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpDelete("task/{id}")]
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -3,10 +3,8 @@ using Marco.Pms.Model.Directory;
 | 
			
		||||
using Marco.Pms.Model.Dtos.Master;
 | 
			
		||||
using Marco.Pms.Model.Employees;
 | 
			
		||||
using Marco.Pms.Model.Mapper;
 | 
			
		||||
using Marco.Pms.Model.Master;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Master;
 | 
			
		||||
using Marco.Pms.Services.Service;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
@ -18,19 +16,13 @@ namespace Marco.Pms.Services.Helpers
 | 
			
		||||
        private readonly ApplicationDbContext _context;
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        private readonly UserHelper _userHelper;
 | 
			
		||||
        private readonly PermissionServices _permissionService;
 | 
			
		||||
        private readonly Guid View_Master;
 | 
			
		||||
        private readonly Guid Manage_Master;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public MasterHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices)
 | 
			
		||||
        public MasterHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _userHelper = userHelper;
 | 
			
		||||
            _permissionService = permissionServices;
 | 
			
		||||
            View_Master = Guid.Parse("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d");
 | 
			
		||||
            Manage_Master = Guid.Parse("588a8824-f924-4955-82d8-fc51956cf323");
 | 
			
		||||
        }
 | 
			
		||||
        // -------------------------------- Contact Category  --------------------------------
 | 
			
		||||
        public async Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto contactCategoryDto)
 | 
			
		||||
@ -255,215 +247,5 @@ namespace Marco.Pms.Services.Helpers
 | 
			
		||||
            return ApiResponse<object>.SuccessResponse(new { }, "Tag deleted successfully", 200);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // -------------------------------- Work Status  --------------------------------
 | 
			
		||||
        public async Task<ApiResponse<object>> GetWorkStatusList()
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("GetWorkStatusList called.");
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Step 1: Get tenant and logged-in employee info
 | 
			
		||||
                Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
                // Step 2: Check permission to view master data
 | 
			
		||||
                bool hasViewPermission = await _permissionService.HasPermission(View_Master, loggedInEmployee.Id);
 | 
			
		||||
                if (!hasViewPermission)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 3: Fetch work statuses for the tenant
 | 
			
		||||
                var workStatusList = await _context.WorkStatusMasters
 | 
			
		||||
                    .Where(ws => ws.TenantId == tenantId)
 | 
			
		||||
                    .Select(ws => new
 | 
			
		||||
                    {
 | 
			
		||||
                        ws.Id,
 | 
			
		||||
                        ws.Name,
 | 
			
		||||
                        ws.Description,
 | 
			
		||||
                        ws.IsSystem
 | 
			
		||||
                    })
 | 
			
		||||
                    .ToListAsync();
 | 
			
		||||
 | 
			
		||||
                _logger.LogInfo("{Count} work statuses fetched for tenantId: {TenantId}", workStatusList.Count, tenantId);
 | 
			
		||||
 | 
			
		||||
                // Step 4: Return successful response
 | 
			
		||||
                return ApiResponse<object>.SuccessResponse(
 | 
			
		||||
                    workStatusList,
 | 
			
		||||
                    $"{workStatusList.Count} work status records fetched successfully",
 | 
			
		||||
                    200
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("Error occurred while fetching work status list : {Error}", ex.Message);
 | 
			
		||||
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch work status list", 500);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public async Task<ApiResponse<object>> CreateWorkStatus(CreateWorkStatusMasterDto createWorkStatusDto)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("CreateWorkStatus called with Name: {Name}", createWorkStatusDto.Name ?? "");
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Step 1: Get tenant and logged-in employee
 | 
			
		||||
                Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
                // Step 2: Check if user has permission to manage master data
 | 
			
		||||
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
			
		||||
                if (!hasManageMasterPermission)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 3: Check if work status with the same name already exists
 | 
			
		||||
                var existingWorkStatus = await _context.WorkStatusMasters
 | 
			
		||||
                    .FirstOrDefaultAsync(ws => ws.Name == createWorkStatusDto.Name && ws.TenantId == tenantId);
 | 
			
		||||
 | 
			
		||||
                if (existingWorkStatus != null)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Work status already exists: {Name}", createWorkStatusDto.Name ?? "");
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Work status already exists", "Work status already exists", 400);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 4: Create new WorkStatusMaster entry
 | 
			
		||||
                var workStatus = new WorkStatusMaster
 | 
			
		||||
                {
 | 
			
		||||
                    Name = createWorkStatusDto.Name?.Trim() ?? "",
 | 
			
		||||
                    Description = createWorkStatusDto.Description?.Trim() ?? "",
 | 
			
		||||
                    IsSystem = false,
 | 
			
		||||
                    TenantId = tenantId
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                _context.WorkStatusMasters.Add(workStatus);
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                _logger.LogInfo("Work status created successfully: {Id}, Name: {Name}", workStatus.Id, workStatus.Name);
 | 
			
		||||
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status created successfully", 200);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("Error occurred while creating work status : {Error}", ex.Message);
 | 
			
		||||
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to create work status", 500);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public async Task<ApiResponse<object>> UpdateWorkStatus(Guid id, UpdateWorkStatusMasterDto updateWorkStatusDto)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("UpdateWorkStatus called for WorkStatus ID: {Id}, New Name: {Name}", id, updateWorkStatusDto.Name ?? "");
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Step 1: Validate input
 | 
			
		||||
                if (id == Guid.Empty || id != updateWorkStatusDto.Id)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Invalid ID provided for update. Route ID: {RouteId}, DTO ID: {DtoId}", id, updateWorkStatusDto.Id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Invalid data provided", "The provided work status ID is invalid", 400);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 2: Get tenant and logged-in employee
 | 
			
		||||
                Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
                // Step 3: Check permissions
 | 
			
		||||
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
			
		||||
                if (!hasManageMasterPermission)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage Master permission.", loggedInEmployee.Id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Access denied", "You do not have permission to update this work status", 403);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 4: Retrieve the work status record
 | 
			
		||||
                var workStatus = await _context.WorkStatusMasters
 | 
			
		||||
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);
 | 
			
		||||
 | 
			
		||||
                if (workStatus == null)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Work status not found for ID: {Id}", id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Work status not found", "No work status found with the provided ID", 404);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 5: Check for duplicate name (optional)
 | 
			
		||||
                var isDuplicate = await _context.WorkStatusMasters
 | 
			
		||||
                    .AnyAsync(ws => ws.Name == updateWorkStatusDto.Name && ws.Id != id && ws.TenantId == tenantId);
 | 
			
		||||
 | 
			
		||||
                if (isDuplicate)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Duplicate work status name '{Name}' detected during update. ID: {Id}", updateWorkStatusDto.Name ?? "", id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Work status with the same name already exists", "Duplicate name", 400);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 6: Update fields
 | 
			
		||||
                workStatus.Name = updateWorkStatusDto.Name?.Trim() ?? "";
 | 
			
		||||
                workStatus.Description = updateWorkStatusDto.Description?.Trim() ?? "";
 | 
			
		||||
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                _logger.LogInfo("Work status updated successfully. ID: {Id}", id);
 | 
			
		||||
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status updated successfully", 200);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("Error occurred while updating work status ID: {Id} : {Error}", id, ex.Message);
 | 
			
		||||
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public async Task<ApiResponse<object>> DeleteWorkStatus(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("DeleteWorkStatus called for Id: {Id}", id);
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Step 1: Get current tenant and logged-in employee
 | 
			
		||||
                Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
                // Step 2: Check permission to manage master data
 | 
			
		||||
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
			
		||||
                if (!hasManageMasterPermission)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Delete denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Access denied for deleting work status", 403);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 3: Find the work status
 | 
			
		||||
                var workStatus = await _context.WorkStatusMasters
 | 
			
		||||
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);
 | 
			
		||||
 | 
			
		||||
                if (workStatus == null)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Work status not found for Id: {Id}", id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse("Work status not found", "Work status not found", 404);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 4: Check for dependencies in TaskAllocations
 | 
			
		||||
                bool hasDependency = await _context.TaskAllocations
 | 
			
		||||
                    .AnyAsync(ta => ta.TenantId == tenantId && ta.WorkStatusId == id);
 | 
			
		||||
 | 
			
		||||
                if (hasDependency)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogWarning("Cannot delete WorkStatus Id: {Id} due to existing task dependency", id);
 | 
			
		||||
                    return ApiResponse<object>.ErrorResponse(
 | 
			
		||||
                        "Work status has a dependency in assigned tasks and cannot be deleted",
 | 
			
		||||
                        "Deletion failed due to associated tasks",
 | 
			
		||||
                        400
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Step 5: Delete and persist
 | 
			
		||||
                _context.WorkStatusMasters.Remove(workStatus);
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                _logger.LogInfo("Work status deleted successfully. Id: {Id}", id);
 | 
			
		||||
                return ApiResponse<object>.SuccessResponse(new { }, "Work status deleted successfully", 200);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("Error occurred while deleting WorkStatus Id: {Id} : {Error}", id, ex.Message);
 | 
			
		||||
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to delete work status", 500);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user