初始发布: 21 个 skills (Claude Code / Codex / DSH)

This commit is contained in:
2026-08-14 01:51:44 +08:00
commit ea0857bedb
129 changed files with 35566 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
#!/usr/bin/env python3
"""计算 (店, 部门, 班次) 三维交叉营收,并按比例校正到权威 部门业绩 合计。
输入: 月度账务目录(包含订单明细 xlsx + 菜品库 xlsx + 营收分析 xlsx
输出: 标准输出打印交叉矩阵 + 12 名员工的 (部门业绩 / 班次业绩 / 部门×班次业绩) 三元组
用法:
python3 compute_cross.py <月度账务目录>
# 例: python3 compute_cross.py ~/Downloads/大梦5月账务处理
依赖: openpyxl
"""
import glob
import sys
from collections import defaultdict
try:
import openpyxl
except ImportError:
sys.exit("缺少依赖: python3 -m pip install openpyxl")
# ============================================================
# 部门归口规则(来自 分析方法.md,已根据实际菜品库一级分类核对)
# ============================================================
def categorize(primary, secondary, store):
p = (primary or "").lower().strip()
s = (secondary or "").strip()
if store == "西湖":
if p in ["小吃", "主食", "brunch", "零食"]:
return "厨房"
if p in ["咖啡", "甜品", "茶饮tea"]:
return "咖啡"
if p == "软饮":
return "咖啡" if s in ["可尔必思", "海盐荔枝"] else "调酒"
if p in ["精酿", "精酿 老菜单"]:
return "精酿"
if p in ["鸡尾酒", "纯饮"]:
return "调酒"
elif store == "滨江":
if p in ["肉肉肉", "小吃", "主食", "brunch"]:
return "厨房"
if p in ["咖啡", "甜品点心", "茶饮tea"]:
return "咖啡"
if p == "无咖无醇":
return "调酒" if s == "无醇鸡尾酒" else "咖啡"
if p in ["精酿", "瓶罐精酿", "精酿 老菜单(已废弃)"]:
return "精酿"
if p in ["鸡尾酒", "纯饮酒"]:
return "调酒"
return None
# ============================================================
# 计算每店 (餐段, 部门) 营收
# ============================================================
def load_store(base_dir, store_cn):
"""计算指定店 (餐段, 部门) → 顾客实付 (POS 菜品收入) 矩阵"""
print(f"\n===== {store_cn}店 =====", file=sys.stderr)
# 1) 订单 → 餐段
order_files = glob.glob(f"{base_dir}/大梦可能实验室({store_cn}店)_全渠道订单明细_*.xlsx")
if not order_files:
sys.exit(f"找不到 {store_cn}店 全渠道订单明细 xlsx")
wb1 = openpyxl.load_workbook(order_files[0], data_only=True)
ws1 = wb1.active
order_shift = {}
header_row = None
for i, r in enumerate(ws1.iter_rows(values_only=True), start=1):
if r and r[0] == "营业日期":
header_row = i
cols = list(r)
col_segment = cols.index("餐段")
col_order = cols.index("订单号")
continue
if header_row and i > header_row and r[col_order]:
order_shift[str(r[col_order])] = str(r[col_segment]) if r[col_segment] else None
print(f" Loaded {len(order_shift)} 订单", file=sys.stderr)
# 2) 菜品名 → 一级/二级分类
menu_files = glob.glob(f"{base_dir}/大梦_可能实验室_{store_cn}店_菜品库_*.xlsx")
if not menu_files:
sys.exit(f"找不到 {store_cn}店 菜品库 xlsx")
wb2 = openpyxl.load_workbook(menu_files[0], data_only=True)
ws2 = wb2["菜品"]
name_to_cat = {}
header_row2 = None
for i, r in enumerate(ws2.iter_rows(values_only=True), start=1):
if r and r[0] == "菜品编码(SPUID":
header_row2 = i
cols = list(r)
ci_name = cols.index("菜品名称")
ci_cat = cols.index("基础分类")
continue
if header_row2 and i > header_row2 and r[ci_name] and r[ci_cat]:
parts = str(r[ci_cat]).split("/")
primary = parts[0]
secondary = parts[1] if len(parts) > 1 else ""
name_to_cat[str(r[ci_name]).strip()] = (primary, secondary)
print(f" Loaded {len(name_to_cat)} 菜品", file=sys.stderr)
# 3) 菜品明细 → 聚合
detail_files = glob.glob(f"{base_dir}/大梦_可能实验室_{store_cn}店__店内订单明细*.xlsx")
if not detail_files:
sys.exit(f"找不到 {store_cn}店 店内订单明细 xlsx")
wb3 = openpyxl.load_workbook(detail_files[0], data_only=True)
ws3 = wb3["菜品明细"]
bucket = defaultdict(float) # (shift, dept) -> revenue
unmatched_count = 0
header_row3 = None
for i, r in enumerate(ws3.iter_rows(values_only=True), start=1):
if r and r[0] == "订单编号":
header_row3 = i
cols = list(r)
ci_order = cols.index("订单编号")
ci_revenue = cols.index("菜品收入(元)")
ci_name = cols.index("菜品名称")
continue
if header_row3 and i > header_row3:
order = str(r[ci_order]) if r[ci_order] else None
name = str(r[ci_name]).strip() if r[ci_name] else None
revenue = r[ci_revenue]
if not order or revenue is None or not name:
continue
shift = order_shift.get(order)
cat = name_to_cat.get(name)
if not shift or not cat:
unmatched_count += 1
continue
dept = categorize(cat[0], cat[1], store_cn)
if not dept:
unmatched_count += 1
continue
bucket[(shift, dept)] += float(revenue)
print(f" unmatched: {unmatched_count}", file=sys.stderr)
return bucket
# ============================================================
# 读取权威 部门业绩 / 班次业绩(从月度营收分析 xlsx)
# ============================================================
def read_official(base_dir):
rev_files = glob.glob(f"{base_dir}/大梦可能实验室_*月营收分析_西湖店vs滨江店.xlsx")
if not rev_files:
sys.exit("找不到月度营收分析 xlsx")
wb = openpyxl.load_workbook(rev_files[0], data_only=True)
dept = {"西湖店": {}, "滨江店": {}}
ws = wb["部门收入"]
in_section = False
for r in ws.iter_rows(values_only=True):
if r and r[0] and "部门 |" in str(r[0]) or (r and r[0] == "部门"):
in_section = True
continue
if in_section and r and r[0]:
name = str(r[0]).strip()
if name in ["厨房", "咖啡", "精酿", "调酒"]:
# cols: 部门, 滨江_POS, 滨江_团购, 滨江_合计, 西湖_POS, 西湖_团购, 西湖_合计
dept["滨江店"][name] = float(r[3]) if r[3] else 0
dept["西湖店"][name] = float(r[6]) if r[6] else 0
else:
if "部门收入小计" in name:
break
shift = {"西湖店": {}, "滨江店": {}}
ws = wb["班次营收"]
for r in ws.iter_rows(values_only=True):
if r and r[0] in ["西湖店", "滨江店"] and r[1] in ["白班", "晚班"]:
# cols: 门店, 餐段, 订单数, 订单金额, 顾客实付
shift[str(r[0])][str(r[1])] = float(r[4]) if r[4] else 0
return dept, shift
# ============================================================
# 12 员工 (店, 部门, 班次) 配置(行号约定)
# ============================================================
EMPLOYEES = [
(14, "蔡逸丰", "西湖店", "精酿", "晚班"),
(15, "何简", "西湖店", "厨房", "白班"),
(16, "宋群喜", "西湖店", "咖啡", "白班"),
(17, "胡舒", "西湖店", "调酒", "晚班"),
(18, "郭思儒", "西湖店", "咖啡", "白班"),
(19, "秦天", "西湖店", "厨房", "晚班"),
(20, "李想", "滨江店", "调酒", "晚班"),
(21, "王瑛胤", "滨江店", "咖啡", "白班"),
(22, "刘润祥", "滨江店", "厨房", "晚班"), # 不算班次
(23, "朱秋风", "滨江店", "精酿", "晚班"),
(24, "叶磊", "滨江店", "厨房", "白班"), # 不算班次
(25, "尹志艳", "滨江店", "厨房", "中班"), # 不算班次/中班无班次业绩
]
# 滨江厨房团队:只算 部门业绩,班次 + 交叉 都为 0
BINJIANG_KITCHEN = {"刘润祥", "叶磊", "尹志艳"}
def main():
if len(sys.argv) < 2:
sys.exit("用法: python3 compute_cross.py <月度账务目录>")
base = sys.argv[1].rstrip("/")
# 计算 raw cross-tab
raw_xihu = load_store(base, "西湖")
raw_binjiang = load_store(base, "滨江")
raw = {"西湖店": raw_xihu, "滨江店": raw_binjiang}
# 读权威 部门 / 班次
dept_official, shift_official = read_official(base)
# 按部门比例校正:scale factor = 权威总 / raw 部门小计
scaled = {}
for store in ["西湖店", "滨江店"]:
for d in ["厨房", "咖啡", "精酿", "调酒"]:
raw_dept_sum = sum(raw[store].get((sh, d), 0) for sh in ["白班", "晚班"])
if raw_dept_sum > 0 and d in dept_official[store]:
factor = dept_official[store][d] / raw_dept_sum
for sh in ["白班", "晚班"]:
scaled[(store, d, sh)] = round(raw[store].get((sh, d), 0) * factor)
# 打印交叉表
print("\n=== 校正后 部门×班次(用作 V2 col 21 部门x班次业绩)===")
for store in ["西湖店", "滨江店"]:
print(f"\n{store}:")
print(f" {'部门':<6}{'白班':>10}{'晚班':>10}")
for d in ["厨房", "咖啡", "精酿", "调酒"]:
wb = scaled.get((store, d, "白班"), 0)
nb = scaled.get((store, d, "晚班"), 0)
print(f" {d:<6}{wb:>10}{nb:>10}")
# 输出 12 员工三元组
print("\n=== 12 员工 (部门业绩 / 班次业绩 / 部门×班次业绩) ===")
print(f"{'':>3} {'姓名':<6} {'':<5} {'部门':<5} {'班次':<5} {'部门业绩':>10} {'班次业绩':>10} {'部门×班次':>10}")
for row, name, store, dept, shift in EMPLOYEES:
is_dept = dept in dept_official[store]
dr = dept_official[store].get(dept, 0)
sr = shift_official[store].get(shift, 0)
cr = scaled.get((store, dept, shift), 0)
# 特殊规则:滨江厨房团队不算班次
if name in BINJIANG_KITCHEN:
sr = 0
cr = 0
# 中班无班次业绩
if shift == "中班":
sr = 0
cr = 0
# 前厅、空部门
if not is_dept:
dr = 0
cr = 0
print(f"{row:>3} {name:<6} {store:<5} {dept:<5} {shift:<5} {dr:>10.2f} {sr:>10.2f} {cr:>10}")
if __name__ == "__main__":
main()