table('purchase_orders')->where('tenant_id',$n['tenant'])->count()===1&&is_file($s['root'].'/composition-stock-progress.json')){ // Recover the completed source setup after an assertion failure; do not issue another order. $id=$db->table('purchase_orders')->where('tenant_id',$n['tenant'])->value('id'); $order=['id'=>$id,'items'=>[['id'=>$db->table('purchase_order_items')->where('purchase_order_id',$id)->value('id')]]]; file_put_contents($origin,json_encode($order,JSON_THROW_ON_ERROR)); }else{require __DIR__.'/receipt-workspace-source.php';file_put_contents($origin,json_encode($order,JSON_THROW_ON_ERROR));} $enter('validator',0,['module'=>'stock']);$stock='/api/stock-read'; $read=fn($query=[])=>$call('validator',$stock.'?'.http_build_query($query)); $r=$read();$ok($r['status']===200&&$r['data']['rows']===[]&&$r['data']['status']==='available','composed stock starts with a genuine empty physical book');$before=$r['data']['snapshot']; $ok(in_array($call('actor',$stock)['status'],[403,409],true),'purchasing authority does not grant stock access'); $enter('validator',1);$ok(in_array($call('validator',$stock,null,1)['status'],[403,409],true),'stock endpoint refuses Purchasing module context'); $enter('validator',1,['module'=>'stock','foreign'=>1]);$ok($call('validator',$stock,null,1)['status']===403,'another unit cannot inherit the source binding'); $ok($read(['warehouse_id'=>$n['foreign_warehouse']])['status']===403,'foreign warehouse explicitly refused'); $ok($read(['per_page'=>101])['status']===422&&$read(['per_page'=>1])['status']===422&&$read(['unknown'=>1])['status']===422,'page bounds and unknown filters rejected'); $enter('validator',1,['module'=>'stock']); $r=$call('validator',$stock,null,1,['X-SGF-Context'=>'stale']);$ok($r['status']===409,'stale HTTP context refused before source observation'); // Catalogue volume is fixture metadata only, not artificial physical movements. $base=(array)$db->table('stock_items')->where('id',$n['stock'])->first();unset($base['id']); $db->transaction(function()use($db,$base){for($i=1;$i<=102;$i++){$row=$base;$row['code']='QA-COMPOSED-'.str_pad((string)$i,3,'0',STR_PAD_LEFT);$row['name']=$i===102?'Literal%_marker':'Composed QA article '.$i;$db->table('stock_items')->insert($row);}}); $r=$read(['view'=>'items','per_page'=>100]);$r2=$read(['view'=>'items','per_page'=>100,'page'=>2,'snapshot'=>$r['data']['snapshot']]); $total=$db->table('stock_items')->where('tenant_id',$n['tenant'])->count(); $ok($r['status']===200&&$r2['status']===200&&count($r['data']['rows'])===100&&count($r2['data']['rows'])===$total-100,'catalogue uses bounded pages100 and remainder'); $ids=[];for($page=1;$page<=ceil($total/25);$page++){$r=$read(['view'=>'items','page'=>$page]);$ids=array_merge($ids,array_column($r['data']['rows'],'id'));} $ok(count($ids)===$total&&count(array_unique($ids))===$total&&!in_array($n['foreign_stock'],$ids,true),'pages25 cover the same tenant catalogue once without foreign articles'); $r=$read(['view'=>'items','search'=>'%_']);$ok($r['status']===200&&count($r['data']['rows'])===1&&$r['data']['rows'][0]['name']==='Literal%_marker','search preserves literal wildcard semantics'); $r=$read(['view'=>'warehouses']);$ok($r['status']===200&&array_column($r['data']['rows'],'id')===[$n['warehouse']],'only explicitly mapped warehouse is visible'); $r=$read();$before=$r['data']['snapshot']; $id=$order['id'];$line=$order['items'][0]['id'];$url='/api/purchasing-receipts/orders/'.$id; $enter('validator',1);$r=$call('validator',$url,null,1); $payload=['revision'=>$r['data']['revision'],'idempotency_key'=>(string)Str::uuid(),'delivery_note'=>'QA-COMPOSED-STOCK','received_on'=>date('Y-m-d'),'warehouse_id'=>$n['warehouse'], 'items'=>[['purchase_order_item_id'=>$line,'received_quantity'=>'1.000','accepted_quantity'=>'1.000','quality_status'=>'compliant','stock_item_id'=>$n['stock'],'notes'=>null]]]; $p=$call('validator',$url.'/preview',$payload,1);$ok($p['status']===200&&$db->table('stock_movements')->count()===0,'original receipt preview has no physical effect'); $r=$call('validator',$url.'/confirm',['preview'=>$p['data']['preview']],1);$ok($r['status']===201&&count($r['data']['movements'])===1,'original receipt confirmation creates exactly one native movement');$movement=$r['data']['movements'][0]['id']; $r=$call('validator',$url.'/confirm',['preview'=>$p['data']['preview']],1);$ok($r['status']===200&&$r['data']['replayed']&&$db->table('stock_movements')->count()===1,'receipt replay remains idempotent across the integration'); $ok($read(['snapshot'=>$before])['status']===409,'new native movement invalidates an earlier stock snapshot'); $r=$read(['view'=>'movements']);$ok($r['status']===200&&count($r['data']['rows'])===1&&$r['data']['rows'][0]['id']===$movement&&$r['data']['rows'][0]['quantity_milli']==='1000','Stock reads the exact canonical receipt movement and rational quantity'); $ok(!array_intersect(['snapshot','cost','unit_price','supplier_id'],array_keys($r['data']['rows'][0])),'stock projection exposes no raw document or financial snapshot'); $r=$read();$ok($r['data']['rows'][0]['physical_milli']==='1000'&&$r['data']['rows'][0]['available_milli']===null&&!$r['data']['capabilities']['write'],'Stock derives physical balance without inventing availability or another writer');$token=$r['data']['snapshot']; $enter('validator',0,['module'=>'stock']);$ok($read(['snapshot'=>$token])['status']===409,'renewed session context invalidates prior pagination token'); $grant=DB::table('permission_grants')->where($f['scope']+['user_id'=>$f['validator'],'permission'=>'stock.view'])->first(); DB::table('permission_grants')->where('id',$grant->id)->delete();try{$ok($read()['status']===403,'revoked stock grant is rechecked on every page');}finally{DB::table('permission_grants')->insert((array)$grant);} $profile=$s['root'].'/stock-binding.json';$original=file_get_contents($profile);$binding=json_decode($original,true);$key=array_key_first($binding['bindings']); $binding['bindings'][$key]['warehouse_ids']=[$n['foreign_warehouse']];file_put_contents($profile,json_encode($binding)); try{$r=$read();$ok($r['status']===503,'incompatible tenant warehouse mapping returns unavailable rather than a zero balance');}finally{file_put_contents($profile,$original);} $ok($read()['status']===200,'valid private mapping recovers without changing the book'); $binding=json_decode($original,true);$binding['connection']['password']='invalid-qa-only';file_put_contents($profile,json_encode($binding)); try{$r=$read();$ok($r['status']===503&&!str_contains($r['body'],'stock_receipt_read_')&&!str_contains($r['body'],'SQLSTATE'),'source authentication failure is generic and does not expose credentials');}finally{file_put_contents($profile,$original);} $ok($read()['status']===200,'source authentication recovers with original profile'); config(['database.connections.composition_security'=>json_decode($original,true)['connection']]);$reader=DB::connection('composition_security'); foreach(['DELETE FROM stock_movements WHERE false','SELECT * FROM goods_receipts LIMIT 0','SELECT * FROM audit_logs LIMIT 0','SELECT * FROM pa_obligations LIMIT 0']as$sql){$refused=false;try{$reader->statement($sql);}catch(\PDOException $e){$refused=in_array($e->errorInfo[0]??'',['42501','25006'],true);}$ok($refused,'technical reader refuses '.strtok($sql,' ').' outside its capability: '.explode(' ',$sql)[2]);} $refused=false;try{$db->table('stock_movements')->where('id',$movement)->update(['quantity_milli'=>1001]);}catch(\PDOException $e){$refused=($e->errorInfo[0]??'')==='P0001';} $ok($refused&&(int)$db->table('stock_movements')->sum('quantity_milli')===1000,'new QA append-only trigger preserves revision assumptions'); // An outstanding native writer mutex must not block the observational connection. $db->beginTransaction();try{$db->table('tenants')->where('id',$n['tenant'])->lockForUpdate()->first();$ok($read()['status']===200,'Stock read does not acquire the native tenant writer mutex');}finally{$db->rollBack();} $ok($db->table('goods_receipts')->count()===1&&$db->table('stock_movements')->count()===1&&$db->table('invoices')->where('tenant_id',$n['tenant'])->count()===0&&$db->table('pa_obligations')->where('tenant_id',$n['tenant'])->count()===0&&$db->table('external_payments')->where('tenant_id',$n['tenant'])->count()===0,'integration leaves one receipt/movement and creates no invoice debt or payment'); file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'same_native_book'=>true,'receipts'=>1,'movements'=>1,'physical_milli'=>1000,'catalogue_rows'=>$total,'page_sizes'=>[25,100],'new_permission_names'=>[],'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." composed stock checks passed.\n";