Compare commits
1 Commits
main
...
codex/wu-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15c082a712 |
@ -501,6 +501,7 @@ export function SearchableInlineSelect({
|
||||
emptyOptionDisabled = false,
|
||||
maxVisibleOptions,
|
||||
onQueryChange,
|
||||
clearValueOnUnselectedQuery = false,
|
||||
onChange,
|
||||
}: {
|
||||
ariaLabel: string;
|
||||
@ -517,6 +518,7 @@ export function SearchableInlineSelect({
|
||||
emptyOptionDisabled?: boolean;
|
||||
maxVisibleOptions?: number;
|
||||
onQueryChange?: (value: string) => void;
|
||||
clearValueOnUnselectedQuery?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
}) {
|
||||
const listId = useId();
|
||||
@ -525,6 +527,7 @@ export function SearchableInlineSelect({
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [query, setQuery] = useState("");
|
||||
const [hasUnselectedQuery, setHasUnselectedQuery] = useState(false);
|
||||
const normalizedOptions = normalizeInlineSelectOptions(options);
|
||||
const shouldIncludeEmptyOption = includeEmptyOption ?? false;
|
||||
const selectOptions = shouldIncludeEmptyOption
|
||||
@ -595,6 +598,7 @@ export function SearchableInlineSelect({
|
||||
const openPanel = useCallback(() => {
|
||||
if (!hasOptions) return;
|
||||
setQuery(displayValue);
|
||||
setHasUnselectedQuery(false);
|
||||
setIsOpen(true);
|
||||
window.setTimeout(() => inputRef.current?.select(), 0);
|
||||
}, [displayValue, hasOptions]);
|
||||
@ -604,6 +608,7 @@ export function SearchableInlineSelect({
|
||||
if (option.disabled) return;
|
||||
onChange(option.value);
|
||||
setQuery(option.label);
|
||||
setHasUnselectedQuery(false);
|
||||
setIsOpen(false);
|
||||
},
|
||||
[onChange],
|
||||
@ -629,11 +634,13 @@ export function SearchableInlineSelect({
|
||||
if (!hasOptions) return;
|
||||
const nextQuery = event.target.value;
|
||||
setQuery(nextQuery);
|
||||
setHasUnselectedQuery(true);
|
||||
onQueryChange?.(nextQuery);
|
||||
setIsOpen(true);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Escape") {
|
||||
setHasUnselectedQuery(false);
|
||||
setIsOpen(false);
|
||||
return;
|
||||
}
|
||||
@ -650,6 +657,10 @@ export function SearchableInlineSelect({
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (clearValueOnUnselectedQuery && hasUnselectedQuery) {
|
||||
onChange("");
|
||||
}
|
||||
setHasUnselectedQuery(false);
|
||||
setQuery(displayValue);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
|
||||
@ -3,6 +3,7 @@ import { SUPPLIER_DIRECTORY } from "../supplierDirectory";
|
||||
import {
|
||||
InlineSelect,
|
||||
InlineSuggestionInput,
|
||||
SearchableInlineSelect,
|
||||
} from "./InlineControls";
|
||||
import { SkuColumnResizableFrame } from "./SkuTableFields";
|
||||
|
||||
@ -72,6 +73,7 @@ export function SkuSupplierNameField({
|
||||
options = SUPPLIER_DIRECTORY.map((entry) => entry.name),
|
||||
readOnly = false,
|
||||
columnWidth = SKU_SUPPLIER_COLUMN_WIDTH,
|
||||
requireDirectorySelection = false,
|
||||
onChange,
|
||||
onConfirm,
|
||||
onColumnResize,
|
||||
@ -80,6 +82,7 @@ export function SkuSupplierNameField({
|
||||
options?: readonly string[];
|
||||
readOnly?: boolean;
|
||||
columnWidth?: number;
|
||||
requireDirectorySelection?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
onConfirm: () => void;
|
||||
onColumnResize?: (width: number) => void;
|
||||
@ -93,6 +96,22 @@ export function SkuSupplierNameField({
|
||||
readOnly ? "bg-apple-bg-primary" : "bg-apple-bg-secondary"
|
||||
}`}
|
||||
>
|
||||
{requireDirectorySelection ? (
|
||||
<SearchableInlineSelect
|
||||
ariaLabel={"\u4f9b\u5e94\u5546\u540d\u79f0"}
|
||||
value={value}
|
||||
options={options}
|
||||
placeholder={"\u641c\u7d22\u4f9b\u5e94\u5546\u540d\u79f0"}
|
||||
readOnly={readOnly}
|
||||
maxVisibleOptions={10}
|
||||
clearValueOnUnselectedQuery
|
||||
onChange={onChange}
|
||||
inputClassName={`h-8 w-full border-0 bg-transparent px-2 pr-8 text-xs text-apple-label-primary outline-none ${
|
||||
readOnly ? "text-apple-label-secondary" : ""
|
||||
}`}
|
||||
panelClassName="absolute left-0 top-[calc(100%+4px)] z-50 max-h-56 min-w-full overflow-auto rounded-apple-lg border border-apple-separator bg-apple-bg-primary p-1 shadow-apple-md"
|
||||
/>
|
||||
) : (
|
||||
<InlineSuggestionInput
|
||||
ariaLabel={"\u4f9b\u5e94\u5546\u540d\u79f0"}
|
||||
value={value}
|
||||
@ -107,6 +126,7 @@ export function SkuSupplierNameField({
|
||||
}`}
|
||||
panelClassName="absolute left-0 top-[calc(100%+4px)] z-50 max-h-56 min-w-full overflow-auto rounded-apple-lg border border-apple-separator bg-apple-bg-primary p-1 shadow-apple-md"
|
||||
/>
|
||||
)}
|
||||
</SkuColumnResizableFrame>
|
||||
);
|
||||
}
|
||||
@ -124,31 +144,6 @@ export function FactoryWarehouseField({
|
||||
columnWidth?: number;
|
||||
onChange: (value: string) => void;
|
||||
}) {
|
||||
const handleWarehouseChange = (input: string) => {
|
||||
const trimmed = input.trim();
|
||||
|
||||
// 精确匹配就直接用
|
||||
if (options.includes(trimmed)) {
|
||||
onChange(trimmed);
|
||||
return;
|
||||
}
|
||||
|
||||
// 自动纠正:如果输入不完整,尝试匹配唯一包含的选项
|
||||
// 例如输入"河北源沃代发仓",自动匹配为"工厂-河北源沃代发仓"
|
||||
const normalizedInput = trimmed.toLowerCase();
|
||||
const containsMatches = options.filter((option) =>
|
||||
option.toLowerCase().includes(normalizedInput),
|
||||
);
|
||||
|
||||
if (containsMatches.length === 1) {
|
||||
onChange(containsMatches[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// 没匹配到就原样传,让用户自己处理
|
||||
onChange(trimmed);
|
||||
};
|
||||
|
||||
return (
|
||||
<SkuColumnResizableFrame
|
||||
columnWidth={columnWidth}
|
||||
@ -157,14 +152,15 @@ export function FactoryWarehouseField({
|
||||
readOnly ? "bg-apple-bg-primary" : "bg-apple-bg-secondary"
|
||||
}`}
|
||||
>
|
||||
<InlineSuggestionInput
|
||||
<SearchableInlineSelect
|
||||
ariaLabel={"\u4ed3\u5e93"}
|
||||
value={value}
|
||||
options={options}
|
||||
placeholder={"\u641c\u7d22\u4ed3\u5e93"}
|
||||
readOnly={readOnly}
|
||||
filterOptions
|
||||
onChange={handleWarehouseChange}
|
||||
maxVisibleOptions={10}
|
||||
clearValueOnUnselectedQuery
|
||||
onChange={onChange}
|
||||
inputClassName={`h-8 w-full border-0 bg-transparent px-2 pr-8 text-xs text-apple-label-primary outline-none ${
|
||||
readOnly ? "text-apple-label-secondary" : ""
|
||||
}`}
|
||||
|
||||
@ -432,6 +432,7 @@ export function SkuInfoRow({
|
||||
options={supplierNameOptions}
|
||||
readOnly={skuRowReadOnly}
|
||||
columnWidth={skuColumnWidths.supplierName}
|
||||
requireDirectorySelection
|
||||
onChange={(value) => onUpdateSkuSupplierName(item.id, value)}
|
||||
onConfirm={() => void onConfirmSkuSupplierName(item.id)}
|
||||
onColumnResize={(width) => onColumnResize("supplierName", width)}
|
||||
|
||||
@ -36,6 +36,22 @@ export function normalizeFactorySupplier(
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeProductFactorySupplier(
|
||||
value: string,
|
||||
supplierCodeByName: ReadonlyMap<string, string> = SUPPLIER_CODE_BY_NAME,
|
||||
) {
|
||||
const normalizedName = value.trim();
|
||||
const matchedSupplier = resolveSupplierEntryByName(
|
||||
normalizedName,
|
||||
supplierCodeByName,
|
||||
);
|
||||
|
||||
return {
|
||||
name: matchedSupplier?.name ?? "",
|
||||
code: matchedSupplier?.code?.trim().toUpperCase() ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
export function hasPackageSharedFactoryRowContent(
|
||||
row: NonIntegratedFactoryRow,
|
||||
) {
|
||||
@ -207,13 +223,15 @@ export function createProductFactoryRowFromSku(
|
||||
const savedSupplierCode =
|
||||
existingRow?.supplierCode?.trim().toUpperCase() ?? "";
|
||||
const supplierSource = sku.manufacturer || savedSupplierName;
|
||||
const supplier = normalizeFactorySupplier(supplierSource, supplierCodeByName);
|
||||
const supplier = normalizeProductFactorySupplier(
|
||||
supplierSource,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierCode =
|
||||
supplier.code ||
|
||||
sku.manufacturerCode.trim().toUpperCase() ||
|
||||
savedSupplierCode;
|
||||
const supplierName =
|
||||
supplierSource.trim() || supplier.name || sku.manufacturer;
|
||||
const supplierName = supplier.name;
|
||||
const skuCode = sku.skuCode.trim();
|
||||
const fallbackBarcode =
|
||||
sku.barcode.trim() || buildAutoBarcode(skuCode, supplierCode, sku.versionNo);
|
||||
@ -297,13 +315,12 @@ export function normalizeFactoryRowsForPayload(
|
||||
supplierCodeByName: ReadonlyMap<string, string> = SUPPLIER_CODE_BY_NAME,
|
||||
): NonIntegratedFactoryRow[] {
|
||||
const normalizedRows = rows.map((row) => {
|
||||
const supplier = normalizeFactorySupplier(
|
||||
const supplier = normalizeProductFactorySupplier(
|
||||
row.supplierName,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierName = supplier.name || row.supplierName.trim();
|
||||
const supplierCode =
|
||||
row.supplierCode.trim().toUpperCase() || supplier.code;
|
||||
const supplierName = supplier.name;
|
||||
const supplierCode = supplier.code;
|
||||
const skuCode = row.skuCode.trim();
|
||||
|
||||
return {
|
||||
|
||||
@ -186,6 +186,7 @@ export function ProductFactoryInfoTable({
|
||||
value={row.supplierName}
|
||||
options={supplierNameOptions}
|
||||
readOnly={isFactoryFieldReadOnly(row, "supplierName")}
|
||||
requireDirectorySelection
|
||||
onChange={(value) =>
|
||||
onUpdateRow(row.skuId, "supplierName", value)
|
||||
}
|
||||
|
||||
@ -11,12 +11,15 @@ import { buildAutoBarcode } from "../basic-info/skuAutoHelpers";
|
||||
import {
|
||||
buildProductFactoryRows,
|
||||
normalizeFactoryRowsForPayload,
|
||||
normalizeFactorySupplier,
|
||||
normalizeProductFactorySupplier,
|
||||
resolveAutoAwareBarcode,
|
||||
} from "../basic-info/productFactoryHelpers";
|
||||
import { getProductFactoryModelAutofillKey } from "../basic-info/productFactoryKeyHelpers";
|
||||
import { getProductFactoryTargetSkuIds } from "./productFactoryTargetSkuHelpers";
|
||||
|
||||
export const resolveProductFactoryDirectorySupplier =
|
||||
normalizeProductFactorySupplier;
|
||||
|
||||
export function updateProductFactoryRowsForSku({
|
||||
autofillEntries,
|
||||
currentRows,
|
||||
@ -55,8 +58,11 @@ export function updateProductFactoryRowsForSku({
|
||||
const versionNo = derivedSkuItem?.versionNo ?? "V01";
|
||||
|
||||
if (key === "supplierName") {
|
||||
const supplier = normalizeFactorySupplier(value, supplierCodeByName);
|
||||
const supplierName = supplier.name || value;
|
||||
const supplier = resolveProductFactoryDirectorySupplier(
|
||||
value,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierName = supplier.name;
|
||||
const supplierCode = supplier.code;
|
||||
|
||||
return {
|
||||
@ -121,13 +127,12 @@ export function confirmProductFactorySupplierRowsForSku({
|
||||
(item) => item.id === row.skuId,
|
||||
);
|
||||
const versionNo = derivedSkuItem?.versionNo ?? "V01";
|
||||
const supplier = normalizeFactorySupplier(
|
||||
const supplier = resolveProductFactoryDirectorySupplier(
|
||||
row.supplierName,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierName = supplier.name || row.supplierName.trim();
|
||||
const supplierCode =
|
||||
row.supplierCode.trim().toUpperCase() || supplier.code;
|
||||
const supplierName = supplier.name;
|
||||
const supplierCode = supplier.code;
|
||||
|
||||
return {
|
||||
...row,
|
||||
|
||||
@ -20,7 +20,6 @@ import {
|
||||
import {
|
||||
getProductFactoryStatusMessage,
|
||||
normalizeFactoryRowsForPayload,
|
||||
normalizeFactorySupplier,
|
||||
} from "../basic-info/productFactoryHelpers";
|
||||
import { buildSkuItemsAfterSetCountCommit } from "../basic-info/skuSetCountCommitHelpers";
|
||||
import { getStyleKeyFromModel } from "../style-assets/styleFileHelpers";
|
||||
@ -29,6 +28,7 @@ import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers
|
||||
import {
|
||||
confirmProductFactorySupplierRowsForSku,
|
||||
reopenProductFactoryRows,
|
||||
resolveProductFactoryDirectorySupplier,
|
||||
updateProductFactoryRowsForSku,
|
||||
} from "./productFactoryRowUpdateHelpers";
|
||||
import { getProductFactoryTargetSkuIds } from "./productFactoryTargetSkuHelpers";
|
||||
@ -129,8 +129,11 @@ export function useProductFactoryRowController({
|
||||
if (currentFactoryRow?.confirmed) return;
|
||||
|
||||
if (key === "supplierName") {
|
||||
const supplier = normalizeFactorySupplier(value, supplierCodeByName);
|
||||
const supplierName = supplier.name || value;
|
||||
const supplier = resolveProductFactoryDirectorySupplier(
|
||||
value,
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierName = supplier.name;
|
||||
|
||||
setSkuItems((current) => {
|
||||
const targetSkuIds = getProductFactoryTargetSkuIds(
|
||||
@ -228,12 +231,11 @@ export function useProductFactoryRowController({
|
||||
if (isReadOnly) return;
|
||||
const currentRow = productFactoryRows.find((row) => row.skuId === skuId);
|
||||
if (currentRow?.confirmed) return;
|
||||
const supplier = normalizeFactorySupplier(
|
||||
const supplier = resolveProductFactoryDirectorySupplier(
|
||||
currentRow?.supplierName ?? "",
|
||||
supplierCodeByName,
|
||||
);
|
||||
const supplierName =
|
||||
supplier.name || currentRow?.supplierName.trim() || "";
|
||||
const supplierName = supplier.name;
|
||||
|
||||
if (supplierName) {
|
||||
setSkuItems((current) =>
|
||||
|
||||
@ -138,6 +138,10 @@ jest.mock('../intake-non-integrated-operation-log.service', () => ({
|
||||
mockRecordIntakeWorkspaceConfirmationChange(...args),
|
||||
}))
|
||||
|
||||
jest.mock('../intake-directory-selection.service', () => ({
|
||||
normalizeIntakeDirectorySelectionsForSave: jest.fn(async (product) => product),
|
||||
}))
|
||||
|
||||
import { intakeNonIntegratedProductRepository } from '../../repositories/intake-non-integrated-product.repository'
|
||||
import { userRepository } from '../../repositories/user.repository'
|
||||
import { assetSkuGroupRepository, assetVariantRepository } from '../../repositories/asset-center.repository'
|
||||
|
||||
@ -0,0 +1,142 @@
|
||||
import type { NonIntegratedProductRecord } from "@/app/intake/non-integrated/nonIntegratedStorage";
|
||||
|
||||
import { normalizeIntakeDirectorySelections } from "./intake-directory-selection.service";
|
||||
|
||||
function createProduct(
|
||||
overrides: Partial<NonIntegratedProductRecord> = {},
|
||||
): NonIntegratedProductRecord {
|
||||
return {
|
||||
skuItems: [
|
||||
{
|
||||
id: "sku-1",
|
||||
skuSequence: 1,
|
||||
batchNo: 1,
|
||||
packageGroupId: "",
|
||||
packageSuffix: "",
|
||||
image: "",
|
||||
imageName: "",
|
||||
model: "",
|
||||
skuCode: "SKU-1",
|
||||
setCount: "1",
|
||||
specName: "",
|
||||
barcode: "",
|
||||
size: "",
|
||||
color: "",
|
||||
material: "",
|
||||
versionNo: "V01",
|
||||
attributeCode: "",
|
||||
drawingId: "",
|
||||
installMethod: "",
|
||||
executeStandard: "",
|
||||
nationalStandardCompliant: "",
|
||||
manufacturer: "",
|
||||
manufacturerCode: "",
|
||||
salesStatus: "",
|
||||
tagMaterial: "",
|
||||
model3dFileName: "",
|
||||
model3dDataUrl: "",
|
||||
},
|
||||
],
|
||||
factoryRows: [
|
||||
{
|
||||
skuId: "sku-1",
|
||||
confirmed: false,
|
||||
model: "",
|
||||
skuCode: "SKU-1",
|
||||
setCount: "1",
|
||||
barcode: "",
|
||||
specName: "",
|
||||
size: "",
|
||||
color: "",
|
||||
material: "",
|
||||
installMethod: "",
|
||||
supplierName: "",
|
||||
supplierCode: "",
|
||||
warehouse: "",
|
||||
shippingMethod: "",
|
||||
minimumOrderQuantity: "",
|
||||
taxIncludedUnitPrice: "",
|
||||
leadTime: "",
|
||||
assemblyMode: "",
|
||||
productionType: "",
|
||||
installationService: "",
|
||||
supplierCategory: "",
|
||||
purchaseOrderMethod: "",
|
||||
executeStandard: "",
|
||||
},
|
||||
],
|
||||
} as NonIntegratedProductRecord;
|
||||
}
|
||||
|
||||
describe("intake directory selections", () => {
|
||||
const directory = {
|
||||
suppliers: [{ name: "阿凡达商贸", code: "N123" }],
|
||||
warehouses: ["成品代发仓"],
|
||||
};
|
||||
|
||||
it("only keeps an explicitly directory-backed supplier and warehouse", () => {
|
||||
const product = createProduct({
|
||||
skuItems: [
|
||||
{
|
||||
...createProduct().skuItems[0],
|
||||
manufacturer: "阿",
|
||||
manufacturerCode: "OLD",
|
||||
},
|
||||
],
|
||||
factoryRows: [
|
||||
{
|
||||
...createProduct().factoryRows[0],
|
||||
supplierName: "阿",
|
||||
supplierCode: "OLD",
|
||||
warehouse: "不存在的仓库",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = normalizeIntakeDirectorySelections(product, directory);
|
||||
|
||||
expect(result.skuItems[0]).toEqual(
|
||||
expect.objectContaining({ manufacturer: "", manufacturerCode: "" }),
|
||||
);
|
||||
expect(result.factoryRows[0]?.warehouse).toBe("");
|
||||
expect(result.factoryRows[0]).toEqual(
|
||||
expect.objectContaining({ supplierName: "", supplierCode: "" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("stores the selected directory value and its supplier code", () => {
|
||||
const product = createProduct({
|
||||
skuItems: [
|
||||
{
|
||||
...createProduct().skuItems[0],
|
||||
manufacturer: "阿凡达商贸",
|
||||
manufacturerCode: "OLD",
|
||||
},
|
||||
],
|
||||
factoryRows: [
|
||||
{
|
||||
...createProduct().factoryRows[0],
|
||||
supplierName: "阿凡达商贸",
|
||||
supplierCode: "OLD",
|
||||
warehouse: "成品代发仓",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = normalizeIntakeDirectorySelections(product, directory);
|
||||
|
||||
expect(result.skuItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
manufacturer: "阿凡达商贸",
|
||||
manufacturerCode: "N123",
|
||||
}),
|
||||
);
|
||||
expect(result.factoryRows[0]?.warehouse).toBe("成品代发仓");
|
||||
expect(result.factoryRows[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
supplierName: "阿凡达商贸",
|
||||
supplierCode: "N123",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,89 @@
|
||||
import type { NonIntegratedProductRecord } from "@/app/intake/non-integrated/nonIntegratedStorage";
|
||||
|
||||
import {
|
||||
jstPlanningDirectoryService,
|
||||
type JstPlanningDirectoryOptions,
|
||||
} from "./jst-planning-directory.service";
|
||||
|
||||
function normalizeDirectoryKey(value: string) {
|
||||
return value.trim().toLocaleLowerCase();
|
||||
}
|
||||
|
||||
function hasDirectoryBackedValues(product: NonIntegratedProductRecord) {
|
||||
return (
|
||||
product.skuItems.some(
|
||||
(item) => item.manufacturer.trim() || item.manufacturerCode.trim(),
|
||||
) ||
|
||||
product.factoryRows.some(
|
||||
(row) =>
|
||||
row.supplierName.trim() ||
|
||||
row.supplierCode.trim() ||
|
||||
row.warehouse.trim(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeIntakeDirectorySelections<
|
||||
TProduct extends NonIntegratedProductRecord,
|
||||
>(product: TProduct, directory: JstPlanningDirectoryOptions): TProduct {
|
||||
const suppliersByName = new Map(
|
||||
directory.suppliers.map((supplier) => [
|
||||
normalizeDirectoryKey(supplier.name),
|
||||
{
|
||||
name: supplier.name.trim(),
|
||||
code: supplier.code.trim().toUpperCase(),
|
||||
},
|
||||
]),
|
||||
);
|
||||
const warehousesByName = new Map(
|
||||
directory.warehouses.map((warehouse) => [
|
||||
normalizeDirectoryKey(warehouse),
|
||||
warehouse.trim(),
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
...product,
|
||||
skuItems: product.skuItems.map((item) => {
|
||||
const supplier = suppliersByName.get(
|
||||
normalizeDirectoryKey(item.manufacturer),
|
||||
);
|
||||
|
||||
return supplier
|
||||
? {
|
||||
...item,
|
||||
manufacturer: supplier.name,
|
||||
manufacturerCode: supplier.code,
|
||||
}
|
||||
: {
|
||||
...item,
|
||||
manufacturer: "",
|
||||
manufacturerCode: "",
|
||||
};
|
||||
}),
|
||||
factoryRows: product.factoryRows.map((row) => {
|
||||
const supplier = suppliersByName.get(
|
||||
normalizeDirectoryKey(row.supplierName),
|
||||
);
|
||||
const warehouse = warehousesByName.get(
|
||||
normalizeDirectoryKey(row.warehouse),
|
||||
);
|
||||
|
||||
return {
|
||||
...row,
|
||||
supplierName: supplier?.name ?? "",
|
||||
supplierCode: supplier?.code ?? "",
|
||||
warehouse: warehouse ?? "",
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function normalizeIntakeDirectorySelectionsForSave<
|
||||
TProduct extends NonIntegratedProductRecord,
|
||||
>(product: TProduct): Promise<TProduct> {
|
||||
if (!hasDirectoryBackedValues(product)) return product;
|
||||
|
||||
const directory = await jstPlanningDirectoryService.listOptions();
|
||||
return normalizeIntakeDirectorySelections(product, directory);
|
||||
}
|
||||
@ -95,6 +95,7 @@ import { skuQualityInspectionService } from './sku-quality-inspection.service'
|
||||
import { spuNumberReservationService } from './spu-number-reservation.service'
|
||||
import { supplyChainProductArchiveSyncService } from './supply-chain-product-archive-sync.service'
|
||||
import { archiveChangeActorFromUser } from './supply-chain-product-archive-change-log.service'
|
||||
import { normalizeIntakeDirectorySelectionsForSave } from './intake-directory-selection.service'
|
||||
|
||||
type NonIntegratedUploadPresentationPreview = {
|
||||
pdfUrl: string
|
||||
@ -6620,7 +6621,7 @@ class IntakeNonIntegratedProductService {
|
||||
useExactFormalSpu: Boolean(existingFormalSpu),
|
||||
},
|
||||
)
|
||||
const normalizedProduct = buildPersistedProductPayload(
|
||||
let normalizedProduct = buildPersistedProductPayload(
|
||||
assignedProductInput,
|
||||
existingRecord?.payload,
|
||||
existingRecord?.createdAt.toISOString(),
|
||||
@ -6628,6 +6629,7 @@ class IntakeNonIntegratedProductService {
|
||||
if (!normalizedProduct) {
|
||||
throw new AppError(ErrorCode.VALIDATION_FAILED, '非集成产品数据不完整,无法保存')
|
||||
}
|
||||
normalizedProduct = await normalizeIntakeDirectorySelectionsForSave(normalizedProduct)
|
||||
assertNonIntegratedProductMasterDataReadyForFormalUse(normalizedProduct, {
|
||||
allowedMissingFields:
|
||||
normalizedProduct.skuItems.length === 1 ? ['spec_name'] : [],
|
||||
@ -6966,7 +6968,7 @@ class IntakeNonIntegratedProductService {
|
||||
throw new AppError(ErrorCode.PRODUCT_NOT_FOUND, '非集成产品不存在')
|
||||
}
|
||||
|
||||
const normalizedProduct = buildPersistedProductPayload(
|
||||
let normalizedProduct = buildPersistedProductPayload(
|
||||
productInput,
|
||||
existingRecordCandidate?.payload,
|
||||
existingRecordCandidate?.createdAt.toISOString(),
|
||||
@ -6974,6 +6976,7 @@ class IntakeNonIntegratedProductService {
|
||||
if (!normalizedProduct) {
|
||||
throw new AppError(ErrorCode.VALIDATION_FAILED, '非集成产品数据不完整,无法保存')
|
||||
}
|
||||
normalizedProduct = await normalizeIntakeDirectorySelectionsForSave(normalizedProduct)
|
||||
|
||||
const normalizedSpu = resolveNonIntegratedPersistedSpu(normalizedProduct)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user