借款还款同金额回单须经独立复核确认归属后方可核销(09-17-company-loan-principal-repayment)
- 同一出借人同金额的其他批次曾导出或上传回单(含确认失败后已取消的)时, 最终核销入口要求已复核的归属确认;OCR 匹配与人工回单复核均不能绕过, 证据不足保持待对账,本金与占用不变;存在候选时失败回单不能释放占用 - 新表 loan_receipt_attributions(迁移 20261006010000):绑定交易指纹、 回单核验与人工录入证据、当时候选集合;依据类型不含时间吻合;复核人须 当前有资格且独立于登记人、批次创建人、提交人、导出人、回单上传人、 人工录入人与出借员工;失效记录在重新登记时标为 superseded,历史只增不改 - 借款回单上传取出借人锁,与核销候选检查串行;串行只保证按当时已知候选 判断,核销之后才出现的新证据留待晚到结果对账流程处理 - 付款就绪检查反映归属要求;页面登记/复核/失效重登,写明同额分期每期需确认 - 测试:两个错误核销复现改为回归断言;证据绑定、确定性并发两种顺序、 PC 浏览器完整登记复核重登流程;变异测试均变红 注意:借款功能尚不具备真实还款验收条件(失败回单与对账流程、离职后确认 失败、真实回单样本、手机端弹窗、独立复核待完成);未推送、未部署。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -149,7 +149,7 @@ async function claimVerifiedReceipts(tx, config, batch, { readOnly = false } = {
|
||||
});
|
||||
requireFact(existingAliases.every((alias) => alias.claim_fingerprint === match.fingerprint && alias.key_id === keyId),
|
||||
"receipt_transaction_reused");
|
||||
claims.push({ match, exists: existing.length > 0, existingAliases });
|
||||
claims.push({ match, options, exists: existing.length > 0, existingAliases });
|
||||
}
|
||||
requireFact(new Set(claims.map(({ match }) => match.fingerprint)).size === claims.length,
|
||||
"receipt_transaction_reused");
|
||||
@@ -160,7 +160,9 @@ async function claimVerifiedReceipts(tx, config, batch, { readOnly = false } = {
|
||||
aliasOwners.set(alias, match.item_id);
|
||||
}
|
||||
}
|
||||
if (readOnly) return;
|
||||
if (readOnly) return claims.map(({ match, options }) => ({ itemId: match.item_id, fingerprint: match.fingerprint, keyId: match.key_id,
|
||||
verificationIds: [...new Set(options.map((option) => option.verification_id))],
|
||||
manualProposalIds: [...new Set(options.map((option) => option.manual_proposal_id).filter(Boolean))] }));
|
||||
for (const { match, exists, existingAliases } of claims) {
|
||||
if (!exists) {
|
||||
await tx.paymentReceiptClaim.create({ data: {
|
||||
|
||||
在新工单中引用
屏蔽一个用户