id(); $table->string('code')->unique(); $table->string('label'); $table->string('parent_menu_code')->nullable(); $table->foreign('parent_menu_code')->references('code')->on('menues')->nullOnDelete(); }); Schema::create('tenants_menues', function (Blueprint $table): void { $table->id(); $table->string('tenant_code'); $table->string('menu_code'); $table->json('static_content')->nullable(); $table->boolean('is_enabled')->default(true); $table->timestamps(); $table->foreign('menu_code')->references('code')->on('menues')->cascadeOnDelete(); $table->unique(['tenant_code', 'menu_code']); }); Schema::create('roles_menues', function (Blueprint $table): void { $table->id(); $table->string('rol_codigo'); $table->string('menu_codigo'); $table->string('access_level')->default('none'); $table->foreign('menu_codigo')->references('code')->on('menues')->cascadeOnDelete(); $table->unique(['rol_codigo', 'menu_codigo']); }); } public function test_it_preserves_hierarchy_assignments_content_and_access_and_can_run_twice(): void { DB::table('menues')->insert(['code' => 'onticket.custom', 'label' => 'Custom']); DB::table('menues')->insert([ ['code' => 'onticket.custom.child', 'label' => 'Child', 'parent_menu_code' => 'onticket.custom'], ['code' => 'unrelated', 'label' => 'Unrelated', 'parent_menu_code' => 'onticket.custom'], ['code' => 'shopit.adminapp.marca', 'label' => 'Marca', 'parent_menu_code' => null], ]); $createdAt = '2026-09-01 12:00:00'; DB::table('tenants_menues')->insert([ 'tenant_code' => 'tenant', 'menu_code' => 'onticket.custom.child', 'static_content' => '{"title":"Content"}', 'is_enabled' => false, 'created_at' => $createdAt, 'updated_at' => $createdAt, ]); DB::table('roles_menues')->insert([ 'rol_codigo' => 'adminapp', 'menu_codigo' => 'onticket.custom.child', 'access_level' => 'edit', ]); $migration = require database_path('migrations/2026_10_02_000000_remove_onticket_menu_code_prefix.php'); $migration->up(); $migration->up(); $this->assertFalse(DB::table('menues')->where('code', 'like', 'onticket.%')->exists()); $this->assertDatabaseHas('menues', ['code' => 'custom.child', 'label' => 'Child', 'parent_menu_code' => 'custom']); $this->assertDatabaseHas('menues', ['code' => 'unrelated', 'parent_menu_code' => 'custom']); $this->assertDatabaseHas('menues', ['code' => 'shopit.adminapp.marca']); $this->assertDatabaseHas('tenants_menues', [ 'tenant_code' => 'tenant', 'menu_code' => 'custom.child', 'static_content' => '{"title":"Content"}', 'is_enabled' => false, 'created_at' => $createdAt, ]); $this->assertDatabaseHas('roles_menues', [ 'rol_codigo' => 'adminapp', 'menu_codigo' => 'custom.child', 'access_level' => 'edit', ]); $this->assertDatabaseCount('tenants_menues', 1); $this->assertDatabaseCount('roles_menues', 1); } public function test_it_merges_existing_codes_without_losing_the_active_assignment_settings(): void { DB::table('menues')->insert([ ['code' => 'adminapp.tickets', 'label' => 'Tickets'], ['code' => 'onticket.adminapp.tickets', 'label' => 'Tickets prefixed'], ]); DB::table('tenants_menues')->insert([ ['tenant_code' => 'tenant', 'menu_code' => 'adminapp.tickets', 'is_enabled' => true], ['tenant_code' => 'tenant', 'menu_code' => 'onticket.adminapp.tickets', 'is_enabled' => false], ['tenant_code' => 'other', 'menu_code' => 'adminapp.tickets', 'is_enabled' => true], ]); DB::table('roles_menues')->insert([ ['rol_codigo' => 'adminapp', 'menu_codigo' => 'adminapp.tickets', 'access_level' => 'edit'], ['rol_codigo' => 'adminapp', 'menu_codigo' => 'onticket.adminapp.tickets', 'access_level' => 'none'], ['rol_codigo' => 'other', 'menu_codigo' => 'adminapp.tickets', 'access_level' => 'view'], ]); $migration = require database_path('migrations/2026_10_02_000000_remove_onticket_menu_code_prefix.php'); $migration->up(); $this->assertDatabaseCount('menues', 1); $this->assertDatabaseCount('tenants_menues', 2); $this->assertDatabaseCount('roles_menues', 2); $this->assertDatabaseHas('tenants_menues', ['tenant_code' => 'tenant', 'menu_code' => 'adminapp.tickets', 'is_enabled' => false]); $this->assertDatabaseHas('tenants_menues', ['tenant_code' => 'other', 'menu_code' => 'adminapp.tickets', 'is_enabled' => true]); $this->assertDatabaseHas('roles_menues', ['rol_codigo' => 'adminapp', 'menu_codigo' => 'adminapp.tickets', 'access_level' => 'none']); $this->assertDatabaseHas('roles_menues', ['rol_codigo' => 'other', 'menu_codigo' => 'adminapp.tickets', 'access_level' => 'view']); } }