/** * E2E:账期账单部分收付 → partial_paid → 尾款结清 settled。 */ import { test, expect } from '@playwright/test'; import { adminLoginViaBypass, adminCtxOf, fetchBillCreditLedgerRows, fetchOpenDrawNo, playerLoginViaBypass, openSettlementPeriod, setupCreditPlayer, } from './_helper'; import { runWalletDrawSettlement } from './helpers/draw-settlement'; test('confirm 后分两笔收付 → partial_paid → settled', async () => { test.setTimeout(300_000); const credit = await setupCreditPlayer({ credit_limit: 100_000, username: `e2e_partial_${Date.now()}`, }); const drawNo = await fetchOpenDrawNo(); const adminSession = await adminLoginViaBypass(); const admin = await adminCtxOf(adminSession.accessToken); const periodId = await openSettlementPeriod(admin, credit.admin_site_id); const playerSession = await playerLoginViaBypass({ site_code: credit.site_code, username: credit.username, password: credit.password, }); await runWalletDrawSettlement({ adminToken: adminSession.accessToken, playerToken: playerSession.access_token, drawNo, betNumber: '1234', publishWinNumber: '9999', betAmount: 10_000, expectWalletIncrease: false, }); const close = await admin.post(`/api/v1/admin/settlement-periods/${periodId}/close`); expect(close.ok()).toBeTruthy(); expect((await close.json()).code).toBe(0); const bills = await admin.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 confirm = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/confirm`); expect(confirm.ok()).toBeTruthy(); const confirmBody = (await confirm.json()) as any; expect(confirmBody.code).toBe(0); expect(confirmBody.data.status).toBe('confirmed'); const billShow = await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`); const billDetail = (await billShow.json()) as any; const unpaid = Math.abs(Number(billDetail.data.bill.unpaid_amount)); expect(unpaid).toBeGreaterThan(1); const firstPay = Math.floor(unpaid / 2); expect(firstPay).toBeGreaterThan(0); const pay1 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, { data: { amount: firstPay, method: 'e2e_partial_1', remark: 'e2e first half' }, }); expect(pay1.ok()).toBeTruthy(); const pay1Body = (await pay1.json()) as any; expect(pay1Body.code).toBe(0); expect(pay1Body.data.bill.status).toBe('partial_paid'); const ledgerAfterPay1 = await fetchBillCreditLedgerRows( credit.player_id, Number(playerBill.id), 'settlement_confirm', ); expect(ledgerAfterPay1).toHaveLength(1); expect(Number(ledgerAfterPay1[0].amount)).toBe(firstPay); const midShow = await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`); const midDetail = (await midShow.json()) as any; const secondPay = Math.abs(Number(midDetail.data.bill.unpaid_amount)); expect(secondPay).toBeGreaterThan(0); const pay2 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, { data: { amount: secondPay, method: 'e2e_partial_2', remark: 'e2e remainder' }, }); const pay2Text = await pay2.text(); expect(pay2.ok(), `pay2 status=${pay2.status()} body=${pay2Text.slice(0, 400)}`).toBeTruthy(); const pay2Body = JSON.parse(pay2Text) as any; expect(pay2Body.data.bill.status).toBe('settled'); expect(Number(pay2Body.data.bill.unpaid_amount)).toBe(0); const ledgerAfterPay2 = await fetchBillCreditLedgerRows( credit.player_id, Number(playerBill.id), 'settlement_confirm', ); expect(ledgerAfterPay2).toHaveLength(1); expect(Number(ledgerAfterPay2[0].amount)).toBe(unpaid); const exceed = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, { data: { amount: 1, method: 'e2e_over', remark: 'should fail' }, }); const exceedBody = (await exceed.json()) as any; expect(exceedBody.code).not.toBe(0); await admin.dispose(); }); test('玩家赢单 confirm 后分两笔收付 → settlement_payout 累计', async () => { test.setTimeout(300_000); const winNumber = `6${String(Date.now()).slice(-3)}`; const credit = await setupCreditPlayer({ credit_limit: 100_000, username: `e2e_partial_win_${Date.now()}`, }); const drawNo = await fetchOpenDrawNo(); const adminSession = await adminLoginViaBypass(); const admin = await adminCtxOf(adminSession.accessToken); const periodId = await openSettlementPeriod(admin, credit.admin_site_id); const playerSession = await playerLoginViaBypass({ site_code: credit.site_code, username: credit.username, password: credit.password, }); await runWalletDrawSettlement({ adminToken: adminSession.accessToken, playerToken: playerSession.access_token, drawNo, betNumber: winNumber, betAmount: 10_000, expectWalletIncrease: false, }); const close = await admin.post(`/api/v1/admin/settlement-periods/${periodId}/close`); expect(close.ok()).toBeTruthy(); const bills = await admin.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)).toBeLessThan(0); await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/confirm`); const billDetail = (await (await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`)).json()) as any; const unpaid = Math.abs(Number(billDetail.data.bill.unpaid_amount)); expect(unpaid).toBeGreaterThan(1); const firstPay = Math.floor(unpaid / 2); expect(firstPay).toBeGreaterThan(0); const pay1 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, { data: { amount: firstPay, method: 'e2e_win_partial_1', remark: 'win first half' }, }); expect(pay1.ok()).toBeTruthy(); expect((await pay1.json()).data.bill.status).toBe('partial_paid'); const ledgerAfterPay1 = await fetchBillCreditLedgerRows( credit.player_id, Number(playerBill.id), 'settlement_payout', ); expect(ledgerAfterPay1).toHaveLength(1); expect(Number(ledgerAfterPay1[0].amount)).toBe(firstPay); const midDetail = (await (await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`)).json()) as any; const secondPay = Math.abs(Number(midDetail.data.bill.unpaid_amount)); const pay2 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, { data: { amount: secondPay, method: 'e2e_win_partial_2', remark: 'win remainder' }, }); expect(pay2.ok()).toBeTruthy(); expect((await pay2.json()).data.bill.status).toBe('settled'); const ledgerAfterPay2 = await fetchBillCreditLedgerRows( credit.player_id, Number(playerBill.id), 'settlement_payout', ); expect(ledgerAfterPay2).toHaveLength(1); expect(Number(ledgerAfterPay2[0].amount)).toBe(unpaid); await admin.dispose(); });