| 70 | | == Only an available room can be booked |
| 71 | | {{{ |
| 72 | | |
| 73 | | create or replace function trg_room_is_available_for_booking() |
| 74 | | returns trigger |
| 75 | | as $$ |
| 76 | | begin |
| 77 | | if new.check_in_date > new.check_out_date then |
| 78 | | raise exception 'Booking start date cannot be after end date'; |
| 79 | | end if; |
| 80 | | |
| 81 | | |
| 82 | | if not exists( |
| 83 | | select 1 |
| | 70 | == Check if a room is available |
| | 71 | {{{ |
| | 72 | create or replace function fn_is_room_available(p_room_id bigint,p_check_in_date date, p_check_out_date date) |
| | 73 | returns boolean |
| | 74 | language plpgsql |
| | 75 | as $$ |
| | 76 | begin |
| | 77 | if p_check_in_date >= p_check_out_date then |
| | 78 | return false; |
| | 79 | end if; |
| | 80 | |
| | 81 | if not exists( |
| | 82 | select 1 |
| | 83 | from rooms r |
| | 84 | where r.room_id = p_room_id |
| | 85 | and r.status = 'ACTIVE' |
| | 86 | )then |
| | 87 | return false; |
| | 88 | end if; |
| | 89 | |
| | 90 | if exists( |
| | 91 | select 1 |
| | 92 | from bookings b |
| | 93 | where b.room_id = p_room_id |
| | 94 | and b.booking_status in ('PENDING','CONFIRMED') |
| | 95 | and daterange(b.check_in_date,b.check_out_date,'[)') |
| | 96 | && daterange(p_check_in_date,p_check_out_date,'[)') |
| | 97 | )then |
| | 98 | return false; |
| | 99 | end if; |
| | 100 | return true; |
| | 101 | end; |
| | 102 | $$; |
| | 103 | }}} |
| | 104 | == Calculate the number of nights stayed |
| | 105 | {{{ |
| | 106 | create or replace function fn_calculate_nights(p_check_in_date date, p_check_out_date date) |
| | 107 | returns int |
| | 108 | language plpgsql |
| | 109 | as $$ |
| | 110 | begin |
| | 111 | if p_check_in_date >= p_check_out_date then |
| | 112 | raise exception 'Check out must be after check in'; |
| | 113 | end if; |
| | 114 | return p_check_out_date - p_check_in_date; |
| | 115 | end; |
| | 116 | $$; |
| | 117 | }}} |
| | 118 | == Calculate the total price of a booking |
| | 119 | {{{ |
| | 120 | create or replace function fn_calculate_booking_price(p_room_id bigint, p_check_in_date date,p_check_out_date date, |
| | 121 | p_guest_count int) |
| | 122 | returns numeric |
| | 123 | language plpgsql |
| | 124 | as $$ |
| | 125 | declare |
| | 126 | v_price_per_night numeric; |
| | 127 | v_capacity int; |
| | 128 | v_extra_capacity int; |
| | 129 | v_extra_guest_price numeric; |
| | 130 | |
| | 131 | v_nights int; |
| | 132 | v_room_total numeric; |
| | 133 | v_amenities_total numeric; |
| | 134 | v_extra_guests int; |
| | 135 | v_extra_guest_total numeric; |
| | 136 | v_total numeric; |
| | 137 | begin |
| | 138 | select |
| | 139 | r.price_per_night, |
| | 140 | r.capacity, |
| | 141 | coalesce(r.extra_capacity,0), |
| | 142 | coalesce(r.extra_guest_price,0) |
| | 143 | into |
| | 144 | v_price_per_night, |
| | 145 | v_capacity, |
| | 146 | v_extra_capacity, |
| | 147 | v_extra_guest_price |
| 85 | | where r.room_id = new.room_id |
| 86 | | and r.status = 'ACTIVE' |
| 87 | | )then |
| 88 | | raise exception 'Room is not active'; |
| 89 | | end if; |
| 90 | | |
| 91 | | if exists( |
| 92 | | select 1 |
| 93 | | from bookings b |
| 94 | | where b.room_id = new.room_id |
| 95 | | and b.booking_status in ('PENDING','CONFIRMED') |
| 96 | | and b.booking_id <> new.booking_id |
| 97 | | and ( |
| 98 | | (new.check_in_date,new.check_out_date) |
| 99 | | overlaps |
| 100 | | (b.check_in_date,b.check_out_date) |
| 101 | | ) |
| 102 | | )then |
| 103 | | raise exception 'Room is already booked'; |
| 104 | | end if; |
| 105 | | |
| 106 | | return new; |
| 107 | | |
| 108 | | end; |
| 109 | | $$ language plpgsql; |
| 110 | | create or replace trigger book_only_available_room |
| 111 | | before insert or update on bookings for each row |
| 112 | | execute function trg_room_is_available_for_booking(); |
| 113 | | }}} |
| | 149 | where r.room_id = p_room_id; |
| | 150 | |
| | 151 | if p_check_in_date>=p_check_out_date then |
| | 152 | raise exception 'Check out must be after check in'; |
| | 153 | end if; |
| | 154 | v_nights := p_check_in_date - p_check_out_date; |
| | 155 | if p_guest_count > v_capacity + v_extra_capacity then |
| | 156 | raise exception 'Guest count exceeds'; |
| | 157 | end if; |
| | 158 | v_room_total := v_price_per_night * v_nights; |
| | 159 | |
| | 160 | select coalesce(sum(a.price),0) |
| | 161 | into v_amenities_total |
| | 162 | from room_amenities ra |
| | 163 | join amenities a on a.amenity_id = ra.amenity_id |
| | 164 | where ra.room_id = p_room_id |
| | 165 | and a.is_included = false; |
| | 166 | |
| | 167 | v_extra_guests := greatest(p_guest_count-v_capacity,0); |
| | 168 | v_extra_guest_total := v_extra_guests*v_extra_guest_price*v_nights; |
| | 169 | |
| | 170 | v_total := v_room_total + v_extra_guest_total +v_amenities_total; |
| | 171 | return v_total; |
| | 172 | |
| | 173 | end; |
| | 174 | $$; |
| | 175 | }}} |
| | 176 | == Cancel a booking |
| | 177 | {{{ |
| | 178 | create or replace procedure sp_cancel_booking(p_booking_id bigint) |
| | 179 | language plpgsql |
| | 180 | as $$ |
| | 181 | declare |
| | 182 | v_status varchar; |
| | 183 | begin |
| | 184 | select b.booking_status |
| | 185 | into v_status |
| | 186 | from bookings b |
| | 187 | where booking_id = p_booking_id; |
| | 188 | |
| | 189 | if v_status is null then |
| | 190 | raise exception 'Booking does not exsists'; |
| | 191 | end if; |
| | 192 | |
| | 193 | if v_status = 'COMPLETED' then |
| | 194 | raise exception 'Completed booking cannot be canceled'; |
| | 195 | end if; |
| | 196 | if v_status = 'CANCELLED' then |
| | 197 | raise exception 'The booking is already cancelled'; |
| | 198 | end if; |
| | 199 | |
| | 200 | update bookings |
| | 201 | set booking_status = 'CANCELLED' |
| | 202 | where booking_id = p_booking_id; |
| | 203 | |
| | 204 | end; |
| | 205 | $$; |
| | 206 | }}} |
| | 207 | == Complete old bookings |
| | 208 | {{{ |
| | 209 | create or replace procedure sp_complete_finished_bookings() |
| | 210 | language plpgsql |
| | 211 | as $$ |
| | 212 | begin |
| | 213 | update bookings |
| | 214 | set booking_status = 'COMPLETED' |
| | 215 | where booking_status = 'CONFIRMED' |
| | 216 | and check_out_date < current_date; |
| | 217 | end; |
| | 218 | $$; |
| | 219 | }}} |