-- Attendance log book for check-in / check-out CREATE TABLE IF NOT EXISTS attendance_logs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES auth.users(id), duty_schedule_id uuid NOT NULL REFERENCES duty_schedules(id), check_in_at timestamptz NOT NULL DEFAULT now(), check_in_lat double precision NOT NULL, check_in_lng double precision NOT NULL, check_out_at timestamptz, check_out_lat double precision, check_out_lng double precision ); CREATE INDEX idx_attendance_logs_user ON attendance_logs (user_id, check_in_at DESC); -- Enable realtime ALTER PUBLICATION supabase_realtime ADD TABLE attendance_logs; -- RLS ALTER TABLE attendance_logs ENABLE ROW LEVEL SECURITY; -- Users can see their own logs; admin/dispatcher/it_staff can see all CREATE POLICY "attendance_logs_select" ON attendance_logs FOR SELECT TO authenticated USING ( user_id = auth.uid() OR EXISTS ( SELECT 1 FROM profiles p WHERE p.id = auth.uid() AND p.role IN ('admin', 'dispatcher', 'it_staff') ) ); -- Users can insert their own logs CREATE POLICY "attendance_logs_insert" ON attendance_logs FOR INSERT TO authenticated WITH CHECK (user_id = auth.uid()); -- Users can update their own logs (for check-out) CREATE POLICY "attendance_logs_update" ON attendance_logs FOR UPDATE TO authenticated USING (user_id = auth.uid()); -- RPC: attendance_check_in — validates geofence server-side, checks 2-hour window CREATE OR REPLACE FUNCTION public.attendance_check_in( p_duty_id uuid, p_lat double precision, p_lng double precision ) RETURNS uuid LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_schedule duty_schedules%ROWTYPE; v_geofence jsonb; v_log_id uuid; v_now timestamptz := now(); v_status text; BEGIN -- Fetch the duty schedule SELECT * INTO v_schedule FROM duty_schedules WHERE id = p_duty_id; IF NOT FOUND THEN RAISE EXCEPTION 'Duty schedule not found'; END IF; IF v_schedule.user_id != auth.uid() THEN RAISE EXCEPTION 'Not your duty schedule'; END IF; -- Check 2-hour window IF v_now < (v_schedule.start_time - interval '2 hours') THEN RAISE EXCEPTION 'Too early to check in (2-hour window)'; END IF; IF v_now > v_schedule.end_time THEN RAISE EXCEPTION 'Duty has already ended'; END IF; -- Determine status IF v_now <= v_schedule.start_time THEN v_status := 'arrival'; ELSE v_status := 'late'; END IF; -- Insert attendance log INSERT INTO attendance_logs (user_id, duty_schedule_id, check_in_at, check_in_lat, check_in_lng) VALUES (auth.uid(), p_duty_id, v_now, p_lat, p_lng) RETURNING id INTO v_log_id; -- Update duty schedule UPDATE duty_schedules SET check_in_at = v_now, check_in_location = jsonb_build_object('latitude', p_lat, 'longitude', p_lng), status = v_status WHERE id = p_duty_id; RETURN v_log_id; END; $$; -- RPC: attendance_check_out CREATE OR REPLACE FUNCTION public.attendance_check_out( p_attendance_id uuid, p_lat double precision, p_lng double precision ) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_log attendance_logs%ROWTYPE; BEGIN SELECT * INTO v_log FROM attendance_logs WHERE id = p_attendance_id; IF NOT FOUND THEN RAISE EXCEPTION 'Attendance log not found'; END IF; IF v_log.user_id != auth.uid() THEN RAISE EXCEPTION 'Not your attendance log'; END IF; IF v_log.check_out_at IS NOT NULL THEN RAISE EXCEPTION 'Already checked out'; END IF; UPDATE attendance_logs SET check_out_at = now(), check_out_lat = p_lat, check_out_lng = p_lng WHERE id = p_attendance_id; END; $$; -- Also create the missing duty_check_in RPC that workforce_screen.dart calls CREATE OR REPLACE FUNCTION public.duty_check_in( p_duty_id uuid, p_lat double precision, p_lng double precision ) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_schedule duty_schedules%ROWTYPE; v_now timestamptz := now(); v_status text; BEGIN SELECT * INTO v_schedule FROM duty_schedules WHERE id = p_duty_id; IF NOT FOUND THEN RAISE EXCEPTION 'Duty schedule not found'; END IF; IF v_schedule.user_id != auth.uid() THEN RAISE EXCEPTION 'Not your duty schedule'; END IF; IF v_now <= v_schedule.start_time THEN v_status := 'arrival'; ELSE v_status := 'late'; END IF; UPDATE duty_schedules SET check_in_at = v_now, check_in_location = jsonb_build_object('latitude', p_lat, 'longitude', p_lng), status = v_status WHERE id = p_duty_id; -- Also create an attendance log entry INSERT INTO attendance_logs (user_id, duty_schedule_id, check_in_at, check_in_lat, check_in_lng) VALUES (auth.uid(), p_duty_id, v_now, p_lat, p_lng); RETURN v_status; END; $$;