← 返回
未分类 Key

报销整理发票

博维管理咨询差旅报销自动化:从邮箱拉发票、按项目归属、填报销表、按甲方/博维拆zip打印。触发词:处理差旅报销、整理本月发票、月度报销、报销单、差旅费、整理上月发票、帮我做报销。当用户提到"报销""差旅""发票"等关键词,且上下文涉及博维/MOWAYS/管理咨询项目时触发此技能。
博维管理咨询差旅报销自动化:从邮箱拉发票、按项目归属、填报销表、按甲方/博维拆zip打印。触发词:处理差旅报销、整理本月发票、月度报销、报销单、差旅费、整理上月发票、帮我做报销。当用户提到"报销""差旅""发票"等关键词,且上下文涉及博维/MOWAYS/管理咨询项目时触发此技能。
zzuliu
未分类 community v1.0.0 1 版本 66666.7 Key: 需要
★ 0
Stars
📥 2
下载
💾 0
安装
1
版本
#latest

概述

博维差旅报销 Skill

职责边界

本 skill 做这件事:把用户上月的 12306/南航/滴滴电子发票从邮箱抓下来,按项目规则自动归属,按甲方拆 sheet 填进项目费用报销表。涉及的项目规则(地点/抬头/报销方/项目组成员)在 _config/projects.yaml 里配置化。

本 skill 不做:开发票、改发票、提交报销到公司系统、计算个人所得税。

Skill 根目录

本 skill 的所有脚本和模板位于:

SKILL_ROOT = C:\Users\Lenovo\.workbuddy\skills\moways-expense-reimbursement
  • SKILL_ROOT/scripts/ — Python 脚本
  • SKILL_ROOT/templates/ — 配置模板

重要:在后续指令中,$SKILL_ROOT 均指此路径。

Python 运行环境

使用 WorkBuddy 管理的 Python 环境:

PYTHON=C:\Users\Lenovo\.workbuddy\binaries\python\envs\default\Scripts\python.exe

如果虚拟环境尚未创建,先执行:

C:\Users\Lenovo\.workbuddy\binaries\python\versions\3.13.12\python.exe -m venv C:\Users\Lenovo\.workbuddy\binaries\python\envs\default

依赖安装:

C:\Users\Lenovo\.workbuddy\binaries\python\envs\default\Scripts\pip.exe install pdfplumber pypdf openpyxl ruamel.yaml pyyaml reportlab

Windows 编码注意:所有脚本运行时需设置环境变量 PYTHONIOENCODING=utf-8,否则含 emoji 的 print 输出会触发 GBK 编码错误。

工作区判定

每次触发都先确定工作区路径(记为 $WS)。优先级:

  1. 若当前会话工作目录包含 _config/ 子目录,那就是 $WS
  2. 否则若用户消息里提到具体路径,用那个路径
  3. 否则让用户选择一个空文件夹或已有的报销文件夹作为工作区

定下 $WS 后,后续所有脚本调用都传 EXPENSE_WORKSPACE=$WS

首次运行检测(关键!)

判断依据$WS/_config/mailbox.yaml 是否存在。

如果不存在 → 进入 Setup Mode

如果存在 → 进入 Monthly Mode


Setup Mode — 首次运行引导

按顺序完成以下步骤,每一步用 AskUserQuestion 或清晰的提示收集信息。

Step 1:把模板拷进工作区

把 skill 自带的模板文件复制到工作区:

  • $SKILL_ROOT/templates/mailbox.yaml$WS/_config/mailbox.yaml
  • $SKILL_ROOT/templates/projects.yaml$WS/_config/projects.yaml

> Excel 模板说明:项目费用报销表由 fill_excel.py 在运行时自动生成(列结构、A1标题、合计行等),无需预置 .xlsx 模板文件。

同时 mkdir$WS/_收件箱/$WS/_backups/$WS/_报销包/

Step 2:引导填写邮箱

两轮交互:第一轮让用户输入所有邮箱地址,第二轮逐个邮箱问授权码。

第一轮:邮箱地址

AskUserQuestion 问(至少2个选项,用户通过"其他"自由输入邮箱地址):

> 你的邮箱是?(可以填写多个,用逗号分隔,请点击"其他"输入)

选项示例:我来填写 / 还没有邮箱(用户实际通过"其他"输入邮箱地址)

