Files
SGF/scripts/supplier-documents-tail.php
2026-11-04 09:37:20 +02:00

18 lines
6.4 KiB
PHP

<?php
if(!($s['supplierDocuments']??false))throw new LogicException('New document fixture required');$id=$n['supplier_ids'][0];$url='/api/purchasing-suppliers/'.$id;$docs=$url.'/documents';$bytes=base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl6pAoAAAAASUVORK5CYII=').str_repeat("\0",2048);file_put_contents($s['root'].'/qa-document.png',$bytes);$enter('actor',1);
$send=function($role,$path,$fields=[],$content=null,$filename='qa-document.png',$i=0)use($clients,$options,$bytes){$parts=[];foreach($fields as$name=>$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;};
$checks=json_decode(file_get_contents($s['root'].'/supplier-documents-progress.json'),true);if(count($checks)!==17||!in_array('concurrent same-key recovery stores one native document and one permanent file',$checks,true))throw new LogicException('17 valid recorded checks required');$root=$s['root'].'/supplier-documents';$stage=$s['root'].'/supplier-document-previews';$document=$db->table('supplier_documents')->first();if($db->table('supplier_documents')->count()!==1||$countFiles($root)!==1)throw new LogicException('Original committed document required');$statuses=[200,201];
$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,'../<unsafe>.png');$r=$confirm($p['data']['preview'],null,'../<unsafe>.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";