Files
SGF/scripts/receipt-workspace-body.php
2026-11-04 09:37:20 +02:00

19 lines
8.1 KiB
PHP

<?php
if(!($s['receiptWorkspace']??false))throw new LogicException('New receipt workspace fixture required');$seed=json_decode(file_get_contents($s['root'].'/receipt-workspace-seed.json'),true);$enter('validator',1);require __DIR__.'/receipt-workspace-source.php';$id=$order['id'];$line=$order['items'][0]['id'];$url='/api/purchasing-receipts/orders/'.$id;$catalogue=$url.'/lines/'.$line.'/catalogue';
$ok($db->table('goods_receipts')->count()===0&&$db->table('stock_movements')->count()===0,'new issued source starts before physical effects');$r=$call('validator',$catalogue);$ok($r['status']===200&&$r['data']['total']===127&&$r['data']['per_page']===25,'bounded catalogue includes127 unit-compatible articles');$all=[];for($p=1;$p<=6;$p++){$r=$call('validator',$catalogue.'?page='.$p);if($r['status']!==200)throw new RuntimeException('Catalogue page');$all=array_merge($all,$r['data']['data']);}$ids=array_column($all,'id');$ok(count($ids)===127&&count(array_unique($ids))===127&&in_array($seed['ids'][110],$ids,true),'all articles beyond100 reachable without overlapping pages');
$ok(in_array($seed['ids'][125],$ids,true)&&!in_array($seed['ids'][126],$ids,true)&&!in_array($n['foreign_stock'],$ids,true),'unit case/whitespace matches native rule; incompatible and foreign articles excluded');$r=$call('validator',$catalogue.'?per_page=100');$r2=$call('validator',$catalogue.'?per_page=100&page=2');$ok(count($r['data']['data'])===100&&count($r2['data']['data'])===27,'catalogue pages100/27 cover same eligible universe');
$r=$call('validator',$catalogue.'?search=qa-catalog-0110');$selected=$r['data']['data'][0];$ok($r['data']['total']===1&&$selected['id']===$seed['ids'][110]&&strlen($selected['selection_revision'])===64,'case-insensitive code search reaches beyond100 with selection fingerprint');$r=$call('validator',$catalogue.'?search='.rawurlencode('%_'));$ok($r['data']['total']===1&&$r['data']['data'][0]['id']===$seed['ids'][127],'name search treats wildcard characters literally');
$ok(array_keys($selected)===['id','code','name','unit','selection_revision'],'catalogue projection exposes no cost or valuation fields');$ok($call('actor',$catalogue)['status']===403&&$call('issuer',$catalogue)['status']===403,'article selection requires existing physical confirmation authority');$ok($call('validator',$url.'/lines/999999/catalogue')['status']===403,'another source line cannot reuse catalogue entry');$ok($call('validator',$catalogue.'?page=0')['status']===422&&$call('validator',$catalogue.'?per_page=500')['status']===422&&$call('validator',$catalogue.'?unit=KG')['status']===422,'caller cannot override unit or bypass page bounds');
$preview=function($quantity,$revisions=true,$stock=null,$key=null)use($call,$url,$line,$n,$selected){$r=$call('validator',$url);$data=['revision'=>$r['data']['revision'],'idempotency_key'=>$key??(string)Str::uuid(),'delivery_note'=>'QA-CATALOG-'.Str::uuid(),'received_on'=>date('Y-m-d'),'warehouse_id'=>$n['warehouse'],'items'=>[['purchase_order_item_id'=>$line,'received_quantity'=>$quantity,'accepted_quantity'=>$quantity,'quality_status'=>'compliant','stock_item_id'=>$stock??$selected['id'],'notes'=>null]]];if($revisions)$data['item_revisions']=[$selected['id']=>$selected['selection_revision']];return $call('validator',$url.'/preview',$data);};$confirm=fn($cut,$i=0)=>$call('validator',$url.'/confirm',['preview'=>$cut],$i);
$p=$preview('0.010');$ok($p['status']===200&&$db->table('goods_receipts')->count()===0&&$db->table('stock_movements')->count()===0,'fingerprinted preview creates no receipt or movement');$old=$db->table('stock_items')->where('id',$selected['id'])->value('name');$db->table('stock_items')->where('id',$selected['id'])->update(['name'=>'QA changed catalogue label']);try{$ok($confirm($p['data']['preview'])['status']===409&&$preview('0.010')['status']===409,'catalogue change invalidates confirmation and old selected fingerprint');}finally{$db->table('stock_items')->where('id',$selected['id'])->update(['name'=>$old]);}
$bad=$preview('0.001',false,$seed['ids'][126]);$ok($bad['status']===200&&$confirm($bad['data']['preview'])['status']===422&&$db->table('stock_movements')->count()===0,'original native workflow remains final unit validator for legacy requests');$bad=$preview('0.001',false,$n['foreign_stock']);$ok($bad['status']===422,'foreign explicit article cannot be fingerprinted');
$responses=GuzzleHttp\Promise\Utils::unwrap([$clients[0]->postAsync($url.'/confirm',$options('validator',0,['preview'=>$p['data']['preview']])),$clients[1]->postAsync($url.'/confirm',$options('validator',1,['preview'=>$p['data']['preview']]))]);$statuses=array_map(fn($r)=>$r->getStatusCode(),$responses);sort($statuses);$ok($statuses===[200,201]&&$db->table('goods_receipts')->count()===1&&$db->table('stock_movements')->count()===1,'same-key concurrency still creates one canonical receipt and physical movement');$ok($confirm($p['data']['preview'])['status']===200,'receipt recovery uses original historical result without another movement');
// Add only the history-volume fixture through the same pinned original writer, without rerunning earlier suites.
$user=App\Models\User::findOrFail($f['validator']);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_receipts.confirm',function($actor)use($id,$line,$n,$selected){$source=Tempo\Purchasing\Embedded\Models\PurchaseOrder::findOrFail($id);for($i=1;$i<=25;$i++)app(Tempo\Purchasing\Embedded\Services\ReceiptWorkflow::class)->confirm($actor,$source,['idempotency_key'=>(string)Str::uuid(),'delivery_note'=>'QA-HISTORY-'.$i,'received_on'=>date('Y-m-d'),'warehouse_id'=>$n['warehouse'],'items'=>[['purchase_order_item_id'=>$line,'received_quantity'=>'0.001','accepted_quantity'=>'0.001','quality_status'=>'compliant','stock_item_id'=>$selected['id'],'notes'=>null]]]);});
$r=$call('validator',$url.'/history?page=1');$r2=$call('validator',$url.'/history?page=2');$ok($r['status']===200&&$r2['status']===200&&count($r['data']['data'])===25&&count($r2['data']['data'])===1&&$r['data']['total']===26,'all26 original receipts visible across two history pages');$ok(!array_intersect(array_column($r['data']['data'],'id'),array_column($r2['data']['data'],'id')),'history pages contain no duplicated evidence');$h=$call('validator','/api/purchasing-receipts/history/'.$r2['data']['data'][0]['id']);$ok($h['status']===200&&$h['data']['receipt']['items'][0]['stock_item_id']===$selected['id'],'older receipt detail retains original chosen article and snapshot');
$r3=$call('validator',$url.'/history?page=3');$ok($r3['status']===200&&$r3['data']['data']===[],'empty later history page invents no receipt');$ok($call('actor',$url.'/history')['status']===200,'existing receipt-view profile retains history without stock selection authority');$ok($call('validator',$url.'/history?unknown=1')['status']===422,'history rejects unsupported filters');
$p=$preview('0.965',false);$r=$confirm($p['data']['preview']);$ok($r['status']===201&&$db->table('goods_receipts')->count()===27&&(int)$db->table('stock_movements')->sum('quantity_milli')===1000,'legacy request finishes exactly1UN once across27 partial receipts');$r=$call('validator',$url);$ok($r['data']['progress']['state']==='complete','original writer completes actual order with no over-receipt');
$enter('validator',1,['foreign'=>1]);$ok($call('validator',$catalogue,null,1)['status']===403&&$call('validator',$url.'/history',null,1)['status']===403,'unmapped unit cannot inspect catalogue or history');$ok($db->table('invoices')->count()===0&&$db->table('pa_obligations')->count()===0&&$db->table('external_payments')->count()===0&&DB::table('geo_locations')->count()===0,'new receipt workspace triggers no invoice debt payment or localisation');
file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'eligible_articles'=>127,'receipts'=>27,'quantity_milli'=>1000,'native_history_fixture_receipts'=>25,'source_workflow_setup_only'=>true,'concurrency_statuses'=>$statuses,'new_right_names'=>[],'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." receipt workspace checks passed.\n";