Files
SGF/scripts/sgf-statements-proof.php
T
2026-11-11 09:00:17 +02:00

14 lines
7.0 KiB
PHP

<?php
require __DIR__.'/sgf-statements-proof-bootstrap.php';$before=[];foreach(['gl_accounts','gl_journals','gl_lines','gl_periods','pa_obligations','stock_movements','audit_logs']as$t)$before[$t]=$native->table($t)->orderBy('id')->get()->toJson();$grants=$host->table('permission_grants')->orderBy('id')->get()->toJson();$bm=(int)$native->table('fs_versions')->where('kind','mapping')->max('version');$br=(int)$native->table('fs_versions')->where('kind','result')->max('version');$native->beginTransaction();$host->beginTransaction();try{
$input=$fsInput('mapping','prepare',$bm,$fsMapping);$preview=$fsCall(15,'preview',$input);$ok($preview['status']===200&&$preview['value']['impact']['new_journals']===0,'mapping preview no journal');$ok($native->table('fs_versions')->where('kind','mapping')->max('version')===($bm?:null),'preview rolled back');[$m1,$cut,$key]=$fsCommand(15,$input);$again=$fsCall(15,'confirm',['preview'=>$cut['preview']]);$ok($again['status']===200&&$again['value']['replayed']&&$again['value']['id']===$m1['id'],'same mapping key no duplication');$fsReject(fn()=>$fsCall(15,'preview',$fsInput('mapping','review',$bm+1,['decision'=>'approved'])),403,'preparer cannot review');$fsReject(fn()=>$fsCall(16,'preview',$input),403,'reviewer cannot prepare');$fsReject(fn()=>$fsCall(17,'preview',$input),403,'reader cannot prepare');$stale=$fsCall(16,'preview',$fsInput('mapping','review',$bm+1,['decision'=>'approved']));[$m2]=$fsCommand(15,$fsInput('mapping','prepare',$bm+1,$fsMapping));$fsReject(fn()=>$fsCall(16,'confirm',['preview'=>$stale['value']['preview']]),409,'stale review refused');[$m3]=$fsCommand(16,$fsInput('mapping','review',$bm+2,['decision'=>'approved']));$ok($m3['state']==='approved','distinct mapping review');
$resultData=['mapping_id'=>$m3['id'],'from_on'=>'2026-11-10','to_on'=>'2026-11-10','per_page'=>25,'evidence'=>'Resultado parcial QA, mesmas fontes do balancete original, sem capital presumido.'];$result=$fsInput('result','prepare',$br,$resultData);$p=$fsCall(15,'preview',$result);$ok(($p['value']['impact']['data']['summary']['currencies']['MZN']['equity_minor']??null)===null,'capital not fabricated');$ok($native->table('fs_results')->count()===0&&$native->table('fs_result_lines')->count()===0,'snapshot and lines preview rolled back');[$r1,$rp,$rk]=$fsCommand(15,$result);$summary=$r1['impact']['data']['summary'];$ok($summary['accounts']===0?$summary['currencies']===[]:($summary['currencies']['MZN']['unclassified_accounts']>0&&$summary['currencies']['MZN']['complete_result_minor']===null),'unclassified prevents full result');$ok($native->table('fs_result_lines')->where('snapshot_key',$r1['impact']['data']['snapshot_key'])->count()===$summary['accounts'],'original rows conserved once');$fsReject(fn()=>$fsCall(17,'export',[],(string)$r1['id']),403,'unreviewed result not exported');[$r2]=$fsCommand(16,$fsInput('result','review',$br+1,['decision'=>'approved']));$download=$fsCall(17,'export',[],(string)$r2['id']);$ok($download['status']===200&&hash('sha256',$download['bytes'])===$download['sha256']&&$download['sha256']===$r1['impact']['data']['csv_sha256'],'reviewed frozen CSV checksum');$ok(str_contains($download['bytes'],'SEM MODELO LEGAL')&&str_contains($download['bytes'],'INDETERMINADO'),'export explicitly partial and capital unknown');$read=$fsCall(17,'index',['view'=>'result','version_id'=>$r2['id'],'per_page'=>25]);$ok($read['value']['current_source_matches']===true,'current native source matches frozen result');$read100=$fsCall(17,'index',['view'=>'result','version_id'=>$r2['id'],'per_page'=>100]);$ok($read['value']['records']['data']===$read100['value']['records']['data'],'known result25/100 equivalent');
// Compare the frozen row projection directly to the original engine; no other balance formula.
$original=$call('read_only','view',fn($e)=>$e->trialBalance($resultData=array_intersect_key($resultData,array_flip(['from_on','to_on','per_page']))));$by=collect($read100['value']['records']['data'])->keyBy('account_id');foreach($original['rows']as$row){$saved=$by[$row['account_id']];$ok((string)$row['closing_net_minor']===$saved['closing_net_minor']&&(string)$row['debit_minor']===$saved['debit_minor']&&(string)$row['credit_minor']===$saved['credit_minor'],'original values account '.$row['code']);}
$own=$fsCall(15,'receipt',[],$key,'new-session');$ok($own['value']['historical_receipt_only']&&!$own['value']['operation_executed'],'own receipt recovered fresh session');$fsReject(fn()=>$fsCall(16,'receipt',[],$key),404,'other actor receipt hidden');$fsReject(fn()=>$fsCall(17,'index',['view'=>'result','version_id'=>2147483647]),403,'foreign result refused');$fsReject(fn()=>$fsCall(17,'index',['tenant_id'=>2]),422,'context override refused');$fsReject(fn()=>$fsCall(17,'index',['per_page'=>1000]),422,'unbounded page refused');$fsReject(fn()=>$fsCall(17,'index',[],null,'statements-proof','finance'),409,'wrong module refused');
$bad=$fsMapping;$bad['entries'][0]['account_id']=2147483647;$fsReject(fn()=>$fsCall(15,'preview',$fsInput('mapping','prepare',$bm+3,$bad)),403,'foreign account refused');$bad=$fsMapping;$bad['entries'][]=$bad['entries'][0];$fsReject(fn()=>$fsCall(15,'preview',$fsInput('mapping','prepare',$bm+3,$bad)),422,'duplicate account refused');$bad=$fsMapping;$bad['entries'][0]['category']='equity';$fsReject(fn()=>$fsCall(15,'preview',$fsInput('mapping','prepare',$bm+3,$bad)),422,'unapproved equity classification refused');
// Mapping revision makes current-source status false but old reviewed export stays byte-identical.
[$successor]=$fsCommand(15,$fsInput('mapping','prepare',$bm+3,$fsMapping));[$newMap]=$fsCommand(16,$fsInput('mapping','review',$bm+4,['decision'=>'approved']));$outdated=$fsCall(17,'index',['view'=>'result','version_id'=>$r2['id']]);$ok($outdated['value']['current_source_matches']===false,'new approved mapping marks old snapshot historic');$oldAgain=$fsCall(17,'export',[],(string)$r2['id']);$ok($oldAgain['bytes']===$download['bytes'],'historic export never silently recalculated');
$fresh=$fsCall(15,'preview',$fsInput('mapping','prepare',$bm+5,$fsMapping));$fsReject(fn()=>$fsCall(15,'confirm',['preview'=>$fresh['value']['preview']],null,'changed-generation'),409,'changed context refused');$host->table('permission_grants')->where(['user_id'=>15,'permission'=>'general_ledger.statements.prepare','unit_id'=>2])->delete();$fsReject(fn()=>$fsCall(15,'confirm',['preview'=>$fresh['value']['preview']]),403,'revoked right revalidated');
}finally{while($native->transactionLevel()>0)$native->rollBack();while($host->transactionLevel()>0)$host->rollBack();}
foreach($before as$t=>$json)$ok($native->table($t)->orderBy('id')->get()->toJson()===$json,'original '.$t.' unchanged');$ok($host->table('permission_grants')->orderBy('id')->get()->toJson()===$grants,'test grants rolled back');file_put_contents($work.'/statements-result.json',json_encode(['passed'=>true,'checks'=>$checks,'fixtures_rolled_back'=>true,'new_journals'=>0,'new_demo'=>false],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/statements-result.json']).PHP_EOL;