shoc-backend/SeaHaven.Services/Validation/WorkOrderBoardCreateValidation.cs
Arthur Bassi 61923b2a7d feat(work-orders): stamp board create account from location
Org-wide create no longer depends on customer name. POST /workorders/board requires locationId and stamps WorkOrder.AccountId from Location.AccountId.
2026-08-26 09:12:48 -03:00

125 lines
4.7 KiB
C#

using FluentValidation;
using Data.SeaHavenIndustries.Enums;
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.")
.Must(t => t != WorkOrderType.AddOn)
.WithMessage("workOrderType AddOn is not allowed on create; use isAddOn with a real type.");
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)
.NotNull().WithMessage("locationId is required.")
.GreaterThan(0).WithMessage("locationId is required.");
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(HasNameAndPhoneWhenPresent)
.WithMessage("Each additional contact must include a name and 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);
}
private static bool HasNameAndPhoneWhenPresent(WorkOrderAdditionalContactDto contact)
{
var hasName = !string.IsNullOrWhiteSpace(contact.Name);
var hasPhone = !string.IsNullOrWhiteSpace(contact.Phone);
if (!hasName && !hasPhone)
return true;
return hasName && hasPhone;
}
}
}