feat(supply-chain): SABC 不考核原因改用档案产品类型,并纳入每日回填
This commit is contained in:
parent
9ae9423742
commit
4c518b054e
@ -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')
|
||||
|
||||
@ -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
|
||||
? {
|
||||
|
||||
@ -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',
|
||||
}))
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user