source: petify-backend/src/main/resources/db/migration/R__appointments_rules_and_views.sql

Last change on this file was 92e7c7a, checked in by veronika-ils <ilioskaveronika@…>, 8 hours ago

Petify fullstack project

  • Property mode set to 100644
File size: 4.2 KB
Line 
1CREATE OR REPLACE FUNCTION petify_trg_appointments_enforce()
2 RETURNS trigger
3 LANGUAGE plpgsql
4AS $$
5DECLARE
6 v_animal_owner bigint;
7BEGIN
8 SELECT owner_id
9 INTO v_animal_owner
10 FROM animals
11 WHERE animal_id = NEW.animal_id;
12
13 IF v_animal_owner IS NULL THEN
14 RAISE EXCEPTION 'Animal % not found for appointment', NEW.animal_id;
15 END IF;
16
17 IF NEW.responsible_owner_id <> v_animal_owner THEN
18 RAISE EXCEPTION
19 'Appointment responsible_owner_id (%) must match animals.owner_id (%) for animal %',
20 NEW.responsible_owner_id, v_animal_owner, NEW.animal_id;
21 END IF;
22
23 IF EXISTS (
24 SELECT 1
25 FROM clinic_unavailable_slots cus
26 WHERE cus.clinic_id = NEW.clinic_id
27 AND cus.date_time = NEW.date_time
28 ) THEN
29 RAISE EXCEPTION
30 'Clinic % is unavailable at %',
31 NEW.clinic_id, NEW.date_time;
32 END IF;
33
34 IF NEW.status = 'CONFIRMED' AND NEW.date_time < now() THEN
35 RAISE EXCEPTION
36 'Cannot CONFIRM an appointment in the past (date_time=%)',
37 NEW.date_time;
38 END IF;
39
40 IF NEW.status = 'DONE' AND NEW.date_time > now() THEN
41 RAISE EXCEPTION
42 'Cannot mark DONE for an appointment that is in the future (date_time=%)',
43 NEW.date_time;
44 END IF;
45
46 RETURN NEW;
47END;
48$$;
49
50
51-- Prevent overlapping 30-minute appointments for the same clinic, owner, or animal
52CREATE OR REPLACE FUNCTION petify_trg_appointments_no_overlap()
53 RETURNS trigger
54 LANGUAGE plpgsql
55AS $$
56BEGIN
57 IF NEW.status NOT IN ('CONFIRMED', 'DONE') THEN
58 RETURN NEW;
59 END IF;
60
61 IF EXISTS (
62 SELECT 1
63 FROM appointments a
64 WHERE a.clinic_id = NEW.clinic_id
65 AND a.status IN ('CONFIRMED', 'DONE')
66 AND tsrange(a.date_time, a.date_time + interval '30 minutes', '[)')
67 && tsrange(NEW.date_time, NEW.date_time + interval '30 minutes', '[)')
68 AND (TG_OP <> 'UPDATE' OR a.appointment_id <> NEW.appointment_id)
69 ) THEN
70 RAISE EXCEPTION
71 'Overlapping appointment for clinic % at %',
72 NEW.clinic_id, NEW.date_time;
73 END IF;
74
75 IF EXISTS (
76 SELECT 1
77 FROM appointments a
78 WHERE a.responsible_owner_id = NEW.responsible_owner_id
79 AND a.status IN ('CONFIRMED', 'DONE')
80 AND tsrange(a.date_time, a.date_time + interval '30 minutes', '[)')
81 && tsrange(NEW.date_time, NEW.date_time + interval '30 minutes', '[)')
82 AND (TG_OP <> 'UPDATE' OR a.appointment_id <> NEW.appointment_id)
83 ) THEN
84 RAISE EXCEPTION
85 'Overlapping appointment for owner % at %',
86 NEW.responsible_owner_id, NEW.date_time;
87 END IF;
88
89 IF EXISTS (
90 SELECT 1
91 FROM appointments a
92 WHERE a.animal_id = NEW.animal_id
93 AND a.status IN ('CONFIRMED', 'DONE')
94 AND tsrange(a.date_time, a.date_time + interval '30 minutes', '[)')
95 && tsrange(NEW.date_time, NEW.date_time + interval '30 minutes', '[)')
96 AND (TG_OP <> 'UPDATE' OR a.appointment_id <> NEW.appointment_id)
97 ) THEN
98 RAISE EXCEPTION
99 'Overlapping appointment for animal % at %',
100 NEW.animal_id, NEW.date_time;
101 END IF;
102
103 RETURN NEW;
104END;
105$$;
106
107
108DROP TRIGGER IF EXISTS trg_appointments_enforce ON appointments;
109
110CREATE TRIGGER trg_appointments_enforce
111 BEFORE INSERT OR UPDATE
112 ON appointments
113 FOR EACH ROW
114EXECUTE FUNCTION petify_trg_appointments_enforce();
115
116
117DROP TRIGGER IF EXISTS trg_appointments_no_overlap ON appointments;
118
119CREATE TRIGGER trg_appointments_no_overlap
120 BEFORE INSERT OR UPDATE
121 ON appointments
122 FOR EACH ROW
123EXECUTE FUNCTION petify_trg_appointments_no_overlap();
124
125CREATE OR REPLACE VIEW v_clinic_appointments_monthly AS
126SELECT
127 clinic_id,
128 date_trunc('month', date_time) AS month,
129 COUNT(*) FILTER (WHERE status = 'CONFIRMED') AS confirmed_cnt,
130 COUNT(*) FILTER (WHERE status = 'DONE') AS done_cnt,
131 COUNT(*) FILTER (WHERE status = 'NO_SHOW') AS no_show_cnt,
132 COUNT(*) FILTER (WHERE status = 'CANCELLED') AS cancelled_cnt,
133 COUNT(*) AS total_cnt
134FROM appointments
135GROUP BY clinic_id, date_trunc('month', date_time)
136ORDER BY month DESC;
Note: See TracBrowser for help on using the repository browser.