true,'stock_returns.enabled'=>true]); $f=json_decode(file_get_contents($s['root'].'/fixture.json'),true,64,JSON_THROW_ON_ERROR);$scope=$f['scope']; $enter=function(string $role)use($f){$u=User::findOrFail($f['actors'][$role]);auth()->setUser($u);return StockValuationAdapter::for($u);}; $command=fn($role,$action,$body,$key=null,$fault=null)=>$enter($role)->engine($fault)->command($action,$body,$key??(string)Str::uuid()); $rate=function()use($enter,$f){$r=$enter('prepare')->stock->command('rate-prepare',['book_id'=>$f['book_id'],'version'=>0,'unit_minor'=>5000,'reason'=>'Synthetic50MZN per UN, no real valuation policy.'],(string)Str::uuid());$enter('validate')->stock->command('rate-validate',['rate_id'=>$r['rate_id'],'version'=>$r['version'],'decision'=>'approved','reason'=>'Distinct review of selected synthetic50MZN/UN.'],(string)Str::uuid());return $r;}; $body=fn($m)=>['effect_type'=>StockValuation::TYPES[$m['movement_type']],'source_id'=>$m['id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>StockValuation::TYPES[$m['movement_type']],'reason'=>'Explicit individual selected physical movement valuation.']; $post=function($m)use($command,$body){$p=$command('prepare','effect-prepare',$body($m));$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Distinct exact physical origin/rate/two-line impact review.']);$d=['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Apply reviewed stock valuation once without moving quantities.'];return [$command('prepare','post',$d),$d,$p];}; $fingerprint=function(){ $h=hash_init('sha256');foreach(['stock_movements','stock_operations','goods_receipts','goods_receipt_items','warehouse_request_events','supply_records','pa_access','pa_obligations','pa_commands','budget_entries','budget_consumptions']as$t){hash_update($h,$t);foreach(DB::table($t)->orderBy('id')->cursor()as$r)hash_update($h,Tempo\Finance\Payables\Payables::canonical((array)$r));}return hash_final($h);}; $checks=[];$ok=function(bool $v,string $name)use(&$checks){if(!$v)throw new RuntimeException($name);$checks[]=$name;}; $refuse=function(callable $fn,string $name)use($ok){try{$fn();}catch(DomainException|InvalidArgumentException|Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$ok(true,$name);return;}throw new RuntimeException('Expected refusal: '.$name);}; $case=function(callable $fn)use($fingerprint,$ok){$before=$fingerprint();$level=DB::transactionLevel();DB::beginTransaction();try{$fn();}finally{while(DB::transactionLevel()>$level)DB::rollBack();}$ok($fingerprint()===$before,'case rollback preserves physical/financial/rights fingerprint');}; if($phase==='contract'){ $out=$s['root'].'/contract.json';if(file_exists($out))throw new LogicException('Completed contract proof preserved');$sql=[];foreach(['gl_assert_balanced','gl_stock_journal_assert','gl_stock_scope']as$name)$sql[$name]=DB::selectOne('SELECT pg_get_functiondef(?::regprocedure) definition',[$name.'('.($name==='gl_assert_balanced'?'bigint':'').')'])->definition;if(!str_contains($sql['gl_stock_journal_assert'],'pm.idindexdef;file_put_contents($out,json_encode(['passed'=>true,'sql_contract_sha256'=>hash('sha256',Tempo\Finance\Payables\Payables::canonical($sql)),'predecessor_required'=>true,'source_class_sha256'=>hash_file('sha256',(new ReflectionClass(StockValuation::class))->getFileName()),'read_only_checks'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));exit; } if(in_array($phase,['demo-baseline','demo-preservation'],true)){ $out=$s['root'].'/'.$phase.'.json';if(file_exists($out))throw new LogicException('Completed demo proof preserved');$current=['physical_financial_rights_hash'=>$fingerprint(),'stock_mapping_hash'=>hash('sha256',DB::table('gl_stock_books')->orderBy('id')->get()->toJson()),'rates'=>DB::table('gl_stock_rates')->count(),'journals'=>DB::table('gl_journals')->count(),'closures'=>DB::table('gl_period_closures')->count()];if($current['rates']!==0||$current['journals']!==0||$current['closures']!==0)throw new LogicException('New demo must keep individual decisions pending; no reset');if($phase==='demo-preservation'&&$current!==json_decode(file_get_contents($s['root'].'/demo-baseline.json'),true)['state'])throw new LogicException('Demo changed; no reset');file_put_contents($out,json_encode(['passed'=>true,'state'=>$current,'no_reset'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));exit; } if($phase==='cases'){ if(file_exists($s['root'].'/cases.json'))throw new LogicException('Completed proof preserved'); $case(function()use($enter,$body,$f,$refuse,$ok,$command){$page=$enter('read')->stock->page($f['book_id']);$ok(count($page['rows'])===3&&count(array_filter($page['rows'],fn($r)=>$r['source']===null))===3,'missing rate visible; no invented values');$refuse(fn()=>$command('prepare','effect-prepare',$body($f['movements'][0])),'missing rate cannot capture');$refuse(fn()=>$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'No rate must block the period conference.']),'unvalued movements block close conference');}); $case(function()use($enter,$f,$refuse,$ok){$d=['book_id'=>$f['book_id'],'version'=>0,'unit_minor'=>5000,'reason'=>'Synthetic rate prepared for distinct review.'];$k=(string)Str::uuid();$a=$enter('prepare')->stock->command('rate-prepare',$d,$k);$r=$enter('prepare')->stock->command('rate-prepare',$d,$k);$ok($r['replayed']&&$r['rate_id']===$a['rate_id'],'rate replay no duplication');$refuse(fn()=>$enter('prepare')->stock->command('rate-prepare',array_replace($d,['unit_minor'=>5100]),$k),'changed rate replay refused');$v=['rate_id'=>$a['rate_id'],'version'=>1,'decision'=>'approved','reason'=>'Distinct review is obligatory for rate.'];$refuse(fn()=>$enter('prepare')->stock->command('rate-validate',$v,(string)Str::uuid()),'rate self review refused');$key=(string)Str::uuid();$enter('validate')->stock->command('rate-validate',$v,$key);$ok($enter('validate')->stock->command('rate-validate',$v,$key)['replayed'],'rate review replay unique');$refuse(fn()=>$enter('read')->stock->command('rate-prepare',$d,(string)Str::uuid()),'read actor cannot prepare rate');}); $case(function()use($rate,$enter,$f,$ok,$refuse,$body,$command,$post){$rate();$refuse(fn()=>$command('prepare','effect-prepare',$body($f['movements'][1])),'issue cannot precede valued receipt');$refuse(fn()=>$command('prepare','effect-prepare',$body($f['movements'][2])),'return cannot precede valued issue');$amounts=[];foreach($f['movements']as$m){$amounts[]=$enter('read')->stock->source(StockValuation::TYPES[$m['movement_type']],$m['id'])['principal_minor'];$post($m);}$ok($amounts===[10000,5000,2500],'actual2/-1/+0.5 at50 gives100/50/25');$ok($enter('read')->stock->page($f['book_id'],2)['rows']===[],'server pagination boundary');$refuse(fn()=>$enter('read')->stock->page($f['book_id'],1,26),'unsupported page size refused');$refuse(fn()=>$enter('read')->stock->source('stock.issue',$f['movements'][0]['id']),'wrong physical effect type refused');$refuse(fn()=>$command('prepare','effect-prepare',array_replace($body($f['movements'][0]),['journal_on'=>'2026-10-28'])),'journal date must match physical date');$s2=$f['scope'];$s2['unit_id']=99;$u=User::findOrFail($f['actors']['read']);auth()->setUser($u);$provider=new StockValuation(DB::connection(),$s2,$u->id,'qa',fn()=>null,fn()=>null);$refuse(fn()=>$provider->source('stock.receipt',$f['movements'][0]['id']),'other unit cannot read mapped movement');}); $case(function()use($rate,$enter,$f,$body,$command,$refuse,$ok){$rate();$p=$command('prepare','effect-prepare',$body($f['movements'][0]));$r=$enter('prepare')->stock->command('rate-prepare',['book_id'=>$f['book_id'],'version'=>1,'unit_minor'=>5000,'reason'=>'New explicit same-value rate version for stale review proof.'],(string)Str::uuid());$enter('validate')->stock->command('rate-validate',['rate_id'=>$r['rate_id'],'version'=>2,'decision'=>'approved','reason'=>'Distinct same-value successor review for stale version proof.'],(string)Str::uuid());$refuse(fn()=>$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Old source capture must be refused.']),'same-value rate successor invalidates old capture');$p2=$command('prepare','effect-prepare',$body($f['movements'][0]));$ok($p2['revision']!==$p['revision'],'new capture after rate version change');}); $case(function()use($rate,$f,$body,$command,$refuse,$ok){$rate();$p=$command('prepare','effect-prepare',$body($f['movements'][0]));$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent review before rollback injection.']);$d=['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Test fault recovery of original reviewed stock value.'];$key=(string)Str::uuid();try{$command('prepare','post',$d,$key,fn($at)=>$at==='after_header'?throw new RuntimeException('QA-STOCK-FAULT'):null);throw new RuntimeException('Fault missing');}catch(RuntimeException $e){if($e->getMessage()!=='QA-STOCK-FAULT')throw$e;}$ok(DB::table('gl_journals')->count()===0&&DB::table('gl_lines')->count()===0&&!DB::table('gl_commands')->where('command_key',$key)->exists(),'fault rolls back header lines and receipt');$j=$command('prepare','post',$d,$key);$ok($command('prepare','post',$d,$key)['journal_id']===$j['journal_id']&&$command('prepare','post',$d)['journal_id']===$j['journal_id'],'exact and other-key retry one journal');$refuse(fn()=>$command('prepare','correct-prepare',['journal_id'=>$j['journal_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','reason'=>'Stock correction remains explicitly excluded.']),'stock correction excluded');}); $case(function()use($rate,$enter,$f,$body,$command,$refuse){$rate();$p=$command('prepare','effect-prepare',$body($f['movements'][0]));$r=App\Models\SupplyRecord::findOrFail($f['return_proposal']);$d=$r->data;$d['operation_key']=(string)Str::uuid();$d['quantity_milli']=250;$d['reason']='Explicit additional unused quarter unit for version invalidation.';$u=User::findOrFail($f['physical_actors']['issuer']);auth()->setUser($u);$p2=app(App\Services\WarehouseReturns::class)->prepare($u,$d);$u=User::findOrFail($f['physical_actors']['receiver']);auth()->setUser($u);app(App\Services\WarehouseReturns::class)->confirm($u,$p2['id'],['version'=>1,'received_in_warehouse'=>true,'reason'=>'Independent actual receipt for physical revision proof.']);$refuse(fn()=>$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Old physical book conference must be refused.']),'actual later physical movement invalidates pending capture');}); $case(function()use($rate,$f,$post,$enter,$command,$refuse){$rate();[$j,$d]=$post($f['movements'][0]);DB::table('pa_access')->where($f['scope'])->where('user_id',$f['actors']['prepare'])->where('action','general_ledger.post')->delete();$refuse(fn()=>$command('prepare','post',$d),'revoked post right refused before existing effect replay');$refuse(fn()=>$enter('prepare')->stock->command('rate-prepare',['book_id'=>$f['book_id'],'version'=>1,'unit_minor'=>5100,'reason'=>'No correction of already posted stock valuation.'],(string)Str::uuid()),'rate correction after posting excluded');}); file_put_contents($s['root'].'/cases.json',json_encode(['passed'=>true,'checks'=>$checks,'all_cases_rolled_back'=>true,'no_persistent_journals'=>DB::table('gl_journals')->count()===0],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." stock source/rate/writer checks passed.\n";exit; } if($phase==='workflow'){ if(file_exists($s['root'].'/workflow.json'))throw new LogicException('Completed workflow preserved');$before=$fingerprint();if(!DB::table('gl_stock_rates')->exists())$rate();foreach($f['movements']as$m)if(!DB::table('gl_journals')->where('snapshot->source->stock_movement_id',$m['id'])->exists())$post($m); $report=$enter('read')->engine()->trialBalance(['from_on'=>'2026-10-01','to_on'=>'2026-10-31']);$stock=$enter('read')->engine()->accountLedger(['from_on'=>'2026-10-01','to_on'=>'2026-10-31','account_id'=>$f['accounts']['QA-STOCK']]);$cost=$enter('read')->engine()->accountLedger(['from_on'=>'2026-10-01','to_on'=>'2026-10-31','account_id'=>$f['accounts']['QA-CONSUMO-STOCK']]); $ok($report['totals']['debit_minor']===17500&&$report['totals']['credit_minor']===17500&&$stock['closing_net_minor']===7500&&$cost['closing_net_minor']===2500,'same core reports gross175 stock75 consumption25'); $p=$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Complete selected physical and valuation period conference.']);$command('validate','period-validate',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Distinct physical quantities rates stock and journal conference.']);$key=(string)Str::uuid();$d=['check_id'=>$p['check_id'],'revision'=>$p['revision'],'reason'=>'Close only the reviewed synthetic stock period.'];$closed=$command('prepare','period-close',$d,$key);$ok($command('prepare','period-close',$d,$key)['closure_id']===$closed['closure_id'],'period close replay unique'); $ok(DB::table('gl_journals')->count()===3&&DB::table('gl_lines')->count()===6&&DB::table('gl_period_closures')->count()===1,'three journals six lines one close');DB::select('SELECT gl_assert_balanced(id) FROM gl_journals');$ok($fingerprint()===$before,'valuation leaves physical financial budget and grants intact'); file_put_contents($s['root'].'/workflow.json',json_encode(['passed'=>true,'checks'=>$checks,'journals'=>3,'lines'=>6,'gross_minor'=>17500,'stock_minor'=>7500,'consumption_minor'=>2500,'physical_quantity_milli'=>1500,'closures'=>1,'physical_financial_rights_fingerprint_unchanged'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Three journals/six lines; stock75 and consumption25, physical1.5UN; reviewed close once.\n";exit; } if(in_array($phase,['race','worker'],true)){require __DIR__.'/stock-ledger-race.php';exit;} if($phase==='extra'){require __DIR__.'/stock-ledger-extra.php';exit;} if($phase==='order-guard'){require __DIR__.'/stock-ledger-order-guard.php';exit;} if($phase==='http-proof'){require __DIR__.'/stock-ledger-http-proof.php';exit;} if($phase==='http'){require __DIR__.'/stock-ledger-http.php';exit;} throw new LogicException('Unknown stock proof phase');