feat: add commission settlement exports and proofs

This commit is contained in:
Codex
2026-06-29 21:03:57 +08:00
parent ec23cd3b9b
commit 8d4428214a
11 changed files with 706 additions and 24 deletions

View File

@@ -7066,6 +7066,58 @@ async function testReferralAndCrmGrowth() {
assert.equal(paidSettlement.item?.paidBy, TENANT_ADMIN_USER_ID, 'settlement paid status should record operator');
assert.equal(paidSettlement.item?.paymentMethod, 'offline_bank', 'settlement paid status should persist payment method');
const exportedSettlement = await request('/api/commission/settlements/export', {
userId: TENANT_ADMIN_USER_ID,
query: { settlementId: generatedSettlement.item.id, format: 'csv' },
});
assert.equal(exportedSettlement.item?.format, 'csv', 'tenant admin should export settlement csv');
assert.equal(exportedSettlement.item?.rowCount >= 2, true, 'settlement export should include item rows');
assert.ok(exportedSettlement.item?.sha256, 'settlement export should include content hash');
assert.ok(Buffer.from(exportedSettlement.item?.contentBase64 || '', 'base64').toString('utf8').includes('commissionAmountCents'), 'settlement export should include csv headers');
const settlementProof = await request('/api/commission/settlements/proofs', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
settlementId: generatedSettlement.item.id,
proofType: 'payment',
title: '集成测试打款凭证',
externalUrl: 'https://finance.example.test/proofs/commission-paid.png',
amountCents: paidSettlement.item.commissionAmountCents,
paymentMethod: 'offline_bank',
paymentAccount: 'masked-bank-account',
metadata: { source: 'integration-test' },
},
});
assert.equal(settlementProof.item?.status, 'submitted', 'tenant admin should submit payment proof');
assert.equal(settlementProof.item?.submittedBy, TENANT_ADMIN_USER_ID, 'proof should record submitter');
const approvedProof = await request('/api/commission/settlements/proofs/status', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
proofId: settlementProof.item.id,
status: 'approved',
reviewNote: 'integration proof reviewed',
},
});
assert.equal(approvedProof.item?.status, 'approved', 'tenant admin should approve payment proof');
assert.equal(approvedProof.item?.reviewedBy, TENANT_ADMIN_USER_ID, 'proof approval should record reviewer');
const proofList = await request('/api/commission/settlements/proofs', {
userId: TENANT_AGENT_USER_ID,
query: { settlementId: generatedSettlement.item.id },
});
assert.ok(proofList.items?.some(item => item.id === settlementProof.item.id), 'agent should read own settlement proof');
assert.ok(!JSON.stringify(proofList).includes('crm-secret-smoke'), 'settlement proofs must not leak CRM secret');
const salesExportDenied = await request('/api/commission/settlements/export', {
userId: TENANT_SALES_USER_ID,
query: { settlementId: generatedSettlement.item.id, format: 'json' },
expectStatus: 403,
});
assert.equal(salesExportDenied.code, 'COMMISSION_SCOPE_REQUIRED', 'sales must not export another referrer settlement');
const paidSettlementLocked = await request('/api/commission/settlements/status', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',