$ledger->report($enter('read'),$kind,$options);$range=['from_on'=>'2026-10-29','to_on'=>'2026-10-29']; if(str_ends_with($phase,'-seed')){ if(DB::table('gl_journals')->exists()||DB::table('gl_proposals')->exists())throw new LogicException('Fresh dedicated report book required');$before=$financialFingerprint(); $p=$command('prepare','prepare',$g['body']);$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent recognition for dedicated report fixture.']);$command('prepare','post',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'One actual workflow recognition for report baseline.']); DB::transaction(function()use($scope){ $original=(array)DB::table('gl_journals')->first();$proposal=(array)DB::table('gl_proposals')->where('id',$original['proposal_id'])->first();$review=(array)DB::table('gl_reviews')->where('id',$original['review_id'])->first();$originalSnapshot=json_decode($original['snapshot'],true,64,JSON_THROW_ON_ERROR); // Explicit SQL-only reporting data, not claims of 130 real recognised debts or writer execution. for($n=1;$n<=130;$n++){ $a=DB::table('gl_accounts')->insertGetId($scope+['code'=>sprintf('QA-REPORT-%03d',$n),'label'=>'SQL-only reporting fixture '.$n,'is_movement'=>true,'currency'=>'MZN']);$date=$n<=10?'2026-10-28':($n<=120?'2026-10-29':'2026-10-30');$snap=$originalSnapshot;$snap['journal_on']=$date;$snap['source']['effect_type']='report.fixture';$snap['source']['obligation_id']=900000+$n;$snap['source']['origin_key']='qa-report-only-'.$n;$snap['source']['principal_minor']=$n;$snap['source']['recognition_revision']=hash('sha256','qa-report-only-'.$n); $snap['lines'][0]['debit_minor']=$n;$snap['lines'][1]=['account_id'=>$a,'account_code'=>sprintf('QA-REPORT-%03d',$n),'debit_minor'=>0,'credit_minor'=>$n];$snap['impact']=['debit_minor'=>$n,'credit_minor'=>$n,'financial_balance_changed'=>false,'payment_created'=>false];$snapshot=Payables::canonical($snap);$revision=hash('sha256',$snapshot);$effect=hash('sha256','report-fixture-'.$n); $p=$proposal;unset($p['id']);$p=array_replace($p,['effect_key'=>$effect,'obligation_id'=>900000+$n,'snapshot'=>$snapshot,'revision'=>$revision,'reason'=>'SQL-only synthetic reporting row, not a financial recognition.']);$pid=DB::table('gl_proposals')->insertGetId($p); $r=$review;unset($r['id']);$r=array_replace($r,['proposal_id'=>$pid,'revision'=>$revision,'reason'=>'SQL-only reporting fixture review metadata.']);$rid=DB::table('gl_reviews')->insertGetId($r); $j=$original;unset($j['id']);$j=array_replace($j,['effect_key'=>$effect,'obligation_id'=>900000+$n,'proposal_id'=>$pid,'review_id'=>$rid,'source_revision'=>$snap['source']['recognition_revision'],'journal_on'=>$date,'debit_minor'=>$n,'credit_minor'=>$n,'snapshot'=>$snapshot,'reason'=>'SQL-only reporting fixture, no financial effect.']);$jid=DB::table('gl_journals')->insertGetId($j); foreach($snap['lines'] as $i=>$line)DB::table('gl_lines')->insert($scope+['journal_id'=>$jid,'line_no'=>$i+1,'account_id'=>$line['account_id'],'debit_minor'=>$line['debit_minor'],'credit_minor'=>$line['credit_minor']]);DB::select('SELECT gl_assert_balanced(?)',[$jid]); } }); if($financialFingerprint()!==$before)throw new LogicException('Report seed changed financial authority'); file_put_contents($out,json_encode(['passed'=>true,'dedicated_report_database'=>true,'actual_workflow_recognitions'=>1,'sql_only_reporting_journals'=>130,'journals'=>131,'lines'=>262,'no_extra_financial_effects'=>true,'financial_fingerprint'=>$before],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Dedicated reporting book: one actual workflow journal and 130 explicitly SQL-only balanced reporting rows; financial authority unchanged.\n";exit; } $checks=0;$ok=function($v,$message)use(&$checks){$checks++;if(!$v)throw new LogicException($message);};$refuse=function($fn)use($ok){$caught=false;try{$fn();}catch(InvalidArgumentException|DomainException|Symfony\Component\HttpKernel\Exception\HttpException|Illuminate\Auth\Access\AuthorizationException){$caught=true;}$ok($caught,'Invalid or unauthorised report must be refused');}; $seed=json_decode(file_get_contents($s['root'].'/ledger-reports-seed.json'),true,64,JSON_THROW_ON_ERROR);$before=$financialFingerprint();$sql=[];DB::listen(function($e)use(&$sql){$sql[]=$e->sql;}); $first=$report('trial-balance',$range);$debit=DB::table('gl_accounts')->where($scope)->where('code','QA-CONTRAPARTIDA')->value('id'); $ok($first['pagination']['total']===122&&count($first['rows'])===25,'Server account pagination includes opening and movements but excludes future-only rows');$ok($first['totals']['opening_debit_minor']===55&&$first['totals']['opening_credit_minor']===55&&$first['totals']['debit_minor']===13205&&$first['totals']['credit_minor']===13205&&$first['totals']['closing_debit_minor']===13260&&$first['totals']['closing_credit_minor']===13260,'Exact opening/movement/closing totals'); $second=$report('trial-balance',$range+['page'=>2]);$hundred=$report('trial-balance',$range+['per_page'=>100]);$ok($second['totals']===$first['totals']&&count($hundred['rows'])===100&&$hundred['totals']===$first['totals'],'Totals independent of page size and offset');$ok(!array_intersect(array_column($first['rows'],'account_id'),array_column($second['rows'],'account_id')),'Stable code/ID account ordering'); $entries=$report('account-ledger',$range+['account_id'=>$debit]);$ok($entries['opening_net_minor']===55&&$entries['closing_net_minor']===13260&&$entries['pagination']['total']===111&&count($entries['rows'])===25,'Account report uses all matching facts');$ok($entries['rows'][0]['balance_minor']===6055&&$entries['rows'][0]['effect_type']==='supplier_payable.recognised','First real recognition retains source evidence and opening balance'); $next=$report('account-ledger',$range+['account_id'=>$debit,'page'=>2]);$expected=6055+array_sum(range(11,35));$ok($next['rows'][0]['balance_minor']===$expected,'Window accumulation crosses page boundary');$all=[];for($page=1;$page<=5;$page++)$all=array_merge($all,$report('account-ledger',$range+['account_id'=>$debit,'page'=>$page])['rows']);$ok(count(array_unique(array_column($all,'line_id')))===111&&end($all)['balance_minor']===13260,'All account pages reconcile without duplicate or missing lines'); $after=$report('trial-balance',['from_on'=>'2026-10-30','to_on'=>'2026-10-30']);$ok($after['totals']['opening_debit_minor']===13260&&$after['totals']['debit_minor']===1255&&$after['totals']['closing_debit_minor']===14515,'Inclusive date boundaries and future facts');$empty=$report('trial-balance',['from_on'=>'2026-01-01','to_on'=>'2026-01-02']);$ok($empty['rows']===[]&&$empty['basis']['journals']===0&&$empty['totals']['debit_minor']===0,'Available empty source explicitly distinguished'); foreach([[],['from_on'=>'2026-02-30','to_on'=>'2026-10-29'],['from_on'=>'2026-10-30','to_on'=>'2026-10-29'],$range+['per_page'=>26],$range+['page'=>100001],$range+['tenant_id'=>99],$range+['account_id'=>999999]] as $bad)$refuse(fn()=>$report('trial-balance',$bad));$refuse(fn()=>$report('account-ledger',$range)); $ok(!array_filter($sql,fn($s)=>str_contains(strtolower($s),'for update')),'Reads never acquire writer row locks'); DB::beginTransaction();try{ $user=$enter('read');if($native)DB::table('pa_access')->where($scope)->where('user_id',$user->id)->where('action','general_ledger.view')->delete();else DB::table('permission_grants')->where($scope)->where('user_id',$user->id)->where('permission','general_ledger.view')->delete();$refuse(fn()=>$report('trial-balance',$range)); }finally{DB::rollBack();} $ok($financialFingerprint()===$before&&$before===$seed['financial_fingerprint'],'Financial records, rights, original sources and approvals unchanged'); file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'opening_minor'=>55,'movement_minor'=>13205,'closing_minor'=>13260,'accounts'=>122,'account_lines'=>111,'page_sizes'=>[25,100],'exact_dates'=>true,'running_balance_across_pages'=>true,'revoked_access_refused'=>true,'writer_locks'=>0,'financial_authority_unchanged'=>true,'sql_fixture_explicit'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Report checks: $checks, exact opening/movements/closing, scoped pagination and revoked-access refusal.\n";