Commit 6a82d6a4 authored by hasan khaddour's avatar hasan khaddour

Refactor and Comment

parent 37f508dc
......@@ -84,6 +84,14 @@ namespace PSManagement.Api.DI
.AllowAnyMethod()
.AllowCredentials());
options.AddPolicy("AllowHiast",
builder => builder
.WithOrigins("**.hiast.edu.sy/") // Add your frontend URL here
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
});
......
......@@ -46,6 +46,8 @@ namespace PSManagement.Application.Mappers
CreateMap<StepTrack, StepTrackDTO>()
.ForMember(d => d.StepInfo, opt => opt.MapFrom(s => s.Step.StepInfo))
.ForMember(d => d.TrackInfo, op => op.MapFrom(e => e.Track.TrackInfo))
.ForMember(d => d.StepWeight, op => op.MapFrom(e => e.Step.Weight))
;
CreateMap<EmployeeTrack, EmployeeTrackDTO>()
.ForMember(d => d.TrackInfo, op => op.MapFrom(e => e.Track.TrackInfo))
......
......@@ -52,6 +52,7 @@ namespace PSManagement.Application.Projects.UseCases.Commands.CreateProject
.WithClassification(request.ProjectClassification)
.Build();
project.ProjectTypeId = request.ProjectTypeId;
project.Propose();
......@@ -60,7 +61,9 @@ namespace PSManagement.Application.Projects.UseCases.Commands.CreateProject
project.AddDomainEvent(new ProjectCreatedEvent(project.Id,project.TeamLeaderId,project.ProjectManagerId));
await _unitOfWork.SaveChangesAsync();
return Result.Success(project.Id);
}
......
......@@ -42,11 +42,10 @@ namespace PSManagement.Application.Projects.UseCases.Queries.GetCompletionContri
public async Task<Result<IEnumerable<EmployeeContributionDTO>>> Handle(GetCompletionContributionQuery request, CancellationToken cancellationToken)
{
_trackSpecification.AddInclude(e => e.EmployeeTracks);
_trackSpecification.AddInclude("EmployeeTracks.Employee");
_trackSpecification.Criteria = e => e.ProjectId == request.ProjectId;
_projectSpecification.AddInclude(e=> e.Tracks);
_projectSpecification.AddInclude("Tracks.EmployeeTracks.Employee");
_projectSpecification.AddInclude(e=>e.ProjectCompletion);
Project project = await _projectsRepository.GetByIdAsync(request.ProjectId, _projectSpecification);
if (project is null)
......@@ -61,49 +60,9 @@ namespace PSManagement.Application.Projects.UseCases.Queries.GetCompletionContri
return Result.Invalid(ProjectsErrors.UnCompletedError);
}
var tracks = await _tracksRepository.ListAsync(_trackSpecification);
// Dictionary to accumulate contributions by employee
var contributionsByEmployee = new Dictionary<int, EmployeeContributionDTO>();
foreach (Track track in tracks)
{
foreach (EmployeeTrack employeeTrack in track.EmployeeTracks)
{
var employeeId = employeeTrack.Employee.Id; // Assuming Employee has an Id property
var contributionRatio = employeeTrack.EmployeeWork.ContributingRatio;
// If employee already has contributions, sum them up
if (contributionsByEmployee.ContainsKey(employeeId))
{
contributionsByEmployee[employeeId].ContributionRatio += contributionRatio;
}
else
{
// Otherwise, add a new entry for the employee
contributionsByEmployee[employeeId] = new EmployeeContributionDTO(contributionRatio, _mapper.Map<EmployeeDTO>(employeeTrack.Employee));
}
}
}
// Convert the dictionary values to a list for the final result
var contributions = contributionsByEmployee.Values.ToList();
//var tracks = await _tracksRepository.ListAsync(_trackSpecification);
//var contributions = new List<EmployeeContributionDTO>();
//foreach (Track track in tracks) {
// foreach (EmployeeTrack employeeTrack in track.EmployeeTracks) {
// // need to fix
// contributions.Add(
// new (employeeTrack.EmployeeWork.ContributingRatio,employeeTrack.Employee));
// }
//}
return Result.Success(contributions.AsEnumerable());
IEnumerable<ParticipantContribution> contributions = project.CalculateContributions();
return Result.Success(_mapper.Map<IEnumerable<EmployeeContributionDTO>>(contributions));
}
......
......@@ -31,7 +31,7 @@ namespace PSManagement.Application.Steps.UseCases.Commands.ChangeStepWeight
return Result.Invalid(StepsErrors.InvalidWeightError);
}
step.Weight = request.Weight;
step.UpdateWeight(request.Weight);
await _stepsRepository.UpdateAsync(step);
......
......@@ -32,7 +32,7 @@ namespace PSManagement.Application.Steps.UseCases.Commands.UpdateCompletionRatio
return Result.Invalid(StepsErrors.InvalidCompletionRatioError);
}
step.CurrentCompletionRatio = request.CompletionRatio;
step.ChangeCompletionRatio(request.CompletionRatio);
await _stepsRepository.UpdateAsync(step);
......
......@@ -10,6 +10,7 @@ namespace PSManagement.Application.Tracks.Common
public int Id { get; set; }
public int StepId { get; set; }
public int TrackId { get; set; }
public int StepWeight { get; set; }
public StepInfo StepInfo { get; set; }
public TrackInfo TrackInfo { get; set; }
......
......@@ -48,21 +48,22 @@ namespace PSManagement.Application.Tracks.UseCaes.Commands.AddEmployeeTrack
}
if (track.TrackInfo.IsCompleted)
if (track.IsCompleted())
{
return Result.Invalid(TracksErrors.TrackCompletedUpdateError);
}
if (track.EmployeeTracks.Any(e => e.EmployeeId == request.EmployeeId)) {
if (track.HasEmployee(request.EmployeeId)) {
return Result.Invalid(TracksErrors.ParticipantTrackExistError);
}
var r =_mapper.Map<EmployeeTrack>(request);
//Console.WriteLine(r.EmloyeeId);
EmployeeTrack employeeTrack = await _employeeTracksRepository.AddAsync(_mapper.Map<EmployeeTrack>(request));
var employeeTrack =_mapper.Map<EmployeeTrack>(request);
track.AddEmployeeTrack(request.EmployeeId,request.EmployeeWork,request.EmployeeWorkInfo,request.Notes);
await _unitOfWork.SaveChangesAsync();
return Result.Success(employeeTrack.Id);
......
......@@ -34,7 +34,7 @@ namespace PSManagement.Application.Tracks.UseCaes.Queries.GetTracksByFilter
public async Task<Result<IEnumerable<TrackDTO>>> Handle(GetTracksByFilterQuery request, CancellationToken cancellationToken)
{
_specification.ApplyOptionalPagination(request.PageSize, request.PageNumber);
_specification.AddInclude(e => e.Project);
var tracks = await _tracksRepository.ListAsync(_specification);
return Result.Success(_mapper.Map<IEnumerable<TrackDTO>>(tracks));
......
......@@ -31,7 +31,7 @@ namespace PSManagement.Application.Tracks.UseCaes.Queries.GetUncompletedTracks
{
_specification.Criteria = c => c.TrackInfo.IsCompleted == false;
_specification.AddInclude(e => e.Project);
var tracks = await _tracksRepository.ListAsync(_specification);
......
......@@ -12,7 +12,7 @@ namespace PSManagement.Contracts.Tracks.Response
TrackInfo TrackInfo,
String ExecutionState,
int TrackExecutionRatio,
int OldExecutionRatio
int OldExecutionRatio,
int StepWeight
);
}
......@@ -10,11 +10,18 @@ using System.Threading.Tasks;
namespace PSManagement.Domain.Customers.Entities
{
/// <summary>
/// the class represent the contact info item for the customer
/// </summary>
public sealed class ContactInfo : BaseEntity
{
// the key for contact
public String ContactType { get; private set; }
//the value for contact
public String ContactValue { get; private set; }
#region Constructors
public ContactInfo(string contactValue, string contactType)
{
ContactValue = contactValue;
......@@ -25,6 +32,6 @@ namespace PSManagement.Domain.Customers.Entities
{
}
#endregion Constructors
}
}
......@@ -7,6 +7,11 @@ using System.Threading.Tasks;
namespace PSManagement.Domain.Customers.ValueObjects
{
/// <summary>
/// this is a value object to hide the contact number
/// its old and deprected
/// i use the record instead
/// </summary>
public sealed class ContactNumber : ValueObject
{
public int Number { get; private set; }
......
......@@ -10,6 +10,8 @@ namespace PSManagement.Domain.Employees.Entities
public class Department : BaseEntity
{
public string Name { get; set; }
#region Constructors
public Department(string name)
{
Name = name;
......@@ -18,5 +20,6 @@ namespace PSManagement.Domain.Employees.Entities
{
}
#endregion Constructors
}
}
......@@ -13,6 +13,13 @@ using System.Threading.Tasks;
namespace PSManagement.Domain.Employees.Entities
{
/// <summary>
/// Employee Class
///
/// </summary>
/// this class represent an employee that work and manage and lead a project
/// in this class we abstract the employee data and take
/// the only needed data in our problem
public class Employee : BaseEntity
{
public int HIASTId { get; set; }
......@@ -22,6 +29,10 @@ namespace PSManagement.Domain.Employees.Entities
public Department Department { get; set; }
public PersonalInfo PersonalInfo { get; set; }
public WorkInfo WorkInfo { get; set; }
// the availablity value object
// we only care about the working hours
// any other inof can be hide in it
public Availability Availability { get; set; }
......
namespace PSManagement.Domain.Employees.Entities
{
/// <summary>
/// Availability Details
/// </summary>
/// Current Working Hours on our system
/// this info should be integrated with ldap
public record Availability(
int CurrentWorkingHours,
bool IsAvailable
......
namespace PSManagement.Domain.Employees.Entities
{
/// <summary>
/// Personal Information
/// </summary>
/// this cvalue object record represent the
/// personal information that we care about it in our case
/// we only need the first and last name
public record PersonalInfo(
string FirstName,
string LastName
......
......@@ -2,6 +2,12 @@
namespace PSManagement.Domain.Employees.Entities
{
/// <summary>
/// Work Informations
/// </summary>
/// this record (VO) contain two proerties :
/// Work Type : for the type of work that the employee do like programmer , designer researcher ...
/// Work Job : for the career of the employee like doctor , engineer ,...
public record WorkInfo (
String WorkType ,
String WorkJob
......
......@@ -5,7 +5,11 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="FinancialSpends\DomainErrors\" />
<Folder Include="FinancialSpends\DomainEvents\" />
<Folder Include="FinancialSpends\ValueObjects\" />
<Folder Include="Identity\ValueObjects\" />
<Folder Include="ProjectsTypes\ValueObjects\" />
</ItemGroup>
<ItemGroup>
......
using PSManagement.Domain.Employees.Entities;
namespace PSManagement.Domain.Projects.Entities
{
public class ParticipantContribution {
public int ContributionRatio { get; set; }
public Employee Employee { get; set; }
public ParticipantContribution(int contributionRatio, Employee employee)
{
ContributionRatio = contributionRatio;
Employee = employee;
}
}
}
......@@ -65,7 +65,7 @@ namespace PSManagement.Domain.Projects.Entities
public string CurrentState { get; private set; } // Persisted in the database
[NotMapped]
private IProjectState _state ;
private IProjectState _state;
[NotMapped]
public IProjectState State {
......@@ -77,7 +77,7 @@ namespace PSManagement.Domain.Projects.Entities
return _state;
}
set => _state = value;
}
......@@ -144,20 +144,20 @@ namespace PSManagement.Domain.Projects.Entities
public bool HasAttachment(int attachmentId)
{
return Attachments.Where(e => e.Id == attachmentId).FirstOrDefault() is null;
return Attachments.Where(e => e.Id == attachmentId).FirstOrDefault() is not null;
}
public void AddParticipation(int participantId, int projectId, string role, int partialTimeRatio)
{
this.EmployeeParticipates.Add(new (participantId,projectId,role, partialTimeRatio));
this.EmployeeParticipates.Add(new(participantId, projectId, role, partialTimeRatio));
AddDomainEvent(new ParticipantAddedEvent(participantId, projectId,partialTimeRatio, role));
AddDomainEvent(new ParticipantAddedEvent(participantId, projectId, partialTimeRatio, role));
}
public void AddAttachment(string attachmentUrl,string attachmentName,string attachmentDescription,int projectId)
public void AddAttachment(string attachmentUrl, string attachmentName, string attachmentDescription, int projectId)
{
Attachment attachment = new(attachmentUrl, attachmentName,attachmentDescription, projectId);
Attachment attachment = new(attachmentUrl, attachmentName, attachmentDescription, projectId);
Attachments.Add(attachment);
......@@ -175,6 +175,46 @@ namespace PSManagement.Domain.Projects.Entities
#endregion Encapsulating the collection operations
// this methods encaplsulate the way to calcs the
// total contributions of the employees overall the tracks
//
#region Encapsulate Complteion Rules
public IEnumerable<ParticipantContribution> CalculateContributions() {
// Dictionary to accumulate contributions by employee
var contributionsByEmployee = new Dictionary<int, ParticipantContribution>();
foreach (Track track in Tracks)
{
foreach (EmployeeTrack employeeTrack in track.EmployeeTracks)
{
// to aggreagte the employees based on there ids
var employeeId = employeeTrack.Employee.Id;
var contributionRatio = employeeTrack.EmployeeWork.ContributingRatio;
// If employee already has contributions, sum them up
if (contributionsByEmployee.ContainsKey(employeeId))
{
contributionsByEmployee[employeeId].ContributionRatio += contributionRatio;
}
else
{
// Otherwise, add a new entry for the employee
contributionsByEmployee[employeeId] = new ParticipantContribution(contributionRatio,employeeTrack.Employee);
}
}
}
// Convert the dictionary values to a list for the final result
var contributions = contributionsByEmployee.Values.ToList();
return contributions.AsEnumerable();
}
#endregion Encapsulate Completion Rules
// the transition of the project state
// each handler (stated transition) move the project state form one ot other
......@@ -182,10 +222,10 @@ namespace PSManagement.Domain.Projects.Entities
#region State Transitions
public Result Complete(ProjectCompletion projectCompletion)
public Result Complete(ProjectCompletion projectCompletion)
{
return State.Complete(this, projectCompletion );
return State.Complete(this, projectCompletion);
}
public Result Plan()
{
......@@ -199,7 +239,7 @@ namespace PSManagement.Domain.Projects.Entities
}
public Result Cancel(DateTime canellationTime)
{
return State.Cancel(this,canellationTime);
return State.Cancel(this, canellationTime);
}
public Result Propose()
......@@ -235,16 +275,16 @@ namespace PSManagement.Domain.Projects.Entities
var participate = EmployeeParticipates.Where(e => e.EmployeeId == participantId).FirstOrDefault();
AddDomainEvent(new ParticipationChangedEvent(
participantId,
participate.PartialTimeRatio,partialTimeRation,
role,participate.Role, Id, DateTime.Now));
participate.PartialTimeRatio, partialTimeRation,
role, participate.Role, Id, DateTime.Now));
participate.Role = role;
participate.PartialTimeRatio = partialTimeRation;
}
public bool HasParticipant(int participantId)
{
return EmployeeParticipates.Where(e => e.EmployeeId ==participantId).FirstOrDefault() is not null;
return EmployeeParticipates.Where(e => e.EmployeeId == participantId).FirstOrDefault() is not null;
}
#endregion Busines rules validators
......@@ -318,5 +358,4 @@ namespace PSManagement.Domain.Projects.Entities
#endregion state extracting from state name
}
}
......@@ -14,16 +14,24 @@ namespace PSManagement.Domain.Projects.Entities
{
public class Step : BaseEntity
{
// value object represent the subjective information of the step
public StepInfo StepInfo { get; set; }
// this field can be calculated from the track
// but we use it for performance matter
public int CurrentCompletionRatio { get; set; }
//
public int Weight { get; set; }
#region Association
public int ProjectId { get; set; }
public Project Project { get; set; }
public ICollection<StepTrack> StepTracks { get; set; }
#endregion Association
#region Constructors
public Step()
{
......@@ -38,8 +46,20 @@ namespace PSManagement.Domain.Projects.Entities
Weight = weight;
}
#endregion Constructors
#region Encpasulate Business Rules
public void UpdateWeight(int weight)
{
Weight = weight;
}
public void ChangeCompletionRatio(int completionRatio)
{
CurrentCompletionRatio = completionRatio;
}
public void ChangeInfo(StepInfo stepInfo)
......@@ -47,6 +67,7 @@ namespace PSManagement.Domain.Projects.Entities
this.StepInfo = new (stepInfo.StepName,stepInfo.Description,stepInfo.StartDate,stepInfo.Duration,stepInfo.NumberOfWorker);
}
#endregion Encpasulate Business Rules
}
......
using PSManagement.SharedKernel.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PSManagement.Domain.ProjectsTypes.DomainEvents
{
public record NewTypeAddedEvent(
string TypeName ,
int Id
):IDomainEvent;
}
......@@ -13,6 +13,12 @@ using System.Threading.Tasks;
namespace PSManagement.Domain.Tracking
{
/// <summary>
/// Track Class
/// </summary>
/// this class represent the track action
/// on a specific project
///
public class Track : BaseEntity
{
public TrackInfo TrackInfo { get; set; }
......@@ -36,6 +42,7 @@ namespace PSManagement.Domain.Tracking
#endregion Constructors
#region Encapsulation
// this method hide the publishing of the domain events
public void Complete(DateTime completionDate)
......@@ -45,5 +52,31 @@ namespace PSManagement.Domain.Tracking
AddDomainEvent(new TrackCompletedEvent(ProjectId, Id, completionDate));
}
public bool IsCompleted()
{
return TrackInfo.IsCompleted;
}
public bool HasEmployee(int employeeId)
{
return EmployeeTracks.Any(e => e.EmployeeId == employeeId);
}
public void AddEmployeeTrack(int employeeId, EmployeeWork employeeWork, EmployeeWorkInfo employeeWorkInfo, string notes)
{
EmployeeTracks.Add(new EmployeeTrack {
EmployeeWork=employeeWork,
EmployeeWorkInfo=employeeWorkInfo,
EmployeeId=employeeId,
TrackId=Id,
Notes=notes
});
}
#endregion Encapsulation
}
}
......@@ -79,7 +79,7 @@ namespace PSManagement.Presentation.Controllers.Projects
}
[HttpGet("Completion{id}")]
[HttpGet("Completion/{id}")]
public async Task<IActionResult> GetProjectCompletion(int id)
{
var query = new GetProjectCompletionQuery(id);
......
......@@ -19,7 +19,7 @@ using PSManagement.Contracts.Tracks.Response;
using PSManagement.Presentation.Controllers.ApiBase;
using System.Collections.Generic;
using System.Threading.Tasks;
using PSManagement.Application.Tracks.UseCaes.Queries.GetTracksByFilter;
namespace PSManagement.Presentation.Controllers.Tracks
{
[Route("api/[controller]")]
......@@ -54,6 +54,19 @@ namespace PSManagement.Presentation.Controllers.Tracks
return HandleResult(result);
}
[HttpGet("ByFilter")]
public async Task<IActionResult> GeTracksByFilter([FromQuery] GetTracksByFilterRequest request)
{
var query = _mapper.Map<GetTracksByFilterQuery>(request);
var result = _mapper.Map<Result<IEnumerable<TrackResponse>>>(await _sender.Send(query));
return HandleResult(result);
}
[HttpGet("UnCompleted")]
public async Task<IActionResult> GetUnCompleted()
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment