# 🧾 B01｜AI 帮你 10 分钟做完一张报销表 — 配套资产

> **系列**：AI 办公实战：从小白到达人
> **文章号**：B1（Track 1 表格处理 / Level 1 基础篇）
> **配套中文标题**：AI 帮你 10 分钟做完一张报销表
> **配套英文标题**：Build an Expense Sheet in 10 Minutes with AI

---

## 一、这是给谁的？解决什么问题？

写给**最怕 Excel、月底报销就崩溃**的办公小白：不用记函数、不用练手速。本目录里的脚本可以**一键生成一张「金额自动算、合计永不漏」的 Excel 报销表**——你只要把要报销的明细（日期、类别、金额、说明、发票号、报销人）填到示例 CSV 里，运行 Python 脚本，就能拿到一张已经设好货币格式、转成「超级表」、并自动启用「总计行」的成品 `.xlsx`。再也不用为「最后一行加总总是错」「样式丑得拿不出手」而加班。

如果你已经看完主教程《B1_报销表_双语.md》，这份资产就是它的**动手版**——脚本跑出来就是主教程里那张截图该有的样子。

---

## 二、使用方法

### 第一步：准备环境（一次性，5 分钟）

确保你装了 **Python 3.8+** 和 **openpyxl**：

```bash
# 检查 Python（Windows / macOS / Linux 都一样）
python --version

# 安装 openpyxl（生成 .xlsx 用的第三方库）
pip install openpyxl
```

> 💡 如果你在公司电脑装不上 Python，可以装 Anaconda 后用 Spyder / VS Code / PyCharm 运行本目录里的 `.py` 文件。

### 第二步：编辑示例数据

打开本目录里的 `示例数据_sample.csv`，按你的真实报销改一改：

```csv
序号,日期,类别,金额,说明,发票号,报销人
1,2026-08-02,机票,1280,北京-上海往返机票,INV-2026-0802-A01,张李
2,2026-08-02,酒店,640,差旅住宿一晚,INV-2026-0802-H01,张李
…
```

字段含义见下方「字段清单」一节。如果列顺序不变，**不需要改 Python 脚本**。

### 第三步：跑脚本生成 .xlsx

```bash
# 进入本目录
cd 配套资产/B01_报销表

# 最简单的用法（用默认的示例数据 + 默认输出文件名）
python 生成报销表_zh.py

# 指定输入输出
python 生成报销表_zh.py --input 我的8月报销.csv --output 报销表_2026年8月.xlsx
```

跑完后，本目录会多出一个 `报销表.xlsx`。双击用 Excel 打开：

- 表头已加粗、底色加深，整张表做了"超级表"格式化；
- 金额列设了货币格式（显示 `¥553.00` 而不是 `553`）；
- 末尾多了一行 `总计/Total`，对金额列**自动求和**，新加的行也会被算进去。

### 第四步：自己再加行 / 替换示例数据

- 直接在表里**新加一行**，填日期、类别、金额、说明……金额列如果有"单价 / 数量"，可以参考主教程里 `=D2*E2` 的写法自行扩展；
- 合计行**永远跟着表走**，不用你手动改；
- 觉得样式不喜欢？打开 `生成报销表_zh.py`，改 `TABLE_STYLE = "TableStyleMedium2"` 这一行（可选 `TableStyleLight1`~`TableStyleDark11`）。

---

## 三、文件清单

| 文件名 | 作用 |
|--------|------|
| `B01_报销表_中文README.md` | 本文件，中文使用说明 |
| `B01_ExpenseSheet_EnglishREADME.md` | 英文版使用说明（结构与本文件一致） |
| `生成报销表_zh.py` | 中文版 Python 脚本：注释以中文为主 + 英文辅助 |
| `生成ExpenseSheet_en.py` | 英文版 Python 脚本：注释以英文为主 + 中文辅助 |
| `示例数据_sample.csv` | 5 行示例报销数据（机票 / 酒店 / 打车 / 餐饮 / 办公用品） |

---

## 四、字段清单（`示例数据_sample.csv`）

| 序号 | 字段名 | 中文 | 类型 | 示例 | 必填 | 说明 |
|------|--------|------|------|------|------|------|
| 1 | 序号 | Serial | 整数 | `1` | ✅ | 从 1 开始递增，手动编 |
| 2 | 日期 | Date | 日期 | `2026-08-02` | ✅ | 哪天花的钱，建议 `YYYY-MM-DD` |
| 3 | 类别 | Category | 文本 | `机票` | ✅ | 高铁 / 酒店 / 打车 / 餐饮 / 办公用品 … |
| 4 | 金额 | Amount | 数字 | `1280` | ✅ | 单笔金额，单位元。脚本会设货币格式 |
| 5 | 说明 | Description | 文本 | `北京-上海往返机票` | ✅ | 一句话说清是干嘛的 |
| 6 | 发票号 | InvoiceNo | 文本 | `INV-2026-0802-A01` | ✅ | 与发票/小票上的号对应，方便核账 |
| 7 | 报销人 | Submitter | 文本 | `张李` | ✅ | 提交报销的人 |

> 想加公司要求的「部门」「工号」「项目编号」？直接在 CSV 里多加一列，并在脚本里的 `FIELDS` 列表追加一项（同步填列宽）。`AMOUNT_COL_INDEX` 也要跟着改到金额列的新位置。

---

## 五、实战代码（核心片段）

下面是 `生成报销表_zh.py` 里的"超级表 + 总计行"三件套——主教程里所有手工动作的代码版：

