diff --git a/furniture-3d-system/scripts/local-sync-archive.mjs b/furniture-3d-system/scripts/local-sync-archive.mjs index 71b8b411..2e652ea6 100755 --- a/furniture-3d-system/scripts/local-sync-archive.mjs +++ b/furniture-3d-system/scripts/local-sync-archive.mjs @@ -3,8 +3,9 @@ // 1) 聚水潭产品档案全量同步 // 2) 聚水潭销售日数据增量同步(最近 7 天) // 3) 以正式库产品全量档案对齐(生产状态 + 创建时间) -// 4) 以正式库类目档位表同步本地类目配置 -// 5) 重算指定月份(默认上个月)SABC 并写库 +// 4) 回填产品全量档案「产品类型」(定厂优先,聚水潭 vc_name 兜底) +// 5) 以正式库类目档位表同步本地类目配置 +// 6) 重算指定月份(默认上个月)SABC 并写库 // 用法: // node scripts/local-sync-archive.mjs [--month=2026-08] [--dry-run] [--skip-sabc] import { spawn } from 'node:child_process' @@ -135,9 +136,9 @@ async function main() { } if (apply) { - console.log(`==> 1/5 同步聚水潭产品档案(${env.JST_REPORT_DB_HOST}/${env.JST_REPORT_DB_NAME} → 本地)`) + console.log(`==> 1/6 同步聚水潭产品档案(${env.JST_REPORT_DB_HOST}/${env.JST_REPORT_DB_NAME} → 本地)`) } else { - console.log('==> 1/5 预览聚水潭产品档案差异(dry-run,不写库)') + console.log('==> 1/6 预览聚水潭产品档案差异(dry-run,不写库)') } const syncArgs = ['scripts/sync-jst-sku-product-archive.ts', '--all', '--batch-size=500', '--concurrency=8'] if (apply) { @@ -153,7 +154,7 @@ async function main() { printSyncSummary(await run(syncArgs, { capture: true })) if (apply) { - console.log('==> 2/5 同步聚水潭销售日数据(最近 7 天)') + console.log('==> 2/6 同步聚水潭销售日数据(最近 7 天)') await run([ 'scripts/sync-supply-chain-product-archive-sales-performance-daily.ts', '--apply', @@ -163,32 +164,40 @@ async function main() { '--lookback-days=7', ]) } else { - console.log('==> 2/5 已跳过销售日数据同步(dry-run)') + console.log('==> 2/6 已跳过销售日数据同步(dry-run)') } if (apply) { - console.log('==> 3/5 以正式库产品全量档案对齐(状态 + 创建时间)') + console.log('==> 3/6 以正式库产品全量档案对齐(状态 + 创建时间)') await run(['scripts/reconcile-archive-from-prod.ts', '--apply', '--confirm=reconcile-archive-from-prod']) } else { - console.log('==> 3/5 预览正式库对齐差异(dry-run,不写库)') + console.log('==> 3/6 预览正式库对齐差异(dry-run,不写库)') await run(['scripts/reconcile-archive-from-prod.ts']) } if (apply) { - console.log('==> 4/5 同步正式库类目档位配置') + console.log('==> 4/6 回填产品类型(定厂优先,聚水潭 vc_name 兜底)') + await run(['scripts/backfill-product-archive-production-type.ts', '--apply', '--batch-size=200']) + } else { + console.log('==> 4/6 预览产品类型差异(dry-run,不写库)') + await run(['scripts/backfill-product-archive-production-type.ts', '--batch-size=200']) + } + + if (apply) { + console.log('==> 5/6 同步正式库类目档位配置') await run(['scripts/sync-product-category-config.ts', '--apply', '--confirm=sync-product-category-config']) } else { - console.log('==> 4/5 预览类目档位差异(dry-run,不写库)') + console.log('==> 5/6 预览类目档位差异(dry-run,不写库)') await run(['scripts/sync-product-category-config.ts']) } if (apply && !skipSabc) { const year = month.split('-')[0] const monthNumber = String(Number(month.split('-')[1])) - console.log(`==> 5/5 重算 ${year}-${monthNumber} SABC 并写库(APPLY)`) + console.log(`==> 6/6 重算 ${year}-${monthNumber} SABC 并写库(APPLY)`) await run(['scripts/run-product-sabc.ts', `--year=${year}`, `--month=${monthNumber}`, '--mode=APPLY', '--confirm=run-product-sabc']) } else { - console.log('==> 5/5 已跳过 SABC 重算') + console.log('==> 6/6 已跳过 SABC 重算') } console.log('==> 完成。SABC 页面:http://localhost:3000/supply-chain/management/blank-pages/blank-page-3') diff --git a/furniture-3d-system/src/server/services/supply-chain-product-sabc.service.ts b/furniture-3d-system/src/server/services/supply-chain-product-sabc.service.ts index a0a67c1b..de4ca7cd 100644 --- a/furniture-3d-system/src/server/services/supply-chain-product-sabc.service.ts +++ b/furniture-3d-system/src/server/services/supply-chain-product-sabc.service.ts @@ -35,6 +35,7 @@ const archiveSelect = { productName: true, productStatus: true, productAttribute: true, + productionType: true, brand: true, productTag: true, sabcRating: true, @@ -312,7 +313,8 @@ function createBlankRow(target: ArchiveTarget, periodEnd: Date, configuration: S grossMargin: '', afterSaleRatio: '', assessmentStatus: '', - noAssessmentReason: '', + // 「不考核原因」取产品全量档案的「产品类型」(OEM/ODM 等)。 + noAssessmentReason: target.productionType ?? '', sameProductCode: '无', eligible: isSabcEligibleProduct(target), } @@ -323,12 +325,7 @@ function applyValues(target: ArchiveTarget, periodEnd: Date, values: MonthlyValu if (row.eligible) applyRawMetrics(row, values, periodEnd.getUTCDate()) // 零业绩(真实业绩为 0 且发货件数为 0)按人工表口径固定为 异常/滞销款/五级/D,不依赖类目阈值配置。 const zeroPerformance = row.eligible && values.realPerformance.isZero() && values.shipQty === BigInt(0) - if (!row.eligible || (!configuration?.rules.length && !zeroPerformance)) { - row.noAssessmentReason = row.eligible - ? '类目未配置 SABC 阈值' - : '' - return row - } + if (!row.eligible || (!configuration?.rules.length && !zeroPerformance)) return row const daysInPeriod = periodEnd.getUTCDate() const classification = zeroPerformance ? { diff --git a/furniture-3d-system/src/server/services/supply-chain-product-sabc.test.ts b/furniture-3d-system/src/server/services/supply-chain-product-sabc.test.ts index 5862bd0f..4a2b3323 100644 --- a/furniture-3d-system/src/server/services/supply-chain-product-sabc.test.ts +++ b/furniture-3d-system/src/server/services/supply-chain-product-sabc.test.ts @@ -225,6 +225,7 @@ describe('supply-chain-product-sabc rules', () => { productName: '休闲椅', productStatus: '销售', productAttribute: '成品', + productionType: 'ODM', brand: '品牌', productTag: '', sabcRating: '', @@ -271,7 +272,7 @@ describe('supply-chain-product-sabc rules', () => { grossMargin: '0.79674797', afterSaleRatio: '0.02439024', sabcRating: '', - noAssessmentReason: '类目未配置 SABC 阈值', + noAssessmentReason: 'ODM', })) }) @@ -288,6 +289,7 @@ describe('supply-chain-product-sabc rules', () => { productName: '飘窗柜', productStatus: '销售', productAttribute: '成品', + productionType: 'OEM', brand: '其他品牌-山东专用', productTag: '', sabcRating: '', @@ -314,7 +316,7 @@ describe('supply-chain-product-sabc rules', () => { sabcRating: 'D', movingStatus: '不动销', assessmentStatus: '考核', - noAssessmentReason: '', + noAssessmentReason: 'OEM', })) })