初始发布: 21 个 skills (Claude Code / Codex / DSH)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""从「菜品销售明细」导出反推菜品库(6月起使用)
|
||||
|
||||
用法: python3 make_menu_lib.py <月度账务目录>
|
||||
|
||||
背景:build_analysis.py 依赖菜品库做部门归类,但本地菜品库是旧月份快照,
|
||||
当月新上的 SKU 不在库里会掉进关键词兜底、容易归错。
|
||||
菜品销售明细自带「菜品大类/菜品小类」= POS 系统里的真实归类,用它生成菜品库覆盖率 100%。
|
||||
|
||||
输出: 大梦_可能实验室_{店}店_菜品库_自销售明细生成_{YYYYMM}.xlsx
|
||||
(文件名符合 build_analysis.py 的 glob 模式,会被自动读到)
|
||||
"""
|
||||
import openpyxl, glob, sys, os, re
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
BASE = (sys.argv[1] if len(sys.argv) > 1 else ".").rstrip("/")
|
||||
|
||||
def build(store):
|
||||
fs = glob.glob(f"{BASE}/*{store}店__菜品销售明细*.xlsx")
|
||||
if not fs:
|
||||
print(f" [{store}] 未找到菜品销售明细,跳过")
|
||||
return
|
||||
wb = openpyxl.load_workbook(fs[0], data_only=True)
|
||||
ws = wb["已销售"]
|
||||
|
||||
# 表头在第 3 行
|
||||
hr = None
|
||||
for r in range(1, 8):
|
||||
row = [ws.cell(r, c).value for c in range(1, ws.max_column + 1)]
|
||||
if any(v and "菜品大类" in str(v) for v in row):
|
||||
hr = r
|
||||
H = [str(v).strip() if v else "" for v in row]
|
||||
break
|
||||
if hr is None:
|
||||
print(f" [{store}] 找不到含「菜品大类」的表头行,跳过")
|
||||
return
|
||||
|
||||
ci_nm = H.index("菜品名称") + 1
|
||||
ci_d = H.index("菜品大类") + 1
|
||||
ci_x = H.index("菜品小类") + 1
|
||||
|
||||
# 同名多类时取众数(如"深烘拿铁"既有 咖啡/经典 也有 经典咖啡)
|
||||
name2cats = defaultdict(Counter)
|
||||
ym = None
|
||||
ci_date = H.index("营业日期") + 1 if "营业日期" in H else None
|
||||
for r in range(hr + 1, ws.max_row + 1):
|
||||
nm = ws.cell(r, ci_nm).value
|
||||
if not nm:
|
||||
continue
|
||||
d = ws.cell(r, ci_d).value
|
||||
x = ws.cell(r, ci_x).value
|
||||
if d:
|
||||
name2cats[str(nm).strip()][(str(d).strip(), str(x).strip() if x else "")] += 1
|
||||
if ym is None and ci_date:
|
||||
dv = str(ws.cell(r, ci_date).value or "")
|
||||
m = re.search(r"(\d{4})[/-](\d{2})", dv)
|
||||
if m:
|
||||
ym = m.group(1) + m.group(2)
|
||||
wb.close()
|
||||
|
||||
conflicts = {n: c for n, c in name2cats.items() if len(c) > 1}
|
||||
|
||||
out = openpyxl.Workbook()
|
||||
ws2 = out.active
|
||||
ws2.title = "菜品"
|
||||
ws2.append(["菜品编码(SPUID)", "菜品名称", "基础分类"])
|
||||
for n, c in sorted(name2cats.items()):
|
||||
(d, x), _ = c.most_common(1)[0]
|
||||
ws2.append(["", n, f"{d}/{x}" if x else d])
|
||||
|
||||
fn = f"{BASE}/大梦_可能实验室_{store}店_菜品库_自销售明细生成_{ym or 'latest'}.xlsx"
|
||||
out.save(fn)
|
||||
|
||||
cats = Counter()
|
||||
for n, c in name2cats.items():
|
||||
cats[c.most_common(1)[0][0][0]] += 1
|
||||
print(f" [{store}] {len(name2cats)} 个 SKU → {os.path.basename(fn)}")
|
||||
print(f" 大类分布: {dict(cats.most_common(8))}")
|
||||
if conflicts:
|
||||
print(f" 同名多类 {len(conflicts)} 个(已取众数,正常现象): "
|
||||
+ ", ".join(list(conflicts)[:4]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"从菜品销售明细生成菜品库 — {BASE}")
|
||||
for st in ["滨江", "西湖"]:
|
||||
build(st)
|
||||
print("完成。接着跑: python3 build_analysis.py <目录> <YYYY-MM>")
|
||||
Reference in New Issue
Block a user