| | 1 | = Procedures = |
| | 2 | |
| | 3 | == create_parking_reservation == |
| | 4 | |
| | 5 | {{{ |
| | 6 | CREATE OR REPLACE PROCEDURE public.create_parking_reservation( |
| | 7 | IN p_user_id integer, |
| | 8 | IN p_vehicle_id integer, |
| | 9 | IN p_parking_id integer, |
| | 10 | IN p_reservation_status_id integer, |
| | 11 | IN p_start_time timestamp without time zone, |
| | 12 | IN p_end_time timestamp without time zone |
| | 13 | ) |
| | 14 | LANGUAGE plpgsql |
| | 15 | AS $procedure$ |
| | 16 | BEGIN |
| | 17 | IF NOT EXISTS (SELECT 1 FROM app_user WHERE user_id = p_user_id) THEN |
| | 18 | RAISE EXCEPTION 'User does not exist.'; |
| | 19 | END IF; |
| | 20 | |
| | 21 | IF NOT EXISTS ( |
| | 22 | SELECT 1 FROM vehicle |
| | 23 | WHERE vehicle_id = p_vehicle_id AND user_id = p_user_id |
| | 24 | ) THEN |
| | 25 | RAISE EXCEPTION 'Vehicle does not exist or does not belong to this user.'; |
| | 26 | END IF; |
| | 27 | |
| | 28 | IF NOT EXISTS (SELECT 1 FROM parking WHERE parking_id = p_parking_id) THEN |
| | 29 | RAISE EXCEPTION 'Parking does not exist.'; |
| | 30 | END IF; |
| | 31 | |
| | 32 | IF NOT EXISTS ( |
| | 33 | SELECT 1 FROM parking |
| | 34 | WHERE parking_id = p_parking_id AND supports_reservation = true |
| | 35 | ) THEN |
| | 36 | RAISE EXCEPTION 'Parking does not support reservations.'; |
| | 37 | END IF; |
| | 38 | |
| | 39 | IF p_start_time >= p_end_time THEN |
| | 40 | RAISE EXCEPTION 'Start time must be before end time.'; |
| | 41 | END IF; |
| | 42 | |
| | 43 | IF NOT EXISTS ( |
| | 44 | SELECT 1 FROM parking_spot |
| | 45 | WHERE parking_id = p_parking_id AND status = 'free' |
| | 46 | ) THEN |
| | 47 | RAISE EXCEPTION 'No free parking spots available.'; |
| | 48 | END IF; |
| | 49 | |
| | 50 | INSERT INTO reservation ( |
| | 51 | user_id, |
| | 52 | vehicle_id, |
| | 53 | parking_id, |
| | 54 | reservation_status_id, |
| | 55 | start_time, |
| | 56 | end_time, |
| | 57 | reservation_code |
| | 58 | ) |
| | 59 | VALUES ( |
| | 60 | p_user_id, |
| | 61 | p_vehicle_id, |
| | 62 | p_parking_id, |
| | 63 | p_reservation_status_id, |
| | 64 | p_start_time, |
| | 65 | p_end_time, |
| | 66 | 'RES-' || p_user_id || '-' || EXTRACT(EPOCH FROM NOW())::INT |
| | 67 | ); |
| | 68 | |
| | 69 | UPDATE parking_spot |
| | 70 | SET status = 'reserved' |
| | 71 | WHERE parking_spot_id = ( |
| | 72 | SELECT parking_spot_id |
| | 73 | FROM parking_spot |
| | 74 | WHERE parking_id = p_parking_id AND status = 'free' |
| | 75 | LIMIT 1 |
| | 76 | ); |
| | 77 | END; |
| | 78 | $procedure$; |
| | 79 | }}} |
| | 80 | |
| | 81 | Оваа процедура креира нова паркинг резервација само доколку се исполнети сите потребни услови, како постоење на корисник, возило, паркинг и слободно место. |
| | 82 | Ја имплементира бизнис логиката за управување со резервации и автоматски резервира слободно паркинг место. |
| | 83 | |
| | 84 | == cancel_reservation == |
| | 85 | |
| | 86 | {{{ |
| | 87 | CREATE OR REPLACE PROCEDURE public.cancel_reservation( |
| | 88 | IN p_reservation_id integer |
| | 89 | ) |
| | 90 | LANGUAGE plpgsql |
| | 91 | AS $procedure$ |
| | 92 | BEGIN |
| | 93 | IF NOT EXISTS ( |
| | 94 | SELECT 1 FROM reservation |
| | 95 | WHERE reservation_id = p_reservation_id |
| | 96 | ) THEN |
| | 97 | RAISE EXCEPTION 'Reservation does not exist.'; |
| | 98 | END IF; |
| | 99 | |
| | 100 | IF EXISTS ( |
| | 101 | SELECT 1 FROM reservation |
| | 102 | WHERE reservation_id = p_reservation_id |
| | 103 | AND reservation_status = 'cancelled' |
| | 104 | ) THEN |
| | 105 | RAISE EXCEPTION 'Reservation is already cancelled.'; |
| | 106 | END IF; |
| | 107 | |
| | 108 | IF EXISTS ( |
| | 109 | SELECT 1 FROM reservation |
| | 110 | WHERE reservation_id = p_reservation_id |
| | 111 | AND reservation_status = 'completed' |
| | 112 | ) THEN |
| | 113 | RAISE EXCEPTION 'Completed reservation cannot be cancelled.'; |
| | 114 | END IF; |
| | 115 | |
| | 116 | UPDATE reservation |
| | 117 | SET reservation_status = 'cancelled' |
| | 118 | WHERE reservation_id = p_reservation_id; |
| | 119 | END; |
| | 120 | $procedure$; |
| | 121 | }}} |
| | 122 | |
| | 123 | Оваа процедура овозможува откажување на постоечка резервација, при што спречува откажување на веќе завршени или претходно откажани резервации. |
| | 124 | Ја имплементира бизнис логиката за контрола и правилно управување со статусите на резервациите. |
| | 125 | |
| | 126 | == complete_reservation == |
| | 127 | |
| | 128 | {{{ |
| | 129 | CREATE OR REPLACE PROCEDURE public.complete_reservation( |
| | 130 | IN p_reservation_id integer |
| | 131 | ) |
| | 132 | LANGUAGE plpgsql |
| | 133 | AS $procedure$ |
| | 134 | BEGIN |
| | 135 | IF NOT EXISTS ( |
| | 136 | SELECT 1 FROM reservation |
| | 137 | WHERE reservation_id = p_reservation_id |
| | 138 | ) THEN |
| | 139 | RAISE EXCEPTION 'Reservation does not exist.'; |
| | 140 | END IF; |
| | 141 | |
| | 142 | IF EXISTS ( |
| | 143 | SELECT 1 FROM reservation |
| | 144 | WHERE reservation_id = p_reservation_id |
| | 145 | AND reservation_status = 'completed' |
| | 146 | ) THEN |
| | 147 | RAISE EXCEPTION 'Reservation is already completed.'; |
| | 148 | END IF; |
| | 149 | |
| | 150 | IF EXISTS ( |
| | 151 | SELECT 1 FROM reservation |
| | 152 | WHERE reservation_id = p_reservation_id |
| | 153 | AND reservation_status = 'cancelled' |
| | 154 | ) THEN |
| | 155 | RAISE EXCEPTION 'Cancelled reservation cannot be completed.'; |
| | 156 | END IF; |
| | 157 | |
| | 158 | UPDATE reservation |
| | 159 | SET reservation_status = 'completed' |
| | 160 | WHERE reservation_id = p_reservation_id; |
| | 161 | END; |
| | 162 | $procedure$; |
| | 163 | }}} |
| | 164 | |
| | 165 | Оваа процедура ја означува резервацијата како завршена, при што врши проверки за валидност на тековниот статус. |
| | 166 | Ја имплементира бизнис логиката за правилно завршување на процесот на резервација и спречување нелогични промени. |
| | 167 | |
| | 168 | == end_parking_session == |
| | 169 | |
| | 170 | {{{ |
| | 171 | CREATE OR REPLACE PROCEDURE public.end_parking_session( |
| | 172 | IN p_session_id integer, |
| | 173 | IN p_session_status_id integer |
| | 174 | ) |
| | 175 | LANGUAGE plpgsql |
| | 176 | AS $procedure$ |
| | 177 | BEGIN |
| | 178 | IF NOT EXISTS (SELECT 1 FROM parking_session WHERE session_id = p_session_id) THEN |
| | 179 | RAISE EXCEPTION 'Parking session does not exist.'; |
| | 180 | END IF; |
| | 181 | |
| | 182 | IF EXISTS ( |
| | 183 | SELECT 1 FROM parking_session |
| | 184 | WHERE session_id = p_session_id AND end_time IS NOT NULL |
| | 185 | ) THEN |
| | 186 | RAISE EXCEPTION 'Parking session is already finished.'; |
| | 187 | END IF; |
| | 188 | |
| | 189 | IF NOT EXISTS ( |
| | 190 | SELECT 1 FROM session_status |
| | 191 | WHERE session_status_id = p_session_status_id |
| | 192 | ) THEN |
| | 193 | RAISE EXCEPTION 'Session status does not exist.'; |
| | 194 | END IF; |
| | 195 | |
| | 196 | UPDATE parking_session |
| | 197 | SET end_time = NOW(), |
| | 198 | session_status_id = p_session_status_id |
| | 199 | WHERE session_id = p_session_id; |
| | 200 | END; |
| | 201 | $procedure$; |
| | 202 | }}} |
| | 203 | |
| | 204 | Оваа процедура ја завршува активната паркинг сесија со поставување на крајно време и ажурирање на нејзиниот статус. |
| | 205 | Ја имплементира бизнис логиката за правилно затворање на паркинг сесиите и спречување повторно завршување на веќе затворени сесии. |
| | 206 | |
| | 207 | == fill_total_amount == |
| | 208 | |
| | 209 | {{{ |
| | 210 | CREATE OR REPLACE PROCEDURE public.fill_total_amount( |
| | 211 | IN p_session_id integer |
| | 212 | ) |
| | 213 | LANGUAGE plpgsql |
| | 214 | AS $procedure$ |
| | 215 | BEGIN |
| | 216 | IF NOT EXISTS (SELECT 1 FROM parking_session WHERE session_id = p_session_id) THEN |
| | 217 | RAISE EXCEPTION 'Parking session does not exist.'; |
| | 218 | END IF; |
| | 219 | |
| | 220 | IF EXISTS ( |
| | 221 | SELECT 1 FROM parking_session |
| | 222 | WHERE session_id = p_session_id AND end_time IS NULL |
| | 223 | ) THEN |
| | 224 | RAISE EXCEPTION 'Parking session is still active.'; |
| | 225 | END IF; |
| | 226 | |
| | 227 | UPDATE parking_session |
| | 228 | SET total_amount = calculate_session_amount(p_session_id) |
| | 229 | WHERE session_id = p_session_id; |
| | 230 | END; |
| | 231 | $procedure$; |
| | 232 | }}} |
| | 233 | |
| | 234 | Оваа процедура го пресметува и ажурира вкупниот износ за завршена паркинг сесија користејќи функција за пресметка на цена. |
| | 235 | Ја имплементира бизнис логиката за автоматска наплата според времетраењето и тарифата на паркирањето. |
| | 236 | |
| | 237 | == start_parking_session == |
| | 238 | |
| | 239 | {{{ |
| | 240 | CREATE OR REPLACE PROCEDURE public.start_parking_session( |
| | 241 | IN p_user_id integer, |
| | 242 | IN p_vehicle_id integer, |
| | 243 | IN p_parking_id integer, |
| | 244 | IN p_tariff_id integer, |
| | 245 | IN p_session_status_id integer |
| | 246 | ) |
| | 247 | LANGUAGE plpgsql |
| | 248 | AS $procedure$ |
| | 249 | BEGIN |
| | 250 | IF NOT EXISTS (SELECT 1 FROM app_user WHERE user_id = p_user_id) THEN |
| | 251 | RAISE EXCEPTION 'User does not exist.'; |
| | 252 | END IF; |
| | 253 | |
| | 254 | IF NOT EXISTS ( |
| | 255 | SELECT 1 FROM vehicle |
| | 256 | WHERE vehicle_id = p_vehicle_id AND user_id = p_user_id |
| | 257 | ) THEN |
| | 258 | RAISE EXCEPTION 'Vehicle does not exist or does not belong to this user.'; |
| | 259 | END IF; |
| | 260 | |
| | 261 | IF NOT EXISTS (SELECT 1 FROM parking WHERE parking_id = p_parking_id) THEN |
| | 262 | RAISE EXCEPTION 'Parking does not exist.'; |
| | 263 | END IF; |
| | 264 | |
| | 265 | IF NOT EXISTS (SELECT 1 FROM tariff WHERE tariff_id = p_tariff_id) THEN |
| | 266 | RAISE EXCEPTION 'Tariff does not exist.'; |
| | 267 | END IF; |
| | 268 | |
| | 269 | IF NOT EXISTS ( |
| | 270 | SELECT 1 FROM session_status WHERE session_status_id = p_session_status_id |
| | 271 | ) THEN |
| | 272 | RAISE EXCEPTION 'Session status does not exist.'; |
| | 273 | END IF; |
| | 274 | |
| | 275 | IF has_active_session(p_user_id) THEN |
| | 276 | RAISE EXCEPTION 'User already has an active parking session.'; |
| | 277 | END IF; |
| | 278 | |
| | 279 | IF NOT EXISTS ( |
| | 280 | SELECT 1 FROM parking_spot |
| | 281 | WHERE parking_id = p_parking_id AND status = 'free' |
| | 282 | ) THEN |
| | 283 | RAISE EXCEPTION 'No free parking spots available.'; |
| | 284 | END IF; |
| | 285 | |
| | 286 | INSERT INTO parking_session ( |
| | 287 | user_id, |
| | 288 | vehicle_id, |
| | 289 | parking_id, |
| | 290 | tariff_id, |
| | 291 | session_status_id, |
| | 292 | start_time, |
| | 293 | end_time, |
| | 294 | total_amount |
| | 295 | ) |
| | 296 | VALUES ( |
| | 297 | p_user_id, |
| | 298 | p_vehicle_id, |
| | 299 | p_parking_id, |
| | 300 | p_tariff_id, |
| | 301 | p_session_status_id, |
| | 302 | NOW(), |
| | 303 | NULL, |
| | 304 | 0.00 |
| | 305 | ); |
| | 306 | |
| | 307 | UPDATE parking_spot |
| | 308 | SET status = 'occupied' |
| | 309 | WHERE parking_spot_id = ( |
| | 310 | SELECT parking_spot_id |
| | 311 | FROM parking_spot |
| | 312 | WHERE parking_id = p_parking_id AND status = 'free' |
| | 313 | LIMIT 1 |
| | 314 | ); |
| | 315 | END; |
| | 316 | $procedure$; |
| | 317 | }}} |
| | 318 | |
| | 319 | Оваа процедура започнува нова паркинг сесија доколку корисникот ги исполнува сите услови и има достапно слободно место. |
| | 320 | Ја имплементира бизнис логиката за иницирање на паркирање, контрола на активни сесии и автоматско зафаќање на паркинг место. |
| | 321 | |
| | 322 | = Functions = |
| | 323 | |
| | 324 | == calculate_session_amount == |
| | 325 | |
| | 326 | {{{ |
| | 327 | CREATE OR REPLACE FUNCTION public.calculate_session_amount(p_session_id integer) |
| | 328 | RETURNS numeric |
| | 329 | LANGUAGE plpgsql |
| | 330 | AS $function$ |
| | 331 | DECLARE |
| | 332 | v_start TIMESTAMP; |
| | 333 | v_end TIMESTAMP; |
| | 334 | v_tariff_id INT; |
| | 335 | v_price NUMERIC; |
| | 336 | v_hours NUMERIC; |
| | 337 | BEGIN |
| | 338 | SELECT start_time, end_time, tariff_id |
| | 339 | INTO v_start, v_end, v_tariff_id |
| | 340 | FROM parking_session |
| | 341 | WHERE session_id = p_session_id; |
| | 342 | |
| | 343 | IF NOT FOUND THEN |
| | 344 | RAISE EXCEPTION 'Parking session does not exist.'; |
| | 345 | END IF; |
| | 346 | |
| | 347 | IF v_end IS NULL THEN |
| | 348 | RETURN 0.00; |
| | 349 | END IF; |
| | 350 | |
| | 351 | v_hours := CEIL(EXTRACT(EPOCH FROM (v_end - v_start)) / 3600.0); |
| | 352 | |
| | 353 | SELECT price |
| | 354 | INTO v_price |
| | 355 | FROM price_list |
| | 356 | WHERE tariff_id = v_tariff_id |
| | 357 | AND CURRENT_DATE BETWEEN valid_from AND valid_to |
| | 358 | ORDER BY valid_from DESC |
| | 359 | LIMIT 1; |
| | 360 | |
| | 361 | IF NOT FOUND THEN |
| | 362 | RAISE EXCEPTION 'Valid price for tariff does not exist.'; |
| | 363 | END IF; |
| | 364 | |
| | 365 | RETURN ROUND(v_hours * v_price, 2); |
| | 366 | END; |
| | 367 | $function$; |
| | 368 | }}} |
| | 369 | |
| | 370 | Оваа функција ја пресметува вкупната цена за дадена паркинг сесија според времетраењето и важечката тарифа. |
| | 371 | Ја имплементира бизнис логиката за автоматска пресметка на наплата и се користи при ажурирање на вкупниот износ за завршена сесија. |
| | 372 | |
| | 373 | == has_active_session == |
| | 374 | |
| | 375 | {{{ |
| | 376 | CREATE OR REPLACE FUNCTION public.has_active_session(p_user_id integer) |
| | 377 | RETURNS boolean |
| | 378 | LANGUAGE plpgsql |
| | 379 | AS $function$ |
| | 380 | DECLARE |
| | 381 | v_exists BOOLEAN; |
| | 382 | BEGIN |
| | 383 | IF NOT EXISTS ( |
| | 384 | SELECT 1 |
| | 385 | FROM app_user |
| | 386 | WHERE user_id = p_user_id |
| | 387 | ) THEN |
| | 388 | RAISE EXCEPTION 'User does not exist.'; |
| | 389 | END IF; |
| | 390 | |
| | 391 | SELECT EXISTS ( |
| | 392 | SELECT 1 |
| | 393 | FROM parking_session |
| | 394 | WHERE user_id = p_user_id |
| | 395 | AND end_time IS NULL |
| | 396 | ) |
| | 397 | INTO v_exists; |
| | 398 | |
| | 399 | RETURN v_exists; |
| | 400 | END; |
| | 401 | $function$; |
| | 402 | }}} |
| | 403 | |
| | 404 | Оваа функција проверува дали даден корисник има активна паркинг сесија, односно сесија која сè уште нема крајно време. |
| | 405 | Ја имплементира бизнис логиката за спречување на повеќе активни сесии кај ист корисник и се користи при започнување нова паркинг сесија. |
| | 406 | |
| | 407 | == trg_set_total_amount == |
| | 408 | |
| | 409 | {{{ |
| | 410 | CREATE OR REPLACE FUNCTION public.trg_set_total_amount() |
| | 411 | RETURNS trigger |
| | 412 | LANGUAGE plpgsql |
| | 413 | AS $function$ |
| | 414 | DECLARE |
| | 415 | v_price NUMERIC; |
| | 416 | v_hours NUMERIC; |
| | 417 | BEGIN |
| | 418 | IF NEW.end_time IS NULL THEN |
| | 419 | NEW.total_amount := 0.00; |
| | 420 | ELSE |
| | 421 | IF NEW.start_time IS NULL THEN |
| | 422 | RAISE EXCEPTION 'Start time cannot be null.'; |
| | 423 | END IF; |
| | 424 | |
| | 425 | IF NEW.end_time <= NEW.start_time THEN |
| | 426 | RAISE EXCEPTION 'End time must be after start time.'; |
| | 427 | END IF; |
| | 428 | |
| | 429 | SELECT price |
| | 430 | INTO v_price |
| | 431 | FROM price_list |
| | 432 | WHERE tariff_id = NEW.tariff_id |
| | 433 | AND CURRENT_DATE BETWEEN valid_from AND valid_to |
| | 434 | ORDER BY valid_from DESC |
| | 435 | LIMIT 1; |
| | 436 | |
| | 437 | IF NOT FOUND THEN |
| | 438 | RAISE EXCEPTION 'Valid price for tariff does not exist.'; |
| | 439 | END IF; |
| | 440 | |
| | 441 | v_hours := CEIL(EXTRACT(EPOCH FROM (NEW.end_time - NEW.start_time)) / 3600.0); |
| | 442 | |
| | 443 | NEW.total_amount := ROUND(v_hours * v_price, 2); |
| | 444 | END IF; |
| | 445 | |
| | 446 | RETURN NEW; |
| | 447 | END; |
| | 448 | $function$; |
| | 449 | }}} |
| | 450 | |
| | 451 | Оваа trigger функција автоматски го поставува или пресметува вкупниот износ на паркинг сесијата при внесување или ажурирање на податоци. |
| | 452 | Ја имплементира бизнис логиката за автоматска наплата според времетраењето на сесијата и важечката тарифа. |
| | 453 | |
| | 454 | = Triggers = |
| | 455 | |
| | 456 | == trg_update_total_amount == |
| | 457 | |
| | 458 | {{{ |
| | 459 | CREATE TRIGGER trg_update_total_amount |
| | 460 | BEFORE INSERT OR UPDATE OF end_time |
| | 461 | ON public.parking_session |
| | 462 | FOR EACH ROW |
| | 463 | EXECUTE FUNCTION trg_set_total_amount(); |
| | 464 | }}} |
| | 465 | |
| | 466 | Овој trigger автоматски се активира при креирање нова паркинг сесија или при промена на крајното време на постоечка сесија. |
| | 467 | Ја имплементира бизнис логиката за автоматска пресметка на вкупниот износ за паркирање без потреба од рачна интервенција, преку повикување на trigger функцијата trg_set_total_amount(). |