收敛报销退回动作与审批布局 [09-04-approval-return-reject-actions]
这个提交包含在:
@@ -490,6 +490,66 @@ const label = status === "draft" ? "草稿" : status;
|
||||
{needsBankReceipt ? <UploadReceipt /> : canRegister ? <RegisterPaid /> : null}
|
||||
```
|
||||
|
||||
## Scenario: Entity-Specific Return And Reject Actions
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: changing approval action availability, labels or layouts for reimbursement, leave, attendance correction, purchase or travel tasks.
|
||||
- Action availability follows the business entity's real edit/resubmit consequence; do not infer one global meaning from workflow outcome names alone.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Capability source: `server/src/routes/workflow.js#listAllowedActionsForTask`.
|
||||
- Reimbursement UI: `client/src/pages/reimbursement-workspace/ReimbursementApprovalAdapter.tsx`.
|
||||
- Legacy audit values remain `workflow_history.action=return|reject` and task outcome `returned|rejected`.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- New reimbursement tasks advertise `approve + return` and optional permitted `transfer`; they do not advertise `reject`. The UI labels return as `退回修改`, writes the existing returned history and returns the same reimbursement to `draft` for editing and resubmission.
|
||||
- Historical rejected reimbursements remain readable and editable under the existing compatibility rule. Never rewrite them to returned or remove reject mappings from history, print, processing progress or notifications.
|
||||
- Leave, purchase and travel retain both return and reject until their module semantics are separately changed. Attendance correction retains reject only. Do not globally remove reject from fixtures or shared confirmation copy.
|
||||
- Purchase/travel return restores `draft`; reject leaves the entity `rejected`, which is not editable or submittable in place. This is a real consequence difference.
|
||||
- The legacy reimbursement reject endpoints may remain callable during compatibility rollout even though new `allowedActions` and product UI do not expose them. Endpoint retirement requires a separate caller/compatibility audit.
|
||||
- Reimbursement fail-closed conditions, permissions, B1/B2 checks and return transaction stay unchanged when action presentation changes.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Reimbursement pending + approve permission -> `approve,return` plus optional transfer; reject absent.
|
||||
- Leave/purchase/travel pending + matching permission -> `approve,reject,return` plus optional transfer.
|
||||
- Attendance pending + matching permission -> `approve,reject` plus optional transfer; return absent.
|
||||
- Processed, non-actionable or unauthorized task -> no actions.
|
||||
- Historical reimbursement status rejected -> keep `已驳回`, edit/resubmit compatibility and original audit history.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: finance sees one clear modification outcome, `退回修改`, and the applicant edits the same reimbursement before a new workflow instance starts.
|
||||
- Base: purchase approval keeps both return and reject because only return permits in-place correction.
|
||||
- Bad: hide reject only with CSS while the API still advertises it, rename reject to return while calling the reject endpoint, or erase historical rejected labels because new reimbursement tasks no longer produce them.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Table-driven unit coverage for the five entity action matrices, transfer permission, processed tasks and unauthorized actors.
|
||||
- Reimbursement PC/mobile coverage for no visible reject, return progressive disclosure, fail-closed disabled actions and a two-action responsive group.
|
||||
- Regression coverage proving the four non-reimbursement action sets and shared panel DOM remain unchanged.
|
||||
- Compatibility coverage for historical rejected reimbursement display/edit/resubmit and legacy reject endpoint behavior.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```text
|
||||
API allowedActions = approve,reject,return
|
||||
reimbursement UI hides reject locally
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```text
|
||||
API reimbursement allowedActions = approve,return[,transfer]
|
||||
reimbursement UI renders only declared capabilities
|
||||
legacy reject endpoint and rejected history remain compatible but undiscoverable from the new UI
|
||||
```
|
||||
|
||||
## Scenario: Shared Payment Processing Projection
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户