fixturePath = storage_path('framework/testing/quote-management-' . uniqid('', true) . '.json'); file_put_contents($this->fixturePath, json_encode([ 'sourcePath' => 'C:/private/报价单合并.xlsx', 'sheets' => [ [ 'index' => 0, 'name' => '新北中通报价', 'address' => 'A1:D4', 'values' => [ ['省份', '面单', '费率', "备注:\n执行时间:2025.12.01"], [null, null, null, null], ['江苏省', 3.8, 0.5, null], ['安徽省', 3.8, 0.5, null], ], 'formulas' => [[], [], ['', '', '=B3/2', ''], []], ], [ 'index' => 1, 'name' => '常州圆通-上楼', 'address' => 'A1:H2', 'values' => [ ['报价表', '常州圆通'], ['江苏省', 4.2], ], 'formulas' => [[], []], ], [ 'index' => 2, 'name' => '新德邦快递超长报价', 'address' => 'A1:F2', 'values' => [ ['0-1kg', '1kg以上'], ['江苏省', 5.5], ], 'formulas' => [[], []], ], [ 'index' => 3, 'name' => '新北中通其他报价', 'address' => 'A1:D2', 'values' => [ ['省份', '面单', '费率', '备注'], ['江苏省', 4.1, 0.6, null], ], 'formulas' => [[], []], ], ], ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); app()->instance(QuoteManagementSnapshot::class, new QuoteManagementSnapshot($this->fixturePath)); } protected function tearDown(): void { if (isset($this->fixturePath) && is_file($this->fixturePath)) { unlink($this->fixturePath); } parent::tearDown(); } public function testQuoteManagementPageRendersImportedWorkbookVersions(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'quote-management', ])) ->assertOk() ->assertSeeText('报价管理') ->assertSee('data-quote-view="reverse-logistics"', false) ->assertSee('data-quote-panel="reverse-logistics"', false) ->assertSeeText('逆向物流报价尚未维护') ->assertSeeText('报价单合并.xlsx') ->assertSeeText('报价名称') ->assertSeeText('快递公司') ->assertSeeText('资料状态') ->assertSeeText('报价历史') ->assertSeeText('发起报价变更') ->assertSee(route('business.courier-quote-changes.index'), false) ->assertSee(route('business.courier-quote-changes.create'), false) ->assertDontSee('data-quote-change-modal', false) ->assertDontSeeText('只生成浏览器会话内的草稿提示') ->assertSee('', false) ->assertSee('', false) ->assertSee('', false) ->assertSee('"id":"QB-001"', false) ->assertSee('"company_name":"新北中通"', false) ->assertSee('"company_name":"新北中通其他"', false) ->assertSee('新北中通报价', false) ->assertSee('常州圆通-上楼', false) ->assertSee('2025-12-01', false) ->assertSee('=B3/2', false) ->assertSeeText('原始报价规则保留,当前页面不重新计算金额') ->assertDontSee('报价列表正在配置中'); $quotes = app(QuoteManagementSnapshot::class)->load()['quotes']; $this->assertSame(['QB-001', 'QB-002', 'QB-003', 'QB-004'], array_column($quotes, 'id')); $this->assertSame( ['新北中通', '常州圆通-上楼', '新德邦快递超长', '新北中通其他'], array_column($quotes, 'company_name') ); } public function testReverseLogisticsQuoteTabCanBeOpenedDirectly(): void { $courierPayables = collect(config('business_navigation')) ->firstWhere('slug', 'courier-payables'); $this->assertIsArray($courierPayables); $this->assertContains('quote-management', array_column($courierPayables['pages'], 'slug')); $this->assertNotContains('reverse-logistics-quotes', array_column($courierPayables['pages'], 'slug')); $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'quote-management', 'quote_view' => 'reverse-logistics', ])) ->assertOk() ->assertSee('data-quote-view="reverse-logistics" aria-controls="quote-reverse-logistics-panel" aria-selected="true"', false) ->assertSee('id="quote-reverse-logistics-panel" role="tabpanel" data-quote-panel="reverse-logistics"', false) ->assertSeeText('逆向物流报价尚未维护'); } public function testQuoteManagementPageKeepsTheShellWhenThePrivateSnapshotIsMissing(): void { app()->instance( QuoteManagementSnapshot::class, new QuoteManagementSnapshot(storage_path('framework/testing/missing-quote-snapshot.json')) ); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'quote-management', ])) ->assertOk() ->assertSeeText('尚未导入本机报价文件。') ->assertSeeText('报价页面仍可访问') ->assertDontSee('class="business-blank-page"', false); } public function testQuoteManagementPageRejectsUsersWithoutItsSidebarPermission(): void { $subject = 'user_' . $this->user->id; $this->user->roles()->sync([\App\Models\Auth\Role::where('name', 'admin')->firstOrFail()->id]); setting()->set('system.sidebar_permissions', json_encode([ 'configured' => [$subject], 'subjects' => [ $subject => ['courier-payables/freight-reconciliation'], ], ], JSON_UNESCAPED_UNICODE)); try { $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'quote-management', ])); $this->fail('缺少报价管理页面权限的用户不应访问该页面。'); } catch (HttpException|HttpResponseException $exception) { $status = method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : $exception->getResponse()->getStatusCode(); $this->assertSame(404, $status); } } }