diff --git a/app/Domains/Authorization/Models/Role.php b/app/Domains/Authorization/Models/Role.php index 1ac419a..8b4d305 100644 --- a/app/Domains/Authorization/Models/Role.php +++ b/app/Domains/Authorization/Models/Role.php @@ -3,6 +3,8 @@ namespace App\Domains\Authorization\Models; use App\Domains\Auth\Models\User; +use App\Domains\Menu\Models\Menu; +use App\Domains\Menu\Models\MenuRole; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -39,4 +41,19 @@ class Role extends Model { return $this->hasMany(User::class, 'rol_codigo', 'codigo'); } + + /** + * @return BelongsToMany + */ + public function menus(): BelongsToMany + { + return $this->belongsToMany( + Menu::class, + 'menues_roles', + 'rol_codigo', + 'menu_codigo', + 'codigo', + 'code' + )->using(MenuRole::class); + } } diff --git a/app/Domains/Menu/Models/Menu.php b/app/Domains/Menu/Models/Menu.php index 26a1a77..ccae6eb 100644 --- a/app/Domains/Menu/Models/Menu.php +++ b/app/Domains/Menu/Models/Menu.php @@ -2,6 +2,7 @@ namespace App\Domains\Menu\Models; +use App\Domains\Authorization\Models\Role; use App\Domains\Tenant\Models\Tenant; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -78,4 +79,16 @@ class Menu extends Model ->withPivot('static_content') ->withTimestamps(); } + + public function roles(): BelongsToMany + { + return $this->belongsToMany( + Role::class, + 'menues_roles', + 'menu_codigo', + 'rol_codigo', + 'code', + 'codigo' + )->using(MenuRole::class); + } } diff --git a/app/Domains/Menu/Models/MenuRole.php b/app/Domains/Menu/Models/MenuRole.php new file mode 100644 index 0000000..0f87f15 --- /dev/null +++ b/app/Domains/Menu/Models/MenuRole.php @@ -0,0 +1,31 @@ +belongsTo(Role::class, 'rol_codigo', 'codigo'); + } + + public function menu(): BelongsTo + { + return $this->belongsTo(Menu::class, 'menu_codigo', 'code'); + } +} diff --git a/database/migrations/2026_07_30_000000_create_menues_roles_table.php b/database/migrations/2026_07_30_000000_create_menues_roles_table.php new file mode 100644 index 0000000..ea0db5e --- /dev/null +++ b/database/migrations/2026_07_30_000000_create_menues_roles_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('rol_codigo'); + $table->string('menu_codigo'); + + $table->foreign('rol_codigo') + ->references('codigo') + ->on('roles') + ->cascadeOnUpdate() + ->cascadeOnDelete(); + $table->foreign('menu_codigo') + ->references('code') + ->on('menues') + ->cascadeOnUpdate() + ->cascadeOnDelete(); + + $table->unique(['rol_codigo', 'menu_codigo']); + }); + } + + public function down(): void + { + Schema::dropIfExists('menues_roles'); + } +}; diff --git a/database/seeders/AuthorizationSeeder.php b/database/seeders/AuthorizationSeeder.php index 3c18bbd..28428fd 100644 --- a/database/seeders/AuthorizationSeeder.php +++ b/database/seeders/AuthorizationSeeder.php @@ -111,6 +111,11 @@ class AuthorizationSeeder extends Seeder 'descripcion' => 'Administra la plataforma y todos sus tenants.', 'permisos' => [], ], + 'adminapp' => [ + 'nombre' => 'Administrador de la aplicación', + 'descripcion' => 'Accede a los menús administrativos de la aplicación.', + 'permisos' => [], + ], 'tenant_admin' => [ 'nombre' => 'Administrador del tenant', 'descripcion' => 'Administra la operación de su propio tenant.', diff --git a/database/seeders/MenuSeeder.php b/database/seeders/MenuSeeder.php index c87a770..c4afb33 100644 --- a/database/seeders/MenuSeeder.php +++ b/database/seeders/MenuSeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Domains\Authorization\Models\Role; use App\Domains\Menu\Models\Menu; use App\Domains\Tenant\Models\Tenant; use Illuminate\Database\Seeder; @@ -133,6 +134,19 @@ class MenuSeeder extends Seeder ->whereIn('code', ['profile', 'purchases', 'tickets']) ->delete(); + $allRoleMenuCodes = Menu::query()->pluck('code'); + $userMenuCodes = Menu::query() + ->where('code', 'not like', 'admin.%') + ->pluck('code'); + + Role::query() + ->whereIn('codigo', ['admin', 'adminapp']) + ->each(fn (Role $role) => $role->menus()->sync($allRoleMenuCodes)); + + Role::query() + ->where('codigo', 'user') + ->each(fn (Role $role) => $role->menus()->sync($userMenuCodes)); + $tenants = Tenant::all(); $allMenus = Menu::pluck('code')->toArray(); diff --git a/tests/Feature/Seeders/AuthorizationSeederTest.php b/tests/Feature/Seeders/AuthorizationSeederTest.php index 32ff411..93d8b42 100644 --- a/tests/Feature/Seeders/AuthorizationSeederTest.php +++ b/tests/Feature/Seeders/AuthorizationSeederTest.php @@ -17,7 +17,7 @@ class AuthorizationSeederTest extends TestCase $this->seed(AuthorizationSeeder::class); $this->assertSame( - ['admin', 'tenant_admin', 'user'], + ['admin', 'adminapp', 'tenant_admin', 'user'], Role::query()->orderBy('codigo')->pluck('codigo')->all() ); $this->assertCount(22, Permission::query()->get()); @@ -28,10 +28,12 @@ class AuthorizationSeederTest extends TestCase $this->seed(AuthorizationSeeder::class); $admin = Role::query()->where('codigo', 'admin')->firstOrFail(); + $appAdmin = Role::query()->where('codigo', 'adminapp')->firstOrFail(); $tenantAdmin = Role::query()->where('codigo', 'tenant_admin')->firstOrFail(); $user = Role::query()->where('codigo', 'user')->firstOrFail(); $this->assertCount(22, $admin->permissions); + $this->assertCount(0, $appAdmin->permissions); $this->assertCount(21, $tenantAdmin->permissions); $this->assertFalse($tenantAdmin->permissions->contains('codigo', 'roles.gestionar')); $this->assertCount(0, $user->permissions); @@ -42,7 +44,7 @@ class AuthorizationSeederTest extends TestCase $this->seed(AuthorizationSeeder::class); $this->seed(AuthorizationSeeder::class); - $this->assertCount(3, Role::query()->get()); + $this->assertCount(4, Role::query()->get()); $this->assertCount(22, Permission::query()->get()); $this->assertDatabaseCount('roles_permisos', 43); } diff --git a/tests/Feature/Seeders/MenuSeederTest.php b/tests/Feature/Seeders/MenuSeederTest.php index 6c394f0..d980775 100644 --- a/tests/Feature/Seeders/MenuSeederTest.php +++ b/tests/Feature/Seeders/MenuSeederTest.php @@ -4,8 +4,10 @@ namespace Tests\Feature\Seeders; use App\Domains\Attachable\Enums\AttachmentType; use App\Domains\Attachable\Models\Attachment; +use App\Domains\Authorization\Models\Role; use App\Domains\Menu\Models\Menu; use App\Domains\Tenant\Models\Tenant; +use Database\Seeders\AuthorizationSeeder; use Database\Seeders\MenuSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -45,6 +47,48 @@ class MenuSeederTest extends TestCase } } + public function test_it_assigns_menus_to_the_expected_roles(): void + { + $this->seed(AuthorizationSeeder::class); + $this->seed(MenuSeeder::class); + + $allMenuCodes = Menu::query() + ->orderBy('code') + ->pluck('code') + ->all(); + $userMenuCodes = Menu::query() + ->where('code', 'not like', 'admin.%') + ->orderBy('code') + ->pluck('code') + ->all(); + + foreach (['admin', 'adminapp'] as $roleCode) { + $roleMenuCodes = Role::query() + ->where('codigo', $roleCode) + ->firstOrFail() + ->menus() + ->orderBy('menues.code') + ->pluck('menues.code') + ->all(); + + $this->assertSame($allMenuCodes, $roleMenuCodes); + } + + $userRoleMenuCodes = Role::query() + ->where('codigo', 'user') + ->firstOrFail() + ->menus() + ->orderBy('menues.code') + ->pluck('menues.code') + ->all(); + + $this->assertSame($userMenuCodes, $userRoleMenuCodes); + $this->assertDatabaseCount( + 'menues_roles', + (count($allMenuCodes) * 2) + count($userMenuCodes) + ); + } + public function test_it_seeds_the_account_menu_hierarchy(): void { $tenant = $this->createTenant('account_tenant');