mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-06 15:52:04 +00:00
fix(work-orders): require company not technician for Completed (SH-183)
This commit is contained in:
parent
aaba7ca2ea
commit
37a6098ecb
3 changed files with 58 additions and 11 deletions
|
|
@ -4,13 +4,13 @@ import type {
|
||||||
} from "@/domain/work-orders/types/work-order-table-row";
|
} from "@/domain/work-orders/types/work-order-table-row";
|
||||||
import type { WOType, WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
|
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 = {
|
export type WoClosabilityInput = {
|
||||||
dispatcherId: string;
|
dispatcherId: string;
|
||||||
dispatcherName: string;
|
dispatcherName: string;
|
||||||
/** Vendor/company assignment on the board row. */
|
/** Company / vendor assignment — required for Completed (SH-183). */
|
||||||
company: string;
|
company: string;
|
||||||
/** Technician assigned to the work order. */
|
/** Technician is optional for closability; modeled separately from company. */
|
||||||
tech: string;
|
tech: string;
|
||||||
/** Work completed date (API CompletedDate). Empty until set. */
|
/** Work completed date (API CompletedDate). Empty until set. */
|
||||||
completedDate: string;
|
completedDate: string;
|
||||||
|
|
@ -51,6 +51,10 @@ function hasDispatcher(row: Pick<WoClosabilityInput, "dispatcherId" | "dispatche
|
||||||
return Boolean(row.dispatcherId.trim() || row.dispatcherName.trim());
|
return Boolean(row.dispatcherId.trim() || row.dispatcherName.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasCompany(company: string): boolean {
|
||||||
|
return Boolean(company.trim());
|
||||||
|
}
|
||||||
|
|
||||||
function hasService(pm: string): boolean {
|
function hasService(pm: string): boolean {
|
||||||
return Boolean(pm.trim());
|
return Boolean(pm.trim());
|
||||||
}
|
}
|
||||||
|
|
@ -70,8 +74,8 @@ export function getClosabilityGaps(row: WoClosabilityInput): string[] {
|
||||||
if (!hasDispatcher(row)) {
|
if (!hasDispatcher(row)) {
|
||||||
gaps.push("Dispatcher");
|
gaps.push("Dispatcher");
|
||||||
}
|
}
|
||||||
if (!row.tech.trim()) {
|
if (!hasCompany(row.company)) {
|
||||||
gaps.push("Technician");
|
gaps.push("Company");
|
||||||
}
|
}
|
||||||
if (!row.completedDate.trim()) {
|
if (!row.completedDate.trim()) {
|
||||||
gaps.push("Completed Date");
|
gaps.push("Completed Date");
|
||||||
|
|
@ -133,7 +137,7 @@ export function wizardDraftToClosabilityInput(draft: WorkOrderWizardDraft): WoCl
|
||||||
dispatcherId: draft.assigneeId,
|
dispatcherId: draft.assigneeId,
|
||||||
dispatcherName: draft.assigneeName,
|
dispatcherName: draft.assigneeName,
|
||||||
company: draft.vendorName,
|
company: draft.vendorName,
|
||||||
tech: draft.vendorName,
|
tech: "",
|
||||||
completedDate: "",
|
completedDate: "",
|
||||||
woNumber: draft.woNumber,
|
woNumber: draft.woNumber,
|
||||||
pm: draft.pm,
|
pm: draft.pm,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ function makeRow(overrides: Partial<WorkOrderTableRow> = {}): WorkOrderTableRow
|
||||||
woNumber: "1001",
|
woNumber: "1001",
|
||||||
site: "BK5",
|
site: "BK5",
|
||||||
type: "PM",
|
type: "PM",
|
||||||
|
severity: null,
|
||||||
dispatcherId: "u1",
|
dispatcherId: "u1",
|
||||||
dispatcherName: "Alex",
|
dispatcherName: "Alex",
|
||||||
scheduledOn: "2026-07-15",
|
scheduledOn: "2026-07-15",
|
||||||
|
|
@ -116,4 +117,43 @@ describe("StatusCell pending uplift closability", () => {
|
||||||
|
|
||||||
expect(onChangeStatus).toHaveBeenCalledWith("Completed");
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,15 @@ describe("getClosabilityGaps", () => {
|
||||||
expect(getClosabilityGaps(makeInput())).toEqual([]);
|
expect(getClosabilityGaps(makeInput())).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("blocks Completed when technician is missing", () => {
|
it("allows company without technician (SH-183)", () => {
|
||||||
expect(getClosabilityGaps(makeInput({ tech: "" }))).toEqual(["Technician"]);
|
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", () => {
|
it("blocks Completed when company is missing even if technician is assigned", () => {
|
||||||
expect(getClosabilityGaps(makeInput({ company: "" }))).toEqual([]);
|
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", () => {
|
it("lists field gaps before uplift and document gaps", () => {
|
||||||
|
|
@ -86,7 +89,7 @@ describe("getClosabilityGaps", () => {
|
||||||
),
|
),
|
||||||
).toEqual([
|
).toEqual([
|
||||||
"Dispatcher",
|
"Dispatcher",
|
||||||
"Technician",
|
"Company",
|
||||||
"Completed Date",
|
"Completed Date",
|
||||||
"WO number",
|
"WO number",
|
||||||
"Service",
|
"Service",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue