mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 09:33:13 +00:00
349 lines
15 KiB
C#
349 lines
15 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.Services.Interfaces;
|
|
using SeaHaven.Services.DTOs;
|
|
|
|
namespace Api.SeaHavenIndustries.Controllers
|
|
{
|
|
[Authorize]
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
[Route("api/dispatches")]
|
|
public class DispatchController : Controller
|
|
{
|
|
private readonly IDispatchService _dispatchService;
|
|
private readonly ApplicationDbContext _db; // Keep for complex queries
|
|
|
|
public DispatchController(IDispatchService dispatchService, ApplicationDbContext db)
|
|
{
|
|
_dispatchService = dispatchService;
|
|
_db = db;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> List(
|
|
[FromQuery] string? search = null,
|
|
[FromQuery] string? status = null,
|
|
[FromQuery] int? vendorId = null,
|
|
[FromQuery] DateTime? dateFrom = null,
|
|
[FromQuery] DateTime? dateTo = null,
|
|
[FromQuery] string? sortBy = "dispatchedAt",
|
|
[FromQuery] string? sortDir = "desc",
|
|
[FromQuery] int page = 1,
|
|
[FromQuery] int pageSize = 25)
|
|
{
|
|
var q = _db.Dispatches
|
|
.Include(d => d.Vendor)
|
|
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
|
.Where(d => d.IsDeleted == null || d.IsDeleted == false);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
var s = 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(status))
|
|
q = q.Where(d => d.Status == status);
|
|
|
|
if (vendorId.HasValue)
|
|
q = q.Where(d => d.VendorId == vendorId.Value);
|
|
|
|
if (dateFrom.HasValue)
|
|
q = q.Where(d => d.DispatchedAt >= dateFrom.Value);
|
|
|
|
if (dateTo.HasValue)
|
|
{
|
|
var end = dateTo.Value.Date.AddDays(1);
|
|
q = q.Where(d => d.DispatchedAt < end);
|
|
}
|
|
|
|
var total = await q.CountAsync();
|
|
|
|
var dir = (sortDir ?? "desc").ToLowerInvariant() == "asc";
|
|
switch ((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(page, 1) - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.Select(d => new
|
|
{
|
|
d.Id,
|
|
d.DispatchNumber,
|
|
d.PONumber,
|
|
d.NTEAmount,
|
|
d.Status,
|
|
d.DispatchedAt,
|
|
d.ScheduledDate,
|
|
d.CompletedDate,
|
|
d.AcknowledgedAt,
|
|
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 = _db.DispatchWorkOrders.Count(dwo => dwo.DispatchId == d.Id),
|
|
PendingUpliftCount = _db.DispatchUpliftRequests.Count(u => u.DispatchId == d.Id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false))
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Ok(new DataResponse
|
|
{
|
|
Status = "Success",
|
|
Data = new { total, page, pageSize, items = rows }
|
|
});
|
|
}
|
|
|
|
[HttpGet("{id:int}")]
|
|
public async Task<IActionResult> GetById(int id)
|
|
{
|
|
var dispatch = await _db.Dispatches
|
|
.Include(d => d.Vendor)
|
|
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
|
.FirstOrDefaultAsync(d => d.Id == id);
|
|
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
|
|
|
var linkedWOs = await _db.DispatchWorkOrders
|
|
.Where(dwo => dwo.DispatchId == id)
|
|
.Select(dwo => new
|
|
{
|
|
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();
|
|
|
|
if (linkedWOs.Count == 0 && dispatch.WorkOrderId.HasValue)
|
|
{
|
|
var wo = await _db.workOrders
|
|
.Include(w => w.Locations)
|
|
.FirstOrDefaultAsync(w => w.Id == dispatch.WorkOrderId.Value);
|
|
if (wo != null)
|
|
{
|
|
linkedWOs = new[] { new
|
|
{
|
|
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
|
|
}}.ToList();
|
|
}
|
|
}
|
|
|
|
var checklist = await _db.DispatchChecklistItems
|
|
.Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false))
|
|
.OrderBy(c => c.SortOrder)
|
|
.Select(c => new { c.Id, c.ItemText, c.IsCompleted, c.CompletedBy, c.CompletedAt, c.SortOrder, c.WorkOrderId })
|
|
.ToListAsync();
|
|
|
|
var signoffs = await _db.DispatchSignoffs
|
|
.Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false))
|
|
.Select(s => new { s.Id, s.SignoffType, s.Name, s.Signature, s.SignatureMethod, s.SignedAt })
|
|
.ToListAsync();
|
|
|
|
var rawComments = await (from c in _db.Comments
|
|
where c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false)
|
|
join u in _db.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();
|
|
|
|
var comments = rawComments.Select(c => new
|
|
{
|
|
c.Id,
|
|
c.Commenttext,
|
|
c.Commenter,
|
|
c.CommentType,
|
|
c.RecordType,
|
|
c.CreatedDate,
|
|
UserName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName }
|
|
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
|
}).ToList();
|
|
|
|
var rawUplifts = await (from u in _db.DispatchUpliftRequests
|
|
where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false)
|
|
join dec in _db.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();
|
|
|
|
var upliftRequests = rawUplifts.Select(u => new
|
|
{
|
|
u.Id,
|
|
u.CurrentNTE,
|
|
u.RequestedNTE,
|
|
Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m),
|
|
u.VendorReason,
|
|
u.Status,
|
|
u.RequiredTier,
|
|
u.RequestedByVendorName,
|
|
u.RequestedAt,
|
|
u.DecidedAt,
|
|
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 _db.WorkOrderAuditLogs
|
|
where workOrderIds.Contains(a.WorkOrderId)
|
|
&& a.FieldName != null
|
|
&& a.FieldName.Contains(dispatchNumber)
|
|
join u in _db.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();
|
|
|
|
var auditLog = rawAudit.Select(a => new
|
|
{
|
|
Id = a.Id,
|
|
Action = a.Action,
|
|
FieldName = a.FieldName,
|
|
OldValue = a.OldValue,
|
|
NewValue = a.NewValue,
|
|
CreatedAt = a.CreatedAt,
|
|
UserName = string.Join(" ", new[] { a.UserFirstName, a.UserLastName }
|
|
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
|
}).ToList();
|
|
|
|
return Ok(new DataResponse
|
|
{
|
|
Status = "Success",
|
|
Data = new
|
|
{
|
|
dispatch.Id,
|
|
dispatch.DispatchNumber,
|
|
dispatch.PONumber,
|
|
dispatch.NTEAmount,
|
|
dispatch.Description,
|
|
dispatch.Status,
|
|
dispatch.ScheduledDate,
|
|
dispatch.CompletedDate,
|
|
dispatch.DispatchedAt,
|
|
dispatch.AcknowledgedAt,
|
|
dispatch.VerifiedAt,
|
|
dispatch.VerifiedBy,
|
|
dispatch.EmailSent,
|
|
dispatch.WorkOrderId,
|
|
Vendor = dispatch.Vendor == null ? null : new
|
|
{
|
|
dispatch.Vendor.Id,
|
|
dispatch.Vendor.CompanyName,
|
|
dispatch.Vendor.ContactName,
|
|
dispatch.Vendor.Email,
|
|
dispatch.Vendor.Phone,
|
|
dispatch.Vendor.Address,
|
|
dispatch.Vendor.City,
|
|
dispatch.Vendor.State,
|
|
dispatch.Vendor.Zip,
|
|
dispatch.Vendor.TradeSpecialties
|
|
},
|
|
PrimaryLocation = dispatch.WorkOrder?.Locations == null ? null : new
|
|
{
|
|
dispatch.WorkOrder.Locations.Id,
|
|
dispatch.WorkOrder.Locations.Name,
|
|
dispatch.WorkOrder.Locations.Address1,
|
|
dispatch.WorkOrder.Locations.City,
|
|
dispatch.WorkOrder.Locations.State,
|
|
dispatch.WorkOrder.Locations.Zip
|
|
},
|
|
WorkOrders = linkedWOs,
|
|
Checklist = checklist,
|
|
Signoffs = signoffs,
|
|
Comments = comments,
|
|
UpliftRequests = upliftRequests,
|
|
AuditLog = auditLog
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|