fix: 财务本人报销由总经理独立审批(09-04-reimbursement-state-gap-finance-view)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-09-12 20:03:29 +08:00
父节点 31ab9fbb2b
当前提交 229867f5fd
共修改 18 个文件,包含 629 行新增和 20 行删除
+90
查看文件
@@ -17,6 +17,96 @@ task before modifying approval behavior.
- Reviewer review is required after changes touching workflow, status machine,
approval rules, permissions or migrations.
## Verification: Direct Manager And CEO Are The Same Employee
`scripts/qa/test-invoice-first-http.cjs` supports opt-in
`RUN_MANAGER_IS_CEO_TEST=1` with the existing disposable-database guard.
It creates employee, ceo+employee and finance+employee synthetic identities;
employee and finance report to the CEO, with no manager-role holder.
The default separate-manager fixture remains supported.
For the fixture's manager → ceo → finance rule, the direct-manager task is
assigned by employee relationship without requiring a manager role. The same
CEO must approve two separate steps; they are not automatically collapsed by
employee identity. Verify pending task identity/role, HTTP approval and replay
rejection at each step, then approved status and zero pending tasks.
Payment creation by finance and review by CEO remain separate responsibilities.
This is a contract test, not permission to deduplicate workflow steps or to add
manager roles to production. It does not cover finance submitting its own
reimbursement, CEO self-fallback, or every amount/workflow definition. Keep
those scenarios separate from the employee-owned fixture and do not infer
production account readiness from synthetic login results.
## Scenario: Finance-Owned Reimbursement Uses One CEO Approval
### 1. Scope / Trigger
New reimbursement submissions and resubmissions whose owner holds explicit
`finance` use one CEO approval. A finance submitter acting for an ordinary
employee does not trigger this rule. This is not global same-person step
deduplication and does not rewrite existing instances.
### 2. Signatures
`buildApprovalWorkflowSnapshot({ tx, approvalRule, ownerId, submitterId, entityType, logger })`
applies the specialization only to `entityType: "reimbursement"`.
`buildFinanceOwnerApprovalSnapshot({ tx, ownerId, submitterId })` is shared
with reimbursement operational precheck and returns null for non-finance owners.
### 3. Contracts
The new step is `approval_1_ceo_finance_owner`, titled 总经理审批, with existing
`{ type: "role", value: "ceo" }` assignment semantics. Require at least one
active, unarchived CEO outside both owner and submitter before submission.
Another finance employee does not change this rule; finance+CEO applicants
still cannot self-approve. Execution-time actor/principal self-approval and
business-role guards remain authoritative. No role or payment permission changes.
Finance creates the payment batch; the separate eligible non-maker reviews it.
For this new step, transfer targets and both execution actor and delegation
principal must still hold active, unarchived CEO membership. A transferred
specific-user assignment is not a substitute for that role. This restriction
is enforced in shared action handling, not only frontend choices.
Reimbursement list pending-workflow actions and workflow task actions also
exclude non-CEO actors for this step, including transferred assignees whose
role was revoked and non-CEO delegates. The server guard remains authoritative.
### 4. Validation / Error Matrix
No non-applicant CEO: snapshot raises 409 `no_actionable_approver` with
`finance_owner_ceo_unavailable`; operational precheck exposes a blocking reason
with that code and administrator guidance. This operational check runs with
budget precheck both enabled and disabled. Submission must leave no pending
instance on rejection; role deactivation after submission is not prevented by
an earlier precheck.
Ineligible transfer/execution returns 403 with reason code
`reimbursement.finance_owner_ceo_required`. Old executors can parse the new
step but lack this guard: rolling back only the image after new snapshots exist
is not a safe way to continue processing them.
### 5. Good / Base / Bad Cases
Good: finance submits → CEO approves once → finance makes batch → CEO reviews.
Base: employee-owned manager/CEO/finance steps and CEO-owned finance fallback
retain their earlier semantics. Bad: grant finance to CEO, allow self-approval,
or treat reimbursement approval as payment authorization.
### 6. Tests Required
Workflow unit and operational-precheck tests cover identity exclusions and
unchanged non-reimbursement paths. The disposable HTTP fixture with
`RUN_MANAGER_IS_CEO_TEST=1 RUN_OWNER_APPROVAL_TEST=1` covers the new chain,
return/resubmit, second finance, multi-role self-denial, unavailable CEO with
budget gates on/off, and separate payment review. Browser-enabled runs cover
desktop/mobile submission/approval and payment detail handoff.
This is synthetic validation, not production login, live OCR or bank acceptance.
### 7. Wrong vs Correct
Wrong: keep three identical CEO approvals or wait for a finance self-task to fail.
Correct: explicitly construct one CEO step for finance-owned new snapshots;
retain a separate payment review and immutable historical workflow facts.
## Scenario: System Operator Is Not A Business Approver
### 1. Scope / Trigger