借款出借人线上确认借款并以站内通知直达(09-17-company-loan-principal-repayment)
- 新增出借人确认声明:财务发起,出借人本人查看原件后确认或退回;快照与决定不可改删,金额、日期、原件或身份变化后整组失效 - 余额确认接受纸质协议或有效线上声明二选一,代账余额核对材料仍必需;声明与公司代表授权分离 - 普通员工无常驻入口,财务发起后仅通知对应出借人并直达确认页 - 新增迁移 20261010010000_loan_lender_acknowledgements(仅新增三表) - 测试:单测、隔离 HTTP/浏览器、完整借款回归;独立只读复核无阻断;同步发布记录与操作手册 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -18,11 +18,16 @@ module.exports = async function relatedBrowser(h) {
|
||||
try {
|
||||
for (const s of relatedParty.ui) {
|
||||
const mobile = s.mode === 'mobile'; const prefix = mobile ? '/mobile' : ''; const contexts = [];
|
||||
const technicalFinanceRequests = [];
|
||||
console.log(JSON.stringify({ stage: 'loan_related_browser_start', mode: s.mode }));
|
||||
async function login(who, route = `${prefix}/company-loans`) {
|
||||
const context = await browser.newContext({ viewport: mobile ? { width: 390, height: 844 } : { width: 1440, height: 1000 }, serviceWorkers: 'block' });
|
||||
contexts.push(context); await context.route('**/*', r => r.request().url().startsWith(`${origin}/`) ? r.continue() : r.abort());
|
||||
const p = await context.newPage();
|
||||
if (who.id === ordinary.id) p.on('request', request => {
|
||||
const pathname = new URL(request.url()).pathname;
|
||||
if (/^\/api\/(company-loans|loan-lenders|loan-repayments|loan-related-authorizations)(\/|$)/.test(pathname)) technicalFinanceRequests.push(pathname);
|
||||
});
|
||||
await p.goto(`${origin}/login?redirect=${encodeURIComponent(route)}`);
|
||||
await p.getByTestId('login-identifier').fill(who.email); await p.getByTestId('login-password').fill(password); await p.getByTestId('login-submit').click();
|
||||
await p.waitForURL(url => !url.pathname.includes('login'));
|
||||
@@ -237,7 +242,12 @@ module.exports = async function relatedBrowser(h) {
|
||||
assert.equal(await mp.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1), false);
|
||||
await mp.screenshot({ path: path.join(output, `related-${s.mode}-closed.png`), fullPage: true });
|
||||
checks.push(`${s.mode}_related_evidenced_duplicate_dismissal_and_no_overflow`);
|
||||
const tech = await login(ordinary); await tech.waitForURL(/\/403$/); await expect(tech.getByRole('heading', { name: '公司借款', exact: true })).toHaveCount(0);
|
||||
const tech = await login(ordinary);
|
||||
await expect(tech.getByRole('heading', { name: '出借人确认借款', exact: true })).toBeVisible();
|
||||
await expect(tech.getByText('暂无可查看的出借人确认。', { exact: true })).toBeVisible();
|
||||
await expect(tech.getByRole('tablist')).toHaveCount(0);
|
||||
await expect(tech.getByRole('button', { name: /登记借入|登记出借人|申请还本|发起出借人确认|登记公司代表授权范围|代表公司确认授权范围|停止新付款授权/ })).toHaveCount(0);
|
||||
assert.deepEqual(technicalFinanceRequests, [], 'technical employee own declarations must not load finance or company-authority data');
|
||||
assert.equal(await prisma.notification.count({ where: { recipient_employee_id: ordinary.id, target_id: { in: [b.id, failed.id, old.id] } } }), 0);
|
||||
// Stopping from the actual scope card uses the current grant only.
|
||||
await mp.goto(`${origin}${prefix}/company-loans`);
|
||||
|
||||
在新工单中引用
屏蔽一个用户