Changes between Initial Version and Version 1 of DatabaseProgramming


Ignore:
Timestamp:
06/12/26 20:44:37 (7 days ago)
Author:
231138
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseProgramming

    v1 v1  
     1= Напредни бази на податоци =
     2= Фаза 5 — Функции, процедури и тригери =
     3== Проект: DriveNet ==
     4
     5Александар Милошевски 231138
     6Исидора Кузмановска 231052
     7Андон Михајлов 231016
     8
     9Во оваа фаза ги имплементираме функциите, процедурите и тригерите кои ја покриваат основната бизнис логика на апликацијата DriveNet. Секоја имплементација е поврзана со реален сценарио од апликацијата.
     10
     11----
     12
     13= ФУНКЦИИ =
     14
     15=== 1. calculate_booking_fare ===
     16
     17==== Опис:
     18
     19Ја пресметува вкупната цена за одредена резервација врз основа на сегментите кои патникот ги поминува и бројот на патници на секој сегмент. Цената се дели рамномерно меѓу сите патници на секој сегмент — доколку на еден сегмент патуваат 3 патници, секој плаќа 1/3 од цената на тој сегмент.
     20
     21==== Сигнатура:
     22{{{
     23calculate_booking_fare(p_booking_id INT) RETURNS DECIMAL(10,2)
     24}}}
     25
     26==== Влезни параметри:
     27
     28|| **Параметар** || **Тип** || **Опис** ||
     29|| `p_booking_id` || INT || ID на резервацијата ||
     30
     31==== Логика:
     32
     33 1. Го наоѓа `ride_id` и `price_per_km` преку `bookings JOIN rides`
     34 2. За секој сегмент во `passenger_segments` каде `is_present = TRUE` ја пресметува цената: `(distance_km × price_per_km) / passengers_on_segment`
     35 3. Ги сумира сите сегменти и го враќа вкупниот износ
     36
     37==== Исклучоци:
     38
     39 * `RAISE EXCEPTION` — доколку `booking_id` не постои
     40
     41==== Имплементација:
     42{{{
     43CREATE OR REPLACE FUNCTION calculate_booking_fare(p_booking_id INT)
     44RETURNS DECIMAL(10,2)
     45LANGUAGE plpgsql
     46AS $$
     47DECLARE
     48    v_total        DECIMAL(10,2) := 0;
     49    v_ride_id      INT;
     50    v_price_per_km DECIMAL(8,2);
     51    rec            RECORD;
     52BEGIN
     53    SELECT b.ride_id, r.price_per_km
     54    INTO v_ride_id, v_price_per_km
     55    FROM bookings b
     56    JOIN rides r ON r.id = b.ride_id
     57    WHERE b.id = p_booking_id;
     58
     59    IF NOT FOUND THEN
     60        RAISE EXCEPTION 'Booking % not found', p_booking_id;
     61    END IF;
     62
     63    FOR rec IN
     64        SELECT rs.distance_km, ps.passengers_on_segment
     65        FROM passenger_segments ps
     66        JOIN route_segments rs ON rs.id = ps.segment_id
     67        WHERE ps.booking_id = p_booking_id
     68          AND ps.is_present = TRUE
     69    LOOP
     70        v_total := v_total + ROUND(
     71            (rec.distance_km * v_price_per_km) / rec.passengers_on_segment,
     72            2
     73        );
     74    END LOOP;
     75
     76    RETURN v_total;
     77END;
     78$$;
     79}}}
     80
     81==== Пример за употреба:
     82{{{
     83SELECT calculate_booking_fare(1);
     84}}}
     85
     86----
     87
     88=== 2. is_driver_available ===
     89
     90==== Опис:
     91
     92Проверува дали возачот веќе има закажано возење кое временски се преклопува со бараниот термин. Се користи пред креирање на ново возење за да се спречат конфликти во распоредот. Буферот е ±3 часа околу бараното време на поаѓање.
     93
     94==== Сигнатура:
     95{{{
     96is_driver_available(p_driver_id INT, p_departure TIMESTAMP) RETURNS BOOLEAN
     97}}}
     98
     99==== Влезни параметри:
     100
     101|| **Параметар** || **Тип** || **Опис** ||
     102|| `p_driver_id` || INT || ID на возачот ||
     103|| `p_departure` || TIMESTAMP || Бараното време на поаѓање ||
     104
     105==== Логика:
     106
     107 1. Брои колку возења со статус `scheduled` или `in_progress` има возачот во прозорецот `[departure - 3h, departure + 3h]`
     108 2. Доколку бројот е 0 враќа `TRUE` (слободен), инаку враќа `FALSE` (зафатен)
     109
     110==== Имплементација:
     111{{{
     112CREATE OR REPLACE FUNCTION is_driver_available(
     113    p_driver_id INT,
     114    p_departure TIMESTAMP
     115)
     116RETURNS BOOLEAN
     117LANGUAGE plpgsql
     118AS $$
     119DECLARE
     120    v_count INT;
     121BEGIN
     122    SELECT COUNT(*)
     123    INTO v_count
     124    FROM rides
     125    WHERE driver_id = p_driver_id
     126      AND status IN ('scheduled', 'in_progress')
     127      AND departure_time BETWEEN (p_departure - INTERVAL '3 hours')
     128                             AND (p_departure + INTERVAL '3 hours');
     129
     130    RETURN v_count = 0;
     131END;
     132$$;
     133}}}
     134
     135==== Пример за употреба:
     136{{{
     137-- Провери дали возачот е слободен на 15.06.2025 во 10:00
     138SELECT is_driver_available(1, '2025-06-15 10:00:00');
     139}}}
     140
     141----
     142
     143=== 3. get_available_seats ===
     144
     145==== Опис:
     146
     147Го враќа тековниот број на слободни места за одредено возење. Се користи пред резервација за да се потврди дека има место за нов патник.
     148
     149==== Сигнатура:
     150{{{
     151get_available_seats(p_ride_id INT) RETURNS INT
     152}}}
     153
     154==== Влезни параметри:
     155
     156|| **Параметар** || **Тип** || **Опис** ||
     157|| `p_ride_id` || INT || ID на возењето ||
     158
     159==== Логика:
     160
     161 1. Го чита полето `seats_available` директно од табелата `rides`
     162 2. Доколку возењето не постои фрла исклучок
     163
     164==== Исклучоци:
     165
     166 * `RAISE EXCEPTION` — доколку `ride_id` не постои
     167
     168==== Имплементација:
     169{{{
     170CREATE OR REPLACE FUNCTION get_available_seats(p_ride_id INT)
     171RETURNS INT
     172LANGUAGE plpgsql
     173AS $$
     174DECLARE
     175    v_seats INT;
     176BEGIN
     177    SELECT seats_available
     178    INTO v_seats
     179    FROM rides
     180    WHERE id = p_ride_id;
     181
     182    IF NOT FOUND THEN
     183        RAISE EXCEPTION 'Ride % not found', p_ride_id;
     184    END IF;
     185
     186    RETURN v_seats;
     187END;
     188$$;
     189}}}
     190
     191==== Пример за употреба:
     192{{{
     193SELECT get_available_seats(1);
     194}}}
     195
     196----
     197
     198= ПРОЦЕДУРИ =
     199
     200=== 4. book_ride ===
     201
     202==== Опис:
     203
     204Го извршува целосниот процес на резервација на возење. Ги опфаќа сите потребни проверки и ажурирања во рамки на една трансакција: верификација на статусот и местата, проверка за дупликат резервација, креирање на резервацијата и намалување на слободните места.
     205
     206==== Сигнатура:
     207{{{
     208book_ride(p_passenger_id INT, p_ride_id INT,
     209          p_pickup_stop_id INT, p_dropoff_stop_id INT)
     210}}}
     211
     212==== Влезни параметри:
     213
     214|| **Параметар** || **Тип** || **Опис** ||
     215|| `p_passenger_id` || INT || ID на патникот ||
     216|| `p_ride_id` || INT || ID на возењето ||
     217|| `p_pickup_stop_id` || INT || ID на постојката за качување ||
     218|| `p_dropoff_stop_id` || INT || ID на постојката за слегување ||
     219
     220==== Логика:
     221
     222 1. Ги заклучува редовите со `FOR UPDATE` за да се спречат состојби на трка
     223 2. Проверува дали возењето постои и дали е со статус `scheduled`
     224 3. Проверува дали `seats_available > 0`
     225 4. Проверува дали патникот веќе има резервација за ова возење
     226 5. Вметнува нов ред во `bookings` со статус `confirmed`
     227 6. Го намалува `seats_available` за 1
     228 7. Вметнува запис во `booking_status_history`
     229
     230==== Исклучоци:
     231
     232 * `RAISE EXCEPTION` — возењето не постои
     233 * `RAISE EXCEPTION` — возењето не е со статус `scheduled`
     234 * `RAISE EXCEPTION` — нема слободни места
     235 * `RAISE EXCEPTION` — патникот веќе има резервација за ова возење
     236
     237==== Имплементација:
     238{{{
     239CREATE OR REPLACE PROCEDURE book_ride(
     240    p_passenger_id    INT,
     241    p_ride_id         INT,
     242    p_pickup_stop_id  INT,
     243    p_dropoff_stop_id INT
     244)
     245LANGUAGE plpgsql
     246AS $$
     247DECLARE
     248    v_booking_id  INT;
     249    v_seats       INT;
     250    v_ride_status VARCHAR(20);
     251BEGIN
     252    SELECT status, seats_available
     253    INTO v_ride_status, v_seats
     254    FROM rides
     255    WHERE id = p_ride_id
     256    FOR UPDATE;
     257
     258    IF NOT FOUND THEN
     259        RAISE EXCEPTION 'Ride % not found', p_ride_id;
     260    END IF;
     261
     262    IF v_ride_status != 'scheduled' THEN
     263        RAISE EXCEPTION 'Ride % is not available for booking (status: %)',
     264            p_ride_id, v_ride_status;
     265    END IF;
     266
     267    IF v_seats <= 0 THEN
     268        RAISE EXCEPTION 'No available seats for ride %', p_ride_id;
     269    END IF;
     270
     271    IF EXISTS (
     272        SELECT 1 FROM bookings
     273        WHERE ride_id = p_ride_id
     274          AND passenger_id = p_passenger_id
     275    ) THEN
     276        RAISE EXCEPTION 'Passenger % already has a booking for ride %',
     277            p_passenger_id, p_ride_id;
     278    END IF;
     279
     280    INSERT INTO bookings (
     281        ride_id, passenger_id,
     282        pickup_stop_id, dropoff_stop_id,
     283        status, created_at
     284    )
     285    VALUES (
     286        p_ride_id, p_passenger_id,
     287        p_pickup_stop_id, p_dropoff_stop_id,
     288        'confirmed', NOW()
     289    )
     290    RETURNING id INTO v_booking_id;
     291
     292    UPDATE rides
     293    SET seats_available = seats_available - 1
     294    WHERE id = p_ride_id;
     295
     296    INSERT INTO booking_status_history (booking_id, status, changed_at)
     297    VALUES (v_booking_id, 'confirmed', NOW());
     298
     299    RAISE NOTICE 'Booking % created for passenger % on ride %',
     300        v_booking_id, p_passenger_id, p_ride_id;
     301END;
     302$$;
     303}}}
     304
     305==== Пример за употреба:
     306{{{
     307CALL book_ride(1, 1, 1, 4);
     308}}}
     309
     310----
     311
     312=== 5. cancel_booking ===
     313
     314==== Опис:
     315
     316Го извршува целосниот процес на откажување на резервација. Проверува дали откажувањето е дозволено (статус и временска рамка), го менува статусот, враќа едно место во возењето и ја запишува причината за откажување во историјата.
     317
     318==== Сигнатура:
     319{{{
     320cancel_booking(p_booking_id INT, p_reason VARCHAR(300) DEFAULT NULL)
     321}}}
     322
     323==== Влезни параметри:
     324
     325|| **Параметар** || **Тип** || **Опис** ||
     326|| `p_booking_id` || INT || ID на резервацијата ||
     327|| `p_reason` || VARCHAR(300) || Причина за откажување (опционално) ||
     328
     329==== Логика:
     330
     331 1. Ги зема деталите за резервацијата и поврзаното возење со `FOR UPDATE`
     332 2. Проверува дали статусот е `pending` или `confirmed`
     333 3. Проверува дали до поаѓањето има повеќе од 1 час
     334 4. Го менува статусот на `cancelled`
     335 5. Го зголемува `seats_available` за 1
     336 6. Вметнува запис во `booking_status_history` со причината
     337
     338==== Исклучоци:
     339
     340 * `RAISE EXCEPTION` — резервацијата не постои
     341 * `RAISE EXCEPTION` — статусот не дозволува откажување
     342 * `RAISE EXCEPTION` — помалку од 1 час до поаѓање
     343
     344==== Имплементација:
     345{{{
     346CREATE OR REPLACE PROCEDURE cancel_booking(
     347    p_booking_id INT,
     348    p_reason     VARCHAR(300) DEFAULT NULL
     349)
     350LANGUAGE plpgsql
     351AS $$
     352DECLARE
     353    v_ride_id   INT;
     354    v_status    VARCHAR(20);
     355    v_departure TIMESTAMP;
     356BEGIN
     357    SELECT b.ride_id, b.status, r.departure_time
     358    INTO v_ride_id, v_status, v_departure
     359    FROM bookings b
     360    JOIN rides r ON r.id = b.ride_id
     361    WHERE b.id = p_booking_id
     362    FOR UPDATE;
     363
     364    IF NOT FOUND THEN
     365        RAISE EXCEPTION 'Booking % not found', p_booking_id;
     366    END IF;
     367
     368    IF v_status NOT IN ('pending', 'confirmed') THEN
     369        RAISE EXCEPTION 'Cannot cancel booking % with status %',
     370            p_booking_id, v_status;
     371    END IF;
     372
     373    IF v_departure <= NOW() + INTERVAL '1 hour' THEN
     374        RAISE EXCEPTION 'Cannot cancel booking % — less than 1 hour before departure',
     375            p_booking_id;
     376    END IF;
     377
     378    UPDATE bookings
     379    SET status = 'cancelled'
     380    WHERE id = p_booking_id;
     381
     382    UPDATE rides
     383    SET seats_available = seats_available + 1
     384    WHERE id = v_ride_id;
     385
     386    INSERT INTO booking_status_history (booking_id, status, changed_at, reason)
     387    VALUES (p_booking_id, 'cancelled', NOW(), p_reason);
     388
     389    RAISE NOTICE 'Booking % cancelled successfully', p_booking_id;
     390END;
     391$$;
     392}}}
     393
     394==== Пример за употреба:
     395{{{
     396CALL cancel_booking(1, 'Патникот не може да патува');
     397}}}
     398
     399----
     400
     401=== 6. complete_ride ===
     402
     403==== Опис:
     404
     405Го завршува возењето и автоматски ги завршува сите активни резервации. За секоја резервација се пресметува и зачувува финалната тарифа преку функцијата `calculate_booking_fare`.
     406
     407==== Сигнатура:
     408{{{
     409complete_ride(p_ride_id INT)
     410}}}
     411
     412==== Влезни параметри:
     413
     414|| **Параметар** || **Тип** || **Опис** ||
     415|| `p_ride_id` || INT || ID на возењето ||
     416
     417==== Логика:
     418
     419 1. Проверува дали возењето постои и дали е со статус `in_progress`
     420 2. Го менува статусот на возењето во `completed`
     421 3. За секоја резервација со статус `confirmed` или `picked_up`:
     422    * Го менува статусот во `completed`
     423    * Го поставува `dropoff_confirmed_at = NOW()`
     424    * Ја пресметува финалната тарифа преку `calculate_booking_fare`
     425    * Ја зачувува тарифата во `booking_final_fare` (INSERT или UPDATE)
     426    * Вметнува запис во `booking_status_history`
     427
     428==== Исклучоци:
     429
     430 * `RAISE EXCEPTION` — возењето не постои
     431 * `RAISE EXCEPTION` — возењето не е со статус `in_progress`
     432
     433==== Имплементација:
     434{{{
     435CREATE OR REPLACE PROCEDURE complete_ride(p_ride_id INT)
     436LANGUAGE plpgsql
     437AS $$
     438DECLARE
     439    v_status VARCHAR(20);
     440    rec      RECORD;
     441    v_fare   DECIMAL(10,2);
     442BEGIN
     443    SELECT status INTO v_status
     444    FROM rides
     445    WHERE id = p_ride_id
     446    FOR UPDATE;
     447
     448    IF NOT FOUND THEN
     449        RAISE EXCEPTION 'Ride % not found', p_ride_id;
     450    END IF;
     451
     452    IF v_status != 'in_progress' THEN
     453        RAISE EXCEPTION 'Ride % cannot be completed (status: %)',
     454            p_ride_id, v_status;
     455    END IF;
     456
     457    UPDATE rides
     458    SET status = 'completed'
     459    WHERE id = p_ride_id;
     460
     461    FOR rec IN
     462        SELECT id FROM bookings
     463        WHERE ride_id = p_ride_id
     464          AND status IN ('confirmed', 'picked_up')
     465    LOOP
     466        UPDATE bookings
     467        SET status = 'completed',
     468            dropoff_confirmed_at = NOW()
     469        WHERE id = rec.id;
     470
     471        v_fare := calculate_booking_fare(rec.id);
     472
     473        INSERT INTO booking_final_fare (booking_id, total_amount, calculated_at)
     474        VALUES (rec.id, v_fare, NOW())
     475        ON CONFLICT (booking_id)
     476        DO UPDATE SET
     477            total_amount  = EXCLUDED.total_amount,
     478            calculated_at = EXCLUDED.calculated_at;
     479
     480        INSERT INTO booking_status_history (booking_id, status, changed_at)
     481        VALUES (rec.id, 'completed', NOW());
     482    END LOOP;
     483
     484    RAISE NOTICE 'Ride % completed successfully', p_ride_id;
     485END;
     486$$;
     487}}}
     488
     489==== Пример за употреба:
     490{{{
     491CALL complete_ride(1);
     492}}}
     493
     494----
     495
     496= ТРИГЕРИ =
     497
     498=== 7. trg_booking_status_history ===
     499
     500==== Опис:
     501
     502Автоматски запишува во табелата `booking_status_history` при секоја промена на статусот на резервација. Обезбедува целосна историја на промени без да мора апликацијата рачно да ги следи.
     503
     504==== Тип: `AFTER UPDATE` на табела `bookings`
     505
     506==== Логика:
     507
     508 1. Се активира по секој `UPDATE` на табелата `bookings`
     509 2. Проверува дали `OLD.status IS DISTINCT FROM NEW.status`
     510 3. Доколку статусот се сменил, вметнува нов ред во `booking_status_history`
     511
     512==== Имплементација:
     513{{{
     514CREATE OR REPLACE FUNCTION fn_booking_status_history()
     515RETURNS TRIGGER
     516LANGUAGE plpgsql
     517AS $$
     518BEGIN
     519    IF OLD.status IS DISTINCT FROM NEW.status THEN
     520        INSERT INTO booking_status_history (booking_id, status, changed_at)
     521        VALUES (NEW.id, NEW.status, NOW());
     522    END IF;
     523    RETURN NEW;
     524END;
     525$$;
     526
     527CREATE TRIGGER trg_booking_status_history
     528AFTER UPDATE ON bookings
     529FOR EACH ROW
     530EXECUTE FUNCTION fn_booking_status_history();
     531}}}
     532
     533==== Пример — активирање:
     534{{{
     535-- Овој UPDATE автоматски ќе вметне ред во booking_status_history
     536UPDATE bookings SET status = 'picked_up' WHERE id = 1;
     537}}}
     538
     539----
     540
     541=== 8. trg_ride_status_history ===
     542
     543==== Опис:
     544
     545Автоматски запишува во табелата `ride_status_history` при секоја промена на статусот на возење. Обезбедува целосна хронологија на состојбите на возењето.
     546
     547==== Тип: `AFTER UPDATE` на табела `rides`
     548
     549==== Логика:
     550
     551 1. Се активира по секој `UPDATE` на табелата `rides`
     552 2. Проверува дали `OLD.status IS DISTINCT FROM NEW.status`
     553 3. Доколку статусот се сменил, вметнува нов ред во `ride_status_history`
     554
     555==== Имплементација:
     556{{{
     557CREATE OR REPLACE FUNCTION fn_ride_status_history()
     558RETURNS TRIGGER
     559LANGUAGE plpgsql
     560AS $$
     561BEGIN
     562    IF OLD.status IS DISTINCT FROM NEW.status THEN
     563        INSERT INTO ride_status_history (ride_id, status, changed_at)
     564        VALUES (NEW.id, NEW.status, NOW());
     565    END IF;
     566    RETURN NEW;
     567END;
     568$$;
     569
     570CREATE TRIGGER trg_ride_status_history
     571AFTER UPDATE ON rides
     572FOR EACH ROW
     573EXECUTE FUNCTION fn_ride_status_history();
     574}}}
     575
     576==== Пример — активирање:
     577{{{
     578-- Овој UPDATE автоматски ќе вметне ред во ride_status_history
     579UPDATE rides SET status = 'in_progress' WHERE id = 1;
     580}}}
     581
     582----
     583
     584=== 9. trg_audit_log ===
     585
     586==== Опис:
     587
     588Автоматски запишува во табелата `audit_logs` за сите `INSERT`, `UPDATE` и `DELETE` операции на критичните табели: `rides`, `bookings` и `users`. Го чува и старото и новото состојба на редот во JSON формат.
     589
     590==== Тип: `AFTER INSERT OR UPDATE OR DELETE` на табели `rides`, `bookings`, `users`
     591
     592==== Логика:
     593
     594 1. За `INSERT` — зачувува `new_data = row_to_json(NEW)`, `old_data = NULL`
     595 2. За `UPDATE` — зачувува `old_data = row_to_json(OLD)` и `new_data = row_to_json(NEW)`
     596 3. За `DELETE` — зачувува `old_data = row_to_json(OLD)`, `new_data = NULL`
     597
     598==== Имплементација:
     599{{{
     600CREATE OR REPLACE FUNCTION fn_audit_log()
     601RETURNS TRIGGER
     602LANGUAGE plpgsql
     603AS $$
     604BEGIN
     605    IF TG_OP = 'INSERT' THEN
     606        INSERT INTO audit_logs (
     607            table_name, record_id, operation,
     608            old_data, new_data, changed_at
     609        )
     610        VALUES (
     611            TG_TABLE_NAME, NEW.id, 'INSERT',
     612            NULL, row_to_json(NEW)::TEXT, NOW()
     613        );
     614        RETURN NEW;
     615
     616    ELSIF TG_OP = 'UPDATE' THEN
     617        INSERT INTO audit_logs (
     618            table_name, record_id, operation,
     619            old_data, new_data, changed_at
     620        )
     621        VALUES (
     622            TG_TABLE_NAME, NEW.id, 'UPDATE',
     623            row_to_json(OLD)::TEXT, row_to_json(NEW)::TEXT, NOW()
     624        );
     625        RETURN NEW;
     626
     627    ELSIF TG_OP = 'DELETE' THEN
     628        INSERT INTO audit_logs (
     629            table_name, record_id, operation,
     630            old_data, new_data, changed_at
     631        )
     632        VALUES (
     633            TG_TABLE_NAME, OLD.id, 'DELETE',
     634            row_to_json(OLD)::TEXT, NULL, NOW()
     635        );
     636        RETURN OLD;
     637    END IF;
     638END;
     639$$;
     640
     641CREATE TRIGGER trg_audit_log_rides
     642AFTER INSERT OR UPDATE OR DELETE ON rides
     643FOR EACH ROW EXECUTE FUNCTION fn_audit_log();
     644
     645CREATE TRIGGER trg_audit_log_bookings
     646AFTER INSERT OR UPDATE OR DELETE ON bookings
     647FOR EACH ROW EXECUTE FUNCTION fn_audit_log();
     648
     649CREATE TRIGGER trg_audit_log_users
     650AFTER INSERT OR UPDATE OR DELETE ON users
     651FOR EACH ROW EXECUTE FUNCTION fn_audit_log();
     652}}}
     653
     654==== Пример — активирање:
     655{{{
     656-- Секој од овие автоматски ќе вметне ред во audit_logs
     657INSERT INTO rides (...) VALUES (...);
     658UPDATE bookings SET status = 'cancelled' WHERE id = 1;
     659DELETE FROM users WHERE id = 999;
     660}}}
     661
     662----
     663
     664= Резиме =
     665
     666|| **#** || **Име** || **Тип** || **Опис** ||
     667|| 1 || `calculate_booking_fare` || Функција || Пресметува вкупна цена за резервација ||
     668|| 2 || `is_driver_available` || Функција || Проверува конфликт на термин за возач ||
     669|| 3 || `get_available_seats` || Функција || Враќа број на слободни места за возење ||
     670|| 4 || `book_ride` || Процедура || Целосен процес на резервација ||
     671|| 5 || `cancel_booking` || Процедура || Откажување на резервација со проверки ||
     672|| 6 || `complete_ride` || Процедура || Завршување на возење и пресметка на тарифи ||
     673|| 7 || `trg_booking_status_history` || Тригер || Автоматски лог на промени на статус на резервација ||
     674|| 8 || `trg_ride_status_history` || Тригер || Автоматски лог на промени на статус на возење ||
     675|| 9 || `trg_audit_log` || Тригер || Audit лог за rides, bookings и users ||