fix(work-orders): derive lifecycle status only when the schedule patch changes

This commit is contained in:
Arthur Bassi 2026-09-03 13:35:26 -03:00
parent c2d1add79c
commit 9fc67516ca
5 changed files with 37 additions and 26 deletions

View file

@ -2,7 +2,7 @@ import type { LocationOption, WorkOrderUser } from "@/domain/work-orders/types/w
import type { WorkOrderTableDraft } from "@/domain/work-orders/types/work-order-table-row";
import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
import { resolveSiteCode } from "@/domain/work-orders/mappers/work-order-board-mapper";
import { maybeAutoSchedulePatch } from "@/domain/work-orders/utils/wo-lifecycle-transitions";
import { resolveCreationStatus } from "@/domain/work-orders/utils/wo-lifecycle-transitions";
export function buildInlineDraftCreateInput(
draft: WorkOrderTableDraft,
@ -15,14 +15,6 @@ export function buildInlineDraftCreateInput(
const hasWeekTarget = Boolean(draft.targetWeek?.trim());
const scheduleWeekOnly = draft.scheduleWeekOnly && (hasWeekTarget || Boolean(draft.scheduledOn));
const status = draft.status || "Incomplete";
const autoStatus = maybeAutoSchedulePatch({
status,
scheduledOn: draft.scheduledOn,
scheduleWeekOnly,
}).status;
const resolvedStatus =
autoStatus === "Scheduled" || autoStatus === "Pending" ? autoStatus : status;
return {
locationId: String(draft.locationId || ""),
@ -45,7 +37,11 @@ export function buildInlineDraftCreateInput(
vendorNotes: "",
apptStart: draft.apptStart,
apptEnd: draft.apptEnd,
status: resolvedStatus,
status: resolveCreationStatus({
status: draft.status || "Incomplete",
scheduledOn: draft.scheduledOn,
scheduleWeekOnly,
}),
pocName: draft.pocName,
pocPhone: draft.pocPhone,
pocNotes: draft.pocNotes,

View file

@ -19,6 +19,7 @@ import {
import {
CREATION_INLINE_STATUSES,
getStatusOptionState,
withAutoScheduleStatus,
} from "@/domain/work-orders/utils/wo-lifecycle-transitions";
import { WizardFieldSelect } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select";
import {
@ -277,7 +278,7 @@ export function WizardStepVendorTime({
if (option.disabled) {
return;
}
onUpdate("status", status as WizardWOStatus);
onPatch(withAutoScheduleStatus(draft, { status: status as WizardWOStatus }));
}}
/>
</div>

View file

@ -130,10 +130,16 @@ export function useWorkOrderTableMutations(
const patchMutation = useMutation({
meta: { suppressErrorToast: true },
mutationFn: ({ id, patch }: { id: string | number; patch: WorkOrderTablePatch }) =>
enqueuePatch(id, () => runWorkOrderTablePatch(id, patch, getRow)),
onMutate: async ({ id, patch }) => {
onPatch(id, patch);
mutationFn: ({
id,
persistPatch,
}: {
id: string | number;
patch: WorkOrderTablePatch;
persistPatch: WorkOrderTablePatch;
}) => enqueuePatch(id, () => runWorkOrderTablePatch(id, persistPatch, getRow)),
onMutate: async ({ id, persistPatch }) => {
onPatch(id, persistPatch);
},
onSuccess: (result, { id }) => {
if (result.kind === "board") {
@ -168,14 +174,17 @@ export function useWorkOrderTableMutations(
mergeAutoSchedulePatch(getRow?.(id), patch);
const patchField: WorkOrderTablePatchFn = (id, patch, options) => {
void patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) }).then(
const persistPatch = resolvePatch(id, patch);
void patchMutation.mutateAsync({ id, patch, persistPatch }).then(
() => options?.onSuccess?.(),
(error: Error) => options?.onError?.(error),
);
};
const patchFieldAsync = (id: string | number, patch: WorkOrderTablePatch) =>
patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) });
const patchFieldAsync = (id: string | number, patch: WorkOrderTablePatch) => {
const persistPatch = resolvePatch(id, patch);
return patchMutation.mutateAsync({ id, patch, persistPatch });
};
return {
patchField,

View file

@ -151,15 +151,11 @@ function patchTouchesSchedule(patch: WorkOrderTablePatch): boolean {
);
}
function patchTouchesScheduleOrStatus(patch: WorkOrderTablePatch): boolean {
return patch.status !== undefined || patchTouchesSchedule(patch);
}
export function mergeAutoSchedulePatch(
row: WorkOrderTableRow | undefined,
patch: WorkOrderTablePatch,
): WorkOrderTablePatch {
if (!row || !patchTouchesScheduleOrStatus(patch)) {
if (!row || !patchTouchesSchedule(patch)) {
return patch;
}
const merged = {
@ -187,3 +183,12 @@ export function withAutoScheduleStatus<T extends ScheduleDateInput & { status?:
};
return { ...schedulePatch, ...maybeAutoSchedulePatch(next) };
}
/** Normalize create-time status after schedule auto-derive rules. */
export function resolveCreationStatus(row: ScheduleDateInput & { status: string }): WizardWOStatus {
const autoStatus = maybeAutoSchedulePatch(row).status;
if (autoStatus === "Scheduled" || autoStatus === "Pending") {
return autoStatus;
}
return (row.status || "Incomplete") as WizardWOStatus;
}

View file

@ -103,15 +103,15 @@ describe("wo-lifecycle-transitions", () => {
expect(mergeAutoSchedulePatch(row, { vendorId: "9" })).toEqual({ vendorId: "9" });
});
it("reapplies auto-schedule after a Pending or Incomplete pick on a dated row", () => {
it("keeps explicit status picks on dated rows", () => {
const row = {
status: "Scheduled",
scheduledOn: "2026-06-01",
scheduleWeekOnly: false,
} as WorkOrderTableRow;
expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Scheduled" });
expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Pending" });
expect(mergeAutoSchedulePatch(row, { status: "Incomplete" })).toEqual({
status: "Scheduled",
status: "Incomplete",
});
});