249 lines
10 KiB
PHP
249 lines
10 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature\Business;
|
||
|
||
use App\Models\Auth\Permission;
|
||
use App\Support\QuoteManagementSnapshot;
|
||
use App\Support\ReverseLogisticsQuoteSnapshot;
|
||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||
use Tests\Feature\FeatureTestCase;
|
||
|
||
class QuoteManagementPageTest extends FeatureTestCase
|
||
{
|
||
private string $fixturePath;
|
||
|
||
private string $reverseFixturePath;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
|
||
$this->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));
|
||
|
||
$this->reverseFixturePath = storage_path('framework/testing/reverse-logistics-' . uniqid('', true) . '.json');
|
||
file_put_contents($this->reverseFixturePath, json_encode([
|
||
'sourcePath' => 'C:/private/逆向物流报价.xlsx',
|
||
'sheets' => [
|
||
[
|
||
'index' => 0,
|
||
'name' => '申通退货',
|
||
'address' => 'A1:D5',
|
||
'values' => [
|
||
['报价单', null, null, null],
|
||
['出发省份', '到达省份', '江苏', null],
|
||
[null, null, '面单价格', '续重价格'],
|
||
[null, '广东', 9, 1.75],
|
||
['1、该价格不含税,生效日期:2026年5月1日', null, null, null],
|
||
],
|
||
'formulas' => [[], [], [], [], []],
|
||
],
|
||
],
|
||
], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
|
||
|
||
app()->instance(
|
||
ReverseLogisticsQuoteSnapshot::class,
|
||
new ReverseLogisticsQuoteSnapshot($this->reverseFixturePath)
|
||
);
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
if (isset($this->fixturePath) && is_file($this->fixturePath)) {
|
||
unlink($this->fixturePath);
|
||
}
|
||
|
||
if (isset($this->reverseFixturePath) && is_file($this->reverseFixturePath)) {
|
||
unlink($this->reverseFixturePath);
|
||
}
|
||
|
||
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('计费规则')
|
||
->assertSee('data-reverse-rate-body', false)
|
||
->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('<option value="新北中通">新北中通</option>', false)
|
||
->assertSee('<option value="新北中通其他">新北中通其他</option>', false)
|
||
->assertSee('<option value="新德邦快递超长">新德邦快递超长</option>', 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('计费规则')
|
||
->assertSeeText('申通退货')
|
||
->assertSee('共 1 条运价', false)
|
||
->assertSee('首重 + 续重 × (结算重量 - 1)', false)
|
||
->assertSee('2026-05-01', false)
|
||
->assertSee('"origin":"广东"', false)
|
||
->assertSee('"destination":"江苏"', false)
|
||
->assertSee('"increment_amount":1.75', false)
|
||
->assertSee('data-reverse-calc-body', false)
|
||
->assertSeeText('运费计算器')
|
||
->assertSee('data-reverse-calc="weight"', false)
|
||
->assertSee('data-reverse-rate-body', false);
|
||
}
|
||
|
||
public function testQuoteManagementPageKeepsTheShellWhenThePrivateSnapshotIsMissing(): void
|
||
{
|
||
app()->instance(
|
||
QuoteManagementSnapshot::class,
|
||
new QuoteManagementSnapshot(storage_path('framework/testing/missing-quote-snapshot.json'))
|
||
);
|
||
|
||
app()->instance(
|
||
ReverseLogisticsQuoteSnapshot::class,
|
||
new ReverseLogisticsQuoteSnapshot(storage_path('framework/testing/missing-reverse-logistics-snapshot.json'))
|
||
);
|
||
|
||
$this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'courier-payables',
|
||
'page' => 'quote-management',
|
||
'quote_view' => 'reverse-logistics',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('尚未导入本机报价文件。')
|
||
->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);
|
||
}
|
||
}
|
||
}
|