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');
}
}