fix(work-orders): require company not technician for Completed (SH-183)

This commit is contained in:
arthur.bassi 2026-08-17 13:54:05 -03:00
parent aaba7ca2ea
commit 37a6098ecb
3 changed files with 58 additions and 11 deletions

View file

@ -4,13 +4,13 @@ import type {
} from "@/domain/work-orders/types/work-order-table-row";
import type { WOType, WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
/** Fields required to evaluate whether a WO may be marked Completed (SH-114 ∪ pending uplift). */
/** Fields required to evaluate whether a WO may be marked Completed (SH-183 ∪ pending uplift). */
export type WoClosabilityInput = {
dispatcherId: string;
dispatcherName: string;
/** Vendor/company assignment on the board row. */
/** Company / vendor assignment — required for Completed (SH-183). */
company: string;
/** Technician assigned to the work order. */
/** Technician is optional for closability; modeled separately from company. */
tech: string;
/** Work completed date (API CompletedDate). Empty until set. */
completedDate: string;
@ -51,6 +51,10 @@ function hasDispatcher(row: Pick<WoClosabilityInput, "dispatcherId" | "dispatche
return Boolean(row.dispatcherId.trim() || row.dispatcherName.trim());
}
function hasCompany(company: string): boolean {
return Boolean(company.trim());
}
function hasService(pm: string): boolean {
return Boolean(pm.trim());
}
@ -70,8 +74,8 @@ export function getClosabilityGaps(row: WoClosabilityInput): string[] {
if (!hasDispatcher(row)) {
gaps.push("Dispatcher");
}
if (!row.tech.trim()) {
gaps.push("Technician");
if (!hasCompany(row.company)) {
gaps.push("Company");
}
if (!row.completedDate.trim()) {
gaps.push("Completed Date");
@ -133,7 +137,7 @@ export function wizardDraftToClosabilityInput(draft: WorkOrderWizardDraft): WoCl
dispatcherId: draft.assigneeId,
dispatcherName: draft.assigneeName,
company: draft.vendorName,
tech: draft.vendorName,
tech: "",
completedDate: "",
woNumber: draft.woNumber,
pm: draft.pm,

View file

@ -10,6 +10,7 @@ function makeRow(overrides: Partial<WorkOrderTableRow> = {}): WorkOrderTableRow
woNumber: "1001",
site: "BK5",
type: "PM",
severity: null,
dispatcherId: "u1",
dispatcherName: "Alex",
scheduledOn: "2026-07-15",
@ -116,4 +117,43 @@ describe("StatusCell pending uplift closability", () => {
expect(onChangeStatus).toHaveBeenCalledWith("Completed");
});
it("disables Completed when company is missing (SH-183)", () => {
const onChangeStatus = vi.fn();
renderWithProviders(
<StatusCell
wo={makeRow({ company: "" })}
onChangeStatus={onChangeStatus}
onOpenSlideOver={vi.fn()}
/>,
{ withAuth: false },
);
fireEvent.click(screen.getByRole("button", { name: /in progress/i }));
const completed = screen.getByRole("button", { name: /completed/i });
expect(completed).toBeDisabled();
expect(completed).toHaveAttribute("title", "Missing: Company");
fireEvent.click(completed);
expect(onChangeStatus).not.toHaveBeenCalled();
});
it("allows Completed when technician is empty if company is set (SH-183)", () => {
const onChangeStatus = vi.fn();
renderWithProviders(
<StatusCell
wo={makeRow({ tech: "" })}
onChangeStatus={onChangeStatus}
onOpenSlideOver={vi.fn()}
/>,
{ withAuth: false },
);
fireEvent.click(screen.getByRole("button", { name: /in progress/i }));
fireEvent.click(screen.getByRole("button", { name: /completed/i }));
expect(onChangeStatus).toHaveBeenCalledWith("Completed");
});
});

View file

@ -60,12 +60,15 @@ describe("getClosabilityGaps", () => {
expect(getClosabilityGaps(makeInput())).toEqual([]);
});
it("blocks Completed when technician is missing", () => {
expect(getClosabilityGaps(makeInput({ tech: "" }))).toEqual(["Technician"]);
it("allows company without technician (SH-183)", () => {
const gaps = getClosabilityGaps(makeInput({ tech: "" }));
expect(gaps).toEqual([]);
expect(gaps).not.toContain("Technician");
});
it("does not block Completed when vendor/company is empty if technician is assigned", () => {
expect(getClosabilityGaps(makeInput({ company: "" }))).toEqual([]);
it("blocks Completed when company is missing even if technician is assigned", () => {
expect(getClosabilityGaps(makeInput({ company: "", tech: "Sam" }))).toEqual(["Company"]);
expect(getClosabilityGaps(makeInput({ company: " ", tech: "Sam" }))).toEqual(["Company"]);
});
it("lists field gaps before uplift and document gaps", () => {
@ -86,7 +89,7 @@ describe("getClosabilityGaps", () => {
),
).toEqual([
"Dispatcher",
"Technician",
"Company",
"Completed Date",
"WO number",
"Service",