Files
SGF/scripts/sgf-human-resources-preservation.php
2026-11-11 09:00:17 +02:00

7 lines
5.0 KiB
PHP

<?php
$book='8085';require __DIR__.'/sgf-single-bootstrap.php';$pointer=json_decode(file_get_contents($s['root'].'/remunerations-backup-pointer.json'),true);$before=json_decode(file_get_contents($pointer['root'].'/preservation-private.json'),true);$checks=[];$same=[];$append=[];$ok=function($v,$n)use(&$checks){if(!$v)throw new LogicException($n);$checks[]=$n;};$p=json_decode(file_get_contents($repo.'/docs/integration/sgf-human-resources-access-proposal.json'),true);$profiles=$host->table('hr_qa_profiles')->orderBy('profile_key')->get();$ids=$profiles->pluck('user_id')->all();
foreach(['host','native']as$name){$db=$name==='host'?$host:$native;foreach($before[$name]as$table=>$json){$order=in_array($table,['sr_outbox','sr_deliveries'],true)?'event_id':(in_array($table,['sc_catalogue_meta','pd_due_meta','fo_meta','fs_meta'],true)?'policy':(in_array($table,['fs_results','fs_result_lines'],true)?'snapshot_key':'id'));$rows=$db->table($table)->orderBy($order)->when($table==='fs_result_lines',fn($q)=>$q->orderBy('account_id'))->get();if($name==='host'&&$table==='permission_grants'){$old=collect(json_decode($json,true))->keyBy('id');$original=$rows->filter(fn($r)=>$old->has($r->id))->values();$ok($original->toJson()===$json,'old permission grants identical');$new=$rows->reject(fn($r)=>$old->has($r->id))->values();$expected=[];foreach($p['profiles']as$profile){$id=$profiles->firstWhere('profile_key',$profile['id'])->user_id;foreach($profile['rights']as$r)$expected[]=['user_id'=>$id,'permission'=>$r['permission'],'tenant_id'=>1,'company_id'=>2,'unit_id'=>$r['scope']==='unit'?2:null];}$actual=$new->map(fn($r)=>['user_id'=>$r->user_id,'permission'=>$r->permission,'tenant_id'=>$r->tenant_id,'company_id'=>$r->company_id,'unit_id'=>$r->unit_id])->all();$sort=fn($r)=>$r['user_id'].'/'.$r['permission'];usort($expected,fn($a,$b)=>strcmp($sort($a),$sort($b)));usort($actual,fn($a,$b)=>strcmp($sort($a),$sort($b)));$ok($actual===$expected&&count($actual)===11,'only exact11 approved HR grants appended');$append[]='host.permission_grants';}else{$ok($rows->toJson()===$json,'original identical '.$name.'.'.$table);$same[]=$name.'.'.$table;}}}
foreach(['binding'=>'binding.json','single_binding'=>'single-bindings.json','stock_binding'=>'stock-binding.json']as$key=>$file)$ok(hash_file('sha256',$s['root'].'/'.$file)===$before[$key],$file.' unchanged');$ok(count($ids)===3&&count(array_intersect($ids,range(1,17)))===0,'three new separate QA identities');$ok($host->table('company_memberships')->whereIn('user_id',$ids)->get()->every(fn($r)=>$r->tenant_id===1&&$r->company_id===2),'new memberships only approved company');$ok(!$host->table('permission_grants')->where('permission','like','remunerations.%')->exists(),'obsolete finance payroll grants absent');$versions=$host->table('rm_versions')->orderBy('id')->get();$ok($versions->count()===6&&$versions->pluck('kind')->all()===['worker','worker','rules','rules','payroll','payroll'],'exactly six individual versions');$prepare=$profiles->firstWhere('profile_key','hr_prepare')->user_id;$review=$profiles->firstWhere('profile_key','hr_review')->user_id;$ok($versions->pluck('actor_id')->all()===[$prepare,$review,$prepare,$review,$prepare,$review],'separate actors each source and payroll');$ok($host->table('rm_commands')->count()===6,'six technical receipts no duplicate');$row=json_decode($versions->last()->data,true);$ok($row['calculation']['net_cents']==='890000'&&$row['calculation']['employer_cost_cents']==='1040000','persisted approved amounts exact');$ok(hash('sha256',$row['artifact_csv'])===$row['artifact_sha256']&&file_get_contents($s['root'].'/hr-frozen-export-private.csv')===$row['artifact_csv'],'download equals immutable approved bytes');$ok($native->table('gl_journals')->count()===27&&$native->table('gl_accounts')->where(['tenant_id'=>1,'company_id'=>2,'unit_id'=>2])->count()===19,'27 journal entries19accounts unchanged');
$public=$host->select("SELECT c.relname FROM pg_class c CROSS JOIN LATERAL aclexplode(coalesce(c.relacl,CASE WHEN c.relkind='S' THEN acldefault('S',c.relowner) ELSE acldefault('r',c.relowner) END)) a WHERE c.relnamespace='public'::regnamespace AND c.relname IN('hr_qa_profiles','rm_meta','rm_versions','rm_commands','rm_versions_id_seq','rm_commands_id_seq') AND a.grantee=0");$ok($public===[],'no PUBLIC table or sequence rights');$pubfun=$host->select("SELECT a.* FROM pg_proc p CROSS JOIN LATERAL aclexplode(coalesce(p.proacl,acldefault('f',p.proowner))) a WHERE p.pronamespace='public'::regnamespace AND p.proname='rm_append_only' AND a.grantee=0");$ok($pubfun===[],'no PUBLIC append-only function execution');
file_put_contents($work.'/hr-preservation.json',json_encode(['passed'=>true,'checks'=>$checks,'identical_original_tables'=>$same,'original_rows_preserved_with_authorized_append'=>$append,'new_hr_profiles'=>3,'new_grants'=>11,'new_versions'=>6,'new_receipts'=>6,'new_journals'=>0,'cache_cleared'=>false,'protected_six_targeted'=>false],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'identical_original_tables'=>count($same),'proof'=>$work.'/hr-preservation.json']).PHP_EOL;