| Version 1 (modified by , 11 hours ago) ( diff ) |
|---|
Контрола на баланс со дозволен лимит за тип на акаунт
Опис
Овој тригер овозможува напредна валидација на трансакции со цел да се спречи надминување на дозволениот лимит на трансакциска сметка, при што:
- се поддржува дозволен лимит и наместо фиксна граница (0), тригерот користи параметаризиран лимит, кој се дефинира при креирање на тригерот
- се обработува edge case кога при
UPDATEсе менува трансакцискиот акаунт - се применува различна логика во зависност од типот на акаунт
- типот на акаунтот не е експлицитен атрибут, туку се утврдува преку името на акаунтот, кое мора да го содржи типот (пример: Cash Wallet, Credit Card VISA, Forex EUR Account)
Тригерот се извршува само ако името на акаунтот го содржи типот проследен како аргумент пред внесување или ажурирање на помошна трансакција
Пример:
- кеш сметки → лимит 0
- кредитни сметки → лимит -10000
Табели опфатени со тригерот
transaction_accounttransaction_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 код
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;
$$;
Креирање на тригерите:
- Кеш акаунти (без дозволен минус)
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)
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);
- Дебитни акаунти (без дозволен минус)
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)
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" вредности
- Лесна промена без измена на кодот
