76 lines
3.5 KiB
PHP
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;
|
|
}
|
|
}
|