diff --git a/client/src/pages/StoreDetailPage.tsx b/client/src/pages/StoreDetailPage.tsx index 0309e05..ea31aea 100644 --- a/client/src/pages/StoreDetailPage.tsx +++ b/client/src/pages/StoreDetailPage.tsx @@ -261,10 +261,10 @@ export function StoreDetailPage() { formatPercent(r.theoretical_cost_rate_pct) }, - { key: 'actual_cost_rate_pct', label: '实际成本率', align: 'right', render: (r) => formatPercent(r.actual_cost_rate_pct) }, - { key: 'variance_pct', label: '差异', align: 'right', render: (r) => 20 ? 'text-red-600' : 'text-green-600'}>{Number(r.variance_pct).toFixed(1)}% }, - { key: 'benchmark_rate_pct', label: '同类基准', align: 'right', render: (r) => formatPercent(r.benchmark_rate_pct) }, + { key: 'consumption_amount', label: '消耗金额', align: 'right', render: (r) => formatCurrency(r.consumption_amount) }, + { key: 'actual_cost_rate_pct', label: '占比', align: 'right', render: (r) => formatPercent(r.actual_cost_rate_pct) }, + { key: 'benchmark_rate_pct', label: '公司基准', align: 'right', render: (r) => formatPercent(r.benchmark_rate_pct) }, + { key: 'variance_pct', label: '差异', align: 'right', render: (r) => 2 ? 'text-red-600' : 'text-green-600'}>{Number(r.variance_pct).toFixed(1)}% }, ]} data={cost.categories} /> diff --git a/server/src/routes/data.ts b/server/src/routes/data.ts index 7879496..975698a 100644 --- a/server/src/routes/data.ts +++ b/server/src/routes/data.ts @@ -383,7 +383,30 @@ router.get('/stores/:code/cost', async (req: AuthRequest, res) => { try { const [costResult, catResult] = await Promise.all([ query(`SELECT * FROM analytics.v_store_theoretical_actual_cost_april WHERE store_code = $1`, [req.params.code]), - query(`SELECT * FROM analytics.v_store_category_cost_benchmark_april WHERE store_code = $1`, [req.params.code]), + query(` + WITH store_cat AS ( + SELECT finance_category AS category_name, + round(sum(consumption_amount)::numeric, 2) AS consumption_amount, + round(sum(consumption_amount)::numeric / nullif(sum(sum(consumption_amount)) OVER (), 0) * 100, 1) AS actual_cost_rate_pct + FROM analytics.v_inventory_cost_classified_april + WHERE sales_store_code = $1 AND finance_category IS NOT NULL + GROUP BY finance_category + ORDER BY sum(consumption_amount) DESC + ), + company_cat AS ( + SELECT finance_category, + round(sum(consumption_amount)::numeric / nullif(sum(sum(consumption_amount)) OVER (), 0) * 100, 1) AS benchmark_rate_pct + FROM analytics.v_inventory_cost_classified_april + WHERE finance_category IS NOT NULL + GROUP BY finance_category + ) + SELECT s.category_name, s.consumption_amount, s.actual_cost_rate_pct, + COALESCE(c.benchmark_rate_pct, 0) AS benchmark_rate_pct, + round(s.actual_cost_rate_pct - COALESCE(c.benchmark_rate_pct, 0), 1) AS variance_pct + FROM store_cat s + LEFT JOIN company_cat c ON s.category_name = c.finance_category + ORDER BY s.consumption_amount DESC + `, [req.params.code]), ]) sendSuccess(res, { cost: costResult.rows[0], categories: catResult.rows }) } catch (err: any) { sendError(res, err.message) }