#!/usr/bin/env node /** * 导入聚水潭「进销存按仓导出」到每日进销存校正表 * * 用法: * node scripts/importDailyInventoryOfficial.js <业务日期> * node scripts/importDailyInventoryOfficial.js 2026-09-26 ./进销存_按仓_20260926.xlsx * * 作用: * 1) 解析导出文件,校验「期初 + Σ流水 = 期末」逐仓闭合 * 2) 写入本地表 daily_inventory_official(同一业务日整体覆盖) * 3) 回写本地快照:官方期初 → 上一业务日期末、官方期末 → 该业务日期末 * * 页面右上角「导入聚水潭进销存」走的是同一个后端接口(POST /api/daily-inventory/official-import)。 */ 'use strict'; const fs = require('fs'); const path = require('path'); const { parseWorkbook, importOfficial, checkBalance } = require('../src/services/dailyInventoryOfficial'); const snapshotDb = require('../src/config/dailyInventorySnapshotDb'); const { DAILY_INVENTORY_BASELINE } = require('../src/config/dailyInventoryBaseline'); function prevDay(bizDate) { const d = new Date(`${bizDate}T00:00:00Z`); d.setUTCDate(d.getUTCDate() - 1); return d.toISOString().slice(0, 10); } async function main() { const [bizDate, file] = process.argv.slice(2); if (!bizDate || !/^\d{4}-\d{2}-\d{2}$/.test(bizDate)) { console.error('用法: node scripts/importDailyInventoryOfficial.js <业务日期 YYYY-MM-DD> '); process.exit(1); } if (!file || !fs.existsSync(file)) { console.error(`文件不存在: ${file}`); process.exit(1); } const { rows, warnings, sheetName } = parseWorkbook(fs.readFileSync(file)); console.log(`解析 ${path.basename(file)} [${sheetName}]:${rows.length} 行,提示:${warnings.join(';') || '无'}`); if (!rows.length) { console.error('未解析到仓库行,已中止'); process.exit(1); } const mismatches = checkBalance(rows); if (mismatches.length) { console.warn(`⚠ 以下 ${mismatches.length} 个仓「期初 + 流水 ≠ 期末」:`); mismatches.forEach((m) => console.warn(` ${m.warehouse}: 差 ${m.diff}`)); } else { console.log('恒等式校验:期初 + 流水 = 期末,逐仓闭合 ✓'); } const result = await importOfficial(bizDate, rows); console.log(`写入 official:${result.imported} 行(${bizDate})`); if (result.unknownWarehouses.length) { console.warn(`⚠ 未匹配到仓库 ID(仅按仓库名存档):${result.unknownWarehouses.join('、')}`); } if (result.missingWarehouses.length) { console.warn(`⚠ 清单中未出现在导出里的仓库:${result.missingWarehouses.join('、')}`); } const byId = new Map(DAILY_INVENTORY_BASELINE.map((b) => [String(b.wmsCoId), b])); const known = rows.filter((r) => r.wmsCoId != null); const prevDate = prevDay(bizDate); const prev = await snapshotDb.fetchSnapshot(prevDate); const prevCount = await snapshotDb.upsertSnapshot(prevDate, known .filter((r) => r.openingQty != null) .map((r) => ({ wmsCoId: r.wmsCoId, warehouse: r.warehouse, dbWarehouse: byId.get(String(r.wmsCoId))?.dbWarehouse || null, closingQty: Number(r.openingQty), skuCount: Number(prev.get(String(r.wmsCoId))?.skuCount || 0), takenAt: null, }))); const current = await snapshotDb.fetchSnapshot(bizDate); const currentCount = await snapshotDb.upsertSnapshot(bizDate, known .filter((r) => r.closingQty != null) .map((r) => ({ wmsCoId: r.wmsCoId, warehouse: r.warehouse, dbWarehouse: byId.get(String(r.wmsCoId))?.dbWarehouse || null, closingQty: Number(r.closingQty), skuCount: Number(current.get(String(r.wmsCoId))?.skuCount || 0), takenAt: null, }))); console.log(`快照回写:${prevDate}(官方期初 → 期末)${prevCount} 行,${bizDate}(官方期末)${currentCount} 行`); process.exit(0); } main().catch((e) => { console.error('导入失败:', e.message); process.exit(1); });