Changes between Version 28 and Version 29 of DatabaseProgramming
- Timestamp:
- 06/30/26 22:04:06 (5 days ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
DatabaseProgramming
v28 v29 11 11 12 12 CREATE OR REPLACE FUNCTION get_current_price(p_ticket_id BIGINT) 13 RETURNS DECIMAL(10,2) 14 LANGUAGE plpgsql 15 AS $$ 16 DECLARE 17 v_base_price DECIMAL(10,2); 18 v_happening_id BIGINT; 13 RETURNS DECIMAL(10,2) LANGUAGE plpgsql AS $$ 14 DECLARE 15 v_base_price DECIMAL(10,2); 16 v_happening_id BIGINT; 19 17 v_discount_percent INT; 20 v_final_price DECIMAL(10,2); 21 BEGIN 22 SELECT base_price, event_happening_id INTO v_base_price, v_happening_id 23 FROM "Ticket" WHERE ticket_id = p_ticket_id; 24 25 SELECT price_discount_percent INTO v_discount_percent 18 v_final_price DECIMAL(10,2); 19 BEGIN 20 SELECT base_price, event_happening_id 21 INTO v_base_price, v_happening_id 22 FROM "Ticket" 23 WHERE ticket_id = p_ticket_id; 24 25 SELECT price_discount_percent 26 INTO v_discount_percent 26 27 FROM "Event_Period" 27 28 WHERE event_happening_id = v_happening_id … … 29 30 LIMIT 1; 30 31 31 IF FOUND THEN 32 v_final_price := v_base_price * (1 - (v_discount_percent / 100.0)); 33 ELSE 34 v_final_price := v_base_price; 35 END IF; 32 v_final_price := COALESCE(v_base_price * (1 - (v_discount_percent / 100.0)), v_base_price); 36 33 37 34 RETURN v_final_price; … … 49 46 50 47 CREATE OR REPLACE FUNCTION calculate_refund_amount(p_order_item_id BIGINT) 51 RETURNS DECIMAL(10,2) 52 LANGUAGE plpgsql 53 AS $$ 48 RETURNS DECIMAL(10,2) LANGUAGE plpgsql AS $$ 54 49 DECLARE 55 50 v_original_price DECIMAL(10,2); 56 v_refund_amount DECIMAL(10,2); 57 BEGIN 58 SELECT item_priceINTO v_original_price51 BEGIN 52 SELECT item_price 53 INTO v_original_price 59 54 FROM "Ticket_Order_Item" 60 55 WHERE order_item_id = p_order_item_id; … … 64 59 END IF; 65 60 66 v_refund_amount := v_original_price * 0.85; 67 68 RETURN v_refund_amount; 61 RETURN v_original_price * 0.85; 69 62 END; 70 63 $$; … … 81 74 {{{ 82 75 83 CREATE OR REPLACE PROCEDURE buy_ticket( 84 p_user_id BIGINT, 85 p_ticket_id BIGINT 86 ) 87 LANGUAGE plpgsql 88 AS $$ 89 DECLARE 90 v_price DECIMAL(10,2); 91 v_qr_code TEXT; 92 v_order_id BIGINT; 93 BEGIN 76 CREATE OR REPLACE PROCEDURE buy_ticket(p_user_id BIGINT, p_ticket_id BIGINT) 77 LANGUAGE plpgsql AS $$ 78 DECLARE 79 v_price DECIMAL(10,2); 80 v_qr_code TEXT; 81 v_order_id BIGINT; 82 v_active BOOLEAN; 83 BEGIN 84 SELECT is_active INTO v_active FROM "User" WHERE user_id = p_user_id; 85 IF v_active IS NOT TRUE THEN 86 RAISE EXCEPTION 'Access Denied: User is inactive.'; 87 END IF; 88 94 89 v_price := get_current_price(p_ticket_id); 95 90 v_qr_code := 'QR-' || p_ticket_id || '-' || p_user_id || '-' || EXTRACT(EPOCH FROM NOW()); … … 103 98 104 99 UPDATE "Ticket" SET is_available = FALSE WHERE ticket_id = p_ticket_id; 105 106 COMMIT;107 100 END; 108 101 $$; … … 117 110 {{{ 118 111 119 CREATE OR REPLACE PROCEDURE cancel_ticket( 120 p_order_item_id BIGINT, 121 p_reason TEXT 122 ) 123 LANGUAGE plpgsql 124 AS $$ 112 CREATE OR REPLACE PROCEDURE cancel_ticket(p_order_item_id BIGINT, p_reason TEXT) 113 LANGUAGE plpgsql AS $$ 125 114 DECLARE 126 115 v_refund_amount DECIMAL(10,2); 127 v_ticket_id BIGINT;128 v_order_id BIGINT;129 v_refund_id BIGINT;116 v_ticket_id BIGINT; 117 v_order_id BIGINT; 118 v_refund_id BIGINT; 130 119 BEGIN 131 120 v_refund_amount := calculate_refund_amount(p_order_item_id); 132 121 133 SELECT order_id, ticket_id INTO v_order_id, v_ticket_id 122 SELECT order_id, ticket_id 123 INTO v_order_id, v_ticket_id 134 124 FROM "Ticket_Order_Item" 135 125 WHERE order_item_id = p_order_item_id; … … 143 133 144 134 UPDATE "Ticket" SET is_available = TRUE WHERE ticket_id = v_ticket_id; 145 146 RAISE NOTICE 'Ticket item cancelled successfully. Refund amount: %', v_refund_amount;147 135 END; 148 136 $$; … … 165 153 p_base_price DECIMAL(10,2) 166 154 ) 167 LANGUAGE plpgsql 168 AS $$ 155 LANGUAGE plpgsql AS $$ 169 156 DECLARE 170 157 v_happening_id BIGINT; 171 BEGIN 172 IF NOT EXISTS ( 173 SELECT 1 FROM "Admin" WHERE user_id = p_admin_id 174 ) THEN 175 RAISE EXCEPTION 'Access Denied: Only administrators can schedule a new event happening.'; 158 v_active BOOLEAN; 159 BEGIN 160 IF NOT EXISTS (SELECT 1 FROM "Admin" WHERE user_id = p_admin_id) THEN 161 RAISE EXCEPTION 'Access Denied: Not an admin.'; 162 END IF; 163 164 SELECT is_active INTO v_active FROM "Event" WHERE event_id = p_event_id; 165 IF v_active IS NOT TRUE THEN 166 RAISE EXCEPTION 'Cannot schedule for an inactive event.'; 176 167 END IF; 177 168 178 169 INSERT INTO "Event_Happening" (event_id, event_time, venue_id, duration_minutes, organizers) 179 VALUES (p_event_id, p_time, p_venue_id, p_duration_minutes, 'System Generated Organizer')170 VALUES (p_event_id, p_time, p_venue_id, p_duration_minutes, 'System Generated') 180 171 RETURNING event_happening_id INTO v_happening_id; 181 172 182 173 INSERT INTO "Ticket" (base_price, is_available, event_happening_id, seat_id) 183 SELECT 184 p_base_price, 185 TRUE, 186 v_happening_id, 187 s.seat_id 174 SELECT p_base_price, TRUE, v_happening_id, s.seat_id 188 175 FROM "Seat" s 189 176 JOIN "Section" sec ON s.section_id = sec.section_id 190 177 WHERE sec.venue_id = p_venue_id; 191 192 RAISE NOTICE 'Event happening successfully scheduled by admin % and tickets generated.', p_admin_id;193 178 END; 194 179 $$; … … 203 188 {{{ 204 189 205 CREATE OR REPLACE PROCEDURE create_rating( 206 p_user_id BIGINT, 207 p_event_happening_id BIGINT, 208 p_rating INT, 209 p_comment TEXT 210 ) 211 LANGUAGE plpgsql 212 AS $$ 190 CREATE OR REPLACE PROCEDURE create_rating(p_user_id BIGINT, p_event_happening_id BIGINT, p_rating INT, p_comment TEXT) 191 LANGUAGE plpgsql AS $$ 213 192 BEGIN 214 193 IF NOT EXISTS ( … … 222 201 AND tri.refund_item_id IS NULL 223 202 ) THEN 224 RAISE EXCEPTION 'Access Denied: You can only rate events you attended and did not refund.';203 RAISE EXCEPTION 'Access Denied: Cannot rate.'; 225 204 END IF; 226 205 227 206 INSERT INTO "Event_Happening_Rating" (rating, comment, event_happening_id, user_id, timestamp) 228 207 VALUES (p_rating, p_comment, p_event_happening_id, p_user_id, CURRENT_TIMESTAMP); 229 230 RAISE NOTICE 'Rating successfully submitted.';231 208 END; 232 209 $$; … … 244 221 245 222 CREATE OR REPLACE FUNCTION trg_check_user_age() 246 RETURNS TRIGGER 247 LANGUAGE plpgsql 248 AS $$ 249 DECLARE 250 v_user_age INT; 251 v_min_age INT; 252 v_user_id BIGINT; 253 BEGIN 254 SELECT user_id INTO v_user_id 255 FROM "Ticket_Order" WHERE order_id = NEW.order_id; 256 257 SELECT DATE_PART('year', AGE(date_of_birth)) INTO v_user_age 258 FROM "Regular_User" WHERE user_id = v_user_id; 259 260 SELECT e.min_age INTO v_min_age 223 RETURNS TRIGGER LANGUAGE plpgsql AS $$ 224 DECLARE 225 v_active BOOLEAN; 226 v_user_age INT; 227 v_min_age INT; 228 v_user_id BIGINT; 229 BEGIN 230 SELECT user_id INTO v_user_id FROM "Ticket_Order" WHERE order_id = NEW.order_id; 231 SELECT is_active INTO v_active FROM "User" WHERE user_id = v_user_id; 232 233 IF v_active IS NOT TRUE THEN 234 RAISE EXCEPTION 'User is inactive.'; 235 END IF; 236 237 SELECT DATE_PART('year', AGE(date_of_birth)) 238 INTO v_user_age 239 FROM "Regular_User" 240 WHERE user_id = v_user_id; 241 242 SELECT e.min_age 243 INTO v_min_age 261 244 FROM "Ticket" t 262 245 JOIN "Event_Happening" eh ON t.event_happening_id = eh.event_happening_id … … 265 248 266 249 IF v_user_age < v_min_age THEN 267 RAISE EXCEPTION 'A ccess Denied: User does not meet the minimum age restriction for this event.';250 RAISE EXCEPTION 'Age restriction.'; 268 251 END IF; 269 252 … … 279 262 280 263 281 === `trg_sync_order_amount` 282 283 Овој тригер одржува конзистентност на податоците преку автоматска синхронизација на финансиската сума во главната нарачка при секоја измена на нејзините ставки. Тој динамички го прекалкулира вкупниот износ земајќи ги предвид само активните и нерефундираните билети по секој '''INSERT''', '''UPDATE''' или '''DELETE'''. 284 285 {{{ 286 287 CREATE OR REPLACE FUNCTION trg_sync_order_amount() 288 RETURNS TRIGGER 289 LANGUAGE plpgsql 290 AS $$ 291 DECLARE 292 v_target_order_id BIGINT; 293 v_new_total DECIMAL(10,2); 294 BEGIN 295 IF TG_OP = 'DELETE' THEN 296 v_target_order_id := OLD.order_id; 297 ELSE 298 v_target_order_id := NEW.order_id; 299 END IF; 300 301 SELECT COALESCE(SUM(toi.item_price), 0.00) INTO v_new_total 264 === `trg_limit_tickets_per_happening` 265 266 ... 267 268 {{{ 269 270 CREATE OR REPLACE FUNCTION trg_limit_tickets_per_happening() 271 RETURNS TRIGGER LANGUAGE plpgsql AS $$ 272 DECLARE 273 v_happening_id BIGINT; 274 v_count INT; 275 BEGIN 276 SELECT event_happening_id INTO v_happening_id FROM "Ticket" WHERE ticket_id = NEW.ticket_id; 277 278 SELECT COUNT(*) INTO v_count 302 279 FROM "Ticket_Order_Item" toi 303 LEFT JOIN "Ticket_Refund_Item" tri ON toi.order_item_id = tri.order_item_id 304 WHERE toi.order_id = v_target_order_id AND tri.refund_item_id IS NULL; 305 306 UPDATE "Ticket_Order" 307 SET order_amount = v_new_total 308 WHERE order_id = v_target_order_id; 280 JOIN "Ticket_Order" o ON toi.order_id = o.order_id 281 JOIN "Ticket" t ON toi.ticket_id = t.ticket_id 282 WHERE o.user_id = (SELECT user_id FROM "Ticket_Order" WHERE order_id = NEW.order_id) 283 AND t.event_happening_id = v_happening_id; 284 285 IF v_count >= 4 THEN 286 RAISE EXCEPTION 'Purchase limit reached: You cannot own more than 4 tickets for this event.'; 287 END IF; 309 288 310 289 RETURN NEW; … … 312 291 $$; 313 292 314 CREATE TRIGGER sync_order_total_on_change315 AFTER INSERT OR UPDATE OR DELETEON "Ticket_Order_Item"316 FOR EACH ROW EXECUTE FUNCTION trg_ sync_order_amount();293 CREATE TRIGGER trg_limit_user_happening_tickets 294 BEFORE INSERT ON "Ticket_Order_Item" 295 FOR EACH ROW EXECUTE FUNCTION trg_limit_tickets_per_happening(); 317 296 318 297 }}} … … 326 305 327 306 CREATE OR REPLACE FUNCTION trg_prevent_double_booking() 328 RETURNS TRIGGER 329 LANGUAGE plpgsql 330 AS $$ 307 RETURNS TRIGGER LANGUAGE plpgsql AS $$ 331 308 BEGIN 332 309 IF EXISTS ( … … 334 311 WHERE venue_id = NEW.venue_id 335 312 AND event_happening_id <> NEW.event_happening_id 336 AND (NEW.event_time, NEW.event_time + (NEW.duration_minutes || ' minutes')::INTERVAL + INTERVAL '3 hours') OVERLAPS337 (event_time, event_time + (duration_minutes || ' minutes')::INTERVAL + INTERVAL '3 hours')313 AND (NEW.event_time, NEW.event_time + (NEW.duration_minutes || ' minutes')::INTERVAL + INTERVAL '3 hours') 314 OVERLAPS (event_time, event_time + (duration_minutes || ' minutes')::INTERVAL + INTERVAL '3 hours') 338 315 ) THEN 339 RAISE EXCEPTION 'Access Denied: Venue is already occupied or currently in technical preparation (3-hour buffer required).'; 340 END IF; 316 RAISE EXCEPTION 'Venue occupied.'; 317 END IF; 318 341 319 RETURN NEW; 342 320 END;
