收敛报销详情并展示五阶段进度(07-27-yang-jun-reimbursement-readiness)

这个提交包含在:
2026-08-01 12:37:11 +08:00
父节点 4cf73ce0d2
当前提交 421c2d6e1e
共修改 11 个文件,包含 840 行新增和 352 行删除
+88
查看文件
@@ -93,3 +93,91 @@ if (!hasPermission(actor, "payment.batch.review")
throw forbidden();
}
```
## Scenario: Reimbursement End-To-End Processing Projection
### 1. Scope / Trigger
- Trigger: showing one reimbursement's progress across approval workflow,
payment-batch review, payment execution and receipt retention.
- This is a read-only projection for diagnosis. It must not merge the workflow
and payment state machines or create synthetic workflow tasks.
### 2. Signatures
- Detail API: `GET /api/reimbursements/:id`.
- Response: `processingProgress.stages[]` with `key`, `label`, `state`,
nullable `actorName`, `occurredAt`, `result`, `comment`, `detail` and
permission-gated `targetHref`.
- Stable stage keys: `submitted`, `manager_approval`, `finance_review`,
`payment_batch_review`, `payment_and_receipt`.
### 3. Contracts
- Always return the five stages in the stable order above. UI labels describe
responsibilities and never expose step IDs, role codes or enum values.
- Completed stages use the actual completer/actor and completion time. Current
workflow stages use the actual pending assignee when available. Future stages
keep `actorName=null`; never guess a person from today's role holders.
- A reimbursement approved without a batch makes `payment_batch_review` the
current stage with `detail="待财务创建付款批次"`. A draft batch says
`所在付款批次待非制单人复核`; shared-batch wording must not imply that this
reimbursement alone is being reviewed again.
- Missing workflow, batch or receipt facts degrade only their stages. Earlier
known stages remain visible; missing downstream objects are normal, not 5xx.
- `targetHref` is returned only when the viewer already has one of the existing
payment-batch read permissions. The projection never returns batch peers,
aggregate amount, other materials or bank receipt contents.
### 4. Validation & Error Matrix
- No workflow instance -> submission/applicant state remains readable; approval
and payment stages stay pending.
- Workflow approved + no batch -> payment-batch stage is current, future actor
remains null.
- Batch `draft|reviewed|executed|failed|cancelled` -> map to explicit current,
completed or exception states without changing the batch.
- Executed payment + zero active receipts -> completed payment with
`付款已完成,回单待留存`; positive count -> `付款与回单已留存`.
- Viewer lacks batch-read permission -> `targetHref=null`; do not return 403 for
the reimbursement detail itself.
- Database/query failure -> existing 5xx behavior and server logging; do not
fabricate a completed or pending business fact.
### 5. Good/Base/Bad Cases
- Good: `财务复核=通过` followed by current `待财务创建付款批次`, so the owner
can diagnose the exact responsibility without asking three people.
- Base: a newly submitted reimbursement shows known submission/current approval
and three future stages with no future names.
- Bad: display an expected checker before batch creation, label a shared batch
as `您的报销待复核`, or hide the whole timeline because no receipt exists.
### 6. Tests Required
- Route tests for no workflow, pending/completed/returned/rejected tasks, all
five batch statuses, receipt count zero/positive and paid-without-batch legacy
data.
- Assert future stages have null actors and ordinary applicants receive no
batch link or batch-adjacent business data.
- PC/mobile tests assert five responsibility labels, no `第 N 步`, comments on
exception nodes, four-block order and no loss of earlier stages when a
downstream source is absent.
### 7. Wrong vs Correct
#### Wrong
```js
return workflowInstance ? buildTimeline(workflowInstance) : null;
```
#### Correct
```js
return buildProcessingProgress({
reimbursement,
workflowInstance: workflowInstance ?? null,
paymentBatchItem: paymentBatchItem ?? null,
});
```