| | 1 | = Контрола на баланс со дозволен лимит за тип на акаунт |
| | 2 | |
| | 3 | ==== Опис |
| | 4 | |
| | 5 | Овој тригер овозможува напредна валидација на трансакции со цел да се спречи надминување на дозволениот лимит на трансакциска сметка, при што: |
| | 6 | - се поддржува дозволен лимит и наместо фиксна граница (0), тригерот користи параметаризиран лимит, кој се дефинира при креирање на тригерот |
| | 7 | - се обработува edge case кога при `UPDATE` се менува трансакцискиот акаунт |
| | 8 | - се применува различна логика во зависност од типот на акаунт |
| | 9 | - типот на акаунтот не е експлицитен атрибут, туку се утврдува преку името на акаунтот, кое мора да го содржи типот (пример: Cash Wallet, Credit Card VISA, Forex EUR Account) |
| | 10 | |
| | 11 | Тригерот се извршува само ако името на акаунтот го содржи типот проследен како аргумент пред внесување или ажурирање на помошна трансакција |
| | 12 | |
| | 13 | Пример: |
| | 14 | - кеш сметки → лимит 0 |
| | 15 | - кредитни сметки → лимит -10000 |
| | 16 | |
| | 17 | ==== Табели опфатени со тригерот |
| | 18 | - `transaction_account` |
| | 19 | - `transaction_breakdown` |
| | 20 | |
| | 21 | ==== Тип на тригер |
| | 22 | - **BEFORE INSERT |
| | 23 | - **BEFORE UPDATE |
| | 24 | Активиран над табелата `transaction_breakdown` |
| | 25 | |
| | 26 | ==== Поддржани типови на акаунти |
| | 27 | || //Тип на акаунт || //Пример име || //Логика || |
| | 28 | || **CASH || Cash, Cash Wallet, Cash MKD || Не е дозволен негативен баланс || |
| | 29 | || **DEBIT || Debit Account || Не е дозволен негативен баланс || |
| | 30 | || **CREDIT || Credit Card VISA, Credit Card Gold || Дозволен негативен баланс до дефиниран лимит || |
| | 31 | || **FOREX || Forex EUR, FX USD || Дозволен негативен баланс до дефиниран лимит || |
| | 32 | |
| | 33 | ==== Параметри на тригерот |
| | 34 | || //Параметар || //Опис || |
| | 35 | || **`TG_ARGV[0]` || Тип на акаунт (пример: cash, credit, debit, forex) || |
| | 36 | || **`TG_ARGV[1]` || Дозволен минимален баланс (лимит) || |
| | 37 | |
| | 38 | ==== SQL код |
| | 39 | {{{#!sql |
| | 40 | CREATE OR REPLACE FUNCTION enforce_account_limit_by_name() |
| | 41 | RETURNS TRIGGER |
| | 42 | LANGUAGE plpgsql |
| | 43 | AS $$ |
| | 44 | DECLARE |
| | 45 | allowed_type TEXT; |
| | 46 | allowed_limit NUMERIC; |
| | 47 | |
| | 48 | old_balance NUMERIC; |
| | 49 | new_balance NUMERIC; |
| | 50 | |
| | 51 | old_account_name TEXT; |
| | 52 | new_account_name TEXT; |
| | 53 | BEGIN |
| | 54 | allowed_type := LOWER(TG_ARGV[0]); |
| | 55 | allowed_limit := TG_ARGV[1]::NUMERIC; |
| | 56 | |
| | 57 | -- =============================== |
| | 58 | -- INSERT |
| | 59 | -- =============================== |
| | 60 | IF TG_OP = 'INSERT' THEN |
| | 61 | SELECT account_name, balance |
| | 62 | INTO new_account_name, new_balance |
| | 63 | FROM transaction_account |
| | 64 | WHERE transaction_account_id = NEW.transaction_account_id; |
| | 65 | |
| | 66 | IF LOWER(new_account_name) LIKE '%' || allowed_type || '%' THEN |
| | 67 | new_balance := new_balance |
| | 68 | + COALESCE(NEW.earned_amount, 0) |
| | 69 | - COALESCE(NEW.spent_amount, 0); |
| | 70 | |
| | 71 | IF new_balance < allowed_limit THEN |
| | 72 | RAISE EXCEPTION |
| | 73 | 'The limit for the % account has been exceeded! Balance: %, limit: %', |
| | 74 | allowed_type, new_balance, allowed_limit; |
| | 75 | END IF; |
| | 76 | END IF; |
| | 77 | |
| | 78 | RETURN NEW; |
| | 79 | END IF; |
| | 80 | |
| | 81 | -- =============================== |
| | 82 | -- UPDATE |
| | 83 | -- =============================== |
| | 84 | IF TG_OP = 'UPDATE' THEN |
| | 85 | |
| | 86 | -- Читање на стар и нов акаунт |
| | 87 | SELECT account_name, balance |
| | 88 | INTO old_account_name, old_balance |
| | 89 | FROM transaction_account |
| | 90 | WHERE transaction_account_id = OLD.transaction_account_id; |
| | 91 | |
| | 92 | SELECT account_name, balance |
| | 93 | INTO new_account_name, new_balance |
| | 94 | FROM transaction_account |
| | 95 | WHERE transaction_account_id = NEW.transaction_account_id; |
| | 96 | |
| | 97 | -- Проверка на стариот акаунт (rollback ефект) |
| | 98 | IF LOWER(old_account_name) LIKE '%' || allowed_type || '%' THEN |
| | 99 | old_balance := old_balance |
| | 100 | - COALESCE(OLD.earned_amount, 0) |
| | 101 | + COALESCE(OLD.spent_amount, 0); |
| | 102 | |
| | 103 | IF old_balance < allowed_limit THEN |
| | 104 | RAISE EXCEPTION |
| | 105 | 'The limit for the % account has been exceeded during rollback! Balance: %, limit: %', |
| | 106 | allowed_type, old_balance, allowed_limit; |
| | 107 | END IF; |
| | 108 | END IF; |
| | 109 | |
| | 110 | -- Проверка на новиот акаунт (apply нов ефект) |
| | 111 | IF LOWER(new_account_name) LIKE '%' || allowed_type || '%' THEN |
| | 112 | new_balance := new_balance |
| | 113 | + COALESCE(NEW.earned_amount, 0) |
| | 114 | - COALESCE(NEW.spent_amount, 0); |
| | 115 | |
| | 116 | IF new_balance < allowed_limit THEN |
| | 117 | RAISE EXCEPTION |
| | 118 | 'The limit for the % account has been exceeded during updation! Balance: %, limit: %', |
| | 119 | allowed_type, new_balance, allowed_limit; |
| | 120 | END IF; |
| | 121 | END IF; |
| | 122 | |
| | 123 | RETURN NEW; |
| | 124 | END IF; |
| | 125 | |
| | 126 | RETURN NEW; |
| | 127 | END; |
| | 128 | $$; |
| | 129 | }}} |
| | 130 | |
| | 131 | ===== Креирање на тригерите: |
| | 132 | |
| | 133 | - Кеш акаунти (без дозволен минус) |
| | 134 | {{{#!sql |
| | 135 | CREATE TRIGGER trg_cash_account_limit |
| | 136 | BEFORE INSERT OR UPDATE |
| | 137 | ON transaction_breakdown |
| | 138 | FOR EACH ROW |
| | 139 | EXECUTE FUNCTION enforce_account_limit_by_name('cash', 0); |
| | 140 | }}} |
| | 141 | |
| | 142 | - Кредитни акаунти (дозволен минус до -10000) |
| | 143 | {{{#!sql |
| | 144 | CREATE TRIGGER trg_credit_account_limit |
| | 145 | BEFORE INSERT OR UPDATE |
| | 146 | ON transaction_breakdown |
| | 147 | FOR EACH ROW |
| | 148 | EXECUTE FUNCTION enforce_account_limit_by_name('credit', -10000); |
| | 149 | }}} |
| | 150 | |
| | 151 | - Дебитни акаунти (без дозволен минус) |
| | 152 | {{{#!sql |
| | 153 | CREATE TRIGGER trg_debit_account_limit |
| | 154 | BEFORE INSERT OR UPDATE |
| | 155 | ON transaction_breakdown |
| | 156 | FOR EACH ROW |
| | 157 | EXECUTE FUNCTION enforce_account_limit_by_name('debit', 0); |
| | 158 | }}} |
| | 159 | |
| | 160 | - Forex акаунти (дозволен минус до -1000) |
| | 161 | {{{#!sql |
| | 162 | CREATE TRIGGER trg_forex_account_limit |
| | 163 | BEFORE INSERT OR UPDATE |
| | 164 | ON transaction_breakdown |
| | 165 | FOR EACH ROW |
| | 166 | EXECUTE FUNCTION enforce_account_limit_by_name('forex', -1000); |
| | 167 | }}} |
| | 168 | |
| | 169 | ==== Објаснување на логиката |
| | 170 | |
| | 171 | Типот на акаунтот се утврдува со пребарување во `account_name`, се користи `LIKE %type%` за флексибилност |
| | 172 | |
| | 173 | При `UPDATE`: |
| | 174 | - се проверува стариот акаунт (rollback) |
| | 175 | - потоа новиот акаунт (apply) |
| | 176 | |
| | 177 | Лимитот се применува само ако типот се совпаѓа |
| | 178 | |
| | 179 | Дозволениот лимит се проследува како аргумент на тригерот, функцијата ја чита вредноста преку `TG_ARGV`, балансот се пресметува пред реалното запишување, доколку се надмине лимитот → трансакцијата се блокира |
| | 180 | |
| | 181 | Овој пристап овозможува флексибилна и повторно употреблива логика |
| | 182 | |
| | 183 | ==== Причина за користење |
| | 184 | - Поддршка за различни типови сметки |
| | 185 | - Централизирана контрола на бизнис логика |
| | 186 | - Избегнување "hardcoded" вредности |
| | 187 | - Лесна промена без измена на кодот |