14 KiB
14 KiB
报销费用政策与票据门禁生产部署
Goal
把已完成并复核的报销费用政策与票据门禁安全发布到生产:先以 B2 有效关闭的状态完成 B1 migration、主数据和 owner 基线验收,再在隔离部门内验证 B2,最后全局启用并让财务统一退回 4 张真实审批中单据。全过程不得拿真人单据做灰度试验,不得因应用回滚误删 migration 或已建立的票据—明细关系。
Background and Confirmed Facts
- 生产当前运行候选为
59dc5f6;本地main比origin/main领先 4 个提交,B1/B2 业务实现为6e999d5,尚未推送、部署或应用生产 migration。 - 待应用 migration 固定为
20260829120000_reimbursement_expense_policy_invoice_guard,只新增expense_types.requires_invoice(非空、默认 true)、reimbursement_items.asset_prior_approval_basis(可空),并把两项补助精确回填为免票。 - init 会运行 migration、
seed:config与seed:trialrun;费用类型改名、停用和新建由 seed 落地,不能只核 migration 表。 - B2 是 env hard gate、代码白名单和 DB flag 三件套。生产部署使用
SYNC_ENV=0,不会自动把新增 env 写入生产;env 未开启时 DBforce_on无效。 - B2 提交门禁只检查:已绑定发票是否关联明细、需票明细是否有关联票面金额、申报金额是否高于关联票面合计。它不自动校验“同一费用类型 + 同一发生日期”的明细粒度。
- 当前 4 张审批中单据均需退回重做:
0014票据关联/金额完整但 5 个日期被压成 1 条明细,B2 无法拦截;0016、0017、0018可被关联或金额门禁拦住,但日期/粒度仍需人工说明和复核。 - 生产现有 admin/employee smoke 可登录,旧 finance smoke 身份已不在 OA 用户表;employee smoke 当前没有自有报销。现有 3 个部门中有 1 个无成员,可在明确授权后作为隔离灰度范围;employee smoke 当前所在部门有 5 个用户,不能直接作为灰度范围。
- 材料合并预览会创建短期进程内预览包并写预览审计记录;这是设计内审计写入,必须纳入验收授权,而不是伪装成零写入检查。
Scope
- 创建生产验收条件:把现有空部门固定为常驻 smoke 部门,新建专用 OA-only manager/finance smoke 身份,把 employee smoke 迁入并长期保留,建立专用合成草稿和合成票据;凭据只通过私有 env 注入。
- 冻结、推送并严格验证包含 B1/B2 实现与本部署合同的最终候选。
- 使用 existing 部署入口完成 migration、seed、应用切换、构建标识、runtime/external smoke、备份合同刷新和角色化公网验收。
- B1 验收通过后,在 DB 继续
force_off时显式启用生产 env hard gate 并重启同一候选;随后仅对隔离部门开启 B2,验证通过后再全局开启。 - B2 全局开启后,由真实财务角色统一退回
BX-20260828-0014/0016/0017/0018;员工按新规则修正并重提,财务人工复核日期粒度。 - 已付款
BX-20260730-0008仅留历史关联缺口记录,不退回、不改付款数据;空草稿BX-20260805-0013不处理。
Requirements
R1 授权与候选冻结
- 规划批准、生产验收准备写入批准、push 批准、生产配置批准、部署批准、B2 灰度批准、B2 全局启用批准和真实单据退回批准互不推定。
- 本次规划文档提交完成后,以最终干净
HEAD冻结候选;具体完整 SHA 和 12 位 bundle marker 记在外部审批记录,不写回仓库。冻结后任何提交、tracked/untracked 变化或 HEAD 漂移都使审批失效。 - push 后必须确认候选是 upstream 祖先,严格 provenance 为 PASS,
published=true、remoteVerified=true、overrideUsed=false;禁止 unpublished override。 - 固定应用回滚基线为当前生产候选
59dc5f6。不得临场选择其他提交。
R2 验收身份与合成数据
- 不恢复已从用户表消失的旧 manager/finance smoke;新建角色纯净的 OA-only manager smoke(manager + employee)和 finance smoke(finance + employee),不绑定或重置真实员工身份。
- 把已确认无成员的部门改名为“系统测试专用(勿分配员工)”并固定为常驻 smoke 部门;employee smoke 迁入后不再迁回,manager smoke 设置为其直属 manager,三类 smoke 身份留在该部门供后续隔离灰度复用。迁移前以及每次部门灰度开启前,都必须确认成员集合恰好只有这三个 smoke;部门名称只是防误用提示,不能替代成员集合校验。
- finance smoke 持有完整 finance + employee 角色,包含付款和全量财务职责,因此只允许在单独获批的验收窗口内启用。窗口结束后立即置为 disabled;后续再次启用、使用和停用都属于生产账号写入,不得从既往验收授权推定。
- 普通 employee 缺直属 manager 时,报销提交会因
blockOnUnresolvedAssignee=true和no_manager_found返回 409;owner_self_fallback_role=finance只处理 owner 本人持有 CEO 角色的特殊情形,不得把 finance fallback 当作普通 smoke 的兜底方案。 - 合成草稿、明细、票据和附件必须使用明显测试标识与非真实材料;不得复制真人票据、金额、说明或附件。
- 所有成功提交的合成单据在验证后按合法 owner 流程撤回并删除;失败场景草稿直接删除。审计/工作流历史按系统合同保留,不做数据库硬删。
- 材料预览允许产生
REIMBURSEMENT_MATERIAL_PREVIEW_BUNDLE_ISSUED/REIMBURSEMENT_MATERIAL_PREVIEWED审计记录;证据只保留状态、页数、页序、比例与构建标识。
R3 B1 部署
- 部署前必须显式记录 migration status,期望 pending migration 只能包含
20260829120000_reimbursement_expense_policy_invoice_guard;多、少或漂移均停止。 - 使用
deploy/trialrun/deploy-existing.sh、existing 数据模式和SYNC_ENV=0。不 reset 数据库,不覆盖环境、DB 或附件目录。 - init 后再次显式记录 migration status,并核对:新增列存在、两项补助
requires_invoice=false、其他启用类型为 true、费用类型改名/停用/新建准确、account_code_default未被 seed 清空。 - 若正式科目表仍未提供,
account_code_default保持 null;责任角色为财务负责人,原目标回填日期2026-09-05保留在父 task,不由开发猜测编码,也不以此阻断本轮材料门禁上线。 - B1 首次切换后 B2 DB 记录必须由 seed 显式创建且为
force_off,不得以missing_flag_record代替关闭意图;B2 必须保持 effective off。owner 普通草稿维护、assets 事前批准、2,000 元提示、无票补助和非参与人只读投影均须通过。
R4 B2 hard gate 与隔离灰度
- B1 验收通过后,先确认 DB 为
force_off,再经单独生产配置批准把REIMBURSEMENT_INVOICE_GUARD_ENABLED=true写入受控生产 env,并重启同一候选。重启后应显示 env enabled 但 effective off。 - 隔离灰度只能将 DB flag 设置为
force_on + department_allowlist + 隔离部门 ID;不得使用 employee smoke 原部门,不得把 4 张真人单据作为灰度样本。 - 更新部门 allowlist 前必须重新读取部门成员与状态,确认只有 employee/manager/finance 三个 smoke、无真人成员,且 finance smoke 仅在当前获批窗口临时启用;任何额外成员或身份不确定都阻断灰度。
- 每个状态必须记录精确 reason:env 未开为
env_hard_off;env on + DB off 为flag_force_off;灰度名单外部门为department_not_allowed;隔离部门在本轮force_on模式下为flag_force_on_department_allowed;全局开启为flag_force_on_global。不得只凭提交成功/失败推断门禁已运行。 - 普通 feature flag 列表使用
strictDepartment=false,在部门 allowlist 状态只能给出延后判断,不能证明具体 actor 命中。灰度证据必须运行受控容器内只读 actor/department 诊断,并与报销详情 DTO 的invoiceGuardEnabled交叉验证;诊断不输出部门 ID、账号或原始 payload。 - 隔离灰度必须验证:owner 普通提交成功;两项无票补助不被阻断;多票显式关联成功;超票面准确阻断;旧草稿补齐后可提交;非参与人零写入副作用;切回
force_off后旧提交路径恢复且既有关联数据不删除、不解绑。 - B2 不校验日期粒度的事实必须在验收结论和财务退回说明中保留;不得把 flag PASS 描述为“系统已自动保证明细粒度”。
- 灰度失败立即把 DB 恢复为调用前完整快照,最迟先落
force_off;关闭 flag 只回退门禁,不回滚关联数据。
R5 全局启用与真实单据处置
- 隔离灰度全部通过并清理合成单据后,必须再次获得全局启用批准,才可把 rollout 从部门 allowlist 改为 global;更新使用版本并发控制并产生 feature flag 审计。
- 全局启用后先验证 effective global on、owner 基线、财务/管理员只读投影、错误提示和运行指标,再安排真实财务账号退回 4 张单据。
0014退回说明必须写死“按 5 个发票日期拆成 5 条明细”,重提后由财务人工确认;B2 不会自动拦截原样重提。0016至少按 3 个发票日期拆分并逐票关联;0017按 2 个发票日期重做、上传正式 PDF、逐票关联并解释1,949.81 元差额;0018若同日同类可保留一条,但须改正日期并关联全部 5 张票。- 退回必须由真实 finance 审批主体执行。admin、smoke admin 或脚本不得代行财务职责;Codex 不保存真实财务凭据。
R6 失败与回滚
- 切换前失败只停止,不执行应用回滚。migration/seed 或应用切换开始后任一必需门禁失败,先确保 B2 DB
force_off,再按同一 existing 入口回滚应用到59dc5f6。 - 应用回滚不逆向 migration、不删新增列、不删除票据—明细关联。新增列对旧应用为向前兼容。
- init 已改变费用类型主数据,应用回滚不会自动恢复旧名称/启用状态。部署前必须保存受影响类型的脱敏主数据快照;只有 B1 主数据本身被判定错误且取得单独补偿批准时,才按快照执行精确补偿,不运行整库回滚或重放猜测 SQL。
- 回滚后必须核对
/ready、bundle marker、migration status、runtime/external smoke、DB/附件挂载和备份合同;若 env hard gate 已写入,可保持 true,但 DB 必须force_off。若回滚原因涉及 flag 配置或新代码误触发,则同时恢复 env hard-off 并重启旧应用。
Acceptance Criteria
- 常驻部门已改名为“系统测试专用(勿分配员工)”;专用 manager/finance smoke、employee smoke 迁入和直属 manager 关系经单独生产写入批准,成员集合只有三个 smoke且不涉及任何真实员工身份或材料。
- 最终候选冻结、单独 push、upstream 祖先关系和严格 provenance 全部通过。
- 用户在 provenance 通过后另行批准 B1 生产部署;pre/post migration status 均明确记录,唯一 migration 正确应用。
- B1 schema、费用主数据、requires_invoice、assets 批准依据、2,000 元提示和 account code 保留检查通过;B2 DB 记录明确存在且为 force_off,effective off reason 不是 missing record。
/ready、12 位 bundle marker、runtime/external smoke、备份合同刷新和角色化公网检查通过。- owner 合成草稿维护正常;finance/admin 非参与人无维护入口;材料合并预览成功并只产生获批的设计内审计。
- env hard gate 在 DB
force_off状态下单独启用并重启,确认 env on / effective off 后才进入隔离灰度。 - B2 隔离灰度七项合同全部通过;失败时 DB 快照恢复,既有关联数据未回滚。
- 用户单独批准后 B2 全局 effective on;合成数据已按合法流程清理,employee/manager/finance smoke 常驻隔离部门且无真人混入,finance smoke 已在验收窗口结束后 disabled。
- 真实财务按明确说明退回 0014/0016/0017/0018,重提后人工复核日期粒度;0008 与 0013 未被误改。
- 任一失败按固定回滚/补偿边界处理,无 reset、无逆 migration、无批量伪造批准依据或票据关联。
- 所有公网门禁完成前不通知普通用户测试、不归档父任务。
Out of Scope
- 修改 B1/B2 业务代码、把日期粒度新增为服务端阻断、补数量/单价模型或提前实施 B3 一页式。
- 反向修改已付款 0008、替真人财务审批、保存真实凭据、复制真实票据作为测试材料。
- 在本轮编造会计科目编码、重置数据库、回退 migration、批量写入事前批准依据或清空审计记录。
- 把合成灰度批准推定为真实单据退回批准,或把部门灰度 PASS 推定为全局启用批准。
Test Plan
- 来源:干净工作区、候选 SHA、独立 push、upstream 祖先关系、严格 provenance。
- 生产 preflight:existing 部署合同、身份、数据挂载、唯一 pending migration、受影响主数据快照、B2 env/DB 初始状态。
- B1:init 前后 migration status、schema/seed/account code 断言、build marker、runtime/external smoke、备份合同和角色化页面。
- B2:env on + DB off、隔离部门 allowlist、七项合成业务合同、flag 快照恢复、关联数据保留、全局启用后复核。
- 真实业务:四张单据退回说明逐单核对,重提后财务人工核验日期粒度;0008/0013 零改动。
- 治理:task validate、Trellis governance、process contract、docs registry audit、敏感信息扫描和
git diff --check。