Files
SGF/scripts/sgf-hr-personnel-boundaries.php
2026-11-11 09:00:17 +02:00

17 lines
8.7 KiB
PHP

<?php
require __DIR__.'/sgf-hr-personnel-proof-bootstrap.php';require __DIR__.'/sgf-hr-personnel-fixture.php';$counts=[$host->table('hp_versions')->count(),$host->table('hp_commands')->count(),$host->table('rm_versions')->count(),$host->table('identity_audit')->count()];$host->beginTransaction();try{
$f2=$hpFixture('FRONTEIRAS');$k=$f2['keys'];$inputs=$f2['inputs'];$leave=$f2['leave'];$lk=$uuid();$cut=$hpPreview(13,$hpInput('leave',$lk,0,$leave));$dc=array_replace($inputs['day10'],['operation'=>'amend','planned'=>[['from'=>480,'to'=>720]]]);[$a]=$hpCommand(13,$hpInput('day',$k['day10'],2,$dc));$reject(fn()=>$hpConfirm(13,$cut['preview']),409,'direct referenced prepared version invalidates pending leave');[$a]=$hpCommand(14,$hpInput('day',$k['day10'],3,['decision'=>'refused'],'review'));
[$a]=$hpCommand(13,$hpInput('leave',$lk,0,$leave));$ownReview=$hpPreview(14,$hpInput('leave',$lk,1,['decision'=>'approved'],'review'));[$a]=$hpCommand(13,$hpInput('leave',$lk,1,$leave));$reject(fn()=>$hpConfirm(14,$ownReview['preview']),409,'new own version invalidates old review');[$a]=$hpCommand(14,$hpInput('leave',$lk,2,['decision'=>'approved'],'review'));$reject(fn()=>$hpPreview(13,$hpInput('leave',$uuid(),0,$leave)),409,'overlapping leave requests refused');
$cancel=array_replace($leave,['operation'=>'cancel']);[$a]=$hpCommand(13,$hpInput('leave',$lk,3,$cancel));[$a]=$hpCommand(14,$hpInput('leave',$lk,4,['decision'=>'approved'],'review'));$unknown=array_replace($inputs['entitlement'],['operation'=>'amend','minutes'=>null]);[$a]=$hpCommand(13,$hpInput('entitlement',$k['entitlement'],2,$unknown));[$a]=$hpCommand(14,$hpInput('entitlement',$k['entitlement'],3,['decision'=>'approved'],'review'));$reject(fn()=>$hpPreview(13,$hpInput('leave',$uuid(),0,$leave)),409,'unknown entitlement cannot silently allow a request');
$restore=array_replace($inputs['entitlement'],['operation'=>'amend','minutes'=>240]);[$a]=$hpCommand(13,$hpInput('entitlement',$k['entitlement'],4,$restore));[$a]=$hpCommand(14,$hpInput('entitlement',$k['entitlement'],5,['decision'=>'approved'],'review'));$reject(fn()=>$hpPreview(13,$hpInput('leave',$uuid(),0,$leave)),409,'insufficient budget refused');
$dayWork=array_replace($inputs['day10'],['operation'=>'amend','actual'=>$ranges]);[$a]=$hpCommand(13,$hpInput('day',$k['day10'],4,$dayWork));[$a]=$hpCommand(14,$hpInput('day',$k['day10'],5,['decision'=>'approved'],'review'));$reject(fn()=>$hpPreview(13,$hpInput('leave',$uuid(),0,$leave)),409,'work conflicts with requested leave');
$fresh=$uuid();$small=array_replace($inputs['day11'],['operation'=>'amend','actual'=>[],'leave_conferred'=>true]);$cut=$hpPreview(13,$hpInput('day',$k['day11'],2,$small));$before=[$host->table('hp_versions')->count(),$host->table('hp_commands')->count(),$host->table('identity_audit')->count()];$host->beginTransaction();try{$host->unprepared("CREATE FUNCTION pg_temp.hp_qa_failure() RETURNS trigger LANGUAGE plpgsql AS \$\$ BEGIN RAISE EXCEPTION 'QA_HP_AFTER_VERSION'; END; \$\$;REVOKE ALL ON FUNCTION pg_temp.hp_qa_failure() FROM PUBLIC;CREATE TRIGGER hp_qa_failure BEFORE INSERT ON hp_commands FOR EACH ROW EXECUTE FUNCTION pg_temp.hp_qa_failure();");try{$hpConfirm(13,$cut['preview']);throw new LogicException('Expected failure');}catch(Illuminate\Database\QueryException$e){$ok(str_contains($e->getMessage(),'QA_HP_AFTER_VERSION'),'failure after version before receipt injected');}}finally{$host->rollBack();}$ok([$host->table('hp_versions')->count(),$host->table('hp_commands')->count(),$host->table('identity_audit')->count()]===$before,'failed command rolls back version receipt audit atomically');$a=$hpConfirm(13,$cut['preview']);$b=$hpConfirm(13,$cut['preview']);$ok(!$a['replayed']&&$b['replayed']&&$a['id']===$b['id'],'recovery after failure exact once');
$reject(fn()=>$hpPreview(14,$hpInput('contract',$uuid(),0,$contractData)),403,'reviewer cannot prepare contracts');$reject(fn()=>$hpPreview(13,$hpInput('contract',$uuid(),0,array_replace($contractData,['valid_to'=>'2026-10-31']))),422,'inverted dates refused');$reject(fn()=>$hpPreview(13,$hpInput('day',$uuid(),0,array_replace($inputs['day11'],['date'=>'2026-11-12','actual'=>[]]))),422,'future actual declaration refused');$select(13,'rm-proof','finance');$reject(fn()=>$hp->listing($hrActor(13),['kind'=>'contract']),403,'cross module personnel read refused');$select(13);$reject(fn()=>$hp->listing($hrActor(13),['kind'=>'contract','tenant_id'=>2]),422,'listing tenant override denied');$reject(fn()=>$hp->listing($hrActor(13),['kind'=>'contract','record_key'=>$uuid()]),403,'unknown foreign history denied');
// A second worker and contract exercise the salary link without changing old salary records.
$wk=$uuid();[$a]=$command(13,$input('worker',$wk,0,['operation'=>'establish','code'=>'QA-PESSOAL-OUTRA','status'=>'active']));[$a]=$command(14,$input('worker',$wk,1,['decision'=>'approved'],'review'));$ck=$uuid();$newC=array_replace($contractData,['worker_key'=>$wk,'reference'=>'QA-OUTRA-CONTRATO']);[$a]=$hpCommand(13,$hpInput('contract',$ck,0,$newC));[$a]=$hpCommand(14,$hpInput('contract',$ck,1,['decision'=>'approved'],'review'));$rules=$host->table('rm_versions')->where(['kind'=>'rules','state'=>'approved'])->orderByDesc('id')->first();$pk=$uuid();$pay=['operation'=>'establish','worker_key'=>$wk,'rules_key'=>$rules->record_key,'contract_key'=>$ck,'period'=>'2026-11','gross'=>'10000.00'];[$a]=$command(13,$input('payroll',$pk,0,$pay));[$b]=$command(14,$input('payroll',$pk,1,['decision'=>'approved'],'review'));$ok($b['impact']['after']['effective']['dependencies']['contract']['record_key']===$ck,'new payroll explicitly captures chosen reviewed contract');$select(17);$frozen=$rm->export($hrActor(17),$b['id']);$pay2=array_replace($pay,['period'=>'2026-12']);$pending=$preview(13,$input('payroll',$uuid(),0,$pay2));[$a]=$hpCommand(13,$hpInput('contract',$ck,2,array_replace($newC,['operation'=>'amend','gross'=>'11000.00'])));[$a]=$hpCommand(14,$hpInput('contract',$ck,3,['decision'=>'approved'],'review'));$reject(fn()=>$confirm(13,$pending['preview']),409,'changed contract invalidates pending payroll without recalculating old');$select(17);$ok($rm->export($hrActor(17),$b['id'])===$frozen,'old reviewed payroll contract export immutable');$reject(fn()=>$preview(13,$input('payroll',$uuid(),0,$pay2)),409,'gross must agree with chosen contract');
$term=array_replace($newC,['operation'=>'terminate','valid_to'=>'2026-11-30','gross'=>'11000.00']);[$a]=$hpCommand(13,$hpInput('contract',$ck,4,$term));[$a]=$hpCommand(14,$hpInput('contract',$ck,5,['decision'=>'approved'],'review'));$ok($a['impact']['after']['effective']['status']==='terminated','compatible termination reviewed as separate version');
// SQL read fixtures only; no employee or contract operations for pagination.
for($i=0;$i<27;$i++){$key=$uuid();$data=['input'=>array_replace($contractData,['reference'=>'QA-PAGE-HP-'.sprintf('%03d',$i)]),'dependencies'=>['worker'=>['code'=>'QA-PAGE-HP']], 'result'=>[],'status'=>'active'];$host->table('hp_versions')->insert(['tenant_id'=>1,'company_id'=>2,'unit_id'=>2,'kind'=>'contract','record_key'=>$key,'version'=>1,'action'=>'prepare','state'=>'prepared','prepared_by'=>$hrActor(13),'actor_id'=>$hrActor(13),'preparer_identity'=>str_repeat('0',64),'data'=>json_encode($data),'source_basis'=>str_repeat('0',64),'impact'=>'{}','reason'=>'Fixture SQL de leitura de relatório, sem workflow.','created_at'=>now()]);}
$q=['worker_key'=>$workerKey,'search'=>'QA-PAGE-HP','page'=>1,'per_page'=>25];$a=$hpList('contract',$q);$b=$hpList('contract',array_replace($q,['page'=>2]));$c=$hpList('contract',array_replace($q,['per_page'=>100]));$ok(count($a['records']['data'])===25&&count($b['records']['data'])===2&&$c['records']['total']===27,'SQL filtered25 plus2 and100');$ok(array_merge(array_column($a['records']['data'],'id'),array_column($b['records']['data'],'id'))===array_column($c['records']['data'],'id'),'page order equivalent no duplicate');$ok($hpList('contract',array_replace($q,['page'=>100000]))['records']['data']===[],'far page no fallback');$ok($hpList('contract',['worker_key'=>$wk,'search'=>'QA-PAGE-HP'])['records']['total']===0,'second person excludes first person SQL fixtures');$ok($hpList('contract',array_replace($q,['search'=>'QA-PAGE-HP%']))['records']['total']===0,'literal search wildcard escaped');
}finally{$host->rollBack();}$ok([$host->table('hp_versions')->count(),$host->table('hp_commands')->count(),$host->table('rm_versions')->count(),$host->table('identity_audit')->count()]===$counts,'all boundary and salary-link fixtures reverted');file_put_contents($work.'/hp-boundaries.json',json_encode(['passed'=>true,'checks'=>$checks,'regression_only'=>true,'all_fixtures_rolled_back'=>true],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/hp-boundaries.json']).PHP_EOL;