Files
SGF/scripts/ledger-flow-cases.php
2026-11-04 09:37:20 +02:00

49 lines
4.9 KiB
PHP

<?php
// Additional integration checks only. All mutation probes roll back in this owned QA database.
$output=$s['root'].'/ledger-flow-cases.json';
if(file_exists($output)||!($g['integrated_flow']??false))throw new LogicException('Dedicated unfinished flow proof required');
$http=json_decode(file_get_contents($s['root'].'/ledger-flow-http.json'),true,64,JSON_THROW_ON_ERROR);
$checks=0;$check=function(bool $ok,string $why)use(&$checks){$checks++;if(!$ok)throw new LogicException($why);};
$reader=$enter('read');$before=$financialFingerprint();$queries=[];$mode=null;
DB::listen(function($event)use(&$queries,&$mode){$queries[]=$event->sql;if(str_contains($event->sql,'"gl_journals"')){$pdo=$event->connection->getPdo();$mode=['isolation'=>$pdo->query('SHOW transaction_isolation')->fetchColumn(),'read_only'=>$pdo->query('SHOW transaction_read_only')->fetchColumn()];}});
$data=$ledger->flow($reader,$g['local_source_id']);
$check($data['position']['id']===$http['obligation_id']&&$data['journal']->id===$http['journal_id'],'Same scoped financial and accounting origin');
$check($mode===['isolation'=>'repeatable read','read_only'=>'on'],'Single coherent read-only snapshot');
$check(!array_filter($queries,fn($q)=>str_contains(strtolower($q),'for update')),'No writer locks in flow read');
$check(count($data['financial_proposals'])===1&&count($data['financial_events'])<=25,'Bounded source evidence');
$reject=function(Closure $probe,int $expected=403)use($check){try{$probe();throw new LogicException('Expected access refusal');}catch(Symfony\Component\HttpKernel\Exception\HttpException $e){$check($e->getStatusCode()===$expected,'Explicit access refusal');}};
foreach(['general_ledger.view','supplier_payables.view'] as $permission){
DB::beginTransaction();try{
DB::table('permission_grants')->where($scope)->where(['user_id'=>$reader->id,'permission'=>$permission])->delete();
$reject(fn()=>$ledger->flow($reader,$g['local_source_id']));
}finally{DB::rollBack();}
}
DB::beginTransaction();try{
DB::table('pa_access')->where($scope)->where(['user_id'=>$reader->id,'action'=>'view'])->delete();
$reject(fn()=>$ledger->flow($reader,$g['local_source_id']));
}finally{DB::rollBack();}
DB::beginTransaction();try{
$row=(array)DB::table('pa_imported_sources')->where('id',$g['local_source_id'])->first();unset($row['id']);$row['unit_id']+=1000;$other=DB::table('pa_imported_sources')->insertGetId($row);
$reject(fn()=>$ledger->flow($reader,$other));
}finally{DB::rollBack();}
DB::beginTransaction();try{
$row=(array)DB::table('pa_imported_sources')->where('id',$g['local_source_id'])->first();unset($row['id']);$row['company_id']+=1000;$other=DB::table('pa_imported_sources')->insertGetId($row);
$reject(fn()=>$ledger->flow($reader,$other));
}finally{DB::rollBack();}
DB::beginTransaction();try{
$row=(array)DB::table('pa_imported_sources')->where('id',$g['local_source_id'])->first();unset($row['id']);$row['tenant_id']+=1000;$other=DB::table('pa_imported_sources')->insertGetId($row);
$reject(fn()=>$ledger->flow($reader,$other));
}finally{DB::rollBack();}
$data=$ledger->listing($enter('read'),['obligation_id'=>$http['obligation_id']]);
$check(count($data['events'])===3&&count($data['proposals'])===1&&count($data['journals'])===1,'Focus retains prepare, independent review and posting evidence');
$check($data['totals']===['debit_minor'=>6000,'credit_minor'=>6000,'net_minor'=>0],'Focused accounting totals reconciled');
$check($before===$financialFingerprint(),'All authorisation and foreign-scope probes rolled back');
$check(DB::table('pa_obligations')->count()===1&&DB::table('gl_journals')->count()===1&&DB::table('gl_lines')->count()===2,'One debt, one journal, two lines after retries');
$check(DB::table('gl_events')->where('kind','journal.posted')->count()===1,'One durable posting event');
$check(DB::table('permission_grants')->where('permission','like','general_ledger.%')->count()===6,'Only explicitly authorised synthetic grants retained');
$check(DB::table('gl_proposals')->value('prepared_by')!==DB::table('gl_reviews')->value('reviewed_by'),'Distinct accounting identities');
foreach(DB::table('pa_imported_sources')->get() as $row)$check(hash_equals($row->receipt_hash,hash('sha256',Tempo\Finance\Payables\Payables::canonical(json_decode($row->payload,true)))),'Document receipt unchanged');
$check((require $c['root'].'/config/general_ledger.php')['synthetic_enabled']===false,'Destination remains disabled');
file_put_contents($output,json_encode(['passed'=>true,'checks'=>$checks,'coherent_read'=>$mode,'flow_query_count'=>count($queries),'scope_refusals'=>['tenant','company','unit'],'revoked_rights_refused'=>true,'mutating_probes_rolled_back'=>true,'one_obligation_one_journal'=>true,'six_qa_grants'=>true,'default_enabled'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo "Integrated isolation, current permissions, source receipts, read snapshot and single effect verified: $checks checks.\n";