1. 15 Oct, 2026 11 commits
    • wenwen.tang's avatar
      docs: 线上数据回拉 + 合同×台账核对报告(29 份 → 35 单元,18 对 / 17 有出入) · 1ac8a1e1
      wenwen.tang authored
      同步:线上 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 张线上没有的测试残留(未动)。
      1ac8a1e1
    • wenwen.tang's avatar
      feat(tools): 数据回拉通道 + 合同×台账核对脚本 · c9146ddc
      wenwen.tang authored
      - tools/sync_pull.py:把线上 /opt/rentos/data 回拉到本地 data/(deploy_git.py 只管推代码)。
        --dry 先列差异 → 备份 data/_snap/pull-<ts>/ → SFTP 递归下载;默认跳过 attachments(61MB),
        sessions.json 永不回拉(线上活跃 token)。⚠️ 只增/改、不删本地独有文件。
      - tools/check_contracts_vs_table.py:按文件名解析单元/租客/期限与台账逐条比对,
        复用 import_contracts 的单元解析保证「挂上的行」与「核对的行」是同一行。
        支持 --pdf 抽正文,-v 出明细,--json 落盘。
      c9146ddc
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      fix(tools): 合同导入按文件只落盘一次,多单元共用同一份 · d54f0e09
      wenwen.tang authored
      原先每个单元都调一次 contracts.save(),导致多单元合同(1101、1115 / 124.155 /
      132&227 / 307&603 / 501-502 / 601A&702)在服务器上重复存了多份:
      29 个源文件变成 35 份存储(127MB vs 源 93MB),也与「多行共享同一份」的设计相悖。
      改为按文件名缓存 meta,同一份只落盘一次、多个单元挂同一个 id;
      失败也按文件记一次,不再每个单元重复报。
      d54f0e09
    • wenwen.tang's avatar
      fix(contracts): 单份上限 20MB -> 40MB(1101/1115 室那份合同 21.8MB 被拒) · 53b23899
      wenwen.tang authored
      批量导入「项目合约」时发现最大的一份扫描件 21.8MB,被 20MB 上限挡下
      (29 份里其余 ≤ 12.7MB)。上限设 40MB:既覆盖扫描件,base64 后 53MB
      仍小于 server 的 MAX_BODY=64MB,不会在 HTTP 层被拒。
      同步前端 pickContract 的预检与两处文案;补一条守护测试(上限 ≥ 22MB 且
      base64 后 < 64MB),避免以后又把上限调小回去。
      53b23899
    • wenwen.tang's avatar
      fix(deploy): 线上登录失败时不再抛栈,明确提示密码可能已改 · 9b731ea8
      wenwen.tang authored
      线上 admin 密码已被改过(auth.verify('admin','1989@rent05') 返回 False),
      部署自检的「接口抽查」「企微状态抽查」两段会 401 抛一大段 traceback,
      看上去像部署失败。改为捕获后打印「登录 : FAIL —— 线上 admin 密码可能已被改过」
      并跳过后续 HTTP 抽查(容器内自测 / 宿主机 HTTP 200 / 6b 能力自检照常执行)。
      不改动任何密码,仅调整自检脚本的报错呈现。
      
      顺带记录:合同原件功能的线上验证走容器内模块调用(32 列、AL 列、合同目录可写、
      写入回读删除全通),HTTP/UI 链路已在 runner 沙箱与 ui_verify12 覆盖。
      9b731ea8
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      test(contracts): 单测 20 项 + API 冒烟 24 项 + UI 回归 30 项;沙箱补 CONTRACT_DIR · bcbfeeba
      wenwen.tang authored
      - test_contracts.py(新,20 项):解码白名单 / 超限 / 非法 base64、存取与路径穿越、
        列值 parse/dump/describe 容错、AL 列登记、coerce、files_text、
        台账写读 + 导出 xlsx 是文件名、合同目录与备注图片目录互不干扰
      - smoke_verify_contracts.py(新,24 项,经 runner 沙箱):上传 → 查阅 200 +
        application/pdf + 字节一致 → ?dl=1 是 attachment → 写 AL → 导出 xlsx 是文件名 →
        访客上传/删 403 但可查阅 → 非法类型 400 → 穿越 404 → 清空 AL → 删文件后 404
      - ui_verify12.py(新,30 项,自带沙箱 + 真浏览器):表头「合同原件」在最右、
        只读无上传入口、编辑态「+ 合同」→ 浮层 → 真实文件选择器上传 → 三个操作齐全 →
        查阅链接带 token 且返回原字节 → 关浮层变「📎 1 份」且 pending → 保存落盘 →
        只读浮层只有查阅/下载 → 移除后 AL 清空但服务器文件仍在 → 无 console 错误
      - 沙箱常量清单补 CONTRACT_DIR(runner + ui_verify7/8/9/10/11 + check_scheduler 共 7 处),
        漏了会像 DRAFT_DIR 那样把合同写进真实 data/
      - test_rules:列数 31→32,并断言「合同原件」排最后、AJ 紧随其后
      bcbfeeba
    • wenwen.tang's avatar
      feat(tools): 合同批量导入脚本;合同目录不入库 · 7a75747e
      wenwen.tang authored
      - tools/import_contracts.py:把「项目合约」里的合同挂到台账 AL 列
        默认干跑(只报匹配),--apply 才写入,--replace 覆盖(默认追加,一份可挂多份)
        解析文件名开头的单元号串:1101、1115 / 124.155号 / 132&227车位 / 501-502室 / 601A&702 / 169
        每个号再按台账实际存在的 n室 / n车位 定夺(都在时看「车位 / 号」线索)
        干跑结果:29 个文件 → 35 个单元,零未匹配、零歧义
      - .gitignore:data/contracts/(运行时数据)、项目合约/(93MB + 商业条款,不入库)
      7a75747e
    • wenwen.tang's avatar
      feat(web): 台账「合同原件」列的按钮与合同管理浮层 · d6fd7909
      wenwen.tang authored
      - td.files 不再走打字编辑:有合同显示「📎 N 份」,编辑态空行显示「+ 合同」,只读空行显示 —
      - openFilesDlg:列出文件名 / 大小 / 上传时间 + 查阅 / 下载 / 移除,底部「上传合同」;
        pickContract 走真实文件选择器(可多选,逐个上传,>20MB 跳过)
      - 增删都走 stageCell(rowId,'AL','files',…) 暂存,与普通格子一致——
        回列表点「保存 N 处改动」才写台账;只读态浮层没有上传/移除入口
      - 修 bug:renderGrid 的 files 分支原先读台账原值,上传后(暂存未保存)格子仍显示旧按钮 →
        改为优先读 TB_PEND 暂存值
      - startEdit 对 files 列直接返回;delRow/startEdit 的行标签统一走 rowLabelOf()
      - fmtVal 增加 files 分支(修改记录 / 确认框里显示文件名列表)
      - CSS:td.files 按钮样式(pill + 虚线加号 + 暂存态黄底);admin.html 补用法说明
      d6fd7909
    • wenwen.tang's avatar
      feat(contracts): 台账新增「合同原件」列(AL),支持上传 / 查阅 / 移除 · 09815170
      wenwen.tang authored
      需求:房租车位费数据表需要支持上传合同留档并可查阅合同,增加合同原件的列。
      
      - 新增 rentos/contracts.py:data/contracts/{id}.{ext} + index.json,
        白名单 pdf/doc/docx/xls/xlsx/png/jpg/gif/webp/bmp,单份 ≤ 20MB;
        save/find/path_of/list_all/remove/parse/dump/describe;clean_id 挡路径穿越
      - config:加 CONTRACT_DIR 并纳入 ensure_dirs
      - tables:列 AL「合同原件」type=files(排 AJ 之后、最右),一格可挂多份;
        coerce 支持 list → 紧凑 JSON;新增 files_text 供导出 xlsx 输出文件名文本
      - server:
        POST /api/contracts(上传,仅管理员)
        GET  /api/contracts(列表)
        GET  /api/contracts/{id}(内联预览;?dl=1 下载;支持 ?token=,便于 <a href> 直接引用)
        DELETE /api/contracts/{id}(仅管理员)
        合同写接口不匹配任何 CAP_ELEC 规则 → 落 _write_cap 兜底 "*" = 仅管理员;
        访客只读仍可查阅(这点由冒烟验证)
      - 列定义以代码为准,故加列不需要数据迁移
      09815170
  2. 14 Oct, 2026 11 commits
    • wenwen.tang's avatar
      chore: data/_trash/ 回收站不入库 · 12bc591c
      wenwen.tang authored
      12bc591c
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      test: 沙箱补 DRAFT_DIR 防污染;smoke 重写、round6 覆盖草稿 · 9b014b66
      wenwen.tang authored
      - ui_verify_runner + ui_verify7/8/9/10/11 + check_scheduler:沙箱配置枚举里补
        DRAFT_DIR 指向沙箱 data/drafts —— 不补会让草稿写进真实 data/(同 17 批次泄漏那类坑)
      - test_round6:删 TestKeepVerify(9 项,服务端不再保留核对),新增 5 项草稿用例
      - 新增 tools/smoke_verify_edit.py:核对态编辑 → 确认 → 改动落盘且核对状态保持,
        外加「部分核对 ok=True partial=True」共 16 断言,全部跑在沙箱
      9b014b66
    • wenwen.tang's avatar
      feat(web): 核对态可直接改数据并随「确认核对」一并存盘;录入加临时保存 · fff9b2a6
      wenwen.tang authored
      核对:
      - 核对模式下表格恢复可编辑,改数据不再影响核对状态(勾选仍由人工决定)
      - 删除「保存修改」按钮与 saveVerifyEdits();改为 confirmVerify() 先 saveVerifyData()
        按批次分组逐张 PUT,再 POST /api/periods/verify,按钮文案「确认核对(存 N 处改动)」
      - 脏基线由数组下标改为稳定行号 _rid(ekey/assignRowIds),修掉删行导致的基线漂移
      - 退出核对若有未保存改动,提示「退出就丢了」
      
      录入:
      - 新增「临时保存」按钮:saveElecDraft/clearElecDraft/restoreElecDraft/elecBootFill
      - 有草稿时不再自动带入上期(以草稿为准),生成成功后自动清除草稿
      - td 取值由 children[N] 改为按 data-col 定位
      
      文案:admin.html 同步说明临时保存与「改数据不影响核对状态、确认时一并保存」
      fff9b2a6
    • wenwen.tang's avatar
      feat(elec): 录入新增「临时保存」草稿,核对态改由前端统一存盘 · 9f0dd9ea
      wenwen.tang authored
      - config: 新增 DRAFT_DIR=data/drafts,纳入 ensure_dirs 初始化
      - store: 草稿读写删 draft_path/save_draft/load_draft/delete_draft(按账号隔离)
      - server: 新增 GET|PUT|DELETE /api/drafts/elec;去掉核对 keep_verify 透传
      - service: 删除 keep_verify 指纹比对那套(reingest 恢复为「核对一律重置」),
        核对状态不再由服务端猜,改由前端「确认核对」时统一提交
      - gitignore: data/drafts/ 属运行时状态,不入库
      
      单测 test_rules/test_round6/test_directory/test_rent/test_round5/test_accounts/test_masters 全绿
      9f0dd9ea
    • wenwen.tang's avatar
    • wenwen.tang's avatar
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      test: 补行粒度核对与楼层总表的单测,回归脚本改数据驱动并加沙箱闸门 · 9326844a
      wenwen.tang authored
      - 新增 tools/test_round6.py(39 项):覆盖行粒度核对全链路 ——
        进度与部分确认、旧 units 展开成行、只核单一类目、确认后取消勾选会撤销确认、
        未知行过滤、账期级部分确认放行、批次摘要字段
      - 新增 tools/test_masters.py:楼层总表 公共/套内/空调总 的系数与金额口径
      - test_rules.py:免租期断言改为与 `rentfree.windows()` 对拍的数据驱动写法,
        不再写死「3 段 / 只有 302室」(台账新增 801/802 后会过期)
      - ui_verify.py/2/3/4/5/6.py:**未设 RENTOS_BASE 时直接拒绝运行**。
        此前 ui_verify2 直连 8899 真实服务,往 `data/batches/` 写了 17 个测试批次、
        还改脏了台账 2 行。跑这批脚本必须经 `tools/ui_verify_runner.py` 沙箱
      - ui_verify2/5/7.py:批次台账改为按账期汇总默认折叠后,脚本改为只点
        `.collapsed` 分组(hash 导航不刷新页面,折叠状态会延续)
      - 新增 tools/ui_verify11.py(85 项):核对按行 + 列显示 + 部分确认全链路
      9326844a
    • wenwen.tang's avatar
      feat(电费): 核对改按行粒度 + 台账按账期汇总 + 楼层总表登记 · 3aa94d82
      wenwen.tang authored
      一、核对粒度:房间 → 行
      核对从「按房间」改为「按行」,行键 = `单元|类目`(如 `302室|照明`),
      于是可以只核照明、或只核空调,也可只核一部分行。
      
      - service: `blank_verify()` 增 rows/scope;新增 `_row_keys()` / `_checked_rows()`
        (旧 `units` 自动按整室展开,向后兼容)
      - `verify_summary()` 的 `ok` 由「全勾完」改为「已点过确认核对」,
        新增 `partial`(done 且仍有未勾行)
      - `set_verify()` 确认时把勾选快照存进 `verify.scope`;之后进度保存若勾选集合
        少于 scope 就**自动撤销确认**,防止改完数据还挂着「已核对」
      - 归一化时 `units` 置空只留 `rows`,否则行级取消勾选会被派生 units 抵消
      - `period_summary()` 改行并集:一行算已核对 ⇔ 含它的每张批次都勾了它
      
      二、批次台账:按账期汇总、入口收敛
      - 列表按账期(月)汇总成一行,默认折叠,点汇总头展开该账期批次
      - 移除列表里的「重新编辑 / 核对」,统一进批次详情
      - 详情页展示是否核对状态;部分核对发送前二次提醒
      
      三、核对模式的列显示
      - 12 个可显隐列 + 持久化 `localStorage['rentos_vcols']`;「核对」列不可隐藏
      - 修:`[hidden]` 被 `.colpick-menu{display:flex}` 顶掉导致菜单收不回来
      - 修:`bindVCols()` 每次重渲染往 document 挂新监听导致无限累积
      - 开关状态提到模块级 `VCOLS_OPEN`,支持再点按钮/×/点外部/Esc 四种收起方式
      
      四、新增:楼层电费总表(3F/6F/7F)
      - `rentos/masters.py`:每层拆 公共/套内/空调总,按 用量×电价×系数 算金额
      - 口径:**只记账,不出单、不发送**,随批次存档并支持导出
      - 接入 `GET /api/masters`、`ingest_elec(masters=…)`、`reingest_elec(masters=…)`
      3aa94d82
    • wenwen.tang's avatar
      feat(定时): 电费通知单改为 100% 手动发送,退出定时排期 · fce1db5e
      wenwen.tang authored
      口径(巴特菲 2026-10-13):电费通知单全部手动发送,不参与定时自动发送。
      
      - PLANS 增加 `auto` 开关;elec 为 False、rent_parking 为 True
      - 新增 `plans.AUTO_KEYS` / `plans.is_auto()`:定时器只跑 AUTO_KEYS
      - `normalize()` 对 `auto=False` 的计划强制 `enabled=False`,历史配置写什么都保持关闭
      - `plan_info()` 返回 state="manual",设置页给出「手动发送」说明与理由
      - `status()` 增 `auto` / `manual_count`;`enabled` 对手动计划恒为 False
      - `due_plans()` 只遍历 AUTO_KEYS;`fire()` 不带 plan_key 时同样只跑 AUTO_KEYS
        (设置页「手动触发」显式指定 plan_key 时才发该张)
      fce1db5e
  3. 13 Oct, 2026 18 commits
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      feat(权限): 访客账号改按页授权 —— guest 仅「电费通知单」可操作 · 35f0fbce
      wenwen.tang authored
      能力模型:账号/会话新增 perms(目前只有 elec = 电费通知单)。管理员隐含全部能力;访客按 perms 逐接口放行,perms 为空即纯只读。
      
      写闸门重构:server._write_cap(method,path) 逐接口解析所需能力,_guard_write(user,cap) 按能力判定并给出「账号 X 只能操作:电费通知单」的明确提示。批次类接口再按批次 kind 复核 —— 房租车位费批次的删/重出/发送/收款登记仍只有管理员能动。
      
      前端能力感知:body 加 cap-elec 类,电费页控件由 data-ro 改 data-cap=elec;批次列表/单据表的按钮按 kind 区分;api() 前端拦截改为按能力;顶栏与只读横幅按能力显示「访客 · 仅电费」。
      
      账号管理:新增「权限」列(管理员=全部 / 访客=能力标签 / 空=只读),新增账号可勾选「电费通知单」,已有访客可一键开通/关闭;新增 PUT /api/accounts/{name} 改角色/能力/备注;主账号不可降级且强制清空 perms。
      
      一次性回填:老配置没有 perms 字段 → ensure_default_account 按 guest_perms_seeded 标记补上默认能力(之后管理员关掉不会再被打开)。
      
      guest 落地:密码 guest@2026,perms=['elec'],备注「电费通知单操作员」(本地已改,线上部署后同步)。
      
      修 bug:删除不存在的批次时 service.rollback_receipt_sync(None) 抛 AttributeError → 500;现静默返回 0。
      
      验证:单测 245 项全绿(accounts 60);新增 ui_verify10(69 项,guest/admin/纯只读三方对照)全绿;ui_verify7(访客,改成用纯只读样本 + 新增 guest 段)69/0;ui_verify2 72/0;ui_verify 全绿。
      35f0fbce
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      feat(台账): 删房产/房产税六列 + 免租分两种类型 + 台账改批量暂存编辑 · e21a96c3
      wenwen.tang authored
      列模型 36 → 31:删 AD~AI(房产六列,RETIRED_TAX_COLS),新增 AK「免租类型」(显示位置插在 L 之后)。
      
      rentfree 重写为两类型:首N月免租 = [I, I+L月);每年8-9月免租 = 合约期 [I,J] 内每个自然年 8-9 月整月(跨年裁剪,J 按含当日)。hit() 按时点、overlaps() 按账单期间交集(年度类型必需)。
      
      账单按免租数据跳过:rent_preview 对每行与每份单据标 rentfree,汇总新增 rentfree_count / rentfree_rows。
      
      台账编辑改批量暂存:改格先高亮暂存(不再逐格弹窗),「保存全部」一次确认后单次 PUT /api/tables/{key}/cells 落盘(带 428 闸门),可在修改记录里整批撤销。
      
      迁移 tools/migrate_rentfree_type.py(幂等):删 6 列 / 165 存量值 → 按代码重建 31 列 → 回填 302/303/315「首N月免租」。
      
      修渲染真 bug:renderGrid() 末尾补 renderPendBar()(否则保存成功后待保存条不消失)。
      
      验证:单测 219 项全绿;UI 回归 ui_verify 1~9 全绿(新增 ui_verify9.py 39 项 + ui_verify_runner.py 沙箱跑需连服务的脚本)。
      e21a96c3
    • wenwen.tang's avatar
      docs(memory): 记录 push + 同步部署(三方对齐 b559ac4b) · cae0872d
      wenwen.tang authored
      Co-authored-by: CodeBuddy
      cae0872d
    • wenwen.tang's avatar
      test(elec): 新增「源表 xlsx vs 电费批次」逐行核对脚本 · b559ac4b
      wenwen.tang authored
      按源表自身的公式口径核对(本期抄表数为空 = 未抄表 = 不计费,
      与用量列是否残留数字无关);识别合并单元格向下沿用、总表行、摘录表;
      并标出用量列「手填常量且本期为空」的可疑格子。
      
      实测:9月批次与源表 20 处不一致(多计 10 行 ¥167,947.41、少计 601B ¥299.67);
      8月工作表为摘录,核对到的 8 行 0 差异。
      
      Co-authored-by: CodeBuddy
      b559ac4b
    • wenwen.tang's avatar
      docs(memory): 记录定时出单/通知书 能力检查结果 · 0ff97a08
      wenwen.tang authored
      Co-authored-by: CodeBuddy
      0ff97a08
    • wenwen.tang's avatar
      test(scheduler): 新增「定时出单/通知书」能力检查脚本 · fdb5148d
      wenwen.tang authored
      沙箱 + 触发日注入,验证:
        电费       到点 -> 取已录入批次 -> 发送
        房租车位费 到点 -> 自动出单(批次 + PDF 通知书)-> 出单后推进 AC/AJ -> 发送
        反例:未到点不触发
      发送用桩替代(不发真消息),真实 data/ 零改动。
      
      Co-authored-by: CodeBuddy
      fdb5148d
    • wenwen.tang's avatar
      7a896646
    • wenwen.tang's avatar
      chore(data): 落库 auth 多账号结构的运行时迁移结果 · 5df2bfbc
      wenwen.tang authored
      data/config.json 由代码在启动时自动从单账号迁移为 accounts[] 结构
      (admin 保留原 salt/hash 未重置 + 播种只读访客 guest,guest_seeded=true)。
      本地与线上运行时早已是此结构,只是此前未提交,导致工作区长期显示为已修改。
      (data/config.json 为仓库跟踪的配置文件,非运行时业务库,提交无副作用。)
      
      Co-authored-by: CodeBuddy
      5df2bfbc
    • wenwen.tang's avatar
      feat(房租出单): 本期出单筛选改为按账期区间(起~止) · bad77032
      wenwen.tang authored
      「房租车位费」本期出单原先按「出单基准日」筛(账单起始日 <= 基准日),
      现改为按账期区间筛:命中 = 该行账期起始日落在 [起, 止] 内,区间取代基准日。
      
      - rent_parser: 新增 in_period() / classify_period()(-> 区间内/区间外/不可出单);
        原 classify(rows, on_date) 保留,定时器 due_for_push 仍走它
      - service: rent_preview(from, to, all) 改用 classify_period,返回 from/to 取代 on;
        later 语义变为「区间外」;新增 _period_label() 生成账期标签(单月/跨月);
        ingest_rent 增 from_date/to_date 三分支选行,批次记 period_filter_from/to
      - server: /api/rent/preview 收 ?from=&to=(?on= 作兼容别名 = to);
        POST /api/rent/batches 透传 from/to
      - web: #rp-on 拆为 #rp-from / #rp-to;「显示未到期」-> 「显示区间外」;
        文案改「按账期区间筛选」
      - 测试: test_rent 新增 21 项(区间端点/空区间/区间外原因/原 classify 不变/出单/账期标签);
        新增 ui_verify8.py(沙箱+Playwright 端到端 30 项);ui_verify5.py 适配新控件
      
      单测 212 项全绿;ui_verify8 30/30;ui_verify5 43/43。
      
      Co-authored-by: CodeBuddy
      bad77032
    • wenwen.tang's avatar
      chore(memory): 重写长期记忆,去重并压回限额内 · 422a556c
      wenwen.tang authored
      原文超限被注入层截断。合并同类条目(渲染/模板、名册、数据模型、
      业务口径、部署、版本管理、验证脚本),删除已完成的一次性迁移叙述
      与冗余时间线,保留全部有效操作知识(列映射、接口、坑位、铁律)。
      
      Co-authored-by: CodeBuddy
      422a556c
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      chore: 去掉 ui_verify7 未用的 import · cc02d713
      wenwen.tang authored
      cc02d713
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      feat(收款+权限): 收款日期同步台账最近收款月份;新增只读访客账户 · a39ca9ca
      wenwen.tang authored
      1) 收款日期 → 台账账期同步
      - set_receipt 登记/修改收款日期时,把该单据覆盖的台账行「最近收款月份」(AB)
        同步为【收款日期所在月】(口径经确认);清空收款日期则退回同步前的值
      - 批次记 receipt_sync 记录,删除批次时一并回滚「最近收款月份」
      - 批次明细快照的 last_paid 同步跟进,页面不会出现两个口径
      - 修:不能拿 service._d()(失败回落"今天")算月份,改用 rent_parser._d()
      
      2) 只读访客账户
      - auth.py 支持多账号 + 角色(admin 可读写 / viewer 只读),首次启动播种
        admin(1989@rent05) 与 guest(guest@2026);未知角色一律降级 viewer;
        主账号不可删;访客被删后不再自动复活
      - server.py 增加写闸门:除 /api/logout 外,viewer 的 POST/PUT/DELETE 一律 403
      - 新增账号管理接口 GET/POST /api/accounts、DELETE /api/accounts/{u}、
        POST /api/accounts/{u}/password(仅管理员)
      - 前端按角色隐藏写入口 + api() 兜底拦截;设置页新增「账号管理」卡片
      
      测试
      - tools/test_accounts.py(新增 34 项,含真 HTTP 写闸门实测)
      - tools/test_round5.py 补 11 项收款同步用例;全量 191 项通过
      - tools/ui_verify7.py(新增,自带沙箱数据目录 + 真浏览器)59/59 通过
      a39ca9ca
    • wenwen.tang's avatar
    • wenwen.tang's avatar
      fix(deploy): 修正 pull 未真正生效 —— fetch 带分支名只更新 FETCH_HEAD · 51c66dbf
      wenwen.tang authored
      `git fetch origin <branch>` 只更新 FETCH_HEAD,**不会**更新
      refs/remotes/origin/<branch>,于是随后的 `git reset --hard origin/main`
      仍指向旧提交。症状是服务器 repo 停在旧版本(实测停在 0f4af2e8,
      落后远端 3 个提交),而 sync 表面上"成功"。
      
      改为不带分支名的 `git fetch origin`,并输出当前提交与"落后远端提交数"
      便于肉眼确认同步结果。
      51c66dbf