From 6b21750fb0fc93608f6c46d27ae5df247559a35d Mon Sep 17 00:00:00 2001 From: shanshi <3316401569@qq.com> Date: Fri, 6 Nov 2026 17:56:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B3=95=E4=BA=BA=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E6=8C=89=E8=B4=A6=E6=88=B7=E6=95=B0=E9=87=8F=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Business/FinanceLedger.php | 106 ++++++++++++------ .../unretained-account-tasks.blade.php | 4 +- .../FinanceLedgerAccountKeepTodoTest.php | 47 ++++++++ 3 files changed, 121 insertions(+), 36 deletions(-) diff --git a/app/Http/Controllers/Business/FinanceLedger.php b/app/Http/Controllers/Business/FinanceLedger.php index 46abc8f6..9008fa42 100644 --- a/app/Http/Controllers/Business/FinanceLedger.php +++ b/app/Http/Controllers/Business/FinanceLedger.php @@ -8,6 +8,7 @@ use App\Utilities\FinanceLedgerAccountType; use App\Utilities\FinanceLedgerAccess; use App\Utilities\FinanceLedgerIndependentAccounts; use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Carbon; @@ -609,6 +610,9 @@ class FinanceLedger extends Controller ->reorder() ->select('accounts.company_name') ->pluck('accounts.company_name'); + $legalRepresentativeCounts = $legalRepresentativeCountSource->countBy( + static fn ($companyName): string => trim((string) ($legalRepresentatives[trim((string) $companyName)] ?? '')), + ); $summaryQuery = clone $query; $summary = [ 'count' => (clone $query)->count(), @@ -624,19 +628,8 @@ class FinanceLedger extends Controller $sortField = in_array($sortField, $headerFilterColumns, true) ? $sortField : ''; $sortDirection = strtolower((string) $request->query('keep_direction', 'asc')); $sortDirection = $sortDirection === 'desc' ? 'desc' : 'asc'; - if ($sortField !== '') { - if ($sortField === 'legal_representative') { - if ($connection->getSchemaBuilder()->hasTable('ops_fys_company_info')) { - $legalRepresentativeOrder = $connection->table('ops_fys_company_info') - ->select('legalRepresentative') - ->whereColumn('businessLicenseName', 'accounts.company_name') - ->orderByDesc('Id') - ->limit(1); - $query->orderBy($legalRepresentativeOrder, $sortDirection); - } else { - $query->orderBy('accounts.company_name', $sortDirection); - } - } elseif ($sortField === 'account_type') { + if ($sortField !== '' && $sortField !== 'legal_representative') { + if ($sortField === 'account_type') { $query->orderByRaw( 'CASE WHEN type_override_id IS NOT NULL THEN effective_account_type' . ' ELSE sort_account_type END ' . $sortDirection, @@ -674,27 +667,75 @@ class FinanceLedger extends Controller } } - $rows = $query + $hydrateRows = static function ($rows) use ($legalRepresentatives, $legalRepresentativeCounts): void { + $rows->transform(static function ($row) use ($legalRepresentatives, $legalRepresentativeCounts) { + $row->account_status = FinanceLedgerAccountStatus::normalize($row->effective_account_status ?? null); + if ($row->type_override_id !== null) { + $row->account_type = (string) ($row->effective_account_type ?? ''); + } + $row->todo_current_progress = $row->todo_current_progress ?: 'not_started'; + $row->legal_representative = $legalRepresentatives[trim((string) $row->company_name)] ?? null; + $row->legal_representative_count = (int) ($legalRepresentativeCounts[ + trim((string) $row->legal_representative) + ] ?? 0); + + return $row; + }); + }; + + $orderedQuery = $query ->orderBy('accounts.company_name') - ->orderBy('accounts.account_short') - ->paginate(50) - ->appends($filters) - ->appends(array_filter([ + ->orderBy('accounts.account_short'); + if ($sortField === 'legal_representative') { + $allRows = $orderedQuery->get(); + $hydrateRows($allRows); + $allRows = $allRows->sort(static function ($left, $right) use ($sortDirection): int { + $countOrder = (int) $left->legal_representative_count <=> (int) $right->legal_representative_count; + if ($sortDirection === 'desc') { + $countOrder *= -1; + } + if ($countOrder !== 0) { + return $countOrder; + } + + $nameOrder = strnatcasecmp( + trim((string) $left->legal_representative), + trim((string) $right->legal_representative), + ); + if ($nameOrder !== 0) { + return $nameOrder; + } + + $companyOrder = strnatcasecmp((string) $left->company_name, (string) $right->company_name); + + return $companyOrder !== 0 + ? $companyOrder + : strnatcasecmp((string) $left->account_short, (string) $right->account_short); + })->values(); + $lastPage = max(1, (int) ceil($allRows->count() / 50)); + $page = min($lastPage, max(1, (int) $request->query('page', 1))); + $rows = new LengthAwarePaginator( + $allRows->forPage($page, 50)->values(), + $allRows->count(), + 50, + $page, + ['path' => $request->url(), 'query' => $request->query()], + ); + $rows->appends($filters)->appends([ 'keep_sort' => $sortField, 'keep_direction' => $sortField !== '' ? $sortDirection : '', - ])) - ->withQueryString(); - - $rows->getCollection()->transform(static function ($row) use ($legalRepresentatives) { - $row->account_status = FinanceLedgerAccountStatus::normalize($row->effective_account_status ?? null); - if ($row->type_override_id !== null) { - $row->account_type = (string) ($row->effective_account_type ?? ''); - } - $row->todo_current_progress = $row->todo_current_progress ?: 'not_started'; - $row->legal_representative = $legalRepresentatives[trim((string) $row->company_name)] ?? null; - - return $row; - }); + ])->withQueryString(); + } else { + $rows = $orderedQuery + ->paginate(50) + ->appends($filters) + ->appends(array_filter([ + 'keep_sort' => $sortField, + 'keep_direction' => $sortField !== '' ? $sortDirection : '', + ])) + ->withQueryString(); + $hydrateRows($rows->getCollection()); + } $ownerOptions = self::UNRETAINED_OWNER_OPTIONS; $headerOptionValues = static function (iterable $values, bool $includeEmpty = false): array { @@ -714,9 +755,6 @@ class FinanceLedger extends Controller }; $hasEmpty = static fn (string $column): bool => $headerOptionRows ->contains(static fn ($row): bool => trim((string) ($row->{$column} ?? '')) === ''); - $legalRepresentativeCounts = $legalRepresentativeCountSource->countBy( - static fn ($companyName): string => trim((string) ($legalRepresentatives[trim((string) $companyName)] ?? '')), - ); $legalRepresentativeOptions = []; if (($legalRepresentativeCounts[''] ?? 0) > 0) { $legalRepresentativeOptions[] = '未填写(' . $legalRepresentativeCounts[''] . ')'; diff --git a/resources/views/business/pages/financial-ledger/unretained-account-tasks.blade.php b/resources/views/business/pages/financial-ledger/unretained-account-tasks.blade.php index f4209e91..0ea0f0df 100644 --- a/resources/views/business/pages/financial-ledger/unretained-account-tasks.blade.php +++ b/resources/views/business/pages/financial-ledger/unretained-account-tasks.blade.php @@ -5,7 +5,7 @@ $keepHeaderColumns = [ ['column' => 'company_name', 'label' => '公司名称', 'filterType' => 'searchable-enum'], ['column' => 'account_short', 'label' => '账户简称', 'filterType' => 'searchable-enum'], - ['column' => 'legal_representative', 'label' => '法人名称', 'filterType' => 'searchable-enum'], + ['column' => 'legal_representative', 'label' => '法人名称 / 数量', 'filterType' => 'searchable-enum'], ['column' => 'account_type', 'label' => '账户类型', 'filterType' => 'searchable-enum'], ['column' => 'account_status', 'label' => '账户状态', 'filterType' => 'searchable-enum'], ['column' => 'account_no', 'label' => '账号', 'filterType' => 'searchable-enum'], @@ -333,7 +333,7 @@ {{ $row->company_name ?: '未填写' }} {{ $row->account_short ?: '未填写' }} - {{ $row->legal_representative ?: '未填写' }} + {{ $row->legal_representative ?: '未填写' }}({{ $row->legal_representative_count }}) {{ $row->account_type ?: '未填写' }} {{ $accountStatusOptions[$row->account_status] ?? '正常' }} {{ $row->account_no ?: '未填写' }} diff --git a/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php b/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php index 6b3da586..8711ddad 100644 --- a/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php +++ b/tests/Feature/Business/FinanceLedgerAccountKeepTodoTest.php @@ -391,6 +391,53 @@ class FinanceLedgerAccountKeepTodoTest extends FeatureTestCase ->assertDontSee('data-keep-account="法人计数甲二"', false); } + public function test_legal_representative_sort_uses_account_group_counts(): void + { + $connection = DB::connection('business_mirror'); + $connection->table('fin_accounts')->insert([ + ['company_name' => '法人排序公司甲', 'account_short' => '法人排序甲一', 'account_should_keep' => '否'], + ['company_name' => '法人排序公司乙', 'account_short' => '法人排序甲二', 'account_should_keep' => '否'], + ['company_name' => '法人排序公司丙', 'account_short' => '法人排序甲三', 'account_should_keep' => '否'], + ['company_name' => '法人排序公司丁', 'account_short' => '法人排序乙一', 'account_should_keep' => '否'], + ['company_name' => '法人排序公司戊', 'account_short' => '法人排序丙一', 'account_should_keep' => '否'], + ]); + $connection->table('ops_fys_company_info')->insert([ + ['Id' => 41, 'businessLicenseName' => '法人排序公司甲', 'legalRepresentative' => '赵法人'], + ['Id' => 42, 'businessLicenseName' => '法人排序公司乙', 'legalRepresentative' => '赵法人'], + ['Id' => 43, 'businessLicenseName' => '法人排序公司丙', 'legalRepresentative' => '赵法人'], + ['Id' => 44, 'businessLicenseName' => '法人排序公司丁', 'legalRepresentative' => '李法人'], + ['Id' => 45, 'businessLicenseName' => '法人排序公司戊', 'legalRepresentative' => '王法人'], + ]); + + $rows = function (string $direction): array { + $content = $this->loginAs()->get($this->keepTasksUrl([ + 'keep_sort' => 'legal_representative', + 'keep_direction' => $direction, + ]))->assertOk()->getContent(); + preg_match_all('/(.*?)<\/tr>/su', $content, $matches, PREG_SET_ORDER); + + return array_map(static function (array $match): array { + preg_match('/data-keep-legal-representative-count="(\d+)"/u', $match[2], $count); + + return [(string) $match[1], (int) ($count[1] ?? 0)]; + }, $matches); + }; + + $descending = $rows('desc'); + $this->assertSame([3, 3, 3], array_column(array_slice($descending, 0, 3), 1)); + $this->assertSame([1, 1], array_column(array_slice($descending, 3, 2), 1)); + + $ascending = $rows('asc'); + $this->assertSame([1, 1], array_column(array_slice($ascending, 0, 2), 1)); + $this->assertSame([3, 3, 3], array_column(array_slice($ascending, 2, 3), 1)); + $this->loginAs()->get($this->keepTasksUrl([ + 'keep_sort' => 'legal_representative', + 'keep_direction' => 'desc', + ]))->assertOk() + ->assertSee('data-keep-legal-representative-count="3"', false) + ->assertSee('赵法人(3)'); + } + public function test_keep_tasks_header_filters_apply_with_typeahead_options(): void { $connection = DB::connection('business_mirror'); -- 2.47.2