mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 02:22:06 +00:00
fix(work-orders): fail closed on unknown uplift status
This commit is contained in:
parent
ccfb3d9e6d
commit
a3e9a20477
3 changed files with 37 additions and 8 deletions
|
|
@ -25,6 +25,7 @@ function rowClosabilityGaps(wo: WorkOrderTableRow) {
|
||||||
return getClosabilityGaps({
|
return getClosabilityGaps({
|
||||||
dispatcherId: wo.dispatcherId,
|
dispatcherId: wo.dispatcherId,
|
||||||
dispatcherName: wo.dispatcherName,
|
dispatcherName: wo.dispatcherName,
|
||||||
|
company: wo.company,
|
||||||
tech: wo.tech,
|
tech: wo.tech,
|
||||||
completedDate: wo.completedDate ?? "",
|
completedDate: wo.completedDate ?? "",
|
||||||
woNumber: wo.woNumber,
|
woNumber: wo.woNumber,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import type { WOType, WorkOrderWizardDraft } from "@/domain/work-orders/types/wo
|
||||||
export type WoClosabilityInput = {
|
export type WoClosabilityInput = {
|
||||||
dispatcherId: string;
|
dispatcherId: string;
|
||||||
dispatcherName: string;
|
dispatcherName: string;
|
||||||
/** Vendor / technician assignment on the board row. */
|
/** Vendor/company assignment on the board row. */
|
||||||
|
company: string;
|
||||||
|
/** Technician name is informational and does not satisfy the vendor requirement. */
|
||||||
tech: string;
|
tech: string;
|
||||||
/** Work completed date (API CompletedDate). Empty until set. */
|
/** Work completed date (API CompletedDate). Empty until set. */
|
||||||
completedDate: string;
|
completedDate: string;
|
||||||
|
|
@ -22,6 +24,7 @@ export type WoClosabilityInput = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PENDING_UPLIFT_GAP = "Pending uplift approval";
|
export const PENDING_UPLIFT_GAP = "Pending uplift approval";
|
||||||
|
export const UPLIFT_STATUS_UNAVAILABLE_GAP = "Uplift status unavailable";
|
||||||
|
|
||||||
/** Design copy when Completed is blocked solely by a pending uplift. */
|
/** Design copy when Completed is blocked solely by a pending uplift. */
|
||||||
export const UPLIFT_PENDING_COMPLETED_TITLE =
|
export const UPLIFT_PENDING_COMPLETED_TITLE =
|
||||||
|
|
@ -45,6 +48,12 @@ export function hasPendingUplift(
|
||||||
return (row.upliftSummary?.pendingCount ?? 0) > 0;
|
return (row.upliftSummary?.pendingCount ?? 0) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasKnownUpliftStatus(
|
||||||
|
row: Pick<WoClosabilityInput, "upliftSummary" | "hasPendingUplift">,
|
||||||
|
): boolean {
|
||||||
|
return typeof row.hasPendingUplift === "boolean" || row.upliftSummary != null;
|
||||||
|
}
|
||||||
|
|
||||||
function hasDispatcher(row: Pick<WoClosabilityInput, "dispatcherId" | "dispatcherName">): boolean {
|
function hasDispatcher(row: Pick<WoClosabilityInput, "dispatcherId" | "dispatcherName">): boolean {
|
||||||
return Boolean(row.dispatcherId.trim() || row.dispatcherName.trim());
|
return Boolean(row.dispatcherId.trim() || row.dispatcherName.trim());
|
||||||
}
|
}
|
||||||
|
|
@ -68,8 +77,8 @@ export function getClosabilityGaps(row: WoClosabilityInput): string[] {
|
||||||
if (!hasDispatcher(row)) {
|
if (!hasDispatcher(row)) {
|
||||||
gaps.push("Dispatcher");
|
gaps.push("Dispatcher");
|
||||||
}
|
}
|
||||||
if (!row.tech.trim()) {
|
if (!row.company.trim()) {
|
||||||
gaps.push("Technician");
|
gaps.push("Company / vendor");
|
||||||
}
|
}
|
||||||
if (!row.completedDate.trim()) {
|
if (!row.completedDate.trim()) {
|
||||||
gaps.push("Completed Date");
|
gaps.push("Completed Date");
|
||||||
|
|
@ -81,7 +90,9 @@ export function getClosabilityGaps(row: WoClosabilityInput): string[] {
|
||||||
gaps.push("Service");
|
gaps.push("Service");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPendingUplift(row)) {
|
if (!hasKnownUpliftStatus(row)) {
|
||||||
|
gaps.push(UPLIFT_STATUS_UNAVAILABLE_GAP);
|
||||||
|
} else if (hasPendingUplift(row)) {
|
||||||
gaps.push(PENDING_UPLIFT_GAP);
|
gaps.push(PENDING_UPLIFT_GAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +137,7 @@ export function wizardDraftToClosabilityInput(draft: WorkOrderWizardDraft): WoCl
|
||||||
return {
|
return {
|
||||||
dispatcherId: draft.assigneeId,
|
dispatcherId: draft.assigneeId,
|
||||||
dispatcherName: draft.assigneeName,
|
dispatcherName: draft.assigneeName,
|
||||||
|
company: draft.vendorName,
|
||||||
tech: draft.vendorName,
|
tech: draft.vendorName,
|
||||||
completedDate: "",
|
completedDate: "",
|
||||||
woNumber: draft.woNumber,
|
woNumber: draft.woNumber,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||||
import type { WoClosabilityInput } from "@/domain/work-orders/utils/wo-closability";
|
import type { WoClosabilityInput } from "@/domain/work-orders/utils/wo-closability";
|
||||||
import {
|
import {
|
||||||
PENDING_UPLIFT_GAP,
|
PENDING_UPLIFT_GAP,
|
||||||
|
UPLIFT_STATUS_UNAVAILABLE_GAP,
|
||||||
UPLIFT_PENDING_COMPLETED_TITLE,
|
UPLIFT_PENDING_COMPLETED_TITLE,
|
||||||
canGenerateCompDoc,
|
canGenerateCompDoc,
|
||||||
formatClosabilityMissingTitle,
|
formatClosabilityMissingTitle,
|
||||||
|
|
@ -14,6 +15,7 @@ function makeInput(overrides: Partial<WoClosabilityInput> = {}): WoClosabilityIn
|
||||||
return {
|
return {
|
||||||
dispatcherId: "u1",
|
dispatcherId: "u1",
|
||||||
dispatcherName: "Alex",
|
dispatcherName: "Alex",
|
||||||
|
company: "Acme HVAC",
|
||||||
tech: "Sam",
|
tech: "Sam",
|
||||||
completedDate: "2026-07-01",
|
completedDate: "2026-07-01",
|
||||||
woNumber: "1001",
|
woNumber: "1001",
|
||||||
|
|
@ -21,8 +23,7 @@ function makeInput(overrides: Partial<WoClosabilityInput> = {}): WoClosabilityIn
|
||||||
docStatus: "Yes",
|
docStatus: "Yes",
|
||||||
mediaCount: 1,
|
mediaCount: 1,
|
||||||
type: "PM",
|
type: "PM",
|
||||||
upliftSummary: undefined,
|
hasPendingUplift: false,
|
||||||
hasPendingUplift: undefined,
|
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +48,7 @@ describe("hasPendingUplift", () => {
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("is false when no uplift signals are present", () => {
|
it("is false when uplift is explicitly not pending", () => {
|
||||||
expect(hasPendingUplift(makeInput())).toBe(false);
|
expect(hasPendingUplift(makeInput())).toBe(false);
|
||||||
expect(
|
expect(
|
||||||
hasPendingUplift(makeInput({ upliftSummary: { hasUplift: false, pendingCount: 0 } })),
|
hasPendingUplift(makeInput({ upliftSummary: { hasUplift: false, pendingCount: 0 } })),
|
||||||
|
|
@ -60,12 +61,21 @@ describe("getClosabilityGaps", () => {
|
||||||
expect(getClosabilityGaps(makeInput())).toEqual([]);
|
expect(getClosabilityGaps(makeInput())).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("accepts a company without a technician", () => {
|
||||||
|
expect(getClosabilityGaps(makeInput({ tech: "" }))).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not let a technician satisfy a missing company requirement", () => {
|
||||||
|
expect(getClosabilityGaps(makeInput({ company: "" }))).toEqual(["Company / vendor"]);
|
||||||
|
});
|
||||||
|
|
||||||
it("lists field gaps before uplift and document gaps", () => {
|
it("lists field gaps before uplift and document gaps", () => {
|
||||||
expect(
|
expect(
|
||||||
getClosabilityGaps(
|
getClosabilityGaps(
|
||||||
makeInput({
|
makeInput({
|
||||||
dispatcherId: "",
|
dispatcherId: "",
|
||||||
dispatcherName: "",
|
dispatcherName: "",
|
||||||
|
company: " ",
|
||||||
tech: " ",
|
tech: " ",
|
||||||
completedDate: "",
|
completedDate: "",
|
||||||
woNumber: "SH-1",
|
woNumber: "SH-1",
|
||||||
|
|
@ -77,7 +87,7 @@ describe("getClosabilityGaps", () => {
|
||||||
),
|
),
|
||||||
).toEqual([
|
).toEqual([
|
||||||
"Dispatcher",
|
"Dispatcher",
|
||||||
"Technician",
|
"Company / vendor",
|
||||||
"Completed Date",
|
"Completed Date",
|
||||||
"WO number",
|
"WO number",
|
||||||
"Service",
|
"Service",
|
||||||
|
|
@ -96,6 +106,12 @@ describe("getClosabilityGaps", () => {
|
||||||
expect(getClosabilityGaps(makeInput({ hasPendingUplift: true }))).toEqual([PENDING_UPLIFT_GAP]);
|
expect(getClosabilityGaps(makeInput({ hasPendingUplift: true }))).toEqual([PENDING_UPLIFT_GAP]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("blocks Completed when the backend does not provide uplift status", () => {
|
||||||
|
expect(
|
||||||
|
getClosabilityGaps(makeInput({ hasPendingUplift: undefined, upliftSummary: undefined })),
|
||||||
|
).toEqual([UPLIFT_STATUS_UNAVAILABLE_GAP]);
|
||||||
|
});
|
||||||
|
|
||||||
it("requires a photo for Emergency / Reactive", () => {
|
it("requires a photo for Emergency / Reactive", () => {
|
||||||
expect(
|
expect(
|
||||||
getClosabilityGaps(makeInput({ type: "Emergency", mediaCount: 0, docStatus: "No" })),
|
getClosabilityGaps(makeInput({ type: "Emergency", mediaCount: 0, docStatus: "No" })),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue