diff --git a/app/Business/UpstreamBusinessInformation.php b/app/Business/UpstreamBusinessInformation.php index 3cdf2f74..9c17dcd1 100644 --- a/app/Business/UpstreamBusinessInformation.php +++ b/app/Business/UpstreamBusinessInformation.php @@ -152,6 +152,13 @@ SQL); { $filters = StoreReportRepository::requestFilters(); $rows = $this->storeReportRows($filters); + + // 所有店铺该月都没有业绩的月份列默认隐藏;「筛选字段」中仍保留勾选项,可手动选出来。 + request()->attributes->set( + 'store-report.default_hidden_columns', + $this->columnsWithoutData($rows, 'sales_', zeroCountsAsData: false), + ); + $offset = ($page - 1) * $perPage; return ['data' => array_slice($rows, $offset, $perPage), 'total' => count($rows)]; @@ -299,7 +306,7 @@ SQL); // 所有主体该月收入均为空时,该月份列在页面上默认隐藏;「筛选字段」中仍保留勾选项,可手动选出来。 request()->attributes->set( 'subject-operating-report.default_hidden_columns', - $this->subjectReportColumnsWithoutData($rows), + $this->columnsWithoutData($rows, 'income_', zeroCountsAsData: true), ); $rows = array_values(array_filter($rows, fn (array $row): bool => $this->subjectMatchesFilters($row, $filters))); @@ -311,23 +318,28 @@ SQL); } /** - * 没有数据的月份列:全部主体该月收入都为空(null/空字符串)时才视为没有数据。 - * 金额为 0 的月份代表当月有账单,不隐藏。 + * 没有数据的月份列:所有行该月都没有数值时返回该列名,页面据此默认隐藏。 + * + * 收入(income_)以 null/空字符串表示无账单,金额 0 也算有账单; + * 销量(sales_)没有空值标记,数值 0 视为当月没有业绩。 * * @param array> $rows * @return array */ - private function subjectReportColumnsWithoutData(array $rows): array + private function columnsWithoutData(array $rows, string $prefix, bool $zeroCountsAsData): array { $columns = []; for ($month = 1; $month <= 12; $month++) { - $key = sprintf('income_%02d', $month); + $key = sprintf('%s%02d', $prefix, $month); foreach ($rows as $row) { $value = $row[$key] ?? null; if ($value === null || $value === '') { continue; } + if (! $zeroCountsAsData && (float) $value === 0.0) { + continue; + } continue 2; } diff --git a/resources/views/business/pages/partials/directory-table.blade.php b/resources/views/business/pages/partials/directory-table.blade.php index 97b46b10..c3485026 100644 --- a/resources/views/business/pages/partials/directory-table.blade.php +++ b/resources/views/business/pages/partials/directory-table.blade.php @@ -1125,12 +1125,17 @@ array_filter(explode(',', (string) request('columns', ''))), $directoryColumnNames, )); - // 主体维度经营报表:没有数据的月份列默认隐藏,仍保留在「筛选字段」中供手动勾选显示。 + // 主体维度经营报表、店铺维度报表:没有数据的月份列默认隐藏,仍保留在「筛选字段」中供手动勾选显示。 $directoryDefaultColumns = $directoryColumnNames; - if ($directoryPageKey === 'company-management/subject-operating-report') { + $directoryDefaultHiddenColumns = match ($directoryPageKey) { + 'company-management/subject-operating-report' => request()->attributes->get('subject-operating-report.default_hidden_columns', []), + 'store-management/store-report' => request()->attributes->get('store-report.default_hidden_columns', []), + default => [], + }; + if ((array) $directoryDefaultHiddenColumns !== []) { $directoryDefaultColumns = array_values(array_diff( $directoryColumnNames, - (array) request()->attributes->get('subject-operating-report.default_hidden_columns', []), + (array) $directoryDefaultHiddenColumns, )); } $visibleDirectoryColumns = request()->has('columns') ? $requestedDirectoryColumns : $directoryDefaultColumns; diff --git a/tests/Feature/Business/PrototypePageTest.php b/tests/Feature/Business/PrototypePageTest.php index 7c250929..89f0be0d 100644 --- a/tests/Feature/Business/PrototypePageTest.php +++ b/tests/Feature/Business/PrototypePageTest.php @@ -177,9 +177,9 @@ class PrototypePageTest extends FeatureTestCase $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->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); @@ -194,8 +194,64 @@ class PrototypePageTest extends FeatureTestCase $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); + $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); + } + + public function testStoreReportHidesSalesMonthsWithoutDataAndKeepsThemSelectable(): void + { + $service = Mockery::mock(UpstreamBusinessInformation::class); + $service->shouldReceive('paginate')->with('store-management', 'store-report', 50) + ->andReturnUsing(function (): LengthAwarePaginator { + request()->attributes->set( + 'store-report.default_hidden_columns', + ['sales_01', 'sales_08'], + ); + + return new LengthAwarePaginator([(object) [ + 'erp_store_name' => 'ERP测试店铺', + 'store_name' => '前台测试店铺', + 'store_id' => 'SHOP-001', + 'status' => '开店中', + 'entity' => '测试主体', + 'company_type' => '普通公司', + 'period_sales' => 100, + 'sales_01' => 0, + 'sales_07' => 100, + 'sales_08' => 0, + ]], 1, 50, 1, ['path' => '/']); + }); + $this->app->instance(UpstreamBusinessInformation::class, $service); + + $response = $this->loginAs()->get(route('business.pages.show', [ + 'section' => 'store-management', + 'page' => 'store-report', + ])); + + $response->assertOk(); + $html = $response->getContent(); + + // 没有业绩的月份默认隐藏,但表头和「筛选字段」勾选项仍在页面中。 + $this->assertMatchesRegularExpression('/]*data-column-key="sales_01"[^>]*is-column-hidden/', $html); + $this->assertMatchesRegularExpression('/]*data-column-key="sales_08"[^>]*is-column-hidden/', $html); + $this->assertDoesNotMatchRegularExpression('/]*data-column-key="sales_07"[^>]*is-column-hidden/', $html); + $this->assertStringContainsString('value="sales_01" data-directory-query-column', $html); + $this->assertStringNotContainsString('value="sales_01" data-directory-query-column checked', $html); + $this->assertStringContainsString('value="sales_07" data-directory-query-column checked', $html); + $this->assertStringContainsString('筛选字段 24/26', $html); + + // 手动勾选(columns 参数)后,对应月份列恢复显示。 + $restored = $this->get(route('business.pages.show', [ + 'section' => 'store-management', + 'page' => 'store-report', + 'columns' => 'erp_store_name,store_name,store_id,status,entity,period_sales,sales_01,sales_07', + ])); + + $restored->assertOk(); + $restoredHtml = $restored->getContent(); + $this->assertDoesNotMatchRegularExpression('/]*data-column-key="sales_01"[^>]*is-column-hidden/', $restoredHtml); + $this->assertDoesNotMatchRegularExpression('/]*data-column-key="sales_07"[^>]*is-column-hidden/', $restoredHtml); + $this->assertMatchesRegularExpression('/]*data-column-key="sales_08"[^>]*is-column-hidden/', $restoredHtml); } } diff --git a/tests/Unit/EcommerceFinance/StoreReportRepositoryTest.php b/tests/Unit/EcommerceFinance/StoreReportRepositoryTest.php index 4e88c374..2be1f47b 100644 --- a/tests/Unit/EcommerceFinance/StoreReportRepositoryTest.php +++ b/tests/Unit/EcommerceFinance/StoreReportRepositoryTest.php @@ -238,6 +238,36 @@ class StoreReportRepositoryTest extends TestCase $this->assertSame(60.0, $paginator->items()[0]->period_sales); } + public function test_store_report_hides_sales_months_without_data_by_default(): void + { + Carbon::setTestNow('2026-09-15 10:00:00'); + $this->insertBill('店铺A', '2026-07-05', 60); + $mirror = Mockery::mock(BusinessInformationMirrorRepository::class); + $mirror->shouldReceive('ready')->with('store-information.read')->andReturnTrue(); + $mirror->shouldReceive('allStores')->once()->andReturn([[ + 'erp_store_name' => '店铺A', 'company_name' => '主体甲', 'store_status' => '0', + ]]); + $accounts = Mockery::mock(StoreAccountMirrorRepository::class); + $accounts->shouldReceive('ownershipByStoreName')->andReturn([]); + $service = new UpstreamBusinessInformation( + Mockery::mock(UpstreamQueryCacheFactory::class), + $mirror, + Mockery::mock(InstallationDetailMirrorRepository::class), + $this->repository, + $accounts, + ); + + $paginator = $service->paginate('store-management', 'store-report', 50); + + // 只有 7 月有业绩,其余月份列默认隐藏。 + $this->assertSame( + ['sales_01', 'sales_02', 'sales_03', 'sales_04', 'sales_05', 'sales_06', 'sales_08', 'sales_09', 'sales_10', 'sales_11', 'sales_12'], + $this->app['request']->attributes->get('store-report.default_hidden_columns'), + ); + $this->assertSame(1, $paginator->total()); + $this->assertSame(60.0, (float) $paginator->items()[0]->sales_07); + } + public function test_subject_operating_report_aggregates_income_by_entity(): void { Carbon::setTestNow('2026-09-01 10:00:00');