Files
lotteryLaravel/e2e/tests/api/19.settlement-bad-debt-partial.spec.ts
kang 3f29229499
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
fix: 部分收付累计释额并扩展结算 E2E 与 CI
账期收付改为按累计已付同步 credit_ledger,修复多笔 partial_paid 与坏账核销重复释额;新增 API E2E(占成、权限、部分收付/坏账)与 GitHub Actions e2e-api job。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 09:48:01 +08:00

106 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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();
});