MKH-BOM 资料
diff --git a/frontend/src/pages/ModularStockMonitor/index.jsx b/frontend/src/pages/ModularStockMonitor/index.jsx
index ec08b52..dd3c9a2 100644
--- a/frontend/src/pages/ModularStockMonitor/index.jsx
+++ b/frontend/src/pages/ModularStockMonitor/index.jsx
@@ -271,7 +271,10 @@ const AccessoryStockPanel = () => {
: int(value)),
},
{
- title: '实时库存',
+ title:
,
dataIndex: 'realtimeStock',
width: 96,
align: 'right',
@@ -281,7 +284,7 @@ const AccessoryStockPanel = () => {
{
title:
,
dataIndex: 'purchaseTransit',
width: 120,
diff --git a/frontend/src/router/index.jsx b/frontend/src/router/index.jsx
index cd0ec68..5b297d6 100644
--- a/frontend/src/router/index.jsx
+++ b/frontend/src/router/index.jsx
@@ -19,6 +19,7 @@ const OpsBIncentiveBoard = lazy(() => import('../pages/PlanningUpload/OpsBIncent
const ProductMonitoring = lazy(() => import('../pages/ProductMonitoring'));
const ModularStockMonitor = lazy(() => import('../pages/ModularStockMonitor'));
const MkhBomData = lazy(() => import('../pages/MkhBomData'));
+const DailyInventory = lazy(() => import('../pages/DailyInventory'));
const ReturnsInventoryMonitor = lazy(() => import('../pages/ReturnsInventoryWarning/Monitor'));
const lazyLoadingElement =
;
@@ -112,6 +113,10 @@ const router = createBrowserRouter([
path: 'planning/operation/mkh-bom-data',
element: renderLazyComponent(MkhBomData),
},
+ {
+ path: 'planning/operation/daily-inventory',
+ element: renderLazyComponent(DailyInventory),
+ },
// 客退数据监控(5 张看板共用同一个页面,靠 warehouseKey 区分取哪个仓;
// 单一「退货库存预警」视图,原 明细/超期/汇总/差异 四个页签已下线)
{
diff --git a/frontend/src/utils/mkhFreshContract.js b/frontend/src/utils/mkhFreshContract.js
new file mode 100644
index 0000000..2ba828d
--- /dev/null
+++ b/frontend/src/utils/mkhFreshContract.js
@@ -0,0 +1,15 @@
+export const MKH_FRESH_DATA_CONTRACT_VERSION = 1;
+
+const OUTDATED_BACKEND_MESSAGE = '本地后端版本过旧或更新后未重启,已停止展示可能过期的库存。'
+ + '请停止当前 npm run remote:dev,拉取最新 main 后重新启动。';
+
+export function requireMkhFreshContract(snapshot, { requireApplied = false } = {}) {
+ const contractVersion = Number(snapshot?.freshDataContractVersion) || 0;
+ if (contractVersion < MKH_FRESH_DATA_CONTRACT_VERSION) {
+ throw new Error(OUTDATED_BACKEND_MESSAGE);
+ }
+ if (requireApplied && snapshot?.freshDataApplied !== true) {
+ throw new Error('后端未确认已重读最新运营数据,已停止展示可能过期的库存,请重试。');
+ }
+ return snapshot;
+}
diff --git a/frontend/test/mkhFreshContract.test.mjs b/frontend/test/mkhFreshContract.test.mjs
new file mode 100644
index 0000000..261f79d
--- /dev/null
+++ b/frontend/test/mkhFreshContract.test.mjs
@@ -0,0 +1,23 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+
+import { requireMkhFreshContract } from '../src/utils/mkhFreshContract.js';
+
+test('MKH fresh contract accepts a backend-confirmed live refresh', () => {
+ const snapshot = { freshDataContractVersion: 1, freshDataApplied: true };
+ assert.equal(requireMkhFreshContract(snapshot, { requireApplied: true }), snapshot);
+});
+
+test('MKH fresh contract rejects an outdated local backend instead of showing stale stock', () => {
+ assert.throws(
+ () => requireMkhFreshContract({ rows: [{ stock: 4 }] }, { requireApplied: true }),
+ /本地后端版本过旧/
+ );
+});
+
+test('MKH fresh contract requires explicit refresh completion for fresh requests', () => {
+ assert.throws(
+ () => requireMkhFreshContract({ freshDataContractVersion: 1 }, { requireApplied: true }),
+ /未确认已重读/
+ );
+});