= Procedures = == create_parking_reservation == {{{ CREATE OR REPLACE PROCEDURE public.create_parking_reservation( IN p_user_id integer, IN p_vehicle_id integer, IN p_parking_id integer, IN p_reservation_status_id integer, IN p_start_time timestamp without time zone, IN p_end_time timestamp without time zone ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS (SELECT 1 FROM app_user WHERE user_id = p_user_id) THEN RAISE EXCEPTION 'User does not exist.'; END IF; IF NOT EXISTS ( SELECT 1 FROM vehicle WHERE vehicle_id = p_vehicle_id AND user_id = p_user_id ) THEN RAISE EXCEPTION 'Vehicle does not exist or does not belong to this user.'; END IF; IF NOT EXISTS (SELECT 1 FROM parking WHERE parking_id = p_parking_id) THEN RAISE EXCEPTION 'Parking does not exist.'; END IF; IF NOT EXISTS ( SELECT 1 FROM parking WHERE parking_id = p_parking_id AND supports_reservation = true ) THEN RAISE EXCEPTION 'Parking does not support reservations.'; END IF; IF p_start_time >= p_end_time THEN RAISE EXCEPTION 'Start time must be before end time.'; END IF; IF NOT EXISTS ( SELECT 1 FROM parking_spot WHERE parking_id = p_parking_id AND status = 'free' ) THEN RAISE EXCEPTION 'No free parking spots available.'; END IF; INSERT INTO reservation ( user_id, vehicle_id, parking_id, reservation_status_id, start_time, end_time, reservation_code ) VALUES ( p_user_id, p_vehicle_id, p_parking_id, p_reservation_status_id, p_start_time, p_end_time, 'RES-' || p_user_id || '-' || EXTRACT(EPOCH FROM NOW())::INT ); UPDATE parking_spot SET status = 'reserved' WHERE parking_spot_id = ( SELECT parking_spot_id FROM parking_spot WHERE parking_id = p_parking_id AND status = 'free' LIMIT 1 ); END; $procedure$; }}} Оваа процедура креира нова паркинг резервација само доколку се исполнети сите потребни услови, како постоење на корисник, возило, паркинг и слободно место. Ја имплементира бизнис логиката за управување со резервации и автоматски резервира слободно паркинг место. == cancel_reservation == {{{ CREATE OR REPLACE PROCEDURE public.cancel_reservation( IN p_reservation_id integer ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id ) THEN RAISE EXCEPTION 'Reservation does not exist.'; END IF; IF EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id AND reservation_status = 'cancelled' ) THEN RAISE EXCEPTION 'Reservation is already cancelled.'; END IF; IF EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id AND reservation_status = 'completed' ) THEN RAISE EXCEPTION 'Completed reservation cannot be cancelled.'; END IF; UPDATE reservation SET reservation_status = 'cancelled' WHERE reservation_id = p_reservation_id; END; $procedure$; }}} Оваа процедура овозможува откажување на постоечка резервација, при што спречува откажување на веќе завршени или претходно откажани резервации. Ја имплементира бизнис логиката за контрола и правилно управување со статусите на резервациите. == complete_reservation == {{{ CREATE OR REPLACE PROCEDURE public.complete_reservation( IN p_reservation_id integer ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id ) THEN RAISE EXCEPTION 'Reservation does not exist.'; END IF; IF EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id AND reservation_status = 'completed' ) THEN RAISE EXCEPTION 'Reservation is already completed.'; END IF; IF EXISTS ( SELECT 1 FROM reservation WHERE reservation_id = p_reservation_id AND reservation_status = 'cancelled' ) THEN RAISE EXCEPTION 'Cancelled reservation cannot be completed.'; END IF; UPDATE reservation SET reservation_status = 'completed' WHERE reservation_id = p_reservation_id; END; $procedure$; }}} Оваа процедура ја означува резервацијата како завршена, при што врши проверки за валидност на тековниот статус. Ја имплементира бизнис логиката за правилно завршување на процесот на резервација и спречување нелогични промени. == end_parking_session == {{{ CREATE OR REPLACE PROCEDURE public.end_parking_session( IN p_session_id integer, IN p_session_status_id integer ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS (SELECT 1 FROM parking_session WHERE session_id = p_session_id) THEN RAISE EXCEPTION 'Parking session does not exist.'; END IF; IF EXISTS ( SELECT 1 FROM parking_session WHERE session_id = p_session_id AND end_time IS NOT NULL ) THEN RAISE EXCEPTION 'Parking session is already finished.'; END IF; IF NOT EXISTS ( SELECT 1 FROM session_status WHERE session_status_id = p_session_status_id ) THEN RAISE EXCEPTION 'Session status does not exist.'; END IF; UPDATE parking_session SET end_time = NOW(), session_status_id = p_session_status_id WHERE session_id = p_session_id; END; $procedure$; }}} Оваа процедура ја завршува активната паркинг сесија со поставување на крајно време и ажурирање на нејзиниот статус. Ја имплементира бизнис логиката за правилно затворање на паркинг сесиите и спречување повторно завршување на веќе затворени сесии. == fill_total_amount == {{{ CREATE OR REPLACE PROCEDURE public.fill_total_amount( IN p_session_id integer ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS (SELECT 1 FROM parking_session WHERE session_id = p_session_id) THEN RAISE EXCEPTION 'Parking session does not exist.'; END IF; IF EXISTS ( SELECT 1 FROM parking_session WHERE session_id = p_session_id AND end_time IS NULL ) THEN RAISE EXCEPTION 'Parking session is still active.'; END IF; UPDATE parking_session SET total_amount = calculate_session_amount(p_session_id) WHERE session_id = p_session_id; END; $procedure$; }}} Оваа процедура го пресметува и ажурира вкупниот износ за завршена паркинг сесија користејќи функција за пресметка на цена. Ја имплементира бизнис логиката за автоматска наплата според времетраењето и тарифата на паркирањето. == start_parking_session == {{{ CREATE OR REPLACE PROCEDURE public.start_parking_session( IN p_user_id integer, IN p_vehicle_id integer, IN p_parking_id integer, IN p_tariff_id integer, IN p_session_status_id integer ) LANGUAGE plpgsql AS $procedure$ BEGIN IF NOT EXISTS (SELECT 1 FROM app_user WHERE user_id = p_user_id) THEN RAISE EXCEPTION 'User does not exist.'; END IF; IF NOT EXISTS ( SELECT 1 FROM vehicle WHERE vehicle_id = p_vehicle_id AND user_id = p_user_id ) THEN RAISE EXCEPTION 'Vehicle does not exist or does not belong to this user.'; END IF; IF NOT EXISTS (SELECT 1 FROM parking WHERE parking_id = p_parking_id) THEN RAISE EXCEPTION 'Parking does not exist.'; END IF; IF NOT EXISTS (SELECT 1 FROM tariff WHERE tariff_id = p_tariff_id) THEN RAISE EXCEPTION 'Tariff does not exist.'; END IF; IF NOT EXISTS ( SELECT 1 FROM session_status WHERE session_status_id = p_session_status_id ) THEN RAISE EXCEPTION 'Session status does not exist.'; END IF; IF has_active_session(p_user_id) THEN RAISE EXCEPTION 'User already has an active parking session.'; END IF; IF NOT EXISTS ( SELECT 1 FROM parking_spot WHERE parking_id = p_parking_id AND status = 'free' ) THEN RAISE EXCEPTION 'No free parking spots available.'; END IF; INSERT INTO parking_session ( user_id, vehicle_id, parking_id, tariff_id, session_status_id, start_time, end_time, total_amount ) VALUES ( p_user_id, p_vehicle_id, p_parking_id, p_tariff_id, p_session_status_id, NOW(), NULL, 0.00 ); UPDATE parking_spot SET status = 'occupied' WHERE parking_spot_id = ( SELECT parking_spot_id FROM parking_spot WHERE parking_id = p_parking_id AND status = 'free' LIMIT 1 ); END; $procedure$; }}} Оваа процедура започнува нова паркинг сесија доколку корисникот ги исполнува сите услови и има достапно слободно место. Ја имплементира бизнис логиката за иницирање на паркирање, контрола на активни сесии и автоматско зафаќање на паркинг место. = Functions = == calculate_session_amount == {{{ CREATE OR REPLACE FUNCTION public.calculate_session_amount(p_session_id integer) RETURNS numeric LANGUAGE plpgsql AS $function$ DECLARE v_start TIMESTAMP; v_end TIMESTAMP; v_tariff_id INT; v_price NUMERIC; v_hours NUMERIC; BEGIN SELECT start_time, end_time, tariff_id INTO v_start, v_end, v_tariff_id FROM parking_session WHERE session_id = p_session_id; IF NOT FOUND THEN RAISE EXCEPTION 'Parking session does not exist.'; END IF; IF v_end IS NULL THEN RETURN 0.00; END IF; v_hours := CEIL(EXTRACT(EPOCH FROM (v_end - v_start)) / 3600.0); SELECT price INTO v_price FROM price_list WHERE tariff_id = v_tariff_id AND CURRENT_DATE BETWEEN valid_from AND valid_to ORDER BY valid_from DESC LIMIT 1; IF NOT FOUND THEN RAISE EXCEPTION 'Valid price for tariff does not exist.'; END IF; RETURN ROUND(v_hours * v_price, 2); END; $function$; }}} Оваа функција ја пресметува вкупната цена за дадена паркинг сесија според времетраењето и важечката тарифа. Ја имплементира бизнис логиката за автоматска пресметка на наплата и се користи при ажурирање на вкупниот износ за завршена сесија. == has_active_session == {{{ CREATE OR REPLACE FUNCTION public.has_active_session(p_user_id integer) RETURNS boolean LANGUAGE plpgsql AS $function$ DECLARE v_exists BOOLEAN; BEGIN IF NOT EXISTS ( SELECT 1 FROM app_user WHERE user_id = p_user_id ) THEN RAISE EXCEPTION 'User does not exist.'; END IF; SELECT EXISTS ( SELECT 1 FROM parking_session WHERE user_id = p_user_id AND end_time IS NULL ) INTO v_exists; RETURN v_exists; END; $function$; }}} Оваа функција проверува дали даден корисник има активна паркинг сесија, односно сесија која сè уште нема крајно време. Ја имплементира бизнис логиката за спречување на повеќе активни сесии кај ист корисник и се користи при започнување нова паркинг сесија. == trg_set_total_amount == {{{ CREATE OR REPLACE FUNCTION public.trg_set_total_amount() RETURNS trigger LANGUAGE plpgsql AS $function$ DECLARE v_price NUMERIC; v_hours NUMERIC; BEGIN IF NEW.end_time IS NULL THEN NEW.total_amount := 0.00; ELSE IF NEW.start_time IS NULL THEN RAISE EXCEPTION 'Start time cannot be null.'; END IF; IF NEW.end_time <= NEW.start_time THEN RAISE EXCEPTION 'End time must be after start time.'; END IF; SELECT price INTO v_price FROM price_list WHERE tariff_id = NEW.tariff_id AND CURRENT_DATE BETWEEN valid_from AND valid_to ORDER BY valid_from DESC LIMIT 1; IF NOT FOUND THEN RAISE EXCEPTION 'Valid price for tariff does not exist.'; END IF; v_hours := CEIL(EXTRACT(EPOCH FROM (NEW.end_time - NEW.start_time)) / 3600.0); NEW.total_amount := ROUND(v_hours * v_price, 2); END IF; RETURN NEW; END; $function$; }}} Оваа trigger функција автоматски го поставува или пресметува вкупниот износ на паркинг сесијата при внесување или ажурирање на податоци. Ја имплементира бизнис логиката за автоматска наплата според времетраењето на сесијата и важечката тарифа. = Triggers = == trg_update_total_amount == {{{ CREATE TRIGGER trg_update_total_amount BEFORE INSERT OR UPDATE OF end_time ON public.parking_session FOR EACH ROW EXECUTE FUNCTION trg_set_total_amount(); }}} Овој trigger автоматски се активира при креирање нова паркинг сесија или при промена на крајното време на постоечка сесија. Ја имплементира бизнис логиката за автоматска пресметка на вкупниот износ за паркирање без потреба од рачна интервенција, преку повикување на trigger функцијата trg_set_total_amount().