true,'general_ledger.synthetic_enabled'=>true,'general_ledger.cycle_enabled'=>true]); if($phase==='ledger-sales-setup'){ if(DB::getSchemaBuilder()->hasTable('users'))throw new LogicException('New sales QA 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<='2026_10_27_070000_create_simulated_receivables.php'))(require $migration)->up();} PayablesSchema::install();HistorySchema::install();SettlementSchema::install(); // Reversal schema's old environment guard remains unchanged; this explicit setup is still on the checked QA connection. $app->instance('env','payable-testing');ReversalSchema::install();$app->instance('env','testing'); LedgerSchema::install(DB::connection());CycleSchema::install(DB::connection());SalesSchema::install(DB::connection()); // Only this freshly created database gets its own exact SQL environment assertion. 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\ReceivablesFixture::create();$scope=['tenant_id'=>$base['tenant'],'company_id'=>$base['company'],'unit_id'=>$base['unit']]; $reader=App\Models\User::create(['name'=>'Consulta contabilĂ­stica QA','email'=>'sales-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'=>$reader->id]); $actors=['prepare'=>$base['manager']->id,'validate'=>$base['reader']->id,'read'=>$reader->id]; DB::table('permission_grants')->whereIn('user_id',[...array_values($actors),$base['limited']->id])->delete(); $definition=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($definition,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($actors as $role=>$actor){ foreach(['company.view'=>null,'unit.view'=>$scope['unit_id'],'service_invoices.view'=>$scope['unit_id'],'service_receivables.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'=>['service_receivables.prepare','service_receivables.allocate','service_receivables.correction_request','general_ledger.prepare','general_ledger.post','general_ledger.period_prepare','general_ledger.period_close'],'validate'=>['service_receivables.confirm','service_receivables.correction_approve','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'=>'sales-qa-person-'.$actor,'action'=>'view']); } $ids=[];foreach(['QA-CLIENTES','QA-VENDAS','QA-IMPOSTO-TESTE','QA-LIQUIDACAO-CLIENTE'] as $code)$ids[$code]=DB::table('gl_accounts')->insertGetId($scope+['code'=>$code,'label'=>'Conta artificial '.$code,'is_movement'=>true,'currency'=>'MZN']); $common=['created_by'=>$actors['prepare'],'reason'=>'Explicit authorised sales QA configuration.','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']); foreach(['QA-SALES'=>['sales.invoice','QA-CLIENTES','QA-VENDAS','QA-IMPOSTO-TESTE'],'QA-RECEIVABLE'=>['receivable.allocated','QA-LIQUIDACAO-CLIENTE','QA-CLIENTES',null],'QA-RECEIVABLE-REVERSE'=>['receivable.reversed','QA-CLIENTES','QA-LIQUIDACAO-CLIENTE',null]] as $key=>[$type,$d,$cr,$tax])DB::table('gl_rules')->insert($scope+$common+['rule_key'=>$key,'version'=>1,'policy'=>'QA-SALES-JOURNAL-v1','effect_type'=>$type,'currency'=>'MZN','valid_from'=>'2026-10-01','valid_to'=>'2026-10-31','debit_account_id'=>$ids[$d],'credit_account_id'=>$ids[$cr],'tax_account_id'=>$tax?$ids[$tax]:null]); // This is a declared association matrix for NEW synthetic records, never legacy inference. foreach(App\Shared\Context\ModuleRecords::AREAS as $area)foreach(DB::table('a_'.$area)->where('tenant_id',$scope['tenant_id'])->where('company_id',$scope['company_id'])->get(['id','version']) as $row)foreach(['commercial','finance','accounting'] as $module)App\Shared\Context\ModuleRecords::associate($area,$row->id,$actors['prepare'],['tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id'],'unit_id'=>str_ends_with($area,'_drafts')?$scope['unit_id']:null],$module,'Explicit new sales QA evidence-sharing fixture.',false,$row->version); $g=['scope'=>$scope,'actors'=>$actors,'invoice_id'=>$base['invoice'],'draft_id'=>$base['draft'],'accounts'=>$ids,'fixture_day'=>'2026-10-29'];file_put_contents($s['root'].'/sales-fixture.json',json_encode($g,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)); echo "New sales composition: actual test invoice, isolated SQL/PHP guards, explicit role/association matrix; no financial or ledger effects yet.\n";exit; } $g=json_decode(file_get_contents($s['root'].'/sales-fixture.json'),true,64,JSON_THROW_ON_ERROR);$scope=$g['scope']; config(['modules.contracted'=>['commercial','finance','accounting'],'modules.companies.'.$scope['tenant_id'].':'.$scope['company_id']=>['contracted'=>['commercial','finance','accounting'],'enabled'=>['commercial','finance','accounting']]]); $enter=function(string $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);$sr=app(ServiceReceivables::class); $command=fn($role,$action,$body,$key=null,$fault=null)=>$ledger->engine($enter($role),$fault)->command($action,$body,$key??(string)Str::uuid()); if($phase==='ledger-sales-http'){require __DIR__.'/ledger-sales-http-entry.php';exit;} if($phase==='ledger-sales-cost-compatibility'){ $out=$s['root'].'/cost-extension-sales-compatibility.json';if(file_exists($out))throw new LogicException('Completed compatibility proof preserved');$j=DB::table('gl_journals')->where('snapshot->source->effect_type','sales.invoice')->first();$old=json_decode($j->snapshot,true);$u=$enter('read');$source=app(App\Modules\Accounting\SalesLedgerSource::class)->source($u,'sales.invoice',$g['invoice_id']);$provider=app(App\Modules\Accounting\SalesLedgerSource::class)->period($u,$old['period']);$engine=$ledger->engine($u);$closure=(new ReflectionProperty($engine,'additionalPeriod'))->getValue($engine);$wrapped=$closure($old['period']); if(Payables::canonical($source)!==Payables::canonical($old['source'])||$wrapped!==$provider||config('cost_ledger.synthetic_enabled')!==false||$ledger->report($u,'trial-balance',['from_on'=>'2026-10-01','to_on'=>'2026-10-31'])['totals']['debit_minor']!==240000)throw new LogicException('Cost extension changed existing Sales source/period packet or report'); file_put_contents($out,json_encode(['passed'=>true,'actual_sales_source_matches_original_posted_snapshot'=>true,'single_sales_provider_hash_unchanged'=>true,'existing_trial_balance_debit_minor'=>240000,'cost_default_off'=>true,'no_business_commands'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Existing Sales source and single-provider period packet preserved exactly; closed report still2400 per side.\n";exit; } if($phase==='ledger-sales-report-reconcile'){require __DIR__.'/ledger-reports-reconcile.php';exit;} if($phase==='ledger-sales-source-postcheck'){ $output=$s['root'].'/sales-source-postcheck.json';if(file_exists($output))throw new LogicException('Completed source proof preserved'); $invoice=DB::table('si_invoices')->where('id',$g['invoice_id'])->first();$snapshots=DB::table('gl_journals')->get(['snapshot']);$matched=0; foreach($snapshots as $row){$snap=json_decode($row->snapshot,true,64,JSON_THROW_ON_ERROR);if(($snap['source']['invoice_id']??null)===$g['invoice_id']){if(($snap['source']['document_revision']??null)!==hash('sha256',$invoice->snapshot))throw new LogicException('Posted documentary source changed');$matched++;}} if(!$matched||hash('sha256',base64_decode($invoice->pdf_base64,true))!==$invoice->pdf_sha256)throw new LogicException('Missing original documentary/PDF evidence'); file_put_contents($output,json_encode(['passed'=>true,'current_invoice_matches_posted_document_revision'=>true,'matching_journals'=>$matched,'pdf_hash_matches'=>true,'no_business_writes'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Original documentary snapshot and PDF verified against posted source, read only.\n";exit; } if($phase==='ledger-sales-cases'){require __DIR__.'/ledger-sales-cases.php';exit;} if($phase==='ledger-sales-integrity'){require __DIR__.'/ledger-sales-integrity.php';exit;} if($phase==='ledger-sales-coexistence'){require __DIR__.'/ledger-sales-coexistence.php';exit;} if($phase==='ledger-sales-module-check'){ $output=$s['root'].'/sales-module-check.json';if(file_exists($output))throw new LogicException('Completed module proof preserved');$old=request()->attributes->get('sgf_module');$before=DB::table('identity_audit')->count();DB::beginTransaction(); try{request()->attributes->set('sgf_module','accounting');$u=$enter('prepare');App\Shared\Context\ModuleRecords::associate('billing_drafts',$g['draft_id'],$u->id,$scope,'accounting','Rollback-only source isolation adversarial probe.',true,1); $empty=$ledger->sales($enter('read'));if($empty['commercial']['invoices'])throw new LogicException('Unshared invoice metadata leaked into accounting picker');$caught=false;try{$ledger->sales($enter('read'),$g['invoice_id']);}catch(DomainException|Symfony\Component\HttpKernel\Exception\HttpException){$caught=true;}if(!$caught)throw new LogicException('Unshared source must be refused'); $caught=false;try{$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Hidden unaccounted invoice must remain a completeness blocker.']);}catch(DomainException){$caught=true;}if(!$caught)throw new LogicException('Unshared issued fact cannot disappear from completeness check'); }finally{while(DB::transactionLevel()>0)DB::rollBack();if($old===null)request()->attributes->remove('sgf_module');else request()->attributes->set('sgf_module',$old);} if(DB::table('identity_audit')->count()!==$before)throw new LogicException('Association probe not rolled back');file_put_contents($output,json_encode(['passed'=>true,'checks'=>4,'picker_respects_module_associations'=>true,'source_without_association_refused'=>true,'whole_period_still_blocks_unaccounted_source'=>true,'associations_unchanged_after_rollback'=>true],JSON_PRETTY_PRINT));echo "Module isolation and completeness: four checks, original associations restored by rollback.\n";exit; } if($phase==='ledger-sales-demo-harden'){ $output=$s['root'].'/sales-demo-access.json';if(file_exists($output)||DB::table('sr_records')->exists()||DB::table('gl_journals')->exists())throw new LogicException('Only the new unconsumed team fixture may be prepared'); $extra=DB::table('users')->whereNotIn('id',array_values($g['actors']))->pluck('id')->all();$count=DB::table('permission_grants')->whereIn('user_id',$extra)->count(); DB::transaction(function()use($extra,$count,$g,$scope){DB::table('permission_grants')->whereIn('user_id',$extra)->delete();if($count)App\Modules\Identity\Services\Audit::record('qa.fixture.unused_grants_removed',$g['actors']['prepare'],$scope['tenant_id'],$scope['company_id'],$scope['unit_id'],details:['reason'=>'Only the three declared sales QA roles retain access before team delivery.','count'=>$count]);}); file_put_contents($output,json_encode(['passed'=>true,'declared_roles'=>3,'unlisted_users_with_grants'=>DB::table('permission_grants')->whereIn('user_id',$extra)->count(),'pruned_helper_grants'=>$count,'original_users_and_approvals_preserved'=>true,'business_effects'=>0],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "New team fixture access matrix checked: only three declared QA roles, no business effects.\n";exit; } if($phase==='ledger-sales-defaults'){ $output=$s['root'].'/sales-defaults.json';if(file_exists($output))throw new LogicException('Completed defaults proof preserved'); $value=getenv('SGF_SALES_LEDGER_QA');$server=$_SERVER['SGF_SALES_LEDGER_QA']??null;$env=$_ENV['SGF_SALES_LEDGER_QA']??null;putenv('SGF_SALES_LEDGER_QA');unset($_SERVER['SGF_SALES_LEDGER_QA'],$_ENV['SGF_SALES_LEDGER_QA']); try{$default=(require $c['root'].'/config/sales_ledger.php')['synthetic_enabled'];}finally{if($value!==false)putenv('SGF_SALES_LEDGER_QA='.$value);if($server!==null)$_SERVER['SGF_SALES_LEDGER_QA']=$server;if($env!==null)$_ENV['SGF_SALES_LEDGER_QA']=$env;} if($default!==false||config('sales_ledger.synthetic_enabled')!==true)throw new LogicException('Absent setting must be off; explicit QA composition remains on'); file_put_contents($output,json_encode(['passed'=>true,'default_without_environment'=>false,'explicit_owned_qa_runtime'=>true,'real_environment_not_changed'=>true,'no_business_writes'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Sales capability defaults off; explicit owned QA runtime only is enabled.\n";exit; } if($phase==='ledger-sales-final'){ $output=$s['root'].'/sales-final.json';if(file_exists($output))throw new LogicException('Completed final proof preserved');$u=$enter('read');$data=$ledger->sales($u,$g['invoice_id']);$invoice=DB::table('si_invoices')->where($scope)->where('id',$g['invoice_id'])->first();$snapshot=json_decode(DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->value('snapshot'),true); if($snapshot['source']['document_revision']!==hash('sha256',$invoice->snapshot)||hash('sha256',base64_decode($invoice->pdf_base64,true))!==$invoice->pdf_sha256||$data['commercial']['position']['balance']!=='400.00'||$data['client_account']['net_minor']!==40000||count($data['book']['journals'])!==4||count($data['book']['lines'])!==9)throw new LogicException('Final invoice/PDF/financial/ledger reconciliation differs'); $constraint=DB::selectOne("SELECT tgdeferrable,tginitdeferred FROM pg_trigger WHERE tgname='gl_sales_line_balance' AND tgrelid='gl_lines'::regclass");if(!$constraint||!$constraint->tgdeferrable||!$constraint->tginitdeferred)throw new LogicException('New line constraint missing in final schema'); $html=view('accounting.sales-ledger',['data'=>$ledger->sales($enter('prepare'),$g['invoice_id'])])->render();if(str_contains($html,'data-finance=')||str_contains($html,'data-ledger=')||!str_contains($html,'PerĂ­odo QA encerrado'))throw new LogicException('Closed demonstration must remain a readable receipt without new command forms'); file_put_contents($output,json_encode(['passed'=>true,'invoice_snapshot_sha256'=>hash('sha256',$invoice->snapshot),'pdf_sha256'=>$invoice->pdf_sha256,'original_snapshot_and_pdf_preserved'=>true,'principal_minor'=>100000,'balance_minor'=>40000,'client_account_minor'=>40000,'journals'=>4,'lines'=>9,'gross_debits_minor'=>240000,'gross_credits_minor'=>240000,'late_line_constraint_deferred'=>true,'closed_ui_read_only'=>true,'default_sales_flag'=>(require $c['root'].'/config/sales_ledger.php')['synthetic_enabled']],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Final source/PDF/account/financial reconciliation and closed UI checked on latest schema.\n";exit; } if(in_array($phase,['ledger-sales-race','ledger-sales-worker'],true)){require __DIR__.'/ledger-sales-race.php';exit;} if($phase==='ledger-sales-workflow'){ if(file_exists($s['root'].'/sales-workflow.json')||DB::table('sr_effects')->exists()||DB::table('gl_journals')->exists())throw new LogicException('Fresh unconsumed sales workflow required'); $checks=0;$ok=function($v,$why)use(&$checks){$checks++;if(!$v)throw new LogicException($why);}; $originHash=hash('sha256',DB::table('si_invoices')->where('id',$g['invoice_id'])->value('snapshot')); $p=$sr->propose($enter('prepare')->id,['kind'=>'obligation','invoice_id'=>$g['invoice_id'],'currency'=>'MZN','source'=>'TEST-MANUAL','external_id'=>'SALES-QA-OPEN','revision'=>0,'reason'=>'Explicit financial opening of the actual test invoice.'],(string)Str::uuid());$ob=$p['id']; $sr->decide($ob,$enter('validate')->id,['version'=>1,'revision'=>0,'decision'=>'approved','reason'=>'Independent documentary opening review.'],(string)Str::uuid()); $post=function($kind,$id,$rule)use($command){$p=$command('prepare','effect-prepare',['effect_type'=>$kind,'source_id'=>$id,'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>$rule,'reason'=>'Capture exact synthetic commercial financial effect.']);$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent exact commercial accounting review.']);$body=['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Apply reviewed commercial effect once.'];$key=(string)Str::uuid();$j=$command('prepare','post',$body,$key);return [$j,$command('prepare','post',$body,$key)];}; [$invoiceJournal,$repeat]=$post('sales.invoice',$g['invoice_id'],'QA-SALES');$ok($repeat['replayed']&&DB::table('gl_lines')->count()===3,'Three-line invoice posted once'); $allocations=[];$receipts=[];$rev=fn()=>DB::table('sr_effects')->where('obligation_id',$ob)->count(); foreach(['400.00','600.00'] as $n=>$amount){ $p=$sr->propose($enter('prepare')->id,['kind'=>'receipt','invoice_id'=>$g['invoice_id'],'obligation_id'=>$ob,'currency'=>'MZN','amount'=>$amount,'date'=>'2026-10-29','evidence'=>'Explicit synthetic receipt; no money movement.','source'=>'TEST-MANUAL','external_id'=>'SALES-QA-RECEIPT-'.$n,'revision'=>$rev(),'reason'=>'Individual receipt preparation.'],(string)Str::uuid());$receipts[]=$p['id']; $sr->decide($p['id'],$enter('validate')->id,['version'=>1,'revision'=>$rev(),'decision'=>'approved','reason'=>'Independent receipt confirmation.'],(string)Str::uuid()); $a=$sr->allocate($p['id'],$enter('prepare')->id,['version'=>1,'revision'=>$rev(),'amount'=>$amount],(string)Str::uuid());$allocations[]=$a['id']; $position=$sr->position($ob,$enter('read')->id);$ok($position['balance']===($n===0?'600.00':'0.00'),'Authoritative partial/full balance'); $post('receivable.allocated',$a['id'],'QA-RECEIVABLE');$ok($sr->position($ob,$enter('read')->id)['balance']===$position['balance'],'Accounting never deducts financial balance again'); } $p=$sr->propose($enter('prepare')->id,['kind'=>'correction','invoice_id'=>$g['invoice_id'],'obligation_id'=>$ob,'currency'=>'MZN','source'=>'TEST-MANUAL','external_id'=>'SALES-QA-REVERSE-400','revision'=>$rev(),'action'=>'reverse_allocation','target_effect_id'=>$allocations[0],'reason'=>'Reverse first synthetic allocation with original preserved.'],(string)Str::uuid()); $reversal=$sr->decide($p['id'],$enter('validate')->id,['version'=>1,'revision'=>$rev(),'decision'=>'approved','reason'=>'Independent integral allocation reversal review.'],(string)Str::uuid()); $ok($sr->position($ob,$enter('read')->id)['balance']==='400.00','Financial reversal restores400');$post('receivable.reversed',$reversal['id'],'QA-RECEIVABLE-REVERSE'); $ok($sr->position($ob,$enter('read')->id)['balance']==='400.00','Reversal journal does not affect financial balance'); $p=$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Capture full commercial cycle and pending evidence.']);$command('validate','period-validate',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent reconciled sales period review.']);$closed=$command('prepare','period-close',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'reason'=>'Close artificial commercial period once.']); $book=$ledger->listing($enter('read'));$ok($book['totals']===['debit_minor'=>240000,'credit_minor'=>240000,'net_minor'=>0]&&count($book['journals'])===4&&count($book['lines'])===9,'Four journals/nine lines reconcile to gross2400 each side'); $client=$ledger->listing($enter('read'),['account_id'=>$g['accounts']['QA-CLIENTES']]);$ok($client['totals']['net_minor']===40000,'Client account agrees with financial balance400'); $ok($originHash===hash('sha256',DB::table('si_invoices')->where('id',$g['invoice_id'])->value('snapshot')),'Original issued invoice remains immutable');DB::select('SELECT gl_assert_balanced(id) FROM gl_journals'); file_put_contents($s['root'].'/sales-workflow.json',json_encode(['passed'=>true,'checks'=>$checks,'invoice_id'=>$g['invoice_id'],'obligation_id'=>$ob,'receipts'=>$receipts,'allocations'=>$allocations,'reversal_effect_id'=>$reversal['id'],'invoice_journal_id'=>$invoiceJournal['journal_id'],'journals'=>4,'lines'=>9,'gross_debit_minor'=>240000,'gross_credit_minor'=>240000,'client_balance_minor'=>40000,'financial_balance_minor'=>40000,'period_closed'=>true,'origin_unchanged'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Sales workflow complete: $checks checks, principal1000, balance400, four journals/nine lines and QA close.\n";exit; } throw new LogicException('Unknown sales phase');