| 1 | -- =====================================================
|
|---|
| 2 | -- PROCEDURES FOR REZERVO PROJECT
|
|---|
| 3 | -- =====================================================
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 | -- =====================================================
|
|---|
| 7 | -- 1. Book appointment
|
|---|
| 8 | -- Inserts appointment and marks selected slot as unavailable
|
|---|
| 9 | -- =====================================================
|
|---|
| 10 |
|
|---|
| 11 | CREATE OR REPLACE PROCEDURE book_appointment(
|
|---|
| 12 | p_customer_id INT,
|
|---|
| 13 | p_employee_id INT,
|
|---|
| 14 | p_business_id INT,
|
|---|
| 15 | p_service_id INT,
|
|---|
| 16 | p_slot_id INT
|
|---|
| 17 | )
|
|---|
| 18 | LANGUAGE plpgsql
|
|---|
| 19 | AS $$
|
|---|
| 20 | BEGIN
|
|---|
| 21 | IF NOT EXISTS (
|
|---|
| 22 | SELECT 1
|
|---|
| 23 | FROM time_slot
|
|---|
| 24 | WHERE slot_id = p_slot_id
|
|---|
| 25 | AND is_available = TRUE
|
|---|
| 26 | ) THEN
|
|---|
| 27 | RAISE EXCEPTION 'Slot % is not available', p_slot_id;
|
|---|
| 28 | END IF;
|
|---|
| 29 |
|
|---|
| 30 | INSERT INTO appointment (
|
|---|
| 31 | customer_id,
|
|---|
| 32 | employee_id,
|
|---|
| 33 | business_id,
|
|---|
| 34 | service_id,
|
|---|
| 35 | slot_id,
|
|---|
| 36 | status,
|
|---|
| 37 | created_at
|
|---|
| 38 | )
|
|---|
| 39 | VALUES (
|
|---|
| 40 | p_customer_id,
|
|---|
| 41 | p_employee_id,
|
|---|
| 42 | p_business_id,
|
|---|
| 43 | p_service_id,
|
|---|
| 44 | p_slot_id,
|
|---|
| 45 | 'confirmed',
|
|---|
| 46 | CURRENT_TIMESTAMP
|
|---|
| 47 | );
|
|---|
| 48 |
|
|---|
| 49 | UPDATE time_slot
|
|---|
| 50 | SET is_available = FALSE
|
|---|
| 51 | WHERE slot_id = p_slot_id;
|
|---|
| 52 | END;
|
|---|
| 53 | $$;
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 | -- Example:
|
|---|
| 57 | -- CALL book_appointment(100, 1000002, 1, 1, 5);
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 | -- =====================================================
|
|---|
| 62 | -- 2. Cancel appointment
|
|---|
| 63 | -- Updates appointment status, frees slot and inserts cancellation
|
|---|
| 64 | -- =====================================================
|
|---|
| 65 |
|
|---|
| 66 | CREATE OR REPLACE PROCEDURE cancel_appointment(
|
|---|
| 67 | p_appointment_id INT,
|
|---|
| 68 | p_cancelled_by TEXT,
|
|---|
| 69 | p_reason TEXT,
|
|---|
| 70 | p_employee_id INT DEFAULT NULL
|
|---|
| 71 | )
|
|---|
| 72 | LANGUAGE plpgsql
|
|---|
| 73 | AS $$
|
|---|
| 74 | DECLARE
|
|---|
| 75 | v_slot_id INT;
|
|---|
| 76 | BEGIN
|
|---|
| 77 | SELECT slot_id
|
|---|
| 78 | INTO v_slot_id
|
|---|
| 79 | FROM appointment
|
|---|
| 80 | WHERE appointment_id = p_appointment_id;
|
|---|
| 81 |
|
|---|
| 82 | IF v_slot_id IS NULL THEN
|
|---|
| 83 | RAISE EXCEPTION 'Appointment % does not exist', p_appointment_id;
|
|---|
| 84 | END IF;
|
|---|
| 85 |
|
|---|
| 86 | UPDATE appointment
|
|---|
| 87 | SET status = 'cancelled'
|
|---|
| 88 | WHERE appointment_id = p_appointment_id;
|
|---|
| 89 |
|
|---|
| 90 | UPDATE time_slot
|
|---|
| 91 | SET is_available = TRUE
|
|---|
| 92 | WHERE slot_id = v_slot_id;
|
|---|
| 93 |
|
|---|
| 94 | INSERT INTO cancellation (
|
|---|
| 95 | appointment_id,
|
|---|
| 96 | cancelled_by,
|
|---|
| 97 | reason,
|
|---|
| 98 | refund_amount,
|
|---|
| 99 | created_at,
|
|---|
| 100 | employee_id
|
|---|
| 101 | )
|
|---|
| 102 | VALUES (
|
|---|
| 103 | p_appointment_id,
|
|---|
| 104 | p_cancelled_by,
|
|---|
| 105 | p_reason,
|
|---|
| 106 | 0,
|
|---|
| 107 | CURRENT_TIMESTAMP,
|
|---|
| 108 | p_employee_id
|
|---|
| 109 | );
|
|---|
| 110 | END;
|
|---|
| 111 | $$;
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 | -- Example:
|
|---|
| 115 | -- CALL cancel_appointment(100, 'customer', 'Customer cannot attend', NULL);
|
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 | -- =====================================================
|
|---|
| 120 | -- 3. Create review for appointment
|
|---|
| 121 | -- =====================================================
|
|---|
| 122 |
|
|---|
| 123 | CREATE OR REPLACE PROCEDURE create_review_for_appointment(
|
|---|
| 124 | p_appointment_id INT,
|
|---|
| 125 | p_customer_id INT,
|
|---|
| 126 | p_employee_id INT,
|
|---|
| 127 | p_manager_id INT,
|
|---|
| 128 | p_business_id INT,
|
|---|
| 129 | p_rating INT,
|
|---|
| 130 | p_comment TEXT
|
|---|
| 131 | )
|
|---|
| 132 | LANGUAGE plpgsql
|
|---|
| 133 | AS $$
|
|---|
| 134 | BEGIN
|
|---|
| 135 | IF p_rating < 1 OR p_rating > 5 THEN
|
|---|
| 136 | RAISE EXCEPTION 'Rating must be between 1 and 5';
|
|---|
| 137 | END IF;
|
|---|
| 138 |
|
|---|
| 139 | IF NOT EXISTS (
|
|---|
| 140 | SELECT 1
|
|---|
| 141 | FROM appointment
|
|---|
| 142 | WHERE appointment_id = p_appointment_id
|
|---|
| 143 | ) THEN
|
|---|
| 144 | RAISE EXCEPTION 'Appointment % does not exist', p_appointment_id;
|
|---|
| 145 | END IF;
|
|---|
| 146 |
|
|---|
| 147 | INSERT INTO review (
|
|---|
| 148 | appointment_id,
|
|---|
| 149 | customer_id,
|
|---|
| 150 | employee_id,
|
|---|
| 151 | manager_id,
|
|---|
| 152 | business_id,
|
|---|
| 153 | rating,
|
|---|
| 154 | comment,
|
|---|
| 155 | created_at
|
|---|
| 156 | )
|
|---|
| 157 | VALUES (
|
|---|
| 158 | p_appointment_id,
|
|---|
| 159 | p_customer_id,
|
|---|
| 160 | p_employee_id,
|
|---|
| 161 | p_manager_id,
|
|---|
| 162 | p_business_id,
|
|---|
| 163 | p_rating,
|
|---|
| 164 | p_comment,
|
|---|
| 165 | CURRENT_TIMESTAMP
|
|---|
| 166 | );
|
|---|
| 167 | END;
|
|---|
| 168 | $$;
|
|---|
| 169 |
|
|---|
| 170 |
|
|---|
| 171 | -- Example:
|
|---|
| 172 | -- CALL create_review_for_appointment(100, 100, 1000002, 1, 1, 5, 'Great service');
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 | -- =====================================================
|
|---|
| 177 | -- 4. Request reschedule
|
|---|
| 178 | -- Creates pending reschedule request
|
|---|
| 179 | -- =====================================================
|
|---|
| 180 |
|
|---|
| 181 | CREATE OR REPLACE PROCEDURE request_reschedule(
|
|---|
| 182 | p_appointment_id INT,
|
|---|
| 183 | p_old_slot_id INT,
|
|---|
| 184 | p_new_slot_id INT,
|
|---|
| 185 | p_manager_id INT,
|
|---|
| 186 | p_employee_id INT,
|
|---|
| 187 | p_reason TEXT
|
|---|
| 188 | )
|
|---|
| 189 | LANGUAGE plpgsql
|
|---|
| 190 | AS $$
|
|---|
| 191 | BEGIN
|
|---|
| 192 | IF NOT EXISTS (
|
|---|
| 193 | SELECT 1
|
|---|
| 194 | FROM appointment
|
|---|
| 195 | WHERE appointment_id = p_appointment_id
|
|---|
| 196 | ) THEN
|
|---|
| 197 | RAISE EXCEPTION 'Appointment % does not exist', p_appointment_id;
|
|---|
| 198 | END IF;
|
|---|
| 199 |
|
|---|
| 200 | IF NOT EXISTS (
|
|---|
| 201 | SELECT 1
|
|---|
| 202 | FROM time_slot
|
|---|
| 203 | WHERE slot_id = p_new_slot_id
|
|---|
| 204 | AND is_available = TRUE
|
|---|
| 205 | ) THEN
|
|---|
| 206 | RAISE EXCEPTION 'New slot % is not available', p_new_slot_id;
|
|---|
| 207 | END IF;
|
|---|
| 208 |
|
|---|
| 209 | INSERT INTO reschedule_request (
|
|---|
| 210 | appointment_id,
|
|---|
| 211 | old_slot_id,
|
|---|
| 212 | new_slot_id,
|
|---|
| 213 | manager_id,
|
|---|
| 214 | employee_id,
|
|---|
| 215 | status,
|
|---|
| 216 | reason,
|
|---|
| 217 | created_at
|
|---|
| 218 | )
|
|---|
| 219 | VALUES (
|
|---|
| 220 | p_appointment_id,
|
|---|
| 221 | p_old_slot_id,
|
|---|
| 222 | p_new_slot_id,
|
|---|
| 223 | p_manager_id,
|
|---|
| 224 | p_employee_id,
|
|---|
| 225 | 'pending',
|
|---|
| 226 | p_reason,
|
|---|
| 227 | CURRENT_TIMESTAMP
|
|---|
| 228 | );
|
|---|
| 229 | END;
|
|---|
| 230 | $$;
|
|---|
| 231 |
|
|---|
| 232 |
|
|---|
| 233 | -- Example:
|
|---|
| 234 | -- CALL request_reschedule(100, 5, 6, 1, 1000002, 'Customer requested another time');
|
|---|
| 235 |
|
|---|
| 236 |
|
|---|
| 237 |
|
|---|
| 238 | -- =====================================================
|
|---|
| 239 | -- 5. Approve reschedule request
|
|---|
| 240 | -- Updates appointment slot and request status
|
|---|
| 241 | -- =====================================================
|
|---|
| 242 |
|
|---|
| 243 | CREATE OR REPLACE PROCEDURE approve_reschedule_request(
|
|---|
| 244 | p_request_id INT
|
|---|
| 245 | )
|
|---|
| 246 | LANGUAGE plpgsql
|
|---|
| 247 | AS $$
|
|---|
| 248 | DECLARE
|
|---|
| 249 | v_appointment_id INT;
|
|---|
| 250 | v_old_slot_id INT;
|
|---|
| 251 | v_new_slot_id INT;
|
|---|
| 252 | BEGIN
|
|---|
| 253 | SELECT appointment_id, old_slot_id, new_slot_id
|
|---|
| 254 | INTO v_appointment_id, v_old_slot_id, v_new_slot_id
|
|---|
| 255 | FROM reschedule_request
|
|---|
| 256 | WHERE request_id = p_request_id
|
|---|
| 257 | AND status = 'pending';
|
|---|
| 258 |
|
|---|
| 259 | IF v_appointment_id IS NULL THEN
|
|---|
| 260 | RAISE EXCEPTION 'Pending reschedule request % does not exist', p_request_id;
|
|---|
| 261 | END IF;
|
|---|
| 262 |
|
|---|
| 263 | IF NOT EXISTS (
|
|---|
| 264 | SELECT 1
|
|---|
| 265 | FROM time_slot
|
|---|
| 266 | WHERE slot_id = v_new_slot_id
|
|---|
| 267 | AND is_available = TRUE
|
|---|
| 268 | ) THEN
|
|---|
| 269 | RAISE EXCEPTION 'New slot % is not available', v_new_slot_id;
|
|---|
| 270 | END IF;
|
|---|
| 271 |
|
|---|
| 272 | UPDATE appointment
|
|---|
| 273 | SET slot_id = v_new_slot_id
|
|---|
| 274 | WHERE appointment_id = v_appointment_id;
|
|---|
| 275 |
|
|---|
| 276 | UPDATE time_slot
|
|---|
| 277 | SET is_available = TRUE
|
|---|
| 278 | WHERE slot_id = v_old_slot_id;
|
|---|
| 279 |
|
|---|
| 280 | UPDATE time_slot
|
|---|
| 281 | SET is_available = FALSE
|
|---|
| 282 | WHERE slot_id = v_new_slot_id;
|
|---|
| 283 |
|
|---|
| 284 | UPDATE reschedule_request
|
|---|
| 285 | SET status = 'approved'
|
|---|
| 286 | WHERE request_id = p_request_id;
|
|---|
| 287 | END;
|
|---|
| 288 | $$;
|
|---|
| 289 |
|
|---|
| 290 |
|
|---|
| 291 | -- Example:
|
|---|
| 292 | -- CALL approve_reschedule_request(1);
|
|---|
| 293 |
|
|---|
| 294 |
|
|---|
| 295 |
|
|---|
| 296 | -- =====================================================
|
|---|
| 297 | -- 6. Reject reschedule request
|
|---|
| 298 | -- =====================================================
|
|---|
| 299 |
|
|---|
| 300 | CREATE OR REPLACE PROCEDURE reject_reschedule_request(
|
|---|
| 301 | p_request_id INT
|
|---|
| 302 | )
|
|---|
| 303 | LANGUAGE plpgsql
|
|---|
| 304 | AS $$
|
|---|
| 305 | BEGIN
|
|---|
| 306 | UPDATE reschedule_request
|
|---|
| 307 | SET status = 'rejected'
|
|---|
| 308 | WHERE request_id = p_request_id
|
|---|
| 309 | AND status = 'pending';
|
|---|
| 310 |
|
|---|
| 311 | IF NOT FOUND THEN
|
|---|
| 312 | RAISE EXCEPTION 'Pending reschedule request % does not exist', p_request_id;
|
|---|
| 313 | END IF;
|
|---|
| 314 | END;
|
|---|
| 315 | $$;
|
|---|
| 316 |
|
|---|
| 317 |
|
|---|
| 318 | -- Example:
|
|---|
| 319 | -- CALL reject_reschedule_request(1);
|
|---|
| 320 |
|
|---|
| 321 | CREATE OR REPLACE PROCEDURE make_employee_slots_unavailable(
|
|---|
| 322 | p_employee_id INT,
|
|---|
| 323 | p_date_from DATE,
|
|---|
| 324 | p_date_to DATE,
|
|---|
| 325 | p_reason TEXT DEFAULT 'Employee unavailable',
|
|---|
| 326 | p_business_id INT DEFAULT NULL
|
|---|
| 327 | )
|
|---|
| 328 | LANGUAGE plpgsql
|
|---|
| 329 | AS $$
|
|---|
| 330 | DECLARE
|
|---|
| 331 | v_booked_count INT;
|
|---|
| 332 | v_updated_count INT;
|
|---|
| 333 | BEGIN
|
|---|
| 334 | IF p_date_from > p_date_to THEN
|
|---|
| 335 | RAISE EXCEPTION 'date_from cannot be after date_to';
|
|---|
| 336 | END IF;
|
|---|
| 337 |
|
|---|
| 338 | SELECT COUNT(*)
|
|---|
| 339 | INTO v_booked_count
|
|---|
| 340 | FROM time_slot ts
|
|---|
| 341 | JOIN appointment a ON a.slot_id = ts.slot_id
|
|---|
| 342 | WHERE ts.employee_id = p_employee_id
|
|---|
| 343 | AND ts.date BETWEEN p_date_from AND p_date_to
|
|---|
| 344 | AND a.status <> 'cancelled'
|
|---|
| 345 | AND (
|
|---|
| 346 | p_business_id IS NULL
|
|---|
| 347 | OR ts.business_id = p_business_id
|
|---|
| 348 | );
|
|---|
| 349 |
|
|---|
| 350 | IF v_booked_count > 0 THEN
|
|---|
| 351 | RAISE EXCEPTION
|
|---|
| 352 | 'Cannot make slots unavailable. Employee % has % booked slots between % and %.',
|
|---|
| 353 | p_employee_id, v_booked_count, p_date_from, p_date_to;
|
|---|
| 354 | END IF;
|
|---|
| 355 |
|
|---|
| 356 | UPDATE time_slot
|
|---|
| 357 | SET is_available = FALSE
|
|---|
| 358 | WHERE employee_id = p_employee_id
|
|---|
| 359 | AND date BETWEEN p_date_from AND p_date_to
|
|---|
| 360 | AND (
|
|---|
| 361 | p_business_id IS NULL
|
|---|
| 362 | OR business_id = p_business_id
|
|---|
| 363 | );
|
|---|
| 364 |
|
|---|
| 365 | GET DIAGNOSTICS v_updated_count = ROW_COUNT;
|
|---|
| 366 |
|
|---|
| 367 | RAISE NOTICE
|
|---|
| 368 | 'Marked % slots as unavailable for employee % between % and %. Reason: %',
|
|---|
| 369 | v_updated_count, p_employee_id, p_date_from, p_date_to, p_reason;
|
|---|
| 370 | END;
|
|---|
| 371 | $$;
|
|---|
| 372 |
|
|---|
| 373 | CALL make_employee_slots_unavailable(
|
|---|
| 374 | 1000002,
|
|---|
| 375 | '2026-05-01',
|
|---|
| 376 | '2026-05-07',
|
|---|
| 377 | 'Vacation'
|
|---|
| 378 | );
|
|---|
| 379 |
|
|---|
| 380 | SELECT ts.slot_id, ts.employee_id, ts.date, a.appointment_id, a.status
|
|---|
| 381 | FROM time_slot ts
|
|---|
| 382 | JOIN appointment a ON a.slot_id = ts.slot_id
|
|---|
| 383 | WHERE ts.employee_id = 1000002
|
|---|
| 384 | AND ts.date BETWEEN '2026-05-01' AND '2026-05-07'
|
|---|
| 385 | AND a.status <> 'cancelled'; |
|---|