Commit b6741e60 authored by hasan khaddour's avatar hasan khaddour

Add State Pattern.

parent 1d47855f
......@@ -23,7 +23,7 @@ namespace PSManagement.Domain.Projects.Builders
private int _projectManagerId;
private int _executerId;
private int _proposerId;
private string _stateName;
private ICollection<Step> _steps;
private ICollection<EmployeeParticipate> _participants;
private ICollection<Attachment> _attachments;
......@@ -94,10 +94,26 @@ namespace PSManagement.Domain.Projects.Builders
}
return this;
}
public ProjectBuilder WithState(string stateName)
{
_stateName = stateName;
return this;
}
public Project Build()
{
Project project= new (_proposalInfo, _projectInfo,_projectAggreement,_proposerId,_teamLeaderId,_projectManagerId ,_executerId);
if (_stateName is null || _stateName == "") {
_stateName = "Proposed";
}
Project project= new (
_proposalInfo,
_projectInfo,
_projectAggreement,
_proposerId,
_teamLeaderId,
_projectManagerId ,
_executerId,
_stateName);
project.FinancialFund = _financialFund;
if (_attachments is not null) {
......
......@@ -7,5 +7,8 @@ using System.Threading.Tasks;
namespace PSManagement.Domain.Projects.DomainEvents
{
public record ProjectCreatedEvent(int projectId) : IDomainEvent;
public record ProjectCreatedEvent(
int ProjectId,
int TeamLeaderId,
int ProjectManagerId) : IDomainEvent;
}
......@@ -4,10 +4,17 @@ namespace PSManagement.Domain.Projects.Entities
{
public class Attachment : BaseEntity
{
public int ProjectId { get; set; }
public string AttachmentUrl { get; set; }
public string AttachmentName { get; set; }
public string AttachmenDescription { get; set; }
public string AttachmentDescription { get; set; }
public Attachment(string attachmentUrl, string attachmentName, string attachmentDescription, int projectId)
{
AttachmentUrl = attachmentUrl;
AttachmentName = attachmentName;
AttachmentDescription = attachmentDescription;
ProjectId = projectId;
}
}
}
......@@ -5,8 +5,10 @@ using PSManagement.Domain.ProjectsStatus.Entites;
using PSManagement.Domain.ProjectTypes.Entities;
using PSManagement.Domain.Tracking;
using PSManagement.SharedKernel.Aggregate;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
......@@ -18,8 +20,12 @@ namespace PSManagement.Domain.Projects.Entities
// information about the project itself
public ProposalInfo ProposalInfo { get; set; }
public ProjectInfo ProjectInfo { get; set; }
public ProjectStatus ProjectStatus { get; set; }
public Aggreement ProjectAggreement { get; set; }
// state management
[NotMapped]
public string CurrentState { get; private set; } // Persisted in the database
private IProjectState _state;
// information about who lead and execute the project
public int TeamLeaderId { get; set; }
......@@ -72,8 +78,10 @@ namespace PSManagement.Domain.Projects.Entities
int proposerId,
int teamLeaderId,
int projectManagerId,
int executerId)
int executerId,
string stateName)
{
SetStateFromString(stateName);
ProposalInfo = proposalInfo;
ProjectInfo = projectInfo;
ProjectAggreement = projectAggreement;
......@@ -91,9 +99,64 @@ namespace PSManagement.Domain.Projects.Entities
}
public Project()
{
_state = new ProposedState();
CurrentState = _state.StateName;
}
public void SetState(IProjectState newState)
{
_state = newState;
CurrentState = _state.StateName; // Update the persisted state
}
public void Complete()
{
_state.Complete(this);
}
public void Plan()
{
_state.Complete(this);
}
public void Approve()
{
_state.Complete(this);
}
public void Cancle()
{
_state.Complete(this);
}
public void Propose()
{
_state.Complete(this);
}
public void SetStateFromString(string stateName)
{
switch (stateName)
{
case "Proposed":
SetState(new ProposedState());
break;
case "InPlan":
SetState(new InPlanState());
break;
case "Cancled":
SetState(new CancledState());
break;
case "InProgress":
SetState(new InProgressState());
break;
case "Completed":
SetState(new CompletedState());
break;
default:
throw new InvalidOperationException("Unknown state");
}
}
}
}
using PSManagement.Domain.Projects.Entities;
using PSManagement.SharedKernel.Specification;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace PSManagement.Domain.Projects
{
public class ProjectSpecification : BaseSpecification<Project>
{
public ProjectSpecification(Expression<Func<Project, bool>> criteria = null) : base(criteria)
{
AddInclude(u => u.Steps);
}
}
}
namespace PSManagement.Domain.Projects.Entities
{
public class CancledState : IProjectState
{
public string StateName => "CancledState";
public void Approve(Project project)
{
}
public void Cancle(Project project)
{
}
public void Complete(Project project)
{
}
public void Plan(Project project)
{
}
public void Propose(Project project)
{
}
}
}
namespace PSManagement.Domain.Projects.Entities
{
public class CompletedState : IProjectState
{
public string StateName => "Completed";
public void Approve(Project project)
{
}
public void Cancle(Project project)
{
}
public void Complete(Project project)
{
}
public void Plan(Project project)
{
}
public void Propose(Project project)
{
}
}
}
namespace PSManagement.Domain.Projects.Entities
{
public interface IProjectState
{
void Complete(Project project);
void Plan(Project project);
void Approve(Project project);
void Cancle(Project project);
void Propose(Project project);
string StateName { get; }
}
}
namespace PSManagement.Domain.Projects.Entities
{
public class InPlanState : IProjectState
{
public string StateName => "InPlan";
public void Approve(Project project)
{
project.SetState(new InProgressState());
}
public void Cancle(Project project)
{
project.SetState(new CancledState());
}
public void Complete(Project project)
{
project.SetState(new CompletedState());
}
public void Plan(Project project)
{
}
public void Propose(Project project)
{
}
}
}
namespace PSManagement.Domain.Projects.Entities
{
public class InProgressState : IProjectState
{
public string StateName => "InProgress";
public void Approve(Project project)
{
}
public void Cancle(Project project)
{
project.SetState(new CancledState());
}
public void Complete(Project project)
{
project.SetState(new CompletedState());
}
public void Plan(Project project)
{
project.SetState(new InPlanState());
}
public void Propose(Project project)
{
}
}
}
namespace PSManagement.Domain.Projects.Entities
{
public class ProposedState : IProjectState
{
public string StateName => "Proposed";
public void Approve(Project project)
{
project.SetState(new InPlanState());
}
public void Cancle(Project project)
{
}
public void Complete(Project project)
{
}
public void Plan(Project project)
{
}
public void Propose(Project project)
{
}
}
}
......@@ -6,12 +6,14 @@ using PSManagement.Domain.Employees.Repositories;
using PSManagement.Domain.Identity.Repositories;
using PSManagement.Domain.Projects.Builders;
using PSManagement.Domain.Projects.Repositories;
using PSManagement.Infrastructure.Persistence.Repositories.Base;
using PSManagement.Infrastructure.Persistence.Repositories.CustomerRepository;
using PSManagement.Infrastructure.Persistence.Repositories.EmployeeRepository;
using PSManagement.Infrastructure.Persistence.Repositories.ProjectRepository;
using PSManagement.Infrastructure.Persistence.Repositories.UserRepository;
using PSManagement.Infrastructure.Persistence.UoW;
using PSManagement.SharedKernel.Interfaces;
using PSManagement.SharedKernel.Repositories;
namespace PSManagement.Infrastructure.Persistence.DI
{
......@@ -23,6 +25,7 @@ namespace PSManagement.Infrastructure.Persistence.DI
services.AddDbContext<AppDbContext>(options => {
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
});
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddScoped<ICustomersRepository, CustomersReposiotry>();
services.AddScoped<IProjectsRepository, ProjectsRepository>();
......@@ -31,6 +34,7 @@ namespace PSManagement.Infrastructure.Persistence.DI
services.AddScoped<ProjectBuilder>();
services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
services.AddScoped<IUnitOfWork, UnitOfWork>();
return services;
}
......
......@@ -62,8 +62,10 @@ namespace PSManagement.Infrastructure.Persistence.EntitiesConfiguration
p.Property(e => e.Source).HasColumnName("FinicialSource");
}
);
builder.HasOne(e => e.ProjectStatus).WithMany();
builder.Property(p => p.CurrentState).HasDefaultValue("Proposed");
builder.HasMany(e => e.Attachments).WithOne().HasForeignKey(e => e.ProjectId);
builder.HasMany(e => e.FinancialSpending);
builder.HasMany(e => e.Tracks).WithOne(e => e.Project).HasForeignKey(e => e.ProjectId);
......
using Microsoft.EntityFrameworkCore.Migrations;
namespace PSManagement.Infrastructure.Persistence.Migrations
{
public partial class fixSomeEntitiesNames : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "AttachmenDescription",
table: "Attachment",
newName: "AttachmentDescription");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "AttachmentDescription",
table: "Attachment",
newName: "AttachmenDescription");
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace PSManagement.Infrastructure.Persistence.Migrations
{
public partial class fixSomeEntitiesNames1 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Attachment_Projects_ProjectId",
table: "Attachment");
migrationBuilder.AlterColumn<int>(
name: "ProjectId",
table: "Attachment",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "int",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_Attachment_Projects_ProjectId",
table: "Attachment",
column: "ProjectId",
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Attachment_Projects_ProjectId",
table: "Attachment");
migrationBuilder.AlterColumn<int>(
name: "ProjectId",
table: "Attachment",
type: "int",
nullable: true,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AddForeignKey(
name: "FK_Attachment_Projects_ProjectId",
table: "Attachment",
column: "ProjectId",
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace PSManagement.Infrastructure.Persistence.Migrations
{
public partial class AddStatePattern : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Projects_ProjectStatus_ProjectStatusId",
table: "Projects");
migrationBuilder.DropTable(
name: "ProjectStatus");
migrationBuilder.DropIndex(
name: "IX_Projects_ProjectStatusId",
table: "Projects");
migrationBuilder.DropColumn(
name: "ProjectStatusId",
table: "Projects");
migrationBuilder.AddColumn<string>(
name: "CurrentState",
table: "Projects",
type: "nvarchar(max)",
nullable: true,
defaultValue: "Proposed");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CurrentState",
table: "Projects");
migrationBuilder.AddColumn<int>(
name: "ProjectStatusId",
table: "Projects",
type: "int",
nullable: true);
migrationBuilder.CreateTable(
name: "ProjectStatus",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Code = table.Column<string>(type: "nvarchar(max)", nullable: true),
Details = table.Column<string>(type: "nvarchar(max)", nullable: true),
Name = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProjectStatus", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Projects_ProjectStatusId",
table: "Projects",
column: "ProjectStatusId");
migrationBuilder.AddForeignKey(
name: "FK_Projects_ProjectStatus_ProjectStatusId",
table: "Projects",
column: "ProjectStatusId",
principalTable: "ProjectStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}
......@@ -175,7 +175,7 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AttachmenDescription")
b.Property<string>("AttachmentDescription")
.HasColumnType("nvarchar(max)");
b.Property<string>("AttachmentName")
......@@ -184,7 +184,7 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
b.Property<string>("AttachmentUrl")
.HasColumnType("nvarchar(max)");
b.Property<int?>("ProjectId")
b.Property<int>("ProjectId")
.HasColumnType("int");
b.HasKey("Id");
......@@ -258,6 +258,11 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("CurrentState")
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(max)")
.HasDefaultValue("Proposed");
b.Property<int?>("CustomerId")
.HasColumnType("int");
......@@ -267,9 +272,6 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
b.Property<int>("ProjectManagerId")
.HasColumnType("int");
b.Property<int?>("ProjectStatusId")
.HasColumnType("int");
b.Property<int>("ProposerId")
.HasColumnType("int");
......@@ -284,8 +286,6 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
b.HasIndex("ProjectManagerId");
b.HasIndex("ProjectStatusId");
b.HasIndex("ProposerId");
b.HasIndex("TeamLeaderId");
......@@ -328,27 +328,6 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
b.ToTable("Steps");
});
modelBuilder.Entity("PSManagement.Domain.ProjectsStatus.Entites.ProjectStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Code")
.HasColumnType("nvarchar(max)");
b.Property<string>("Details")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("ProjectStatus");
});
modelBuilder.Entity("PSManagement.Domain.Reports.Entities.Answer", b =>
{
b.Property<int>("Id")
......@@ -724,7 +703,9 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
{
b.HasOne("PSManagement.Domain.Projects.Entities.Project", null)
.WithMany("Attachments")
.HasForeignKey("ProjectId");
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("PSManagement.Domain.Projects.Entities.EmployeeParticipate", b =>
......@@ -797,10 +778,6 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
.HasForeignKey("ProjectManagerId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("PSManagement.Domain.ProjectsStatus.Entites.ProjectStatus", "ProjectStatus")
.WithMany()
.HasForeignKey("ProjectStatusId");
b.HasOne("PSManagement.Domain.Customers.Entities.Customer", "Proposer")
.WithMany()
.HasForeignKey("ProposerId")
......@@ -926,8 +903,6 @@ namespace PSManagement.Infrastructure.Persistence.Migrations
b.Navigation("ProjectManager");
b.Navigation("ProjectStatus");
b.Navigation("ProposalInfo");
b.Navigation("Proposer");
......
......@@ -42,7 +42,10 @@ namespace PSManagement.Infrastructure.BackgroundServcies
var syncService = scope.ServiceProvider.GetRequiredService<ISyncEmployeesService>();
// Now you can use the repository to sync employees data
await syncService.SyncEmployees(dataProvider);
SyncResponse response = await syncService.SyncEmployees(dataProvider);
Console.WriteLine("A Data sync for Employees Occured At " +response.SyncDate +"\n The number of new Employees are "+response.SyncDataCount );
}
}
......
......@@ -4,15 +4,18 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PSManagement.Application.Contracts.Authentication;
using PSManagement.Application.Contracts.Authorization;
using PSManagement.Application.Contracts.Email;
using PSManagement.Application.Contracts.Providers;
using PSManagement.Application.Contracts.Storage;
using PSManagement.Application.Contracts.SyncData;
using PSManagement.Application.Contracts.Tokens;
using PSManagement.Domain.Identity.Repositories;
using PSManagement.Infrastructure.Authentication;
using PSManagement.Infrastructure.BackgroundServcies;
using PSManagement.Infrastructure.Services.Authentication;
using PSManagement.Infrastructure.Services.Authorization;
using PSManagement.Infrastructure.Services.Email;
using PSManagement.Infrastructure.Services.Providers;
using PSManagement.Infrastructure.Services.Storage;
using PSManagement.Infrastructure.Settings;
using PSManagement.Infrastructure.Tokens;
......@@ -35,8 +38,8 @@ namespace PSManagement.Infrastructure.DI
{
services.AddSingleton<IDateTimeProvider, DateTimeProvider>();
services.AddScoped<IEmployeesProvider, EmployeesProvider>();
services.AddScoped<IFileService,FileService>();
services.AddScoped<IEmailService,EmailService>();
return services;
}
private static IServiceCollection AddBackgroundServices(this IServiceCollection services,IConfiguration configuration)
......
using PSManagement.Application.Contracts.Email;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PSManagement.Infrastructure.Services.Email
{
public class EmailService : IEmailService
{
public Task SendAsync(string recipient, string subject, string body)
{
Console.WriteLine("a message sended to "+recipient+"|| body is "+body+"|| subject is"+subject);
return Task.CompletedTask;
}
}
}
......@@ -24,6 +24,8 @@
<ProjectReference Include="..\PSManagement.Application\PSManagement.Application.csproj" />
<ProjectReference Include="..\PSManagement.Contracts\PSManagement.Contracts.csproj" />
<ProjectReference Include="..\PSManagement.Domain\PSManagement.Domain.csproj" />
<ProjectReference Include="..\PSManagement.Infrastructure.Persistence\PSManagement.Infrastructure.Persistence.csproj" />
<ProjectReference Include="..\PSManagement.SharedKernel\PSManagement.SharedKernel.csproj" />
</ItemGroup>
</Project>
using Ardalis.Result;
using Microsoft.AspNetCore.Http;
using PSManagement.Application.Contracts.Storage;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PSManagement.Infrastructure.Services.Storage
{
public class FileService : IFileService
{
public async Task<Result<String>> StoreFile(string fileName, IFormFile file)
{
var allowedExtensions = new string[] { ".pdf", ".png" };
if (file is null)
{
return Result.Invalid(new ValidationError("File couldn't be empty."));
}
var extension = Path.GetExtension(file.FileName);
if (!allowedExtensions.Contains(extension.ToLower()))
{
return Result.Invalid(new ValidationError("File type not allowed."));
}
fileName = fileName + "." + Path.GetExtension(file.FileName);
var filePath = Path.Combine("wwwroot\\uploads", fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Result.Success(fileName);
}
}
}
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using PSManagement.Application.Contracts.Providers;
using PSManagement.Application.Contracts.SyncData;
using PSManagement.Domain.Employees.Entities;
using PSManagement.Domain.Employees.Repositories;
using PSManagement.Domain.Employees.Specification;
using PSManagement.SharedKernel.Specification;
using System;
using System.Collections.Generic;
using System.Linq;
......@@ -14,6 +8,12 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using PSManagement.Infrastructure.Settings;
using PSManagement.Application.Contracts.SyncData;
using PSManagement.Application.Contracts.Providers;
using PSManagement.Domain.Employees.Repositories;
using PSManagement.Domain.Employees.Entities;
using PSManagement.SharedKernel.Specification;
using PSManagement.Domain.Employees.Specification;
namespace PSManagement.Infrastructure.BackgroundServcies
{
......
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