fix: sync stock spu filters and imported sku codes
This commit is contained in:
parent
668391f983
commit
273097f8b5
@ -4664,13 +4664,21 @@ function deriveSkuItem(
|
||||
const autoSpecName = buildAutoSpecName(item);
|
||||
const versionNo = normalizeSkuVersionNo(item.versionNo);
|
||||
const normalizedItem = { ...item, versionNo };
|
||||
const autoDrawingId = buildAutoDrawingId(normalizedItem);
|
||||
const importedContentLocked = isImportedSkuContentLocked(item);
|
||||
const resolvedSkuCode =
|
||||
importedContentLocked && item.skuCode.trim()
|
||||
? item.skuCode.trim().toUpperCase()
|
||||
: finalSkuCode;
|
||||
const autoDrawingId = buildAutoDrawingId({
|
||||
...normalizedItem,
|
||||
skuCode: resolvedSkuCode,
|
||||
});
|
||||
const autoBarcode = buildAutoBarcode(
|
||||
finalSkuCode,
|
||||
resolvedSkuCode,
|
||||
item.manufacturerCode,
|
||||
versionNo,
|
||||
);
|
||||
const importedLockedBarcode = isImportedSkuContentLocked(item)
|
||||
const importedLockedBarcode = importedContentLocked
|
||||
? item.barcode.trim().toUpperCase()
|
||||
: "";
|
||||
|
||||
@ -4680,22 +4688,28 @@ function deriveSkuItem(
|
||||
Number.isFinite(item.batchNo) && item.batchNo > 0
|
||||
? Math.floor(item.batchNo)
|
||||
: 1,
|
||||
skuCode: finalSkuCode,
|
||||
specName: autoSpecName,
|
||||
skuCode: resolvedSkuCode,
|
||||
specName:
|
||||
importedContentLocked && item.specName.trim()
|
||||
? item.specName.trim()
|
||||
: autoSpecName,
|
||||
barcode:
|
||||
importedLockedBarcode ||
|
||||
(shouldKeepCustomBarcode(
|
||||
item.barcode,
|
||||
finalSkuCode,
|
||||
resolvedSkuCode,
|
||||
item.manufacturerCode,
|
||||
versionNo,
|
||||
)
|
||||
? item.barcode.trim()
|
||||
: autoBarcode),
|
||||
versionNo,
|
||||
drawingId: shouldKeepCustomDrawingId(item.drawingId, normalizedItem)
|
||||
? item.drawingId.trim()
|
||||
: autoDrawingId,
|
||||
drawingId:
|
||||
importedContentLocked && item.drawingId.trim()
|
||||
? item.drawingId.trim().toUpperCase()
|
||||
: shouldKeepCustomDrawingId(item.drawingId, normalizedItem)
|
||||
? item.drawingId.trim()
|
||||
: autoDrawingId,
|
||||
installMethod: item.installMethod.trim(),
|
||||
executeStandard: item.executeStandard.trim(),
|
||||
nationalStandardCompliant: normalizeNationalStandardCompliance(
|
||||
@ -4816,13 +4830,22 @@ function createProductFactoryRowFromSku(
|
||||
): NonIntegratedFactoryRow {
|
||||
const confirmed = existingRow?.confirmed ?? false;
|
||||
const savedSupplierName = existingRow?.supplierName?.trim() ?? "";
|
||||
const supplierSource = confirmed
|
||||
? sku.manufacturer || savedSupplierName
|
||||
: savedSupplierName || sku.manufacturer;
|
||||
const importedContentLocked = isImportedSkuContentLocked(sku);
|
||||
const supplierSource =
|
||||
importedContentLocked && !confirmed
|
||||
? sku.manufacturer || savedSupplierName
|
||||
: confirmed
|
||||
? sku.manufacturer || savedSupplierName
|
||||
: savedSupplierName || sku.manufacturer;
|
||||
const supplier = normalizeFactorySupplier(supplierSource);
|
||||
const supplierCode =
|
||||
confirmed
|
||||
? sku.manufacturerCode.trim().toUpperCase() || supplier.code
|
||||
importedContentLocked && !confirmed
|
||||
? supplier.code ||
|
||||
sku.manufacturerCode.trim().toUpperCase() ||
|
||||
existingRow?.supplierCode?.trim().toUpperCase() ||
|
||||
""
|
||||
: confirmed
|
||||
? sku.manufacturerCode.trim().toUpperCase() || supplier.code
|
||||
: existingRow?.supplierCode?.trim().toUpperCase() ||
|
||||
supplier.code ||
|
||||
sku.manufacturerCode.trim().toUpperCase();
|
||||
@ -13495,7 +13518,9 @@ export default function NonIntegratedDetailClient({
|
||||
|
||||
setSkuItems((current) =>
|
||||
current.map((item, index) =>
|
||||
item.id === skuId
|
||||
item.id === skuId &&
|
||||
!isStockProductRecord &&
|
||||
!isImportedSkuContentLocked(item)
|
||||
? applySkuSupplierName(item, index, supplierName)
|
||||
: item,
|
||||
),
|
||||
@ -13515,12 +13540,17 @@ export default function NonIntegratedDetailClient({
|
||||
const supplier = normalizeFactorySupplier(value);
|
||||
const supplierName = supplier.name || value;
|
||||
const supplierCode = supplier.code;
|
||||
const importedContentLocked =
|
||||
derivedSkuItem !== undefined &&
|
||||
isImportedSkuContentLocked(derivedSkuItem);
|
||||
|
||||
return {
|
||||
...row,
|
||||
supplierName,
|
||||
supplierCode,
|
||||
barcode: buildAutoBarcode(row.skuCode, supplierCode, versionNo),
|
||||
barcode: importedContentLocked
|
||||
? row.barcode.trim() || derivedSkuItem?.barcode.trim() || ""
|
||||
: buildAutoBarcode(row.skuCode, supplierCode, versionNo),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,11 @@ type JstSkuSourceField =
|
||||
|
||||
type JstSkuSourceRow = Partial<Record<JstSkuSourceField, unknown>>
|
||||
|
||||
type JstSkuSourceGroup = {
|
||||
sourceSpu: string
|
||||
rows: JstSkuSourceRow[]
|
||||
}
|
||||
|
||||
type JstSkuStockSyncOptions = {
|
||||
apply?: boolean
|
||||
batchSize?: number
|
||||
@ -203,7 +208,7 @@ function mergeDefinedSourceFields<T extends Record<string, unknown>>(
|
||||
return next
|
||||
}
|
||||
|
||||
function buildSourceSkuItem(row: JstSkuSourceRow): NonIntegratedSkuItem {
|
||||
function buildSourceSkuItem(row: JstSkuSourceRow, sequence: number): NonIntegratedSkuItem {
|
||||
const skuId = normalizeSkuId(row.sku_id)
|
||||
const name = normalizeSourceString(row.name)
|
||||
const image = pickSourceImage(row)
|
||||
@ -213,7 +218,7 @@ function buildSourceSkuItem(row: JstSkuSourceRow): NonIntegratedSkuItem {
|
||||
|
||||
return {
|
||||
id: buildStableId('jst-stock-sku', skuId),
|
||||
skuSequence: 1,
|
||||
skuSequence: sequence,
|
||||
batchNo: 1,
|
||||
packageGroupId: '',
|
||||
packageSuffix: '',
|
||||
@ -246,25 +251,90 @@ function buildSourceSkuItem(row: JstSkuSourceRow): NonIntegratedSkuItem {
|
||||
} as NonIntegratedSkuItem
|
||||
}
|
||||
|
||||
function readSourceSpuKey(row: JstSkuSourceRow) {
|
||||
return normalizeSourceString(row.i_id) || normalizeSkuId(row.sku_id)
|
||||
}
|
||||
|
||||
function firstSourceValue(rows: JstSkuSourceRow[], key: JstSkuSourceField) {
|
||||
for (const row of rows) {
|
||||
const value = normalizeSourceString(row[key])
|
||||
if (value) return value
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function preferredSourceValue(
|
||||
rows: JstSkuSourceRow[],
|
||||
key: JstSkuSourceField,
|
||||
preferredValue?: string,
|
||||
) {
|
||||
if (preferredValue) {
|
||||
const matched = rows.some((row) => normalizeSourceString(row[key]) === preferredValue)
|
||||
if (matched) return preferredValue
|
||||
}
|
||||
|
||||
return firstSourceValue(rows, key)
|
||||
}
|
||||
|
||||
function buildSourceSkuItems(rows: JstSkuSourceRow[]) {
|
||||
const seenSkuIds = new Set<string>()
|
||||
|
||||
return rows
|
||||
.map((row) => {
|
||||
const skuId = normalizeSkuId(row.sku_id)
|
||||
if (!skuId || seenSkuIds.has(skuId)) return null
|
||||
seenSkuIds.add(skuId)
|
||||
return row
|
||||
})
|
||||
.filter((row): row is JstSkuSourceRow => row !== null)
|
||||
.map((row, index) => buildSourceSkuItem(row, index + 1))
|
||||
}
|
||||
|
||||
function buildSourceProductPayload(
|
||||
row: JstSkuSourceRow,
|
||||
group: JstSkuSourceGroup,
|
||||
syncedAt: string,
|
||||
): (NonIntegratedProductRecord & Record<string, unknown>) | null {
|
||||
const skuId = normalizeSkuId(row.sku_id)
|
||||
const sourceSpu = normalizeSourceString(row.i_id)
|
||||
const spu = skuId.toUpperCase()
|
||||
const name = normalizeSourceString(row.name) || skuId
|
||||
const image = pickSourceImage(row)
|
||||
const imageName = pickImageName(skuId, image)
|
||||
const color = normalizeSourceString(row.properties_value)
|
||||
const material = normalizeSourceString(row.other_10)
|
||||
const sourceSpu = group.sourceSpu.trim()
|
||||
if (!sourceSpu) return null
|
||||
|
||||
const skuItems = buildSourceSkuItems(group.rows)
|
||||
if (skuItems.length === 0) return null
|
||||
|
||||
const representativeRow =
|
||||
group.rows.find((row) => normalizeSourceString(row.item_type) === '成品') ??
|
||||
group.rows[0] ??
|
||||
{}
|
||||
const firstSku = skuItems[0]
|
||||
const name = normalizeSourceString(representativeRow.name) || firstSku?.specName || sourceSpu
|
||||
const image =
|
||||
pickSourceImage(representativeRow) ||
|
||||
skuItems.find((item) => item.image.trim())?.image.trim() ||
|
||||
''
|
||||
const imageName = pickImageName(firstSku?.barcode || sourceSpu, image)
|
||||
const color = normalizeSourceString(representativeRow.properties_value) || firstSku?.color || ''
|
||||
const material = normalizeSourceString(representativeRow.other_10) || firstSku?.material || ''
|
||||
const size = extractSizeFromName(name)
|
||||
const skuItem = buildSourceSkuItem(row)
|
||||
const sourceCategory = firstSourceValue(group.rows, 'category')
|
||||
const sourceItemType = preferredSourceValue(group.rows, 'item_type', '成品')
|
||||
const hasOnSaleSku = group.rows.some(
|
||||
(row) =>
|
||||
normalizeSourceString(row.item_type) === '成品' &&
|
||||
normalizeSourceString(row.other_5) === '销售' &&
|
||||
normalizeSourceString(row.enabled) === '1',
|
||||
)
|
||||
const sourceSalesStatus = hasOnSaleSku
|
||||
? '销售'
|
||||
: firstSourceValue(group.rows, 'other_5')
|
||||
const sourceEnabled = group.rows.some((row) => normalizeSourceString(row.enabled) === '1')
|
||||
? '1'
|
||||
: firstSourceValue(group.rows, 'enabled')
|
||||
const firstSourceRow = group.rows[0] ?? {}
|
||||
|
||||
const rawPayload = {
|
||||
id: buildStableId('jst-stock', skuId),
|
||||
id: buildStableId('jst-stock', sourceSpu),
|
||||
productDevelopmentKind: 'product',
|
||||
spu,
|
||||
spu: sourceSpu.toUpperCase(),
|
||||
projectStatus: 'approved',
|
||||
projectReview: {
|
||||
reviewDate: '',
|
||||
@ -281,7 +351,7 @@ function buildSourceProductPayload(
|
||||
productImage: image,
|
||||
productImageName: imageName,
|
||||
productName: name,
|
||||
temporaryCode: skuId,
|
||||
temporaryCode: sourceSpu,
|
||||
brand: BASIC_BRAND,
|
||||
adaptableBrands: [BASIC_BRAND],
|
||||
productCategory: '',
|
||||
@ -338,7 +408,6 @@ function buildSourceProductPayload(
|
||||
sampleRequired: '',
|
||||
flatOutputPath: '',
|
||||
output3dPath: '',
|
||||
productCode: skuId,
|
||||
size,
|
||||
color,
|
||||
material,
|
||||
@ -366,8 +435,9 @@ function buildSourceProductPayload(
|
||||
qualityInspectionSheet: {
|
||||
rows: [],
|
||||
},
|
||||
skuItems: [skuItem],
|
||||
skuBatchCount: 1,
|
||||
productCode: sourceSpu,
|
||||
skuItems,
|
||||
skuBatchCount: Math.max(1, ...skuItems.map((item) => item.batchNo || 1)),
|
||||
skuBatchFlowNos: {},
|
||||
skuBatchChangeTypes: {},
|
||||
factoryRows: [],
|
||||
@ -390,28 +460,30 @@ function buildSourceProductPayload(
|
||||
source: 'jst_sku',
|
||||
sourceTable: JST_SKU_TABLE,
|
||||
sourceSpu,
|
||||
sourceSkuId: skuId,
|
||||
sourceCategory: normalizeSourceString(row.category),
|
||||
sourceItemType: normalizeSourceString(row.item_type),
|
||||
sourceSalesStatus: normalizeSourceString(row.other_5),
|
||||
sourceEnabled: normalizeSourceString(row.enabled),
|
||||
sourceStockDisabled: normalizeSourceString(row.stock_disabled),
|
||||
sourceSkuId: firstSku?.barcode ?? '',
|
||||
sourceCategory,
|
||||
sourceItemType,
|
||||
sourceSalesStatus,
|
||||
sourceEnabled,
|
||||
sourceStockDisabled: firstSourceValue(group.rows, 'stock_disabled'),
|
||||
skuCount: skuItems.length,
|
||||
hasOnSaleSku,
|
||||
sourceFields: {
|
||||
sku_id: skuId,
|
||||
sku_id: firstSku?.barcode ?? '',
|
||||
i_id: sourceSpu,
|
||||
name,
|
||||
category: normalizeSourceString(row.category),
|
||||
item_type: normalizeSourceString(row.item_type),
|
||||
enabled: normalizeSourceString(row.enabled),
|
||||
stock_disabled: normalizeSourceString(row.stock_disabled),
|
||||
category: sourceCategory,
|
||||
item_type: sourceItemType,
|
||||
enabled: sourceEnabled,
|
||||
stock_disabled: firstSourceValue(group.rows, 'stock_disabled'),
|
||||
properties_value: color,
|
||||
other_5: normalizeSourceString(row.other_5),
|
||||
other_7: normalizeSourceString(row.other_7),
|
||||
other_8: normalizeSourceString(row.other_8),
|
||||
other_9: normalizeSourceString(row.other_9),
|
||||
other_5: sourceSalesStatus,
|
||||
other_7: normalizeSourceString(firstSourceRow.other_7),
|
||||
other_8: normalizeSourceString(firstSourceRow.other_8),
|
||||
other_9: normalizeSourceString(firstSourceRow.other_9),
|
||||
other_10: material,
|
||||
pic: normalizeSourceString(row.pic),
|
||||
pic_big: normalizeSourceString(row.pic_big),
|
||||
pic: normalizeSourceString(firstSourceRow.pic),
|
||||
pic_big: normalizeSourceString(firstSourceRow.pic_big),
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<NonIntegratedProductRecord> & Record<string, unknown>
|
||||
@ -432,27 +504,48 @@ function mergeExistingProductPayload(
|
||||
(isRecord(existingPayload) ? existingPayload : {}) as Partial<NonIntegratedProductRecord>,
|
||||
) ?? sourcePayload
|
||||
const existingRecord = isRecord(existingPayload) ? existingPayload : {}
|
||||
const sourceSku = sourcePayload.skuItems[0]
|
||||
const existingSku =
|
||||
existingProduct.skuItems.find((item) => item.id === sourceSku.id) ??
|
||||
existingProduct.skuItems[0]
|
||||
const nextSku = existingSku
|
||||
? mergeDefinedSourceFields(existingSku, sourceSku, [
|
||||
'image',
|
||||
'imageName',
|
||||
'model',
|
||||
'skuCode',
|
||||
'setCount',
|
||||
'specName',
|
||||
'barcode',
|
||||
'size',
|
||||
'color',
|
||||
'material',
|
||||
'drawingId',
|
||||
'salesStatus',
|
||||
])
|
||||
: sourceSku
|
||||
const remainingSkus = existingProduct.skuItems.filter((item) => item.id !== nextSku.id)
|
||||
const matchedExistingSkuIds = new Set<string>()
|
||||
const nextSourceSkus = sourcePayload.skuItems.map((sourceSku) => {
|
||||
const existingSku = existingProduct.skuItems.find((item) => {
|
||||
const matched =
|
||||
item.id === sourceSku.id ||
|
||||
item.sourceSkuId === sourceSku.sourceSkuId ||
|
||||
(sourceSku.barcode.trim() && item.barcode.trim() === sourceSku.barcode.trim())
|
||||
if (matched) matchedExistingSkuIds.add(item.id)
|
||||
return matched
|
||||
})
|
||||
|
||||
if (!existingSku) return sourceSku
|
||||
|
||||
const mergedSku = mergeDefinedSourceFields(existingSku, sourceSku, [
|
||||
'image',
|
||||
'imageName',
|
||||
'model',
|
||||
'skuCode',
|
||||
'setCount',
|
||||
'specName',
|
||||
'barcode',
|
||||
'size',
|
||||
'color',
|
||||
'material',
|
||||
'drawingId',
|
||||
'salesStatus',
|
||||
'source',
|
||||
'sourceTable',
|
||||
'sourceSkuId',
|
||||
])
|
||||
|
||||
return {
|
||||
...mergedSku,
|
||||
skuSequence: sourceSku.skuSequence,
|
||||
source: 'jst_sku',
|
||||
sourceTable: JST_SKU_TABLE,
|
||||
sourceSkuId: sourceSku.sourceSkuId,
|
||||
}
|
||||
})
|
||||
const remainingSkus = existingProduct.skuItems.filter(
|
||||
(item) => !matchedExistingSkuIds.has(item.id),
|
||||
)
|
||||
const sourceOpeningReference = sourcePayload.openingReference
|
||||
const nextOpeningReference = {
|
||||
...existingProduct.openingReference,
|
||||
@ -497,8 +590,11 @@ function mergeExistingProductPayload(
|
||||
previewImageName: sourcePayload.previewImage
|
||||
? sourcePayload.previewImageName
|
||||
: existingProduct.previewImageName,
|
||||
skuItems: [nextSku, ...remainingSkus],
|
||||
skuBatchCount: existingProduct.skuBatchCount ?? 1,
|
||||
skuItems: [...nextSourceSkus, ...remainingSkus],
|
||||
skuBatchCount: Math.max(
|
||||
existingProduct.skuBatchCount ?? 1,
|
||||
sourcePayload.skuBatchCount ?? 1,
|
||||
),
|
||||
documentWorkspaceConfirmations: existingProduct.documentWorkspaceConfirmations,
|
||||
documentWorkspaceApprovedAt: existingProduct.documentWorkspaceApprovedAt,
|
||||
createdAt: existingProduct.createdAt,
|
||||
@ -556,22 +652,65 @@ function buildSourceSelectColumns(columns: Set<string>) {
|
||||
]
|
||||
}
|
||||
|
||||
async function fetchSourceBatch(params: {
|
||||
async function fetchSourceSpuKeys(params: {
|
||||
connection: mysql.Connection
|
||||
selectColumns: readonly string[]
|
||||
offset: number
|
||||
take: number
|
||||
}) {
|
||||
const selectSql = params.selectColumns.map((column) => `\`${column}\``).join(', ')
|
||||
const [rows] = await params.connection.execute<RowDataPacket[]>(
|
||||
`SELECT ${selectSql} FROM \`${JST_SKU_TABLE}\` WHERE \`sku_id\` IS NOT NULL AND TRIM(CAST(\`sku_id\` AS CHAR)) <> '' ORDER BY \`sku_id\` LIMIT ? OFFSET ?`,
|
||||
`SELECT DISTINCT COALESCE(NULLIF(TRIM(CAST(\`i_id\` AS CHAR)), ''), TRIM(CAST(\`sku_id\` AS CHAR))) AS source_spu
|
||||
FROM \`${JST_SKU_TABLE}\`
|
||||
WHERE \`sku_id\` IS NOT NULL
|
||||
AND TRIM(CAST(\`sku_id\` AS CHAR)) <> ''
|
||||
AND COALESCE(NULLIF(TRIM(CAST(\`i_id\` AS CHAR)), ''), TRIM(CAST(\`sku_id\` AS CHAR))) <> ''
|
||||
ORDER BY source_spu
|
||||
LIMIT ? OFFSET ?`,
|
||||
[params.take, params.offset],
|
||||
)
|
||||
|
||||
return rows as JstSkuSourceRow[]
|
||||
return rows
|
||||
.map((row) => normalizeSourceString(row.source_spu))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
async function findExistingStockProduct(id: string, spu: string, sourceSkuId: string) {
|
||||
async function fetchSourceGroups(params: {
|
||||
connection: mysql.Connection
|
||||
selectColumns: readonly string[]
|
||||
sourceSpuKeys: string[]
|
||||
}) {
|
||||
if (params.sourceSpuKeys.length === 0) return []
|
||||
|
||||
const selectSql = params.selectColumns.map((column) => `\`${column}\``).join(', ')
|
||||
const placeholders = params.sourceSpuKeys.map(() => '?').join(', ')
|
||||
const [rows] = await params.connection.execute<RowDataPacket[]>(
|
||||
`SELECT ${selectSql},
|
||||
COALESCE(NULLIF(TRIM(CAST(\`i_id\` AS CHAR)), ''), TRIM(CAST(\`sku_id\` AS CHAR))) AS __source_spu
|
||||
FROM \`${JST_SKU_TABLE}\`
|
||||
WHERE \`sku_id\` IS NOT NULL
|
||||
AND TRIM(CAST(\`sku_id\` AS CHAR)) <> ''
|
||||
AND COALESCE(NULLIF(TRIM(CAST(\`i_id\` AS CHAR)), ''), TRIM(CAST(\`sku_id\` AS CHAR))) IN (${placeholders})
|
||||
ORDER BY __source_spu, \`sku_id\``,
|
||||
params.sourceSpuKeys,
|
||||
)
|
||||
|
||||
const rowsBySourceSpu = new Map<string, JstSkuSourceRow[]>()
|
||||
;(rows as Array<JstSkuSourceRow & { __source_spu?: unknown }>).forEach((row) => {
|
||||
const sourceSpu = normalizeSourceString(row.__source_spu) || readSourceSpuKey(row)
|
||||
if (!sourceSpu) return
|
||||
const nextRow = { ...row }
|
||||
delete nextRow.__source_spu
|
||||
rowsBySourceSpu.set(sourceSpu, [...(rowsBySourceSpu.get(sourceSpu) ?? []), nextRow])
|
||||
})
|
||||
|
||||
return params.sourceSpuKeys
|
||||
.map((sourceSpu) => ({
|
||||
sourceSpu,
|
||||
rows: rowsBySourceSpu.get(sourceSpu) ?? [],
|
||||
}))
|
||||
.filter((group) => group.rows.length > 0)
|
||||
}
|
||||
|
||||
async function findExistingStockProduct(id: string, spu: string, sourceSpu: string) {
|
||||
const existingById = await prisma.intakeNonIntegratedProduct.findUnique({
|
||||
where: { id },
|
||||
})
|
||||
@ -583,14 +722,8 @@ async function findExistingStockProduct(id: string, spu: string, sourceSkuId: st
|
||||
{ spu },
|
||||
{
|
||||
payload: {
|
||||
path: ['metadata', 'sourceSkuId'],
|
||||
equals: sourceSkuId,
|
||||
},
|
||||
},
|
||||
{
|
||||
payload: {
|
||||
path: ['skuItems', '0', 'barcode'],
|
||||
equals: sourceSkuId,
|
||||
path: ['metadata', 'sourceSpu'],
|
||||
equals: sourceSpu,
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -601,14 +734,17 @@ async function findExistingStockProduct(id: string, spu: string, sourceSkuId: st
|
||||
})
|
||||
}
|
||||
|
||||
async function syncOneSourceRow(row: JstSkuSourceRow, apply: boolean, syncedAt: string) {
|
||||
const sourcePayload = buildSourceProductPayload(row, syncedAt)
|
||||
async function syncOneSourceGroup(group: JstSkuSourceGroup, apply: boolean, syncedAt: string) {
|
||||
const sourcePayload = buildSourceProductPayload(group, syncedAt)
|
||||
if (!sourcePayload) {
|
||||
return 'skipped' as const
|
||||
}
|
||||
|
||||
const sourceSkuId = normalizeSkuId(row.sku_id)
|
||||
const existing = await findExistingStockProduct(sourcePayload.id, sourcePayload.spu, sourceSkuId)
|
||||
const existing = await findExistingStockProduct(
|
||||
sourcePayload.id,
|
||||
sourcePayload.spu,
|
||||
group.sourceSpu,
|
||||
)
|
||||
|
||||
if (existing && existing.id !== sourcePayload.id && !isJstSkuPayload(existing.payload)) {
|
||||
return 'conflicts' as const
|
||||
@ -702,31 +838,36 @@ export async function syncJstSkuStockProducts(
|
||||
const remaining = limit === null ? batchSize : Math.min(batchSize, limit - counters.scanned)
|
||||
if (remaining <= 0) break
|
||||
|
||||
const rows = await fetchSourceBatch({
|
||||
const sourceSpuKeys = await fetchSourceSpuKeys({
|
||||
connection,
|
||||
selectColumns,
|
||||
offset,
|
||||
take: remaining,
|
||||
})
|
||||
if (rows.length === 0) break
|
||||
if (sourceSpuKeys.length === 0) break
|
||||
|
||||
for (const row of rows) {
|
||||
const groups = await fetchSourceGroups({
|
||||
connection,
|
||||
selectColumns,
|
||||
sourceSpuKeys,
|
||||
})
|
||||
|
||||
for (const group of groups) {
|
||||
counters.scanned += 1
|
||||
try {
|
||||
const result = await syncOneSourceRow(row, apply, new Date().toISOString())
|
||||
const result = await syncOneSourceGroup(group, apply, new Date().toISOString())
|
||||
counters[result] += 1
|
||||
} catch (error) {
|
||||
counters.skipped += 1
|
||||
errors.push(
|
||||
`${normalizeSkuId(row.sku_id) || '<empty sku_id>'}: ${
|
||||
`${group.sourceSpu || '<empty source_spu>'}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
offset += rows.length
|
||||
if (rows.length < remaining) break
|
||||
offset += sourceSpuKeys.length
|
||||
if (sourceSpuKeys.length < remaining) break
|
||||
}
|
||||
} finally {
|
||||
await connection.end()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user