diff --git a/backend/scripts/gen-daily-inventory-baseline.cjs b/backend/scripts/gen-daily-inventory-baseline.cjs new file mode 100644 index 0000000..8ed220f --- /dev/null +++ b/backend/scripts/gen-daily-inventory-baseline.cjs @@ -0,0 +1,106 @@ +process.env.CANGKU_SKIP_BOOTSTRAP = '1'; +const fs = require('fs'); +const path = require('path'); +const XLSX = require('xlsx'); +const cloudDb = require('../src/config/cloudDatabase'); + +// 表格的 12 个流水列 → 配置字段名 +const FLOW_FIELDS = [ + ['加工进仓', 'processIn'], + ['采购退货', 'purchaseReturn'], + ['采购进仓', 'purchaseIn'], + ['其它进仓', 'otherIn'], + ['其它退货', 'otherReturn'], + ['销售出仓', 'salesOut'], + ['盘点', 'stocktaking'], + ['加工出仓', 'processOut'], + ['调拨出', 'transferOut'], + ['销售退货', 'salesReturn'], + ['调拨入', 'transferIn'], + ['其它出库', 'otherOut'], +]; + +(async () => { + const wb = XLSX.readFile('C:/Users/15018737611/Desktop/库存监控.xlsx'); + const rows = XLSX.utils.sheet_to_json(wb.Sheets['Sheet1'], { header: 1, defval: '' }); + const h = rows[0]; + const idx = Object.fromEntries(h.map((c, i) => [String(c).trim(), i])); + + const level1 = String(rows[1][idx['一级功能按键']]).trim(); + const level2 = String(rows[1][idx['二级功能按键']]).trim(); + console.log('一级功能:', level1, '| 二级功能:', level2); + + const whs = await cloudDb.query(`SELECT name, wms_co_id FROM rpt.jst_warehouseInfo`); + const byName = new Map(whs.map((w) => [String(w.name).trim(), w.wms_co_id])); + + const records = []; + const missing = []; + for (const r of rows.slice(1)) { + const sheetName = String(r[idx['仓库']] || '').trim(); + if (!sheetName) continue; + const base = sheetName.replace(/主仓$/, ''); + const wmsCoId = byName.get(base) ?? byName.get(sheetName) ?? null; + if (wmsCoId == null) missing.push(sheetName); + + const num = (k) => { + const v = r[idx[k]]; + return typeof v === 'number' ? v : Number(String(v || '').replace(/,/g, '')) || 0; + }; + + const flows = Object.fromEntries(FLOW_FIELDS.map(([cn, en]) => [en, num(cn)])); + const openExtra = r[idx['期初']]; // 第二个「期初」列,索引会与「期初数量」冲突,单独处理 + records.push({ + warehouse: sheetName, + dbWarehouse: base, + wmsCoId, + openingQty: num('期初数量'), + openingQtyExtra: typeof openExtra === 'number' ? openExtra : Number(openExtra) || 0, + ...flows, + }); + } + + console.log('记录数:', records.length, '| 缺 wms_co_id:', missing.length, missing.join(',') || '(无)'); + + const FLOW_COLUMNS_DEF = FLOW_FIELDS.map(([cn, en]) => ({ label: cn, field: en })); + const out = `/** + * 每日进销存 —— 2026-09-22 进销存基准流水 + * + * 来源:桌面《库存监控.xlsx》(聚水潭 22 号进销存导出) + * 用途:业务库 JSReportReplica 没有按天的历史库存结存表,也缺少调拨/盘点/加工/其它出入库 + * 单据表,期初与各流水列无法从业务库还原,故以该导出作为 22 号基准值。 + * + * 口径约定: + * - openingQty 与 12 个流水列:22 号基准值(本文件静态存储) + * - 期末数量:运行时取 jst_inventoryInfo 实时库存(与本文件基准期末一致,24/25 精确) + * - 库存周转天数 / 四级库存量:运行时由 pm_stockMonitor 计算 + */ + +/** 一级/二级菜单名(取自表格) */ +const DAILY_INVENTORY_LEVEL1 = '${level1}'; +const DAILY_INVENTORY_LEVEL2 = '${level2}'; + +/** 基准数据日期 */ +const BASELINE_DATE = '2026-09-22'; + +/** 12 个流水列的中文表头与字段映射(顺序与表格一致) */ +const FLOW_COLUMNS = ${JSON.stringify(FLOW_COLUMNS_DEF, null, 2)}; + +/** 25 个仓库的 22 号基准流水 */ +const DAILY_INVENTORY_BASELINE = ${JSON.stringify(records, null, 2)}; + +module.exports = { + DAILY_INVENTORY_LEVEL1, + DAILY_INVENTORY_LEVEL2, + BASELINE_DATE, + FLOW_COLUMNS, + DAILY_INVENTORY_BASELINE, +}; +`; + + const target = path.join(__dirname, '../src/config/dailyInventoryBaseline.js'); + fs.writeFileSync(target, out, 'utf8'); + console.log('已生成:', target); + console.log('\n前 2 条预览:'); + console.log(JSON.stringify(records.slice(0, 2), null, 1)); + process.exit(0); +})().catch((e) => { console.log('ERR', e.message); process.exit(1); });