From e661bc2dcbdebd17a7324cbcf99fe6dea15323fe Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 23 Dec 2025 14:52:41 +0330 Subject: [PATCH 1/6] add: enhance ProjectBoardListQueryHandler and ProjectPhase to manage task section statuses and deployment states --- .../ProjectBoardListQueryHandler.cs | 2 +- .../ProjectAgg/Entities/ProjectPhase.cs | 25 +++++++++++++++++++ .../ProjectAgg/Enums/TaskSectionStatus.cs | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs index 5a085060..18a52853 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs @@ -23,7 +23,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.InitialEstimatedHours > TimeSpan.Zero) + .Where(x => x.InitialEstimatedHours > TimeSpan.Zero && x.Status != TaskSectionStatus.Completed) .Include(x => x.Task) .ThenInclude(x => x.Phase) .ThenInclude(x => x.Project) diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectPhase.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectPhase.cs index 539ac278..8d7583bc 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectPhase.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectPhase.cs @@ -23,6 +23,7 @@ public class ProjectPhase : ProjectHierarchyNode ProjectId = projectId; _tasks = new List(); _phaseSections = new List(); + DeployStatus = ProjectDeployStatus.NoTCompleted; AddDomainEvent(new PhaseCreatedEvent(Id, projectId, name)); } @@ -36,6 +37,8 @@ public class ProjectPhase : ProjectHierarchyNode public DateTime? StartDate { get; private set; } public DateTime? EndDate { get; private set; } public int OrderIndex { get; private set; } + public bool IsArchived { get; set; } + public ProjectDeployStatus DeployStatus { get; set; } #region Task Management @@ -196,4 +199,26 @@ public class ProjectPhase : ProjectHierarchyNode } #endregion + + public void SetArchived() + { + IsArchived = true; + } + public void SetUnarchived() + { + IsArchived = false; + } + public void UpdateDeployStatus(ProjectDeployStatus status) + { + DeployStatus = status; + } +} + +public enum ProjectDeployStatus +{ + NoTCompleted, + PendingDevDeploy, + DevDeployed, + PendingDeploy, + Deployed } diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs index d5208bfb..9e63c1f6 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs @@ -6,5 +6,6 @@ public enum TaskSectionStatus ReadyToStart = 1, // آماده شروع InProgress = 2, // درحال انجام Incomplete = 3, // ناتمام شده - Completed = 4 // تکمیل شده + PendingForCompletion = 5, // در انتظار تکمیل + Completed = 5 // تکمیل شده } \ No newline at end of file From b9e271de1ac5e46c552d2c8ae69f50e0a4da02c7 Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 23 Dec 2025 15:00:18 +0330 Subject: [PATCH 2/6] fix: update OccurredOn property to use DateTime.Now instead of DateTime.UtcNow in event records --- .../ProjectAgg/Events/ProjectEvents.cs | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Events/ProjectEvents.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Events/ProjectEvents.cs index fb6ca424..155fec66 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Events/ProjectEvents.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Events/ProjectEvents.cs @@ -8,171 +8,171 @@ namespace GozareshgirProgramManager.Domain.ProjectAgg.Events; // Project Events public record ProjectCreatedEvent(Guid ProjectId, string Name) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectStatusUpdatedEvent(Guid ProjectId, ProjectStatus Status) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectAssignedEvent(Guid ProjectId, long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectUnassignedEvent(Guid ProjectId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } // Phase Events public record PhaseCreatedEvent(Guid PhaseId, Guid ProjectId, string Name) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record PhaseAddedEvent(Guid PhaseId, Guid ProjectId, string Name) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record PhaseRemovedEvent(Guid PhaseId, Guid ProjectId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record PhaseStatusUpdatedEvent(Guid PhaseId, PhaseStatus Status) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record PhaseAssignedEvent(Guid PhaseId, long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record PhaseUnassignedEvent(Guid PhaseId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } // Task Events public record TaskCreatedEvent(Guid TaskId, Guid PhaseId, string Name) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskAddedEvent(Guid TaskId, Guid PhaseId, string Name) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskRemovedEvent(Guid TaskId, Guid PhaseId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskStatusUpdatedEvent(Guid TaskId, TaskStatus Status) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskPriorityUpdatedEvent(Guid TaskId, TaskPriority Priority) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskAssignedEvent(Guid TaskId, long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskUnassignedEvent(Guid TaskId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskSectionAddedEvent(Guid TaskId, Guid SectionId, Guid SkillId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskSectionRemovedEvent(Guid TaskId, Guid SectionId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } // TaskSection Events public record TaskSectionStatusChangedEvent(Guid SectionId, TaskSectionStatus OldStatus, TaskSectionStatus NewStatus,long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskSectionAssignedEvent(Guid SectionId, long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record TaskSectionTransferredEvent(Guid SectionId, long FromUserId, long ToUserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } // Section Events (Legacy - keeping for backward compatibility) public record ProjectPhaseAddedEvent(Guid ProjectId, Guid PhaseId, string PhaseName) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectTaskStatusChangedEvent(Guid SectionId, TaskStatus OldStatus, TaskStatus NewStatus) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectSectionAddedEvent(Guid SectionId, Guid TaskId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectSectionAssignedEvent(Guid SectionId, long UserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectSectionTransferredEvent(Guid SectionId, long FromUserId, long ToUserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record WorkStartedEvent(Guid SectionId, long UserId, DateTime StartTime, string? Notes) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record WorkStoppedEvent(Guid SectionId, long UserId, DateTime StartTime, DateTime EndTime, TimeSpan Duration, string? Notes) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record ProjectSectionCompletedEvent(Guid ProjectId, long UserId, TimeSpan TotalTimeSpent, string? Notes) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record AdditionalTimeAddedEvent(Guid ProjectId, Guid AdditionalTimeId, TimeSpan Hours, string? Reason, long? AddedByUserId) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record AdditionalTimeRemovedEvent(Guid ProjectId, Guid AdditionalTimeId, TimeSpan RemovedHours) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } public record InitialEstimatedTimeUpdatedEvent(Guid ProjectId, TimeSpan OldEstimate, TimeSpan NewEstimate) : IDomainEvent { - public DateTime OccurredOn { get; init; } = DateTime.UtcNow; + public DateTime OccurredOn { get; init; } = DateTime.Now; } From 39a5918a1150611433abd9806ccd2f34f1a67fcb Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 24 Dec 2025 18:55:01 +0330 Subject: [PATCH 3/6] add: implement ProjectDeployBoardListQueryHandler and initialize GetProjectsListQueryHandler structure --- .../ProjectBoardList/ProjectBoardListQueryHandler.cs | 1 + .../ProjectDeployBoardListQueryHandler.cs | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs index 18a52853..69e82940 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs @@ -22,6 +22,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.InitialEstimatedHours > TimeSpan.Zero && x.Status != TaskSectionStatus.Completed) .Include(x => x.Task) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs new file mode 100644 index 00000000..e040c52c --- /dev/null +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs @@ -0,0 +1,10 @@ +namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectDeployBoardList; + +public record GetProjectsListQueryHandler : IBaseQueryHandler; + +public record GetProjectsListResponse(); + +public class ProjectDeployBoardListQueryHandler +{ + +} \ No newline at end of file From 4c638cbdae3d7973c5cbc0194c7c384c3af5e25b Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 27 Dec 2025 13:59:37 +0330 Subject: [PATCH 4/6] add: implement DeployStatus and IsArchived properties in ProjectPhase, and create ProjectDeployBoardListQueryHandler for project deployment management --- .../ProjectDeployBoardListQueryHandler.cs | 67 +- .../ProjectAgg/Enums/TaskSectionStatus.cs | 2 +- ... deploy status and is archived.Designer.cs | 865 ++++++++++++++++++ ...add phase deploy status and is archived.cs | 41 + .../Migrations/AppDbContextModelSnapshot.cs | 12 +- .../Mappings/ProjectPhaseMapping.cs | 3 + .../ProgramManager/ProjectController.cs | 8 + 7 files changed, 990 insertions(+), 8 deletions(-) create mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.Designer.cs create mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.cs diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs index e040c52c..742e3455 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs @@ -1,10 +1,67 @@ +using System.Xml.Schema; +using GozareshgirProgramManager.Application._Common.Interfaces; +using GozareshgirProgramManager.Application._Common.Models; +using GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList; +using GozareshgirProgramManager.Domain.ProjectAgg.Entities; +using GozareshgirProgramManager.Domain.ProjectAgg.Enums; +using Microsoft.EntityFrameworkCore; + namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectDeployBoardList; -public record GetProjectsListQueryHandler : IBaseQueryHandler; - -public record GetProjectsListResponse(); - -public class ProjectDeployBoardListQueryHandler +public record ProjectDeployBoardListItem() { + public string ProjectName { get; set; } + public string PhaseName { get; set; } + public int TotalTasks { get; set; } + public int DoneTasks { get; set; } + public TimeSpan TotalTimeSpan { get; set; } + public TimeSpan DoneTimeSpan { get; set; } +} +public record GetProjectsDeployBoardListResponse(List Items); + + +public record GetProjectDeployBoardListQuery():IBaseQuery; + +public class ProjectDeployBoardListQueryHandler:IBaseQueryHandler +{ + private readonly IProgramManagerDbContext _dbContext; + private readonly IAuthHelper _authHelper; + + public ProjectDeployBoardListQueryHandler(IProgramManagerDbContext dbContext, IAuthHelper authHelper) + { + _dbContext = dbContext; + _authHelper = authHelper; + } + + public async Task> Handle(GetProjectDeployBoardListQuery request, CancellationToken cancellationToken) + { + var userId = _authHelper.GetCurrentUserId(); + if (userId == null) + { + return OperationResult.NotFound("کاربر یافت نشد"); + } + + var query =await _dbContext.TaskSections + .Include(x=>x.Task) + .ThenInclude(x => x.Phase) + .ThenInclude(x => x.Project) + .AsNoTracking() + .Where(x => x.Status == TaskSectionStatus.Completed + || x.Status == TaskSectionStatus.PendingForCompletion + || x.Task.Phase.DeployStatus != ProjectDeployStatus.NoTCompleted) + .GroupBy(x=>x.Task.PhaseId).ToListAsync(cancellationToken: cancellationToken); + + var list = query.Select(g => new ProjectDeployBoardListItem + { + ProjectName = g.First().Task.Phase.Project.Name, + PhaseName = g.First().Task.Phase.Name, + TotalTasks = g.Select(x => x.TaskId).Distinct().Count(), + DoneTasks = g.Where(x => x.Status == TaskSectionStatus.Completed).Select(x => x.TaskId).Distinct().Count(), + TotalTimeSpan = TimeSpan.FromHours(g.Sum(x => x.InitialEstimatedHours.TotalHours)), + DoneTimeSpan = TimeSpan.FromHours(g.Where(x => x.Status == TaskSectionStatus.Completed).Sum(x => x.InitialEstimatedHours.TotalHours)) + }).ToList(); + var response = new GetProjectsDeployBoardListResponse(list); + return OperationResult.Success(response); + } } \ No newline at end of file diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs index 9e63c1f6..5c5a019e 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Enums/TaskSectionStatus.cs @@ -6,6 +6,6 @@ public enum TaskSectionStatus ReadyToStart = 1, // آماده شروع InProgress = 2, // درحال انجام Incomplete = 3, // ناتمام شده - PendingForCompletion = 5, // در انتظار تکمیل + PendingForCompletion = 4, // در انتظار تکمیل Completed = 5 // تکمیل شده } \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.Designer.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.Designer.cs new file mode 100644 index 00000000..74452d75 --- /dev/null +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.Designer.cs @@ -0,0 +1,865 @@ +// +using System; +using GozareshgirProgramManager.Infrastructure.Persistence.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace GozareshgirProgramManager.Infrastructure.Migrations +{ + [DbContext(typeof(ProgramManagerDbContext))] + [Migration("20251227094008_add phase deploy status and is archived")] + partial class addphasedeploystatusandisarchived + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.CheckoutAgg.Entities.Checkout", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CheckoutEndDate") + .HasColumnType("datetime2"); + + b.Property("CheckoutStartDate") + .HasColumnType("datetime2"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("DeductionFromSalary") + .HasColumnType("float"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MandatoryHours") + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("MonthlySalaryDefined") + .HasColumnType("float"); + + b.Property("MonthlySalaryPay") + .HasColumnType("float"); + + b.Property("RemainingHours") + .HasColumnType("int"); + + b.Property("TotalDaysWorked") + .HasColumnType("int"); + + b.Property("TotalHoursWorked") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Checkouts", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.CustomerAgg.Customer", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Customers", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("PhaseId") + .HasColumnType("uniqueidentifier"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PhaseId"); + + b.HasIndex("SkillId"); + + b.ToTable("PhaseSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PlannedEndDate") + .HasColumnType("datetime2"); + + b.Property("PlannedStartDate") + .HasColumnType("datetime2"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("DeployStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("IsArchived") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectPhases", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectSection", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("SkillId"); + + b.ToTable("ProjectSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllocatedTime") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("HasTimeOverride") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.Property("PhaseId") + .HasColumnType("uniqueidentifier"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("PhaseId"); + + b.ToTable("ProjectTasks", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("CurrentAssignedUserId") + .HasColumnType("bigint"); + + b.Property("InitialDescription") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("InitialEstimatedHours") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("OriginalAssignedUserId") + .HasColumnType("bigint"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TaskId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.HasIndex("TaskId"); + + b.ToTable("TaskSections", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionActivity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("EndNotes") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("SectionId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("TaskSectionActivities", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionAdditionalTime", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AddedAt") + .HasColumnType("datetime2"); + + b.Property("AddedByUserId") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Hours") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TaskSectionId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TaskSectionId"); + + b.ToTable("TaskSectionAdditionalTimes", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.RoleAgg.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("GozareshgirRoleId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("PmRoles", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.SalaryPaymentSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("EndSettingDate") + .HasColumnType("datetime2"); + + b.Property("HolidayWorking") + .HasColumnType("bit"); + + b.Property("MonthlySalary") + .HasColumnType("float"); + + b.Property("StartSettingDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SalaryPaymentSetting", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Skills", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Mobile") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("VerifyCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.UserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("RevokedAt") + .HasColumnType("datetime2"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ExpiresAt"); + + b.HasIndex("Token") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("UserRefreshTokens", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", "Phase") + .WithMany("PhaseSections") + .HasForeignKey("PhaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Phase"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", "Project") + .WithMany("Phases") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", "Project") + .WithMany("ProjectSections") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Project"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", "Phase") + .WithMany("Tasks") + .HasForeignKey("PhaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Phase"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany("Sections") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", "Task") + .WithMany("Sections") + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("Task"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionActivity", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", "Section") + .WithMany("Activities") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionAdditionalTime", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", null) + .WithMany("AdditionalTimes") + .HasForeignKey("TaskSectionId"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.RoleAgg.Entities.Role", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.PermissionAgg.Entities.Permission", "Permissions", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("Code") + .HasColumnType("int"); + + b1.Property("RoleId") + .HasColumnType("bigint"); + + b1.HasKey("Id"); + + b1.HasIndex("RoleId"); + + b1.ToTable("PmRolePermissions", (string)null); + + b1.WithOwner("Role") + .HasForeignKey("RoleId"); + + b1.Navigation("Role"); + }); + + b.Navigation("Permissions"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.SalaryPaymentSetting", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.WorkingHours", "WorkingHoursList", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("EndShiftOne") + .HasColumnType("time(0)"); + + b1.Property("EndShiftTwo") + .HasColumnType("time(0)"); + + b1.Property("HasRestTime") + .HasColumnType("bit"); + + b1.Property("HasShiftOne") + .HasColumnType("bit"); + + b1.Property("HasShiftTow") + .HasColumnType("bit"); + + b1.Property("IsActiveDay") + .HasColumnType("bit"); + + b1.Property("PersianDayOfWeek") + .HasColumnType("int"); + + b1.Property("RestTime") + .HasColumnType("time(0)"); + + b1.Property("SalaryPaymentSettingId") + .HasColumnType("bigint"); + + b1.Property("ShiftDurationInMinutes") + .HasColumnType("int"); + + b1.Property("StartShiftOne") + .HasColumnType("time(0)"); + + b1.Property("StartShiftTwo") + .HasColumnType("time(0)"); + + b1.HasKey("Id"); + + b1.HasIndex("SalaryPaymentSettingId"); + + b1.ToTable("WorkingHours", (string)null); + + b1.WithOwner("SalaryPaymentSetting") + .HasForeignKey("SalaryPaymentSettingId"); + + b1.Navigation("SalaryPaymentSetting"); + }); + + b.Navigation("WorkingHoursList"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.RoleUserAgg.RoleUser", "RoleUser", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("RoleId") + .HasColumnType("bigint"); + + b1.Property("UserId") + .HasColumnType("bigint"); + + b1.HasKey("Id"); + + b1.HasIndex("UserId"); + + b1.ToTable("RoleUsers", (string)null); + + b1.WithOwner("User") + .HasForeignKey("UserId"); + + b1.Navigation("User"); + }); + + b.Navigation("RoleUser"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.UserRefreshToken", b => + { + b.HasOne("GozareshgirProgramManager.Domain.UserAgg.Entities.User", "User") + .WithMany("RefreshTokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => + { + b.Navigation("Phases"); + + b.Navigation("ProjectSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.Navigation("PhaseSections"); + + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.Navigation("Activities"); + + b.Navigation("AdditionalTimes"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.Navigation("RefreshTokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.cs new file mode 100644 index 00000000..7dce42cd --- /dev/null +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251227094008_add phase deploy status and is archived.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace GozareshgirProgramManager.Infrastructure.Migrations +{ + /// + public partial class addphasedeploystatusandisarchived : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DeployStatus", + table: "ProjectPhases", + type: "nvarchar(30)", + maxLength: 30, + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "IsArchived", + table: "ProjectPhases", + type: "bit", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DeployStatus", + table: "ProjectPhases"); + + migrationBuilder.DropColumn( + name: "IsArchived", + table: "ProjectPhases"); + } + } +} diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index 25e60639..4382f61b 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -126,7 +126,7 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.HasIndex("SkillId"); - b.ToTable("PhaseSections", (string)null); + b.ToTable("PhaseSections"); }); modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => @@ -179,6 +179,11 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.Property("CreationDate") .HasColumnType("datetime2"); + b.Property("DeployStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + b.Property("Description") .HasMaxLength(1000) .HasColumnType("nvarchar(1000)"); @@ -189,6 +194,9 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.Property("HasAssignmentOverride") .HasColumnType("bit"); + b.Property("IsArchived") + .HasColumnType("bit"); + b.Property("Name") .IsRequired() .HasMaxLength(200) @@ -238,7 +246,7 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.HasIndex("SkillId"); - b.ToTable("ProjectSections", (string)null); + b.ToTable("ProjectSections"); }); modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectPhaseMapping.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectPhaseMapping.cs index 838b7f0e..01a43077 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectPhaseMapping.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectPhaseMapping.cs @@ -48,6 +48,9 @@ public class ProjectPhaseMapping : IEntityTypeConfiguration builder.Property(ph => ph.HasAssignmentOverride) .IsRequired(); + builder.Property(x => x.DeployStatus) + .HasConversion().HasMaxLength(30); + // Relationship with Project builder.HasOne(ph => ph.Project) .WithMany(p => p.Phases) diff --git a/ServiceHost/Areas/Admin/Controllers/ProgramManager/ProjectController.cs b/ServiceHost/Areas/Admin/Controllers/ProgramManager/ProjectController.cs index 225a1e9b..1aa439e8 100644 --- a/ServiceHost/Areas/Admin/Controllers/ProgramManager/ProjectController.cs +++ b/ServiceHost/Areas/Admin/Controllers/ProgramManager/ProjectController.cs @@ -12,6 +12,7 @@ using GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectA using GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList; using GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardDetail; using GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardList; +using GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectDeployBoardList; using GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectSetTimeDetails; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -112,4 +113,11 @@ public class ProjectController : ProgramManagerBaseController var res = await _mediator.Send(query); return res; } + + [HttpGet("deploy-board")] + public async Task>> GetProjectDeployBoard() + { + var request = new GetProjectDeployBoardListQuery(); + return await _mediator.Send(request); + } } \ No newline at end of file From 7ce7854091b8737183d526c025bf4a947fa868f3 Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 27 Dec 2025 16:26:07 +0330 Subject: [PATCH 5/6] add: include DeployStatus in ProjectDeployBoardListItem for deployment status tracking --- .../ProjectDeployBoardListQueryHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs index 742e3455..3cbac6e0 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardList/ProjectDeployBoardListQueryHandler.cs @@ -16,6 +16,7 @@ public record ProjectDeployBoardListItem() public int DoneTasks { get; set; } public TimeSpan TotalTimeSpan { get; set; } public TimeSpan DoneTimeSpan { get; set; } + public ProjectDeployStatus DeployStatus { get; set; } } public record GetProjectsDeployBoardListResponse(List Items); @@ -59,7 +60,9 @@ public class ProjectDeployBoardListQueryHandler:IBaseQueryHandler x.TaskId).Distinct().Count(), DoneTasks = g.Where(x => x.Status == TaskSectionStatus.Completed).Select(x => x.TaskId).Distinct().Count(), TotalTimeSpan = TimeSpan.FromHours(g.Sum(x => x.InitialEstimatedHours.TotalHours)), - DoneTimeSpan = TimeSpan.FromHours(g.Where(x => x.Status == TaskSectionStatus.Completed).Sum(x => x.InitialEstimatedHours.TotalHours)) + DoneTimeSpan = TimeSpan.FromHours(g.Where(x => x.Status == TaskSectionStatus.Completed) + .Sum(x => x.InitialEstimatedHours.TotalHours)), + DeployStatus = g.First().Task.Phase.DeployStatus }).ToList(); var response = new GetProjectsDeployBoardListResponse(list); return OperationResult.Success(response); From 21599016143290a34521a7878751579a7540e807 Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 27 Dec 2025 19:42:13 +0330 Subject: [PATCH 6/6] add: implement ProjectDeployBoardDetailsQuery and its handler with validation for project phase details --- .../ProjectDeployBoardDetailsQueryHandler.cs | 101 ++++++++++++++++++ ...ProjectDeployBoardDetailsQueryValidator.cs | 11 ++ 2 files changed, 112 insertions(+) create mode 100644 ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs create mode 100644 ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryValidator.cs diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs new file mode 100644 index 00000000..3e3657d9 --- /dev/null +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs @@ -0,0 +1,101 @@ +using System.Security.AccessControl; +using GozareshgirProgramManager.Application._Common.Interfaces; +using GozareshgirProgramManager.Application._Common.Models; +using Microsoft.EntityFrameworkCore; + +namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectDeployBoardDetail; + +public record ProjectDeployBoardDetailsResponse( + ProjectDeployBoardDetailPhaseItem Phase, + List Tasks); + +public record ProjectDeployBoardDetailPhaseItem( + string Name, + TimeSpan TotalTimeSpan, + TimeSpan DoneTimeSpan); + +public record ProjectDeployBoardDetailTaskItem( + string Name, + TimeSpan TotalTimeSpan, + TimeSpan DoneTimeSpan, + List Skills) + : ProjectDeployBoardDetailPhaseItem(Name, TotalTimeSpan, DoneTimeSpan); + +public record ProjectDeployBoardDetailItemSkill(string OriginalUserFullName, string SkillName, int TimePercentage); + +public record ProjectDeployBoardDetailsQuery(Guid PhaseId) : IBaseQuery; + +public class + ProjectDeployBoardDetailsQueryHandler : IBaseQueryHandler +{ + private readonly IProgramManagerDbContext _dbContext; + + public ProjectDeployBoardDetailsQueryHandler(IProgramManagerDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> Handle(ProjectDeployBoardDetailsQuery request, + CancellationToken cancellationToken) + { + var phase = await _dbContext.ProjectPhases + .Include(x => x.Tasks) + .ThenInclude(x => x.Sections) + .ThenInclude(x => x.Activities) + .Include(x => x.Tasks) + .ThenInclude(x => x.Sections) + .ThenInclude(x => x.AdditionalTimes) + .Include(x => x.Tasks) + .ThenInclude(x => x.Sections) + .ThenInclude(x => x.Skill) + .FirstOrDefaultAsync(x => x.Id == request.PhaseId, cancellationToken); + + if (phase == null) + return OperationResult.NotFound("بخش اصلی مورد نظر یافت نشد"); + + var userIds = phase.Tasks + .SelectMany(t => t.Sections) + .Select(s => s.OriginalAssignedUserId) + .Distinct() + .ToList(); + + var usersDict = await _dbContext.Users + .Where(x => userIds.Contains(x.Id)) + .ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken); + + var tasksRes = phase.Tasks.Select(t => + { + var totalTime = t.Sections.Select(s => s.FinalEstimatedHours) + .Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next)); + + var doneTime = t.Sections.Aggregate(TimeSpan.Zero, + (sum, next) => sum.Add(next.GetTotalTimeSpent())); + var skills = t.Sections + .Select(s => new ProjectDeployBoardDetailItemSkill( + usersDict.GetValueOrDefault(s.OriginalAssignedUserId, "کاربر ناشناس"), + s.Skill?.Name ?? "بدون مهارت", + totalTime.TotalSeconds > 0 + ? (int)((doneTime.TotalSeconds / totalTime.TotalSeconds) * 100) + : 0)).ToList(); + + return new ProjectDeployBoardDetailTaskItem( + t.Name, + totalTime, + doneTime, + skills); + }).ToList(); + + var totalTimeSpan = tasksRes.Aggregate(TimeSpan.Zero, + (sum, next) => sum.Add(next.TotalTimeSpan)); + + var doneTimeSpan = tasksRes.Aggregate(TimeSpan.Zero, + (sum, next) => sum.Add(next.DoneTimeSpan)); + + var phaseRes = new ProjectDeployBoardDetailPhaseItem(phase.Name, totalTimeSpan, doneTimeSpan); + + var res = new ProjectDeployBoardDetailsResponse(phaseRes, tasksRes); + + return OperationResult.Success(res); + } +} \ No newline at end of file diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryValidator.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryValidator.cs new file mode 100644 index 00000000..0cbf5cd9 --- /dev/null +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectDeployBoardDetail; + +public class ProjectDeployBoardDetailsQueryValidator:AbstractValidator +{ + public ProjectDeployBoardDetailsQueryValidator() + { + RuleFor(x=>x.PhaseId).NotNull().WithMessage("شناسه بخش اصلی نمی‌تواند خالی باشد"); + } +} \ No newline at end of file