```python
# -*- coding: utf-8 -*-
import argparse, csv, sys
from pathlib import Path
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.worksheet.table import Table, TableStyleInfo

# 字段定义 / Field definitions
FIELDS = [
    ("序号",    8),
    ("日期",    14),
    ("类别",    12),
    ("金额",    12),  # 金额列 / Amount column
    ("说明",    36),
    ("发票号",  22),
    ("报销人",  12),
]
AMOUNT_COL_INDEX = 4  # "金额"在第 4 列 / "Amount" is the 4th column
TABLE_NAME = "ExpenseSheet"
TABLE_STYLE = "TableStyleMedium2"

# ---- 写入表头与数据 ----
wb = Workbook()
ws = wb.active
ws.title = "报销表"

for c_idx, (name, width) in enumerate(FIELDS, start=1):
    cell = ws.cell(row=1, column=c_idx, value=name)
    cell.font = Font(bold=True, color="FFFFFF")
    cell.fill = PatternFill("solid", fgColor="305496")
    cell.alignment = Alignment(horizontal="center", vertical="center")
    ws.column_dimensions[cell.column_letter].width = width

# 这里假设 rows = list[dict]，从 CSV 读出来
for r_offset, row in enumerate(rows, start=2):
    for c_idx, (name, _) in enumerate(FIELDS, start=1):
        ws.cell(row=r_offset, column=c_idx, value=row.get(name, ""))

# 金额列设为货币格式
for r in range(2, len(rows) + 2):
    ws.cell(row=r, column=AMOUNT_COL_INDEX).number_format = '"¥"#,##0.00'

# ---- 关键：转超级表 + 开总计行 ----
last_col_letter = ws.cell(row=1, column=len(FIELDS)).column_letter
ref = f"A1:{last_col_letter}{len(rows) + 1}"

tbl = Table(displayName=TABLE_NAME, ref=ref)
tbl.tableStyleInfo = TableStyleInfo(
    name=TABLE_STYLE, showRowStripes=True
)
tbl.totalsRowShown = True                 # ← 关键 1：开启总计行
tbl.totalsRowCount = 1                    # ← 关键 2：总计行就一行
tbl.totalsRowFunction = [                # ← 关键 3：每列配置 totals 函数
    "custom", "custom", "custom", "sum",  # "金额"列用 SUM
    "custom", "custom", "custom",
]
tbl.totalsRowLabel = ["", "", "", "总计/Total", "", "", ""]

ws.add_table(tbl)
wb.save("报销表.xlsx")
```

> 上面这一段几乎就是主教程「步骤 4：转超级表 + 勾总计行」的代码版——你**不用亲手按 `Ctrl+T`** 了，脚本一键搞定。

---

## 六、常见问题 / 避坑指南

1. **`ModuleNotFoundError: No module named 'openpyxl'`**
   说明没装 openpyxl。先跑一次 `pip install openpyxl`（如果公司电脑不让装，搜 `pip install openpyxl 国内镜像`，或考虑离线 `.whl`）。

2. **打开 .xlsx 发现"金额列没设货币"，显示成 `553`**
   检查脚本里 `format_money(...)` 是否被调用（默认会被调用）。极少数情况下 Excel 会因为区域设置显示成 `$553.00`，那是 Windows 区域问题，不影响计算。

3. **合计行"金额"列没自动求和，显示成空白**
   是 `totalsRowFunction` 配置问题：必须是 `"sum"` 而不是 `"SUM"` 或 `None`；列表长度必须**等于列数**，且 "金额" 那一项的位置必须正确。

4. **CSV 在 Excel 里打开中文乱码**
   本脚本用 `encoding="utf-8-sig"` 读 CSV，自动处理 BOM。如果你用 Excel 直接保存 CSV 时选了 `ANSI / GBK`，脚本读到中文会乱。建议保存 CSV 时选 `UTF-8` 或 `CSV UTF-8`。

5. **想加一列"部门/工号"却忘了改 `FIELDS`**
   只改 CSV 不改脚本，输出结果不会包含新列。两边都要改：CSV 加列头；脚本 `FIELDS` 追加一项（带列宽）；如果新列要参与"总计"，调整 `totalsRowFunction / totalsRowLabel` 长度同步。

6. **脚本跑成功但 Excel 弹窗"文件已损坏"**
   几乎都是 openpyxl 版本太旧：`pip install -U openpyxl` 升级到最新即可。

7. **想要 `=D2*E2`（单价 × 数量）那种公式版**
   本脚本默认字段不包含"单价/数量"，所以不写公式。如果你的表里需要，请把 `字段` 改成含 `单价 / 数量` 两列，并在 `write_rows` 时给"金额"列写 `=B2*C2` 这种公式（Excel 一打开就会自动算），并在 `format_money` 之前先填好公式。

---

## 七、下一步

- **B2 一键清洗脏数据**：同事发来的报销表常常合并单元格、多空行、日期格式乱。学完这篇，你就能用 AI 把脏数据一键整理干净，再丢进本目录的脚本生成标准报销表。
- **A3 提示词工程入门**：想让 AI 帮你「加一列部门」「改成含单价的版本」？提示词写"字段清单 + 示例 + 输出格式"三要素，AI 就不会自由发挥跑偏。

> 💬 **配套反馈**：如果脚本跑不通或 Excel 打开异常，请保留 `python 生成报销表_zh.py --help` 的输出，连同 Python 版本号（`python --version`）和 openpyxl 版本号（`pip show openpyxl`）一并发到系列反馈邮箱，主创团队会第一时间处理。

---

*本目录为《AI 办公实战：从小白到达人》系列 B1 文章的配套资产。所有脚本均可直接在装有 Python 3.8+ 与 openpyxl 的环境中运行；示例数据可直接编辑复用。*