fix(work-orders): treat Canceled and Refused dispatches as inactive (SH-183)

Backend returns Cancelled, Canceled, and Refused; only Cancelled was skipped,
so Complete stayed enabled with no live vendor.
This commit is contained in:
Arthur Bassi 2026-08-19 15:04:50 -03:00
parent a43de20ed4
commit e3b8360a68
2 changed files with 34 additions and 13 deletions

View file

@ -157,12 +157,19 @@ export function wizardDraftToClosabilityInput(draft: WorkOrderWizardDraft): WoCl
};
}
/** Current vendor assignment — cancelled dispatches do not count as company (SH-183). */
/** Backend terminal-cancel spellings; dispatch status is a raw API string. */
const INACTIVE_DISPATCH_STATUSES = new Set(["cancelled", "canceled", "refused"]);
function isInactiveDispatchStatus(status: string | undefined): boolean {
return INACTIVE_DISPATCH_STATUSES.has((status ?? "").trim().toLowerCase());
}
/** Current vendor assignment — cancelled/refused dispatches do not count as company (SH-183). */
function currentAssignedDispatch(
dispatches: WorkOrderDispatchSummary[],
): WorkOrderDispatchSummary | undefined {
return dispatches.find((dispatch) => {
if (dispatch.status === "Cancelled") {
if (isInactiveDispatchStatus(dispatch.status)) {
return false;
}
const vendorId = String(dispatch.vendorId ?? "").trim();

View file

@ -327,8 +327,30 @@ describe("detailToClosabilityInput", () => {
expect(getClosabilityGaps(input)).toEqual([]);
});
it("ignores cancelled dispatches when resolving assigned company (SH-183)", () => {
const cancelledOnly = {
it.each(["Cancelled", "Canceled", "Refused", "CANCELLED", " canceled "])(
"ignores inactive dispatch status %s when resolving assigned company (SH-183)",
(status) => {
const inactiveOnly = {
assignedTo: "Alice",
completedDate: "2026-07-15",
workOrderNumber: "20260623001",
internalWONumber: "SH-1",
problem: "HVAC PM",
source: "procurement-integration",
type: "",
docStatus: "",
mediaCount: 0,
dispatches: [{ vendorName: "Old Co", vendorId: 45, status }],
} as WorkOrderDetail;
expect(detailToClosabilityInput(inactiveOnly).vendorId).toBe("");
expect(detailToClosabilityInput(inactiveOnly).company).toBe("");
expect(getClosabilityGaps(detailToClosabilityInput(inactiveOnly))).toContain("Company");
},
);
it("uses the active dispatch after an inactive cancelled spelling (SH-183)", () => {
const supersededThenCurrent = {
assignedTo: "Alice",
completedDate: "2026-07-15",
workOrderNumber: "20260623001",
@ -338,16 +360,8 @@ describe("detailToClosabilityInput", () => {
type: "",
docStatus: "",
mediaCount: 0,
dispatches: [{ vendorName: "", vendorId: 45, status: "Cancelled" }],
} as WorkOrderDetail;
expect(detailToClosabilityInput(cancelledOnly).vendorId).toBe("");
expect(getClosabilityGaps(detailToClosabilityInput(cancelledOnly))).toContain("Company");
const supersededThenCurrent = {
...cancelledOnly,
dispatches: [
{ vendorName: "Old Co", vendorId: 1, status: "Cancelled" },
{ vendorName: "Old Co", vendorId: 1, status: "Canceled" },
{ vendorName: "ACME Tech", vendorId: 45, status: "Sent" },
],
} as WorkOrderDetail;