mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 09:33:13 +00:00
Work-order requests store the requested increase in RequestedNTE; vendor portal requests store the requested NTE total. The queue Delta, the pending and approved exposure totals, the work-order uplift list, the board summary and the notification Delta now all read one definition (UpliftAmount) that honours both meanings and translates to SQL. Approving a work-order request now adds its increase to the dispatch NTE instead of replacing the NTE with the increase; vendor requests still end at their requested total. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.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<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();
|
|
}
|
|
}
|
|
}
|