feat: 完成错误原因合同与付款就绪提示(09-05-error-explainability-payment-readiness)
这个提交包含在:
@@ -23,6 +23,95 @@ QA script patterns over new abstractions.
|
||||
- `cd server && ./scripts/regression-workflow-actions.sh`
|
||||
- `cd server && ./scripts/regression-all.sh`
|
||||
|
||||
## Scenario: Business Error Reasons And Payment Creation Readiness
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Adding or changing user-facing business validation, especially reimbursement,
|
||||
invoice binding/unbinding, payment batches, leave and attendance correction.
|
||||
- Root cause addressed by task `09-05-error-explainability-payment-readiness`:
|
||||
server details and UI text evolved independently; successful static coverage
|
||||
was incorrectly inferred from shared details keys or string counts.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Registry: `server/src/constants/errorReasons.js`, `ERROR_REASONS`.
|
||||
- Builder: `buildBusinessValidationError(reasonCode, legacyDetails, statusCode)`.
|
||||
- Client map: `client/src/api/errorReasons.ts`.
|
||||
- Gate: `node scripts/qa/check-error-reason-contract.mjs --strict`; negative
|
||||
subprocess fixtures: the same command with `--self-test`.
|
||||
- Candidate API accepts `paymentChannel`; each candidate adds
|
||||
`readiness: { ready, blockers }`.
|
||||
- `POST /api/payment-batches/readiness` accepts only `entityType`, `entityIds`,
|
||||
and optional `defaultPaymentChannel`; this is authenticated, manage-authorized
|
||||
and read-only. Creation remains independently authoritative.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Keep HTTP status, top-level error code and legacy details. Add the registered
|
||||
domain-scoped `details.reasonCode`; do not infer business identity from a
|
||||
details key alone. The builder rejects unregistered reasons.
|
||||
- **迁移一条遗留原因码 = 服务端注册 + 客户端映射 + 具体内容断言,三项缺一不可。**
|
||||
Fixed expected text lives in `server/tests/unit/fixtures/error-reason-messages.json`
|
||||
and is asserted against both registry and client rendering; do not replace
|
||||
these assertions with broad message regexes or expectations derived at runtime.
|
||||
- Legacy entries are versioned AST-shape SHA-256 fingerprints plus relative file/operation,
|
||||
occurrence count, owner, explanation and expiry date (UTC). Whitespace, quotes,
|
||||
indentation, trailing commas and parenthesization must not change identity.
|
||||
A changed node is a new obligation; one deletion cannot offset one addition.
|
||||
- The first-wave route files and invoice bind/unbind/shared item resolver are
|
||||
zero-exception operations. Do not baseline their new or old generic errors.
|
||||
- Candidate rows, combination preflight and the locked creation transaction all
|
||||
consume `resolvePaymentBatchCreationReadiness` from `paymentBatchReadiness.js`.
|
||||
`loadPaymentCreationFacts` loads sources, reservations, accounts and reviewers
|
||||
in batches. Department flag reads are memoized only inside that fact load;
|
||||
do not cache readiness across requests or write readiness to the database.
|
||||
- Creation takes source locks, reloads facts, runs the resolver and only then
|
||||
allocates/creates a batch, items, success audit and notifications. Blocked
|
||||
preflight/create must not emit any of those success side effects.
|
||||
- Public blockers contain only reason code and optional source association.
|
||||
Neither decrypted account numbers nor account snapshots enter readiness DTOs.
|
||||
Bank-channel constraints must not silently become constraints on cash or other
|
||||
already-supported channels. No bank capability, role or maker-checker changes.
|
||||
- Execution keeps persisted `failure_code/failure_message` and their API fields
|
||||
compatible. Store only the registered reason code in the existing execution-risk
|
||||
`payload_json`, with a safe detail; refresh that open risk's explanatory metadata
|
||||
inside the same transaction without changing status, creator or notification
|
||||
rules. This is not a readiness field or a new server logging mechanism.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing source -> `404 VALID_001` on create; invalid input -> `400 VALID_001`.
|
||||
- Status, occupancy, department, missing company/payee/account, decryption,
|
||||
cross-company and missing independent reviewer -> safe blockers; create `409`.
|
||||
- Three synthetic missing fields return three distinct actionable blockers.
|
||||
- Missing mapping/content fixture, typo, duplicate, changed/new/stale fingerprint,
|
||||
expired exception or primary-operation exception -> gate non-zero exit.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: individually ready records from different companies produce a blocked
|
||||
combination; changing source/account facts changes the next response.
|
||||
- Base: complete bank fixture produces `{ ready: true, blockers: [] }` and can
|
||||
create; a competing reservation after preflight is refused under the lock.
|
||||
- Bad: hide invalid candidates, copy create conditions into an API/UI, approve
|
||||
permanent path exemptions, or mark deployment acceptance from local fixtures.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Gate self-test must launch failing subprocesses and assert their exit codes,
|
||||
including formatting stability, one-added/one-removed and primary exceptions.
|
||||
- Focused route tests assert exact reason, HTTP status and retained details.
|
||||
- Shared readiness matrix plus route tests cover batch loading, repaired facts,
|
||||
combination rules, post-lock changes and zero batch/item/audit/notification.
|
||||
- Keep existing B2, invoice, leave, correction, receipt and maker-checker tests.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
- Wrong: count generic error strings, or `details.status` as the cross-domain key.
|
||||
- Correct: register `payment_batch.source_status_invalid`, map and assert its
|
||||
concrete safe text, and consume the shared readiness result at every boundary.
|
||||
|
||||
## Scenario: Prisma Client Defaults In Raw SQL
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
@@ -12,6 +12,71 @@ This spec keeps hyb-oa-specific React/Vite, shared-component, business-boundary
|
||||
and verification contracts. Do not copy broad aesthetic guidance from
|
||||
`frontend-design` into this file; use the skill directly for design judgment.
|
||||
|
||||
## Scenario: Actionable Validation And Selectable Payment Channels
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- New validation feedback, disabled selections, or payment creation controls.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- `resolveErrorMessage(ApiError)` prefers the explicit domain-scoped reason map.
|
||||
- `PaymentReadiness` is `{ ready, blockers: [{ reasonCode, entityId?, entityCode? }] }`.
|
||||
- Candidate query identity includes payment channel; combination query identity
|
||||
includes selected IDs and channel. Never reuse another selection's readiness.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Unknown `VALID_001` uses actionable safe fallback, with a bounded diagnostic
|
||||
request ID only when present. Do not render raw message/details, IDs or paths.
|
||||
- Keep reason registration, client map and fixed concrete-text tests together.
|
||||
Structured invoice-count rendering remains narrowed and must tolerate malformed
|
||||
fields without exposing payloads.
|
||||
- Blocker text stays beside its row/action; use persistent text, `role=alert`
|
||||
or `aria-live`, and `aria-describedby` on disabled actions. Toast supplements,
|
||||
never replaces, the persistent explanation. Normal rows add no success noise.
|
||||
- The payment channel selector is available before any selection, otherwise
|
||||
bank-blocked rows make other supported channels unreachable.
|
||||
- A not-ready unselected row cannot be selected; a previously selected row that
|
||||
becomes invalid can still be deselected. Pending writes may disable edits.
|
||||
- Pending/error/not-ready combination state disables creation, and the mutation
|
||||
handler checks the same result. Server create rechecks independently.
|
||||
- Persisted payment failure summaries are not UI copy. Use the failure projection
|
||||
in `paymentBatchDisplay.ts`, associating risk reasons by batch ID and item ID;
|
||||
historical/unknown failure text and execution-risk details use safe fallback.
|
||||
Do not announce complete success when the execution result is `failed`.
|
||||
- Only authorized maintainers may receive maintenance links; other roles receive
|
||||
recovery-owner text. Do not instruct users to falsify their actual bank details.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing company/bank/card or unsupported bank -> exact local row explanation.
|
||||
- Cross-company combination -> action-local explanation and disabled button.
|
||||
- Concurrent create refusal -> remain on the page with action-local safe text.
|
||||
- Unknown reason -> safe fallback, optional request ID, no empty identifier label.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: switch to an existing non-bank channel before selection; deselect a row
|
||||
whose readiness changed without losing the rest of the selection.
|
||||
- Base: normal bank fixture remains selectable and submits for independent review.
|
||||
- Bad: hide the channel selector until a selectable bank row is chosen, or disable
|
||||
an already-selected invalid checkbox permanently.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Concrete message fixtures, unknown reasons with/without request IDs, and malformed
|
||||
structured invoice details; no broad message regex substitutions.
|
||||
- Local synthetic Playwright covers omissions, channel limits, combination block,
|
||||
concurrent failure, keyboard selection, accessible descriptions, channel-before-
|
||||
selection and deselection after invalidation. No real payment data is required.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
- Wrong: `disabled={!candidateReady}` for every checkbox.
|
||||
- Correct: block only new invalid selections while preserving deselection, and
|
||||
share the reason mapping between row, combination and create errors.
|
||||
|
||||
## Scenario: PC Shell And Detail Breakpoint Coordination
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户