-- Migration: Add Menu Action Access System -- Description: Create menu_actions, user_level_menu_accesses, and user_level_menu_action_accesses tables -- Date: 2026-01-16 -- ============================================================================ -- Step 1: Create menu_actions table -- ============================================================================ CREATE TABLE IF NOT EXISTS menu_actions ( id SERIAL PRIMARY KEY, menu_id INT NOT NULL, action_code VARCHAR(50) NOT NULL, action_name VARCHAR(255) NOT NULL, description TEXT NULL, path_url VARCHAR(255) NULL, http_method VARCHAR(10) NULL, position INT NULL, client_id UUID NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, UNIQUE(menu_id, action_code, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) ); -- Add indexes for performance CREATE INDEX idx_menu_actions_menu_id ON menu_actions(menu_id); CREATE INDEX idx_menu_actions_action_code ON menu_actions(action_code); CREATE INDEX idx_menu_actions_client_id ON menu_actions(client_id) WHERE client_id IS NOT NULL; CREATE INDEX idx_menu_actions_is_active ON menu_actions(is_active); -- Add comments COMMENT ON TABLE menu_actions IS 'Actions yang tersedia di setiap menu (view, create, edit, delete, approve, export, etc)'; COMMENT ON COLUMN menu_actions.menu_id IS 'Foreign key ke master_menus'; COMMENT ON COLUMN menu_actions.action_code IS 'Kode action: view, create, edit, delete, approve, export, etc'; COMMENT ON COLUMN menu_actions.action_name IS 'Nama action yang ditampilkan ke user'; COMMENT ON COLUMN menu_actions.path_url IS 'Optional: URL path untuk routing frontend'; COMMENT ON COLUMN menu_actions.http_method IS 'Optional: HTTP method (GET, POST, PUT, DELETE)'; -- ============================================================================ -- Step 2: Create user_level_menu_accesses table -- ============================================================================ CREATE TABLE IF NOT EXISTS user_level_menu_accesses ( id SERIAL PRIMARY KEY, user_level_id INT NOT NULL, menu_id INT NOT NULL, can_access BOOLEAN DEFAULT TRUE, client_id UUID NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, UNIQUE(user_level_id, menu_id, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) ); -- Add indexes for performance CREATE INDEX idx_user_level_menu_accesses_user_level_id ON user_level_menu_accesses(user_level_id); CREATE INDEX idx_user_level_menu_accesses_menu_id ON user_level_menu_accesses(menu_id); CREATE INDEX idx_user_level_menu_accesses_client_id ON user_level_menu_accesses(client_id) WHERE client_id IS NOT NULL; CREATE INDEX idx_user_level_menu_accesses_is_active ON user_level_menu_accesses(is_active); -- Add comments COMMENT ON TABLE user_level_menu_accesses IS 'Mengatur akses user_level ke menu tertentu'; COMMENT ON COLUMN user_level_menu_accesses.user_level_id IS 'Foreign key ke user_levels'; COMMENT ON COLUMN user_level_menu_accesses.menu_id IS 'Foreign key ke master_menus'; COMMENT ON COLUMN user_level_menu_accesses.can_access IS 'Apakah user level boleh mengakses menu ini'; -- ============================================================================ -- Step 3: Create user_level_menu_action_accesses table -- ============================================================================ CREATE TABLE IF NOT EXISTS user_level_menu_action_accesses ( id SERIAL PRIMARY KEY, user_level_id INT NOT NULL, menu_id INT NOT NULL, action_code VARCHAR(50) NOT NULL, can_access BOOLEAN DEFAULT TRUE, client_id UUID NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, UNIQUE(user_level_id, menu_id, action_code, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) ); -- Add indexes for performance CREATE INDEX idx_user_level_menu_action_accesses_user_level_id ON user_level_menu_action_accesses(user_level_id); CREATE INDEX idx_user_level_menu_action_accesses_menu_id ON user_level_menu_action_accesses(menu_id); CREATE INDEX idx_user_level_menu_action_accesses_action_code ON user_level_menu_action_accesses(action_code); CREATE INDEX idx_user_level_menu_action_accesses_client_id ON user_level_menu_action_accesses(client_id) WHERE client_id IS NOT NULL; CREATE INDEX idx_user_level_menu_action_accesses_is_active ON user_level_menu_action_accesses(is_active); -- Add comments COMMENT ON TABLE user_level_menu_action_accesses IS 'Mengatur akses user_level ke action tertentu di dalam menu'; COMMENT ON COLUMN user_level_menu_action_accesses.user_level_id IS 'Foreign key ke user_levels'; COMMENT ON COLUMN user_level_menu_action_accesses.menu_id IS 'Foreign key ke master_menus'; COMMENT ON COLUMN user_level_menu_action_accesses.action_code IS 'Kode action (harus sesuai dengan menu_actions.action_code)'; COMMENT ON COLUMN user_level_menu_action_accesses.can_access IS 'Apakah user level boleh melakukan action ini'; -- ============================================================================ -- Step 4: Create trigger for updated_at -- ============================================================================ CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ language 'plpgsql'; -- Apply trigger to menu_actions DROP TRIGGER IF EXISTS update_menu_actions_updated_at ON menu_actions; CREATE TRIGGER update_menu_actions_updated_at BEFORE UPDATE ON menu_actions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Apply trigger to user_level_menu_accesses DROP TRIGGER IF EXISTS update_user_level_menu_accesses_updated_at ON user_level_menu_accesses; CREATE TRIGGER update_user_level_menu_accesses_updated_at BEFORE UPDATE ON user_level_menu_accesses FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Apply trigger to user_level_menu_action_accesses DROP TRIGGER IF EXISTS update_user_level_menu_action_accesses_updated_at ON user_level_menu_action_accesses; CREATE TRIGGER update_user_level_menu_action_accesses_updated_at BEFORE UPDATE ON user_level_menu_action_accesses FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ============================================================================ -- Verification -- ============================================================================ DO $$ DECLARE v_menu_actions_exists BOOLEAN; v_user_level_menu_accesses_exists BOOLEAN; v_user_level_menu_action_accesses_exists BOOLEAN; BEGIN -- Check menu_actions table SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'menu_actions' ) INTO v_menu_actions_exists; -- Check user_level_menu_accesses table SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user_level_menu_accesses' ) INTO v_user_level_menu_accesses_exists; -- Check user_level_menu_action_accesses table SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user_level_menu_action_accesses' ) INTO v_user_level_menu_action_accesses_exists; -- Report results RAISE NOTICE '========================================'; RAISE NOTICE 'Migration Verification Results:'; RAISE NOTICE '========================================'; IF v_menu_actions_exists THEN RAISE NOTICE '✓ Table menu_actions created successfully'; ELSE RAISE WARNING '✗ Table menu_actions NOT created'; END IF; IF v_user_level_menu_accesses_exists THEN RAISE NOTICE '✓ Table user_level_menu_accesses created successfully'; ELSE RAISE WARNING '✗ Table user_level_menu_accesses NOT created'; END IF; IF v_user_level_menu_action_accesses_exists THEN RAISE NOTICE '✓ Table user_level_menu_action_accesses created successfully'; ELSE RAISE WARNING '✗ Table user_level_menu_action_accesses NOT created'; END IF; RAISE NOTICE '========================================'; IF v_menu_actions_exists AND v_user_level_menu_accesses_exists AND v_user_level_menu_action_accesses_exists THEN RAISE NOTICE 'Migration completed successfully! ✓'; ELSE RAISE WARNING 'Migration completed with errors! Please check above.'; END IF; RAISE NOTICE '========================================'; END $$;