Commit 1ac8a1e1 authored by wenwen.tang's avatar wenwen.tang 😕

docs: 线上数据回拉 + 合同×台账核对报告(29 份 → 35 单元,18 对 / 17 有出入)

同步:线上 63 个文件 / 93.6MB 回拉本地,二次干跑 0 新增 0 变化 63 一致。
核对(合同正文 vs 台账):
- 影响出单 10 条:903室月租 36300/合同 16500;904室月租空/合同 19800;
  501室期空、502室标(空置)(合同 501+502 一体租给明境 2025-06-16~2028-06-15,月租合计 35648);
  126/128 车位合同期整体早一年;154 车位台账空置(合同 徐相天);
  601B 室租客名带「(空置)」后缀会被出单逻辑跳过;315 室期错(合同 2026-11-01~2028-10-31);
  305 室物业费台账 1512 / 合同 1312
- 待确认 6 项:132/227 车位合同承租方是伯凌而台账是爱种草(且已按爱种草出单);
  119/124/155/191/227 车位合同甲方均为精锐而台账出租方是瑞格嘉德/科博;
  903/904 面积都填 271.17(均值);306/310「租赁期(月)」12/19 应为 24
- 当前不出单的 9 个单元月租合计约 ¥98,262
另发现:合同文件名不可信(119 车位租客与期限都写错;1102 漏「安装」);
本机 data/batches 有 4 张线上没有的测试残留(未动)。
parent c9146ddc
......@@ -99,3 +99,62 @@
- 线上 admin 密码当前值(问巴特菲;他选择「保持现状,不做 HTTP 抽查」)
- 9 月电费批次错计(10 行 ¥167,947.41 / 少计 601B ¥299.67 / 出租方错记精锐)仍未处置
- `ui_verify11` 里「保存修改」按钮的断言需同步改写(10-14 删了该按钮)
---
# 2026-10-15(下半场)· 线上数据回拉 + 合同×台账核对
## 需求
> 先从远端将数据同步过来,再对照合约看数据表里的内容是否正确
## 新增工具
- **`tools/sync_pull.py`** —— 数据**回拉**通道(deploy_git.py 只管推代码)。
`--dry` 先列差异 → 备份 `data/_snap/pull-<ts>/` → SFTP 递归下载。
默认跳过 `attachments`(61MB)、**永不回拉 `sessions.json`**(线上活跃 token)。
⚠️ 只增/改,**不删本地独有文件** → 本地残留会一直留着(见下)。
- **`tools/check_contracts_vs_table.py`** —— 按文件名解析单元/租客/期限与台账比对。
⚠️ 结论:**文件名不可信**(119 车位文件名把租客与期限都写错,1102/307&603/601A&702 也漏字或多主体)。
- 扫描件 PDF 无文本层 → **渲染成图人工判读**(`pypdfium2`,多看第 1/2 页甲乙方、第 3/4 页「第三条 期限与交付」)。
- `.doc`(Word 97)可读:`raw.decode("utf-16-le","ignore")` + 中文串抽取,正文基本完整。
## 回拉结果
63 个文件 / 93.6MB,15 秒;二次干跑 **0 新增 0 变化 63 一致**。
线上 data 现状:tables 840K · batches 284K(7 张) · contracts 93M(29 份) · attachments 61M · drafts 空。
## 核对结论(29 份合同 → 35 单元,18 对 / 17 有出入)
**影响出单的 10 条**:
1. 903室 月租台账 **36300**,合同 **16500**(2.2元/㎡×247.24)
2. 904室 月租**空**,合同 **19800**
3. 501室 合同期**空**(合同 2025-06-16~2028-06-15)
4. 502室 标**(空置)**、期空(合同把 501+502 一体出租给明境,月租合计 35648,台账 501/502 各 17824 ✓)
5. 126车位 台账 **2025-03-19~2026-03-18**,合同 **2026-03-19~2027-12-30**(早一年)
6. 128车位 台账 **2025-03-01~2026-02-28**,合同 **2026-03-01~2027-12-30**(早一年)
7. 154车位 台账**(空置)**,合同 **徐相天 2026-10-01~2027-09-30**
8. 601B室 租客名带 **「(空置)」后缀** → 出单逻辑判空置跳过(合同有效期到 2027-09-30)
9. 315室 台账 **2026-01-01~2026-10-31**,合同 **2026-11-01~2028-10-31**(JR202609315)
10. 305室 物业费台账 **1512**,合同(补充协议)**1312** → 已出单 12000/期,应为 11800
**需确认**:132/227车位 合同承租方是**上海伯凌建筑设计**,台账是**上海爱种草**且已按爱种草出单;
119/124/155/191/227 车位合同甲方均为**精锐**,台账出租方却是 瑞格嘉德/科博(且已按台账出单);
603室 台账出租方瑞格嘉德 vs 合同精锐;903/904 面积都填 271.17(均值,应 247.24/295.10);
306/310 的「租赁期(月)」12/19 应为 24;307&603、601A&702 的第二承租方(锦众心诚、上海央旗)未记账。
**当前处于「不出单」状态的 9 个单元**(未出现在任何房租批次):
401室 · 501室 · 502室 · 903室 · 904室 · 126车位 · 128车位 · 154车位 · 601B室 —— 月租合计约 **¥98,262/月**。
**核对通过 18 个**:302/303/306/307/310/312/601A/607/702/706A/706B/1101/1115/1102/1114/401(金额)/
168车位/169车位。
## 本机残留(不影响线上)
`data/batches/` 有 4 张线上没有的测试批次:`20261014-134503`、`20261014-134504`、
`20261015-134717`、`20261015-135238`(均 elec/2026年10月/302·303室/prev=100 cur=180/核对人 "u")。
线上 7 张批次完好。**未动**(等巴特菲发话再移 `_trash/`)。
## 产出
- `output/合同-台账核对报告.md`(可入库)
- `output/合同-台账核对明细.csv`(35 行 × 17 列,output/*.csv 被忽略)
## 待办
- 上面 17 个单元的修正要不要动手(涉及台账写操作,需 `confirm=true`)
- 4 张本机残留批次是否移入 `_trash/`
- 9 月电费批次错计、`ui_verify11` 按钮断言(老待办)
......@@ -10,14 +10,13 @@
- `BILL_COLS`:owner=A unit=C tenant=F begin=I end=J rentfree=L **free_type=AK** cycle=N fee=O rent=P
last_paid=AB bill_start=AC next_bill=AJ;另 **AL「合同原件」**(type=`files`,排最右)
- **AK「免租类型」**(显示在 L 之后):`首N月免租` / `每年8-9月免租`,空=无免租
- AJ「下一账单日」**只用于定时排期**(推送日 = AJ − 提前天数);出单口径看 AC;出单后 AC/AJ 一起推进
- AJ **只用于定时排期**(推送日 = AJ − 提前天数);出单口径看 AC;出单后 AC/AJ 一起推进
- **AD~AI 房产/房产税六列已删**(`RETIRED_TAX_COLS`,有守护测试);列定义**以代码为准**
(`load_table` 用 spec 覆盖存档列)→ **加列不需要数据迁移**
- 默认锁定只读;改单元格/增行/删行必须 `confirm=true`(否则 428);备份 `data/tables/.backup/`
- **电费批次** `data/batches/{id}.json`:只留原始数据,按账期留档;测试残留移入
`data/batches/_trash/YYYYMMDD/`(store 只 listdir 顶层 `*.json`,子目录隐身、可恢复)
- **账期口径**:**批次 = 点一次「生成通知书」的产物**(`YYYYMMDD-HHMMSS`);账期 `period_label`
只是批次上的**文本字段**,`list_periods()` 按它归组 = 页面「账期汇总」(组内 `created_at` 倒序)
- **批次** `data/batches/{id}.json`(= 点一次「生成通知书」的产物,id=`YYYYMMDD-HHMMSS`);
测试残留移入 `data/batches/_trash/YYYYMMDD/`(store 只 listdir 顶层 `*.json`,子目录隐身可恢复)
- **账期口径**:`period_label` 只是批次上的**文本字段**,`list_periods()` 按它归组 = 页面「账期汇总」
- 来源:① 录入页「所属期间」(默认今天年月;带入上期时 +1 月)② `elec_parser._extract_period`
③ 后端兜底用**通知日期**年月
- ⚠️ **自由文本、无归一化**:`strip()` 后精确比较 →「2026-11」与「2026年11月」会分成两组
......@@ -33,7 +32,7 @@
- `companies` 租客(名称即唯一键,`kind`=company|person);`owners` 出租方(**键=简称**)
- 当前 4 家:精锐 37 / 科博 24 / 瑞格嘉德 16 / 国隆 8
- `None`/缺失 = 未维护过 → 回落 `DEFAULT_OWNERS`;**写过(哪怕 `{}`)= 权威**
- 改名**联动台账**(F 列/A 列);删除有引用 → 409,`force=true` 才真删
- 改名**联动台账**(F/A 列);删除有引用 → 409,`force=true` 才真删
- 归属三级优先:**行内 `owner` > 台账 A 列 > 名单兜底(仅当名单恰好一家)**
- ⚠️ 写入必须 `store.set_config()`(顶层整体替换);`patch_config` 浅合并**删不掉字典键**
......@@ -52,8 +51,8 @@
- 账期级:一行算已核对 ⇔ **含它的每张批次都勾了它**;`POST /api/periods/verify` 一次写全账期
- 核对表**可直接编辑**,**改数据不影响核对状态**;`confirmVerify()` 两步:
① `saveVerifyData()` 按 `_bid` 分组逐张 `PUT /api/batches/{id}`(有改动才发)
② `POST /api/periods/verify {rows, done:true}` 整期写成「已核对」→ 要不要重核**由用户勾选决定**,服务端不猜
- 已删 `keep_verify` 那套(与新口径矛盾);`reingest` = **一律重置核对**
② `POST /api/periods/verify {rows, done:true}` → 要不要重核**由用户勾选决定**,服务端不猜
- 已删 `keep_verify` 那套;`reingest` = **一律重置核对**
- 脏基线:`ELEC_CMP_FIELDS` + `ESNAP`/`takeVerifySnapshot()`/`dirtyRows()`/`editSummary()`
- ⚠️ 基线键用稳定 `_rid`,**不能用数组下标**;⚠️ 改动提示只改文字、绝不重渲染模式条(否则输入框失焦)
- ⚠️ `PUT` 是**全量覆盖**:每组必须带该批次**全部行**;退出核对有未保存改动要**明确警告会丢**
......@@ -69,30 +68,51 @@
- 前端 `saveElecDraft/clearElecDraft/restoreElecDraft/elecBootFill`;`applyNav` 改调 `elecBootFill()`
—— **有草稿就不「带入上期」**
## 合同原件(台账 AL 列,2026-10-15 新增)
## 合同原件(台账 AL 列,2026-10-15)
一格可挂多份(租赁合同 + 补充协议 + 车位合同)。
- 单元格值 = 紧凑 JSON `[{"id","name"}]`;本体在 `data/contracts/{id}.{ext}` + `index.json`
- `rentos/contracts.py`:save/find/path_of/list_all/remove/parse/dump/describe;
白名单 pdf/doc/docx/xls/xlsx/png/jpg/gif/webp/bmp,单份 **≤ 40MB**;id=uuid4 hex,`clean_id` 挡路径穿越
- ⚠️ 上限 40MB 不是随手定的:扫描件合同能到 21.8MB,而 40MB base64 后 53MB 仍 < server `MAX_BODY=64MB`
- 接口:`POST|GET /api/contracts` · `GET /api/contracts/{id}`(内联预览;`?dl=1` 下载;支持 `?token=`)·
- ⚠️ 40MB 不是随手定的:扫描件能到 21.8MB,而 40MB base64 后 53MB 仍 < server `MAX_BODY=64MB`
- 接口:`POST|GET /api/contracts` · `GET /api/contracts/{id}`(内联预览;`?dl=1`;支持 `?token=`)·
`DELETE /api/contracts/{id}` —— **增删仅管理员**(访客可查阅、不能增删)
- 前端:`td.files` 渲染按钮(`📎 N 份` / 编辑态 `+ 合同`)→ `openFilesDlg()` 浮层;
增删走 `stageCell(rowId,'AL','files',…)` 暂存,**与台账「保存 N 处改动」同一口径**
- 前端:`td.files` 按钮(`📎 N 份` / 编辑态 `+ 合同`)→ `openFilesDlg()`;
增删走 `stageCell(rowId,'AL','files',…)`,**与「保存 N 处改动」同一口径**
- ⚠️ **从某行移除只解关联、不删文件**(多行可共享同一份)
- ⚠️ `renderGrid` 渲染 `files` 列**必须读 `TB_PEND` 暂存值**,否则上传后格子仍显示旧的「+ 合同」
- ⚠️ `renderGrid` 渲染 `files` 列**必须读 `TB_PEND` 暂存值**,否则上传后格子仍显示旧按钮
- 导出 xlsx 该列输出**文件名文本**(`tables.files_text`),不是 JSON
- 批量导入:`tools/import_contracts.py`(默认干跑,`--apply` 写入,`--replace` 覆盖);
解析文件名**开头**的单元号串(`1101、1115` / `124.155号` / `132&227车位` / `501-502室` / `601A&702`),
再按台账实际存在的 `n室` / `n车位` 定夺;`项目合约/`(93MB)**不入库**
- ⚠️ **同一份文件只落盘一次**(按文件名缓存 meta),多单元挂同一个 id —— 否则 29 份会膨胀成 35 份
- **已导入(2026-10-15 · 线上)**:29 份 → **35 个单元**,落盘 **29 份 / 92.3MB**、无失效关联;
6 组共用同一份:307/603 · 601A/702 · 132/227车位 · 124/155车位 · 1101/1115 · 501/502
- 线上跑法:文件放 `/opt/rentos/uploads/项目合约`(挂载进容器 = `/app/uploads/...`),
再 `docker exec rentos python /app/tools/import_contracts.py --dir /app/uploads/项目合约 --apply`
(脚本 `--dir` 传绝对路径即可,`os.path.join` 会丢弃 ROOT)
- 导入:`tools/import_contracts.py`(默认干跑,`--apply` 写入,`--replace` 覆盖);
解析文件名**开头**的单元号串(`1101、1115` / `124.155号` / `132&227车位` / `501-502室` / `601A&702`)
- ⚠️ **同一份文件只落盘一次**(按文件名缓存 meta),多单元挂同一个 id —— 否则 29 份膨胀成 35 份
- **线上已导入**:29 份 → **35 个单元**,落盘 **29 份 / 92.3MB**、无失效关联;
6 组共用:307/603 · 601A/702 · 132/227车位 · 124/155车位 · 1101/1115 · 501/502
- 线上跑法:文件放 `/opt/rentos/uploads/项目合约`(容器内 `/app/uploads/...`)→
`docker exec rentos python /app/tools/import_contracts.py --dir /app/uploads/项目合约 --apply`
- 源副本留服务器 `/opt/rentos/contracts-src/项目合约`(93MB),`uploads/` 已清空
## 合同 × 台账核对(2026-10-15 下半场)
- **`tools/sync_pull.py`** —— 数据**回拉**通道(`deploy_git.py` 只管推代码)。
`--dry` 列差异 → 备份 `data/_snap/pull-<ts>/` → SFTP 递归下载;默认跳 `attachments`、
**永不回拉 `sessions.json`**;⚠️ **只增/改,不删本地独有文件** → 本地残留需自己清。
- **`tools/check_contracts_vs_table.py`** —— 文件名级机械比对(复用 `import_contracts` 的单元解析)
- ⚠️ **合同文件名不可信**!已知 4 处与正文不符:119车位(租客/期限都写错)、
1102(漏「安装」)、307&603、601A&702(多主体)。**判断一律以正文为准**
- 读扫描件:`pypdfium2` 渲染成图人工判读。房屋合同的甲乙方在**第 2 页**,
「第三条 期限与交付」在**第 3~4 页**(因模板而异,别写死页号)
- 读 `.doc`:`raw.decode("utf-16-le","ignore")` + 中文串抽取即可读到正文
- **核对结论(29 份 → 35 单元,18 对 / 17 有出入)**:
- 影响出单:903室月租台账 36300/合同 16500;904室月租空/合同 19800;
501室期空、502室标「(空置)」(合同 501+502 一体租给明境 2025-06-16~2028-06-15、月租合计 35648);
126/128车位合同期整体早一年(应 2026-03-19~/2026-03-01~);154车位台账空置(合同 徐相天);
601B室租客名带**「(空置)」后缀**会被出单逻辑跳过;315室期错(合同 2026-11-01~2028-10-31,JR202609315);
305室物业费台账 1512 / 合同 1312
- 待确认:132/227车位合同承租方=**上海伯凌建筑设计**而台账=上海爱种草(且已按爱种草出单);
119/124/155/191/227 车位合同甲方均为**精锐**,台账出租方是 瑞格嘉德/科博;
903/904 面积都填 271.17(均值,应 247.24/295.10);306/310「租赁期(月)」12/19 应为 24
- **当前不出单的 9 个单元**(未出现在任何房租批次):401 · 501 · 502 · 903 · 904 · 126车位 ·
128车位 · 154车位 · 601B室,月租合计约 **¥98,262/月**
- 产出:`output/合同-台账核对报告.md`(入库)+ `output/合同-台账核对明细.csv`(output/*.csv 被忽略)
## 房租车位费出单
链路:`rent_parser.py` → `rent_render.py`(→ `notice_pdf`)→ `service.ingest_rent` → 批次
- 计费:期间起 = **AC**,止 = 起 + N 个自然月 − 1 天;`房屋租赁费 = P × N`、`物业费 = O × N`;
......@@ -103,7 +123,7 @@
- 推进/回滚走 `tables.update_cells`;`rollback_rent_advance` **只回滚当前值仍等于推进后的行**
- **账期区间筛选**:`classify_period(from, to)` 命中 = 账期起始日落在区间内(旧 `?on=` 是 `to` 别名)
- 接口:`GET /api/batches?kind=` · `GET /api/rent/preview?from=&to=&all=1` · `POST /api/rent/batches` ·
`DELETE /api/batches/{id}` `{rollback:true}` · `POST /api/batches/{id}/receipt`(收款/开票,**只房租批次**)·
`DELETE /api/batches/{id}` `{rollback:true}` · `POST /api/batches/{id}/receipt`(**只房租批次**)·
`GET /api/batches/{id}/export`
- ⚠️ `GET /api/batches/{id}` 的 `receipt` 在**响应顶层**
......@@ -129,18 +149,23 @@
字体 `simhei.ttf` 子集嵌入;A4、`MARGIN = 1.9cm`
## 环境
- Python `…/python/versions/3.13.12/python.exe`(openpyxl / reportlab / pillow;无 pypdfium2)
- venv `…/envs/default/Scripts/python.exe`:**Playwright + reportlab + pypdfium2** → **单测与 UI 回归都用它**
- Python `…/python/versions/3.13.12/python.exe`(openpyxl / reportlab / pillow;**无 pypdfium2**)
- venv `…/envs/default/Scripts/python.exe`:**Playwright + reportlab + pypdfium2 + PyPDF2** →
**单测 / UI 回归 / 合同判读都用它**
- Bash 先 `export PATH=…/PortableGit/versions/1.2.0/usr/bin:…/bin:$PATH`
- `.bat` 必须 **cp936 + CRLF**;分支用 `goto` 标签;echo 不放半角括号/`!`
- **改完后端代码必须 kill 所有在跑实例再重启**(旧进程会按旧逻辑写同一 `data/`)
- ⚠️ 本工具链会把命令里的 `\n` 字面化 → 写多行脚本用 heredoc + `textwrap.dedent`,别用 `"\n".join`
- ⚠️ 别用 `io.TextIOWrapper(sys.stdout.buffer,…)` 二次包 stdout(旧包装器 GC 时关掉 buffer →
「I/O operation on closed file」);用 `sys.stdout.reconfigure(encoding="utf-8")`
## 启动 / 部署(192.168.68.121 · CentOS 7)
- `start.bat [safe] [端口]` / `stop.bat`;`notify.enabled=True` 时双击 `start.bat` **会开定时发送**
→ **会话内一律 `start.bat safe`**;会话里它内层 `start` 会被拦 → 直接后台起
`python -m rentos.server --port 8899 --no-scheduler`
- 云端 `http://192.168.68.121:8899/`(admin / 1989@rent05),容器 `rentos`,root / 12345678
- 云端 `http://192.168.68.121:8899/`,容器 `rentos`,root / 12345678
- ⚠️ **线上 admin 密码已不是 1989@rent05**(10-14 之后被改过,`auth.verify` 实测 False)→
部署自检已改成登录失败时提示并跳过 HTTP 抽查,不再抛栈
- **主用 Git 同步**(⚠️ **必须用 venv python**,3.13.12 无 paramiko):
`RENTOS_SSH_PASSWORD=12345678 …python tools/deploy_git.py sync`
- push 绕代理:`env -u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY git push`
......@@ -153,23 +178,22 @@
- 云端已能直发企微:`/usr/local/bin/wecom-cli` + 凭据 `/opt/rentos/wecom-config/`(700)
- ⚠️ **线上是唯一生产方,本机只作预览**;**本机与云端不要同时开定时器**
- 远端 `https://git.thiztech.com/accounting/rentos.git`(Gitea),分支 `main`;
`.gitignore` 排除 `data/sessions.json`(含活跃 token)/`batches`/`drafts`/`contracts`/`项目合约/`;
`.gitattributes`:`* text=auto eol=lf`、`*.bat -text`、**Dockerfile 必须 LF**
`.gitignore` 排除 `data/sessions.json`/`batches`/`drafts`/`contracts`/`attachments`/`_snap`/`_trash`/
`output/*`(**`!output/*.md` 保留验收报告**)/`项目合约/`;`.gitattributes`:`* text=auto eol=lf`、
`*.bat -text`、**Dockerfile 必须 LF**
## 验证脚本(tools/)
- 单测 **328 项**:`test_rules` 45 · `test_directory` 33 · `test_rent` 53 · `test_round5` 54 ·
`test_accounts` 60 · `test_round6` 44 · `test_masters` 19 · **`test_contracts` 20**
- 单测 **329 项**:`test_rules` 45 · `test_directory` 33 · `test_rent` 53 · `test_round5` 54 ·
`test_accounts` 60 · `test_round6` 44 · `test_masters` 19 · `test_contracts` 21
(`test_scheduler.py` 需活跃 8899 服务,无服务时失败属预期)
- UI 回归:**自带沙箱** `ui_verify7~12.py`;**需连服务** `ui_verify`·`2`~`6.py`
→ 台账+电费录入 / 设置+锁定+批量暂存 / 带入+照明+图片 / 公司管理 / 房租出单全链 /
收款开票+导出 / 访客能力 / 账期区间 / 批量编辑 / 行粒度核对+账期汇总 /
**合同原件(ui_verify12,30 项)**
- API 冒烟(沙箱):`smoke_verify_edit.py`(核对态改数据 + 草稿)·
**`smoke_verify_contracts.py`(合同 24 项)**
收款开票+导出 / 访客能力 / 账期区间 / 批量编辑 / 行粒度核对+账期汇总 / **合同原件(ui_verify12)**
- API 冒烟(沙箱):`smoke_verify_edit.py` · `smoke_verify_contracts.py`(合同 24 项)
- ⚠️ `ui_verify7` 的只读样本是脚本自建 `ro_verify`(guest 已是「电费操作员」,不能当纯只读样本)
- **`tools/ui_verify_runner.py <脚本…>`**:每个脚本起**全新沙箱**(复制 data/ + 改写 config 常量 +
另起端口 + 注入 `RENTOS_BASE`);⚠️ **1~6 号有硬闸门**(未设 `RENTOS_BASE`/`RENTOS_ALLOW_REAL=1` 直接退出)
- ⚠️ **沙箱常量清单要同步补**(`BATCH_DIR`/`OUTPUT_DIR`/`DRAFT_DIR`/**`CONTRACT_DIR`**)——
- ⚠️ **沙箱常量清单要同步补**(`BATCH_DIR`/`OUTPUT_DIR`/`DRAFT_DIR`/`CONTRACT_DIR`)——
共 7 处:`ui_verify_runner` + `ui_verify7/8/9/10/11` + `check_scheduler`;漏了就会写进**真实** `data/`
(2026-10-13 直连 8899 曾误写 17 个测试批次)
- 迁移:`migrate_rentfree_type.py` · `import_park_notes.py` · `migrate_next_bill.py`
......@@ -186,5 +210,7 @@
## 待巴特菲拍板
多办公地是否带公司名、车位是否算「办公地」、同名单据加期间、电费 0 用量是否出单、
4F/5F 与爱种草渠道口径、通知日期/缴费期限口径、是否做收款核销。
**未处置**:9 月电费批次多计 10 行 ¥167,947.41 / 少计 601B ¥299.67 / 出租方错记「精锐」。
4F/5F 与爱种草渠道口径、通知日期/缴费期限口径、是否做收款核销;
**合同核对发现的 17 处台账出入要不要动手修**;本机 4 张残留测试批次是否移 `_trash/`。
**未处置**:9 月电费批次多计 10 行 ¥167,947.41 / 少计 601B ¥299.67 / 出租方错记「精锐」;
`ui_verify11` 里「保存修改」按钮的断言待改写。
# 合同原件 × 房租车位费台账 核对报告
- 核对时间:2026-10-15
- 数据来源:`/opt/rentos/data`(线上权威数据,已用 `tools/sync_pull.py` 回拉到本机)
- 合同来源:`data/contracts/` 29 份原件(扫描件渲染成图人工判读;`.doc` 走 UTF-16 正文抽取)
- 台账来源:`data/tables/rent_parking.json`(85 行 × 32 列)
- 覆盖:**29 份合同 → 35 个单元**(台账共 85 行,其余 50 行为空置/无合同)
## 一、结论速览
| 结果 | 单元数 |
|---|---|
| 与合同一致 | **18** |
| 有出入(需修正/确认) | **17** |
| 其中影响「能不能出单」 | 7 个单元 |
**17 个有出入的单元按月租估算,系统内处于「不出单」或「金额不对」状态的月租合计约 ¥98,262/月。**
---
## 二、需要立刻处理的(会影响出单 / 金额)
| # | 单元 | 合同写的 | 台账写的 | 后果 |
|---|---|---|---|---|
| 1 | **903室** | 月租 **16,500**(2.2元/㎡×247.24㎡) | **36,300** | 若已出单则每期多收 19,800;未出现在任何房租批次 |
| 2 | **904室** | 月租 **19,800** | **空** | 月租为空 → 永不出单,漏收 19,800/月 |
| 3 | **501室** | 明境承租,**2025-06-16~2028-06-15**,501+502 一体月租 **35,648** | 合同期**空**、面积 253.70、月租 17,824 | 合同期空 → 不出单 |
| 4 | **502室** | 同上(合同覆盖 501+502) | 承租方 **(空置)**、合同期**空** | 被当空置 → 不出单(月租栏却填了 17,824) |
| 5 | **126车位** | **2026-03-19~2027-12-30**,700/月 | **2025-03-19~2026-03-18** | 整体早一年,台账已"过期" → 不出单,漏 700/月 |
| 6 | **128车位** | **2026-03-01~2027-12-30**,480/月 | **2025-03-01~2026-02-28** | 同上,漏 480/月 |
| 7 | **154车位** | **徐相天**,2026-10-01~2027-09-30,600/月 | **(空置)**,期限空 | 被当空置 → 漏 600/月 |
| 8 | **601B室** | 上海博睿孚信息科技发展有限公司,2025-10-01~2027-09-30 | **上海博睿孚信息科技发展有限公司(空置)** | 租客名带「空置」→ 出单逻辑判为空置跳过,漏 6,792/月 |
| 9 | **315室** | **2026-11-01~2028-10-31**(合同号 JR202609315,24 个月) | **2026-01-01~2026-10-31** | 台账期止早于期始的下一期,内部矛盾;K 列已是 24,说明只改了月数没改日期 |
| 10 | **305室** | 物业费 **1,312**/月(补充协议原文) | **1,512** | 已出单 12,000/期 = 10,488+1,512;应为 11,800,**每期多收 200** |
> 903/904/501/502/401/126/128/154/601B 共 9 个单元,在现存的 4 张房租批次(2026年10月、11月、12月、2027年1~3月)里**一次都没出现**,说明它们当前都不会出单。
## 三、主体/面积等需要确认的
| 单元 | 问题 |
|---|---|
| **132车位 / 227车位** | 合同(2026-10-01 起)承租方是 **上海伯凌建筑设计有限公司**,台账是 **上海爱种草科技发展有限公司**,且已按爱种草出了 2026年10月 与 2027Q1 的单 → 谁在租?换租客没更新台账,还是合同未生效? |
| **119 / 124 / 155 / 191 / 227 车位** | 这 5 份车位合同甲方都是 **精锐(上海)仓储有限公司**,台账出租方分别是 瑞格嘉德 / 科博 / 瑞格嘉德 / 科博 / 科博,且已按台账出租方出单 → 是台账填错,还是收租主体与合同甲方本就不同? |
| **603室** | 合同出租方=精锐,台账=瑞格嘉德(同 307室 一份合同,台账两行出租方不一致) |
| **903室 / 904室 面积** | 两行都填 **271.17**(= 247.24 与 295.10 的均值);合同分别是 247.24 / 295.10 |
| **306室 / 310室 租赁期(月)** | 台账 12 / 19;按合同期应为 24(不影响出单,出单用付款周期 N,但表格自相矛盾) |
| **第二承租方未记账** | 307&603 合同有「锦众心诚(上海)美容科技」、601A&702 合同有「上海央旗国际贸易」,台账只记了锦玉良源 / 上海长盟 |
| **401室** | 月租 17,742.1329 vs 合同起始月租 17,742(差 0.13,可忽略);但 401 也从未出单 |
## 四、核对通过的 18 个单元
302室 · 303室 · 306室 · 307室 · 310室 · 312室 · 601A室 · 607室 · 702室 · 706A室 · 706B室 ·
1101室 · 1115室 · 1102室 · 1114室 · 401室(金额口径)· 168车位 · 169车位
抽样证据:
- **302室** 杭州逢林科技 / 2026-08-16~2028-08-31 / 免租 8-16~8-31 / 80㎡ / 精锐 —— 全对
- **303室** 上海绿炬环保 / 2026-09-01~2028-08-31 / 5,992 / 1,008 / 80㎡ —— 全对(含免租 0.5 月)
- **607室** 上海禧华堂建筑 / 2026-08-01~2028-07-31 / 17,720 / 2,583 / 205㎡ —— 全对
- **706A室** 针知运 / 2026-08-01~2028-07-31 / 9,490 / 1,512 / 120㎡ —— 全对
- **706B室** 上海柏晟昊服饰 / 2026-08-01~2028-07-31 / 6,326 / 1,008 / 80㎡ —— 全对
- **1101室/1115室** 上海向安实业 / 2025-10-01~2027-09-30 / 免租 4 个月(每年 8-9 月)/ 107.88㎡ —— 全对
- **1102室** 上海明境建筑**安装**有限公司 / 2026-01-01~2026-12-31 / 55.64㎡ —— 全对
- **1114室** 上海豪瞬 / 2026-04-01~2027-03-31 / 4,550 —— 全对
- **168车位** 针知运 / 2026-10-01~2027-09-30 / 600 / 精锐 —— 全对
- **169车位** 刘毓敏 / 2026-07-01~2027-12-31 / 600 / 精锐 —— 全对
- **401室** 晟音汇 / 2023-12-18~2027-12-17 / 出租方变更为科博 —— 全对
## 五、⚠️ 重要发现:合同文件名不可信
文件名是人工起的,有 4 处与正文不符,**不能拿文件名当数据源**:
| 文件名写的 | 正文实际 |
|---|---|
| `119车位合同(针织运 2026.10.1-2027.9.30)` | 针知运;**2026-08-05 ~ 2027-12-31**(台账与正文一致,文件名错) |
| `1102室 上海明境建筑有限公司 2026.1.1-2026.12.31` | 上海明境建筑**安装**有限公司 |
| `307&603房屋租赁合同(锦玉&锦众…)` | 一份合同两个承租方,分别挂 307 / 603 |
| `601A&702房屋租赁合同(上海长盟&央旗…)` | 同上,央旗未记账 |
## 六、顺带发现的本机数据残留(不影响线上)
本机 `data/batches/` 有 4 张**线上没有**的批次,是本机测试残留:
```
20261014-134503 · 20261014-134504 · 20261015-134717 · 20261015-135238
(均为 elec / 2026年10月 / 302室·303室 / prev=100 cur=180 / 核对人 "u")
```
线上 7 张批次完好(电费 3 张 + 房租 4 张),本机多出的这 4 张只是本地文件,未污染线上。已建议移入 `data/batches/_trash/`。
---
## 附:核对方法
| 步骤 | 工具 |
|---|---|
| 拉线上数据 → 本机 | `tools/sync_pull.py`(新增;拉前自动备份到 `data/_snap/pull-<时间戳>/`) |
| 文件名/机器比对 | `tools/check_contracts_vs_table.py`(新增) |
| 扫描件 PDF → 图片判读 | `pypdfium2` 渲染,多份合同并排成一张图 |
| `.doc` 正文 | UTF-16LE 解码 + 中文串抽取(Word 97 格式可读) |
| 明细导出 | `output/合同-台账核对明细.csv`(35 行 × 17 列,Excel 可直接打开) |
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment