unis_manager/.workbuddy/memory/2026-09-17.md

15 KiB
Raw Permalink Blame History

2026-09-17

清空「定开项目」模块数据

按用户要求清空 data/board.db 里定开项目模块的全部数据(保留表结构与其余模块)。

  • 备份:data/board.db.bak-before-clear-custom-20260917_105824
  • 删除顺序:先子表 custom_updates(28 行)→ 再父表 custom_projects(119 行),避免外键孤儿。
  • 清空前摘要:119 个定开项目(归档 0),合同额合计约 395.2 万,累计计收 0。
  • 清空后校验:两表均 0 行,custom_updates 孤儿记录 0。
  • 接口验证:/api/custom/projects 返回 [];/api/weekly 的 custom 段 0 条,KPI 中 custom_total / custom_amount / custom_new / custom_updated / revenue_delta 全部归零。项目管理模块(projects/tasks/updates)未受影响。
  • ledger_items(定开台账)本来就是 0 行,未动。

导入《定开项目.xlsx》市场台账 + 扩展 custom_projects 字段

用户提供 C:/Users/Administrator/Desktop/定开项目.xlsx(Sheet1,120 行含表头 → 119 条数据,14 列),要求导入定开项目库、按表格调整字段、并加「更新时间」字段便于按周查看变化。经确认采用:新增字段(不重构) + 每行一个更新时间 + 项目状态新增独立字段。

表结构(10 个新字段,靠 schema_patch.ensure_columns 自动 ALTER TABLE)

origin(项目归属) / project_code(项目ID, index) / office(办事处) / industry(行业) / man_days(市场下单人天) / market_owner(市场责任人) / project_status(项目状态) / accept_plan(推动验收计划) / risk_level(风险值) / update_date(更新时间 YYYY-MM-DD, index)

字段映射(表格列 → DB 列)

  • 项目归属→origin、项目名称→name、项目ID→project_code、行业→industry、市场下单人天→man_days、市场责任人→market_owner、项目状态→project_status、推动验收计划→accept_plan、风险值→risk_level、备注→remark
  • 下单时间→sign_date(原字段注释就是「签单/下单时间」);办事处→region(归一化)+ office(原样);验收时间→accept_date
  • entry_date(入库时间)兜底取下单时间,保证明细列表「按入库时间倒序」有排序键

数据质量(表格里就有的问题,未擅自改)

  • 重名 2 行:「甘肃省水利厅图像站建设项目二期」×2,项目ID 不同(HGSE25122401N / HGS025010202N)→ 导入脚本有 ID 时只按 ID 匹配,否则会互相覆盖
  • 1 行无项目ID(高新区感知源-钰田(定制开发))、1 行无办事处
  • 「验收时间/推动验收计划」是季度文本(Q1..Q4/27年),互补:60+59=119
  • 「下单时间」混格式:91 个真日期 + 28 个文本(2024年3月前、2024/)→ 日期转 YYYY-MM-DD,文本原样存
  • 「市场责任人」51 人全部不在花名册(销售侧人员,与 owner 交付负责人是两个概念)
  • 只有「深圳」不在区域词表 → 已在 constants.REGION_ALIAS 加 深圳/深圳市 → 广东

改动文件

  • app/models.py:CustomProject 加 10 字段 + to_dict 输出
  • app/routers/custom.py:PATCH 白名单加新字段;台账字段有变化时刷新 update_date(无变化不刷新,已实测);创建时写 update_date;CustomProjectIn 补字段
  • app/routers/export.py:导出追加 10 列(末尾追加,保持原 money_cols 索引不变)
  • web/app.js:明细列表 16→25 列(加 项目ID/行业/项目归属/市场责任人/项目状态/风险值/人天/推动验收/更新时间);新增 statusPill/riskPill 小色块;详情抽屉加「市场台账」区块(只读 + 编辑);KPI 提示加 人天/高风险
  • scripts/import_custom_ledger.py:新建,openpyxl 读取 + 按 ID/名称 upsert + 变更比对,支持 --dry-run / --date

验证

  • 导入:新增 119 / 更新 0 / 无变化 0;重复导入幂等(0/0/119)
  • 分布与源表一致:项目状态 已验收60/待开发31/交付完成17/交付中9/退单2;行业 安平51/政府25/企业22/教育11/交通6/医疗2/行业1/高速1
  • /api/custom/projects 返回 119 条含全部新字段;导出 34 列、合计行人天 5066
  • PATCH 改 industry → update_date 由 2026-01-01 刷新为今天;提交相同值 → 保持旧日期(变更检测正确),测试后已还原
  • 备份:data/board.db.bak-before-ledger-import-20260917_141759

遗留 / 待确认

  • 119 行 stage 全是默认 signed、progress 0、计收/开票/回款为空 —— 新台账不带这些业务字段,模块里依赖它们的旧列(进度%、可确认、已计收、开票、回款)目前是空的
  • 页面未做浏览器截图验证(agent-browser 未安装,装 Chromium 要 ~500MB 且规则禁止全局 npm),只做了 JS 语法校验 + 接口/导出功能验证

定开项目明细表改为按 Excel 表头顺序展示

用户要求明细表字段参考导入的 Excel 表头。实际 Excel 表头顺序(14 列)为:项目归属 / 项目名称 / 项目ID / 下单时间 / 办事处 / 行业 / 市场下单人天 / 订单金额 / 市场责任人 / 项目状态 / 验收时间 / 推动验收计划 / 风险值 / 备注。

  • 重排 renderCustomList 表头+数据行,完全按此顺序;追加「更新时间」(用户此前要求,非 Excel 字段)与「操作」列 → 共 16 列。
  • 从主表移除 Excel 没有的旧列:区域、负责人(owner)、入库、交付、阶段、进度%、可确认/已计收/开票/回款 —— 这些仍保留在详情抽屉里。
  • 顶部统计行改为:合同额/万、人天、高风险数、已验收数(去掉空的计收/开票/回款)。
  • 纯前端改动(web/app.js),后端无需重启;接口此前已验证返回全部新字段。node --check 语法通过。

明细表交互改造:去按钮 / 状态筛选 / 新建表单对齐 Excel / 统计条

用户四项要求:① 明细表去掉「详情」「归档」按钮 ② 新增「项目状态」筛选 ③「新建定开项目」字段参考 Excel 调整 ④ 表格上方加统计(项目数量+金额、本月/本年新增下单、本月/本年验收)。

新增字段 accept_date_real(验收日期,真实日期)

Excel 里「验收时间」是季度文本(Q1..Q4),无法算「本月/本年」,故新增 accept_date_real(String(16), YYYY-MM-DD, index)专门用于验收统计,与 accept_date(季度文本,展示用)并存互补。

  • 改动:models.py(字段 + to_dict)、custom.py(CustomProjectIn / PATCH 白名单 / create 时 region 可由 office 归一化推导)、scripts/import_custom_ledger.py(导入时对「已验收」+季度文本的行补当年季度末占位;仅在库中为空时补,不覆盖用户手改的真日期)。
  • 一次性回填:已导入的 119 行中 60 条「已验收」补上 2026 季度末(Q1→03-31/Q2→06-30/Q3→09-30/Q4→12-31),不动 update_date。结果:本年验收 60、本月(9月)验收 17。

前端(web/app.js + styles.css)

  • 去按钮:renderCustomList 行操作列只留「修改」(data-cedit);名称仍可点开详情(data-copen)。bindCustomList 移除 data-carch 绑定。
  • 状态筛选:工具栏加 <select id="cStatus">(全部/已验收/待开发/交付完成/交付中/退单),state 加 customStatus,在 renderCustomList 内对已加载行做客户端筛选。
  • 新建表单:openNewCustomDialog 重写为对齐 Excel 的字段(项目归属/项目名称*/项目ID/下单时间/办事处/行业/市场下单人天/订单金额/市场责任人/项目状态/验收时间(季度文本)/推动验收计划/风险值/验收日期/备注/重点项目);提交时把台账字段发给后端,region 由后端按 office 归一化(实测 深圳→广东)。更新录入页共用此函数,一并生效。
  • 统计条:新增 .stat-row/.stat(styles.css),在 renderCustomList 表头上方展示 5 个格子:项目数量/合同额、本月新增下单、本年新增下单、本月验收、本年验收(均「个数 · 金额万」)。下单按 sign_date、验收按 accept_date_real,基于当前筛选后的行计算;card-head 的 period-hint 保留人天/高风险/已验收。

验证

  • 语法:node --check(app.js)、ast.parse(models.py/custom.py/export.py/import_custom_ledger.py/constants.py)全通过。
  • 接口:重启后 /api/custom/projects 119 条含 accept_date_real;POST 新建含全部台账字段 → region 由 office 推导正确(深圳→广东),测试项目已删除;/api/export/custom 200。
  • 统计口径实测:本年验收 60、本月(2026-09)验收 17、本年新增下单 31、本月新增下单 0。
  • 页面未做浏览器截图(agent-browser 未装),刷新浏览器查看即可。

明细表:去掉归档下拉 / 按更新时间排序 / 删除本周更新

用户(附截图:归档状态下拉「在库/已归档/全部」)要求:① 删除该下拉 ② 新增展示并「按更新时间排序」 ③ 可按周删除「本周更新」的数据。

前端(web/app.js)

  • 移除明细表 card-head 里的 #cArchived 归档状态下拉及 bindCustomList 里的绑定;state.customArchived 保留(恒为 "false",仍只查非归档)。
  • 排序:renderCustomList 的 view 改为按 update_date 倒序(String(b.update_date).localeCompare(...)),表头副标题改为「按「更新时间」倒序」。
  • 删除本周更新:card-head 加 <button id="btnPurgeWeek" class="btn sm danger">删除本周更新</button>;purgeWeek() 先 GET /custom/weekly-preview 拿数量/金额/名单 → confirmDialog({danger:true, requireText:"删除"}) 二次确认(并要求手动输入「删除」)→ POST /custom/purge-week → afterWrite() 刷新。
  • confirmDialog 增加可选 o.requirePlaceholder(原来输入框 placeholder 硬编码「逐字输入项目名称」,批量删除场景不适用)。

