user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); } public function testInstallationDetailsRendersWorkbookHeadersAndImportedRows(): void { $mirror = Mockery::mock(InstallationDetailMirrorRepository::class); $mirror->shouldReceive('paginate')->once()->andReturn([ 'ready' => true, 'total' => 1, 'data' => [array_replace(array_fill_keys(array_keys(InstallationDetail::columnLabels()), null), [ 'id' => 1, 'installation_no' => 'INSTALL-TEST-001', 'installation_company' => '测试安装公司', 'store_name' => '测试店铺', 'requested_amount' => '120.50', 'settled_amount' => '100.00', 'item_quantity' => 2, 'related_guid' => 'guid-install-test', ])], ]); $mirror->shouldReceive('summary')->once()->andReturn(['rows' => 1, 'requested' => 120.50, 'settled' => 100.00, 'paid' => 0.0]); $this->app->instance(InstallationDetailMirrorRepository::class, $mirror); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'financial-operations', 'page' => 'installation-details', ])) ->assertOk() ->assertSeeText('安装明细 · 数据列表') ->assertSeeText('安装单号') ->assertSeeText('申请总金额') ->assertSeeText('商品名称(1)') ->assertSeeText('INSTALL-TEST-001') ->assertSeeText('测试安装公司') ->assertSeeText('120.50') ->assertSeeText('共 1 条'); } public function testInstallationDetailsEndpointReturnsChildItemsForMainRecord(): void { $mirror = Mockery::mock(InstallationDetailMirrorRepository::class); $mirror->shouldReceive('details')->once()->with('guid-install-test')->andReturn([ 'ready' => true, 'data' => [[ 'transaction_no' => 'INSTALL-TEST-001', 'product_id' => '10035070813217', 'product_name' => '测试商品', 'detail_requested_amount' => '53.59', 'detail_requested_quantity' => 1, ]], ]); $this->app->instance(InstallationDetailMirrorRepository::class, $mirror); $this->loginAs() ->get(route('business.installation-details.details', ['guid' => 'guid-install-test'])) ->assertOk() ->assertJsonPath('data.0.transaction_no', 'INSTALL-TEST-001') ->assertJsonPath('data.0.product_name', '测试商品'); } public function testCompanyInformationUsesWorkbookHeadersAndReadOnlyUpstreamRows(): void { $this->mockUpstreamPage((object) [ 'id' => 1045, 'unified_social_credit_code' => '91320404MADJRTTA0U', 'company_name' => '测试上游公司', 'subscribed_capital' => '10.00', 'company_type' => '店铺-普通公司', 'establishment_date' => '2024-04-28', 'legal_representative' => '测试法人', 'registered_address' => '测试地址', 'business_scope' => '测试经营范围', 'basic_deposit_account_number' => 'J0001', 'company_status' => '正常', 'financial_officer' => '测试财务', 'supervisor' => '', 'taxpayer_category' => '小规模纳税人', 'shareholders' => null, ]); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'company-management', 'page' => 'basic-information', ])) ->assertOk() ->assertSeeText('公司基础信息') ->assertSeeText('认缴注册资本(万元)') ->assertSeeText('基本存款账户信息') ->assertSeeText('纳税人类型') ->assertSeeText('已配置') ->assertSeeText('测试上游公司') ->assertSeeText('既有只读上游') ->assertDontSeeText('导入') ->assertDontSeeText('新增'); } public function testStoreInformationKeepsTwelveHeadersAndShowsReadOnlyUpstreamRows(): void { $this->mockUpstreamPage((object) [ 'id' => 2047, 'opened_at' => '2026-07-18', 'erp_store_name' => '测试上游店铺', 'unified_social_credit_code' => '91320412MA216EYU49', 'company_name' => '测试关联公司', 'trademark_number' => '35774784', 'trademark_name' => null, 'platform' => '天猫', 'profit_center' => '运营中心A', 'store_manager' => '测试负责人', 'store_status' => '正常', 'store_type' => '旗舰店', 'channel_mode' => '直销', ]); $response = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'store-management', 'page' => 'basic-information', ])); $response ->assertOk() ->assertSeeText('店铺名称') ->assertSeeText('店铺主体') ->assertSeeText('业务部门') ->assertSeeText('测试上游店铺') ->assertSeeText('测试关联公司') ->assertSeeText('既有只读上游') ->assertDontSeeText('导入') ->assertDontSeeText('新增'); preg_match_all('/)/', $response->getContent(), $headers); $this->assertCount(14, $headers[0]); } public function testTrademarkInformationShowsReadOnlyUpstreamRows(): void { $this->mockUpstreamPage((object) [ 'id' => 600, 'trademark_name' => '测试上游商标', 'trademark_type' => '20', 'trademark_status' => '有效', 'registration_date' => '2025-12-21', 'expiry_date' => '2035-12-20', 'notes' => null, ]); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'trademark-management', 'page' => 'information', ])) ->assertOk() ->assertSeeText('商标名称') ->assertSeeText('注册时间') ->assertSeeText('测试上游商标') ->assertSeeText('既有只读上游') ->assertDontSeeText('导入') ->assertDontSeeText('新增'); } public function testWorkbookDirectoryReplacesManagementSubmenus(): void { $navigation = BusinessNavigation::all(); $sections = collect($navigation)->keyBy('slug'); $this->assertSame( ['subject-operating-report', 'basic-information', 'personnel-inventory', 'company-progress'], array_column($sections['company-management']['pages'], 'slug') ); $this->assertSame( ['information', 'trademark-progress'], array_column($sections['trademark-management']['pages'], 'slug') ); $this->assertSame( ['store-report', 'basic-information', 'change-progress'], array_column($sections['store-management']['pages'], 'slug') ); $this->assertSame( ['todos', 'warnings'], array_column($sections['other']['pages'], 'slug') ); foreach (['company-management', 'trademark-management', 'store-management', 'other'] as $sectionSlug) { foreach ($sections[$sectionSlug]['pages'] as $page) { $this->assertArrayHasKey('directory', $page, "{$sectionSlug}/{$page['slug']} 缺少目录字段契约"); foreach ($page['directory']['fields'] as $field) { $this->assertMatchesRegularExpression( '/\p{Han}/u', $field['label'], "{$sectionSlug}/{$page['slug']} 的 {$field['name']} 仍在使用英文表头 {$field['label']}" ); } } } $this->assertArrayNotHasKey('todos', $sections); $this->assertArrayNotHasKey('registration', collect($sections['company-management']['pages'])->keyBy('slug')); $this->assertArrayNotHasKey('changes', collect($sections['trademark-management']['pages'])->keyBy('slug')); $this->assertArrayNotHasKey('opening-process', collect($sections['store-management']['pages'])->keyBy('slug')); } public function testWorkbookDirectoryPagesRenderTheirDeclaredHeaders(): void { $this->mockUpstreamPage((object) [ 'id' => 1, 'unified_social_credit_code' => null, 'company_name' => null, 'subscribed_capital' => null, 'company_type' => null, 'establishment_date' => null, 'legal_representative' => null, 'registered_address' => null, 'business_scope' => null, 'basic_deposit_account_number' => null, 'company_status' => null, 'financial_officer' => null, 'supervisor' => null, 'taxpayer_category' => null, 'shareholders' => null, ]); $this->loginAs() ->get(route('business.pages.show', ['section' => 'company-management', 'page' => 'basic-information'])) ->assertOk() ->assertSeeText('统一社会信用代码') ->assertSeeText('认缴时间'); $this->mockUpstreamPage((object) [ 'id' => 2, 'trademark_name' => null, 'trademark_type' => null, 'trademark_status' => null, 'registration_date' => null, 'expiry_date' => null, 'notes' => null, ]); $this->get(route('business.pages.show', ['section' => 'trademark-management', 'page' => 'information'])) ->assertOk() ->assertSeeText('国际分类') ->assertSeeText('核定使用商品') ->assertSeeText('保管部门'); $expectedHeaders = [ ['section' => 'company-management', 'page' => 'subject-operating-report', 'headers' => ['市', '主体', '连续12个月主体累计销量']], ['section' => 'company-management', 'page' => 'personnel-inventory', 'headers' => ['人员名称', '税务局密码', '可担任公司数量']], ['section' => 'company-management', 'page' => 'company-progress', 'headers' => ['公司进度表字段尚未定义']], ['section' => 'trademark-management', 'page' => 'trademark-progress', 'headers' => ['商标号', '是否完成', '办理人']], ['section' => 'store-management', 'page' => 'store-report', 'headers' => ['店铺ID(唯一值)', '1月销量', '12月销量']], ['section' => 'store-management', 'page' => 'change-progress', 'headers' => ['接替公司', '完成时间', '办理人']], ['section' => 'other', 'page' => 'todos', 'headers' => ['待办编号', '是否逾期', '关闭日期']], ['section' => 'other', 'page' => 'warnings', 'headers' => ['风险等级', '影响金额', '处理证据']], ]; foreach ($expectedHeaders as $page) { $response = $this->get(route('business.pages.show', [ 'section' => $page['section'], 'page' => $page['page'], ])); $response->assertOk(); foreach ($page['headers'] as $header) { $response->assertSeeText($header); } } } private function mockUpstreamPage(object $row): void { $paginator = new LengthAwarePaginator([$row], 1, 50, 1, ['path' => '/']); $service = Mockery::mock(UpstreamBusinessInformation::class); $service->shouldReceive('paginate')->once()->andReturn($paginator); $this->app->instance(UpstreamBusinessInformation::class, $service); } }