fix(work-orders): show duplicate-WO copy for edit conflicts (SH-375)

A duplicate work order number edit returns 422 DuplicateWoNumber from the
board PATCH endpoint, which mapped to the generic save-failed copy. Add the
code to the known validation copy map so edits reuse the same duplicate-WO
product text as create, and share the message constant across both paths.
This commit is contained in:
Codex Review Integration 2026-09-17 06:15:23 -03:00
parent 5c093e8c26
commit b551421115
4 changed files with 61 additions and 4 deletions

View file

@ -1,12 +1,12 @@
import { isHTTPError, isNetworkError, isTimeoutError } from "ky";
import { mapHttpStatusToMessage } from "@/api/api-error";
import { DISPATCH_REQUIRED_MESSAGE } from "@/domain/work-orders/errors/board-patch-errors";
import {
DISPATCH_REQUIRED_MESSAGE,
DUPLICATE_WO_NUMBER_MESSAGE,
} from "@/domain/work-orders/errors/board-patch-errors";
export const BOARD_CREATE_FAILED_MESSAGE = "Unable to create the work order. Please try again.";
const DUPLICATE_WO_NUMBER_MESSAGE =
"This work order number is already assigned to another work order.";
/** Statuses whose mapped copy is fixed product text, never the response body. */
const FIXED_COPY_STATUSES = new Set([401, 403, 404, 500]);

View file

@ -31,9 +31,15 @@ export const BOARD_PATCH_FAILED_MESSAGE = "Unable to save the work order. Please
export const NO_PERSISTABLE_FIELDS_MESSAGE =
"These changes cannot be saved on the board. Refresh and edit a supported field.";
export const DUPLICATE_WO_NUMBER_MESSAGE =
"This work order number is already assigned to another work order.";
const KNOWN_VALIDATION_COPY: ReadonlyMap<string, string> = new Map([
["DispatchRequired", DISPATCH_REQUIRED_MESSAGE],
["NoPersistableFields", NO_PERSISTABLE_FIELDS_MESSAGE],
// why: editing a WO number to one that already exists returns 422 DuplicateWoNumber;
// show the same product copy as create, not the generic save-failed fallback.
["DuplicateWoNumber", DUPLICATE_WO_NUMBER_MESSAGE],
]);
/**

View file

@ -615,6 +615,28 @@ describe("workOrdersApi.patchBoardField", () => {
}),
).rejects.toBeInstanceOf(BoardPatchConflictError);
});
it("throws BoardPatchValidationError with DuplicateWoNumber code on 422", async () => {
apiPatchFn.mockResolvedValue({
ok: false,
status: 422,
json: async () => ({
code: "DuplicateWoNumber",
message: "WO number already exists.",
}),
});
await expect(
workOrdersApi.patchBoardField(10, {
field: "woNumber",
value: "12345678901",
workOrderVersion: "AAAAAAA=",
}),
).rejects.toMatchObject({
name: "BoardPatchValidationError",
code: "DuplicateWoNumber",
});
});
});
describe("workOrdersApi.createComment", () => {

View file

@ -0,0 +1,29 @@
import { describe, expect, it } from "vitest";
import {
BOARD_PATCH_CONFLICT_MESSAGE,
BOARD_PATCH_FAILED_MESSAGE,
BoardPatchConflictError,
BoardPatchValidationError,
DUPLICATE_WO_NUMBER_MESSAGE,
boardPatchErrorMessage,
} from "@/domain/work-orders/errors/board-patch-errors";
describe("boardPatchErrorMessage", () => {
it("maps a DuplicateWoNumber validation error to the duplicate-WO product copy", () => {
const error = new BoardPatchValidationError("DuplicateWoNumber", "WO number already exists.");
expect(boardPatchErrorMessage(error)).toBe(DUPLICATE_WO_NUMBER_MESSAGE);
});
it("never leaks backend validation text for unknown codes", () => {
const error = new BoardPatchValidationError("SomethingElse", "vendorId is required.");
expect(boardPatchErrorMessage(error)).toBe(BOARD_PATCH_FAILED_MESSAGE);
});
it("maps a conflict error to the concurrent-edit copy", () => {
const error = new BoardPatchConflictError("stale", null);
expect(boardPatchErrorMessage(error)).toBe(BOARD_PATCH_CONFLICT_MESSAGE);
});
});