Files
SGF/scripts/service-fiscal-preservation.php

35 lines
3.7 KiB
PHP

<?php
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
if(!\App\Modules\Identity\Services\LocalDatabaseGuard::allows()) throw new RuntimeException('Own local development database required.');
$db=\Illuminate\Support\Facades\DB::class;
$file=storage_path('app/service-fiscal-preservation-before.json');
$before=in_array('--before',$argv,true); $grants=in_array('--after-grants',$argv,true);
$baseline=$before ? null : json_decode(file_get_contents($file),true,64,JSON_THROW_ON_ERROR);
$tables=$before ? array_map(fn($r)=>$r->tablename,$db::select("SELECT tablename FROM pg_tables WHERE schemaname='public' AND (tablename ~ '^(a_|b_|ca_|doc_|document_|service_calculation_)' OR tablename IN ('users','tenants','companies','units','company_memberships','permission_grants','identity_audit')) ORDER BY tablename")) : array_keys($baseline['tables']);
$summary=[];
foreach($tables as $table) {
if(!preg_match('/^[a-z_]+$/D',$table)) throw new RuntimeException('Unexpected table.');
$where='';
if($grants && in_array($table,['permission_grants','identity_audit'],true)) $where=' WHERE id <= '.(int)$baseline['max'][$table];
$r=$db::selectOne('SELECT count(*) AS n, md5(COALESCE(string_agg(row_to_json(t)::text, E\'\\n\' ORDER BY row_to_json(t)::text),\'\')) AS fingerprint FROM (SELECT * FROM "'.$table.'"'.$where.') t');
$summary[$table]=['count'=>(int)$r->n,'hash'=>$r->fingerprint];
}
if($before) {
file_put_contents($file,json_encode(['tables'=>$summary,'max'=>['permission_grants'=>(int)$db::table('permission_grants')->max('id'),'identity_audit'=>(int)$db::table('identity_audit')->max('id')]],JSON_THROW_ON_ERROR));
echo "Existing SGF development records fingerprinted in database; no record contents disclosed.\n"; exit;
}
if($summary!==$baseline['tables']) throw new RuntimeException('Existing SGF record fingerprint changed; review before proceeding.');
foreach(['sf_proposals','sf_decisions','sf_activation_events'] as $table) if($db::table($table)->count()!==0) throw new RuntimeException('Unexpected real fiscal lifecycle record.');
if($grants) {
$actual=$db::table('permission_grants')->where('permission','like','service_fiscal.%')->orderBy('user_id')->orderBy('permission')->get(['user_id','tenant_id','company_id','unit_id','permission'])->map(fn($r)=>(array)$r)->all();
$expected=[];
foreach([1=>['view','propose','calculate'],2=>['view','approve']] as $user=>$actions) foreach($actions as $a) $expected[]=['user_id'=>$user,'tenant_id'=>1,'company_id'=>1,'unit_id'=>$a==='calculate'?1:null,'permission'=>'service_fiscal.'.$a];
usort($expected,fn($a,$b)=>[$a['user_id'],$a['permission']]<=>[$b['user_id'],$b['permission']]);
if($actual!==$expected) throw new RuntimeException('Fiscal grants differ from specifically confirmed matrix.');
$new=$db::table('identity_audit')->where('id','>',$baseline['max']['identity_audit'])->where('action','service_fiscal.initial_grant')->count();
if($new!==5) throw new RuntimeException('Expected five audited initial grants.');
}
file_put_contents(base_path('docs/proofs/service-fiscal-dev-preservation'.($grants?'-grants':'').'.json'),json_encode(['database'=>'sgf_tempo_dev','preexisting_tables_checked'=>count($summary),'preexisting_records_unchanged'=>true,'new_fiscal_tables_empty'=>true,'real_profile_activated'=>false,'confirmed_grants_verified'=>$grants?5:0,'prior_grants_and_audit_unchanged'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo $grants ? "Exactly five confirmed fiscal grants and audit entries verified; prior records preserved; no real profile created or activated.\n" : "Migration preserved existing records and grants; all new fiscal tables empty.\n";