取消报销资产事前批准强制(08-30-reimbursement-assets-prior-approval-correction)
这个提交包含在:
@@ -30,6 +30,10 @@ duplicate detection or attachment behavior.
|
||||
require the same own-or-`expense.view_all` guard as reimbursement detail reads
|
||||
before invoking the precheck service; an ordinary nonparticipant must receive
|
||||
403 without service-side evaluation.
|
||||
- 取消或放宽必填校验时,必须继续审计该校验之后的代码是否依赖它提供的
|
||||
非空、类型或状态保证,并为生产真实存在的空值路径补回归测试。不得只
|
||||
删除前置错误分支;可空字段的 `.trim()`、`.length`、字符串拼接和传给
|
||||
非空参数的调用都应逐处确认。
|
||||
|
||||
## Scenario: OCR Disabled Without Mock Data
|
||||
|
||||
@@ -996,8 +1000,10 @@ await selectApprovalRule(reimbursement.total_amount);
|
||||
- Only `travel_meal` and `travel_local_transport_allowance` are invoice-free in
|
||||
the initial policy. Invoice-free means skip invoice presence/amount checks;
|
||||
it does not remove explanation, formula, approval or attachment obligations.
|
||||
- Every `assets` item requires a trimmed approval basis on create, update and
|
||||
submit. Switching an item to a non-assets type clears the stored basis.
|
||||
- 公司当前没有 assets 事前审批制度。Item create/update 继续兼容可空的
|
||||
`assetPriorApprovalBasis`:创建时缺失或空白落 null;更新时 omitted 保留
|
||||
现值,显式 null/空白清空;切换到非 assets 类型仍清空。当前 PC/移动 UI
|
||||
不展示或发送该字段,历史值不得因普通编辑被静默清空。
|
||||
- `office_supplies` at or above CNY 2,000 per item shows the same non-blocking
|
||||
office-electronics hint on editable PC/mobile forms and read-only finance
|
||||
detail. It does not change the type or decide accounting fixed-asset status.
|
||||
@@ -1027,9 +1033,9 @@ await selectApprovalRule(reimbursement.total_amount);
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Assets item without basis -> `400 VALID_001` with
|
||||
`assetPriorApprovalBasis=required_for_assets`; submit returns affected item
|
||||
IDs under `asset_prior_approval_basis_required`.
|
||||
- Assets item without basis -> 允许创建、编辑和提交;字段缺失、数据库现值
|
||||
为 null 或显式清空均不得产生空值解引用或 `500`,其他金额和票据门禁照常
|
||||
执行。
|
||||
- Guard-on bind without `linkedItemId` -> `400 VALID_001` with
|
||||
`required_when_invoice_guard_enabled`; an item outside the form remains
|
||||
`item_not_in_reimbursement` with zero writes.
|
||||
@@ -1052,9 +1058,12 @@ await selectApprovalRule(reimbursement.total_amount);
|
||||
guard either on or off.
|
||||
- Base: guard off preserves an older draft and its auto-created link; later
|
||||
disabling the guard preserves links created during a guarded rollout.
|
||||
- Good: assets 编辑请求 omitted 兼容字段时,数据库 null 保持 null、历史非空值
|
||||
原样保留;显式 null/空白才清空。
|
||||
- Bad: compare every positive item to a zero invoice total, silently create an
|
||||
item while explicit selection is required, use JavaScript floating-point
|
||||
sums for money, or let repeated init erase finance account mappings.
|
||||
sums for money, let repeated init erase finance account mappings, or 删除前置
|
||||
必填校验后继续对可空值直接调用 `.trim()`。
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
@@ -1064,13 +1073,15 @@ await selectApprovalRule(reimbursement.total_amount);
|
||||
- Expense-type route tests assert list mapping, create/update persistence and
|
||||
audit changes for `requiresInvoice`; seed tests assert the two allowances,
|
||||
disabled legacy types, default-off flag and account-code preservation.
|
||||
- Item/submit route tests cover assets create/update/submit, invoice-free zero
|
||||
invoice, missing link, unlinked bound invoice, declared-over-invoice and
|
||||
participant/nonparticipant side effects.
|
||||
- Item/submit route tests 覆盖 assets 创建 omitted→null、编辑 null+omitted→null、
|
||||
有值+omitted→保留、显式清空、切换非 assets 清空和无依据提交,并继续覆盖
|
||||
invoice-free zero invoice、missing link、unlinked bound invoice、
|
||||
declared-over-invoice 与 participant/nonparticipant side effects。
|
||||
- Bind tests cover flag on/off, missing/foreign/successful explicit links and
|
||||
prove the guard-on path does not create a reimbursement item.
|
||||
- PC/mobile Playwright covers descriptions, approval-basis input, the CNY 2,000
|
||||
warning, allowance-only submit projection, disabled add action until every
|
||||
- PC/mobile Playwright covers asset 登记导向说明、批准依据输入/展示均不存在且
|
||||
保存 payload omission、CNY 2,000 warning、allowance-only submit projection、
|
||||
disabled add action until every
|
||||
selected invoice has an item, and pending-OCR binding retaining that item.
|
||||
- Run full backend Vitest with the pinned BIFF8 Python dependencies, Prisma
|
||||
validate/generate check, frontend typecheck/build, focused Playwright,
|
||||
@@ -1090,6 +1101,11 @@ if (item.amount > linkedInvoices.reduce((sum, invoice) => sum + Number(invoice.t
|
||||
update: { account_code_default: null }; // erases finance configuration on init
|
||||
```
|
||||
|
||||
```js
|
||||
// 旧必填校验删除后,omitted/null 会在这里变成 500。
|
||||
asset_prior_approval_basis: targetApprovalBasis.trim();
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```js
|
||||
@@ -1107,6 +1123,10 @@ update: { name, description, requires_invoice }; // preserve account_code_defaul
|
||||
create: { name, description, requires_invoice, account_code_default: null };
|
||||
```
|
||||
|
||||
```js
|
||||
asset_prior_approval_basis: targetApprovalBasis?.trim() || null;
|
||||
```
|
||||
|
||||
## Scenario: Payment Batch Receipt Attention Count
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户