using Data.SeaHavenIndustries; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Dto; using SeaHaven.DataServices.Interfaces; using SeaHaven.DataServices.Models; namespace SeaHaven.DataServices.Implementation { public class DispatchDataService : IDispatchDataService { private readonly ApplicationDbContext _context; public DispatchDataService(ApplicationDbContext context) { _context = context; } public async Task GetByIdAsync(int id) { return await _context.Dispatches.FindAsync(id); } public async Task GetByIdWithDetailsAsync(int id) { return await _context.Dispatches .Include(d => d.Vendor) .FirstOrDefaultAsync(d => d.Id == id); } public async Task GetByIdWithVendorAndWorkOrderAsync(int id) { return await _context.Dispatches .Include(dispatch => dispatch.Vendor) .Include(dispatch => dispatch.WorkOrder) .FirstOrDefaultAsync(dispatch => dispatch.Id == id); } public async Task> GetAllAsync() { return await _context.Dispatches.ToListAsync(); } public async Task> GetByVendorIdAsync(int vendorId) { return await _context.Dispatches .Where(d => d.VendorId == vendorId) .ToListAsync(); } public async Task> GetByStatusAsync(string status) { return await _context.Dispatches .Where(d => d.Status == status) .ToListAsync(); } public async Task<(IEnumerable Items, int TotalCount)> GetPagedAsync( int page, int pageSize, string? search = null, string? status = null) { var query = _context.Dispatches.AsQueryable(); if (!string.IsNullOrWhiteSpace(search)) { query = query.Where(d => d.DispatchNumber!.Contains(search)); } if (!string.IsNullOrWhiteSpace(status)) { query = query.Where(d => d.Status == status); } var totalCount = await query.CountAsync(); var items = await query .OrderByDescending(d => d.CreatedDate) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); return (items, totalCount); } public async Task AddAsync(Dispatch dispatch) { dispatch.CreatedDate = DateTime.Now; await _context.Dispatches.AddAsync(dispatch); await _context.SaveChangesAsync(); return dispatch; } public async Task UpdateAsync(Dispatch dispatch) { dispatch.LastModificationTime = DateTime.Now; _context.Dispatches.Update(dispatch); await _context.SaveChangesAsync(); } public async Task DeleteAsync(int id) { var entity = await GetByIdAsync(id); if (entity != null) { _context.Dispatches.Remove(entity); await _context.SaveChangesAsync(); } } public async Task ExistsAsync(int id) { return await _context.Dispatches.AnyAsync(d => d.Id == id); } public async Task GenerateNextDispatchNumberAsync() { return await Task.FromResult($"DISP-{DateTime.Now:yyyyMMdd}-{new Random().Next(1000, 9999)}"); } public async Task CountAsync() { return await _context.Dispatches.CountAsync(); } public async Task> GetByWorkOrderIdAsync(int workOrderId) { var dispatchIds = await _context.DispatchWorkOrders .Where(dwo => dwo.WorkOrderId == workOrderId) .Select(dwo => dwo.DispatchId) .Distinct() .ToListAsync(); var directIds = await _context.Dispatches .Where(d => d.WorkOrderId == workOrderId) .Select(d => d.Id) .ToListAsync(); var allIds = dispatchIds.Union(directIds).Distinct().ToList(); return await _context.Dispatches .Where(d => allIds.Contains(d.Id)) .Include(d => d.Vendor) .ToListAsync(); } public async Task> GetByWorkOrderIdsAsync(IEnumerable workOrderIds) { return await _context.Dispatches .Where(d => workOrderIds.Contains(d.WorkOrderId ?? 0)) .Include(d => d.Vendor) .ToListAsync(); } public async Task GenerateNextPONumberAsync() { var lastPO = await _context.Dispatches .Where(d => d.PONumber != null) .OrderByDescending(d => d.PONumber) .Select(d => d.PONumber) .FirstOrDefaultAsync(); int nextPO = 1000; if (!string.IsNullOrEmpty(lastPO) && int.TryParse(lastPO, out int lastNum)) { nextPO = lastNum + 1; } return nextPO.ToString(); } public async Task> GetChecklistItemsAsync(int dispatchId) { return await _context.DispatchChecklistItems .Where(ci => ci.DispatchId == dispatchId) .OrderBy(ci => ci.SortOrder) .ToListAsync(); } public async Task GetChecklistItemByIdAsync(int id) { return await _context.DispatchChecklistItems.FindAsync(id); } public async Task AddChecklistItemAsync(DispatchChecklistItem item) { await _context.DispatchChecklistItems.AddAsync(item); await _context.SaveChangesAsync(); } public async Task UpdateChecklistItemAsync(DispatchChecklistItem item) { _context.DispatchChecklistItems.Update(item); await _context.SaveChangesAsync(); } public async Task> GetSignoffsAsync(int dispatchId) { return await _context.DispatchSignoffs .Where(s => s.DispatchId == dispatchId) .ToListAsync(); } public async Task AddSignoffAsync(DispatchSignoff signoff) { signoff.SignedAt = DateTime.UtcNow; await _context.DispatchSignoffs.AddAsync(signoff); await _context.SaveChangesAsync(); } public async Task HasSignoffTypeAsync(int dispatchId, string signoffType) { return await _context.DispatchSignoffs .AnyAsync(s => s.DispatchId == dispatchId && s.SignoffType == signoffType); } public async Task> GetDispatchWorkOrdersAsync(int dispatchId) { return await _context.DispatchWorkOrders .Where(dwo => dwo.DispatchId == dispatchId) .Include(dwo => dwo.WorkOrder) .ToListAsync(); } public async Task AddDispatchWorkOrderAsync(DispatchWorkOrder dispatchWorkOrder) { await _context.DispatchWorkOrders.AddAsync(dispatchWorkOrder); await _context.SaveChangesAsync(); } public async Task GetListAsync(DispatchListQuery query, CancellationToken cancellationToken) { var q = _context.Dispatches .Include(d => d.Vendor) .Include(d => d.WorkOrder).ThenInclude(w => w!.Locations) .AsNoTracking() .Where(d => d.IsDeleted == null || d.IsDeleted == false); if (!string.IsNullOrWhiteSpace(query.Search)) { var s = query.Search.ToLower(); q = q.Where(d => (d.DispatchNumber ?? "").ToLower().Contains(s) || (d.PONumber ?? "").ToLower().Contains(s) || (d.Vendor != null && (d.Vendor.CompanyName ?? "").ToLower().Contains(s)) || (d.WorkOrder != null && (d.WorkOrder.InternalWONumber ?? "").ToLower().Contains(s)) || (d.WorkOrder != null && (d.WorkOrder.WorkerOrderTitle ?? "").ToLower().Contains(s)) || (d.WorkOrder != null && d.WorkOrder.Locations != null && (d.WorkOrder.Locations.Name ?? "").ToLower().Contains(s)) ); } if (!string.IsNullOrWhiteSpace(query.Status)) q = q.Where(d => d.Status == query.Status); if (query.VendorId.HasValue) q = q.Where(d => d.VendorId == query.VendorId.Value); if (query.DateFrom.HasValue) q = q.Where(d => d.DispatchedAt >= query.DateFrom.Value); if (query.DateTo.HasValue) { var end = query.DateTo.Value.Date.AddDays(1); q = q.Where(d => d.DispatchedAt < end); } var total = await q.CountAsync(cancellationToken); var dir = (query.SortDir ?? "desc").ToLowerInvariant() == "asc"; switch ((query.SortBy ?? "").ToLowerInvariant()) { case "ponumber": q = dir ? q.OrderBy(d => d.PONumber) : q.OrderByDescending(d => d.PONumber); break; case "dispatchnumber": q = dir ? q.OrderBy(d => d.DispatchNumber) : q.OrderByDescending(d => d.DispatchNumber); break; case "vendor": q = dir ? q.OrderBy(d => d.Vendor!.CompanyName) : q.OrderByDescending(d => d.Vendor!.CompanyName); break; case "nte": case "nteamount": q = dir ? q.OrderBy(d => d.NTEAmount) : q.OrderByDescending(d => d.NTEAmount); break; case "status": q = dir ? q.OrderBy(d => d.Status) : q.OrderByDescending(d => d.Status); break; case "scheduleddate": q = dir ? q.OrderBy(d => d.ScheduledDate) : q.OrderByDescending(d => d.ScheduledDate); break; case "completeddate": q = dir ? q.OrderBy(d => d.CompletedDate) : q.OrderByDescending(d => d.CompletedDate); break; default: q = dir ? q.OrderBy(d => d.DispatchedAt) : q.OrderByDescending(d => d.DispatchedAt); break; } var rows = await q .Skip((Math.Max(query.Page, 1) - 1) * query.PageSize) .Take(query.PageSize) .Select(d => new DispatchListItemDTO { Id = d.Id, DispatchNumber = d.DispatchNumber, PONumber = d.PONumber, NTEAmount = d.NTEAmount, Status = d.Status, DispatchedAt = d.DispatchedAt, ScheduledDate = d.ScheduledDate, CompletedDate = d.CompletedDate, AcknowledgedAt = d.AcknowledgedAt, VerifiedAt = d.VerifiedAt, VendorId = d.VendorId, VendorName = d.Vendor != null ? d.Vendor.CompanyName : null, WorkOrderId = d.WorkOrderId, PrimaryWOInternalNumber = d.WorkOrder != null ? d.WorkOrder.InternalWONumber : null, PrimaryWOTitle = d.WorkOrder != null ? d.WorkOrder.WorkerOrderTitle : null, LocationName = d.WorkOrder != null && d.WorkOrder.Locations != null ? d.WorkOrder.Locations.Name : null, WorkOrderCount = _context.DispatchWorkOrders.Count(dwo => dwo.DispatchId == d.Id), PendingUpliftCount = _context.DispatchUpliftRequests.Count(u => u.DispatchId == d.Id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false)) }) .ToListAsync(cancellationToken); return new DispatchListResult { Items = rows, Total = total, Page = query.Page, PageSize = query.PageSize }; } public async Task GetDetailAsync(int id, CancellationToken cancellationToken) { var dispatch = await _context.Dispatches .Include(d => d.Vendor) .Include(d => d.WorkOrder).ThenInclude(w => w!.Locations) .AsNoTracking() .FirstOrDefaultAsync(d => d.Id == id, cancellationToken); if (dispatch == null) return null; var linked = await _context.DispatchWorkOrders .AsNoTracking() .Where(dwo => dwo.DispatchId == id) .Select(dwo => new DispatchLinkedWorkOrderDTO { WorkOrderId = dwo.WorkOrderId, InternalWONumber = dwo.WorkOrder != null ? dwo.WorkOrder.InternalWONumber : null, Title = dwo.WorkOrder != null ? dwo.WorkOrder.WorkerOrderTitle : null, Priority = dwo.WorkOrder != null ? dwo.WorkOrder.Priority : null, Status = dwo.WorkOrder != null ? dwo.WorkOrder.Status : null, LocationName = dwo.WorkOrder != null && dwo.WorkOrder.Locations != null ? dwo.WorkOrder.Locations.Name : null, Trade = dwo.WorkOrder != null ? dwo.WorkOrder.Trade : null, SubTrade = dwo.WorkOrder != null ? dwo.WorkOrder.SubTrade : null, Problem = dwo.WorkOrder != null ? dwo.WorkOrder.Problem : null }) .ToListAsync(cancellationToken); var linkedWOs = linked; if (linkedWOs.Count == 0 && dispatch.WorkOrderId.HasValue) { var wo = await _context.workOrders .Include(w => w.Locations) .AsNoTracking() .FirstOrDefaultAsync(w => w.Id == dispatch.WorkOrderId.Value, cancellationToken); if (wo != null) { linkedWOs = new List { new DispatchLinkedWorkOrderDTO { WorkOrderId = wo.Id, InternalWONumber = wo.InternalWONumber, Title = wo.WorkerOrderTitle, Priority = wo.Priority, Status = wo.Status, LocationName = wo.Locations != null ? wo.Locations.Name : null, Trade = wo.Trade, SubTrade = wo.SubTrade, Problem = wo.Problem } }; } } var checklist = await _context.DispatchChecklistItems .AsNoTracking() .Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false)) .OrderBy(c => c.SortOrder) .Select(c => new DispatchChecklistDTO { Id = c.Id, ItemText = c.ItemText, IsCompleted = c.IsCompleted, CompletedBy = c.CompletedBy, CompletedAt = c.CompletedAt, SortOrder = c.SortOrder, WorkOrderId = c.WorkOrderId }) .ToListAsync(cancellationToken); var signoffs = await _context.DispatchSignoffs .AsNoTracking() .Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false)) .Select(s => new DispatchSignoffDTO { Id = s.Id, SignoffType = s.SignoffType, Name = s.Name, Signature = s.Signature, SignatureMethod = s.SignatureMethod, SignedAt = s.SignedAt }) .ToListAsync(cancellationToken); var rawComments = await (from c in _context.Comments where c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false) join u in _context.Users on c.UserId equals u.Id into users from u in users.DefaultIfEmpty() orderby c.CreatedDate select new { c.Id, c.Commenttext, c.Commenter, c.CommentType, c.RecordType, c.CreatedDate, c.UserId, UserFirstName = u != null ? u.FirstName : null, UserLastName = u != null ? u.LastName : null }) .ToListAsync(cancellationToken); var comments = rawComments.Select(c => new DispatchCommentDTO { Id = c.Id, Commenttext = c.Commenttext, Commenter = c.Commenter, CommentType = c.CommentType, RecordType = c.RecordType, CreatedDate = c.CreatedDate, UserId = c.UserId, UserName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName } .Where(s => !string.IsNullOrWhiteSpace(s))).Trim() }).ToList(); var rawUplifts = await (from u in _context.DispatchUpliftRequests where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false) join dec in _context.Users on u.DecidedByUserId equals dec.Id into decs from dec in decs.DefaultIfEmpty() orderby u.CreatedDate descending select new { u.Id, u.CurrentNTE, u.RequestedNTE, u.VendorReason, u.Status, u.RequiredTier, u.RequestedByVendorName, RequestedAt = u.CreatedDate, u.DecidedAt, u.DecisionNote, DecidedByFirstName = dec != null ? dec.FirstName : null, DecidedByLastName = dec != null ? dec.LastName : null }) .ToListAsync(cancellationToken); var upliftRequests = rawUplifts.Select(u => new DispatchUpliftRequestDTO { Id = u.Id, CurrentNTE = u.CurrentNTE, RequestedNTE = u.RequestedNTE, Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m), VendorReason = u.VendorReason, Status = u.Status, RequiredTier = u.RequiredTier, RequestedByVendorName = u.RequestedByVendorName, RequestedAt = u.RequestedAt, DecidedAt = u.DecidedAt, DecisionNote = u.DecisionNote, DecidedByName = string.Join(" ", new[] { u.DecidedByFirstName, u.DecidedByLastName } .Where(s => !string.IsNullOrWhiteSpace(s))).Trim() }).ToList(); var dispatchNumber = dispatch.DispatchNumber ?? ""; var workOrderIds = linkedWOs.Select(l => l.WorkOrderId).Where(wid => wid > 0).ToList(); var rawAudit = await (from a in _context.WorkOrderAuditLogs where workOrderIds.Contains(a.WorkOrderId) && a.FieldName != null && a.FieldName.Contains(dispatchNumber) join u in _context.Users on a.UserId equals u.Id into users from u in users.DefaultIfEmpty() orderby a.CreatedAt descending select new { a.Id, a.Action, a.FieldName, a.OldValue, a.NewValue, a.CreatedAt, a.UserId, UserFirstName = u != null ? u.FirstName : null, UserLastName = u != null ? u.LastName : null }) .ToListAsync(cancellationToken); var auditLog = rawAudit.Select(a => new DispatchAuditLogDTO { Id = a.Id, Action = a.Action, FieldName = a.FieldName, OldValue = a.OldValue, NewValue = a.NewValue, CreatedAt = a.CreatedAt, UserId = a.UserId, UserName = string.Join(" ", new[] { a.UserFirstName, a.UserLastName } .Where(s => !string.IsNullOrWhiteSpace(s))).Trim() }).ToList(); return new DispatchDetailDTO { Id = dispatch.Id, DispatchNumber = dispatch.DispatchNumber, PONumber = dispatch.PONumber, NTEAmount = dispatch.NTEAmount, Description = dispatch.Description, Status = dispatch.Status, ScheduledDate = dispatch.ScheduledDate, CompletedDate = dispatch.CompletedDate, DispatchedAt = dispatch.DispatchedAt, AcknowledgedAt = dispatch.AcknowledgedAt, VerifiedAt = dispatch.VerifiedAt, VerifiedBy = dispatch.VerifiedBy, EmailSent = dispatch.EmailSent, WorkOrderId = dispatch.WorkOrderId, Vendor = dispatch.Vendor == null ? null : new DispatchDetailVendorDTO { Id = dispatch.Vendor.Id, CompanyName = dispatch.Vendor.CompanyName, ContactName = dispatch.Vendor.ContactName, Email = dispatch.Vendor.Email, Phone = dispatch.Vendor.Phone, Address = dispatch.Vendor.Address, City = dispatch.Vendor.City, State = dispatch.Vendor.State, Zip = dispatch.Vendor.Zip, TradeSpecialties = dispatch.Vendor.TradeSpecialties }, PrimaryLocation = dispatch.WorkOrder?.Locations == null ? null : new DispatchPrimaryLocationDTO { Id = dispatch.WorkOrder.Locations.Id, Name = dispatch.WorkOrder.Locations.Name, Address1 = dispatch.WorkOrder.Locations.Address1, City = dispatch.WorkOrder.Locations.City, State = dispatch.WorkOrder.Locations.State, Zip = dispatch.WorkOrder.Locations.Zip }, WorkOrders = linkedWOs, Checklist = checklist, Signoffs = signoffs, Comments = comments, UpliftRequests = upliftRequests, AuditLog = auditLog }; } public async Task StageSignoffAsync( DispatchSignoff signoff, CancellationToken cancellationToken) { await _context.DispatchSignoffs.AddAsync(signoff, cancellationToken); } public async Task> GetVendorDispatchSummariesAsync( int vendorId, string? status, CancellationToken cancellationToken) { var query = _context.Dispatches .AsNoTracking() .Where(dispatch => dispatch.VendorId == vendorId && (dispatch.IsDeleted == null || dispatch.IsDeleted == false)); if (!string.IsNullOrWhiteSpace(status)) query = query.Where(dispatch => dispatch.Status == status); return await query .OrderByDescending(dispatch => dispatch.DispatchedAt ?? dispatch.CreatedDate) .Select(dispatch => new VendorDispatchSummaryData { Id = dispatch.Id, DispatchNumber = dispatch.DispatchNumber, PONumber = dispatch.PONumber, Status = dispatch.Status, NTEAmount = dispatch.NTEAmount, ScheduledDate = dispatch.ScheduledDate, CompletedDate = dispatch.CompletedDate, DispatchedAt = dispatch.DispatchedAt, AcknowledgedAt = dispatch.AcknowledgedAt, WorkOrderTitle = dispatch.WorkOrder != null ? dispatch.WorkOrder.WorkerOrderTitle : null, InternalWONumber = dispatch.WorkOrder != null ? dispatch.WorkOrder.InternalWONumber : null, LocationName = dispatch.WorkOrder != null && dispatch.WorkOrder.Locations != null ? dispatch.WorkOrder.Locations.Name : null, LocationCity = dispatch.WorkOrder != null && dispatch.WorkOrder.Locations != null ? dispatch.WorkOrder.Locations.City : null, LocationState = dispatch.WorkOrder != null && dispatch.WorkOrder.Locations != null ? dispatch.WorkOrder.Locations.State : null }) .ToListAsync(cancellationToken); } public async Task GetVendorDispatchDetailAsync( int id, int vendorId, CancellationToken cancellationToken) { return await _context.Dispatches .Include(dispatch => dispatch.WorkOrder) .ThenInclude(workOrder => workOrder!.Locations) .AsNoTracking() .FirstOrDefaultAsync(dispatch => dispatch.Id == id && dispatch.VendorId == vendorId && (dispatch.IsDeleted == null || dispatch.IsDeleted == false), cancellationToken); } public async Task GetVendorDispatchForMutationAsync( int id, int vendorId, CancellationToken cancellationToken) { return await _context.Dispatches .Include(dispatch => dispatch.WorkOrder) .ThenInclude(workOrder => workOrder!.Locations) .FirstOrDefaultAsync(dispatch => dispatch.Id == id && dispatch.VendorId == vendorId && (dispatch.IsDeleted == null || dispatch.IsDeleted == false), cancellationToken); } public async Task> GetPortalChecklistAsync( int dispatchId, CancellationToken cancellationToken) { return await _context.DispatchChecklistItems .AsNoTracking() .Where(item => item.DispatchId == dispatchId && (item.IsDeleted == null || item.IsDeleted == false)) .OrderBy(item => item.SortOrder) .Select(item => new PortalChecklistItemData { Id = item.Id, ItemText = item.ItemText, IsCompleted = item.IsCompleted, CompletedBy = item.CompletedBy, CompletedAt = item.CompletedAt }) .ToListAsync(cancellationToken); } public async Task> GetPortalSignoffsAsync( int dispatchId, CancellationToken cancellationToken) { return await _context.DispatchSignoffs .AsNoTracking() .Where(signoff => signoff.DispatchId == dispatchId && (signoff.IsDeleted == null || signoff.IsDeleted == false)) .Select(signoff => new PortalSignoffData { Id = signoff.Id, SignoffType = signoff.SignoffType, Name = signoff.Name, SignatureMethod = signoff.SignatureMethod, SignedAt = signoff.SignedAt }) .ToListAsync(cancellationToken); } public async Task GetChecklistItemForDispatchAsync( int itemId, int dispatchId, CancellationToken cancellationToken) { return await _context.DispatchChecklistItems .FirstOrDefaultAsync( item => item.Id == itemId && item.DispatchId == dispatchId, cancellationToken); } public async Task StageAuditLogAsync( WorkOrderAuditLog log, CancellationToken cancellationToken) { await _context.WorkOrderAuditLogs.AddAsync(log, cancellationToken); } public async Task GetDispatchDispatcherUserIdAsync( int workOrderId, CancellationToken cancellationToken) { return await _context.WorkOrderAuditLogs .AsNoTracking() .Where(audit => audit.WorkOrderId == workOrderId && audit.Action == "dispatch" && audit.UserId != null && audit.UserId != "") .OrderByDescending(audit => audit.CreatedAt) .Select(audit => audit.UserId) .FirstOrDefaultAsync(cancellationToken); } public async Task SaveChangesAsync(CancellationToken cancellationToken) { await _context.SaveChangesAsync(cancellationToken); } public async Task GetDispatchDetailAsync(int id) { return await _context.Dispatches .AsNoTracking() .Where(dispatch => dispatch.Id == id) .Select(dispatch => new DispatchDetailReadModel { Id = dispatch.Id, DispatchNumber = dispatch.DispatchNumber, PONumber = dispatch.PONumber, NTEAmount = dispatch.NTEAmount, Description = dispatch.Description, Status = dispatch.Status, ScheduledDate = dispatch.ScheduledDate, CompletedDate = dispatch.CompletedDate, DispatchedAt = dispatch.DispatchedAt, EmailSent = dispatch.EmailSent, WorkOrderId = dispatch.WorkOrderId, AcknowledgedAt = dispatch.AcknowledgedAt, VerifiedBy = dispatch.VerifiedBy, VerifiedAt = dispatch.VerifiedAt, WoNumber = dispatch.WorkOrder != null ? dispatch.WorkOrder.InternalWONumber : "", WoTitle = dispatch.WorkOrder != null ? dispatch.WorkOrder.WorkerOrderTitle : "", VendorName = dispatch.Vendor != null ? dispatch.Vendor.CompanyName : "", VendorEmail = dispatch.Vendor != null ? dispatch.Vendor.Email : "", VendorId = dispatch.VendorId, WorkOrders = _context.DispatchWorkOrders .Where(link => link.DispatchId == dispatch.Id) .Select(link => new DispatchWorkOrderReadModel { WorkOrderId = link.WorkOrderId, WoNumber = link.WorkOrder != null ? link.WorkOrder.InternalWONumber : "", WoTitle = link.WorkOrder != null ? link.WorkOrder.WorkerOrderTitle : "" }).ToList(), ChecklistItems = _context.DispatchChecklistItems .Where(item => item.DispatchId == dispatch.Id) .OrderBy(item => item.SortOrder) .Select(item => new ChecklistItemReadModel { Id = item.Id, ItemText = item.ItemText, IsCompleted = item.IsCompleted, CompletedBy = item.CompletedBy, CompletedAt = item.CompletedAt, SortOrder = item.SortOrder, WorkOrderId = item.WorkOrderId }).ToList(), Signoffs = _context.DispatchSignoffs .Where(signoff => signoff.DispatchId == dispatch.Id) .Select(signoff => new SignoffReadModel { Id = signoff.Id, SignoffType = signoff.SignoffType, Name = signoff.Name, Signature = signoff.Signature, SignatureMethod = signoff.SignatureMethod, SignedAt = signoff.SignedAt }).ToList(), Comments = _context.Comments .Where(comment => comment.DispatchId == dispatch.Id) .OrderBy(comment => comment.CreatedDate) .Select(comment => new DispatchCommentReadModel { Id = comment.Id, CreatedDate = comment.CreatedDate, Commenttext = comment.Commenttext, Commenter = comment.Commenter, CommentType = comment.CommentType, UserName = comment.ApplicationUser != null ? comment.ApplicationUser.FirstName + " " + comment.ApplicationUser.LastName : "" }).ToList() }) .FirstOrDefaultAsync(); } } }