fix: 利润机会池改为门店级计算,修复整体平均掩盖问题门店
问题:利润机会池6项中有4项返回0,总额仅15.3万
原因:用整体平均费率判断是否超标,整体已达标则机会=0,
掩盖了个别门店严重超标的问题
修复:
1. 食材成本差异回收:改用 mv_store_theoretical_actual_cost_monthly
(门店级理论vs实际成本),只对正差异(超耗)门店求和 × 30%
修复前 0 → 修复后 1,898,587
2. 标准店人工优化:改为门店级,只对费率>24%的门店计算机会
修复前 0 → 修复后 1,561,133(58家超标)
3. 标准店能源优化:改为门店级,只对费率>5%的门店计算机会
修复前 0 → 修复后 466,609(49家超标)
4. 平台佣金优化:改为门店级,只对佣金率>20%的门店计算机会
修复前 0 → 修复后 3,430(1家超标)
5. 高优惠门店治理和SKU复杂度压缩逻辑不变
总额:152,995 → 4,082,755(约408万)
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
+66
-59
@@ -1144,6 +1144,7 @@ router.get('/overview/store-profit-ranking', async (req: AuthRequest, res) => {
|
||||
})
|
||||
|
||||
// 利润机会池
|
||||
// 门店级计算:只对超标门店计算机会金额,避免整体平均掩盖问题门店
|
||||
router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
@@ -1154,64 +1155,67 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
|
||||
sku_abc AS (
|
||||
SELECT * FROM analytics.mv_dish_sku_abc_monthly WHERE month_start = $1
|
||||
),
|
||||
standard_stores AS (
|
||||
SELECT r.store_code, r.store_name, r.received,
|
||||
COALESCE(e.actual_food_cost, 0) AS actual_food_cost,
|
||||
COALESCE(e.operating_expense, 0) AS operating_expense,
|
||||
COALESCE(e.wage_expense, 0) AS wage_expense,
|
||||
COALESCE(e.utility_expense, 0) AS utility_expense,
|
||||
r.theoretical_margin_pct, r.discount_rate_pct
|
||||
FROM risk r
|
||||
LEFT JOIN analytics.mv_store_operating_expense_monthly e
|
||||
ON r.store_code = e.sales_store_code AND e.report_month = $1::date
|
||||
WHERE r.received IS NOT NULL AND r.received > 0
|
||||
),
|
||||
-- 食材成本差异:改用 mv_store_theoretical_actual_cost_monthly(门店级理论vs实际成本)
|
||||
cost_diff AS (
|
||||
SELECT
|
||||
round(sum(actual_food_cost)::numeric, 2) AS actual_cost,
|
||||
round(sum(received * (1 - COALESCE(theoretical_margin_pct, 0) / 100))::numeric, 2) AS theoretical_cost
|
||||
FROM standard_stores
|
||||
count(*) FILTER (WHERE food_cost_variance > 0) AS over_cost_stores,
|
||||
round(sum(food_cost_variance) FILTER (WHERE food_cost_variance > 0)::numeric, 2) AS total_positive_variance,
|
||||
round(sum(theoretical_cost)::numeric, 2) AS total_theoretical_cost,
|
||||
round(sum(actual_food_cost)::numeric, 2) AS total_actual_cost,
|
||||
round(sum(sales_received)::numeric, 2) AS total_received
|
||||
FROM analytics.mv_store_theoretical_actual_cost_monthly
|
||||
WHERE month_start = $1
|
||||
),
|
||||
cost_diff_stores AS (
|
||||
SELECT store_name, received,
|
||||
round(actual_food_cost::numeric, 2) AS actual_food_cost,
|
||||
round((received * (1 - COALESCE(theoretical_margin_pct, 0) / 100))::numeric, 2) AS theoretical_cost,
|
||||
round((actual_food_cost - received * (1 - COALESCE(theoretical_margin_pct, 0) / 100))::numeric, 2) AS diff_amount,
|
||||
round((actual_food_cost / nullif(received, 0) * 100)::numeric, 2) AS actual_cost_rate,
|
||||
round((received * (1 - COALESCE(theoretical_margin_pct, 0) / 100) / nullif(received, 0) * 100)::numeric, 2) AS theoretical_cost_rate
|
||||
FROM standard_stores
|
||||
WHERE actual_food_cost IS NOT NULL
|
||||
ORDER BY (actual_food_cost - received * (1 - COALESCE(theoretical_margin_pct, 0) / 100)) DESC
|
||||
SELECT store_name, sales_received AS received,
|
||||
round(food_cost_variance::numeric, 2) AS diff_amount,
|
||||
round(actual_food_cost_rate_pct::numeric, 2) AS actual_cost_rate,
|
||||
round(theoretical_cost_rate_pct::numeric, 2) AS theoretical_cost_rate
|
||||
FROM analytics.mv_store_theoretical_actual_cost_monthly
|
||||
WHERE month_start = $1 AND food_cost_variance > 0
|
||||
ORDER BY food_cost_variance DESC
|
||||
LIMIT 5
|
||||
),
|
||||
-- 人工优化:门店级,只对费率>24%的门店计算机会
|
||||
labor AS (
|
||||
SELECT
|
||||
count(*) FILTER (WHERE wage_rate_pct > 24) AS over_wage_stores,
|
||||
round(sum(wage_expense) FILTER (WHERE wage_rate_pct > 24)::numeric, 2) AS over_wage_total,
|
||||
round(sum(wage_expense * GREATEST(1 - 24.0 / nullif(wage_rate_pct, 0), 0)) FILTER (WHERE wage_rate_pct > 24)::numeric, 2) AS opportunity,
|
||||
round(sum(wage_expense)::numeric, 2) AS total_wage,
|
||||
round(sum(received)::numeric, 2) AS total_received
|
||||
FROM standard_stores
|
||||
FROM analytics.mv_store_operating_expense_monthly
|
||||
WHERE report_month = $1::date AND wage_expense IS NOT NULL AND wage_expense > 0 AND received > 0
|
||||
),
|
||||
labor_stores AS (
|
||||
SELECT store_name, received,
|
||||
SELECT sales_store_name AS store_name, received,
|
||||
round(wage_expense::numeric, 2) AS wage,
|
||||
round((wage_expense / nullif(received, 0) * 100)::numeric, 2) AS wage_rate
|
||||
FROM standard_stores
|
||||
WHERE wage_expense IS NOT NULL
|
||||
ORDER BY wage_expense / nullif(received, 0) DESC
|
||||
round(wage_rate_pct::numeric, 2) AS wage_rate
|
||||
FROM analytics.mv_store_operating_expense_monthly
|
||||
WHERE report_month = $1::date AND wage_expense IS NOT NULL AND wage_expense > 0 AND received > 0
|
||||
AND wage_rate_pct > 24
|
||||
ORDER BY wage_rate_pct DESC
|
||||
LIMIT 5
|
||||
),
|
||||
-- 能源优化:门店级,只对费率>5%的门店计算机会
|
||||
energy AS (
|
||||
SELECT
|
||||
count(*) FILTER (WHERE utility_rate_pct > 5) AS over_utility_stores,
|
||||
round(sum(utility_expense) FILTER (WHERE utility_rate_pct > 5)::numeric, 2) AS over_utility_total,
|
||||
round(sum(utility_expense * GREATEST(1 - 5.0 / nullif(utility_rate_pct, 0), 0)) FILTER (WHERE utility_rate_pct > 5)::numeric, 2) AS opportunity,
|
||||
round(sum(utility_expense)::numeric, 2) AS total_utility,
|
||||
round(sum(received)::numeric, 2) AS total_received
|
||||
FROM standard_stores
|
||||
FROM analytics.mv_store_operating_expense_monthly
|
||||
WHERE report_month = $1::date AND utility_expense IS NOT NULL AND utility_expense > 0 AND received > 0
|
||||
),
|
||||
energy_stores AS (
|
||||
SELECT store_name, received,
|
||||
SELECT sales_store_name AS store_name, received,
|
||||
round(utility_expense::numeric, 2) AS utility,
|
||||
round((utility_expense / nullif(received, 0) * 100)::numeric, 2) AS utility_rate
|
||||
FROM standard_stores
|
||||
WHERE utility_expense IS NOT NULL
|
||||
ORDER BY utility_expense / nullif(received, 0) DESC
|
||||
round(utility_rate_pct::numeric, 2) AS utility_rate
|
||||
FROM analytics.mv_store_operating_expense_monthly
|
||||
WHERE report_month = $1::date AND utility_expense IS NOT NULL AND utility_expense > 0 AND received > 0
|
||||
AND utility_rate_pct > 5
|
||||
ORDER BY utility_rate_pct DESC
|
||||
LIMIT 5
|
||||
),
|
||||
discount AS (
|
||||
@@ -1230,19 +1234,22 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
|
||||
ORDER BY discount_rate_pct DESC
|
||||
LIMIT 5
|
||||
),
|
||||
-- 平台佣金优化:门店级,只对佣金率>20%的门店计算机会
|
||||
platform AS (
|
||||
SELECT
|
||||
count(*) FILTER (WHERE delivery_commission_expense > 0 AND delivery_commission_expense / nullif(received, 0) * 100 > 20) AS over_commission_stores,
|
||||
round(sum(delivery_commission_expense)::numeric, 2) AS total_commission,
|
||||
round(sum(received)::numeric, 2) AS platform_received
|
||||
round(sum(received)::numeric, 2) AS platform_received,
|
||||
round(sum(delivery_commission_expense * GREATEST(1 - 20.0 / nullif(delivery_commission_expense / nullif(received, 0) * 100, 0), 0)) FILTER (WHERE delivery_commission_expense > 0 AND delivery_commission_expense / nullif(received, 0) * 100 > 20)::numeric, 2) AS opportunity
|
||||
FROM analytics.mv_store_operating_expense_monthly
|
||||
WHERE report_month = $1::date AND delivery_commission_expense IS NOT NULL
|
||||
WHERE report_month = $1::date AND delivery_commission_expense IS NOT NULL AND received > 0
|
||||
),
|
||||
platform_stores AS (
|
||||
SELECT e.sales_store_code AS store_code, e.received,
|
||||
round(e.delivery_commission_expense::numeric, 2) AS commission,
|
||||
round((e.delivery_commission_expense / nullif(e.received, 0) * 100)::numeric, 2) AS commission_rate
|
||||
FROM analytics.mv_store_operating_expense_monthly e
|
||||
WHERE e.report_month = $1::date AND e.delivery_commission_expense IS NOT NULL
|
||||
WHERE e.report_month = $1::date AND e.delivery_commission_expense IS NOT NULL AND e.received > 0
|
||||
ORDER BY e.delivery_commission_expense / nullif(e.received, 0) DESC
|
||||
LIMIT 5
|
||||
),
|
||||
@@ -1264,47 +1271,47 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
|
||||
'items', json_build_array(
|
||||
json_build_object(
|
||||
'category', '食材成本差异回收',
|
||||
'baseline', (SELECT round((actual_cost - theoretical_cost)::numeric, 2) FROM cost_diff),
|
||||
'baseline', (SELECT total_positive_variance FROM cost_diff),
|
||||
'target_pct', 30,
|
||||
'opportunity', GREATEST(round((SELECT (actual_cost - theoretical_cost) FROM cost_diff) * 0.30, 2), 0),
|
||||
'opportunity', round((SELECT total_positive_variance FROM cost_diff) * 0.30, 2),
|
||||
'confidence', '中高',
|
||||
'owner', '商品/供应链/门店',
|
||||
'evidence', '采购价差、用量差、盘点差、报损差',
|
||||
'detail', (SELECT
|
||||
'实际食材成本' || (SELECT actual_cost FROM cost_diff) || '元 vs 理论成本' || (SELECT theoretical_cost FROM cost_diff) || '元,差异' || round((SELECT actual_cost - theoretical_cost FROM cost_diff)::numeric, 2) || '元(成本率' || round((SELECT actual_cost FROM cost_diff) / nullif((SELECT sum(received) FROM standard_stores), 0) * 100, 2) || '% vs 理论' || round((SELECT theoretical_cost FROM cost_diff) / nullif((SELECT sum(received) FROM standard_stores), 0) * 100, 2) || '%)。\n\n' ||
|
||||
'差异TOP5门店(需优先排查):\n' ||
|
||||
'超耗门店' || (SELECT over_cost_stores FROM cost_diff) || '家,实际食材成本' || (SELECT total_actual_cost FROM cost_diff) || '元 vs 理论成本' || (SELECT total_theoretical_cost FROM cost_diff) || '元,正差异合计' || (SELECT total_positive_variance FROM cost_diff) || '元。\n\n' ||
|
||||
'超耗TOP5门店(需优先排查):\n' ||
|
||||
string_agg(store_name || ':差异' || diff_amount || '元(实际成本率' || actual_cost_rate || '% vs 理论' || theoretical_cost_rate || '%,实收' || received || '元)', ';\n') ||
|
||||
'\n\n行动指向:\n1)上述5家门店实际成本率均远超理论值,需逐店排查采购单价与BOM标准价差异;\n2)盘点差——核查月末盘点与系统库存一致性,差异>3%需复盘;\n3)报损差——对比报损记录与行业基准,报损率>2%的门店需检查存储和加工流程;\n4)30天目标:将TOP5门店成本率降低5个百分点,预计回收' || round((SELECT (actual_cost - theoretical_cost) FROM cost_diff) * 0.30, 2) || '元。'
|
||||
'\n\n行动指向:\n1)上述5家门店实际成本率均远超理论值,需逐店排查采购单价与BOM标准价差异;\n2)盘点差——核查月末盘点与系统库存一致性,差异>3%需复盘;\n3)报损差——对比报损记录与行业基准,报损率>2%的门店需检查存储和加工流程;\n4)30天目标:回收正差异的30%,预计节约' || round((SELECT total_positive_variance FROM cost_diff) * 0.30, 2) || '元。'
|
||||
FROM cost_diff_stores)
|
||||
),
|
||||
json_build_object(
|
||||
'category', '标准店人工优化',
|
||||
'baseline', (SELECT round(total_wage / total_received * 100, 2) FROM labor),
|
||||
'baseline', (SELECT round(total_wage / nullif(total_received, 0) * 100, 2) FROM labor),
|
||||
'target_pct', 100,
|
||||
'opportunity', GREATEST(round((SELECT total_wage * (1 - 24.0 / nullif(total_wage / total_received * 100, 0)) FROM labor), 2), 0),
|
||||
'opportunity', (SELECT opportunity FROM labor),
|
||||
'confidence', '中',
|
||||
'owner', '运营/人力',
|
||||
'evidence', '工时、工资、餐段销售与服务质量',
|
||||
'detail', (SELECT
|
||||
'标准店人工合计' || (SELECT total_wage FROM labor) || '元,费率' || round((SELECT total_wage FROM labor) / nullif((SELECT total_received FROM labor), 0) * 100, 2) || '%,目标降至24%。\n\n' ||
|
||||
'人工费率TOP5门店(需重点督导):\n' ||
|
||||
string_agg(store_name || ':人工' || wage || '元(费率' || wage_rate || '%,实收' || received || '元)', ';\n') ||
|
||||
'\n\n行动指向:\n1)上述门店人工费率远超24%目标,需核查排班与实际打卡工时,识别冗余工时;\n2)低峰时段用小时工替代月薪员工,预计可降费率2-3个百分点;\n3)对费率>30%的门店启动人效专项督导,要求店长提交排班优化方案;\n4)30天目标:TOP5门店人工费率平均降低2个百分点。'
|
||||
'人工费率超24%的门店共' || (SELECT over_wage_stores FROM labor) || '家,超标门店人工合计' || (SELECT over_wage_total FROM labor) || '元,理论可优化' || (SELECT opportunity FROM labor) || '元。\n\n' ||
|
||||
'人工费率TOP5超标门店(需重点督导):\n' ||
|
||||
COALESCE(string_agg(store_name || ':人工' || wage || '元(费率' || wage_rate || '%,实收' || received || '元)', ';\n'), '无超标门店') ||
|
||||
'\n\n行动指向:\n1)上述门店人工费率远超24%目标,需核查排班与实际打卡工时,识别冗余工时;\n2)低峰时段用小时工替代月薪员工,预计可降费率2-3个百分点;\n3)对费率>30%的门店启动人效专项督导,要求店长提交排班优化方案;\n4)30天目标:将超标门店人工费率降至24%以内。'
|
||||
FROM labor_stores)
|
||||
),
|
||||
json_build_object(
|
||||
'category', '标准店能源优化',
|
||||
'baseline', (SELECT round(total_utility / total_received * 100, 2) FROM energy),
|
||||
'baseline', (SELECT round(total_utility / nullif(total_received, 0) * 100, 2) FROM energy),
|
||||
'target_pct', 100,
|
||||
'opportunity', GREATEST(round((SELECT total_utility * (1 - 5.0 / nullif(total_utility / total_received * 100, 0)) FROM energy), 2), 0),
|
||||
'opportunity', (SELECT opportunity FROM energy),
|
||||
'confidence', '中',
|
||||
'owner', '工程/门店',
|
||||
'evidence', '账单/抄表、面积、营业时长',
|
||||
'detail', (SELECT
|
||||
'标准店水电合计' || (SELECT total_utility FROM energy) || '元,费率' || round((SELECT total_utility FROM energy) / nullif((SELECT total_received FROM energy), 0) * 100, 2) || '%,目标降至5%。\n\n' ||
|
||||
'水电费率TOP5门店(需排查设备):\n' ||
|
||||
string_agg(store_name || ':水电' || utility || '元(费率' || utility_rate || '%,实收' || received || '元)', ';\n') ||
|
||||
'\n\n行动指向:\n1)上述门店水电费率远超5%目标,需排查是否存在设备老化、管道泄漏或空调空转;\n2)对比近3个月水电账单,波动>20%的门店需现场检查;\n3)缩短非营业时段的照明和空调,预计可降费率0.3-0.5个百分点;\n4)30天目标:TOP5门店水电费率平均降低0.5个百分点。'
|
||||
'水电费率超5%的门店共' || (SELECT over_utility_stores FROM energy) || '家,超标门店水电合计' || (SELECT over_utility_total FROM energy) || '元,理论可优化' || (SELECT opportunity FROM energy) || '元。\n\n' ||
|
||||
'水电费率TOP5超标门店(需排查设备):\n' ||
|
||||
COALESCE(string_agg(store_name || ':水电' || utility || '元(费率' || utility_rate || '%,实收' || received || '元)', ';\n'), '无超标门店') ||
|
||||
'\n\n行动指向:\n1)上述门店水电费率远超5%目标,需排查是否存在设备老化、管道泄漏或空调空转;\n2)对比近3个月水电账单,波动>20%的门店需现场检查;\n3)缩短非营业时段的照明和空调,预计可降费率0.3-0.5个百分点;\n4)30天目标:将超标门店水电费率降至5%以内。'
|
||||
FROM energy_stores)
|
||||
),
|
||||
json_build_object(
|
||||
@@ -1326,15 +1333,15 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
|
||||
'category', '平台佣金优化',
|
||||
'baseline', (SELECT round(total_commission / nullif(platform_received, 0) * 100, 2) FROM platform),
|
||||
'target_pct', 100,
|
||||
'opportunity', GREATEST(round((SELECT total_commission * (1 - 20.0 / nullif(total_commission / nullif(platform_received, 0) * 100, 0)) FROM platform), 2), 0),
|
||||
'opportunity', (SELECT opportunity FROM platform),
|
||||
'confidence', '中低',
|
||||
'owner', '外卖/采购',
|
||||
'evidence', '平台结算单与订单对账',
|
||||
'detail', (SELECT
|
||||
'平台佣金合计' || (SELECT total_commission FROM platform) || '元,佣金率' || round((SELECT total_commission FROM platform) / nullif((SELECT platform_received FROM platform), 0) * 100, 2) || '%。\n\n' ||
|
||||
'平台佣金合计' || (SELECT total_commission FROM platform) || '元,整体佣金率' || round((SELECT total_commission FROM platform) / nullif((SELECT platform_received FROM platform), 0) * 100, 2) || '%,佣金率超20%的门店' || (SELECT over_commission_stores FROM platform) || '家。\n\n' ||
|
||||
'佣金费率TOP5门店(需对账核查):\n' ||
|
||||
COALESCE(string_agg(store_code || ':佣金' || commission || '元(费率' || commission_rate || '%,实收' || received || '元)', ';\n'), '无数据') ||
|
||||
'\n\n行动指向:\n1)当前整体佣金率6.08%已低于20%目标,暂无大幅优化空间;\n2)上述门店佣金费率偏高,需逐月核对平台结算单与订单明细,识别多扣佣金;\n3)平台活动费与佣金应分离核算,避免活动费被计入佣金;\n4)提升自配送比例,降低对平台配送依赖;\n5)30天目标:完成TOP5门店平台结算单对账。'
|
||||
'\n\n行动指向:\n1)佣金率超20%的门店需逐月核对平台结算单与订单明细,识别多扣佣金;\n2)平台活动费与佣金应分离核算,避免活动费被计入佣金;\n3)提升自配送比例,降低对平台配送依赖;\n4)30天目标:完成TOP5门店平台结算单对账。'
|
||||
FROM platform_stores)
|
||||
),
|
||||
json_build_object(
|
||||
|
||||
Reference in New Issue
Block a user