CREATE OR REPLACE FUNCTION get_reservation_total_with_discount(
p_reservation_id INT, p_promotion_id INT
) RETURNS FLOAT AS $$ DECLARE
v_total FLOAT; v_discount FLOAT; v_final FLOAT;
BEGIN
-- Вкупна цена на сите тикети за резервацијата SELECT SUM(price) INTO v_total FROM TICKET WHERE reservation_id = p_reservation_id;
-- Попустот од промоцијата (во проценти) SELECT discount INTO v_discount FROM PROMOTION WHERE promotion_id = p_promotion_id;
-- Пресметај крајна цена v_final := v_total - (v_total * v_discount / 100);
RETURN v_final;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION get_available_seats(
p_screening_id INT
) RETURNS INT AS $$ DECLARE
v_capacity INT; v_sold INT;
BEGIN
-- Капацитет на салата SELECT ch.capacity INTO v_capacity FROM SCREENING s JOIN CINEMA_HALL ch ON ch.cinemahall_id = s.cinemahall_id WHERE s.screening_id = p_screening_id;
-- Продадени тикети SELECT COUNT(*) INTO v_sold FROM TICKET WHERE screening_id = p_screening_id;
RETURN v_capacity - v_sold;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION get_movie_avg_rating(
p_movie_id INT
) RETURNS FLOAT AS $$ DECLARE
v_avg FLOAT;
BEGIN
SELECT ROUND(AVG(rating)::numeric, 2) INTO v_avg FROM REVIEW WHERE movie_id = p_movie_id;
RETURN v_avg;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE PROCEDURE generate_tickets_for_screening(
p_screening_id INT, p_price FLOAT
) LANGUAGE plpgsql AS $$ DECLARE
v_cinemahall_id INT; v_cinema_id INT; v_seat RECORD; v_count INT := 0; v_screening_date DATE;
BEGIN
-- Провери дали проекцијата постои IF NOT EXISTS (SELECT 1 FROM SCREENING WHERE screening_id = p_screening_id) THEN
RAISE EXCEPTION 'Проекцијата со ID % не постои!', p_screening_id;
END IF;
-- Провери дали датумот на проекцијата не е во минатото SELECT date INTO v_screening_date FROM SCREENING WHERE screening_id = p_screening_id; IF v_screening_date < CURRENT_DATE THEN
RAISE EXCEPTION 'Не може да се генерираат тикети за минати проекции! Датум: %', v_screening_date;
END IF;
-- Провери дали цената е валидна IF p_price <= 0 THEN
RAISE EXCEPTION 'Цената мора да биде поголема од 0!';
END IF;
-- Земи ја салата SELECT cinemahall_id INTO v_cinemahall_id FROM SCREENING WHERE screening_id = p_screening_id;
-- Провери дали салата постои IF NOT EXISTS (SELECT 1 FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id) THEN
RAISE EXCEPTION 'Салата со ID % не постои!', v_cinemahall_id;
END IF;
-- Провери дали салата е активна IF NOT EXISTS (SELECT 1 FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id AND status = 'ACTIVE') THEN
RAISE EXCEPTION 'Салата со ID % не е активна!', v_cinemahall_id;
END IF;
-- Провери дали киното постои SELECT cinema_id INTO v_cinema_id FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id; IF NOT EXISTS (SELECT 1 FROM CINEMA WHERE cinema_id = v_cinema_id) THEN
RAISE EXCEPTION 'Киното со ID % не постои!', v_cinema_id;
END IF;
-- Провери дали воопшто има седишта во салата IF NOT EXISTS (SELECT 1 FROM SEAT WHERE cinemahall_id = v_cinemahall_id) THEN
RAISE EXCEPTION 'Салата со ID % нема седишта!', v_cinemahall_id;
END IF;
-- За секое седиште во салата креирај тикет FOR v_seat IN
SELECT seat_id FROM SEAT WHERE cinemahall_id = v_cinemahall_id
LOOP
IF NOT EXISTS (
SELECT 1 FROM TICKET WHERE seat_id = v_seat.seat_id AND screening_id = p_screening_id
) THEN
INSERT INTO TICKET (price, user_id, reservation_id, seat_id, screening_id) VALUES (p_price, NULL, NULL, v_seat.seat_id, p_screening_id); v_count := v_count + 1;
END IF;
END LOOP;
IF v_count = 0 THEN
RAISE NOTICE 'Сите тикети за проекција % веќе се генерирани!', p_screening_id;
ELSE
RAISE NOTICE 'Генерирани % тикети за проекција % во сала % по цена %',
v_count, p_screening_id, v_cinemahall_id, p_price;
END IF;
END; $$;
CREATE OR REPLACE PROCEDURE purchase_ticket_with_products(
p_ticket_id INT, p_user_id VARCHAR(13), p_reservation_id INT, p_products INT[]
) LANGUAGE plpgsql AS $$ DECLARE
v_product_id INT; v_price FLOAT; v_screening_id INT; v_screening_date DATE; v_total_price FLOAT := 0;
BEGIN
-- Провери дали тикетот постои IF NOT EXISTS (SELECT 1 FROM TICKET WHERE ticket_id = p_ticket_id) THEN
RAISE EXCEPTION 'Тикетот со ID % не постои!', p_ticket_id;
END IF;
-- Провери дали тикетот е слободен IF EXISTS (SELECT 1 FROM TICKET WHERE ticket_id = p_ticket_id AND user_id IS NOT NULL) THEN
RAISE EXCEPTION 'Тикетот со ID % е веќе купен!', p_ticket_id;
END IF;
-- Провери дали корисникот постои IF NOT EXISTS (SELECT 1 FROM "USER" WHERE user_id = p_user_id) THEN
RAISE EXCEPTION 'Корисникот % не постои!', p_user_id;
END IF;
-- Провери дали резервацијата постои IF NOT EXISTS (SELECT 1 FROM RESERVATION WHERE reservation_id = p_reservation_id) THEN
RAISE EXCEPTION 'Резервацијата со ID % не постои!', p_reservation_id;
END IF;
-- Провери дали резервацијата не е откажана IF EXISTS (SELECT 1 FROM RESERVATION WHERE reservation_id = p_reservation_id AND status = 'CANCELLED') THEN
RAISE EXCEPTION 'Резервацијата со ID % е откажана!', p_reservation_id;
END IF;
-- Провери дали проекцијата не е во минатото SELECT screening_id INTO v_screening_id FROM TICKET WHERE ticket_id = p_ticket_id; SELECT date INTO v_screening_date FROM SCREENING WHERE screening_id = v_screening_id; IF v_screening_date < CURRENT_DATE THEN
RAISE EXCEPTION 'Не може да се купи тикет за минати проекции! Датум: %', v_screening_date;
END IF;
-- Провери дали производите постојат и пресметај вкупна цена FOREACH v_product_id IN ARRAY p_products LOOP
IF NOT EXISTS (SELECT 1 FROM PRODUCT WHERE product_id = v_product_id) THEN
RAISE EXCEPTION 'Производот со ID % не постои!', v_product_id;
END IF; SELECT price INTO v_price FROM PRODUCT WHERE product_id = v_product_id; v_total_price := v_total_price + v_price;
END LOOP;
-- Ажурирај го тикетот UPDATE TICKET SET user_id = p_user_id,
reservation_id = p_reservation_id
WHERE ticket_id = p_ticket_id;
-- Додај ги производите FOREACH v_product_id IN ARRAY p_products LOOP
INSERT INTO PURCHASED_PRODUCT (numbers, ticket_id, product_id) VALUES (1, p_ticket_id, v_product_id);
END LOOP;
RAISE NOTICE 'Тикетот % е успешно купен! Корисник: %, Производи: %, Вкупна цена на производи: %',
p_ticket_id, p_user_id, array_length(p_products, 1), v_total_price;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Грешка: % — се прави ROLLBACK!', SQLERRM; ROLLBACK; RETURN;
END; $$;
CREATE OR REPLACE PROCEDURE add_movie_review(
p_user_id VARCHAR(13), p_movie_id INT, p_rating INT, p_comment VARCHAR(255)
) LANGUAGE plpgsql AS $$ DECLARE
v_existing_reviews INT; v_avg_rating FLOAT;
BEGIN
-- Провери дали корисникот постои IF NOT EXISTS (SELECT 1 FROM "USER" WHERE user_id = p_user_id) THEN
RAISE EXCEPTION 'Корисникот % не постои!', p_user_id;
END IF;
-- Провери дали филмот постои IF NOT EXISTS (SELECT 1 FROM MOVIE WHERE movie_id = p_movie_id) THEN
RAISE EXCEPTION 'Филмот со ID % не постои!', p_movie_id;
END IF;
-- Провери дали рејтингот е валиден IF p_rating < 1 OR p_rating > 10 THEN
RAISE EXCEPTION 'Рејтингот мора да биде помеѓу 1 и 10! Внесен рејтинг: %', p_rating;
END IF;
-- Провери дали коментарот е празен IF p_comment IS NULL OR TRIM(p_comment) = THEN
RAISE EXCEPTION 'Коментарот не може да биде празен!';
END IF;
-- Провери дали корисникот купил тикет за овој филм IF NOT EXISTS (
SELECT 1 FROM TICKET t JOIN SCREENING s ON s.screening_id = t.screening_id WHERE t.user_id = p_user_id AND s.movie_id = p_movie_id
) THEN
RAISE EXCEPTION 'Корисникот % не купил тикет за филмот %!', p_user_id, p_movie_id;
END IF;
-- Провери дали корисникот веќе оставил review за овој филм IF EXISTS (SELECT 1 FROM REVIEW WHERE user_id = p_user_id AND movie_id = p_movie_id) THEN
RAISE EXCEPTION 'Корисникот % веќе оставил review за филмот %!', p_user_id, p_movie_id;
END IF;
-- Додај review INSERT INTO REVIEW (comment, date, user_id, movie_id, rating) VALUES (p_comment, CURRENT_DATE, p_user_id, p_movie_id, p_rating);
-- Пресметај нов просечен рејтинг SELECT COUNT(*), ROUND(AVG(rating)::numeric, 2) INTO v_existing_reviews, v_avg_rating FROM REVIEW WHERE movie_id = p_movie_id;
RAISE NOTICE 'Review за филм % додаден! Рејтинг: %, Вкупно reviews: %, Просечен рејтинг: %',
p_movie_id, p_rating, v_existing_reviews, v_avg_rating;
END; $$;
CREATE OR REPLACE FUNCTION check_seat_availability() RETURNS TRIGGER AS $$ BEGIN
IF EXISTS (
SELECT 1 FROM TICKET WHERE seat_id = NEW.seat_id AND screening_id = NEW.screening_id
) THEN
RAISE EXCEPTION 'Седиштето % е веќе зафатено за оваа проекција!', NEW.seat_id;
END IF; RETURN NEW;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER trg_check_seat_availability BEFORE INSERT ON TICKET FOR EACH ROW EXECUTE FUNCTION check_seat_availability();
CREATE OR REPLACE FUNCTION notify_on_reservation()
RETURNS TRIGGER AS $$ BEGIN
INSERT INTO NOTIFICATION (message, date, user_id) VALUES (
'Вашата резервација е успешно креирана со ID: ' NEW.reservation_id, CURRENT_DATE, NEW.user_id
); RETURN NEW;
END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER trg_notify_on_reservation AFTER INSERT ON USER_RESERVATION FOR EACH ROW EXECUTE FUNCTION notify_on_reservation();
CREATE OR REPLACE FUNCTION complete_past_reservations() RETURNS TRIGGER AS $$ BEGIN
IF NEW.date < CURRENT_DATE THEN
UPDATE RESERVATION r SET status = 'COMPLETED' FROM TICKET t WHERE t.reservation_id = r.reservation_id AND t.screening_id = NEW.screening_id AND r.status = 'CONFIRMED';
END IF; RETURN NEW;
END; $$ LANGUAGE plpgsql; CREATE EXTENSION IF NOT EXISTS postgis;
-- Тест процедура 1 — генерирај тикети CALL generate_tickets_for_screening(55105026, 350.00);
ALTER TABLE TICKET ALTER COLUMN user_id DROP NOT NULL; ALTER TABLE TICKET ALTER COLUMN reservation_id DROP NOT NULL;
SELECT column_name, is_nullable FROM information_schema.columns WHERE table_name = 'ticket' AND column_name IN ('user_id', 'reservation_id');
CALL generate_tickets_for_screening(55105026, 350.00);
-- Земи слободен тикет од новата проекција SELECT ticket_id FROM TICKET WHERE screening_id = 55105026 AND user_id IS NULL LIMIT 1;
-- Земи вистински user_id SELECT user_id FROM "USER" LIMIT 1;
-- Земи вистински reservation_id со статус PENDING SELECT reservation_id FROM RESERVATION WHERE status = 'PENDING' LIMIT 1;
-- Земи вистински product_id SELECT product_id FROM PRODUCT LIMIT 3;
CALL purchase_ticket_with_products(12818005, 'admin01', 4000005, ARRAY[1, 2, 3]); CALL add_movie_review('admin01', 195, 8, 'Одличен филм, препорачувам!');
-- Земи филм за кој admin01 има тикет но нема review SELECT DISTINCT s.movie_id FROM TICKET t JOIN SCREENING s ON s.screening_id = t.screening_id WHERE t.user_id = 'admin01' AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01') LIMIT 1;
SELECT DISTINCT s.movie_id FROM TICKET t JOIN SCREENING s ON s.screening_id = t.screening_id WHERE t.user_id = 'admin01' AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01') LIMIT 1;
SELECT DISTINCT s.movie_id FROM TICKET t JOIN SCREENING s ON s.screening_id = t.screening_id WHERE t.user_id = 'admin01' AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01') LIMIT 1; SELECT user_id FROM "USER" WHERE user_id != 'admin01' LIMIT 1;
SELECT DISTINCT s.movie_id FROM TICKET t JOIN SCREENING s ON s.screening_id = t.screening_id WHERE t.user_id = 'admin02' AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin02') LIMIT 1; CALL add_movie_review('admin02', 195, 7, 'Многу добар филм!');
