mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 10:43:13 +00:00
WorkOrderDataService.AddAsync overwrote the UTC CreatedDate set by WorkOrderService with local server time, offsetting the SLA response clock on any host not running in UTC. Data services now stamp CreatedDate, LastModificationTime and DeletionTime with DateTime.UtcNow, and a work order keeps the creation time its caller set.
805 lines
35 KiB
C#
805 lines
35 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Dto;
|
|
using SeaHaven.DataServices.Helpers;
|
|
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<Dispatch?> GetByIdAsync(int id)
|
|
{
|
|
return await _context.Dispatches.FindAsync(id);
|
|
}
|
|
|
|
public async Task<Dispatch?> GetByIdWithDetailsAsync(int id)
|
|
{
|
|
return await _context.Dispatches
|
|
.Include(d => d.Vendor)
|
|
.FirstOrDefaultAsync(d => d.Id == id);
|
|
}
|
|
|
|
public async Task<Dispatch?> GetByIdWithVendorAndWorkOrderAsync(int id)
|
|
{
|
|
return await _context.Dispatches
|
|
.Include(dispatch => dispatch.Vendor)
|
|
.Include(dispatch => dispatch.WorkOrder)
|
|
.FirstOrDefaultAsync(dispatch => dispatch.Id == id);
|
|
}
|
|
|
|
public async Task<IEnumerable<Dispatch>> GetAllAsync()
|
|
{
|
|
return await _context.Dispatches.ToListAsync();
|
|
}
|
|
|
|
public async Task<IEnumerable<Dispatch>> GetByVendorIdAsync(int vendorId)
|
|
{
|
|
return await _context.Dispatches
|
|
.Where(d => d.VendorId == vendorId)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<IEnumerable<Dispatch>> GetByStatusAsync(string status)
|
|
{
|
|
return await _context.Dispatches
|
|
.Where(d => d.Status == status)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<(IEnumerable<Dispatch> 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<Dispatch> AddAsync(Dispatch dispatch)
|
|
{
|
|
dispatch.CreatedDate = DateTime.UtcNow;
|
|
await _context.Dispatches.AddAsync(dispatch);
|
|
await _context.SaveChangesAsync();
|
|
return dispatch;
|
|
}
|
|
|
|
public async Task UpdateAsync(Dispatch dispatch)
|
|
{
|
|
dispatch.LastModificationTime = DateTime.UtcNow;
|
|
_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<bool> ExistsAsync(int id)
|
|
{
|
|
return await _context.Dispatches.AnyAsync(d => d.Id == id);
|
|
}
|
|
|
|
public async Task<string> GenerateNextDispatchNumberAsync()
|
|
{
|
|
return await Task.FromResult($"DISP-{DateTime.Now:yyyyMMdd}-{new Random().Next(1000, 9999)}");
|
|
}
|
|
|
|
public async Task<int> CountAsync()
|
|
{
|
|
return await _context.Dispatches.CountAsync();
|
|
}
|
|
|
|
public async Task<IEnumerable<Dispatch>> GetByWorkOrderIdAsync(
|
|
int workOrderId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var dispatchIds = await _context.DispatchWorkOrders
|
|
.Where(dwo => dwo.WorkOrderId == workOrderId)
|
|
.Select(dwo => dwo.DispatchId)
|
|
.Distinct()
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var directIds = await _context.Dispatches
|
|
.Where(d => d.WorkOrderId == workOrderId)
|
|
.Select(d => d.Id)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var allIds = dispatchIds.Union(directIds).Distinct().ToList();
|
|
|
|
return await _context.Dispatches
|
|
.Where(d => allIds.Contains(d.Id))
|
|
.Include(d => d.Vendor)
|
|
.ToListAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task<IEnumerable<Dispatch>> GetByWorkOrderIdsAsync(IEnumerable<int> workOrderIds)
|
|
{
|
|
return await _context.Dispatches
|
|
.Where(d => workOrderIds.Contains(d.WorkOrderId ?? 0))
|
|
.Include(d => d.Vendor)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<string> 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<IEnumerable<DispatchChecklistItem>> GetChecklistItemsAsync(int dispatchId)
|
|
{
|
|
return await _context.DispatchChecklistItems
|
|
.Where(ci => ci.DispatchId == dispatchId)
|
|
.OrderBy(ci => ci.SortOrder)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<DispatchChecklistItem?> 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<IEnumerable<DispatchSignoff>> 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<bool> HasSignoffTypeAsync(int dispatchId, string signoffType)
|
|
{
|
|
return await _context.DispatchSignoffs
|
|
.AnyAsync(s => s.DispatchId == dispatchId && s.SignoffType == signoffType);
|
|
}
|
|
|
|
public async Task<IEnumerable<DispatchWorkOrder>> 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<DispatchListResult> 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<DispatchDetailDTO?> 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<DispatchLinkedWorkOrderDTO>
|
|
{
|
|
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.createdby,
|
|
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 = UpliftAmount.Of(u.createdby, u.RequestedNTE, u.CurrentNTE),
|
|
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<IReadOnlyList<VendorDispatchSummaryData>> 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<Dispatch?> 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<Dispatch?> 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<IReadOnlyList<PortalChecklistItemData>> 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<IReadOnlyList<PortalSignoffData>> 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<DispatchChecklistItem?> 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<string?> 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<DispatchDetailReadModel?> 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();
|
|
}
|
|
}
|
|
}
|