CaiWuXiTong/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php
shanshi abf4b92fe0 feat(财务台账): 增加不保留账户表头排序 (#511)
不保留账户待办表头筛选菜单增加升序、降序,并按生效展示值排序。
2026-10-24 18:48:36 +08:00

556 lines
22 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Models\Auth\Role;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Tests\Feature\FeatureTestCase;
class FinanceLedgerAccountKeepTodoTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$schema = Schema::connection('business_mirror');
if (! $schema->hasTable('ops_fys_company_info')) {
$schema->create('ops_fys_company_info', function (Blueprint $table): void {
$table->integer('Id')->primary();
$table->string('businessLicenseName')->nullable();
$table->string('legalRepresentative')->nullable();
});
}
}
private function ledgerUrl(): string
{
return route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'financial-operations',
'page' => 'ledger',
]);
}
private function keepTasksUrl(array $parameters = []): string
{
return route('business.financial-ledger.unretained-accounts', array_merge([
'company_id' => $this->company->id,
], $parameters));
}
private function keepTodoUrl(): string
{
return route('business.financial-ledger.accounts.keep-todo', [
'company_id' => $this->company->id,
]);
}
public function test_ledger_card_follows_account_count_and_links_to_independent_keep_tasks(): void
{
DB::connection('business_mirror')->table('fin_accounts')->insert([
[
'company_name' => '不保留测试公司甲',
'account_short' => '显式不保留账户',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '不保留测试公司乙',
'account_short' => '规划不保留账户',
'company_plan' => '待注销',
'account_should_keep' => null,
],
[
'company_name' => '不保留测试公司丙',
'account_short' => '明确保留账户',
'company_plan' => '待注销',
'account_should_keep' => '是',
],
[
'company_name' => '不保留测试公司丁',
'account_short' => '规划保留账户',
'company_plan' => '保留',
'account_should_keep' => null,
],
]);
$response = $this->loginAs()->get($this->ledgerUrl())->assertOk();
$content = $response->getContent();
$this->assertMatchesRegularExpression(
'/<span>账户数<\/span><b>4<\/b><\/div>\s*<a[^>]+aria-label="查看不保留账户处理待办"[^>]*><span>不保留账户数<\/span><b>2<\/b><\/a>/u',
$content,
);
$response->assertSee('href="' . $this->keepTasksUrl() . '"', false);
}
public function test_keep_tasks_page_uses_required_columns_filters_and_retention_rule(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '展示测试公司甲',
'account_short' => '显式不保留账户',
'account_type' => '公户',
'account_no' => '6222000000000001',
'bank_name' => '展示测试银行甲',
'bank_city_branch' => '上海 / 测试支行',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '展示测试公司乙',
'account_short' => '规划不保留账户',
'account_type' => '个人卡',
'account_no' => null,
'bank_name' => null,
'bank_city_branch' => null,
'company_plan' => '待注销',
'account_should_keep' => null,
],
[
'company_name' => '展示测试公司丙',
'account_short' => '明确保留账户',
'account_type' => '支付宝',
'account_no' => null,
'bank_name' => null,
'bank_city_branch' => null,
'company_plan' => '待注销',
'account_should_keep' => '是',
],
[
'company_name' => '展示测试公司丁',
'account_short' => '规划保留账户',
'account_type' => '网商银行',
'account_no' => null,
'bank_name' => null,
'bank_city_branch' => null,
'company_plan' => '保留',
'account_should_keep' => null,
],
]);
$connection->table('fin_account_statuses')->insert([
'account_short' => '显式不保留账户',
'status' => 'limited',
'updated_by' => 'Test User',
]);
$connection->table('ops_fys_company_info')->insert([
[
'Id' => 1,
'businessLicenseName' => '展示测试公司甲',
'legalRepresentative' => '旧法人甲',
],
[
'Id' => 2,
'businessLicenseName' => '展示测试公司甲',
'legalRepresentative' => '展示法人甲',
],
[
'Id' => 3,
'businessLicenseName' => '展示测试公司乙',
'legalRepresentative' => '展示法人乙',
],
]);
$response = $this->loginAs()->get($this->keepTasksUrl())->assertOk();
$content = $response->getContent();
$response
->assertSee('不保留账户处理待办')
->assertSee('不与其他页面的待办事项合并')
->assertSee('data-keep-account="显式不保留账户"', false)
->assertSee('data-keep-account="规划不保留账户"', false)
->assertSee('法人名称')
->assertSee('展示法人甲')
->assertSee('展示法人乙')
->assertDontSee('旧法人甲')
->assertDontSee('data-keep-account="明确保留账户"', false)
->assertDontSee('data-keep-account="规划保留账户"', false)
->assertSee('name="keep_short"', false)
->assertSee('name="keep_type"', false)
->assertSee('name="keep_status"', false)
->assertSee('name="keep_method"', false)
->assertSee('name="keep_owner"', false)
->assertSee('name="keep_progress"', false)
->assertSee('开始时间排序和筛选')
->assertSee('完成时间排序和筛选')
->assertSee('线上')
->assertSee('线下')
->assertSee('玉兔')
->assertSee('山时')
->assertSee('aria-label="责任人员"', false)
->assertSee('aria-label="开始时间"', false)
->assertSee('aria-label="完成时间"', false)
->assertSee('type="date" name="started_at"', false)
->assertSee('type="date" name="completed_at"', false)
->assertSee('data-ledger-header-searchable-filter', false)
->assertSee('list="ledger-header-options-keep-account_short"', false)
->assertSee('name="keep_filter_column"', false)
->assertSee('name="keep_filter_value"', false)
->assertSee('公司名称排序和筛选')
->assertSee('keep_sort=account_short', false)
->assertSee('keep_sort=account_status', false)
->assertSee('keep_sort=owner_name', false)
->assertSee('keep_sort=current_progress', false)
->assertSee('已完成')
->assertSee('进行中')
->assertSee('待开始')
->assertSee('data-keep-tbody', false)
->assertSee('data-keep-pagination', false)
->assertSee('拖动表头右侧边界可调整列宽')
->assertSee('cursor:col-resize', false)
->assertSee('position:sticky', false)
->assertSee('currentBody.replaceWith', false)
->assertSee('history.replaceState', false)
->assertSee('nextWrap.scrollTop = scrollTop', false);
preg_match_all('/data-ledger-header-field="([^"]+)"/u', $content, $matches);
$this->assertSame([
'company_name',
'account_short',
'legal_representative',
'account_type',
'account_status',
'account_no',
'bank_name',
'bank_city_branch',
'account_should_keep',
'handling_method',
'owner_name',
'started_at',
'completed_at',
'current_progress',
], $matches[1]);
}
public function test_keep_tasks_sort_all_headers_by_effective_display_values(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '排序公司甲',
'account_short' => '排序账户甲',
'account_type' => '公户',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '排序公司乙',
'account_short' => '排序账户乙',
'account_type' => '网商银行',
'company_plan' => '待注销',
'account_should_keep' => null,
],
]);
$connection->table('fin_account_type_overrides')->insert([
'account_short' => '排序账户甲',
'account_type' => '个人卡',
'updated_by' => 'Test User',
]);
$connection->table('fin_account_statuses')->insert([
'account_short' => '排序账户甲',
'status' => 'limited',
'updated_by' => 'Test User',
]);
$connection->table('fin_account_keep_todos')->insert([
[
'account_short' => '排序账户甲',
'handling_method' => 'online',
'owner_name' => '玉兔',
'current_progress' => 'completed',
],
[
'account_short' => '排序账户乙',
'handling_method' => 'offline',
'owner_name' => '山时',
'current_progress' => 'not_started',
],
]);
$connection->table('ops_fys_company_info')->insert([
[
'Id' => 21,
'businessLicenseName' => '排序公司甲',
'legalRepresentative' => '鲍法人',
],
[
'Id' => 22,
'businessLicenseName' => '排序公司乙',
'legalRepresentative' => '安法人',
],
]);
$sort = function (string $field, string $direction = 'asc'): array {
$response = $this->loginAs()->get($this->keepTasksUrl([
'keep_sort' => $field,
'keep_direction' => $direction,
]))->assertOk();
preg_match_all('/data-keep-account="([^"]+)"/u', $response->getContent(), $matches);
return $matches[1];
};
$this->assertSame(['排序账户乙', '排序账户甲'], $sort('legal_representative'));
$this->assertSame(['排序账户甲', '排序账户乙'], $sort('account_type'));
$this->assertSame(['排序账户乙', '排序账户甲'], $sort('account_status'));
$this->assertSame(['排序账户乙', '排序账户甲'], $sort('current_progress'));
$this->assertSame(['排序账户甲', '排序账户乙'], $sort('current_progress', 'desc'));
}
public function test_keep_tasks_header_filters_apply_with_typeahead_options(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '表头筛选公司甲',
'account_short' => '表头筛选甲',
'account_type' => '公户',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '表头筛选公司乙',
'account_short' => '表头筛选乙',
'account_type' => '个人卡',
'company_plan' => '待注销',
'account_should_keep' => null,
],
]);
$connection->table('fin_account_statuses')->insert([
'account_short' => '表头筛选甲',
'status' => 'limited',
'updated_by' => 'Test User',
]);
$connection->table('fin_account_keep_todos')->insert([
[
'account_short' => '表头筛选甲',
'handling_method' => 'online',
'owner_name' => '玉兔',
'started_at' => '2026-10-21 00:00:00',
'current_progress' => 'in_progress',
],
[
'account_short' => '表头筛选乙',
'handling_method' => 'offline',
'owner_name' => '山时',
'started_at' => '2026-10-22 00:00:00',
'current_progress' => 'not_started',
],
]);
$connection->table('ops_fys_company_info')->insert([
[
'Id' => 11,
'businessLicenseName' => '表头筛选公司甲',
'legalRepresentative' => '表头法人甲',
],
[
'Id' => 12,
'businessLicenseName' => '表头筛选公司乙',
'legalRepresentative' => '表头法人乙',
],
]);
$accountFiltered = $this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'account_short',
'keep_filter_value' => '表头筛选甲',
]))->assertOk()
->assertSee('data-keep-account="表头筛选甲"', false)
->assertDontSee('data-keep-account="表头筛选乙"', false)
->assertSee('ledger-header-cell', false)
->assertSee('is-filtered', false);
$accountFiltered->assertSee('<option value="表头筛选乙"', false);
$this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'legal_representative',
'keep_filter_value' => '表头法人乙',
]))->assertOk()
->assertSee('data-keep-account="表头筛选乙"', false)
->assertDontSee('data-keep-account="表头筛选甲"', false);
$this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'started_at',
'keep_filter_value' => '2026-10-21',
]))->assertOk()
->assertSee('data-keep-account="表头筛选甲"', false)
->assertDontSee('data-keep-account="表头筛选乙"', false);
}
public function test_keep_tasks_filters_sort_and_ajax_pagination_work(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '筛选公司甲',
'account_short' => '筛选账户甲',
'account_type' => '公户',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '筛选公司乙',
'account_short' => '筛选账户乙',
'account_type' => '个人卡',
'company_plan' => '待注销',
'account_should_keep' => null,
],
]);
$connection->table('fin_account_statuses')->insert([
'account_short' => '筛选账户甲',
'status' => 'limited',
'updated_by' => 'Test User',
]);
$connection->table('fin_account_keep_todos')->insert([
[
'account_short' => '筛选账户甲',
'handling_method' => 'online',
'owner_name' => '玉兔',
'started_at' => '2026-10-21 09:00:00',
'current_progress' => 'in_progress',
],
[
'account_short' => '筛选账户乙',
'handling_method' => 'offline',
'owner_name' => '山时',
'started_at' => '2026-10-22 09:00:00',
'current_progress' => 'not_started',
],
]);
$this->loginAs()->get($this->keepTasksUrl([
'keep_short' => '筛选账户甲',
'keep_type' => '公户',
'keep_status' => 'limited',
'keep_method' => 'online',
'keep_owner' => '玉兔',
'keep_progress' => 'in_progress',
]))
->assertOk()
->assertSee('data-keep-account="筛选账户甲"', false)
->assertDontSee('data-keep-account="筛选账户乙"', false)
->assertSee('value="limited" selected', false)
->assertSee('value="online" selected', false)
->assertSee('value="in_progress" selected', false);
$sortResponse = $this->loginAs()->get($this->keepTasksUrl([
'keep_sort' => 'started_at',
'keep_direction' => 'desc',
]))->assertOk()->assertSee('aria-sort="descending"', false);
preg_match_all('/data-keep-account="([^"]+)"/u', $sortResponse->getContent(), $sortedAccounts);
$this->assertSame(['筛选账户乙', '筛选账户甲'], $sortedAccounts[1]);
$pageRows = [];
for ($index = 1; $index <= 51; $index++) {
$pageRows[] = [
'company_name' => '分页公司',
'account_short' => 'keep-page-' . str_pad((string) $index, 2, '0', STR_PAD_LEFT),
'account_should_keep' => '否',
];
}
$connection->table('fin_accounts')->insert($pageRows);
$this->loginAs()->get($this->keepTasksUrl(['keep_short' => 'keep-page-']))
->assertOk()
->assertSee('1 / 2')
->assertSee('aria-label="下一页"', false)
->assertSee('data-keep-pagination', false);
$this->loginAs()->get($this->keepTasksUrl(['keep_short' => 'keep-page-', 'page' => 2]))
->assertOk()
->assertSee('2 / 2')
->assertSee('data-keep-account="keep-page-51"', false);
}
public function test_keep_todo_save_is_isolated_from_abnormal_tasks_and_permission_protected(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '待办保存公司甲',
'account_short' => '待办不保留账户',
'company_plan' => '保留',
'account_should_keep' => '否',
],
[
'company_name' => '待办保存公司乙',
'account_short' => '规划不保留待办账户',
'company_plan' => '待注销',
'account_should_keep' => null,
],
[
'company_name' => '待办保存公司丙',
'account_short' => '待办保留账户',
'company_plan' => '保留',
'account_should_keep' => '是',
],
]);
$payload = [
'account_short' => '待办不保留账户',
'handling_method' => 'online',
'owner_name' => '玉兔',
'started_at' => '2026-10-21T09:00',
'completed_at' => '2026-10-21T11:30',
'current_progress' => 'in_progress',
];
$this->loginAs()->post($this->keepTodoUrl(), $payload)->assertRedirect();
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '待办不保留账户',
'handling_method' => 'online',
'owner_name' => '玉兔',
'current_progress' => 'in_progress',
'updated_by' => 'Test User',
], 'business_mirror');
$this->assertDatabaseCount('fin_account_todos', 0, 'business_mirror');
$this->loginAs()->post($this->keepTodoUrl(), array_merge($payload, [
'current_progress' => 'completed',
]))->assertRedirect();
$this->assertDatabaseCount('fin_account_keep_todos', 1, 'business_mirror');
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '待办不保留账户',
'current_progress' => 'completed',
], 'business_mirror');
$this->loginAs()->post($this->keepTodoUrl(), [
'account_short' => '规划不保留待办账户',
'current_progress' => 'not_started',
])->assertRedirect();
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '规划不保留待办账户',
'current_progress' => 'not_started',
], 'business_mirror');
$this->withExceptionHandling()->loginAs()->post($this->keepTodoUrl(), [
'account_short' => '待办保留账户',
'current_progress' => 'not_started',
])->assertStatus(422);
$viewer = user_model_class()::create([
'name' => '不保留账户只读用户',
'email' => 'keep-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->keepTasksUrl())
->assertOk()
->assertSee('disabled', false)
->assertDontSee('title="保存待办"', false);
$this->withExceptionHandling()->loginAs($viewer)->post($this->keepTodoUrl(), $payload)
->assertForbidden();
}
}