From 71e746b5f5ae1a8fb3d1db465c7708907d35c51f Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Wed, 26 Aug 2026 16:23:46 +0800 Subject: [PATCH] fix(intake): isolate basic info confirmation by batch --- ...tegratedDocumentActionControllers.test.tsx | 6 + ...NonIntegratedDocumentActionControllers.tsx | 1 + ...edWorkflowConfirmActionController.test.tsx | 132 ++++++++++++++++++ ...tegratedWorkflowConfirmActionController.ts | 7 +- 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.test.tsx diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.test.tsx b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.test.tsx index 3dbff7e0..f5124fa3 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.test.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.test.tsx @@ -83,6 +83,7 @@ describe("useNonIntegratedDocumentActionControllers", () => { }, documentState: { activeDocumentWorkspaceCategory: "basicInfo", + activeWorkflowBatchNo: 2, documentWorkspaceApprovedAt: {}, documentWorkspaceConfirmations: {}, selectedUploadFileIds: new Set(), @@ -148,6 +149,11 @@ describe("useNonIntegratedDocumentActionControllers", () => { }); expect(result.current.basicInfoConfirmationAction).toBeTruthy(); expect(result.current.documentWorkspaceConfirmationAction).toBeTruthy(); + expect( + jest.mocked(useNonIntegratedWorkflowConfirmActionController), + ).toHaveBeenCalledWith( + expect.objectContaining({ activeWorkflowBatchNo: 2 }), + ); act(() => { expect( diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.tsx b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.tsx index 63e2f639..575b2f65 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedDocumentActionControllers.tsx @@ -185,6 +185,7 @@ export function useNonIntegratedDocumentActionControllers({ }); const workflowConfirmActions = useNonIntegratedWorkflowConfirmActionController({ + activeWorkflowBatchNo: documentState.activeWorkflowBatchNo, basicInfoHasContent: documentDerived.basicInfoHasContent, basicInfoWorkspaceCategory: NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY, diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.test.tsx b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.test.tsx new file mode 100644 index 00000000..d13c0703 --- /dev/null +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.test.tsx @@ -0,0 +1,132 @@ +import { act, renderHook } from "@testing-library/react"; + +import { NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY } from "./core/nonIntegratedStorage"; +import type { NonIntegratedSkuItem } from "./core/nonIntegratedStorage"; +import { useNonIntegratedWorkflowConfirmActionController } from "./useNonIntegratedWorkflowConfirmActionController"; + +function createSkuItem( + overrides: Partial, +): NonIntegratedSkuItem { + return { + id: "sku-1", + model: "XG0012K01", + skuCode: "XG001254", + barcode: "XG001254N160V01", + setCount: "1", + specName: "款式一 100/17/130 珍珠白色", + size: "100/17/130", + color: "珍珠白色", + material: "12+15密度板", + boardMaterialType: "非ENF级", + versionNo: "V01", + attributeCode: "A", + drawingId: "XG0012K01AV01", + installMethod: "整装", + executeStandard: "T/CZFA 0001-2025", + nationalStandardCompliant: "否", + batchNo: 2, + ...overrides, + } as NonIntegratedSkuItem; +} + +function createControllerParams({ + activeWorkflowBatchNo = 2, + derivedSkuItems, +}: { + activeWorkflowBatchNo?: number; + derivedSkuItems: NonIntegratedSkuItem[]; +}) { + return { + activeWorkflowBatchNo, + basicInfoHasContent: true, + basicInfoWorkspaceCategory: NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY, + derivedSkuItems, + documentWorkspaceApprovedAt: {}, + documentWorkspaceConfirmations: {}, + documentWorkspaceCountByCategory: {}, + documentWorkspaceRevisionSignatureRef: { current: null }, + effectiveSelectedReviewRoundId: "", + isReadOnly: false, + isWorkflowNodeConfirmed: jest.fn(() => false), + persistEditedProduct: jest.fn(async () => true), + reviewRounds: [], + setDocumentWorkspaceApprovedAt: jest.fn(), + setDocumentWorkspaceConfirmations: jest.fn(), + setFinishedSampleShipmentRows: jest.fn(), + setReviewRounds: jest.fn(), + setSaveMessage: jest.fn(), + setSaveMessageType: jest.fn(), + uploadFiles: [], + } as unknown as Parameters< + typeof useNonIntegratedWorkflowConfirmActionController + >[0]; +} + +describe("useNonIntegratedWorkflowConfirmActionController", () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("confirms the active batch without revalidating a completed historical batch", async () => { + const params = createControllerParams({ + derivedSkuItems: [ + createSkuItem({ + id: "sku-batch-1", + skuCode: "XG001251", + barcode: "XG001251N160V01", + batchNo: 1, + installMethod: "", + }), + createSkuItem({ id: "sku-batch-2" }), + ], + }); + 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("XG001251"), + ); + }); + + it("still blocks confirmation when the active batch itself is incomplete", async () => { + const params = createControllerParams({ + derivedSkuItems: [ + createSkuItem({ + id: "sku-batch-1", + skuCode: "XG001251", + barcode: "XG001251N160V01", + batchNo: 1, + }), + createSkuItem({ + id: "sku-batch-2", + installMethod: "", + }), + ], + }); + + const { result } = renderHook(() => + useNonIntegratedWorkflowConfirmActionController(params), + ); + + await act(async () => { + await result.current.handleConfirmWorkflowNode( + NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY, + ); + }); + + expect(params.persistEditedProduct).not.toHaveBeenCalled(); + expect(params.setSaveMessage).toHaveBeenCalledWith( + "基础信息未填写完整,SKU XG001254 缺少:安装方式。请补全后再确认。", + ); + }); +}); diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.ts b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.ts index 0043850c..d9fe922e 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmActionController.ts @@ -51,6 +51,7 @@ type PersistEditedProduct = (params: { }) => Promise; type UseNonIntegratedWorkflowConfirmActionControllerParams = { + activeWorkflowBatchNo: number; basicInfoHasContent: boolean; basicInfoWorkspaceCategory: WorkflowNodeCategory; derivedSkuItems: NonIntegratedSkuItem[]; @@ -84,6 +85,7 @@ type UseNonIntegratedWorkflowConfirmActionControllerParams = { }; export function useNonIntegratedWorkflowConfirmActionController({ + activeWorkflowBatchNo, basicInfoHasContent, basicInfoWorkspaceCategory, derivedSkuItems, @@ -128,8 +130,11 @@ export function useNonIntegratedWorkflowConfirmActionController({ } if (category === basicInfoWorkspaceCategory) { + const activeBatchSkuItems = derivedSkuItems.filter( + (item) => (item.batchNo || 1) === activeWorkflowBatchNo, + ); const validationMessage = getBasicInfoConfirmationValidationMessage( - derivedSkuItems, + activeBatchSkuItems, ); if (validationMessage) { setSaveMessage(validationMessage);