Merge pull request #140 from Sea-Haven-Industries/feat/ab/sh-207-uplift-queue

SH-207: Deliver the uplift approval queue read contract
This commit is contained in:
Alexandre Brandizzi 2026-09-17 13:28:16 -03:00 • committed by GitHub
commit 45519c1a82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 3 deletions

View file

@ -672,6 +672,32 @@ public sealed class UpliftQueueReadTests
total.AutoApprovedTotal.Should().Be(0m);
}
[Fact]
public async Task Exposure_AutoApprovedSumsGrantedAmountsPerCreationPath()
{
using var context = NewContext();
var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-A", "SITE-A", "HVAC");
var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-A");
context.DispatchUpliftRequests.AddRange(
// Vendor-portal rows (createdby null) store the requested new NTE total:
// 1000 -> 1500 grants 500, then 1500 -> 1800 grants 300.
Request(dispatch, "NoApprovalRequired", new DateTime(2026, 3, 1), decided: new DateTime(2026, 3, 2), requested: 1500m, currentNte: 1000m),
Request(dispatch, "NoApprovalRequired", new DateTime(2026, 3, 3), decided: new DateTime(2026, 3, 4), requested: 1800m, currentNte: 1500m),
// Work-order-path rows (createdby set) store the granted increment: 200.
Request(dispatch, "NoApprovalRequired", new DateTime(2026, 3, 5), decided: new DateTime(2026, 3, 6), requested: 200m, currentNte: 1800m, createdBy: "user-7"));
await context.SaveChangesAsync();
var data = new UpliftDataService(context);
var exposure = await data.GetApprovedExposureForWorkOrdersAsync(
new[] { workOrder.Id }, CancellationToken.None);
// Granted exposure is 500 + 300 + 200; summing raw RequestedNTE would
// double-count whole NTE totals and report 3500.
var total = exposure.Should().ContainSingle(e => e.WorkOrderId == workOrder.Id).Subject;
total.AutoApprovedTotal.Should().Be(1000m);
total.AdminApprovedTotal.Should().Be(0m);
}
// --- Read authorization (tier roles) ---
[Fact]

View file

@ -361,15 +361,17 @@ namespace SeaHaven.DataServices.Implementation
.Select(g => new
{
DispatchId = g.Key,
AutoApproved = g.Where(x => x.Status == "NoApprovalRequired")
.Sum(x => (decimal?)x.RequestedNTE),
// The two creation paths store different meanings in RequestedNTE.
// Vendor-portal rows store the requested new NTE total, so the
// granted amount is RequestedNTE - CurrentNTE; work-order-path rows
// store the granted increment directly. Vendor sessions have no
// identity user, so createdby is null only on vendor-portal rows. Summing
// granted amounts keeps sequential approvals from double-counting whole
// NTE totals.
// NTE totals. This applies to both buckets.
AutoApproved = g.Where(x => x.Status == "NoApprovalRequired")
.Sum(x => (decimal?)(x.createdby == null
? x.RequestedNTE - (x.CurrentNTE ?? 0m)
: x.RequestedNTE)),
AdminApproved = g.Where(x => x.Status == "Approved")
.Sum(x => (decimal?)(x.createdby == null
? x.RequestedNTE - (x.CurrentNTE ?? 0m)