16 lines
7.2 KiB
PHP
16 lines
7.2 KiB
PHP
<?php
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Tempo\Finance\Payables\Payables;
|
|
use App\Modules\Purchasing\SupplierPayables;
|
|
$output=$s['root'].'/cost-correction-coexistence.json';if(file_exists($output)||DB::table('gl_corrections')->exists()||DB::table('pa_obligations')->exists())throw new LogicException('Fresh correction mixed-source fixture required');$costSource=app(App\Modules\Accounting\CostLedgerSource::class)->source($enter('read'),$g['order_id']);$outbox=DB::table('dc_outbox')->orderBy('event_id')->get()->toJson();
|
|
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))');$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='supplier_payables.view'))");foreach(['prepare','validate','read'] as $role){$actor=$g['actors'][$role];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'=>'cost-qa-person-'.$actor]);}
|
|
$accounts=[];foreach(['QA-CONTRAPARTIDA','QA-FORNECEDOR'] as $code)$accounts[$code]=DB::table('gl_accounts')->insertGetId($scope+['code'=>$code,'label'=>'Artificial supplier integration account','is_movement'=>true,'currency'=>'MZN']);foreach(['QA-PAYABLE-JOURNAL'=>['supplier_payable.recognised','QA-CONTRAPARTIDA','QA-FORNECEDOR'],'QA-CORRECTION-REVERSE'=>['journal.reversal','QA-FORNECEDOR','QA-CONTRAPARTIDA'],'QA-CORRECTION-REPLACE'=>['journal.replacement','QA-CONTRAPARTIDA','QA-FORNECEDOR']] as $rule=>[$kind,$d,$c])DB::table('gl_rules')->insert($scope+['created_by'=>$g['actors']['prepare'],'reason'=>'Existing approved supplier correction in same new cost-correction schema.','created_at'=>now(),'rule_key'=>$rule,'version'=>1,'policy'=>Tempo\Finance\GeneralLedger\PayableJournal::POLICY,'effect_type'=>$kind,'currency'=>'MZN','valid_from'=>'2026-10-01','valid_to'=>'2026-10-31','debit_account_id'=>$accounts[$d],'credit_account_id'=>$accounts[$c]]);
|
|
$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']=990003;$doc['number']='QA-CORRECTION-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)]);$finance=app(SupplierPayables::class);$p=$finance->engine($enter('prepare'))->command('prepare',$local,'Controlled synthetic supplier receipt, not a live import.',(string)Str::uuid());$o=$finance->engine($enter('validate'))->command('validate',$p['proposal_id'],'Independent supplier60 recognition.',(string)Str::uuid());if($o['obligation_id']!==$g['order_id'])throw new LogicException('Coinciding financial/order IDs required');$p=$command('prepare','prepare',['obligation_id'=>$o['obligation_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','rule_key'=>'QA-PAYABLE-JOURNAL','reason'=>'Existing approved supplier60 beside original cost275.']);$command('validate','validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent supplier journal review.']);$supplier=$command('prepare','post',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Apply supplier once.']);
|
|
$costGroup=$captureCorrection();$approveCorrection($costGroup);$command('prepare','correct-post',$postCorrectionBody($costGroup));$supplierGroup=$command('prepare','correct-prepare',['journal_id'=>$supplier['journal_id'],'journal_on'=>'2026-10-29','period_key'=>'QA-2026-10','reason'=>'Existing technical supplier reversal60/replacement60 compatibility.']);$command('validate','correct-validate',['correction_id'=>$supplierGroup['correction_id'],'revision'=>$supplierGroup['revision'],'decision'=>'approved','reason'=>'Independent supplier reversal60/replacement60 conference; separate cost275->295 group.']);$command('prepare','correct-post',['correction_id'=>$supplierGroup['correction_id'],'revision'=>$supplierGroup['revision'],'reason'=>'Apply existing supplier60/60 compatibility pair once, no financial settlement.']);
|
|
$costBook=$ledger->listing($enter('read'),['cost_order_id'=>$g['order_id']]);$supplierBook=$ledger->listing($enter('read'),['obligation_id'=>$o['obligation_id']]);$workspace=$ledger->costs($enter('read'),$g['order_id']);$report=$ledger->report($enter('read'),'trial-balance',['from_on'=>'2026-10-01','to_on'=>'2026-10-31']);
|
|
if(count($costBook['journals'])!==3||count($costBook['events'])!==9||$costBook['totals']['debit_minor']!==84500||count($supplierBook['journals'])!==3||count($supplierBook['events'])!==9||$supplierBook['totals']['debit_minor']!==18000||count($workspace['corrections'])!==1||$workspace['pagination']['corrections']['total']!==1||$report['totals']['debit_minor']!==102500||DB::table('gl_journals')->count()!==6||DB::table('gl_lines')->count()!==12)throw new LogicException('Cost/supplier corrections mixed filters/groups or totals');
|
|
$p=$command('prepare','period-prepare',['period_key'=>'QA-2026-10','reason'=>'Joint current cost/supplier correction conference.']);$command('validate','period-validate',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Independent joint resolved source review.']);$command('prepare','period-close',['check_id'=>$p['check_id'],'revision'=>$p['revision'],'reason'=>'Close both corrected source families in one QA period.']);
|
|
if(Payables::canonical(app(App\Modules\Accounting\CostLedgerSource::class)->source($enter('read'),$g['order_id']))!==Payables::canonical($costSource)||DB::table('dc_outbox')->orderBy('event_id')->get()->toJson()!==$outbox||$finance->engine($enter('read'))->positionForSource($local)['balance_minor']!==6000||hash('sha256',Payables::canonical((array)DB::table('gl_journals')->where('id',$cg['original_journal_id'])->first()))!==$cg['original_journal_sha256'])throw new LogicException('Authoritative cost/outbox/supplier balance or original changed');DB::select('SELECT gl_assert_balanced(id) FROM gl_journals');DB::select('SELECT gl_cost_pair_assert(?)',[$costGroup['correction_id']]);
|
|
file_put_contents($output,json_encode(['passed'=>true,'coinciding_order_and_supplier_obligation_ids'=>true,'cost_typed_journals'=>3,'cost_typed_events'=>9,'supplier_typed_journals'=>3,'supplier_typed_events'=>9,'focused_cost_groups'=>1,'both_sql_pair_domains_validated'=>true,'journals'=>6,'lines'=>12,'applied_groups'=>2,'gross_minor'=>102500,'current_cost_minor'=>29500,'supplier_balance_minor'=>6000,'source_original_outbox_preserved'=>true,'joint_qa_close'=>true,'controlled_supplier_receipt_not_live_import'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Cost and supplier corrections coexist: coinciding source IDs isolated, six journals/twelve lines, gross1025, one joint close; both SQL pair domains preserved.\n";
|