In progress
This commit is contained in:
@@ -88,4 +88,17 @@ public class Epic : Entity
|
||||
Priority = newPriority;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void RemoveStory(StoryId storyId)
|
||||
{
|
||||
var story = _stories.FirstOrDefault(s => s.Id == storyId);
|
||||
if (story == null)
|
||||
throw new DomainException($"Story with ID {storyId.Value} not found in epic");
|
||||
|
||||
if (story.Tasks.Any())
|
||||
throw new DomainException($"Cannot delete story with ID {storyId.Value}. The story has {story.Tasks.Count} associated task(s). Please delete or reassign the tasks first.");
|
||||
|
||||
_stories.Remove(story);
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,16 @@ public class Story : Entity
|
||||
return task;
|
||||
}
|
||||
|
||||
public void RemoveTask(TaskId taskId)
|
||||
{
|
||||
var task = _tasks.FirstOrDefault(t => t.Id == taskId);
|
||||
if (task == null)
|
||||
throw new DomainException($"Task with ID {taskId.Value} not found in story");
|
||||
|
||||
_tasks.Remove(task);
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdateDetails(string title, string description)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
@@ -109,4 +119,10 @@ public class Story : Entity
|
||||
ActualHours = hours;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdatePriority(TaskPriority newPriority)
|
||||
{
|
||||
Priority = newPriority;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
using ColaFlow.Shared.Kernel.Common;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
|
||||
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Events;
|
||||
namespace ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// Story Entity (part of Project aggregate)
|
||||
/// </summary>
|
||||
public class Story : Entity
|
||||
{
|
||||
public new StoryId Id { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public EpicId EpicId { get; private set; }
|
||||
public WorkItemStatus Status { get; private set; }
|
||||
public TaskPriority Priority { get; private set; }
|
||||
public decimal? EstimatedHours { get; private set; }
|
||||
public decimal? ActualHours { get; private set; }
|
||||
public UserId? AssigneeId { get; private set; }
|
||||
|
||||
private readonly List<WorkTask> _tasks = new();
|
||||
public IReadOnlyCollection<WorkTask> Tasks => _tasks.AsReadOnly();
|
||||
|
||||
public DateTime CreatedAt { get; private set; }
|
||||
public UserId CreatedBy { get; private set; }
|
||||
public DateTime? UpdatedAt { get; private set; }
|
||||
|
||||
// EF Core constructor
|
||||
private Story()
|
||||
{
|
||||
Id = null!;
|
||||
Title = null!;
|
||||
Description = null!;
|
||||
EpicId = null!;
|
||||
Status = null!;
|
||||
Priority = null!;
|
||||
CreatedBy = null!;
|
||||
}
|
||||
|
||||
public static Story Create(string title, string description, EpicId epicId, TaskPriority priority, UserId createdBy)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
throw new DomainException("Story title cannot be empty");
|
||||
|
||||
if (title.Length > 200)
|
||||
throw new DomainException("Story title cannot exceed 200 characters");
|
||||
|
||||
return new Story
|
||||
{
|
||||
Id = StoryId.Create(),
|
||||
Title = title,
|
||||
Description = description ?? string.Empty,
|
||||
EpicId = epicId,
|
||||
Status = WorkItemStatus.ToDo,
|
||||
Priority = priority,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = createdBy
|
||||
};
|
||||
}
|
||||
|
||||
public WorkTask CreateTask(string title, string description, TaskPriority priority, UserId createdBy)
|
||||
{
|
||||
var task = WorkTask.Create(title, description, this.Id, priority, createdBy);
|
||||
_tasks.Add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public void UpdateDetails(string title, string description)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
throw new DomainException("Story title cannot be empty");
|
||||
|
||||
if (title.Length > 200)
|
||||
throw new DomainException("Story title cannot exceed 200 characters");
|
||||
|
||||
Title = title;
|
||||
Description = description ?? string.Empty;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdateStatus(WorkItemStatus newStatus)
|
||||
{
|
||||
Status = newStatus;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void AssignTo(UserId assigneeId)
|
||||
{
|
||||
AssigneeId = assigneeId;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdateEstimate(decimal hours)
|
||||
{
|
||||
if (hours < 0)
|
||||
throw new DomainException("Estimated hours cannot be negative");
|
||||
|
||||
EstimatedHours = hours;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void LogActualHours(decimal hours)
|
||||
{
|
||||
if (hours < 0)
|
||||
throw new DomainException("Actual hours cannot be negative");
|
||||
|
||||
ActualHours = hours;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user