using Data.SeaHavenIndustries; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Helpers; using SeaHaven.DataServices.Interfaces; namespace SeaHaven.DataServices.Implementation { public class WorkOrderBoardDataService : IWorkOrderBoardDataService { private readonly ApplicationDbContext _context; public WorkOrderBoardDataService(ApplicationDbContext context) { _context = context; } public async Task GetBoardRowsAsync( WorkOrderBoardQuery query, CancellationToken cancellationToken = default) { var weekStartDate = query.WeekStart.ToDateTime(TimeOnly.MinValue); var weekEndExclusive = query.WeekEnd.AddDays(1).ToDateTime(TimeOnly.MinValue); var baseQuery = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking()); if (query.AccountId.HasValue) baseQuery = WorkOrderBoardQueryFilters.ApplyAccountScope(baseQuery, query.AccountId.Value); baseQuery = WorkOrderBoardQueryFilters.ApplyDispatcherFilter( baseQuery, query.Dispatchers, query.MyWorkOrders, query.CurrentUserId); baseQuery = WorkOrderBoardQueryFilters.ApplyTypeFilter(baseQuery, query.Types, query.Overdue); var scheduledBase = baseQuery.Where(w => (w.ScheduledDate != null && w.ScheduledDate >= weekStartDate && w.ScheduledDate < weekEndExclusive) || (w.ScheduleWeekOnly == true && w.TargetWeek == query.WeekStart)); var scheduledTotal = await scheduledBase.CountAsync(cancellationToken); var normalizedSearch = WorkOrderBoardSearchFilter.NormalizeSearch(query.Search); if (normalizedSearch != null) scheduledBase = WorkOrderBoardSearchFilter.Apply(scheduledBase, normalizedSearch); var scheduledRows = await WorkOrderBoardProjection.ProjectRowsAsync( _context, scheduledBase.OrderBy(w => w.ScheduledDate).ThenBy(w => w.InternalWONumber), isUnscheduled: false, cancellationToken); // Weekly board is scheduled-in-week only. Undated rows belong on GET /board/search. return new WorkOrderBoardQueryResult( scheduledRows, Array.Empty(), scheduledTotal); } public async Task> GetDispatcherLookupsAsync() { return await _context.Users .AsNoTracking() .Where(u => u.IsDeleted != true) .OrderBy(u => u.FirstName) .ThenBy(u => u.LastName) .Select(u => new DispatcherLookupRow( u.Id, u.FirstName ?? "", u.LastName, u.Initials, u.Color)) .ToListAsync(); } public async Task GetBoardRowByIdAsync( int workOrderId, int? accountId = null, CancellationToken cancellationToken = default) { var query = WorkOrderBoardQueryFilters.ApplyBaseScope( _context.workOrders.AsNoTracking().Where(w => w.Id == workOrderId)); if (accountId.HasValue) query = WorkOrderBoardQueryFilters.ApplyAccountScope(query, accountId.Value); var rows = await WorkOrderBoardProjection.ProjectRowsAsync( _context, query, isUnscheduled: false, cancellationToken); return rows.FirstOrDefault(); } public async Task InternalWoNumberExistsAsync(string normalizedWoNumber, int excludeWorkOrderId) { return await _context.workOrders .AsNoTracking() .AnyAsync(w => w.Id != excludeWorkOrderId && w.istemplate != true && (w.IsDeleted != true || w.IsDeleted == null) && w.InternalWONumber == normalizedWoNumber); } } }