mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
Compare commits
2 commits
3cb1e3e3f3
...
e0e45d5ed3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0e45d5ed3 | ||
|
|
60e006e8d7 |
6 changed files with 77 additions and 7 deletions
|
|
@ -86,7 +86,8 @@ public sealed class UpliftQueueReadTests
|
|||
var vendor = new Vendor { CompanyName = vendorName, IsActive = true };
|
||||
var workOrder = new WorkOrder
|
||||
{
|
||||
WorkerOrderNumber = number,
|
||||
InternalWONumber = number,
|
||||
WorkerOrderNumber = $"legacy-{number}",
|
||||
SiteCode = site,
|
||||
Service = service,
|
||||
WorkerOrderTitle = "Repair"
|
||||
|
|
@ -407,6 +408,36 @@ public sealed class UpliftQueueReadTests
|
|||
itemB.WorkOrderApprovedExposureTotal.Should().Be(75m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Exposure_UsesGrantedAmountForVendorPortalAndRequestedAmountForWorkOrderRequests()
|
||||
{
|
||||
using var context = NewContext();
|
||||
var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-ORIGIN", "SITE-A", "HVAC");
|
||||
var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-ORIGIN");
|
||||
context.DispatchUpliftRequests.AddRange(
|
||||
Request(
|
||||
dispatch,
|
||||
"Approved",
|
||||
new DateTime(2026, 3, 1),
|
||||
decided: new DateTime(2026, 3, 2),
|
||||
requested: 400m,
|
||||
currentNte: 100m),
|
||||
Request(
|
||||
dispatch,
|
||||
"Approved",
|
||||
new DateTime(2026, 3, 3),
|
||||
decided: new DateTime(2026, 3, 4),
|
||||
requested: 250m,
|
||||
createdBy: "internal-user",
|
||||
currentNte: 100m));
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var exposure = await new UpliftDataService(context)
|
||||
.GetApprovedExposureForWorkOrdersAsync(new[] { workOrder.Id }, CancellationToken.None);
|
||||
|
||||
exposure.Should().ContainSingle().Which.AdminApprovedTotal.Should().Be(550m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Exposure_IncludesLinkedDispatchesViaServerDerivedWorkOrderLinks()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ namespace Data.SeaHavenIndustries
|
|||
public string? RequestedByVendorName { get; set; }
|
||||
public string? RequestedByFirstName { get; set; }
|
||||
public string? RequestedByLastName { get; set; }
|
||||
public string? DecidedByFirstName { get; set; }
|
||||
public string? DecidedByLastName { get; set; }
|
||||
public decimal? CurrentNTE { get; set; }
|
||||
public decimal RequestedNTE { get; set; }
|
||||
public string? VendorReason { get; set; }
|
||||
|
|
@ -104,6 +106,8 @@ namespace Data.SeaHavenIndustries
|
|||
public DateTime? ExpiresAt { get; set; }
|
||||
public string? NotificationStatus { get; set; }
|
||||
public string? NotificationError { get; set; }
|
||||
public int AttachmentCount { get; set; }
|
||||
public bool WorkOrderClosed { get; set; }
|
||||
}
|
||||
|
||||
public class UpliftForWorkOrderData : UpliftForDispatchData
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
|
|
@ -28,9 +29,11 @@ namespace SeaHaven.DataServices.Implementation
|
|||
from wo in workOrders.DefaultIfEmpty()
|
||||
join reqUser in _context.Users on u.createdby equals reqUser.Id into requestUsers
|
||||
from reqUser in requestUsers.DefaultIfEmpty()
|
||||
join decUser in _context.Users on u.DecidedByUserId equals decUser.Id into decisionUsers
|
||||
from decUser in decisionUsers.DefaultIfEmpty()
|
||||
where (u.IsDeleted == null || u.IsDeleted == false)
|
||||
&& (d.IsDeleted == null || d.IsDeleted == false)
|
||||
select new { u, d, v, ev, wo, reqUser };
|
||||
select new { u, d, v, ev, wo, reqUser, decUser };
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
query = query.Where(x => x.u.Status == status);
|
||||
|
|
@ -61,12 +64,14 @@ namespace SeaHaven.DataServices.Implementation
|
|||
PONumber = x.d.PONumber,
|
||||
WorkOrderId = x.d.WorkOrderId,
|
||||
VendorCompanyName = x.v != null ? x.v.CompanyName : x.u.RequestedByVendorName,
|
||||
WorkOrderNumber = x.wo != null ? x.wo.WorkerOrderNumber : null,
|
||||
WorkOrderNumber = x.wo != null ? x.wo.InternalWONumber : null,
|
||||
WorkOrderSiteCode = x.wo != null ? x.wo.SiteCode : null,
|
||||
WorkOrderService = x.wo != null ? x.wo.Service : null,
|
||||
RequestedByVendorName = x.u.RequestedByVendorName,
|
||||
RequestedByFirstName = x.reqUser != null ? x.reqUser.FirstName : null,
|
||||
RequestedByLastName = x.reqUser != null ? x.reqUser.LastName : null,
|
||||
DecidedByFirstName = x.decUser != null ? x.decUser.FirstName : null,
|
||||
DecidedByLastName = x.decUser != null ? x.decUser.LastName : null,
|
||||
CurrentNTE = x.u.CurrentNTE,
|
||||
RequestedNTE = x.u.RequestedNTE,
|
||||
VendorReason = x.u.VendorReason,
|
||||
|
|
@ -81,13 +86,29 @@ namespace SeaHaven.DataServices.Implementation
|
|||
EvidenceSizeBytes = x.ev != null ? x.ev.SizeBytes : null,
|
||||
ExpiresAt = x.u.ExpiresAt,
|
||||
NotificationStatus = x.u.NotificationStatus,
|
||||
NotificationError = x.u.NotificationError
|
||||
NotificationError = x.u.NotificationError,
|
||||
AttachmentCount = _context.VendorCompletionDocuments.Count(document =>
|
||||
document.DispatchId == x.d.Id
|
||||
&& (document.IsDeleted == null || document.IsDeleted == false)),
|
||||
WorkOrderClosed = x.wo != null
|
||||
&& (x.wo.LifecycleStatus == LifecycleStatus.Completed
|
||||
|| x.wo.LifecycleStatus == LifecycleStatus.Canceled)
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (total, items);
|
||||
}
|
||||
|
||||
public Task<decimal> GetPendingExposureAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _context.DispatchUpliftRequests
|
||||
.Where(u => (u.IsDeleted == null || u.IsDeleted == false)
|
||||
&& u.Status == "Pending")
|
||||
.SumAsync(u => u.createdby == null
|
||||
? u.RequestedNTE - (u.CurrentNTE ?? 0m)
|
||||
: u.RequestedNTE, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<UpliftForDispatchData>> GetForDispatchAsync(int dispatchId, CancellationToken cancellationToken)
|
||||
{
|
||||
return await (from u in _context.DispatchUpliftRequests
|
||||
|
|
@ -317,9 +338,13 @@ namespace SeaHaven.DataServices.Implementation
|
|||
{
|
||||
DispatchId = g.Key,
|
||||
AutoApproved = g.Where(x => x.Status == "NoApprovalRequired")
|
||||
.Sum(x => (decimal?)x.RequestedNTE),
|
||||
.Sum(x => (decimal?)(x.createdby == null
|
||||
? x.RequestedNTE - (x.CurrentNTE ?? 0m)
|
||||
: x.RequestedNTE)),
|
||||
AdminApproved = g.Where(x => x.Status == "Approved")
|
||||
.Sum(x => (decimal?)x.RequestedNTE)
|
||||
.Sum(x => (decimal?)(x.createdby == null
|
||||
? x.RequestedNTE - (x.CurrentNTE ?? 0m)
|
||||
: x.RequestedNTE))
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
{
|
||||
Task<(int TotalCount, IReadOnlyList<UpliftListItemData> Items)> GetPagedAsync(
|
||||
string? status, int? tier, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<decimal> GetPendingExposureAsync(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<UpliftForDispatchData>> GetForDispatchAsync(int dispatchId, CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<UpliftForWorkOrderData>> GetForWorkOrderAsync(int workOrderId, CancellationToken cancellationToken);
|
||||
Task<DispatchUpliftRequest?> GetByIdAndWorkOrderAsync(int requestId, int workOrderId, CancellationToken cancellationToken);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace SeaHaven.Services.DTOs
|
|||
public string? WorkOrderSite { get; set; }
|
||||
public string? WorkOrderService { get; set; }
|
||||
public string? RequestedByName { get; set; }
|
||||
public string? DecidedByName { get; set; }
|
||||
public decimal? CurrentNTE { get; set; }
|
||||
public decimal RequestedNTE { get; set; }
|
||||
public decimal Delta { get; set; }
|
||||
|
|
@ -31,6 +32,8 @@ namespace SeaHaven.Services.DTOs
|
|||
public DateTime? ExpiresAt { get; set; }
|
||||
public string? NotificationStatus { get; set; }
|
||||
public string? NotificationError { get; set; }
|
||||
public int AttachmentCount { get; set; }
|
||||
public bool WorkOrderClosed { get; set; }
|
||||
// Approved-on-WO exposure totals for this item's work order: auto-approved and
|
||||
// admin-approved amounts; pending, rejected, cancelled/withdrawn, and revoked
|
||||
// requests are excluded.
|
||||
|
|
@ -44,6 +47,7 @@ namespace SeaHaven.Services.DTOs
|
|||
public int Total { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public decimal PendingExposureTotal { get; set; }
|
||||
public IEnumerable<UpliftListItemDTO> Items { get; set; } = Enumerable.Empty<UpliftListItemDTO>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ namespace SeaHaven.Services.Implementation
|
|||
public async Task<UpliftListResultDTO> ListAsync(ClaimsPrincipal user, string? status, int? tier, int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (total, items) = await _upliftData.GetPagedAsync(status, tier, page, pageSize, cancellationToken);
|
||||
var pendingExposureTotal = await _upliftData.GetPendingExposureAsync(cancellationToken);
|
||||
|
||||
var mapped = items.Select(r => new UpliftListItemDTO
|
||||
{
|
||||
|
|
@ -46,6 +47,7 @@ namespace SeaHaven.Services.Implementation
|
|||
WorkOrderSite = r.WorkOrderSiteCode,
|
||||
WorkOrderService = r.WorkOrderService,
|
||||
RequestedByName = ResolveRequestedByName(r.RequestedByVendorName, r.RequestedByFirstName, r.RequestedByLastName),
|
||||
DecidedByName = ResolveRequestedByName(null, r.DecidedByFirstName, r.DecidedByLastName),
|
||||
CurrentNTE = r.CurrentNTE,
|
||||
RequestedNTE = r.RequestedNTE,
|
||||
Delta = r.RequestedNTE - (r.CurrentNTE ?? 0m),
|
||||
|
|
@ -62,7 +64,9 @@ namespace SeaHaven.Services.Implementation
|
|||
EvidenceSizeBytes = r.EvidenceSizeBytes,
|
||||
ExpiresAt = r.ExpiresAt,
|
||||
NotificationStatus = r.NotificationStatus,
|
||||
NotificationError = r.NotificationError
|
||||
NotificationError = r.NotificationError,
|
||||
AttachmentCount = r.AttachmentCount,
|
||||
WorkOrderClosed = r.WorkOrderClosed
|
||||
}).ToList();
|
||||
|
||||
// Approved-on-WO exposure totals are aggregated once for the whole page
|
||||
|
|
@ -90,6 +94,7 @@ namespace SeaHaven.Services.Implementation
|
|||
Total = total,
|
||||
Page = page,
|
||||
PageSize = pageSize,
|
||||
PendingExposureTotal = pendingExposureTotal,
|
||||
Items = mapped
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue