diff --git a/app/Business/UpstreamBusinessInformation.php b/app/Business/UpstreamBusinessInformation.php index c52eb352..3cdf2f74 100644 --- a/app/Business/UpstreamBusinessInformation.php +++ b/app/Business/UpstreamBusinessInformation.php @@ -296,6 +296,12 @@ SQL); } unset($row); + // 所有主体该月收入均为空时,该月份列在页面上默认隐藏;「筛选字段」中仍保留勾选项,可手动选出来。 + request()->attributes->set( + 'subject-operating-report.default_hidden_columns', + $this->subjectReportColumnsWithoutData($rows), + ); + $rows = array_values(array_filter($rows, fn (array $row): bool => $this->subjectMatchesFilters($row, $filters))); $this->sortSubjectRows($rows, $filters); $offset = ($page - 1) * $perPage; @@ -304,6 +310,34 @@ SQL); return ['data' => $pageRows, 'total' => count($rows)]; } + /** + * 没有数据的月份列:全部主体该月收入都为空(null/空字符串)时才视为没有数据。 + * 金额为 0 的月份代表当月有账单,不隐藏。 + * + * @param array> $rows + * @return array + */ + private function subjectReportColumnsWithoutData(array $rows): array + { + $columns = []; + + for ($month = 1; $month <= 12; $month++) { + $key = sprintf('income_%02d', $month); + foreach ($rows as $row) { + $value = $row[$key] ?? null; + if ($value === null || $value === '') { + continue; + } + + continue 2; + } + + $columns[] = $key; + } + + return $columns; + } + /** @param array $row @param array $filters */ private function subjectMatchesFilters(array $row, array $filters): bool { diff --git a/resources/views/business/pages/partials/directory-table.blade.php b/resources/views/business/pages/partials/directory-table.blade.php index 8c35e150..97b46b10 100644 --- a/resources/views/business/pages/partials/directory-table.blade.php +++ b/resources/views/business/pages/partials/directory-table.blade.php @@ -1125,7 +1125,15 @@ array_filter(explode(',', (string) request('columns', ''))), $directoryColumnNames, )); - $visibleDirectoryColumns = request()->has('columns') ? $requestedDirectoryColumns : $directoryColumnNames; + // 主体维度经营报表:没有数据的月份列默认隐藏,仍保留在「筛选字段」中供手动勾选显示。 + $directoryDefaultColumns = $directoryColumnNames; + if ($directoryPageKey === 'company-management/subject-operating-report') { + $directoryDefaultColumns = array_values(array_diff( + $directoryColumnNames, + (array) request()->attributes->get('subject-operating-report.default_hidden_columns', []), + )); + } + $visibleDirectoryColumns = request()->has('columns') ? $requestedDirectoryColumns : $directoryDefaultColumns; $directoryQueryFormId = 'directory-query-form-' . ($page['slug'] ?? 'page'); @endphp @if ($directoryUsesFieldQuery) @@ -1251,7 +1259,7 @@ @endif
- 筛选字段 + 筛选字段{{ count($visibleDirectoryColumns) < count($directoryFields) ? ' ' . count($visibleDirectoryColumns) . '/' . count($directoryFields) : '' }}
勾选需要显示的表头,可重复调整。 @foreach ($directoryFields as $field) diff --git a/resources/views/business/pages/partials/finance-table-interactions.blade.php b/resources/views/business/pages/partials/finance-table-interactions.blade.php index 580b6e21..d9ef417f 100644 --- a/resources/views/business/pages/partials/finance-table-interactions.blade.php +++ b/resources/views/business/pages/partials/finance-table-interactions.blade.php @@ -530,8 +530,10 @@ label.draggable = true; const key = header.dataset.financeColumnKey; const fixed = Boolean(header.dataset.financeFixed); + // 服务端按业务规则默认隐藏的列(如无数据的月份)保持取消勾选,可在这里手动选出来。 + const visible = fixed || !header.classList.contains('is-column-hidden'); label.dataset.financeOptionKey = key; - label.innerHTML = `${header.textContent.trim() || '未命名字段'}`; + label.innerHTML = `${header.textContent.trim() || '未命名字段'}`; panel.appendChild(label); }); const actions = document.createElement('div'); diff --git a/tests/Feature/Business/PrototypePageTest.php b/tests/Feature/Business/PrototypePageTest.php index 4e2abd1d..7c250929 100644 --- a/tests/Feature/Business/PrototypePageTest.php +++ b/tests/Feature/Business/PrototypePageTest.php @@ -142,4 +142,60 @@ class PrototypePageTest extends FeatureTestCase ->assertOk() ->assertSeeTextInOrder(['最近12个月累计收入', '1月收入', '12月收入']); } + + public function testSubjectOperatingReportHidesMonthsWithoutDataAndKeepsThemSelectable(): void + { + $service = Mockery::mock(UpstreamBusinessInformation::class); + $service->shouldReceive('paginate')->with('company-management', 'subject-operating-report', 50) + ->andReturnUsing(function (): LengthAwarePaginator { + request()->attributes->set( + 'subject-operating-report.default_hidden_columns', + ['income_01', 'income_02'], + ); + + return new LengthAwarePaginator([(object) [ + 'entity' => '测试主体', + 'city' => '常州市', + 'company_type' => '有限公司', + 'rolling_12m_income' => 100, + 'income_01' => null, + 'income_02' => null, + 'income_03' => 50, + 'can_change_entity' => null, + 'plan' => null, + 'new_entity' => null, + ]], 1, 50, 1, ['path' => '/']); + }); + $this->app->instance(UpstreamBusinessInformation::class, $service); + + $response = $this->loginAs()->get(route('business.pages.show', [ + 'section' => 'company-management', + 'page' => 'subject-operating-report', + ])); + + $response->assertOk(); + $html = $response->getContent(); + + // 没有数据的月份默认隐藏,但表头和「筛选字段」勾选项仍在页面中。 + $this->assertMatchesRegularExpression('/data-column-key="income_01"[^>]*is-column-hidden/', $html); + $this->assertMatchesRegularExpression('/data-column-key="income_02"[^>]*is-column-hidden/', $html); + $this->assertDoesNotMatchRegularExpression('/data-column-key="income_03"[^>]*is-column-hidden/', $html); + $this->assertStringContainsString('value="income_01" data-directory-query-column', $html); + $this->assertStringNotContainsString('value="income_01" data-directory-query-column checked', $html); + $this->assertStringContainsString('value="income_03" data-directory-query-column checked', $html); + $this->assertStringContainsString('筛选字段 17/19', $html); + + // 手动勾选(columns 参数)后,对应月份列恢复显示。 + $restored = $this->get(route('business.pages.show', [ + 'section' => 'company-management', + 'page' => 'subject-operating-report', + 'columns' => 'city,entity,company_type,rolling_12m_income,income_01,income_03', + ])); + + $restored->assertOk(); + $restoredHtml = $restored->getContent(); + $this->assertDoesNotMatchRegularExpression('/data-column-key="income_01"[^>]*is-column-hidden/', $restoredHtml); + $this->assertDoesNotMatchRegularExpression('/data-column-key="income_03"[^>]*is-column-hidden/', $restoredHtml); + $this->assertMatchesRegularExpression('/data-column-key="income_04"[^>]*is-column-hidden/', $restoredHtml); + } } diff --git a/tests/Unit/EcommerceFinance/UpstreamBusinessInformationTest.php b/tests/Unit/EcommerceFinance/UpstreamBusinessInformationTest.php index 51297f25..dc98b0fa 100644 --- a/tests/Unit/EcommerceFinance/UpstreamBusinessInformationTest.php +++ b/tests/Unit/EcommerceFinance/UpstreamBusinessInformationTest.php @@ -7,6 +7,8 @@ use Closure; use Mockery; use Modules\EcommerceFinance\Repositories\BusinessInformationMirrorRepository; use Modules\EcommerceFinance\Repositories\InstallationDetailMirrorRepository; +use Modules\EcommerceFinance\Repositories\StoreAccountMirrorRepository; +use Modules\EcommerceFinance\Repositories\StoreReportRepository; use Modules\EcommerceFinance\Services\UpstreamQueryCache; use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory; use Tests\TestCase; @@ -123,6 +125,54 @@ class UpstreamBusinessInformationTest extends TestCase ->paginate('store-management', 'basic-information'); } + public function test_subject_operating_report_hides_income_months_without_data_by_default(): void + { + $mirror = Mockery::mock(BusinessInformationMirrorRepository::class); + $mirror->shouldReceive('ready')->with('store-information.read')->andReturnTrue(); + $mirror->shouldReceive('allStores')->andReturn([['erp_store_name' => '测试店铺']]); + $mirror->shouldReceive('ready')->with('company-information.read')->andReturnTrue(); + $mirror->shouldReceive('companiesByNames')->andReturn([]); + + $storeReports = Mockery::mock(StoreReportRepository::class); + $storeReports->shouldReceive('rows')->andReturn([[ + 'entity' => '测试主体', + 'company_type' => '店铺-普通公司', + 'rolling_12m_income' => 188.5, + 'income_01' => null, + 'income_02' => 88.5, + 'income_03' => 0.0, + 'income_04' => null, + 'income_05' => null, + 'income_06' => null, + 'income_07' => null, + 'income_08' => null, + 'income_09' => null, + 'income_10' => null, + 'income_11' => null, + 'income_12' => null, + ]]); + $storeAccounts = Mockery::mock(StoreAccountMirrorRepository::class); + $storeAccounts->shouldReceive('ownershipByStoreName')->andReturn([]); + + $information = new UpstreamBusinessInformation( + Mockery::mock(UpstreamQueryCacheFactory::class), + $mirror, + Mockery::mock(InstallationDetailMirrorRepository::class), + $storeReports, + $storeAccounts, + ); + + $paginator = $information->paginate('company-management', 'subject-operating-report', 50); + + // income_02 有金额、income_03 为 0 都算有数据,其余空月份默认隐藏。 + $this->assertSame( + ['income_01', 'income_04', 'income_05', 'income_06', 'income_07', 'income_08', 'income_09', 'income_10', 'income_11', 'income_12'], + $this->app['request']->attributes->get('subject-operating-report.default_hidden_columns'), + ); + $this->assertSame(1, $paginator->total()); + $this->assertSame('测试主体', $paginator->items()[0]->entity); + } + public static function businessInformationPages(): array { return [