setUser($user);$r=Illuminate\Http\Request::create('/api/finance/payable-positions','GET',$query);$r->attributes->set('sgf_module','finance');$r->setUserResolver(fn()=>$user);$session=app('session')->driver();$session->start();$session->put('company_context',$f['scope']+['module_id'=>'finance','token'=>'due-list-proof']);$r->setLaravelSession($session);$app->instance('request',$r);app(App\Shared\Context\CompanyContext::class)->selectFor(17,1,2,2);return app(App\Modules\Purchasing\PayablePositionReadController::class)->index($r,app(App\Modules\Purchasing\ComposedFinance::class))->getData(true);}; $row=['obligation_id'=>1,'source_conflict'=>false,'balance_minor'=>'10000','reviewed_terms'=>['due_on'=>'2026-11-30']];$class=fn($r,$date)=>App\Modules\Purchasing\PayableAge::classify($r,$date);foreach([0=>'not_due',1=>'days_1_30',30=>'days_1_30',31=>'days_31_60',60=>'days_31_60',61=>'days_61_90',90=>'days_61_90',91=>'days_over_90']as$days=>$bucket){$reference=(new DateTimeImmutable('2026-11-30'))->modify('+'.$days.' days')->format('Y-m-d');$r=$class($row,$reference);$ok($r['reviewed_age_days']===$days&&$r['reviewed_age_bucket']===$bucket,'exact age boundary '.$days);} $ok($class($row,'2026-11-29')['reviewed_age_days']===0,'future due no negative age');$ok($class($row,null)['reviewed_age_bucket']===null,'no implicit reference');$ok($class(array_replace($row,['reviewed_terms'=>null]),'2026-12-15')===['reviewed_age_days'=>null,'reviewed_age_bucket'=>'unknown'],'unknown due not zero');$ok($class(array_replace($row,['balance_minor'=>null,'source_conflict'=>true]),'2026-12-15')['reviewed_age_bucket']==='indeterminate','conflicting source not assumed aging debt');$ok($class(array_replace($row,['obligation_id'=>null,'balance_minor'=>null]),'2026-12-15')['reviewed_age_bucket']==='unrecognised','unrecognised source distinct');$ok($class(array_replace($row,['balance_minor'=>'0']),'2026-12-15')['reviewed_age_bucket']==='settled','zero book balance separate from overdue debt'); $before=[];foreach(['pa_obligations','pd_due_versions','pd_due_commands','ps_allocations','gl_journals']as$t)$before[$t]=$native->table($t)->orderBy('id')->get()->toJson();$plain=$positionCall();$dated=$positionCall(['reference_on'=>'2026-12-15','per_page'=>25]);$large=$positionCall(['reference_on'=>'2026-12-15','per_page'=>100]);$ok($dated['positions']===$large['positions'],'known source25/100 equal');$ok($dated['aging_reference_on']==='2026-12-15'&&$dated['aging_basis']==='reviewed_administrative_terms_current_balance','reference and administrative basis explicit');foreach($dated['positions']as$r){$old=collect($plain['positions'])->firstWhere('document_id',$r['document_id']);$ok($old['balance_minor']===$r['balance_minor']&&$old['due_on']===$r['due_on']&&$old['days_overdue']===$r['days_overdue'],'original financial state and due unchanged '.$r['document_id']);if($r['obligation_id'])$ok($r['reviewed_age_days']===15&&$r['reviewed_age_bucket']==='days_1_30','effective reviewed deadline15 days');} $rejectCatalogue(fn()=>$positionCall(['reference_on'=>'2026-02-30']),422,'impossible reference refused');$rejectCatalogue(fn()=>$positionCall(['reference_on'=>'tomorrow']),422,'relative reference refused');$ok($positionCall(['reference_on'=>'2026-12-15','page'=>100000])['positions']===[],'empty page no fallback');foreach($before as$t=>$json)$ok($native->table($t)->orderBy('id')->get()->toJson()===$json,'original '.$t.' unchanged');file_put_contents($work.'/payable-aging-result.json',json_encode(['passed'=>true,'checks'=>$checks,'business_commands'=>0,'new_rights'=>0,'new_demo'=>false],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/payable-aging-result.json']).PHP_EOL;