= Резервирање термин == Актери * Најавен корисник (клиент) == Чекор 1 Најавениот корисник кликнува на копчето „Book Now“ од главната навигација. == Чекор 2 Системот ја прикажува страницата за резервирање термин со: * листа на услуги по категории (име, цена, времетраење), * листа на пакети (име, максимален број користења, вкупна цена и услуги во пакетот), * формa за избор на датум, термин и начин на плаќање. Податоците за категориите и услугите се вчитани преку SQL погледот `v_services_grouped_by_category`: {{{ SELECT category_id, category_name, services FROM v_services_grouped_by_category ORDER BY category_id; }}} Пакетите се вчитуваат со: {{{ SELECT p.package_id, p.name AS package_name, p.max_usage, CASE WHEN EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'package' AND column_name = 'total_price' ) THEN p.total_price ELSE package_price.calc_total_price END::numeric AS total_price, s.service_id, s.name AS service_name, ps.discounted_price FROM Package p LEFT JOIN LATERAL ( SELECT COALESCE(SUM(ps2.discounted_price), 0)::numeric AS calc_total_price FROM PackageService ps2 WHERE ps2.package_id = p.package_id ) package_price ON true LEFT JOIN PackageService ps ON ps.package_id = p.package_id LEFT JOIN Service s ON s.service_id = ps.service_id ORDER BY p.package_id, s.name; }}} == Чекор 3 Корисникот избира една или повеќе услуги (пр. Anti-cellulite, Back Massage…). Системот ја пресметува вкупната цена и вкупното времетраење на терминот врз основа на избраните услуги. Овие податоци подоцна ќе се користат при креирањето на терминот во базата (преку функциите `fn_service_total_minutes` и `fn_service_total_price` кои ги користи `sp_create_appointment`). == Чекор 4 Корисникот избира датум за резервација во делот „Choose Date & Time“. Системот, врз основа на избраниот датум и избраните услуги, ги вчитува сите слободни термински слотови за тој ден, користејќи ја функцијата `fn_available_slots`: {{{ SELECT to_char(start_time, 'YYYY-MM-DD"T"HH24:MI:SS') AS start_time, to_char(end_time, 'YYYY-MM-DD"T"HH24:MI:SS') AS end_time FROM fn_available_slots($1::date, $2::int[]); }}} Параметри: * `$1` – избраниот датум, * `$2` – низа од `service_id` на избраните услуги. Функцијата `fn_available_slots` ги користи записите од табелата `availability` (само оние со `is_closed = false`) и ги исклучува сите слотови кои се преклопуваат со постоечки закажани термини (`SCHEDULED` appointments). == Чекор 5 Системот му прикажува на корисникот листа од достапни термини (почетно време и траење) за избраниот датум. Корисникот избира еден термин од листата (пример 2025-05-10 14:00–15:00). == Чекор 6 Корисникот во делот „Payment Option“ ја избира опцијата **„Pay now (online card payment)“** и по желба внесува број на лојалти поени што сака да ги искористи. Системот ја прикажува проценетата вредност: * вкупна сума, * можен попуст преку поени, * финална сума за плаќање. (Пресметката на овој чекор е на апликативно ниво, но истиот број поени и сума ќе се користат во SQL функцијата за креирање плаќање.) == Чекор 7 Корисникот го потврдува закажувањето со клик на „Confirm booking“. Системот креира нов appointment во базата со повик на функцијата `sp_create_appointment`: {{{ SELECT sp_create_appointment($1, $2::timestamp, $3::int[], $4) AS appointment_id; }}} Параметри: * `$1` – `user_id` на најавениот корисник, * `$2` – избраниот `appointment_time` (почетно време на терминот), * `$3` – низа од избрани `service_id`, * `$4` – текстуална забелешка (`notes`), може да биде и `NULL`. Функцијата `sp_create_appointment`: * проверува дека сите услуги постојат во табелата `service`, * ја пресметува вкупната должина и крајното време на терминот, * го наоѓа статусот `SCHEDULED` во табелата `status`, * внесува запис во табелата `appointment`, * внесува записи во табелата `appointmentservice` за сите услуги, * ја повикува функцијата `fn_validate_appointment`, која проверува: * дека терминот е во иднина, * дека терминот е внатре во некој прозорец од табелата `availability`, * дека нема преклопување со друг `SCHEDULED` термин. == Чекор 8 По успешно креирање на appointment-от, системот креира плаќање за истиот термин користејќи ја функцијата `sp_create_payment`: {{{ SELECT * FROM sp_create_payment($1, $2::int, $3::text, $4::int); }}} Параметри: * `$1` – `user_id` на корисникот, * `$2` – `appointment_id` од претходниот чекор, * `$3` – метод на плаќање, на пример `'CARD'`, * `$4` – број на лојалти поени што корисникот сака да ги искористи. Функцијата: * го зема `total_price` од табелата `appointment`, * ја проверува лојалти картичката (`loyaltycard`) преку `sp_ensure_loyalty_card`, * пресметува колку поени може да се искористат (ограничено на процент од сумата), * внесува запис во табелата `payment` со статус `PENDING` и поле `points_used`. == Чекор 9 Кога плаќањето е успешно потврдено, системот го означува плаќањето како платено и ги ажурира лојалти поените со повик на функцијата `sp_mark_payment_paid`: {{{ SELECT sp_mark_payment_paid($1::int) AS points_used; }}} Параметар: * `$1` – `payment_id` на креираното плаќање. Функцијата: * го ажурира статусот на записот во `payment` на `PAID`, * ги намалува поените на корисникот (доколку користел поени) и ја поставува финалната сума, * осигурува дека нема друго `PAID` плаќање за истиот appointment. == Резултат * Во табелата `appointment` и `appointmentservice` се запишува успешно креиран термин со избраните услуги и временски опсег. * Во табелата `payment` постои запис за плаќањето со статус `PAID` и коректно ажурирани `points_used` и износ. * Корисникот на екранот добива потврда за успешно резервиран термин и подоцна може да го прегледа во „My Appointments“, како и да остави рецензија по извршување на услугата.