shoc-backend/SeaHaven.DataServices/Interfaces/IUpliftDataService.cs
Alexandre Brandizzi 41957d52a7 merge: rebase queue flags onto SH-210 decisions, drop duplicated contract fields
#144 branched from the SH-210 decision-actions commit before the SH-207/SH-208
read-contract corrections landed, so it re-implemented workOrderClosed,
attachmentCount, decidedByName, and pendingExposureTotal with stale semantics:
it projected WorkerOrderNumber (the CRM external id) instead of InternalWONumber
(what the board renders) and summed raw RequestedNTE, which double-counts the new
NTE total on vendor-portal rows across sequential approvals.

Merge origin/feat/ab/sh-210-uplift-decisions (#141, what lands) in and resolve
every conflict in #141's favour, so those fields and their granted-amount
exposure math now come from #141 rather than being duplicated here. Keep only the
two deltas #144 actually adds on top of #141:

- attachmentCount counts non-deleted UpliftEvidence documents on the dispatch,
  not every completion document, so completion photos no longer inflate the chip;
- the queue read resolves the effective work order via the primary-plus-linked
  (DispatchWorkOrders) convention the sibling reads use, so a dispatch linked only
  through that table surfaces its WO context, closed flag, and exposure. Covered
  by an in-memory test and a SQLite relational test that proves the fallback
  translates to SQL.

Drop the superseded attachment-count test that asserted completion documents
count, and align the relational test to seed InternalWONumber.
2026-09-17 12:52:13 -03:00

47 lines
3.8 KiB
C#

using Data.SeaHavenIndustries;
namespace SeaHaven.DataServices.Interfaces
{
public interface IUpliftDataService
{
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);
Task<IReadOnlyList<PortalUpliftData>> GetForVendorDispatchAsync(int dispatchId, CancellationToken cancellationToken);
Task<DispatchUpliftRequest?> GetByIdAsync(int id, CancellationToken cancellationToken);
Task<int?> GetWorkOrderIdForUpliftAsync(int upliftRequestId, CancellationToken cancellationToken);
Task<DispatchUpliftRequest?> GetByIdAndDispatchAsync(int requestId, int dispatchId, CancellationToken cancellationToken);
// SH-101: server-side join of an uplift request with its linked evidence document.
// Returns null when the request, the linked evidence, or the dispatch linkage is absent.
Task<UpliftEvidenceDownloadData?> GetEvidenceForInternalDownloadAsync(int upliftRequestId, CancellationToken cancellationToken);
Task<bool> HasPendingAsync(int dispatchId, CancellationToken cancellationToken);
Task<bool> HasPendingForWorkOrderAsync(int workOrderId, CancellationToken cancellationToken);
Task<decimal> SumAutoApprovedAmountForWorkOrderAsync(int workOrderId, CancellationToken cancellationToken);
// Approval queue read contract: set-based per-work-order exposure totals covering
// auto-approved and admin-approved uplift amounts (pending, rejected,
// cancelled/withdrawn, and revoked requests are excluded). Work orders absent
// from the result have no approved exposure.
Task<IReadOnlyList<WorkOrderUpliftExposureData>> GetApprovedExposureForWorkOrdersAsync(
IReadOnlyCollection<int> workOrderIds, CancellationToken cancellationToken);
// SH-207: queue-wide pending exposure for the approvals header (sum of
// RequestedNTE over non-deleted Pending requests on non-deleted dispatches).
Task<decimal> GetPendingExposureTotalAsync(CancellationToken cancellationToken);
Task<List<DispatchUpliftRequest>> GetPendingForWorkOrderAsync(int workOrderId, CancellationToken cancellationToken);
// SH-101: active = Pending or ChangesRequested (the only states that block a new request).
Task<bool> HasActiveAsync(int dispatchId, CancellationToken cancellationToken);
Task<DispatchUpliftRequest?> GetActiveRequestAsync(int dispatchId, CancellationToken cancellationToken);
Task<DispatchUpliftRequest?> GetByRequestKeyAsync(int dispatchId, string requestKey, CancellationToken cancellationToken);
Task<List<DispatchUpliftRequest>> GetDueForExpiryAsync(DateTime utcNow, int count, CancellationToken cancellationToken);
Task<List<DispatchUpliftRequest>> GetDueForInitialNotificationAsync(int count, CancellationToken cancellationToken);
Task<List<DispatchUpliftRequest>> GetDueForEscalationAsync(int count, CancellationToken cancellationToken);
Task StageAsync(DispatchUpliftRequest request, CancellationToken cancellationToken);
Task SaveChangesAsync(CancellationToken cancellationToken);
Task<T> ExecuteWorkOrderMutationAsync<T>(
int workOrderId,
Func<CancellationToken, Task<T>> work,
CancellationToken cancellationToken);
}
}