merge: 解决冲突,保留使用 SharedAssetFamilyDTO 的类型修复
This commit is contained in:
commit
e7d2c8626c
@ -18,8 +18,7 @@ interface VariantDTO {
|
||||
}
|
||||
|
||||
interface AssemblyWorkspaceSnapshot {
|
||||
assetSeriesId?: string
|
||||
[key: string]: unknown
|
||||
assetSeriesId?: string | null
|
||||
}
|
||||
|
||||
type InspectorTabKey = 'assembly' | 'ai'
|
||||
@ -86,7 +85,7 @@ export default function AssistedAssemblyMobileView({
|
||||
</div>
|
||||
|
||||
{/* 系列列表(未选择时显示) */}
|
||||
{!selectedSeriesId && assetSeries.length > 0 && (
|
||||
{!selectedAssetSeriesId && assetSeries.length > 0 && (
|
||||
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
||||
<div className="space-y-2 px-4 py-2">
|
||||
{assetSeries.map((series) => (
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
NON_INTEGRATED_DOCUMENT_WORKSPACE_CATEGORIES,
|
||||
replaceNonIntegratedProducts,
|
||||
saveNonIntegratedProduct,
|
||||
type NonIntegratedFactoryRow,
|
||||
type NonIntegratedSkuItem,
|
||||
type NonIntegratedProductRecord,
|
||||
} from "./nonIntegratedStorage";
|
||||
@ -454,6 +455,38 @@ function createSkuItem(
|
||||
};
|
||||
}
|
||||
|
||||
function createFactoryRow(
|
||||
overrides: Partial<NonIntegratedFactoryRow> = {},
|
||||
): NonIntegratedFactoryRow {
|
||||
return {
|
||||
skuId: "sku-test-1",
|
||||
confirmed: false,
|
||||
model: "SZ0230K01",
|
||||
skuCode: "SZ023001",
|
||||
setCount: "1",
|
||||
barcode: "SZ023001N1280V01",
|
||||
specName: "款式一 140/60/75",
|
||||
size: "140/60/75",
|
||||
color: "胡桃色",
|
||||
material: "橡胶木",
|
||||
installMethod: "拆装",
|
||||
supplierName: "兰考华兰家具",
|
||||
supplierCode: "N1280",
|
||||
warehouse: "郑州仓",
|
||||
shippingMethod: "入仓",
|
||||
minimumOrderQuantity: "50",
|
||||
taxIncludedUnitPrice: "",
|
||||
leadTime: "30天",
|
||||
assemblyMode: "",
|
||||
productionType: "OEM",
|
||||
installationService: "送货",
|
||||
supplierCategory: "主力供应商",
|
||||
purchaseOrderMethod: "订单式",
|
||||
executeStandard: "GB/T 3324",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createMaterialFinish(
|
||||
overrides: Partial<MaterialDTO> & Pick<MaterialDTO, "id" | "name">,
|
||||
): MaterialDTO {
|
||||
@ -4446,7 +4479,7 @@ describe("NonIntegratedDetailClient", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shares product factory selection fields by model without changing SKU content", async () => {
|
||||
it("shares product factory selection fields by SPU without changing SKU content", async () => {
|
||||
const actor = userEvent.setup();
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
@ -4573,12 +4606,12 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect(savedProduct?.factoryRows.map((row) => row.warehouse)).toEqual([
|
||||
"郑州仓",
|
||||
"郑州仓",
|
||||
"",
|
||||
"郑州仓",
|
||||
]);
|
||||
expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([
|
||||
"代发",
|
||||
"代发",
|
||||
"",
|
||||
"代发",
|
||||
]);
|
||||
expect(
|
||||
savedProduct?.factoryRows.map((row) => row.minimumOrderQuantity),
|
||||
@ -4586,7 +4619,7 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([
|
||||
"OEM",
|
||||
"OEM",
|
||||
"",
|
||||
"OEM",
|
||||
]);
|
||||
expect(
|
||||
savedProduct?.factoryRows.map((row) => row.installationService),
|
||||
@ -4594,11 +4627,11 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect(savedProduct?.factoryRows.map((row) => row.supplierCategory)).toEqual([
|
||||
"主力供应商",
|
||||
"主力供应商",
|
||||
"",
|
||||
"主力供应商",
|
||||
]);
|
||||
expect(
|
||||
savedProduct?.factoryRows.map((row) => row.purchaseOrderMethod),
|
||||
).toEqual(["订单式", "订单式", ""]);
|
||||
).toEqual(["订单式", "订单式", "订单式"]);
|
||||
expect(savedProduct?.skuItems.map((item) => item.manufacturer)).toEqual([
|
||||
"曹县嘉欢",
|
||||
"曹县嘉欢",
|
||||
@ -4640,17 +4673,17 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect(savedProduct?.factoryRows.map((row) => row.warehouse)).toEqual([
|
||||
"杭州仓",
|
||||
"杭州仓",
|
||||
"",
|
||||
"杭州仓",
|
||||
]);
|
||||
expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([
|
||||
"入仓",
|
||||
"入仓",
|
||||
"",
|
||||
"入仓",
|
||||
]);
|
||||
expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([
|
||||
"ODM",
|
||||
"ODM",
|
||||
"",
|
||||
"ODM",
|
||||
]);
|
||||
expect(savedProduct?.skuItems.map((item) => item.manufacturer)).toEqual([
|
||||
"曹县嘉欢",
|
||||
@ -4679,16 +4712,82 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([
|
||||
"入仓",
|
||||
"入仓",
|
||||
"",
|
||||
"入仓",
|
||||
]);
|
||||
expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([
|
||||
"ODM",
|
||||
"ODM",
|
||||
"",
|
||||
"ODM",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("requires product factory rows to be manually confirmed after all fields are filled", async () => {
|
||||
const actor = userEvent.setup();
|
||||
const productId = "SZ0230-product-factory-manual-confirm";
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
createProductRecord({
|
||||
id: productId,
|
||||
spu: "SZ0230",
|
||||
skuBatchConfirmedAt: CONFIRMED_SKU_BATCH_AT,
|
||||
skuItems: [
|
||||
createSkuItem({
|
||||
id: "sku-factory-confirm-1",
|
||||
model: "SZ0230K01",
|
||||
skuCode: "SZ023001",
|
||||
barcode: "SZ023001N1280V01",
|
||||
manufacturer: "兰考华兰家具",
|
||||
manufacturerCode: "N1280",
|
||||
installMethod: "拆装",
|
||||
executeStandard: "GB/T 3324",
|
||||
}),
|
||||
],
|
||||
factoryRows: [
|
||||
createFactoryRow({
|
||||
skuId: "sku-factory-confirm-1",
|
||||
confirmed: false,
|
||||
model: "SZ0230K01",
|
||||
skuCode: "SZ023001",
|
||||
barcode: "SZ023001N1280V01",
|
||||
}),
|
||||
],
|
||||
documentWorkspaceConfirmations:
|
||||
createDefaultDocumentWorkspaceConfirmations(false),
|
||||
}),
|
||||
);
|
||||
|
||||
render(
|
||||
<NonIntegratedDetailClient mode="edit" productId={productId} />,
|
||||
);
|
||||
|
||||
await actor.click(screen.getByTestId("document-workspace-trigger-产品定厂"));
|
||||
|
||||
const confirmButton = screen.getByTestId("product-factory-confirm");
|
||||
expect(confirmButton).toHaveTextContent("资料已填完核对无误请确认。");
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(productId);
|
||||
expect(savedProduct?.factoryRows?.[0]?.confirmed).toBe(false);
|
||||
expect(savedProduct?.documentWorkspaceConfirmations?.产品定厂).not.toBe(
|
||||
true,
|
||||
);
|
||||
expect(savedProduct?.documentWorkspaceApprovedAt?.产品定厂).toBeUndefined();
|
||||
});
|
||||
|
||||
await actor.click(confirmButton);
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(productId);
|
||||
expect(savedProduct?.factoryRows?.[0]?.confirmed).toBe(true);
|
||||
expect(savedProduct?.documentWorkspaceConfirmations?.产品定厂).toBe(true);
|
||||
expect(savedProduct?.documentWorkspaceApprovedAt?.产品定厂).toEqual(
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
expect(confirmButton).toHaveTextContent("已确认");
|
||||
});
|
||||
|
||||
it("persists the SKU national standard compliance selection", async () => {
|
||||
const actor = userEvent.setup();
|
||||
|
||||
@ -6447,8 +6546,16 @@ describe("NonIntegratedDetailClient", () => {
|
||||
weightKg: "18.5",
|
||||
},
|
||||
]);
|
||||
expect(savedProduct?.documentWorkspaceConfirmations?.样包测量).not.toBe(
|
||||
true,
|
||||
);
|
||||
expect(savedProduct?.documentWorkspaceApprovedAt?.样包测量).toBeUndefined();
|
||||
});
|
||||
|
||||
const confirmButton = screen.getByTestId("sample-measurement-confirm");
|
||||
expect(confirmButton).toHaveTextContent("资料已填完核对无误请确认。");
|
||||
await actor.click(confirmButton);
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(
|
||||
"SZ0230-sample-measurement",
|
||||
@ -6458,6 +6565,7 @@ describe("NonIntegratedDetailClient", () => {
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
expect(confirmButton).toHaveTextContent("已确认");
|
||||
|
||||
expect(
|
||||
screen.getByTestId("sample-measurement-lengthCm-sku-sample-1"),
|
||||
|
||||
@ -1370,7 +1370,9 @@ export default function NonIntegratedDetailClient({
|
||||
isReadOnly || isSeriesSkuComponentAggregateMode;
|
||||
const {
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsConfirmed,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsConfirmedByPricing,
|
||||
allSampleMeasurementRowsComplete,
|
||||
confirmedProductFactoryRowCount,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
@ -1383,6 +1385,7 @@ export default function NonIntegratedDetailClient({
|
||||
displayedProductPricingRowsAllSystemEntered,
|
||||
displayedProductPricingRowsComplete,
|
||||
displayedProductPricingRowsReadyForPush,
|
||||
displayedSampleMeasurementRowsConfirmedByPricing,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
isSampleMeasurementSkuLocked,
|
||||
planningLayoutHasContent,
|
||||
@ -1460,6 +1463,9 @@ export default function NonIntegratedDetailClient({
|
||||
const activeDocumentWorkspaceHasContent =
|
||||
(documentWorkspaceCountByCategory[activeDocumentWorkspaceCategory] ?? 0) >
|
||||
0;
|
||||
const displayedSampleMeasurementHasPendingRows =
|
||||
displayedProductSampleMeasurementRows.length > 0 &&
|
||||
!displayedSampleMeasurementRowsComplete;
|
||||
const {
|
||||
basicInfoDisplayCount,
|
||||
basicInfoIsApproved,
|
||||
@ -1476,19 +1482,19 @@ export default function NonIntegratedDetailClient({
|
||||
isWorkflowNodeConfirmed,
|
||||
} = useNonIntegratedWorkflowConfirmationState({
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
allProductFactoryRowsConfirmed,
|
||||
allSampleMeasurementRowsConfirmedByPricing,
|
||||
basicInfoHasContent,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
displayedFirstQualityRowsComplete,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductLimitRowsComplete,
|
||||
displayedProductSampleMeasurementRows,
|
||||
displayedSampleMeasurementRowsConfirmedByPricing,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatures,
|
||||
isSampleMeasurementSkuLocked,
|
||||
previousDocumentWorkspaceRevisionSignatures:
|
||||
documentWorkspaceRevisionSignatureRef.current,
|
||||
productPricingCanBeConfirmed,
|
||||
@ -1498,20 +1504,13 @@ export default function NonIntegratedDetailClient({
|
||||
});
|
||||
useNonIntegratedWorkflowAutoConfirmationEffects({
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
existingProductCreatedAt,
|
||||
factoryRows,
|
||||
finishedSampleShipmentRows,
|
||||
isReadOnly,
|
||||
mode,
|
||||
productFactoryRows,
|
||||
productId,
|
||||
sampleMeasurementRows,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
setFactoryRows,
|
||||
supplierCodeByName,
|
||||
});
|
||||
useNonIntegratedWorkflowRevisionResetEffect({
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
@ -1803,6 +1802,7 @@ export default function NonIntegratedDetailClient({
|
||||
documentWorkspaceConfirmations;
|
||||
const nextDocumentWorkspaceApprovedAt =
|
||||
overrides?.documentWorkspaceApprovedAt ?? documentWorkspaceApprovedAt;
|
||||
const nextFactoryRows = overrides?.factoryRows ?? productFactoryRows;
|
||||
const nextReviewRounds = overrides?.reviewRounds ?? reviewRounds;
|
||||
const nextFinishedSampleShipmentRows =
|
||||
overrides?.finishedSampleShipmentRows ?? productFinishedSampleShipmentRows;
|
||||
@ -1810,6 +1810,8 @@ export default function NonIntegratedDetailClient({
|
||||
overrides?.openingReference ?? openingReferenceRef.current;
|
||||
const nextSkuItems = overrides?.skuItems ?? skuItems;
|
||||
const nextPricingRows = overrides?.pricingRows ?? productPricingRows;
|
||||
const nextSampleMeasurementRows =
|
||||
overrides?.sampleMeasurementRows ?? productSampleMeasurementRows;
|
||||
const nextSkuBatchCount = overrides?.skuBatchCount ?? skuBatchCount;
|
||||
const nextSkuBatchConfirmedAt =
|
||||
overrides?.skuBatchConfirmedAt ?? skuBatchConfirmedAt;
|
||||
@ -1874,11 +1876,11 @@ export default function NonIntegratedDetailClient({
|
||||
bomDetails,
|
||||
nextReviewRounds,
|
||||
qualityInspectionSheet,
|
||||
productFactoryRows,
|
||||
nextFactoryRows,
|
||||
nextPricingRows,
|
||||
nextPlanningLayoutRows,
|
||||
productLimitRows,
|
||||
productSampleMeasurementRows,
|
||||
nextSampleMeasurementRows,
|
||||
nextFinishedSampleShipmentRows,
|
||||
productFirstQualityRows,
|
||||
uploadFiles,
|
||||
@ -2585,13 +2587,16 @@ export default function NonIntegratedDetailClient({
|
||||
derivedSkuItems,
|
||||
displayedProductFactoryHasPendingRows,
|
||||
displayedProductFactoryHasSystemEnteredPricingRows,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductFactoryRows,
|
||||
displayedProductFactoryRowsComplete,
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
isCurrentUserAdmin,
|
||||
isReadOnly,
|
||||
isSkuItemFactoryConfirmed,
|
||||
persistEditedProduct,
|
||||
productFactoryModelAutofillRef,
|
||||
productFactoryReopenField,
|
||||
productFactoryRows,
|
||||
@ -2631,7 +2636,7 @@ export default function NonIntegratedDetailClient({
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
isProductFactorySkuComplete,
|
||||
isProductFactorySkuConfirmed: isSkuItemFactoryConfirmed,
|
||||
isProductLimitNodeLocked,
|
||||
isProductPricingNodeLocked,
|
||||
isReadOnly,
|
||||
@ -2643,6 +2648,8 @@ export default function NonIntegratedDetailClient({
|
||||
planningLayoutRowsRef,
|
||||
pricingRows,
|
||||
productId,
|
||||
productSampleMeasurementRows,
|
||||
sampleMeasurementRows,
|
||||
scheduleAutoSave,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
@ -2879,11 +2886,20 @@ export default function NonIntegratedDetailClient({
|
||||
handleFinishedSampleShipmentStatusClick,
|
||||
handleSampleMeasurementStatusClick,
|
||||
} = useSampleWorkflowStatusController({
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
displayedFinishedSampleShipmentRows,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductSampleMeasurementRows,
|
||||
displayedSampleMeasurementIsConfirmed,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
isReadOnly,
|
||||
persistEditedProduct,
|
||||
productSampleMeasurementRows,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
setSaveMessage,
|
||||
setSaveMessageType,
|
||||
});
|
||||
@ -3820,7 +3836,7 @@ export default function NonIntegratedDetailClient({
|
||||
allRows: productSampleMeasurementRows,
|
||||
displayedRows: displayedProductSampleMeasurementRows,
|
||||
isReadOnly,
|
||||
isProductFactorySkuComplete,
|
||||
isProductFactorySkuConfirmed: isSkuItemFactoryConfirmed,
|
||||
isSampleMeasurementSkuLocked,
|
||||
onUpdateRow: updateSampleMeasurementRow,
|
||||
},
|
||||
@ -4320,9 +4336,12 @@ export default function NonIntegratedDetailClient({
|
||||
documentWorkspaceCountByCategory
|
||||
}
|
||||
documentWorkspaceView={documentWorkspaceView}
|
||||
isProductFactoryRowComplete={isProductFactoryRowComplete}
|
||||
isSampleMeasurementConfirmed={
|
||||
displayedSampleMeasurementIsConfirmed
|
||||
}
|
||||
isWorkflowNodeConfirmed={isWorkflowNodeConfirmed}
|
||||
productFactoryRows={productFactoryRows}
|
||||
sampleMeasurementRows={productSampleMeasurementRows}
|
||||
visibleGroupedUploadFiles={visibleGroupedUploadFiles}
|
||||
onOpenBasicInfo={() => setDocumentWorkspaceView("detail")}
|
||||
onOpenDocumentWorkspace={handleOpenDocumentWorkspace}
|
||||
@ -4781,6 +4800,7 @@ export default function NonIntegratedDetailClient({
|
||||
displayedProductPricingRowsReadyForPush,
|
||||
displayedProductSampleMeasurementRowCount:
|
||||
displayedProductSampleMeasurementRows.length,
|
||||
displayedSampleMeasurementHasPendingRows,
|
||||
displayedSampleMeasurementIsConfirmed,
|
||||
documentWorkspaceConfirmationAction:
|
||||
renderDocumentWorkspaceConfirmationAction(),
|
||||
|
||||
@ -2,6 +2,7 @@ import type { MaterialDTO } from "@/server/services";
|
||||
import type {
|
||||
NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
NonIntegratedFactoryRow,
|
||||
NonIntegratedFinishedSampleShipmentRow,
|
||||
NonIntegratedOpeningReference,
|
||||
NonIntegratedProductRecord,
|
||||
@ -11,6 +12,7 @@ import type {
|
||||
NonIntegratedReviewIssue,
|
||||
NonIntegratedReviewRound,
|
||||
NonIntegratedReviewSheet,
|
||||
NonIntegratedSampleMeasurementRow,
|
||||
NonIntegratedSkuItem,
|
||||
} from "./core/nonIntegratedStorage";
|
||||
import type {
|
||||
@ -58,6 +60,8 @@ export type AutoSaveNoticeType = "saving" | "success" | "error";
|
||||
export type PersistEditedProductOverrides = {
|
||||
documentWorkspaceConfirmations?: NonIntegratedDocumentWorkspaceConfirmationMap;
|
||||
documentWorkspaceApprovedAt?: NonIntegratedDocumentWorkspaceApprovalMap;
|
||||
factoryRows?: NonIntegratedFactoryRow[];
|
||||
sampleMeasurementRows?: NonIntegratedSampleMeasurementRow[];
|
||||
reviewRounds?: NonIntegratedReviewRound[];
|
||||
finishedSampleShipmentRows?: NonIntegratedFinishedSampleShipmentRow[];
|
||||
openingReference?: NonIntegratedOpeningReference;
|
||||
|
||||
@ -1,89 +1,41 @@
|
||||
import { useEffect, type Dispatch, type SetStateAction } from "react";
|
||||
import {
|
||||
FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY,
|
||||
PRODUCT_FACTORY_WORKSPACE_CATEGORY,
|
||||
SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY,
|
||||
} from "./workspaces/documents/documentWorkspaceConfig";
|
||||
import type { DocumentWorkspaceCategory } from "./workspaces/documents/documentWorkspaceHelpers";
|
||||
import {
|
||||
isProductFactoryRowComplete,
|
||||
normalizeFactoryRowsForPayload,
|
||||
} from "./workspaces/basic-info/productFactoryHelpers";
|
||||
import type {
|
||||
NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
NonIntegratedFactoryRow,
|
||||
NonIntegratedFinishedSampleShipmentRow,
|
||||
NonIntegratedSampleMeasurementRow,
|
||||
} from "./core/nonIntegratedStorage";
|
||||
|
||||
type DetailMode = "create" | "edit" | "view";
|
||||
|
||||
type UseNonIntegratedWorkflowAutoConfirmationEffectsParams = {
|
||||
allFinishedSampleShipmentRowsComplete: boolean;
|
||||
allProductFactoryRowsComplete: boolean;
|
||||
allSampleMeasurementRowsComplete: boolean;
|
||||
existingProductCreatedAt: string;
|
||||
factoryRows: NonIntegratedFactoryRow[];
|
||||
finishedSampleShipmentRows: NonIntegratedFinishedSampleShipmentRow[];
|
||||
isReadOnly: boolean;
|
||||
mode: DetailMode;
|
||||
productFactoryRows: NonIntegratedFactoryRow[];
|
||||
productId?: string;
|
||||
sampleMeasurementRows: NonIntegratedSampleMeasurementRow[];
|
||||
setDocumentWorkspaceApprovedAt: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceApprovalMap>
|
||||
>;
|
||||
setDocumentWorkspaceConfirmations: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceConfirmationMap>
|
||||
>;
|
||||
setFactoryRows: Dispatch<SetStateAction<NonIntegratedFactoryRow[]>>;
|
||||
supplierCodeByName: Map<string, string>;
|
||||
};
|
||||
|
||||
export function useNonIntegratedWorkflowAutoConfirmationEffects({
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
existingProductCreatedAt,
|
||||
factoryRows,
|
||||
finishedSampleShipmentRows,
|
||||
isReadOnly,
|
||||
mode,
|
||||
productFactoryRows,
|
||||
productId,
|
||||
sampleMeasurementRows,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
setFactoryRows,
|
||||
supplierCodeByName,
|
||||
}: UseNonIntegratedWorkflowAutoConfirmationEffectsParams) {
|
||||
useEffect(() => {
|
||||
if (isReadOnly || factoryRows.length === 0) return;
|
||||
|
||||
const normalizedRows = normalizeFactoryRowsForPayload(
|
||||
productFactoryRows,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const hasConfirmationChange = normalizedRows.some(
|
||||
(row) => row.confirmed !== isProductFactoryRowComplete(row),
|
||||
);
|
||||
if (!hasConfirmationChange) return;
|
||||
|
||||
setFactoryRows(
|
||||
normalizedRows.map((row) => ({
|
||||
...row,
|
||||
confirmed: isProductFactoryRowComplete(row),
|
||||
})),
|
||||
);
|
||||
}, [
|
||||
factoryRows.length,
|
||||
isReadOnly,
|
||||
productFactoryRows,
|
||||
setFactoryRows,
|
||||
supplierCodeByName,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
mode !== "edit" ||
|
||||
@ -99,17 +51,6 @@ export function useNonIntegratedWorkflowAutoConfirmationEffects({
|
||||
touched: boolean;
|
||||
complete: boolean;
|
||||
}> = [
|
||||
{
|
||||
category: PRODUCT_FACTORY_WORKSPACE_CATEGORY,
|
||||
touched: factoryRows.length > 0,
|
||||
complete: allProductFactoryRowsComplete,
|
||||
},
|
||||
{
|
||||
category: SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY,
|
||||
touched: sampleMeasurementRows.length > 0,
|
||||
complete:
|
||||
allProductFactoryRowsComplete && allSampleMeasurementRowsComplete,
|
||||
},
|
||||
{
|
||||
category: FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY,
|
||||
touched: finishedSampleShipmentRows.length > 0,
|
||||
@ -149,15 +90,11 @@ export function useNonIntegratedWorkflowAutoConfirmationEffects({
|
||||
});
|
||||
}, [
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
existingProductCreatedAt,
|
||||
factoryRows.length,
|
||||
finishedSampleShipmentRows.length,
|
||||
isReadOnly,
|
||||
mode,
|
||||
productId,
|
||||
sampleMeasurementRows.length,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
]);
|
||||
|
||||
@ -56,7 +56,7 @@ export function useNonIntegratedWorkflowCompletion({
|
||||
productSampleMeasurementRows: NonIntegratedSampleMeasurementRow[];
|
||||
}) {
|
||||
const confirmedProductFactoryRowCount = useMemo(
|
||||
() => productFactoryRows.filter(isProductFactoryRowComplete).length,
|
||||
() => productFactoryRows.filter((row) => row.confirmed).length,
|
||||
[productFactoryRows],
|
||||
);
|
||||
const populatedProductPricingRowCount = useMemo(
|
||||
@ -109,7 +109,10 @@ export function useNonIntegratedWorkflowCompletion({
|
||||
displayedProductFactoryRows.some((row) => !isProductFactoryRowComplete(row));
|
||||
const displayedProductFactoryIsConfirmed =
|
||||
displayedProductFactoryRows.length > 0 &&
|
||||
!displayedProductFactoryHasPendingRows;
|
||||
displayedProductFactoryRows.every((row) => row.confirmed);
|
||||
const allProductFactoryRowsConfirmed =
|
||||
productFactoryRows.length > 0 &&
|
||||
productFactoryRows.every((row) => row.confirmed);
|
||||
const displayedProductFactoryRowsComplete =
|
||||
displayedProductFactoryRows.length > 0 &&
|
||||
displayedProductFactoryRows.every(isProductFactoryRowComplete);
|
||||
@ -162,6 +165,16 @@ export function useNonIntegratedWorkflowCompletion({
|
||||
),
|
||||
[productPricingRows],
|
||||
);
|
||||
const allSampleMeasurementRowsConfirmedByPricing =
|
||||
productSampleMeasurementRows.length > 0 &&
|
||||
productSampleMeasurementRows.every((row) =>
|
||||
productPricingSystemEnteredSkuIdSet.has(row.skuId),
|
||||
);
|
||||
const displayedSampleMeasurementRowsConfirmedByPricing =
|
||||
displayedProductSampleMeasurementRows.length > 0 &&
|
||||
displayedProductSampleMeasurementRows.every((row) =>
|
||||
productPricingSystemEnteredSkuIdSet.has(row.skuId),
|
||||
);
|
||||
const displayedProductFactoryHasSystemEnteredPricingRows =
|
||||
displayedProductFactoryRows.some((row) =>
|
||||
productPricingSystemEnteredSkuIdSet.has(row.skuId),
|
||||
@ -179,7 +192,10 @@ export function useNonIntegratedWorkflowCompletion({
|
||||
|
||||
return {
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsConfirmed,
|
||||
allProductFactoryRowsComplete,
|
||||
allProductPricingRowsSystemEntered,
|
||||
allSampleMeasurementRowsConfirmedByPricing,
|
||||
allSampleMeasurementRowsComplete,
|
||||
confirmedProductFactoryRowCount,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
@ -192,6 +208,7 @@ export function useNonIntegratedWorkflowCompletion({
|
||||
displayedProductPricingRowsAllSystemEntered,
|
||||
displayedProductPricingRowsComplete,
|
||||
displayedProductPricingRowsReadyForPush,
|
||||
displayedSampleMeasurementRowsConfirmedByPricing,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
isSampleMeasurementSkuLocked,
|
||||
planningLayoutHasContent,
|
||||
|
||||
@ -25,19 +25,19 @@ import {
|
||||
|
||||
type UseNonIntegratedWorkflowConfirmationStateParams = {
|
||||
allFinishedSampleShipmentRowsComplete: boolean;
|
||||
allProductFactoryRowsComplete: boolean;
|
||||
allSampleMeasurementRowsComplete: boolean;
|
||||
allProductFactoryRowsConfirmed: boolean;
|
||||
allSampleMeasurementRowsConfirmedByPricing: boolean;
|
||||
basicInfoHasContent: boolean;
|
||||
displayedFinishedSampleShipmentRowsComplete: boolean;
|
||||
displayedFirstQualityRowsComplete: boolean;
|
||||
displayedProductFactoryIsConfirmed: boolean;
|
||||
displayedProductLimitRowsComplete: boolean;
|
||||
displayedProductSampleMeasurementRows: Array<{ skuId: string }>;
|
||||
displayedSampleMeasurementRowsConfirmedByPricing: boolean;
|
||||
displayedSampleMeasurementRowsComplete: boolean;
|
||||
documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap;
|
||||
documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap;
|
||||
documentWorkspaceRevisionSignatures: Record<WorkflowNodeCategory, string>;
|
||||
isSampleMeasurementSkuLocked: (skuId: string) => boolean;
|
||||
previousDocumentWorkspaceRevisionSignatures:
|
||||
| Record<WorkflowNodeCategory, string>
|
||||
| null
|
||||
@ -54,19 +54,19 @@ type UseNonIntegratedWorkflowConfirmationStateParams = {
|
||||
|
||||
export function useNonIntegratedWorkflowConfirmationState({
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
allProductFactoryRowsConfirmed,
|
||||
allSampleMeasurementRowsConfirmedByPricing,
|
||||
basicInfoHasContent,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
displayedFirstQualityRowsComplete,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductLimitRowsComplete,
|
||||
displayedProductSampleMeasurementRows,
|
||||
displayedSampleMeasurementRowsConfirmedByPricing,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatures,
|
||||
isSampleMeasurementSkuLocked,
|
||||
previousDocumentWorkspaceRevisionSignatures,
|
||||
productPricingCanBeConfirmed,
|
||||
projectStatus,
|
||||
@ -92,13 +92,15 @@ export function useNonIntegratedWorkflowConfirmationState({
|
||||
const isWorkflowNodeConfirmed = useCallback(
|
||||
(category: WorkflowNodeCategory, hasContent: boolean) => {
|
||||
if (category === PRODUCT_FACTORY_WORKSPACE_CATEGORY) {
|
||||
return hasContent && allProductFactoryRowsComplete;
|
||||
return hasContent && allProductFactoryRowsConfirmed;
|
||||
}
|
||||
if (category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY) {
|
||||
if (!hasContent || !allProductFactoryRowsConfirmed) return false;
|
||||
if (allSampleMeasurementRowsConfirmedByPricing) return true;
|
||||
return (
|
||||
hasContent &&
|
||||
allProductFactoryRowsComplete &&
|
||||
allSampleMeasurementRowsComplete
|
||||
documentWorkspaceConfirmations[category] === true &&
|
||||
Boolean(documentWorkspaceApprovedAt[category]) &&
|
||||
!dirtyWorkflowNodeCategories.has(category)
|
||||
);
|
||||
}
|
||||
if (category === FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY) {
|
||||
@ -110,12 +112,6 @@ export function useNonIntegratedWorkflowConfirmationState({
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY &&
|
||||
!displayedProductFactoryIsConfirmed
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
!shouldRequireWorkflowConfirmation(category) ||
|
||||
@ -127,10 +123,9 @@ export function useNonIntegratedWorkflowConfirmationState({
|
||||
},
|
||||
[
|
||||
allFinishedSampleShipmentRowsComplete,
|
||||
allProductFactoryRowsComplete,
|
||||
allSampleMeasurementRowsComplete,
|
||||
allProductFactoryRowsConfirmed,
|
||||
allSampleMeasurementRowsConfirmedByPricing,
|
||||
dirtyWorkflowNodeCategories,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
productPricingCanBeConfirmed,
|
||||
@ -142,11 +137,6 @@ export function useNonIntegratedWorkflowConfirmationState({
|
||||
Boolean(documentWorkspaceApprovedAt[category]),
|
||||
[documentWorkspaceApprovedAt, documentWorkspaceConfirmations],
|
||||
);
|
||||
const isSampleMeasurementNodeLocked =
|
||||
displayedProductSampleMeasurementRows.length > 0 &&
|
||||
displayedProductSampleMeasurementRows.every((row) =>
|
||||
isSampleMeasurementSkuLocked(row.skuId),
|
||||
);
|
||||
const isFinishedSampleShipmentNodeLocked = isDocumentWorkspaceNodeLocked(
|
||||
FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY,
|
||||
);
|
||||
@ -156,12 +146,20 @@ export function useNonIntegratedWorkflowConfirmationState({
|
||||
const isProductLimitNodeLocked = isDocumentWorkspaceNodeLocked(
|
||||
PRODUCT_LIMIT_WORKSPACE_CATEGORY,
|
||||
);
|
||||
const displayedSampleMeasurementIsConfirmed = isWorkflowNodeConfirmed(
|
||||
SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
);
|
||||
const displayedSampleMeasurementHasContent =
|
||||
displayedProductSampleMeasurementRows.length > 0;
|
||||
const displayedSampleMeasurementStoredConfirmed =
|
||||
documentWorkspaceConfirmations[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY] ===
|
||||
true &&
|
||||
Boolean(documentWorkspaceApprovedAt[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]) &&
|
||||
!dirtyWorkflowNodeCategories.has(SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY);
|
||||
const displayedSampleMeasurementIsConfirmed =
|
||||
displayedSampleMeasurementHasContent &&
|
||||
displayedProductFactoryIsConfirmed &&
|
||||
(displayedSampleMeasurementRowsConfirmedByPricing ||
|
||||
displayedSampleMeasurementStoredConfirmed);
|
||||
const isSampleMeasurementNodeApproved =
|
||||
isSampleMeasurementNodeLocked &&
|
||||
displayedSampleMeasurementIsConfirmed &&
|
||||
!dirtyWorkflowNodeCategories.has(SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY);
|
||||
const displayedFinishedSampleShipmentIsConfirmed = isWorkflowNodeConfirmed(
|
||||
FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY,
|
||||
|
||||
@ -128,7 +128,7 @@ export function useNonIntegratedWorkflowRows({
|
||||
() =>
|
||||
new Set(
|
||||
productFactoryRows
|
||||
.filter((row) => row.confirmed || isProductFactoryRowComplete(row))
|
||||
.filter((row) => row.confirmed)
|
||||
.map((row) => row.skuId),
|
||||
),
|
||||
[productFactoryRows],
|
||||
|
||||
@ -15,7 +15,7 @@ type SampleMeasurementWorkspaceProps = {
|
||||
displayedRows: ProductSampleMeasurementDisplayRow[];
|
||||
inheritedSkuBatchTabs: ReactNode;
|
||||
isReadOnly: boolean;
|
||||
isProductFactorySkuComplete: (skuId: string) => boolean;
|
||||
isProductFactorySkuConfirmed: (skuId: string) => boolean;
|
||||
isSampleMeasurementSkuLocked: (skuId: string) => boolean;
|
||||
onUpdateRow: (
|
||||
skuId: string,
|
||||
@ -50,7 +50,7 @@ export function SampleMeasurementWorkspace({
|
||||
displayedRows,
|
||||
inheritedSkuBatchTabs,
|
||||
isReadOnly,
|
||||
isProductFactorySkuComplete,
|
||||
isProductFactorySkuConfirmed,
|
||||
isSampleMeasurementSkuLocked,
|
||||
onUpdateRow,
|
||||
}: SampleMeasurementWorkspaceProps) {
|
||||
@ -64,7 +64,7 @@ export function SampleMeasurementWorkspace({
|
||||
placeholder={placeholder}
|
||||
readOnly={
|
||||
isReadOnly ||
|
||||
!isProductFactorySkuComplete(row.skuId) ||
|
||||
!isProductFactorySkuConfirmed(row.skuId) ||
|
||||
isSampleMeasurementSkuLocked(row.skuId)
|
||||
}
|
||||
mode="singleLineResizable"
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
MODEL_SHARED_PRODUCT_FACTORY_KEYS,
|
||||
PACKAGE_SHARED_PRODUCT_FACTORY_PURCHASING_KEYS,
|
||||
PRODUCT_FACTORY_REQUIRED_FIELDS,
|
||||
SPU_SHARED_PRODUCT_FACTORY_KEYS,
|
||||
} from "./skuFieldConfig";
|
||||
import { buildAutoBarcode, shouldKeepCustomBarcode } from "./skuAutoHelpers";
|
||||
import { SUPPLIER_CODE_BY_NAME } from "./supplierDirectory";
|
||||
@ -84,6 +85,8 @@ export function normalizeModelSharedProductFactoryRows(
|
||||
});
|
||||
|
||||
return rows.map((row) => {
|
||||
if (row.confirmed) return row;
|
||||
|
||||
const modelKey = getSkuFactoryModelKey(row);
|
||||
if (!modelKey) return row;
|
||||
|
||||
@ -101,6 +104,40 @@ export function normalizeModelSharedProductFactoryRows(
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeSpuSharedProductFactoryRows(
|
||||
rows: NonIntegratedFactoryRow[],
|
||||
) {
|
||||
const sharedValueByKey = new Map<string, string>();
|
||||
SPU_SHARED_PRODUCT_FACTORY_KEYS.forEach((key) => {
|
||||
const sharedValue = rows.find((row) => row[key].trim())?.[key].trim() ?? "";
|
||||
sharedValueByKey.set(key, sharedValue);
|
||||
});
|
||||
|
||||
return rows.map((row) => {
|
||||
if (row.confirmed) return row;
|
||||
|
||||
let nextRow = row;
|
||||
SPU_SHARED_PRODUCT_FACTORY_KEYS.forEach((key) => {
|
||||
const sharedValue = sharedValueByKey.get(key);
|
||||
if (sharedValue === undefined || nextRow[key] === sharedValue) return;
|
||||
nextRow = {
|
||||
...nextRow,
|
||||
[key]: sharedValue,
|
||||
};
|
||||
});
|
||||
|
||||
return nextRow;
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeSharedProductFactoryRows(
|
||||
rows: NonIntegratedFactoryRow[],
|
||||
) {
|
||||
return normalizeSpuSharedProductFactoryRows(
|
||||
normalizeModelSharedProductFactoryRows(rows),
|
||||
);
|
||||
}
|
||||
|
||||
export function getProductFactoryMissingFieldLabels(
|
||||
row: NonIntegratedFactoryRow,
|
||||
) {
|
||||
@ -123,7 +160,7 @@ export function getProductFactoryStatusMessage({
|
||||
if (rows.length === 0) return null;
|
||||
if (isRowsComplete) {
|
||||
return {
|
||||
message: "产品定厂已完成",
|
||||
message: "资料已填完核对无误请确认。",
|
||||
type: "success" as const,
|
||||
};
|
||||
}
|
||||
@ -249,7 +286,7 @@ export function buildProductFactoryRows(
|
||||
);
|
||||
});
|
||||
|
||||
return normalizeModelSharedProductFactoryRows(rows);
|
||||
return normalizeSharedProductFactoryRows(rows);
|
||||
}
|
||||
|
||||
export function normalizeFactoryRowsForPayload(
|
||||
@ -294,5 +331,5 @@ export function normalizeFactoryRowsForPayload(
|
||||
};
|
||||
});
|
||||
|
||||
return normalizeModelSharedProductFactoryRows(normalizedRows);
|
||||
return normalizeSharedProductFactoryRows(normalizedRows);
|
||||
}
|
||||
|
||||
@ -161,10 +161,13 @@ export const PACKAGE_SHARED_PRODUCT_FACTORY_PURCHASING_KEYS =
|
||||
"assemblyMode",
|
||||
]);
|
||||
export const MODEL_SHARED_PRODUCT_FACTORY_KEYS = new Set<ProductFactoryEditableKey>([
|
||||
"installationService",
|
||||
]);
|
||||
export const SPU_SHARED_PRODUCT_FACTORY_KEYS = new Set<ProductFactoryEditableKey>([
|
||||
"warehouse",
|
||||
"shippingMethod",
|
||||
"installMethod",
|
||||
"productionType",
|
||||
"installationService",
|
||||
"supplierCategory",
|
||||
"purchaseOrderMethod",
|
||||
]);
|
||||
|
||||
@ -28,8 +28,6 @@ export const PRODUCT_LIMIT_WORKSPACE_CATEGORY: DocumentWorkspaceCategory =
|
||||
export const PLANNING_LAYOUT_WORKSPACE_CATEGORY: DocumentWorkspaceCategory =
|
||||
NON_INTEGRATED_PLANNING_LAYOUT_WORKSPACE_CATEGORY;
|
||||
export const AUTO_COMPLETED_WORKFLOW_CATEGORIES = new Set<WorkflowNodeCategory>([
|
||||
PRODUCT_FACTORY_WORKSPACE_CATEGORY,
|
||||
SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY,
|
||||
FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY,
|
||||
]);
|
||||
export const FACTORY_QUALITY_WORKSPACE_CATEGORY: DocumentWorkspaceCategory =
|
||||
|
||||
@ -10,6 +10,7 @@ type ProductFactoryHeaderActionsProps = {
|
||||
isReadOnly: boolean;
|
||||
rowCount: number;
|
||||
hasPendingRows: boolean;
|
||||
isConfirmed: boolean;
|
||||
hasSystemEnteredPricingRows: boolean;
|
||||
isReopenPickerOpen: boolean;
|
||||
reopenField: ProductFactoryEditableKey;
|
||||
@ -26,6 +27,7 @@ export function ProductFactoryHeaderActions({
|
||||
isReadOnly,
|
||||
rowCount,
|
||||
hasPendingRows,
|
||||
isConfirmed,
|
||||
hasSystemEnteredPricingRows,
|
||||
isReopenPickerOpen,
|
||||
reopenField,
|
||||
@ -98,10 +100,15 @@ export function ProductFactoryHeaderActions({
|
||||
) : null}
|
||||
<Button
|
||||
size="small"
|
||||
data-testid="product-factory-confirm"
|
||||
onClick={onStatusClick}
|
||||
disabled={rowCount === 0}
|
||||
>
|
||||
{hasPendingRows ? "当前资料未填完" : "已完成"}
|
||||
{hasPendingRows
|
||||
? "当前资料未填完"
|
||||
: isConfirmed
|
||||
? "已确认"
|
||||
: "资料已填完核对无误请确认。"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -141,14 +141,18 @@ export function ProductFactoryInfoTable({
|
||||
<td className="px-2 py-2">
|
||||
<span
|
||||
className={`inline-flex whitespace-nowrap rounded-full px-2 py-0.5 text-[11px] ${
|
||||
isProductFactoryRowComplete(row)
|
||||
row.confirmed
|
||||
? "bg-[#dff7e8] text-[#137a38]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
: isProductFactoryRowComplete(row)
|
||||
? "bg-[#e7f1ff] text-[#1764ad]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
}`}
|
||||
>
|
||||
{isProductFactoryRowComplete(row)
|
||||
? "已完成"
|
||||
: "当前资料未填完"}
|
||||
{row.confirmed
|
||||
? "已确认"
|
||||
: isProductFactoryRowComplete(row)
|
||||
? "资料已填完"
|
||||
: "当前资料未填完"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-2 py-2 text-apple-label-secondary">
|
||||
|
||||
@ -40,14 +40,18 @@ export function ProductFactorySkuInfoTable({
|
||||
<td className="px-3 py-2">
|
||||
<span
|
||||
className={`inline-flex rounded-full px-2 py-0.5 text-[11px] ${
|
||||
isProductFactoryRowComplete(row)
|
||||
row.confirmed
|
||||
? "bg-[#dff7e8] text-[#137a38]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
: isProductFactoryRowComplete(row)
|
||||
? "bg-[#e7f1ff] text-[#1764ad]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
}`}
|
||||
>
|
||||
{isProductFactoryRowComplete(row)
|
||||
? "已完成"
|
||||
: "当前资料未填完"}
|
||||
{row.confirmed
|
||||
? "已确认"
|
||||
: isProductFactoryRowComplete(row)
|
||||
? "资料已填完"
|
||||
: "当前资料未填完"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2">{row.model || "-"}</td>
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
MODEL_AUTOFILL_PRODUCT_FACTORY_KEYS,
|
||||
MODEL_SHARED_PRODUCT_FACTORY_KEYS,
|
||||
PACKAGE_SHARED_PRODUCT_FACTORY_KEYS,
|
||||
SPU_SHARED_PRODUCT_FACTORY_KEYS,
|
||||
type ProductFactoryEditableKey,
|
||||
type ProductFactoryModelAutofillEntry,
|
||||
} from "../basic-info/skuFieldConfig";
|
||||
@ -48,6 +49,10 @@ export function getProductFactoryTargetSkuIds(
|
||||
const targetItem = items.find((item) => item.id === targetId);
|
||||
if (!targetItem) return new Set([targetId]);
|
||||
|
||||
if (SPU_SHARED_PRODUCT_FACTORY_KEYS.has(key)) {
|
||||
return new Set(items.map((item) => item.id));
|
||||
}
|
||||
|
||||
if (MODEL_SHARED_PRODUCT_FACTORY_KEYS.has(key)) {
|
||||
const targetModelKey = getSkuFactoryModelKey(targetItem);
|
||||
if (!targetModelKey) return new Set([targetId]);
|
||||
|
||||
@ -4,6 +4,7 @@ import type {
|
||||
SetStateAction,
|
||||
} from "react";
|
||||
|
||||
import type { PersistEditedProductOverrides } from "../../nonIntegratedDetailTypes";
|
||||
import type {
|
||||
NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
@ -16,10 +17,15 @@ import {
|
||||
type ProductFactoryEditableKey,
|
||||
type ProductFactoryModelAutofillEntry,
|
||||
} from "../basic-info/skuFieldConfig";
|
||||
import { normalizeFactorySupplier, getProductFactoryStatusMessage } from "../basic-info/productFactoryHelpers";
|
||||
import {
|
||||
getProductFactoryStatusMessage,
|
||||
normalizeFactoryRowsForPayload,
|
||||
normalizeFactorySupplier,
|
||||
} from "../basic-info/productFactoryHelpers";
|
||||
import { buildSkuItemsAfterSetCountCommit } from "../basic-info/skuSetCountCommitHelpers";
|
||||
import { getStyleKeyFromModel } from "../style-assets/styleFileHelpers";
|
||||
import { PRODUCT_FACTORY_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig";
|
||||
import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers";
|
||||
import {
|
||||
confirmProductFactorySupplierRowsForSku,
|
||||
reopenProductFactoryRows,
|
||||
@ -35,18 +41,30 @@ type ApplySkuSupplierName = (
|
||||
value: string,
|
||||
) => NonIntegratedSkuItem;
|
||||
|
||||
type PersistEditedProduct = (params: {
|
||||
successMessage: string;
|
||||
errorFallbackMessage: string;
|
||||
overrides?: PersistEditedProductOverrides;
|
||||
suppressSuccessMessage?: boolean;
|
||||
}) => Promise<boolean>;
|
||||
|
||||
type UseProductFactoryRowControllerParams = {
|
||||
applySkuSupplierName: ApplySkuSupplierName;
|
||||
derivedSkuItems: NonIntegratedSkuItem[];
|
||||
displayedProductFactoryHasPendingRows: boolean;
|
||||
displayedProductFactoryHasSystemEnteredPricingRows: boolean;
|
||||
displayedProductFactoryIsConfirmed: boolean;
|
||||
displayedProductFactoryRows: NonIntegratedFactoryRow[];
|
||||
displayedProductFactoryRowsComplete: boolean;
|
||||
documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap;
|
||||
documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap;
|
||||
documentWorkspaceRevisionSignatureRef: MutableRefObject<
|
||||
Record<WorkflowNodeCategory, string> | null
|
||||
>;
|
||||
isCurrentUserAdmin: boolean;
|
||||
isReadOnly: boolean;
|
||||
isSkuItemFactoryConfirmed: (item: NonIntegratedSkuItem) => boolean;
|
||||
persistEditedProduct: PersistEditedProduct;
|
||||
productFactoryModelAutofillRef: MutableRefObject<
|
||||
Map<string, ProductFactoryModelAutofillEntry>
|
||||
>;
|
||||
@ -73,13 +91,16 @@ export function useProductFactoryRowController({
|
||||
derivedSkuItems,
|
||||
displayedProductFactoryHasPendingRows,
|
||||
displayedProductFactoryHasSystemEnteredPricingRows,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductFactoryRows,
|
||||
displayedProductFactoryRowsComplete,
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
isCurrentUserAdmin,
|
||||
isReadOnly,
|
||||
isSkuItemFactoryConfirmed,
|
||||
persistEditedProduct,
|
||||
productFactoryModelAutofillRef,
|
||||
productFactoryReopenField,
|
||||
productFactoryRows,
|
||||
@ -156,32 +177,32 @@ export function useProductFactoryRowController({
|
||||
|
||||
if (key === "installMethod") {
|
||||
setSkuItems((current) => {
|
||||
const targetItem = workflowSkuItems.find((item) => item.id === skuId);
|
||||
if (!targetItem) return current;
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
workflowSkuItems,
|
||||
skuId,
|
||||
key,
|
||||
);
|
||||
const targetStyleKeys = new Set(
|
||||
workflowSkuItems
|
||||
.filter((item) => targetSkuIds.has(item.id))
|
||||
.map((item) => getStyleKeyFromModel(item.model))
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
const targetStyleKey = getStyleKeyFromModel(targetItem.model);
|
||||
if (targetStyleKey) {
|
||||
const targetSkuIds = new Set(
|
||||
workflowSkuItems
|
||||
.filter((item) => getStyleKeyFromModel(item.model) === targetStyleKey)
|
||||
.map((item) => item.id),
|
||||
);
|
||||
|
||||
setStyleDetails((currentDetails) =>
|
||||
updateStyleDetailEntry(currentDetails, targetStyleKey, {
|
||||
installMethod: value,
|
||||
}),
|
||||
);
|
||||
|
||||
return current.map((item) =>
|
||||
targetSkuIds.has(item.id)
|
||||
? { ...item, installMethod: value }
|
||||
: item,
|
||||
);
|
||||
if (targetStyleKeys.size > 0) {
|
||||
setStyleDetails((currentDetails) => {
|
||||
let nextDetails = currentDetails;
|
||||
targetStyleKeys.forEach((styleKey) => {
|
||||
nextDetails = updateStyleDetailEntry(nextDetails, styleKey, {
|
||||
installMethod: value,
|
||||
});
|
||||
});
|
||||
return nextDetails;
|
||||
});
|
||||
}
|
||||
|
||||
return current.map((item) =>
|
||||
item.id === skuId ? { ...item, installMethod: value } : item,
|
||||
targetSkuIds.has(item.id) ? { ...item, installMethod: value } : item,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -235,13 +256,65 @@ export function useProductFactoryRowController({
|
||||
setSaveMessageType("success");
|
||||
}
|
||||
|
||||
function handleProductFactoryStatusClick() {
|
||||
async function handleProductFactoryStatusClick() {
|
||||
const statusMessage = getProductFactoryStatusMessage({
|
||||
isRowsComplete: displayedProductFactoryRowsComplete,
|
||||
rows: displayedProductFactoryRows,
|
||||
});
|
||||
if (!statusMessage) return;
|
||||
|
||||
if (
|
||||
!isReadOnly &&
|
||||
displayedProductFactoryRowsComplete &&
|
||||
!displayedProductFactoryIsConfirmed
|
||||
) {
|
||||
const targetSkuIds = new Set(
|
||||
displayedProductFactoryRows.map((row) => row.skuId),
|
||||
);
|
||||
const normalizedRows = normalizeFactoryRowsForPayload(
|
||||
productFactoryRows,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const nextRows = normalizedRows.map((row) =>
|
||||
targetSkuIds.has(row.skuId) ? { ...row, confirmed: true } : row,
|
||||
);
|
||||
const allRowsConfirmed =
|
||||
nextRows.length > 0 && nextRows.every((row) => row.confirmed);
|
||||
const approvedAt = new Date().toISOString();
|
||||
const nextConfirmations = {
|
||||
...documentWorkspaceConfirmations,
|
||||
[PRODUCT_FACTORY_WORKSPACE_CATEGORY]: allRowsConfirmed,
|
||||
};
|
||||
const nextApprovedAt = { ...documentWorkspaceApprovedAt };
|
||||
if (allRowsConfirmed) {
|
||||
nextApprovedAt[PRODUCT_FACTORY_WORKSPACE_CATEGORY] = approvedAt;
|
||||
} else {
|
||||
delete nextApprovedAt[PRODUCT_FACTORY_WORKSPACE_CATEGORY];
|
||||
}
|
||||
|
||||
setFactoryRows(nextRows);
|
||||
setDocumentWorkspaceConfirmations(nextConfirmations);
|
||||
setDocumentWorkspaceApprovedAt(nextApprovedAt);
|
||||
if (documentWorkspaceRevisionSignatureRef.current) {
|
||||
documentWorkspaceRevisionSignatureRef.current = {
|
||||
...documentWorkspaceRevisionSignatureRef.current,
|
||||
[PRODUCT_FACTORY_WORKSPACE_CATEGORY]: JSON.stringify(nextRows),
|
||||
};
|
||||
}
|
||||
await persistEditedProduct({
|
||||
successMessage: allRowsConfirmed
|
||||
? "产品定厂已确认"
|
||||
: "当前批次产品定厂已确认",
|
||||
errorFallbackMessage: "产品定厂确认保存失败",
|
||||
overrides: {
|
||||
factoryRows: nextRows,
|
||||
documentWorkspaceConfirmations: nextConfirmations,
|
||||
documentWorkspaceApprovedAt: nextApprovedAt,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setSaveMessage(statusMessage.message);
|
||||
setSaveMessageType(statusMessage.type);
|
||||
}
|
||||
|
||||
@ -20,7 +20,10 @@ import type {
|
||||
NonIntegratedSkuItem,
|
||||
} from "../../nonIntegratedStorage";
|
||||
import type { FinishedSampleShipmentEditableKey } from "../basic-info/skuFieldConfig";
|
||||
import { PRODUCT_PRICING_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig";
|
||||
import {
|
||||
PRODUCT_PRICING_WORKSPACE_CATEGORY,
|
||||
SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY,
|
||||
} from "../documents/documentWorkspaceConfig";
|
||||
import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers";
|
||||
import {
|
||||
updateFirstQualityRowsForSku,
|
||||
@ -77,7 +80,7 @@ type UseProductWorkflowRowControllerParams = {
|
||||
documentWorkspaceRevisionSignatureRef: MutableRefObject<
|
||||
Record<WorkflowNodeCategory, string> | null
|
||||
>;
|
||||
isProductFactorySkuComplete: (skuId: string) => boolean;
|
||||
isProductFactorySkuConfirmed: (skuId: string) => boolean;
|
||||
isProductLimitNodeLocked: boolean;
|
||||
isProductPricingNodeLocked: boolean;
|
||||
isReadOnly: boolean;
|
||||
@ -89,6 +92,8 @@ type UseProductWorkflowRowControllerParams = {
|
||||
planningLayoutRowsRef: MutableRefObject<NonIntegratedPlanningLayoutRow[]>;
|
||||
pricingRows: NonIntegratedPricingRow[];
|
||||
productId?: string;
|
||||
productSampleMeasurementRows: NonIntegratedSampleMeasurementRow[];
|
||||
sampleMeasurementRows: NonIntegratedSampleMeasurementRow[];
|
||||
scheduleAutoSave: (overrides?: PersistEditedProductOverrides) => void;
|
||||
setDocumentWorkspaceApprovedAt: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceApprovalMap>
|
||||
@ -128,7 +133,7 @@ export function useProductWorkflowRowController({
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
isProductFactorySkuComplete,
|
||||
isProductFactorySkuConfirmed,
|
||||
isProductLimitNodeLocked,
|
||||
isProductPricingNodeLocked,
|
||||
isReadOnly,
|
||||
@ -140,6 +145,8 @@ export function useProductWorkflowRowController({
|
||||
planningLayoutRowsRef,
|
||||
pricingRows,
|
||||
productId,
|
||||
productSampleMeasurementRows,
|
||||
sampleMeasurementRows,
|
||||
scheduleAutoSave,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
@ -233,16 +240,25 @@ export function useProductWorkflowRowController({
|
||||
});
|
||||
const nextProductPricingCanBeConfirmed =
|
||||
canConfirmProductPricingRows(nextPricingRows);
|
||||
const nextSampleMeasurementCanBeConfirmedByPricing =
|
||||
nextProductPricingCanBeConfirmed &&
|
||||
productSampleMeasurementRows.length > 0;
|
||||
const nextApprovedAt = nextProductPricingCanBeConfirmed
|
||||
? {
|
||||
...documentWorkspaceApprovedAt,
|
||||
[PRODUCT_PRICING_WORKSPACE_CATEGORY]: pushedAt,
|
||||
...(nextSampleMeasurementCanBeConfirmedByPricing
|
||||
? { [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: pushedAt }
|
||||
: {}),
|
||||
}
|
||||
: documentWorkspaceApprovedAt;
|
||||
const nextConfirmations = nextProductPricingCanBeConfirmed
|
||||
? {
|
||||
...documentWorkspaceConfirmations,
|
||||
[PRODUCT_PRICING_WORKSPACE_CATEGORY]: true,
|
||||
...(nextSampleMeasurementCanBeConfirmedByPricing
|
||||
? { [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: true }
|
||||
: {}),
|
||||
}
|
||||
: documentWorkspaceConfirmations;
|
||||
const pushSuccessMessage =
|
||||
@ -257,6 +273,12 @@ export function useProductWorkflowRowController({
|
||||
...documentWorkspaceRevisionSignatureRef.current,
|
||||
[PRODUCT_PRICING_WORKSPACE_CATEGORY]:
|
||||
JSON.stringify(nextPricingRows),
|
||||
...(nextSampleMeasurementCanBeConfirmedByPricing
|
||||
? {
|
||||
[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]:
|
||||
JSON.stringify(sampleMeasurementRows),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -456,7 +478,7 @@ export function useProductWorkflowRowController({
|
||||
) {
|
||||
if (
|
||||
isReadOnly ||
|
||||
!isProductFactorySkuComplete(skuId) ||
|
||||
!isProductFactorySkuConfirmed(skuId) ||
|
||||
isSampleMeasurementSkuLocked(skuId)
|
||||
)
|
||||
return;
|
||||
|
||||
@ -4,6 +4,7 @@ type SampleMeasurementHeaderActionsProps = {
|
||||
actionGroupClassName: string;
|
||||
rowCount: number;
|
||||
isProductFactoryConfirmed: boolean;
|
||||
hasPendingRows: boolean;
|
||||
isSampleMeasurementConfirmed: boolean;
|
||||
onStatusClick: () => void;
|
||||
};
|
||||
@ -12,6 +13,7 @@ export function SampleMeasurementHeaderActions({
|
||||
actionGroupClassName,
|
||||
rowCount,
|
||||
isProductFactoryConfirmed,
|
||||
hasPendingRows,
|
||||
isSampleMeasurementConfirmed,
|
||||
onStatusClick,
|
||||
}: SampleMeasurementHeaderActionsProps) {
|
||||
@ -19,6 +21,7 @@ export function SampleMeasurementHeaderActions({
|
||||
<div className={actionGroupClassName}>
|
||||
<Button
|
||||
size="small"
|
||||
data-testid="sample-measurement-confirm"
|
||||
onClick={onStatusClick}
|
||||
disabled={rowCount === 0}
|
||||
title={
|
||||
@ -28,8 +31,10 @@ export function SampleMeasurementHeaderActions({
|
||||
{!isProductFactoryConfirmed
|
||||
? "定厂未完成"
|
||||
: isSampleMeasurementConfirmed
|
||||
? "已完成"
|
||||
: "当前资料未填完"}
|
||||
? "已确认"
|
||||
: hasPendingRows
|
||||
? "当前资料未填完"
|
||||
: "资料已填完核对无误请确认。"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -153,7 +153,7 @@ export function getSampleMeasurementStatusMessage({
|
||||
}
|
||||
|
||||
return {
|
||||
message: "样包测量已完成",
|
||||
message: "资料已填完核对无误请确认。",
|
||||
type: "success" as const,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,37 +1,77 @@
|
||||
import type {
|
||||
Dispatch,
|
||||
MutableRefObject,
|
||||
SetStateAction,
|
||||
} from "react";
|
||||
|
||||
import type { PersistEditedProductOverrides } from "../../nonIntegratedDetailTypes";
|
||||
import type {
|
||||
NonIntegratedDocumentWorkspaceApprovalMap,
|
||||
NonIntegratedDocumentWorkspaceConfirmationMap,
|
||||
} from "../../nonIntegratedStorage";
|
||||
import { SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig";
|
||||
import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers";
|
||||
import {
|
||||
getFinishedSampleShipmentStatusMessage,
|
||||
getSampleMeasurementStatusMessage,
|
||||
normalizeSampleMeasurementRowsForPayload,
|
||||
type ProductFinishedSampleShipmentDisplayRow,
|
||||
type ProductSampleMeasurementDisplayRow,
|
||||
} from "./sampleWorkflowHelpers";
|
||||
|
||||
type SaveMessageType = "success" | "error";
|
||||
|
||||
type PersistEditedProduct = (params: {
|
||||
successMessage: string;
|
||||
errorFallbackMessage: string;
|
||||
overrides?: PersistEditedProductOverrides;
|
||||
suppressSuccessMessage?: boolean;
|
||||
}) => Promise<boolean>;
|
||||
|
||||
type UseSampleWorkflowStatusControllerParams = {
|
||||
documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap;
|
||||
documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap;
|
||||
displayedFinishedSampleShipmentRows: ProductFinishedSampleShipmentDisplayRow[];
|
||||
displayedFinishedSampleShipmentRowsComplete: boolean;
|
||||
displayedProductFactoryIsConfirmed: boolean;
|
||||
displayedProductSampleMeasurementRows: ProductSampleMeasurementDisplayRow[];
|
||||
displayedSampleMeasurementIsConfirmed: boolean;
|
||||
displayedSampleMeasurementRowsComplete: boolean;
|
||||
documentWorkspaceRevisionSignatureRef: MutableRefObject<
|
||||
Record<WorkflowNodeCategory, string> | null
|
||||
>;
|
||||
isReadOnly: boolean;
|
||||
persistEditedProduct: PersistEditedProduct;
|
||||
productSampleMeasurementRows: ProductSampleMeasurementDisplayRow[];
|
||||
setDocumentWorkspaceApprovedAt: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceApprovalMap>
|
||||
>;
|
||||
setDocumentWorkspaceConfirmations: Dispatch<
|
||||
SetStateAction<NonIntegratedDocumentWorkspaceConfirmationMap>
|
||||
>;
|
||||
setSaveMessage: Dispatch<SetStateAction<string>>;
|
||||
setSaveMessageType: Dispatch<SetStateAction<SaveMessageType>>;
|
||||
};
|
||||
|
||||
export function useSampleWorkflowStatusController({
|
||||
documentWorkspaceApprovedAt,
|
||||
documentWorkspaceConfirmations,
|
||||
displayedFinishedSampleShipmentRows,
|
||||
displayedFinishedSampleShipmentRowsComplete,
|
||||
displayedProductFactoryIsConfirmed,
|
||||
displayedProductSampleMeasurementRows,
|
||||
displayedSampleMeasurementIsConfirmed,
|
||||
displayedSampleMeasurementRowsComplete,
|
||||
documentWorkspaceRevisionSignatureRef,
|
||||
isReadOnly,
|
||||
persistEditedProduct,
|
||||
productSampleMeasurementRows,
|
||||
setDocumentWorkspaceApprovedAt,
|
||||
setDocumentWorkspaceConfirmations,
|
||||
setSaveMessage,
|
||||
setSaveMessageType,
|
||||
}: UseSampleWorkflowStatusControllerParams) {
|
||||
function handleSampleMeasurementStatusClick() {
|
||||
async function handleSampleMeasurementStatusClick() {
|
||||
const statusMessage = getSampleMeasurementStatusMessage({
|
||||
isFactoryConfirmed: displayedProductFactoryIsConfirmed,
|
||||
isRowsComplete: displayedSampleMeasurementRowsComplete,
|
||||
@ -39,6 +79,47 @@ export function useSampleWorkflowStatusController({
|
||||
});
|
||||
if (!statusMessage) return;
|
||||
|
||||
if (
|
||||
!isReadOnly &&
|
||||
displayedProductFactoryIsConfirmed &&
|
||||
displayedSampleMeasurementRowsComplete &&
|
||||
!displayedSampleMeasurementIsConfirmed
|
||||
) {
|
||||
const approvedAt = new Date().toISOString();
|
||||
const nextConfirmations = {
|
||||
...documentWorkspaceConfirmations,
|
||||
[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: true,
|
||||
};
|
||||
const nextApprovedAt = {
|
||||
...documentWorkspaceApprovedAt,
|
||||
[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: approvedAt,
|
||||
};
|
||||
const nextSampleMeasurementRows = normalizeSampleMeasurementRowsForPayload(
|
||||
productSampleMeasurementRows,
|
||||
);
|
||||
|
||||
setDocumentWorkspaceConfirmations(nextConfirmations);
|
||||
setDocumentWorkspaceApprovedAt(nextApprovedAt);
|
||||
if (documentWorkspaceRevisionSignatureRef.current) {
|
||||
documentWorkspaceRevisionSignatureRef.current = {
|
||||
...documentWorkspaceRevisionSignatureRef.current,
|
||||
[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: JSON.stringify(
|
||||
nextSampleMeasurementRows,
|
||||
),
|
||||
};
|
||||
}
|
||||
await persistEditedProduct({
|
||||
successMessage: "样包测量已确认",
|
||||
errorFallbackMessage: "样包测量确认保存失败",
|
||||
overrides: {
|
||||
sampleMeasurementRows: nextSampleMeasurementRows,
|
||||
documentWorkspaceConfirmations: nextConfirmations,
|
||||
documentWorkspaceApprovedAt: nextApprovedAt,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setSaveMessage(statusMessage.message);
|
||||
setSaveMessageType(statusMessage.type);
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ export function DocumentWorkspaceHeaderActionsSlot({
|
||||
displayedProductPricingRowsComplete,
|
||||
displayedProductPricingRowsReadyForPush,
|
||||
displayedProductSampleMeasurementRowCount,
|
||||
displayedSampleMeasurementHasPendingRows,
|
||||
displayedSampleMeasurementIsConfirmed,
|
||||
documentWorkspaceConfirmationAction,
|
||||
documentWorkspaceUploadInputRef,
|
||||
@ -157,6 +158,7 @@ export function DocumentWorkspaceHeaderActionsSlot({
|
||||
displayedProductPricingRowsComplete: boolean;
|
||||
displayedProductPricingRowsReadyForPush: boolean;
|
||||
displayedProductSampleMeasurementRowCount: number;
|
||||
displayedSampleMeasurementHasPendingRows: boolean;
|
||||
displayedSampleMeasurementIsConfirmed: boolean;
|
||||
documentWorkspaceConfirmationAction: ReactNode;
|
||||
documentWorkspaceUploadInputRef: RefObject<HTMLInputElement | null>;
|
||||
@ -262,6 +264,7 @@ export function DocumentWorkspaceHeaderActionsSlot({
|
||||
isReadOnly={isReadOnly}
|
||||
rowCount={displayedProductFactoryRowCount}
|
||||
hasPendingRows={displayedProductFactoryHasPendingRows}
|
||||
isConfirmed={displayedProductFactoryIsConfirmed}
|
||||
hasSystemEnteredPricingRows={
|
||||
displayedProductFactoryHasSystemEnteredPricingRows
|
||||
}
|
||||
@ -282,6 +285,7 @@ export function DocumentWorkspaceHeaderActionsSlot({
|
||||
actionGroupClassName={actionGroupClassName}
|
||||
rowCount={displayedProductSampleMeasurementRowCount}
|
||||
isProductFactoryConfirmed={displayedProductFactoryIsConfirmed}
|
||||
hasPendingRows={displayedSampleMeasurementHasPendingRows}
|
||||
isSampleMeasurementConfirmed={displayedSampleMeasurementIsConfirmed}
|
||||
onStatusClick={onSampleMeasurementStatusClick}
|
||||
/>
|
||||
|
||||
@ -10,8 +10,10 @@ import {
|
||||
import { DocumentWorkspaceSidebarStatusIcon } from "../../components/DocumentWorkspaceControls";
|
||||
import type {
|
||||
NonIntegratedFactoryRow,
|
||||
NonIntegratedSampleMeasurementRow,
|
||||
NonIntegratedUploadFile,
|
||||
} from "../../nonIntegratedStorage";
|
||||
import { SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig";
|
||||
|
||||
type DocumentWorkspaceView = "detail" | "document";
|
||||
|
||||
@ -27,9 +29,10 @@ export function DocumentWorkspaceSidebar({
|
||||
basicInfoNeedsConfirmation,
|
||||
documentWorkspaceCountByCategory,
|
||||
documentWorkspaceView,
|
||||
isProductFactoryRowComplete,
|
||||
isWorkflowNodeConfirmed,
|
||||
productFactoryRows,
|
||||
sampleMeasurementRows,
|
||||
isSampleMeasurementConfirmed,
|
||||
visibleGroupedUploadFiles,
|
||||
onOpenBasicInfo,
|
||||
onOpenDocumentWorkspace,
|
||||
@ -42,12 +45,13 @@ export function DocumentWorkspaceSidebar({
|
||||
Record<DocumentWorkspaceCategory, number>
|
||||
>;
|
||||
documentWorkspaceView: DocumentWorkspaceView;
|
||||
isProductFactoryRowComplete: (row: NonIntegratedFactoryRow) => boolean;
|
||||
isWorkflowNodeConfirmed: (
|
||||
category: DocumentWorkspaceCategory,
|
||||
hasContent: boolean,
|
||||
) => boolean;
|
||||
productFactoryRows: NonIntegratedFactoryRow[];
|
||||
sampleMeasurementRows: NonIntegratedSampleMeasurementRow[];
|
||||
isSampleMeasurementConfirmed: boolean;
|
||||
visibleGroupedUploadFiles: GroupedWorkspaceFiles[];
|
||||
onOpenBasicInfo: () => void;
|
||||
onOpenDocumentWorkspace: (category: DocumentWorkspaceCategory) => void;
|
||||
@ -74,13 +78,14 @@ export function DocumentWorkspaceSidebar({
|
||||
const categoryHasContent =
|
||||
category === PRODUCT_FACTORY_WORKSPACE_CATEGORY
|
||||
? productFactoryRows.length > 0
|
||||
: category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY
|
||||
? sampleMeasurementRows.length > 0
|
||||
: hasFiles;
|
||||
const categoryNeedsConfirmation =
|
||||
category === PRODUCT_FACTORY_WORKSPACE_CATEGORY
|
||||
? !(
|
||||
productFactoryRows.length > 0 &&
|
||||
productFactoryRows.every(isProductFactoryRowComplete)
|
||||
)
|
||||
? !productFactoryRows.every((row) => row.confirmed)
|
||||
: category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY
|
||||
? !isSampleMeasurementConfirmed
|
||||
: needsConfirmation;
|
||||
const shouldInsertBasicInfoTrigger = index === basicInfoInsertIndex;
|
||||
const basicInfoIsActive = documentWorkspaceView === "detail";
|
||||
|
||||
@ -8,7 +8,7 @@ import Input, { Textarea } from '@/components/ui/Input'
|
||||
import Modal from '@/components/ui/Modal'
|
||||
import Button from '@/components/ui/Button'
|
||||
import Select from '@/components/ui/Select'
|
||||
import { ToastContainer } from '@/components/ui/Toast'
|
||||
import { ToastContainer, type ToastData } from '@/components/ui/Toast'
|
||||
import type { ProductWithSkus } from '../home/home-client.types'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
|
||||
@ -63,12 +63,12 @@ export interface HomeMobileViewProps {
|
||||
|
||||
/* 删除产品 */
|
||||
deletingProduct: ProductWithSkus | null
|
||||
onDeleteProduct: () => void
|
||||
onDeleteProduct: () => void | Promise<void>
|
||||
onCloseDeleteProduct: () => void
|
||||
|
||||
/* 删除 SKU */
|
||||
deletingSku: { product: ProductWithSkus; sku: { skuCode: string } } | null
|
||||
onDeleteSKU: () => void
|
||||
onDeleteSKU: () => void | Promise<void>
|
||||
onCloseDeleteSKU: () => void
|
||||
|
||||
/* 添加 SKU */
|
||||
@ -81,7 +81,7 @@ export interface HomeMobileViewProps {
|
||||
onMarkHomeReturn: () => void
|
||||
|
||||
/* Toast */
|
||||
toasts: Array<{ id: string; message: string; type?: string }>
|
||||
toasts: ToastData[]
|
||||
onRemoveToast: (id: string) => void
|
||||
}
|
||||
|
||||
@ -406,7 +406,9 @@ export default function HomeMobileView({
|
||||
<DeleteConfirmDialog
|
||||
isOpen={!!deletingProduct}
|
||||
onClose={onCloseDeleteProduct}
|
||||
onConfirm={onDeleteProduct}
|
||||
onConfirm={async () => {
|
||||
await onDeleteProduct()
|
||||
}}
|
||||
title="删除产品"
|
||||
description={
|
||||
deletingProduct
|
||||
@ -428,7 +430,9 @@ export default function HomeMobileView({
|
||||
<DeleteConfirmDialog
|
||||
isOpen={!!deletingSku}
|
||||
onClose={onCloseDeleteSKU}
|
||||
onConfirm={onDeleteSKU}
|
||||
onConfirm={async () => {
|
||||
await onDeleteSKU()
|
||||
}}
|
||||
title="删除 SKU"
|
||||
description={
|
||||
deletingSku
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user