412 lines
16 KiB
PHP
412 lines
16 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature\Business;
|
||
|
||
use App\Models\Auth\Permission;
|
||
use App\Models\Auth\Role;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Tests\Feature\FeatureTestCase;
|
||
|
||
class FinanceLedgerAccountStatusTest extends FeatureTestCase
|
||
{
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
|
||
$this->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,
|
||
]);
|
||
}
|
||
|
||
private function planUrl(): string
|
||
{
|
||
return route('business.financial-ledger.accounts.plan', [
|
||
'company_id' => $this->company->id,
|
||
]);
|
||
}
|
||
|
||
private function keepUrl(): string
|
||
{
|
||
return route('business.financial-ledger.accounts.keep', [
|
||
'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('data-account-plan', false)
|
||
->assertSee('data-account-keep', false)
|
||
->assertSee('data-account-plan-display', false)
|
||
->assertSee('data-account-keep-display', 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('<th>账户简称</th><th>账户类型</th><th class="ledger-status-column">账户状态</th>', false)
|
||
->assertSee('<th>公司规划</th><th class="ledger-keep-column">账户是否保留</th><th>后4位</th>', 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('data-ledger-edit-mode-toggle', false)
|
||
->assertSee('aria-checked="false"', false)
|
||
->assertSee('ledger-account-editor', false)
|
||
->assertSee('data-account-edit-display', 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_company_plan_and_account_keep_can_be_updated(): void
|
||
{
|
||
DB::connection('business_mirror')->table('fin_accounts')->insert([
|
||
'company_name' => '规划选项公司',
|
||
'account_short' => '规划选项账户',
|
||
'company_plan' => '待注销',
|
||
]);
|
||
|
||
$this->loginAs()->postJson($this->planUrl(), [
|
||
'account_short' => '测试账户',
|
||
'company_plan' => '待注销',
|
||
])->assertOk()->assertJsonPath('label', '待注销');
|
||
|
||
$this->loginAs()->postJson($this->keepUrl(), [
|
||
'account_short' => '测试账户',
|
||
'account_should_keep' => '否',
|
||
])->assertOk()->assertJsonPath('label', '否');
|
||
|
||
$this->assertDatabaseHas('fin_accounts', [
|
||
'account_short' => '测试账户',
|
||
'company_plan' => '待注销',
|
||
'account_should_keep' => '否',
|
||
], 'business_mirror');
|
||
|
||
$this->withExceptionHandling()->loginAs()->postJson($this->planUrl(), [
|
||
'account_short' => '测试账户',
|
||
'company_plan' => '不存在的规划',
|
||
])->assertUnprocessable()->assertJsonPath('message', '请选择现有公司规划。');
|
||
|
||
$this->withExceptionHandling()->loginAs()->postJson($this->keepUrl(), [
|
||
'account_short' => '测试账户',
|
||
'account_should_keep' => '可能',
|
||
])->assertUnprocessable()->assertJsonValidationErrors('account_should_keep');
|
||
}
|
||
|
||
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->roles()->attach(Role::where('name', 'user')->firstOrFail()->id);
|
||
$viewer->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
|
||
|
||
$this->loginAs($viewer)->get($this->pageUrl())
|
||
->assertOk()
|
||
->assertSee('data-ledger-edit-mode-toggle', false)
|
||
->assertSee('当前账号没有财务台账编辑权限')
|
||
->assertDontSee('data-account-status data-account-short', false)
|
||
->assertDontSee('data-account-type data-account-short', false);
|
||
|
||
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->statusUrl(), [
|
||
'account_short' => '测试账户',
|
||
'status' => 'limited',
|
||
])->assertForbidden();
|
||
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->planUrl(), [
|
||
'account_short' => '测试账户',
|
||
'company_plan' => '保留',
|
||
])->assertForbidden();
|
||
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->keepUrl(), [
|
||
'account_short' => '测试账户',
|
||
'account_should_keep' => '否',
|
||
])->assertForbidden();
|
||
|
||
$this->assertDatabaseCount('fin_account_statuses', 0, 'business_mirror');
|
||
}
|
||
}
|