Merge branch 'Feature/program-manager/set-user-time' into Main

This commit is contained in:
2026-01-04 13:51:55 +03:30
3 changed files with 154 additions and 36 deletions

View File

@@ -3,7 +3,7 @@ using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectSetTimeDetails;
public record ProjectSetTimeDetailsQuery(Guid TaskId)
public record ProjectSetTimeDetailsQuery(Guid Id, ProjectHierarchyLevel Level)
: IBaseQuery<ProjectSetTimeResponse>;
public record ProjectSetTimeResponse(
List<ProjectSetTimeResponseSkill> SkillItems,

View File

@@ -22,17 +22,30 @@ public class ProjectSetTimeDetailsQueryHandler
public async Task<OperationResult<ProjectSetTimeResponse>> Handle(ProjectSetTimeDetailsQuery request,
CancellationToken cancellationToken)
{
return request.Level switch
{
ProjectHierarchyLevel.Task => await GetTaskSetTimeDetails(request.Id, request.Level, cancellationToken),
ProjectHierarchyLevel.Phase => await GetPhaseSetTimeDetails(request.Id, request.Level, cancellationToken),
ProjectHierarchyLevel.Project => await GetProjectSetTimeDetails(request.Id, request.Level, cancellationToken),
_ => OperationResult<ProjectSetTimeResponse>.Failure("سطح معادل نامعتبر است")
};
}
private async Task<OperationResult<ProjectSetTimeResponse>> GetTaskSetTimeDetails(Guid id, ProjectHierarchyLevel level,
CancellationToken cancellationToken)
{
var task = await _context.ProjectTasks
.Where(p => p.Id == request.TaskId)
.Where(p => p.Id == id)
.Include(x => x.Sections)
.ThenInclude(x => x.AdditionalTimes).AsNoTracking()
.FirstOrDefaultAsync(cancellationToken);
if (task == null)
{
return OperationResult<ProjectSetTimeResponse>.NotFound("Project not found");
return OperationResult<ProjectSetTimeResponse>.NotFound("تسک یافت نشد");
}
var userIds = task.Sections.Select(x => x.OriginalAssignedUserId)
.Distinct().ToList();
@@ -41,41 +54,141 @@ public class ProjectSetTimeDetailsQueryHandler
.AsNoTracking()
.ToListAsync(cancellationToken);
var skills = await _context.Skills
var skills = await _context.Skills
.AsNoTracking()
.ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse(
skills.Select(skill =>
skills.Select(skill =>
{
var section = task.Sections
.FirstOrDefault(x => x.SkillId == skill.Id);
var user = users.FirstOrDefault(x => x.Id == section?.OriginalAssignedUserId);
return new ProjectSetTimeResponseSkill
{
var section = task.Sections
.FirstOrDefault(x => x.SkillId == skill.Id);
var user = users.FirstOrDefault(x => x.Id == section?.OriginalAssignedUserId);
return new ProjectSetTimeResponseSkill
{
AdditionalTimes = section?.AdditionalTimes
.Select(x => new ProjectSetTimeResponseSectionAdditionalTime
{
Description = x.Reason ?? "",
Hours = (int)x.Hours.TotalHours,
Minutes = x.Hours.Minutes,
CreationDate = x.CreationDate.ToFarsi()
}).OrderBy(x=>x.CreationDate).ToList()??[],
InitCreationTime = section?.CreationDate.ToFarsi()??"",
SkillName = skill?.Name ?? "",
UserFullName = user?.FullName ?? "",
SectionId = section?.Id??Guid.Empty,
InitialDescription = section?.InitialDescription ?? "",
InitialHours = (int)(section?.InitialEstimatedHours.TotalHours ?? 0),
InitialMinutes = section?.InitialEstimatedHours.Minutes??0,
UserId = section?.OriginalAssignedUserId??0,
SkillId = task.Id,
};
}).OrderBy(x=>x.SkillId).ToList(),
task.Id,
ProjectHierarchyLevel.Task);
AdditionalTimes = section?.AdditionalTimes
.Select(x => new ProjectSetTimeResponseSectionAdditionalTime
{
Description = x.Reason ?? "",
Hours = (int)x.Hours.TotalHours,
Minutes = x.Hours.Minutes,
CreationDate = x.CreationDate.ToFarsi()
}).OrderBy(x => x.CreationDate).ToList() ?? [],
InitCreationTime = section?.CreationDate.ToFarsi() ?? "",
SkillName = skill?.Name ?? "",
UserFullName = user?.FullName ?? "",
SectionId = section?.Id ?? Guid.Empty,
InitialDescription = section?.InitialDescription ?? "",
InitialHours = (int)(section?.InitialEstimatedHours.TotalHours ?? 0),
InitialMinutes = section?.InitialEstimatedHours.Minutes ?? 0,
UserId = section?.OriginalAssignedUserId ?? 0,
SkillId = task.Id,
};
}).OrderBy(x => x.SkillId).ToList(),
task.Id,
level);
return OperationResult<ProjectSetTimeResponse>.Success(res);
}
private async Task<OperationResult<ProjectSetTimeResponse>> GetPhaseSetTimeDetails(Guid id, ProjectHierarchyLevel level,
CancellationToken cancellationToken)
{
var phase = await _context.ProjectPhases
.Where(p => p.Id == id)
.Include(x => x.PhaseSections).AsNoTracking()
.FirstOrDefaultAsync(cancellationToken);
if (phase == null)
{
return OperationResult<ProjectSetTimeResponse>.NotFound("فاز یافت نشد");
}
var userIds = phase.PhaseSections.Select(x => x.UserId)
.Distinct().ToList();
var users = await _context.Users
.Where(x => userIds.Contains(x.Id))
.AsNoTracking()
.ToListAsync(cancellationToken);
var skills = await _context.Skills
.AsNoTracking()
.ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse(
skills.Select(skill =>
{
var section = phase.PhaseSections
.FirstOrDefault(x => x.SkillId == skill.Id);
var user = users.FirstOrDefault(x => x.Id == section?.UserId);
return new ProjectSetTimeResponseSkill
{
AdditionalTimes = [],
InitCreationTime = "",
SkillName = skill.Name ?? "",
UserFullName = user?.FullName ?? "",
SectionId = section?.Id ?? Guid.Empty,
InitialDescription = "",
InitialHours = 0,
InitialMinutes = 0,
UserId = section?.UserId ?? 0,
SkillId = skill.Id,
};
}).OrderBy(x => x.SkillId).ToList(),
phase.Id,
level);
return OperationResult<ProjectSetTimeResponse>.Success(res);
}
private async Task<OperationResult<ProjectSetTimeResponse>> GetProjectSetTimeDetails(Guid id, ProjectHierarchyLevel level,
CancellationToken cancellationToken)
{
var project = await _context.Projects
.Where(p => p.Id == id)
.Include(x => x.ProjectSections).AsNoTracking()
.FirstOrDefaultAsync(cancellationToken);
if (project == null)
{
return OperationResult<ProjectSetTimeResponse>.NotFound("پروژه یافت نشد");
}
var userIds = project.ProjectSections.Select(x => x.UserId)
.Distinct().ToList();
var users = await _context.Users
.Where(x => userIds.Contains(x.Id))
.AsNoTracking()
.ToListAsync(cancellationToken);
var skills = await _context.Skills
.AsNoTracking()
.ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse(
skills.Select(skill =>
{
var section = project.ProjectSections
.FirstOrDefault(x => x.SkillId == skill.Id);
var user = users.FirstOrDefault(x => x.Id == section?.UserId);
return new ProjectSetTimeResponseSkill
{
AdditionalTimes = [],
InitCreationTime = "",
SkillName = skill.Name ?? "",
UserFullName = user?.FullName ?? "",
SectionId = section?.Id ?? Guid.Empty,
InitialDescription = "",
InitialHours = 0,
InitialMinutes = 0,
UserId = section?.UserId ?? 0,
SkillId = skill.Id,
};
}).OrderBy(x => x.SkillId).ToList(),
project.Id,
level);
return OperationResult<ProjectSetTimeResponse>.Success(res);
}

View File

@@ -1,13 +1,18 @@
using FluentValidation;
using FluentValidation;
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectSetTimeDetails;
public class ProjectSetTimeDetailsQueryValidator:AbstractValidator<ProjectSetTimeDetailsQuery>
public class ProjectSetTimeDetailsQueryValidator : AbstractValidator<ProjectSetTimeDetailsQuery>
{
public ProjectSetTimeDetailsQueryValidator()
{
RuleFor(x => x.TaskId)
RuleFor(x => x.Id)
.NotEmpty()
.WithMessage("شناسه پروژه نمی‌تواند خالی باشد.");
.WithMessage("شناسه نمی‌تواند خالی باشد.");
RuleFor(x => x.Level)
.IsInEnum()
.WithMessage("سطح معادل نامعتبر است.");
}
}