/** * E2E:部分收付后坏账核销 → settlement_confirm 累计释额至账单全额。 */ import { test, expect } from '@playwright/test'; import { adminLoginViaBypass, adminCtxOf, adminLoginWithAccount, fetchBillCreditLedgerRows, fetchOpenDrawNo, openSettlementPeriod, playerLoginViaBypass, setupCreditPlayer, setupSiteOperator, } from './_helper'; import { runWalletDrawSettlement } from './helpers/draw-settlement'; const DEFAULT_PASS = process.env.E2E_PLAYER_PASSWORD ?? '12345678'; test('部分收付后坏账核销 → credit_ledger 累计至全额', async () => { test.setTimeout(360_000); const credit = await setupCreditPlayer({ credit_limit: 100_000, username: `e2e_bd_partial_${Date.now()}`, }); const finance = await setupSiteOperator({ username: `e2e_fin_bd_${Date.now()}`, password: DEFAULT_PASS, role_slug: 'site_finance', }); const drawNo = await fetchOpenDrawNo(); 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: '4321', publishWinNumber: '9999', 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(); expect(Number(playerBill.net_amount)).toBeGreaterThan(0); const billId = Number(playerBill.id); await superAdmin.post(`/api/v1/admin/settlement-bills/${billId}/confirm`); const billDetail = (await (await superAdmin.get(`/api/v1/admin/settlement-bills/${billId}`)).json()) as any; const unpaid = Math.abs(Number(billDetail.data.bill.unpaid_amount)); const firstPay = Math.floor(unpaid / 2); expect(firstPay).toBeGreaterThan(0); const financeSession = await adminLoginWithAccount(finance.username, finance.password); const financeAdmin = await adminCtxOf(financeSession.accessToken); const pay1 = await financeAdmin.post(`/api/v1/admin/settlement-bills/${billId}/payments`, { data: { amount: firstPay, method: 'e2e_bd_partial', remark: 'partial before write-off' }, }); expect(pay1.ok()).toBeTruthy(); expect((await pay1.json()).data.bill.status).toBe('partial_paid'); const ledgerAfterPay1 = await fetchBillCreditLedgerRows(credit.player_id, billId, 'settlement_confirm'); expect(ledgerAfterPay1).toHaveLength(1); expect(Number(ledgerAfterPay1[0].amount)).toBe(firstPay); const writeOff = await financeAdmin.post(`/api/v1/admin/settlement-bills/${billId}/bad-debt-write-off`, { data: { reason: 'e2e partial then bad debt' }, }); expect(writeOff.ok(), `bad debt status=${writeOff.status()}`).toBeTruthy(); expect((await writeOff.json()).code).toBe(0); const settledBill = (await (await superAdmin.get(`/api/v1/admin/settlement-bills/${billId}`)).json()) as any; expect(settledBill.data.bill.status).toBe('settled'); expect(Number(settledBill.data.bill.unpaid_amount)).toBe(0); const ledgerAfterWriteOff = await fetchBillCreditLedgerRows(credit.player_id, billId, 'settlement_confirm'); expect(ledgerAfterWriteOff).toHaveLength(1); expect(Number(ledgerAfterWriteOff[0].amount)).toBe(unpaid); await financeAdmin.dispose(); await superAdmin.dispose(); });