CaiWuXiTong/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php
shanshi 1ba637c983 feat: 支持财务待办批量填写 (#914)
为账户异常待办和不保留账户处理待办增加批量填写责任人员、开始时间、完成时间。
2026-11-05 21:49:39 +08:00

746 lines
31 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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,
]);
}
private function batchKeepTodoUrl(): string
{
return route('business.financial-ledger.accounts.keep-todo.batch', [
'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('data-keep-batch-form', false)
->assertSee('name="account_shorts[]"', false)
->assertSee('全选本页账户')
->assertSee('批量责任人员')
->assertSee('批量开始时间')
->assertSee('批量完成时间')
->assertSee('应用到已选账户')
->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_legal_representative_filter_options_show_account_counts(): 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' => '否',
],
[
'company_name' => '法人计数公司乙',
'account_short' => '法人计数乙',
'account_type' => '网商银行',
'company_plan' => '保留',
'account_should_keep' => '否',
],
]);
$connection->table('ops_fys_company_info')->insert([
'Id' => 31,
'businessLicenseName' => '法人计数公司甲',
'legalRepresentative' => '王法人',
]);
$optionsResponse = $this->loginAs()->get($this->keepTasksUrl())->assertOk();
preg_match(
'/<datalist id="ledger-header-options-keep-legal_representative">(.*?)<\/datalist>/su',
$optionsResponse->getContent(),
$legalRepresentativeOptions,
);
$legalRepresentativeOptions = $legalRepresentativeOptions[1] ?? '';
$this->assertStringContainsString('value="王法人(2)"', $legalRepresentativeOptions);
$this->assertStringContainsString('value="未填写(1)"', $legalRepresentativeOptions);
$this->assertStringNotContainsString('__empty__', $legalRepresentativeOptions);
$typeFilteredResponse = $this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'account_type',
'keep_filter_value' => '网商银行',
]))->assertOk();
preg_match(
'/<datalist id="ledger-header-options-keep-legal_representative">(.*?)<\/datalist>/su',
$typeFilteredResponse->getContent(),
$typeFilteredLegalOptions,
);
$typeFilteredLegalOptions = $typeFilteredLegalOptions[1] ?? '';
$this->assertStringContainsString('value="王法人(1)"', $typeFilteredLegalOptions);
$this->assertStringContainsString('value="未填写(1)"', $typeFilteredLegalOptions);
$this->assertStringNotContainsString('王法人(2)', $typeFilteredLegalOptions);
$this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'legal_representative',
'keep_filter_value' => '王法人(2)',
]))->assertOk()
->assertSee('data-keep-account="法人计数甲一"', false)
->assertSee('data-keep-account="法人计数甲二"', false)
->assertDontSee('data-keep-account="法人计数乙"', false);
$this->loginAs()->get($this->keepTasksUrl([
'keep_filter_column' => 'legal_representative',
'keep_filter_value' => '未填写(1)',
]))->assertOk()
->assertSee('data-keep-account="法人计数乙"', false)
->assertDontSee('data-keep-account="法人计数甲一"', false)
->assertDontSee('data-keep-account="法人计数甲二"', false);
}
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('<form id="keep-batch-form"', false)
->assertDontSee('title="保存待办"', false);
$this->withExceptionHandling()->loginAs($viewer)->post($this->keepTodoUrl(), $payload)
->assertForbidden();
$this->withExceptionHandling()->loginAs($viewer)->post($this->batchKeepTodoUrl(), [
'account_shorts' => ['待办不保留账户'],
'owner_name' => '玉兔',
])->assertForbidden();
}
public function test_batch_keep_todo_update_changes_selected_accounts_and_preserves_other_fields(): 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' => '是',
],
]);
$connection->table('fin_account_keep_todos')->insert([
'account_short' => '批量不保留账户甲',
'handling_method' => 'offline',
'owner_name' => '山时',
'started_at' => '2026-10-01 00:00:00',
'completed_at' => null,
'current_progress' => 'in_progress',
'updated_by' => 'Test User',
]);
$this->loginAs()->post($this->batchKeepTodoUrl(), [
'account_shorts' => ['批量不保留账户甲', '批量不保留账户乙'],
'owner_name' => '玉兔',
'started_at' => '2026-11-01',
'completed_at' => '2026-11-02',
])->assertRedirect()->assertSessionHas('success', '已为 2 个不保留账户批量填写待办信息');
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '批量不保留账户甲',
'handling_method' => 'offline',
'owner_name' => '玉兔',
'started_at' => '2026-11-01 00:00:00',
'completed_at' => '2026-11-02 00:00:00',
'current_progress' => 'in_progress',
'updated_by' => 'Test User',
], 'business_mirror');
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '批量不保留账户乙',
'owner_name' => '玉兔',
'started_at' => '2026-11-01 00:00:00',
'completed_at' => '2026-11-02 00:00:00',
'updated_by' => 'Test User',
], 'business_mirror');
$this->assertDatabaseMissing('fin_account_keep_todos', [
'account_short' => '批量保留账户',
], 'business_mirror');
}
public function test_batch_keep_todo_update_keeps_existing_dates_when_only_owner_is_filled(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '批量保留字段公司',
'account_short' => '批量保留字段不保留账户',
'company_plan' => '待注销',
'account_should_keep' => null,
]);
$connection->table('fin_account_keep_todos')->insert([
'account_short' => '批量保留字段不保留账户',
'handling_method' => 'online',
'owner_name' => '山时',
'started_at' => '2026-10-03 00:00:00',
'completed_at' => '2026-10-04 00:00:00',
'current_progress' => 'completed',
'updated_by' => 'Test User',
]);
$this->loginAs()->post($this->batchKeepTodoUrl(), [
'account_shorts' => ['批量保留字段不保留账户'],
'owner_name' => '玉兔',
'started_at' => '',
'completed_at' => '',
])->assertRedirect();
$this->assertDatabaseHas('fin_account_keep_todos', [
'account_short' => '批量保留字段不保留账户',
'handling_method' => 'online',
'owner_name' => '玉兔',
'started_at' => '2026-10-03 00:00:00',
'completed_at' => '2026-10-04 00:00:00',
'current_progress' => 'completed',
], 'business_mirror');
}
}