commit melhorias sgf

This commit is contained in:
2026-11-11 09:00:17 +02:00
parent 0b05a1192c
commit 1d11a5e57b
736 changed files with 40746 additions and 210 deletions
+33 -2
View File
@@ -21,6 +21,16 @@ final class ReceiptRuntime
public function run(string $right,\Closure $work):mixed
{
abort_unless(config('receipt_qa.enabled')===true,404);
if(str_starts_with($right,'general_ledger.'))abort_unless(config('sgf_single_installation.enabled')===true&&config('sgf_single_installation.ledger_enabled')===true,404);
if(str_starts_with($right,'supplier_payables.'))abort_unless(config('sgf_single_installation.enabled')===true&&config('sgf_single_installation.finance_enabled')===true,404);
if(str_starts_with($right,'stock.')){
abort_unless(config('stock_operations.enabled')===true,404);
$allowed=['stock.view','stock.operations.prepare','stock.operations.confirm'];
if(config('stock_operations.final_enabled')===true)$allowed=[...$allowed,'stock.transfers.receive','stock.lots.review'];
if(config('stock_catalogue.enabled')===true)$allowed=[...$allowed,'stock.items.prepare','stock.items.review'];
if(config('stock_completion.enabled')===true)$allowed=[...$allowed,'stock.planning.view','stock.planning.prepare','stock.planning.review','stock.disposition.view','stock.disposition.prepare','stock.disposition.review'];
abort_unless(in_array($right,$allowed,true),403);
}
if(str_starts_with($right,'purchasing_obligations.'))abort_unless(config('receipt_qa.obligations_enabled')===true,404);
if(str_starts_with($right,'purchasing_invoices.'))abort_unless(config('receipt_qa.invoices_enabled')===true,404);
if(str_starts_with($right,'purchasing_requests.'))abort_unless(config('receipt_qa.requests_enabled')===true,404);
@@ -41,7 +51,23 @@ final class ReceiptRuntime
$p=json_decode(file_get_contents($profile),true,64,JSON_THROW_ON_ERROR);
$ctx=app(CompanyContext::class);$scope=$ctx->identifiers()+['unit_id'=>$ctx->unitId()];$user=$ctx->userId();
abort_unless($scope['unit_id']&&request()->user()->id===$user,403);
$key=implode(':',array_values($scope)).':'.$user;$binding=$p['bindings'][$key]??null;abort_unless($binding,403,'Origem de recepção não autorizada neste contexto.');
$key=implode(':',array_values($scope)).':'.$user;$binding=$p['bindings'][$key]??null;
if(config('sgf_single_installation.enabled')===true&&config('sgf_single_installation.bindings_sha256')!==null){
$file=$expected.'/single-bindings.json';$pin=config('sgf_single_installation.bindings_sha256');
abort_unless(is_string($pin)&&preg_match('/^[a-f0-9]{64}$/D',$pin)&&is_file($file)&&!is_link($file)&&hash_equals($pin,hash_file('sha256',$file)),503);
$additional=json_decode(file_get_contents($file),true,32,JSON_THROW_ON_ERROR);
abort_unless(($additional['composition']??null)===$m[1],403);
if(isset($additional['bindings'][$key]))$binding=$additional['bindings'][$key];
}
abort_unless($binding,403,'Origem de recepção não autorizada neste contexto.');
// A Stock-specific map is explicit and pinned; it cannot change purchasing bindings.
if(str_starts_with($right,'stock.')&&config('stock_operations.profile_sha256')!==null){
$mapFile=$expected.'/stock-operations-binding.json';$pin=config('stock_operations.profile_sha256');
abort_unless(is_string($pin)&&preg_match('/^[a-f0-9]{64}$/D',$pin)&&is_file($mapFile)&&!is_link($mapFile)&&hash_equals($pin,hash_file('sha256',$mapFile)),503);
$map=json_decode(file_get_contents($mapFile),true,32,JSON_THROW_ON_ERROR);$warehouses=$map['warehouse_bindings'][$key]??null;
abort_unless(($map['composition']??null)===$m[1]&&is_array($warehouses)&&count($warehouses)>0&&count($warehouses)<=1000&&!array_filter($warehouses,fn($id)=>!is_int($id)||$id<1),403);
$binding['warehouse_ids']=$warehouses;$binding['stock_profile_sha256']=$pin;
}
if(config('receipt_qa.obligations_enabled')===true)abort_unless(($binding['payable_instance']??null)==='qa-stock-ledger-'.$m[1]&&config('common-payables.source_instance')===$binding['payable_instance']&&config('common-payables.history_enabled')===false&&config('common-payables.history_decisions_enabled')===false,403);
$p['context_key']=$key;
$bundle=realpath($expected.'/bundle');$manifest=$bundle.'/manifest.json';
@@ -69,11 +95,15 @@ final class ReceiptRuntime
$i=$native->selectOne("SELECT current_user role,current_database() db,current_setting('cluster_name') cluster,rolsuper OR rolcreatedb OR rolcreaterole OR rolreplication OR rolbypassrls elevated FROM pg_roles WHERE rolname=current_user");
abort_unless(!$i->elevated&&$i->role===$connection['username']&&$i->db===$connection['database']&&$i->cluster==='sgf_modular_regression_'.$run,403);
return $host->transaction(function()use($host,$scope,$user,$right,$binding,$work,$p,$native){
if(str_starts_with($right,'stock.')){$host->statement("SET LOCAL lock_timeout='5s'");$host->statement("SET LOCAL statement_timeout='15s'");}
// Hold existing authority until the native transaction ends; no host business writes.
abort_unless($host->table('users')->where('id',$user)->lockForUpdate()->first(),403);
abort_unless($host->table('company_memberships')->where(['user_id'=>$user,'tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id']])->lockForUpdate()->first(),403);
$view=str_starts_with($right,'purchasing_invoices.')?'purchasing_invoices.view':(str_starts_with($right,'purchasing_requests.')?'purchasing_requests.view':(str_starts_with($right,'purchasing_suppliers.')?'purchasing_suppliers.view':(str_starts_with($right,'purchasing_sourcing.')?'purchasing_sourcing.view':(str_starts_with($right,'purchasing_orders.')?'purchasing_orders.view':'purchasing_receipts.view'))));
if(str_starts_with($right,'purchasing_obligations.'))$view='purchasing_obligations.view';
if(str_starts_with($right,'stock.'))$view='stock.view';
if(str_starts_with($right,'general_ledger.'))$view='general_ledger.view';
if(str_starts_with($right,'supplier_payables.'))$view='supplier_payables.view';
foreach(['company.view'=>null,'unit.view'=>$scope['unit_id'],$view=>$scope['unit_id'],$right=>$scope['unit_id']]as$permission=>$unit)
abort_unless($host->table('permission_grants')->where(['user_id'=>$user,'tenant_id'=>$scope['tenant_id'],'company_id'=>$scope['company_id'],'unit_id'=>$unit,'permission'=>$permission])->lockForUpdate()->first(),403);
$default=DB::getDefaultConnection();$guard=Auth::getDefaultDriver();$environment=app()->environment();
@@ -81,7 +111,8 @@ final class ReceiptRuntime
DB::setDefaultConnection('receipt_native');Schema::clearResolvedInstance('db.schema');Auth::shouldUse('receipt_native');app()->instance('env','testing');
try{
$actor=\Tempo\Purchasing\Embedded\Models\User::where('tenant_id',$binding['tenant_id'])->findOrFail($binding['actor_id']);Auth::guard('receipt_native')->setUser($actor);
return $native->transaction(function()use($native,$actor,$binding,$work,$p){
return $native->transaction(function()use($native,$actor,$binding,$work,$p,$right){
if(str_starts_with($right,'stock.')){$native->statement("SET LOCAL lock_timeout='5s'");$native->statement("SET LOCAL statement_timeout='15s'");}
$native->table('tenants')->where('id',$actor->tenant_id)->lockForUpdate()->firstOrFail();
$version=[$p['context_key'],$binding,$p['manifest_sha256']];$origin=['sgf_context_key'=>$p['context_key'],'workflow_manifest'=>$p['manifest_sha256']];
if(isset($p['internal_manifest_sha256'])){$version[]=$p['internal_manifest_sha256'];$origin['internal_manifest']=$p['internal_manifest_sha256'];}