Files
SGF/scripts/sgf-multiline-invoice-recovery.php
2026-11-11 15:04:01 +02:00

8 lines
4.9 KiB
PHP

<?php
use App\Modules\Services\{MultiLineDrafts,MultiLineInvoiceCalculation,ServiceInvoicing};use Illuminate\Support\Str;
require __DIR__.'/sgf-multiline-invoice-proof-bootstrap.php';$host->beginTransaction();try{
$actor=$select('commercial_prepare');$w=app(MultiLineDrafts::class);$body=['draft_id'=>null,'version'=>0,'source_version'=>0,'preparation_version'=>0,'name'=>'QA-MULTI-FAILURE','client_id'=>$client,'currency'=>'MZN','price_mode'=>'added','service_date'=>'2026-11-11','period_start'=>'2026-11-01','period_end'=>'2026-11-30','proposed_due_date'=>'2026-11-30','payment_terms'=>'Condições artificiais.','lines'=>[['service_id'=>$service,'description'=>'Linha de recuperação','quantity'=>'1','unit_price'=>'100','unit'=>'UN']],'reason'=>'Recuperação sintética após falha parcial controlada.'];$cut=$w->preview($actor,$body,(string)Str::uuid());$counts=fn()=>array_map(fn($t)=>$host->table($t)->count(),['a_service_drafts','a_billing_drafts','a_history','a_events','a_commands','mi_drafts','mi_commands','identity_audit']);$before=$counts();
$host->statement("CREATE FUNCTION mi_fail_test() RETURNS trigger LANGUAGE plpgsql AS \$\$ BEGIN RAISE EXCEPTION 'controlled partial failure'; END \$\$");$host->statement('CREATE TRIGGER mi_fail_test BEFORE INSERT ON mi_commands FOR EACH ROW EXECUTE FUNCTION mi_fail_test()');$failed=false;try{$w->confirm($actor,$cut['preview']);}catch(Illuminate\Database\QueryException $e){$failed=str_contains($e->getMessage(),'controlled partial failure');}$ok($failed,'failure injected after original draft writers before receipt');$ok($counts()===$before,'draft source preparation events registry and receipts roll back together');$host->statement('DROP TRIGGER mi_fail_test ON mi_commands');$result=$w->confirm($actor,$cut['preview']);$again=$w->confirm($actor,$cut['preview']);$ok($again['replayed']&&$again['id']===$result['id'],'same draft cut recovers exactly once after failure');
$c=app(MultiLineInvoiceCalculation::class);$actor=$select('finance_prepare');$p=$c->preview($actor,['draft_id'=>$result['id'],'draft_version'=>1,'preparation_version'=>1,'rule_event_id'=>$ruleEvent,'reason'=>'Cálculo do rascunho recuperado explicitamente.'],(string)Str::uuid());$calc=$c->confirm($actor,$p['preview']);$actor=$select('commercial_prepare');$e=app(ServiceInvoicing::class);$code='TEST-MULTI-FAIL-'.strtoupper(substr((string)Str::uuid(),0,8));$series=$e->propose($actor,['kind'=>'series','identifier'=>$code,'data'=>['code'=>$code,'first_number'=>1,'state'=>'open','valid_from'=>'2026-11-01','valid_until'=>'2026-11-30','evidence'=>'Série artificicial de recuperação.']]);$temporal=$e->propose($actor,['kind'=>'temporal','identifier'=>'MULTI-FAIL-'.$result['id'],'data'=>['calculation_policy'=>MultiLineInvoiceCalculation::POLICY,'common_calculation_id'=>$calc['id'],'common_rule_event_id'=>$ruleEvent,'draft_id'=>$result['id'],'draft_version'=>1,'preparation_version'=>1,'valid_from'=>'2026-11-01','valid_until'=>'2026-11-30','period_start'=>'2026-11-01','period_end'=>'2026-11-30','issue_window_start'=>'2026-11-11','issue_window_end'=>'2026-11-11','event'=>'on_issue_within_deadline','readiness_basis'=>'Documento recuperado com cálculo actual.','evidence'=>'Origem e valores fictícios conferidos.']]);$actor=$select('commercial_review');foreach([$series,$temporal]as$p)$e->decide($p['id'],$actor,['version'=>1,'decision'=>'approved','reason'=>'Revisão independente do documento de recuperação.']);$actor=$select('commercial_prepare');$ready=$e->review($result['id'],$series['id'],$temporal['id'],$actor);$input=['series_id'=>$series['id'],'rule_id'=>$temporal['id'],'review_hash'=>$ready['review_hash']];$key=(string)Str::uuid();$invoiceCounts=fn()=>array_map(fn($t)=>$host->table($t)->count(),['si_invoices','si_events','si_outbox','si_counters','si_commands']);$before=$invoiceCounts();$host->statement('CREATE TRIGGER mi_invoice_fail_test BEFORE INSERT ON si_commands FOR EACH ROW EXECUTE FUNCTION mi_fail_test()');$failed=false;try{$e->issue($result['id'],$actor,$input,$key);}catch(Illuminate\Database\QueryException $ex){$failed=str_contains($ex->getMessage(),'controlled partial failure');}$ok($failed&&$invoiceCounts()===$before,'PDF number invoice outbox and receipt roll back on late failure');$host->statement('DROP TRIGGER mi_invoice_fail_test ON si_commands');$i=$e->issue($result['id'],$actor,$input,$key);$again=$e->issue($result['id'],$actor,$input,$key);$ok($i['number']===1&&$again['replayed']&&$again['id']===$i['id'],'same invoice request recovers number1 once');$ok($host->table('si_invoices')->where('draft_id',$result['id'])->count()===1,'one invoice after recovery');
}finally{$host->rollBack();}file_put_contents($work.'/multiline-invoice-recovery.json',json_encode(['passed'=>true,'checks'=>$checks,'fixtures_rolled_back'=>true,'regression_only'=>true],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/multiline-invoice-recovery.json']).PHP_EOL;