user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); DB::connection('business_mirror')->table('fin_accounts')->insert([ 'company_name' => '测试财务公司', 'account_short' => '测试账户', 'account_no' => '6222000000000001', 'bank_name' => '测试银行', 'company_plan' => '保留', ]); } private function pageUrl(): string { return route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', ]); } private function statusUrl(): string { return route('business.financial-ledger.accounts.status', [ 'company_id' => $this->company->id, ]); } private function typeUrl(): string { return route('business.financial-ledger.accounts.type', [ 'company_id' => $this->company->id, ]); } public function test_account_table_renders_status_selector_with_four_options(): void { $this->loginAs()->get($this->pageUrl()) ->assertOk() ->assertSee('账户状态') ->assertSee('data-account-status', false) ->assertSee('正常') ->assertSee('网银异常') ->assertSee('限额') ->assertSee('工商注销') ->assertSee('name="accounts_short"', false) ->assertSee('name="accounts_status"', false) ->assertSee('name="accounts_region"', false) ->assertSee('name="accounts_type"', false) ->assertSee('data-account-type', false) ->assertSee('ledger-type-select', false) ->assertSee('data-mapping-bulk', false) ->assertSee('导出录入模板') ->assertSee('data-account-template-download', false) ->assertSee('data-account-template-download-status', false) ->assertSee('账户简称账户类型账户状态', false) ->assertSee('公司规划账户是否保留后4位', false) ->assertSee('data-account-short="测试账户" data-account-should-keep="是"', false) ->assertSee('网商银行') ->assertSee('ledger-accounts-table-wrap', false) ->assertSee('ledger-pagination__controls', false) ->assertSee('ledger-page-box', false) ->assertSee('1 / 1', false) ->assertSee('data-ledger-column-picker', false) ->assertSee('data-ledger-column-panel', false) ->assertSee('列设置') ->assertSee('拖动字段可调整列顺序') ->assertSee('恢复默认') ->assertSee('可用金额合计') ->assertSee('不可用金额合计') ->assertSee('可用金额 − 不可用金额') ->assertSee('未填写') ->assertDontSee('data-account-filter-state', false) ->assertSee('value="normal" selected', false); } public function test_financial_ledger_tab_forms_submit_to_query_tab_routes(): void { $pageUrl = $this->pageUrl(); $response = $this->loginAs()->get($pageUrl)->assertOk(); foreach (['accounts', 'shops', 'flows', 'transactions', 'mapping'] as $tab) { $response->assertSee('action="' . $pageUrl . '?tab=' . $tab . '"', false); $response->assertDontSee('action="' . $pageUrl . '/' . $tab . '"', false); } } public function test_explicit_account_keep_value_takes_precedence_over_company_plan(): void { DB::connection('business_mirror')->table('fin_accounts')->insert([ 'company_name' => '保留判断测试公司', 'account_short' => '显式不保留账户', 'company_plan' => '保留', 'account_should_keep' => '否', ]); $this->loginAs()->get($this->pageUrl()) ->assertOk() ->assertSee('data-account-short="显式不保留账户" data-account-should-keep="否"', false); } public function test_account_pagination_lists_page_urls_with_current_filter(): void { $rows = []; for ($index = 1; $index <= 101; $index++) { $rows[] = [ 'company_name' => '分页测试公司', 'account_short' => 'page-test-' . $index, ]; } DB::connection('business_mirror')->table('fin_accounts')->insert($rows); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_short' => 'page-test', ])) ->assertOk() ->assertSee('ledger-page-box', false) ->assertSee('aria-label="下一页"', false) ->assertSee('accounts_page=2', false) ->assertSee('accounts_short=page-test', false); } public function test_account_filters_narrow_by_short_status_region_and_type(): void { $connection = DB::connection('business_mirror'); $connection->table('fin_accounts')->insert([ [ 'company_name' => '筛选测试公司甲', 'account_short' => '筛选账户甲', 'region' => '上海', 'account_type' => '公户', ], [ 'company_name' => '筛选测试公司乙', 'account_short' => '筛选账户乙', 'region' => '杭州', 'account_type' => '个人卡', ], ]); $connection->table('fin_account_statuses')->insert([ 'account_short' => '筛选账户乙', 'status' => 'limited', 'updated_by' => 'Test User', ]); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_short' => '筛选账户', 'accounts_status' => 'limited', 'accounts_region' => '杭州', 'accounts_type' => '个人卡', ])) ->assertOk() ->assertSee('data-account-short="筛选账户乙"', false) ->assertDontSee('data-account-short="筛选账户甲"', false) ->assertSee('data-account-filter-state', false) ->assertSee('筛选中') ->assertSee('value="limited" selected', false); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_short' => '筛选账户', 'accounts_status' => 'normal', ])) ->assertOk() ->assertSee('data-account-short="筛选账户甲"', false) ->assertDontSee('data-account-short="筛选账户乙"', false); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_short' => '测试账户', 'accounts_status' => 'normal', 'accounts_region' => '__empty__', 'accounts_type' => '__empty__', ])) ->assertOk() ->assertSee('data-account-short="测试账户"', false); } public function test_account_type_can_be_saved_and_filtered_by_effective_value(): void { $connection = DB::connection('business_mirror'); $connection->table('fin_accounts')->insert([ [ 'company_name' => '类型测试公司甲', 'account_short' => '类型账户甲', 'account_type' => '公户', ], [ 'company_name' => '类型测试公司乙', 'account_short' => '类型账户乙', 'account_type' => '个人卡', ], [ 'company_name' => '类型测试公司丙', 'account_short' => '类型账户丙', 'account_type' => '支付宝', ], ]); $this->loginAs()->postJson($this->typeUrl(), [ 'account_short' => '类型账户甲', 'account_type' => '网商银行', ]) ->assertOk() ->assertJsonPath('account_type', '网商银行') ->assertJsonPath('label', '网商银行'); $this->loginAs()->postJson($this->typeUrl(), [ 'account_short' => '类型账户甲', 'account_type' => '个人卡', ])->assertOk(); $this->assertDatabaseCount('fin_account_type_overrides', 1, 'business_mirror'); $this->assertDatabaseHas('fin_account_type_overrides', [ 'account_short' => '类型账户甲', 'account_type' => '个人卡', 'updated_by' => 'Test User', ], 'business_mirror'); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_type' => '个人卡', ])) ->assertOk() ->assertSee('data-account-short="类型账户甲"', false) ->assertSee('data-account-short="类型账户乙"', false) ->assertDontSee('data-account-short="类型账户丙"', false) ->assertSee('value="个人卡" selected', false); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'financial-operations', 'page' => 'ledger', 'accounts_type' => '公户', ])) ->assertOk() ->assertDontSee('data-account-short="类型账户甲"', false); $this->withExceptionHandling()->loginAs()->postJson($this->typeUrl(), [ 'account_short' => '类型账户甲', 'account_type' => '不存在的类型', ])->assertUnprocessable()->assertJsonPath('message', '请选择现有账户类型。'); } public function test_account_status_can_be_saved_and_updated_without_duplicate_rows(): void { $payload = [ 'account_short' => '测试账户', 'status' => 'limited', ]; $this->loginAs()->postJson($this->statusUrl(), $payload) ->assertOk() ->assertJsonPath('status', 'limited') ->assertJsonPath('label', '限额'); $this->loginAs()->postJson($this->statusUrl(), [ 'account_short' => '测试账户', 'status' => 'normal', ])->assertOk()->assertJsonPath('label', '正常'); $this->assertDatabaseCount('fin_account_statuses', 1, 'business_mirror'); $this->assertDatabaseHas('fin_account_statuses', [ 'account_short' => '测试账户', 'status' => 'normal', 'updated_by' => 'Test User', ], 'business_mirror'); $this->loginAs()->get($this->pageUrl()) ->assertOk() ->assertSee('value="normal" selected', false); } public function test_invalid_status_or_account_is_rejected(): void { $this->withExceptionHandling()->loginAs()->postJson($this->statusUrl(), [ 'account_short' => '测试账户', 'status' => 'unknown', ])->assertUnprocessable()->assertJsonValidationErrors('status'); $this->withExceptionHandling()->loginAs()->postJson($this->statusUrl(), [ 'account_short' => '不存在的账户', 'status' => 'limited', ])->assertUnprocessable()->assertJsonPath('message', '账户简称不存在,请刷新账户总表后重试。'); $this->assertDatabaseCount('fin_account_statuses', 0, 'business_mirror'); } public function test_user_without_finance_editor_role_cannot_change_account_status(): void { $viewer = user_model_class()::create([ 'name' => '只读测试用户', 'email' => 'ledger-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->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $this->withExceptionHandling()->loginAs($viewer)->postJson($this->statusUrl(), [ 'account_short' => '测试账户', 'status' => 'limited', ])->assertForbidden(); $this->assertDatabaseCount('fin_account_statuses', 0, 'business_mirror'); } }