| | 71 | {{{ |
| | 72 | CREATE OR REPLACE PROCEDURE borrow_book( |
| | 73 | p_user_id INT, |
| | 74 | p_barcode VARCHAR, |
| | 75 | p_librarian_id INT |
| | 76 | ) AS $$ |
| | 77 | DECLARE |
| | 78 | v_copy_id INT; |
| | 79 | BEGIN |
| | 80 | IF NOT EXISTS ( |
| | 81 | SELECT 1 |
| | 82 | FROM member |
| | 83 | WHERE user_id = p_user_id |
| | 84 | ) THEN |
| | 85 | RAISE EXCEPTION 'User is not a member.'; |
| | 86 | END IF; |
| | 87 | |
| | 88 | IF NOT EXISTS ( |
| | 89 | SELECT 1 |
| | 90 | FROM librarian |
| | 91 | WHERE user_id = p_librarian_id |
| | 92 | ) THEN |
| | 93 | RAISE EXCEPTION 'User is not a librarian.'; |
| | 94 | END IF; |
| | 95 | |
| | 96 | IF NOT has_active_membership(p_user_id) THEN |
| | 97 | RAISE EXCEPTION 'Member does not have an active membership.'; |
| | 98 | END IF; |
| | 99 | |
| | 100 | SELECT copy_id |
| | 101 | INTO v_copy_id |
| | 102 | FROM book_copy |
| | 103 | WHERE barcode = p_barcode |
| | 104 | AND status = 'available' |
| | 105 | LIMIT 1 |
| | 106 | FOR UPDATE; |
| | 107 | |
| | 108 | IF v_copy_id IS NULL THEN |
| | 109 | RAISE EXCEPTION 'No available copies for this book.'; |
| | 110 | END IF; |
| | 111 | |
| | 112 | INSERT INTO loan_history ( |
| | 113 | member_user_id, |
| | 114 | copy_id, |
| | 115 | librarian_user_id, |
| | 116 | borrow_date, |
| | 117 | due_date, |
| | 118 | status |
| | 119 | ) |
| | 120 | VALUES ( |
| | 121 | p_user_id, |
| | 122 | v_copy_id, |
| | 123 | p_librarian_id, |
| | 124 | CURRENT_DATE, |
| | 125 | CURRENT_DATE + INTERVAL '14 days', |
| | 126 | 'borrowed' |
| | 127 | ); |
| | 128 | |
| | 129 | UPDATE book_copy |
| | 130 | SET status = 'borrowed' |
| | 131 | WHERE copy_id = v_copy_id; |
| | 132 | |
| | 133 | RAISE NOTICE 'Book borrowed successfully. Copy ID: %', v_copy_id; |
| | 134 | END; |
| | 135 | $$ LANGUAGE plpgsql; |
| | 136 | }}} |
| | 138 | {{{ |
| | 139 | CREATE OR REPLACE PROCEDURE return_book( |
| | 140 | IN p_copy_id INTEGER, |
| | 141 | IN p_is_damaged BOOLEAN |
| | 142 | ) |
| | 143 | LANGUAGE plpgsql |
| | 144 | AS $$ |
| | 145 | DECLARE |
| | 146 | v_loan_id INTEGER; |
| | 147 | BEGIN |
| | 148 | UPDATE loan_history |
| | 149 | SET return_date = CURRENT_DATE, |
| | 150 | status = 'returned' |
| | 151 | WHERE copy_id = p_copy_id |
| | 152 | AND status = 'borrowed' |
| | 153 | RETURNING loan_id INTO v_loan_id; |
| | 154 | |
| | 155 | IF v_loan_id IS NULL THEN |
| | 156 | RAISE NOTICE 'No active borrowed loan found for copy %.', p_copy_id; |
| | 157 | RETURN; |
| | 158 | END IF; |
| | 159 | |
| | 160 | IF p_is_damaged = TRUE THEN |
| | 161 | UPDATE book_copy |
| | 162 | SET status = 'damaged' |
| | 163 | WHERE copy_id = p_copy_id; |
| | 164 | ELSE |
| | 165 | UPDATE book_copy |
| | 166 | SET status = 'available' |
| | 167 | WHERE copy_id = p_copy_id; |
| | 168 | END IF; |
| | 169 | |
| | 170 | RAISE NOTICE 'Book copy % returned successfully.', p_copy_id; |
| | 171 | END; |
| | 172 | $$; |
| | 173 | }}} |
| | 175 | {{{ |
| | 176 | CREATE OR REPLACE PROCEDURE reserve_event( |
| | 177 | p_event_id INT, |
| | 178 | p_member_id INT, |
| | 179 | p_seats INT |
| | 180 | ) |
| | 181 | LANGUAGE plpgsql |
| | 182 | AS $$ |
| | 183 | DECLARE |
| | 184 | v_available_seats INT; |
| | 185 | BEGIN |
| | 186 | SELECT available_seats |
| | 187 | INTO v_available_seats |
| | 188 | FROM event |
| | 189 | WHERE event_id = p_event_id; |
| | 190 | |
| | 191 | IF NOT FOUND THEN |
| | 192 | RAISE EXCEPTION 'Event with ID % does not exist.', p_event_id; |
| | 193 | END IF; |
| | 194 | |
| | 195 | IF v_available_seats < p_seats THEN |
| | 196 | RAISE EXCEPTION 'Not enough available seats.'; |
| | 197 | END IF; |
| | 198 | |
| | 199 | INSERT INTO event_reservation( |
| | 200 | event_id, |
| | 201 | member_user_id, |
| | 202 | seats_reserved, |
| | 203 | status |
| | 204 | ) |
| | 205 | VALUES( |
| | 206 | p_event_id, |
| | 207 | p_member_id, |
| | 208 | p_seats, |
| | 209 | 'reserved' |
| | 210 | ); |
| | 211 | |
| | 212 | UPDATE event |
| | 213 | SET available_seats = available_seats - p_seats |
| | 214 | WHERE event_id = p_event_id; |
| | 215 | END; |
| | 216 | $$; |
| | 217 | }}} |
| | 218 | |
| | 220 | {{{ |
| | 221 | CREATE OR REPLACE PROCEDURE cancel_event_reservation( |
| | 222 | p_event_reservation_id INT |
| | 223 | ) |
| | 224 | LANGUAGE plpgsql |
| | 225 | AS $$ |
| | 226 | DECLARE |
| | 227 | v_event_id INT; |
| | 228 | v_seats INT; |
| | 229 | v_current_status event_reservation_status; |
| | 230 | BEGIN |
| | 231 | SELECT event_id, seats_reserved, status |
| | 232 | INTO v_event_id, v_seats, v_current_status |
| | 233 | FROM event_reservation |
| | 234 | WHERE event_reservation_id = p_event_reservation_id; |
| | 235 | |
| | 236 | IF NOT FOUND THEN |
| | 237 | RAISE EXCEPTION 'Reservation with ID % does not exist!', p_event_reservation_id; |
| | 238 | END IF; |
| | 239 | |
| | 240 | IF v_current_status = 'cancelled' THEN |
| | 241 | RAISE EXCEPTION 'Reservation is already cancelled!'; |
| | 242 | END IF; |
| | 243 | |
| | 244 | UPDATE event_reservation |
| | 245 | SET status = 'cancelled' |
| | 246 | WHERE event_reservation_id = p_event_reservation_id; |
| | 247 | |
| | 248 | UPDATE event |
| | 249 | SET available_seats = available_seats + v_seats |
| | 250 | WHERE event_id = v_event_id; |
| | 251 | END; |
| | 252 | $$; |
| | 253 | }}} |
| | 343 | {{{ |
| | 344 | CREATE OR REPLACE FUNCTION trigger_create_fine_function() |
| | 345 | RETURNS TRIGGER AS $$ |
| | 346 | DECLARE |
| | 347 | v_amount INT; |
| | 348 | BEGIN |
| | 349 | IF NEW.return_date IS NOT NULL |
| | 350 | AND NEW.return_date > NEW.due_date THEN |
| | 351 | |
| | 352 | v_amount := calculate_fine(NEW.loan_id); |
| | 353 | |
| | 354 | IF v_amount > 0 THEN |
| | 355 | INSERT INTO fines( |
| | 356 | loan_id, |
| | 357 | amount, |
| | 358 | payment_due_date, |
| | 359 | status |
| | 360 | ) |
| | 361 | VALUES( |
| | 362 | NEW.loan_id, |
| | 363 | v_amount, |
| | 364 | CURRENT_DATE + INTERVAL '7 days', |
| | 365 | 'unpaid' |
| | 366 | ); |
| | 367 | END IF; |
| | 368 | END IF; |
| | 369 | |
| | 370 | RETURN NEW; |
| | 371 | END; |
| | 372 | $$ LANGUAGE plpgsql; |
| | 373 | }}} |
| | 374 | {{{ |
| | 375 | CREATE TRIGGER trigger_create_fine |
| | 376 | AFTER UPDATE OF return_date ON loan_history |
| | 377 | FOR EACH ROW |
| | 378 | EXECUTE FUNCTION trigger_create_fine_function(); |
| | 379 | }}} |
| | 381 | {{{ |
| | 382 | CREATE OR REPLACE FUNCTION trigger_notification_on_fine_function() |
| | 383 | RETURNS TRIGGER AS $$ |
| | 384 | DECLARE |
| | 385 | v_member_id INT; |
| | 386 | v_notification_id INT; |
| | 387 | BEGIN |
| | 388 | SELECT member_user_id |
| | 389 | INTO v_member_id |
| | 390 | FROM loan_history |
| | 391 | WHERE loan_id = NEW.loan_id; |
| | 392 | |
| | 393 | INSERT INTO notification( |
| | 394 | member_user_id, |
| | 395 | notification_type, |
| | 396 | title, |
| | 397 | message, |
| | 398 | status |
| | 399 | ) |
| | 400 | VALUES( |
| | 401 | v_member_id, |
| | 402 | 'fine', |
| | 403 | 'New Fine Created', |
| | 404 | 'You have received a fine for returning a book late.', |
| | 405 | 'pending' |
| | 406 | ) |
| | 407 | RETURNING notification_id INTO v_notification_id; |
| | 408 | |
| | 409 | INSERT INTO fines_notification( |
| | 410 | fine_id, |
| | 411 | notification_id |
| | 412 | ) |
| | 413 | VALUES( |
| | 414 | NEW.fine_id, |
| | 415 | v_notification_id |
| | 416 | ); |
| | 417 | |
| | 418 | RETURN NEW; |
| | 419 | END; |
| | 420 | $$ LANGUAGE plpgsql; |
| | 421 | |
| | 422 | }}} |
| | 423 | |
| | 424 | {{{ |
| | 425 | CREATE TRIGGER trigger_notification_on_fine |
| | 426 | AFTER INSERT ON fines |
| | 427 | FOR EACH ROW |
| | 428 | EXECUTE FUNCTION trigger_notification_on_fine_function(); |
| | 429 | }}} |
| | 431 | {{{ |
| | 432 | CREATE OR REPLACE FUNCTION trigger_activate_reservation_function() |
| | 433 | RETURNS TRIGGER AS $$ |
| | 434 | DECLARE |
| | 435 | v_barcode VARCHAR(13); |
| | 436 | v_reservation_id INT; |
| | 437 | v_member_id INT; |
| | 438 | BEGIN |
| | 439 | SELECT barcode |
| | 440 | INTO v_barcode |
| | 441 | FROM book_copy |
| | 442 | WHERE copy_id = NEW.copy_id; |
| | 443 | |
| | 444 | SELECT reservation_id, member_user_id |
| | 445 | INTO v_reservation_id, v_member_id |
| | 446 | FROM reservation |
| | 447 | WHERE barcode = v_barcode |
| | 448 | AND status = 'pending' |
| | 449 | ORDER BY reservation_timestamp |
| | 450 | LIMIT 1; |
| | 451 | |
| | 452 | IF v_reservation_id IS NOT NULL THEN |
| | 453 | |
| | 454 | UPDATE reservation |
| | 455 | SET status = 'active', |
| | 456 | notified_at = CURRENT_TIMESTAMP |
| | 457 | WHERE reservation_id = v_reservation_id; |
| | 458 | |
| | 459 | INSERT INTO notification( |
| | 460 | member_user_id, |
| | 461 | notification_type, |
| | 462 | title, |
| | 463 | message, |
| | 464 | status |
| | 465 | ) |
| | 466 | VALUES( |
| | 467 | v_member_id, |
| | 468 | 'reservation', |
| | 469 | 'Reserved Book Available', |
| | 470 | 'The book you reserved is now available.', |
| | 471 | 'pending' |
| | 472 | ); |
| | 473 | |
| | 474 | END IF; |
| | 475 | |
| | 476 | RETURN NEW; |
| | 477 | END; |
| | 478 | $$ LANGUAGE plpgsql; |
| | 479 | }}} |
| | 480 | |
| | 481 | {{{ |
| | 482 | CREATE TRIGGER trigger_activate_reservation |
| | 483 | AFTER UPDATE OF status ON book_copy |
| | 484 | FOR EACH ROW |
| | 485 | WHEN (NEW.status = 'available') |
| | 486 | EXECUTE FUNCTION trigger_activate_reservation_function(); |
| | 487 | }}} |