DatabaseProgramming: db-programming.sql

File db-programming.sql, 27.2 KB (added by 231070, 9 hours ago)
Line 
1-- ---------------------------------- ФУНКЦИИ ---------------------------------------- --
2
3
4-- -------------ФУНКЦИЈА 1: Слободни места за настан--------------------
5CREATE OR REPLACE FUNCTION fn_slobodni_mesta(p_eventid BIGINT)
6RETURNS INT AS $$
7DECLARE
8 v_kapacitet INT;
9 v_prodadeni INT;
10BEGIN
11 SELECT SUM(h.capacity)
12 INTO v_kapacitet
13 FROM event_hall eh
14 JOIN hall h ON h.id = eh.hallid
15 WHERE eh.eventid = p_eventid;
16
17 SELECT COUNT(*)
18 INTO v_prodadeni
19 FROM ticket
20 WHERE eventid = p_eventid AND status = 'ACTIVE';
21
22 RETURN v_kapacitet - v_prodadeni;
23END;
24$$ LANGUAGE plpgsql;
25
26SELECT fn_slobodni_mesta(100) AS slobodni_mesta;
27-- Резултат: slobodni_mesta 736
28
29-- Функцијата го пресметува бројот на слободни места за даден настан така што
30-- од вкупниот капацитет на сите сали го одзема бројот на активни тикети.
31-- Ја имплементира бизнис логиката за проверка на достапност пред купување на тикет
32-- и се користи во процедурата proc_kupi_tiket и proc_dodaj_vo_waitlist.
33
34
35-- --------------ФУНКЦИЈА 2: Вкупен приход по настан ---------------------
36CREATE OR REPLACE FUNCTION fn_prihod_po_nastan(p_eventid BIGINT)
37RETURNS INT AS $$
38DECLARE
39 v_prihod INT;
40BEGIN
41 SELECT SUM(p.amount)
42 INTO v_prihod
43 FROM payment p
44 JOIN user_order uo ON uo.id = p.user_orderid
45 JOIN ticket t ON t.user_orderid = uo.id
46 WHERE t.eventid = p_eventid AND p.status = 'COMPLETED';
47
48 RETURN v_prihod;
49END;
50$$ LANGUAGE plpgsql;
51
52SELECT fn_prihod_po_nastan(1) AS vkupen_prihod;
53-- Резултат: 370300
54
55-- Функцијата го пресметува вкупниот приход за даден настан преку
56-- сумирање на сите завршени плаќања поврзани со тикетите за тој настан.
57-- Ја имплементира бизнис логиката за финансиски извештај
58-- и се користи при генерирање на статистики за организаторите на настани.
59
60
61
62-- --------------ФУНКЦИЈА 3: Дозвола за оставање рецензија ---------------------
63
64CREATE OR REPLACE FUNCTION fn_moze_review(
65 p_userid BIGINT,
66 p_eventid BIGINT
67)
68RETURNS BOOLEAN AS $$
69BEGIN
70 IF NOT EXISTS (
71 SELECT 1 FROM ticket
72 WHERE app_userid = p_userid
73 AND eventid = p_eventid
74 AND status IN ('USED')
75 ) THEN
76 RETURN FALSE;
77 END IF;
78
79 IF EXISTS (
80 SELECT 1 FROM review
81 WHERE app_userid = p_userid
82 AND eventid = p_eventid
83 ) THEN
84 RETURN FALSE;
85 END IF;
86
87 RETURN TRUE;
88END;
89$$ LANGUAGE plpgsql;
90
91SELECT fn_moze_review(28547, 35) AS moze_review;
92
93-- Резултат: true
94
95-- Функцијата проверува дали корисникот смее да остави рецензија за даден настан
96-- — враќа TRUE само ако корисникот има искористен тикет за тој настан и сè уште нема оставено рецензија.
97-- Ја имплементира бизнис логиката за заштита од лажни рецензии
98-- и се користи во процедурата proc_ostavi_review и тригерот trig_review.
99
100
101-- --------------ФУНКЦИЈА 4: Статистика за рецензии ---------------------
102
103CREATE OR REPLACE FUNCTION fn_statistika_recenzii(p_eventid BIGINT)
104RETURNS TABLE (
105 vkupno_recenzii BIGINT,
106 prosecen_rejting NUMERIC,
107 pet_zvezdi BIGINT,
108 cetiri_zvezdi BIGINT,
109 tri_zvezdi BIGINT,
110 dve_zvezdi BIGINT,
111 edna_zvezda BIGINT
112) AS $$
113BEGIN
114 RETURN QUERY
115 SELECT
116 COUNT(*) AS vkupno_recenzii,
117 ROUND(AVG(rating), 2) AS prosecen_rejting,
118 COUNT(*) FILTER (WHERE rating = 5) AS pet_zvezdi,
119 COUNT(*) FILTER (WHERE rating = 4) AS cetiri_zvezdi,
120 COUNT(*) FILTER (WHERE rating = 3) AS tri_zvezdi,
121 COUNT(*) FILTER (WHERE rating = 2) AS dve_zvezdi,
122 COUNT(*) FILTER (WHERE rating = 1) AS edna_zvezda
123 FROM review
124 WHERE eventid = p_eventid;
125END;
126$$ LANGUAGE plpgsql;
127SELECT * FROM fn_statistika_recenzii(33);
128
129
130-- Резултат: 94 3.83 33 29 19 9 4
131
132
133-- Функцијата враќа детална статистика за рецензиите на даден настан
134-- — вкупен број, просечен рејтинг и распределба по број на ѕвезди.
135-- Ја имплементира бизнис логиката за приказ на статистики на страницата на настанот
136-- и им овозможува на корисниците да донесат информирана одлука пред купување на тикет.
137
138
139-- --------------ФУНКЦИЈА 5: Позиција во waitlist ---------------------
140
141CREATE OR REPLACE FUNCTION fn_pozicija_vo_waitlist(
142 p_userid BIGINT,
143 p_eventid BIGINT
144)
145RETURNS INT AS $$
146DECLARE
147 v_pozicija INT;
148BEGIN
149 SELECT pozicija INTO v_pozicija
150 FROM (
151 SELECT
152 uw.app_userid,
153 ROW_NUMBER() OVER (ORDER BY w.created_at ASC) AS pozicija
154 FROM waitlist w
155 JOIN user_waitlist uw ON uw.waitlistid = w.id
156 WHERE w.eventid = p_eventid
157 AND w.status = 'PENDING'
158 ) sub
159 WHERE app_userid = p_userid;
160
161 IF v_pozicija IS NULL THEN
162 RAISE EXCEPTION 'Korisnikot % ne e vo waitlist za event %', p_userid, p_eventid;
163 END IF;
164
165 RETURN v_pozicija;
166END;
167$$ LANGUAGE plpgsql;
168
169SELECT fn_pozicija_vo_waitlist(2, 1) AS pozicija;
170-- Резултат: pozicija 1
171
172-- Функцијата го враќа редниот број на корисникот во листата
173-- на чекање за даден настан, подреден по времето на пријавување.
174-- Ја имплементира бизнис логиката за информирање на корисникот за
175-- неговата позиција во редот при пополнет настан.
176
177
178-- --------------ФУНКЦИЈА 6: Верификација на тикет ---------------------
179
180CREATE OR REPLACE FUNCTION fn_verifikacija_tiket(p_ticketid BIGINT)
181RETURNS BOOLEAN AS $$
182DECLARE
183 v_status VARCHAR(20);
184 v_start_date DATE;
185 v_end_date DATE;
186BEGIN
187 SELECT t.status, e.start_date, e.end_date
188 INTO v_status, v_start_date, v_end_date
189 FROM ticket t
190 JOIN event e ON e.id = t.eventid
191 WHERE t.id = p_ticketid;
192
193 IF v_status IS NULL THEN
194 RAISE EXCEPTION 'Tiketot % ne postoi', p_ticketid;
195 END IF;
196
197 IF v_status != 'ACTIVE' THEN
198 RETURN FALSE;
199 END IF;
200
201 IF CURRENT_DATE > v_end_date THEN
202 RETURN FALSE;
203 END IF;
204
205 RETURN TRUE;
206END;
207$$ LANGUAGE plpgsql;
208
209SELECT fn_verifikacija_tiket(1) AS e_validen;
210-- Резултат: false
211
212-- Функцијата проверува дали тикетот е валиден за скенирање при влез на настан
213-- — враќа TRUE само ако тикетот е со статус ACTIVE и настанот сè уште не е завршен.
214-- Ја имплементира бизнис логиката за контрола на влез на настани.
215
216
217
218-- ---------------------------------- ПРОЦЕДУРИ ---------------------------------------- --
219
220
221-- --------------ПРОЦЕДУРА 1: Најава на корисник ---------------------
222
223CREATE OR REPLACE PROCEDURE proc_najavi_korisnik(
224 p_email VARCHAR(50),
225 p_password VARCHAR(255)
226) LANGUAGE plpgsql AS $$
227DECLARE
228 v_userid BIGINT;
229 v_role_name VARCHAR(20);
230BEGIN
231 SELECT id INTO v_userid
232 FROM app_user
233 WHERE email = p_email
234 AND user_password = p_password
235 AND id != 1;
236
237 IF v_userid IS NULL THEN
238 RAISE EXCEPTION 'Pogreshеn email ili lozinka';
239 END IF;
240
241 SELECT r.role_name INTO v_role_name
242 FROM user_roles ur
243 JOIN roles r ON r.id = ur.rolesid
244 WHERE ur.app_userid = v_userid
245 LIMIT 1;
246
247 RAISE NOTICE 'Korisnikot % se najavi kako %', p_email, v_role_name;
248END;
249$$;
250
251
252SELECT email, user_password FROM app_user WHERE id != 1 LIMIT 1;
253-- Gertie.Gold0@gmail.com GertieGold
254
255CALL proc_najavi_korisnik('Gertie.Gold0@gmail.com', 'GertieGold');
256-- Резултат: Korisnikot Gertie.Gold0@gmail.com se najavi kako USER
257
258-- Процедурата ја верификува најавата на корисникот преку проверка на email и лозинка
259-- и ја враќа неговата улога во системот.
260-- Ја имплементира бизнис логиката за автентикација на корисници.
261-- Напомена: ова е поедноставена верзија — во продукциски систем
262-- лозинката би се споредувала со bcrypt хаш.
263
264
265-- --------------ПРОЦЕДУРА 2: Купување на тикет ---------------------
266
267CREATE OR REPLACE PROCEDURE proc_kupi_tiket(
268 p_userid BIGINT,
269 p_eventid BIGINT,
270 p_hallid BIGINT,
271 p_typeid BIGINT,
272 p_seatid BIGINT,
273 p_promo_codeid BIGINT DEFAULT NULL
274) LANGUAGE plpgsql AS $$
275DECLARE
276 v_orderid BIGINT;
277 v_price INT;
278 v_discount INT := 0;
279 v_final_price INT;
280 v_status_id BIGINT;
281 v_ticket_code VARCHAR(50);
282BEGIN
283 SELECT price INTO v_price
284 FROM event_ticket_type
285 WHERE eventid = p_eventid AND ticket_typeid = p_typeid;
286
287 IF v_price IS NULL THEN
288 RAISE EXCEPTION 'Ne postoi ticket type % za event %', p_typeid, p_eventid;
289 END IF;
290
291 IF fn_slobodni_mesta(p_eventid) <= 0 THEN
292 RAISE EXCEPTION 'Nema slobodni mesta za event %', p_eventid;
293 END IF;
294
295 IF p_promo_codeid IS NOT NULL THEN
296 SELECT discount_percent INTO v_discount
297 FROM promo_code
298 WHERE id = p_promo_codeid AND expiration_date >= CURRENT_DATE;
299
300 IF v_discount IS NULL THEN
301 RAISE EXCEPTION 'Promo kodot ne e validen ili e istecen';
302 END IF;
303 END IF;
304
305 v_final_price := v_price - (v_price * v_discount / 100);
306
307 SELECT id INTO v_status_id
308 FROM status
309 WHERE status_name = 'CREATED';
310
311 INSERT INTO user_order (order_date, total_amount, app_userid, statusid, promo_codeid)
312 VALUES (CURRENT_DATE, v_final_price, p_userid, v_status_id, p_promo_codeid)
313 RETURNING id INTO v_orderid;
314
315 v_ticket_code := 'TKT-' || v_orderid || '-' || extract(epoch FROM now())::BIGINT;
316
317 INSERT INTO ticket (code, status, ticket_typeid, user_orderid, seatid, app_userid, eventid, hallid)
318 VALUES (v_ticket_code, 'ACTIVE', p_typeid, v_orderid, p_seatid, p_userid, p_eventid, p_hallid);
319
320 RAISE NOTICE 'Tiketot e kreiran so kod: %', v_ticket_code;
321END;
322$$;
323
324CALL proc_kupi_tiket(2, 1, 1, 1, 5, NULL);
325-- Резултат: Tiketot e kreiran so kod: TKT-12500002-1778661805
326
327-- Процедурата го имплементира целиот процес на купување тикет
328-- — проверува дали постои типот на тикет за тој настан, дали има слободни места,
329-- дали промо кодот е валиден, ја пресметува конечната цена со попуст и креира нарачка и тикет.
330-- Ја имплементира централната бизнис логика за продажба на тикети во апликацијата.
331
332
333-- --------------ПРОЦЕДУРА 3: Плаќање на нарачка ---------------------
334
335CREATE OR REPLACE PROCEDURE proc_plati_narachka(
336 p_orderid BIGINT,
337 p_payment_methodid BIGINT
338) LANGUAGE plpgsql AS $$
339DECLARE
340 v_amount INT;
341 v_status_name VARCHAR(20);
342 v_paid_id BIGINT;
343BEGIN
344 -- Proveri dali orderot postoi
345 IF NOT EXISTS (SELECT 1 FROM user_order WHERE id = p_orderid) THEN
346 RAISE EXCEPTION 'Order % ne postoi', p_orderid;
347 END IF;
348
349 -- Zemi status na orderot
350 SELECT s.status_name INTO v_status_name
351 FROM user_order uo
352 JOIN status s ON s.id = uo.statusid
353 WHERE uo.id = p_orderid;
354
355 -- Proveri dali e vekje platen
356 IF v_status_name = 'PAID' THEN
357 RAISE EXCEPTION 'Order % e vekje platen', p_orderid;
358 END IF;
359
360 -- Proveri dali e otkazan
361 IF v_status_name = 'CANCELLED' THEN
362 RAISE EXCEPTION 'Order % e otkazan i ne moze da se plati', p_orderid;
363 END IF;
364
365 -- Zemi go iznosot
366 SELECT total_amount INTO v_amount
367 FROM user_order
368 WHERE id = p_orderid;
369
370 -- Kreiraj payment
371 INSERT INTO payment (amount, status, payment_date, user_orderid, payment_methodid)
372 VALUES (v_amount, 'COMPLETED', CURRENT_DATE, p_orderid, p_payment_methodid);
373
374
375 -- Azuriraj status na orderot vo PAID direktno vo procedurata
376 SELECT id INTO v_paid_id FROM status WHERE status_name = 'PAID';
377 UPDATE user_order SET statusid = v_paid_id WHERE id = p_orderid;
378
379 -- Prati notifikacija
380 INSERT INTO notification (message, created_at, app_userid)
381 SELECT 'Narachkata e uspeshno platena', CURRENT_DATE, app_userid
382 FROM user_order WHERE id = p_orderid;
383
384
385 RAISE NOTICE 'Order % e platеn so iznos %', p_orderid, v_amount;
386END;
387$$;
388
389
390SELECT uo.id
391FROM user_order uo
392JOIN status s ON s.id = uo.statusid
393WHERE s.status_name = 'CREATED'
394LIMIT 1;
395
396CALL proc_plati_narachka(4, 1);
397
398-- Резултат:
399-- Order 4 e azuriran vo PAID
400-- Order 4 e platеn so iznos 3000
401
402-- Процедурата го обработува плаќањето на нарачка — проверува дали нарачката постои
403-- и дали не е веќе платена или откажана, креира запис за плаќање, го
404-- ажурира статусот на нарачката во PAID и праќа нотификација до корисникот.
405-- Ја имплементира бизнис логиката за финансиски трансакции во апликацијата.
406
407-- --------------ПРОЦЕДУРА 4: Откажување на нарачка ---------------------
408
409
410-- Поради споро извршување, додаден е индекс
411CREATE INDEX IF NOT EXISTS idx_ticket_userorderid ON ticket(user_orderid);
412
413
414CREATE OR REPLACE PROCEDURE proc_otkazi_narachka(p_orderid BIGINT)
415LANGUAGE plpgsql AS $$
416DECLARE
417 v_paymentid BIGINT;
418 v_amount INT;
419 v_status_id BIGINT;
420BEGIN
421 IF NOT EXISTS (SELECT 1 FROM user_order WHERE id = p_orderid) THEN
422 RAISE EXCEPTION 'Order % ne postoi', p_orderid;
423 END IF;
424
425 SELECT id INTO v_status_id
426 FROM status
427 WHERE status_name = 'CANCELLED';
428
429 UPDATE user_order
430 SET statusid = v_status_id
431 WHERE id = p_orderid;
432
433 UPDATE ticket
434 SET status = 'CANCELLED'
435 WHERE user_orderid = p_orderid;
436
437 SELECT id, amount INTO v_paymentid, v_amount
438 FROM payment
439 WHERE user_orderid = p_orderid AND status = 'COMPLETED'
440 LIMIT 1;
441
442 IF v_paymentid IS NOT NULL THEN
443 INSERT INTO refund (amount, reason, status, paymentid)
444 VALUES (v_amount, 'Customer request', 'APPROVED', v_paymentid);
445
446 RAISE NOTICE 'Refund kreiran za order %', p_orderid;
447 END IF;
448
449 RAISE NOTICE 'Narachkata % e otkazana', p_orderid;
450END;
451$$;
452
453
454-- Барање на order со COMPLETED payment
455SELECT uo.id, count(t.id) AS tiketi
456FROM user_order uo
457JOIN payment p ON p.user_orderid = uo.id AND p.status = 'COMPLETED'
458LEFT JOIN ticket t ON t.user_orderid = uo.id
459GROUP BY uo.id
460ORDER BY tiketi ASC
461LIMIT 5;
462
463CALL proc_otkazi_narachka(12500001 );
464
465-- Процедурата го обработува откажувањето на нарачка — ги откажува нарачката
466-- и сите нејзини тикети и доколку нарачката е претходно платена, автоматски креира барање за рефундирање.
467-- Ја имплементира бизнис логиката за откажување и рефундирање во апликацијата.
468
469
470-- --------------ПРОЦЕДУРА 5: Додавање корисник на waitlist ---------------------
471
472
473CREATE OR REPLACE PROCEDURE proc_dodaj_vo_waitlist(
474 p_userid BIGINT,
475 p_eventid BIGINT
476) LANGUAGE plpgsql AS $$
477DECLARE
478 v_waitlist_id BIGINT;
479BEGIN
480 -- Dali postoi nastanot
481 IF NOT EXISTS (SELECT 1 FROM event WHERE id = p_eventid) THEN
482 RAISE EXCEPTION 'Nastanot % ne postoi', p_eventid;
483 END IF;
484
485 -- Dali korisnikot postoi
486 IF NOT EXISTS (SELECT 1 FROM app_user WHERE id = p_userid AND id != 1) THEN
487 RAISE EXCEPTION 'Korisnikot % ne postoi', p_userid;
488 END IF;
489
490 -- Ako ima slobodni mesta ne go stavaj vo waitlist
491 IF fn_slobodni_mesta(p_eventid) > 0 THEN
492 RAISE EXCEPTION 'Ima slobodni mesta za event %, kupi tiket namesto waitlist', p_eventid;
493 END IF;
494
495 -- Dali korisnikot vekje e vo waitlist za toj event
496 IF EXISTS (
497 SELECT 1 FROM user_waitlist uw
498 JOIN waitlist w ON w.id = uw.waitlistid
499 WHERE uw.app_userid = p_userid
500 AND w.eventid = p_eventid
501 AND w.status = 'PENDING'
502 ) THEN
503 RAISE EXCEPTION 'Korisnikot % vekje e vo waitlist za event %', p_userid, p_eventid;
504 END IF;
505
506 -- Kreiranje nov waitlist zapis za toj event
507 INSERT INTO waitlist (created_at, status, eventid)
508 VALUES (CURRENT_DATE, 'PENDING', p_eventid)
509 RETURNING id INTO v_waitlist_id;
510
511 -- Dodavanje na korisnikot vo waitlist
512 INSERT INTO user_waitlist (app_userid, waitlistid)
513 VALUES (p_userid, v_waitlist_id);
514
515 -- Trigеrot trig_waitlist ke go konfirmira avtomatski
516 -- koga ke se oslobodi mesto (pri otkazuvanje na tiket)
517
518 -- Prati notifikacija
519 INSERT INTO notification (message, created_at, app_userid)
520 VALUES ('You have been added to the waitlist.', CURRENT_DATE, p_userid);
521
522 RAISE NOTICE 'Korisnikot % e dodaden vo waitlist za event %', p_userid, p_eventid;
523END;
524$$;
525
526CALL proc_dodaj_vo_waitlist(
527 (SELECT id FROM app_user WHERE id != 1 LIMIT 1),
528 (SELECT id FROM event LIMIT 1)
529);
530
531SELECT id FROM event WHERE fn_slobodni_mesta(id) <= 0 LIMIT 1;
532-- Процедурата го додава корисникот на листата на чекање за пополнет настан
533-- — проверува дали настанот и корисникот постојат, дали навистина нема слободни места,
534-- дали корисникот не е веќе на листата и по успешно додавање праќа нотификација.
535-- Ја имплементира бизнис логиката за управување со листата на чекање при пополнети настани.
536
537
538-- --------------ПРОЦЕДУРА 6: Оставање на рецензија ---------------------
539
540
541CREATE OR REPLACE PROCEDURE proc_ostavi_review(
542 p_userid BIGINT,
543 p_eventid BIGINT,
544 p_rating INT,
545 p_comment VARCHAR(255) DEFAULT NULL
546) LANGUAGE plpgsql AS $$
547BEGIN
548 -- Proveri dali nastanot postoi
549 IF NOT EXISTS (SELECT 1 FROM event WHERE id = p_eventid) THEN
550 RAISE EXCEPTION 'Nastanot % ne postoi', p_eventid;
551 END IF;
552
553 -- Proveri dali nastanot e minаt
554 IF NOT EXISTS (SELECT 1 FROM event WHERE id = p_eventid AND end_date < CURRENT_DATE) THEN
555 RAISE EXCEPTION 'Ne moze da se ostavi review za nastan koj ne e zavrshen';
556 END IF;
557
558 -- Koristi ja funkcijata fn_moze_review
559 IF NOT fn_moze_review(p_userid, p_eventid) THEN
560 RAISE EXCEPTION 'Korisnikot % ne smee da ostavi review za event %', p_userid, p_eventid;
561 END IF;
562
563 -- Proveri dali ratingot e validen
564 IF p_rating NOT BETWEEN 1 AND 5 THEN
565 RAISE EXCEPTION 'Ratingot mora da bide pomegu 1 i 5';
566 END IF;
567
568 -- Vmetni review
569 INSERT INTO review (rating, review_comment, app_userid, eventid)
570 VALUES (p_rating, p_comment, p_userid, p_eventid);
571
572 RAISE NOTICE 'Review e ostaveno za event % od korisnik %', p_eventid, p_userid;
573END;
574$$;
575
576
577SELECT app_userid, eventid
578FROM ticket
579WHERE status = 'USED'
580LIMIT 1;
581
582-- 21615 49952
583
584SELECT * FROM review WHERE app_userid = 21615 AND eventid = 49952;
585-- Овој корисник нема reviews за тој настан
586
587CALL proc_ostavi_review(21615, 49952, 5, 'Odlichno iskustvo!');
588-- Резултат: Review e ostaveno za event 49952 od korisnik 21615
589
590-- Процедурата го обработува оставањето рецензија за минат настан
591-- — проверува дали настанот постои и е завршен, го користи fn_moze_review за
592-- да провери дали корисникот смее да остави рецензија и дали рејтингот е во валиден опсег.
593-- Ја имплементира бизнис логиката за систем на рецензии во апликацијата.
594
595
596-- ---------------------------------- ТРИГЕРИ ---------------------------------------- --
597
598
599-- --------------ТРИГЕР 1: При откажан тикет -> confirm на следен од waitlist ---------------------
600CREATE OR REPLACE FUNCTION fn_trig_waitlist()
601RETURNS TRIGGER AS $$
602DECLARE
603 v_waitlist_id BIGINT;
604BEGIN
605 IF NEW.status = 'CANCELLED' AND OLD.status != 'CANCELLED' THEN
606
607 SELECT w.id INTO v_waitlist_id
608 FROM waitlist w
609 WHERE w.eventid = NEW.eventid AND w.status = 'PENDING'
610 ORDER BY w.created_at
611 LIMIT 1;
612
613 IF v_waitlist_id IS NOT NULL THEN
614 UPDATE waitlist
615 SET status = 'CONFIRMED'
616 WHERE id = v_waitlist_id;
617
618 RAISE NOTICE 'Waitlist % e potvrden za event %', v_waitlist_id, NEW.eventid;
619 END IF;
620 END IF;
621
622 RETURN NEW;
623END;
624$$ LANGUAGE plpgsql;
625
626CREATE TRIGGER trig_waitlist
627AFTER UPDATE ON ticket
628FOR EACH ROW
629EXECUTE FUNCTION fn_trig_waitlist();
630
631
632UPDATE ticket SET status = 'CANCELLED' WHERE id = (
633 SELECT t.id FROM ticket t
634 JOIN waitlist w ON w.eventid = t.eventid
635 WHERE w.status = 'PENDING'
636 LIMIT 1
637);
638
639-- Тригерот се активира автоматски по секое ажурирање на статусот на тикет во CANCELLED
640-- и го потврдува првиот корисник во редот на чекање за тој настан.
641-- Ја имплементира бизнис логиката за автоматско управување со листата на чекање при ослободување на место.
642
643
644-- -------------- ТРИГЕР 2: Заштита од двоен refund ---------------------
645
646CREATE OR REPLACE FUNCTION fn_trig_refund()
647RETURNS TRIGGER AS $$
648BEGIN
649 IF EXISTS (
650 SELECT 1 FROM refund
651 WHERE paymentid = NEW.paymentid
652 AND status IN ('REQUESTED', 'APPROVED')
653 ) THEN
654 RAISE EXCEPTION 'Vekje postoi refund za payment %', NEW.paymentid;
655 END IF;
656
657 RETURN NEW;
658END;
659$$ LANGUAGE plpgsql;
660
661CREATE TRIGGER trig_refund
662BEFORE INSERT ON refund
663FOR EACH ROW
664EXECUTE FUNCTION fn_trig_refund();
665
666-- Test - obid za dvoen refund
667-- Najdi payment koj vekje ima refund
668SELECT paymentid FROM refund
669WHERE status IN ('REQUESTED', 'APPROVED')
670LIMIT 1; --1
671
672
673INSERT INTO refund (amount, reason, status, paymentid)
674VALUES (100, 'Test duplikat', 'REQUESTED', 1);
675-- Резултат: Error: Vekje postoi refund za payment 1
676
677-- Тригерот се активира пред секое вметнување во табелата REFUND
678-- и проверува дали за тоа плаќање веќе постои активно барање за рефундирање.
679-- Ја имплементира бизнис логиката за заштита од двојно рефундирање на исто плаќање.
680
681
682-- -------------- ТРИГЕР 3: Ажурирање на quantity_available ---------------------
683
684CREATE OR REPLACE FUNCTION fn_trig_quantity()
685RETURNS TRIGGER AS $$
686BEGIN
687 -- Pri nov ACTIVE tiket - namalи quantity_available
688 IF TG_OP = 'INSERT' AND NEW.status = 'ACTIVE' THEN
689 UPDATE event_ticket_type
690 SET quantity_available = quantity_available - 1
691 WHERE eventid = NEW.eventid
692 AND ticket_typeid = NEW.ticket_typeid;
693
694 IF NOT FOUND THEN
695 RAISE EXCEPTION 'Ne postoi ticket type % za event %',
696 NEW.ticket_typeid, NEW.eventid;
697 END IF;
698
699 -- Pri otkazuvanje na tiket - zgolemi quantity_available
700 ELSIF TG_OP = 'UPDATE'
701 AND NEW.status = 'CANCELLED'
702 AND OLD.status != 'CANCELLED' THEN
703
704 UPDATE event_ticket_type
705 SET quantity_available = quantity_available + 1
706 WHERE eventid = NEW.eventid
707 AND ticket_typeid = NEW.ticket_typeid;
708 END IF;
709
710 RETURN NEW;
711END;
712$$ LANGUAGE plpgsql;
713
714CREATE TRIGGER trig_quantity
715AFTER INSERT OR UPDATE ON ticket
716FOR EACH ROW
717EXECUTE FUNCTION fn_trig_quantity();
718
719-- Test INSERT - нов тикет
720SELECT quantity_available FROM event_ticket_type
721WHERE eventid = 33 AND ticket_typeid = 1;
722-- quantity_available пред insert == 692
723
724-- Додавање нов тикет
725INSERT INTO ticket (code, status, ticket_typeid, user_orderid, seatid, app_userid, eventid, hallid)
726VALUES ('TKT-TEST-TRIG', 'ACTIVE', 1,
727 (SELECT MIN(id) FROM user_order),
728 NULL,
729 (SELECT MIN(id) FROM app_user WHERE id != 1),
730 33,
731 (SELECT MIN(hallid) FROM event_hall WHERE eventid = 33));
732
733
734SELECT quantity_available FROM event_ticket_type
735WHERE eventid = 33 AND ticket_typeid = 1;
736-- quantity_available по insert == 691
737
738-- Test UPDATE - откажување тикет
739UPDATE ticket SET status = 'CANCELLED' WHERE code = 'TKT-TEST-TRIG';
740
741SELECT quantity_available FROM event_ticket_type
742WHERE eventid = 33 AND ticket_typeid = 1;
743-- quantity_available по откажување == 692
744
745
746-- Тригерот се активира по секое вметнување или ажурирање на тикет и
747-- автоматски го ажурира бројот на достапни тикети во табелата event_ticket_type
748-- — го намалува при продажба и го зголемува при откажување.
749-- Ја имплементира бизнис логиката за следење на достапноста на тикети во реално време.