补齐借款对账界面验收并记录 S0 只读核实(09-17-company-loan-principal-repayment)

电脑和手机覆盖提交清单、失败归属、晚到补记、部分及全额追回和有依据的误报关闭;不修改应用业务代码。最终隔离验证 81 迁移、HTTP 95、浏览器 19、数据库约束 9 组通过,脚本和治理检查通过。

获准只读核实:生产 OA 借款模块当前无记录,已应用 73 个迁移与本地校验值一致,待执行 8 个;不证明银行或线下没有付款。记录迁移差集、发布步骤和回退边界。

人员、银行材料、真实样本及业务独立验收待完成;正式发布门禁尚未运行。本次仅本地提交,不推送、不部署。
这个提交包含在:
2026-10-07 22:25:05 +08:00
父节点 431c36eae3
当前提交 7a48a1e76f
共修改 6 个文件,包含 199 行新增和 7 行删除
+27
查看文件
@@ -1301,6 +1301,33 @@ module.exports = async function runLoanHttpFixture(pool, scratch) {
}
(recovery ? reconciliationUi.recoveryBatches : reconciliationUi.terminalBatches).push({ id: batch.id, instruction: ref });
}
reconciliationUi.failures = []; reconciliationUi.late = [];
for (const mode of ['desktop', 'mobile']) {
const failLender = await verifiedLender(`合成失败界面-${mode}`, null, '7');
const failLoan = await confirmedLoan(failLender.id, '100.00', `合成失败归属-${mode}`);
const target = await repay(failLender.id, failLoan.id, '7.00');
const competitor = await repay(failLender.id, failLoan.id, '7.00');
for (const b of [target, competitor]) { await call(manager, 'POST', `/payment-batches/${b.id}/review`, {}); await exportOrder(b.id); }
const reference = `UI-FAIL-${mode}`.toUpperCase();
await receipt(target.id, '700', reference, { payeeAccount: failLender.bankAccount, transactionStatus: 'failed' });
reconciliationUi.failures.push({ id: target.id, repaymentId: target.loanRepaymentId, reference });
const lateLender = await verifiedLender(`合成追回界面-${mode}`, null, '8');
const lateLoan = await confirmedLoan(lateLender.id, '100.00', `合成晚到追回-${mode}`);
const original = await repay(lateLender.id, lateLoan.id, '80.00');
await call(manager, 'POST', `/payment-batches/${original.id}/review`, {}); await exportOrder(original.id);
const exp = await prisma.paymentOrderExport.findFirstOrThrow({ where: { payment_batch_id: original.id } });
const sub = await register(original.id, `UI-ORIGINAL-${mode}`, exp.id); await attest(original.id);
const fact = await proposeFact(original.id, { submissionId: sub.id, kind: 'termination_evidence', terminalStatus: 'failed', evidenceId: await uploadEvidence(original.id) });
await reviewFact(original.id, fact.id); await confirmNotPaid(original.id); await cancelBatch(original.id);
const paid = await repay(lateLender.id, lateLoan.id, '60.00');
await call(manager, 'POST', `/payment-batches/${paid.id}/review`, {}); await exportOrder(paid.id);
await receipt(paid.id, '6000', `UI-PAID-${mode}`, { payeeAccount: lateLender.bankAccount });
await call(finance, 'POST', `/payment-batches/${paid.id}/execute`, {});
const saved = await prisma.paymentBatch.findUniqueOrThrow({ where: { id: original.id } });
reconciliationUi.late.push({ id: original.id, lenderId: lateLender.id, reference: `UI-LATE-${mode}`,
payer: decryptStringOrThrow(config, saved.payment_account_snapshot_json.accountNumberEncrypted), payee: lateLender.bankAccount });
}
await require('./company-loan-browser.cjs')({ app, finance, manager, ordinary, password, company, bank, lender, loan, config, prisma, attributionUi, reconciliationUi });
}
console.log(JSON.stringify({ stage: 'loan_ledger_http', status: 'PASS', checks }));