fix(vendor-portal): vendors revise only uplift requests they raised

A work-order request stores the requested increase, not a total. Letting
the vendor revise one after changes were requested rewrote RequestedNTE
as a total while it still read as a work-order request, corrupting its
amount and the NTE it would be approved to. Revise now answers not-found
for any request the vendor did not raise and leaves the row untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alexandre Brandizzi 2026-09-25 02:35:06 -03:00
parent eb2b442775
commit d33ba34db9
3 changed files with 56 additions and 1 deletions

View file

@ -419,6 +419,44 @@ public sealed class UpliftWorkflowTests
context.WorkOrderAuditLogs.Should().Contain(a => a.Action == "uplift_revised");
}
[Fact]
public async Task Revise_WorkOrderRequest_IsRefusedAsNotFound_AndLeavesTheRowUnchanged()
{
using var context = NewContext();
var (vendor, workOrder, dispatch) = await SeedAsync(context, nte: 600m);
context.VendorCompletionDocuments.Add(EvidenceDocument(vendor.Id, dispatch.Id, workOrder.Id));
// Raised from the work order by an internal user and sent back for changes:
// RequestedNTE holds the 90 increase, not a total.
var workOrderRequest = new DispatchUpliftRequest
{
DispatchId = dispatch.Id,
CurrentNTE = 600m,
RequestedNTE = 90m,
Status = UpliftStatus.ChangesRequested,
RequiredTier = 1,
VendorReason = "Extra parts",
RequestedByVendorName = "Alex Dispatcher",
NotificationStatus = "Sent",
createdby = "dispatcher-1",
CreatedDate = new DateTime(2026, 3, 1),
};
context.DispatchUpliftRequests.Add(workOrderRequest);
await context.SaveChangesAsync();
var service = NewPortalService(context, new FakeEmailSender(deliver: true));
var session = await service.ResolveSessionAsync(Token, CancellationToken.None);
var act = () => service.ReviseUpliftAsync(session!, dispatch.Id, workOrderRequest.Id, 900m, "vendor total", 1, CancellationToken.None);
await act.Should().ThrowAsync<KeyNotFoundException>();
var after = context.DispatchUpliftRequests.AsNoTracking().Single(u => u.Id == workOrderRequest.Id);
after.Status.Should().Be(UpliftStatus.ChangesRequested);
after.RequestedNTE.Should().Be(90m);
after.CurrentNTE.Should().Be(600m);
after.VendorReason.Should().Be("Extra parts");
after.createdby.Should().Be("dispatcher-1");
context.WorkOrderAuditLogs.Should().NotContain(a => a.Action == "uplift_revised");
}
[Fact]
public async Task Withdraw_Pending_TransitionsToWithdrawn_SetsDecidedAt()
{

View file

@ -736,7 +736,9 @@ namespace SeaHaven.Services.Implementation
}
var req = await _upliftData.GetByIdAndDispatchAsync(requestId, id, cancellationToken);
if (req == null) throw new KeyNotFoundException("Uplift request not found");
// A vendor revises only requests it raised. Work-order requests (createdby set)
// store the increase, not a total, so a vendor revise would corrupt their amount.
if (req == null || req.createdby != null) throw new KeyNotFoundException("Uplift request not found");
if (UpliftStatus.ToCanonical(req.Status) != UpliftStatus.ChangesRequested)
{

View file

@ -261,6 +261,21 @@ public sealed class UpliftAmountPerCreationPathTests
.SumAutoApprovedAmountForWorkOrderAsync(WorkOrderId, CancellationToken.None));
}
[Fact]
public async Task WorkOrderUpliftList_ShowsEachRequestsIncrease()
{
await using var context = CreateContext();
await SeedWorkOrderAsync(context);
var workOrderRequestId = await CreatePendingWorkOrderRequestAsync(context, 90m);
var vendorRequestId = await SeedVendorRequestAsync(context, SecondDispatchId, currentNte: 600m, requestedTotal: 900m);
var uplifts = await NewWorkOrderUpliftService(context)
.ListAsync(WorkOrderId, Admin(), CancellationToken.None);
Assert.Equal(90m, Assert.Single(uplifts!, u => u.Id == workOrderRequestId).Amount);
Assert.Equal(300m, Assert.Single(uplifts!, u => u.Id == vendorRequestId).Amount);
}
[Fact]
public void AmountDefinition_TranslatesToSqlServer_ForRowAndGroupedSums()
{