= Купување пакет == Актери * Најавен корисник (клиент) == Чекор 1 Најавениот корисник ја отвора страницата за пакети, на пример: * страницата '''Book an Appointment''' (/book), каде се прикажува листа на пакети, или * страницата '''My Packages''' (/my-packages). Системот му прикажува листа на достапни пакети со: * име на пакет, * максимален број користења (max_usage), * вкупна цена, * услугите што се вклучени во пакетот. Податоците за пакетите се вчитуваат со следниот SQL: {{{ 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; }}} == Чекор 2 Корисникот избира едно од две можни дејства: * '''Buy only''' – корисникот го купува пакетот, без веднаш да резервира термин. Во овој случај, системот само ја евидентира куповката во базата. * '''Buy & use now''' – корисникот го купува пакетот и веднаш планира да го искористи во тековната резервација. Во овој случај, по успешно купување, сценариото продолжува кон чекорите за резервирање термин, при што купениот пакет ќе се примени на резервацијата. == Чекор 3 Системот го проверува избраниот пакет и ја пресметува вкупната цена што треба да се плати. Се вчитува пакетот од базата според ''package_id'': {{{ SELECT p.package_id, p.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 FROM package p LEFT JOIN LATERAL ( SELECT COALESCE(SUM(ps.discounted_price), 0)::numeric AS calc_total_price FROM packageservice ps WHERE ps.package_id = p.package_id ) package_price ON true WHERE p.package_id = $1; }}} Параметар: * $1 – ''package_id'' на избраниот пакет. Ако пакетот не постои, системот враќа грешка и купувањето се прекинува. == Чекор 4 Доколку пакетот е валиден, системот креира запис дека корисникот го купил пакетот, преку складираната функција '''sp_user_purchase_package''': {{{ SELECT sp_user_purchase_package($1::int, $2::int) AS purchase_id; }}} Параметри: * $1 – ''user_id'' на најавениот корисник, * $2 – ''package_id'' на избраниот пакет. Функцијата ''sp_user_purchase_package'' вметнува ред во табелата ''UserPackagePurchase'' со: * ''user_id'' – корисникот кој купува пакет, * ''package_id'' – избраниот пакет, * ''total_uses'' – максимален број користења (''max_usage'' од ''Package''), * ''remaining_uses'' – почетно еднакво на ''total_uses'', * ''status'' – 'ACTIVE'. == Чекор 5 Потоа системот креира запис за плаќањето на овој пакет во табелата ''payment''. Ова плаќање е поврзано со купениот пакет (''package_purchase_id''), а не со конкретен термин (''appointment_id'' е NULL): {{{ INSERT INTO payment (amount, method, status, appointment_id, points_used, package_purchase_id) VALUES ($1::numeric, $2::text, 'PENDING', NULL, 0, $3::int) RETURNING payment_id; }}} Параметри: * $1 – вкупната цена на пакетот (''total_price'' од Чекор 3), * $2 – метод на плаќање, на пример 'online' или 'CARD', * $3 – ''purchase_id'' добиен од ''sp_user_purchase_package''. Во овој тек: * ''appointment_id'' е NULL (се купува пакет, не термин), * ''points_used = 0'' – не се дозволува користење лојалти поени за купување пакет. Ограничувањата за ова плаќање се спроведуваат и преку trigger функцијата '''trg_payment_validate''', која осигурува дека: * има или ''appointment_id'' или ''package_purchase_id'', * за купување пакет (''appointment_id IS NULL'' и ''package_purchase_id IS NOT NULL'') сумата на плаќањето одговара на вкупната цена на пакетот, * не може да се користат лојалти поени (''points_used'' мора да биде 0). == Чекор 6 По успешно креирање на плаќањето, системот го означува плаќањето како платено и доделува лојалти поени на корисникот со повик на функцијата '''sp_mark_payment_paid''': {{{ SELECT sp_mark_payment_paid($1::int) AS points_used; }}} Параметар: * $1 – ''payment_id'' на креираното плаќање. Во случај на купување пакет (т.е. кога плаќањето има ''package_purchase_id'' и ''appointment_id IS NULL''), функцијата: * проверува дека не постои друго 'PAID' плаќање за истото ''package_purchase_id'', * ја ажурира табелата ''payment'': * ''status = 'PAID' '', * ''amount'' = вкупната цена на пакетот, * ''points_used = 0'', * се грижи да постои лојалти картичка за корисникот преку ''sp_ensure_loyalty_card'', * пресметува колку поени да се доделат (на пример 5% од вкупната цена на пакетот) и ги зголемува ''points'' во табелата ''loyaltycard''. Пример логика за доделување поени (во функцијата): * ''v_points_earned := FLOOR(COALESCE(v_total_price, 0) * 0.05);'' * ''UPDATE loyaltycard SET points = points + v_points_earned WHERE user_id = v_user_id;'' == Чекор 7 По означувањето на плаќањето како платено, системот ги враќа деталите за купениот пакет за прикажување на корисникот: {{{ SELECT upp.purchase_id, upp.package_id, p.name AS package_name, upp.total_uses, upp.remaining_uses, upp.status, upp.purchased_at, upp.expires_at FROM userpackagepurchase upp JOIN package p ON p.package_id = upp.package_id WHERE upp.purchase_id = $1 LIMIT 1; }}} Параметар: * $1 – ''purchase_id'' на купениот пакет. Дополнително, се вчитува и моменталниот број лојалти поени: {{{ SELECT points FROM loyaltycard WHERE user_id = $1; }}} == Резултат * Во табелата ''UserPackagePurchase'' постои нов запис за купениот пакет, со ''status = 'ACTIVE' '' и иницијално ''remaining_uses = total_uses''. * Во табелата ''payment'' постои запис за плаќањето на пакетот со ''status = 'PAID' '', правилен износ и ''points_used = 0''. * Во табелата ''loyaltycard'' се ажурира бројот на поени на корисникот (се доделуваат поени за купениот пакет). * Корисникот на екранот добива потврда дека пакетот е успешно купен и може: * да го види во '''My Packages''' со број на преостанати користења, или * во случај на опцијата '''Buy & use now''', веднаш да го искористи при создавање термин