后端(app/routers/custom.py)

  • 新增 _sort_by_update()(update_date 倒序,无则回落 entry_date);GET /custom/projects 改用之(原来是 _sort_by_entry)。
  • 新增 _week_range()(当前自然周 周一~周日,基于 date.today() 的 weekday())。
  • 新增 GET /custom/weekly-preview(本周有 update_date 的项目数/金额/名单,供确认)。
  • 新增 POST /custom/purge-week(删除 update_date 落在本周的项目,db.delete(cp) 靠 relationship cascade 连带删 custom_updates),返回 {deleted,start,end,names}。
  • 路径用 /custom/weekly-preview、/custom/purge-week,避开 /custom/projects/{cid}(int 路径参数会被非数字段 422),未放在 projects 前缀下。

验证

  • node --check + ast OK;服务重启后 /health ok。
  • /api/custom/weekly-preview → 本周 2026-09-14 ~ 2026-09-20,119 个(因导入把 119 行 update_date 都写成 2026-09-17=今天),合同额约 1130.7 万。
  • /api/custom/projects 已按 update_date 倒序返回。
  • ⚠️ 注意:当前所有行 update_date 都是导入当周,所以「删除本周更新」= 会删掉全部 119 条;已用「显示数量 + 需输入『删除』」做强确认。未实际执行 purge(只验证了 preview)。

定开金额单位:明细表用元、汇总统计用万

用户要求表格列「订单金额/万」去掉「/万」,单位换成正常的个位(元);但汇总统计仍需用「万」。

  • 关键事实:custom_projects.amount 在库里本来就是元(实测 sum≈11,307,268、avg≈95,019、max=1,000,000),之前的「/万」只是展示约定——fen()/wan() 把元÷10000 显示成万。
  • 最终约定(定开金额):明细表格每行 = 元(money(p.amount),¥+千分位);所有汇总/统计 = 万(fen()/wan())。
  • 改动(纯前端 web/app.js,静态服务无需重启):
    • 定开项目明细表:表头 订单金额/万 → 订单金额;单元格 fen(p.amount) → money(p.amount)(元)。
    • 上方统计条、人力资源页「定开合同额/定开待计收」、工作看板 KPI「定开合同额」、删除本周更新确认框的「合同额合计」——仍用万(fen()/wan()),未改/已改回。
  • 未动:项目管理(交付跟踪)模块的「合同额」仍用 wan/fen+万(另一套 projects 数据);导出沿用原始元值。
  • 服务 /health ok,浏览器刷新即可见(static 带 no-cache+ETag)。

更新时间 = 下单时间 + 修改表单对齐台账字段

两项需求:(1) 更新时间全部改为项目下单时间;(2) 定开「修改」表单只保留《定开项目.xlsx》14 列表字段,删除交付跟踪类多余字段。

1) update_date = sign_date(全量)

  • 备份:data/board.db.bak-before-updatedate-sign-20260917_000000。
  • UPDATE custom_projects SET update_date = sign_date(119/119 行;0 个 null sign_date,91 个真日期 + 28 个文本如「2024年3月前」)。
  • 后端 app/routers/custom.py PATCH:原本改任何字段就 update_date=today,改为 update_date = cp.sign_date if cp.sign_date else cp.update_date(跟随下单时间)。已用 API 实测:改 remark 后 update_date 仍为 2024-04-30(=sign_date),不是今天。
  • scripts/import_custom_ledger.py:新增/更新行的 update_date 改默认取 sign_date(无则回退 args.date);同步改了文档注释。
  • 副作用:「删除本周更新」现在按 sign_date 周判断,历史台账(2024/2025)不会命中本周 → 之前「会删光 119 条」的危险消失。

2) 修改表单对齐台账(前端 web/app.js)

  • openCustomDrawer 的 edit 模式:section「定开项目信息」只放 Excel 14 列(项目归属/项目名称/项目ID/下单时间/办事处/行业/市场下单人天/订单金额/市场责任人/项目状态/验收时间/推动验收计划/风险值/备注)+ 验收日期 + 只读「更新时间(跟随下单时间)」。
  • 删除的多余字段:基础信息里的 阶段/开发进度/已计收/确认比例/交付环节/区域/交付负责人/团队成员/客户;收入确认里的 开票金额/已回款 编辑框(收入确认 KPI 只读区仅在 view 模式显示,edit 模式整段不渲染)。edit 模式只渲染 ledgerSec 表单,baseSec/收入确认/周期执行记录/阶段流转 仅 view 模式渲染。
  • cdSave PATCH payload 精简为上述台账字段(不再传 stage/owner/region/members 等,未传字段不动;set_people 对 None 不改成员)。
  • view 模式补充显示 下单时间、验收时间(之前漏显示)。
  • 数据保护:编辑表单的「下单时间」用 text 输入(非 date),因为 28 行 sign_date 是文本(date 控件会清空它们);新建对话框仍用 date。
  • 重启 uvicorn 使后端生效(原 PID 40088 → 新进程);node --check 与 ast.parse 均通过;前端 static 无需重启。