Accept isolated invoice issuance and reproducible PDF delivery

This commit is contained in:
2026-10-27 08:56:13 +02:00
parent 9f480045fa
commit 4276aca5c2
25 changed files with 1072 additions and 1 deletions
+22
View File
@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\DB;
use Symfony\Component\Process\Process;
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\App\Modules\Services\ServiceInvoicing::guard();
$f=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);
$fixture=$f+$f['invoice']+['manager'=>\App\Models\User::findOrFail($f['manager_id']),'reader'=>\App\Models\User::findOrFail($f['reader_id'])];
$fixture=DB::transaction(fn()=>\Tests\Support\InvoiceFixture::additionalDraft($fixture));
$review=app(\App\Modules\Services\ServiceInvoicing::class)->review($fixture['draft'],$fixture['series'],$fixture['rule'],$f['manager_id']);
if(!$review['ready']) throw new RuntimeException('Synthetic concurrent revision not ready.');
$input=['actor'=>$f['manager_id'],'tenant'=>$f['tenant'],'company'=>$f['company'],'unit'=>$f['unit'],'draft'=>$fixture['draft'],'key'=>(string)\Illuminate\Support\Str::uuid(),'input'=>['series_id'=>$fixture['series'],'rule_id'=>$fixture['rule'],'review_hash'=>$review['review_hash']]];
$before=DB::table('si_invoices')->count();
$a=new Process([PHP_BINARY,'scripts/invoice-worker.php','--env=testing'],base_path(),null,json_encode($input+['pause'=>true]));
$b=new Process([PHP_BINARY,'scripts/invoice-worker.php','--env=testing'],base_path(),null,json_encode($input));
$a->start();if(!$a->waitUntil(fn($type,$buffer)=>str_contains($buffer,'LOCK_HELD')))throw new RuntimeException('No overlapping lock established.');
$b->start();$a->wait();$b->wait();$results=[];
foreach([$a,$b] as $p){if(!$p->isSuccessful())throw new RuntimeException('Synthetic worker failed.');$lines=array_values(array_filter(explode("\n",trim($p->getOutput())),fn($s)=>str_starts_with($s,'{')));$results[]=json_decode(end($lines),true,32,JSON_THROW_ON_ERROR);}
if($results[0]['id']!==$results[1]['id'] || $results[0]['replayed'] || !$results[1]['replayed'] || DB::table('si_invoices')->count()!==$before+1)throw new RuntimeException('Duplicate or missing concurrent invoice.');
if(DB::table('si_invoices')->distinct()->count('number')!==DB::table('si_invoices')->count())throw new RuntimeException('Series number repeated.');
file_put_contents(base_path('docs/proofs/invoice-concurrency.json'),json_encode(['database'=>'sgf_tempo_test','overlapping_processes'=>2,'one_invoice_one_replay'=>true,'numbers_distinct_in_shared_series'=>true,'prior_documents_preserved'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo "Concurrent synthetic issuance verified: one invoice, one replay, shared series without duplicate number.\n";
+20
View File
@@ -0,0 +1,20 @@
<?php
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\App\Modules\Services\ServiceInvoicing::guard();
$f=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);
$fixture=$f+$f['invoice']+['manager'=>\App\Models\User::findOrFail($f['manager_id']),'reader'=>\App\Models\User::findOrFail($f['reader_id'])];
$fixture=\Illuminate\Support\Facades\DB::transaction(fn()=>\Tests\Support\InvoiceFixture::additionalDraft($fixture));
$issuer=app(\App\Modules\Services\ServiceInvoicing::class);
$review=$issuer->review($fixture['draft'],$fixture['series'],$fixture['rule'],$f['manager_id']);
if(!$review['ready'])throw new RuntimeException('Synthetic export revision not ready.');
$result=$issuer->issue($fixture['draft'],$f['manager_id'],['series_id'=>$fixture['series'],'rule_id'=>$fixture['rule'],'review_hash'=>$review['review_hash']],(string)\Illuminate\Support\Str::uuid());
$pdf=$issuer->pdf($result['id'],$f['manager_id']);
$dir=base_path('output/pdf'); if(!is_dir($dir))mkdir($dir,0777,true);
file_put_contents($dir.'/sgf-tempo-factura-teste.pdf',$pdf);
$view=$issuer->show($result['id'],$f['manager_id']);
$long=$view['snapshot']; unset($long['document_lines']); $long['document']['observations']=str_repeat('Conteúdo sintético extenso para verificar paginação e margens. ',80).str_repeat('W',800);
file_put_contents(storage_path('app/invoice-long-proof.pdf'),app(\App\Modules\Services\InvoicePdf::class)->render($long));
if($pdf!==$issuer->pdf($result['id'],$f['manager_id']))throw new RuntimeException('PDF bytes changed.');
file_put_contents(base_path('docs/proofs/invoice-pdf-results.json'),json_encode(['database'=>'sgf_tempo_test','renderer'=>$view['snapshot']['renderer'],'pdf_sha256'=>hash('sha256',$pdf),'repeated_download_identical'=>true,'browser_and_pdf_share_document_lines'=>true,'synthetic_only'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo json_encode(['invoice_id'=>$result['id'],'number'=>$result['number'],'pdf_sha256'=>hash('sha256',$pdf),'renderer'=>$view['snapshot']['renderer']],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR).PHP_EOL;
+11
View File
@@ -0,0 +1,11 @@
<?php
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\App\Modules\Services\ServiceInvoicing::guard();
$file='C:/laragon/data/sgf-tempo/secrets/browser-fixture.json';
$f=json_decode(file_get_contents($file),true,32,JSON_THROW_ON_ERROR);
if(\Illuminate\Support\Facades\DB::table('a_services')->exists() || \Illuminate\Support\Facades\DB::table('si_proposals')->exists()) throw new RuntimeException('Synthetic fixture must be empty; nothing deleted.');
$fixture=\Illuminate\Support\Facades\DB::transaction(fn()=>\Tests\Support\InvoiceFixture::create($f+['manager'=>\App\Models\User::findOrFail($f['manager_id']),'reader'=>\App\Models\User::findOrFail($f['reader_id'])]));
$ids=array_intersect_key($fixture,array_flip(['client','service','source','draft','profile','temporal','calculation','series','rule','today','start','end']));
file_put_contents($file,json_encode($f+['invoice'=>$ids],JSON_THROW_ON_ERROR));
echo "Synthetic invoice fixture ready only in sgf_tempo_test; no invoice or PDF pre-issued.\n";
+8
View File
@@ -0,0 +1,8 @@
<?php
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\App\Modules\Services\ServiceInvoicing::guard();
$in=json_decode(stream_get_contents(STDIN),true,32,JSON_THROW_ON_ERROR);
app(\App\Shared\Context\CompanyContext::class)->selectFor($in['actor'],$in['tenant'],$in['company'],$in['unit']);
if($in['pause'] ?? false) { $paused=false; \Illuminate\Support\Facades\DB::listen(function($q)use(&$paused){if(!$paused && str_contains($q->sql,'"companies"') && str_contains($q->sql,'for update')){$paused=true;echo "LOCK_HELD\n";flush();usleep(1500000);}}); }
echo json_encode(app(\App\Modules\Services\ServiceInvoicing::class)->issue($in['draft'],$in['actor'],$in['input'],$in['key']),JSON_THROW_ON_ERROR).PHP_EOL;