['sgf:read_only','/?start=service_invoice_setup','Comercial','Clientes, orçamentos, documentos e PDF; passagem explícita para recebíveis'], 'purchasing'=>['purchasing:actor','/purchasing/workspace','Compras','Fornecedores, requisição, propostas, ordem, recepção, factura e obrigação'], 'stock'=>['purchasing:validator','/stock/final','Stock','Artigos, armazéns, existências, trânsito, lotes, contagem e histórico'], 'workshop'=>['sgf:workshop_manage','/?start=workshop','Oficinas','Equipamento, cliente, agenda, ordem, autorização e apontamentos verificados'], 'finance'=>['sgf:read_only','/?start=service_receivables','Financeiro','Recebíveis, obrigações, Caixa, conferência de extracto e indicadores'], 'accounting'=>['sgf:read_only','/general-ledger','Contabilidade','Origens, diário integrado, plano, balancete, razão e mapas fiscais internos'], 'human_resources'=>['hr:hr_read','/?start=remunerations','Recursos Humanos','Cadastro, contratos, assiduidade, férias e remunerações internas QA; acesso próprio e revisão distinta'], ]; private function guard():void { abort_unless(in_array(request()->ip(),['127.0.0.1','::1'],true),404); CompositionRuntime::assert(); abort_unless(is_string(config('sgf_single_installation.profiles_sha256'))&&preg_match('/^[a-f0-9]{64}$/D',config('sgf_single_installation.profiles_sha256')),503); } private function profiles():array { $pin=config('sgf_single_installation.profiles_sha256');$out=[];$context=app(CompanyContext::class); $rows=DB::table('sgf_presentation_profiles')->orderBy('profile_key')->get(); abort_unless($rows->count()===10&&$rows->every(fn($r)=>hash_equals($r->source_hash,$pin)),503,'Perfis QA não conferidos.'); try{ foreach($rows as $row){ $context->selectFor((int)$row->user_id,(int)$row->tenant_id,(int)$row->company_id,(int)$row->unit_id); $out['sgf:'.$row->profile_key]=['user_id'=>(int)$row->user_id,'label'=>$row->label,'scope'=>['tenant_id'=>(int)$row->tenant_id,'company_id'=>(int)$row->company_id,'unit_id'=>(int)$row->unit_id],'modules'=>app(ModuleNavigation::class)->available($context)]; } if(config('human_resources.enabled')===true){foreach(HumanResourcesProfiles::registered() as$row){ $context->selectFor($row->user_id,$row->tenant_id,$row->company_id,$row->unit_id); $out['hr:'.$row->profile_key]=['user_id'=>$row->user_id,'label'=>$row->label,'scope'=>['tenant_id'=>$row->tenant_id,'company_id'=>$row->company_id,'unit_id'=>$row->unit_id],'modules'=>app(ModuleNavigation::class)->available($context)]; }} $scope=config('purchasing_workspace.scope'); foreach(config('purchasing_workspace.profiles',[]) as $key=>$profile){ $context->selectFor($profile['user_id'],$scope['tenant_id'],$scope['company_id'],$scope['unit_id']); $out['purchasing:'.$key]=['user_id'=>$profile['user_id'],'label'=>$profile['label'].' — Compras','scope'=>$scope,'modules'=>app(ModuleNavigation::class)->available($context)]; } }finally{$context->clear();} return $out; } public function screen(Request $r) { $this->guard();abort_if($r->query()!==[],422); $profiles=$this->profiles();$tour=[];foreach(self::TOUR as $module=>[$key,$url,$label,$description])if(isset($profiles[$key])&&in_array($module,array_column($profiles[$key]['modules'],'id'),true))$tour[$module]=['profile'=>$key,'function'=>$profiles[$key]['label'],'label'=>$label,'description'=>$description]; $primary=['commercial'=>['sgf:commercial_prepare','sgf:commercial_review'],'purchasing'=>array_map(fn($id)=>'purchasing:'.$id,['requester','approver','actor','issuer','validator','reviewer','auditor']),'stock'=>['purchasing:validator','sgf:workshop_verify','sgf:read_only'],'workshop'=>['sgf:workshop_manage','sgf:workshop_record','sgf:workshop_verify','sgf:finance_prepare','sgf:finance_review','purchasing:validator'],'finance'=>['sgf:finance_prepare','sgf:finance_review'],'accounting'=>['sgf:accounting_prepare','sgf:accounting_review','sgf:finance_prepare','sgf:finance_review']]; if(config('human_resources.enabled')===true)$primary['human_resources']=['hr:hr_prepare','hr:hr_review','hr:hr_read']; $moduleProfiles=[];foreach($primary as $module=>$keys)foreach($keys as $key)if(isset($profiles[$key])&&in_array($module,array_column($profiles[$key]['modules'],'id'),true))$moduleProfiles[$module][$key]=$profiles[$key]; return response()->view('sgf-presentation-entry',['profiles'=>$profiles,'tour'=>$tour,'moduleProfiles'=>$moduleProfiles])->header('Cache-Control','no-store')->header('X-SGF-Installation',CompositionRuntime::physical()['composition']); } public function enter(Request $r) { $this->guard();$d=$r->validate(['profile'=>'required|string|max:100','module_id'=>'required|string|max:30','landing'=>'sometimes|in:'.implode(',',array_keys(self::TOUR))]); abort_if(array_diff(array_keys($r->all()),['_token','profile','module_id','landing']),422); $target='/';if(isset($d['landing'])){$route=self::TOUR[$d['landing']];abort_unless($d['profile']===$route[0]&&$d['module_id']===$d['landing'],403);$target=$route[1];} $profile=$this->profiles()[$d['profile']]??null; abort_unless($profile&&in_array($d['module_id'],array_column($profile['modules'],'id'),true),403,'Função ou módulo não autorizado.'); $user=User::findOrFail($profile['user_id']);auth()->login($user);$r->session()->regenerate(); $r->session()->put('auth_version',$user->auth_version); $r->session()->put('company_context',$profile['scope']+['module_id'=>$d['module_id'],'token'=>bin2hex(random_bytes(24))]); \App\Modules\Identity\Services\Audit::record('context.synthetic_profile_selected',$user->id,$profile['scope']['tenant_id'],$profile['scope']['company_id'],$profile['scope']['unit_id'],details:['module_id'=>$d['module_id'],'reason'=>'Escolha explícita da função QA aprovada, na instalação única.']); return redirect($target); } }