Changes between Version 27 and Version 28 of DatabaseProgramming


Ignore:
Timestamp:
06/16/26 00:37:14 (28 hours ago)
Author:
231093
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseProgramming

    v27 v28  
    2929    commit;
    3030end;
    31 $$;
    32 }}}
    33 
    34 **Процедура за завршување на смена на возач**
    35 
    36 Процедурата проверува дали постои id-то на возачот, доколку не постои фрла соодветен исклучок. Потоа се проверува дали возачот има точно една активна смена која не е завршена. На крај се ажурира записот во табелата `Driver_Vehicle` на тој начин што се поставува тековното време во колоната `time_to`. Во апликацијата, оваа логика се повикува кога возачот сака да се одјави од системот, означувајќи го крајот на неговото работно време.
    37 
    38 {{{
    39 create or replace procedure finish_shift(
    40     in driver_id int4
    41 )
    42     language plpgsql as
    43 $$
    44 declare
    45     num_of_shifts int4;
    46 begin
    47     if not exists(select user_id from driver where user_id = driver_id) then
    48         raise exception 'Driver with id % does not exist', driver_id;
    49     end if;
    50 
    51     num_of_shifts := (select count(*) from driver_vehicle where id_driver = driver_id and time_to is null);
    52 
    53     if (num_of_shifts != 1) then
    54         raise exception 'Driver has not started a shift';
    55     end if;
    56 
    57     update driver_vehicle
    58     set time_to=now()
    59     where id_driver = driver_id
    60       and time_to is null;
    61     commit;
    62 end;
    63 $$;
    64 }}}
    65 **Процедура за вработување на возач во компанија**
    66 
    67 Процедурата проверува дали id-то на возачот е валидно, доколку не постои фрла соодветен исклучок. Оваа процедура е дел од административниот модул на апликацијата и овозможува менаџирање на човечки ресурси, односно формално поврзување на возачот со конкретна такси компанија.
    68 
    69 {{{
    70 create or replace procedure employ_driver(id_employee int, id_company int)
    71 language plpgsql
    72 as $$
    73     begin
    74         if not exists (
    75                    select 1
    76                    from driver
    77                    where user_id=id_employee)
    78                then
    79                    raise exception 'Driver does not exist';
    80         else
    81                insert into
    82                    employmenthistory (employee_user_id, start_date, end_date, company_id)
    83                    values (id_employee,current_date, null,id_company);
    84         end if;
    85     end;
    86 $$;
    87 }}}
    88 
    89 **Процедура за вработување на диспечер во компанија**
    90 
    91 Процедурата проверува дали id-то на диспечерот е валидно, доколку не постои фрла соодветен исклучок. Се користи за управување со кадар во рамките на компанијата. Ова овозможува диспечерот да добие овластување за креирање и испраќање на понуди во таа компанија.
    92 
    93 {{{
    94 create or replace procedure employ_dispatcher(id_employee int, id_company int)
    95 language plpgsql
    96 as $$
    97     begin
    98         if not exists (
    99                    select 1
    100                    from dispatcher
    101                    where user_id=id_employee)
    102                then
    103                    raise notice 'Dispatcher does not exist';
    104         else
    105                insert into
    106                    employmenthistory (employee_user_id, start_date, end_date, company_id)
    107                    values (id_employee,current_date, null,id_company);
    108         end if;
    109     end;
    110 $$;
    111 }}}
    112 
    113 **Процедура за отпуштање/завршување на договор на возач во компанија**
    114 
    115 Процедурата прави две проверки. Првата проверка ја пребарува табелата Driver за id-то на возачот, доколку истото не постои се фрла соодветен исклучок. Потоа се проверува дали постои соодветен запис во табелата `EmploymentHistory` кој потврдува дека возачот е тековно вработен во компанијата. На крај се променува записот од табелата `EmploymentHistory` на тој начин што се става краен датум. Оваа процедура служи за прекинување на соработката, оневозможувајќи му на возачот понатаму да вози за таа компанија.
    116 
    117 {{{
    118 create or replace procedure fire_driver(id_employee int, id_company int)
    119 language plpgsql
    120 as $$
    121     begin
    122         if not exists (
    123                select 1
    124                from driver
    125                where user_id=id_employee)
    126            then
    127                raise exception 'Driver does not exist';
    128        else
    129            if not exists (
    130                select 1
    131                from employmenthistory
    132                where employee_user_id=id_employee and end_date==null and company_id=id_company)
    133            then
    134                raise exception 'Driver does not have an employment';
    135            else
    136                update employmenthistory
    137                set end_date=current_date
    138                where employee_user_id==id_employee and end_date==null and company_id=id_company;
    139                raise notice 'Driver is fired';
    140                commit;
    141            end if;
    142        end if;
    143     end;
    144 $$;
    145 }}}
    146 
    147 **Процедура за отпуштање/завршување на договор на диспечер во компанија**
    148 
    149 Процедурата прави две проверки. Првата проверка ја пребарува табелата `Dispatcher` за id-то на диспечарот, доколку истото не постои се фрла соодветен исклучок. Потоа се проверува дали постои соодветен запис во табелата `EmploymentHistory` кој потврдува дека деспечерот е тековно вработен во компанијата. На крај се променува записот од табелата `EmploymentHistory` на тој начин што се става краен датум. Се користи за администрирање на правата на вработените, по извршувањето диспечерот ги губи сите привилегии за менаџирање со барањата на таа компанија.
    150 
    151 {{{
    152 create or replace procedure fire_dispatcher(id_employee int, id_company int)
    153 language plpgsql
    154 as $$
    155     begin
    156         if not exists (
    157                select 1
    158                from dispatcher
    159                where user_id=id_employee)
    160            then
    161                raise notice 'Dispatcher does not exist';
    162        else
    163            if not exists(
    164                select 1
    165                from employmenthistory
    166                where employee_user_id=id_employee and end_date==null and company_id=id_company)
    167            then
    168                raise exception 'Dispatcher does not have an employment';
    169            else
    170                update employmenthistory
    171                set end_date=current_date
    172                where employee_user_id=id_employee and end_date==null and company_id=id_company;
    173                raise notice 'Dispatcher is fired';
    174                commit;
    175            end if;
    176        end if;
    177     end;
    178 $$;
    179 }}}
    180 
    181 **Процедура за пишување на оцена**
    182 
    183 Оваа процедурата се повикува кога корисник испраќа свој коментар и рејтинг за возењето откако ќе пристигне на дестинацијата. Се проверува дали постои запис во табелата Ride за соодветните `id_ride` и `id_customer` доколку не постои се фрла исклучок `Customer can not give a rating on a ride they do not have.`. Имплементира логика за корисничка повратна информација, која е клучна за евалуација на квалитетот на такси услугата.
    184  
    185 {{{
    186 create or replace procedure write_rating(rating numeric, comment text, id_ride int, id_customer int)
    187 language plpgsql
    188 as $$
    189     begin
    190         if not exists(
    191             select 1
    192             from Ride r
    193             join Request req on r.request_id=req.id
    194             where r.id=id_ride and req.customer_user_id=id_customer
    195         )
    196         then
    197             raise exception 'Customer can not give a rating on a ride they do not have.';
    198         end if;
    199         insert into review (rating, comment, ride_id, customer_user_id)
    200         values (write_rating.rating,write_rating.comment,id_ride,id_customer);
    201     end;
    202 $$;
    203 }}}
    204 
    205 **Процедура за испраќање порака**
    206 
    207 Пораките можат да бидат испратени од возачот или патникот и се однесуваат на едно возење. Доколку не постои возење за соодветните `id_ride` и `id_user` се фрла исклучок `User can not write a chat for a ride they do not have.`. Оваа процедура е главна во системот за чет помеѓу патникот и возачот за време на реализацијата на услугата.
    208 
    209 {{{
    210 create or replace procedure write_chat(new_message text, id_ride int, id_user int)
    211 language plpgsql
    212 as $$
    213     begin
    214         if not exists(
    215             select 1
    216             from Ride r
    217             join Request req on r.request_id=req.id
    218             where r.id=id_ride and req.customer_user_id=id_user
    219         )
    220         then
    221             raise exception 'User can not write a chat for a ride they do not have.';
    222         end if;
    223         insert into chatmessage(message, user_id_from, ride_id)
    224         values (new_message,id_user,id_ride);
    225     end;
    226 $$;
    227 }}}
    228 
    229 **Процедура за поднесување пријава**
    230 
    231 Оваа процедура се повикува кога корисниците поднесуваат пријава за време на возењето. Тоа може да биде некоја поплака од типот брзо возење или скршнување од патот. Доколку не постои возење за соодветните `id_ride` и `user_id` се фрла исклучок `Customer can not write a report on a ride they do not have.`. Се користи во модулот за безбедност и заштита на корисниците, овозможувајќи брза реакција на поддршката.
    232 
    233 {{{
    234 create or replace procedure write_report(id_ride int, user_id int, new_message text, new_title text,temp_latitude float, temp_long float, new_reason text)
    235 language plpgsql
    236 as $$
    237     begin
    238         if not exists(
    239             select 1
    240             from Ride r
    241             join Request req on r.request_id=req.id
    242             where r.id=id_ride and req.customer_user_id=user_id
    243         )
    244         then
    245             raise exception 'Customer can not write a report on a ride they do not have.';
    246         end if;
    247         insert into report(ride_id, customer_user_id, message, title, latitude, longitude, reason)
    248         values (id_ride,user_id,new_message,new_title,temp_latitude,temp_long,new_reason);
    249     end;
    25031$$;
    25132}}}
     
    471252}}}
    472253
     254**Процедура за вработување на возач во компанија**
     255
     256Процедурата проверува дали id-то на возачот е валидно, доколку не постои фрла соодветен исклучок. Оваа процедура е дел од административниот модул на апликацијата и овозможува менаџирање на човечки ресурси, односно формално поврзување на возачот со конкретна такси компанија.
     257
     258{{{
     259create or replace procedure employ_driver(id_employee int, id_company int)
     260language plpgsql
     261as $$
     262    begin
     263        if not exists (
     264                   select 1
     265                   from driver
     266                   where user_id=id_employee)
     267               then
     268                   raise exception 'Driver does not exist';
     269        else
     270               insert into
     271                   employmenthistory (employee_user_id, start_date, end_date, company_id)
     272                   values (id_employee,current_date, null,id_company);
     273        end if;
     274    end;
     275$$;
     276}}}
     277
     278**Процедура за вработување на диспечер во компанија**
     279
     280Процедурата проверува дали id-то на диспечерот е валидно, доколку не постои фрла соодветен исклучок. Се користи за управување со кадар во рамките на компанијата. Ова овозможува диспечерот да добие овластување за креирање и испраќање на понуди во таа компанија.
     281
     282{{{
     283create or replace procedure employ_dispatcher(id_employee int, id_company int)
     284language plpgsql
     285as $$
     286    begin
     287        if not exists (
     288                   select 1
     289                   from dispatcher
     290                   where user_id=id_employee)
     291               then
     292                   raise notice 'Dispatcher does not exist';
     293        else
     294               insert into
     295                   employmenthistory (employee_user_id, start_date, end_date, company_id)
     296                   values (id_employee,current_date, null,id_company);
     297        end if;
     298    end;
     299$$;
     300}}}
     301
     302**Процедура за отпуштање/завршување на договор на возач во компанија**
     303
     304Процедурата прави две проверки. Првата проверка ја пребарува табелата Driver за id-то на возачот, доколку истото не постои се фрла соодветен исклучок. Потоа се проверува дали постои соодветен запис во табелата `EmploymentHistory` кој потврдува дека возачот е тековно вработен во компанијата. На крај се променува записот од табелата `EmploymentHistory` на тој начин што се става краен датум. Оваа процедура служи за прекинување на соработката, оневозможувајќи му на возачот понатаму да вози за таа компанија.
     305
     306{{{
     307create or replace procedure fire_driver(id_employee int, id_company int)
     308language plpgsql
     309as $$
     310    begin
     311        if not exists (
     312               select 1
     313               from driver
     314               where user_id=id_employee)
     315           then
     316               raise exception 'Driver does not exist';
     317       else
     318           if not exists (
     319               select 1
     320               from employmenthistory
     321               where employee_user_id=id_employee and end_date==null and company_id=id_company)
     322           then
     323               raise exception 'Driver does not have an employment';
     324           else
     325               update employmenthistory
     326               set end_date=current_date
     327               where employee_user_id==id_employee and end_date==null and company_id=id_company;
     328               raise notice 'Driver is fired';
     329               commit;
     330           end if;
     331       end if;
     332    end;
     333$$;
     334}}}
     335
     336**Процедура за отпуштање/завршување на договор на диспечер во компанија**
     337
     338Процедурата прави две проверки. Првата проверка ја пребарува табелата `Dispatcher` за id-то на диспечарот, доколку истото не постои се фрла соодветен исклучок. Потоа се проверува дали постои соодветен запис во табелата `EmploymentHistory` кој потврдува дека деспечерот е тековно вработен во компанијата. На крај се променува записот од табелата `EmploymentHistory` на тој начин што се става краен датум. Се користи за администрирање на правата на вработените, по извршувањето диспечерот ги губи сите привилегии за менаџирање со барањата на таа компанија.
     339
     340{{{
     341create or replace procedure fire_dispatcher(id_employee int, id_company int)
     342language plpgsql
     343as $$
     344    begin
     345        if not exists (
     346               select 1
     347               from dispatcher
     348               where user_id=id_employee)
     349           then
     350               raise notice 'Dispatcher does not exist';
     351       else
     352           if not exists(
     353               select 1
     354               from employmenthistory
     355               where employee_user_id=id_employee and end_date==null and company_id=id_company)
     356           then
     357               raise exception 'Dispatcher does not have an employment';
     358           else
     359               update employmenthistory
     360               set end_date=current_date
     361               where employee_user_id=id_employee and end_date==null and company_id=id_company;
     362               raise notice 'Dispatcher is fired';
     363               commit;
     364           end if;
     365       end if;
     366    end;
     367$$;
     368}}}
     369
     370**Процедура за пишување на оцена**
     371
     372Оваа процедурата се повикува кога корисник испраќа свој коментар и рејтинг за возењето откако ќе пристигне на дестинацијата. Се проверува дали постои запис во табелата Ride за соодветните `id_ride` и `id_customer` доколку не постои се фрла исклучок `Customer can not give a rating on a ride they do not have.`. Имплементира логика за корисничка повратна информација, која е клучна за евалуација на квалитетот на такси услугата.
     373 
     374{{{
     375create or replace procedure write_rating(rating numeric, comment text, id_ride int, id_customer int)
     376language plpgsql
     377as $$
     378    begin
     379        if not exists(
     380            select 1
     381            from Ride r
     382            join Request req on r.request_id=req.id
     383            where r.id=id_ride and req.customer_user_id=id_customer
     384        )
     385        then
     386            raise exception 'Customer can not give a rating on a ride they do not have.';
     387        end if;
     388        insert into review (rating, comment, ride_id, customer_user_id)
     389        values (write_rating.rating,write_rating.comment,id_ride,id_customer);
     390    end;
     391$$;
     392}}}
     393
     394**Процедура за испраќање порака**
     395
     396Пораките можат да бидат испратени од возачот или патникот и се однесуваат на едно возење. Доколку не постои возење за соодветните `id_ride` и `id_user` се фрла исклучок `User can not write a chat for a ride they do not have.`. Оваа процедура е главна во системот за чет помеѓу патникот и возачот за време на реализацијата на услугата.
     397
     398{{{
     399create or replace procedure write_chat(new_message text, id_ride int, id_user int)
     400language plpgsql
     401as $$
     402    begin
     403        if not exists(
     404            select 1
     405            from Ride r
     406            join Request req on r.request_id=req.id
     407            where r.id=id_ride and req.customer_user_id=id_user
     408        )
     409        then
     410            raise exception 'User can not write a chat for a ride they do not have.';
     411        end if;
     412        insert into chatmessage(message, user_id_from, ride_id)
     413        values (new_message,id_user,id_ride);
     414    end;
     415$$;
     416}}}
     417
     418**Процедура за поднесување пријава**
     419
     420Оваа процедура се повикува кога корисниците поднесуваат пријава за време на возењето. Тоа може да биде некоја поплака од типот брзо возење или скршнување од патот. Доколку не постои возење за соодветните `id_ride` и `user_id` се фрла исклучок `Customer can not write a report on a ride they do not have.`. Се користи во модулот за безбедност и заштита на корисниците, овозможувајќи брза реакција на поддршката.
     421
     422{{{
     423create or replace procedure write_report(id_ride int, user_id int, new_message text, new_title text,temp_latitude float, temp_long float, new_reason text)
     424language plpgsql
     425as $$
     426    begin
     427        if not exists(
     428            select 1
     429            from Ride r
     430            join Request req on r.request_id=req.id
     431            where r.id=id_ride and req.customer_user_id=user_id
     432        )
     433        then
     434            raise exception 'Customer can not write a report on a ride they do not have.';
     435        end if;
     436        insert into report(ride_id, customer_user_id, message, title, latitude, longitude, reason)
     437        values (id_ride,user_id,new_message,new_title,temp_latitude,temp_long,new_reason);
     438    end;
     439$$;
     440}}}
     441
     442
     443
    473444** Процедура за креирање понуда за одредено барање **
    474445
     
    539510$$;
    540511}}}
     512
     513**Процедура за завршување на смена на возач**
     514
     515Процедурата проверува дали постои id-то на возачот, доколку не постои фрла соодветен исклучок. Потоа се проверува дали возачот има точно една активна смена која не е завршена. На крај се ажурира записот во табелата `Driver_Vehicle` на тој начин што се поставува тековното време во колоната `time_to`. Во апликацијата, оваа логика се повикува кога возачот сака да се одјави од системот, означувајќи го крајот на неговото работно време.
     516
     517{{{
     518create or replace procedure finish_shift(
     519    in driver_id int4
     520)
     521    language plpgsql as
     522$$
     523declare
     524    num_of_shifts int4;
     525begin
     526    if not exists(select user_id from driver where user_id = driver_id) then
     527        raise exception 'Driver with id % does not exist', driver_id;
     528    end if;
     529
     530    num_of_shifts := (select count(*) from driver_vehicle where id_driver = driver_id and time_to is null);
     531
     532    if (num_of_shifts != 1) then
     533        raise exception 'Driver has not started a shift';
     534    end if;
     535
     536    update driver_vehicle
     537    set time_to=now()
     538    where id_driver = driver_id
     539      and time_to is null;
     540    commit;
     541end;
     542$$;
     543}}}
     544
    541545== Функции и тригери
    542 
    543 **Функција и тригер за проверка на достапност на возач**
    544 
    545 Функцијата `ride_assign` враќа тригер кој фрла исклучок во случај возачот да е зафатен со друго возење во моментот на доделување на возач. Тригерот се извршува пред внес на запис во табелата `Ride`. Во контекст на бизнис логиката, ова спречува преклопување на возењата и гарантира дека еден возач не може физички да извршува две патувања истовремено.
    546 
    547 {{{
    548 create or replace function ride_assign() returns trigger as
    549 $ride_assign$
    550 begin
    551     if exists(select * from ride where ride.status = 'in_progress' and ride.driver_user_id = NEW.driver_user_id) then
    552         raise exception 'Driver not free';
    553     end if;
    554     return NEW;
    555 end;
    556 $ride_assign$ language plpgsql;
    557 
    558 create trigger ride_assign before insert on ride
    559     for each row execute function ride_assign();
    560 }}}
    561546
    562547**Функција и тригер кој спречува два пати доделување на активна смена на ист возач**
     
    594579}}}
    595580
    596 ** Функција и тригер за спречување на манипулација со цената **
    597 
    598 Функцијата враќа тригер кој спречува промена на цената на понудата откако истата ќе добие статус `accepted` или `completed`. Дополнително, се прави проверка дали внесената цена е поголема од нула, при што се фрла соодветен исклучок доколку овие услови не се исполнети. Ова директно ја заштитува финансиската сигурност во системот и спречува неовластени или измамнички промени на трошоците по договарањето.
    599 
    600 {{{
    601 create or replace function prevent_price_manipulation()
     581**Функција и тригер за спречување на промена на статус на завршена понуда**
     582
     583Функцијата проверува дали претходниот статус на понудата бил `completed`, при што фрла соодветен исклучок доколку се направи обид за негова промена во било кој друг статус. Тригерот се извршува пред ажурирање на запис во табелата `Offer`. Ова ја гарантира веродостојноста на архивските податоци во системот кои подоцна се користат за генерирање извештаи.
     584
     585{{{
     586create or replace function prevent_completed_offer_downgrade()
    602587    returns trigger as
    603 $prevent_price_manipulation$
    604 begin
    605     if OLD.status in ('accepted', 'completed') and NEW.price <> OLD.price then
    606         raise exception 'Price cannot be modified once the offer is accepted or completed. Current status: %', OLD.status;
    607     end if;
    608 
    609     if NEW.price <= 0 then
    610         raise exception 'Price must be a positive value';
    611     end if;
    612 
    613     return NEW;
    614 end;
    615 $prevent_price_manipulation$ language plpgsql;
    616 
    617 create trigger prevent_price_manipulation
    618     before update on offer
     588$prevent_completed_offer_downgrade$
     589begin
     590    if old.status = 'completed'
     591        and new.status <> 'completed' then
     592        raise exception 'Completed offer cannot change status';
     593    end if;
     594
     595    return new;
     596end;
     597$prevent_completed_offer_downgrade$ language plpgsql;
     598create or replace trigger prevent_completed_offer_downgrade
     599    before update
     600    on offer
    619601    for each row
    620     execute function prevent_price_manipulation();
     602execute function prevent_completed_offer_downgrade();
     603}}}
     604
     605**Функција и тригер за спречување на возење со неважечка возачка дозвола**
     606
     607Функцијата го пребарува датумот на истекување на возачката дозвола за соодветниот возач преку поврзување на табелите `Driver` и `DriverLicense`. Доколку дозволата е истечена во однос на тековниот датум, се фрла соодветен исклучок, а тригерот се извршува пред внес на нов запис во табелата `Ride`. Целта на оваа рестрикција е правна и сигурносна заштита на платформата, осигурувајќи дека само лиценцирани возачи можат да превезуваат патници.
     608
     609{{{
     610create or replace function prevent_expired_license_ride()
     611    returns trigger as
     612$prevent_expired_license_ride$
     613declare
     614    exp_date date;
     615begin
     616    select dl.expire_date
     617    into exp_date
     618    from driver d
     619             join driverlicense dl on d.driver_license_id = dl.id
     620    where d.user_id = new.driver_user_id;
     621
     622    if exp_date is not null and exp_date < current_date then
     623        raise exception 'Driver license expired';
     624    end if;
     625
     626    return new;
     627end;
     628$prevent_expired_license_ride$ language plpgsql;
     629create or replace trigger prevent_expired_license_ride
     630    before insert
     631    on ride
     632    for each row
     633execute function prevent_expired_license_ride();
    621634}}}
    622635
     
    673686    for each row
    674687    execute function check_available_seats();
    675 }}}
    676 
    677 **Функција и тригер за спречување на возење со неважечка возачка дозвола**
    678 
    679 Функцијата го пребарува датумот на истекување на возачката дозвола за соодветниот возач преку поврзување на табелите `Driver` и `DriverLicense`. Доколку дозволата е истечена во однос на тековниот датум, се фрла соодветен исклучок, а тригерот се извршува пред внес на нов запис во табелата `Ride`. Целта на оваа рестрикција е правна и сигурносна заштита на платформата, осигурувајќи дека само лиценцирани возачи можат да превезуваат патници.
    680 
    681 {{{
    682 create or replace function prevent_expired_license_ride()
    683     returns trigger as
    684 $prevent_expired_license_ride$
    685 declare
    686     exp_date date;
    687 begin
    688     select dl.expire_date
    689     into exp_date
    690     from driver d
    691              join driverlicense dl on d.driver_license_id = dl.id
    692     where d.user_id = new.driver_user_id;
    693 
    694     if exp_date is not null and exp_date < current_date then
    695         raise exception 'Driver license expired';
    696     end if;
    697 
    698     return new;
    699 end;
    700 $prevent_expired_license_ride$ language plpgsql;
    701 create or replace trigger prevent_expired_license_ride
    702     before insert
    703     on ride
    704     for each row
    705 execute function prevent_expired_license_ride();
    706 }}}
    707 
    708 **Функција и тригер за спречување на промена на статус на завршена понуда**
    709 
    710 Функцијата проверува дали претходниот статус на понудата бил `completed`, при што фрла соодветен исклучок доколку се направи обид за негова промена во било кој друг статус. Тригерот се извршува пред ажурирање на запис во табелата `Offer`. Ова ја гарантира веродостојноста на архивските податоци во системот кои подоцна се користат за генерирање извештаи.
    711 
    712 {{{
    713 create or replace function prevent_completed_offer_downgrade()
    714     returns trigger as
    715 $prevent_completed_offer_downgrade$
    716 begin
    717     if old.status = 'completed'
    718         and new.status <> 'completed' then
    719         raise exception 'Completed offer cannot change status';
    720     end if;
    721 
    722     return new;
    723 end;
    724 $prevent_completed_offer_downgrade$ language plpgsql;
    725 create or replace trigger prevent_completed_offer_downgrade
    726     before update
    727     on offer
    728     for each row
    729 execute function prevent_completed_offer_downgrade();
    730 }}}
    731 
    732 ** Функција и тригер за контрола на времето на важност на понудата **
    733 
    734 Оваа функција враќа тригер кој спречува прифаќање на понуди кои се постари од 5 минути од моментот на нивното креирање. Доколку се направи обид за промена на статусот од `pending` во `accepted` по истекот на овој рок, статусот на понудата автоматски се менува во `expired` и истата не може да биде прифатена. Оваа логика овозможува динамичност на пазарот, со цел возачите и диспечерите да не бидат долго блокирани чекајќи одговор од патникот.
    735 
    736 {{{
    737 create or replace function enforce_offer_validity()
    738     returns trigger as
    739 $enforce_offer_validity$
    740 begin
    741     if OLD.status = 'pending' and NEW.status = 'accepted' then
    742       if (NOW() - OLD.created_at) > interval '5 minutes' then
    743             NEW.status := 'expired';
    744             raise notice 'Offer % has expired and cannot be accepted.', OLD.id;
    745             return NEW;
    746         end if;
    747     end if;
    748 
    749     return NEW;
    750 end;
    751 $enforce_offer_validity$ language plpgsql;
    752 
    753 create trigger trg_enforce_offer_validity
    754     before update on offer
    755     for each row
    756     execute function enforce_offer_validity();
    757688}}}
    758689
     
    788719execute function unique_email_and_phone()
    789720}}}
     721
     722
     723**Функција и тригер за проверка на достапност на возач**
     724
     725Функцијата `ride_assign` враќа тригер кој фрла исклучок во случај возачот да е зафатен со друго возење во моментот на доделување на возач. Тригерот се извршува пред внес на запис во табелата `Ride`. Во контекст на бизнис логиката, ова спречува преклопување на возењата и гарантира дека еден возач не може физички да извршува две патувања истовремено.
     726
     727{{{
     728create or replace function ride_assign() returns trigger as
     729$ride_assign$
     730begin
     731    if exists(select * from ride where ride.status = 'in_progress' and ride.driver_user_id = NEW.driver_user_id) then
     732        raise exception 'Driver not free';
     733    end if;
     734    return NEW;
     735end;
     736$ride_assign$ language plpgsql;
     737
     738create trigger ride_assign before insert on ride
     739    for each row execute function ride_assign();
     740}}}
     741
     742** Функција и тригер за спречување на манипулација со цената **
     743
     744Функцијата враќа тригер кој спречува промена на цената на понудата откако истата ќе добие статус `accepted` или `completed`. Дополнително, се прави проверка дали внесената цена е поголема од нула, при што се фрла соодветен исклучок доколку овие услови не се исполнети. Ова директно ја заштитува финансиската сигурност во системот и спречува неовластени или измамнички промени на трошоците по договарањето.
     745
     746{{{
     747create or replace function prevent_price_manipulation()
     748    returns trigger as
     749$prevent_price_manipulation$
     750begin
     751    if OLD.status in ('accepted', 'completed') and NEW.price <> OLD.price then
     752        raise exception 'Price cannot be modified once the offer is accepted or completed. Current status: %', OLD.status;
     753    end if;
     754
     755    if NEW.price <= 0 then
     756        raise exception 'Price must be a positive value';
     757    end if;
     758
     759    return NEW;
     760end;
     761$prevent_price_manipulation$ language plpgsql;
     762
     763create trigger prevent_price_manipulation
     764    before update on offer
     765    for each row
     766    execute function prevent_price_manipulation();
     767}}}
     768
     769** Функција и тригер за контрола на времето на важност на понудата **
     770
     771Оваа функција враќа тригер кој спречува прифаќање на понуди кои се постари од 5 минути од моментот на нивното креирање. Доколку се направи обид за промена на статусот од `pending` во `accepted` по истекот на овој рок, статусот на понудата автоматски се менува во `expired` и истата не може да биде прифатена. Оваа логика овозможува динамичност на пазарот, со цел возачите и диспечерите да не бидат долго блокирани чекајќи одговор од патникот.
     772
     773{{{
     774create or replace function enforce_offer_validity()
     775    returns trigger as
     776$enforce_offer_validity$
     777begin
     778    if OLD.status = 'pending' and NEW.status = 'accepted' then
     779      if (NOW() - OLD.created_at) > interval '5 minutes' then
     780            NEW.status := 'expired';
     781            raise notice 'Offer % has expired and cannot be accepted.', OLD.id;
     782            return NEW;
     783        end if;
     784    end if;
     785
     786    return NEW;
     787end;
     788$enforce_offer_validity$ language plpgsql;
     789
     790create trigger trg_enforce_offer_validity
     791    before update on offer
     792    for each row
     793    execute function enforce_offer_validity();
     794}}}