table('pa_obligations')->count()===0&&$db->table('pa_proposals')->count()===0,'new canonical book starts without payable recognition'); $ok($call('issuer',$ob)['status']===200&&$call('issuer',$ob)['data']['data']===[],'bounded list starts empty'); $ok($call('actor',$ob)['status']===403&&$call('validator',$ob)['status']===403,'procurement and warehouse cannot read payable economics'); $ok($call('issuer',$ob.'?page=0')['status']===422&&$call('issuer',$ob.'?unknown=1')['status']===422,'invalid pagination and unsupported filters refused'); $source='/api/purchasing-orders/awards/'.$n['award'];$r=$call('actor',$source);$p=$call('actor',$source.'/preview',['revision'=>$r['data']['revision']]);$r=$call('actor',$source.'/issue',['preview'=>$p['data']['preview']]);$ok($r['status']===201,'actual source award issues order for integrated boundary');$order=$r['data']['order'];$id=$order['id'];$line=$order['items'][0]['id']; $receipt='/api/purchasing-receipts/orders/'.$id;$r=$call('validator',$receipt);$p=$call('validator',$receipt.'/preview',['revision'=>$r['data']['revision'],'idempotency_key'=>(string)Str::uuid(),'delivery_note'=>'QA-OBLIGATION','received_on'=>date('Y-m-d'),'warehouse_id'=>$n['warehouse'],'items'=>[['purchase_order_item_id'=>$line,'received_quantity'=>'1.000','accepted_quantity'=>'1.000','quality_status'=>'compliant','stock_item_id'=>$n['stock'],'notes'=>null]]]);$r=$call('validator',$receipt.'/confirm',['preview'=>$p['data']['preview']]);$ok($r['status']===201,'original receipt writer supplies accepted invoice source'); $invoiceUrl='/api/purchasing-invoices/orders/'.$id; $invoiceCommand=function($role,$action,$data)use($call,$invoiceUrl){$s=$call($role,$invoiceUrl);$p=$call($role,$invoiceUrl.'/preview',['operation_key'=>(string)Str::uuid(),'revision'=>$s['data']['revision'],'action'=>$action,'reason'=>'Documento de ensaio para reconhecimento distinto.','data'=>$data]);if($p['status']!==200)throw new RuntimeException('Invoice boundary preview '.$p['status']);return $call($role,$invoiceUrl.'/confirm',['preview'=>$p['data']['preview']]);}; $r=$invoiceCommand('issuer','register',['number'=>'QA-PAYABLE-100','issued_on'=>date('Y-m-d'),'subtotal'=>'100.00','tax'=>'0.00','total'=>'100.00','items'=>[['purchase_order_item_id'=>$line,'quantity'=>'1.000']]]);$invoice=$r['data']['invoice_id'];$url=$ob.'/'.$invoice; $ok($r['status']===201&&$db->table('pa_obligations')->count()===0,'registration remains separate from recognition'); $preview=function($role,$action,$proposal=0,$key=null,$i=0)use($call,$url){$r=$call($role,$url,null,$i);if($r['status']!==200)throw new RuntimeException('Payable source unavailable '.$r['status']);return $call($role,$url.'/preview',['operation_key'=>$key??(string)Str::uuid(),'revision'=>$r['data']['revision'],'action'=>$action,'proposal_id'=>$proposal,'reason'=>'Conferencia individual sintetica da responsabilidade.'],$i);};$confirm=fn($role,$cut,$i=0)=>$call($role,$url.'/confirm',['preview'=>$cut],$i); $paFingerprint=function()use($db){$state=[];foreach(['pa_sources','pa_proposals','pa_obligations','pa_commands','pa_events','pa_outbox']as$t)$state[$t]=$db->table($t)->orderBy($t==='pa_outbox'?'event_id':'id')->get();return hash('sha256',json_encode($state));}; $before=$paFingerprint();$p=$preview('issuer','prepare');$ok($p['status']===200&&$p['data']['impact']['result']['state']==='pending_source'&&!isset($p['data']['impact']['result']['proposal_id']),'pending document preview exposes missing source without temporary ids');$ok($paFingerprint()===$before,'rollback preview leaves financial facts and audit unchanged'); $r=$confirm('issuer',$p['data']['preview']);$proposal=$r['data']['proposal_id'];$ok($r['status']===201&&$r['data']['state']==='pending_source'&&$db->table('pa_obligations')->count()===0,'pending source capture cannot recognise debt'); $ok($preview('issuer','validate',$proposal)['status']===422,'preparer cannot validate own proposal'); $access=$db->table('pa_access')->where('user_id',$n['finance_auditor'])->where('action','view')->first();$db->table('pa_access')->where('id',$access->id)->update(['person'=>'qa-person-issuer']);try{$ok($preview('auditor','validate',$proposal)['status']===422,'another account of same person cannot validate');}finally{$db->table('pa_access')->where('id',$access->id)->update(['person'=>$access->person]);} $p=$preview('reviewer','validate',$proposal);$ok($p['status']===200&&$p['data']['impact']['result']['state']==='conflict','incomplete source preview exposes refusal without recognising');$stale=$p['data']['preview']; $r=$invoiceCommand('reviewer','validate',['invoice_id'=>$invoice]);$ok($r['status']===201&&$db->table('pa_obligations')->count()===0,'original validation consumes budget but does not recognise automatically'); $ok($confirm('reviewer',$stale)['status']===409,'actual source validation invalidates prior recognition conference'); $financialFingerprint=function()use($db){$state=[];foreach(['goods_receipts','goods_receipt_items','stock_movements','budget_entries','budget_consumptions','external_payments','invoices','invoice_items']as$t)$state[$t]=$db->table($t)->orderBy('id')->get();return hash('sha256',json_encode($state));};$financialBefore=$financialFingerprint(); $key=(string)Str::uuid();$p=$preview('issuer','prepare',0,$key);$p2=$preview('issuer','prepare',0,$key,1);$ok($p['status']===200&&$p2['status']===200,'independent sessions preview same explicit source'); $responses=GuzzleHttp\Promise\Utils::unwrap([$clients[0]->postAsync($url.'/confirm',$options('issuer',0,['preview'=>$p['data']['preview']])),$clients[1]->postAsync($url.'/confirm',$options('issuer',1,['preview'=>$p2['data']['preview']]))]);$statuses=array_map(fn($r)=>$r->getStatusCode(),$responses);sort($statuses);$ok($statuses===[200,201],'concurrent same-key preparation returns one commit and one replay'); $proposal=(int)$db->table('pa_proposals')->orderByDesc('id')->value('id');$ok($db->table('pa_proposals')->count()===2&&$db->table('pa_obligations')->count()===0,'new validated source creates second immutable capture only'); $p=$preview('reviewer','validate',$proposal);$q=$preview('auditor','validate',$proposal,null,1);$ok($p['status']===200&&$p['data']['impact']['result']['state']==='recognised'&&$p['data']['impact']['principal_minor']===10000&&$p['data']['impact']['due_on']===null,'distinct recognition preview shows exact principal and unknown maturity'); $grant=DB::table('permission_grants')->where($f['scope'])->where('user_id',$f['reviewer'])->where('permission','purchasing_obligations.validate')->first();DB::table('permission_grants')->where('id',$grant->id)->delete();try{$ok($confirm('reviewer',$p['data']['preview'])['status']===403,'host permission is rechecked after preview');}finally{DB::table('permission_grants')->insert((array)$grant);} $nativeGrant=$db->table('pa_access')->where('user_id',$n['finance_reviewer'])->where('action','validate')->first();$db->table('pa_access')->where('id',$nativeGrant->id)->delete();try{$ok($confirm('reviewer',$p['data']['preview'])['status']===409,'native permission change invalidates conference');}finally{$db->table('pa_access')->insert((array)$nativeGrant);} $before=$paFingerprint();$db->unprepared("CREATE FUNCTION public.obligation_qa_fail() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.action='sgf.obligation.command' THEN RAISE EXCEPTION 'QA_ATOMIC_FAILURE'; END IF; RETURN NEW; END $$; CREATE TRIGGER obligation_qa_fail BEFORE INSERT ON audit_logs FOR EACH ROW EXECUTE FUNCTION obligation_qa_fail()"); try{$ok($confirm('reviewer',$p['data']['preview'])['status']===500&&$paFingerprint()===$before,'audit failure rolls back principal command outbox and native events');}finally{$db->unprepared('DROP TRIGGER obligation_qa_fail ON audit_logs; DROP FUNCTION public.obligation_qa_fail()');} $responses=GuzzleHttp\Promise\Utils::unwrap([$clients[0]->postAsync($url.'/confirm',$options('reviewer',0,['preview'=>$p['data']['preview']])),$clients[1]->postAsync($url.'/confirm',$options('auditor',1,['preview'=>$q['data']['preview']]))]);$codes=array_map(fn($r)=>$r->getStatusCode(),$responses);$winner=$codes[0]===201?'reviewer':'auditor';$cut=$codes[0]===201?$p['data']['preview']:$q['data']['preview'];$wi=$winner==='reviewer'?0:1;sort($codes);$ok($codes===[201,409],'competing validators recover after rollback and refuse stale second decision'); $obligation=$db->table('pa_obligations')->first();$ok($db->table('pa_obligations')->count()===1&&(int)$obligation->principal_minor===10000&&$obligation->due_on===null,'exactly one canonical100MZN principal with no invented due date'); $before=$paFingerprint();$r=$confirm($winner,$cut,$wi);$ok($r['status']===200&&$r['data']['replayed']===true&&$paFingerprint()===$before,'recognition retry revalidates source and produces no duplicate'); $r=$call('issuer',$url);$ok($r['status']===200&&$r['data']['obligations'][0]['balance_minor']===10000&&$r['data']['obligations'][0]['source_conflict']===false&&$r['data']['obligations'][0]['days_overdue']===null,'read position uses same kernel and keeps overdue indeterminate'); $p=$preview('issuer','prepare');$r=$confirm('issuer',$p['data']['preview']);$ok($r['status']===201&&$r['data']['state']==='recognised'&&$db->table('pa_obligations')->count()===1,'different command recovers compatible existing responsibility without second debt'); $ok($financialFingerprint()===$financialBefore,'recognition does not change invoice stock budget or payments'); $r=$call('issuer',$ob.'?status=validated&search=PAYABLE');$r2=$call('issuer',$ob.'?page=2');$ok($r['status']===200&&$r['data']['total']===1&&$r['data']['per_page']===25&&$r2['data']['data']===[],'server filters and pagination return authorised document only'); $r=$call('issuer',$url.'?proposal_page=2&event_page=2');$ok($r['status']===200&&$r['data']['proposals']===[]&&$r['data']['events']===[]&&$r['data']['pagination']['events']['per_page']===25,'independent evidence pages do not load entire financial universe'); $ok($call('issuer',$ob.'/999999')['status']===403,'unknown or foreign document id exposes no financial evidence'); $ok($call('issuer','/purchasing/obligations')['status']===200,'workspace screen served without browser automation'); $p=$preview('issuer','prepare');$enter('issuer',0,['foreign'=>1]);$ok($confirm('issuer',$p['data']['preview'])['status']===403,'unmapped unit cannot reuse source or preview');$enter('issuer');$ok($confirm('issuer',$p['data']['preview'])['status']===409,'session context regeneration invalidates old preview'); $ok($db->table('pa_events')->where('kind','payable.recognised')->count()===1,'one immutable recognition event despite concurrency and retries'); file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'preparation_statuses'=>$statuses,'validation_statuses'=>$codes,'principal_minor'=>10000,'obligations'=>1,'due_on'=>null,'financial_sources_unchanged'=>true,'same_native_authority'=>true,'automatic_recognition'=>false,'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." payable integration checks passed.\n";