diff --git a/furniture-3d-system/src/server/services/__tests__/intake-non-integrated-product.service.test.ts b/furniture-3d-system/src/server/services/__tests__/intake-non-integrated-product.service.test.ts index b707a20e..b7dc9995 100644 --- a/furniture-3d-system/src/server/services/__tests__/intake-non-integrated-product.service.test.ts +++ b/furniture-3d-system/src/server/services/__tests__/intake-non-integrated-product.service.test.ts @@ -927,6 +927,72 @@ describe('intakeNonIntegratedProductService', () => { ) }) + it('分配正式 SPU 时用产品名和 SPU 补齐单 SKU 主数据', async () => { + const payload = createPayload({ + id: 'draft-single-sku-master-data-autofill', + spu: '', + productCategory: '角几/边几', + productDevelopmentKind: 'product', + openingReference: { + productName: '禾沐边几(A款)', + productCategory: '角几/边几', + }, + skuItems: [ + createSkuItem({ + skuSequence: 1, + specName: '', + model: '', + skuCode: '', + barcode: '', + }), + ], + }) + mockedSpuNumberReservationService.reserveInTransaction.mockResolvedValue({ + productCategory: '角几/边几', + prefix: 'BJ', + index: 214, + spu: 'BJ0214', + }) + mockedRepository.findDeletedBySpuInTransaction.mockResolvedValue(null as never) + mockedRepository.findBySpuInTransaction.mockResolvedValue(null as never) + mockedRepository.createInTransaction.mockResolvedValue( + createRecord({ id: 'BJ0214-1770000000000' }) as never, + ) + mockedRepository.findActiveById.mockResolvedValue( + createRecord({ + id: 'BJ0214-1770000000000', + spu: 'BJ0214', + spuBase: 'BJ0214', + payload: createPayload({ id: 'BJ0214-1770000000000', spu: 'BJ0214' }), + }) as never, + ) + + await intakeNonIntegratedProductService.save(payload, baseUser, { + formalSpuAssignmentProductCategory: '角几/边几', + }) + + const createArg = mockedRepository.createInTransaction.mock.calls[0]?.[1] as { + payload?: { skuItems?: Array> } + skuItems?: { createMany?: { data?: Array> } } + } + expect(createArg.payload?.skuItems?.[0]).toEqual( + expect.objectContaining({ + specName: '禾沐边几(A款)', + model: 'BJ0214', + skuCode: 'BJ021401', + barcode: 'BJ021401N01V01', + }), + ) + expect(createArg.skuItems?.createMany?.data?.[0]).toEqual( + expect.objectContaining({ + specName: '禾沐边几(A款)', + model: 'BJ0214', + dataQualityStatus: 'READY', + missingRequiredFields: [], + }), + ) + }) + it('分配正式 SPU 时阻断未补齐的 SKU 主数据,避免生成新的地基阻塞', async () => { const payload = createPayload({ id: 'draft-incomplete-before-formal-spu', diff --git a/furniture-3d-system/src/server/services/intake-non-integrated-product.service.ts b/furniture-3d-system/src/server/services/intake-non-integrated-product.service.ts index b19d7e30..2f3aa6e3 100644 --- a/furniture-3d-system/src/server/services/intake-non-integrated-product.service.ts +++ b/furniture-3d-system/src/server/services/intake-non-integrated-product.service.ts @@ -345,7 +345,13 @@ function shouldPreserveFormalSkuCode(skuCode: string, formalSpu: string) { return Boolean(normalizedSkuCode && normalizedFormalSpu && normalizedSkuCode.startsWith(normalizedFormalSpu)) } -function assignFormalSpuToSkuItems(value: unknown, formalSpu: string) { +function assignFormalSpuToSkuItems( + value: unknown, + formalSpu: string, + options: { + fallbackSingleSkuSpecName?: string + } = {}, +) { if (!Array.isArray(value)) return value return value.map((item, index) => { @@ -360,9 +366,19 @@ function assignFormalSpuToSkuItems(value: unknown, formalSpu: string) { const barcode = existingBarcode && existingBarcode.startsWith(skuCode) ? existingBarcode : buildAutoBarcode(skuCode, item.manufacturerCode, versionNo) + const shouldAutofillSingleSkuMasterData = + value.length === 1 && Boolean(readText(options.fallbackSingleSkuSpecName)) return { ...item, + specName: + readText(item.specName) || + (shouldAutofillSingleSkuMasterData + ? readText(options.fallbackSingleSkuSpecName) + : item.specName), + model: + readText(item.model) || + (shouldAutofillSingleSkuMasterData ? formalSpu.trim().toUpperCase() : item.model), skuCode, barcode, versionNo, @@ -4094,7 +4110,9 @@ class IntakeNonIntegratedProductService { : assignedSpu, brand: normalizedBrand, formalSpuAssigned: true, - skuItems: assignFormalSpuToSkuItems(productInput.skuItems, assignedSpu), + skuItems: assignFormalSpuToSkuItems(productInput.skuItems, assignedSpu, { + fallbackSingleSkuSpecName: readText(openingReference.productName), + }), openingReference: { ...openingReference, brand: readText(openingReference.brand) || normalizedBrand,