Files
SGF/scripts/sgf-assets-correction-proof.php
T
2026-11-11 15:04:01 +02:00

19 lines
5.3 KiB
PHP

<?php
require __DIR__.'/sgf-assets-proof-bootstrap.php';$progress=$s['root'].'/assets-regression-progress-private.json';$p=json_decode(file_get_contents($progress),true);$save=function()use(&$p,$progress){file_put_contents($progress,json_encode($p));};$step=function($name,Closure $fn)use(&$p,$save,$ok){if(!isset($p['steps'][$name])){$p['steps'][$name]=$fn();$save();}$ok(true,$name.' recorded once');return$p['steps'][$name];};$cmd=function($name,$actor,$data)use($step,$faCommand){return$step($name,fn()=>$faCommand($actor,$data)[0]);};$gl=function($name,$role,$action,$data)use($step,$call){return$step($name,fn()=>$call($role,$action==='effect-prepare'?'prepare':str_replace('-','_',$action),fn($e)=>$e->command($action,$data,(string)Str::uuid())));};
$old=App\Modules\Accounting\FixedAssets::class;$plan=['operation'=>'correct_plan','asset_key'=>$p['asset'],'life_months'=>10,'status'=>'active','evidence'=>'Correcção técnica artificial do plano12 para10 explicitamente autorizada na regressão.'];
if(!isset($p['steps']['plan_correct_prepare'])){$bad=$plan;$bad['operation']='amend';$reject(fn()=>$faCall(15,'preview',$faInput('plan',$p['plan'],2,$bad)),409,'ordinary life change with posted quotas denied');}
$cmd('plan_correct_prepare',15,$faInput('plan',$p['plan'],2,$plan));$cmd('plan_correct_review',16,$faInput('plan',$p['plan'],3,['decision'=>'approved'],'review'));
$charge=['operation'=>'amend','asset_key'=>$p['asset'],'plan_key'=>$p['plan'],'month'=>'2026-10','evidence'=>'Nova quota120 calculada a partir do plano10 revisto na regressão.'];$cmd('charge_correct_prepare',15,$faInput('charge',$p['charge'],2,$charge));$cr=$cmd('charge_correct_review',16,$faInput('charge',$p['charge'],3,['decision'=>'approved'],'review'));
$group=$gl('correction_prepare','accounting_prepare','correct-prepare',['journal_id'=>$p['steps']['charge_ledger_post']['journal_id'],'journal_on'=>'2026-10-31','period_key'=>'QA-2026-INTEGRADO','reason'=>'Par técnico explícito estorno100 e substituição120.']);
$gl('correction_review','accounting_review','correct-validate',['correction_id'=>$group['correction_id'],'revision'=>$group['revision'],'decision'=>'approved','reason'=>'Revisão distinta do par técnico100 para120 artificial.']);
$input=['correction_id'=>$group['correction_id'],'revision'=>$group['revision'],'reason'=>'Aplicar par indivisível estorno100 e substituição120.'];
if(!isset($p['steps']['correction_post'])){
$before=$native->table('gl_journals')->count();$failed=false;try{$call('accounting_prepare','correct_post',function($e)use($input){$r=new ReflectionProperty($e,'checkpoint');$r->setValue($e,function($stage){if($stage==='after_correction_first')throw new RuntimeException('injected_asset_pair_failure');});return$e->command('correct-post',$input,(string)Str::uuid());});}catch(RuntimeException$e){if($e->getMessage()!=='injected_asset_pair_failure')throw$e;$failed=true;}$ok($failed&&$native->table('gl_journals')->count()===$before,'failure after first correction header rolls back pair');
}
$out=$gl('correction_post','accounting_prepare','correct-post',$input);
$balance=app($old)->balance($f['scope'],$p['asset'],$native);if(!isset($p['steps']['writeoff_post']))$ok($balance['accumulated_minor']===12000&&$balance['carrying_minor']===108000,'pair gives accumulated120 carrying1080');
$disposal=['operation'=>'establish','asset_key'=>$p['asset'],'effect_on'=>'2026-11-01','evidence'=>'Abate artificial sem contraprestação só no livro de regressão.'];$cmd('disposal_prepare',15,$faInput('disposal',$p['disposal'],0,$disposal));$dr=$cmd('disposal_review',16,$faInput('disposal',$p['disposal'],1,['decision'=>'approved'],'review'));
$dp=$gl('writeoff_prepare','accounting_prepare','effect-prepare',['effect_type'=>'assets.writeoff','source_id'=>$dr['id'],'journal_on'=>'2026-11-01','period_key'=>'QA-2026-INTEGRADO','rule_key'=>'QA-ASSET-WRITEOFF','reason'=>'Abate artificial sem contraprestação em três linhas.']);$gl('writeoff_review','accounting_review','validate',['proposal_id'=>$dp['proposal_id'],'revision'=>$dp['revision'],'decision'=>'approved','reason'=>'Revisão distinta do abate artificial em três linhas.']);$dj=$gl('writeoff_post','accounting_prepare','post',['proposal_id'=>$dp['proposal_id'],'revision'=>$dp['revision'],'reason'=>'Aplicar abate artificial após revisão distinta.']);$lines=$native->table('gl_lines')->where('journal_id',$dj['journal_id'])->orderBy('line_no')->get();$ok($lines->count()===3&&array_sum($lines->pluck('debit_minor')->all())===120000&&array_sum($lines->pluck('credit_minor')->all())===120000,'three line writeoff120 accumulated and1080 loss balanced1200');$ok(app($old)->balance($f['scope'],$p['asset'],$native)['carrying_minor']===0,'written off carrying zero');
$reject(fn()=>$call('accounting_prepare','prepare',fn($e)=>$e->command('effect-prepare',['effect_type'=>'assets.depreciation','source_id'=>$cr['id'],'journal_on'=>'2026-10-31','period_key'=>'QA-2026-INTEGRADO','rule_key'=>'QA-ASSET-DEPRECIATION','reason'=>'Tentativa indevida de duplicar quota já aplicada.'],(string)Str::uuid())),409,'duplicate depreciation after writeoff denied');
file_put_contents($work.'/assets-correction.json',json_encode(['passed'=>true,'checks'=>$checks,'regression_only'=>true,'pair_atomic'=>true,'writeoff_three_lines'=>true],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/assets-correction.json']).PHP_EOL;