Files
SGF/scripts/composition-stock-setup.php
2026-11-04 09:37:20 +02:00

28 lines
3.6 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
$s=json_decode(file_get_contents(getenv('RECEIPT_COMPOSITION_STATE')),true,64,JSON_THROW_ON_ERROR);
if(!($s['stockComposition']??false)||is_file($s['root'].'/stock-binding.json'))throw new LogicException('Fresh stock composition required');
require $s['app'].'/vendor/autoload.php';$app=require $s['app'].'/bootstrap/app.php';$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
App\Modules\Purchasing\BudgetAdapter::guard();
$tag=$s['tag'];$run=$s['env']['SGF_ISOLATED_REGRESSION_RUN'];
if(!preg_match('/^[a-f0-9]{12}$/D',$tag)||!preg_match('/^[a-f0-9]{12}$/D',$run)||str_replace('\\','/',$s['root'])!=='C:/laragon/data/sgf-tempo/run/modular-regression-'.$run.'/private/receipt-composition-'.$tag)throw new LogicException('Owned fixture required');
$f=json_decode(file_get_contents($s['root'].'/fixture.json'),true);$n=json_decode(file_get_contents($s['root'].'/native-fixture.json'),true);
$admin=new PDO('pgsql:host=127.0.0.1;port=55439;dbname=compras_receipts_test_'.$tag,'sgf_regression_owner',trim(file_get_contents(dirname($s['root']).'/admin-password.txt')),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
if($admin->query("SELECT current_setting('cluster_name')")->fetchColumn()!=='sgf_modular_regression_'.$run)throw new LogicException('Wrong physical cluster');
$reader='stock_receipt_read_'.$tag;$password=bin2hex(random_bytes(32));
$admin->exec("CREATE ROLE $reader LOGIN PASSWORD '$password' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS; ALTER ROLE $reader SET default_transaction_read_only=on; GRANT CONNECT ON DATABASE compras_receipts_test_$tag TO $reader; GRANT USAGE ON SCHEMA public TO $reader; GRANT SELECT ON stock_items,warehouses,stock_movements TO $reader;");
// New isolated book only: make the reader revision's append-only assumption enforceable.
$admin->exec("CREATE FUNCTION composition_stock_append_only() RETURNS trigger LANGUAGE plpgsql AS \$\$ BEGIN RAISE EXCEPTION 'Preserve stock movement evidence'; END; \$\$;
CREATE TRIGGER composition_stock_append_only BEFORE UPDATE OR DELETE ON stock_movements FOR EACH ROW EXECUTE FUNCTION composition_stock_append_only();");
DB::transaction(function()use($f){
$def=DB::selectOne("SELECT pg_get_constraintdef(oid) definition FROM pg_constraint WHERE conname='permission_grants_scope_check' AND conrelid='permission_grants'::regclass")->definition;
DB::statement('ALTER TABLE permission_grants DROP CONSTRAINT permission_grants_scope_check');
DB::statement('ALTER TABLE permission_grants ADD CONSTRAINT permission_grants_scope_check CHECK ('.substr($def,6)." OR (unit_id IS NOT NULL AND permission='stock.view'))");
DB::table('permission_grants')->insert($f['scope']+['user_id'=>$f['validator'],'permission'=>'stock.view','created_at'=>now(),'updated_at'=>now()]);
});
$binding=['schema'=>'public','source_tenant_id'=>$n['tenant'],'warehouse_ids'=>[$n['warehouse']],'append_only'=>true];
$connection=$s['nativeConnection'];$connection['username']=$reader;$connection['password']=$password;
file_put_contents($s['root'].'/stock-binding.json',json_encode(['connection'=>$connection,'bindings'=>[implode(':',array_values($f['scope'])).':'.$f['validator']=>$binding]],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
file_put_contents($s['root'].'/composition-stock-baseline.json',json_encode(['passed'=>true,'existing_qa_right'=>'stock.view','granted_only_to_fixture_validator'=>true,'source_select_tables'=>['stock_items','warehouses','stock_movements'],'native_writer_changed'=>false,'append_only_trigger_new_qa_book_only'=>true,'new_permission_names'=>[],'real_grants'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo "Stock read composition prepared.\n";