make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();$f=json_decode(file_get_contents($s['root'].'/fixture.json'),true);$n=json_decode(file_get_contents($s['root'].'/native-fixture.json'),true);config(['database.connections.sourcing_verify'=>$s['nativeConnection']]);$db=DB::connection('sourcing_verify');$checks=[];$ok=function($v,$name)use(&$checks,$s){if(!$v)throw new RuntimeException($name);$checks[]=$name;file_put_contents($s['root'].'/sourcing-progress.json',json_encode($checks,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));};$servers=[];$clients=[];$sessions=[]; try{ foreach([0,1]as$i){$socket=stream_socket_server('tcp://127.0.0.1:0');$port=(int)substr(strrchr(stream_socket_get_name($socket,false),':'),1);fclose($socket);$server=new Symfony\Component\Process\Process([PHP_BINARY,'-S','127.0.0.1:'.$port,'-t',$s['app'].'/public',dirname(__DIR__).'/scripts/receipt-composition-http.php'],$s['app'],null,null,180);$server->start();$servers[]=$server;for($j=0;$j<80;$j++){if($socket=@fsockopen('127.0.0.1',$port,$errno,$error,.1)){fclose($socket);break;}usleep(50000);}$clients[]=new GuzzleHttp\Client(['base_uri'=>'http://127.0.0.1:'.$port,'http_errors'=>false,'timeout'=>25]);} $enter=function($role,$i=0,$query=[])use(&$sessions,$clients){$jar=new GuzzleHttp\Cookie\CookieJar;$r=$clients[$i]->get('/qa/enter/'.$role,['cookies'=>$jar,'query'=>$query]);$sessions[$role.$i]=['jar'=>$jar]+json_decode((string)$r->getBody(),true,32,JSON_THROW_ON_ERROR);};$options=function($role,$i=0,$data=null,$headers=[])use(&$sessions){$v=$sessions[$role.$i];$o=['cookies'=>$v['jar'],'headers'=>$headers+['Accept'=>'application/json','X-SGF-Context'=>$v['context'],'X-CSRF-TOKEN'=>$v['csrf']]];if($data!==null)$o['json']=$data;return $o;};$call=function($role,$path,$data=null,$i=0,$headers=[])use($clients,$options,$s){$r=$clients[$i]->request($data===null?'GET':'POST',$path,$options($role,$i,$data,$headers));$text=(string)$r->getBody();if($r->getStatusCode()>=500)file_put_contents($s['root'].'/sourcing-last-500.txt',$text);return ['status'=>$r->getStatusCode(),'data'=>json_decode($text,true),'body'=>$text];};foreach(['actor','issuer','validator',...(($s['requests']??false)?['approver']:[])]as$role)$enter($role);$decisionRole=($s['requests']??false)?'approver':'issuer';$enter('actor',1);$base='/api/purchasing-sourcing';$url=$base.'/requests/'.$n['requisition']; $checks=json_decode(file_get_contents($s['root'].'/sourcing-progress.json'),true);$award=$db->table('purchase_awards')->where('requisition_id',$n['requisition'])->value('id');$receipt=$db->table('audit_logs')->where('requisition_id',$n['requisition'])->where('user_id',$n['head'])->where('action','sgf.sourcing.command')->where('after->command','decide')->first();$receipt=json_decode($receipt->after,true);$cut=['request_id'=>$n['requisition'],'actor'=>$n['head'],'binding'=>$receipt['binding_revision'],'observed'=>time(),'action'=>'decide','revision'=>str_repeat('0',64),'operation_key'=>$receipt['operation_key'],'data'=>['award_id'=>$award,'decision'=>'approved','comment'=>'ConferĂȘncia independente da base e do valor final.']];$replay=$call('approver',$url.'/confirm',['preview'=>Illuminate\Support\Facades\Crypt::encryptString(json_encode($cut,JSON_THROW_ON_ERROR))]);$ok($replay['status']===200&&$db->table('purchase_award_approvals')->where('decision','approved')->count()===1,'approval repetition preserves one recorded decision');$statuses=[200,201]; $orders='/api/purchasing-orders/awards/'.$award;$r=$call('actor',$orders);$ok($r['status']===200&&$r['data']['can_issue']===true,'adjudication created in SGF reaches order workflow without rebinding IDs');$p=$call('actor',$orders.'/preview',['revision'=>$r['data']['revision']]);$r=$call('actor',$orders.'/issue',['preview'=>$p['data']['preview']]);$ok($r['status']===201,'approved SGF adjudication issues one canonical order');$order=$r['data']['order'];$receipts='/api/purchasing-receipts/orders/'.$order['id'];$r=$call('validator',$receipts);$payload=['revision'=>$r['data']['revision'],'idempotency_key'=>(string)Str::uuid(),'delivery_note'=>'SGF-SOURCING-RECEIPT','received_on'=>date('Y-m-d'),'warehouse_id'=>$n['warehouse'],'items'=>[['purchase_order_item_id'=>$order['items'][0]['id'],'received_quantity'=>'1.000','accepted_quantity'=>'1.000','quality_status'=>'compliant','stock_item_id'=>$n['stock'],'notes'=>null]]];$p=$call('validator',$receipts.'/preview',$payload);$r=$call('validator',$receipts.'/confirm',['preview'=>$p['data']['preview']]);$ok($r['status']===201&&count($r['data']['movements'])===1,'warehouse receives the actual order created from SGF sourcing');$r=$call('validator',$receipts);$ok($r['data']['progress']['state']==='complete','proposal-to-adjudication-to-order-to-receipt path completes');$ok($db->table('quotations')->count()===3&&$db->table('proposal_evaluations')->count()===6&&$db->table('purchase_awards')->count()===1&&$db->table('purchase_orders')->count()===1&&$db->table('stock_movements')->count()===1,'complete path leaves exactly the intended business facts');$ok($db->table('invoices')->count()===0&&$db->table('budget_consumptions')->count()===0&&(int)$db->table('budget_entries')->where('tenant_id',$n['tenant'])->sum('committed_minor')===10000,'one source-budget commitment without invoice payment or consumption'); $h=$call('issuer',$url.'/history?page=1');$ok($h['status']===200&&$h['data']['per_page']===25&&count($h['data']['data'])===25,'full append-only history is paginated on server');$h2=$call('issuer',$url.'/history?page=2');$ids1=array_column($h['data']['data'],'id');$ids2=array_column($h2['data']['data'],'id');$ok($h2['status']===200&&count($ids2)>0&&array_intersect($ids1,$ids2)===[],'history pages are distinct and expose older evidence'); file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'quotes'=>3,'evaluations'=>6,'awards'=>1,'approved_decisions'=>1,'orders'=>1,'receipts'=>1,'movements'=>1,'commit_minor'=>10000,'whole_selected_path_complete'=>true,'concurrency_statuses'=>$statuses,'preview_persistent_fingerprint_unchanged'=>true,'rollback_recovery'=>true,'original_controllers_and_workflows'=>true,'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." sourcing-to-receipt checks passed.\n"; }finally{foreach($servers as$i=>$server){$server->stop(2);file_put_contents($s['root'].'/sourcing-http-'.$i.'.log',$server->getOutput()."\n".$server->getErrorOutput());}}