= Контрола на баланс со дозволен лимит за тип на акаунт ==== Опис Овој тригер овозможува напредна валидација на трансакции со цел да се спречи надминување на дозволениот лимит на трансакциска сметка, при што: - се поддржува дозволен лимит и наместо фиксна граница (0), тригерот користи параметаризиран лимит, кој се дефинира при креирање на тригерот - се обработува edge case кога при `UPDATE` се менува трансакцискиот акаунт - се применува различна логика во зависност од типот на акаунт - типот на акаунтот не е експлицитен атрибут, туку се утврдува преку името на акаунтот, кое мора да го содржи типот (пример: Cash Wallet, Credit Card VISA, Forex EUR Account) Тригерот се извршува само ако името на акаунтот го содржи типот проследен како аргумент пред внесување или ажурирање на помошна трансакција Пример: - кеш сметки → лимит 0 - кредитни сметки → лимит -10000 ==== Табели опфатени со тригерот - `transaction_account` - `transaction_breakdown` ==== Тип на тригер - **BEFORE INSERT - **BEFORE UPDATE Активиран над табелата `transaction_breakdown` ==== Поддржани типови на акаунти || //Тип на акаунт || //Пример име || //Логика || || **CASH || Cash, Cash Wallet, Cash MKD || Не е дозволен негативен баланс || || **DEBIT || Debit Account || Не е дозволен негативен баланс || || **CREDIT || Credit Card VISA, Credit Card Gold || Дозволен негативен баланс до дефиниран лимит || || **FOREX || Forex EUR, FX USD || Дозволен негативен баланс до дефиниран лимит || ==== Параметри на тригерот || //Параметар || //Опис || || **`TG_ARGV[0]` || Тип на акаунт (пример: cash, credit, debit, forex) || || **`TG_ARGV[1]` || Дозволен минимален баланс (лимит) || ==== SQL код {{{#!sql CREATE OR REPLACE FUNCTION enforce_account_limit_by_name() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE allowed_type TEXT; allowed_limit NUMERIC; old_balance NUMERIC; new_balance NUMERIC; old_account_name TEXT; new_account_name TEXT; BEGIN allowed_type := LOWER(TG_ARGV[0]); allowed_limit := TG_ARGV[1]::NUMERIC; -- =============================== -- INSERT -- =============================== IF TG_OP = 'INSERT' THEN SELECT account_name, balance INTO new_account_name, new_balance FROM transaction_account WHERE transaction_account_id = NEW.transaction_account_id; IF LOWER(new_account_name) LIKE '%' || allowed_type || '%' THEN new_balance := new_balance + COALESCE(NEW.earned_amount, 0) - COALESCE(NEW.spent_amount, 0); IF new_balance < allowed_limit THEN RAISE EXCEPTION 'The limit for the % account has been exceeded! Balance: %, limit: %', allowed_type, new_balance, allowed_limit; END IF; END IF; RETURN NEW; END IF; -- =============================== -- UPDATE -- =============================== IF TG_OP = 'UPDATE' THEN -- Читање на стар и нов акаунт SELECT account_name, balance INTO old_account_name, old_balance FROM transaction_account WHERE transaction_account_id = OLD.transaction_account_id; SELECT account_name, balance INTO new_account_name, new_balance FROM transaction_account WHERE transaction_account_id = NEW.transaction_account_id; -- Проверка на стариот акаунт (rollback ефект) IF LOWER(old_account_name) LIKE '%' || allowed_type || '%' THEN old_balance := old_balance - COALESCE(OLD.earned_amount, 0) + COALESCE(OLD.spent_amount, 0); IF old_balance < allowed_limit THEN RAISE EXCEPTION 'The limit for the % account has been exceeded during rollback! Balance: %, limit: %', allowed_type, old_balance, allowed_limit; END IF; END IF; -- Проверка на новиот акаунт (apply нов ефект) IF LOWER(new_account_name) LIKE '%' || allowed_type || '%' THEN new_balance := new_balance + COALESCE(NEW.earned_amount, 0) - COALESCE(NEW.spent_amount, 0); IF new_balance < allowed_limit THEN RAISE EXCEPTION 'The limit for the % account has been exceeded during updation! Balance: %, limit: %', allowed_type, new_balance, allowed_limit; END IF; END IF; RETURN NEW; END IF; RETURN NEW; END; $$; }}} ===== Креирање на тригерите: - Кеш акаунти (без дозволен минус) {{{#!sql CREATE TRIGGER trg_cash_account_limit BEFORE INSERT OR UPDATE ON transaction_breakdown FOR EACH ROW EXECUTE FUNCTION enforce_account_limit_by_name('cash', 0); }}} - Кредитни акаунти (дозволен минус до -10000) {{{#!sql CREATE TRIGGER trg_credit_account_limit BEFORE INSERT OR UPDATE ON transaction_breakdown FOR EACH ROW EXECUTE FUNCTION enforce_account_limit_by_name('credit', -10000); }}} - Дебитни акаунти (без дозволен минус) {{{#!sql CREATE TRIGGER trg_debit_account_limit BEFORE INSERT OR UPDATE ON transaction_breakdown FOR EACH ROW EXECUTE FUNCTION enforce_account_limit_by_name('debit', 0); }}} - Forex акаунти (дозволен минус до -1000) {{{#!sql CREATE TRIGGER trg_forex_account_limit BEFORE INSERT OR UPDATE ON transaction_breakdown FOR EACH ROW EXECUTE FUNCTION enforce_account_limit_by_name('forex', -1000); }}} ==== Објаснување на логиката Типот на акаунтот се утврдува со пребарување во `account_name`, се користи `LIKE %type%` за флексибилност При `UPDATE`: - се проверува стариот акаунт (rollback) - потоа новиот акаунт (apply) Лимитот се применува само ако типот се совпаѓа Дозволениот лимит се проследува како аргумент на тригерот, функцијата ја чита вредноста преку `TG_ARGV`, балансот се пресметува пред реалното запишување, доколку се надмине лимитот → трансакцијата се блокира Овој пристап овозможува флексибилна и повторно употреблива логика ==== Причина за користење - Поддршка за различни типови сметки - Централизирана контрола на бизнис логика - Избегнување "hardcoded" вредности - Лесна промена без измена на кодот