Added Functionality for approving the reported task
This commit is contained in:
parent
9d5535edf1
commit
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; }
|
||||
@ -429,6 +430,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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
});
|
||||
|
||||
@ -1920,6 +1944,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")
|
||||
@ -2429,12 +2506,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")
|
||||
@ -2447,11 +2532,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 =>
|
||||
@ -3052,6 +3147,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
|
||||
|
@ -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; }
|
||||
|
@ -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.ReportedTask - taskAllocation.CompletedTask),
|
||||
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.ReportedTask - taskAllocation.CompletedTask),
|
||||
AssignedBy = taskAllocation.Employee?.ToBasicEmployeeVMFromEmployee(),
|
||||
ReportedBy = taskAllocation.ReportedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
ApprovedBy = taskAllocation.ApprovedBy?.ToBasicEmployeeVMFromEmployee(),
|
||||
WorkItemId = taskAllocation.WorkItemId,
|
||||
WorkItem = taskAllocation.WorkItem
|
||||
};
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Activities;
|
||||
using Marco.Pms.Model.Dtos.Activities;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Activities;
|
||||
using Marco.Pms.Model.ViewModels.Employee;
|
||||
using Marco.Pms.Services.Service;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@ -27,13 +26,20 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly S3UploadService _s3Service;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly PermissionServices _permissionServices;
|
||||
private readonly Guid Approve_Task;
|
||||
private readonly Guid Assign_Report_Task;
|
||||
|
||||
|
||||
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service)
|
||||
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices)
|
||||
{
|
||||
_context = context;
|
||||
_userHelper = userHelper;
|
||||
_s3Service = s3Service;
|
||||
_logger = logger;
|
||||
_permissionServices = permissionServices;
|
||||
Approve_Task = Guid.Parse("db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c");
|
||||
Assign_Report_Task = Guid.Parse("6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2");
|
||||
}
|
||||
|
||||
private Guid GetTenantId()
|
||||
@ -44,49 +50,68 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("assign")]
|
||||
public async Task<IActionResult> AssignTask([FromBody] AssignTaskDto assignTask)
|
||||
{
|
||||
// Validate the incoming model
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var errors = ModelState.Values
|
||||
.SelectMany(v => v.Errors)
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToList();
|
||||
|
||||
_logger.LogWarning("AssignTask failed validation: {@Errors}", errors);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
|
||||
}
|
||||
var tenantId = GetTenantId();
|
||||
var Employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(Employee.Id, tenantId);
|
||||
// Retrieve tenant and employee context
|
||||
var tenantId = GetTenantId();
|
||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Check for permission to approve tasks
|
||||
var hasPermission = await _permissionServices.HasPermission(Assign_Report_Task, employee.Id);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Employee {EmployeeId} attempted to assign Task without permission", employee.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to approve tasks", 403));
|
||||
}
|
||||
|
||||
_logger.LogInfo("Employee {EmployeeId} is assigning a new task", employee.Id);
|
||||
|
||||
// Convert DTO to entity and save TaskAllocation
|
||||
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(employee.Id, tenantId);
|
||||
_context.TaskAllocations.Add(taskAllocation);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInfo("Task {TaskId} assigned by Employee {EmployeeId}", taskAllocation.Id, employee.Id);
|
||||
|
||||
var response = taskAllocation.ToAssignTaskVMFromTaskAllocation();
|
||||
|
||||
var teamMembers = new List<TaskMembers> { };
|
||||
if (assignTask.TaskTeam != null)
|
||||
// Map team members
|
||||
var teamMembers = new List<TaskMembers>();
|
||||
if (assignTask.TaskTeam != null && assignTask.TaskTeam.Any())
|
||||
{
|
||||
foreach (var teamMember in assignTask.TaskTeam)
|
||||
{
|
||||
var result = new TaskMembers
|
||||
teamMembers = assignTask.TaskTeam.Select(memberId => new TaskMembers
|
||||
{
|
||||
TaskAllocationId = taskAllocation.Id,
|
||||
EmployeeId = teamMember,
|
||||
TenantId = tenantId,
|
||||
};
|
||||
teamMembers.Add(result);
|
||||
}
|
||||
}
|
||||
EmployeeId = memberId,
|
||||
TenantId = tenantId
|
||||
}).ToList();
|
||||
|
||||
_context.TaskMembers.AddRange(teamMembers);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var idList = teamMembers.Select(m => m.EmployeeId);
|
||||
List<Employee> employees = await _context.Employees.Where(e => idList.Contains(e.Id)).ToListAsync();
|
||||
List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
|
||||
foreach (var employee in employees)
|
||||
{
|
||||
team.Add(employee.ToBasicEmployeeVMFromEmployee());
|
||||
_logger.LogInfo("Team members added to Task {TaskId}: {@TeamMemberIds}", taskAllocation.Id, assignTask.TaskTeam);
|
||||
}
|
||||
|
||||
// Get team member details
|
||||
var employeeIds = teamMembers.Select(m => m.EmployeeId).ToList();
|
||||
var employees = await _context.Employees
|
||||
.Where(e => employeeIds.Contains(e.Id))
|
||||
.ToListAsync();
|
||||
|
||||
var team = employees.Select(e => e.ToBasicEmployeeVMFromEmployee()).ToList();
|
||||
response.teamMembers = team;
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assignned successfully", 200));
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assigned successfully", 200));
|
||||
}
|
||||
|
||||
[HttpPost("report")]
|
||||
@ -98,363 +123,661 @@ namespace MarcoBMS.Services.Controllers
|
||||
.SelectMany(v => v.Errors)
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToList();
|
||||
|
||||
_logger.LogWarning("Task report validation failed: {@Errors}", errors);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||
|
||||
}
|
||||
|
||||
var tenantId = GetTenantId();
|
||||
var Employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var taskAllocation = await _context.TaskAllocations.Include(t => t.WorkItem).FirstOrDefaultAsync(t => t.Id == reportTask.Id);
|
||||
|
||||
var checkListIds = reportTask.CheckList != null ? reportTask.CheckList.Select(c => c.Id).ToList() : new List<Guid>();
|
||||
var checkList = await _context.ActivityCheckLists.Where(c => checkListIds.Contains(c.Id)).ToListAsync();
|
||||
if (taskAllocation == null || taskAllocation.WorkItem == null)
|
||||
var hasPermission = await _permissionServices.HasPermission(Assign_Report_Task, loggedInEmployee.Id);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Unauthorized task report attempt by Employee {EmployeeId} for Task {TaskId}", loggedInEmployee.Id, reportTask.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to report tasks", 403));
|
||||
}
|
||||
|
||||
var taskAllocation = await _context.TaskAllocations
|
||||
.Include(t => t.WorkItem)
|
||||
.FirstOrDefaultAsync(t => t.Id == reportTask.Id);
|
||||
|
||||
if (taskAllocation == null)
|
||||
{
|
||||
_logger.LogWarning("No task allocation found with ID {TaskId}", reportTask.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400));
|
||||
}
|
||||
WorkArea workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == taskAllocation.WorkItem.WorkAreaId) ?? new WorkArea();
|
||||
|
||||
var bulding = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty));
|
||||
var checkListIds = reportTask.CheckList?.Select(c => c.Id).ToList() ?? new List<Guid>();
|
||||
var checkList = await _context.ActivityCheckLists
|
||||
.Where(c => checkListIds.Contains(c.Id))
|
||||
.ToListAsync();
|
||||
|
||||
if (taskAllocation.WorkItem != null)
|
||||
{
|
||||
if (taskAllocation.CompletedTask != 0)
|
||||
{
|
||||
if (taskAllocation.CompletedTask > 0)
|
||||
taskAllocation.WorkItem.CompletedWork -= taskAllocation.CompletedTask;
|
||||
}
|
||||
taskAllocation.ReportedDate = reportTask.ReportedDate;
|
||||
taskAllocation.CompletedTask = reportTask.CompletedTask;
|
||||
|
||||
taskAllocation.WorkItem.CompletedWork += reportTask.CompletedTask;
|
||||
}
|
||||
List<CheckListMappings> checkListMappings = new List<CheckListMappings>();
|
||||
List<CheckListVM> checkListVMs = new List<CheckListVM>();
|
||||
|
||||
taskAllocation.ParentTaskId = reportTask.ParentTaskId;
|
||||
taskAllocation.ReportedDate = reportTask.ReportedDate;
|
||||
taskAllocation.ReportedById = loggedInEmployee.Id;
|
||||
taskAllocation.CompletedTask = reportTask.CompletedTask;
|
||||
taskAllocation.ReportedTask = reportTask.CompletedTask;
|
||||
|
||||
var checkListMappings = new List<CheckListMappings>();
|
||||
var checkListVMs = new List<CheckListVM>();
|
||||
|
||||
if (reportTask.CheckList != null)
|
||||
{
|
||||
var activityId = taskAllocation.WorkItem?.ActivityId ?? Guid.Empty;
|
||||
|
||||
foreach (var checkDto in reportTask.CheckList)
|
||||
{
|
||||
checkListVMs.Add(checkDto.ToCheckListVMFromReportCheckListDto(taskAllocation.WorkItem != null ? taskAllocation.WorkItem.ActivityId : Guid.Empty));
|
||||
if (checkDto.IsChecked)
|
||||
checkListVMs.Add(checkDto.ToCheckListVMFromReportCheckListDto(activityId));
|
||||
|
||||
if (checkDto.IsChecked && checkList.Any(c => c.Id == checkDto.Id))
|
||||
{
|
||||
var check = checkList.Find(c => c.Id == checkDto.Id);
|
||||
if (check != null)
|
||||
checkListMappings.Add(new CheckListMappings
|
||||
{
|
||||
CheckListMappings checkListMapping = new CheckListMappings
|
||||
{
|
||||
CheckListId = check.Id,
|
||||
CheckListId = checkDto.Id,
|
||||
TaskAllocationId = reportTask.Id
|
||||
};
|
||||
checkListMappings.Add(checkListMapping);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_context.CheckListMappings.AddRange(checkListMappings);
|
||||
var comment = reportTask.ToCommentFromReportTaskDto(tenantId, Employee.Id);
|
||||
}
|
||||
|
||||
var Images = reportTask.Images;
|
||||
var comment = reportTask.ToCommentFromReportTaskDto(tenantId, loggedInEmployee.Id);
|
||||
_context.TaskComments.Add(comment);
|
||||
|
||||
if (Images != null && Images.Count > 0)
|
||||
if (reportTask.Images?.Any() == true)
|
||||
{
|
||||
var workAreaId = taskAllocation.WorkItem?.WorkAreaId;
|
||||
var workArea = await _context.WorkAreas.Include(a => a.Floor)
|
||||
.FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea();
|
||||
|
||||
foreach (var Image in Images)
|
||||
var buildingId = workArea.Floor?.BuildingId;
|
||||
|
||||
var building = await _context.Buildings
|
||||
.FirstOrDefaultAsync(b => b.Id == buildingId);
|
||||
|
||||
foreach (var image in reportTask.Images)
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||
if (string.IsNullOrEmpty(image.Base64Data))
|
||||
{
|
||||
_logger.LogWarning("Image upload failed: Base64 data is missing");
|
||||
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;
|
||||
var base64 = image.Base64Data.Contains(',')
|
||||
? image.Base64Data[(image.Base64Data.IndexOf(",") + 1)..]
|
||||
: image.Base64Data;
|
||||
|
||||
string fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
|
||||
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Actitvity/{fileName}";
|
||||
|
||||
string objectKey = $"tenant-{tenantId}/project-{bulding?.ProjectId}/Actitvity/{fileName}";
|
||||
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||
|
||||
Document document = new Document
|
||||
var document = new Document
|
||||
{
|
||||
FileName = Image.FileName ?? "",
|
||||
ContentType = Image.ContentType ?? "",
|
||||
FileName = image.FileName ?? "",
|
||||
ContentType = image.ContentType ?? "",
|
||||
S3Key = objectKey,
|
||||
Base64Data = Image.Base64Data,
|
||||
FileSize = Image.FileSize,
|
||||
Base64Data = image.Base64Data,
|
||||
FileSize = image.FileSize,
|
||||
UploadedAt = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.Documents.Add(document);
|
||||
TaskAttachment attachment = new TaskAttachment
|
||||
|
||||
var attachment = new TaskAttachment
|
||||
{
|
||||
DocumentId = document.Id,
|
||||
ReferenceId = reportTask.Id
|
||||
};
|
||||
_context.TaskAttachments.Add(attachment);
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
_context.TaskComments.Add(comment);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var response = taskAllocation.ToReportTaskVMFromTaskAllocation();
|
||||
List<TaskComment> comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
|
||||
List<CommentVM> resultComments = new List<CommentVM> { };
|
||||
foreach (var result in comments)
|
||||
{
|
||||
resultComments.Add(result.ToCommentVMFromTaskComment());
|
||||
}
|
||||
response.Comments = resultComments;
|
||||
var comments = await _context.TaskComments
|
||||
.Where(c => c.TaskAllocationId == taskAllocation.Id)
|
||||
.ToListAsync();
|
||||
|
||||
response.Comments = comments.Select(c => c.ToCommentVMFromTaskComment()).ToList();
|
||||
response.checkList = checkListVMs;
|
||||
|
||||
_logger.LogInfo("Task {TaskId} reported successfully by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task reported successfully", 200));
|
||||
}
|
||||
|
||||
[HttpPost("comment")]
|
||||
public async Task<IActionResult> AddCommentForTask([FromBody] CreateCommentDto createComment)
|
||||
{
|
||||
var tenantId = GetTenantId();
|
||||
var Employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
_logger.LogInfo("AddCommentForTask called for TaskAllocationId: {TaskId}", createComment.TaskAllocationId);
|
||||
|
||||
var tenantId = GetTenantId();
|
||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Validate Task Allocation and associated WorkItem
|
||||
var taskAllocation = await _context.TaskAllocations
|
||||
.Include(t => t.WorkItem)
|
||||
.FirstOrDefaultAsync(t => t.Id == createComment.TaskAllocationId);
|
||||
|
||||
var taskAllocation = await _context.TaskAllocations.Include(t => t.WorkItem).FirstOrDefaultAsync(t => t.Id == createComment.TaskAllocationId);
|
||||
if (taskAllocation == null || taskAllocation.WorkItem == null)
|
||||
{
|
||||
_logger.LogWarning("Invalid task allocation or work item not found.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400));
|
||||
}
|
||||
WorkArea workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == taskAllocation.WorkItem.WorkAreaId) ?? new WorkArea();
|
||||
|
||||
var bulding = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty));
|
||||
// Fetch WorkArea and Building (if available)
|
||||
var workArea = await _context.WorkAreas
|
||||
.Include(a => a.Floor)
|
||||
.FirstOrDefaultAsync(a => a.Id == taskAllocation.WorkItem.WorkAreaId) ?? new WorkArea();
|
||||
|
||||
var comment = createComment.ToCommentFromCommentDto(tenantId, Employee.Id);
|
||||
var buildingId = workArea.Floor?.BuildingId ?? Guid.Empty;
|
||||
var building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == buildingId);
|
||||
|
||||
// Save comment
|
||||
var comment = createComment.ToCommentFromCommentDto(tenantId, employee.Id);
|
||||
_context.TaskComments.Add(comment);
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Comment saved with Id: {CommentId}", comment.Id);
|
||||
|
||||
var Images = createComment.Images;
|
||||
// Process image uploads
|
||||
var images = createComment.Images;
|
||||
|
||||
if (Images != null && Images.Count > 0)
|
||||
if (images != null && images.Any())
|
||||
{
|
||||
|
||||
foreach (var Image in Images)
|
||||
foreach (var image in images)
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||
if (string.IsNullOrWhiteSpace(image.Base64Data))
|
||||
{
|
||||
_logger.LogWarning("Missing Base64 data in one of the images.");
|
||||
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;
|
||||
// Clean base64 string
|
||||
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, "task_report");
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment");
|
||||
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Activity/{fileName}";
|
||||
|
||||
string objectKey = $"tenant-{tenantId}/project-{bulding?.ProjectId}/Actitvity/{fileName}";
|
||||
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||
_logger.LogInfo("Image uploaded to S3 with key: {ObjectKey}", objectKey);
|
||||
|
||||
Document document = new Document
|
||||
var document = new Document
|
||||
{
|
||||
FileName = Image.FileName ?? "",
|
||||
ContentType = Image.ContentType ?? "",
|
||||
FileName = image.FileName ?? string.Empty,
|
||||
ContentType = image.ContentType ?? fileType,
|
||||
S3Key = objectKey,
|
||||
Base64Data = Image.Base64Data,
|
||||
FileSize = Image.FileSize,
|
||||
Base64Data = image.Base64Data,
|
||||
FileSize = image.FileSize,
|
||||
UploadedAt = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
};
|
||||
|
||||
_context.Documents.Add(document);
|
||||
TaskAttachment attachment = new TaskAttachment
|
||||
|
||||
var attachment = new TaskAttachment
|
||||
{
|
||||
DocumentId = document.Id,
|
||||
ReferenceId = comment.Id
|
||||
};
|
||||
|
||||
_context.TaskAttachments.Add(attachment);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInfo("Documents and attachments saved for commentId: {CommentId}", comment.Id);
|
||||
}
|
||||
|
||||
CommentVM response = comment.ToCommentVMFromTaskComment();
|
||||
// Convert to view model and return response
|
||||
var response = comment.ToCommentVMFromTaskComment();
|
||||
_logger.LogInfo("Returning response for commentId: {CommentId}", comment.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> GetTasksList([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
|
||||
{
|
||||
_logger.LogInfo("GetTasksList called for projectId: {ProjectId}, dateFrom: {DateFrom}, dateTo: {DateTo}", projectId, dateFrom ?? "", dateTo ?? "");
|
||||
|
||||
Guid tenantId = GetTenantId();
|
||||
DateTime fromDate = new DateTime();
|
||||
DateTime toDate = new DateTime();
|
||||
|
||||
if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
|
||||
// Parse and validate dateFrom
|
||||
if (dateFrom != null && !DateTime.TryParse(dateFrom, out fromDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid starting date provided: {DateFrom}", dateFrom);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid starting date.", "Invalid starting date.", 400));
|
||||
|
||||
}
|
||||
if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
|
||||
|
||||
// Parse and validate dateTo
|
||||
if (dateTo != null && !DateTime.TryParse(dateTo, out toDate))
|
||||
{
|
||||
_logger.LogWarning("Invalid ending date provided: {DateTo}", dateTo);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid ending date.", "Invalid ending date.", 400));
|
||||
|
||||
}
|
||||
if (dateFrom == null) fromDate = DateTime.UtcNow.Date;
|
||||
if (dateTo == null) toDate = fromDate.AddDays(1);
|
||||
|
||||
var jobroles = await _context.JobRoles.Where(r => r.TenantId == tenantId).ToListAsync();
|
||||
//var taskAllocations = await _context.TaskAllocations.Where(t => t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId && t.AssignmentDate >= fromDate && t.AssignmentDate <= toDate && t.TenantId == tenantId).Include(t => t.WorkItemId).ToListAsync();
|
||||
List<Building> buildings = await _context.Buildings.Where(b => b.ProjectId == projectId && b.TenantId == tenantId).ToListAsync();
|
||||
List<Guid> idList = buildings.Select(b => b.Id).ToList();
|
||||
// Set default date range if not provided
|
||||
fromDate = dateFrom == null ? DateTime.UtcNow.Date : fromDate;
|
||||
toDate = dateTo == null ? fromDate.AddDays(1) : toDate;
|
||||
|
||||
List<Floor> floors = await _context.Floor.Where(f => idList.Contains(f.BuildingId) && f.TenantId == tenantId).ToListAsync();
|
||||
idList = floors.Select(f => f.Id).ToList();
|
||||
// 1. Get all buildings under this project
|
||||
_logger.LogInfo("Fetching buildings for projectId: {ProjectId}", projectId);
|
||||
var buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId && b.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
List<WorkArea> workAreas = await _context.WorkAreas.Where(a => idList.Contains(a.FloorId) && a.TenantId == tenantId).ToListAsync();
|
||||
idList = workAreas.Select(a => a.Id).ToList();
|
||||
var buildingIds = buildings.Select(b => b.Id).ToList();
|
||||
|
||||
List<WorkItem> workItems = await _context.WorkItems.Where(i => idList.Contains(i.WorkAreaId) && i.TenantId == tenantId).Include(i => i.ActivityMaster).ToListAsync();
|
||||
idList = workItems.Select(i => i.Id).ToList();
|
||||
var activityIdList = workItems.Select(i => i.ActivityId).ToList();
|
||||
// 2. Get floors under the buildings
|
||||
var floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId) && f.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var floorIds = floors.Select(f => f.Id).ToList();
|
||||
|
||||
List<TaskAllocation> taskAllocations = await _context.TaskAllocations.Where(t => idList.Contains(t.WorkItemId) && t.AssignmentDate.Date >= fromDate.Date && t.AssignmentDate.Date <= toDate.Date && t.TenantId == tenantId).Include(t => t.WorkItem).Include(t => t.Employee).ToListAsync();
|
||||
var taskIdList = taskAllocations.Select(t => t.Id).ToList();
|
||||
// 3. Get work areas under the floors
|
||||
var workAreas = await _context.WorkAreas
|
||||
.Where(a => floorIds.Contains(a.FloorId) && a.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
var workAreaIds = workAreas.Select(a => a.Id).ToList();
|
||||
|
||||
List<TaskMembers> teamMembers = await _context.TaskMembers.Where(t => taskIdList.Contains(t.TaskAllocationId)).ToListAsync();
|
||||
var employeeIdList = teamMembers.Select(e => e.EmployeeId).ToList();
|
||||
// 4. Get work items under the work areas
|
||||
var workItems = await _context.WorkItems
|
||||
.Where(i => workAreaIds.Contains(i.WorkAreaId) && i.TenantId == tenantId)
|
||||
.Include(i => i.ActivityMaster)
|
||||
.ToListAsync();
|
||||
var workItemIds = workItems.Select(i => i.Id).ToList();
|
||||
|
||||
List<Employee> employees = await _context.Employees.Where(e => employeeIdList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
|
||||
_logger.LogInfo("Fetching task allocations between {FromDate} and {ToDate}", fromDate, toDate);
|
||||
|
||||
List<TaskComment> allComments = await _context.TaskComments.Include(c => c.Employee).Where(c => taskIdList.Contains(c.TaskAllocationId)).ToListAsync();
|
||||
var allCommentIds = allComments.Select(c => c.Id).ToList();
|
||||
// 5. Get task allocations in the specified date range
|
||||
var taskAllocations = await _context.TaskAllocations
|
||||
.Include(t => t.Employee)
|
||||
.Include(t => t.ReportedBy)
|
||||
.Include(t => t.ApprovedBy)
|
||||
.Include(t => t.WorkStatus)
|
||||
.Include(t => t.WorkItem)
|
||||
.Where(t => workItemIds.Contains(t.WorkItemId) &&
|
||||
t.AssignmentDate.Date >= fromDate.Date &&
|
||||
t.AssignmentDate.Date <= toDate.Date &&
|
||||
t.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var taskIds = taskAllocations.Select(t => t.Id).ToList();
|
||||
|
||||
// 6. Load team members
|
||||
_logger.LogInfo("Loading task members and related employee data.");
|
||||
var teamMembers = await _context.TaskMembers
|
||||
.Include(t => t.Employee)
|
||||
.Where(t => taskIds.Contains(t.TaskAllocationId))
|
||||
.ToListAsync();
|
||||
|
||||
// 7. Load task comments
|
||||
_logger.LogInfo("Fetching comments and attachments.");
|
||||
var allComments = await _context.TaskComments
|
||||
.Include(c => c.Employee)
|
||||
.Where(c => taskIds.Contains(c.TaskAllocationId))
|
||||
.ToListAsync();
|
||||
var commentIds = allComments.Select(c => c.Id).ToList();
|
||||
|
||||
// 8. Load all attachments (task and comment)
|
||||
var attachments = await _context.TaskAttachments
|
||||
.Where(t => taskIds.Contains(t.ReferenceId) || commentIds.Contains(t.ReferenceId))
|
||||
.ToListAsync();
|
||||
|
||||
var documentIds = attachments.Select(t => t.DocumentId).ToList();
|
||||
|
||||
// 9. Load actual documents from attachment references
|
||||
var documents = await _context.Documents
|
||||
.Where(d => documentIds.Contains(d.Id))
|
||||
.ToListAsync();
|
||||
|
||||
var tasks = new List<ListTaskVM>();
|
||||
|
||||
_logger.LogInfo("Constructing task response data.");
|
||||
|
||||
var taskAttachments = await _context.TaskAttachments.Where(t => taskIdList.Contains(t.ReferenceId) || allCommentIds.Contains(t.ReferenceId)).ToListAsync();
|
||||
var documentIds = taskAttachments.Select(t => t.DocumentId).ToList();
|
||||
var documents = await _context.Documents.Where(d => documentIds.Contains(d.Id)).ToListAsync();
|
||||
|
||||
List<ListTaskVM> tasks = new List<ListTaskVM>();
|
||||
//foreach (var workItem in workItems)
|
||||
//{
|
||||
foreach (var taskAllocation in taskAllocations)
|
||||
{
|
||||
|
||||
var response = taskAllocation.ToListTaskVMFromTaskAllocation();
|
||||
|
||||
List<TaskComment> comments = allComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToList();
|
||||
List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
|
||||
List<TaskMembers> taskMembers = teamMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).ToList();
|
||||
// Attach documents to the main task
|
||||
var taskDocIds = attachments
|
||||
.Where(a => a.ReferenceId == taskAllocation.Id)
|
||||
.Select(a => a.DocumentId)
|
||||
.ToList();
|
||||
|
||||
var taskDocumentIds = taskAttachments.Where(t => t.ReferenceId == taskAllocation.Id).Select(t => t.DocumentId).ToList();
|
||||
var taskDocuments = documents.Where(d => taskDocumentIds.Contains(d.Id)).ToList();
|
||||
var taskDocs = documents
|
||||
.Where(d => taskDocIds.Contains(d.Id))
|
||||
.ToList();
|
||||
|
||||
List<string> taskPreSignedUrls = new List<string>();
|
||||
foreach (var document in taskDocuments)
|
||||
{
|
||||
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||
taskPreSignedUrls.Add(preSignedUrl);
|
||||
}
|
||||
response.ReportedPreSignedUrls = taskPreSignedUrls;
|
||||
response.ReportedPreSignedUrls = taskDocs
|
||||
.Select(d => _s3Service.GeneratePreSignedUrlAsync(d.S3Key))
|
||||
.ToList();
|
||||
|
||||
foreach (var taskMember in taskMembers)
|
||||
{
|
||||
var teamMember = employees.Find(e => e.Id == taskMember.EmployeeId);
|
||||
if (teamMember != null)
|
||||
{
|
||||
team.Add(teamMember.ToBasicEmployeeVMFromEmployee());
|
||||
}
|
||||
}
|
||||
List<CommentVM> commentVM = new List<CommentVM> { };
|
||||
foreach (var comment in comments)
|
||||
{
|
||||
var commentDocumentIds = taskAttachments.Where(t => t.ReferenceId == comment.Id).Select(t => t.DocumentId).ToList();
|
||||
var commentDocuments = documents.Where(d => commentDocumentIds.Contains(d.Id)).ToList();
|
||||
List<string> commentPreSignedUrls = new List<string>();
|
||||
foreach (var document in commentDocuments)
|
||||
{
|
||||
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||
commentPreSignedUrls.Add(preSignedUrl);
|
||||
}
|
||||
CommentVM commentVm = comment.ToCommentVMFromTaskComment();
|
||||
commentVm.PreSignedUrls = commentPreSignedUrls;
|
||||
// Add team members
|
||||
var taskMemberEntries = teamMembers
|
||||
.Where(m => m.TaskAllocationId == taskAllocation.Id)
|
||||
.ToList();
|
||||
|
||||
commentVM.Add(commentVm);
|
||||
}
|
||||
List<ActivityCheckList> checkLists = await _context.ActivityCheckLists.Where(x => x.ActivityId == (taskAllocation.WorkItem != null ? taskAllocation.WorkItem.ActivityId : Guid.Empty)).ToListAsync();
|
||||
List<CheckListMappings> checkListMappings = await _context.CheckListMappings.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
|
||||
List<CheckListVM> checkList = new List<CheckListVM>();
|
||||
foreach (var check in checkLists)
|
||||
response.teamMembers = taskMemberEntries
|
||||
.Select(m => m.Employee)
|
||||
.Where(e => e != null)
|
||||
.Select(e => e!.ToBasicEmployeeVMFromEmployee())
|
||||
.ToList();
|
||||
|
||||
// Add comments with attachments
|
||||
var commentVMs = new List<CommentVM>();
|
||||
var taskComments = allComments
|
||||
.Where(c => c.TaskAllocationId == taskAllocation.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var comment in taskComments)
|
||||
{
|
||||
var checkListMapping = checkListMappings.Find(c => c.CheckListId == check.Id);
|
||||
if (checkListMapping != null)
|
||||
var commentDocIds = attachments
|
||||
.Where(a => a.ReferenceId == comment.Id)
|
||||
.Select(a => a.DocumentId)
|
||||
.ToList();
|
||||
|
||||
var commentDocs = documents
|
||||
.Where(d => commentDocIds.Contains(d.Id))
|
||||
.ToList();
|
||||
|
||||
var commentVm = comment.ToCommentVMFromTaskComment();
|
||||
commentVm.PreSignedUrls = commentDocs
|
||||
.Select(d => _s3Service.GeneratePreSignedUrlAsync(d.S3Key))
|
||||
.ToList();
|
||||
|
||||
commentVMs.Add(commentVm);
|
||||
}
|
||||
|
||||
response.comments = commentVMs;
|
||||
|
||||
// Checklists
|
||||
var activityId = taskAllocation.WorkItem?.ActivityId ?? Guid.Empty;
|
||||
|
||||
var checkLists = await _context.ActivityCheckLists
|
||||
.Where(c => c.ActivityId == activityId)
|
||||
.ToListAsync();
|
||||
|
||||
var checkListMappings = await _context.CheckListMappings
|
||||
.Where(c => c.TaskAllocationId == taskAllocation.Id)
|
||||
.ToListAsync();
|
||||
|
||||
response.CheckList = checkLists.Select(check =>
|
||||
{
|
||||
checkList.Add(check.ToCheckListVMFromActivityCheckList(check.ActivityId, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
checkList.Add(check.ToCheckListVMFromActivityCheckList(check.ActivityId, false));
|
||||
}
|
||||
}
|
||||
response.comments = commentVM;
|
||||
response.teamMembers = team;
|
||||
response.CheckList = checkList;
|
||||
var isChecked = checkListMappings.Any(m => m.CheckListId == check.Id);
|
||||
return check.ToCheckListVMFromActivityCheckList(check.ActivityId, isChecked);
|
||||
}).ToList();
|
||||
|
||||
tasks.Add(response);
|
||||
}
|
||||
|
||||
//}
|
||||
_logger.LogInfo("Task list constructed successfully. Returning {Count} tasks.", tasks.Count);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(tasks, "Success", 200));
|
||||
}
|
||||
|
||||
[HttpGet("get/{taskId}")]
|
||||
public async Task<IActionResult> GetTask(Guid taskId)
|
||||
{
|
||||
if (taskId == Guid.Empty) return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invalid data", 400));
|
||||
_logger.LogInfo("GetTask called with taskId: {TaskId}", taskId);
|
||||
|
||||
var taskAllocation = await _context.TaskAllocations.Include(t => t.Tenant).Include(t => t.Employee).Include(t => t.WorkItem).FirstOrDefaultAsync(t => t.Id == taskId);
|
||||
if (taskAllocation != null && taskAllocation.Employee != null && taskAllocation.Tenant != null)
|
||||
// Validate input
|
||||
if (taskId == Guid.Empty)
|
||||
{
|
||||
//var employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == taskAllocation.AssignedBy);
|
||||
string employeeName = System.String.Format("{0} {1}", taskAllocation.Employee.FirstName, taskAllocation.Employee.LastName);
|
||||
string tenantName = taskAllocation.Tenant.ContactName ?? string.Empty;
|
||||
_logger.LogWarning("Invalid taskId provided.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invalid data", 400));
|
||||
}
|
||||
|
||||
// Fetch Task Allocation with required related data
|
||||
var taskAllocation = await _context.TaskAllocations
|
||||
.Include(t => t.Tenant)
|
||||
.Include(t => t.Employee)
|
||||
.Include(t => t.ReportedBy)
|
||||
.Include(t => t.ApprovedBy)
|
||||
.Include(t => t.WorkItem)
|
||||
.Include(t => t.WorkStatus)
|
||||
.FirstOrDefaultAsync(t => t.Id == taskId);
|
||||
|
||||
if (taskAllocation == null)
|
||||
{
|
||||
_logger.LogWarning("Task not found for taskId: {TaskId}", taskId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
|
||||
}
|
||||
|
||||
if (taskAllocation.Employee == null || taskAllocation.Tenant == null)
|
||||
{
|
||||
_logger.LogWarning("Task found but missing Employee or Tenant data for taskId: {TaskId}", taskId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
|
||||
}
|
||||
|
||||
_logger.LogInfo("Task allocation found. Preparing response.");
|
||||
|
||||
var taskVM = taskAllocation.TaskAllocationToTaskVM();
|
||||
|
||||
// Fetch comments and attachments
|
||||
_logger.LogInfo("Fetching comments and attachments for taskId: {TaskId}", taskId);
|
||||
|
||||
var comments = await _context.TaskComments
|
||||
.Where(c => c.TaskAllocationId == taskId)
|
||||
.Include(c => c.Employee)
|
||||
.ToListAsync();
|
||||
|
||||
if (taskAllocation == null) return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
|
||||
var taskVM = taskAllocation.TaskAllocationToTaskVM(employeeName);
|
||||
var comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
|
||||
var commentIds = comments.Select(c => c.Id).ToList();
|
||||
var taskAttachments = await _context.TaskAttachments.Where(t => t.ReferenceId == taskAllocation.Id || commentIds.Contains(t.ReferenceId)).ToListAsync();
|
||||
var documentIds = taskAttachments.Select(t => t.DocumentId).ToList();
|
||||
var documents = await _context.Documents.Where(d => documentIds.Contains(d.Id)).ToListAsync();
|
||||
var team = await _context.TaskMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).Include(m => m.Employee).ToListAsync();
|
||||
var teamMembers = new List<EmployeeVM> { };
|
||||
|
||||
var taskDocumentIds = taskAttachments.Where(t => t.ReferenceId == taskAllocation.Id).Select(t => t.DocumentId).ToList();
|
||||
var taskDocuments = documents.Where(d => taskDocumentIds.Contains(d.Id)).ToList();
|
||||
var taskAttachments = await _context.TaskAttachments
|
||||
.Where(t => t.ReferenceId == taskId || commentIds.Contains(t.ReferenceId))
|
||||
.ToListAsync();
|
||||
|
||||
var documentIds = taskAttachments.Select(t => t.DocumentId).Distinct().ToList();
|
||||
|
||||
var documents = await _context.Documents
|
||||
.Where(d => documentIds.Contains(d.Id))
|
||||
.ToListAsync();
|
||||
|
||||
// Fetch team members
|
||||
_logger.LogInfo("Fetching team members for taskId: {TaskId}", taskId);
|
||||
var team = await _context.TaskMembers
|
||||
.Where(m => m.TaskAllocationId == taskId)
|
||||
.Include(m => m.Employee)
|
||||
.ToListAsync();
|
||||
|
||||
var teamMembers = team
|
||||
.Where(m => m.Employee != null)
|
||||
.Select(m => m.Employee!.ToBasicEmployeeVMFromEmployee())
|
||||
.ToList();
|
||||
|
||||
List<string> taskPreSignedUrls = new List<string>();
|
||||
foreach (var document in taskDocuments)
|
||||
{
|
||||
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||
taskPreSignedUrls.Add(preSignedUrl);
|
||||
}
|
||||
taskVM.PreSignedUrls = taskPreSignedUrls;
|
||||
foreach (var member in team)
|
||||
{
|
||||
var result = member.Employee != null ? member.Employee.ToEmployeeVMFromEmployee() : new EmployeeVM();
|
||||
teamMembers.Add(result);
|
||||
}
|
||||
List<CommentVM> Comments = new List<CommentVM> { };
|
||||
foreach (var comment in comments)
|
||||
{
|
||||
var commentDocumentIds = taskAttachments.Where(t => t.ReferenceId == comment.Id).Select(t => t.DocumentId).ToList();
|
||||
var commentDocuments = documents.Where(d => commentDocumentIds.Contains(d.Id)).ToList();
|
||||
List<string> commentPreSignedUrls = new List<string>();
|
||||
foreach (var document in commentDocuments)
|
||||
{
|
||||
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||
commentPreSignedUrls.Add(preSignedUrl);
|
||||
}
|
||||
CommentVM commentVM = comment.ToCommentVMFromTaskComment();
|
||||
commentVM.PreSignedUrls = commentPreSignedUrls;
|
||||
Comments.Add(commentVM);
|
||||
}
|
||||
taskVM.Comments = Comments;
|
||||
taskVM.TeamMembers = teamMembers;
|
||||
|
||||
// Attach documents to the main task
|
||||
_logger.LogInfo("Generating presigned URLs for task documents.");
|
||||
var taskDocumentIds = taskAttachments
|
||||
.Where(t => t.ReferenceId == taskId)
|
||||
.Select(t => t.DocumentId)
|
||||
.ToList();
|
||||
|
||||
var taskDocuments = documents
|
||||
.Where(d => taskDocumentIds.Contains(d.Id))
|
||||
.ToList();
|
||||
|
||||
taskVM.PreSignedUrls = taskDocuments
|
||||
.Select(d => _s3Service.GeneratePreSignedUrlAsync(d.S3Key))
|
||||
.ToList();
|
||||
|
||||
// Construct CommentVM list with document URLs
|
||||
_logger.LogInfo("Preparing comment response data.");
|
||||
var commentVMs = comments.Select(comment =>
|
||||
{
|
||||
var commentDocIds = taskAttachments
|
||||
.Where(t => t.ReferenceId == comment.Id)
|
||||
.Select(t => t.DocumentId)
|
||||
.ToList();
|
||||
|
||||
var commentDocs = documents
|
||||
.Where(d => commentDocIds.Contains(d.Id))
|
||||
.ToList();
|
||||
|
||||
var commentVM = comment.ToCommentVMFromTaskComment();
|
||||
commentVM.PreSignedUrls = commentDocs
|
||||
.Select(d => _s3Service.GeneratePreSignedUrlAsync(d.S3Key))
|
||||
.ToList();
|
||||
|
||||
return commentVM;
|
||||
}).ToList();
|
||||
|
||||
taskVM.Comments = commentVMs;
|
||||
|
||||
_logger.LogInfo("Task details prepared successfully for taskId: {TaskId}", taskId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(taskVM, "Success", 200));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves a reported task after validation, updates status, and stores attachments/comments.
|
||||
/// </summary>
|
||||
/// <param name="approveTask">DTO containing task approval details.</param>
|
||||
/// <returns>IActionResult indicating success or failure.</returns>
|
||||
[HttpPost("approve")]
|
||||
public async Task<IActionResult> ApproveTask(ApproveTaskDto approveTask)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not Found", 404));
|
||||
_logger.LogInfo("Employee {EmployeeId} is attempting to approve Task {TaskId}", loggedInEmployee.Id, approveTask.Id);
|
||||
|
||||
// Fetch task allocation with work item, only if it's reported
|
||||
var taskAllocation = await _context.TaskAllocations
|
||||
.Include(t => t.WorkItem)
|
||||
.FirstOrDefaultAsync(t => t.Id == approveTask.Id && t.TenantId == tenantId && t.ReportedDate != null);
|
||||
|
||||
if (taskAllocation == null)
|
||||
{
|
||||
_logger.LogWarning("Task {TaskId} not found or not reported yet for Tenant {TenantId} by Employee {EmployeeId}",
|
||||
approveTask.Id, tenantId, loggedInEmployee.Id);
|
||||
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Task not found", "Task not found", 404));
|
||||
}
|
||||
|
||||
// Check for permission to approve tasks
|
||||
var hasPermission = await _permissionServices.HasPermission(Approve_Task, loggedInEmployee.Id);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Employee {EmployeeId} attempted to approve Task {TaskId} without permission", loggedInEmployee.Id, approveTask.Id);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to approve tasks", 403));
|
||||
}
|
||||
|
||||
// Validation: Approved task count cannot exceed completed task count
|
||||
if (taskAllocation.CompletedTask < approveTask.ApprovedTask)
|
||||
{
|
||||
_logger.LogWarning("Invalid approval attempt on Task {TaskId}: Approved tasks ({ApprovedTask}) > Completed tasks ({CompletedTask})",
|
||||
approveTask.Id, approveTask.ApprovedTask, taskAllocation.CompletedTask);
|
||||
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Approved tasks cannot be greater than completed tasks",
|
||||
"Approved tasks cannot be greater than completed tasks", 400));
|
||||
}
|
||||
|
||||
// Update completed work in the associated work item, if it exists
|
||||
if (taskAllocation.WorkItem != null && taskAllocation.CompletedTask != approveTask.ApprovedTask)
|
||||
{
|
||||
if (taskAllocation.CompletedTask > 0)
|
||||
{
|
||||
taskAllocation.WorkItem.CompletedWork -= taskAllocation.CompletedTask;
|
||||
}
|
||||
taskAllocation.WorkItem.CompletedWork += approveTask.ApprovedTask;
|
||||
}
|
||||
|
||||
// Update task allocation details
|
||||
taskAllocation.ApprovedById = loggedInEmployee.Id;
|
||||
taskAllocation.ApprovedDate = DateTime.UtcNow;
|
||||
taskAllocation.WorkStatusId = approveTask.WorkStatus;
|
||||
taskAllocation.CompletedTask = approveTask.ApprovedTask;
|
||||
|
||||
// Add a comment (optional)
|
||||
var comment = new TaskComment
|
||||
{
|
||||
TaskAllocationId = taskAllocation.Id,
|
||||
CommentDate = DateTime.UtcNow,
|
||||
Comment = approveTask.Comment ?? string.Empty,
|
||||
CommentedBy = loggedInEmployee.Id,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.TaskComments.Add(comment);
|
||||
|
||||
// Handle image attachments, if any
|
||||
if (approveTask.Images?.Count > 0)
|
||||
{
|
||||
var workAreaId = taskAllocation.WorkItem?.WorkAreaId;
|
||||
var workArea = await _context.WorkAreas.Include(a => a.Floor)
|
||||
.FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea();
|
||||
|
||||
var buildingId = workArea.Floor?.BuildingId;
|
||||
|
||||
var building = await _context.Buildings
|
||||
.FirstOrDefaultAsync(b => b.Id == buildingId);
|
||||
|
||||
foreach (var image in approveTask.Images)
|
||||
{
|
||||
if (string.IsNullOrEmpty(image.Base64Data))
|
||||
{
|
||||
_logger.LogWarning("Image for Task {TaskId} is missing base64 data", approveTask.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||
}
|
||||
|
||||
var base64 = image.Base64Data.Contains(",") ? image.Base64Data[(image.Base64Data.IndexOf(",") + 1)..] : image.Base64Data;
|
||||
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment");
|
||||
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Activity/{fileName}";
|
||||
|
||||
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||
|
||||
var document = new Document
|
||||
{
|
||||
FileName = fileName,
|
||||
ContentType = image.ContentType ?? string.Empty,
|
||||
S3Key = objectKey,
|
||||
Base64Data = image.Base64Data,
|
||||
FileSize = image.FileSize,
|
||||
UploadedAt = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
};
|
||||
|
||||
_context.Documents.Add(document);
|
||||
|
||||
var attachment = new TaskAttachment
|
||||
{
|
||||
DocumentId = document.Id,
|
||||
ReferenceId = comment.Id
|
||||
};
|
||||
|
||||
_context.TaskAttachments.Add(attachment);
|
||||
|
||||
_logger.LogInfo("Attachment uploaded for Task {TaskId}: {FileName}", approveTask.Id, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// Commit all changes to the database
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInfo("Task {TaskId} successfully approved by Employee {EmployeeId}", approveTask.Id, loggedInEmployee.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user