fix(intake): preserve confirmation after factory edits
This commit is contained in:
parent
01a9d1e2c8
commit
48e496b01b
@ -528,6 +528,147 @@ describe("NonIntegratedDetailClient factory and SKU editing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps basic information confirmed after a factory set count change", async () => {
|
||||
const actor = userEvent.setup();
|
||||
const productId = "ZWJ0218-factory-preserves-basic-confirmation";
|
||||
const basicInfoApprovedAt = "2026-08-27T03:00:00.000Z";
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
createProductRecord({
|
||||
id: productId,
|
||||
spu: "ZWJ0218",
|
||||
skuBatchConfirmedAt: CONFIRMED_SKU_BATCH_AT,
|
||||
documentWorkspaceConfirmations: {
|
||||
...createDefaultDocumentWorkspaceConfirmations(),
|
||||
[NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY]: true,
|
||||
},
|
||||
documentWorkspaceApprovedAt: {
|
||||
[NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY]: basicInfoApprovedAt,
|
||||
},
|
||||
skuItems: [
|
||||
createSkuItem({
|
||||
id: "sku-factory-preserves-basic-confirmation-1",
|
||||
skuSequence: 1,
|
||||
model: "ZWJ0218K01",
|
||||
skuCode: "ZWJ021801",
|
||||
setCount: "1",
|
||||
barcode: "ZWJ021801N396V01",
|
||||
manufacturer: "曹县嘉欢",
|
||||
manufacturerCode: "N396",
|
||||
installMethod: "整装",
|
||||
source: "sku_workbook",
|
||||
sourceTable: "sku_workbook",
|
||||
contentLocked: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
render(<NonIntegratedDetailClient mode="edit" productId={productId} />);
|
||||
await actor.click(
|
||||
screen.getByTestId("document-workspace-trigger-产品定厂"),
|
||||
);
|
||||
|
||||
const factoryWorkspace = screen.getByTestId("product-factory-workspace");
|
||||
const setCountInput =
|
||||
within(factoryWorkspace).getByPlaceholderText("请输入套件量");
|
||||
fireEvent.change(setCountInput, { target: { value: "2" } });
|
||||
fireEvent.blur(setCountInput);
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(productId);
|
||||
expect(savedProduct?.skuItems.map((item) => item.setCount)).toEqual([
|
||||
"2",
|
||||
"2",
|
||||
]);
|
||||
expect(
|
||||
savedProduct?.documentWorkspaceConfirmations?.[
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
],
|
||||
).toBe(true);
|
||||
expect(
|
||||
savedProduct?.documentWorkspaceApprovedAt?.[
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
],
|
||||
).toBe(basicInfoApprovedAt);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("keeps basic information confirmed after a factory install method change", async () => {
|
||||
const actor = userEvent.setup();
|
||||
const productId = "ZWJ0218-factory-install-preserves-confirmation";
|
||||
const basicInfoApprovedAt = "2026-08-27T03:00:00.000Z";
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
createProductRecord({
|
||||
id: productId,
|
||||
spu: "ZWJ0218",
|
||||
skuBatchConfirmedAt: CONFIRMED_SKU_BATCH_AT,
|
||||
documentWorkspaceConfirmations: {
|
||||
...createDefaultDocumentWorkspaceConfirmations(),
|
||||
[NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY]: true,
|
||||
},
|
||||
documentWorkspaceApprovedAt: {
|
||||
[NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY]: basicInfoApprovedAt,
|
||||
},
|
||||
skuItems: [
|
||||
createSkuItem({
|
||||
id: "sku-factory-install-preserves-confirmation-1",
|
||||
skuSequence: 1,
|
||||
model: "ZWJ0218K01",
|
||||
skuCode: "ZWJ021801",
|
||||
setCount: "1",
|
||||
barcode: "ZWJ021801N396V01",
|
||||
manufacturer: "曹县嘉欢",
|
||||
manufacturerCode: "N396",
|
||||
installMethod: "整装",
|
||||
source: "sku_workbook",
|
||||
sourceTable: "sku_workbook",
|
||||
contentLocked: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
render(<NonIntegratedDetailClient mode="edit" productId={productId} />);
|
||||
await actor.click(
|
||||
screen.getByTestId("document-workspace-trigger-产品定厂"),
|
||||
);
|
||||
|
||||
const factoryWorkspace = screen.getByTestId("product-factory-workspace");
|
||||
const installMethodSelect = within(factoryWorkspace).getByRole(
|
||||
"combobox",
|
||||
{ name: "安装方式" },
|
||||
);
|
||||
await actor.click(installMethodSelect);
|
||||
fireEvent.mouseDown(screen.getByRole("option", { name: "拆装" }));
|
||||
await waitFor(() => expect(installMethodSelect).toHaveTextContent("拆装"));
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(productId);
|
||||
expect(savedProduct?.skuItems.map((item) => item.installMethod)).toEqual([
|
||||
"拆装",
|
||||
]);
|
||||
expect(
|
||||
savedProduct?.factoryRows.map((row) => row.installMethod),
|
||||
).toEqual(["拆装"]);
|
||||
expect(
|
||||
savedProduct?.styleDetails.map((detail) => detail.installMethod),
|
||||
).toEqual(["拆装"]);
|
||||
expect(
|
||||
savedProduct?.documentWorkspaceConfirmations?.[
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
],
|
||||
).toBe(true);
|
||||
expect(
|
||||
savedProduct?.documentWorkspaceApprovedAt?.[
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
],
|
||||
).toBe(basicInfoApprovedAt);
|
||||
});
|
||||
});
|
||||
|
||||
it("fills the first warehouse selection by SPU and keeps later warehouse changes per SKU", async () => {
|
||||
const actor = userEvent.setup();
|
||||
|
||||
|
||||
@ -271,6 +271,7 @@ export function useNonIntegratedFormActionControllers({
|
||||
productFactoryModelAutofillRef: workflowState.productFactoryModelAutofillRef,
|
||||
productFactoryReopenField: workflowState.productFactoryReopenField,
|
||||
productFactoryRows: workflowRows.productFactoryRows,
|
||||
scheduleAutoSave,
|
||||
setDocumentWorkspaceApprovedAt: documentState.setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations:
|
||||
documentState.setDocumentWorkspaceConfirmations,
|
||||
@ -281,6 +282,9 @@ export function useNonIntegratedFormActionControllers({
|
||||
setSaveMessageType: overlayState.setSaveMessageType,
|
||||
setSkuItems: skuState.setSkuItems,
|
||||
setStyleDetails: artifactState.setStyleDetails,
|
||||
skuBatchChangeTypes: skuState.skuBatchChangeTypes,
|
||||
skuBatchCount: skuState.skuBatchCount,
|
||||
skuBatchFlowNos: skuState.skuBatchFlowNos,
|
||||
supplierCodeByName: directoryOptions.supplierCodeByName,
|
||||
workflowSkuItems: seriesDerived.workflowSkuItems,
|
||||
});
|
||||
|
||||
@ -98,7 +98,7 @@ describe("useNonIntegratedWorkflowConfirmActionController", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("still blocks confirmation when the active batch itself is incomplete", async () => {
|
||||
it("still blocks confirmation when the active batch itself is missing a required field", async () => {
|
||||
const params = createControllerParams({
|
||||
derivedSkuItems: [
|
||||
createSkuItem({
|
||||
@ -109,7 +109,7 @@ describe("useNonIntegratedWorkflowConfirmActionController", () => {
|
||||
}),
|
||||
createSkuItem({
|
||||
id: "sku-batch-2",
|
||||
installMethod: "",
|
||||
material: "",
|
||||
}),
|
||||
],
|
||||
});
|
||||
@ -126,7 +126,29 @@ describe("useNonIntegratedWorkflowConfirmActionController", () => {
|
||||
|
||||
expect(params.persistEditedProduct).not.toHaveBeenCalled();
|
||||
expect(params.setSaveMessage).toHaveBeenCalledWith(
|
||||
"基础信息未填写完整,SKU XG001254 缺少:安装方式。请补全后再确认。",
|
||||
"基础信息未填写完整,SKU XG001254 缺少:材质。请补全后再确认。",
|
||||
);
|
||||
});
|
||||
|
||||
it("allows the active batch to confirm without an install method", async () => {
|
||||
const params = createControllerParams({
|
||||
derivedSkuItems: [createSkuItem({ installMethod: "" })],
|
||||
});
|
||||
jest.spyOn(window, "confirm").mockReturnValue(true);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useNonIntegratedWorkflowConfirmActionController(params),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleConfirmWorkflowNode(
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY,
|
||||
);
|
||||
});
|
||||
|
||||
expect(params.persistEditedProduct).toHaveBeenCalledTimes(1);
|
||||
expect(params.setSaveMessage).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("安装方式"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -86,4 +86,13 @@ describe("buildSkuContentRevisionSignature", () => {
|
||||
buildSignature(createSku()),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps install method and set count revision-sensitive for direct basic information edits", () => {
|
||||
expect(buildSignature(createSku({ installMethod: "整装" }))).not.toBe(
|
||||
buildSignature(createSku()),
|
||||
);
|
||||
expect(buildSignature(createSku({ setCount: "2" }))).not.toBe(
|
||||
buildSignature(createSku()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,11 +5,12 @@ import type {
|
||||
} from "react";
|
||||
|
||||
import type { PersistEditedProductOverrides } from "../../nonIntegratedDetailTypes";
|
||||
import type {
|
||||
NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
NonIntegratedFactoryRow,
|
||||
NonIntegratedSkuItem,
|
||||
import {
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY,
|
||||
type NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
type NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
type NonIntegratedFactoryRow,
|
||||
type NonIntegratedSkuItem,
|
||||
} from "../../nonIntegratedStorage";
|
||||
import { updateStyleDetailEntry, type StyleDetailForm } from "../style-assets/styleDetailState";
|
||||
import {
|
||||
@ -32,6 +33,7 @@ import {
|
||||
updateProductFactoryRowsForSku,
|
||||
} from "./productFactoryRowUpdateHelpers";
|
||||
import { getProductFactoryTargetSkuIds } from "./productFactoryTargetSkuHelpers";
|
||||
import { buildSkuContentRevisionSignature } from "../preview/previewState";
|
||||
|
||||
type SaveMessageType = "success" | "error";
|
||||
|
||||
@ -71,6 +73,7 @@ type UseProductFactoryRowControllerParams = {
|
||||
>;
|
||||
productFactoryReopenField: ProductFactoryEditableKey;
|
||||
productFactoryRows: NonIntegratedFactoryRow[];
|
||||
scheduleAutoSave: () => void;
|
||||
setDocumentWorkspaceApprovedAt: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceApprovalMap>
|
||||
>;
|
||||
@ -83,6 +86,9 @@ type UseProductFactoryRowControllerParams = {
|
||||
setSaveMessageType: Dispatch<SetStateAction<SaveMessageType>>;
|
||||
setSkuItems: Dispatch<SetStateAction<NonIntegratedSkuItem[]>>;
|
||||
setStyleDetails: Dispatch<SetStateAction<Record<string, StyleDetailForm>>>;
|
||||
skuBatchChangeTypes: Record<string, string>;
|
||||
skuBatchCount: number;
|
||||
skuBatchFlowNos: Record<string, string>;
|
||||
supplierCodeByName: ReadonlyMap<string, string>;
|
||||
workflowSkuItems: NonIntegratedSkuItem[];
|
||||
};
|
||||
@ -106,6 +112,7 @@ export function useProductFactoryRowController({
|
||||
productFactoryModelAutofillRef,
|
||||
productFactoryReopenField,
|
||||
productFactoryRows,
|
||||
scheduleAutoSave,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
setFactoryRows,
|
||||
@ -114,9 +121,33 @@ export function useProductFactoryRowController({
|
||||
setSaveMessageType,
|
||||
setSkuItems,
|
||||
setStyleDetails,
|
||||
skuBatchChangeTypes,
|
||||
skuBatchCount,
|
||||
skuBatchFlowNos,
|
||||
supplierCodeByName,
|
||||
workflowSkuItems,
|
||||
}: UseProductFactoryRowControllerParams) {
|
||||
function setFactoryProjectedSkuItems(
|
||||
update: (current: NonIntegratedSkuItem[]) => NonIntegratedSkuItem[],
|
||||
) {
|
||||
setSkuItems((current) => {
|
||||
const nextSkuItems = update(current);
|
||||
if (documentWorkspaceRevisionSignatureRef.current) {
|
||||
documentWorkspaceRevisionSignatureRef.current = {
|
||||
...documentWorkspaceRevisionSignatureRef.current,
|
||||
[NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY]:
|
||||
buildSkuContentRevisionSignature({
|
||||
skuItems: nextSkuItems,
|
||||
skuBatchCount,
|
||||
skuBatchFlowNos,
|
||||
skuBatchChangeTypes,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return nextSkuItems;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProductFactoryRow(
|
||||
skuId: string,
|
||||
key: ProductFactoryEditableKey,
|
||||
@ -135,35 +166,34 @@ export function useProductFactoryRowController({
|
||||
);
|
||||
const supplierName = supplier.name;
|
||||
|
||||
setSkuItems((current) => {
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
workflowSkuItems,
|
||||
skuId,
|
||||
key,
|
||||
);
|
||||
return current.map((item, index) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
current.map((item, index) =>
|
||||
targetSkuIds.has(item.id)
|
||||
? applySkuSupplierName(item, index, supplierName)
|
||||
: item,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === "setCount") {
|
||||
setSkuItems((current) => {
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
workflowSkuItems,
|
||||
skuId,
|
||||
key,
|
||||
);
|
||||
return current.map((item) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
current.map((item) =>
|
||||
targetSkuIds.has(item.id) ? { ...item, setCount: value } : item,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === "minimumOrderQuantity") {
|
||||
setSkuItems((current) => {
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
workflowSkuItems,
|
||||
skuId,
|
||||
@ -172,16 +202,16 @@ export function useProductFactoryRowController({
|
||||
value,
|
||||
productFactoryModelAutofillRef.current,
|
||||
);
|
||||
return current.map((item) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
current.map((item) =>
|
||||
targetSkuIds.has(item.id)
|
||||
? { ...item, minimumOrderQuantity: value }
|
||||
: item,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === "installMethod") {
|
||||
setSkuItems((current) => {
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
workflowSkuItems,
|
||||
skuId,
|
||||
@ -206,10 +236,12 @@ export function useProductFactoryRowController({
|
||||
});
|
||||
}
|
||||
|
||||
return current.map((item) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
current.map((item) =>
|
||||
targetSkuIds.has(item.id) ? { ...item, installMethod: value } : item,
|
||||
),
|
||||
);
|
||||
});
|
||||
scheduleAutoSave();
|
||||
}
|
||||
|
||||
setFactoryRows((current) =>
|
||||
@ -238,7 +270,7 @@ export function useProductFactoryRowController({
|
||||
const supplierName = supplier.name;
|
||||
|
||||
if (supplierName) {
|
||||
setSkuItems((current) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
current.map((item, index) =>
|
||||
item.id === skuId
|
||||
? applySkuSupplierName(item, index, supplierName)
|
||||
@ -390,7 +422,7 @@ export function useProductFactoryRowController({
|
||||
);
|
||||
if (currentFactoryRow?.confirmed) return;
|
||||
|
||||
setSkuItems((current) =>
|
||||
setFactoryProjectedSkuItems((current) =>
|
||||
buildSkuItemsAfterSetCountCommit({
|
||||
allowImportedContentLocked: true,
|
||||
current,
|
||||
|
||||
@ -11,7 +11,6 @@ type BasicInfoRequiredSkuField =
|
||||
| "versionNo"
|
||||
| "attributeCode"
|
||||
| "drawingId"
|
||||
| "installMethod"
|
||||
| "executeStandard"
|
||||
| "nationalStandardCompliant"
|
||||
| "batchNo";
|
||||
@ -30,7 +29,6 @@ type BasicInfoConfirmationSku = {
|
||||
versionNo: string;
|
||||
attributeCode: string;
|
||||
drawingId: string;
|
||||
installMethod: string;
|
||||
executeStandard: string;
|
||||
nationalStandardCompliant: string;
|
||||
batchNo: number;
|
||||
@ -52,7 +50,6 @@ export const BASIC_INFO_CONFIRMATION_REQUIRED_FIELDS: ReadonlyArray<{
|
||||
{ key: "versionNo", label: "版本号" },
|
||||
{ key: "attributeCode", label: "属性代码" },
|
||||
{ key: "drawingId", label: "图纸ID" },
|
||||
{ key: "installMethod", label: "安装方式" },
|
||||
{ key: "executeStandard", label: "执行标准" },
|
||||
{ key: "nationalStandardCompliant", label: "符合国标" },
|
||||
{ key: "batchNo", label: "创建批次" },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user