QP Fleet Current Project State Snapshot
Last updated: 2026-08-21T17:06:07.610Z
# QP_CURRENT_STATE.md
## 2026-08-09 — A1 Clean 只读诊断接口
- 新增 `GET /api/ops/a1/clean-diagnostic`,严格读取本地 `DataHub.xlsx → A_clean`,并用本地 DataHub 的 Vehicle Master / GPS 身份作只读对照;不读取临时 Tab `clean`,不写 Google Sheet 或 Drive。
- 诊断区分今日/昨日原始清洁行数、成功匹配车辆数和每车最新有效日期的页面最终数量,统一使用 `America/Los_Angeles`,并返回日期字段、原始值、解析失败、重复记录、未匹配车牌及四辆目标车辆诊断。
- A1 页面现有 A_clean 卡片增加轻量诊断入口与核心数量,不改变清洁匹配算法、Vehicle Status、GPS、A_Records、Finance 或 OP 公式。页面显示 0 的真实数据原因须在合并部署 staging 后读取接口确认,开发 fixture 不冒充线上结论。
## 2026-08-09 — A_GPS 完整车牌即回场
- A1 / Vehicle Status 决策层改为:`A_GPS` 的 `Current 完整Plate` 能读到标准化后的完整车牌,即视为有效 GPS 且已回停车场。
- 不再使用 GPS 日期、日期新旧或可解析性、时间、`Mark as old Data`、`有效GPS信息`、位置或其他 GPS 条件决定是否回场;日期仅作辅助展示。空车牌不会生成有效 GPS / 回场证据。
- 保留车牌标准化及 Vehicle Master 当前/原始/历史完整车牌匹配;A1 页面“有完整车牌 GPS 车辆”和“已回停车场”统计与决策层使用同一口径。
- 本次不改财务逻辑、OP 公式、Cleaning、A_Records 行程状态或其他模块;不写 Google Sheet。
## 2026-08-09 — GPS 实际来源诊断显示
- A1 工作台的 DataHub / A_GPS 诊断现在直接显示本次请求实际读取的文件完整路径、表名、读取方式和是否使用 fallback。
- 本次只增加读取元信息的透传与页面展示,不改变 GPS 日期筛选、old 数据排除、车辆匹配、车辆分组或任何业务判断。
- 目的:当页面出现记录数不一致时,可以直接看到它到底读取了哪个 `DataHub.xlsx` 和哪个 Tab,不再只依赖“LOCAL_XLSX”这几个字判断。
## 2026-08-09 — Finance / Payout / Records P0 DataHub 单一原始数据源
- 正式原始业务数据读取规则固定为当前环境唯一的 `DataHub.xlsx`:staging 使用 `/opt/qp-fleet-system-staging/data/datahub/DataHub.xlsx`,production 使用 `/opt/qp-fleet-system/data/datahub/DataHub.xlsx`;`DATAHUB_XLSX_PATH` 仍可作为显式运行环境 override。
- `/api/finance-payout-latest-debug`、`/api/finance-car-trip-rule-preview`、`/api/finance-op-compare-debug`、Owner Report V3 operational fallback 统一通过严格 local-only reader 读取 `A_Records`、`A_Turo_Payouts`、`A_overview`、`A_management_fee_rules` 与相关 Guide 表;本地工作簿或 Tab 缺失时明确失败,禁止实时 Google Sheet fallback。
- `/api/finance-cache-refresh` 不再从 Google Sheet 刷新原始业务 JSON cache;它先验证 DataHub 原始表可读,只继续兼容刷新 Finance Master 派生结果 cache。既有 cache 文件不删除,Finance / OP / Payment Batch 公式不变,也不写 Google Sheet。
- Native 与其余 Legacy 入口的全面收敛不属于本 P0;后续必须继续按小范围 PR 迁移,不能借本次改动大范围重构。
## 2026-08-08 — Skeleton Tables 通用数据查看能力
- 所有 Guide-A / ECS Table Registry 注册的 Skeleton 表格共用全量数据关键词搜索、按列筛选、类型感知三态排序、分页和列显示能力;处理顺序统一为完整读取 → 搜索 → 列筛选 → 排序 → 计数 → 分页,而不是仅处理当前 50 行。
- 页面状态保留在 `/ops/table` URL 中,支持旧 `q/page/pageSize/sort/dir/filterField/filterValue` 参数,并新增 `sortBy/sortDirection/filters/hiddenColumns`;筛选后自动修正越界页码。
- A_GPS 使用集中式重要字段默认配置,优先呈现日期、时间、当前/原始/历史车牌、VIN、Vehicle ID、Turo Vehicle ID、GPS、位置、车辆分组/状态、更新时间和备注;所有源字段仍完整读取并可通过列设置重新显示。
- 功能保持只读,不编辑、写回、删除或自动推断/修改车辆分组;后续分组规则必须依据真实数据和人工确认。
## 2026-08-07 — PR577:Skeleton V1 正式收口
### 状态:**COMPLETED / FROZEN**
Skeleton V1 已完成工程实现、自动兼容性回归和 staging 验收,可以正式冻结。PR561–PR576 的结果如下:
| PR | 收口结果 |
|---|---|
| PR561–PR565 | Maintenance 受控更新、单车运营汇总、车队列表、状态一致性检查与批次验收导航完成。 |
| PR566–PR567 | Batch A 验收缺陷修复,Maintenance 生命周期与状态语义对齐。 |
| PR568–PR569 | Batch B 迁移验证完成;不存在的来源不再由 Registry 伪造,所有表回归真实 Guide-A 来源。 |
| PR570–PR572 | `A_CarDetail` 只读接入、Guide-A 解析与 strict Show Spreadsheet ID 解析完成。 |
| PR573–PR575 | `SOURCE_ROW`、稳定 Row Key policy、页面与 audit adapter chain、物理 `sourceRowIndex` 一致性完成。 |
| PR576 | 普通 Show 表零代码架构与 Compatibility Test 收口完成。 |
冻结能力包括:Guide-A 驱动接入;普通 Show 表零代码接入;Google Sheet、`LOCAL_XLSX`、`LOCAL_XLSM` 配置识别;`SOURCE_ROW`、`PRIMARY`、`FALLBACK`、`REQUIRE_STABLE_BUSINESS_KEY`;strict source 与 primary/secondary fallback;Search、Sort、Pagination、Detail;Preview / Execute 与物理 `sourceRowIndex` 对齐。Compatibility Matrix 的权威地址是 [`docs/SKELETON_COMPATIBILITY_MATRIX.md`](./SKELETON_COMPATIBILITY_MATRIX.md),使用说明见 [`docs/QP_SKELETON_V1_USAGE.md`](./QP_SKELETON_V1_USAGE.md)。
staging 已验收:`A_CarDetail`、`A_clean`、`A_repaire`、`A_maintenance`、`A_CallHandling`。
### 长期冻结规则
> 普通 Show 表以后只需要在 Guide-A 增加来源配置,不得因为新增普通表而修改 Registry、Adapter、Renderer、Route 或新增 table-specific profile。
普通 Show 表默认 `READ_ONLY`,未声明业务 key 时使用 `SOURCE_ROW`。只有需要长期追踪、跨表关联或写回的核心业务表,才显式声明 `PrimaryKey`、`FallbackKey`、`REQUIRE_STABLE_BUSINESS_KEY`,以及所需的 `Actions` / `WriteMode` / `Relations`。这些声明代表业务扩展,不是新增普通表的步骤。
### 冻结后的边界与下一站
已知限制:`LOCAL_XLSM` 当前是文件格式配置识别,reader 的共享 workbook 参数仍固定为 `DataHub.xlsx`;部分核心表仍保留历史 profile fallback、业务 enrichment、controlled-write service wiring 和字段白名单;Compatibility Test 使用内存 fixture,不代替 staging 的真实权限、文件与 Google API 验收;`SOURCE_ROW` 适合只读定位,但物理行移动后不提供跨版本业务身份。
以下均属于未来业务扩展,而不是 Skeleton V1:新增或改变写回、业务 Actions / Relations、核心表 metadata 声明式迁移、Repair / Maintenance 生命周期变化、发车判断变化、Finance 变化、ECS-Master 数据迁移。下一阶段可以进入 **ECS-Master Phase 1|车辆主档案只读工作台**;应另立范围,并继续保持 Skeleton V1 冻结。
---
## 2026-07-27 主线切换:Finance Stable / Maintenance → ECS Table UI
### Finance Mainline:**STABLE / MAINTENANCE(Regression Protected)**
- **Finance V2 Income Classification Phase Complete**
- **QR Owner View Phase Complete**
- **Vehicle Settlement Explanation Phase Complete**
- **Monthly Settlement Workflow Complete**
Finance 已正式退出持续功能开发主线。Monthly Settlement V1 是 Legacy / Frozen Core;V2 是 Current explanation / classification layer。后续 Finance 修改不得直接重写 V1 Frozen Core;新 settlement rule 必须创建新的 algorithm version。历史 Snapshot 不得 migrate、overwrite,或重新计算后伪装为原版本。Owner View 永远绑定 token 指向的 frozen Snapshot;Debug 只用于解释与排查,绝不是 official settlement source。
继续保护 Quenna / `2026-06` / `8VHA555`、Henry / `2026-06` / `8YLW725`、Henry / `2026-06` / `7DYB608` 与 WHP8888 parking fee 等回归 / reconciliation 样本。
以下仅为 **non-blocking backlog**,不得阻塞 Skeleton:小 UI 美化、字体/间距、wording 微调、展开按钮视觉、Owner Portal、Owner login、历史月份 Dashboard、email/SMS distribution、更高级 token management UI。
### 新开发主线:**ECS Table UI / Skeleton Phase 1 Complete — Stable / Read Only**
统一只读数据查看链路已经建立:`Guide-A → Table Registry → Table Data Adapter → existing DataHub.xlsx / Google Sheet read chain → Table Renderer → /ops/tables + /ops/table?tab=...`。
- Registry 只接受 `ECS-Ctrl=yes`、有效 `TabName` 及 `TabType=Ctrl|Show` 的 Guide-A 行;无效配置安全跳过。
- 首批真实业务表由 Guide-A 自动注册;当前确认的基础接入目标是 `A_overview`、`A_Records`、`A_CallHandling`。实际 `/ops/tables` 清单始终以部署环境 Guide-A 中 `ECS-Ctrl=yes` 的实时内容为准,不在代码中伪造表。
- ECS Table UI / Skeleton Phase 1.1 统一复用 read strategy:`Ctrl` 表依 Guide-A `ID + SourceTab` 实时读 `GOOGLE_SHEET`(无缓存 fallback);`Show` 表默认 `LOCAL_XLSX` (`DataHub.xlsx`) 优先,失败后回退 DataHub `GOOGLE_SHEET`,也支持 Guide-A 显式 `SHEET_FIRST`。Guide-A `ID` 对 Show 表只是 registry row ID,不得覆盖 DataHub spreadsheet ID。
- `/ops/tables` 的 Health 为进程内“最近一次真实读取”结果:未读取为 `Unknown`,成功/失败为 `Available` / `Unavailable`,并显示 resolved/actual Source 与检查时间;列表不会为 health 串行读取所有大表。只读 audit 命令为 `npm run audit:ecs-tables`,它遍历当前 Guide-A 所有 `ECS-Ctrl=yes` 表并输出 source、行列数、fallback 与安全 error code。
- 通用 renderer 支持 server-side 搜索、表头排序、25/50/100 分页、Registry `VisibleColumns` / `SearchableColumns` 与“显示全部列”。默认每页 50 行,HTML 仅输出当前页。
- Phase 1 对 Ctrl 和 Show 一律强制 `READ ONLY`;没有 Save、Delete、Inline Edit 或 Sheet writeback。Google Sheet 仍是 Master,ECS 仍是 cache / UI / automation co-pilot;本阶段不是 ECS-Master migration。
- `/system-nav` / Developer Center 已提供 `ECS Tables → /ops/tables` 统一入口。
- Phase 1.2 收口了 Ctrl 历史 metadata 兼容:规范字段 `SpreadsheetId` 优先于旧 `ID`,仅在规范字段为空时才把旧 `ID` 当作原始 Google Sheet ID。`A_clean` 的失败根因是该行同时存在 registry `ID` 与明确 `SpreadsheetId` 时,旧 resolver 错把 `ID` 当成 spreadsheetId;不是 LOCAL_XLSX、DataHub runtime path 或 tab semantics 问题。
- Ctrl 内部诊断现区分 `SPREADSHEET_ID_MISSING`、`SOURCE_TAB_MISSING`、`SHEET_NOT_FOUND`、`PERMISSION_DENIED` 与 `GOOGLE_API_ERROR`;页面继续只显示安全的不可用提示,详细分类仅进入 audit / server log。
- Phase 1 完成范围:Guide-A → Registry → Read Strategy;Ctrl → Google Sheet;Show → DataHub.xlsx;runtime path resolver;DataHub tab semantics;read-only renderer;Search;Sort;Pagination;Visible Columns;Health Status;Audit。
### ECS Table UI / Skeleton Phase 2 — Planned, Not Started
推荐依次推进 registry-driven row actions、单行详情页、Ctrl table controlled writeback、write 前 validation、audit log、权限层,最后接入 Vehicle / Repair / Case 等业务模块。每项均须另立版本和权限/审计设计;本轮没有 Phase 2 代码、业务动作或 Sheet writeback,也不进入 ECS-Master migration。
## Finance LD 正式入口收口(2026-07-17)
Finance 普通用户正式页面**仅有四个**:`/ld/finance`、`/ld/finance/snapshots`、`/ld/finance/snapshot-detail`、`/ld/finance/debug`。主导航仅保留 `Finance → /ld/finance`;其余三个正式功能从 Finance Landing 或四页内部导航进入,并透传适用查询参数。
LD 外同功能页面默认属于 **LEGACY / DEPRECATED / DO NOT USE FOR NORMAL OPERATIONS**:代码与兼容路由继续保留,但不出现在普通导航或日常入口。Developer Center 的正式 Finance 区也只列出上述四条 LD 路由。未经用户明确批准,不得新增第五个正式 Finance 页面。
> 当前同步时间:2026-07-15 | 面向读者:ChatGPT / Codex / Cursor / 新接手开发者
> 这是一份**真实工程现状快照**,不是理想架构文档。
## 0. 2026-07 当前权威状态(覆盖下方历史段落)
### 当前主线
QP Fleet 财务第一版已进入 **LD 落地页、Snapshot / Owner Report、Finance Debug 和最终口径收口阶段**。当前重点:
- Settlement Algorithm v1。
- Native / BasicN 正式口径。
- Snapshot / Owner Report 作为最终结算结果。
- LD Finance 页面作为日常使用入口。
- Finance Debug 作为排查工具。
- Debug 与正式报表口径对齐。
- Earning Date 与 Monthly Settlement 两种 Debug 模式。
- 后续再做结果区 UI 优化和财务页面收口。
C2.1g Ops Upload 真实业务验证仍是 Internal Ops 支线状态,但**不是当前财务主线**。
### 当前真实项目结构入口
当前仓库存在的关键入口包括:
- `server.js`:Express 单体入口,包含主要路由、API 和 server-side HTML。
- `finance-debug-landing.html`:LD Finance Debug 精简日常页。
- `finance-payout-debug.html`:Finance Payout Debug 历史完整页。
- `docs/`:项目工程说明与规则文档。
- `scripts/`:staging / production 启动、校验、数据刷新脚本。
- `cache/`:运行时缓存目录(仓库中可能为空或由部署环境生成)。
- `data/`:运行时数据目录(仓库中可能为空或由部署环境生成)。
注意:仓库根目录存在 `CLAUDE.md` 与 `CODEX.md`;当前未发现 `docs/CLAUDE.md`,不要再写“必须先读 docs/CLAUDE.md”。
### QP_CURRENT_STATE.md / QP_CURRENT_STATUS.md 命名处理
- `docs/QP_CURRENT_STATE.md` 是唯一当前工程状态文档。
- `docs/QP_CURRENT_STATUS.md` 已标记为历史状态快照,仅供追溯;后续不要把它作为当前状态入口。
### 当前 LD Finance 路由
- `/ld/finance`
- `/ld/finance/report`
- `/ld/finance/debug`
- `/ld/finance/debug-history`
Finance Debug 历史兼容路由:
- `/finance-payout-debug`
- `/finance-payout-debug-history`
### Finance Debug 两种模式
- `mode=earningDate`:原始流水视角;按 Turo payout 明细行 Earning Date 自然月筛选;显示原始正负流水;用于查漏、查重复、查 adjustment;不代表最终车主月报。
- `mode=monthlySettlement`:正式月报视角;直接复用 Native Snapshot / Owner Report 正式计算;Summary 必须与正式 Snapshot / Owner Report 一致;用于解释最终月度结算结果。
Debug 只用于排查差异。最终结果以当前展示版本 Owner Report / Snapshot 为准。Monthly Settlement Debug 应直接复用 `buildOwnerMonthFinanceResultNative(...)` 或当前 main 中同一正式计算链路;禁止为 Debug 单独复制月度算法、为了 UI 修改计算、用 Debug 结果覆盖 Snapshot、Debug 写 Google Sheet、Debug 修改 Owner Report。
### 部署规则
- staging 路径:`/opt/qp-fleet-system-staging`;端口:`4021`;PM2:`qp-fleet-staging`。
- production 路径:`/opt/qp-fleet-system`;端口:`4020`;PM2:`qp-fleet-system`。
- 默认先部署 staging;只有用户明确要求 production 才操作 4020。
- 禁止 `pm2 restart all` / `pm2 delete all`;禁止误操作 Etsy 4030。
PR 合并后 staging 一次执行命令:
```bash
cd /opt/qp-fleet-system-staging && \
git fetch origin && \
git checkout main && \
git reset --hard origin/main && \
npm install && \
node --check server.js && \
pm2 restart qp-fleet-staging && \
pm2 status
```
---
## 1. 项目定位
QP Fleet System 是 QuickPath 内部车队运营与财务管理系统。
- 不是单一应用,是"多层协作"的运营+财务体系。
- 当前主线已更新为财务第一版收口;C2.1g Ops Upload 真实业务验证保留为 Internal Ops 支线。
- Internal Ops 新增清洁记录查看:`/ops/cleaning-submissions`(只读,读取 `Cleaning_Submissions`,不写 Google Sheet)。
- 2026-05-25 支线最小收尾:A-guide 已接入 ECS 表格控制中心只读展示(含 control/show、缓存、刷新、同步、默认来源与数据来源类型),不新增写入;下一步主线回归 V3 财务收口。
- 部署地址:`http://47.77.231.107:4020`
- 服务器路径:`/opt/qp-fleet-system`
- PM2 进程名:`qp-fleet-system`(唯一允许操作的进程)
- 端口:`4020`(同机另有 Etsy Image Tool,端口 4030,**禁止误碰**)
---
## 2. 项目文件结构(真实现状)
```
/opt/qp-fleet-system/
├── server.js # 唯一入口,所有路由和页面都在这里
├── package.json # express / googleapis / xlsx / axios
├── service-account.json # Google API 鉴权(不提交)
├── cache/
│ └── finance/ # Finance cache-first 本地缓存
│ ├── A_overview.json
│ ├── A_Records.json
│ ├── A_Turo_Payouts.json
│ ├── A_management_fee_rules.json
│ ├── Guide-Owner.json
│ ├── Guide-OP-detail.json
│ ├── Finance_Summary_Master.json
│ ├── Finance_Cars_Master.json
│ ├── Finance_Reimbursement_Master.json
│ └── Finance_Batch_Overview.json
├── data/
│ └── datahub/
│ └── DataHub.xlsx # 本地中间数据文件(xlsx)
├── data/
│ └── settlements/ # 结算草稿文件(owner__month.json)
└── docs/ # 项目规则 MD 文档
├── CLAUDE.md # AI 工具入口说明(必须先读)
├── QP_CURRENT_STATUS.md
├── QP_PROJECT_OVERVIEW.md
├── QP_VERSION_TREE.md
├── QP_FINANCE_RULES.md
├── QP_DATAHUB_STRUCTURE.md
├── QP_ECS_MASTER_MIGRATION.md
├── QP_INTERNAL_OPS_PLAN.md
└── QP_SCRAPER_ARCHITECTURE.md
```
> ⚠️ `server.js` 是单体文件,所有路由、HTML 页面渲染、API、中间件都在其中。没有 React/Vue 框架,前端是 server-side rendered HTML + inline JS。
---
## 3. 技术栈
| 层级 | 技术 |
|------|------|
| 后端框架 | Express 5 (Node.js) |
| 认证 | Google Service Account (googleapis) |
| 主数据源(当前) | Google Sheet(两个 Sheet ID) |
| 中间层 | DataHub.xlsx(本地) |
| 缓存层 | `/cache/finance/*.json`(本地 JSON) |
| 前端渲染 | Server-side HTML,inline JS,无构建工具 |
| 包管理 | npm(无 lock file 限制)|
| 进程管理 | PM2 |
---
## 4. 数据源结构与三层关系
### 4.1 数据来源层级(当前真实状态)
```
[Google Sheet - 主数据源]
├── DATA_HUB_SHEET_ID = "1_e5chw2tstso6P7AFQwgHpmLtuR9HQBQXCfnPSAx0a8"
│ ├── A_overview ← 车辆/车主/平台/位置/归属关系
│ ├── A_Records ← 行程分类、费用拆解(辅助,非收入主口径)
│ ├── A_Turo_Payouts ← 正式收入口径 ✅
│ ├── A_management_fee_rules
│ ├── Guide-Owner ← 车主 PayOption / OP 规则
│ ├── Guide-OP-detail ← 各费用类型 OP1/OP2/OP3 处理规则
│ └── Guide-A ← ECS Table UI registry(ECS-Ctrl=yes 才进入)
│
└── DEFAULT_FINANCE_OUTPUT_SHEET_ID = "1eUESU1D6oj8PoXNxRgplITKZrY9xPZ-5adf2Z4kRICE"
├── Finance_Summary_Master
├── Finance_Cars_Master
├── Finance_Reimbursement_Master
└── Finance_Batch_Overview
[DataHub.xlsx - 本地中间层]
路径:/opt/qp-fleet-system/data/datahub/DataHub.xlsx
作用:接住抓取结果、缓存结果、财务中间结果,降低直接读 Sheet 耦合
读取函数:readDataHubTab(tabName)
[cache/finance/*.json - 本地 JSON 缓存]
作用:Finance API cache-first 读取,减少 Google Sheet 调用频率
刷新入口:GET /api/finance-cache-refresh
状态查询:GET /api/finance-cache-status
```
### 4.2 读取优先级(当前已确认)
```
Finance Master API:
local cache (JSON) → [fallback] Google Sheet
ECS Table UI (/ops/tables):
Guide-A 规则总表驱动
- TabType=Show: cache/DataHub/local cache first
- TabType=Ctrl: CONTROL_BACKTRACE to original Google Sheet
- PARAM_CONFIG: Guide-A / A_management_fee_rules / Guide-Owner / Guide-OP-detail
- 展示字段:TabName/TableCategory/TabType/SourceMode/CacheMode/WriteMode/Description/Notes
ECS Table UI (/ops/table?tab=...):
先读 Guide-A 元数据,再按 TabType 决定读取链路(Show=缓存优先,Ctrl=回溯原始表)
原则:本地没有再 fallback;不确定来源时看 dataSourceDiagnostics 字段
```
### 4.3 Google Sheet / DataHub / ECS 三者关系
```
当前阶段:Sheet-Master + ECS Co-Pilot(副驾驶验证期)
Google Sheet ──→ [抓取/同步] ──→ DataHub.xlsx
↓
cache/finance/*.json
↓
Finance API (cache-first)
↓
/finance/* 页面(只读展示)
ECS(当前)= 读取、展示、验证
ECS(未来)= 主数据源(ECS-Master,尚未切换)
```
---
## 5. 当前主线模块(已完成 - Finance V1)
### 5.1 Finance Shell 入口
- 路由:`GET /finance`
- 左侧菜单分组(Finance / Owner / Operations / Tools)+ 可折叠
- iframe 框架,月份/车主选择器,语言切换(en/zh)
### 5.2 已完成页面(Finance V1 收口状态)
| 页面 | 路由 | 状态 |
|------|------|------|
| Dashboard | `/finance` → `dashboard` view | ✅ READY |
| Finance V1 Acceptance | `financeV1Acceptance` view | ✅ READY |
| Owner Detail | `/finance-owner-detail` | ✅ READY |
| Monthly Settlement Report | `/finance-owner-report` | ✅ READY |
| Owner Print | `/finance-owner-print` | ✅ READY |
| Expense Detail Report | `/finance-expense-report-v1` | ✅ READY |
| Payment Tracking | 内嵌 view | ✅ READY |
| Vehicle Anomalies | view | ✅ READY |
| Cache Refresh | `/finance-cache-refresh` | ✅ READY |
| Payout Debug | `/finance-payout-debug` | ✅ DEBUG工具 |
| Trip Income Debug | `/finance-trip-income-debug` | ✅ DEBUG工具 |
| Car Rule Preview | `/finance-car-trip-rule-preview` | ✅ DEBUG工具 |
### 5.3 Finance V1 线上验收状态(2026-05)
```
Owners = 5
Pay To Owners = $17,818.35
Paid/Remaining = $17,455.01 / $363.34
Henry = NEEDS_REVIEW(人工复核,不是系统错误)
Tan/Roy/Quenna/Ning W = READY
monthly-run-status: readyCount=5, missingCount=0, errorCount=0
```
---
## 6. 当前进行中模块(V3.2 Internal Ops)
### 6.1 /ops 入口
- 路由:`GET /ops`
- 内部运营后台,**不对车主开放**
- 当前全部只读,不写回 Google Sheet
### 6.2 V3.2 已落地步骤
| Step | 内容 | 状态 |
|------|------|------|
| Step2 | `/ops` 首页,模块卡片(Overview/Anomalies/Maintenance等)| ✅ |
| Step3 | Ops Overview Snapshot(复用 `/api/finance-dashboard-month`)| ✅ |
| Step4 | Vehicle Anomalies Snapshot(复用 `/api/finance-vehicles-month`)| ✅ |
| Step4 | ECS Table UI:`/ops/tables` + `/ops/table?tab=...` | ✅ |
| Step4.1 | Guide-A TabType 语义(Ctrl/Show)+ Sheet-first 策略 | ✅ |
| Step5 | 单表字段 checkbox 选择 + 搜索框 | ✅ |
| Step6 | Select All/Clear/Default10 + row limit(50/100/200/500) + sticky header | ✅ |
| Step7 | 分页 + 行顺序(Original/Reverse)+ localStorage 视图偏好 | ✅ |
| Step7.1 | 全量数据分页顺序修复(limit 语义改为 page size)| ✅ |
| Step7.2 | localStorage columns 对齐校验 + 越界修正 + Reset Saved View | ✅ |
| Step8.1 | Cleaning Upload Prototype(`/ops/cleaning-upload` + `/api/ops/cleaning-upload` 受控写入) | ✅ |
---
## 7. 核心 API 清单
### Finance 核心 API(cache-first)
```
GET /api/finance-dashboard-month?month=YYYY-MM
GET /api/finance-owner-detail?owner=...&month=...
GET /api/finance-dashboard-master?month=...
GET /api/finance-owner-master?owner=...&month=...
GET /api/finance-monthly-run-status?month=...
GET /api/finance-owner-report-v1-calc?owner=...&month=...
GET /api/finance-owner-cards?month=...
GET /api/finance-vehicles-month?month=...
GET /api/finance-home-dashboard?month=...
```
### Cache 管理 API
```
GET /api/finance-cache-refresh ← 触发缓存刷新(写 JSON 文件)
GET /api/finance-cache-status ← 查询各 tab 缓存状态
POST /api/datahub-xlsx-upload ← 上传新版 DataHub.xlsx
GET /api/datahub-xlsx-status ← 查询 DataHub.xlsx 状态
```
### Debug API
```
GET /api/finance-payout-debug?...
GET /api/finance-trip-income-debug?tripId=...
GET /api/finance-op-compare-debug?owner=...&from=OP1&to=OP3
GET /api/finance-owner-op?owner=...
GET /api/finance-car-trip-rule-preview-note?...
```
### Settlement 草稿 API
```
GET /api/finance-owner-car-settlement-preview?owner=...&month=...
POST /api/finance-settlement-draft-save
GET /api/finance-settlement-draft-load
```
---
## 8. Cache 体系
### 8.1 cache-first 读取函数(server.js 内)
```javascript
loadFinanceCache(tabName) // 读 JSON cache
readDataHubTab(tabName) // 读 DataHub.xlsx(含 fallback Google Sheet)
readSheet(sheetId, tabName) // 直接读 Google Sheet
```
### 8.2 缓存覆盖 Tab 列表
**FINANCE_CACHE_TABS**(来源:DATA_HUB_SHEET_ID)
- A_overview, A_Records, A_Turo_Payouts, A_management_fee_rules
- Guide-Owner, Guide-OP-detail(+ 其他 Guide 表)
**FINANCE_MASTER_CACHE_TABS**(来源:DEFAULT_FINANCE_OUTPUT_SHEET_ID)
- Finance_Summary_Master, Finance_Cars_Master
- Finance_Reimbursement_Master, Finance_Batch_Overview
### 8.3 兼容性注意(已知坑)
```
- cached rows 可能是二维数组或对象数组,读取层必须兼容两种格式
- ownerMonthKey 可能是 owner__month 或 owner_month,两种都要支持
- rowsToObjectList() 负责标准化处理
```
---
## 9. 财务核心规则(禁止搞错)
```
✅ A_Turo_Payouts = 正式收入口径
❌ A_Records ≠ 正式收入(只做分类/参考)
✅ 收入以 Turo payout 入账月份为准
❌ 不按 trip start/end 做入账
✅ Paid By = 谁垫付(不代表费用归属)
✅ 费用归属 = Plate / Belongs To
✅ PaytoCost = 不参与利润分成(直接返还)
✅ reimbursement = 按类型拆分(toll/EV/ticket/gas/cleaning/smoking/damage)
✅ OP1/OP2/OP3 = PayOption 计算路径
PBP-100%:管理公司拿100%,车主拿0%
PBP-30%: 管理公司拿30%,车主拿70%
✅ Payment Batch = 判断同批 payout 多条记录归属关系(不能随便改逻辑)
✅ 车主打印版 = Owner Standard(不展示内部 debug 字段)
```
---
## 10. ECS 迁移路线(当前阶段)
```
当前:Sheet-Master + ECS Co-Pilot(只读验证)
迁移路线(已确认,不可跳步):
Google Sheet → ECS Table UI → ECS-Master → 后期数据库化
当前说明:Google Sheet 当前仍是主数据源;ECS 当前是副驾驶(展示、读取、测试、验证、逐步承接);ECS-Master 是未来单独大版本,不是当前 V3.2 目标;当前只预留 record_id、updated_at、sync_version、操作日志、冲突检测等未来能力;当前不新增写 Google Sheet,除非用户明确确认。
当前已完成:ECS Table UI(/ops/tables + /ops/table)只读展示
当前下一步:继续完善 ECS Table UI / Internal Ops 只读与验证能力。当前仍是 Sheet-Master + ECS Co-Pilot 副驾驶验证期,不进入 ECS-Master 正式切换。
强制规则:
- 每次只迁移一个模块
- 每个模块:先只读 → 再编辑 → 再主数据源切换
- 不允许直接废掉 Google Sheet 逻辑
```
---
## 11. 当前风险点
| 风险 | 说明 | 严重度 |
|------|------|--------|
| server.js 单体过大 | 所有逻辑在一个文件,维护难度高 | 中(已知,暂不重构)|
| cache 时效性 | cache 未自动定时刷新,依赖手动触发 | 中 |
| 二维数组兼容 | cached rows 格式不一致,需要 rowsToObjectList 适配 | 中 |
| ownerMonthKey 双格式 | owner__month vs owner_month 需要双向兼容 | 低-中 |
| Henry NEEDS_REVIEW | 人工复核状态,非系统错误,但需定期清理 | 低 |
| ECS-Master 未切换 | Google Sheet 仍是主数据源,Sheet 故障会影响数据刷新 | 中 |
| PM2 同机多进程 | 同机运行 Etsy Image Tool,误操作 restart all 会影响生产 | 高 |
| A_Records 误用风险 | 如果不熟悉规则,容易把 A_Records 当收入主口径 | 高 |
---
## 12. 当前下一步重点
### 近期(当前进行)
- V3.2 继续推进:`/ops` 模块从只读展示向 Maintenance/Repair/Clean/Task 扩展
- Finance V1 收口稳定观察期
### 中期(计划中)
- 当前下一步:继续完善 ECS Table UI / Internal Ops 只读与验证能力。当前仍是 Sheet-Master + ECS Co-Pilot 副驾驶验证期,不进入 ECS-Master 正式切换。
- Scraper 统一框架(当前 Turo 优先稳定)
- Owner Portal 正式化
- Checkout 后续完善
### 暂停中(不做)
- 大型重构
- 数据库化重写
- 新增写 Google Sheet 行为(需明确确认才能做)
---
## 13. 开发规则速查(AI 工具必读)
```
禁止事项:
❌ 直接修改 main 分支
❌ 直接操作 ECS 生产数据
❌ pm2 restart all / pm2 delete all
❌ 影响 BookCars / Etsy Image Tool(端口 4030)
❌ 新增写 Google Sheet(未经确认)
❌ 修改 OP1/OP2/OP3/PayOption/Payment Batch 计算逻辑
❌ 用 A_Records 替代 A_Turo_Payouts 做收入来源
❌ 把 Paid By 当费用归属
❌ 把 PaytoCost 纳入利润分成
每次改动必须说明:
✅ 改哪些文件
✅ 改哪些函数或路由
✅ 是否影响 API
✅ 是否影响 OP 财务规则
✅ 是否写 Google Sheet
✅ 是否影响 BookCars / Etsy Tool
```
---
## 14. 上下文缺失说明
以下内容从项目知识库中**无法完整确认**,需要人工补充:
1. **server.js 完整路由列表**:文件过大,只能读到片段,无法枚举所有路由
2. **FINANCE_CACHE_TABS 完整列表**:仅确认了部分 tab 名称,完整列表未完全可见
3. **Guide-A 当前收录的 Tab 列表**:ECS-Ctrl=yes 的表有哪些,未完全确认
4. **Settlement 草稿的完整状态机**:draft save/load/confirm 流程边界未完全可见
5. **Scraper 当前实际运行状态**:本地 Data Collector Machine 的实际抓取频率未知
6. **OP 公式具体数值**:管理费比例、停车费规则的具体数值未在文档中完整列出
7. **Owner 列表**:文档提到 Henry/Tan/Roy/Quenna/Ning W,是否有其他 owner 未确认
8. **BookCars 系统**:提到"不影响 BookCars"但无 BookCars 相关文档,边界不清楚
---
*本文档由 Claude 根据 QP Fleet 项目知识库自动生成。如有出入,以实际代码和 docs/ 目录下 MD 文档为准。*
# QP_CURRENT_STATE.md
> 生成时间:2026-05 | 面向读者:ChatGPT / Codex / Cursor / 新接手开发者
> 这是一份**真实工程现状快照**,不是理想架构文档。
---
## 1. 项目定位
QP Fleet System 是 QuickPath 内部车队运营与财务管理系统。
- 不是单一应用,是"多层协作"的运营+财务体系。
- 当前主线已更新为财务第一版收口;C2.1g Ops Upload 真实业务验证保留为 Internal Ops 支线。
- 部署地址:`http://47.77.231.107:4020`
- 服务器路径:`/opt/qp-fleet-system`
- PM2 进程名:`qp-fleet-system`(唯一允许操作的进程)
- 端口:`4020`(同机另有 Etsy Image Tool,端口 4030,**禁止误碰**)
---
## 2. 项目文件结构(真实现状)
```
/opt/qp-fleet-system/
├── server.js # 唯一入口,所有路由和页面都在这里
├── package.json # express / googleapis / xlsx / axios
├── service-account.json # Google API 鉴权(不提交)
├── cache/
│ └── finance/ # Finance cache-first 本地缓存
│ ├── A_overview.json
│ ├── A_Records.json
│ ├── A_Turo_Payouts.json
│ ├── A_management_fee_rules.json
│ ├── Guide-Owner.json
│ ├── Guide-OP-detail.json
│ ├── Finance_Summary_Master.json
│ ├── Finance_Cars_Master.json
│ ├── Finance_Reimbursement_Master.json
│ └── Finance_Batch_Overview.json
├── data/
│ └── datahub/
│ └── DataHub.xlsx # 本地中间数据文件(xlsx)
├── data/
│ └── settlements/ # 结算草稿文件(owner__month.json)
└── docs/ # 项目规则 MD 文档
├── CLAUDE.md # AI 工具入口说明(必须先读)
├── QP_CURRENT_STATUS.md
├── QP_PROJECT_OVERVIEW.md
├── QP_VERSION_TREE.md
├── QP_FINANCE_RULES.md
├── QP_DATAHUB_STRUCTURE.md
├── QP_ECS_MASTER_MIGRATION.md
├── QP_INTERNAL_OPS_PLAN.md
└── QP_SCRAPER_ARCHITECTURE.md
```
> ⚠️ `server.js` 是单体文件,所有路由、HTML 页面渲染、API、中间件都在其中。没有 React/Vue 框架,前端是 server-side rendered HTML + inline JS。
---
## 3. 技术栈
| 层级 | 技术 |
|------|------|
| 后端框架 | Express 5 (Node.js) |
| 认证 | Google Service Account (googleapis) |
| 主数据源(当前) | Google Sheet(两个 Sheet ID) |
| 中间层 | DataHub.xlsx(本地) |
| 缓存层 | `/cache/finance/*.json`(本地 JSON) |
| 前端渲染 | Server-side HTML,inline JS,无构建工具 |
| 包管理 | npm(无 lock file 限制)|
| 进程管理 | PM2 |
---
## 4. 数据源结构与三层关系
### 4.1 数据来源层级(当前真实状态)
```
[Google Sheet - 主数据源]
├── DATA_HUB_SHEET_ID = "1_e5chw2tstso6P7AFQwgHpmLtuR9HQBQXCfnPSAx0a8"
│ ├── A_overview ← 车辆/车主/平台/位置/归属关系
│ ├── A_Records ← 行程分类、费用拆解(辅助,非收入主口径)
│ ├── A_Turo_Payouts ← 正式收入口径 ✅
│ ├── A_management_fee_rules
│ ├── Guide-Owner ← 车主 PayOption / OP 规则
│ ├── Guide-OP-detail ← 各费用类型 OP1/OP2/OP3 处理规则
│ └── Guide-A ← ECS Table UI registry(ECS-Ctrl=yes 才进入)
│
└── DEFAULT_FINANCE_OUTPUT_SHEET_ID = "1eUESU1D6oj8PoXNxRgplITKZrY9xPZ-5adf2Z4kRICE"
├── Finance_Summary_Master
├── Finance_Cars_Master
├── Finance_Reimbursement_Master
└── Finance_Batch_Overview
[DataHub.xlsx - 本地中间层]
路径:/opt/qp-fleet-system/data/datahub/DataHub.xlsx
作用:接住抓取结果、缓存结果、财务中间结果,降低直接读 Sheet 耦合
读取函数:readDataHubTab(tabName)
[cache/finance/*.json - 本地 JSON 缓存]
作用:Finance API cache-first 读取,减少 Google Sheet 调用频率
刷新入口:GET /api/finance-cache-refresh
状态查询:GET /api/finance-cache-status
```
### 4.2 读取优先级(当前已确认)
```
Finance Master API:
local cache (JSON) → [fallback] Google Sheet
ECS Table UI (/ops/tables):
Google Sheet first → [fallback] DataHub.xlsx
ECS Table UI (/ops/table?tab=...):
DataHub.xlsx → [fallback] Google Sheet
原则:本地没有再 fallback;不确定来源时看 dataSourceDiagnostics 字段
```
### 4.3 Google Sheet / DataHub / ECS 三者关系
```
当前阶段:Sheet-Master + ECS Co-Pilot(副驾驶验证期)
Google Sheet ──→ [抓取/同步] ──→ DataHub.xlsx
↓
cache/finance/*.json
↓
Finance API (cache-first)
↓
/finance/* 页面(只读展示)
ECS(当前)= 读取、展示、验证
ECS(未来)= 主数据源(ECS-Master,尚未切换)
```
---
## 5. 当前主线模块(已完成 - Finance V1)
### 5.1 Finance Shell 入口
- 路由:`GET /finance`
- 左侧菜单分组(Finance / Owner / Operations / Tools)+ 可折叠
- iframe 框架,月份/车主选择器,语言切换(en/zh)
### 5.2 已完成页面(Finance V1 收口状态)
| 页面 | 路由 | 状态 |
|------|------|------|
| Dashboard | `/finance` → `dashboard` view | ✅ READY |
| Finance V1 Acceptance | `financeV1Acceptance` view | ✅ READY |
| Owner Detail | `/finance-owner-detail` | ✅ READY |
| Monthly Settlement Report | `/finance-owner-report` | ✅ READY |
| Owner Print | `/finance-owner-print` | ✅ READY |
| Expense Detail Report | `/finance-expense-report-v1` | ✅ READY |
| Payment Tracking | 内嵌 view | ✅ READY |
| Vehicle Anomalies | view | ✅ READY |
| Cache Refresh | `/finance-cache-refresh` | ✅ READY |
| Payout Debug | `/finance-payout-debug` | ✅ DEBUG工具 |
| Trip Income Debug | `/finance-trip-income-debug` | ✅ DEBUG工具 |
| Car Rule Preview | `/finance-car-trip-rule-preview` | ✅ DEBUG工具 |
### 5.3 Finance V1 线上验收状态(2026-05)
```
Owners = 5
Pay To Owners = $17,818.35
Paid/Remaining = $17,455.01 / $363.34
Henry = NEEDS_REVIEW(人工复核,不是系统错误)
Tan/Roy/Quenna/Ning W = READY
monthly-run-status: readyCount=5, missingCount=0, errorCount=0
```
---
## 6. 当前进行中模块(V3.2 Internal Ops)
### 6.1 /ops 入口
- 路由:`GET /ops`
- 内部运营后台,**不对车主开放**
- 当前全部只读,不写回 Google Sheet
### 6.2 V3.2 已落地步骤
| Step | 内容 | 状态 |
|------|------|------|
| Step2 | `/ops` 首页,模块卡片(Overview/Anomalies/Maintenance等)| ✅ |
| Step3 | Ops Overview Snapshot(复用 `/api/finance-dashboard-month`)| ✅ |
| Step4 | Vehicle Anomalies Snapshot(复用 `/api/finance-vehicles-month`)| ✅ |
| Step4 | ECS Table UI:`/ops/tables` + `/ops/table?tab=...` | ✅ |
| Step4.1 | Guide-A TabType 语义(Ctrl/Show)+ Sheet-first 策略 | ✅ |
| Step5 | 单表字段 checkbox 选择 + 搜索框 | ✅ |
| Step6 | Select All/Clear/Default10 + row limit(50/100/200/500) + sticky header | ✅ |
| Step7 | 分页 + 行顺序(Original/Reverse)+ localStorage 视图偏好 | ✅ |
| Step7.1 | 全量数据分页顺序修复(limit 语义改为 page size)| ✅ |
| Step7.2 | localStorage columns 对齐校验 + 越界修正 + Reset Saved View | ✅ |
---
## 7. 核心 API 清单
### Finance 核心 API(cache-first)
```
GET /api/finance-dashboard-month?month=YYYY-MM
GET /api/finance-owner-detail?owner=...&month=...
GET /api/finance-dashboard-master?month=...
GET /api/finance-owner-master?owner=...&month=...
GET /api/finance-monthly-run-status?month=...
GET /api/finance-owner-report-v1-calc?owner=...&month=...
GET /api/finance-owner-cards?month=...
GET /api/finance-vehicles-month?month=...
GET /api/finance-home-dashboard?month=...
```
### Cache 管理 API
```
GET /api/finance-cache-refresh ← 触发缓存刷新(写 JSON 文件)
GET /api/finance-cache-status ← 查询各 tab 缓存状态
POST /api/datahub-xlsx-upload ← 上传新版 DataHub.xlsx
GET /api/datahub-xlsx-status ← 查询 DataHub.xlsx 状态
```
### Debug API
```
GET /api/finance-payout-debug?...
GET /api/finance-trip-income-debug?tripId=...
GET /api/finance-op-compare-debug?owner=...&from=OP1&to=OP3
GET /api/finance-owner-op?owner=...
GET /api/finance-car-trip-rule-preview-note?...
```
### Settlement 草稿 API
```
GET /api/finance-owner-car-settlement-preview?owner=...&month=...
POST /api/finance-settlement-draft-save
GET /api/finance-settlement-draft-load
```
---
## 8. Cache 体系
### 8.1 cache-first 读取函数(server.js 内)
```javascript
loadFinanceCache(tabName) // 读 JSON cache
readDataHubTab(tabName) // 读 DataHub.xlsx(含 fallback Google Sheet)
readSheet(sheetId, tabName) // 直接读 Google Sheet
```
### 8.2 缓存覆盖 Tab 列表
**FINANCE_CACHE_TABS**(来源:DATA_HUB_SHEET_ID)
- A_overview, A_Records, A_Turo_Payouts, A_management_fee_rules
- Guide-Owner, Guide-OP-detail(+ 其他 Guide 表)
**FINANCE_MASTER_CACHE_TABS**(来源:DEFAULT_FINANCE_OUTPUT_SHEET_ID)
- Finance_Summary_Master, Finance_Cars_Master
- Finance_Reimbursement_Master, Finance_Batch_Overview
### 8.3 兼容性注意(已知坑)
```
- cached rows 可能是二维数组或对象数组,读取层必须兼容两种格式
- ownerMonthKey 可能是 owner__month 或 owner_month,两种都要支持
- rowsToObjectList() 负责标准化处理
```
---
## 9. 财务核心规则(禁止搞错)
```
✅ A_Turo_Payouts = 正式收入口径
❌ A_Records ≠ 正式收入(只做分类/参考)
✅ 收入以 Turo payout 入账月份为准
❌ 不按 trip start/end 做入账
✅ Paid By = 谁垫付(不代表费用归属)
✅ 费用归属 = Plate / Belongs To
✅ PaytoCost = 不参与利润分成(直接返还)
✅ reimbursement = 按类型拆分(toll/EV/ticket/gas/cleaning/smoking/damage)
✅ OP1/OP2/OP3 = PayOption 计算路径
PBP-100%:管理公司拿100%,车主拿0%
PBP-30%: 管理公司拿30%,车主拿70%
✅ Payment Batch = 判断同批 payout 多条记录归属关系(不能随便改逻辑)
✅ 车主打印版 = Owner Standard(不展示内部 debug 字段)
```
---
## 10. ECS 迁移路线(当前阶段)
```
当前:Sheet-Master + ECS Co-Pilot(只读验证)
迁移路线(已确认,不可跳步):
Google Sheet → ECS Table UI → ECS-Master → 后期数据库化
当前说明:Google Sheet 当前仍是主数据源;ECS 当前是副驾驶(展示、读取、测试、验证、逐步承接);ECS-Master 是未来单独大版本,不是当前 V3.2 目标;当前只预留 record_id、updated_at、sync_version、操作日志、冲突检测等未来能力;当前不新增写 Google Sheet,除非用户明确确认。
当前已完成:ECS Table UI(/ops/tables + /ops/table)只读展示
当前下一步:继续完善 ECS Table UI / Internal Ops 只读与验证能力。当前仍是 Sheet-Master + ECS Co-Pilot 副驾驶验证期,不进入 ECS-Master 正式切换。
强制规则:
- 每次只迁移一个模块
- 每个模块:先只读 → 再编辑 → 再主数据源切换
- 不允许直接废掉 Google Sheet 逻辑
```
---
## 11. 当前风险点
| 风险 | 说明 | 严重度 |
|------|------|--------|
| server.js 单体过大 | 所有逻辑在一个文件,维护难度高 | 中(已知,暂不重构)|
| cache 时效性 | cache 未自动定时刷新,依赖手动触发 | 中 |
| 二维数组兼容 | cached rows 格式不一致,需要 rowsToObjectList 适配 | 中 |
| ownerMonthKey 双格式 | owner__month vs owner_month 需要双向兼容 | 低-中 |
| Henry NEEDS_REVIEW | 人工复核状态,非系统错误,但需定期清理 | 低 |
| ECS-Master 未切换 | Google Sheet 仍是主数据源,Sheet 故障会影响数据刷新 | 中 |
| PM2 同机多进程 | 同机运行 Etsy Image Tool,误操作 restart all 会影响生产 | 高 |
| A_Records 误用风险 | 如果不熟悉规则,容易把 A_Records 当收入主口径 | 高 |
---
## 12. 当前下一步重点
### 近期(当前进行)
- V3.2 继续推进:`/ops` 模块从只读展示向 Maintenance/Repair/Clean/Task 扩展
- Finance V1 收口稳定观察期
### 中期(计划中)
- 当前下一步:继续完善 ECS Table UI / Internal Ops 只读与验证能力。当前仍是 Sheet-Master + ECS Co-Pilot 副驾驶验证期,不进入 ECS-Master 正式切换。
- Scraper 统一框架(当前 Turo 优先稳定)
- Owner Portal 正式化
- Checkout 后续完善
### 暂停中(不做)
- 大型重构
- 数据库化重写
- 新增写 Google Sheet 行为(需明确确认才能做)
---
## 13. 开发规则速查(AI 工具必读)
```
禁止事项:
❌ 直接修改 main 分支
❌ 直接操作 ECS 生产数据
❌ pm2 restart all / pm2 delete all
❌ 影响 BookCars / Etsy Image Tool(端口 4030)
❌ 新增写 Google Sheet(未经确认)
❌ 修改 OP1/OP2/OP3/PayOption/Payment Batch 计算逻辑
❌ 用 A_Records 替代 A_Turo_Payouts 做收入来源
❌ 把 Paid By 当费用归属
❌ 把 PaytoCost 纳入利润分成
每次改动必须说明:
✅ 改哪些文件
✅ 改哪些函数或路由
✅ 是否影响 API
✅ 是否影响 OP 财务规则
✅ 是否写 Google Sheet
✅ 是否影响 BookCars / Etsy Tool
```
---
## 14. 上下文缺失说明
以下内容从项目知识库中**无法完整确认**,需要人工补充:
1. **server.js 完整路由列表**:文件过大,只能读到片段,无法枚举所有路由
2. **FINANCE_CACHE_TABS 完整列表**:仅确认了部分 tab 名称,完整列表未完全可见
3. **Guide-A 当前收录的 Tab 列表**:ECS-Ctrl=yes 的表有哪些,未完全确认
4. **Settlement 草稿的完整状态机**:draft save/load/confirm 流程边界未完全可见
5. **Scraper 当前实际运行状态**:本地 Data Collector Machine 的实际抓取频率未知
6. **OP 公式具体数值**:管理费比例、停车费规则的具体数值未在文档中完整列出
7. **Owner 列表**:文档提到 Henry/Tan/Roy/Quenna/Ning W,是否有其他 owner 未确认
8. **BookCars 系统**:提到"不影响 BookCars"但无 BookCars 相关文档,边界不清楚
---
*本文档由 Claude 根据 QP Fleet 项目知识库自动生成。如有出入,以实际代码和 docs/ 目录下 MD 文档为准。*
- V3.2 Step8.3: Upgraded `/ops/cleaning-upload` to Fleet Ops Quick Entry Cleaning flow with plate search, lock/unlock, upload overlay, front-end compression, wake lock, and resumable upload prompts (overwrite/append).
- Added cleaning APIs: search-plates, init, upload-photo, finalize, log-error; writes to Cleaning_Submissions, VIN8 tab, clean tab (YES only), System_Log/System_Error.
- 2026-05-24:完成 V3.2 Step8.5,仅增强 `/ops/cleaning-today` 只读看板入口与筛选:新增顶部 Back/ViewAll/Refresh、新增 `plate/ctxLot/canDispatch/ctxWho` 筛选、保留 `?date=YYYY-MM-DD`,并增加空状态与只读声明;未改上传链路、Drive 层级、Finance/OP、Etsy/4030。
- 2026-05-24:A档快速PR完成 `/ops/cleaning-upload` 中文文案优化与员工可读性提升:页面标题、字段名、按钮文案改为中文优先;`Checkout` 调整为 `还车检查(暂未开放)`;`状态更新` 调整为 `车辆状态更新(暂未开放)`;保留既有三个入口与 Cleaning 上传主流程;未改任何 API、上传逻辑、Drive 层级及 Finance/OP/Etsy/BookCars。
- 2026-05-24:A档快速PR收口验收增强:`/ops/cleaning-upload` 顶部新增“当前仅开放:清洁完成上传;还车检查和车辆状态更新暂未开放。”提示,上传成功卡片按钮优化为“继续上传下一辆 / 查看今日清洁看板 / 查看该车记录”;`/ops/cleaning-today` 与 `/ops/cleaning-submissions` 改为中文优先文案(标题、筛选项、按钮、状态与空状态),未改筛选逻辑、读取逻辑、软删除逻辑,Cleaning 主流程进入收口验收阶段。
- 2026-05-24:A档快速PR修复 Cleaning 记录页批量隐藏交互:`/ops/cleaning-submissions` 与 `/ops/cleaning-today` 的“一键隐藏当前无效记录”改为收集当前页全部可标记删除项并逐条调用 `POST /api/ops/cleaning-submissions/mark-deleted`;无候选时提示“当前没有可隐藏的无效记录。”;批量完成提示成功/失败数量,并在完成后自动刷新当前筛选结果;未改上传逻辑、Drive删除、Sheet物理删除、Finance/OP/Etsy/BookCars。
- 2026-05-24:A档快速PR(验收收口文案与默认显示优化)完成:`/ops/cleaning-upload` 上传成功卡片新增验收提示“上传成功后,请到“今日清洁看板”确认记录是否有效。测试记录可在“清洁记录列表”中隐藏。”;`/ops/cleaning-today` 与 `/ops/cleaning-submissions` 按钮文案统一为“一键隐藏测试/失效记录”,并将状态说明改为“有效 = 文件夹还在;文件夹不存在/无链接 = 测试或失效记录;已删除 = 已隐藏记录。”;默认仍只显示有效记录(不显示 Deleted=YES 与无效记录);未改上传逻辑、Drive 层级、Google Sheet 写入逻辑及 Finance/OP/Etsy/BookCars。
- 2026-05-24:B档功能PR新增 `/ops/status-update` 草稿页与配套 API:新增 `GET /ops/status-update`、`GET /api/ops/status-update/search-plates`、`POST /api/ops/status-update/submit-draft`;页面支持车牌尾号搜索与锁车(数据源优先 `A_overview` / 本地 DataHub overview),提交只写入 `Status_Update_Drafts`(字段:submittedAt/plate/vinLast8/owner/oldStatus/newStatus/submittedBy/note/sourcePage/rawPayload),明确提示“当前为状态更新草稿,不会改变真实车辆状态”;未改 Finance API/OP公式、Cleaning上传逻辑、Drive层级、Etsy/4030/BookCars。
- 2026-05-25:V3.1 Step5F-0 开始,新增 `GET /finance-owner-legacy-report-v1` 旧报表数据结构复刻页(四模块:结算汇总 / 收益明细 / 报销费用明细 / 花销费用明细),参数支持 `month` + `owner`,优先复用 `finance-owner-detail`、`finance-owner-report-v1-calc`、`finance-expense-flow-debug` 现有读取结果;缺失字段统一前端显示“暂无数据 / 缺少字段”,不报错。未改 Finance 公式、未写 Google Sheet、未影响 OP / Cleaning / Etsy / BookCars。
- 2026-05-25:C档前置最小修复仅针对 Dashboard UI/状态读取层:修复 `/finance-dashboard` 月份默认来源(优先 URL `?month=YYYY-MM`,无则当前月/既有默认),并确保页面 `monthInput` 与实际请求 `/api/finance-dashboard-month` 的 month 一致;顶部新增只读口径提示(当前加载月份、Finance Master cache-first + Google Sheet fallback、Owner 搜索仅列表筛选不触发重算);`/finance` 外壳在 dashboard 视图下禁用 Owner 输入并提示“Dashboard 为全车主汇总,Owner 只用于 Owner 页面”;“Recalculate This Month”降权为“管理员重算(会写入 Sheet,耗时较久)”并新增明确确认弹窗。未改财务公式、未改 OP 规则、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly` 计算逻辑、未新增 Google Sheet 写入、未改 Cleaning/Etsy/BookCars。
- 2026-05-25:C档核心工程前置诊断新增只读页面与只读 API:新增 `GET /finance-dashboard-debug` 与 `GET /api/finance-dashboard-debug`,用于定位 Dashboard 在 `month=2026-03` 时 Owners/PayToOwners 为 0 的缺失层级(cache、Google Sheet fallback、OwnerMonthKey、月份格式)。诊断输出覆盖 `Finance_Summary_Master` / `Finance_Cars_Master` / `Finance_Reimbursement_Master` / `Finance_Batch_Overview` 四表的来源、总行数、月匹配行、样例 key、列名、cache 状态与 sheet fallback 状态,并给出下一步建议。明确只读:不写 Google Sheet、不触发重算、不改公式。未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未改 OP 规则、未改 Cleaning/Etsy/BookCars。
- 2026-05-25:V3.1 Step5F-1 最小修复完成,目标为 2026-03 Finance Master 结果层与 cache 链路(不改财务公式/OP 规则)。`/api/finance-batch-monthly` 新增批量完成后自动刷新 Finance Master 本地缓存(默认开启,可用 `refreshCache=0` 关闭),并在响应返回 `cacheRefreshed/cacheRefreshResults`;同时 `/api/finance-cache-refresh` 支持 `outputSheetId` 参数,刷新 Master cache 时按目标输出表读取,不再固定默认表。本次修复涉及 2026-03 月份生成链路与缓存一致性:Google Sheet 写入保持原有四张 Master 表写入逻辑;cache 刷新改为明确执行;未改 OP 公式、未改 Guide-OP-detail 规则、未改 A_Records/A_Turo_Payouts 口径、未改 Cleaning/Etsy/BookCars。
- 2026-05-25:V3.1 Step5F-2 完成,旧版车主月报复刻页 `/finance-owner-legacy-report-v1` 四模块切换为真实数据口径:结算汇总与收益明细优先读取 `Finance_Summary_Master`/`Finance_Cars_Master`(通过 `/api/finance-owner-detail` cache-first 链路);报销费用明细优先读取 `Finance_Reimbursement_Master`(按 PaidBy 归集展示);花销费用明细读取 `A_Flow` 调试口径(按 owner/BelongsTo/Plate 归集展示),并明确与报销分离。缺字段统一显示“缺少字段/暂无数据”且页面不报错。未改 Finance 公式、未写 Google Sheet、未影响 OP / Dashboard / Cleaning / Etsy / BookCars。
- 2026-05-25:V3.1 Step5F-3 最小字段对齐修复:`/finance-owner-legacy-report-v1` 修正 month 提示逻辑(仅非法 `YYYY-MM` 才提示)、花销费用日期展示统一为 `YYYY-MM-DD`(仅前端显示格式化,不改原数据)、花销“费用类型/备注”增加多字段兼容(`feeType/Fee Type/费用类型/category/Type/备注说明/Note`),收益明细增加车辆字段别名兼容读取(出租率/出租天数/出租次数/均价尝试 `UtilizationRate/RentalDays|RentalDayCount/TripCount|RentalCount/AvgDailyRate|AveragePrice` 及中文别名)。未改 Finance 公式、未改 OP 规则、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未新增 Google Sheet 写入。
- 2026-05-25:V3.1 Step5F-4 A档快速 PR 完成:`/finance-owner-legacy-report-v1` 仅展示层优化,新增顶部数据来源轻提示;结算汇总按“收入类/扣费类/返还报销类/最终结果”分组并高亮“最终应付车主”;收益明细为出租率/出租天数/出租次数/均价缺失值改为“待接月度统计”,并新增“运营字段待接 A_Records 月度统计”说明;报销费用明细与花销费用明细各自补充口径说明文案。未改 Finance 公式、未改 OP 规则、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未涉及 Google Sheet 写入。
- 2026-05-25:V3.1 Step5F-5 A档快速 PR 完成:修复 `/finance-owner-legacy-report-v1` 结算汇总中“最终应付车主 Final Net To Owner”标签被当作 `<span ...>` 文本显示的问题,改为正常中英文字段名展示并保留最终金额绿色高亮;同时在 Finance 入口页新增“旧版车主月报预览”快捷入口(`/finance-owner-legacy-report-v1`),方便日常验收进入旧版预览。未改 Finance 公式、未写 Google Sheet、未影响 OP/Cleaning/Etsy/BookCars,未改 `/api/finance-step6` 与 `/api/finance-batch-monthly`。
- 2026-05-25:V3.1 Step5F-6 完成,旧版车主月报 `/finance-owner-legacy-report-v1` 收益明细补齐运营统计字段(出租率/出租天数/出租次数/均价):优先读取 `Finance_Cars_Master` 既有字段,缺失时通过新增只读接口 `GET /api/finance-owner-legacy-monthly-stats` 按 `owner+month+plate` 从 `A_Records` 当月行程补算(tripCount、rentalDays、utilizationRate、avgDailyRate,均价基于 `Finance_Cars_Master.rentalBaseIncome / rentalDays`);无行程显示“0/暂无行程”,不再大面积显示“待接月度统计”。本次仅展示层与只读读取增强,未改 Finance 公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet。
- 2026-05-25:V3.1 Step5F-7 完成,旧版车主月报 `/finance-owner-legacy-report-v1` 修正口径:报销费用明细由“汇总”改为“A_Flow 明细行”并按 `PaidBy=owner` 过滤(不再按 Belongs To 判定报销);花销费用明细继续按 Belongs To 归属口径展示并保留说明;收益明细“车型”改为读取 overview/Finance Master 车型字段,不再固定平台字样;出租率/出租天数/出租次数/均价继续走 `A_Records` 只读补算链路并按跨月重叠天数计算。未改公式、未写 Sheet、未影响 OP/Dashboard/Cleaning/Etsy/BookCars,未改 `/api/finance-step6` 与 `/api/finance-batch-monthly`。
- 2026-05-25:V3.1 Step5F-8 完成,`/finance-owner-legacy-report-v1` 报销费用明细改为专用 PaidBy 口径读取:扩展 `/api/finance-expense-flow-debug` 只读参数 `mode`(`paidBy` / `belongsTo` / `ownerAny`)与 `paidByOwner`,页面改为双通道读取(报销明细仅 `mode=paidBy&paidByOwner=owner`;花销明细继续 `mode=belongsTo`)。报销列表保留展示 Belongs To 字段但不参与筛选。未改 Finance 公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet、未影响 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-25:V3.1 Step5F-10 完成,新增 `GET /finance-owner-legacy-report-v2` 纯后端渲染版本,保留旧页 `/finance-owner-legacy-report-v1` 不改动;V2 支持 `month=YYYY-MM&owner=车主名`,四模块(结算汇总/收益明细/报销费用明细/花销费用明细)均由服务端渲染输出,避免 inline JS 拼接语法风险。数据口径保持现有规则:结算汇总与收益明细读取 `finance-owner-detail`(`Finance Master / owner detail`)并继续只读接入 `A_Records` 运营补算;报销费用明细读取 `A_Flow` 且仅按 `PaidBy=owner` 过滤(不按 `Belongs To` 限制);花销费用明细读取 `A_Flow` 且按 `Belongs To=owner`。同时在 Finance 入口新增“旧版车主月报 V2”按钮。未改 Finance 公式、未改 OP 规则、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet、未影响 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-25:V3.1 Step5F-7 补丁修复完成(仅 A_Records 车牌匹配):`/api/finance-owner-legacy-monthly-stats` 与 `/api/finance-owner-legacy-monthly-stats-debug` 的 Vehicle 车牌提取由旧的 `CA ` 固定截断逻辑改为 `extractPlateFromVehicleText` + 统一 normalize(转大写、去 `#`、去空格、仅保留字母数字);修复 `Henry (CA #9NLY246)` 等文本被提取成 `#9NLY24` 的问题,匹配前对 `ownerPlates` 与 `extractedPlate` 同口径标准化,确保 `ownerPlateMatched` 与 `statsByPlate` 有效统计恢复。未改 Finance 公式、未改 OP 规则、未写 Google Sheet、未改 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-26:V3.1 Step5F-9 完成,`/finance-owner-legacy-report-v2` 新增展示层展开/收起交互:收益明细、报销费用明细、花销费用明细支持模块级折叠(默认:收益/报销展开,花销收起);收益明细每车新增“查看行程/收起行程”(数据来自 `/api/finance-owner-legacy-monthly-stats` 已参与本月统计的 overlap 行程);每车新增“查看费用/收起费用”(数据来自 `A_Flow` 只读链路 `/api/finance-expense-flow-debug`,按 `month+plate` 过滤,不按 PaidBy/owner 筛选)。本次仅改 V2 展示与必要只读读取,未改公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet,未影响 V1 与 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-26:A档文档任务完成,新增 `docs/QP_FINANCE_DATA_MAP.md`(Claude 审计结果正式化):补齐财务系统总数据流、数据源总表、`A_Turo_Payouts / A_Records / A_Flow / A_overview` 关系、字段字典、收入口径、费用归属 vs 报销归属、API 数据地图、页面数据地图、cache-first 策略、出租天数/出租率/报销费用/车辆花销推导例子、已知坑点。仅文档更新;未改代码、未改 `server.js`、未写 Google Sheet、未改 Finance 公式、未改 OP、未触碰 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-26:V3.1 Step5F-10(V2 打印与车主可读版优化)完成,`/finance-owner-legacy-report-v2` 仅展示层优化:页面标题改为“车主月度报表 Owner Monthly Report”并在顶部展示车主与月份;四模块保留且默认状态为收益明细展开、报销费用明细展开、花销费用明细收起、每车行程/每车费用明细收起;结算汇总“最终应付车主 Final Net To Owner”按正负金额高亮(正数绿色、负数红色并提示车主需向公司补付);表格统一金额右对齐、长备注自动换行、费用明细收据链接统一显示“查看收据”;新增打印样式,打印时隐藏返回链接/加载按钮/技术说明与调试弱文案,仅保留标题、月份、车主、四模块及当前展开内容;内部版本提示移至页底弱化展示。未改财务公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet、未改 V1 与 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-26:V3.1 Step5F-11(旧版车主月报 V2 最终验收收口记录)完成:`/finance-owner-legacy-report-v2` 最小可用版已收口;已完成项包括结算汇总展示、收益明细报告、出租率/出租天数/出租次数显示、每车行程展开、每车费用展开、报销费用明细按 `PaidBy=owner`、花销费用明细按 `Belongs To=owner`、展开/收起、打印样式与车主可读版基础美化。本次仅文档更新,不改 `server.js`、不改 API、不改公式、不改 OP、不写 Google Sheet,且未触碰 Dashboard/Cleaning/Etsy/BookCars。
- 2026-05-26:V3.3 Step1(Finance Monthly Snapshot 基础层)完成最小可用底座:新增 `POST /api/finance-snapshots/system-auto`(生成 System Auto Version,默认不覆盖,已存在返回 alreadyExists)与 `GET /api/finance-snapshots`(按 owner/month 查询 snapshot 列表);新增 `/finance-snapshots` 内部管理页(查询 + 生成 + 列表)。Snapshot 存储采用本地 JSON 目录 `data/finance-snapshots/`,文件名 `<month>__<ownerNormalized>__system-auto.json`。本次仅新增 snapshot 新层,不改 Finance 公式、不改 OP 规则、不改 `/api/finance-step6`、不改 `/api/finance-batch-monthly`、不写 Google Sheet、不做 Manual Adjustment。
- 2026-05-26:V3.3 Step1.1(Snapshot 详情查看与基础预览)完成:新增只读 API `GET /api/finance-snapshots/:snapshotId`,可返回 snapshot `raw`(完整 JSON)与 `detail`(基础信息、数据摘要、关键金额、车辆明细预览);`/finance-snapshots` 列表页新增“查看详情”按钮与页面内只读详情预览区域。仅查看不编辑;未改 Finance 公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet、未做 Manual Adjustment/Display Version 切换/Lock。
- 2026-05-26:V3.3 Step1.4(Snapshot 管理页基础收口优化)完成:仅优化 `/finance-snapshots` 只读管理页展示体验(未改 Snapshot 保存逻辑、未改 Snapshot API 业务逻辑)。新增 completeness 颜色标识:`Complete` 绿色、`Missing Cars/Missing Expenses/Missing Stats/Missing Summary` 橙色、`Need Review` 红色;新增解释文案(`Complete = 可作为人工调整底稿`、`Need Review = 数据不完整,不建议进入人工调整`);增强筛选提示(默认提示输入 month/owner,owner 为空表示查看该月份全部 owner,month 为空表示查看全部 snapshot);对 `Missing/Need Review` 旧测试快照增加“不完整(保留旧测试快照,不删除)”标记;页面底部新增下一步提示(Step1 完成后进入 Step2 Manual Adjustment,且 Manual Adjustment 只基于 Complete snapshot)。未改 Finance 公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet、未做 Manual Adjustment。
---
## 12. V3.3 Step2-0(Manual Adjustment 设计)
- 2026-05-26:已新增 `docs/QP_FINANCE_MANUAL_ADJUSTMENT_PLAN.md`。
- 本次仅完成 Manual Adjustment 设计文档(业务规则、数据结构、处理流程、边界、Step2 拆分)。
- 当前仍处于“文档先行”阶段,**尚未进入代码实现**。
- 明确未改:`server.js`、API、Finance 公式、OP、step6、batch monthly、Google Sheet 写入链路。
- 2026-05-26:V3.3 Step2.1(Manual v1 创建最小版)完成:新增 `POST /api/finance-snapshots/manual-v1`,仅允许基于 `Complete + SYSTEM_AUTO` 快照创建 `Manual v1` 本地 JSON(文件名 `<month>__<ownerNormalized>__manual-v1.json`),写入最小 adjustment 字段(`adjustmentType/plate/amount/reason`)并复制 base snapshot `data`,默认不覆盖已存在 manual-v1(返回 `alreadyExists:true`)。`/finance-snapshots` 页面新增“创建 Manual v1”按钮(仅 Complete + SYSTEM_AUTO 显示)和最小表单提交流程。未做重算、未改公式、未改 OP、未改 `/api/finance-step6`、未改 `/api/finance-batch-monthly`、未写 Google Sheet。
---
## 13. Staging 测试环境规划(2026-05-26 新增)
为避免未充分验证代码直接影响正式环境,新增 staging 规划:
- Production 保持不变:`/opt/qp-fleet-system` + `qp-fleet-system` + `4020`
- Staging 新增环境:`/opt/qp-fleet-system-staging` + `qp-fleet-staging` + `4021`
- 发布流程更新:PR 合并后先部署 staging,staging 验收通过后再部署正式 4020。
- 运维禁令继续生效:禁止 `pm2 restart all` / `pm2 delete all`;禁止误碰 Etsy Image Tool(4030)。
- 2026-05-26:V3.3 Step2.2(Manual v1 PAYOUT_ADJUSTMENT 最小重算版)完成:Manual v1 从 System Auto 派生后执行最小重算,仅处理 `PAYOUT_ADJUSTMENT`。在 `data.cars` 按 plate 查找目标车并更新收入字段(兼容 `Income_Payout/incomePayout/income/totalIncome/ownerIncome`),同步更新车辆最终结算字段(兼容 `FinalSettlement/finalSettlement/Final Net To Owner/finalNetToOwner/netToOwner`),并同步更新 `data.summary` 的 `Total Income` 与 `Final Net To Owner`(字段存在时)。新增 `adjustmentsApplied`、`adjustmentSummary`。不覆盖 System Auto、不写 Google Sheet、不改 finance-step6/batch monthly。
- 2026-05-26:V3.3 Step2.3(Manual v1 Difference Preview 最小版)完成:`GET /api/finance-snapshots/:snapshotId` 新增 `detail.diffPreview` 只读差异预览,`/finance-snapshots` 详情区新增 “Manual差异预览” 展示。仅用于查看 base vs manual 的最小差异,不修改 Manual v1 创建逻辑、不改 System Auto、不写 Google Sheet、不改 Finance 公式/OP/step6/batch monthly。
- 2026-06-05:V3.3 Step2.6(Owner Display Version 最小版)完成:新增 `POST /api/finance-snapshots/display-version`,入参 `snapshotId`,仅允许 `MANUAL` snapshot 被设置为车主展示版;同 owner/month 下所有 snapshot 先统一写 `isOwnerDisplayVersion=false`,再将目标 Manual 写为 `true`,保存本地 JSON。`/finance-snapshots` 页面顶部新增 Owner Display Version 说明,Manual 行新增“设为车主展示版 / 当前展示版”,System Auto 展示版显示“默认展示版”。本阶段只做 snapshot 层标记,不接入正式 Owner Report / PDF / 车主看板,不做权限、锁定或完整审计;未改 Finance 公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`、System Auto 生成逻辑、Manual v1 重算逻辑,且不写 Google Sheet。`/system-nav` 已同步更新 Owner Display Version 导航项,指向 `/finance-snapshots`。
- 2026-05-27:A档快速PR(staging 4021)完成 `/ops/cleaning-upload` 重复上传冲突收尾:当同日期+同车辆已有清洁照片/目录时,前端改为中文傻瓜式二选一(“补充上传(推荐)/ 覆盖重传”);覆盖重传新增二次确认(“确认覆盖上传?删除后无法恢复”);后端 `init` 支持 `resumeMode=replace` 并仅清空当天该车目标目录,不影响其他日期/车牌;`finalize` 在 `Cleaning_Submissions.rawPayload` 记录 `uploadMode`(`supplemental upload` / `replace upload`)。影响范围仅 Cleaning Upload 轻支线;Google Sheet 仍写 `Cleaning_Submissions`(附模式标记)、VIN8、clean(YES)、System_Log;不影响 Finance API/OP/Owner Report/A-guide/BookCars/Checkout 主系统。部署/验收状态:仅 staging 4021 推进,待页面验收,不发布 4020。
- 2026-05-27:A档快速PR(staging 4021)继续修正 `/ops/cleaning-upload` 重复上传弹窗交互:废弃浏览器原生 confirm,改为页面内移动端友好三选项弹窗(`覆盖上传 / 追加上传 / 取消上传`);其中“覆盖上传”增加二次确认弹窗(返回 / 确认覆盖上传),文案明确“删除后无法恢复”;行为保持:追加= `supplemental upload`(保留旧照片并追加)、覆盖= `replace upload`(仅清空同日期同车目录后上传本次照片)、取消=中止本次流程且不写入。边界保持不改其他日期/车辆/车牌及 Finance API / A-guide / Owner Report / BookCars。
---
## 15. 配置表每日快照 V1(2026-05-28)
- 新增关键配置表 ECS 本地 JSON 快照保护,覆盖:`Guide-A`、`A_management_fee_rules`、`Guide-Owner`、`Guide-OP-detail`、`A_overview`。
- 快照保存到当前项目目录下的 `cache/snapshots/YYYY-MM-DD/{TabName}.json`;production 为 `/opt/qp-fleet-system/cache/snapshots/...`,staging 为 `/opt/qp-fleet-system-staging/cache/snapshots/...`。
- 新增内部手动 API:`POST /api/ops/config-snapshots/create`,可指定 `tabs` 与 `reason`;`tabs` 为空时默认快照全部关键配置表。
- 快照 JSON 仅保存只读备份字段:`tabName`、`reason`、`createdAt`、`rowCount`、`rows`。
- 保留策略:每日快照仅保留最近 7 天,清理范围限制在 `cache/snapshots` 日期文件夹内。
- `POST /api/ops/guide-a/low-risk-update` 在执行 Guide-A 低风险写入前,会先自动创建 `Guide-A` 快照,`reason=before-guide-a-low-risk-update`。
- 当前 V1 不做恢复页面、不做 diff 页面、不做一键还原;手动快照 API 不写 Google Sheet,不影响财务计算、Cleaning Upload、Owner Report、BookCars 或 Etsy Image Tool。
---
## 2026-05-31 流水追加:PR #295~#300 Internal Ops 文档补录
> 本记录按流水追加,不覆盖旧记录。核对范围为当前 `main` 代码中的 Cleaning / Claim / Checkout 上传链路、公共停车场逻辑、CallHandling 自动 Follow-up 与相关 QP 文档。
| 日期 | PR编号 | 完成内容 | 影响模块 | 是否影响 Google Sheet | 是否影响 Finance | 是否部署到 4021 | 是否部署到 4020 | 下一步计划 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 2026-05-31 | #295 | 补录 Cleaning/Claim EXIF UX 状态:Cleaning 默认去 EXIF,高级选项折叠;Claim 默认保留 EXIF;Drive 文件夹按 EXIF 模式区分;CallHandling 记录 EXIF/时间戳口径。 | Cleaning Upload、Claim Upload、Drive Evidence、CallHandling | 是:Cleaning 写 `Cleaning_Submissions.rawPayload`;Claim 写 `Claim_Evidence_Submissions.PhotoMode/RawPayload` | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | 若 Cleaning 需要独立 `PhotoMode` 列,另开 Sheet migration / backfill 任务 |
| 2026-05-31 | #296 | 补录 Checkout V1 scaffold 与停车场逻辑统一:`/ops/checkout-upload`、`Checkout_Submissions`、Drive Folder、Overview Location -> Parking Lot 映射、本次实际位置记录。 | Checkout Upload、A_overview、Drive Evidence、RawPayload | 是:写 `Checkout_Submissions` 与 `System_Log` | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | C2.2 继续完善 Checkout Evidence / Claim Candidate |
| 2026-05-31 | #297 | 补录 Checkout parking lookup 与照片限制:锁车后读取系统位置,员工选择实际位置;位置变更写入 Note / CallHandling / RawPayload;Checkout 采用单照片上传组织模式。 | Checkout UI、Parking Lot Logic、Evidence Upload | 是:写 `Checkout_Submissions.RawPayload/Note` | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | 后续补只读查看页与证据完整性校验 |
| 2026-05-31 | #298 | 补录 Compact Checkout UI、Check-in odometer 与 mileage 修正:最近行程默认一行、详情展开、每天允许里程可改、Allowed Mileage / Driven Mileage / Over Mileage 自动计算。 | Checkout UI、A_Records、Mileage | 是:写 `Checkout_Submissions` 里程相关字段 | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | C2.2 明确 mileage 异常处理与人工确认规则 |
| 2026-05-31 | #299 | 补录 Checkout Upload V1 layout 优化:车辆信息紧凑展示,车况与理赔候选默认收起,备注统一在提交前,理赔候选自动汇总到备注 / Follow-up。 | Checkout UI、Claim Candidate、CallHandling | 是:写 `Checkout_Submissions.ClaimCandidates/Note/RawPayload` | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | 将 Claim Candidate 与正式 Claim 边界继续固化到 C2.2 |
| 2026-05-31 | #300 | 补录 Cleaning / Claim / Checkout 位置选择与自动备注统一逻辑:系统位置来自 Overview,实际位置来自员工现场选择,位置变更仅记录本次操作,不直接改 Overview。 | Cleaning Upload、Claim Upload、Checkout Upload、Parking Lot Logic、CallHandling | 是:写各自 submissions 的 Note/RawPayload,并写 CallHandling | 否 | 需以部署记录为准,本文档未验证运行中实例 | 需以部署记录为准,本文档未验证运行中实例 | 后续如需永久改 Overview,必须设计独立审批/确认流程 |
### 2026-05-31 当前 Checkout 阶段结论
- Checkout 当前属于 **C2.1 Checkout Upload V1 / 基础版**。
- 已包含 Vehicle Lock、Trip Lookup、Mileage、Fuel、Evidence Upload、Claim Candidate、CallHandling、Parking Lot Logic。
- 下一阶段:**C2.2 Checkout Evidence & Claim Candidate**。
- 后续阶段:**C2.3 Vehicle Health**、**C2.4 Maintenance Reminder**。
### 2026-05-31 当前仍未写入/未完全落地的内容
- Cleaning 独立 `PhotoMode` 列尚未在当前代码中落地;目前是 `rawPayload.stripExif/uploadMode` 口径。
- Checkout 独立 `PhotoMode` 字段尚未落地。
- Claim Candidate 仍不是正式 Claim;正式 Claim 编号、状态机、财务影响与结算口径尚未落地。
- 4021 / 4020 实际部署状态未通过本次文档任务验证,需以部署流水或 PM2/服务器检查为准。
## 2026-05-31 当前状态补录:C2.1a Task Engine Lite
- Cleaning / Claim / Checkout 上传成功后会生成自动 Task#:`CLN-YYMMDD-XXXX`、`CLM-YYMMDD-XXXX`、`CHK-YYMMDD-XXXX`,日期按洛杉矶时间,后四位在写入前读取 CallHandling 避免与既有 Task# 重复。
- CallHandling 仍是 Lite 版任务承载层:Task# 写入 `Phone#`/`Phone` 列,`Status=Follow-up`,`任务类型=S1`,`Call Type=Other`,车牌字段写完整车牌,Agent 留空。
- CallHandling Description 已从多行长描述改为短单行;Drive 文件夹链接和后台下一步说明移入 `To do` 列。
- Cleaning_Submissions / Claim_Evidence_Submissions / Checkout_Submissions 的 RawPayload 同步保存 Task#;若提交表缺少 `Task#` 表头,后端会自动补表头。
- `/ops/cleaning-upload`、`/ops/claim-upload`、`/ops/checkout-upload` 顶部都有默认收起的“今日自动任务状态”栏;展开后调用 `GET /api/ops/tasks/today` 从 CallHandling 只读统计今日 total / Follow-up / Done / 类型数量和最近任务。
- 本阶段没有引入账号、权限、Done 编辑或独立 Task 表;也不影响 Finance、Guide-A 主控逻辑、Owner Report、BookCars、Overview 写入或 Turo 自动提交。
## 2026-05-31 当前状态补录:C2.1b Task Center / 任务中心 Lite
- `/ops/tasks` 已作为 Fleet Ops 任务中心入口落地,用于查看 Cleaning / Claim / Checkout 自动生成的 Task#,并处理 Follow-up 到 Done 的闭环。
- 数据源仍为 `CallHandling`,第一版不新增独立 Task 表;后端仅读取 `Phone#` / `Phone` 中符合 `CLN-YYMMDD-XXXX`、`CLM-YYMMDD-XXXX`、`CHK-YYMMDD-XXXX` 的行。
- `GET /api/ops/tasks` 支持按洛杉矶日期、Status、任务类型和车牌筛选,并返回 total / Follow-up / Done / Cleaning / Claim / Checkout 统计及任务列表。
- `POST /api/ops/tasks/update-status` 按 Task# 查找行,只支持自动任务从 `Follow-up` 标记为 `Done`;写入 Google Sheet 的 `CallHandling.Status`,如有 `Done By` 字段则写 `Done By`,否则写 `Agent`。
- 更新状态时不修改 `Description` / `To do`,不删除行,不允许非 Task# 行被更新,也不让前端用 rowNumber 直接写表。
- `/ops/cleaning-upload`、`/ops/claim-upload`、`/ops/checkout-upload` 的今日自动任务状态栏仍保留,并增加“查看任务中心”入口。
- 当前仍是 Lite 版:无登录系统、无复杂权限、无独立任务数据库、无 Need Review / Waiting Owner 等扩展状态。
- 影响边界保持不变:不影响 Cleaning / Claim / Checkout 上传主流程;不影响 Finance / Guide-A / Owner Report / BookCars / Overview。
## 2026-05-31 当前状态补录:C2.1c Task Center 小闭环增强
- `/ops/tasks` 任务详情已扩展显示 Task#、类型、车牌、Status、任务类型 / Priority、创建时间、Description、To do、Drive 文件夹链接、Submitter、Done By、Done At 与 RowNumber debug;Drive 链接从 `To do` 中提取,原文仍保留。
- Done 闭环现在要求前端页面内确认并填写 / 选择完成人;后端拒绝空 doneBy,按 Task# 更新 Google Sheet `CallHandling` 的 `Status=Done`、`Done By` 和洛杉矶时间 `Done At (YYYY-MM-DD HH:mm)`。
- `CallHandling` 若缺少 `Done By` / `Done At` 表头会在更新前自动补表头;兼容旧表的 `Agent` 回退逻辑保留,但优先写 `Done By`。
- `GET /api/ops/tasks` 已支持 `taskId` 查询,用于 `/ops/tasks?taskId=CLN-260531-0001` 这类成功卡片跳转定位;命中后页面高亮并自动展开该任务,未命中则显示空结果提示。
- Cleaning / Claim / Checkout 上传成功卡片新增“查看任务中心”入口,链接优先带 Task#;今日自动任务状态栏仍保留,并将 Follow-up 入口指向 `/ops/tasks?status=Follow-up`。
- 本阶段仍不新增独立 Task 数据库,不做复杂权限 / 复杂状态,不修改正式 Claim Candidate 审批,不影响 Finance、Guide-A、Owner Report、BookCars、Overview 写入或 Turo 自动提交。
## 2026-05-31 当前状态补录:C2.1d Ops Upload & Task Center 验收收口版
- 当前阶段进入 C2.1d 验收收口:Cleaning / Claim / Checkout / Task Center 先做到员工能看懂、后台能验收、上线前能测试,不继续扩新功能。
- 新增 `/ops/help` 员工使用说明页,覆盖清洁上传、理赔上传、还车检查 Checkout、Task# 含义、Follow-up 处理、Done 含义、Google Drive 文件夹查看方式与常见提醒。
- `/ops/tasks` 默认显示 `Follow-up`,页面明确显示“当前显示:Follow-up”,顶部显示“今日未处理 X 条”,并提供“今日未处理 / 今日已完成 / 全部任务 / 刷新任务”入口。
- Done 任务仍可通过筛选显示,但默认不抢占任务中心页面;移动端任务卡片更紧凑,突出 Task#、Plate、Type、Status,并在有值时显示 Done By / Done At 小字。
- `/ops/cleaning-upload`、`/ops/claim-upload`、`/ops/checkout-upload`、`/ops/tasks` 均已增加 `/ops/help` 使用说明入口。
- 新增 `docs/QP_OPS_UPLOAD_TEST_CHECKLIST.md` 作为上线前人工验收清单,覆盖 Cleaning、Claim、Checkout、Task Center 与 4021 / 4020 上线检查。
- 上传流程、Task# 生成规则、Done By / Done At 写入逻辑均保持不变;不影响 Finance、Guide-A、Owner Report、BookCars、Overview 写入或 Turo 自动提交。
- 下一步:先按清单验证 4021,确认无误后再决定是否同步 4020。
## 2026-05-31 当前状态补录:C2.1e Ops Upload & Task Center 4021 验收修复包
- 本次按 `docs/QP_OPS_UPLOAD_TEST_CHECKLIST.md` 对 Cleaning / Claim / Checkout / Task Center / Help 做收口验收修复,只处理明显 bug、UI 状态与链接/筛选问题,不扩展新业务功能。
- 已确认 `/ops/help` 可作为员工说明入口,并提供返回清洁上传、理赔上传、Checkout 与任务中心的导航;本次未改动该页面业务范围。
- 修复 `/ops/tasks` 的“全部任务”快捷按钮:前端点击后会清空日期筛选并显示全部状态;后端 `GET /api/ops/tasks` 现在区分“未传 date”(默认今天)与“传空 date”(全部日期),避免“全部任务”仍被锁在当天。
- `/ops/tasks` 的当前筛选提示增加日期上下文:默认显示 `Follow-up(YYYY-MM-DD)`,全部任务显示 `全部任务(全部日期)`,便于 4021 验收时判断按钮状态是否正确。
- Cleaning / Claim / Checkout 上传成功卡片已有 Task# 与“查看任务中心”入口,CallHandling 自动 Follow-up 逻辑保持原样;本次未改写上传主流程、Drive 上传流程、Overview 写入规则或 Finance 相关逻辑。
- 本次未修项 / 未扩项:未新增独立 Task 数据库、未新增复杂权限/状态机、未改 Finance、Guide-A、Owner Report、BookCars、Overview 写入,也未自动 merge 或部署到 4020。
## 2026-05-31 当前状态补录:C2.1g Business Validation
- 当前进入 **C2.1g Business Validation(Ops Upload & Task Center 真实业务验证)**,任务类型为 B 档业务验证,不是新增功能开发。
- 验证范围为 Cleaning Upload、Claim Upload、Checkout Upload 与 Task Center,目标是确认员工能在真实业务中独立完成上传、Task# 反馈、Follow-up 查看和 Done 闭环。
- 已新增 `docs/QP_OPS_REAL_WORLD_VALIDATION.md` 作为真实业务验证记录表,覆盖 Cleaning / Claim / Checkout / Task Center 检查项、问题等级和 4020 上线标准。
- 当前暂停新增业务功能:不改 API,不改 Finance,不改 Guide-A,不改 Owner Report,不改 BookCars,不改 Overview。
- 阶段原则:先验证、后上线;只有满足连续 3 天真实使用、Cleaning >= 10、Claim >= 5、Checkout >= 5、Task Center >= 20、无 BLOCKER、无未修复 MAJOR、员工可独立完成后,才建议同步 4020。
## 2026-05-31 当前状态补录:C2.1f Roy UX Round 1(A档 UX 优化)
- `/ops/cleaning-upload`、`/ops/claim-upload`、`/ops/checkout-upload` 已完成 Roy 第一轮 UX 收口:顶部主导航只保留三类上传入口,`使用说明` 改为标题说明下方小号蓝色链接。
- 三页已删除“停车场逻辑”说明块;页面不再展示开发解释文本,锁车后的系统位置 / 实际位置提示仍保留在车辆信息区。
- 顶部日期与提交人已收口为同一行,减少首屏空白和滚动距离。
- 三页提交区新增默认勾选的 `同时创建 Follow-up 任务(推荐)`;取消勾选时仍写 Cleaning / Claim / Checkout Submission,但跳过 Task# 生成、CallHandling 写入和 Task Center 入口。
- 勾选时 Task# 规则和 Task Center Lite 行为保持不变;本次不影响 Finance、Guide-A、Owner Report、BookCars、Overview 写入、Drive 上传逻辑、Done By / Done At 或 MD 同步。
## 2026-05-31 当前状态补录:C2.1f-3 CallHandling 字段与文案收口
- Cleaning / Claim / Checkout 上传后写入 `CallHandling` 的 Status 已从 `Follow-up` 改为 `Waiting Reply`,Task Center 页面显示可用“等待处理”。
- 自动创建任务时 `Agent` 列保持留空,不再写 Roy / Geng / Henry / Tan 等提交人;提交人信息继续保留在各 Submission 表和 `RawPayload` 中。
- `Description` 已按三个流程统一简化:开头带提交人姓名,不再显示 `备注:`,没有备注时不追加备注字段,也不会出现 `备注:无`。
- `To do` 列只写 Drive 文件夹 URL,不再写“链接:”或“下一步:”。
- `createTask=false` 的跳过逻辑保持不变:不写 `CallHandling`,不生成 Task#;Task# 仍写入 `Phone#` / `Phone` 列。
- Task Center 默认识别 `Waiting Reply` 并可从 `Waiting Reply -> Done`;同时兼容旧 `Follow-up` 自动任务,避免历史记录不可见或无法 Done。
- 本次不影响 Submission 写入、Drive 上传、Finance、Guide-A、Owner Report、BookCars、Overview 或 Turo 自动提交。
## 2026-06-01 当前状态补录:C2.1f-4 Checkout Description 异常优先
- Checkout 上传后写入 `CallHandling.Description` 已改为异常优先短格式;无异常时只显示“无异常”和 Checkout 里程,例如 `Henry还车检查;无异常,里程:169999`。
- 有异常时才显示异常内容,并按超里程、缺油、理赔候选、位置变更、员工备注合并为中文逗号分隔的短描述,例如 `Henry还车检查;异常:超里程85,缺油,理赔候选:清洁;里程:169999`。
- `Checkout_Submissions` 和 `RawPayload` 仍保留 checkinMileage、checkoutMileage、drivenMileage、allowedMileage、overMileage、fuelLevel、lowFuel / fuelShortage、claimCandidates、note 等详细字段;本次只简化 `CallHandling.Description`。
- 本次不改 Cleaning / Claim,不改 Drive 上传,不改 Checkout_Submissions 字段,不改 Task# 规则,也不改 Task Center 主逻辑;Task Center 仅自然显示新的 Description。
## 2026-06-05 当前状态补录:System Navigation Center V0.1
- 新增系统导航中心页面:`GET /system-nav`。
- 当前页面定位为 QP Fleet System 功能地图,用于统一收口 Finance、Owner Report、Snapshot、Ops Table UI、Guide-A、Cleaning Upload、Scraper / DataHub 等已完成、开发中、暂停、调试和历史入口。
- 当前只在 staging 4021 使用,不发布 4020;完成后只创建 PR,不自动 merge。
- 导航树由 `server.js` 中 `SYSTEM_NAV_ITEMS` 人工确认维护,不自动扫描全部路由。
- 后续每完成一个新功能或新页面,必须判断是否同步挂入 `/system-nav`;Codex Summary 必须包含“是否需要更新 `/system-nav`:是/否”。
- 本次仅新增导航中心页面和文档,不改已有业务逻辑、不改已有 API、不写 Google Sheet、不改 Finance 公式 / OP / `/api/finance-step6` / `/api/finance-batch-monthly`。
## 2026-06-05 当前状态补录:V3.3 Step2.7 Snapshot Monthly Result Builder
- 当前财务主线窗口继续只在 staging 4021 推进,未发布 4020,完成后只创建 PR、不自动 merge。
- `/finance-snapshots` 顶部新增“月报结果构建 / Rebuild Monthly Result”区域,用于输入 `month + owner` 后构建或刷新本月 System Auto Snapshot。
- 新增 `POST /api/finance-snapshots/monthly-build`:入参为 `month`、`owner`、`force`,基于现有 cache-first Finance API / ECS cache 读取链路组装 System Auto Snapshot,不依赖 `/finance` 页面 Calculate 当前状态。
- monthly-build 仅写入或刷新 `data/finance-snapshots/<month>__<owner>__system-auto.json`,不同月份 snapshot 按文件名隔离,构建 `2026-05` 不影响 `2026-03` 已有 snapshot。
- `/finance-snapshots` 页面展示 Data Source Status Panel:数据模式、cache status、last refresh time、source APIs;旧“生成 System Auto Snapshot”按钮保留并标注为旧/调试入口。
- 本阶段不创建 Manual v1,不设置新的 Owner Display Version,不改正式 Owner Report / PDF / 车主看板,不写 Google Sheet。
- 未改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,未触碰 Etsy / BookCars / Cleaning / A-guide / Checkout / Scraper 支线。
- `/system-nav` 已同步更新 Snapshot 管理页说明与 Snapshot API List 说明;不新增单独导航入口,因为功能位于既有 `/finance-snapshots` 页面。
## 2026-06-08 当前状态补录:V3.3 Step2.8 Monthly Build 防降级覆盖保护
- 当前财务主线窗口继续只在 staging 4021 推进,未发布 4020;完成后只创建 PR、不自动 merge。
- `POST /api/finance-snapshots/monthly-build` 已增加 System Auto 防降级覆盖保护:目标不存在时正常创建;目标存在且 `force=false` 时保持 existing 返回逻辑;目标存在且 `force=true` 时先比较 existing / new completeness,再决定是否写文件。
- 若已有 System Auto 为 `Complete`,而新构建结果不是 `Complete`,默认不写入文件并返回 HTTP 409、`success:false`、`error:"would_downgrade_existing_complete_snapshot"`,以及 `existingCompleteness / newCompleteness / existingSnapshotId / path / counts / dataSourceStatus`。
- API 可选支持显式布尔值 `allowDowngrade:true`;只有显式传入时才允许 Complete 被 Missing / Need Review 覆盖。`/finance-snapshots` 页面默认不传 `allowDowngrade`。
- `/finance-snapshots` Monthly Result Builder 已增加防降级说明;拦截时醒目提示“已有 Complete 快照已保留;本次构建结果不完整,未覆盖。”;构建结果为 Missing / Need Review 时提示“不建议进入 Manual Adjustment”;Data Source Status Panel 继续显示。
- 本阶段未修改 Manual v1 重算逻辑、Owner Display Version 逻辑、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,不写 Google Sheet,未触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars 支线。
- `/system-nav` 无需更新:本次没有新增页面或独立入口,既有 `/finance-snapshots` 导航已覆盖该能力。
## 2026-06-08 当前状态补录:V3.3 Step2.9A Monthly Build cars 数据来源诊断与修复
- `buildSystemAutoSnapshotPayload(owner, month)` 的 cars 主来源改为直接读取现有 cache-first `Finance_Cars_Master`,不再把 legacy owner-detail 的严格 `OwnerMonthKey` 等值匹配结果作为 monthly-build cars 的首选来源。
- cars 匹配对 owner 做 `trim + 连续空格收口 + lowercase`,并兼容 `Owner` / `OwnerName` 字段;month 匹配兼容 `Month` / `ReportMonth` / `SettlementMonth`、`OwnerMonthKey` 尾部的 `YYYY-MM` / `YYYY-M`,以及日期字段。
- monthly-build 响应与 snapshot payload 新增 cars diagnostics:`carsSource`、`carsRowsScanned`、`carsRowsMatchedOwner`、`carsRowsMatchedMonth`、`carsRowsMatchedOwnerMonth`、`ownerNormalized`、`month`,用于解释 cache 存在但 cars=0 的具体匹配层级。
- expenses 继续沿用 owner-detail / owner-expense-report-v1 链路;因此原先 expenses 有数据但 cars=0,是两个数据域使用不同来源与匹配逻辑造成,不代表 Finance_Cars_Master cache 为空。
- 本次仅修复 monthly-build / System Auto payload 的 cars 读取与诊断;不改 Manual v1、Owner Display Version、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,不写 Google Sheet,不触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 无需更新:未新增页面、API 路由或独立入口,能力仍位于既有 `/finance-snapshots` monthly-build。
## 2026-06-08 当前状态补录:V3.3 Step2.10 Snapshot Version Chain 最小版
- 当前财务主线窗口继续只在 staging 4021 推进,不发布 4020;完成后只创建 PR、不自动 merge。
- Finance Snapshot 新增最小版本链 helper:`normalizeSnapshotOwner(owner)`、`listSnapshotsByOwnerMonth(owner, month)`、`getNextSnapshotVersionNumber(owner, month, versionType)`、`buildVersionedSnapshotId(...)`、`buildVersionedSnapshotPath(...)`;同一个 owner + month 可保存多个 `SYSTEM_AUTO` / `MANUAL` snapshot 文件。
- `POST /api/finance-snapshots/monthly-build` 新增可选布尔值 `createNewVersion:true`:每次调用生成新的 `<month>__<ownerNormalized>__system-auto-vN.json`,不会覆盖旧版本,并将前一个 System Auto snapshotId 写入 `baseSnapshotId`。未传或为 `false` 时继续沿用固定 `<month>__<ownerNormalized>__system-auto.json` 的兼容行为。
- 新版本 JSON 明确写入 `snapshotId / owner / month / versionType / versionName / versionNumber / baseSnapshotId / isOwnerDisplayVersion / isLocked / createdAt / updatedAt / completeness / data`。
- `GET /api/finance-snapshots` 与详情读取兼容固定旧格式 `system-auto.json` 和既有 `manual-v1.json`,并为旧文件推断最小版本元数据;`/finance-snapshots` 新增“生成新 System Auto 版本”按钮,列表和详情新增显示 `versionNumber / versionName / baseSnapshotId`。
- 本阶段不修改 Manual v1 创建/重算核心逻辑,不修改 Owner Display Version 逻辑,不写 Google Sheet;未改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,未触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 无需更新:本次没有新增页面或独立路由入口,版本链操作仍位于既有 `/finance-snapshots` 页面,既有导航项已覆盖。
## 2026-06-08 当前状态补录:V3.3 Step2.11 Manual vN from any Snapshot 最小版
- 当前财务主线窗口继续只在 staging 4021 推进,不发布 4020;完成后只创建 PR、不自动 merge。
- 新增 `POST /api/finance-snapshots/manual-version`,允许从任意现有 `SYSTEM_AUTO`(legacy / vN)或 `MANUAL` snapshot 派生新的 Manual 版本;旧 `POST /api/finance-snapshots/manual-v1` 保留并复用相同 Manual vN 创建逻辑。
- 同 owner/month 的 Manual 自动取下一个 `versionNumber`,使用 `<month>__<ownerNormalized>__manual-vN` / `Manual vN`;每次写新文件,不覆盖旧 Manual,并记录入参 `baseSnapshotId`。
- 继续仅支持 `PAYOUT_ADJUSTMENT` 最小重算;新 Manual 固定 `isOwnerDisplayVersion=false`、`isLocked=false`,不设置 Owner Display Version,不写 Google Sheet。
- `/finance-snapshots` 的 SYSTEM_AUTO 与 MANUAL 行均可“从此版本创建 Manual”,表单明确显示 baseSnapshotId / owner / month / base versionName,保存后刷新列表。
- 未改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,未触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 无需更新:未新增页面或独立导航入口,能力仍位于既有 `/finance-snapshots` 页面。
## 2026-06-08 当前状态补录:V3.3 Step2.11A Snapshot 列表 completeness helper 修复
- 恢复 PR #319 重构时意外删除的既有 `getSnapshotCompletenessStatus(input)` helper,修复 `GET /api/finance-snapshots` 因 helper 未定义而返回 `success:false` 的问题。
- Snapshot 列表继续通过既有 `buildFinanceSnapshotCompletenessFromPayload(payload)` 计算缺失 completeness;可正常列出同 owner/month 下的 legacy System Auto、System Auto vN 与 Manual vN。
- 本次只恢复既有 helper,不改 Manual vN 创建逻辑、Owner Display Version、monthly-build、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,不写 Google Sheet,不触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 无需更新:未新增页面、API 路由或独立入口,修复发生在既有 Snapshot 列表接口内部。
## V3.3 Step2.12 — Owner Display Version 支持任意 Snapshot 版本(2026-06-08)
- `POST /api/finance-snapshots/display-version` 现可按 `snapshotId` 将任意 legacy System Auto、System Auto vN 或 Manual vN 设置为同 owner/month 的唯一 Owner Display Version。
- 设置时只更新同 owner/month snapshot 的 `isOwnerDisplayVersion`:目标为 `true`、其余为 `false`;不改变版本链字段、调整内容或 data,不做重算、不写 Google Sheet。
- 目标不存在返回 `snapshot_not_found`;目标 `isLocked=true` 返回 `snapshot_locked`,本阶段禁止切换至 locked 版本。
- `/finance-snapshots` 已对所有 SYSTEM_AUTO / MANUAL 版本展示“当前展示版” badge 或“设为车主展示版”按钮,locked 版本按钮禁用;成功后刷新列表。
- 未修改 Manual vN 创建、monthly-build、Finance 原始公式、OP、step6 或 batch monthly;仅推进 staging 4021,不发布 4020。
- `/system-nav` 判断:无需更新;既有 Owner Display Version 导航项已指向 `/finance-snapshots`,本次未新增页面、路由或独立入口。
## 2026-06-08:V3.3 Step2.13 Owner Report 读取 Owner Display Version
- staging 4021 财务主线已将单一稳定页面 `/finance-owner-legacy-report-v2?month=YYYY-MM&owner=...` 接入 Owner Display Version snapshot,只读展示,不发布 4020。
- Owner Report 先通过 `findOwnerDisplaySnapshot(owner, month)` 查找同 owner/month 下 `isOwnerDisplayVersion=true` 的 snapshot;命中后优先使用 snapshot 的 `data.summary / cars / reimbursements / expenses / stats`,并显示 Data Source Status Panel。
- Data Source Status Panel 显示 `dataMode=SNAPSHOT`、snapshotId、versionType、versionName、versionNumber、baseSnapshotId、completeness、isOwnerDisplayVersion;Manual vN 额外显示 adjustmentSummary。
- 若没有 Owner Display Version,则保持原 Owner Report 旧读取链路并显示 `dataMode=LEGACY_FALLBACK` 与警告;若 snapshot completeness 非 Complete,仍允许只读预览并显示警告。
- 边界:未改 Manual vN 创建、display-version 设置、monthly-build、Finance 原始公式、OP、step6、batch monthly;不写 Google Sheet;不触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 判断:无需更新;本次没有新增页面或入口,只增强已有 Owner Report 页面读取路径与状态说明。
## 2026-06-08 - V3.3 Step2.14 Owner Report Snapshot 展示层收口
- staging 4021 财务主线已完成 `/finance-owner-legacy-report-v2` 的 snapshot 展示层适配;不发布 4020。
- `dataMode=SNAPSHOT` 时,结算汇总兼容 snapshot 现有字段名,并对缺失字段显示 `-` / `字段缺失`,不再把缺失金额误显示为 `$0.00`,也不显示 `undefined` / `null`。
- cars 区块兼容 plate、income / payout、expense、management fee、parking fee、reimbursement 与 final settlement 等已有字段;只读取 snapshot,不新增计算。
- reimbursements、expenses、stats / trips 增加 snapshot 可读展示和明确的“本版本无 ... 数据”空态。
- Manual Owner Display Version 且存在 `adjustmentSummary` 时,Data Source Status Panel 下方显示“人工调整说明”,包含 totalPayoutAdjustment、affectedPlates、recalculationMode、baseSnapshotId,并明确报表读取人工调整后的 snapshot、不是重新现算结果。
- 未修改 snapshot 创建、Manual vN、display-version、monthly-build、Finance 原始公式、OP、step6、batch monthly 或任何写入逻辑;未写 Google Sheet。
- `/system-nav` 判断:无需更新,因为仅优化既有 Owner Report 页面展示,未新增页面、路由或入口。
## 2026-06-08 当前状态补录:V3.3 Step2.15 Snapshot 管理页 Owner Report 展示版入口
- 当前财务主线窗口继续只在 staging 4021 推进,不发布 4020;完成后只创建 PR、不自动 merge。
- `/finance-snapshots` 在明确查询 owner + month 后,列表上方新增 Owner Display Version 展示版摘要卡片,展示 snapshotId、versionType / versionName、versionNumber、completeness、baseSnapshotId;MANUAL 展示版额外展示 affectedPlates 与 totalPayoutAdjustment。
- 摘要卡片新增“查看车主报表”入口,直达 `/finance-owner-legacy-report-v2?month=<month>&owner=<owner>`;列表每行也保留同 owner/month 的辅助入口。
- 没有展示版时,页面明确提示“尚未设置 Owner Display Version / 请先在下方选择一个版本设为车主展示版”,不报错。
- 本步骤仅优化 `/finance-snapshots` 展示入口;未改 snapshot 写入、display-version API、Owner Report 读取、Manual vN、monthly-build、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,不写 Google Sheet,未触碰支线。
- `/system-nav` 无需更新:没有新增页面或独立导航目标,既有 Snapshot 管理页与 Owner Report 路由已覆盖本次页内跳转。
## 2026-06-08 — V3.3 Step2.15A `/finance-snapshots` 前端渲染修复
- 根因:`/finance-snapshots` 的服务端 HTML 模板把内联 JS 中用于 source APIs tooltip 的 `\n` 转换为真实换行,导致浏览器解析内联脚本时报语法错误,`loadList()` 未执行,Owner Display Version 摘要与 snapshot rows 均保持空白。
- 修复范围仅限 `/finance-snapshots` 前端渲染:转义 tooltip 换行、增加加载/成功/失败状态、空列表“无数据”行,并隔离摘要渲染错误,确保摘要异常时 snapshot rows 仍可显示。
- Owner Display Version 摘要继续显示既有 snapshot/version/completeness/baseSnapshotId 字段,MANUAL 继续显示 affectedPlates / totalPayoutAdjustment,并保留“查看车主报表”入口。
- 未修改 display-version API、monthly-build、Manual vN 创建、Owner Report 读取、Finance / OP / step6 / batch monthly,也未写 Google Sheet;无需更新 `/system-nav`。
## V3.3 Step2.16 — Owner Report 正式入口整理(2026-06-08)
- staging 4021 财务主线新增正式入口 `GET /finance-owner-report-v3`,页面标题为“车主月度报表 Owner Monthly Report”。
- 正式入口与兼容入口 `GET /finance-owner-legacy-report-v2` 共用同一个 `renderFinanceOwnerReportV3` 渲染处理器;旧入口保留,不重新实现或改变 Owner Report 数据读取逻辑。
- 两个入口均继续优先读取 Owner Display Version snapshot;未设置时继续使用 `LEGACY_FALLBACK`。Data Source Status Panel 保持展示,MANUAL 展示版继续显示人工调整说明。
- `/finance-snapshots` 的“查看车主报表”入口已统一指向 `/finance-owner-report-v3?month=<month>&owner=<owner>`。
- `/system-nav` 已更新:新增正式 Owner Report 示例入口,并保留旧 V2 为 `LEGACY` 兼容/历史入口;根 Admin Console 的 Owner Report 卡片也同步指向正式入口。
- 本次不发布 4020;不改 snapshot 创建、Manual vN、display-version、monthly-build;不写 Google Sheet;不改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`;不触碰支线。
## 2026-06-08 当前状态补录:V3.3 Step2.17 Owner Report V3 打印 / PDF 友好版第一版
- 当前财务主线窗口继续只在 staging 4021 推进,不发布 4020;完成后只创建 PR、不自动 merge。
- 仅优化正式入口 `/finance-owner-report-v3` 的页面展示与浏览器打印体验:页面顶部新增“打印 / 保存 PDF”按钮,点击后调用 `window.print()`;本阶段不新增服务端 PDF API。
- 正式入口打印时隐藏 month / owner 输入框、加载按钮、返回 Finance、折叠/明细操作按钮和其他浏览器操作区;保留报表标题、车主、月份、结算汇总、车辆明细、reimbursement / expense / stats,以及 Manual 展示版的业务可读人工调整说明。
- Data Source Status Panel 在屏幕上继续完整显示;正式入口打印时压缩为 `Snapshot: <snapshotId> / <versionName> / <completeness>` 一行小字。
- 正式入口增加专用 `@media print` 样式:白色背景、淡化卡片边框、打印时展开主报表区块、重复表头、降低表格截断与 section 标题分页切开风险。旧入口 `/finance-owner-legacy-report-v2` 继续可打开并保留原有打印规则。
- 未改 Owner Report 数据读取逻辑;未改 snapshot 创建、Manual vN 创建、display-version、monthly-build;未写 Google Sheet;未改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`;未触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 无需更新:本次没有新增页面、路由或导航目标,只增强既有正式 Owner Report V3 的打印体验。
## 2026-06-08 当前状态补录:V3.3 Step2.18 Snapshot Manager Lite
- staging 4021 财务主线已将既有 `/finance-snapshots` 整理为 Snapshot Manager Lite 最小友好版;不发布 4020,完成后只创建 PR、不自动 merge。
- 页面现包含顶部 owner/month 查询区、紧凑 System Auto 构建区与 Data Source Status、当前 Owner Display Version 摘要、卡片式快照版本列表、无快照空态、Manual 创建表单,以及默认收起的“高级详情 / Debug”。
- 当前展示版摘要显示 snapshotId、versionType / versionName、versionNumber、completeness、baseSnapshotId;Manual 额外显示 totalPayoutAdjustment、affectedPlates、recalculationMode,并提供 Owner Report V3 / 打印保存 PDF 跳转入口。
- 每个版本卡片显示版本、完整性、展示版、锁定、baseSnapshotId、创建/更新时间及 Manual adjustmentSummary;支持查看详情、设置展示版、从任意 System Auto / Manual 创建 Manual、查看车主报表。locked 版本设置按钮保持禁用。
- 所有“查看车主报表”及“打印 / 保存 PDF 报表”链接统一指向 `/finance-owner-report-v3?month=<month>&owner=<owner>`;本阶段不新增真正 PDF API。
- 本次只优化 `/finance-snapshots` 页面展示与交互;未改 snapshot 创建、Manual vN、display-version、monthly-build、Owner Report V3 读取、snapshot JSON 数据结构或旧 API 行为。
- 未写 Google Sheet;未改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`;未触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 判断:无需更新。Snapshot Manager Lite 仍使用既有 `/finance-snapshots` 路由,未新增页面、路由或独立导航目标,现有 Snapshot 管理入口已覆盖。
## V3.3 Step2.19 — Owner Report V3 Snapshot Version Picker(2026-06-08)
- 状态:staging 4021 财务主线完成开发;不发布 4020;仅创建 PR、不自动 merge。
- 仅优化正式 `/finance-owner-report-v3` 页面展示与版本选择体验:Data Source Status Panel 附近新增 Snapshot Version Picker,列出同 owner/month 的 legacy `system-auto`、`system-auto-vN` 与 `manual-vN`。
- 正式入口新增可选 `snapshotId` query 参数;未传时仍优先读取 Owner Display Version,传入且属于当前 owner/month 时以 `SNAPSHOT_SELECTED` 只读预览指定版本。
- 版本卡片显示 versionName、versionType、versionNumber、completeness、当前展示版/当前预览 badge;Manual 额外显示 totalPayoutAdjustment 与 affectedPlates。预览非展示版时显示浅黄色提示,并只链接到 Snapshot Manager 设置展示版。
- 打印 / 保存 PDF 保留;打印时隐藏 Version Picker 与提示/操作入口,只保留当前实际预览 snapshot 的压缩简要信息。旧入口 `/finance-owner-legacy-report-v2` 保持兼容且不突出 Version Picker。
- 未改 snapshot 创建、snapshot JSON、Manual vN 创建、display-version API、monthly-build API、Owner Report 计算、Finance 原始公式、OP、step6、batch monthly;未写 Google Sheet;未触碰支线。
- `/system-nav` 判断:无需更新,因为没有新增页面、路由或独立导航目标,既有 Owner Report V3 与 Snapshot Manager 导航已覆盖。
## V3.3 Step2.19A — Owner Report V3 Snapshot Schema Compatibility Guard(2026-06-08)
- 状态:仅在 staging 4021 财务主线完成读取侧兼容保护;不发布 4020;完成后只创建 PR、不自动 merge。
- `/finance-owner-report-v3` 读取 snapshot 时新增 `safePick`、`safeMoney`、`safeText`、`safeArray`、`safeSummaryValue`,对 summary / cars / reimbursements / expenses / stats 的缺失、类型异常和历史别名做安全降级。
- 主界面缺字段统一显示 `-`、对应“本版本无 ... 数据”空态,或“本版本缺少部分明细字段,已按可用字段展示。”;技术详情仅进入默认收起且打印隐藏的 Debug / 诊断区域。
- Snapshot Version Picker 只读取 snapshotId、versionType、versionName、versionNumber、completeness、isOwnerDisplayVersion 与 adjustmentSummary 简要字段,不依赖 snapshot 明细字段。
- 边界确认:未改 snapshot JSON、snapshot 创建、Manual vN 创建、display-version、monthly-build、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`;未写 Google Sheet;未触碰支线。
- `/system-nav` 判断:无需更新;本步骤仅增强既有 Owner Report V3 的 snapshot 读取容错,没有新增页面、路由或独立导航目标。
## 2026-06-08 — V3.3 Step2.20 Owner Report V3 Inline Adjustment 第一版
- 当前财务主线仅在 staging 4021 推进;不发布 4020;完成后只创建 PR、不自动 merge。
- 正式 `/finance-owner-report-v3` 的车辆收益明细行新增业务友好的“调整”入口;旧 `/finance-owner-legacy-report-v2` 不显示该入口。
- 调整表单在当前报表上下文中显示当前车辆、当前预览版本,并要求填写可正可负的调整金额与必填原因;负数减少车主结算,正数增加车主结算。
- 提交继续复用既有 `POST /api/finance-snapshots/manual-version` 与 `MINIMAL_PAYOUT_ADJUSTMENT_V1`,以当前正在预览的 snapshot 为 base 创建下一条 Manual vN;成功后跳转到带新 `snapshotId` 的正式 Owner Report V3 预览页。
- 新 Manual 继续固定 `isOwnerDisplayVersion=false`;页面提示如确认用于车主展示,应在 Snapshot Manager 设置为展示版。非展示预览版本与非 Complete 版本均允许打开表单,并分别显示基于当前预览生成、建议先确认完整性的提示。
- 当前版本无 cars 时不显示调整按钮,并显示“本版本无车辆收益数据,无法在报表内调整。”;打印 / 保存 PDF 隐藏调整按钮、表单、成功提示和诊断信息,只保留报表结果与已有人工调整说明。
- 边界:仅新增 Owner Report V3 Inline Adjustment 入口;不新增后端调整算法,不改 Manual vN 创建规则,不写 Google Sheet,不改 Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`、monthly-build、display-version,也不触碰 A-guide / Cleaning / Checkout / Scraper / Etsy / BookCars。
- `/system-nav` 判断:无需更新;本次没有新增页面、路由或独立导航目标,既有 Owner Report V3 正式入口已覆盖该页内能力。
## 2026-06-09 — V3.3 Step2.23 Snapshot Manager Lite 去技术化与操作收口
- `/finance-snapshots` 已收口为业务快照管理台:主界面只展示查询月份、车主、版本数、当前展示版、完整状态和是否需要生成新版本;技术状态、raw snapshotId、path、原始 JSON、force 开关和旧入口统一进入默认收起且打印隐藏的 Debug / 高级详情。
- 主操作文案已统一为“查询快照”“生成本月新快照”“重新生成基础快照”;当前展示版摘要与 Manual / System Auto 版本卡片按业务所需信息分别展示。
- 仅修改 Snapshot Manager 页面展示与前端交互;未改 snapshot 创建、Manual vN、display-version、monthly-build、Owner Report V3、Finance / OP / step6 / batch monthly,未写 Google Sheet,未触碰支线。
- `/system-nav` 无需更新:未新增页面、路由或导航目标。
## 2026-06-09 — V3.3 Step2.23A Snapshot Manager Lite 内联脚本转义修复
- 修复 `/finance-snapshots` 服务端 HTML 模板中 Debug 路径文本的换行转义:模板源码使用双重反斜杠,确保浏览器实际收到的内联 JavaScript 保留 `\n` 转义,而不是在单引号字符串中产生真实换行并触发 `SyntaxError`。
- 已对 `month=2026-04&owner=Henry` 对应模板实际渲染结果提取内联脚本并通过 `node --check`;页面加载链路可继续渲染已加载数量、当前展示版摘要和版本卡片。
- 边界:仅修改 `/finance-snapshots` 内联脚本转义;未改 snapshot 创建、Manual vN、display-version、monthly-build、Owner Report V3、Finance 原始公式、OP、`/api/finance-step6`、`/api/finance-batch-monthly`,未写 Google Sheet。
- `/system-nav` 无需更新:未新增页面、路由或导航目标。
## V3.3 Step2.26 — Snapshot 删除功能闭环(2026-06-09)
- Snapshot Manager Lite 现支持通过 `DELETE /api/finance-snapshots/:snapshotId` 删除普通 System Auto vN / Manual vN;删除动作只移除 `data/finance-snapshots`(或配置的 snapshot 目录)内对应 `.json` 本地文件,不写 Google Sheet。
- 删除 API 对 snapshotId 做严格字符与目录边界校验,拒绝路径穿越;不存在返回 `snapshot_not_found`,当前车主展示版、locked 版本和 legacy 固定 `system-auto` 基础快照分别受保护且不可删除。
- `/finance-snapshots` 在版本卡片操作区展示删除操作;受保护版本显示业务友好的禁用说明。删除前会二次确认并说明版本移除且不可恢复,成功后刷新版本列表;若删除当前高级详情预览版本,则退出预览状态。
- Debug / 高级详情继续默认收起;业务操作区不显示 raw path 或删除技术参数。
- 边界确认:未修改 Owner Report V3 读取、Manual vN 创建、monthly-build、display-version 设置、Finance 原始公式、OP、step6 或 batch monthly;未写 Google Sheet,未触碰其他业务支线。
- `/system-nav` 无需更新:本次没有新增页面或导航目标,删除能力位于既有 Snapshot Manager Lite 页面。
## 2026-07-15 文档同步流水日志
| 日期 | PR | 更新内容 | 文档同步范围 |
| --- | --- | --- | --- |
| 2026-07-15 | Align GitHub project docs with latest QP Brain and Finance state | 对齐 QP 大脑、当前 main、财务 PR / LD 页面、统一需求库中 Settlement Algorithm v1 / Finance Debug / Snapshot / Owner Report 已确认口径;修正 staging 一次执行部署命令;明确 `QP_CURRENT_STATUS.md` 为历史状态。 | `docs/QP_CURRENT_STATE.md`、`docs/QP_FINANCE_RULES.md`、`docs/QP_PROJECT_OVERVIEW.md`、`docs/QP_CURRENT_STATUS.md`。 |
## 2026-07-15 — Settlement Algorithm Version Registry Prototype
- Added the Monthly Settlement Algorithm Registry prototype with only `monthly-settlement-v1` registered as ACTIVE. The registered v1 implementation points directly to the existing native monthly settlement builder, so the financial calculation output is unchanged.
- Added read-only API `GET /api/finance/settlement-algorithms` for UI version selectors.
- Native Snapshot generation accepts `algorithmVersion`, validates it through the registry, and stores permanent metadata: `algorithmFamily`, `algorithmVersion`, `algorithmLabel`, `implementationRevision`, `algorithmStatus`, and `generatedAt`.
- Snapshot Manager displays the selected snapshot algorithm label and exposes the Snapshot-generation algorithm selector from the registry. Versioned Native snapshot file IDs remain `native-vN`, so different generations do not overwrite older snapshots; future algorithm versions can coexist as separate Native versions.
- Finance Debug Monthly Settlement mode accepts `algorithmVersion`, resolves the calculation through the registry, returns algorithm metadata, and keeps Earning Date mode unchanged.
- Owner Report reads algorithm metadata from the selected Snapshot instead of replacing it with the current ACTIVE version. Missing metadata is compatible and displayed as `Legacy / Unknown`.
## 2026-07-20 — Monthly Settlement v1 Freeze and initial v2 isolation (historical milestone)
- `monthly-settlement-v1` is now permanently bound in the registry to `buildOwnerMonthFinanceResultMonthlySettlementV1(...)`. The existing Native compatibility entry point delegates to frozen v1 for non-versioned callers; versioned Snapshot and Debug requests resolve through the registry.
- Added `buildOwnerMonthFinanceResultMonthlySettlementV2(...)` as the separate composition entry point. **Current status (2026-07-27): V2 is ACTIVE and Snapshot-enabled**; it deliberately calls the frozen V1 core and adds classification/reporting.
- Finance Debug and Snapshot Manager expose both versions. The current label is `Monthly Settlement V2 · Current`; V1 is `Monthly Settlement V1 · Legacy / Frozen`.
- Snapshot Manager defaults to V2 Current and permits V2 Snapshot generation. V1 remains available explicitly for legacy compatibility and historical audit.
- Regression fixture: Quenna / `2026-06` / `8VHA555` remains `679.92` under both v1 and v2. No existing amounts, stored Snapshots, Google Sheets, or production configuration were changed.
## Owner Report QR Owner View — Phase 1(2026-07-26)
- 状态:已完成代码与自动化测试,staging 人工扫码/打印验收待 PR 部署后执行。
- 管理员正式 Owner Report 仅在 `Complete` 的 Owner Display Version 上生成二维码;Live Preview、Native fallback 和指定版本预览不分享。
- 公开只读路由为 `/owner-report/view/:token`。token 为服务端密钥 HMAC-SHA-256 派生的 256-bit、base64url 不透明值,不包含 owner、month 或 snapshotId。
- `data/owner-report-shares/shares.json` 仅持久化 token SHA-256 hash 到固定 snapshotId 的映射及 `createdAt/status/revokedAt`;密钥保存在权限为 `0600` 的相邻 `.key` 文件,映射采用临时文件 + rename 原子写入。
- Owner View 严格执行 token → snapshotId → frozen payload;快照缺失、token 无效或已撤销统一返回“该结算报告暂时无法访问”,绝不 fallback 到实时计算、最新版本、owner+month 或 Legacy。
- 底层已提供 active link 复用、revoke 与 revoke 后 regenerate;本 Phase 不增加管理 UI。
- Owner View 为 mobile-first,只呈现结算汇总、车辆、行程、费用和有数据时的报销;不 render Finance 导航、打印设置、人工调整、Diagnostics 或算法信息。
- 本次 PR:`Add secure QR owner view for monthly settlement reports`;commit 随本文件同一提交记录。
- 明确未实现:Owner 账号、登录、历史月份、Dashboard、邮件/SMS 和完整 Owner Portal。
## 2026-07-27 — Monthly Settlement Workflow Final Closure
**Monthly Settlement Workflow Complete**
- **Finance V2 Income Classification Phase Complete**:V2 保持为 V1 payout-first 结果的解释层;不增加 Trip、不改变 payout 金额或 settlement month,也不回写 V1。
- **QR Owner View Phase Complete**:管理员 Owner Report、打印 / PDF 与 QR Owner View 均读取二维码绑定的同一 frozen Owner Display Snapshot。公开 Owner View 不实时计算、不按 owner/month 查找替代版本,并在 token 无效、撤销或 snapshot 缺失时 fail closed。
- **Monthly Settlement Workflow Complete**:最终闭环为 V1 Settlement → V2 Classification → Snapshot → Manual Adjustment → Display Version → Owner Report → Print/PDF → QR → Owner View。
- Display Version 读取优先级保持为:显式合法 `snapshotId` 仅用于管理员只读预览;正常管理员报表读取 Owner Display Version;QR Owner View 只读取 token 固定绑定的 snapshotId。二维码、token 与 share 映射均不参与财务计算。
- Manual Adjustment 始终从 base Snapshot 派生新的 Manual vN,原 Snapshot 不被覆盖;新 Manual 默认不是 Display Version。历史版本文件与其算法元数据保持不变,新计算只创建新版本。
- staging 固定回归 `Quenna / 2026-06` 已确认:Income `$679.92`、Rental Income `$567.90`、Reimbursements `$112.02`、Management Fee `$169.98`、Operating Expenses `$127.74`、Net Income `$312.20`;Trip `57749216` `$247.86`、`58261959` `$320.04`、`57752269` `$112.02`;Fuel `$87.74`、Extra Distance `$24.28`;Expense Gas `$77.74`、Repair `$50.00`。
- `/ld/finance/owner-view-test` 继续是 staging-only fixture;production 环境必须返回 404,不得成为第五个正式 Finance 页面。
### 已完成模块
- Monthly Settlement V1;V2 Income Classification;Adjustment Lifecycle;Long Trip cumulative;Historical Plate Mapping;Expense Debug。
- Owner Monthly Report V3;Manual Snapshot / Display Version;Print / PDF;QR 分享;独立 Owner View;staging Owner View fixture。
### 剩余非阻塞事项
- Owner 账号、登录、历史月份 Dashboard、邮件 / SMS 与完整 Owner Portal 均不属于本轮闭环。
- 可在后续独立阶段补充生产部署后的运营监控、token 管理 UI 与定期真实扫码抽检;这些事项不得改变 frozen Snapshot 或结算公式。
### 下一阶段建议
- 冻结本工作流并进入稳定性维护;下一阶段优先做只读运营监控与发布清单,不扩展 Monthly Settlement 计算。
- 任何未来财务规则变化必须新增算法版本并建立新 fixture,不得原地修改 V1、历史 Snapshot 或既有分享结果。
## 2026-07-27 — Monthly Settlement V2 promoted to Current Official
- **V1 final definition:** Legacy / Frozen Settlement Core. It remains the authority for payout selection, month assignment, settlement total, Snapshot totals, and Owner Report official total.
- **V2 final definition:** Current Official Version = V1 Frozen Settlement Core + V2 income classification, Trip Income Details, reimbursement classification, Adjustment Lifecycle / Long Trip explanation, and richer reporting. A product version describes the complete external capability and does not require rewriting every stable internal algorithm.
- Snapshot Manager defaults new Native Snapshots to `monthly-settlement-v2` and presents `Monthly Settlement V2 · Current` first. Cards show the V2 settlement version before the technical composition `V1 Frozen Core + V2 Classification`.
- Existing V1 Snapshot files and metadata are never renamed, migrated, recalculated, or overwritten. They remain historical V1 records; explicit V1 generation remains available for compatibility/audit.
- This promotion changes version semantics, selection, metadata for newly generated Snapshots, and presentation only. It changes no payout, month, fee, expense, adjustment, long-trip, or final-settlement formula.
## 2026-07-28 — ECS Table UI / Skeleton Phase 2A
- **Skeleton Phase 1 → Stable / Read Only**;Search、Sort、Pagination、Visible Columns、Read Health 与安全错误页继续作为回归基线。
- **Skeleton Phase 2A → Row Detail + Action Framework + Safe Writeback Skeleton**,当前状态为 **Ready for Staging Validation**,尚未标记 Phase 2 Complete。
- 统一链路为 Table Registry + Table Data Adapter + Row Key Resolver + Row Detail Renderer;新增 `/ops/table-row`、`/api/ops/table-row` 与 `/ops/action-preview`,列表仅增加“查看详情”。
- Registry 支持 PrimaryKey、FallbackKey、RowLabel、DetailVisibleColumns、DetailHiddenColumns、Actions、ActionMode、RequiredRole 与 WriteMode。首批验证 profile:`A_overview`、`A_Records`、`A_repaire`、`A_CallHandling`。
- 写回模式为 READ_ONLY / PREVIEW_ONLY / CONTROLLED_WRITE,全局缺省 READ_ONLY;`executeWritePlan()` 无条件以 `WRITEBACK_DISABLED` 拒绝。本阶段不存在 Google Sheet 写入。
- Permission 缺省匿名上下文仅 READ;管理员模拟上下文也仅能生成 Preview。Preview 生成脱敏 audit event,不记录 credential、token、private key、password 或完整大型 payload。
- **Backlog(不阻塞)**:TabType 与 SourceType 完全解耦待后续统一处理;A_clean 暂以 Show 运行。
## ECS Table UI / Skeleton Phase 2B — Staging Validated / Milestone 2 Complete
- Phase 2B is **Staging Validated** and **Milestone 2(统一详情页与跨表关联)is Complete**.
- 真实 staging 验收:`A_overview` 显示行程记录 32 条、电话记录 15 条;`A_Records` 显示关联车辆 1 条;`A_repaire` 显示关联车辆 1 条、同车维修 1 条。页面稳定,不再持续出现 502。
- Related Records uses a shared, bounded, read-only resolver with isolated failure handling.
- Actions remain VIEW / OPEN_LINK / COPY_VALUE / NAVIGATE / PREVIEW_EDIT only. Preview is never executable; no Google Sheet or DataHub writeback exists.
- Preview events use an internal append-only JSONL audit store (`TABLE_AUDIT_PATH`, default `.data/table-audit.jsonl`). It is staging/internal tooling, not a business database.
- Guide-A metadata migration is in progress. `PHASE2_DEFAULTS` remains only as a clearly marked temporary safe compatibility fallback, with GUIDE_A / CODE_FALLBACK / MERGED diagnostics.
- Non-blocking backlog:CallHandling 历史记录字段完整度和关联覆盖率待后续数据治理;部分记录关联车辆或 Trip 为 0 不阻塞 Phase 2B。
## ECS Table Skeleton Phase 2C — First Controlled Writeback / Staging Validated
- Phase 2C 已在 staging 完成一条 `A_CallHandling.Status` 真实单元格写回验证。最终中央枚举为 `Waiting Reply`、`Processing`、`Follow-up`、`Follow-up-Pin`、`Escalated`、`Done`;服务端、页面、迁移和 token 均引用同一份定义。
- 服务端权限 resolver 默认拒绝,仅接受受信 internal context;preview token 有 10 分钟有效期,以 HMAC 绑定 table、rowKey、field、old/new value、operator 和随机 plan ID,执行一次即失效。
- Execute 会从当前 Registry resolved profile 重新解析 spreadsheet ID / SourceTab,再读当前行并检查唯一 rowKey 与 optimistic concurrency;只调用单一 Status cell update,不覆盖整行、不新增或删除记录。冲突返回 `DATA_CHANGED_SINCE_PREVIEW`。
- Preview、EXECUTED、REJECTED、FAILED 均进入脱敏 JSONL audit;文件不可写时显式报告 `MEMORY_FALLBACK`,不会静默丢弃。Preview 本身不调用 Google write adapter。
- Phase 2D 已实现 `A_repaire.Status` 受控单单元格写回,状态仅采用仓库既有 A1 投影与 fixture 已确认的 `Pending Repair`、`Repairing`、`Pending Pickup`、`Done`、`Company Using`、`Total Loss`。允许明确正向迁移;`Done` / `Total Loss` 不允许回退,未识别值只读。本阶段状态为 **Ready for Staging Validation**,尚未标记完成。
- 当前真实写回白名单仅 `A_CallHandling.Status` 与 `A_repaire.Status`。Finance、Settlement、Snapshot、Owner Report、OP 公式、DataHub 与其他 Google Sheet 字段均未修改。
- Browser ADMIN 流程使用 `/ops/internal-unlock` 建立 30 分钟的 `HttpOnly; Secure; SameSite=Lax` 签名 session;浏览器只持有签名 session cookie,从不接触或保存原始 `ECS_INTERNAL_ADMIN_SECRET`。`POST /api/ops/internal-lock` 可立即清除 session;过期或未解锁返回 `ADMIN_SESSION_REQUIRED`。
- `TABLE_WRITE_PLAN_SECRET` 在 staging/production 必须显式配置;缺失时返回 `WRITEBACK_NOT_CONFIGURED` 并保持只读。Preview token 的签名还包含每进程 nonce,因此 PM2 重启后所有旧 plan 自动失效;当前 staging 单 PM2 process 内以 executed plan set 防重复。未来多实例部署前必须改用共享原子 plan store,当前不得横向扩展写入实例。
- Audit 的 write target 只保存 masked spreadsheet ID、SourceTab 与单元格 affectedRange;HTML/普通 Audit API 不暴露完整 Spreadsheet ID。
## 2026-08-07 | PR #585 | Show Vehicle Status results in Vehicle Master
- Vehicle Master 列表直接展示 PR #584 已产出的人工状态、系统建议、一致/不一致/无法比较与需复核摘要;空值与不可比较结果不会被显示为正常或一致。
- 单车详情新增只读“车辆状态判断”卡片;原因、生效/更新时间在主信息区展示,来源和技术诊断默认收起。来源冲突与 Trip 来源未接入均有明确复核提示。
- 展示层仅消费 `vehicle.status` 结果;未修改 PR #584 的判断算法、优先级或来源,未新增 API/路由,未新增或执行任何源数据写回。
- 回归结果:`npm run test:vehicle-status` 通过;`npm run test:vehicle-master` 通过;搜索、排序参数、页码、page size、返回列表、上一辆/下一辆、Timeline / Activity History 与 READ ONLY 边界保持。
- 当前下一步:计划进入 #586“需要复核车辆列表”,本 PR 不提前开发。
## 2026-08-07 — PR #586 Vehicle Status review list
- Vehicle Master 顶部新增“全部车辆 / 需要复核”两个只读视图及数量;复核视图只接受 `vehicle.status.needsReview === true`,不在页面层重新判断状态。
- `review=needs-review` 会在搜索、分页、单车详情、上一辆 / 下一辆及返回列表时保留;未知筛选值安全回退到全部车辆。
- 复核列表继续使用 #585 的人工状态、系统建议、核对结果、复核标签和运营摘要,不增加确认、修改、审批或写回入口。
- 回归结果:`npm run test:vehicle-master`、相关 `node --check` 与 `git diff --check` 全部通过。
- 当前边界:本 PR 只提供发现和查看入口;不修改 #584 状态算法,不处理复核结论,不写回任何来源。
- 下一步:Staging 验证复核数量、筛选结果及详情返回路径;后续处理流程需另行确认,不在本 PR 内开发。
## 2026-08-07 — PR #587 Connect authoritative Trip status to Vehicle Status
- 改动前部署观测为 92 辆全部需复核。#584 未接入 Trip:人工 Rented 会得到 `TRIP_SOURCE_UNAVAILABLE`,其他无可靠建议的车辆通常为 `INSUFFICIENT_EVIDENCE`;两者均不可比较且需复核。本 checkout 不含部署的 92 行快照,因此不伪造各 reason 数量或真实车辆样例。
- 选定现有 Guide-A / table adapter 已读取的 `A_Records` 作为 Vehicle Status 只读 Trip 来源;不另建 Trip cache,不使用 Finance Snapshot 作实时运营状态,不修改 A1 Car Summary / Dispatch / Finance 算法。
- 关联顺序:Vehicle ID / Turo Vehicle ID → VIN → current plate → 既有且唯一的 historical plate;歧义不自动归属。已支持无当前 Trip、当前 Trip、未来 Trip、历史 Trip、结束时间已过但尚无未归还证据、明确仍 active 的 Late Return、来源不可用、关联歧义、时间缺失和多当前 Trip 冲突。
- Rented 仅由单一、未关闭且当前时刻落在 start/end 内的 Trip 建议。Late Return 还要求结束时间已过、无实际归还时间,且 Trip 状态明确仍 active;未新增宽限时间。
- 详情页增加 Trip 来源摘要;Trip ID、start/end、status、关联依据和来源更新时间位于默认收起诊断区。
- 本地没有部署的 92 车数据:改动前为 92 / 92,改动后实际数量必须部署 staging 后重新统计,不在无证据时声称数量已下降。下一步是 staging 导出 reason 多标签分布和真实车辆样例,并确认生产 Trip status 是否还有其他 active/closed 枚举。
## 2026-08-08 — Vehicle Master 真实数据纠正(post-PR #587)
- 原因确认:PR #587 已接入 A_Records,但测试夹具与真实数据不一致;真实人工状态使用“已租出”,A_clean 没有 canDispatch 字段,Trip 还包含 In-progress 与 Guest cancellation,且无时区时间必须按洛杉矶本地时间解释。
- 本次纠正:支持“已租出”;Trip active 支持 Booked / Extended / In-progress,所有 cancellation 类状态按已关闭处理;无时区 Trip 时间固定按 America/Los_Angeles 解析。
- 空闲判断不再依赖不存在的 A_clean.canDispatch。仅当维修/占用无拦截且 A_Records 当前无进行中行程(或只有未来预订)时,才建议 AVAILABLE;未知 Trip 状态继续保守复核。
- 人工状态空白不会被自动补值,统一分类为 CONFIRMED_STATUS_MISSING(待补人工状态)并继续复核。
- Vehicle Master 列表增加“复核原因分布”,直接显示待补人工状态、人工与系统不一致、来源冲突、Trip 来源不可用等数量,验收以真实剩余原因分布为准,不以强行把复核数降为 0 为目标。
- 边界:只读判断与展示;不写回 A_overview、A_Records、A_clean,不修改 A1 Dispatch、Finance 或结算逻辑。
## 2026-08-08 — PR #590 Vehicle Master 复核原因拆分与测试收口
- Staging 基线(PR #589 部署后):92 辆中 84 辆需要复核;其中 41 辆为人工状态空白,43 辆仍被统一归为“人工与系统不一致”;8 辆已可靠匹配并退出复核。
- 本次不改变状态判断结果,也不为降低数字而放宽规则;仅把原来的 `STATUS_MISMATCH` 拆成可操作原因:人工已租出但未找到当前 Trip、人工空闲但存在当前 Trip、人工已租出但系统判断逾期未还、人工空闲但系统判断逾期未还、人工状态与维修/清洁状态不一致、其他人工与系统不一致。
- 列表顶部继续按 `reviewReasonCode` 汇总数量;每辆车的“需要复核”标签同步显示具体原因,便于判断是人工状态待更新、Trip 关联/时间需检查,还是运营来源冲突。分类只描述观测差异,不自动认定员工或系统哪一方错误。
- 修复 PR #589 遗留的 `test:vehicle-master` 旧文案断言;`npm run test:vehicle-master`、相关 `node --check` 和 `git diff --check` 必须恢复通过后方可部署。
- Google Sheet / DataHub:无写入、无结构修改;本 PR 仍为 READ ONLY。Finance / Settlement / Snapshot / Owner Report:无影响。
- 当前状态:Ready for merge / Staging validation。部署后验收 84 辆总复核数及各原因合计;41 辆人工状态空白继续保留,不自动补值。
## 2026-08-08 — PR #591 系统自动 A1 建议版只读骨架
- 在 Vehicle Master 增加独立入口“系统自动 A1 建议版”,从现有 Vehicle Master 只读结果生成 0–8 组建议;仅纳入 `Listed On = Turo` 的车辆,不重新读取或复制另一套 Trip / Repair / Cleaning 判断算法。
- 分组映射:待清洁→1、待维修→2、维修中→3、维修完成待取回→4、逾期未还→5、可发车→6、公司自用/全损/退出运营→7;出租中、无法判断及其他状态进入第 0 组。费用来源尚未接入,因此第 8 组保持空并明确提示。
- 每辆车保留系统建议、判断原因、复核状态、位置和 Trip 摘要;页面提供内容指纹及复制文本,方便未来同一天与人工 A1 逐车比较。需要复核车辆仍可进入建议分组,但必须显示复核标记,不把建议伪装成已确认结果。
- 当前边界:READ ONLY;不覆盖手工 A1,不修改 Auto A1 / AIO_Pack,不写 Excel / Google Sheet,不修改 A_overview、A_Records、A_clean、Finance 或 Dispatch 原算法;当前不持久化快照。
- 已知待办:GPS 现场位置、人工清洁判断与费用来源尚未完成真人对齐。选择实际运营日,由用户手工生成一份 A1、系统同时生成建议版,按车辆拆分“系统数据缺失 / 判断规则不准 / 人工信息未更新 / 特殊情况”,再决定后续规则和长期快照格式。
## 2026-08-08 — PR #592 A1 对齐版本与导出准备
- 系统 A1 建议版为每次页面结果生成独立版本号、洛杉矶对齐日期和内容指纹;版本号同时写入固定 JSON 与 CSV,确保两份文件代表同一时点、同一批车辆结果。
- “保存固定版本”下载完整 JSON;“导出 CSV”下载带 UTF-8 BOM 的 Excel 兼容文件。导出字段包括车牌、车辆编号、位置、系统分组、判断理由、人工状态、Trip ID / 状态、复核结果、置信度和来源更新时间。
- 导出在当前页面内完成,文件内容与屏幕显示的版本完全一致;不因下载再次读取变化中的数据。CSV 对公式型开头内容做安全转义,避免 Excel 把车牌或来源文字当作公式执行。
- 当前保存方式为用户下载留档,不在普通浏览时自动创建服务器快照;不写 Google Sheet,不覆盖手工 A1,不修改 0–8 分组、Vehicle Status、Trip、Repair、Cleaning、Finance 或 Dispatch 判断。
- 首次真人对齐流程保持:同一天由用户按原方法手工生成 A1,同时下载系统固定版本与 CSV,随后逐车比较并按“系统数据缺失 / 判断规则不准 / 人工信息未更新 / 特殊情况”分类,规则调整另开后续 PR。
## 2026-08-08 — 开发导航检查(PR #592 后)
- 最终目标仍是 A1 Daily Workbench:上传/读取 Records → 确认 GPS → 确认清洁 → 生成当天状态 → 人工复核车辆分组 → 保存/复制报告。Vehicle Master 是统一车辆与状态底座,不另建第二套状态算法。
- 当前坐标:Vehicle Master、Trip 状态、复核原因、系统 A1 建议版和同一时点 JSON/CSV 导出已完成;首次真人 A1 对齐尚未完成。
- 下一关键节点:把现有能力收进一个只读、按步骤展示的 A1 每日调度工作台,再选择真实运营日完成手工 A1 与系统建议逐车对齐。
- 导航结论:继续推进,不需要重新规划;暂不修改 0–8 分组,不自动确认 GPS/清洁,不写回手工 A1。
## 2026-08-08 — PR #593 A1 Daily Workbench Phase 1
- 新增只读“A1 每日调度工作台”,按真实 SOP 展示五步:读取当天车辆与行程、确认 GPS/现场位置、确认清洁情况、查看系统 A1 建议版、人工复核并保存对齐版本。
- 工作台复用 Vehicle Master 与 PR #591/#592 的建议结果,只展示车辆总数、已有建议、仍需复核、无法判断及 Trip 来源不可用数量;不复制 Trip/Repair/Cleaning 判断算法。
- Vehicle Master 新增工作台入口;系统 A1 建议版的返回入口改为工作台,形成“工作台 → 建议版 → JSON/CSV 导出”的只读流程。
- GPS 与当天清洁明确显示“待人工确认”;首次真人对齐仍需在实际运营日完成。本 PR 不降低 43 辆复核数、不改 0–8 分组、不写 Google Sheet、不覆盖 Auto A1 / AIO_Pack。
## 2026-08-08 — PR #594 A1 GPS / 清洁对齐准备清单
- A1 每日调度工作台新增只读“GPS / 清洁准备清单”,按 51 辆 Turo 车辆展示车牌、A_overview 当前位置、最近 A_clean 记录、系统建议和复核状态。
- 工作台第 2、3 步从纯说明升级为可人工核对:显示位置缺失数量、有 / 无清洁记录数量,并提供“全部 / 位置缺失 / 无清洁记录 / 需要复核”浏览器内筛选。
- 清单只整理已有证据,不把 A_overview 位置冒充实时 GPS,也不把历史 A_clean 记录冒充当天已清洁;两个步骤仍需在真实运营日人工确认。
- 本 PR 不修改 0–8 分组、Vehicle Status、Trip、Repair、Cleaning、Finance 或 Dispatch 算法;不写 Google Sheet,不覆盖 Auto A1 / AIO_Pack,不新增确认或写回按钮。
## 2026-08-08 — PR #595 A_clean 清洁记录关联诊断
- #594 staging 验收发现:51 辆 Turo 车辆位置均可读取,但 51 辆均未显示 A_clean 历史证据;这不能直接解释为“51 辆都没有清洁”。
- A1 工作台新增 A_clean 关联诊断,分别显示数据源记录数、有车辆信息的记录数、成功匹配记录数、匹配到的车辆数、未匹配数、多车冲突数与识别到的车辆字段。
- 车辆识别兼容明确的 License Plate / Vehicle Plate / Plate Number / 车牌号码等字段,并允许唯一车辆编号匹配;任何多车冲突或无法识别的记录均不自动归属。
- 页面将“无清洁记录”统一修正为“未匹配清洁证据”,并明确“未匹配不等于未清洁”;本 PR 只读诊断,不改变当天清洁判断、0–8 分组、Vehicle Status、Trip、Finance 或 Dispatch,不写 Google Sheet。
## 2026-08-08 — PR #596 A_clean 三位车牌简写安全关联
- #595 staging 诊断确认 A_clean 可读取 494 条记录、494 条均有 `plate` 内容,但与 A_overview 当前/永久/已登记历史车牌的完整值匹配为 0。
- A_clean 的 `plate` 若恰好是三位字母数字简写,只在全部当前、永久和历史车牌中唯一对应一辆车时才关联;对应多辆、没有对应或并非恰好三位时均不自动归属。
- 工作台诊断新增三位简写总数、唯一匹配数、多车冲突数和仍未匹配数,便于 staging 读回真实结果;不使用任意长度后缀或模糊文本猜车。
- 边界保持:只读关联与展示;不写 A_clean / A_overview / Google Sheet,不修改 0–8 分组、Trip、Vehicle Status、Finance、Settlement 或 Dispatch 算法,也不把历史清洁证据当作当天清洁确认。
## 2026-08-08 — PR #597 AIO GPS / Clean 人工正式结果接入
- 当前正式运营口径确认:员工每天在 AIO `GPS` Tab 人工盘点 1、2、3 号停车场;最新日期“有效GPS信息”中出现车辆代表已回场,未出现代表未回场。员工第二天查看 Google Drive 清洁照片后,在 `Clean` Tab 人工登记车辆与 `Cleaned Date`。
- A1 工作台从与 A_clean 相同的 DataHub / AIO_pack 本地镜像链路只读获取 `GPS` Tab;优先使用 Guide-A 已登记的 `A_GPS` / `GPS`,未登记时仅从 A_clean 已确认的 LOCAL_XLSX / LOCAL_XLSM 同源配置派生 `GPS` Tab 读取,不另猜文件或 Google Sheet。
- GPS 只读取洛杉矶当天最新可识别日期的“有效GPS信息”;完整车牌或唯一三位车牌简写才可关联。最新日期不是当天、来源不可用、日期或字段无法识别时均保持未知,不沿用 A_overview 旧位置假装当天已经回场。
- Trip 只读结果新增“最近一次已结束行程”的 Trip ID、结束时间与洛杉矶日期;取消、拒绝、过期和 No-show 不作为最近还车,仍在进行或逾期未还的 Trip 也不冒充已结束行程。
- Clean 优先使用人工 `Cleaned Date`,不使用清洁上传链接的 submittedAt 或 Call Handling 上架提醒覆盖正式人工结果。清洁日期晚于最近还车日期=最近还车后已清洁;早于=需重新清洁;同一天因缺少小时/分钟=先后不明,必须人工复核。
- 已覆盖关键回归案例:同一天早上还车并清洁、中午再次出租、晚上再次还车时,系统锁定晚间第二段 Trip 为最近还车,并把当天 Cleaned Date 标为“同日先后不明”,不会错误判定已清洁。
- 本阶段只把 GPS / Clean 人工结果接入 A1 工作台证据层;不修改现有 Vehicle Status、43辆复核、27辆逾期、0–8分组、Finance、Settlement 或 Dispatch,不写 Google Sheet,也不覆盖手工 A1 / AIO_pack。
- 后续升级方向:清洁链接在人工审核后写入准确完成时间与对应 Trip ID,GPS / Clean 数据接口可再改接 Call Handling;工作台规则与页面无需重做。
## 2026-08-08 — PR #598 Data Hub GPS / Clean / Trip 真实字段修正
- #597 staging 验收确认正式网页数据入口不是直接解析大型 `AIO_pack.xlsx`,而是 Data Hub 已生成的 `A_GPS`、`A_clean`、`A_Records`;本次将工作台读取入口收回这三张 Data Hub 表。
- `A_GPS` 按真实字段读取 `Date`、`Mark as old Data`、`有效GPS信息`、`Current 完整Plate` 与 `Location`:已标记 `old` 的记录不参与当前结果,只读取最新未标记旧数据的批次;“有效GPS信息”非空才算回场,并优先用完整车牌关联。
- `A_clean` 优先读取真实的 `完整Plate` 与 `完成日期 Cleaned Date`,完整车牌为空时才允许三位简写按唯一尾号安全关联,修复 494 条记录有值但 0 辆匹配的问题。
- `A_Records` 补充 Data Hub 常见 camelCase / 组合列名和 `Finished / Closed` 已结束状态识别;工作台新增行程总行数、已关联车辆数、找到最近还车车辆数诊断,便于 staging 一次验证真实链路。
- GPS 日期不是当天但该批次仍未标记 `old` 时,页面明确显示批次日期并继续作为当前人工有效结果;不会沿用已标记旧数据,也不会把空的“有效GPS信息”当作回场。
- 边界保持只读:不写 Data Hub、AIO_pack 或 Google Sheet,不修改现有 Vehicle Status、43辆复核、27辆逾期、0–8分组、Finance、Settlement 或 Dispatch。
## 2026-08-08 — PR #599 A1 / Vehicle Master 严格 DataHub 缓存读取
- #598 staging 截图确认 A1 工作台仍先按 Guide-A 的 Ctrl 规则实时读取 Google Sheet;Google 限流后页面直接显示 `Source: GOOGLE_SHEET`、`未使用 cache fallback`,导致后续 A_GPS / A_clean / A_Records 无法进入判断。
- A1 工作台、Vehicle Master 与系统 A1 建议版统一改为严格读取 ECS 本地 `data/datahub/DataHub.xlsx`;A_overview、A_repaire、A_maintenance、A_clean、A_CallHandling、A_Records、A_GPS 使用同一 DataHub cache-only 入口。
- 页面请求禁止回退到 Google Sheet;Google Sheet 只由既有后台同步任务更新 DataHub 缓存。缓存暂时不可用时明确提示“DataHub 缓存尚未生成或无法读取”,保留上一版缓存由后台同步机制负责,不用页面实时请求轰炸 Google。
- 工作台新增缓存诊断:读取模式、页面实时 Google 读取次数(应为 0)、可用缓存表数、最近缓存时间及逐表真实来源,便于 staging 直接确认 `CACHE ONLY / LOCAL_XLSX`。
- 写回类 Ctrl 页面继续保留原有 Google Sheet 控制路径;本次不改变维修/保养状态写回,不修改 GPS / Clean / Trip 判断、0–8 分组、Finance、Settlement 或 Dispatch。
## 2026-08-09 — A1 A_clean 最近清洁日期判断
- A1 清洁证据继续只读取 DataHub `A_clean`,不读取或合并 Cleaning Upload 临时 Tab `clean`;两者数量无需一致。
- 每辆车允许匹配多条 `A_clean` 记录,但判断只采用该车最近一次可识别的 `Cleaned Date`,并保留多条匹配记录数量供诊断。
- 清洁日期按日期粒度与最近一次已结束行程的 `Trip End` 日期比较:`Cleaned Date >= Trip End` 判定已清洁,早于最近还车日期判定待清洁;不要求清洁记录包含小时或分钟。
- A1 工作台新增今日、昨日清洁完成车辆数,按每辆车最近一次 `A_clean` 日期统计,便于确认 DataHub 更新是否到达。
- A_clean 人工日期支持无年份的中文月日、`M/D` 与 `M-D`;按原始记录从上到下补全年份,首条以洛杉矶业务日期推断,后续日期倒退时按跨年处理。完整年份、Excel serial、ISO 日期和带时区时间戳继续沿用原解析逻辑;无法识别的值保留在诊断中。
- 边界保持只读;不写 Google Sheet,不修改 GPS、Trip 结束行程选择、Repair / Hold、Finance、Settlement 或 Dispatch 数据源。
## 2026-08-08 — PR #600 A1 维修 / 保养 / Hold 运营异常队列
- 按 Phase 3 路线从 GPS / Clean / Trip evidence integration 进入 Maintenance / Repair / Hold operational loop;A1 工作台新增车队级只读运营异常队列,把已有 Vehicle Ops 结果整理成“当前阶段 + 下一步”,不复制另一套维修或发车算法。
- 队列覆盖等待维修、维修中、维修完成待取回、Vehicle Hold、未识别维修状态,以及等待处理 / 持续跟踪的 Maintenance;支持按拦截发车、维修、Hold、Maintenance、待取回、未填写跟踪人筛选,并可回到该车来源记录。
- Repair / Hold 继续沿用既有发车拦截规则;Maintenance 仍只作提醒、不单独拦截。若 Maintenance 事项影响安全、出租或交付,必须转为 Repair Case。
- A_GPS 字段问题按用户决定暂缓:读不到时显示“暂缓对齐”,不阻塞当前阶段;等 AE 表格完全线上时再逐项核对。系统仍不使用 A_overview 旧位置冒充当天 GPS 结果。
- A1 日常步骤由 5 步扩展为 6 步,在系统建议版之前加入“处理维修 / 保养 / Hold 异常队列”。页面继续严格读取 DataHub 缓存,实时 Google Sheet 读取为 0。
- 本阶段严格只读:不写 A_repaire、A_maintenance、Google Sheet、手工 A1 或 AIO_pack;不修改 0–8 分组、Finance、Settlement、BookCars 或 Dispatch 算法。详细边界见 `docs/QP_A1_OPERATIONS_EXCEPTION_QUEUE.md`。
## 2026-08-08 — PR #601 A1 手机端分组折叠优化
- A1 工作台的清洁 / Trip 车辆清单按既有 `groupLabel` 自动归类;每组显示组名与车辆数量,默认收起,可单独展开,也可一键展开全部或收起全部。
- 清洁需处理、系统状态需复核筛选会同步隐藏没有命中车辆的分组,并自动展开有结果的分组;不会留下空白分组。
- A_GPS、A_clean、A_Records 三张技术诊断卡合并到默认收起的“GPS / Clean / Trip 数据诊断”,减少手机端首屏滚动长度;维修 / 保养 / Hold 异常队列继续默认展示。
- 本次仅修改展示与交互,不改变 GPS / Clean / Trip 证据、Repair / Hold 拦截、Maintenance 提醒、0–8 分组或任何数据来源;页面继续只读 DataHub 缓存,实时 Google Sheet 读取为 0。
## 2026-08-08 — PR #602 A1 异常队列折叠优化
- 维修 / 保养 / Hold 运营异常队列改为整体可展开、可收起,手机端默认收起,只显示“拦截事项数量 + Maintenance 提醒数量”。
- 展开后保留原有统计、筛选、当前阶段、下一步、跟踪人与来源记录;不删减任何运营信息。
- 本次仅调整页面展示,不改变 Repair / Hold 发车拦截、Maintenance 提醒、0–8 分组、缓存读取或数据来源;页面继续只读,实时 Google Sheet 读取为 0。
## 2026-08-08 — PR #603 A1 今日优先处理摘要
- A1 工作台新增“今日优先处理”摘要,把发车拦截、维修完成待取回、清洁需处理和系统状态需复核按运营优先级集中展示;员工无需先展开长清单即可看到当天重点。
- 摘要卡片可直接跳到维修 / Hold 异常队列或清洁 / Trip 准备清单;原有详细数据、折叠面板和筛选全部保留。
- Maintenance 继续只作提醒,不单独计入发车拦截;摘要不创建新的状态判断,也不把系统建议伪装成人工确认结果。
- 本阶段仍严格只读 DataHub 缓存,不写 Google Sheet,不修改 GPS、Clean、Trip、Repair / Hold、0–8 分组、Finance、BookCars 或 Dispatch 算法。
## 2026-08-08 — PR604:收紧 A1 逾期归还判断
- 第5组不再仅凭 A_Records 中“结束时间已过但状态仍未关闭”的旧行程直接判定逾期。
- 自动逾期必须同时满足:当前无进行中行程、GPS 当前有效批次明确未回场、且无 Repair / Hold / 临时自用 / 退出运营拦截。
- GPS 已确认回场时排除逾期;GPS 数据不可用或不可靠时也不得由缺失数据制造逾期提醒。
- 进行中行程继续归出租中;Maintenance 仍只提醒,不作为逾期或发车拦截依据。
## 2026-08-08 — PR #605 A1 车辆互斥分类与可发车判定
- 当前行程车辆单独归入“行程中车辆组”,不再进入 1–7 号运营分组或可发车统计。
- 可发车必须同时满足:A_clean 明确完成、A_GPS 明确回到停车场、无 Repair / Hold / 异常拦截。
- 清洁字段缺失、GPS 不可靠或来源缺失时,不再自动判断为 AVAILABLE,改为待复核/无法确定。
- 保留 Repair、Hold、Maintenance 和现有 0–8 号业务边界;本次只调整状态判定和 A1 归类。
## 2026-08-08 — Developer Center ECS Table UI / Skeleton 独立入口
- `/system-nav` 新增独立的 **ECS Table UI / Skeleton** 页面目录,根目录和七个子目录均使用原生 `details/summary`,支持逐层展开、收起,并在侧栏增加页内快捷定位。
- 目录统一归集 Skeleton 总览、所有 ECS 表格、A1 每日调度工作台、A1 建议版与上传页、Car Master / Vehicle Master、Skeleton 验收、DataHub / Read Health、Table Row Detail 相关入口。
- 所有链接复用既有真实页面或参数化入口;Row Detail 引导用户从真实表格行进入,以保留真实 `tab` 与 row key。未删除或改写任何现有网址。
- 本次只调整 Developer Center 导航与页面目录;未修改业务算法、车辆分组、GPS、清洁、维修、Finance 逻辑,未新增写回功能或 API。
## 2026-08-08 — Developer Center V2 页面分类与身份标识
- `/system-nav` 的页面管理入口升级为五个轻量顶层分类:近期开发页面、Skeleton 系列、原始模式 / Legacy、迁移中的页面、内部工具 / Debug;继续使用简单的树状 `details/summary` 展开与收起。
- 页面开发模式统一使用 `SKELETON / LEGACY / MIGRATING / INTERNAL` 四种标签。ECS Table UI、A1 Workbench、Vehicle Master、Skeleton 验收及配套 DataHub / Read Health 归为 `SKELETON`;当前正式 Finance 页面归为 `LEGACY`;Audit、参数化 API 等开发工具归为 `INTERNAL`。
- 当前没有真正迁移中的页面,因此 Migrating 只显示空状态,不登记或虚构页面。近期开发区域只列 A1 Workbench、ECS Table UI / Skeleton、Vehicle Master 和 DataHub / Read Health,并显示状态与真实链接。
- 本次只修改 Developer Center 页面分类、目录与身份标识;未改变现有路由,未新增写回按钮,未修改业务算法、车辆分组、GPS、清洁、维修或 Finance 逻辑。
## 2026-08-08 — A1 / A_GPS 统一读取与车辆身份诊断
- 已确认根因:`/ops/table?tab=A_GPS` 按 Guide-A 配置读取表页真实源,而 A1 派生视图曾把 `A_GPS` 强制改写为本地 `DataHub.xlsx` cache-only profile;两页因此可能具有不同 headers、rows、更新时间与记录数,旧缓存会令有效 GPS 车辆漏读。
- A1 现在优先复用 A_GPS 表页配置的最新只读读取结果;仅当该读取失败时 fallback 到 DataHub `A_GPS` 缓存,缓存不能覆盖更新数据。诊断显示统一记录数、A_GPS 批次与更新时间、DataHub 更新时间、有效 GPS 数、匹配车辆数、未匹配数、冲突数以及真实识别字段。
- GPS 字段使用集中 alias 配置,覆盖有效GPS信息、状态、设备号、位置、地址、停车场、日期、时间、更新时间及车辆身份字段。标准化证据旁保留完整 `raw` 行,不删除或覆盖原始字段;未识别含义继续显示待确认。
- 身份匹配依次尝试 Vehicle ID、Turo Vehicle ID、VIN、完整的当前/原始/历史车牌,再尝试唯一车牌尾号。完整身份为 HIGH/MEDIUM;仅尾号匹配固定标记 LOW,多个候选不会自动合并。未匹配诊断保留车牌、历史车牌、Vehicle ID、Turo ID、VIN 与原始 GPS 值。
- 分组继续沿用已存在的 Vehicle Status 正式规则:Repair / Hold / IAA 不可发车;当前有效 Trip 为行程中;A_clean 明确未完成为待清洁;可发车必须同时满足无当前 Trip、A_clean 明确可发车、GPS 明确回场且无维修/Hold 拦截。多来源冲突保留 `SOURCE_CONFLICT` 并人工复核,不以 GPS 停车场位置覆盖行程状态。
- 待业务确认:同日清洁与还车因缺少小时分钟仍不可判先后;Maintenance 仍仅提醒、不单独拦截;正式分组优先级未新增写死规则,继续复用现有状态引擎。
- 全部功能只读;不写 Google Sheet,不自动合并车辆,不修改 Vehicle Master、历史车牌或手工 A1,不增加保存、写回、编辑或删除入口。
## 2026-08-09 — staging 新版 A1 GPS / Clean 在线数据表配置
- 新版 `A_overview / a1-workbench` 的 GPS 与 Clean 各自拥有默认收起的“在线数据表配置”,候选表仅来自 ECS `Guide-A` 中 `ECS-Ctrl=yes` 的注册表,不接受任意表名。
- 配置保存在 staging 服务器的 `data/runtime/a1-online-table-config.json`(可用 `A1_ONLINE_CONFIG_PATH` 覆盖);GPS、Clean 的模式和字段映射相互独立,首次使用均为本地录入模式。
- GPS 在线配置要求车牌列必选,日期列、位置列可选。所选车牌列非空即作为在场证据,不以日期、old、GPS 状态或辅助字段过滤;PL1/PL2/PL3 位置优先,否则沿用 Vehicle Master 默认停车场;匹配后按车辆去重。
- Clean 在线配置要求车牌列和日期列必选,位置列可选。配置日期列严格用于今日/昨日判断;PL1/PL2/PL3 仅用于停车场归属,否则沿用车辆默认停车场;今日/昨日展示均按最终车牌去重。
- 在线模式只在对应 GPS 或 Clean 切换为“在线表格模式”时读取保存的在线表。保存后若表字段改变,页面安全显示“字段不存在”并保持可重新配置,不读取未受 ECS 控制的表。
- 本次没有改变 GPS/Clean 本地读取链路、“有效GPS信息”规则、Group 1~7、Vehicle Master、Finance、OP、旧版 `/fleet-ops-a1-report-v1` 或 Google Sheet 写入逻辑。
## 2026-08-10 — 多份 Turo Excel 合并导入 A_Records_Staging
- 新版 A1 工作台新增默认收起的“Turo Excel 导入”独立模块;一次至少选择两份格式一致的 Turo Excel,不可靠识别账号时不猜测账号名称。
- 所有文件先在内存中完成解析、关键字段校验、完整字段并集和 Trip ID 唯一性校验。Trip ID 是唯一依据,不与车牌组成复合键;任一重复、空 Trip ID 或关键字段缺失都会整体停止,并明确返回问题。
- 校验全部通过后,以临时文件 + 原子重命名一次性替换 `DataHub.xlsx` 的 `A_Records_Staging` 完整快照;失败不会清空旧 staging、不会部分写入。此流程不写 Google Sheet。
- 正式 `A_Records` 不修改,现有读取默认仍为 `A_Records`;独立配置层预留 `production=A_Records`、`staging=A_Records_Staging`,测试表可通过独立 API 读取,未来切换不需要重写下游规则。
- 边界不变:不修改 AIO、Car Summary、Vehicle Status、GPS / Clean、Finance、OP、BookCars、旧版页面、4030 或生产部署。
# A1 Workbench 统一 AIO 操作流程(staging 验证)
新版 A1 Workbench 已加入按顺序、默认收起的每日操作框架:Turo Excel 导入、Clean、GPS、车辆基本状态、Today's Returns、Auto A1 与最终 A1 报告。Turo Excel 仍只完整替换 `A_Records_Staging`;Clean 支持本地文件、沿用 DataHub 优先读取策略的在线文件、手动输入三种模式并只写 `A_Clean_Staging`;GPS 同样支持三种模式并只写 `A_GPS_Staging`。
派生结果分别写入 `A_Car_Summary_Staging`、`A_Todays_Returns_Staging`、`A_Auto_A1_Staging` 和 `A_A1_Report_Staging`。所有写入均先在内存解析和校验,再以完整 JSON 快照原子替换;失败不清空旧快照、不保留半成品。车辆主数据只沿用正式只读来源,行程测试来源固定为 `A_Records_Staging`。
A1 分组与文字报告复用 `lib/fleet-ops-a1-report-v1.cjs`。状态别名和地点 PL 映射在独立业务模块标准化,无法判定的状态进入复核并保留原始/标准化 Debug。工作区未找到 `AIO12030(V1.5).xlsm` 或导出的 VBA 源码,因此未猜测缺失规则;继续遵循“已有迁移实现优先、原 VBA 业务含义不变”的迁移原则。
本阶段严格 staging 优先验证:不修改正式 `A_Records`、Clean、GPS、Vehicle Master 或 Vehicle Status,不写 Google Sheet,不操作 Finance、OP、BookCars、旧 AIO、4020 或 4030。验证完成后可通过独立数据源配置切换正式读取来源,不允许通过本框架直接覆盖正式表。
## 2026-08-10 — A1 Workbench 每日流程一键化
- A1 Workbench 日常流程由多个独立生成按钮收口为:**导入数据 → 生成今日运营数据 → 人工调整 → 自动更新最终报告**。
- “生成今日运营数据”只读取 `A_Records_Staging`、`A_Clean_Staging`、`A_GPS_Staging` 和车辆只读输入,并按顺序生成车辆基础状态、Today's Returns、Auto A1、A1 报告及每日汇总快照。
- 全部派生结果先在临时目录计算;所有步骤成功才发布完整 staging 快照。失败保留上一次成功结果,不产生可见的半成品。
- 人工车辆状态独立保存在 `A_Vehicle_Status_Override_Staging`,保留系统建议状态、人工状态和最终使用状态。有效人工状态优先进入最终 A1;与系统建议不一致时标记“需要复核”。重新生成不会覆盖人工调整。
- Clean / GPS 手动记录仍只写各自 staging;页面默认在保存后调用统一的今日数据更新,不再要求员工逐个生成下游结果。
- 本次未修改正式 `A_Records`、正式车辆主数据、正式 Clean / GPS 或 Google Sheet;未触及 Finance、OP、BookCars、4020、4030。
## 2026-08-10 — A1 Workbench 步骤与数据源入口收口
- A1 Workbench 顶部状态栏已并入每日步骤,编号、状态、数据量、复核量和更新时间在同一张步骤卡内展示。
- GPS / Clean 统一为三种数据源模式:默认“原始 VBA 数据”读取现有 VBA/AIO 结果;“在线数据”作为后续 Google Sheet 等在线来源的接入入口;“手动输入”用于网页新增、修改和删除 staging 记录。
- GPS / Clean 各自收口为一个默认折叠模块,在线配置、兼容文件解析、手动输入、记录摘要和默认折叠的 Debug 不再在页面其他区域重复。
- 手动输入、删除及兼容/在线来源保存成功后自动调用统一的 `POST /api/ops/a1/generate-today`,更新今日运营数据和全部下游 staging 结果。
- 页面减少重复状态和重复操作;本次仍采用 staging 优先,正式 A_Records、Clean、GPS、车辆主数据和 Google Sheet 均不修改。
## 2026-08-10 — A1 Workbench Payout Excel 独立导入
- A1 Workbench 的每日导入区现在以默认收起的 Earning 与 Payout 两张卡片分别展示状态、文件数、记录数和更新时间;Payout 的 DataHub 标签固定为 `A-payout`。
- Earning 继续原子替换 `DataHub.xlsx` 的 `A_Records_Staging`;Payout 的原始行快照、原文件和长期导入配置独立保存在 A1 staging 目录,二者导入及配置互不覆盖。
- Payout 文件先完整解析并核对已保存字段结构,成功后才原子发布新快照;失败保留旧成功结果,不自动修改今日派生结果。
- Payout 暂不参与“生成今日运营数据”,避免在 A1 算法尚未定义 Payout 用途时擅自加入金额;员工仍可沿用统一生成按钮,现有人工状态优先和全量发布/回滚边界不变。
- 本次继续 staging 优先,不写正式 `A_Records`、正式 GPS、正式 Clean、Vehicle Master 或 Google Sheet,不修改 Finance、OP、BookCars 与 A1 核心判断算法。
## 2026-08-10 — Snapshot Detail 三类金额明细展开
- Snapshot Detail 的 Rental Income、Reimbursements、Management Fee 已增加按车辆、按车牌隔离的展开明细,并复用现有 Show details / Hide details 按钮交互与行内明细样式。
- Rental Income 优先展示已保存 Snapshot 的收入分类行程;Reimbursements 将原有 Reimbursement Details 数据源和展示映射绑定到对应车辆列,不再保留页面级重复区域;Management Fee 仅展示 Snapshot 已保存的逐项明细。
- Snapshot 未保存对应逐项数据时,页面显示明确的中文空状态,不根据 month + owner 重算,也不虚构明细;各面板合计继续显示对应车辆的已保存表格金额。
- 明细仅用于解释已保存 Snapshot 结果;本次不改变财务算法、Snapshot 结构或金额、Owner Report 金额及其他 Finance 页面。
- 本次仍采用 staging 优先;正式数据、Google Sheet 均不修改,也不部署生产环境。
## 2026-08-10 — Snapshot Detail 报销组成与管理费计算解释升级
- Turo 报销展开优先读取已保存 Native FinanceResult 的 `cars[].incomeClassification.trips[].categories`,按车辆、Trip ID 和类别逐笔展示,并读取 `adjustmentLifecycle` / `lifecycleRows` 保留撤销与重新发布的完整过程;Legacy Snapshot 继续兼容页面级 `reimbursements`。
- 报销兼容行继续使用 PaidBy-only 口径;`Belongs To` 仅作为只读说明字段,不会单独创建或绑定报销。没有逐笔组成时明确说明 Snapshot 仅保存了合计,不从 A_Records 或 month + owner 临时创造数据。
- 管理费展开读取 `managementFeeDetails`、`managementFeeItems`、`feeDetails`、`calculationTrace.managementFee` 以及官方 `managementFeeBase` / `managementFeeBase25`、`selfCleanReturn`、`feeRate`(若已保存)和 `managementFeeNet` 字段,展示收入基数、报销扣除关系、比例、组成与已保存公式;缺少计算输入时明确说明,不反推或硬编码比例。
- 每辆车的 Reimbursements 与 Management Fee 保持独立、默认收起,并使用可横向滚动的局部明细容器和移动端单列计算卡,避免整页横向溢出。
- 本次明细只解释按 `snapshotId` 读取的已保存 Snapshot,并继续透传 `algorithmVersion`;不修改 Finance V1/V2、Settlement Algorithm、Snapshot 数据结构或金额、Owner Report、Google Sheet 和正式数据。继续 staging 优先。
## 2026-08-10 — Snapshot Detail 收入展示分类与管理费公式修正
- Snapshot Detail 展示层将 Extra Usage、Extra Distance / Extra Mileage、Late Fee、Improper Return 归入基础租金 Rental Income;Fuel 及其他车主报销项目继续归入 Turo Reimbursements。同一已保存行程项目只在一个分类出现,并保留原 Trip ID、日期、金额和类别。
- 管理费明细只有在当前 Snapshot 保存了管理费计算基数和比例时才展示乘法公式,并按已保存的基础管理费、固定/其他适用费用及 Self-clean / Return 展示组成。
- Snapshot 若只保存管理费总额而没有完整计算输入,必须明确提示无法还原公式,不得反推比例、硬编码费率或展示没有解释意义的总额减零公式。
- 本次仅调整 Snapshot Detail 的展示分类;不修改财务算法、Settlement Algorithm、Snapshot 原始金额、Owner Report 总金额或正式数据,也不从 A_Records 临时创造收入。
## 2026-08-11 — V2.5 Snapshot 不完整管理费 ledger 读取修复
- V2.5 Snapshot 统一读取层不再仅凭 `managementFeeBaseDetailsV25.version` 接受历史 ledger;关键金额必须均为有限数字,并与当前冻结的 `cars[].incomeClassification` Rental Income、车辆已保存费率及 V2.5 公式逐项一致。
- 缺失、只有版本号、包含 null / 非有限数字或与冻结收入不一致的 ledger,均只使用 `cars[].incomeClassification.basicRental` 与 `cars[].incomeClassification.trips[].categories` 中的 V2.5 租金类别,以及 `cars[].managementFeeCalculation` 已保存费率重新生成;旧管理费、旧 base 和旧 final settlement 不作为输入。
- Snapshot 文件读取、列表、详情 API 与 Snapshot Detail 继续共用该标准化结果;Management Fee Base、基础管理费、Self-clean Return、Management Fee Net、逐车扣减和 Net Income 因而来自同一份 V2.5 ledger。
## 2026-08-11 — Snapshot Detail Trip 日期展示修复
- Snapshot Detail 的每条 Trip 标题现在读取该 Trip 自己冻结的 `tripStartDateTime / tripStart / tripStartDate`,旧 Snapshot 缺字段时才按同 Trip ID 回退到 operational Trip 日期。
- Trip 时间范围读取该 Trip 自己的 `tripEndDateTime / tripEnd / tripEndDate`;跨月仅显示既有 `crossesMonth` 标记,不再把月末 settlement window end(例如 `23:59`)渲染成 Trip 实际结束时间。
- 本次仅修改只读展示字段选择;`settlementMonth`、`crossesMonth`、收入分类、V2.5 管理费 ledger、Vehicle Settlement 与 Net Income 均未改动。