fix: 部分收付累计释额并扩展结算 E2E 与 CI
账期收付改为按累计已付同步 credit_ledger,修复多笔 partial_paid 与坏账核销重复释额;新增 API E2E(占成、权限、部分收付/坏账)与 GitHub Actions e2e-api job。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
151
e2e/tests/api/18.settlement-permissions.spec.ts
Normal file
151
e2e/tests/api/18.settlement-permissions.spec.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
Reference in New Issue
Block a user