借款出借人线上确认借款并以站内通知直达(09-17-company-loan-principal-repayment)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

- 新增出借人确认声明:财务发起,出借人本人查看原件后确认或退回;快照与决定不可改删,金额、日期、原件或身份变化后整组失效
- 余额确认接受纸质协议或有效线上声明二选一,代账余额核对材料仍必需;声明与公司代表授权分离
- 普通员工无常驻入口,财务发起后仅通知对应出借人并直达确认页
- 新增迁移 20261010010000_loan_lender_acknowledgements(仅新增三表)
- 测试:单测、隔离 HTTP/浏览器、完整借款回归;独立只读复核无阻断;同步发布记录与操作手册

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
2026-10-10 15:22:12 +08:00
共同撰写人 Claude Opus 5.5
父节点 2063ff1e93
当前提交 4a5cd8f2b3
共修改 34 个文件,包含 1610 行新增和 41 行删除
+11 -3
查看文件
@@ -310,13 +310,21 @@ module.exports = async function runBrowser({ app, finance, manager, ordinary, pa
const context = await browser.newContext({ serviceWorkers: 'block' });
await context.route('**/*', route => route.request().url().startsWith(`${origin}/`) ? route.continue() : route.abort());
const page = await context.newPage();
const financeReads = [];
page.on('request', request => {
if (['/api/company-loans', '/api/loan-lenders', '/api/loan-repayments'].includes(new URL(request.url()).pathname)) financeReads.push(request.url());
});
await page.goto(`${origin}/login?redirect=%2Fcompany-loans`);
await page.getByTestId('login-identifier').fill(ordinary.email);
await page.getByTestId('login-password').fill(password);
await page.getByTestId('login-submit').click();
await expect(page.getByRole('heading', { name: '公司借款', exact: true })).not.toBeVisible();
await page.waitForURL(/\/403$/);
checks.push('ordinary_employee_cannot_open_loan_ledger');
await expect(page.getByRole('heading', { name: '公司借款', exact: true })).toBeVisible();
await expect(page.getByRole('heading', { name: '出借人确认借款', exact: true })).toBeVisible();
await expect(page.getByText('暂无可查看的出借人确认。', { exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: '登记借入', exact: true })).toHaveCount(0);
await expect(page.getByRole('tablist', { name: '借款台账视图' })).toHaveCount(0);
assert.deepEqual(financeReads, [], 'own declaration entry never queries the finance ledger');
checks.push('ordinary_employee_only_own_acknowledgements_without_finance_ledger');
await context.close();
console.log(JSON.stringify({ stage: 'loan_browser', status: 'PASS', checks, output: path.relative(root, output) }));
} finally { await browser.close(); }