_generate_mcp_tech_kjb_detail_excel.py 6.39 KB
# -*- coding: utf-8 -*-
"""从 MCP 明细 JSON 生成科技部老师业绩明细 Excel。

JSON 须由「对齐日报落月」的 SQL 生成,见同目录 mcp_tech_kjb_detail_aligned_YYYYMM.sql。

默认(无参数):沿用 2026-03 输入/输出文件名,兼容旧用法。
示例(4 月):
  python3 ExportFiles/_generate_mcp_tech_kjb_detail_excel.py \\
    --json ExportFiles/_mcp_tech_kjb_detail_202604.json \\
    --out ExportFiles/2026年4月_科技部老师业绩明细_MCP.xlsx \\
    --month 2026-04 --sql-ref mcp_tech_kjb_detail_aligned_202604.sql
"""
import argparse
import json
from pathlib import Path

from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter

ROOT = Path(__file__).resolve().parent

HEADERS = [
    ("bizType", "业务类型"),
    ("storeName", "门店名称"),
    ("teacherId", "科技部老师ID"),
    ("teacherName", "科技部老师姓名"),
    ("teacherAccount", "科技部老师账号"),
    ("itemName", "品项名称"),
    ("itemId", "品项ID"),
    ("itemCategory", "品项分类"),
    ("beautyType", "科美类型"),
    ("amount", "业绩金额"),
    ("bizTime", "业绩时间"),
    ("refBillingId", "关联单号"),
    ("billingItemLineId", "明细行ID"),
    ("laborCost", "手工费"),
    ("billingDate", "单据日期"),
]


def parse_args():
    p = argparse.ArgumentParser(description="从 MCP JSON 生成科技部老师业绩明细 Excel")
    p.add_argument(
        "--json",
        type=Path,
        default=ROOT / "_mcp_tech_kjb_detail_202603.json",
        dest="data_json",
        help="合并后的 MCP 明细 JSON(数组)",
    )
    p.add_argument(
        "--out",
        type=Path,
        default=ROOT / "2026年3月_科技部老师业绩明细_MCP.xlsx",
        help="输出 xlsx 路径",
    )
    p.add_argument(
        "--month",
        default="2026-03",
        help="统计月(写入底部说明,如 2026-04)",
    )
    p.add_argument(
        "--sql-ref",
        default="mcp_tech_kjb_detail_aligned_202603.sql",
        help="底部说明中引用的 SQL 文件名",
    )
    return p.parse_args()


def main():
    args = parse_args()
    data_json = args.data_json
    out_xlsx = args.out
    month = args.month
    sql_ref = args.sql_ref

    with open(data_json, "r", encoding="utf-8") as f:
        rows = json.load(f)

    wb = Workbook()
    ws = wb.active
    ws.title = "科技部老师业绩明细"

    thin = Side(style="thin", color="FF000000")
    grid = Border(left=thin, right=thin, top=thin, bottom=thin)
    header_fill = PatternFill("solid", fgColor="FF4472C4")
    header_font = Font(bold=True, color="FFFFFFFF", size=10)
    header_align = Alignment(horizontal="center", vertical="center", wrap_text=True)
    total_fill = PatternFill("solid", fgColor="FFF2F2F2")
    total_font = Font(bold=True)

    for col, (_, title) in enumerate(HEADERS, 1):
        c = ws.cell(row=1, column=col, value=title)
        c.font = header_font
        c.fill = header_fill
        c.alignment = header_align
        c.border = grid

    keys = [k for k, _ in HEADERS]
    amount_col = keys.index("amount") + 1
    labor_col = keys.index("laborCost") + 1

    for r_i, r in enumerate(rows, start=2):
        for col, key in enumerate(keys, start=1):
            val = r.get(key)
            if val is None:
                val = ""
            elif key in ("amount", "laborCost"):
                val = float(val) if val != "" else 0
            c = ws.cell(row=r_i, column=col, value=val)
            c.border = grid
            if key in ("amount", "laborCost"):
                c.number_format = "#,##0.00"
                c.alignment = Alignment(horizontal="right", vertical="center")
            elif key in ("bizTime", "billingDate"):
                c.alignment = Alignment(horizontal="left", vertical="center")
            else:
                c.alignment = Alignment(horizontal="left", vertical="center", wrap_text=False)

    last_data = 1 + len(rows)
    total_row = last_data + 1
    amt_letter = get_column_letter(amount_col)
    lab_letter = get_column_letter(labor_col)

    ws.cell(row=total_row, column=1, value="合计").font = total_font
    ws.cell(row=total_row, column=1).fill = total_fill
    ws.cell(row=total_row, column=1).border = grid
    for col in range(2, len(HEADERS) + 1):
        ws.cell(row=total_row, column=col).fill = total_fill
        ws.cell(row=total_row, column=col).border = grid

    ws.cell(
        row=total_row,
        column=amount_col,
        value=f"=SUM({amt_letter}2:{amt_letter}{last_data})",
    )
    ws.cell(row=total_row, column=amount_col).number_format = "#,##0.00"
    ws.cell(row=total_row, column=amount_col).font = total_font
    ws.cell(row=total_row, column=amount_col).alignment = Alignment(horizontal="right")

    ws.cell(
        row=total_row,
        column=labor_col,
        value=f"=SUM({lab_letter}2:{lab_letter}{last_data})",
    )
    ws.cell(row=total_row, column=labor_col).number_format = "#,##0.00"
    ws.cell(row=total_row, column=labor_col).font = total_font
    ws.cell(row=total_row, column=labor_col).alignment = Alignment(horizontal="right")

    ws.freeze_panes = "A2"
    ws.auto_filter.ref = f"A1:{get_column_letter(len(HEADERS))}{last_data}"

    widths = [8, 16, 18, 12, 14, 22, 20, 8, 10, 12, 20, 20, 20, 10, 20]
    for i, w in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = w

    note_row = total_row + 2
    note = (
        "数据来源:MCP 只读查询,落月与日报科技部老师统计一致。"
        f"开单=lq_kd_kjbsyj 关联 lq_kd_kdjlb,科美、有效,按单据 kdrq 落在 {month};"
        "业绩时间列 bizTime 仍为子表 yjsj,单据日期 billingDate 为 kdrq。"
        f"退卡=lq_hytk_kjbsyj 关联 lq_hytk_hytk,科美、有效,按主表 tksj 落在 {month};"
        "bizTime 为子表 tksj,billingDate 为主表 tksj。"
        "关联单号:开单 glkdbh,退卡 gltkbh;明细行:开单 F_kdpxid,退卡 F_CardReturn。"
        f"取数 SQL 模板:{sql_ref}"
    )
    ws.merge_cells(start_row=note_row, start_column=1, end_row=note_row + 1, end_column=len(HEADERS))
    nc = ws.cell(row=note_row, column=1, value=note)
    nc.font = Font(size=9, color="FF666666")
    nc.alignment = Alignment(wrap_text=True, vertical="top")

    wb.save(out_xlsx)
    print("Saved:", out_xlsx, "rows:", len(rows))


if __name__ == "__main__":
    main()