#!/usr/bin/env python3 """Generate audit center test cases Excel for xll-miniapp.""" from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill, Border, Side from openpyxl.utils import get_column_letter from datetime import date import os DESKTOP = os.path.expanduser("~/Desktop") OUTPUT = os.path.join(DESKTOP, f"小羚羚-审批中心测试用例-{date.today().isoformat()}.xlsx") HEADERS = [ "用例编号", "测试模块", "流程类型", "测试场景", "前置条件", "操作步骤", "测试数据(Mock)", "预期结果", "优先级", "关联需求/标注", ] CASES = [] def add(module, flow, scene, pre, steps, data, expect, pri, ref): CASES.append([module, flow, scene, pre, steps, data, expect, pri, ref]) # ========== 提车应收款 ========== add( "提车应收款", "提车应收款", "列表-待办卡片展示", "打开审批中心;当前用户=张明辉;Tab=我的待办", "1. 进入 #page=audit\n2. 查看「我的待办」列表", "单据 TC-2026-0312;客户=上海迅杰物流有限公司", "卡片左上角标签「提车应收款」;加粗标题=客户全称;副标题=项目名称;元信息含发起人与实收金额;底部发起时间精确至分钟;显示「去审批」", "P0", "PRD01 §3.3/§5.3;page-audit;page-audit-pickup", ) add( "提车应收款", "提车应收款", "列表-实收金额口径(不含氢费预付款)", "同上", "1. 找到 TC-2026-0312 卡片\n2. 核对实收金额", "3台车明细:租金+服务费+保证金−减免,不含氢费预付款", "列表实收金额=¥218,510.00(与车辆明细动态汇总一致,不含氢费预付款 4100)", "P0", "PRD01 §5.3;ap-2 mock", ) add( "提车应收款", "提车应收款", "搜索-按客户全称", "审批中心列表", "1. 搜索框输入「上海迅杰」\n2. 观察筛选结果", "搜索关键词=上海迅杰", "仅展示加粗标题匹配的任务;TC-2026-0312 可见", "P1", "PRD01 §3.2;page-audit", ) add( "提车应收款", "提车应收款", "Chip筛选", "审批中心", "1. 点击流程 Chip「提车应收款」", "", "列表仅展示 flowType=提车应收款 的任务", "P1", "PRD01 §3.2", ) add( "提车应收款", "提车应收款", "详情-Hero实收金额", "待办 TC-2026-0312", "1. 点击卡片进入详情\n2. 查看 Hero 区", "TC-2026-0312", "Hero 实收款总额与列表卡片同一口径(不含氢费预付款);展示客户/合同/项目信息", "P0", "PRD01 §5.3;page-audit-pickup", ) add( "提车应收款", "提车应收款", "详情-车辆明细间距", "进入 TC-2026-0312 详情", "1. 滚动至车辆明细区块\n2. 检查标题与首卡间距", "", "区块标题行与首张车辆卡片之间保留约 12px 上间距", "P2", "PRD01 §5.2;page-audit-pickup", ) add( "提车应收款", "提车应收款", "详情-氢费预付款展示", "进入 TC-2026-0312 详情", "1. 查看氢费预付款区块", "", "详情可展示氢费预付款信息,但不计入列表/Hero 实收总额", "P1", "PRD01 §5.3", ) add( "提车应收款", "提车应收款", "详情-审批时间轴", "待办 TC-2026-0312", "1. 查看审批情况时间轴", "", "节点展示审批人、状态 Tag、时间;含评论气泡(如业务总负责人节点)", "P1", "PRD01 §4.4/§5.2", ) add( "提车应收款", "提车应收款", "待办-审批通过弹窗", "待办 TC-2026-0312 详情", "1. 点击底部「通过」\n2. 观察弹窗", "", "手机预览框内自底部滑出半屏表单;标题「审批通过」;含通知方式、附件、抄送人、审批意见;取消/确认", "P0", "tc-btn-approve 标注;PRD01 §3.5", ) add( "提车应收款", "提车应收款", "待办-驳回/终止/评论", "待办 TC-2026-0312 详情", "1. 分别点击驳回、终止、评论", "", "均为框内半屏 sheet;驳回/终止字段与通过一致(标题不同)", "P1", "PRD01 §3.5", ) add( "提车应收款", "提车应收款", "已办-只读查看", "Tab=我的已办;用户=财务-赵敏 或切换至 ap-12", "1. 打开 TC-2026-0228(嘉兴某某物流)详情", "ap-12 已通过", "无底部操作栏;可浏览 Hero、车辆明细、审批时间轴", "P1", "PRD01 §3.5", ) add( "提车应收款", "提车应收款", "抄送 Tab 可见性", "Tab=抄送我的;用户=张明辉", "1. 查看是否含 TC-2026-0312", "ccUsers 含张明辉", "抄送列表可见该任务;进入为查看态", "P2", "acFilterByTab cc", ) # ========== 还车应结款主流程 ========== add( "还车应结款主流程", "还车应结款", "场景A-主流程待办展示", "Tab=我的待办;用户=张明辉", "1. 点击 Chip「还车应结款」\n2. 找到粤B58888F 主流程卡片", "HC-2026-0418 / ap-14;四部门已通过且 mainFlowInitiated=true", "卡片标签「还车应结款」;加粗标题=粤B58888F;展示应退还总额;状态=审批中·财务审核", "P0", "PRD01 §4.1 场景A;page-audit-return", ) add( "还车应结款主流程", "还车应结款", "场景C-主流程不出现在列表", "Tab=我的待办/全部", "1. Chip「还车应结款」\n2. 搜索苏E12345F", "HC-2026-0610 / ap-26;四部门已通过但 mainFlowInitiated=false", "主流程「还车应结款」任务不在审批列表;四部门子流程可在已办/历史中出现", "P0", "PRD01 §4.1 场景C", ) add( "还车应结款主流程", "还车应结款", "Chip 匹配主流程+子流程", "审批中心", "1. 点击「还车应结款」Chip", "", "列表同时包含主流程与四部门子流程任务", "P1", "PRD01 §3.2", ) add( "还车应结款主流程", "还车应结款", "搜索-按车牌", "审批中心", "1. 搜索「粤B58888F」", "", "匹配主流程及同车牌的四部门子流程(标题均为车牌)", "P1", "PRD01 §3.2;列表标题规则(当前实现)", ) add( "还车应结款主流程", "还车应结款", "详情-Hero应退应补", "待办 ap-14 主流程详情", "1. 进入粤B58888F 主流程\n2. 查看 Hero", "deposit=5000;pendingSettle=927.50;refundTotal=4072.50", "Hero 展示应退还总额 ¥4,072.50;含保证金、待结算总额、车辆实际租金;有「结算明细」入口", "P0", "PRD01 §4.2", ) add( "还车应结款主流程", "还车应结款", "详情-四部门分组卡片", "主流程 ap-14 详情", "1. 查看四张部门费用卡片", "", "业务管理组/能源服务组/运维组/安全部分组展示;右上角均为「已提交」(绿色)", "P0", "hc-settle-group-status 标注;PRD01 §4.3", ) add( "还车应结款主流程", "还车应结款", "详情-运维组无忧包减免列", "主流程 ap-14 详情", "1. 展开运维组费用表", "", "表格含序号/费用项/金额/无忧包减免;应结算=金额−减免", "P1", "PRD01 §4.3", ) add( "还车应结款主流程", "还车应结款", "详情-安全部三行费项", "主流程 ap-14 详情", "1. 查看安全部分组", "", "固定三行:违章处理违约金、保险上浮、其他违规费用;含违章统计区", "P1", "PRD01 §4.3", ) add( "还车应结款主流程", "还车应结款", "详情-业务管理组不含安全费项", "主流程 ap-14 详情", "1. 查看业务管理组分组", "", "仅 ETC、停车费等;不含违章违约金、保险上浮", "P1", "PRD01 §4.3", ) add( "还车应结款主流程", "还车应结款", "详情-部门子流程进度", "主流程 ap-14 详情", "1. 查看 Hero 或进度区", "", "展示四部门子流程通过状态/进度", "P1", "PRD01 §4.2", ) add( "还车应结款主流程", "还车应结款", "待办-审批通过", "待办 ap-14 详情", "1. 点击「通过」并确认", "", "框内半屏审批通过表单;确认后 toast 提示(原型)", "P1", "tc-btn-approve", ) add( "还车应结款主流程", "还车应结款", "已完结主流程-已办", "Tab=我的已办", "1. 打开沪A03561F HC-2026-0520(ap-4)", "status=已通过", "可查看详情;Hero 展示历史应退信息;无操作栏", "P2", "ap-4 mock", ) # ========== 还车应结款四部门子流程 ========== depts = [ ("还车应结款-业务管理组", "业务管理组", "ETC/停车费", "ap-22", "浙F06901F", "HC-2026-0625-BS"), ("还车应结款-能源服务组", "能源服务组", "氢量差补缴/预付款退费", "ap-23", "浙F06901F", "HC-2026-0625-EN"), ("还车应结款-运维组", "运维组", "清洗/保养/维修/无忧包减免", "ap-24", "浙F06901F", "HC-2026-0625-OP"), ("还车应结款-安全部", "安全部", "违章违约金/保险上浮/其他违规", "ap-25", "浙F06901F", "HC-2026-0625-SF"), ] add( "还车应结款子流程", "四部门合计", "场景B-四部门均在待办", "Tab=我的待办;用户=张明辉", "1. Chip「还车应结款」\n2. 筛选浙F06901F", "场景B / ap-22~25 均为审批中", "同车牌四条子流程均出现在待办;主流程 HC-2026-0625 不在列表", "P0", "PRD01 §4.1 场景B;page-audit-return", ) for flow, dept, fee_hint, ap_id, plate, biz in depts: add( "还车应结款子流程", flow, f"列表-待办卡片展示({dept})", "Tab=我的待办;场景B", f"1. 找到 {flow} 卡片", f"{ap_id};车牌={plate};单据={biz}", f"左上角标签={flow};加粗标题={plate}(车牌重点);副标题客户+项目;元信息仅发起人(不展示汇总应退/应补)", "P0", "page-audit;批注:子流程列表车牌重点", ) add( "还车应结款子流程", flow, f"详情-Hero({dept})", f"待办 {ap_id}", "1. 进入详情\n2. 查看 Hero", plate, f"Hero 标题「{flow}」+ 车牌胶囊重点样式;不展示保证金/应退应补汇总金额", "P0", "PRD01 §4.2", ) add( "还车应结款子流程", flow, f"详情-本部门费用({dept})", f"待办 {ap_id}", "1. 查看费用明细区块", fee_hint, f"仅展示{dept}相关费项;底部本部门应结算总额", "P1", "PRD01 §4.3", ) add( "还车应结款子流程", flow, f"详情-状态标签审批中({dept})", f"待办 {ap_id};status=审批中", "1. 查看分组卡片右上角(若详情含)", "", "子流程详情按实际状态展示;列表卡片状态含当前节点审批人", "P1", "hc-settle-group-status", ) add( "还车应结款子流程", flow, f"待办-审批操作({dept})", f"待办 {ap_id}", "1. 点击通过/驳回/终止/评论", "", "底部操作栏可用;弹窗限定手机框内", "P1", "tc-btn-approve", ) add( "还车应结款子流程", "四部门合计", "场景A-子流程均已通过", "Tab=我的已办或全部", "1. 搜索粤B58888F\n2. 查看四部门子流程", "ap-15/18/19/20 status=已通过", "四条子流程状态已通过;不在待办(currentAssignee 为空)", "P1", "场景A", ) add( "还车应结款子流程", "四部门合计", "场景C-子流程已通过主流程未发起", "Tab=全部/已办", "1. 搜索苏E12345F", "ap-27~30 已通过;ap-26 待发起", "四部门子流程可见且已通过;主流程 ap-26 不出现在列表", "P0", "场景C", ) add( "还车应结款子流程", "还车应结款-业务管理组", "搜索-按流程类型名称", "审批中心", "1. 搜索「业务管理组」", "", "可匹配 flowType 含业务管理组的任务(含浙F06901F 等待办)", "P2", "搜索增强:标题+flowType", ) # ========== 审批中心通用 ========== COMMON = [ ( "审批中心通用", "全部", "四 Tab 切换", "进入审批中心", "1. 依次切换我发起的/待办/已办/抄送", "用户=张明辉", "各 Tab 列表数量与 mock 过滤规则一致", "P1", "PRD01 §3.1", ), ( "审批中心通用", "全部", "更多筛选-框内 sheet", "审批中心", "1. 点击搜索框右侧筛选按钮", "", "更多筛选从手机预览框底部滑出,不铺满浏览器", "P1", "PRD01 §3.6;page-audit", ), ( "审批中心通用", "全部", "更多筛选-发起人滚轮", "打开更多筛选 sheet", "1. 点击发起人「请选择 ›」\n2. 滚轮选择并确定", "", "框内滚轮选择器;首项「不限」", "P1", "PRD01 §3.6", ), ( "审批中心通用", "全部", "更多筛选-发起日期滚轮", "打开更多筛选 sheet", "1. 点击开始/结束日期\n2. 年/月/日滚轮选择", "", "日期滚轮叠在筛选 sheet 之上;高亮条不遮挡文字", "P1", "PRD01 §3.6", ), ( "审批中心通用", "全部", "更多类型 sheet", "审批中心", "1. 点击「更多类型」Chip", "", "框内 sheet 展示全部流程类型;选择后关闭并筛选", "P2", "page-audit", ), ( "审批中心通用", "全部", "Toast 框内反馈", "任意触发 toast 的操作", "1. 如筛选确定、审批确认", "", "提示出现在手机预览框顶部,非浏览器全局 message", "P2", "UI Review P2 修复", ), ] for c in COMMON: add(*c) # Renumber for i, row in enumerate(CASES, 1): module = row[0] prefix = {"提车应收款": "TC", "还车应结款主流程": "RS-M", "还车应结款子流程": "RS-S", "审批中心通用": "AC"}.get(module, "AC") row.insert(0, f"{prefix}-{i:03d}") wb = Workbook() meta = wb.active meta.title = "说明" meta["A1"] = "小羚羚小程序 · 审批中心测试用例" meta["A1"].font = Font(bold=True, size=14) meta["A2"] = f"生成日期:{date.today().isoformat()}" meta["A3"] = "范围:提车应收款、还车应结款主流程、还车应结款四部门子流程" meta["A4"] = "默认测试用户:张明辉(MOCK_USER)" meta["A5"] = "预览入口:http://localhost:51721/prototypes/xll-miniapp#page=audit" meta["A6"] = "数据来源:annotation-source.json + PRD01 V1.4 + index.tsx mock" meta.column_dimensions["A"].width = 80 for title, rows in [ ("提车应收款", [r for r in CASES if r[1] == "提车应收款"]), ("还车应结款-主流程", [r for r in CASES if r[1] == "还车应结款主流程"]), ("还车应结款-子流程", [r for r in CASES if r[1] == "还车应结款子流程"]), ("审批中心通用", [r for r in CASES if r[1] == "审批中心通用"]), ("全部用例", CASES), ]: ws = wb.create_sheet(title[:31]) header_fill = PatternFill("solid", fgColor="7AB929") header_font = Font(bold=True, color="FFFFFF") thin = Side(style="thin", color="E5E6EB") border = Border(left=thin, right=thin, top=thin, bottom=thin) ws.append(HEADERS) for col in range(1, len(HEADERS) + 1): cell = ws.cell(row=1, column=col) cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) cell.border = border for row in rows: ws.append(row) widths = [12, 14, 22, 24, 28, 36, 28, 40, 8, 24] for i, w in enumerate(widths, 1): ws.column_dimensions[get_column_letter(i)].width = w for r in range(2, ws.max_row + 1): for c in range(1, len(HEADERS) + 1): ws.cell(row=r, column=c).alignment = Alignment(vertical="top", wrap_text=True) ws.cell(row=r, column=c).border = border ws.freeze_panes = "A2" wb.save(OUTPUT) print(OUTPUT) print(f"cases={len(CASES)}")