Files
SGF/scripts/ledger-sales-coexistence.php
2026-11-04 09:37:20 +02:00

24 lines
6.1 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Modules\Purchasing\SupplierPayables;
use Tempo\Finance\Payables\Payables;
$output=$s['root'].'/sales-coexistence.json';if(file_exists($output)||DB::table('sr_records')->exists()||DB::table('pa_obligations')->exists()||DB::table('gl_journals')->exists())throw new LogicException('Separate fresh mixed-book fixture required');
DB::statement('CREATE TABLE pa_imported_sources(id bigserial PRIMARY KEY,tenant_id bigint,company_id bigint,unit_id bigint,payload jsonb,receipt_hash varchar(64))');
$constraint=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($constraint,6).") OR (unit_id IS NOT NULL AND permission='supplier_payables.view'))");
foreach($g['actors'] as $role=>$actor){Tests\Support\IdentityFixture::grant($actor,$scope['tenant_id'],$scope['company_id'],'supplier_payables.view',$scope['unit_id']);if($role!=='read')DB::table('pa_access')->insert($scope+['user_id'=>$actor,'action'=>$role==='prepare'?'prepare':'validate','person'=>'sales-qa-person-'.$actor]);}
$accounts=[];foreach(['QA-CONTRAPARTIDA','QA-FORNECEDOR'] as $code)$accounts[$code]=DB::table('gl_accounts')->insertGetId($scope+['code'=>$code,'label'=>'Conta artificial adicional para coexistência','is_movement'=>true,'currency'=>'MZN']);
DB::table('gl_rules')->insert($scope+['created_by'=>$g['actors']['prepare'],'reason'=>'Explicit mixed-book QA rule.','created_at'=>now(),'rule_key'=>'QA-PAYABLE-JOURNAL','version'=>1,'policy'=>Tempo\Finance\GeneralLedger\PayableJournal::POLICY,'effect_type'=>'supplier_payable.recognised','currency'=>'MZN','valid_from'=>'2026-10-01','valid_to'=>'2026-10-31','debit_account_id'=>$accounts['QA-CONTRAPARTIDA'],'credit_account_id'=>$accounts['QA-FORNECEDOR']]);
$r=$sr->propose($enter('prepare')->id,['kind'=>'obligation','invoice_id'=>$g['invoice_id'],'currency'=>'MZN','source'=>'TEST-MANUAL','external_id'=>'MIXED-QA-OPEN','revision'=>0,'reason'=>'Explicit customer debt opening in mixed QA book.'],(string)Str::uuid());$customer=$r['id'];$sr->decide($customer,$enter('validate')->id,['version'=>1,'revision'=>0,'decision'=>'approved','reason'=>'Independent customer debt opening.'],(string)Str::uuid());
$bundle=json_decode(file_get_contents($s['root'].'/source.json'),true,64,JSON_THROW_ON_ERROR);$doc=$bundle['source'];unset($doc['local_id']);$doc['document']=990001;$doc['number']='QA-MIXED-SUPPLIER-60';$doc['payment_count']=0;$payload=Payables::canonical($doc);$local=DB::table('pa_imported_sources')->insertGetId($scope+['payload'=>$payload,'receipt_hash'=>hash('sha256',$payload)]);
$payable=app(SupplierPayables::class);$p=$payable->engine($enter('prepare'))->command('prepare',$local,'Explicit controlled synthetic supplier receipt, not a live import.',(string)Str::uuid());$o=$payable->engine($enter('validate'))->command('validate',$p['proposal_id'],'Independent supplier QA recognition.',(string)Str::uuid());
if($customer!==$o['obligation_id'])throw new LogicException('This fresh fixture must exercise coinciding customer/supplier IDs');
$post=function($p)use($command){$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent typed source review in one ledger.']);return $command('prepare','post',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Apply typed source once without competing writer.']);};
$sales=$post($command('prepare','effect-prepare',['effect_type'=>'sales.invoice','source_id'=>$g['invoice_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>'QA-SALES','reason'=>'Capture three-line customer invoice.']));
$supplier=$post($command('prepare','prepare',['obligation_id'=>$o['obligation_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>'QA-PAYABLE-JOURNAL','reason'=>'Capture two-line supplier obligation in the same ledger.']));
$all=$ledger->listing($enter('read'));$customerBook=$ledger->listing($enter('read'),['sales_invoice_id'=>$g['invoice_id']]);$supplierBook=$ledger->listing($enter('read'),['obligation_id'=>$o['obligation_id']]);
if(count($all['journals'])!==2||count($all['lines'])!==5||$all['totals']['debit_minor']!==106000||count($customerBook['journals'])!==1||count($customerBook['events'])!==3||$customerBook['totals']['debit_minor']!==100000||count($supplierBook['journals'])!==1||count($supplierBook['events'])!==3||$supplierBook['totals']['debit_minor']!==6000||$supplierBook['journals'][0]->id!==$supplier['journal_id'])throw new LogicException('Typed source filters mixed coinciding IDs');
$p=$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Capture both supplier and customer source families.']);$command('validate','period-validate',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent whole-entity mixed-source conference.']);$command('prepare','period-close',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'reason'=>'Close both fully accounted source families.']);
if($sr->position($customer,$enter('read')->id)['balance']!=='1000.00'||$payable->engine($enter('read'))->positionForSource($local)['balance_minor']!==6000)throw new LogicException('Accounting changed either financial balance');DB::select('SELECT gl_assert_balanced(id) FROM gl_journals');
file_put_contents($output,json_encode(['passed'=>true,'customer_supplier_ids_coincide'=>true,'one_writer'=>true,'customer_filter_journals'=>1,'customer_filter_events'=>3,'supplier_filter_journals'=>1,'supplier_filter_events'=>3,'journals'=>2,'lines'=>5,'gross_debit_minor'=>106000,'gross_credit_minor'=>106000,'customer_balance_minor'=>100000,'supplier_balance_minor'=>6000,'joint_period_closed'=>true,'supplier_origin_is_controlled_synthetic_receipt_not_live_import'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Mixed book: coinciding customer/supplier IDs isolated, two/three-line journals balanced and one joint close.\n";