Files
SGF/scripts/sgf-document-cancellation-proof.php
T
2026-11-11 09:00:17 +02:00

26 lines
6.1 KiB
PHP

<?php
use Illuminate\Support\Str;use App\Modules\Services\{ServiceDocuments,ServiceInvoicing,ServiceReceivables};
require __DIR__.'/sgf-service-documents-proof-bootstrap.php';$host->beginTransaction();$native->beginTransaction();config(['sales_ledger.synthetic_enabled'=>true]);$before=$host->table('sd_outputs')->orderBy('id')->get()->toJson();
try{
$actor=$select('commercial_prepare');$i=$inputs['credit_invoice'];$w=app(ServiceInvoicing::class);$r=$w->review($i['draft_id'],$i['series_id'],$i['rule_id'],$actor);$invoice=$w->issue($i['draft_id'],$actor,['series_id'=>$i['series_id'],'rule_id'=>$i['rule_id'],'review_hash'=>$r['review_hash']],(string)Str::uuid())['id'];$sr=app(ServiceReceivables::class);
$actor=$select('finance_prepare');$ob=$sr->propose($actor,['kind'=>'obligation','invoice_id'=>$invoice,'currency'=>'MZN','source'=>'SIM-CANCEL','external_id'=>'OB-'.$invoice,'revision'=>0],(string)Str::uuid())['id'];$actor=$select('finance_review');$sr->decide($ob,$actor,['version'=>1,'revision'=>0,'decision'=>'approved','reason'=>'Preparação de origem para ensaio de anulação documental.'],(string)Str::uuid());
$actor=$select('finance_prepare');$rc=$sr->propose($actor,['kind'=>'receipt','invoice_id'=>$invoice,'obligation_id'=>$ob,'currency'=>'MZN','source'=>'SIM-CANCEL','external_id'=>'RC-'.$invoice,'amount'=>'400.00','date'=>'2026-11-10','evidence'=>'Pagamento fictício para reversão documental em rollback.','revision'=>1],(string)Str::uuid())['id'];$actor=$select('finance_review');$sr->decide($rc,$actor,['version'=>1,'revision'=>1,'decision'=>'approved','reason'=>'Recebimento conferido distintamente para anulação.'],(string)Str::uuid());$actor=$select('finance_prepare');$alloc=$sr->allocate($rc,$actor,['amount'=>'400.00','version'=>1,'revision'=>2],(string)Str::uuid())['id'];
$receiptKey=(string)Str::uuid();$command('commercial_prepare','receipt','prepare',$receiptKey,0,['invoice_id'=>$invoice,'allocation_id'=>$alloc]);$command('commercial_review','receipt','review',$receiptKey,1,['decision'=>'approved']);[$receipt]=$command('commercial_prepare','receipt','issue',$receiptKey,2);
$creditKey=(string)Str::uuid();$command('finance_prepare','credit_note','prepare',$creditKey,0,['invoice_id'=>$invoice,'amount'=>'100.00','explanation'=>'Crédito QA100 para comprovar reversão ligada ao original.']);$command('finance_review','credit_note','review',$creditKey,1,['decision'=>'approved']);[$credit]=$command('finance_review','credit_note','issue',$creditKey,2);
$frOutput=$host->table('sd_outputs')->where($f['scope'])->where('kind','invoice_receipt')->firstOrFail();
$post=function($kind,$id)use($call,$native){$p=$call('accounting_prepare','prepare',fn($e)=>$e->command('effect-prepare',['effect_type'=>$kind,'source_id'=>$id,'journal_on'=>'2026-11-10','period_key'=>'QA-2026-INTEGRADO','rule_key'=>$kind,'reason'=>'Origem ou reversão documental individual de teste.'],(string)Str::uuid()));$call('accounting_review','validate',fn($e)=>$e->command('validate',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'decision'=>'approved','reason'=>'Revisão distinta da origem documental e da reversão.'],(string)Str::uuid()));return $call('accounting_prepare','post',fn($e)=>$e->command('post',['proposal_id'=>$p['proposal_id'],'revision'=>$p['revision'],'reason'=>'Aplicação única da origem ou reversão documental QA.'],(string)Str::uuid()));};
foreach([['sales.invoice',$invoice],['receivable.allocated',$alloc],['receivable.reduced',$credit['effect_id']],['sales.invoice',$frOutput->invoice_id],['receivable.allocated',$frOutput->effect_id]] as [$kind,$id])$post($kind,$id);
$pdfs=$host->table('sd_outputs')->orderBy('id')->get()->toJson();
foreach([['credit_note',$creditKey,'receivable.reduction_reversed'],['receipt',$receiptKey,'receivable.reversed'],['invoice_receipt',$frOutput->document_key,'receivable.reversed']] as [$kind,$key,$effectKind]){
[$prepared]=$command('finance_prepare',$kind,'cancel_prepare',$key,3);$ok($prepared['state']==='prepared','cancellation preparation preserves issued '.$kind);
$reject(fn()=>$command('finance_prepare',$kind,'cancel_review',$key,3),403,'same preparer cannot approve cancellation '.$kind);
[$cancelled,$cut]=$command('finance_review',$kind,'cancel_review',$key,3);$actor=$select('finance_review');$again=app(ServiceDocuments::class)->confirm($actor,$cut['preview']);$ok($again['replayed']&&$again['effect_id']===$cancelled['effect_id'],'cancellation result replays without duplicate '.$kind);$post($effectKind,$cancelled['effect_id']);
$reject(fn()=>$command('finance_prepare',$kind,'cancel_prepare',$key,3),409,'cancelled document cannot be cancelled twice '.$kind);
}
$actor=$select('finance_review');$position=$sr->position($ob,$actor);$ok($position['balance']==='1000.00'&&$position['reduced']==='0.00'&&$position['allocated']==='0.00'&&$position['available']==='400.00','reversals restore1000 debt and leave400 unallocated without refund');
$frOb=$host->table('sr_records')->where(['invoice_id'=>$frOutput->invoice_id,'kind'=>'obligation'])->value('id');$position=$sr->position($frOb,$actor);$ok($position['balance']==='1000.00'&&$position['available']==='1000.00','invoice-receipt cancellation preserves invoice and unallocated receipt');
$ok($pdfs===$host->table('sd_outputs')->orderBy('id')->get()->toJson(),'all original PDFs and snapshots remain byte-identical after cancellation');
$native->statement('SET CONSTRAINTS ALL IMMEDIATE');$host->statement('SET CONSTRAINTS ALL IMMEDIATE');$ok(true,'financial and journal constraints validate all linked reversals');
}finally{while($native->transactionLevel()>0)$native->rollBack();while($host->transactionLevel()>0)$host->rollBack();}
$ok($before===$host->table('sd_outputs')->orderBy('id')->get()->toJson(),'proof rolls back all cancellation fixtures and effects');file_put_contents($work.'/document-cancellation-result.json',json_encode(['passed'=>true,'checks'=>$checks,'rolled_back'=>true,'money_refunded'=>false,'visual_review'=>false],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/document-cancellation-result.json']).PHP_EOL;