CaiWuXiTong/tests/Feature/Business/FinanceLedgerFlowSummarySpreadsheetTest.php
92451 f4f0d5d7b1 feat: 同步账户资金汇总并新增数据来源 (#1114)
Co-authored-by: 92451 <924518135@qq.com>
Co-committed-by: 92451 <924518135@qq.com>
2026-11-11 19:50:50 +08:00

277 lines
11 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Models\Auth\Role;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Tests\Feature\FeatureTestCase;
class FinanceLedgerFlowSummarySpreadsheetTest extends FeatureTestCase
{
private string $accountShort;
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow('2026-11-10 12:00:00');
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail(),
);
$this->accountShort = '汇总表格测试账户-' . strtoupper(substr(md5(uniqid('', true)), 0, 8));
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '汇总表格测试公司',
'account_short' => $this->accountShort,
'account_type' => '公户',
'current_balance' => 100,
'daily_available' => 100,
'blocked_amount' => 0,
]);
$connection->table('fin_account_opening')->insert([
'account_short' => $this->accountShort,
'opening_date' => '2026-11-01',
'opening_balance' => 100,
]);
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function test_summary_exposes_excel_style_add_row_and_add_column_controls(): void
{
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$this->assertStringContainsString('data-ledger-flow-summary-add-row', $html);
$this->assertStringContainsString('data-ledger-flow-summary-add-column', $html);
$this->assertStringContainsString('data-ledger-flow-summary-column-dialog', $html);
$this->assertStringContainsString('>新增行</button>', $html);
$this->assertStringContainsString('>新增列</button>', $html);
}
public function test_summary_custom_column_values_can_be_saved_and_column_can_be_deleted(): void
{
$create = $this->loginAs()->postJson($this->columnStoreUrl(), [
'field_names' => ['核对状态'],
'position' => 'end',
])->assertCreated()
->assertJsonPath('fields.0.field_name', '核对状态');
$fieldId = (int) $create->json('fields.0.id');
$this->assertDatabaseHas('fin_flow_summary_custom_fields', [
'id' => $fieldId,
'field_name' => '核对状态',
], 'business_mirror');
$this->loginAs()->postJson($this->valueUrl(), [
'field_id' => $fieldId,
'account_short' => $this->accountShort,
'value' => '已核对',
])->assertOk()
->assertJsonPath('value.value', '已核对');
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$this->assertStringContainsString('title="核对状态">核对状态</strong>', $html);
$this->assertStringContainsString('data-ledger-flow-summary-custom-value', $html);
$this->assertStringContainsString('data-account-short="' . $this->accountShort . '"', $html);
$this->assertStringContainsString('value="已核对"', $html);
$this->loginAs()->deleteJson($this->columnDeleteUrl($fieldId))
->assertOk()
->assertJsonPath('deleted', true);
$this->assertDatabaseMissing('fin_flow_summary_custom_fields', [
'id' => $fieldId,
], 'business_mirror');
$this->assertDatabaseMissing('fin_flow_summary_custom_values', [
'custom_field_id' => $fieldId,
], 'business_mirror');
}
public function test_summary_keeps_every_account_master_row_and_follows_account_edits(): void
{
$connection = DB::connection('business_mirror');
$originalId = (int) $connection->table('fin_accounts')
->where('account_short', $this->accountShort)
->value('id');
$duplicateId = (int) $connection->table('fin_accounts')->insertGetId([
'company_name' => '重名账户测试公司',
'account_short' => $this->accountShort,
'account_type' => '公户',
'current_balance' => 20,
'daily_available' => 20,
'blocked_amount' => 0,
]);
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$this->assertStringContainsString('data-flow-summary-account-id="' . $originalId . '"', $html);
$this->assertStringContainsString('data-flow-summary-account-id="' . $duplicateId . '"', $html);
$this->assertStringContainsString('汇总表格测试公司', $html);
$this->assertStringContainsString('重名账户测试公司', $html);
$renamedShort = $this->accountShort . '-已同步';
$this->loginAs()->postJson(route('business.financial-ledger.accounts.update', [
'company_id' => $this->company->id,
'id' => $duplicateId,
]), [
'company_name' => '账户编辑后公司',
'account_short' => $renamedShort,
'status' => 'normal',
])->assertOk()->assertJsonPath('account.company_name', '账户编辑后公司');
$renamedHtml = $this->loginAs()->get($this->pageUrlForAccount($renamedShort))
->assertOk()
->getContent();
$this->assertStringContainsString('data-flow-summary-account-id="' . $duplicateId . '"', $renamedHtml);
$this->assertStringContainsString('账户编辑后公司', $renamedHtml);
$this->assertStringNotContainsString('重名账户测试公司', $renamedHtml);
}
public function test_data_source_is_after_company_and_can_be_edited_inline(): void
{
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$flowsStart = strpos($html, 'id="ledger-panel-flows"');
$transactionsStart = strpos($html, 'id="ledger-panel-transactions"');
$flowsHtml = substr($html, (int) $flowsStart, (int) $transactionsStart - (int) $flowsStart);
$accountHeader = strpos($flowsHtml, 'data-ledger-header-field="account_short"');
$companyHeader = strpos($flowsHtml, 'data-ledger-header-field="company_name"');
$sourceHeader = strpos($flowsHtml, 'data-ledger-header-field="data_source"');
$openingHeader = strpos($flowsHtml, 'data-ledger-header-field="opening_balance"');
$this->assertIsInt($accountHeader);
$this->assertIsInt($companyHeader);
$this->assertIsInt($sourceHeader);
$this->assertIsInt($openingHeader);
$this->assertTrue($accountHeader < $companyHeader);
$this->assertTrue($companyHeader < $sourceHeader);
$this->assertTrue($sourceHeader < $openingHeader);
$this->assertStringContainsString('data-ledger-flow-summary-source', $flowsHtml);
$this->assertStringContainsString('placeholder="填写数据来源"', $flowsHtml);
$this->loginAs()->postJson($this->sourceUrl(), [
'account_short' => $this->accountShort,
'value' => '银行流水',
])->assertOk()
->assertJsonPath('value.data_source', '银行流水');
$this->assertDatabaseHas('fin_flow_summary_account_sources', [
'account_short' => $this->accountShort,
'data_source' => '银行流水',
], 'business_mirror');
$accountId = (int) DB::connection('business_mirror')->table('fin_accounts')
->where('account_short', $this->accountShort)
->value('id');
$renamedShort = $this->accountShort . '-来源同步';
$this->loginAs()->postJson(route('business.financial-ledger.accounts.update', [
'company_id' => $this->company->id,
'id' => $accountId,
]), [
'company_name' => '汇总表格测试公司',
'account_short' => $renamedShort,
'status' => 'normal',
])->assertOk();
$this->assertDatabaseHas('fin_flow_summary_account_sources', [
'account_short' => $renamedShort,
'data_source' => '银行流水',
], 'business_mirror');
$this->assertDatabaseHas('fin_account_opening', [
'account_short' => $renamedShort,
], 'business_mirror');
$this->assertDatabaseMissing('fin_account_opening', [
'account_short' => $this->accountShort,
], 'business_mirror');
$renamedHtml = $this->loginAs()->get($this->pageUrlForAccount($renamedShort))
->assertOk()
->getContent();
$this->assertStringContainsString('value="银行流水"', $renamedHtml);
}
public function test_data_source_column_name_is_reserved_for_the_fixed_column(): void
{
$this->withExceptionHandling()->loginAs()->postJson($this->columnStoreUrl(), [
'field_names' => ['数据来源'],
'position' => 'end',
])->assertUnprocessable()
->assertJsonPath('errors.field_names.0', '“数据来源”是账户资金汇总固定列,不能重复新增。');
}
public function test_summary_spreadsheet_endpoints_reject_account_viewers(): void
{
$viewer = user_model_class()::create([
'name' => '汇总只读用户',
'email' => 'flow-summary-viewer-' . uniqid() . '@example.com',
'password' => 'local-test-password',
'locale' => 'zh-CN',
'enabled' => 1,
'landing_page' => 'dashboard',
'created_from' => 'core::test',
]);
$viewer->companies()->attach($this->company->id);
$viewer->roles()->attach(Role::where('name', 'user')->firstOrFail()->id);
$viewer->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->columnStoreUrl(), [
'field_names' => ['无权限列'],
])->assertForbidden();
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->sourceUrl(), [
'account_short' => $this->accountShort,
'value' => '无权限来源',
])->assertForbidden();
}
private function pageUrl(): string
{
return $this->pageUrlForAccount($this->accountShort);
}
private function pageUrlForAccount(string $accountShort): string
{
return route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'financial-operations',
'page' => 'ledger',
]) . '?' . http_build_query([
'tab' => 'flows',
'flows_view' => 'summary',
'flows_from' => '2026-11-01',
'flows_to' => '2026-11-30',
'flows_account' => $accountShort,
]);
}
private function columnStoreUrl(): string
{
return route('business.financial-ledger.flows.summary.columns.store', [
'company_id' => $this->company->id,
]);
}
private function columnDeleteUrl(int $fieldId): string
{
return route('business.financial-ledger.flows.summary.columns.delete', [
'company_id' => $this->company->id,
'id' => $fieldId,
]);
}
private function valueUrl(): string
{
return route('business.financial-ledger.flows.summary.values.update', [
'company_id' => $this->company->id,
]);
}
private function sourceUrl(): string
{
return route('business.financial-ledger.flows.summary.source.update', [
'company_id' => $this->company->id,
]);
}
}