Files
2026-11-02 08:08:05 +02:00

76 lines
3.5 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Tenant;
use App\Models\User;
use App\Services\OrganizationAccess;
use App\Services\WorkContext;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class SgcDeliveryCheck extends Command
{
protected $signature = 'sgc:delivery-check {tenant : ID da empresa}';
protected $description = 'Conferir configuração e ausência de dados de desenvolvimento antes da entrega; não altera dados';
public function handle(WorkContext $contexts): int
{
$tenant = Tenant::query()->find($this->argument('tenant'));
if ($tenant === null || ! Schema::hasColumn('tenants', 'work_context_policy')) {
$this->error('Empresa inexistente ou instalação do contexto pendente.');
return self::FAILURE;
}
$users = User::query()->where('tenant_id', $tenant->id)->get();
$actor = $users->first();
if ($actor === null || $contexts->policy($actor) === null || $contexts->purchased($actor) === [] || ! app(OrganizationAccess::class)->active($tenant->id)) {
$this->error('Defina produto contratado, opções e administração da empresa antes da entrega.');
return self::FAILURE;
}
$administrators = 0;
foreach ($users as $user) {
$profiles = DB::table('organization_memberships as m')->join('organization_profiles as p', 'p.id', '=', 'm.profile_id')
->where('m.tenant_id', $tenant->id)->where('p.tenant_id', $tenant->id)->where('m.user_id', $user->id)
->where('m.maintenance', false)->whereNull('m.expires_at')->pluck('p.permissions');
if ($profiles->contains(fn ($json) => in_array('admin', json_decode($json, true, 32, JSON_THROW_ON_ERROR), true))) {
$administrators++;
}
}
$clean = true;
foreach (Schema::getTables() as $table) {
$name = $table['name'];
if (in_array($name, ['users', 'departments', 'cost_centers', 'organization_profiles', 'organization_memberships', 'organization_access', 'audit_logs'], true)) {
continue;
}
if (Schema::hasColumn($name, 'tenant_id') && DB::table($name)->where('tenant_id', $tenant->id)->exists()) {
$this->error('Existem registos a reconciliar antes da entrega: '.$name);
$clean = false;
}
}
$enabled = $contexts->policy($actor)['enabled'] ?? [];
if ($administrators < 2 || $enabled === [] || array_diff($enabled, $contexts->purchased($actor)) !== []) {
$this->error('Confira dois administradores permanentes e opções activadas dentro do produto contratado.');
$clean = false;
}
foreach ($enabled as $key) {
if (! (WorkContext::OPTIONS[$key]['operational'] ?? false)) {
$this->error('Percurso operacional ainda pendente: '.$key);
$clean = false;
}
if (! $users->contains(fn ($user) => in_array($key, $contexts->available($user), true))) {
$this->error('Opção sem utilizador com perfil/contexto autorizado: '.$key);
$clean = false;
}
}
if ($clean) {
$this->info('Conferência técnica aprovada. Não substitui aceitação funcional, revisão dos cadastros de configuração ou instalação de RH.');
}
return $clean ? self::SUCCESS : self::FAILURE;
}
}