$value)$parts[]=['name'=>$name,'contents'=>(string)$value];$parts[]=['name'=>'file','contents'=>$content??$bytes,'filename'=>$filename];$r=$clients[$i]->post($path,$options($role,$i)+['multipart'=>$parts]);$text=(string)$r->getBody();return ['status'=>$r->getStatusCode(),'data'=>json_decode($text,true),'body'=>$text];}; $preview=function($key=null,$content=null,$filename='qa-document.png',$i=0,$type='license')use($send,$call,$url,$docs){$r=$call('actor',$url,null,$i);return $send('actor',$docs.'/preview',['operation_key'=>$key??(string)Str::uuid(),'revision'=>$r['data']['revision'],'document_type'=>$type,'reason'=>'Anexo sintetico para conferencia documental distinta.'],$content,$filename,$i);};$confirm=fn($cut,$content=null,$filename='qa-document.png',$i=0)=>$send('actor',$docs.'/confirm',['preview'=>$cut],$content,$filename,$i); $countFiles=function($dir){if(!is_dir($dir))return 0;$count=0;foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir,FilesystemIterator::SKIP_DOTS))as$file)if($file->isFile())$count++;return $count;}; $root=$s['root'].'/supplier-documents';$stage=$s['root'].'/supplier-document-previews';$supplierBefore=(array)$db->table('suppliers')->where('id',$id)->first();$auditBefore=$db->table('audit_logs')->count();$ok($db->table('supplier_documents')->count()===0,'new supplier attachment book begins empty');$r=$call('issuer',$docs);$ok($r['status']===200&&$r['data']['data']===[]&&$r['data']['per_page']===15,'financial reviewer can inspect paginated private metadata'); $ok($call('validator',$docs)['status']===403&&$call('requester',$docs)['status']===403,'unrelated physical/request roles cannot read supplier documents'); $p=$preview();$ok($p['status']===200&&$p['data']['impact']['document']['sha256']===hash('sha256',$bytes)&&$p['data']['impact']['supplier_status']==='pending'&&!isset($p['data']['impact']['document']['id']),'original upload preview captures actual bytes and review invalidation without temporary document id'); $ok((array)$db->table('suppliers')->where('id',$id)->first()===$supplierBefore&&$db->table('supplier_documents')->count()===0&&$db->table('audit_logs')->count()===$auditBefore&&$countFiles($root)===0&&$countFiles($stage)===0,'preview rolls back supplier document and audit and removes only its private staged file'); $ok($preview(null,str_repeat('not an image',200))['status']===422&&$preview(null,$bytes,'qa.php')['status']===422,'native MIME and extension checks reject disguised or executable attachments');$ok($preview(null,null,'qa-document.png',0,'invalid')['status']===422,'native document type catalogue remains authoritative'); $r=$call('issuer',$url);$review=$call('issuer',$url.'/preview',['operation_key'=>(string)Str::uuid(),'revision'=>$r['data']['revision'],'action'=>'review','data'=>['status'=>'active','comment'=>'Conferencia sintetica anterior ao novo documento.','version'=>$r['data']['supplier']['version']]]);$oldReview=$review['data']['preview']; $key=(string)Str::uuid();$p0=$preview($key);$p1=$preview($key,null,'qa-document.png',1);$ok($p0['status']===200&&$p1['status']===200,'independent sessions prepare same-key upload with their own context-bound previews'); $ok($confirm($p0['data']['preview'],$bytes.'changed')['status']===409,'confirmation refuses substituted file bytes'); $grant=DB::table('permission_grants')->where($f['scope'])->where('user_id',$f['actor'])->where('permission','purchasing_suppliers.prepare')->first();DB::table('permission_grants')->where('id',$grant->id)->delete();try{$ok($confirm($p0['data']['preview'])['status']===403,'existing preparation right rechecked at confirmation');}finally{DB::table('permission_grants')->insert((array)$grant);} $db->unprepared("CREATE FUNCTION public.document_qa_fail() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.action='sgf.supplier.document' THEN RAISE EXCEPTION 'QA_DOCUMENT_FAILURE'; END IF; RETURN NEW; END $$; CREATE TRIGGER document_qa_fail BEFORE INSERT ON audit_logs FOR EACH ROW EXECUTE FUNCTION document_qa_fail()");try{$r=$confirm($p0['data']['preview']);$ok($r['status']===500&&$db->table('supplier_documents')->count()===0&&$countFiles($root)===0&&(array)$db->table('suppliers')->where('id',$id)->first()===$supplierBefore,'provenance failure rolls back native records and compensates only verified uncommitted file');}finally{$db->unprepared('DROP TRIGGER document_qa_fail ON audit_logs; DROP FUNCTION public.document_qa_fail()');} $parts=function($cut)use($bytes){return [['name'=>'preview','contents'=>$cut],['name'=>'file','contents'=>$bytes,'filename'=>'qa-document.png']];};$responses=GuzzleHttp\Promise\Utils::unwrap([$clients[0]->postAsync($docs.'/confirm',$options('actor')+['multipart'=>$parts($p0['data']['preview'])]),$clients[1]->postAsync($docs.'/confirm',$options('actor',1)+['multipart'=>$parts($p1['data']['preview'])])]);$statuses=array_map(fn($r)=>$r->getStatusCode(),$responses);sort($statuses);$ok($statuses===[200,201]&&$db->table('supplier_documents')->count()===1&&$countFiles($root)===1,'concurrent same-key recovery stores one native document and one permanent file');$document=$db->table('supplier_documents')->first();$ok($db->table('suppliers')->where('id',$id)->value('status')==='pending'&&(int)$db->table('suppliers')->where('id',$id)->value('version')===(int)$supplierBefore['version']+1,'new attachment invalidates previous review and advances supplier version once'); $ok($call('issuer',$url.'/confirm',['preview'=>$oldReview])['status']===409,'review prepared before new evidence requires fresh conference');$r=$call('issuer',$url);$review=$call('issuer',$url.'/preview',['operation_key'=>(string)Str::uuid(),'revision'=>$r['data']['revision'],'action'=>'review','data'=>['status'=>'active','comment'=>'Documento sintetico conferido por identidade distinta.','version'=>$r['data']['supplier']['version']]]);$r=$call('issuer',$url.'/confirm',['preview'=>$review['data']['preview']]);$ok($r['status']===201&&$db->table('suppliers')->where('id',$id)->value('status')==='active','distinct native review reactivates exact supplier version');$version=$db->table('suppliers')->where('id',$id)->value('version');$ok($confirm($p0['data']['preview'])['status']===200&&$db->table('suppliers')->where('id',$id)->value('version')===$version&&$db->table('supplier_documents')->count()===1,'historical upload replay preserves subsequent approved review without another file'); $r=$call('issuer',$docs);$ok($r['status']===200&&!array_key_exists('path',$r['data']['data'][0])&&!array_key_exists('tenant_id',$r['data']['data'][0]),'metadata projection hides storage path and native tenant');$beforeDownload=$db->table('audit_logs')->where('action','supplier.document_downloaded')->count();$response=$clients[0]->get($docs.'/'.$document->id.'/download',$options('issuer'));$ok($response->getStatusCode()===200&&(string)$response->getBody()===$bytes&&str_contains($response->getHeaderLine('Content-Disposition'),'attachment')&&$response->getHeaderLine('X-Content-Type-Options')==='nosniff'&&str_contains($response->getHeaderLine('Content-Security-Policy'),'sandbox'),'private download returns exact original bytes as sandboxed attachment');$ok($db->table('audit_logs')->where('action','supplier.document_downloaded')->count()===$beforeDownload+1,'original download audit recorded for authorised reviewer'); $path=$root.'/'.$document->path;file_put_contents($path,'QA altered storage');try{$ok($call('issuer',$docs.'/'.$document->id.'/download')['status']===409,'altered stored bytes are not silently served as original evidence');}finally{file_put_contents($path,$bytes);} $ok($call('actor','/api/purchasing-suppliers/'.$n['supplier_ids'][1].'/documents/'.$document->id.'/download')['status']===404&&$call('actor','/api/purchasing-suppliers/'.$n['foreign_supplier'].'/documents/'.$document->id.'/download')['status']===404,'document id cannot cross supplier or tenant boundary'); $p=$preview(null,null,'../.png');$r=$confirm($p['data']['preview'],null,'../.png');$ok($r['status']===201&&!preg_match('/[<>\\\\\/]/',$r['data']['document']['original_name'])&&$db->table('supplier_documents')->count()===2,'new attachment preserves prior original and sanitises client filename'); // Only history-volume setup: use the same pinned native controller on artificial files. $user=App\Models\User::findOrFail($f['actor']);auth()->setUser($user);request()->setUserResolver(fn()=>$user);app(App\Shared\Context\CompanyContext::class)->selectFor($user->id,$f['tenant'],$f['company'],$f['unit']); (new App\Modules\Purchasing\ReceiptRuntime)->run('purchasing_suppliers.prepare',function($a)use($id,$s){$supplier=Tempo\Purchasing\Embedded\Models\Supplier::findOrFail($id);$files=app(App\Modules\Purchasing\SupplierDocumentFiles::class);$files->using($files->root(),function()use($a,$supplier,$s){for($i=1;$i<=14;$i++){$file=new Illuminate\Http\UploadedFile($s['root'].'/qa-document.png','QA-volume-'.$i.'.png','image/png',UPLOAD_ERR_OK,true);$r=Illuminate\Http\Request::create('/qa-volume','POST',['document_type'=>'other'],[],['file'=>$file]);$r->setUserResolver(fn()=>$a);(new Tempo\Purchasing\Embedded\Http\Controllers\SupplierDocumentController)->store($r,$supplier,app(Tempo\Purchasing\Embedded\Services\RequisitionWorkflow::class));}});}); $r=$call('issuer',$docs.'?page=1');$r2=$call('issuer',$docs.'?page=2');$ok($r['status']===200&&count($r['data']['data'])===15&&count($r2['data']['data'])===1&&array_intersect(array_column($r['data']['data'],'id'),array_column($r2['data']['data'],'id'))===[],'all16 immutable document records accessible in distinct15/1 pages');$ok($countFiles($root)===16&&$countFiles($stage)===0,'permanent files match recorded volume and preview staging contains no files'); $h=$call('issuer',$url.'/history?page=1');$ok($h['status']===200&&array_filter($h['data']['data'],fn($v)=>in_array($v['action'],['supplier.document_uploaded','sgf.supplier.document'],true)),'supplier history exposes original document evidence in existing administrative timeline'); $enter('actor',1,['module'=>'commercial']);$ok($call('actor',$docs,null,1)['status']===409,'another available module cannot read supplier documents');$enter('actor',1,['foreign'=>1]);$ok($call('actor',$docs,null,1)['status']===403,'unmapped unit cannot reuse document metadata');$ok($db->table('purchase_orders')->count()===0&&$db->table('invoices')->count()===0&&$db->table('pa_obligations')->count()===0&&$db->table('external_payments')->count()===0&&DB::table('geo_locations')->count()===0,'document workflow creates no order invoice debt payment or localisation'); file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'transport_documents'=>2,'native_volume_documents'=>14,'documents'=>16,'permanent_files'=>16,'staged_files'=>0,'concurrency_statuses'=>$statuses,'new_right_names'=>[],'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." supplier document checks passed.\n";