fix: 缺货补货监控改为「崔桥主仓·仅当日待补」并修正指标
- 看板只展示当日待补 SKU(待发订单>拣货位库存,需存储→拣货补货),去除充足/无待发干扰行 - 库存收紧到崔桥工厂仓主仓(jst_packInventoryInfo 加 wh_id=1),数据源对齐「箱及仓位库存·主仓」 - 补齐「有打单拣货待发单但货位库存视图无记录」的 SKU(拣货位为0也应进待补明细) - 指标卡改为四项:实时代发订单数 / 拣货库存数 / 存储库存数 / 缺货数(缺货=待补SKU数,脚注含存储不足子项) - 各库底待补明细=need+short 行;范围筛「待补货/仅缺货」;移除多仓选择器,固定崔桥 - 待发订单仍取 op_OrderNotDispatched 打单拣货/发货中 每日快照(两步 querySlow) - 副本实测:当日快照待补 45 SKU(need42/short3),待发132件/拣货40件/存储2381件/缺口5件
This commit is contained in:
parent
5b0c44c9e2
commit
e43f17fde4
@ -49,7 +49,7 @@ router.get('/export', async (req, res, next) => {
|
||||
keyword: req.query.keyword,
|
||||
status: req.query.status,
|
||||
});
|
||||
const fileName = `${payload.warehouse.shortName}缺货补货监控_待发订单`;
|
||||
const fileName = `${payload.warehouse.shortName}待补明细`;
|
||||
|
||||
sendWorkbookDownload(res, {
|
||||
fileName,
|
||||
|
||||
@ -155,10 +155,40 @@ async function loadReplicaInventory(warehouse) {
|
||||
FROM rpt.jst_packInventoryInfo p
|
||||
LEFT JOIN rpt.jst_sku s ON s.sku_id = p.sku_id
|
||||
WHERE p.wms_co_id = @wmsCoId
|
||||
AND p.wh_id = 1
|
||||
AND ISNULL(p.bin, '') <> ''
|
||||
AND UPPER(LEFT(LTRIM(p.bin), 2)) IN (${PICK_PREFIX_IN}, ${STORE_PREFIX_IN})
|
||||
GROUP BY p.sku_id`;
|
||||
return cloudDb.query(sql, { wmsCoId: warehouse.wmsCoId }, { requestTimeout: 600000 });
|
||||
// 单仓按货位前缀聚合约 1.7s;走 querySlow(独立连接 + 池级超时)避免 60s 池超时误杀。
|
||||
return cloudDb.querySlow(sql, { wmsCoId: warehouse.wmsCoId }, { requestTimeout: 300000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取 SKU 主数据(品名 / 供应商),用于补齐「有订单但无货位库存」的 SKU 名称。
|
||||
* 这批 SKU 不在库存视图里,但确实有打单拣货待发单,必须进待补明细。
|
||||
*/
|
||||
async function loadSkuNames(skuIds) {
|
||||
const uniq = [...new Set((skuIds || []).filter(Boolean).map((id) => toText(id, 120)))];
|
||||
if (!uniq.length) return new Map();
|
||||
const map = new Map();
|
||||
const CHUNK = 400;
|
||||
for (let i = 0; i < uniq.length; i += CHUNK) {
|
||||
const chunk = uniq.slice(i, i + CHUNK);
|
||||
const params = {};
|
||||
const ph = chunk.map((_, idx) => `@s${idx}`).join(',');
|
||||
chunk.forEach((id, idx) => { params[`s${idx}`] = id; });
|
||||
const rows = await cloudDb.querySlow(
|
||||
`SELECT sku_id AS skuId, name AS productName, supplier_name AS supplier
|
||||
FROM rpt.jst_sku WHERE sku_id IN (${ph})`,
|
||||
params,
|
||||
{ requestTimeout: 120000 }
|
||||
);
|
||||
(rows || []).forEach((r) => {
|
||||
const id = toText(r.skuId, 120);
|
||||
if (id) map.set(id, { productName: toText(r.productName, 300), supplier: toText(r.supplier, 120) });
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,40 +288,43 @@ function buildSkuRows({ invRows, orderMap }) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
function buildSummary(rows, orderSkuCount) {
|
||||
/**
|
||||
* 汇总只统计「待补」SKU(need / short)——
|
||||
* 即需要从存储仓位补货到拣货仓位的部分。对应看板四张指标卡:
|
||||
* 实时代发订单数 = 待补 SKU 待发件数合计
|
||||
* 拣货库存数 = 待补 SKU 拣货位库存合计
|
||||
* 存储库存数 = 待补 SKU 存储位库存合计
|
||||
* 缺货数 = 存储也补不齐的 SKU 数(status=short)
|
||||
*/
|
||||
function buildSummary(rows) {
|
||||
const summary = {
|
||||
skuTotal: rows.length,
|
||||
skuTotal: 0,
|
||||
need: 0,
|
||||
short: 0,
|
||||
enough: 0,
|
||||
noOrder: 0,
|
||||
pickEmpty: 0,
|
||||
suggestQtyTotal: 0,
|
||||
orderSkuCount: orderSkuCount || 0,
|
||||
orderQtyTotal: 0,
|
||||
exceedTotal: 0,
|
||||
shortfallTotal: 0,
|
||||
pickTotal: 0,
|
||||
storageTotal: 0,
|
||||
suggestQtyTotal: 0,
|
||||
exceedTotal: 0,
|
||||
shortfallTotal: 0,
|
||||
};
|
||||
const STATUS_SUM_KEY = { need: 'need', short: 'short', enough: 'enough', no_order: 'noOrder' };
|
||||
rows.forEach((row) => {
|
||||
const key = STATUS_SUM_KEY[row.status];
|
||||
if (key && summary[key] !== undefined) summary[key] += 1;
|
||||
summary.suggestQtyTotal += row.suggestQty || 0;
|
||||
summary.skuTotal += 1;
|
||||
if (row.status === STATUS.NEED) summary.need += 1;
|
||||
else if (row.status === STATUS.SHORT) summary.short += 1;
|
||||
summary.orderQtyTotal += row.orderQty || 0;
|
||||
summary.exceedTotal += row.excess || 0;
|
||||
summary.shortfallTotal += row.shortfall || 0;
|
||||
summary.pickTotal += row.pickQty || 0;
|
||||
summary.storageTotal += row.storageQty || 0;
|
||||
if (row.pickEmpty) summary.pickEmpty += 1;
|
||||
summary.suggestQtyTotal += row.suggestQty || 0;
|
||||
summary.exceedTotal += row.excess || 0;
|
||||
summary.shortfallTotal += row.shortfall || 0;
|
||||
});
|
||||
summary.suggestQtyTotal = round2(summary.suggestQtyTotal);
|
||||
summary.orderQtyTotal = round2(summary.orderQtyTotal);
|
||||
summary.exceedTotal = round2(summary.exceedTotal);
|
||||
summary.shortfallTotal = round2(summary.shortfallTotal);
|
||||
summary.pickTotal = round2(summary.pickTotal);
|
||||
summary.storageTotal = round2(summary.storageTotal);
|
||||
summary.suggestQtyTotal = round2(summary.suggestQtyTotal);
|
||||
summary.exceedTotal = round2(summary.exceedTotal);
|
||||
summary.shortfallTotal = round2(summary.shortfallTotal);
|
||||
return summary;
|
||||
}
|
||||
|
||||
@ -311,7 +344,22 @@ async function loadDataset(warehouse, { refresh = false } = {}) {
|
||||
loadPendingOrderMap(warehouse).catch(() => new Map()),
|
||||
]);
|
||||
|
||||
const rows = buildSkuRows({ invRows, orderMap });
|
||||
// 把「有打单拣货待发单、但货位库存视图里没有记录」的 SKU 也补进明细,
|
||||
// 否则这批最该补货(拣货位为 0)的行会被漏掉。
|
||||
const invKeys = new Set((invRows || []).map((r) => toText(r.skuId, 120)));
|
||||
const orderOnly = [...orderMap.keys()]
|
||||
.filter((k) => !invKeys.has(k))
|
||||
.map((k) => ({ skuId: k, pickQty: 0, storageQty: 0, productName: null, supplier: null }));
|
||||
const combined = (invRows || []).concat(orderOnly);
|
||||
if (orderOnly.length) {
|
||||
const nameMap = await loadSkuNames(orderOnly.map((r) => r.skuId));
|
||||
orderOnly.forEach((r) => {
|
||||
const n = nameMap.get(r.skuId);
|
||||
if (n) { r.productName = n.productName; r.supplier = n.supplier; }
|
||||
});
|
||||
}
|
||||
|
||||
const rows = buildSkuRows({ invRows: combined, orderMap });
|
||||
|
||||
const dataset = {
|
||||
at: now,
|
||||
@ -334,15 +382,6 @@ function invalidateCache(warehouseKey) {
|
||||
else cacheStore.clear();
|
||||
}
|
||||
|
||||
function matchesStatus(row, status) {
|
||||
const key = toText(status, 20).toLowerCase();
|
||||
if (!key || key === 'all') return true;
|
||||
if (key === 'action' || key === 'todo' || key === 'replenish') {
|
||||
return row.status === STATUS.NEED || row.status === STATUS.SHORT;
|
||||
}
|
||||
return row.status === key;
|
||||
}
|
||||
|
||||
function sortRows(rows) {
|
||||
return rows.slice().sort((a, b) => (
|
||||
(STATUS_RANK[b.status] || 0) - (STATUS_RANK[a.status] || 0)
|
||||
@ -352,10 +391,19 @@ function sortRows(rows) {
|
||||
));
|
||||
}
|
||||
|
||||
function filterRows(rows, { keyword, status }) {
|
||||
/**
|
||||
* 只看「待补」数据:需要从存储仓位补货到拣货仓位的 SKU(need / short)。
|
||||
* status='short' 时仅保留存储不足(缺货)的 SKU。
|
||||
*/
|
||||
function filterReplenish(rows, { keyword, status }) {
|
||||
const kw = toText(keyword, 120).toLowerCase();
|
||||
const onlyShort = toText(status, 12).toLowerCase() === 'short';
|
||||
return rows.filter((row) => {
|
||||
if (!matchesStatus(row, status)) return false;
|
||||
if (onlyShort) {
|
||||
if (row.status !== STATUS.SHORT) return false;
|
||||
} else if (row.status !== STATUS.NEED && row.status !== STATUS.SHORT) {
|
||||
return false;
|
||||
}
|
||||
if (!kw) return true;
|
||||
return row.skuId.toLowerCase().includes(kw)
|
||||
|| String(row.productName || '').toLowerCase().includes(kw)
|
||||
@ -366,15 +414,16 @@ function filterRows(rows, { keyword, status }) {
|
||||
async function getReplenishList(options = {}) {
|
||||
const warehouse = resolveWarehouse(options.warehouse);
|
||||
const dataset = await loadDataset(warehouse, { refresh: String(options.refresh) === 'true' });
|
||||
const filtered = sortRows(filterRows(dataset.rows, options));
|
||||
const summary = buildSummary(dataset.rows, dataset.meta.orderSkuCount);
|
||||
const replenished = filterReplenish(dataset.rows, options);
|
||||
const sorted = sortRows(replenished);
|
||||
const summary = buildSummary(replenished);
|
||||
const page = Math.max(1, Number.parseInt(options.page, 10) || 1);
|
||||
const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, Number.parseInt(options.pageSize, 10) || 50));
|
||||
const start = (page - 1) * pageSize;
|
||||
|
||||
return {
|
||||
rows: filtered.slice(start, start + pageSize),
|
||||
total: filtered.length,
|
||||
rows: sorted.slice(start, start + pageSize),
|
||||
total: sorted.length,
|
||||
page,
|
||||
pageSize,
|
||||
summary,
|
||||
@ -390,11 +439,12 @@ async function getReplenishList(options = {}) {
|
||||
async function getReplenishExportRows(options = {}) {
|
||||
const warehouse = resolveWarehouse(options.warehouse);
|
||||
const dataset = await loadDataset(warehouse, { refresh: String(options.refresh) === 'true' });
|
||||
const replenished = filterReplenish(dataset.rows, options);
|
||||
return {
|
||||
warehouse,
|
||||
orderScope: PENDING_ORDER_SCOPE,
|
||||
rows: sortRows(filterRows(dataset.rows, options)),
|
||||
summary: buildSummary(dataset.rows, dataset.meta.orderSkuCount),
|
||||
rows: sortRows(replenished),
|
||||
summary: buildSummary(replenished),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Alert, Button, Empty, Input, Segmented, Select, Spin, Table, Tag, Tooltip,
|
||||
Alert, Button, Empty, Input, Segmented, Spin, Table, Tag, Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
@ -24,10 +24,7 @@ const STATUS_TAGS = {
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ label: '待补货', value: 'replenish' },
|
||||
{ label: '存储不足', value: 'short' },
|
||||
{ label: '拣货位充足', value: 'enough' },
|
||||
{ label: '无待发单', value: 'no_order' },
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '仅缺货', value: 'short' },
|
||||
];
|
||||
|
||||
const BinReplenishMonitor = () => {
|
||||
@ -99,40 +96,40 @@ const BinReplenishMonitor = () => {
|
||||
|
||||
const metrics = useMemo(() => ([
|
||||
{
|
||||
key: 'replenish',
|
||||
tone: 'red',
|
||||
label: '待补货 SKU',
|
||||
value: needCount,
|
||||
unit: '个',
|
||||
foot: `建议补货合计 ${num(summary.suggestQtyTotal)} 件`,
|
||||
tip: '待发订单量 > 拣货位库存:拣货位不够待发订单拣,需要从存储位把货补过来',
|
||||
key: 'order',
|
||||
tone: 'blue',
|
||||
label: '实时代发订单数',
|
||||
value: summary.orderQtyTotal || 0,
|
||||
unit: '件',
|
||||
foot: `待补 ${num(summary.skuTotal)} 个 SKU`,
|
||||
tip: '待补 SKU 的待发订单件数合计(取聚水潭「打单拣货 → 发货中」每日快照,组合装已展开为子件;不含已发/异常/待审核)',
|
||||
},
|
||||
{
|
||||
key: 'pick',
|
||||
tone: 'green',
|
||||
label: '拣货库存数',
|
||||
value: summary.pickTotal || 0,
|
||||
unit: '件',
|
||||
foot: '待补 SKU 拣货位库存',
|
||||
tip: '待补 SKU 在 XA / XD / XE 拣货位上的库存合计(库存取自聚水潭「箱及仓位库存 · 简尚-崔桥工厂仓 主仓」)',
|
||||
},
|
||||
{
|
||||
key: 'store',
|
||||
tone: 'orange',
|
||||
label: '存储库存数',
|
||||
value: summary.storageTotal || 0,
|
||||
unit: '件',
|
||||
foot: '待补 SKU 存储位库存',
|
||||
tip: '待补 SKU 在 XX / X9 / X5 / X4 存储位上的库存合计(同上来源,主仓)',
|
||||
},
|
||||
{
|
||||
key: 'short',
|
||||
tone: 'grey',
|
||||
label: '其中存储不足',
|
||||
value: summary.short || 0,
|
||||
tone: 'red',
|
||||
label: '缺货数',
|
||||
value: summary.skuTotal || 0,
|
||||
unit: '个',
|
||||
foot: `存储缺口 ${num(summary.shortfallTotal)} 件`,
|
||||
tip: '拣货位缺货,且存储位也不够补(存储缺口 > 0):光靠库内调拨补不齐,需要采购 / 紧急调拨',
|
||||
},
|
||||
{
|
||||
key: 'exceed',
|
||||
tone: 'orange',
|
||||
label: '超出订单数合计',
|
||||
value: summary.exceedTotal || 0,
|
||||
unit: '件',
|
||||
foot: '待发拣货位缺口订单量',
|
||||
tip: '所有待补货 SKU 的「待发订单量 − 拣货位库存」之和:这是需要从存储位搬到拣货位的总量',
|
||||
},
|
||||
{
|
||||
key: 'order',
|
||||
tone: 'blue',
|
||||
label: '有待发单 SKU',
|
||||
value: summary.orderSkuCount || 0,
|
||||
unit: '个',
|
||||
foot: `待发订单量 ${num(summary.orderQtyTotal)} 件`,
|
||||
tip: '有待发订单的商品种类数(取聚水潭「打单拣货/发货中」快照,组合装已展开为子件;不含已发/异常/待审核)',
|
||||
foot: `其中存储不足 ${num(summary.short)} 个,缺口 ${num(summary.shortfallTotal)} 件`,
|
||||
tip: '需要从存储位补到拣货位的 SKU 数(待发订单量 > 拣货位库存);其中存储位也补不齐的标为「缺货」,需采购 / 紧急调拨',
|
||||
},
|
||||
]), [summary, needCount]);
|
||||
|
||||
@ -238,8 +235,9 @@ const BinReplenishMonitor = () => {
|
||||
<div className="brp-hero__main">
|
||||
<h1 className="brp-hero__title">缺货补货监控</h1>
|
||||
<p className="brp-hero__desc">
|
||||
盯的是<b>拣货位</b>:待发订单如果需要拣的货超过拣货位库存,超出的那部分就要从<b>存储位</b>补到拣货位。
|
||||
库存取自聚水潭「箱及仓位库存」,订单只统计<b>待发</b>(已发货的不再计入,因为货已经发出、不再消耗拣货位)。
|
||||
本看板只盯<b>崔桥工厂仓(主仓)</b>的<b>当日待补</b>数据:也就是那些待发订单超过拣货位库存、
|
||||
需要把货从<b>存储位</b>补到<b>拣货位</b>的 SKU。订单取自聚水潭「打单拣货 → 简尚-崔桥工厂仓」的发货中单据,
|
||||
库存取自聚水潭「箱及仓位库存 → 简尚-崔桥工厂仓 主仓」。<b>已发货的不再计入</b>(货已发出、不再消耗拣货位)。
|
||||
</p>
|
||||
</div>
|
||||
<div className="brp-hero__meta">
|
||||
@ -254,7 +252,7 @@ const BinReplenishMonitor = () => {
|
||||
{data.cacheHit ? <span className="brp-muted">(缓存)</span> : null}
|
||||
</div>
|
||||
<div>
|
||||
共 <b>{num(summary.skuTotal)}</b> 个 SKU,其中无待发单 <b>{num(summary.noOrder)}</b> 个
|
||||
共 <b>{num(summary.skuTotal)}</b> 个待补 SKU,其中存储不足(缺货)<b>{num(summary.short)}</b> 个
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -280,15 +278,7 @@ const BinReplenishMonitor = () => {
|
||||
<div className="brp-toolbar">
|
||||
<span className="brp-toolbar__field">
|
||||
<span className="brp-toolbar__label">仓库</span>
|
||||
<Select
|
||||
value={warehouse}
|
||||
onChange={(value) => { setWarehouse(value); setPage(1); }}
|
||||
style={{ width: 210 }}
|
||||
options={(data.warehouses || []).map((item) => ({
|
||||
value: item.key,
|
||||
label: item.name,
|
||||
}))}
|
||||
/>
|
||||
<span className="brp-toolbar__static">简尚-崔桥工厂仓(主仓)</span>
|
||||
</span>
|
||||
<span className="brp-toolbar__field">
|
||||
<span className="brp-toolbar__label">范围</span>
|
||||
@ -299,11 +289,7 @@ const BinReplenishMonitor = () => {
|
||||
...item,
|
||||
label: item.value === 'replenish'
|
||||
? `${item.label} ${num(needCount)}`
|
||||
: item.value === 'short'
|
||||
? `${item.label} ${num(summary.short || 0)}`
|
||||
: item.value === 'no_order'
|
||||
? `${item.label} ${num(summary.noOrder || 0)}`
|
||||
: item.label,
|
||||
: `${item.label} ${num(summary.short || 0)}`,
|
||||
}))}
|
||||
/>
|
||||
</span>
|
||||
@ -331,10 +317,11 @@ const BinReplenishMonitor = () => {
|
||||
type="info"
|
||||
showIcon
|
||||
className="brp-alert"
|
||||
message="货位口径(崔桥工厂仓约定)"
|
||||
message="货位口径(崔桥工厂仓 主仓约定)"
|
||||
description={(
|
||||
<span>
|
||||
拣货库位以 <b>XA / XD / XE</b> 开头,存储库位以 <b>XX / X9 / X5 / X4</b> 开头;其余货位(DB / Y* / Z* 等,占比极小)不计入。
|
||||
库存只取 <b>崔桥工厂仓主仓(wh_id=1)</b>;拣货库位以 <b>XA / XD / XE</b> 开头,存储库位以 <b>XX / X9 / X5 / X4</b> 开头;
|
||||
其余货位(DB / Y* / Z* 等,占比极小)不计入。
|
||||
判断规则:<b>待发订单量 > 拣货位库存</b> 时,超出的订单数 = 需要从存储位补到拣货位的量(上限为存储位现有库存)。
|
||||
</span>
|
||||
)}
|
||||
@ -390,26 +377,30 @@ const BinReplenishMonitor = () => {
|
||||
<div className="brp-notes__title">数据来源与口径说明</div>
|
||||
<ul>
|
||||
<li>
|
||||
<b>库存</b>:聚水潭「箱及仓位库存」(rpt.jst_packInventoryInfo),按商品编码(sku_id)聚合。
|
||||
「拣货位库存」= 货位号以 XA/XD/XE 开头的库存合计;「存储位库存」= 货位号以 XX/X9/X5/X4 开头的库存合计。
|
||||
<b>库存</b>:聚水潭「箱及仓位库存 → 简尚-崔桥工厂仓 主仓」(rpt.jst_packInventoryInfo,仅 <b>wh_id=1 主仓</b>),
|
||||
按商品编码(sku_id)聚合。「拣货位库存」= 货位号以 XA/XD/XE 开头的库存合计;
|
||||
「存储位库存」= 货位号以 XX/X9/X5/X4 开头的库存合计。
|
||||
</li>
|
||||
<li>
|
||||
<b>订单</b>:直接取聚水潭「<b>打单拣货 → 发货中</b>」快照 = rpt.op_OrderNotDispatched(该仓<b>最新快照日</b>、status=发货中),
|
||||
SKU 级、组合装已展开为子件。这就是你在聚水潭「打单拣货 · 简尚崔桥工厂仓」看到的待发单据。
|
||||
<b>已发货(Sent)、异常挂起、已付款待审核、待付款均不计入</b>——货已发出或短期不会拣,不该驱动补货。
|
||||
口径仅覆盖<b>聚水潭线上店铺渠道</b>(运营中心 / 分销等),<b>不含全民(qmcust) B2B 客户单</b>——与旧「按 send_date 窗口统计当日订单」口径不同,属预期收窄。
|
||||
<b>订单</b>:直接取聚水潭「<b>打单拣货 → 简尚-崔桥工厂仓</b>」的发货中单据快照 = rpt.op_OrderNotDispatched
|
||||
(该仓<b>最新快照日</b>、status=发货中),SKU 级、组合装已展开为子件。
|
||||
<b>已发货 / 异常挂起 / 已付款待审核 / 待付款均不计入</b>——货已发出或短期不会拣,不该驱动补货。
|
||||
口径仅覆盖<b>聚水潭线上店铺渠道</b>(运营中心 / 分销等),<b>不含全民(qmcust) B2B 客户单</b>。
|
||||
</li>
|
||||
<li>
|
||||
<b>判断规则</b>:对每个 SKU,若 <b>待发订单量 > 拣货位库存</b>,超出的订单数即「需要从存储位补到拣货位」的量;
|
||||
「建议补货量」= min(超出订单数, 存储位库存)。存储位也不够时标记「存储不足」,缺口需采购 / 紧急调拨。
|
||||
<b>看板范围</b>:本看板<b>只显示当日待补</b> SKU —— 即「待发订单量 > 拣货位库存」、需要从存储位补到拣货位的这部分
|
||||
(含拣货位为 0、库存视图里查不到但有打单拣货待发单的 SKU)。其余「拣货位充足 / 无待发单」的 SKU 不进本看板。
|
||||
</li>
|
||||
<li>
|
||||
<b>统计范围</b>:取该仓<b>最新一天的「打单拣货」快照</b>(ETL 约每日 07:00 刷新),即当前待拣/待发订单;
|
||||
已发/异常/待审核一律排除。
|
||||
<b>判断规则</b>:对每个待补 SKU,<b>超出订单数</b> = max(0, 待发订单量 − 拣货位库存);
|
||||
「建议补货量」= min(超出订单数, 存储位库存);存储位也不够时标记「缺货」,缺口需采购 / 紧急调拨。
|
||||
</li>
|
||||
<li>
|
||||
<b>时效</b>:仓位库存视图聚合需 30s~1min,待发快照表较快;服务端缓存 10 分钟(启动时已预热);
|
||||
点「刷新数据」会强制回源,需要等约 1 分钟。
|
||||
<b>统计范围</b>:取该仓<b>最新一天的「打单拣货」快照</b>(ETL 约每日 07:00 刷新);已发/异常/待审核一律排除。
|
||||
</li>
|
||||
<li>
|
||||
<b>时效</b>:库存视图聚合约 1~2s,待发快照两步查询约 12s;服务端缓存 10 分钟(启动时已预热);
|
||||
点「刷新数据」会强制回源,需要等约 15~30 秒。
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user