Close isolated direct cost trial by delegated technical acceptance
Preserve verified 275 MZN and incomplete 175 MZN demonstrations. Keep 090000 and four real cost grants unapplied; real costing and fiscal blocks remain intact.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
require __DIR__.'/../vendor/autoload.php';$app=require __DIR__.'/../bootstrap/app.php';$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
use App\Modules\Workshop\DirectCosts;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Process\Process;
|
||||
DirectCosts::guard();$f=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);$fx=$f['direct_costs'];$proof=[];$key=fn()=>(string)Str::uuid();
|
||||
$base=['tenant'=>$f['tenant'],'company'=>$f['company'],'unit'=>$f['unit'],'order'=>$fx['order']];
|
||||
$pair=function($one,$two)use($base){
|
||||
$a=new Process([PHP_BINARY,'scripts/direct-cost-worker.php','--env=testing'],base_path(),null,json_encode($one+$base+['pause'=>true]));$b=new Process([PHP_BINARY,'scripts/direct-cost-worker.php','--env=testing'],base_path(),null,json_encode($two+$base));
|
||||
$a->start();if(!$a->waitUntil(fn($t,$v)=>str_contains($v,'LOCK_HELD')))throw new RuntimeException('Concurrent lock not observed.');$b->start();$a->wait();$b->wait();$out=[];
|
||||
foreach([$a,$b] as $p){if(!$p->isSuccessful())throw new RuntimeException('Cost worker failed.');$lines=array_values(array_filter(explode("\n",trim($p->getOutput())),fn($v)=>str_starts_with($v,'{')));$out[]=json_decode(end($lines),true,32,JSON_THROW_ON_ERROR);}return $out;
|
||||
};
|
||||
$check=function($out,bool $replay){if($out[0]['status']!==200||$out[1]['status']!==($replay?200:409)||($replay&&(!$out[1]['result']['replayed']||$out[0]['result']['id']!==$out[1]['result']['id'])))throw new RuntimeException('Unexpected concurrent outcome.');};
|
||||
$rate=['method'=>'rate','actor'=>$f['manager_id'],'key'=>$key(),'input'=>['entry_id'=>$fx['material'],'entry_version'=>1,'identifier'=>'TEST-CONCURRENT-'.Str::uuid(),'version'=>1,'previous_id'=>null,'currency'=>'MZN','unit'=>'UN','amount'=>'1','evidence'=>'Taxa sintética concorrente.']];
|
||||
$out=$pair($rate,$rate);$check($out,true);$proof['rate_repetition_one_version']=true;$rid=$out[0]['result']['id'];
|
||||
$decision=['method'=>'decide','actor'=>$f['reader_id'],'key'=>$key(),'input'=>['kind'=>'rate','id'=>$rid,'version'=>1,'decision'=>'approved','reason'=>'Validação sintética concorrente.']];$check($pair($decision,$decision),true);$proof['decision_repetition_one_effect']=true;
|
||||
$one=$rate;$one['key']=$key();$one['input']['version']=2;$one['input']['previous_id']=$rid;$two=$one;$two['key']=$key();$two['input']['amount']='2';$check($pair($one,$two),false);$proof['competing_rate_versions_one_winner']=true;
|
||||
$rates=DB::table('dc_rates')->where('order_id',$fx['order'])->whereIn('identifier',['TEST-TRABALHO','TEST-MATERIAL'])->get()->keyBy('identifier');
|
||||
$lines=[['entry_id'=>$fx['time'],'entry_version'=>1,'rate_id'=>$rates['TEST-TRABALHO']->id,'rate_version'=>1],['entry_id'=>$fx['material'],'entry_version'=>1,'rate_id'=>$rates['TEST-MATERIAL']->id,'rate_version'=>1]];
|
||||
$last=DB::table('dc_calculations')->where('order_id',$fx['order'])->orderByDesc('version')->first();
|
||||
$calc=['method'=>'calculate','actor'=>$f['manager_id'],'key'=>$key(),'input'=>['currency'=>'MZN','version'=>$last->version+1,'previous_id'=>$last->id,'lines'=>$lines]];$out=$pair($calc,$calc);$check($out,true);$proof['calculation_repetition_one_snapshot']=true;
|
||||
$last=DB::table('dc_calculations')->where('order_id',$fx['order'])->orderByDesc('version')->first();$calc['key']=$key();$calc['input']['version']=$last->version+1;$calc['input']['previous_id']=$last->id;$calc['input']['lines'][1]['rate_id']=null;$calc['input']['lines'][1]['rate_version']=null;$two=$calc;$two['key']=$key();$two['input']['lines']=$lines;$two['input']['lines'][0]['rate_id']=null;$two['input']['lines'][0]['rate_version']=null;$out=$pair($calc,$two);$check($out,false);$proof['competing_calculations_one_revision']=true;
|
||||
$last=DB::table('dc_calculations')->where('order_id',$fx['order'])->orderByDesc('version')->first();$calc['key']=$key();$calc['input']['version']=$last->version+1;$calc['input']['previous_id']=$last->id;$calc['input']['lines']=$lines;
|
||||
$oldDecision=['method'=>'decide','actor'=>$f['reader_id'],'key'=>$key(),'input'=>['kind'=>'calculation','id'=>$last->id,'version'=>$last->version,'decision'=>'approved','reason'=>'Decisão de uma versão agora antiga.']];$check($pair($calc,$oldDecision),false);$proof['new_calculation_refuses_old_approval']=true;
|
||||
file_put_contents(base_path('docs/proofs/direct-cost-concurrency.json'),json_encode(['database'=>'sgf_tempo_test','overlapping_processes_per_case'=>2,'checks'=>$proof,'synthetic_only'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Six overlapping-process direct cost scenarios passed.\n";
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// Read-only. No apply branch; four prospective cost grants only.
|
||||
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 SGF development database required.');
|
||||
$db=\Illuminate\Support\Facades\DB::class;$grants=[];
|
||||
foreach([1=>['view','prepare'],2=>['view','validate']] as $user=>$rights){
|
||||
$u=$db::table('users')->where('id',$user)->first(['id','name','email']);if(!$u||!$db::table('company_memberships')->where(['user_id'=>$user,'tenant_id'=>1,'company_id'=>1])->exists())throw new RuntimeException('Existing account/member required.');
|
||||
foreach($rights as $r){$permission='direct_costs.'.$r;$scope=['user_id'=>$user,'tenant_id'=>1,'company_id'=>1,'unit_id'=>1,'permission'=>$permission];$grants[]=$scope+['account'=>$u,'already_granted'=>$db::table('permission_grants')->where($scope)->exists()];}
|
||||
}
|
||||
$company=$db::table('companies')->where(['id'=>1,'tenant_id'=>1])->value('name');$unit=$db::table('units')->where(['id'=>1,'company_id'=>1,'tenant_id'=>1])->value('name');if($company!=='Tempo Lda'||$unit!=='SEGI')throw new RuntimeException('Expected scope differs.');
|
||||
try{\App\Modules\Workshop\DirectCosts::guard();throw new RuntimeException('Real costing not blocked.');}catch(\Symfony\Component\HttpKernel\Exception\HttpException $e){if($e->getStatusCode()!==403)throw $e;}
|
||||
echo json_encode(['mode'=>'preview_only','company'=>$company,'unit'=>$unit,'proposed_grants'=>$grants,'applied'=>false,'real_costing_blocked'=>true,'migration_090000_applied'=>$db::table('migrations')->where('migration','2026_10_27_090000_create_workshop_direct_costs')->exists()],JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR).PHP_EOL;
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
require __DIR__.'/../vendor/autoload.php';$app=require __DIR__.'/../bootstrap/app.php';$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
\App\Modules\Workshop\DirectCosts::guard();$f=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);$fx=$f['direct_costs'];$db=\Illuminate\Support\Facades\DB::class;
|
||||
$rows=$db::table('dc_calculations')->where('order_id',$fx['order'])->orderBy('id')->get();if($rows->count()<2)throw new RuntimeException('Two browser calculations required.');
|
||||
$full=json_decode($rows[0]->snapshot,true);$partial=json_decode($rows[1]->snapshot,true);
|
||||
if($full['known_subtotal_exact']!=='275'||$full['incomplete']||$partial['known_subtotal_exact']!=='175'||!$partial['incomplete']||$partial['selected_direct_total_exact']!==null||count($partial['pending'])!==1)throw new RuntimeException('Browser arithmetic/completeness differs.');
|
||||
foreach([$rows[0],$rows[1]] as $c){$decision=$db::table('dc_decisions')->where(['calculation_id'=>$c->id,'decision'=>'approved'])->first();if(!$decision||$decision->actor_id===$c->actor_id)throw new RuntimeException('Distinct approval required.');}
|
||||
if($db::table('wo_entries')->where('order_id',$fx['order'])->count()!==7)throw new RuntimeException('Technical events duplicated.');
|
||||
$proof=['policy'=>\App\Modules\Workshop\DirectCosts::POLICY,'database'=>'sgf_tempo_test','synthetic_only'=>true,'real_browser_login'=>true,'csrf_enabled'=>true,'distinct_cost_roles'=>true,'original_workshop_entries'=>7,'technical_events_not_duplicated'=>true,'entry_versions'=>[['id'=>$fx['time'],'version'=>1],['id'=>$fx['material'],'version'=>1]],'full_selected_direct'=>['material'=>'100','labour'=>'175','known_subtotal'=>'275','complete_cost_claimed'=>false],'missing_material_rate'=>['known_subtotal'=>'175','selected_total'=>null,'incomplete'=>true,'pending'=>true],'both_exact_versions_approved'=>true,'real_grants_applied'=>false,'development_migration_applied'=>false];
|
||||
file_put_contents(base_path('docs/proofs/direct-cost-browser-results.json'),json_encode($proof,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Browser results verified: 275 known, 175 incomplete, distinct approval, seven original technical entries unchanged in number.\n";
|
||||
Reference in New Issue
Block a user