借款出借人改为从现有员工选择并复用资料(09-17-company-loan-principal-repayment)
- 新增员工来源预览、关联与显式刷新接口,受 loan.manage 与员工银行卡权限保护;服务端读取并加密保存,客户端不能另传姓名账号 - 同公司同员工唯一复用,请求键绑定来源;锁序出借人→员工,员工使用 FOR NO KEY UPDATE - 员工关联出借人禁止手工改账号;在途还款期间禁止刷新,刷新推进版本并使旧公司代表授权失效 - 电脑/手机共用对话框,写请求在卸载或身份切换时取消 - 无新增迁移;本地 126 组 HTTP、45 组浏览器、9 个变异通过,两名只读复核及 Claude 独立复核无阻断 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -1144,7 +1144,10 @@ await canReadReimbursementBusinessFact(prisma, reimbursement, employee.id);
|
||||
## Company Loan Principal And Retired ERP Consumers
|
||||
|
||||
- `09-17-company-loan-principal-repayment` adds the explicit `loan_repayment` payment source: one retained repayment/batch/item, multiple allocations to confirmed same-company/lender/CNY loans. Preserve original principal and confirmed opening evidence; Decimal reservations and settlements must not exceed opening outstanding principal. SQL checks/deferred triggers complement locked application validation.
|
||||
- Loan management requires an explicit finance/finance_mgr role plus loan.manage. Independent review requires active manager/CEO with review permission and excludes maker, submitter and lender OA identity; admin wildcard cannot replace these identities. External lenders require explicit verified no-OA identity. Account changes revoke identity confirmation and invalidate old unpaid snapshots.
|
||||
- Loan management requires an explicit finance/finance_mgr role plus loan.manage. Independent review requires active manager/CEO with review permission and excludes maker, submitter and lender OA identity; admin wildcard cannot replace these identities. External lenders require explicit verified no-OA identity. Manual account changes revoke identity confirmation and invalidate old unpaid snapshots; explicit employee-source refresh renews confirmation under the checks below.
|
||||
- Employee-source preview/link/refresh additionally require `employee.bank.read` or `employee.bank.manage`. `loanLenderEmployee` reads the active employee, supported bank and encrypted account on the server; the strict write body carries employee/company IDs, source version and an explicit confirmation, never client-supplied account/name replacements. A source version binds actual source fields, ciphertext and the chosen debtor, not only `updated_at`. Missing, unreadable, stale or unsupported sources reject without a partial lender/audit write; previews expose masks only.
|
||||
- Same-company employee association includes archived/history rows in duplicate checks, including the old identity-confirmation entry. Reuse a unique matching lender; reject ambiguous links and require explicit refresh for different data. Existing-lender actions lock lender before sorted employees. New creation never locks an existing lender after employees: finish and reacquire in the correct order if a link appeared. Use employee `FOR NO KEY UPDATE` to serialize association/source edits while allowing audit/actor FK `KEY SHARE`; `FOR UPDATE` can deadlock against another employee's refresh/audit. Persist and serialize each actor's request-key binding for both creation and reuse, with audit in the same transaction.
|
||||
- Explicit employee refresh rejects reserved repayments, checks source/identity/account versions and advances both lender versions with renewed finance identity confirmation. This invalidates old company-representative grants without changing principal or historical payment snapshots. Employee-linked lenders cannot use the manual account PATCH; employee-master edits alone never rewrite the loan copy. Verify the actual audit-FK interleaving, duplicate/legacy races, refresh versus repayment creation, audit rollback and reverse mutations using `RUN_ISOLATED_LOAN_EMPLOYEE_ONLY=1` and optional `RUN_ISOLATED_LOAN_EMPLOYEE_MUTATIONS=1` on the isolated loan runner; retain the original full-loan regression.
|
||||
- Related-party loans use the explicit `loanApprovalPolicy` branch, not a global reviewer exception. Finance proposes `LoanRelatedAuthorization` for one company/lender, its verified employee identity and specific confirmed loans; that designated active manager/CEO confirms it. Grants, scope and lifecycle events are append-only. Resolve the latest confirmed version without falling back to an older grant when the new scope is narrower or stopped; a superseded version cannot receive stop events.
|
||||
- Freeze `LoanRepayment.approval_mode`, `authorization_id` and `approval_context` at creation. Only `related_party` waives the designated payee-conflict reason. Keep maker/submitter/exporter/uploader/manual-entry/proposer conflicts as a separate union of all history, including voided and superseded materials; do not remove the payee employee from that union. Reject designated-reviewer participation at maker entry points as well as decision/final-consumer checks. Old independent loans and shared reimbursement/travel rules do not switch modes.
|
||||
- New payment creation/approval/export checks current payment authority; new reconciliation decisions and final reservation release check current reconciliation authority. `decisionFields()` stores purpose, actual actor, target, scope, authorization version/event and conflict identities. `assertDecision()` validates immutable historical provenance at receipt reconstruction, settlement, terminal/recovery consumption and bank-event booking; it does not require the old reviewer to remain active or the old grant to remain enabled. Stopping payment and stopping reconciliation are separate events. Previously approved bank facts remain usable if their evidence and all money/transaction checks still hold.
|
||||
|
||||
@@ -40,6 +40,13 @@
|
||||
actions when false; actual review still checks role, permission and all participants.
|
||||
Save/review never call payment execution or upload.
|
||||
|
||||
## Employee Lender Source Selection
|
||||
|
||||
- The default lender action uses `LoanEmployeeLenderDialog` to select an existing employee and confirm the debtor company, name, bank and masked account. Keep employee/company selectors explicitly labelled; do not add duplicate account/name inputs or infer the debtor solely from employment. Missing data directs qualified users to the existing employee-maintenance page; preserve an explicit non-employee route.
|
||||
- Source query keys include actor, employee and company and forward `AbortSignal`; hide cached summaries without current bank-read permission. Disable background form refetch, clear the confirmation on explicit reload/source changes, and require a separate explicit refresh for differing existing lender data. Show the in-flight block and the need to renew company-representative authority after refresh.
|
||||
- Both employee link and refresh writes pass the component's abort signal through `saveLoanAction(path, body, method, signal)`. Abort on unmount/identity change as well as checking actor before success callbacks: a callback guard alone cannot prevent a late 401 from replaying the old POST with the new session. This is a scoped fix, not a claim that all older application mutations have this lifecycle.
|
||||
- Browser acceptance uses the existing isolated loan runner with `RUN_ISOLATED_LOAN_BROWSER=1`: actual desktop/mobile employee selection, historical loan/balance setup and two-person authority confirmation. Test delayed 401 responses across real logout/login in one document, canceled old writes and unchanged lender/audit data; preserve successful same-identity refresh/retry and the original non-employee workflow. Synthetic data and bank tests do not grant production payment authority.
|
||||
|
||||
## Source Of Truth
|
||||
|
||||
- Reminder cooldown hints render server UTC timestamps in Asia/Shanghai with an
|
||||
|
||||
在新工单中引用
屏蔽一个用户