基于客户真实 ledger(1354 条)、六个 SKILL 全文与四张飞书表的实际字段核对得出。含数据迁移、能力补齐、表结构、仪表盘切换与实施步骤。
迁移在身份层面是无损的。新表主键 Invoice ID = SHA256("outlook:"+attachmentSha256)[:24] 可从旧记录确定性推算,1354 条里 1350 条有 attachmentSha256。迁移脚本可重复执行、不会产生重复行、与新表已有的 187 行测试数据天然去重。
① 催办邮件完全没有实现。六个 SKILL 里没有任何发信能力。插件一停,当天起没有任何催办。
② 审批看板的核心字段无处产出。当前审批人 / 当前节点 / 已等待 / 是否超时 / 催办次数 / 风险等级,SKILL 侧全无。这些是本地推断出来的,不是 ApprovalMax 返回的。
③ ApprovalMax 授权仍寄生在插件里。两个 SKILL 直接 require 插件的 config.js 与 approvalmax.js,插件一删立刻失效。
此外 3eyes-commercial-done-reconcile(商业票 QS 回收)整体建立在插件 CLI 之上,调用 5 个插件命令,需重写为独立 SKILL。
新 Invoice Tracking 表没有任何公式字段,而旧表有 24 个公式字段在驱动仪表盘。图表数据源无法直接重指 —— 必须先把公式补上。
客户 ledger 共 1354 条,时间跨度 2026-06-18 至 2026-08-21。
| 类 | 判定依据 | 条数 | 处置 | 风险 |
|---|---|---|---|---|
| C 已进 ApprovalMax | 有 requestId | 528 | 必须迁移 | 最高 |
| B 已处理未推送 | 其余已提取 | 560 | 迁事实不迁结论 | 中 |
| A 仅下载未处理 | outlook_downloaded_to_folder | 221 | 不迁,重跑 | 低 |
| S 拆分母文件 | multi_invoice_split_parent | 41 | 父子一起迁 | 中 |
| X 无 SHA | 缺 attachmentSha256 | 4 | 人工建行 | 需人工 |
approvalmax_submitted 只有 491 条,但带 requestId 的有 528 条。多出的 37 条是 approvalmax_duplicate_remote / superseded 中已认领远端 bill 的记录,同样不能重推。按状态筛会漏掉它们。
按主要标记拆开后,337 条本就是不可推送的文档类型,分类是正确的:
| 标记 | 条数 | 性质 |
|---|---|---|
| Non Invoice | 175 | 正确分类,非积压 |
| Statement | 118 | |
| Quote | 14 | |
| Credit Note | 12 | |
| Payment Claim / Confirmation / Schedule | 17 | |
| Purchase Order | 1 | |
| Project Not Matched · No PO · PO Missing · Site Address Missing 等 | ~77 | 真正待人工处理 |
未识别 467 · Commercial 344 · Residential 340 · Hotel 121 · Office 82。未识别占 34%,其中大部分是 A 类(尚未提取)与非发票文档。
把 wiki 文档 14 章 + 附录逐条与六个 SKILL(118KB 文档 + 220KB 代码)比对:
| 规则章节 | 覆盖 | 承载 SKILL | 缺口 / 需调整 |
|---|---|---|---|
| 1 收件与导入 | 完整 | outlook-intake-filter | 仅 Outlook;旧 manual_folder 来源无对应 |
| 2 多发票拆分 | 完整 | outlook-intake-filter | — |
| 3 字段提取 | 完整 | outlook-intake-filter | — |
| 3.4 业务指纹查重 | 需确认 | intake + enrichment | 「指纹相同且文档类别相同」这条未在文档中明写 |
| 4 文档类型判定 | 完整 | outlook-intake-filter | 含 Watercare 双标题例外 ✓ |
| 5 地块识别 | 完整 | intake + enrichment | 五个水电网账号规则齐全 ✓ |
| 6 PO 匹配合并 | 完整 | project-po-enrichment | — |
| 7 归档目录与命名 | 完整 | enrichment (archive-policy) | — |
| 8 异常票月份分层 | 完整 | intake (business-rules) | urgentCarryBack 已实现 ✓ |
| 8.6 异常票 DONE 回收 | 完整 | exception-invoice-intake | 含 [please cancel] 覆盖 DONE ✓ |
| 9 QS 审核与回收 | 依赖插件 | commercial-done-reconcile | 整体调用 5 个插件 CLI 命令,需重写 |
| 10 审批链与阈值 | 完整 | enrichment (approval-chain) | — |
| 11 推送 ApprovalMax | 部分依赖 | approvalmax-dispatch | 授权借用插件 |
| 12 参考数据 | 完整 | dispatch (live-reference) | 改为实时拉取,不再依赖本地镜像 |
| 13.1 看板字段 | 缺失 | — | 当前审批人/节点/等待/超时/风险全无 |
| 13.2 红标类别 | 完整 | intake | 七类一致 ✓ |
| 13.3 催办 | 缺失 | — | 无任何发信能力 |
| 13.4 审批意见分类 | 部分 | comment-recovery | V1 只做 code 更正;hold / info-missing 仅记录不执行,暂停催办未实现 |
| 13.5 Cross-month | 缺失 | — | 建议改为公式字段 + 视图 |
| 14 实现注意事项 | 完整 | 各 SKILL 分散体现 | — |
| 附录 A cost code | 完整 | dispatch (history-code) | 覆盖住宅/酒店/免 PO 商业 ✓ |
invoice-approvalmax-dispatch/scripts/approvalmax-live-reference.js:3
require(".../plugins/3eyes-invoice/src/config.js") ← 配置解析
require(".../plugins/3eyes-invoice/src/approvalmax.js") ← loadApprovalMaxToken / approvalMaxRequest
invoice-approvalmax-comment-recovery/SKILL.md
"Read ApprovalMax only through the existing plugin/OpenClaw ApprovalMax tools"
3eyes-commercial-done-reconcile/*
bin/3eyes-invoice.js commercial-done-scan | commercial-done-apply
commercial-done-note | approver-history | approver-names| 能力 | 优先级 | 形态建议 | 要点 |
|---|---|---|---|
| ApprovalMax 授权与请求层 | P0 | 公共脚本,六个 SKILL 共用 | 搬 loadApprovalMaxToken / refreshApprovalMaxToken / tokenStatus / approvalMaxRequest + 配置解析;token 文件一并迁移 |
| 催办 SKILL | P0 | 新建 invoice-approval-reminder | PM 按人汇总每 3 天;QS/Manager 超期次日一次;Graph sendMail;暂停规则;状态去重 |
| 审批看板同步 SKILL | P0 | 新建 invoice-approval-dashboard | 轮询状态 + 本地推断当前审批人/节点 + 写 Approval Dashboard 表 |
| 商业票 QS 回收重写 | P0 | 重写现有 SKILL | 文件定位四层、done 门槛、code/PM/QS 取值、历史 bill 回溯,全部脱插件 |
| 审批意见暂停催办 | P1 | 扩 comment-recovery | hold_payment / hold_no_reject / info_missing 三类需真正影响催办 |
| Cross-month 视图 | P1 | 公式字段 + 视图 | 不需要独立表 |
| 文件夹收件 | P2 | 扩 intake 或不做 | 取决于是否还保留手工投递入口 |
ApprovalMax 用 OAuth2 refresh token 轮换 —— 每次刷新作废旧 token。任何并发刷新都会打废授权,需要人重新走一遍完整授权流程,期间整条推送链停摆。
所以:所有取 token 的路径必须走同一个入口函数,批量提交必须串行,不加并发 worker。迁移时 customer-3eyes-token.json(含 refresh_token)必须原样带过去,不要重新授权。
四张表同在 base DAbBb7uJnakKiMstSE5jvUM3p0d,无跨库问题。
| 表 | 字段 | 公式 | 去向 |
|---|---|---|---|
tblmCudpsvnmHVil 旧 Invoice Processing | 63 | 16 | 退役 |
tbl68CNMwGC57HJD 旧 Approval Dashboard | 46 | 8 | 退役 |
tblNLHNj5N5sK0iD 新 Invoice Tracking | 54 | 0 | 主表,需补公式 |
| 新建 Approval Dashboard | ~30 | 8 | 需新建 |
可直接照搬(只依赖 Received At,新表已有):
| 字段 | 作用 |
|---|---|
Dashboard Month | TEXT([Received At],"YYYY-MM") |
Dashboard Current Month | 自然月 |
Dashboard Recent 6 Months | 近 6 个月 |
Dashboard Current Live Cycle — Received | 26–25 周期,当前 |
Dashboard Previous Closed Cycle — Received | 26–25 周期,上一期 |
必须重写(旧公式依赖新表没有的字段):
| 字段 | 旧依赖 | 新写法 |
|---|---|---|
Is Invoice | Current State · Review Flags · Missing / Blockers 的 14 段字符串匹配 | 改读 Category,一行即可:IF(OR([Category]="Invoice",[Category]="Payment Claim"),"true","false") |
Stage | Current State · Is Invoice · Review Flags | 改读 Workflow Status + ApprovalMax Status,映射见下 |
| Stage 值 | 新判定条件 |
|---|---|
| 已提交 | ApprovalMax Status ∈ Submitted / Approved |
| 待自动提交 | Workflow Status = ApprovalMax Ready |
| 重复或作废 | Workflow Status = Duplicate |
| 多发票文件 | Category = Split Source |
| 非发票 | Is Invoice = false |
| 非 3EYES 发票 | Review Label 含 Not 3EYES Construction |
| 等 QS | Workflow Status = Project Matched 且 Review Label 非空 |
| 待人工处理 | Workflow Status = Manual Review |
| 待补信息 | ApprovalMax Status = Blocked |
| 处理中 | 其余 |
依赖 Stage 的 5 个统计公式(上周期已推送 / 待人工 / 待补信息 / 重复非发票 / 完成率)在 Stage 重写后原样可用,不需要改。
不能只扩展 Invoice Tracking。QS 手工传进 ApprovalMax 的 bill 在 Tracking 里没有行,并进单表会丢掉这部分。独立表按 Request ID 建行,外部创建的 bill 也能出现在看板上。
需要的字段:Current Approval Node · Current Approver · Current Approver Email · Approval Deadline · Overdue · Overdue Rule · Blocked Person · Age Days · Reminder Count · Last Reminder At · Reminder Due · Risk Level · Risk Reason · Dashboard State · Action Suggestion · Last Comment · Comment Category · Comment Intent Flags · Last Actor · Submitted By · Escalate Ray · Suggested Recipient + 8 个公式字段。
旧公式判 [Approval Status]="on-approval" / "on-review",而新表 ApprovalMax Status 的取值是 Not Ready / Ready / Created / Submitted / Approved / Rejected / Blocked —— 没有 on-approval。新 Approval Dashboard 表要么单独保留 ApprovalMax 原始状态词,要么把公式改判 Submitted。
插件现在存在 reminder-state.json。这个文件不迁移,切换后所有待批的人会被重新催一遍。建议改成小表(分组键 / 角色 / 收件人 / firstPendingAt / lastReminderAt / reminderCount),可迁移可审计。
顺带修插件那个缺陷:firstPendingAt 按人粘住不清零,导致清空队列后的新账单第一次运行就被判超期。新实现应每轮从当前待批行重算,状态只作兜底。
Invoice ID ← SHA256("outlook:"+attachmentSha256)[:24]
ApprovalMax Request ID ← approvalMax.requestId
ApprovalMax Status ← Submitted / Approved / Rejected
ApprovalMax Phase ← submitted 或 terminal
ApprovalMax Submitted At ← approvalMax.submittedAt
Workflow Status ← ApprovalMax Submitted / Approved / Rejected
File Path ← finalPath
Destination Folder ← Project FolderApprovalMax Phase 最关键 —— dispatch 的相位单调递增(none → preview_ready → preparing → created → attachment_uploaded → submitted → terminal),写成 submitted 后不会回头创建。
dispatch 在 create 前会查远端重复,同供应商 + 同发票号 + 同金额判重。但金额不同时它会判为「修订」去更新那张旧 bill —— 那比重复建单更糟。迁移必须完整,不能靠查重兜底。
| 迁 | 不迁 |
|---|---|
| SHA · Message ID · Sender · Subject · 原文件名 | 旧 review flags(词表不同) |
| Received / Downloaded / Invoice 三个时间 | 旧 currentState(状态机不同) |
| Supplier · Invoice No · Total · Currency · GST | 旧 Manual Review 子目录判断 |
| Site / PO Number / Cost Code / PM / QS | |
File Path |
Workflow Status 统一写 Classified,让 SKILL 从项目/PO 环节重判。理由:旧插件的判断里有已知误判(101 条 No PO 里 97 条是错判),把结论迁过去等于把 bug 迁过去。
其中 337 条非发票文档可直接按 Category 落位、Workflow Status = Manual Review,不需要重判。
这批只承载「附件下载过」一个事实,没有分类、没有地块。新 intake 的分类能力强得多(标题证据、拆分、timesheet 合并、紧急类判定),重跑能拿到旧插件从未产出的结果。
唯一要处理的是重复下载:新 intake 按 state.attachments[sha] 去重,直接跑会再下一遍。建议重下,落到新 stage 目录,旧 Original Files 作为历史归档保留不再使用。
Category = Split Source,Workflow Status = Split Required,Destination Folder = Manual Review/Split Source Files。子记录写 Parent Invoice ID = 母记录的新 ID、Source = Split Child。父子必须一起迁,否则子记录成孤儿。
缺 attachmentSha256,其中一条来源是 approvalmax_manual_upload。无法算出主键,人工建行。
排版可复用已复制的 blkmqotmvLkdtqwY,但每个图表的数据源都要重指,且有严格的先后依赖:
1. Invoice Tracking 补 5 个照搬公式 + 重写 Is Invoice / Stage 2. 新建 Approval Dashboard 表 + 22 个字段 + 8 个公式 3. 数据迁移(C → S → B → A) 4. 逐个图表重指数据源 5. 新旧仪表盘并行对账 1–2 个周期 6. 停插件定时任务,退役旧表
第 1、2 步没做完,第 4 步无从下手 —— 图表读的就是那些公式字段。旧仪表盘的 16 + 8 个公式字段是它全部指标卡与图表的数据来源。
把授权、token 刷新、统一请求、配置解析搬进 SKILL 侧公共脚本。customer-3eyes-token.json 原样迁移,不要重新授权。
plugins/3eyes-invoice;拉一次参考数据成功
脱插件实现:文件定位四层、done 门槛、搜索范围、多候选取舍、code/PM/QS 取值与冲突裁决、历史 bill 回溯。
对当前 11 条final_ready_for_qs 只读跑通,定位结果与插件一致
5 个照搬 + Is Invoice / Stage 重写。先在测试视图上验证 187 行现有数据的 Stage 分布合理。
预演脚本先出清单,核对 requestId 与 Phase 后再写入。分批 50 条,每批读回校验。
528 行ApprovalMax Request ID 与 Phase 全部非空且与 ledger 一致
父子成对写入,校验 Parent Invoice ID 指向存在的行。
337 条非发票直接落位,其余 Workflow Status = Classified 交 SKILL 重判。
Project Not Matched 数量不高于迁移前
新 intake 扫描邮箱,221 个附件重新下载分类。
新表总行数 ≈ 1350 + 187 测试行,无重复 Invoice ID轮询状态、本地推断当前审批人/节点、计算超期与风险等级、写表。
528 条已推送记录在新看板上状态与 ApprovalMax 一致reminder-state.json 转表。先以 dryRun 跑一轮,确认不会对已催过的人重复发信。
逐个图表切换,新旧并行对账。
同一周期的关键指标新旧两边数值一致先停四个定时任务观察一个完整周期,再删插件目录。旧表保留只读。
一个 26–25 周期内无重复推送、无重复催办、无漏处理| 决策 | 选项 | 建议 |
|---|---|---|
| A 类 221 条 | 重新下载 / 播种 state 复用旧文件 | 重下 —— 干净,旧目录整体归档 |
| B 类 560 条 | 统一 Classified 重判 / 保留旧结论 | 重判 —— 旧结论含已知误判 |
| Approval Dashboard | 独立建表 / 并进 Invoice Tracking | 独立 —— 否则丢掉 QS 手工传的 bill |
| 催办状态 | 落表 / 继续用 JSON 文件 | 落表 —— 可迁移可审计 |
| Cross-month | 建表 / 公式字段 + 视图 | 公式 + 视图 —— 只是一个日期筛选 |
| 文件夹收件 | 补实现 / 不再支持 | 看是否还保留手工投递入口 |
| QS/Manager 期限基准 | 发票日期 / 邮件接收日 | 需业务确认 —— 现状会让 QS 期限早于 PM |
| PD 无催办 | 补 / 维持 | 需业务确认 |
先做不动数据的迁移预演脚本:读 ledger → 算出每条的新 Invoice ID → 输出四类清单与完整字段映射 → 检测与新表现有 187 行的 ID 冲突 → 不写任何数据。跑完拿到清单再决定上面这些选项。