用户输入后,解析出所有邮箱地址,根据域名自动推断 IMAP 服务器和 needs_id_command:

  • 126/163 → imap.126.com / imap.163.com,needs_id_command: true
  • QQ → imap.qq.com,needs_id_command: false
  • 腾讯企业邮箱 → imap.exmail.qq.com,needs_id_command: false
  • 阿里云企业邮箱 → imap.mxhichina.com,needs_id_command: false
  • Gmail → imap.gmail.com,needs_id_command: false
  • 无法识别 → 追问 IMAP 服务器地址

第二轮:逐个邮箱问授权码

对第一轮收集到的每个邮箱,逐个用 AskUserQuestion 问授权码,一个问完再问下一个:

> 请提供 {邮箱地址} 的 IMAP 授权码(不是邮箱登录密码!需要在邮箱网页版「设置 → POP3/IMAP/SMTP」里单独生成)。

> ⚠️ 授权码会明文写入 mailbox.yaml;如果不放心,可以先把配置文件移到非云同步目录。

每个邮箱收集完授权码后,立即改写 $WS/_config/mailbox.yaml 里对应账号host / port / user / auth_code / needs_id_command 字段,并把 enabled: true。未配置的账号保留 enabled: false

Step 3:引导填写项目(v0.4 增加项目组成员)

问用户:

> 你手头正在跑的项目有哪些?我需要知道每个项目的:(1)项目简称;(2)甲方抬头全称;(3)主要出差地点关键词(如"厦门"、"华南新材料创新园");(4)大小交通各由谁报销(甲方 / 博维 / 个人);(5)项目组成员清单(用于自动填"使用人员"列)。

