| 1 |
|
|---|
| 2 | -- FUNCTION: Креирање trip
|
|---|
| 3 |
|
|---|
| 4 | CREATE OR REPLACE FUNCTION fn_create_trip(
|
|---|
| 5 | p_user_id INT,
|
|---|
| 6 | p_city_name VARCHAR,
|
|---|
| 7 | p_start_date DATE,
|
|---|
| 8 | p_end_date DATE,
|
|---|
| 9 | p_total_budget DECIMAL(10,2),
|
|---|
| 10 | p_title VARCHAR DEFAULT NULL
|
|---|
| 11 | )
|
|---|
| 12 | RETURNS INT
|
|---|
| 13 | LANGUAGE plpgsql
|
|---|
| 14 | AS $$
|
|---|
| 15 | DECLARE
|
|---|
| 16 | v_city_id INT;
|
|---|
| 17 | v_status_id INT;
|
|---|
| 18 | v_trip_id INT;
|
|---|
| 19 | BEGIN
|
|---|
| 20 | IF p_start_date IS NULL THEN
|
|---|
| 21 | RAISE EXCEPTION 'Start date is required.';
|
|---|
| 22 | END IF;
|
|---|
| 23 |
|
|---|
| 24 | IF p_end_date IS NULL THEN
|
|---|
| 25 | RAISE EXCEPTION 'End date is required.';
|
|---|
| 26 | END IF;
|
|---|
| 27 |
|
|---|
| 28 | IF p_end_date < p_start_date THEN
|
|---|
| 29 | RAISE EXCEPTION 'End date cannot be before start date.';
|
|---|
| 30 | END IF;
|
|---|
| 31 |
|
|---|
| 32 | IF p_total_budget <= 0 THEN
|
|---|
| 33 | RAISE EXCEPTION 'Budget must be greater than 0.';
|
|---|
| 34 | END IF;
|
|---|
| 35 |
|
|---|
| 36 | SELECT city_id
|
|---|
| 37 | INTO v_city_id
|
|---|
| 38 | FROM city
|
|---|
| 39 | WHERE LOWER(name) = LOWER(p_city_name)
|
|---|
| 40 | LIMIT 1;
|
|---|
| 41 |
|
|---|
| 42 | IF v_city_id IS NULL THEN
|
|---|
| 43 | RAISE EXCEPTION 'City "%" does not exist.', p_city_name;
|
|---|
| 44 | END IF;
|
|---|
| 45 |
|
|---|
| 46 | IF NOT EXISTS (
|
|---|
| 47 | SELECT 1
|
|---|
| 48 | FROM app_user
|
|---|
| 49 | WHERE user_id = p_user_id
|
|---|
| 50 | ) THEN
|
|---|
| 51 | RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
|
|---|
| 52 | END IF;
|
|---|
| 53 |
|
|---|
| 54 | INSERT INTO trip_status (
|
|---|
| 55 | user_id,
|
|---|
| 56 | status_name,
|
|---|
| 57 | status_comment,
|
|---|
| 58 | updated_at
|
|---|
| 59 | )
|
|---|
| 60 | VALUES (
|
|---|
| 61 | p_user_id,
|
|---|
| 62 | 'in_progress',
|
|---|
| 63 | 'Trip created',
|
|---|
| 64 | NOW()
|
|---|
| 65 | )
|
|---|
| 66 | RETURNING status_id INTO v_status_id;
|
|---|
| 67 |
|
|---|
| 68 | INSERT INTO trip (
|
|---|
| 69 | user_id,
|
|---|
| 70 | city_id,
|
|---|
| 71 | status_id,
|
|---|
| 72 | history_id,
|
|---|
| 73 | title,
|
|---|
| 74 | start_date,
|
|---|
| 75 | end_date,
|
|---|
| 76 | total_budget,
|
|---|
| 77 | estimated_cost
|
|---|
| 78 | )
|
|---|
| 79 | VALUES (
|
|---|
| 80 | p_user_id,
|
|---|
| 81 | v_city_id,
|
|---|
| 82 | v_status_id,
|
|---|
| 83 | NULL,
|
|---|
| 84 | COALESCE(p_title, 'Trip to ' || p_city_name),
|
|---|
| 85 | p_start_date,
|
|---|
| 86 | p_end_date,
|
|---|
| 87 | p_total_budget,
|
|---|
| 88 | 0
|
|---|
| 89 | )
|
|---|
| 90 | RETURNING trip_id INTO v_trip_id;
|
|---|
| 91 |
|
|---|
| 92 | RETURN v_trip_id;
|
|---|
| 93 | END;
|
|---|
| 94 | $$;
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 | -- test
|
|---|
| 98 |
|
|---|
| 99 | SELECT fn_create_trip(
|
|---|
| 100 | 1,
|
|---|
| 101 | 'City_7',
|
|---|
| 102 | '2026-08-01',
|
|---|
| 103 | '2026-08-05',
|
|---|
| 104 | 700.00,
|
|---|
| 105 | 'August Skopje Trip'
|
|---|
| 106 | ) AS new_trip_id;
|
|---|
| 107 |
|
|---|
| 108 | SELECT *
|
|---|
| 109 | FROM trip
|
|---|
| 110 | WHERE trip_id = 4523757 ;
|
|---|
| 111 |
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 | -- FUNCTION: Најди trip_id
|
|---|
| 115 |
|
|---|
| 116 | CREATE OR REPLACE FUNCTION fn_find_trip_id(
|
|---|
| 117 | p_username VARCHAR,
|
|---|
| 118 | p_city_name VARCHAR,
|
|---|
| 119 | p_start_date DATE,
|
|---|
| 120 | p_end_date DATE DEFAULT NULL,
|
|---|
| 121 | p_title VARCHAR DEFAULT NULL
|
|---|
| 122 | )
|
|---|
| 123 | RETURNS INT
|
|---|
| 124 | LANGUAGE plpgsql
|
|---|
| 125 | AS $$
|
|---|
| 126 | DECLARE
|
|---|
| 127 | v_trip_id INT;
|
|---|
| 128 | BEGIN
|
|---|
| 129 | SELECT t.trip_id
|
|---|
| 130 | INTO v_trip_id
|
|---|
| 131 | FROM trip t
|
|---|
| 132 | JOIN app_user u ON t.user_id = u.user_id
|
|---|
| 133 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 134 | WHERE LOWER(u.username) = LOWER(p_username)
|
|---|
| 135 | AND LOWER(c.name) = LOWER(p_city_name)
|
|---|
| 136 | AND t.start_date = p_start_date
|
|---|
| 137 | AND (p_end_date IS NULL OR t.end_date = p_end_date)
|
|---|
| 138 | AND (
|
|---|
| 139 | p_title IS NULL
|
|---|
| 140 | OR LOWER(t.title) = LOWER(p_title)
|
|---|
| 141 | )
|
|---|
| 142 | ORDER BY t.trip_id DESC
|
|---|
| 143 | LIMIT 1;
|
|---|
| 144 |
|
|---|
| 145 | IF v_trip_id IS NULL THEN
|
|---|
| 146 | RAISE EXCEPTION 'Trip for user "%", city "%", start date "%", title "%" does not exist.',
|
|---|
| 147 | p_username, p_city_name, p_start_date, p_title;
|
|---|
| 148 | END IF;
|
|---|
| 149 |
|
|---|
| 150 | RETURN v_trip_id;
|
|---|
| 151 | END;
|
|---|
| 152 | $$;
|
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 | -- test
|
|---|
| 156 |
|
|---|
| 157 | SELECT
|
|---|
| 158 | CASE
|
|---|
| 159 | WHEN fn_find_trip_id(
|
|---|
| 160 | u.username,
|
|---|
| 161 | c.name,
|
|---|
| 162 | t.start_date,
|
|---|
| 163 | t.end_date,
|
|---|
| 164 | t.title
|
|---|
| 165 | ) = t.trip_id
|
|---|
| 166 | THEN 'TEST PASSED'
|
|---|
| 167 | ELSE 'TEST FAILED'
|
|---|
| 168 | END AS test_result,
|
|---|
| 169 | t.trip_id
|
|---|
| 170 | FROM trip t
|
|---|
| 171 | JOIN app_user u ON t.user_id = u.user_id
|
|---|
| 172 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 173 | LIMIT 5;
|
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 |
|
|---|
| 177 | -- FUNCTION: Пресметување вкупен estimated cost за trip
|
|---|
| 178 |
|
|---|
| 179 | CREATE OR REPLACE FUNCTION fn_calculate_trip_estimated_cost(
|
|---|
| 180 | p_username VARCHAR,
|
|---|
| 181 | p_city_name VARCHAR,
|
|---|
| 182 | p_start_date DATE,
|
|---|
| 183 | p_end_date DATE DEFAULT NULL,
|
|---|
| 184 | p_title VARCHAR DEFAULT NULL
|
|---|
| 185 | )
|
|---|
| 186 | RETURNS DECIMAL(10,2)
|
|---|
| 187 | LANGUAGE plpgsql
|
|---|
| 188 | AS $$
|
|---|
| 189 | DECLARE
|
|---|
| 190 | v_trip_id INT;
|
|---|
| 191 | v_total_cost DECIMAL(10,2);
|
|---|
| 192 | BEGIN
|
|---|
| 193 | v_trip_id := fn_find_trip_id(
|
|---|
| 194 | p_username,
|
|---|
| 195 | p_city_name,
|
|---|
| 196 | p_start_date,
|
|---|
| 197 | p_end_date,
|
|---|
| 198 | p_title
|
|---|
| 199 | );
|
|---|
| 200 |
|
|---|
| 201 | SELECT COALESCE(SUM(pi.estimated_cost), 0)
|
|---|
| 202 | INTO v_total_cost
|
|---|
| 203 | FROM plan_item pi
|
|---|
| 204 | JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
|
|---|
| 205 | WHERE td.trip_id = v_trip_id;
|
|---|
| 206 |
|
|---|
| 207 | RETURN v_total_cost;
|
|---|
| 208 | END;
|
|---|
| 209 | $$;
|
|---|
| 210 |
|
|---|
| 211 |
|
|---|
| 212 | -- test
|
|---|
| 213 |
|
|---|
| 214 | SELECT
|
|---|
| 215 | u.username,
|
|---|
| 216 | c.name AS city_name,
|
|---|
| 217 | t.start_date,
|
|---|
| 218 | t.end_date,
|
|---|
| 219 | t.title,
|
|---|
| 220 | t.trip_id
|
|---|
| 221 | FROM trip t
|
|---|
| 222 | JOIN app_user u ON t.user_id = u.user_id
|
|---|
| 223 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 224 | ORDER BY t.trip_id DESC
|
|---|
| 225 | LIMIT 10;
|
|---|
| 226 |
|
|---|
| 227 |
|
|---|
| 228 |
|
|---|
| 229 | -- PROCEDURE: Додавање trip member
|
|---|
| 230 |
|
|---|
| 231 | CREATE OR REPLACE PROCEDURE pr_add_trip_member(
|
|---|
| 232 | p_owner_username VARCHAR,
|
|---|
| 233 | p_city_name VARCHAR,
|
|---|
| 234 | p_start_date DATE,
|
|---|
| 235 | p_member_username VARCHAR,
|
|---|
| 236 | p_title VARCHAR DEFAULT NULL
|
|---|
| 237 | )
|
|---|
| 238 | LANGUAGE plpgsql
|
|---|
| 239 | AS $$
|
|---|
| 240 | DECLARE
|
|---|
| 241 | v_trip_id INT;
|
|---|
| 242 | v_member_user_id INT;
|
|---|
| 243 | v_member_first_name VARCHAR;
|
|---|
| 244 | v_member_last_name VARCHAR;
|
|---|
| 245 | v_trip_member_id INT;
|
|---|
| 246 | BEGIN
|
|---|
| 247 | -- 1. Најди го trip_id
|
|---|
| 248 | v_trip_id := fn_find_trip_id(
|
|---|
| 249 | p_owner_username,
|
|---|
| 250 | p_city_name,
|
|---|
| 251 | p_start_date,
|
|---|
| 252 | NULL,
|
|---|
| 253 | p_title
|
|---|
| 254 | );
|
|---|
| 255 |
|
|---|
| 256 | -- 2. Најди user податоци за member username
|
|---|
| 257 | SELECT
|
|---|
| 258 | user_id,
|
|---|
| 259 | first_name,
|
|---|
| 260 | last_name
|
|---|
| 261 | INTO
|
|---|
| 262 | v_member_user_id,
|
|---|
| 263 | v_member_first_name,
|
|---|
| 264 | v_member_last_name
|
|---|
| 265 | FROM app_user
|
|---|
| 266 | WHERE LOWER(username) = LOWER(p_member_username);
|
|---|
| 267 |
|
|---|
| 268 | IF v_member_user_id IS NULL THEN
|
|---|
| 269 | RAISE EXCEPTION 'Member user with username "%" does not exist.', p_member_username;
|
|---|
| 270 | END IF;
|
|---|
| 271 |
|
|---|
| 272 | -- 3. Провери дали member веќе е додаден во trip
|
|---|
| 273 | IF EXISTS (
|
|---|
| 274 | SELECT 1
|
|---|
| 275 | FROM trip_member
|
|---|
| 276 | WHERE trip_id = v_trip_id
|
|---|
| 277 | AND user_id = v_member_user_id
|
|---|
| 278 | ) THEN
|
|---|
| 279 | RAISE EXCEPTION 'User "%" is already a member of trip %.',
|
|---|
| 280 | p_member_username, v_trip_id;
|
|---|
| 281 | END IF;
|
|---|
| 282 |
|
|---|
| 283 | -- 4. Додај member во trip
|
|---|
| 284 | INSERT INTO trip_member (
|
|---|
| 285 | trip_id,
|
|---|
| 286 | user_id,
|
|---|
| 287 | first_name,
|
|---|
| 288 | last_name,
|
|---|
| 289 | username,
|
|---|
| 290 | role
|
|---|
| 291 | )
|
|---|
| 292 | VALUES (
|
|---|
| 293 | v_trip_id,
|
|---|
| 294 | v_member_user_id,
|
|---|
| 295 | v_member_first_name,
|
|---|
| 296 | v_member_last_name,
|
|---|
| 297 | p_member_username,
|
|---|
| 298 | 'member'
|
|---|
| 299 | )
|
|---|
| 300 | RETURNING trip_member_id INTO v_trip_member_id;
|
|---|
| 301 |
|
|---|
| 302 | RAISE NOTICE 'User "%" added to trip "%" successfully. Trip member id: %',
|
|---|
| 303 | p_member_username, v_trip_id, v_trip_member_id;
|
|---|
| 304 | END;
|
|---|
| 305 | $$;
|
|---|
| 306 |
|
|---|
| 307 | CALL pr_add_trip_member(
|
|---|
| 308 | 'user_1',
|
|---|
| 309 | 'City_1',
|
|---|
| 310 | '2026-05-16',
|
|---|
| 311 | 'user_64192',
|
|---|
| 312 | Null
|
|---|
| 313 | );
|
|---|
| 314 |
|
|---|
| 315 |
|
|---|
| 316 | -- test
|
|---|
| 317 |
|
|---|
| 318 | SELECT
|
|---|
| 319 | tm.trip_member_id,
|
|---|
| 320 | tm.trip_id,
|
|---|
| 321 | tm.user_id,
|
|---|
| 322 | tm.first_name,
|
|---|
| 323 | tm.last_name,
|
|---|
| 324 | tm.username,
|
|---|
| 325 | tm.role,
|
|---|
| 326 | t.title,
|
|---|
| 327 | t.start_date,
|
|---|
| 328 | c.name AS city_name
|
|---|
| 329 | FROM trip_member tm
|
|---|
| 330 | JOIN trip t ON tm.trip_id = t.trip_id
|
|---|
| 331 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 332 | ORDER BY tm.trip_member_id DESC
|
|---|
| 333 | LIMIT 5;
|
|---|
| 334 |
|
|---|
| 335 |
|
|---|
| 336 |
|
|---|
| 337 | -- PROCEDURE: Додавање plan item во trip
|
|---|
| 338 |
|
|---|
| 339 | CREATE OR REPLACE PROCEDURE pr_add_plan_item_to_trip(
|
|---|
| 340 | p_username VARCHAR,
|
|---|
| 341 | p_city_name VARCHAR,
|
|---|
| 342 | p_start_date DATE,
|
|---|
| 343 | p_day_number INT,
|
|---|
| 344 | p_place_name VARCHAR,
|
|---|
| 345 | p_start_time TIME,
|
|---|
| 346 | p_end_time TIME,
|
|---|
| 347 | p_estimated_cost DECIMAL(10,2),
|
|---|
| 348 | p_notes TEXT DEFAULT NULL,
|
|---|
| 349 | p_title VARCHAR DEFAULT NULL
|
|---|
| 350 | )
|
|---|
| 351 | LANGUAGE plpgsql
|
|---|
| 352 | AS $$
|
|---|
| 353 | DECLARE
|
|---|
| 354 | v_trip_id INT;
|
|---|
| 355 | v_trip_day_id INT;
|
|---|
| 356 | v_place_id INT;
|
|---|
| 357 | v_schedule_time_id INT;
|
|---|
| 358 | v_plan_item_id INT;
|
|---|
| 359 | v_current_estimated_cost DECIMAL(10,2);
|
|---|
| 360 | v_total_budget DECIMAL(10,2);
|
|---|
| 361 | BEGIN
|
|---|
| 362 | IF p_day_number <= 0 THEN
|
|---|
| 363 | RAISE EXCEPTION 'Day number must be greater than 0.';
|
|---|
| 364 | END IF;
|
|---|
| 365 |
|
|---|
| 366 | IF p_end_time <= p_start_time THEN
|
|---|
| 367 | RAISE EXCEPTION 'End time must be after start time.';
|
|---|
| 368 | END IF;
|
|---|
| 369 |
|
|---|
| 370 | IF p_estimated_cost < 0 THEN
|
|---|
| 371 | RAISE EXCEPTION 'Estimated cost cannot be negative.';
|
|---|
| 372 | END IF;
|
|---|
| 373 |
|
|---|
| 374 | v_trip_id := fn_find_trip_id(
|
|---|
| 375 | p_username,
|
|---|
| 376 | p_city_name,
|
|---|
| 377 | p_start_date,
|
|---|
| 378 | NULL,
|
|---|
| 379 | p_title
|
|---|
| 380 | );
|
|---|
| 381 |
|
|---|
| 382 | SELECT trip_day_id
|
|---|
| 383 | INTO v_trip_day_id
|
|---|
| 384 | FROM trip_day
|
|---|
| 385 | WHERE trip_id = v_trip_id
|
|---|
| 386 | AND day_number = p_day_number;
|
|---|
| 387 |
|
|---|
| 388 | IF v_trip_day_id IS NULL THEN
|
|---|
| 389 | RAISE EXCEPTION 'Day % does not exist for this trip.', p_day_number;
|
|---|
| 390 | END IF;
|
|---|
| 391 |
|
|---|
| 392 | SELECT p.place_id
|
|---|
| 393 | INTO v_place_id
|
|---|
| 394 | FROM place p
|
|---|
| 395 | JOIN trip t ON p.city_id = t.city_id
|
|---|
| 396 | WHERE t.trip_id = v_trip_id
|
|---|
| 397 | AND LOWER(p.name) = LOWER(p_place_name)
|
|---|
| 398 | LIMIT 1;
|
|---|
| 399 |
|
|---|
| 400 | IF v_place_id IS NULL THEN
|
|---|
| 401 | RAISE EXCEPTION 'Place "%" does not exist in the trip city.', p_place_name;
|
|---|
| 402 | END IF;
|
|---|
| 403 |
|
|---|
| 404 | INSERT INTO schedule_time (
|
|---|
| 405 | scheduled_start_time,
|
|---|
| 406 | scheduled_end_time
|
|---|
| 407 | )
|
|---|
| 408 | VALUES (
|
|---|
| 409 | p_start_time,
|
|---|
| 410 | p_end_time
|
|---|
| 411 | )
|
|---|
| 412 | RETURNING schedule_time_id INTO v_schedule_time_id;
|
|---|
| 413 |
|
|---|
| 414 | INSERT INTO plan_item (
|
|---|
| 415 | trip_day_id,
|
|---|
| 416 | place_id,
|
|---|
| 417 | schedule_time_id,
|
|---|
| 418 | estimated_cost,
|
|---|
| 419 | notes
|
|---|
| 420 | )
|
|---|
| 421 | VALUES (
|
|---|
| 422 | v_trip_day_id,
|
|---|
| 423 | v_place_id,
|
|---|
| 424 | v_schedule_time_id,
|
|---|
| 425 | p_estimated_cost,
|
|---|
| 426 | p_notes
|
|---|
| 427 | )
|
|---|
| 428 | RETURNING plan_item_id INTO v_plan_item_id;
|
|---|
| 429 |
|
|---|
| 430 | SELECT COALESCE(SUM(pi.estimated_cost), 0)
|
|---|
| 431 | INTO v_current_estimated_cost
|
|---|
| 432 | FROM plan_item pi
|
|---|
| 433 | JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
|
|---|
| 434 | WHERE td.trip_id = v_trip_id;
|
|---|
| 435 |
|
|---|
| 436 | SELECT total_budget
|
|---|
| 437 | INTO v_total_budget
|
|---|
| 438 | FROM trip
|
|---|
| 439 | WHERE trip_id = v_trip_id;
|
|---|
| 440 |
|
|---|
| 441 | IF v_current_estimated_cost >= v_total_budget THEN
|
|---|
| 442 | RAISE EXCEPTION 'Cannot update trip estimated cost because it reaches/exceeds total budget.';
|
|---|
| 443 | END IF;
|
|---|
| 444 |
|
|---|
| 445 | UPDATE trip
|
|---|
| 446 | SET estimated_cost = v_current_estimated_cost
|
|---|
| 447 | WHERE trip_id = v_trip_id;
|
|---|
| 448 |
|
|---|
| 449 | RAISE NOTICE 'Plan item added successfully. Plan item id: %. New trip estimated cost: %',
|
|---|
| 450 | v_plan_item_id, v_current_estimated_cost;
|
|---|
| 451 | END;
|
|---|
| 452 | $$;
|
|---|
| 453 |
|
|---|
| 454 |
|
|---|
| 455 | CALL pr_add_plan_item_to_trip(
|
|---|
| 456 | 'user_1',
|
|---|
| 457 | 'City_1',
|
|---|
| 458 | '2026-05-16',
|
|---|
| 459 | 1,
|
|---|
| 460 | 'Place_1068',
|
|---|
| 461 | '10:00',
|
|---|
| 462 | '12:00',
|
|---|
| 463 | 100,
|
|---|
| 464 | 'Visit and take photos',
|
|---|
| 465 | Null
|
|---|
| 466 | );
|
|---|
| 467 |
|
|---|
| 468 |
|
|---|
| 469 | -- test
|
|---|
| 470 |
|
|---|
| 471 | SELECT
|
|---|
| 472 | u.user_id,
|
|---|
| 473 | u.username,
|
|---|
| 474 | c.city_id,
|
|---|
| 475 | c.name AS city_name,
|
|---|
| 476 | t.trip_id,
|
|---|
| 477 | t.title,
|
|---|
| 478 | t.start_date,
|
|---|
| 479 | t.end_date,
|
|---|
| 480 | t.total_budget,
|
|---|
| 481 | t.estimated_cost,
|
|---|
| 482 | td.trip_day_id,
|
|---|
| 483 | td.day_number,
|
|---|
| 484 | p.place_id,
|
|---|
| 485 | p.name AS place_name
|
|---|
| 486 | FROM app_user u
|
|---|
| 487 | JOIN trip t ON u.user_id = t.user_id
|
|---|
| 488 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 489 | JOIN trip_day td ON t.trip_id = td.trip_id
|
|---|
| 490 | JOIN place p ON p.city_id = c.city_id
|
|---|
| 491 | WHERE LOWER(u.username) = LOWER('user_1')
|
|---|
| 492 | AND LOWER(c.name) = LOWER('City_1')
|
|---|
| 493 | AND t.start_date = '2026-05-16'
|
|---|
| 494 | AND td.day_number = 1
|
|---|
| 495 | AND LOWER(p.name) = LOWER('Place_1068');
|
|---|
| 496 |
|
|---|
| 497 |
|
|---|
| 498 |
|
|---|
| 499 | -- PROCEDURE: Додавање place во favourites
|
|---|
| 500 |
|
|---|
| 501 | CREATE OR REPLACE PROCEDURE pr_add_place_to_favourites(
|
|---|
| 502 | p_username VARCHAR,
|
|---|
| 503 | p_place_name VARCHAR
|
|---|
| 504 | )
|
|---|
| 505 | LANGUAGE plpgsql
|
|---|
| 506 | AS $$
|
|---|
| 507 | DECLARE
|
|---|
| 508 | v_user_id INT;
|
|---|
| 509 | v_place_id INT;
|
|---|
| 510 | BEGIN
|
|---|
| 511 | SELECT user_id
|
|---|
| 512 | INTO v_user_id
|
|---|
| 513 | FROM app_user
|
|---|
| 514 | WHERE LOWER(username) = LOWER(p_username);
|
|---|
| 515 |
|
|---|
| 516 | IF v_user_id IS NULL THEN
|
|---|
| 517 | RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
|
|---|
| 518 | END IF;
|
|---|
| 519 |
|
|---|
| 520 | SELECT place_id
|
|---|
| 521 | INTO v_place_id
|
|---|
| 522 | FROM place
|
|---|
| 523 | WHERE LOWER(name) = LOWER(p_place_name)
|
|---|
| 524 | LIMIT 1;
|
|---|
| 525 |
|
|---|
| 526 | IF v_place_id IS NULL THEN
|
|---|
| 527 | RAISE EXCEPTION 'Place "%" does not exist.', p_place_name;
|
|---|
| 528 | END IF;
|
|---|
| 529 |
|
|---|
| 530 | IF EXISTS (
|
|---|
| 531 | SELECT 1
|
|---|
| 532 | FROM favourites
|
|---|
| 533 | WHERE user_id = v_user_id
|
|---|
| 534 | AND place_id = v_place_id
|
|---|
| 535 | ) THEN
|
|---|
| 536 | RAISE EXCEPTION 'Place "%" is already in favourites for user "%".',
|
|---|
| 537 | p_place_name, p_username;
|
|---|
| 538 | END IF;
|
|---|
| 539 |
|
|---|
| 540 | INSERT INTO favourites (
|
|---|
| 541 | user_id,
|
|---|
| 542 | place_id
|
|---|
| 543 | )
|
|---|
| 544 | VALUES (
|
|---|
| 545 | v_user_id,
|
|---|
| 546 | v_place_id
|
|---|
| 547 | );
|
|---|
| 548 |
|
|---|
| 549 | RAISE NOTICE 'Place "%" added to favourites for user "%".',
|
|---|
| 550 | p_place_name, p_username;
|
|---|
| 551 | END;
|
|---|
| 552 | $$;
|
|---|
| 553 |
|
|---|
| 554 |
|
|---|
| 555 | CALL pr_add_place_to_favourites(
|
|---|
| 556 | 'user_417155',
|
|---|
| 557 | 'Place_854864'
|
|---|
| 558 | );
|
|---|
| 559 |
|
|---|
| 560 |
|
|---|
| 561 | -- test
|
|---|
| 562 |
|
|---|
| 563 | SELECT
|
|---|
| 564 | f.user_id,
|
|---|
| 565 | u.username,
|
|---|
| 566 | f.place_id,
|
|---|
| 567 | p.name AS place_name
|
|---|
| 568 | FROM favourites f
|
|---|
| 569 | JOIN app_user u ON f.user_id = u.user_id
|
|---|
| 570 | JOIN place p ON f.place_id = p.place_id
|
|---|
| 571 | WHERE LOWER(u.username) = LOWER('user_417155')
|
|---|
| 572 | AND LOWER(p.name) = LOWER('Place_854864');
|
|---|
| 573 |
|
|---|
| 574 |
|
|---|
| 575 |
|
|---|
| 576 | -- PROCEDURE: Додавање review за place од trip
|
|---|
| 577 |
|
|---|
| 578 | CREATE OR REPLACE PROCEDURE pr_add_review_for_trip_place(
|
|---|
| 579 | p_username VARCHAR,
|
|---|
| 580 | p_city_name VARCHAR,
|
|---|
| 581 | p_start_date DATE,
|
|---|
| 582 | p_day_number INT,
|
|---|
| 583 | p_place_name VARCHAR,
|
|---|
| 584 | p_rating DECIMAL(3,2),
|
|---|
| 585 | p_comment TEXT DEFAULT NULL,
|
|---|
| 586 | p_title VARCHAR DEFAULT NULL
|
|---|
| 587 | )
|
|---|
| 588 | LANGUAGE plpgsql
|
|---|
| 589 | AS $$
|
|---|
| 590 | DECLARE
|
|---|
| 591 | v_user_id INT;
|
|---|
| 592 | v_trip_id INT;
|
|---|
| 593 | v_plan_item_id INT;
|
|---|
| 594 | v_review_id INT;
|
|---|
| 595 | BEGIN
|
|---|
| 596 | IF p_rating < 1 OR p_rating > 5 THEN
|
|---|
| 597 | RAISE EXCEPTION 'Rating must be between 1 and 5.';
|
|---|
| 598 | END IF;
|
|---|
| 599 |
|
|---|
| 600 | SELECT user_id
|
|---|
| 601 | INTO v_user_id
|
|---|
| 602 | FROM app_user
|
|---|
| 603 | WHERE LOWER(username) = LOWER(p_username);
|
|---|
| 604 |
|
|---|
| 605 | IF v_user_id IS NULL THEN
|
|---|
| 606 | RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
|
|---|
| 607 | END IF;
|
|---|
| 608 |
|
|---|
| 609 | v_trip_id := fn_find_trip_id(
|
|---|
| 610 | p_username,
|
|---|
| 611 | p_city_name,
|
|---|
| 612 | p_start_date,
|
|---|
| 613 | NULL,
|
|---|
| 614 | p_title
|
|---|
| 615 | );
|
|---|
| 616 |
|
|---|
| 617 | SELECT pi.plan_item_id
|
|---|
| 618 | INTO v_plan_item_id
|
|---|
| 619 | FROM plan_item pi
|
|---|
| 620 | JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
|
|---|
| 621 | JOIN place p ON pi.place_id = p.place_id
|
|---|
| 622 | WHERE td.trip_id = v_trip_id
|
|---|
| 623 | AND td.day_number = p_day_number
|
|---|
| 624 | AND LOWER(p.name) = LOWER(p_place_name)
|
|---|
| 625 | LIMIT 1;
|
|---|
| 626 |
|
|---|
| 627 | IF v_plan_item_id IS NULL THEN
|
|---|
| 628 | RAISE EXCEPTION 'Plan item for place "%" on day % does not exist.',
|
|---|
| 629 | p_place_name, p_day_number;
|
|---|
| 630 | END IF;
|
|---|
| 631 |
|
|---|
| 632 | INSERT INTO review (
|
|---|
| 633 | user_id,
|
|---|
| 634 | plan_item_id,
|
|---|
| 635 | rating,
|
|---|
| 636 | comment,
|
|---|
| 637 | visited_at,
|
|---|
| 638 | created_at
|
|---|
| 639 | )
|
|---|
| 640 | VALUES (
|
|---|
| 641 | v_user_id,
|
|---|
| 642 | v_plan_item_id,
|
|---|
| 643 | p_rating,
|
|---|
| 644 | p_comment,
|
|---|
| 645 | NOW(),
|
|---|
| 646 | NOW()
|
|---|
| 647 | )
|
|---|
| 648 | RETURNING review_id INTO v_review_id;
|
|---|
| 649 |
|
|---|
| 650 | RAISE NOTICE 'Review added successfully. Review id: %', v_review_id;
|
|---|
| 651 | END;
|
|---|
| 652 | $$;
|
|---|
| 653 |
|
|---|
| 654 |
|
|---|
| 655 | CALL pr_add_review_for_trip_place(
|
|---|
| 656 | 'user_417155',
|
|---|
| 657 | 'City_246',
|
|---|
| 658 | '2025-11-10',
|
|---|
| 659 | 2,
|
|---|
| 660 | 'Place_854864',
|
|---|
| 661 | 3,
|
|---|
| 662 | NULL,
|
|---|
| 663 | 'Trip_1999999'
|
|---|
| 664 | );
|
|---|
| 665 |
|
|---|
| 666 |
|
|---|
| 667 | -- test
|
|---|
| 668 |
|
|---|
| 669 | SELECT
|
|---|
| 670 | CASE
|
|---|
| 671 | WHEN p.rating = ROUND(AVG(r.rating), 2)
|
|---|
| 672 | THEN 'TEST PASSED: place.rating is correctly updated'
|
|---|
| 673 | ELSE 'TEST FAILED: place.rating is not equal to average review rating'
|
|---|
| 674 | END AS test_result,
|
|---|
| 675 | p.place_id,
|
|---|
| 676 | p.name AS place_name,
|
|---|
| 677 | p.rating AS current_place_rating,
|
|---|
| 678 | ROUND(AVG(r.rating), 2) AS calculated_avg_rating,
|
|---|
| 679 | COUNT(r.review_id) AS total_reviews
|
|---|
| 680 | FROM place p
|
|---|
| 681 | JOIN plan_item pi ON p.place_id = pi.place_id
|
|---|
| 682 | LEFT JOIN review r ON pi.plan_item_id = r.plan_item_id
|
|---|
| 683 | WHERE p.place_id = (
|
|---|
| 684 | SELECT pi2.place_id
|
|---|
| 685 | FROM review r2
|
|---|
| 686 | JOIN plan_item pi2 ON r2.plan_item_id = pi2.plan_item_id
|
|---|
| 687 | ORDER BY r2.review_id DESC
|
|---|
| 688 | LIMIT 1
|
|---|
| 689 | )
|
|---|
| 690 | GROUP BY
|
|---|
| 691 | p.place_id,
|
|---|
| 692 | p.name,
|
|---|
| 693 | p.rating;
|
|---|
| 694 |
|
|---|
| 695 |
|
|---|
| 696 |
|
|---|
| 697 | -- PROCEDURE: Комплетирање trip и креирање trip history
|
|---|
| 698 |
|
|---|
| 699 | CREATE OR REPLACE PROCEDURE pr_complete_trip(
|
|---|
| 700 | p_username VARCHAR,
|
|---|
| 701 | p_city_name VARCHAR,
|
|---|
| 702 | p_start_date DATE,
|
|---|
| 703 | p_end_date DATE DEFAULT NULL,
|
|---|
| 704 | p_title VARCHAR DEFAULT NULL,
|
|---|
| 705 | p_notes TEXT DEFAULT 'Trip completed successfully'
|
|---|
| 706 | )
|
|---|
| 707 | LANGUAGE plpgsql
|
|---|
| 708 | AS $$
|
|---|
| 709 | DECLARE
|
|---|
| 710 | v_trip_id INT;
|
|---|
| 711 | v_user_id INT;
|
|---|
| 712 | v_total_spent DECIMAL(10,2);
|
|---|
| 713 | v_history_id INT;
|
|---|
| 714 | v_status_id INT;
|
|---|
| 715 | BEGIN
|
|---|
| 716 | SELECT user_id
|
|---|
| 717 | INTO v_user_id
|
|---|
| 718 | FROM app_user
|
|---|
| 719 | WHERE LOWER(username) = LOWER(p_username);
|
|---|
| 720 |
|
|---|
| 721 | IF v_user_id IS NULL THEN
|
|---|
| 722 | RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
|
|---|
| 723 | END IF;
|
|---|
| 724 |
|
|---|
| 725 | v_trip_id := fn_find_trip_id(
|
|---|
| 726 | p_username,
|
|---|
| 727 | p_city_name,
|
|---|
| 728 | p_start_date,
|
|---|
| 729 | p_end_date,
|
|---|
| 730 | p_title
|
|---|
| 731 | );
|
|---|
| 732 |
|
|---|
| 733 | SELECT estimated_cost
|
|---|
| 734 | INTO v_total_spent
|
|---|
| 735 | FROM trip
|
|---|
| 736 | WHERE trip_id = v_trip_id;
|
|---|
| 737 |
|
|---|
| 738 | IF v_total_spent IS NULL THEN
|
|---|
| 739 | RAISE EXCEPTION 'Trip not found.';
|
|---|
| 740 | END IF;
|
|---|
| 741 |
|
|---|
| 742 | INSERT INTO trip_history (
|
|---|
| 743 | user_id,
|
|---|
| 744 | total_spent,
|
|---|
| 745 | notes,
|
|---|
| 746 | completed_at
|
|---|
| 747 | )
|
|---|
| 748 | VALUES (
|
|---|
| 749 | v_user_id,
|
|---|
| 750 | v_total_spent,
|
|---|
| 751 | p_notes,
|
|---|
| 752 | NOW()
|
|---|
| 753 | )
|
|---|
| 754 | RETURNING history_id INTO v_history_id;
|
|---|
| 755 |
|
|---|
| 756 | INSERT INTO trip_status (
|
|---|
| 757 | user_id,
|
|---|
| 758 | status_name,
|
|---|
| 759 | status_comment,
|
|---|
| 760 | updated_at
|
|---|
| 761 | )
|
|---|
| 762 | VALUES (
|
|---|
| 763 | v_user_id,
|
|---|
| 764 | 'completed',
|
|---|
| 765 | 'Trip marked as completed',
|
|---|
| 766 | NOW()
|
|---|
| 767 | )
|
|---|
| 768 | RETURNING status_id INTO v_status_id;
|
|---|
| 769 |
|
|---|
| 770 | UPDATE trip
|
|---|
| 771 | SET history_id = v_history_id,
|
|---|
| 772 | status_id = v_status_id
|
|---|
| 773 | WHERE trip_id = v_trip_id;
|
|---|
| 774 |
|
|---|
| 775 | RAISE NOTICE 'Trip % completed successfully. History id: %, Status id: %',
|
|---|
| 776 | v_trip_id, v_history_id, v_status_id;
|
|---|
| 777 | END;
|
|---|
| 778 | $$;
|
|---|
| 779 |
|
|---|
| 780 |
|
|---|
| 781 |
|
|---|
| 782 | CALL pr_complete_trip(
|
|---|
| 783 | 'user_88113',
|
|---|
| 784 | 'City_119',
|
|---|
| 785 | '2025-12-07',
|
|---|
| 786 | '2025-12-18',
|
|---|
| 787 | NULL,
|
|---|
| 788 | 'Trip completed successfully'
|
|---|
| 789 | );
|
|---|
| 790 |
|
|---|
| 791 |
|
|---|
| 792 | -- test
|
|---|
| 793 |
|
|---|
| 794 | SELECT
|
|---|
| 795 | CASE
|
|---|
| 796 | WHEN ts.status_name = 'completed'
|
|---|
| 797 | AND th.history_id IS NOT NULL
|
|---|
| 798 | THEN 'TEST PASSED: Trip is completed'
|
|---|
| 799 | ELSE 'TEST FAILED: Trip is not completed'
|
|---|
| 800 | END AS test_result,
|
|---|
| 801 | t.trip_id,
|
|---|
| 802 | ts.status_name,
|
|---|
| 803 | th.history_id,
|
|---|
| 804 | th.total_spent,
|
|---|
| 805 | th.notes
|
|---|
| 806 | FROM trip t
|
|---|
| 807 | JOIN app_user u ON t.user_id = u.user_id
|
|---|
| 808 | JOIN city c ON t.city_id = c.city_id
|
|---|
| 809 | JOIN trip_status ts ON t.status_id = ts.status_id
|
|---|
| 810 | JOIN trip_history th ON t.history_id = th.history_id
|
|---|
| 811 | WHERE LOWER(u.username) = LOWER('user_88113')
|
|---|
| 812 | AND LOWER(c.name) = LOWER('City_119')
|
|---|
| 813 | AND t.start_date = '2025-12-07'
|
|---|
| 814 | AND t.end_date = '2025-12-18';
|
|---|
| 815 |
|
|---|
| 816 |
|
|---|
| 817 |
|
|---|
| 818 |
|
|---|
| 819 | -- TRIGGER FUNCTION: Ажурирање rating на place кога ќе се додаде/промени/избрише review
|
|---|
| 820 |
|
|---|
| 821 | CREATE OR REPLACE FUNCTION fn_update_place_rating_after_review()
|
|---|
| 822 | RETURNS TRIGGER
|
|---|
| 823 | LANGUAGE plpgsql
|
|---|
| 824 | AS $$
|
|---|
| 825 | DECLARE
|
|---|
| 826 | v_place_id INT;
|
|---|
| 827 | v_avg_rating DECIMAL(3,2);
|
|---|
| 828 | BEGIN
|
|---|
| 829 | -- Ако е DELETE, земаме OLD.plan_item_id
|
|---|
| 830 | IF TG_OP = 'DELETE' THEN
|
|---|
| 831 | SELECT pi.place_id
|
|---|
| 832 | INTO v_place_id
|
|---|
| 833 | FROM plan_item pi
|
|---|
| 834 | WHERE pi.plan_item_id = OLD.plan_item_id;
|
|---|
| 835 | ELSE
|
|---|
| 836 | SELECT pi.place_id
|
|---|
| 837 | INTO v_place_id
|
|---|
| 838 | FROM plan_item pi
|
|---|
| 839 | WHERE pi.plan_item_id = NEW.plan_item_id;
|
|---|
| 840 | END IF;
|
|---|
| 841 |
|
|---|
| 842 | IF v_place_id IS NULL THEN
|
|---|
| 843 | RETURN COALESCE(NEW, OLD);
|
|---|
| 844 | END IF;
|
|---|
| 845 |
|
|---|
| 846 | SELECT COALESCE(ROUND(AVG(r.rating), 2), 0)
|
|---|
| 847 | INTO v_avg_rating
|
|---|
| 848 | FROM review r
|
|---|
| 849 | JOIN plan_item pi ON r.plan_item_id = pi.plan_item_id
|
|---|
| 850 | WHERE pi.place_id = v_place_id;
|
|---|
| 851 |
|
|---|
| 852 | UPDATE place
|
|---|
| 853 | SET rating = v_avg_rating
|
|---|
| 854 | WHERE place_id = v_place_id;
|
|---|
| 855 |
|
|---|
| 856 | RAISE NOTICE 'Place % rating updated to %.', v_place_id, v_avg_rating;
|
|---|
| 857 |
|
|---|
| 858 | RETURN COALESCE(NEW, OLD);
|
|---|
| 859 | END;
|
|---|
| 860 | $$;
|
|---|
| 861 |
|
|---|
| 862 |
|
|---|
| 863 |
|
|---|
| 864 | -- DROP стар trigger ако постои
|
|---|
| 865 |
|
|---|
| 866 | DROP TRIGGER IF EXISTS trg_update_place_rating_after_review ON review;
|
|---|
| 867 |
|
|---|
| 868 |
|
|---|
| 869 |
|
|---|
| 870 | -- CREATE TRIGGER
|
|---|
| 871 |
|
|---|
| 872 |
|
|---|
| 873 | CREATE TRIGGER trg_update_place_rating_after_review
|
|---|
| 874 | AFTER INSERT OR UPDATE OR DELETE ON review
|
|---|
| 875 | FOR EACH ROW
|
|---|
| 876 | EXECUTE FUNCTION fn_update_place_rating_after_review();
|
|---|
| 877 |
|
|---|
| 878 |
|
|---|
| 879 | -- test
|
|---|
| 880 |
|
|---|
| 881 | SELECT
|
|---|
| 882 | pi.plan_item_id,
|
|---|
| 883 | pi.place_id,
|
|---|
| 884 | p.name,
|
|---|
| 885 | p.rating
|
|---|
| 886 | FROM plan_item pi
|
|---|
| 887 | JOIN place p ON pi.place_id = p.place_id
|
|---|
| 888 | ORDER BY pi.plan_item_id
|
|---|
| 889 | LIMIT 10; |
|---|