/** * E2E:结算收付权限 — 站点财务可收付/坏账;绑定代理不可坏账核销。 */ import { test, expect } from '@playwright/test'; import { adminCtxOf, adminLoginWithAccount, adminLoginViaBypass, fetchOpenDrawNo, openSettlementPeriod, playerLoginViaBypass, setupCreditPlayer, setupSiteOperator, } from './_helper'; import { runWalletDrawSettlement } from './helpers/draw-settlement'; const DEFAULT_PASS = process.env.E2E_PLAYER_PASSWORD ?? '12345678'; test('站点财务可 confirm + 收付;绑定代理不可坏账核销', async () => { test.setTimeout(360_000); const credit = await setupCreditPlayer({ credit_limit: 100_000, username: `e2e_perm_${Date.now()}`, }); const finance = await setupSiteOperator({ username: `e2e_fin_${Date.now()}`, password: DEFAULT_PASS, role_slug: 'site_finance', }); const drawNo = await fetchOpenDrawNo(); const winNumber = `5${String(Date.now()).slice(-3)}`; const superSession = await adminLoginViaBypass(); const superAdmin = await adminCtxOf(superSession.accessToken); const periodId = await openSettlementPeriod(superAdmin, credit.admin_site_id); const playerSession = await playerLoginViaBypass({ site_code: credit.site_code, username: credit.username, password: credit.password, }); await runWalletDrawSettlement({ adminToken: superSession.accessToken, playerToken: playerSession.access_token, drawNo, betNumber: winNumber, betAmount: 10_000, expectWalletIncrease: false, }); const close = await superAdmin.post(`/api/v1/admin/settlement-periods/${periodId}/close`); expect(close.ok()).toBeTruthy(); const bills = await superAdmin.get( `/api/v1/admin/settlement-bills?settlement_period_id=${periodId}&size=20`, ); const billsBody = (await bills.json()) as any; const playerBill = billsBody.data.items.find( (b: any) => b.bill_type === 'player' && Number(b.owner_id) === credit.player_id, ); expect(playerBill).toBeTruthy(); const billId = Number(playerBill.id); const unpaid = Number(playerBill.unpaid_amount); expect(unpaid).toBeGreaterThan(0); const financeSession = await adminLoginWithAccount(finance.username, finance.password); const financeAdmin = await adminCtxOf(financeSession.accessToken); const me = await financeAdmin.get('/api/v1/admin/auth/me'); expect(me.ok()).toBeTruthy(); const meBody = (await me.json()) as any; expect(meBody.data.admin.account_kind).toBe('site_finance'); const confirm = await financeAdmin.post(`/api/v1/admin/settlement-bills/${billId}/confirm`); expect(confirm.ok(), `site_finance confirm status=${confirm.status()}`).toBeTruthy(); expect((await confirm.json()).code).toBe(0); const pay = await financeAdmin.post(`/api/v1/admin/settlement-bills/${billId}/payments`, { data: { amount: unpaid, method: 'e2e_finance', remark: 'site finance payment' }, }); expect(pay.ok(), `site_finance payment status=${pay.status()}`).toBeTruthy(); expect((await pay.json()).data.bill.status).toBe('settled'); await financeAdmin.dispose(); const credit2 = await setupCreditPlayer({ credit_limit: 100_000, username: `e2e_perm2_${Date.now()}`, }); const periodId2 = await openSettlementPeriod(superAdmin, credit2.admin_site_id); const drawNo2 = await fetchOpenDrawNo(); const player2 = await playerLoginViaBypass({ site_code: credit2.site_code, username: credit2.username, password: credit2.password, }); await runWalletDrawSettlement({ adminToken: superSession.accessToken, playerToken: player2.access_token, drawNo: drawNo2, betNumber: `4${String(Date.now()).slice(-3)}`, betAmount: 10_000, expectWalletIncrease: false, }); const close2 = await superAdmin.post(`/api/v1/admin/settlement-periods/${periodId2}/close`); expect(close2.ok()).toBeTruthy(); const bills2 = await superAdmin.get( `/api/v1/admin/settlement-bills?settlement_period_id=${periodId2}&size=20`, ); const bill2 = ((await bills2.json()) as any).data.items.find( (b: any) => b.bill_type === 'player' && Number(b.owner_id) === credit2.player_id, ); expect(bill2).toBeTruthy(); await superAdmin.post(`/api/v1/admin/settlement-bills/${bill2.id}/confirm`); const agentSession = await adminLoginWithAccount('e2e_leaf_agent', DEFAULT_PASS); const agentAdmin = await adminCtxOf(agentSession.accessToken); const agentMe = (await (await agentAdmin.get('/api/v1/admin/auth/me')).json()) as any; expect(agentMe.data.admin.account_kind).toBe('agent_operator'); const badDebt = await agentAdmin.post( `/api/v1/admin/settlement-bills/${bill2.id}/bad-debt-write-off`, { data: { reason: 'e2e should deny' } }, ); expect([403, 404]).toContain(badDebt.status()); const financeBadDebt = await setupSiteOperator({ username: `e2e_fin_bd_${Date.now()}`, password: DEFAULT_PASS, role_slug: 'site_finance', }); const finance2 = await adminLoginWithAccount(financeBadDebt.username, DEFAULT_PASS); const finance2Ctx = await adminCtxOf(finance2.accessToken); const writeOff = await finance2Ctx.post( `/api/v1/admin/settlement-bills/${bill2.id}/bad-debt-write-off`, { data: { reason: 'e2e uncollectible' } }, ); expect(writeOff.ok(), `site_finance bad debt status=${writeOff.status()}`).toBeTruthy(); expect((await writeOff.json()).code).toBe(0); await agentAdmin.dispose(); await finance2Ctx.dispose(); await superAdmin.dispose(); });