shoc-backend/SeaHaven.Services/Validation/WorkOrderBoardCreateValidation.cs
Arthur Bassi 36ef0b00f5 feat(work-orders): persist additionalContacts on create, board GET and PATCH (SH-218)
Add JSON column, DTO/mapper, create + PATCH field, board projection, FluentValidation,
and regression tests for additional POC contacts round-trip.
2026-08-13 16:11:36 -03:00

112 lines
4.1 KiB
C#

using FluentValidation;
using SeaHaven.Services.DTOs;
namespace SeaHaven.Services.Validation
{
public interface IWorkOrderBoardCreateValidation : IValidator<WorkOrderBoardCreateRequestDto>
{
}
public class WorkOrderBoardCreateValidation : AbstractValidator<WorkOrderBoardCreateRequestDto>, IWorkOrderBoardCreateValidation
{
public WorkOrderBoardCreateValidation()
{
RuleFor(x => x.WorkOrderType)
.IsInEnum().WithMessage("workOrderType is required and must be a valid value.");
RuleFor(x => x.SiteCode)
.NotEmpty().WithMessage("siteCode is required.")
.MaximumLength(32);
RuleFor(x => x.TargetWeek)
.NotNull().WithMessage("targetWeek is required when scheduleWeekOnly is true.")
.When(x => x.ScheduleWeekOnly == true);
RuleFor(x => x.Description)
.MaximumLength(2000)
.When(x => !string.IsNullOrEmpty(x.Description));
RuleFor(x => x.Trade)
.MaximumLength(128)
.When(x => !string.IsNullOrEmpty(x.Trade));
RuleFor(x => x.PrimaryService)
.MaximumLength(128)
.When(x => !string.IsNullOrEmpty(x.PrimaryService));
RuleFor(x => x.ServiceNotes)
.MaximumLength(4000)
.When(x => !string.IsNullOrEmpty(x.ServiceNotes));
RuleFor(x => x.PocName)
.MaximumLength(200)
.When(x => !string.IsNullOrEmpty(x.PocName));
RuleFor(x => x.PocPhone)
.MaximumLength(50)
.When(x => !string.IsNullOrEmpty(x.PocPhone));
RuleFor(x => x.PocNotes)
.MaximumLength(2000)
.When(x => !string.IsNullOrEmpty(x.PocNotes));
RuleFor(x => x.TechPhone)
.MaximumLength(50)
.When(x => !string.IsNullOrEmpty(x.TechPhone));
RuleFor(x => x.VendorNotes)
.MaximumLength(2000)
.When(x => !string.IsNullOrEmpty(x.VendorNotes));
RuleFor(x => x.LocationId)
.GreaterThan(0)
.When(x => x.LocationId.HasValue);
RuleFor(x => x.VendorId)
.GreaterThan(0)
.When(x => x.VendorId.HasValue);
RuleFor(x => x.PocContactId)
.GreaterThan(0)
.When(x => x.PocContactId.HasValue);
RuleFor(x => x)
.Must(HasPrimaryWhenExtras)
.WithMessage("primaryService (or trade) is required when extraServices is provided.")
.WithName("extraServices");
RuleForEach(x => x.ExtraServices)
.MaximumLength(128)
.When(x => x.ExtraServices != null);
RuleForEach(x => x.AdditionalContacts)
.ChildRules(contact =>
{
contact.RuleFor(c => c.Name)
.MaximumLength(200)
.When(c => !string.IsNullOrEmpty(c.Name));
contact.RuleFor(c => c.Phone)
.MaximumLength(50)
.When(c => !string.IsNullOrEmpty(c.Phone));
contact.RuleFor(c => c.Notes)
.MaximumLength(2000)
.When(c => !string.IsNullOrEmpty(c.Notes));
contact.RuleFor(c => c)
.Must(c => !string.IsNullOrWhiteSpace(c.Name) || !string.IsNullOrWhiteSpace(c.Phone))
.WithMessage("Each additional contact must include a name or phone.");
})
.When(x => x.AdditionalContacts != null);
}
private static bool HasPrimaryWhenExtras(WorkOrderBoardCreateRequestDto request)
{
var hasExtras = request.ExtraServices != null
&& request.ExtraServices.Any(s => !string.IsNullOrWhiteSpace(s));
if (!hasExtras)
return true;
return !string.IsNullOrWhiteSpace(request.PrimaryService)
|| !string.IsNullOrWhiteSpace(request.Trade);
}
}
}