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('>新增行', $html); $this->assertStringContainsString('>新增列', $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="核对状态">核对状态', $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_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(); } private function pageUrl(): 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' => $this->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, ]); } }