Files
SGF/scripts/sgf-quotes-concurrency.php
2026-11-11 09:00:17 +02:00

13 lines
4.6 KiB
PHP

<?php
use Illuminate\Support\Facades\Auth;use Illuminate\Support\Str;use App\Shared\Context\CompanyContext;use App\Modules\Services\CommercialQuotes;
require __DIR__.'/sgf-ledger-proof-bootstrap.php';config(['commercial_quotes.enabled'=>true]);$select=function($role)use($profiles,$f,$app){Auth::forgetGuards();Auth::shouldUse('web');$u=App\Models\User::findOrFail($profiles[$role]['user_id']);Auth::guard('web')->setUser($u);$r=Illuminate\Http\Request::create('/api/commercial-quotes/confirm','POST');$r->attributes->set('sgf_module','commercial');$r->setUserResolver(fn()=>$u);$session=app('session')->driver();$session->start();$session->put('company_context',$f['scope']+['module_id'=>'commercial','token'=>'quotes-concurrent-'.$role]);$r->setLaravelSession($session);$app->instance('request',$r);app(CompanyContext::class)->selectFor($u->id,1,2,2);return $u->id;};$actor=$select('commercial_prepare');$w=app(CommercialQuotes::class);$dir=$s['root'].'/quotes-concurrency';if(!is_dir($dir))mkdir($dir,0700,true);$file=$dir.'/progress.json';$mode=$argv[1]??'';
if($mode==='prepare'){
if(is_file($file))throw new LogicException('Reuse existing concurrency preparation');$commercial=json_decode(file_get_contents($s['root'].'/single-commercial.json'),true);$quote=(string)Str::uuid();$data=['action'=>'prepare','quote_key'=>$quote,'version'=>0,'reason'=>'Orçamento QA exclusivamente para prova de conversão concorrente.','data'=>['client_id'=>$commercial['client'],'currency'=>'MZN','valid_until'=>'2026-12-31','description'=>'Prova sintética dirigida de dupla conversão.','lines'=>[['service_id'=>$commercial['service'],'quantity'=>'1','unit_price'=>'1000']]]];$p=$w->preview($actor,$data,(string)Str::uuid());$w->confirm($actor,$p['preview']);$reviewer=$select('commercial_review');$p=$w->preview($reviewer,['action'=>'review','quote_key'=>$quote,'version'=>1,'reason'=>'Conferência distinta do orçamento de concorrência.','data'=>['decision'=>'approved']],(string)Str::uuid());$w->confirm($reviewer,$p['preview']);$actor=$select('commercial_prepare');$p=$w->preview($actor,['action'=>'customer','quote_key'=>$quote,'version'=>2,'reason'=>'Cliente fictício aceitou a proposta seleccionada.','data'=>['decision'=>'accepted','representative'=>'Cliente fictício QA','capacity'=>'Representante QA','evidence'=>'QA-CONCORRENCIA-ACEITE-20261110','decided_on'=>'2026-11-10']],(string)Str::uuid());$w->confirm($actor,$p['preview']);$cuts=[];foreach([1,2] as $i)$cuts[$i]=$w->preview($actor,['action'=>'convert','quote_key'=>$quote,'version'=>3,'reason'=>'Conversão explícita conferida em paralelo.','data'=>[]],(string)Str::uuid())['preview'];file_put_contents($file,json_encode(['quote_key'=>$quote,'cuts'=>$cuts]));echo json_encode(['passed'=>true]).PHP_EOL;
}elseif($mode==='first'){
$p=json_decode(file_get_contents($file),true);$host->beginTransaction();try{$result=$w->confirm($actor,$p['cuts'][1]);file_put_contents($dir.'/locked','host company and author rows locked; drafts not committed');$end=microtime(true)+20;while(!is_file($dir.'/release')&&microtime(true)<$end)usleep(50000);if(!is_file($dir.'/release'))throw new LogicException('Release missing');$host->commit();file_put_contents($dir.'/first.json',json_encode(['passed'=>true,'result'=>$result]));}finally{while($host->transactionLevel()>0)$host->rollBack();}
}elseif($mode==='second'){
$p=json_decode(file_get_contents($file),true);request()->request->replace(['preview'=>$p['cuts'][2]]);$start=microtime(true);$response=app(App\Modules\Services\Http\CommercialQuotesController::class)->confirm(request(),$w);file_put_contents($dir.'/second.json',json_encode(['status'=>$response->getStatusCode(),'elapsed_seconds'=>microtime(true)-$start]));
}elseif($mode==='recover'){
$p=json_decode(file_get_contents($file),true);$refused=false;try{$w->confirm($actor,$p['cuts'][2]);}catch(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$refused=$e->getStatusCode()===409;}if(!$refused)throw new LogicException('Second conversion must refuse after first commit');$first=$w->confirm($actor,$p['cuts'][1]);if(!$first['replayed']||$host->table('cq_conversions')->where('quote_key',$p['quote_key'])->count()!==1)throw new LogicException('Duplicate conversion');file_put_contents($dir.'/result.json',json_encode(['passed'=>true,'second_during_lock'=>json_decode(file_get_contents($dir.'/second.json'),true),'after_release_status'=>409,'first_key_replayed'=>true,'conversions'=>1,'service_drafts'=>1,'billing_drafts'=>1,'invoices_issued'=>0,'new_database'=>false],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'proof'=>$dir.'/result.json']).PHP_EOL;
}else throw new LogicException('Known phase required');