Compare commits
22 Commits
2ed0e6e5b6
...
70c1c6da9d
| Author | SHA1 | Date | |
|---|---|---|---|
| 70c1c6da9d | |||
| e3ddcee8b3 | |||
| 0d49163d7b | |||
| b94f4bdb63 | |||
| 82ebd07d61 | |||
| b78c5c07c5 | |||
| 64a7cde69c | |||
| a6a842bf10 | |||
| c3f5fe8e34 | |||
| c9bb18d8e5 | |||
| 2e925efcf7 | |||
| 5bc13e215d | |||
|
|
f20b4a42a1 | ||
| 99818c42b0 | |||
| 5d5579882f | |||
| aa2bc674eb | |||
|
|
692b1bfef3 | ||
|
|
1c58265a9f | ||
| ff9c7c9434 | |||
| e391c82659 | |||
| 1f56143142 | |||
| dc21b9d2c6 |
@ -69,6 +69,7 @@ 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; }
|
||||
@ -434,6 +435,32 @@ 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")
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
3375
Marco.Pms.DataAccess/Migrations/20250616064217_Added_Apporved_By_In_TaskAllocation_Table.Designer.cs
generated
Normal file
3375
Marco.Pms.DataAccess/Migrations/20250616064217_Added_Apporved_By_In_TaskAllocation_Table.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,188 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
3381
Marco.Pms.DataAccess/Migrations/20250618112021_EnhancedWorkItemForParentId_Description.Designer.cs
generated
Normal file
3381
Marco.Pms.DataAccess/Migrations/20250618112021_EnhancedWorkItemForParentId_Description.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@
|
||||
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,6 +28,12 @@ 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)");
|
||||
|
||||
@ -40,26 +46,44 @@ 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");
|
||||
});
|
||||
|
||||
@ -1942,6 +1966,59 @@ 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")
|
||||
@ -2121,6 +2198,12 @@ 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");
|
||||
|
||||
@ -2451,12 +2534,20 @@ 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")
|
||||
@ -2469,11 +2560,21 @@ 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 =>
|
||||
@ -3086,6 +3187,17 @@ 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,5 +1,6 @@
|
||||
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;
|
||||
@ -10,29 +11,43 @@ 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 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; }
|
||||
}
|
||||
}
|
||||
|
||||
12
Marco.Pms.Model/Activities/WorkStatusMaster.cs
Normal file
12
Marco.Pms.Model/Activities/WorkStatusMaster.cs
Normal file
@ -0,0 +1,12 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
13
Marco.Pms.Model/Dtos/Activities/ApproveTaskDto.cs
Normal file
13
Marco.Pms.Model/Dtos/Activities/ApproveTaskDto.cs
Normal file
@ -0,0 +1,13 @@
|
||||
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,6 +3,7 @@
|
||||
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
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
namespace Marco.Pms.Model.Dtos.Master
|
||||
{
|
||||
public class CreateWorkStatusMasterDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ 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; }
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
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,5 +11,7 @@ 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,6 +13,7 @@ namespace Marco.Pms.Model.Mapper
|
||||
return new TaskAllocation
|
||||
{
|
||||
AssignmentDate = assignTask.AssignmentDate,
|
||||
ParentTaskId = assignTask.ParentTaskId,
|
||||
PlannedTask = assignTask.PlannedTask,
|
||||
CompletedTask = 0,
|
||||
Description = assignTask.Description,
|
||||
@ -43,18 +44,23 @@ namespace Marco.Pms.Model.Mapper
|
||||
TenantId = tenantId
|
||||
};
|
||||
}
|
||||
public static TaskVM TaskAllocationToTaskVM(this TaskAllocation taskAllocation, string employeeName)
|
||||
public static TaskVM TaskAllocationToTaskVM(this TaskAllocation taskAllocation)
|
||||
{
|
||||
return new TaskVM
|
||||
{
|
||||
Id = taskAllocation.Id,
|
||||
AssignmentDate = taskAllocation.AssignmentDate,
|
||||
ReportedDate = taskAllocation.ReportedDate,
|
||||
ApprovedDate = taskAllocation.ApprovedDate,
|
||||
PlannedTask = taskAllocation.PlannedTask,
|
||||
CompletedTask = taskAllocation.CompletedTask,
|
||||
ReportedDate = taskAllocation.ReportedDate,
|
||||
NotApprovedTask = taskAllocation.ApprovedById == null ? taskAllocation.CompletedTask : (taskAllocation.CompletedTask - taskAllocation.ReportedTask),
|
||||
Description = taskAllocation.Description,
|
||||
AssignBy = employeeName,
|
||||
WorkItem = taskAllocation.WorkItem
|
||||
AssignedBy = taskAllocation.Employee?.ToBasicEmployeeVMFromEmployee(),
|
||||
ReportedBy = taskAllocation.ReportedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
ApprovedBy = taskAllocation.ApprovedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
WorkItem = taskAllocation.WorkItem,
|
||||
WorkStatus = taskAllocation.WorkStatus
|
||||
};
|
||||
}
|
||||
public static AssignedTaskVM ToAssignTaskVMFromTaskAllocation(this TaskAllocation taskAllocation)
|
||||
@ -100,12 +106,18 @@ 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,
|
||||
AssignedBy = taskAllocation.Employee != null ? taskAllocation.Employee.ToBasicEmployeeVMFromEmployee() : new BasicEmployeeVM(),
|
||||
NotApprovedTask = taskAllocation.ApprovedById == null ? taskAllocation.CompletedTask : (taskAllocation.CompletedTask - taskAllocation.ReportedTask),
|
||||
AssignedBy = taskAllocation.Employee?.ToBasicEmployeeVMFromEmployee(),
|
||||
ReportedBy = taskAllocation.ReportedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
ApprovedBy = taskAllocation.ApprovedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
WorkItemId = taskAllocation.WorkItemId,
|
||||
WorkItem = taskAllocation.WorkItem
|
||||
};
|
||||
|
||||
@ -59,7 +59,9 @@ namespace Marco.Pms.Model.Mapper
|
||||
WorkCategoryId = model.WorkCategoryId,
|
||||
TaskDate = DateTime.Now,
|
||||
TenantId = tenantId,
|
||||
WorkAreaId = model.WorkAreaID
|
||||
WorkAreaId = model.WorkAreaID,
|
||||
ParentTaskId = model.ParentTaskId,
|
||||
Description = model.Comment
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -20,11 +20,17 @@ 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,15 +1,22 @@
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Master;
|
||||
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.Projects;
|
||||
using Marco.Pms.Model.ViewModels.Employee;
|
||||
using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.Projects;
|
||||
|
||||
namespace Marco.Pms.Model.ViewModels.Activities
|
||||
{
|
||||
@ -7,14 +7,19 @@ 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 DateTime? ReportedDate { get; set; }
|
||||
public double NotApprovedTask { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? AssignBy { get; set; }
|
||||
public BasicEmployeeVM? AssignedBy { get; set; }
|
||||
public BasicEmployeeVM? ReportedBy { get; set; }
|
||||
public BasicEmployeeVM? ApprovedBy { get; set; }
|
||||
public WorkStatusMaster? WorkStatus { get; set; }
|
||||
public WorkItem? WorkItem { get; set; }
|
||||
public List<string>? PreSignedUrls { get; set; }
|
||||
public List<CommentVM>? Comments { get; set; }
|
||||
public List<EmployeeVM>? TeamMembers { get; set; }
|
||||
public List<BasicEmployeeVM>? TeamMembers { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
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,257 +597,185 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
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");
|
||||
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
|
||||
_logger.LogError("Invalid attendance model received.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
}
|
||||
|
||||
Guid TenantId = GetTenantId();
|
||||
Guid tenantId = GetTenantId();
|
||||
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ;
|
||||
|
||||
// Validate mark time
|
||||
if (recordAttendanceDot.MarkTime == null)
|
||||
{
|
||||
_logger.LogError("User sent Invalid Mark Time while marking attendance");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Invalid Mark Time", 400));
|
||||
_logger.LogWarning("Null mark time provided.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Mark time is required", 400));
|
||||
}
|
||||
|
||||
DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
|
||||
if (recordAttendanceDot.Comment == null)
|
||||
if (string.IsNullOrWhiteSpace(recordAttendanceDot.Comment))
|
||||
{
|
||||
_logger.LogError("User sent Invalid comment while marking attendance");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Invalid Comment", 400));
|
||||
_logger.LogWarning("Empty comment provided.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Comment is required", 400));
|
||||
}
|
||||
|
||||
if (attendance != null)
|
||||
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)
|
||||
{
|
||||
attendance.Comment = recordAttendanceDot.Comment;
|
||||
if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_IN)
|
||||
attendance = new 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);
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
attendance.InTime = finalDateTime;
|
||||
attendance.OutTime = null;
|
||||
attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
|
||||
|
||||
_context.Attendes.Add(attendance);
|
||||
|
||||
_context.Attendes.Update(attendance);
|
||||
}
|
||||
|
||||
// Upload image if present
|
||||
Document? document = null;
|
||||
var Image = recordAttendanceDot.Image;
|
||||
var preSignedUrl = string.Empty;
|
||||
string? preSignedUrl = null;
|
||||
|
||||
if (Image != null && Image.ContentType != null)
|
||||
if (recordAttendanceDot.Image != null && recordAttendanceDot.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));
|
||||
|
||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "attendance");
|
||||
var objectKey = $"tenant-{tenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
|
||||
|
||||
//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 = Image.FileName ?? "",
|
||||
ContentType = Image.ContentType,
|
||||
FileName = recordAttendanceDot.Image.FileName ?? "",
|
||||
ContentType = recordAttendanceDot.Image.ContentType,
|
||||
S3Key = objectKey,
|
||||
Base64Data = Image.Base64Data,
|
||||
FileSize = Image.FileSize,
|
||||
Base64Data = recordAttendanceDot.Image.Base64Data,
|
||||
FileSize = recordAttendanceDot.Image.FileSize,
|
||||
UploadedAt = recordAttendanceDot.Date,
|
||||
TenantId = TenantId
|
||||
TenantId = tenantId
|
||||
};
|
||||
|
||||
_context.Documents.Add(document);
|
||||
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Step 3: Always insert a new log entry
|
||||
if (document != null)
|
||||
// Log attendance
|
||||
var attendanceLog = new AttendanceLog
|
||||
{
|
||||
var attendanceLog = new AttendanceLog
|
||||
{
|
||||
AttendanceId = attendance.Id, // Use existing or new AttendanceId
|
||||
Activity = attendance.Activity,
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
await transaction.CommitAsync(); // Commit transaction
|
||||
|
||||
Employee employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
|
||||
if (employee.JobRole != null)
|
||||
// Construct view model
|
||||
var employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
|
||||
var vm = new EmployeeAttendanceVM
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
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 ?? ""
|
||||
};
|
||||
|
||||
_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));
|
||||
var notification = new
|
||||
{
|
||||
LoggedInUserId = currentEmployee.Id,
|
||||
Keyword = "Attendance",
|
||||
Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0,
|
||||
ProjectId = attendance.ProjectID,
|
||||
Response = vm
|
||||
};
|
||||
|
||||
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(); // 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));
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError("Error while recording attendance : {Error}", ex.Message);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Something went wrong", ex.Message, 500));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static DateTime GetDateFromTimeStamp(DateTime date, string timeString)
|
||||
{
|
||||
//DateTime date = recordAttendanceDot.Date;
|
||||
|
||||
@ -5,6 +5,7 @@ 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;
|
||||
@ -21,11 +22,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly ILoggingService _logger;
|
||||
public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger)
|
||||
private readonly PermissionServices _permissionServices;
|
||||
public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices)
|
||||
{
|
||||
_context = context;
|
||||
_userHelper = userHelper;
|
||||
_logger = logger;
|
||||
_permissionServices = permissionServices;
|
||||
}
|
||||
[HttpGet("progression")]
|
||||
public async Task<IActionResult> GetGraph([FromQuery] double days, [FromQuery] string FromDate, [FromQuery] Guid? projectId)
|
||||
@ -354,5 +357,102 @@ 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,6 +671,45 @@ 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,7 +578,6 @@ namespace MarcoBMS.Services.Controllers
|
||||
projectIds.Add(projectAllocation.ProjectId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -596,59 +595,82 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("task")]
|
||||
public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDot)
|
||||
public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDtos)
|
||||
{
|
||||
Guid tenantId = GetTenantId();
|
||||
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
List<WorkItemVM> workItems = new List<WorkItemVM> { };
|
||||
List<Guid> projectIds = new List<Guid>();
|
||||
string message = "";
|
||||
string responseMessage = "";
|
||||
if (workItemDot != null)
|
||||
_logger.LogInfo("CreateProjectTask called with {Count} items", workItemDtos?.Count ?? 0);
|
||||
|
||||
// Validate request
|
||||
if (workItemDtos == null || !workItemDtos.Any())
|
||||
{
|
||||
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));
|
||||
_logger.LogWarning("No work items provided in the request.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Work Item details are not valid.", 400));
|
||||
}
|
||||
|
||||
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<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 responseMessage = "";
|
||||
// Apply DB changes
|
||||
if (workItemsToCreate.Any())
|
||||
{
|
||||
_logger.LogInfo("Adding {Count} new work items", workItemsToCreate.Count);
|
||||
await _context.WorkItems.AddRangeAsync(workItemsToCreate);
|
||||
responseMessage = "Task Added Successfully";
|
||||
|
||||
}
|
||||
|
||||
if (workItemsToUpdate.Any())
|
||||
{
|
||||
_logger.LogInfo("Updating {Count} existing work items", workItemsToUpdate.Count);
|
||||
_context.WorkItems.UpdateRange(workItemsToUpdate);
|
||||
responseMessage = "Task Updated Successfully";
|
||||
|
||||
}
|
||||
|
||||
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,8 +3,10 @@ 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;
|
||||
@ -16,13 +18,19 @@ 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)
|
||||
public MasterHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices)
|
||||
{
|
||||
_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)
|
||||
@ -247,5 +255,215 @@ 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