Files
SGF/scripts/ledger-cost-proof.php
T
2026-11-04 09:37:20 +02:00

80 lines
22 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Shared\Context\CompanyContext;
use App\Modules\Workshop\DirectCosts;
use Tempo\Finance\GeneralLedger\{LedgerSchema,CycleSchema,CostSchema,CostCorrectionSchema,SalesSchema};
use Tempo\Finance\Payables\{Payables,PayablesSchema,HistorySchema,SettlementSchema,ReversalSchema};
config(['cost_ledger.synthetic_enabled'=>true,'general_ledger.synthetic_enabled'=>true,'general_ledger.cycle_enabled'=>true]);
if(in_array($phase,['ledger-cost-setup','ledger-cost-correction-setup','ledger-cost-composition-setup','ledger-cost-industrial-setup','ledger-cost-industrial-correction-setup'],true)){
$industrialCorrectionSetup=$phase==='ledger-cost-industrial-correction-setup';$industrialSetup=$phase==='ledger-cost-industrial-setup'||$industrialCorrectionSetup;$compositionSetup=$phase==='ledger-cost-composition-setup'||$industrialSetup;$correctionSetup=$phase==='ledger-cost-correction-setup'||$compositionSetup;
if(DB::getSchemaBuilder()->hasTable('users'))throw new LogicException('New dedicated cost database required');$migrations=glob($c['root'].'/database/migrations/*.php');sort($migrations);
foreach($migrations as $migration){$name=basename($migration);if($name==='0001_01_01_000000_create_users_table.php'||(str_starts_with($name,'2026_')&&$name<=($industrialSetup?'2026_10_27_100000_create_industrial_cost_simulation.php':'2026_10_27_090000_create_workshop_direct_costs.php')))(require $migration)->up();}
foreach(($industrialSetup?['sgf_wo_environment','sgf_dc_environment','sgf_ic_environment']:['sgf_wo_environment','sgf_dc_environment']) as $fn)DB::unprepared("CREATE OR REPLACE FUNCTION $fn() RETURNS void LANGUAGE plpgsql AS \$\$ BEGIN IF current_database() IS DISTINCT FROM '".$c['database']."' OR current_user IS DISTINCT FROM '".$c['role']."' OR inet_server_port() IS DISTINCT FROM 55439 OR current_setting('cluster_name') IS DISTINCT FROM '".$s['cluster']."' THEN RAISE EXCEPTION 'SGF_COST_OWNED_QA_ONLY'; END IF; END \$\$");
PayablesSchema::install();HistorySchema::install();SettlementSchema::install();$app->instance('env','payable-testing');ReversalSchema::install();$app->instance('env','testing');LedgerSchema::install(DB::connection());CycleSchema::install(DB::connection());CostSchema::install(DB::connection());
if($industrialSetup){Tempo\Finance\GeneralLedger\IndustrialSchema::install(DB::connection());$s['industrial_ledger']=true;file_put_contents($file,json_encode($s,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));}
if($correctionSetup){CostCorrectionSchema::install(DB::connection());$s['cost_correction']=true;file_put_contents($file,json_encode($s,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));}
if($industrialCorrectionSetup){Tempo\Finance\GeneralLedger\IndustrialCorrectionSchema::install(DB::connection());$s['industrial_correction']=true;file_put_contents($file,json_encode($s,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));}
if($compositionSetup){SalesSchema::install(DB::connection());$s['ledger_composition']=true;file_put_contents($file,json_encode($s,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));DB::unprepared("CREATE OR REPLACE FUNCTION sgf_sr_environment() RETURNS void LANGUAGE plpgsql AS \$\$ BEGIN IF current_database() IS DISTINCT FROM '".$c['database']."' OR current_user IS DISTINCT FROM '".$c['role']."' OR inet_server_port() IS DISTINCT FROM 55439 OR current_setting('cluster_name') IS DISTINCT FROM '".$s['cluster']."' THEN RAISE EXCEPTION 'SGF_SR_OWNED_QA_ONLY'; END IF; END \$\$");}
Illuminate\Support\Facades\Date::setTestNow('2026-10-29 12:00:00');$base=Tests\Support\DirectCostFixture::create();$scope=['tenant_id'=>$base['tenant'],'company_id'=>$base['company'],'unit_id'=>$base['unit']];
$select=function($u)use($scope){auth()->setUser($u);app(CompanyContext::class)->selectFor($u->id,$scope['tenant_id'],$scope['company_id'],$scope['unit_id']);};$d=app(DirectCosts::class);$rates=[];
foreach(['material'=>['UN','50.00'],'time'=>['H','100.00']] as $kind=>[$unit,$amount]){$select($base['manager']);$r=$d->rate($base['order'],$base['manager']->id,['entry_id'=>$base[$kind],'entry_version'=>1,'identifier'=>'QA-COST-'.strtoupper($kind),'version'=>1,'previous_id'=>null,'currency'=>'MZN','unit'=>$unit,'amount'=>$amount,'evidence'=>'Explicit selected artificial cost rate.'],(string)Str::uuid());$rates[$kind]=$r['id'];$select($base['reader']);$d->decide($base['order'],$base['reader']->id,['kind'=>'rate','id'=>$r['id'],'version'=>1,'decision'=>'approved','reason'=>'Independent exact synthetic rate conference.'],(string)Str::uuid());}
$lines=[];foreach($rates as $kind=>$id)$lines[]=['entry_id'=>$base[$kind],'entry_version'=>1,'rate_id'=>$id,'rate_version'=>1];$select($base['manager']);$calc=$d->calculate($base['order'],$base['manager']->id,['version'=>1,'previous_id'=>null,'currency'=>'MZN','lines'=>$lines],(string)Str::uuid());$select($base['reader']);$d->decide($base['order'],$base['reader']->id,['kind'=>'calculation','id'=>$calc['id'],'version'=>1,'decision'=>'approved','reason'=>'Independent composition100+175=275 of selected actual verified origins.'],(string)Str::uuid());
$read=App\Models\User::create(['name'=>'Consulta de custo e diário QA','email'=>'cost-ledger-read@example.test','password'=>bin2hex(random_bytes(24))]);DB::table('company_memberships')->insert(['tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id'],'user_id'=>$read->id]);$actors=['prepare'=>$base['manager']->id,'validate'=>$base['reader']->id,'read'=>$read->id,'technician'=>$base['technician']->id,'technical_review'=>$base['reviewer']->id];
DB::table('permission_grants')->whereIn('user_id',[$base['manager']->id,$base['reader']->id,$base['limited']->id])->delete();
$def=DB::selectOne("SELECT pg_get_constraintdef(oid) d FROM pg_constraint WHERE conrelid='permission_grants'::regclass AND conname='permission_grants_scope_check'")->d;DB::statement('ALTER TABLE permission_grants DROP CONSTRAINT permission_grants_scope_check');DB::statement("ALTER TABLE permission_grants ADD CONSTRAINT permission_grants_scope_check CHECK ((".substr($def,6).") OR (unit_id IS NOT NULL AND permission IN('general_ledger.view','general_ledger.prepare','general_ledger.validate','general_ledger.post','general_ledger.period_prepare','general_ledger.period_validate','general_ledger.period_close')))");
foreach(['prepare','validate','read'] as $role){$actor=$actors[$role];foreach(['company.view'=>null,'unit.view'=>$scope['unit_id'],'workshop.view'=>$scope['unit_id'],'direct_costs.view'=>$scope['unit_id'],'general_ledger.view'=>$scope['unit_id']] as $right=>$unit)Tests\Support\IdentityFixture::grant($actor,$scope['tenant_id'],$scope['company_id'],$right,$unit);foreach(match($role){'prepare'=>['direct_costs.prepare','general_ledger.prepare','general_ledger.post','general_ledger.period_prepare','general_ledger.period_close'],'validate'=>['direct_costs.validate','general_ledger.validate','general_ledger.period_validate'],default=>[]} as $right)Tests\Support\IdentityFixture::grant($actor,$scope['tenant_id'],$scope['company_id'],$right,$scope['unit_id']);DB::table('pa_access')->insert($scope+['user_id'=>$actor,'person'=>'cost-qa-person-'.$actor,'action'=>'view']);}
if($correctionSetup){$def=DB::selectOne("SELECT pg_get_constraintdef(oid) d FROM pg_constraint WHERE conrelid='permission_grants'::regclass AND conname='permission_grants_scope_check'")->d;DB::statement('ALTER TABLE permission_grants DROP CONSTRAINT permission_grants_scope_check');DB::statement("ALTER TABLE permission_grants ADD CONSTRAINT permission_grants_scope_check CHECK ((".substr($def,6).") OR (unit_id IS NOT NULL AND permission IN('general_ledger.correct_prepare','general_ledger.correct_validate','general_ledger.correct_post')))");foreach(['prepare'=>['correct_prepare','correct_post'],'validate'=>['correct_validate']] as $role=>$rights)foreach($rights as $r)Tests\Support\IdentityFixture::grant($actors[$role],$scope['tenant_id'],$scope['company_id'],'general_ledger.'.$r,$scope['unit_id']);}
$accounts=[];foreach(['QA-CUSTO-DIRECTO','QA-COMPENSACAO-ENSAIO'] as $code)$accounts[$code]=DB::table('gl_accounts')->insertGetId($scope+['code'=>$code,'label'=>'Conta artificial '.$code,'currency'=>'MZN','is_movement'=>true]);$common=['created_by'=>$actors['prepare'],'reason'=>'Explicit authorised direct cost QA composition.','created_at'=>now()];DB::table('gl_periods')->insert($scope+$common+['period_key'=>'QA-2026-10','version'=>1,'starts_on'=>'2026-10-01','ends_on'=>'2026-10-31','status'=>'open']);DB::table('gl_rules')->insert($scope+$common+['rule_key'=>'QA-DIRECT-COST','version'=>1,'policy'=>'QA-DIRECT-COST-JOURNAL-v1','effect_type'=>'workshop.direct_cost.validated','currency'=>'MZN','valid_from'=>'2026-10-01','valid_to'=>'2026-10-31','debit_account_id'=>$accounts['QA-CUSTO-DIRECTO'],'credit_account_id'=>$accounts['QA-COMPENSACAO-ENSAIO']]);
if($correctionSetup)foreach(['REVERSE'=>'reversal','REPLACE'=>'replacement'] as $name=>$kind)DB::table('gl_rules')->insert($scope+$common+['rule_key'=>'QA-COST-CORRECTION-'.$name,'version'=>1,'policy'=>'QA-DIRECT-COST-JOURNAL-v1','effect_type'=>'workshop.direct_cost.'.$kind,'currency'=>'MZN','valid_from'=>'2026-10-01','valid_to'=>'2026-10-31','debit_account_id'=>$accounts[$kind==='reversal'?'QA-COMPENSACAO-ENSAIO':'QA-CUSTO-DIRECTO'],'credit_account_id'=>$accounts[$kind==='reversal'?'QA-CUSTO-DIRECTO':'QA-COMPENSACAO-ENSAIO']]);
DB::table('gl_cost_sources')->insert($scope+$common+['order_id'=>$base['order'],'material_origin'=>DB::table('wo_entries')->where('id',$base['material'])->value('origin'),'time_origin'=>DB::table('wo_entries')->where('id',$base['time'])->value('origin'),'period_key'=>'QA-2026-10']);
$g=['scope'=>$scope,'actors'=>$actors,'order_id'=>$base['order'],'material_id'=>$base['material'],'time_id'=>$base['time'],'rate_ids'=>$rates,'calculation_id'=>$calc['id'],'accounts'=>$accounts];file_put_contents($s['root'].'/cost-fixture.json',json_encode($g,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Cost QA: actual workshop verification/rates/calculation275 through existing workflows, selected mapping and rights only in new database; no journal yet.\n";exit;
}
$g=json_decode(file_get_contents($s['root'].'/cost-fixture.json'),true,64,JSON_THROW_ON_ERROR);$scope=$g['scope'];config(['modules.contracted'=>['workshop','finance','accounting'],'modules.companies.'.$scope['tenant_id'].':'.$scope['company_id']=>['contracted'=>['workshop','finance','accounting'],'enabled'=>['workshop','finance','accounting']]]);
$enter=function($role)use($g,$scope){$u=App\Models\User::findOrFail($g['actors'][$role]);auth()->setUser($u);app(CompanyContext::class)->selectFor($u->id,$scope['tenant_id'],$scope['company_id'],$scope['unit_id']);return $u;};$ledger=app(App\Modules\Accounting\GeneralLedger::class);$dc=app(DirectCosts::class);$body=['effect_type'=>'workshop.direct_cost.validated','source_id'=>$g['order_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>'QA-DIRECT-COST','reason'=>'Capture selected cost275 from actual independently validated workflow.'];$command=fn($role,$action,$data,$key=null,$fault=null)=>$ledger->engine($enter($role),$fault)->command($action,$data,$key??(string)Str::uuid());
$sourceFingerprint=function(){ $h=hash_init('sha256');foreach(['wo_orders','wo_entries','wo_decisions','wo_commands','dc_rates','dc_calculations','dc_decisions','dc_commands','dc_events','dc_outbox','permission_grants','pa_obligations','pa_commands'] as $t){hash_update($h,$t);foreach(DB::table($t)->orderBy($t==='dc_outbox'?'event_id':'id')->cursor() as $r)hash_update($h,Payables::canonical((array)$r));}return hash_final($h);};
if(str_starts_with($phase,'ledger-cost-correction-')){require __DIR__.'/ledger-cost-correction-proof.php';exit;}
if(str_starts_with($phase,'ledger-cost-industrial-')){require __DIR__.'/ledger-industrial-proof.php';exit;}
if(str_starts_with($phase,'ledger-cost-composition-')){require __DIR__.'/ledger-composition-proof.php';exit;}
if($phase==='ledger-cost-http'){require __DIR__.'/ledger-cost-http-entry.php';exit;}
if($phase==='ledger-cost-preservation-after-correction'){
$output=$s['root'].'/cost-demo-after-correction.json';if(file_exists($output))throw new LogicException('Completed old demo preservation preserved');$prior=json_decode(file_get_contents($s['root'].'/cost-demo-preservation.json'),true);if(config('cost_ledger.correction_enabled')!==false||DB::getSchemaBuilder()->hasTable('gl_cost_correction_capability')||$sourceFingerprint()!==$prior['source_fingerprint'])throw new LogicException('Old8054 source/outbox/rights or capability changed; never reset');file_put_contents($output,json_encode(['passed'=>true,'old8054_source_outbox_rights_exact'=>true,'correction_flag_off'=>true,'correction_schema_absent'=>true,'journals_observed'=>DB::table('gl_journals')->count(),'no_business_writes'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Old8054 source/outbox/rights remain exact; new correction flag/schema absent, no reset.\n";exit;
}
if($phase==='ledger-cost-legacy-correction-check'){
$output=$s['root'].'/cost-correction-legacy-mode.json';if(file_exists($output))throw new LogicException('Completed legacy mode proof preserved');$u=$enter('read');$original=DB::table('gl_journals')->where('snapshot->source->effect_type','workshop.direct_cost.validated')->first();$snap=json_decode($original->snapshot,true);$provider=app(App\Modules\Accounting\CostLedgerSource::class);$source=$provider->source($u,$g['order_id']);$engine=$ledger->engine($u);$period=(new ReflectionProperty($engine,'additionalPeriod'))->getValue($engine);$result=$period($snap['period']);$expected=$provider->period($u,$snap['period']);$caught=false;try{$provider->correctionSource($u,'workshop.direct_cost.reversal',$original->id);}catch(Symfony\Component\HttpKernel\Exception\HttpException $e){$caught=$e->getStatusCode()===403;}
if(config('cost_ledger.correction_enabled')!==false||DB::getSchemaBuilder()->hasTable('gl_cost_correction_capability')||Payables::canonical($source)!==Payables::canonical($snap['source'])||$result!==$expected||!$caught||DB::table('permission_grants')->where('permission','like','general_ledger.%')->count()!==9)throw new LogicException('Existing cost-only mode was changed or enabled for correction');file_put_contents($output,json_encode(['passed'=>true,'old_cost_source_matches_original_posted_snapshot'=>true,'single_cost_period_provider_packet_preserved'=>true,'new_correction_flag_default_off'=>true,'new_correction_schema_absent'=>true,'uncomposed_correction_refused403'=>true,'old_ledger_grants_still9'=>true,'no_business_writes'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Existing275 source/period/rights preserved; new correction defaults off and is refused without new schema.\n";exit;
}
if($phase==='ledger-cost-ui-postcheck'){
$output=$s['root'].'/cost-ui-postcheck.json';if(file_exists($output))throw new LogicException('Completed UI postcheck preserved');$data=$ledger->listing($enter('read'),['cost_order_id'=>$g['order_id']]);$html=view('accounting.general-ledger',['data'=>$data])->render();$report=$ledger->report($enter('read'),'account-ledger',['from_on'=>'2026-10-01','to_on'=>'2026-10-31','account_id'=>$g['accounts']['QA-CUSTO-DIRECTO']]);
if(count($data['journals'])!==1||count($data['events'])!==3||!str_contains($html,'Ordem #'.$g['order_id'])||str_contains($html,'@else')||!str_contains($html,'/general-ledger/costs?order_id='.$g['order_id'])||$report['rows'][0]['cost_order_id']!==(string)$g['order_id'])throw new LogicException('Generic diary labels/typed filter or report source link differs');file_put_contents($output,json_encode(['passed'=>true,'cost_order_not_mislabelled_as_debt'=>true,'typed_filter_journals'=>1,'typed_filter_events'=>3,'generic_diary_and_account_report_source_links'=>true,'read_only_postcheck'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Generic diary/source link/typed filter postcheck passed, without business writes.\n";exit;
}
if($phase==='ledger-cost-demo-preservation'){
$output=$s['root'].'/cost-demo-preservation.json';if(file_exists($output))throw new LogicException('Completed team proof preserved');$others=DB::table('users')->whereNotIn('id',array_values($g['actors']))->pluck('id')->all();
if(DB::table('gl_proposals')->exists()||DB::table('gl_journals')->exists()||DB::table('gl_period_closures')->exists()||DB::table('permission_grants')->whereIn('user_id',$others)->exists()||DB::table('dc_outbox')->where('state','<>','captured_no_accounting')->exists()||app(App\Modules\Accounting\CostLedgerSource::class)->source($enter('read'),$g['order_id'])['principal_minor']!==27500)throw new LogicException('Unexpected team fixture state; no reset permitted');
file_put_contents($output,json_encode(['passed'=>true,'new_team_fixture'=>true,'declared_synthetic_actors'=>5,'unlisted_users_with_grants'=>0,'actual_validated_cost_minor'=>27500,'no_accounting_decisions_or_effects'=>true,'outbox_captured_no_accounting'=>true,'source_fingerprint'=>$sourceFingerprint()],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Team fixture preserved: actual cost275, five explicit actors, no accounting decisions/effects, outbox untouched.\n";exit;
}
if($phase==='ledger-cost-defaults'){
$output=$s['root'].'/cost-defaults.json';if(file_exists($output))throw new LogicException('Completed defaults proof preserved');$v=getenv('SGF_COST_LEDGER_QA');$a=$_ENV['SGF_COST_LEDGER_QA']??null;$b=$_SERVER['SGF_COST_LEDGER_QA']??null;putenv('SGF_COST_LEDGER_QA');unset($_ENV['SGF_COST_LEDGER_QA'],$_SERVER['SGF_COST_LEDGER_QA']);try{$default=(require $c['root'].'/config/cost_ledger.php')['synthetic_enabled'];}finally{if($v!==false)putenv('SGF_COST_LEDGER_QA='.$v);if($a!==null)$_ENV['SGF_COST_LEDGER_QA']=$a;if($b!==null)$_SERVER['SGF_COST_LEDGER_QA']=$b;}
if($default!==false||config('cost_ledger.synthetic_enabled')!==true||config('sales_ledger.synthetic_enabled')!==false)throw new LogicException('Cost default off; explicit cost-only QA composition required');file_put_contents($output,json_encode(['passed'=>true,'default_without_environment'=>false,'owned_cost_only_qa_runtime'=>true,'sales_not_enabled'=>true,'real_environment_not_changed'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Cost capability defaults off; explicit isolated composition only, Sales remains off.\n";exit;
}
if($phase==='ledger-cost-baseline'){
$out=$s['root'].'/cost-http-baseline.json';if(file_exists($out)||DB::table('gl_proposals')->exists())throw new LogicException('Fresh baseline only');file_put_contents($out,json_encode(['fingerprint'=>$sourceFingerprint(),'journals'=>0],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "New cost HTTP baseline stored privately without business writes.\n";exit;
}
if($phase==='ledger-cost-http-final'){
$out=$s['root'].'/cost-http-final.json';if(file_exists($out))throw new LogicException('Completed final proof preserved');$baseline=json_decode(file_get_contents($s['root'].'/cost-http-baseline.json'),true);$data=$ledger->costs($enter('read'),$g['order_id']);$html=view('accounting.cost-ledger',['data'=>$data])->render();
if($sourceFingerprint()!==$baseline['fingerprint']||DB::table('gl_journals')->count()!==1||DB::table('gl_lines')->count()!==2||DB::table('gl_period_closures')->count()!==1||str_contains($html,'data-action='))throw new LogicException('Cost HTTP final preservation/closed UI mismatch');$report=$ledger->report($enter('read'),'trial-balance',['from_on'=>'2026-10-01','to_on'=>'2026-10-31']);DB::select('SELECT gl_assert_balanced(id) FROM gl_journals');
file_put_contents($out,json_encode(['passed'=>true,'source_outbox_rights_fingerprint_unchanged'=>true,'journals'=>1,'lines'=>2,'closures'=>1,'closed_ui_has_no_commands'=>true,'debit_minor'=>$report['totals']['debit_minor'],'credit_minor'=>$report['totals']['credit_minor']],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Cost HTTP final verified: one journal/two lines, one close, originals/outbox/rights unchanged.\n";exit;
}
if($phase==='ledger-cost-receipt-cases'){require __DIR__.'/ledger-receipt-proof.php';exit;}
if($phase==='ledger-cost-cases'){require __DIR__.'/ledger-cost-cases.php';exit;}
if($phase==='ledger-cost-coexistence'){require __DIR__.'/ledger-cost-coexistence.php';exit;}
if($phase==='ledger-cost-security'){require __DIR__.'/ledger-cost-security.php';exit;}
if(in_array($phase,['ledger-cost-race','ledger-cost-worker'],true)){require __DIR__.'/ledger-cost-race.php';exit;}
if($phase==='ledger-cost-workflow'){
$output=$s['root'].'/cost-workflow.json';if(file_exists($output)||DB::table('gl_proposals')->exists())throw new LogicException('Fresh workflow required');$before=$sourceFingerprint();$p=$command('prepare','effect-prepare',$body);$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent cost source, versions and impact review.']);$post=['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Apply selected cost once without financial/technical mutation.'];$key=(string)Str::uuid();$j=$command('prepare','post',$post,$key);$repeat=$command('prepare','post',$post,$key);$again=$command('prepare','post',$post);
$check=$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Conference of selected current cost and posted evidence.']);$command('validate','period-validate',['check_id'=>$check['check_id'],'revision'=>$check['revision'],'decision'=>'approved','reason'=>'Distinct complete QA period review.']);$closed=$command('prepare','period-close',['check_id'=>$check['check_id'],'revision'=>$check['revision'],'reason'=>'Close selected cost QA period without reopening.']);
$report=$ledger->report($enter('read'),'trial-balance',['from_on'=>'2026-10-01','to_on'=>'2026-10-31']);$account=$ledger->report($enter('read'),'account-ledger',['from_on'=>'2026-10-01','to_on'=>'2026-10-31','account_id'=>$g['accounts']['QA-CUSTO-DIRECTO']]);
if(!$repeat['replayed']||$again['journal_id']!==$j['journal_id']||DB::table('gl_journals')->count()!==1||DB::table('gl_lines')->count()!==2||$report['totals']['debit_minor']!==27500||$account['closing_net_minor']!==27500||$sourceFingerprint()!==$before)throw new LogicException('Cost journal/report/repetition/preservation mismatch');DB::select('SELECT gl_assert_balanced(?)',[$j['journal_id']]);file_put_contents($output,json_encode(['passed'=>true,'journal_id'=>$j['journal_id'],'journals'=>1,'lines'=>2,'debit_minor'=>27500,'credit_minor'=>27500,'source_outbox_rights_fingerprint_unchanged'=>true,'same_and_other_key_no_duplication'=>true,'period_closed'=>true,'trial_balance_and_account_ledger_reconciled'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Actual cost275 -> one balanced journal/two lines -> trial balance/account ledger and reviewed QA close; originals/outbox unchanged.\n";exit;
}
throw new LogicException('Unknown cost phase');