shoc-backend/SeaHaven.Services/Implementation/WorkOrderBoardService.cs
Arthur Bassi ecc1db3726 feat(work-orders): add structured service notes POC and vendor fields
Persist primary/extra services, service notes, free-text POC, and
tech/vendor notes on create/patch/board/detail instead of embedding
them in description.
2026-07-17 10:37:23 -03:00

147 lines
5.7 KiB
C#

using Data.SeaHavenIndustries.Enums;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Helpers;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
public class WorkOrderBoardService : IWorkOrderBoardService
{
private readonly IWorkOrderBoardDataService _boardDataService;
public WorkOrderBoardService(IWorkOrderBoardDataService boardDataService)
{
_boardDataService = boardDataService;
}
public async Task<WorkOrderBoardResponseDto> GetBoardAsync(
WorkOrderBoardQueryDto query,
string? currentUserId)
{
var weekStart = query.WeekStart;
var weekEnd = query.WeekEnd ?? weekStart.AddDays(4);
ValidateWeekWindow(weekStart, weekEnd);
var dataQuery = new WorkOrderBoardQuery(
weekStart,
weekEnd,
query.Dispatchers,
query.MyWorkOrders,
query.Types,
query.Search,
currentUserId);
var result = await _boardDataService.GetBoardRowsAsync(dataQuery);
var utcNow = DateTime.UtcNow;
return new WorkOrderBoardResponseDto
{
WeekStart = weekStart,
WeekEnd = weekEnd,
Counts = new WorkOrderBoardCountsDto
{
Total = result.ScheduledTotalBeforeSearch,
Returned = result.ScheduledRows.Count
},
Scheduled = result.ScheduledRows.Select(r => MapRawRow(r, utcNow)).ToList(),
Unscheduled = result.UnscheduledRows.Select(r => MapRawRow(r, utcNow)).ToList()
};
}
public async Task<IReadOnlyList<DispatcherLookupDto>> GetDispatcherLookupsAsync()
{
var rows = await _boardDataService.GetDispatcherLookupsAsync();
return rows.Select(r => new DispatcherLookupDto
{
Id = r.Id,
Name = FormatName(r.FirstName, r.LastName),
Initials = r.Initials,
Color = r.Color
}).ToList();
}
public async Task<WorkOrderBoardRowDto?> GetBoardRowAsync(int workOrderId)
{
var raw = await _boardDataService.GetBoardRowByIdAsync(workOrderId);
return raw == null ? null : MapRawRow(raw, DateTime.UtcNow);
}
private static void ValidateWeekWindow(DateOnly weekStart, DateOnly weekEnd)
{
if (weekEnd < weekStart)
throw new ArgumentException("weekEnd must be on or after weekStart.");
var spanDays = weekEnd.DayNumber - weekStart.DayNumber;
if (spanDays > 6)
throw new ArgumentException("Week window cannot exceed 7 days.");
}
public static WorkOrderBoardRowDto MapRawRow(WorkOrderBoardRawRow row, DateTime utcNow)
{
var apptStart = row.ScheduledStart ?? row.DispatchApptDate;
var apptEnd = row.ScheduledEnd;
return new WorkOrderBoardRowDto
{
Id = row.Id,
WoNumber = row.InternalWONumber,
RescheduleCount = row.RescheduleCount,
CarriedOver = row.CarriedOver,
WorkOrderType = row.WorkOrderType,
IsPastDue = WorkOrderDerivedFields.IsPastDue(row.ScheduledDate, row.LifecycleStatus, utcNow),
SiteCode = row.SiteCode,
LocationName = row.LocationName,
PocName = row.PocName,
PocPhone = row.PocPhone,
PocNotes = row.PocNotes,
LifecycleStatus = row.LifecycleStatus,
LifecycleStatusLabel = WorkOrderDerivedFields.GetLifecycleStatusLabel(row.LifecycleStatus),
LegacyStatus = row.LegacyStatus,
DispatcherId = row.AssignTo,
DispatcherName = FormatName(row.DispatcherFirstName, row.DispatcherLastName),
Initials = row.Initials,
Color = row.Color,
DueDate = row.DueDate,
ScheduledDate = row.ScheduledDate,
TargetWeek = row.TargetWeek,
ScheduleWeekOnly = row.ScheduleWeekOnly,
DayGroup = WorkOrderDerivedFields.GetDayGroup(row.ScheduledDate),
VendorId = row.VendorId,
VendorName = row.VendorName,
TechName = row.TechName,
TechPhone = row.TechPhone,
ApptDate = row.DispatchApptDate ?? row.ScheduledDate,
ApptTime = WorkOrderDerivedFields.FormatApptTime(apptStart, apptEnd),
Pm = row.Trade ?? row.Problem,
ServiceNotes = row.ServiceNotes,
ExtraServices = ParseExtraServices(row.ExtraServices),
DocStatus = row.DocStatus,
FlagColor = row.FlagColor,
PrimaryDispatchId = row.PrimaryDispatchId,
RowVersion = row.RowVersion,
DispatchRowVersion = row.DispatchRowVersion
};
}
private static string FormatName(string? firstName, string? lastName)
=> $"{firstName ?? ""} {lastName ?? ""}".Trim();
private static List<string>? ParseExtraServices(string? json)
{
if (string.IsNullOrWhiteSpace(json))
return null;
try
{
var list = System.Text.Json.JsonSerializer.Deserialize<List<string>>(json);
return list is { Count: > 0 } ? list : null;
}
catch (System.Text.Json.JsonException)
{
return null;
}
}
}
}