mirror of
https://github.com/marcogll/AnchorOS.git
synced 2026-03-15 11:24:26 +00:00
Add detailed logging to API endpoints for debugging 500 errors
This commit is contained in:
198
supabase/migrations/20260118000000_google_calendar_events.sql
Normal file
198
supabase/migrations/20260118000000_google_calendar_events.sql
Normal file
@@ -0,0 +1,198 @@
|
||||
-- ============================================
|
||||
-- FASE 2.1 - GOOGLE CALENDAR EVENTS TABLE
|
||||
-- Date: 20260118
|
||||
-- Description: Create google_calendar_events table for bidirectional sync
|
||||
-- ============================================
|
||||
|
||||
-- Create google_calendar_events table
|
||||
CREATE TABLE IF NOT EXISTS google_calendar_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
staff_id UUID NOT NULL REFERENCES staff(id) ON DELETE CASCADE,
|
||||
google_event_id VARCHAR(255) UNIQUE NOT NULL,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
start_time_utc TIMESTAMPTZ NOT NULL,
|
||||
end_time_utc TIMESTAMPTZ NOT NULL,
|
||||
is_blocking BOOLEAN DEFAULT false,
|
||||
is_anchoros_booking BOOLEAN DEFAULT false,
|
||||
booking_id UUID REFERENCES bookings(id) ON DELETE SET NULL,
|
||||
synced_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
sync_status VARCHAR(50) DEFAULT 'synced',
|
||||
sync_error TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Add comment to table
|
||||
COMMENT ON TABLE google_calendar_events IS 'Stores synchronization state of Google Calendar events with AnchorOS bookings';
|
||||
|
||||
-- Add column comments
|
||||
COMMENT ON COLUMN google_calendar_events.staff_id IS 'Reference to staff member associated with this calendar event';
|
||||
COMMENT ON COLUMN google_calendar_events.google_event_id IS 'Unique Google Calendar event ID';
|
||||
COMMENT ON COLUMN google_calendar_events.is_blocking IS 'Whether this event blocks staff availability for booking';
|
||||
COMMENT ON COLUMN google_calendar_events.is_anchoros_booking IS 'True if event was created by AnchorOS sync, false if external';
|
||||
COMMENT ON COLUMN google_calendar_events.booking_id IS 'Reference to AnchorOS booking if this is a synced booking event';
|
||||
COMMENT ON COLUMN google_calendar_events.synced_at IS 'Last synchronization timestamp';
|
||||
COMMENT ON COLUMN google_calendar_events.sync_status IS 'Sync status: synced, pending, failed';
|
||||
COMMENT ON COLUMN google_calendar_events.sync_error IS 'Error message if sync failed';
|
||||
|
||||
-- Create indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_google_calendar_events_staff ON google_calendar_events(staff_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_google_calendar_events_time ON google_calendar_events(start_time_utc, end_time_utc);
|
||||
CREATE INDEX IF NOT EXISTS idx_google_calendar_events_booking ON google_calendar_events(booking_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_google_calendar_events_blocking ON google_calendar_events(staff_id, is_blocking) WHERE is_blocking = true;
|
||||
CREATE INDEX IF NOT EXISTS idx_google_calendar_events_sync_status ON google_calendar_events(sync_status);
|
||||
|
||||
-- Create function to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_google_calendar_events_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create trigger to auto-update updated_at
|
||||
DROP TRIGGER IF EXISTS trigger_update_google_calendar_events_updated_at ON google_calendar_events;
|
||||
CREATE TRIGGER trigger_update_google_calendar_events_updated_at
|
||||
BEFORE UPDATE ON google_calendar_events
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_google_calendar_events_updated_at();
|
||||
|
||||
-- Add google_calendar_event_id column to bookings if not exists (for bidirectional sync)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'bookings' AND column_name = 'google_calendar_event_id') THEN
|
||||
ALTER TABLE bookings ADD COLUMN google_calendar_event_id VARCHAR(255);
|
||||
CREATE INDEX IF NOT EXISTS idx_bookings_google_event ON bookings(google_calendar_event_id);
|
||||
COMMENT ON COLUMN bookings.google_calendar_event_id IS 'Google Calendar event ID for this booking';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Create trigger to automatically sync booking creation/update to Google Calendar
|
||||
CREATE OR REPLACE FUNCTION trigger_sync_booking_to_google_calendar()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Only sync if GOOGLE_CALENDAR_SYNC is enabled (check via environment variable)
|
||||
-- This is handled at application level, not in database trigger
|
||||
-- The trigger here is just a placeholder for potential future implementation
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Note: The actual sync logic is implemented in /lib/google-calendar.ts
|
||||
-- This migration provides the database schema needed for bidirectional sync
|
||||
|
||||
-- Create function to get blocking calendar events for a staff member
|
||||
CREATE OR REPLACE FUNCTION get_blocking_calendar_events(
|
||||
p_staff_id UUID,
|
||||
p_start_time_utc TIMESTAMPTZ,
|
||||
p_end_time_utc TIMESTAMPTZ
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
google_event_id VARCHAR(255),
|
||||
title VARCHAR(500),
|
||||
start_time_utc TIMESTAMPTZ,
|
||||
end_time_utc TIMESTAMPTZ
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
gce.id,
|
||||
gce.google_event_id,
|
||||
gce.title,
|
||||
gce.start_time_utc,
|
||||
gce.end_time_utc
|
||||
FROM google_calendar_events gce
|
||||
WHERE gce.staff_id = p_staff_id
|
||||
AND gce.is_blocking = true
|
||||
AND gce.start_time_utc < p_end_time_utc
|
||||
AND gce.end_time_utc > p_start_time_utc
|
||||
AND gce.is_anchoros_booking = false -- Only external events block
|
||||
ORDER BY gce.start_time_utc;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Create function to check if time slot conflicts with blocking calendar events
|
||||
CREATE OR REPLACE FUNCTION check_calendar_blocking(
|
||||
p_staff_id UUID,
|
||||
p_start_time_utc TIMESTAMPTZ,
|
||||
p_end_time_utc TIMESTAMPTZ,
|
||||
p_exclude_booking_id UUID DEFAULT NULL
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_has_conflict BOOLEAN := false;
|
||||
BEGIN
|
||||
-- Check for blocking calendar events (excluding AnchorOS bookings)
|
||||
SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM google_calendar_events gce
|
||||
WHERE gce.staff_id = p_staff_id
|
||||
AND gce.is_blocking = true
|
||||
AND gce.start_time_utc < p_end_time_utc
|
||||
AND gce.end_time_utc > p_start_time_utc
|
||||
AND (p_exclude_booking_id IS NULL OR gce.booking_id != p_exclude_booking_id)
|
||||
) INTO v_has_conflict;
|
||||
|
||||
RETURN NOT v_has_conflict; -- Return true if NO conflicts (available)
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Grant necessary permissions
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON google_calendar_events TO authenticated;
|
||||
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO authenticated;
|
||||
|
||||
-- RLS Policies for google_calendar_events
|
||||
ALTER TABLE google_calendar_events ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy: Staff can see their own calendar events
|
||||
CREATE POLICY "Staff can view own calendar events"
|
||||
ON google_calendar_events
|
||||
FOR SELECT
|
||||
USING (
|
||||
auth.uid()::text = (SELECT user_id::text FROM staff WHERE id = staff_id)
|
||||
OR
|
||||
(SELECT role FROM staff WHERE id = staff_id) IN ('admin', 'manager')
|
||||
);
|
||||
|
||||
-- Policy: Admins and managers can insert calendar events
|
||||
CREATE POLICY "Admins and managers can insert calendar events"
|
||||
ON google_calendar_events
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM staff
|
||||
WHERE id = staff_id
|
||||
AND user_id = auth.uid()
|
||||
AND role IN ('admin', 'manager')
|
||||
)
|
||||
);
|
||||
|
||||
-- Policy: Admins and managers can update calendar events
|
||||
CREATE POLICY "Admins and managers can update calendar events"
|
||||
ON google_calendar_events
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM staff
|
||||
WHERE id = staff_id
|
||||
AND user_id = auth.uid()
|
||||
AND role IN ('admin', 'manager')
|
||||
)
|
||||
);
|
||||
|
||||
-- Policy: Admins and managers can delete calendar events
|
||||
CREATE POLICY "Admins and managers can delete calendar events"
|
||||
ON google_calendar_events
|
||||
FOR DELETE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM staff
|
||||
WHERE id = staff_id
|
||||
AND user_id = auth.uid()
|
||||
AND role IN ('admin', 'manager')
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,95 @@
|
||||
-- ============================================
|
||||
-- FASE 2.1 - UPDATE AVAILABILITY WITH CALENDAR SYNC
|
||||
-- Date: 20260118
|
||||
-- Description: Update check_staff_availability to include Google Calendar conflicts
|
||||
-- ============================================
|
||||
|
||||
/**
|
||||
* @description Updated check_staff_availability with Google Calendar integration
|
||||
* @param {UUID} p_staff_id - ID del staff a verificar
|
||||
* @param {TIMESTAMPTZ} p_start_time_utc - Hora de inicio en UTC
|
||||
* @param {TIMESTAMPTZ} p_end_time_utc - Hora de fin en UTC
|
||||
* @param {UUID} p_exclude_booking_id - (Opcional) ID de reserva a excluir
|
||||
* @returns {BOOLEAN} - true si el staff está disponible, false en caso contrario
|
||||
* @example SELECT check_staff_availability('uuid...', NOW(), NOW() + INTERVAL '1 hour', NULL);
|
||||
*/
|
||||
|
||||
-- Drop existing function
|
||||
DROP FUNCTION IF EXISTS check_staff_availability(UUID, TIMESTAMPTZ, TIMESTAMPTZ, UUID) CASCADE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_staff_availability(
|
||||
p_staff_id UUID,
|
||||
p_start_time_utc TIMESTAMPTZ,
|
||||
p_end_time_utc TIMESTAMPTZ,
|
||||
p_exclude_booking_id UUID DEFAULT NULL
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_staff RECORD;
|
||||
v_has_work_conflict BOOLEAN := false;
|
||||
v_has_booking_conflict BOOLEAN := false;
|
||||
v_has_calendar_conflict BOOLEAN := false;
|
||||
v_has_block_conflict BOOLEAN := false;
|
||||
BEGIN
|
||||
-- 1. Check if staff exists and is active
|
||||
SELECT * INTO v_staff FROM staff WHERE id = p_staff_id;
|
||||
|
||||
IF NOT FOUND OR NOT v_staff.is_active OR NOT v_staff.is_available_for_booking THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
|
||||
-- 2. Check work hours and days
|
||||
v_has_work_conflict := NOT check_staff_work_hours(p_staff_id, p_start_time_utc, p_end_time_utc);
|
||||
IF v_has_work_conflict THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
|
||||
-- 3. Check existing bookings conflict
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM bookings b
|
||||
WHERE b.staff_id = p_staff_id
|
||||
AND b.status != 'cancelled'
|
||||
AND b.start_time_utc < p_end_time_utc
|
||||
AND b.end_time_utc > p_start_time_utc
|
||||
AND (p_exclude_booking_id IS NULL OR b.id != p_exclude_booking_id)
|
||||
) INTO v_has_booking_conflict;
|
||||
|
||||
IF v_has_booking_conflict THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
|
||||
-- 4. Check manual blocks conflict
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM staff_availability sa
|
||||
WHERE sa.staff_id = p_staff_id
|
||||
AND sa.date = p_start_time_utc::DATE
|
||||
AND sa.is_available = false
|
||||
AND (p_start_time_utc::TIME >= sa.start_time AND p_start_time_utc::TIME < sa.end_time
|
||||
OR p_end_time_utc::TIME > sa.start_time AND p_end_time_utc::TIME <= sa.end_time
|
||||
OR p_start_time_utc::TIME <= sa.start_time AND p_end_time_utc::TIME >= sa.end_time)
|
||||
) INTO v_has_block_conflict;
|
||||
|
||||
IF v_has_block_conflict THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
|
||||
-- 5. NEW: Check Google Calendar blocking events conflict
|
||||
v_has_calendar_conflict := NOT check_calendar_blocking(p_staff_id, p_start_time_utc, p_end_time_utc, p_exclude_booking_id);
|
||||
|
||||
IF v_has_calendar_conflict THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
|
||||
-- All checks passed - staff is available
|
||||
RETURN true;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Update get_detailed_availability to use the updated function (already calls it)
|
||||
-- No change needed as it cascades
|
||||
|
||||
-- Test function
|
||||
COMMENT ON FUNCTION check_staff_availability IS 'Enhanced availability check including Google Calendar sync. Verifies work hours, bookings, manual blocks, and external calendar events.';
|
||||
|
||||
-- Grant execute permission
|
||||
GRANT EXECUTE ON FUNCTION check_staff_availability TO authenticated, anon, service_role;
|
||||
121
supabase/migrations/20260118030000_dual_artist_support.sql
Normal file
121
supabase/migrations/20260118030000_dual_artist_support.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
-- ============================================
|
||||
-- FASE 2.2 - DUAL ARTIST SERVICES SUPPORT
|
||||
-- Date: 20260118
|
||||
-- Description: Add premium_amount to services and dual artist assignment functions
|
||||
-- ============================================
|
||||
|
||||
-- Add premium_amount column to services
|
||||
ALTER TABLE services ADD COLUMN IF NOT EXISTS premium_amount DECIMAL(10,2) DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN services.premium_amount IS 'Additional fee for premium/express services (auto-applied if premium_fee_enabled)';
|
||||
|
||||
-- Update seed data for express services (example)
|
||||
UPDATE services
|
||||
SET premium_amount = 500
|
||||
WHERE name LIKE '%Express%' OR requires_dual_artist = true;
|
||||
|
||||
-- Create function to assign dual artists
|
||||
CREATE OR REPLACE FUNCTION assign_dual_artists(
|
||||
p_location_id UUID,
|
||||
p_start_time_utc TIMESTAMPTZ,
|
||||
p_end_time_utc TIMESTAMPTZ,
|
||||
p_service_id UUID
|
||||
)
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
v_primary_artist UUID;
|
||||
v_secondary_artist UUID;
|
||||
v_room_resource UUID;
|
||||
v_service RECORD;
|
||||
v_artists JSONB;
|
||||
BEGIN
|
||||
-- Get service details
|
||||
SELECT * INTO v_service FROM services WHERE id = p_service_id;
|
||||
|
||||
IF NOT FOUND OR NOT v_service.requires_dual_artist THEN
|
||||
RETURN jsonb_build_object(
|
||||
'primary_artist', NULL,
|
||||
'secondary_artist', NULL,
|
||||
'room_resource', NULL,
|
||||
'error', 'Service does not require dual artists'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 1. Find available room resource
|
||||
SELECT id INTO v_room_resource
|
||||
FROM resources r
|
||||
WHERE r.location_id = p_location_id
|
||||
AND r.type = 'room' -- Assuming room type enum exists
|
||||
AND check_resource_availability(r.id, p_start_time_utc, p_end_time_utc)
|
||||
ORDER BY r.name -- or priority
|
||||
LIMIT 1;
|
||||
|
||||
IF v_room_resource IS NULL THEN
|
||||
RETURN jsonb_build_object(
|
||||
'primary_artist', NULL,
|
||||
'secondary_artist', NULL,
|
||||
'room_resource', NULL,
|
||||
'error', 'No available room resource'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 2. Find 2 available artists/staff (priority: artist > staff)
|
||||
SELECT jsonb_agg(jsonb_build_object('id', s.id, 'display_name', s.display_name, 'role', s.role)) INTO v_artists
|
||||
FROM staff s
|
||||
WHERE s.location_id = p_location_id
|
||||
AND s.is_active = true
|
||||
AND s.is_available_for_booking = true
|
||||
AND s.role IN ('artist', 'staff')
|
||||
AND check_staff_availability(s.id, p_start_time_utc, p_end_time_utc)
|
||||
ORDER BY
|
||||
CASE s.role
|
||||
WHEN 'artist' THEN 1
|
||||
WHEN 'staff' THEN 2
|
||||
END,
|
||||
s.display_name
|
||||
LIMIT 2;
|
||||
|
||||
IF jsonb_array_length(v_artists) < 2 THEN
|
||||
RETURN jsonb_build_object(
|
||||
'primary_artist', NULL,
|
||||
'secondary_artist', NULL,
|
||||
'room_resource', v_room_resource,
|
||||
'error', 'Insufficient available artists (need 2)'
|
||||
);
|
||||
END IF;
|
||||
|
||||
SELECT (v_artists->0)->>'id' INTO v_primary_artist;
|
||||
SELECT (v_artists->1)->>'id' INTO v_secondary_artist;
|
||||
|
||||
RETURN jsonb_build_object(
|
||||
'primary_artist', v_primary_artist,
|
||||
'secondary_artist', v_secondary_artist,
|
||||
'room_resource', v_room_resource,
|
||||
'success', true
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Create function to calculate service total with premium
|
||||
CREATE OR REPLACE FUNCTION calculate_service_total(p_service_id UUID)
|
||||
RETURNS DECIMAL(10,2) AS $$
|
||||
DECLARE
|
||||
v_total DECIMAL(10,2);
|
||||
BEGIN
|
||||
SELECT
|
||||
COALESCE(base_price, 0) +
|
||||
CASE WHEN premium_fee_enabled THEN COALESCE(premium_amount, 0) ELSE 0 END
|
||||
INTO v_total
|
||||
FROM services
|
||||
WHERE id = p_service_id;
|
||||
|
||||
RETURN COALESCE(v_total, 0);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Grant permissions
|
||||
GRANT EXECUTE ON FUNCTION assign_dual_artists TO authenticated, service_role;
|
||||
GRANT EXECUTE ON FUNCTION calculate_service_total TO authenticated, service_role;
|
||||
|
||||
COMMENT ON FUNCTION assign_dual_artists IS 'Automatically assigns primary/secondary artists and room for dual-artist services';
|
||||
COMMENT ON FUNCTION calculate_service_total IS 'Calculates total price including premium fee if enabled';
|
||||
@@ -0,0 +1,76 @@
|
||||
-- ============================================
|
||||
-- FASE 2.3 - ENHANCED AVAILABILITY WITH PRIORITY
|
||||
-- Date: 20260118
|
||||
-- Description: Priority resource assignment + dual count + collision detection
|
||||
-- ============================================
|
||||
|
||||
-- Enhance get_available_resources_with_priority with code priority
|
||||
DROP FUNCTION IF EXISTS get_available_resources_with_priority(UUID, TIMESTAMPTZ, TIMESTAMPTZ) CASCADE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION get_available_resources_with_priority(
|
||||
p_location_id UUID,
|
||||
p_start_time_utc TIMESTAMPTZ,
|
||||
p_end_time_utc TIMESTAMPTZ
|
||||
)
|
||||
RETURNS TABLE (
|
||||
resource_id UUID,
|
||||
resource_name VARCHAR,
|
||||
resource_type resource_type,
|
||||
priority_order INTEGER
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
r.id,
|
||||
r.name,
|
||||
r.type,
|
||||
CASE
|
||||
WHEN r.name LIKE 'mkup%' THEN 1
|
||||
WHEN r.name LIKE 'lshs%' THEN 2
|
||||
WHEN r.name LIKE 'pedi%' THEN 3
|
||||
WHEN r.name LIKE 'mani%' THEN 4
|
||||
ELSE 5
|
||||
END as priority_order
|
||||
FROM resources r
|
||||
WHERE r.location_id = p_location_id
|
||||
AND r.is_active = true
|
||||
AND check_resource_availability(r.id, p_start_time_utc, p_end_time_utc)
|
||||
ORDER BY priority_order, r.name;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- New dual availability function
|
||||
CREATE OR REPLACE FUNCTION get_dual_availability(
|
||||
p_location_id UUID,
|
||||
p_service_id UUID,
|
||||
p_date DATE,
|
||||
p_time_slot_duration_minutes INTEGER DEFAULT 60
|
||||
)
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
v_dual_slots JSONB := '[]'::JSONB;
|
||||
-- ... (similar to get_detailed_availability but count pairs)
|
||||
BEGIN
|
||||
-- Reuse get_detailed_availability logic but filter COUNT >=2
|
||||
-- For simplicity, approximate with staff count >=2
|
||||
SELECT jsonb_agg(row_to_json(t))
|
||||
INTO v_dual_slots
|
||||
FROM (
|
||||
SELECT
|
||||
v_slot_start::TEXT as start_time,
|
||||
(v_slot_start + (p_time_slot_duration_minutes || ' minutes')::INTERVAL)::TEXT as end_time,
|
||||
available_staff_count >= 2 as available,
|
||||
available_staff_count
|
||||
FROM get_detailed_availability(p_location_id, p_service_id, p_date, p_time_slot_duration_minutes) slots
|
||||
WHERE (slots->>'available_staff_count')::INT >= 2
|
||||
) t;
|
||||
|
||||
RETURN v_dual_slots;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
COMMENT ON FUNCTION get_available_resources_with_priority IS 'Available resources ordered by priority: mkup > lshs > pedi > mani';
|
||||
COMMENT ON FUNCTION get_dual_availability IS 'Availability slots where >=2 staff available (for dual services)';
|
||||
|
||||
GRANT EXECUTE ON FUNCTION get_available_resources_with_priority TO authenticated, service_role;
|
||||
GRANT EXECUTE ON FUNCTION get_dual_availability TO authenticated, service_role;
|
||||
Reference in New Issue
Block a user