From 6c5d8ed5d3e985ea1f48cafa21cdf76c05741859 Mon Sep 17 00:00:00 2001 From: freedakgmail Date: Thu, 30 Jul 2026 10:17:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=A9=E6=B6=A6=E6=88=90=E6=9C=AC?= =?UTF-8?q?=E8=B4=B9=E7=94=A8=E6=94=B9=E4=B8=BA=E5=85=A8=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=20-=20=E4=BB=A591=E5=AE=B6=E9=97=A8=E5=BA=97=E4=B8=BA=E5=9F=BA?= =?UTF-8?q?=E6=95=B0LEFT=20JOIN=E8=B4=B9=E7=94=A8=E8=A1=A8=EF=BC=8C?= =?UTF-8?q?=E7=BC=BA=E8=B4=B9=E7=94=A8=E9=97=A8=E5=BA=97=E8=90=A5=E6=94=B6?= =?UTF-8?q?=E4=B9=9F=E7=BA=B3=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/routes/store-expense.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/server/src/routes/store-expense.ts b/server/src/routes/store-expense.ts index 1355935..b566139 100644 --- a/server/src/routes/store-expense.ts +++ b/server/src/routes/store-expense.ts @@ -11,11 +11,35 @@ const router = Router() router.get('/overview', async (req: AuthRequest, res) => { try { const result = await query(` + WITH full_scope AS ( + SELECT + r.store_code, + r.store_name, + r.received, + r.theoretical_margin_pct, + COALESCE(e.operating_expense, 0) AS operating_expense, + COALESCE(e.wage_expense, 0) AS wage_expense, + COALESCE(e.rent_expense, 0) AS rent_expense, + COALESCE(e.utility_expense, 0) AS utility_expense, + COALESCE(e.dorm_expense, 0) AS dorm_expense, + COALESCE(e.delivery_commission_expense, 0) AS delivery_commission_expense, + COALESCE(e.card_fee_expense, 0) AS card_fee_expense, + COALESCE(e.repair_clean_expense, 0) AS repair_clean_expense, + COALESCE(e.actual_food_cost, 0) AS actual_food_cost, + COALESCE(e.theoretical_cost, r.received * (1 - COALESCE(r.theoretical_margin_pct, 0) / 100)) AS theoretical_cost, + COALESCE(e.actual_store_contribution, r.received - COALESCE(e.actual_food_cost, 0) - COALESCE(e.operating_expense, 0)) AS actual_store_contribution, + COALESCE(e.theoretical_store_contribution, r.received - r.received * (1 - COALESCE(r.theoretical_margin_pct, 0) / 100) - COALESCE(e.operating_expense, 0)) AS theoretical_store_contribution, + COALESCE(e.area_sqm, 0) AS area_sqm + FROM analytics.mv_store_risk_rating r + LEFT JOIN analytics.mv_store_operating_expense_monthly e + ON r.store_code = e.sales_store_code AND e.report_month = DATE '2026-04-01' + WHERE r.received IS NOT NULL + ) SELECT count(*) AS total_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 received = 0) AS zero_sales_stores, + count(*) FILTER (WHERE operating_expense = 0) AS no_expense_stores, round(sum(received)::numeric, 2) AS total_received, round(sum(operating_expense)::numeric, 2) AS total_expense, round(sum(wage_expense)::numeric, 2) AS total_wage, @@ -41,8 +65,7 @@ router.get('/overview', async (req: AuthRequest, res) => { round(sum(wage_expense) / nullif(sum(received), 0) * 100, 2) AS overall_wage_rate_pct, round(sum(rent_expense) / nullif(sum(received), 0) * 100, 2) AS overall_rent_rate_pct, round(sum(utility_expense) / nullif(sum(received), 0) * 100, 2) AS overall_utility_rate_pct - FROM analytics.mv_store_operating_expense_monthly - WHERE report_month = DATE '2026-04-01' + FROM full_scope `) sendSuccess(res, result.rows[0]) } catch (err: any) {