AskUserQuestion 逐个项目收集。每个项目追问:

  • 代号(英文大写,如 KERUIXIN
  • 项目简称(中文,如 科瑞信
  • 甲方抬头关键词(用于 OCR 命中)
  • 主要地点关键词(primary locations)
  • 次要地点关键词(secondary,可选)
  • 是否有「该区域打车一律归此项目」的硬规则(didi_catch_all,可选)
  • 大交通报销方(甲方 / 博维)
  • 小交通报销方(甲方 / 博维)
  • 项目组成员(逗号分隔,如 张三,张三,李四) ← v0.4 必填

收集完后 改写 $WS/_config/projects.yaml:删掉三个 EXAMPLE 项目,用用户提供的项目填进去,每个项目都要写 team_members 数组。attribution_rulesheader_validation 部分保留不动。

Step 4:确认配置

把写好的 mailbox.yamlprojects.yaml 关键内容向用户复述一遍(隐藏授权码只显示 **),让他确认无误。确认后提示:

> 配置已保存到 _config/ 目录。现在说一句「帮我处理上月报销」就能开始正常流程。


工作模式识别(关键!先分清跑哪种再执行)

博维差旅费报销有两种业务节奏,必须先识别用户意图:

模式 A:博维月度报销(每月 10 日前,刚性截止)

  • 触发词:「处理上月博维报销」「把上月要交财务的整理一下」「4 月博维那部分」
  • 范围:上一个自然月 + 所有项目 + side=博维 的票
  • 动作:拉邮件 → 解析 → 填项目费用报销表 → 打博维 zip
  • 脚本参数:package_for_reimbursement.py --filter-side 博维

模式 B:甲方项目即时报销(出差回来就做)

  • 触发词:「帮我完成科瑞信项目的甲方报销」「把上周宣城那趟的甲方发票整给琥崧」「出差回来了,整理甲方发票」
  • 范围:最近一段时间(默认 14 天)+ 指定项目 + side=甲方 的票
  • 动作:拉邮件 → 解析 → 填项目费用报销表 → 打甲方 zip
  • 脚本参数:package_for_reimbursement.py --filter-project CODE --filter-side 甲方

模式 C:月度全量(8 号前一把梭,可选)

  • 触发词:「帮我处理本月报销」「把上月所有发票都整理出来」
  • 范围:上一自然月全部票
  • 动作:五步全跑,生成所有 zip + 填 Excel
  • 脚本参数:不加过滤

判断优先级:提了具体项目名 → B;提了"博维"或"财务" → A;其他 → C。

遇到歧义用 AskUserQuestion 让用户二选一。


Monthly Mode — 月度/即时流程

Step 0:触发即询问『经办人 + 使用人员 + 特殊归属说明』(v0.4.3 关键步骤)

每次进入 Monthly Mode 都必须先用 AskUserQuestion 一次性收集这三项,再开始拉邮件:

问题1:本次报销的经办人是谁?
  - 默认:张三
  - 如果是其他同事经办,让用户填写

问题2:本项目本次报销涉及人员是谁?
  - 默认:从 projects.yaml 该项目的 team_members 字段把全员展开
  - 让用户确认或改写为本次实际涉及的人员
  - 如果是模式 B(单项目),只问该项目
  - 如果是模式 A/C(多项目),逐个项目问,或允许用户对所有项目用同一组成员

问题3(v0.4.3 新增):本次报销有什么特殊的归属说明吗?
  - 默认:无(让 process_reimbursement.py 自动按抬头/目的地/接驳规则判定)
  - 例 1:"4 月 12 日去厦门那趟全部归科瑞信"
  - 例 2:"郑州的几张票如果归不到,归幸福宜居"
  - 例 3:"5 月 7 日的南航全电发票是去厦门,归科瑞信"
  - 例 4:"这次没有特殊情况"

把答案存成三个变量备用:

  • $OPERATOR(一个字符串,如"张三")
  • $USERS_JSON(JSON 字符串,形如 {"KERUIXIN":"张三、李四"};所有项目共用一组人员可用 {"_default_":"张三、张三"}
  • $ATTRIBUTION_HINT(用户对归属的特殊说明,文本;为空表示完全靠自动归属)

注意

  • 如果 projects.yaml 还没有 team_members 字段(升级前老配置),立刻提示用户补充。
  • $ATTRIBUTION_HINT 用法见 Step 3.5。

Step 1:确定报销窗口

  • 模式 A / C:处理上一个自然月(1 号到月底)。用户若指定了具体月份,按用户说的。
  • 模式 B:默认拉最近 14 天。用户若说了具体出差日期,按用户说的。

计算出 $DATE_FROM$DATE_TO(ISO 格式,YYYY-MM-DD)。

Step 2:拉邮件

$PYTHON "$SKILL_ROOT/scripts/fetch_invoices.py" \
    --since $DATE_FROM --until $DATE_TO

需设置环境变量 EXPENSE_WORKSPACE=$WS

脚本产出:$WS/_收件箱/YYYY-MM/ 下的 PDF 附件和 manifest.csv

如果某个账号拉取失败(授权码过期等),提示用户去邮箱后台重新生成授权码,并询问是否要更新 mailbox.yaml

Step 3:解析 + 归属

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/process_reimbursement.py" \
    --trip-dir _收件箱/YYYY-MM \
    --date-from $DATE_FROM --date-to $DATE_TO

产出:

  • _收件箱/YYYY-MM/03_归属预览.csv
  • _收件箱/YYYY-MM/99_历史票.csv
  • _收件箱/YYYY-MM/99_待人工确认.csv

Step 3.4:补解析航空发票(v0.4.7 通用航空)

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/parse_airline.py" \
    --trip-dir _收件箱/YYYY-MM \
    --date-from $DATE_FROM --date-to $DATE_TO

脚本行为:

  • 同时扫描 _收件箱/YYYY-MM/机票/.pdf_收件箱/YYYY-MM/南航/.pdf
  • 按航班号前缀识别承运商
  • source 统一为 '机票',carrier 字段填承运商中文名
  • 按抬头命中 + 起终点地点匹配自动归属
  • 时间窗内的票 append 到 03_归属预览.csv

如果目录不存在或为空,脚本静默跳过。这一步总是要跑,跑了无害。

Step 3.42:补解析酒店发票(v0.4.5 新增)

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/parse_hotel.py" \
    --trip-dir _收件箱/YYYY-MM \
    --date-from $DATE_FROM --date-to $DATE_TO

脚本行为:

  • 扫描 _收件箱/YYYY-MM/酒店/*.pdf,配对发票 + 结账单
  • 提取:发票号、入住日、离店日、酒店名、买方抬头、价税合计
  • 按抬头命中 + 酒店名/地点匹配自动归属
  • source='酒店',fill_excel.py 看到会写"科目=住宿"

如果目录不存在或为空,脚本静默跳过。

Step 3.43:补解析餐饮发票(v0.4.6 新增)

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/parse_meal.py" \
    --trip-dir _收件箱/YYYY-MM \
    --date-from $DATE_FROM --date-to $DATE_TO

行为同 parse_hotel.py:扫 _收件箱/YYYY-MM/餐饮/*.pdf,按抬头/销方匹配自动归属,

source='餐饮',fill_excel.py 看到会自动写"科目=餐饮"。

如果目录不存在或为空,脚本静默跳过。

Step 3.5:检查待人工确认清单

合并所有解析器之后的 99_待人工确认.csv,按下面顺序处理(v0.4.3 增强):

  1. 先用 Step 0 收集的 $ATTRIBUTION_HINT 自动消化:把每条 ambiguous 票的『日期/起终点/金额/抬头』和 $ATTRIBUTION_HINT 一起读,能匹配到具体项目的就直接写 project_code 进 03_归属预览.csv,不再问用户。
  1. hint 消化不掉的剩余票:再展示给用户,逐条用 AskUserQuestion 让他选 project_code
  1. 处理完后重新写一次 03_归属预览.csv,把 hint/手动归属的票合进来。

Step 3.5:新项目识别与登记

在以下两种情况下,必须主动触发新项目登记子流程:

情况 1(事前):用户在触发消息里提到某个项目名,但 projects.yaml 里不存在。

情况 2(事后):解析完发票后,99_待人工确认.csv 里出现的陌生买方抬头不匹配任何已知项目的 invoice_headers

登记子流程(用 AskUserQuestion)

收集:

  1. 项目代号(英文大写)
  2. 项目简称(中文)
  3. 甲方全称
  4. 甲方抬头识别词(逗号分隔)
  5. 主要出差地点关键词(逗号分隔)
  6. 次要地点关键词(可选)
  7. 滴滴硬规则关键词(可选)
  8. 大交通报销方(甲方 / 博维)
  9. 小交通报销方(甲方 / 博维)
  10. 触发模式(per_trip / monthly / hybrid)
  11. 项目组成员(逗号分隔,如 张三,张三,李四) ← v0.4 必填
  12. 备注(可选)

追加到 projects.yaml

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/add_project.py" \
    --code YUANDA_GUANGRE \
    --name "远大光热" \
    --client-name "远大光热科技股份有限公司" \
    --invoice-headers "远大光热,远大" \
    --locations-primary "长沙,岳麓" \
    --big-transport 甲方 --small-transport 博维 \
    --trigger-mode hybrid \
    --team-members "张三,张三,李四" \
    --notes "..."

追加后必须重跑 Step 3。

Step 4:填项目费用报销表(v0.4 新参数)

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/fill_excel.py" \
    --preview _收件箱/YYYY-MM/03_归属预览.csv \
    --date-from $DATE_FROM \
    --date-to $DATE_TO \
    --operator "$OPERATOR" \
    --users-json "$USERS_JSON"

脚本行为(v0.4.9):

  • 默认 workbook 文件名带日期段 + 涉及项目
  • A1 标题写为 {项目名}项目费用报销表(YYYY年M月D日-YYYY年M月D日)
  • 列结构:A 日期 / B 星期 / C 类别 / D 金额 / E 经办人 / F 使用人员 / G 描述 / H 是否有发票
  • E 列经办人 = --operator 传入的值
  • F 列使用人员 = 优先用 --users-json 中该项目的值
  • 末尾自动加合计行(D 列 SUM 公式)
  • 写完后默认删除所有非本次创建的 sheets
  • 旧表自动备份到 _backups/

Step 5:按报销方打包发票

模式 A(博维月度):只出博维 zip

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/package_for_reimbursement.py" \
    --preview _收件箱/YYYY-MM/03_归属预览.csv \
    --month-tag "M月" \
    --filter-side 博维

模式 B(甲方即时):只出某项目甲方 zip

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/package_for_reimbursement.py" \
    --preview _收件箱/YYYY-MM/03_归属预览.csv \
    --month-tag "4月8-15日" \
    --filter-project KERUIXIN \
    --filter-side 甲方

模式 C(月度全量):不加过滤

EXPENSE_WORKSPACE=$WS $PYTHON "$SKILL_ROOT/scripts/package_for_reimbursement.py" \
    --preview _收件箱/YYYY-MM/03_归属预览.csv \
    --month-tag "M月"

输出在 $WS/_报销包/YYYY-MM/ 下。

Step 6:汇报结果

向用户报告:

  • 本次总共处理了多少张票、总金额多少
  • 每个项目分别多少张、多少钱、经办人是谁、使用人员是谁
  • 生成了哪几个报销包(甲方 zip 发谁、博维 zip 自己打印)
  • 待人工确认的票(如果有)
  • 项目费用报销表文件位置
  • 各个报销包 zip 的路径

业务规则备忘

归属优先级简表:

  1. 抬头优先——发票买方抬头命中项目 invoice_headers → 直接归属(置信度 1.0,最强证据)
  2. 地点 fallback——抬头缺失或开错时按起点/终点匹配
  3. 用户 hint——Step 0 收集的 $ATTRIBUTION_HINT 用于覆盖与兜底

禁忌操作

  • 不要把用户的 mailbox.yaml 内容贴到对话里
  • v0.4 起 fill_excel.py 默认会清掉非本次的 sheets——如果用户希望保留历史 sheet,加 --keep-other-sheets
  • 不要因为某张票"地点像 X 项目"就改归属——抬头明确写了 Y 项目就该归 Y

错误恢复

| 错误现象 | 处理 |

|---|---|

| BadZipFile | 从 _backups/ 拷最近一份回来 |

| IMAP 登录失败 | 告诉用户授权码可能过期或被吊销,引导他去邮箱后台重新生成;拿到新码后更新 mailbox.yamlauth_code 字段 |

| pdfplumber 提取空字符串 | 该 PDF 可能是扫描件/图片版,跳过并记录到 99_待人工确认.csv |

| 时间窗外票据 | 脚本已自动隔离到 99_历史票.csv |

| projects.yaml 没有 team_members | 提示用户补充,可手填本次使用人员后回写 yaml |

首次 hand-off 话术

用户在新工作区首次触发时,先给一段简短欢迎:

> 我发现这是你第一次用这个报销工具(_config/ 还没创建)。下面我会用几个问题收集你的邮箱、项目规则和项目组成员,大概需要 3-5 分钟。配置好以后,每个月只需要说一句「处理上月报销」,我会再问你三件事——本次经办人、本次涉及人员、有没有特殊归属说明,然后就能一键搞定。准备好了吗?

然后进入 Setup Mode 的 Step 1。

自动归属能力说明(用户问到时引用)

第一原则:抬头优先(最强证据)

发票买方抬头命中某项目的 client.invoice_headers → 归该项目,置信度 1.0。

第二原则:地点匹配(抬头不可用时的 fallback)

适用于高铁无抬头、抬头开错、滴滴行程单等场景。按优先级:

  1. 滴滴硬规则 didi_catch_all
  2. 跨项目段:大交通 A→B 起终点分别命中不同项目 → 归目的项目
  3. 滴滴接驳:起/终点含「站/机场」→ 找同日最近一张大交通票继承
  4. 基础地点匹配

第三原则:用户 hint(覆盖与兜底)

Step 0 收集的 $ATTRIBUTION_HINT,用于修正、兜底和用户主动覆盖。

版本历史

共 1 个版本

  • v1.0.0 Initial release 当前
    2026-06-11 16:45 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

去AI味的工具

user_6113fd9f
>此工具主要是去掉用AI生成的word文档和PPT文档的AI味,使其更贴近人类的口吻。
★ 0 📥 31

Excel排版美化工具

user_6113fd9f
按博维管理咨询公司(MOWAYS)Excel行文格式标准生成、改写和审阅Excel表格。 严格遵循博维Excel制表规范:标题用黑体加粗居中,正文中文仿宋/英文数字Times New Roman, 表头黑体居中,重点内容浅黄背景+暗红色字体
★ 1 📥 20

会议纪要

user_6113fd9f
会议纪要 + 工作简报的skill。把会议文字稿按模板整理为正式word版纪要并入库;月底按信函简报样式聚合多份切片为正式word版信函式简报;总结出 5 项优先级清单或 3类交付清单。覆盖销售/经营分析/项目管理/调研/参观/流程梳理/制
★ 0 📥 15