Files
SGF/scripts/ledger-receipt-proof.php
T
2026-11-04 09:37:20 +02:00

9 lines
3.9 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
$out=$s['root'].'/ledger-receipt-cases.json';if(file_exists($out)||DB::table('gl_proposals')->exists())throw new LogicException('Fresh receipt fixture required');$checks=0;$ok=function($v,$m)use(&$checks){$checks++;if(!$v)throw new LogicException($m);};$key=(string)Str::uuid();$p=$command('prepare','effect-prepare',$body);$reviewKey=(string)Str::uuid();$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Distinct actual cost275 review for receipt recovery.'],$reviewKey);$j=$command('prepare','post',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Actual approved synthetic cost275, once.'],$key);
$before=$sourceFingerprint();$count=DB::table('gl_commands')->count();$locks=0;$mode=null;DB::listen(function($q)use(&$locks,&$mode){if(str_contains(strtolower($q->sql),'for update'))$locks++;if(str_contains($q->sql,'"gl_commands"')&&$mode===null)$mode=['isolation'=>$q->connection->getPdo()->query('SHOW transaction_isolation')->fetchColumn(),'read_only'=>$q->connection->getPdo()->query('SHOW transaction_read_only')->fetchColumn()];});$r=$ledger->receipt($enter('prepare'),$key);$ok($r['result']['journal_id']===$j['journal_id']&&$r['operation_executed']===false&&$r['historical_receipt_only'],'Original result recovered without execution');$ok($ledger->receipt($enter('prepare'),$key)===$r,'Repeated read exact historical receipt');$ok($ledger->receipt($enter('validate'),$key)===null&&$ledger->receipt($enter('read'),$key)===null,'Other actors cannot retrieve known key');$ok($ledger->receipt($enter('validate'),$reviewKey)['result']['state']==='approved','Reviewer can retrieve own decision receipt');$ok($ledger->receipt($enter('prepare'),(string)Str::uuid())===null,'Unknown key has no inferred result');$caught=false;try{$ledger->receipt($enter('prepare'),'invalid');}catch(InvalidArgumentException){$caught=true;}$ok($caught,'Malformed UUID refused');
DB::beginTransaction();try{DB::table('permission_grants')->where($scope)->where('user_id',$g['actors']['prepare'])->where('permission','general_ledger.view')->delete();$caught=false;try{$ledger->receipt($enter('prepare'),$key);}catch(Symfony\Component\HttpKernel\Exception\HttpException|Illuminate\Auth\Access\AuthorizationException){$caught=true;}$ok($caught,'Current view right required');}finally{DB::rollBack();}
DB::beginTransaction();try{$u=$enter('prepare');$unit=DB::table('units')->insertGetId(['tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id'],'name'=>'QA foreign receipt unit','created_at'=>now(),'updated_at'=>now()]);foreach(['unit.view','general_ledger.view'] as $right)Tests\Support\IdentityFixture::grant($u->id,$scope['tenant_id'],$scope['company_id'],$right,$unit);DB::table('pa_access')->insert(['tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id'],'unit_id'=>$unit,'user_id'=>$u->id,'action'=>'view','person'=>'cost-qa-person-'.$u->id]);app(App\Shared\Context\CompanyContext::class)->selectFor($u->id,$scope['tenant_id'],$scope['company_id'],$unit);$ok($ledger->receipt($u,$key)===null,'Eligible foreign unit cannot read original key');}finally{DB::rollBack();$enter('read');}
$ok($mode===['isolation'=>'repeatable read','read_only'=>'on']&&$locks===0,'One read-only snapshot without writer locks');$ok($before===$sourceFingerprint()&&DB::table('gl_commands')->count()===$count&&DB::table('gl_journals')->count()===1,'Reads/probes preserve sources, rights, receipts and single posting');file_put_contents($s['root'].'/receipt-fixture.json',json_encode(['key'=>$key,'review_key'=>$reviewKey,'journal_id'=>$j['journal_id']],JSON_PRETTY_PRINT));file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'mode'=>$mode,'writer_locks'=>$locks,'no_duplicate_or_write'=>true,'source_rights_unchanged'=>true],JSON_PRETTY_PRINT));echo "Receipt recovery: $checks checks; scoped own result, current rights, read-only and no duplicate.\n";