fix: 老板驾驶舱统一全口径数据源 - 实收/客单价/账单数/风险占比均取自store-expense/overview,与利润瀑布一致
This commit is contained in:
@@ -73,7 +73,7 @@ export function BossPage() {
|
||||
return acc
|
||||
}, {})
|
||||
const totalStores = riskRows.length
|
||||
const totalRev = Number(od?.received || 0)
|
||||
const totalRev = Number(ex?.total_received || 0)
|
||||
|
||||
// P0/P1门店
|
||||
const p0Stores = priorityRows.filter((s: any) => s.action_priority?.startsWith('P0'))
|
||||
@@ -155,10 +155,10 @@ export function BossPage() {
|
||||
|
||||
{/* ① 核心经营指标 */}
|
||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||
<MetricCard title="实收总额" value={od?.received} format="currency" trend={trendReceived} description={`公式:全部门店实收合计。环比上周 ${trendReceived > 0 ? '↑' : '↓'} ${Math.abs(trendReceived)}%`} />
|
||||
<MetricCard title="实收总额" value={ex?.total_received} format="currency" trend={trendReceived} description={`全口径${ex?.total_stores}家门店。公式:全部门店实收合计。环比上周 ${trendReceived > 0 ? '↑' : '↓'} ${Math.abs(trendReceived)}%`} />
|
||||
<MetricCard title="实际净利润" value={ex?.actual_net_profit} format="currency" status={Number(ex?.actual_net_margin_pct) < 5 ? 'bad' : Number(ex?.actual_net_margin_pct) < 10 ? 'warn' : 'good'} description={`净利率 ${formatPercent(ex?.actual_net_margin_pct)} · 公式:实收 - 食材成本 - 经营费用。建议值:≥ 10% 优秀,5-10% 合格,< 5% 需整改`} />
|
||||
<MetricCard title="门店数" value={totalStores} unit="家" description={`盈利 ${ex?.profitable_stores}家 / 亏损 ${ex?.loss_stores}家。无费用数据 ${ex?.no_expense_stores}家`} />
|
||||
<MetricCard title="客单价" value={od?.avg_bill_value} format="currency" trend={trend((last7.length > 0 ? sumKey(last7, 'received') / sumKey(last7, 'bill_count') : 0), (prev7.length > 0 ? sumKey(prev7, 'received') / sumKey(prev7, 'bill_count') : 0))} description={`账单 ${formatNumber(od?.bill_count)} 笔 · 公式:实收 ÷ 账单数`} />
|
||||
<MetricCard title="门店数" value={ex?.total_stores} unit="家" description={`盈利 ${ex?.profitable_stores}家 / 亏损 ${ex?.loss_stores}家。无费用数据 ${ex?.no_expense_stores}家`} />
|
||||
<MetricCard title="客单价" value={ex?.avg_bill_value} format="currency" trend={trend((last7.length > 0 ? sumKey(last7, 'received') / sumKey(last7, 'bill_count') : 0), (prev7.length > 0 ? sumKey(prev7, 'received') / sumKey(prev7, 'bill_count') : 0))} description={`账单 ${formatNumber(ex?.total_bills)} 笔 · 公式:实收 ÷ 账单数`} />
|
||||
</div>
|
||||
|
||||
{/* ② 利润瀑布 */}
|
||||
|
||||
@@ -16,6 +16,7 @@ router.get('/overview', async (req: AuthRequest, res) => {
|
||||
r.store_code,
|
||||
r.store_name,
|
||||
r.received,
|
||||
r.bill_count,
|
||||
r.theoretical_margin_pct,
|
||||
COALESCE(e.operating_expense, 0) AS operating_expense,
|
||||
COALESCE(e.wage_expense, 0) AS wage_expense,
|
||||
@@ -40,7 +41,9 @@ router.get('/overview', async (req: AuthRequest, res) => {
|
||||
count(*) FILTER (WHERE actual_store_contribution > 0) AS profitable_stores,
|
||||
count(*) FILTER (WHERE actual_store_contribution <= 0 AND received > 0) AS loss_stores,
|
||||
count(*) FILTER (WHERE operating_expense = 0) AS no_expense_stores,
|
||||
sum(bill_count)::bigint AS total_bills,
|
||||
round(sum(received)::numeric, 2) AS total_received,
|
||||
round(sum(received) / nullif(sum(bill_count), 0), 2) AS avg_bill_value,
|
||||
round(sum(operating_expense)::numeric, 2) AS total_expense,
|
||||
round(sum(wage_expense)::numeric, 2) AS total_wage,
|
||||
round(sum(rent_expense)::numeric, 2) AS total_rent,
|
||||
|
||||
Reference in New Issue
Block a user