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

13 lines
7.0 KiB
PHP

<?php
use App\Modules\Services\{MultiLineInvoiceCalculation,MultiLineDrafts,ServiceInvoicing};use App\Modules\Services\Http\MultiLineInvoiceController;use Illuminate\Support\Str;
require __DIR__.'/sgf-multiline-invoice-proof-bootstrap.php';$controller=app(MultiLineInvoiceController::class);$http=function($role,$method,$input=[],?string $key=null,$id=null)use($select,$controller){$select($role);$get=in_array($method,['catalogue','editor','receipt','issueReceipt']);request()->query->replace($get?$input:[]);request()->request->replace($get?[]:$input);request()->headers->set('Idempotency-Key',$key??(string)Str::uuid());$response=match($method){'catalogue'=>$controller->catalogue(request(),app(MultiLineInvoiceCalculation::class)),'editor'=>$controller->editor(request(),$id,app(MultiLineInvoiceCalculation::class)),'draftPreview'=>$controller->draftPreview(request(),app(MultiLineDrafts::class)),'draftConfirm'=>$controller->draftConfirm(request(),app(MultiLineDrafts::class)),'calculationPreview'=>$controller->calculationPreview(request(),app(MultiLineInvoiceCalculation::class)),'calculationConfirm'=>$controller->calculationConfirm(request(),app(MultiLineInvoiceCalculation::class)),'receipt'=>$controller->receipt(request(),$key,app(MultiLineInvoiceCalculation::class)),'issueReceipt'=>$controller->issueReceipt(request(),$key,app(MultiLineInvoiceCalculation::class)),'temporalReview'=>$controller->temporalReview(request(),$id),'readiness'=>$controller->readiness(request(),$id),'issue'=>$controller->issue(request(),$id),default=>$controller->$method(request())};return ['status'=>$response->getStatusCode(),'value'=>$response->getData(true)];};
$host->beginTransaction();try{
$x=$fixture('added',['400','600'],'CONTROL');$id=$x['draft']['id'];$v=$http('commercial_prepare','editor',[],null,$id)['value'];$ok($v['billing']['id']===$id&&count($v['billing']['data']['lines'])===2&&$v['preparation']['version']===1,'single editor retrieves header lines and original versions');$reject(fn()=>$http('commercial_prepare','editor',[],null,(int)$oldDraft->id),403,'new editor does not adopt old draft');$reject(fn()=>$http('read_only','draftPreview',[]),422,'empty body refused before mutation');$reject(fn()=>$http('commercial_prepare','catalogue',['kind'=>'drafts','tenant_id'=>2]),422,'tenant override refused');$reject(fn()=>$http('commercial_prepare','catalogue',['kind'=>'drafts','per_page'=>1000]),422,'unbounded catalogue page refused');$reject(fn()=>$http('commercial_prepare','catalogue',['kind'=>'rules']),403,'commercial reader cannot enumerate accounting rule catalogue');
$input=['draft_id'=>$id,'rule_event_id'=>$ruleEvent,'draft_version'=>1,'preparation_version'=>1,'reason'=>'Cálculo explícito do documento actual pelo preparador fiscal.'];$reject(fn()=>$http('commercial_prepare','calculationPreview',$input),403,'draft preparer cannot acquire fiscal calculation preparation authority');$cut=$http('finance_prepare','calculationPreview',$input)['value'];$calc=$http('finance_prepare','calculationConfirm',['preview'=>$cut['preview']])['value'];$ok($calc['result']['total']==='1160.00'&&$calc['draft_version']===1,'fiscal preparer existing powers calculate current draft');
$key=(string)Str::uuid();$body=['kind'=>'temporal','identifier'=>'MULTI-CONTROL-'.$id,'data'=>['calculation_policy'=>MultiLineInvoiceCalculation::POLICY,'common_calculation_id'=>$calc['id'],'common_rule_event_id'=>$ruleEvent,'draft_id'=>$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'=>'Prontidão artificial do documento/linhas escolhidos.','evidence'=>'Origem fiscal revista ligada individualmente ao documento.']];$first=$http('commercial_prepare','temporal',$body,$key)['value'];$second=$http('commercial_prepare','temporal',$body,$key)['value'];$ok($second['replayed']&&$second['id']===$first['id'],'temporal command receipt prevents duplicate proposal');$receipt=$http('commercial_prepare','receipt',[],$key)['value'];$ok($receipt['historical_receipt_only']&&$receipt['result']['id']===$first['id'],'own temporal receipt recovers without execution');$reject(fn()=>$http('commercial_review','receipt',[],$key),404,'another actor receipt hidden');$reject(fn()=>$http('commercial_prepare','temporalReview',['version'=>1,'decision'=>'approved','reason'=>'Tentativa de revisão própria proibida.'],null,$first['id']),403,'own temporal approval denied');$reviewKey=(string)Str::uuid();$review=['version'=>1,'decision'=>'approved','reason'=>'Revisão distinta do cálculo, linhas e janela de emissão.'];$one=$http('commercial_review','temporalReview',$review,$reviewKey,$first['id'])['value'];$two=$http('commercial_review','temporalReview',$review,$reviewKey,$first['id'])['value'];$ok($two['replayed']&&$two['id']===$one['id'],'temporal review receipt prevents duplicate decision');
$all=$http('commercial_prepare','catalogue',['kind'=>'temporals','draft_id'=>$id,'per_page'=>25])['value'];$ok(count($all['records']['data'])===1&&$all['records']['data'][0]['decision']['decision']==='approved','current decision batched into page');$far=$http('commercial_prepare','catalogue',['kind'=>'temporals','draft_id'=>$id,'page'=>100000,'per_page'=>100])['value'];$ok($far['records']['data']===[],'far page remains empty without fallback');
for($n=0;$n<27;$n++){$d=$host->table('mi_calculations')->where('id',$calc['id'])->first();$a=(array)$d;unset($a['id']);$a['fingerprint']=hash('sha256','mi-pagination-'.$n);$host->table('mi_calculations')->insert($a);}$a=$http('read_only','catalogue',['kind'=>'calculations','draft_id'=>$id,'page'=>1,'per_page'=>25])['value']['records'];$b=$http('read_only','catalogue',['kind'=>'calculations','draft_id'=>$id,'page'=>2,'per_page'=>25])['value']['records'];$c=$http('read_only','catalogue',['kind'=>'calculations','draft_id'=>$id,'page'=>1,'per_page'=>100])['value']['records'];$ok(count($a['data'])===25&&count($b['data'])===3&&$c['total']===28,'calculation choices SQL25/100 beyond first page');$ok(!array_intersect(array_column($a['data'],'id'),array_column($b['data'],'id')),'source page has no overlap');
$actor=$select('finance_prepare');$last=$host->table('mi_calculations')->where('id',$calc['id'])->first();$host->unprepared('ALTER TABLE mi_calculations DISABLE TRIGGER mi_calculations_immutable');$host->table('mi_calculations')->where('id',$calc['id'])->update(['unit_id'=>1]);$host->unprepared('ALTER TABLE mi_calculations ENABLE TRIGGER mi_calculations_immutable');$reject(fn()=>DB::transaction(fn()=>app(MultiLineInvoiceCalculation::class)->current($actor,$calc['id'],$id)),403,'known calculation id in another unit refused');
}finally{$host->rollBack();}
file_put_contents($work.'/multiline-invoice-controller.json',json_encode(['passed'=>true,'checks'=>$checks,'fixtures_rolled_back'=>true,'new_grants'=>0],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$work.'/multiline-invoice-controller.json']).PHP_EOL;