CaiWuXiTong/tests/Feature/Business/QuoteBatchReviewTest.php

107 lines
6.3 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Models\Auth\Role;
use App\Models\Business\CourierQuoteChange;
use App\Models\Business\CourierQuoteVersion;
use App\Support\CourierQuoteChangeService;
use App\Support\CourierStockQuoteEntryService;
use App\Support\QuoteManagementSnapshot;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;
use Tests\Feature\FeatureTestCase;
class QuoteBatchReviewTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
Http::preventStrayRequests();
app()->instance(QuoteManagementSnapshot::class, new QuoteManagementSnapshot('/nonexistent-batch-review-test.json'));
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
config(['courier_onboarding.allow_same_reviewer' => false]);
}
protected function reviewer()
{
$reviewer = user_model_class()::create(['name'=>'批量审核员','email'=>'batch-'.uniqid().'@example.com','password'=>'local-test-password','locale'=>'zh-CN','enabled'=>1,'landing_page'=>'dashboard','created_from'=>'core::test']);
$reviewer->companies()->attach($this->company->id);
$reviewer->attachRole(Role::where('name','admin')->firstOrFail());
$reviewer->attachPermission(Permission::where('name','read-admin-panel')->firstOrFail());
return $reviewer;
}
protected function pending(string $name, ?int $creator = null): CourierQuoteChange
{
$creator ??= $this->user->id;
$entry = app(CourierStockQuoteEntryService::class)->createPrepared(
['quote_type'=>'标准快递报价','effective_date'=>'2026-08-01','summary'=>'批量审核测试','billing_rules'=>'保留原报价规则'],
$creator, ['code'=>'TEST-'.$name,'name'=>$name],
['sheet_name'=>'原报价','source_range'=>'A1:B2','values'=>[['省份','运费'],['江苏',5]],'formulas'=>[[],[]]]
);
return app(CourierQuoteChangeService::class)->submit($entry->change, $creator);
}
public function test_batch_approves_selected_stock_quotes_and_does_not_mark_historical_files_verified(): void
{
$this->loginAs();
$one=$this->pending('快递甲');$two=$this->pending('快递乙');$untouched=$this->pending('未选快递');
$this->loginAs($this->reviewer())->post(route('business.courier-quote-changes.batch-approve'), ['ids'=>[$one->id,$two->id],'stock_rules_confirmed'=>1,'stock_signature_verified'=>1])
->assertRedirect(route('business.courier-quote-changes.index',['page'=>1]))
->assertSessionHas('success','批量审核完成:成功 2 条,未通过 0 条。');
$this->assertSame('approved',$one->fresh()->status);
$this->assertSame('approved',$two->fresh()->status);
$this->assertSame('pending_review',$untouched->fresh()->status);
$this->assertDatabaseCount('business_courier_quote_versions',2);
foreach(CourierQuoteVersion::all() as $version) $this->assertSame('存量报价,审核生效(无需签章)',$version->signature_status);
$this->assertSame(1,$one->events()->where('event_type','approved')->count());
$this->assertSame(1,$two->events()->where('event_type','approved')->count());
Http::assertNothingSent();
}
public function test_partial_batch_keeps_self_review_stale_and_other_company_records_unchanged(): void
{
$this->loginAs();$reviewer=$this->reviewer();
$good=$this->pending('正常');$own=$this->pending('自审',$reviewer->id);$old=$this->pending('已审核');$foreign=$this->pending('外公司');
app(CourierQuoteChangeService::class)->approve($old,$reviewer->id);
DB::table('business_courier_quote_changes')->where('id',$foreign->id)->update(['company_id'=>$this->company->id+1000]);
$this->loginAs($reviewer)->post(route('business.courier-quote-changes.batch-approve'), ['ids'=>[$good->id,$own->id,$old->id,$foreign->id,99999999],'stock_rules_confirmed'=>1])
->assertSessionHas('success','批量审核完成:成功 1 条,未通过 4 条。');
$this->assertSame('approved',$good->fresh()->status);
$this->assertSame('pending_review',$own->fresh()->status);
$this->assertDatabaseCount('business_courier_quote_versions',2);
$this->assertSame('pending_review',DB::table('business_courier_quote_changes')->where('id',$foreign->id)->value('status'));
}
public function test_batch_requires_confirmation_nonempty_distinct_bounded_ids(): void
{
$this->loginAs();$change=$this->pending('校验');
$this->withExceptionHandling()->loginAs($this->reviewer());
foreach ([['ids'=>[$change->id]],['ids'=>[],'stock_rules_confirmed'=>1],['ids'=>[$change->id,$change->id],'stock_rules_confirmed'=>1],['ids'=>range(1,101),'stock_rules_confirmed'=>1]] as $input) {
$this->post(route('business.courier-quote-changes.batch-approve'),$input)->assertSessionHasErrors();
}
$this->assertSame('pending_review',$change->fresh()->status);
$this->assertDatabaseCount('business_courier_quote_versions',0);
}
public function test_non_reviewer_cannot_use_batch_endpoint(): void
{
$this->loginAs();$change=$this->pending('权限');$viewer=$this->reviewer();$viewer->detachRole(Role::where('name','admin')->firstOrFail());
$this->withExceptionHandling()->loginAs($viewer)->post(route('business.courier-quote-changes.batch-approve'),['ids'=>[$change->id],'stock_rules_confirmed'=>1])->assertForbidden();
$this->assertSame('pending_review',$change->fresh()->status);
}
public function test_stale_model_cannot_approve_twice(): void
{
$this->loginAs();$one=$this->pending('并发');$stale=CourierQuoteChange::findOrFail($one->id);$reviewer=$this->reviewer();
app(CourierQuoteChangeService::class)->approve($one,$reviewer->id);
try { app(CourierQuoteChangeService::class)->approve($stale,$reviewer->id); $this->fail('过期状态不应重复审核'); }
catch(ValidationException $exception){$this->assertArrayHasKey('status',$exception->errors());}
$this->assertDatabaseCount('business_courier_quote_versions',1);
$this->assertSame(1,$one->events()->where('event_type','approved')->count());
}
}