fix: 老板驾驶舱统一全口径数据源 - 实收/客单价/账单数/风险占比均取自store-expense/overview,与利润瀑布一致
This commit is contained in:
@@ -73,7 +73,7 @@ export function BossPage() {
|
|||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
const totalStores = riskRows.length
|
const totalStores = riskRows.length
|
||||||
const totalRev = Number(od?.received || 0)
|
const totalRev = Number(ex?.total_received || 0)
|
||||||
|
|
||||||
// P0/P1门店
|
// P0/P1门店
|
||||||
const p0Stores = priorityRows.filter((s: any) => s.action_priority?.startsWith('P0'))
|
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">
|
<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={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={ex?.total_stores} 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?.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>
|
</div>
|
||||||
|
|
||||||
{/* ② 利润瀑布 */}
|
{/* ② 利润瀑布 */}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ router.get('/overview', async (req: AuthRequest, res) => {
|
|||||||
r.store_code,
|
r.store_code,
|
||||||
r.store_name,
|
r.store_name,
|
||||||
r.received,
|
r.received,
|
||||||
|
r.bill_count,
|
||||||
r.theoretical_margin_pct,
|
r.theoretical_margin_pct,
|
||||||
COALESCE(e.operating_expense, 0) AS operating_expense,
|
COALESCE(e.operating_expense, 0) AS operating_expense,
|
||||||
COALESCE(e.wage_expense, 0) AS wage_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) AS profitable_stores,
|
||||||
count(*) FILTER (WHERE actual_store_contribution <= 0 AND received > 0) AS loss_stores,
|
count(*) FILTER (WHERE actual_store_contribution <= 0 AND received > 0) AS loss_stores,
|
||||||
count(*) FILTER (WHERE operating_expense = 0) AS no_expense_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)::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(operating_expense)::numeric, 2) AS total_expense,
|
||||||
round(sum(wage_expense)::numeric, 2) AS total_wage,
|
round(sum(wage_expense)::numeric, 2) AS total_wage,
|
||||||
round(sum(rent_expense)::numeric, 2) AS total_rent,
|
round(sum(rent_expense)::numeric, 2) AS total_rent,
|
||||||
|
|||||||
Reference in New Issue
Block a user