Files
SGF/scripts/industrial-budget-manual-verify.php
T

50 lines
6.5 KiB
PHP

<?php
// Reconcile the preserved manual demonstration without changing test data or credentials.
require __DIR__.'/../vendor/autoload.php';$app=require __DIR__.'/../bootstrap/app.php';$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
use App\Modules\Industry\IndustrialBudgets;
use App\Modules\Identity\Services\Permissions;
use Illuminate\Support\Facades\DB;
function checkManualBudget(bool $ok,string $reason): void {if(!$ok)throw new RuntimeException($reason);}
function canonicalManualBudget(mixed $v): mixed {if(!is_array($v))return $v;if(!array_is_list($v))ksort($v);return array_map('canonicalManualBudget',$v);}
IndustrialBudgets::guard();
$identity=DB::selectOne('SELECT current_database() AS db,current_user AS role,inet_server_port() AS port');
checkManualBudget($identity->db==='sgf_tempo_test'&&$identity->role==='sgf_test_runner'&&(int)$identity->port===55432,'Unexpected database or role.');
$fixture=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);
$lot=$fixture['budget_fixture']['lot'];$manager=$fixture['manager_id'];$reader=$fixture['reader_id'];
$comparisons=DB::table('ib_comparisons')->where('lot_id',$lot)->orderBy('version')->get();
checkManualBudget($comparisons->count()===2,'Expected exactly two manual comparisons.');
$proof=[];
foreach($comparisons as $index=>$c){
$snapshot=json_decode($c->snapshot,true,64,JSON_THROW_ON_ERROR);$budget=DB::table('ib_budgets')->find($c->budget_id);$result=DB::table('ic_results')->find($c->result_id);
checkManualBudget($budget&&$result&&$budget->lot_id===$lot&&$result->lot_id===$lot,'Cross-lot reference.');
checkManualBudget($budget->actor_id===$manager&&$c->actor_id===$manager,'Unexpected preparer.');
foreach([['ib_decisions','budget_id',$budget],['ic_decisions','result_id',$result]] as [$table,$column,$target]){
$decision=DB::table($table)->where([$column=>$target->id,'version'=>$target->version,'decision'=>'approved'])->first();
checkManualBudget($decision&&$decision->actor_id===$reader&&$decision->actor_id!==$target->actor_id,'Distinct exact validation missing.');
}
$actual=json_decode($result->snapshot,true,64,JSON_THROW_ON_ERROR);$amounts=json_decode($budget->amounts,true);
checkManualBudget($amounts==['material'=>'900','labour'=>'600','allocated'=>'300'],'Budget amounts differ.');
$expected=IndustrialBudgets::variance($amounts,['material'=>$actual['material_exact'],'labour'=>$actual['labour_exact'],'allocated'=>$actual['allocated_exact']]);
foreach($expected as $field=>$value)checkManualBudget(canonicalManualBudget($snapshot[$field])===canonicalManualBudget($value),'Comparison arithmetic differs: '.$field);
checkManualBudget($snapshot['budget']===['id'=>$budget->id,'version'=>$budget->version]&&$snapshot['result']['id']===$result->id&&$snapshot['result']['version']===$result->version&&$snapshot['result']['refs']===$actual['refs'],'Exact version references differ.');
checkManualBudget($snapshot['residual_separate']==='300'&&$actual['residual_exact']==='300'&&$snapshot['actual_reconciliation']===$actual['reconciliation'],'Residual or reconciliation differs.');
checkManualBudget($actual['good_units']==='10'&&$actual['currency']==='MZN','Quantity or currency differs.');
checkManualBudget($actual['labour_exact']==='500'&&$actual['allocated_exact']==='300','Known components differ.');
if($index===0){
checkManualBudget($actual['material_exact']==='1000'&&$actual['known_cost_exact']==='1800'&&$actual['selected_total_exact']==='1800'&&$actual['unit_cost_exact']==='180'&&!$expected['incomplete']&&$expected['total_delta']==='0','Full scenario differs.');
checkManualBudget($snapshot['actual_reconciliation']['known_origins_exact']==='2100','Full origins differ.');
}else{
checkManualBudget($actual['material_exact']===null&&$actual['known_cost_exact']==='800'&&$actual['selected_total_exact']===null&&$actual['unit_cost_exact']===null&&$expected['incomplete']&&$expected['total_delta']===null&&$expected['total_percent']===null,'Incomplete scenario differs.');
checkManualBudget($expected['rows']['material']['delta']===null&&$expected['paired_delta']==='-100'&&$snapshot['actual_reconciliation']['known_origins_exact']==='1100','Missing material treated incorrectly.');
checkManualBudget($result->previous_id===$comparisons[0]->result_id&&$c->previous_id===$comparisons[0]->id&&$c->budget_id===$comparisons[0]->budget_id,'History chain differs.');
}
$proof[]=['comparison_id'=>$c->id,'version'=>$c->version,'budget_id'=>$budget->id,'budget_version'=>$budget->version,'result_id'=>$result->id,'result_version'=>$result->version,'actual_known'=>$actual['known_cost_exact'],'total_delta'=>$expected['total_delta'],'incomplete'=>$expected['incomplete'],'residual_separate'=>'300','exact_arithmetic_verified'=>true,'distinct_validation_verified'=>true];
}
$permissions=app(Permissions::class);
foreach([$manager=>['industrial_budgets.view','industrial_budgets.prepare','industrial_budget_comparisons.view','industrial_budget_comparisons.prepare'],$reader=>['industrial_budgets.view','industrial_budgets.validate','industrial_budget_comparisons.view']] as $actor=>$rights)foreach($rights as $right)$permissions->require($actor,$fixture['tenant'],$fixture['company'],$right,$fixture['unit']);
foreach([[$manager,'industrial_budgets.validate'],[$reader,'industrial_budgets.prepare'],[$reader,'industrial_budget_comparisons.prepare']] as [$actor,$right])checkManualBudget(!$permissions->has($actor,$fixture['tenant'],$fixture['company'],$right,$fixture['unit']),'Unexpected functional power.');
checkManualBudget(!DB::table('wo_entries')->exists()&&!DB::table('dc_calculations')->exists(),'Unexpected workshop/direct cost origins.');
$report=['policy'=>IndustrialBudgets::POLICY,'date'=>'2026-10-28','database'=>'sgf_tempo_test','method'=>'manual_user_report_and_read_only_database_reconciliation','manual_user_reported_complete_and_incomplete'=>true,'manual_user_confirmed_reader_forms_absent'=>true,'screenshots_received'=>false,'automated_browser_recovered'=>false,'comparisons'=>$proof,'role_separation_verified'=>true,'duplicate_workshop_or_direct_cost_origins'=>false,'test_records_changed'=>false,'credentials_changed'=>false,'user_acceptance'=>false,'closing_checkpoint_created'=>false];
file_put_contents(base_path('docs/proofs/industrial-budget-manual-results.json'),json_encode($report,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR).PHP_EOL);
echo "Manual report reconciled: two exact comparisons, distinct validations, preserved history, residual and role separation. No database or credential changes. Screenshots remain unavailable.\n";