| | 2 | == Create a booking |
| | 3 | {{{ |
| | 4 | create or replace procedure book_room( |
| | 5 | p_room_id bigint, |
| | 6 | p_guest_id bigint, |
| | 7 | p_check_in_date date, |
| | 8 | p_check_out_date date, |
| | 9 | p_guests_count int |
| | 10 | ) |
| | 11 | as $$ |
| | 12 | declare |
| | 13 | v_total_price numeric; |
| | 14 | begin |
| | 15 | if p_check_out_date <= p_check_in_date then |
| | 16 | raise exception 'Check-out date must be after check-in date'; |
| | 17 | end if; |
| | 18 | |
| | 19 | if fn_is_room_available( |
| | 20 | p_room_id, |
| | 21 | p_check_in_date, |
| | 22 | p_check_out_date |
| | 23 | ) = false then |
| | 24 | raise exception 'Room % is not available for selected dates', p_room_id; |
| | 25 | end if; |
| | 26 | |
| | 27 | if exists ( |
| | 28 | select 1 |
| | 29 | from bookings b |
| | 30 | where b.room_id = p_room_id |
| | 31 | and b.booking_status in ('PENDING', 'CONFIRMED') |
| | 32 | and daterange(b.check_in_date, b.check_out_date, '[)') |
| | 33 | && daterange(p_check_in_date, p_check_out_date, '[)') |
| | 34 | ) then |
| | 35 | raise exception 'Room % is already booked for selected dates', p_room_id; |
| | 36 | end if; |
| | 37 | |
| | 38 | v_total_price := fn_calculate_booking_price( |
| | 39 | p_room_id, |
| | 40 | p_check_in_date, |
| | 41 | p_check_out_date, |
| | 42 | p_guests_count |
| | 43 | ); |
| | 44 | |
| | 45 | insert into bookings ( |
| | 46 | room_id, |
| | 47 | guest_id, |
| | 48 | check_in_date, |
| | 49 | check_out_date, |
| | 50 | guests_count, |
| | 51 | total_price, |
| | 52 | booking_status |
| | 53 | ) |
| | 54 | values ( |
| | 55 | p_room_id, |
| | 56 | p_guest_id, |
| | 57 | p_check_in_date, |
| | 58 | p_check_out_date, |
| | 59 | p_guests_count, |
| | 60 | v_total_price, |
| | 61 | 'PENDING' |
| | 62 | ); |
| | 63 | |
| | 64 | update availability_windows aw |
| | 65 | set status = 'BOOKED' |
| | 66 | where aw.room_id = p_room_id |
| | 67 | and aw.available_date >= p_check_in_date |
| | 68 | and aw.available_date < p_check_out_date; |
| | 69 | |
| | 70 | raise notice 'Booking created successfully. Total price: %', v_total_price; |
| | 71 | end; |
| | 72 | $$ language plpgsql; |
| | 73 | }}} |
| 49 | | create or replace function trg_review_only_completed_booking() |
| 50 | | returns trigger |
| 51 | | as $$ |
| 52 | | declare v_status varchar; |
| 53 | | begin |
| 54 | | select booking_status |
| 55 | | into v_status |
| 56 | | from bookings |
| 57 | | where booking_id = new.booking_id; |
| | 120 | create or replace procedure review_booking(r_booking_id bigint,rating int,comment varchar) |
| | 121 | as $$ |
| | 122 | declare |
| | 123 | v_status varchar; |
| | 124 | guest_id bigint; |
| | 125 | property bigint; |
| | 126 | begin |
| | 127 | select b.booking_status,b.guest_id,r.property_id |
| | 128 | into v_status,guest_id,property |
| | 129 | from bookings b |
| | 130 | join rooms r on b.room_id = r.room_id |
| | 131 | where booking_id = r_booking_id; |
| | 132 | |
| | 133 | |
| | 178 | }}} |
| | 179 | == Create timestamp |
| | 180 | {{{ |
| | 181 | create or replace function fn_planora_make_timestamp(p_date date,p_time time) |
| | 182 | returns timestamp |
| | 183 | language plpgsql |
| | 184 | as $$ |
| | 185 | begin |
| | 186 | return p_date::timestamp + p_time; |
| | 187 | end; |
| | 188 | $$; |
| | 189 | }}} |
| | 190 | == Check if room is available |
| | 191 | {{{ |
| | 192 | |
| | 193 | CREATE OR REPLACE FUNCTION fn_is_room_available( |
| | 194 | p_room_id BIGINT, |
| | 195 | p_check_in_date DATE, |
| | 196 | p_check_out_date DATE |
| | 197 | ) |
| | 198 | RETURNS BOOLEAN |
| | 199 | LANGUAGE plpgsql |
| | 200 | AS $$ |
| | 201 | DECLARE |
| | 202 | v_needed_days INT; |
| | 203 | v_available_days INT; |
| | 204 | BEGIN |
| | 205 | IF p_check_in_date >= p_check_out_date THEN |
| | 206 | RETURN FALSE; |
| | 207 | END IF; |
| | 208 | |
| | 209 | IF NOT EXISTS ( |
| | 210 | SELECT 1 |
| | 211 | FROM rooms r |
| | 212 | WHERE r.room_id = p_room_id |
| | 213 | AND r.status = 'ACTIVE' |
| | 214 | ) THEN |
| | 215 | RETURN FALSE; |
| | 216 | END IF; |
| | 217 | |
| | 218 | v_needed_days := fn_calculate_nights(p_check_in_date := p_check_in_date,p_check_out_date := p_check_out_date ); |
| | 219 | |
| | 220 | SELECT COUNT(*) |
| | 221 | INTO v_available_days |
| | 222 | FROM availability_windows aw |
| | 223 | WHERE aw.room_id = p_room_id |
| | 224 | AND aw.available_date >= p_check_in_date |
| | 225 | AND aw.available_date < p_check_out_date |
| | 226 | AND aw.status = 'AVAILABLE'; |
| | 227 | |
| | 228 | IF v_available_days = v_needed_days THEN |
| | 229 | RETURN TRUE; |
| | 230 | ELSE |
| | 231 | RETURN FALSE; |
| | 232 | END IF; |
| | 233 | END; |
| | 234 | $$; |
| 234 | | IF p_check_out_date <= p_check_in_date THEN |
| 235 | | RAISE EXCEPTION 'Check-out date must be after check-in date'; |
| 236 | | END IF; |
| 237 | | |
| 238 | | IF EXISTS ( |
| 239 | | SELECT 1 |
| 240 | | FROM bookings b |
| 241 | | WHERE b.room_id = p_room_id |
| 242 | | AND b.booking_status IN ('PENDING', 'CONFIRMED') |
| 243 | | AND daterange(b.check_in_date, b.check_out_date, '[)') |
| 244 | | && daterange(p_check_in_date, p_check_out_date, '[)') |
| 245 | | ) THEN |
| 246 | | RAISE EXCEPTION 'Room % is already booked for selected dates', p_room_id; |
| 247 | | END IF; |
| 248 | | |
| 249 | | INSERT INTO bookings ( |
| 250 | | room_id, |
| 251 | | guest_id, |
| 252 | | check_in_date, |
| 253 | | check_out_date, |
| 254 | | guests_count, |
| 255 | | total_price, |
| 256 | | booking_status |
| 257 | | ) |
| 258 | | VALUES ( |
| 259 | | p_room_id, |
| 260 | | p_guest_id, |
| 261 | | p_check_in_date, |
| 262 | | p_check_out_date, |
| 263 | | p_guests_count, |
| 264 | | p_total_price, |
| 265 | | 'PENDING' |
| 266 | | ); |
| 267 | | |
| 268 | | RAISE NOTICE 'Booking created successfully'; |
| | 361 | DELETE FROM availability_windows |
| | 362 | WHERE available_date < CURRENT_DATE; |
| | 363 | |
| | 364 | INSERT INTO availability_windows (room_id,available_date,status) |
| | 365 | SELECT |
| | 366 | r.room_id, |
| | 367 | d.available_day::date, |
| | 368 | 'AVAILABLE' |
| | 369 | FROM rooms r |
| | 370 | CROSS JOIN generate_series( |
| | 371 | CURRENT_DATE, |
| | 372 | CURRENT_DATE + p_days_ahead, |
| | 373 | INTERVAL '1 day' |
| | 374 | ) AS d(available_day) |
| | 375 | WHERE r.status = 'ACTIVE' |
| | 376 | AND NOT EXISTS ( |
| | 377 | SELECT 1 |
| | 378 | FROM bookings b |
| | 379 | WHERE b.room_id = r.room_id |
| | 380 | AND b.booking_status IN ('PENDING', 'CONFIRMED') |
| | 381 | AND daterange(b.check_in_date, b.check_out_date, '[)') |
| | 382 | && daterange(d.available_day::date, d.available_day::date + 1, '[)') |
| | 383 | ) |
| | 384 | AND NOT EXISTS ( |
| | 385 | SELECT 1 |
| | 386 | FROM availability_windows aw |
| | 387 | WHERE aw.room_id = r.room_id |
| | 388 | AND aw.available_date = d.available_day::date |
| | 389 | ); |
| | 390 | |
| | 391 | RAISE NOTICE 'Availability windows refreshed for % days ahead', p_days_ahead; |
| 271 | | }}} |
| | 394 | |
| | 395 | }}} |
| | 396 | == Get user favorite listings |
| | 397 | {{{ |
| | 398 | |
| | 399 | CREATE OR REPLACE FUNCTION get_user_favorite_listings( |
| | 400 | p_user_id BIGINT |
| | 401 | ) |
| | 402 | RETURNS TABLE ( |
| | 403 | property_id BIGINT, |
| | 404 | property_title VARCHAR, |
| | 405 | property_status VARCHAR, |
| | 406 | created_at TIMESTAMP, |
| | 407 | description TEXT, |
| | 408 | city VARCHAR, |
| | 409 | country VARCHAR |
| | 410 | ) |
| | 411 | LANGUAGE plpgsql |
| | 412 | AS $$ |
| | 413 | BEGIN |
| | 414 | RETURN QUERY |
| | 415 | SELECT |
| | 416 | p.property_id, |
| | 417 | p.title::VARCHAR, |
| | 418 | p.status::VARCHAR, |
| | 419 | p.created_at, |
| | 420 | p.description::text, |
| | 421 | a.city::VARCHAR, |
| | 422 | c.country_name::VARCHAR |
| | 423 | FROM favorite_listings f |
| | 424 | LEFT JOIN properties p |
| | 425 | ON p.property_id = f.property_id |
| | 426 | LEFT JOIN addresses a |
| | 427 | ON a.address_id = p.address_id |
| | 428 | LEFT JOIN countries c |
| | 429 | ON c.country_id = a.country_id |
| | 430 | WHERE f.user_id = p_user_id; |
| | 431 | END; |
| | 432 | $$; |
| | 433 | }}} |
| | 434 | == Confirm a booking |
| | 435 | {{{ |
| | 436 | create or replace procedure sp_confirm_booking(p_booking_id bigint) |
| | 437 | as $$ |
| | 438 | declare |
| | 439 | v_status varchar; |
| | 440 | begin |
| | 441 | select b.booking_status |
| | 442 | into v_status |
| | 443 | from bookings b |
| | 444 | where b.booking_id = p_booking_id; |
| | 445 | |
| | 446 | if v_status is null then |
| | 447 | raise exception 'Booking does not exist'; |
| | 448 | end if; |
| | 449 | |
| | 450 | if v_status = 'CONFIRMED' then |
| | 451 | raise exception 'Booking is already confirmed'; |
| | 452 | end if; |
| | 453 | |
| | 454 | if v_status = 'CANCELLED' then |
| | 455 | raise exception 'Cancelled booking cannot be confirmed'; |
| | 456 | end if; |
| | 457 | |
| | 458 | if v_status = 'COMPLETED' then |
| | 459 | raise exception 'Completed booking cannot be confirmed'; |
| | 460 | end if; |
| | 461 | |
| | 462 | update bookings |
| | 463 | set booking_status = 'CONFIRMED' |
| | 464 | where booking_id = p_booking_id; |
| | 465 | |
| | 466 | raise notice 'Booking % confirmed successfully', p_booking_id; |
| | 467 | end; |
| | 468 | $$ language plpgsql; |
| | 469 | }}} |