Files
acmcc/supabase/migrations/20260429111544_44231b22-fe22-4097-af69-10008db2b16b.sql
2026-06-01 20:19:26 -04:00

205 lines
6.3 KiB
PL/PgSQL

-- Flag rentals as owner-occupied (owner using their own lot)
ALTER TABLE public.rv_boat_lot_rentals
ADD COLUMN IF NOT EXISTS is_owner BOOLEAN NOT NULL DEFAULT false;
-- Helpers for rv_owner role (owner_ids and association_ids the user has access to)
CREATE OR REPLACE FUNCTION public.get_rv_owner_owner_ids(_user_id uuid DEFAULT auth.uid())
RETURNS SETOF uuid
LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public
AS $$
SELECT DISTINCT r.owner_id
FROM public.rv_boat_lot_rentals r
WHERE r.user_id = _user_id
AND r.is_owner = true
AND r.owner_id IS NOT NULL
AND r.status = 'active'
$$;
CREATE OR REPLACE FUNCTION public.get_rv_owner_association_ids(_user_id uuid DEFAULT auth.uid())
RETURNS SETOF uuid
LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public
AS $$
SELECT DISTINCT r.association_id
FROM public.rv_boat_lot_rentals r
WHERE r.user_id = _user_id
AND r.is_owner = true
AND r.status = 'active'
$$;
-- RV owners can read their own rental row (covers the is_owner = true rows)
CREATE POLICY "RV owners can view their own rental"
ON public.rv_boat_lot_rentals
FOR SELECT
TO authenticated
USING (user_id = auth.uid() AND is_owner = true);
-- RV owners can read their lot
CREATE POLICY "RV owners can view their lot"
ON public.rv_boat_lots
FOR SELECT
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.rv_boat_lot_rentals r
WHERE r.lot_id = rv_boat_lots.id
AND r.user_id = auth.uid()
AND r.is_owner = true
AND r.status = 'active'
)
);
-- RV owners can read their association
CREATE POLICY "RV owners can view their association"
ON public.associations
FOR SELECT
TO authenticated
USING (id IN (SELECT public.get_rv_owner_association_ids(auth.uid())));
-- RV owners can read the linked owner ledger
CREATE POLICY "RV owners can view linked owner ledger"
ON public.owner_ledger_entries
FOR SELECT
TO authenticated
USING (owner_id IN (SELECT public.get_rv_owner_owner_ids(auth.uid())));
-- RV owners can read the linked owner record
CREATE POLICY "RV owners can view linked owner"
ON public.owners
FOR SELECT
TO authenticated
USING (id IN (SELECT public.get_rv_owner_owner_ids(auth.uid())));
-- Intent to Vacate
CREATE TABLE IF NOT EXISTS public.rv_intent_to_vacate (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
rental_id UUID NOT NULL REFERENCES public.rv_boat_lot_rentals(id) ON DELETE CASCADE,
association_id UUID NOT NULL REFERENCES public.associations(id) ON DELETE CASCADE,
lot_id UUID REFERENCES public.rv_boat_lots(id) ON DELETE SET NULL,
owner_id UUID REFERENCES public.owners(id) ON DELETE SET NULL,
submitted_by_user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
vacate_date DATE NOT NULL,
notes TEXT,
status TEXT NOT NULL DEFAULT 'pending',
acknowledged_at TIMESTAMP WITH TIME ZONE,
acknowledged_by_user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS rv_itv_rental_idx ON public.rv_intent_to_vacate(rental_id);
CREATE INDEX IF NOT EXISTS rv_itv_assoc_idx ON public.rv_intent_to_vacate(association_id);
CREATE INDEX IF NOT EXISTS rv_itv_user_idx ON public.rv_intent_to_vacate(submitted_by_user_id);
ALTER TABLE public.rv_intent_to_vacate ENABLE ROW LEVEL SECURITY;
CREATE TRIGGER update_rv_intent_to_vacate_updated_at
BEFORE UPDATE ON public.rv_intent_to_vacate
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at_column();
-- RLS: RV owners manage their own intents
CREATE POLICY "RV owners can view their intents"
ON public.rv_intent_to_vacate
FOR SELECT
TO authenticated
USING (submitted_by_user_id = auth.uid());
CREATE POLICY "RV owners can submit intents for their rental"
ON public.rv_intent_to_vacate
FOR INSERT
TO authenticated
WITH CHECK (
submitted_by_user_id = auth.uid()
AND EXISTS (
SELECT 1 FROM public.rv_boat_lot_rentals r
WHERE r.id = rv_intent_to_vacate.rental_id
AND r.user_id = auth.uid()
AND r.is_owner = true
AND r.status = 'active'
)
);
CREATE POLICY "RV owners can cancel their pending intents"
ON public.rv_intent_to_vacate
FOR UPDATE
TO authenticated
USING (submitted_by_user_id = auth.uid() AND status = 'pending')
WITH CHECK (submitted_by_user_id = auth.uid());
-- Staff can manage all
CREATE POLICY "Staff can view all intents"
ON public.rv_intent_to_vacate
FOR SELECT
TO authenticated
USING (
public.has_role(auth.uid(), 'admin'::public.app_role)
OR public.has_role(auth.uid(), 'manager'::public.app_role)
);
CREATE POLICY "Staff can update all intents"
ON public.rv_intent_to_vacate
FOR UPDATE
TO authenticated
USING (
public.has_role(auth.uid(), 'admin'::public.app_role)
OR public.has_role(auth.uid(), 'manager'::public.app_role)
)
WITH CHECK (
public.has_role(auth.uid(), 'admin'::public.app_role)
OR public.has_role(auth.uid(), 'manager'::public.app_role)
);
CREATE POLICY "Staff can delete intents"
ON public.rv_intent_to_vacate
FOR DELETE
TO authenticated
USING (
public.has_role(auth.uid(), 'admin'::public.app_role)
OR public.has_role(auth.uid(), 'manager'::public.app_role)
);
-- Notify staff when an Intent to Vacate is submitted
CREATE OR REPLACE FUNCTION public.notify_staff_on_rv_intent_to_vacate()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
staff_user record;
v_renter text;
v_message text;
BEGIN
SELECT r.renter_name INTO v_renter
FROM public.rv_boat_lot_rentals r WHERE r.id = NEW.rental_id;
v_message := COALESCE(v_renter, 'An RV/Boat lot owner')
|| ' submitted an Intent to Vacate effective '
|| to_char(NEW.vacate_date, 'Mon DD, YYYY');
FOR staff_user IN
SELECT DISTINCT user_id FROM public.user_roles
WHERE role IN ('admin'::public.app_role, 'manager'::public.app_role)
AND user_id IS NOT NULL
LOOP
INSERT INTO public.in_app_notifications
(user_id, type, title, message, related_item_id, related_item_type, link)
VALUES (
staff_user.user_id,
'rv_intent_to_vacate',
'RV/Boat Lot — Intent to Vacate',
v_message,
NEW.id,
'rv_intent_to_vacate',
'/dashboard/rv-boat-lots'
);
END LOOP;
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_notify_staff_on_rv_intent_to_vacate
AFTER INSERT ON public.rv_intent_to_vacate
FOR EACH ROW
EXECUTE FUNCTION public.notify_staff_on_rv_intent_to_vacate();