| 1 | --================================================================================================================
|
|---|
| 2 | -- FUNCTIONS
|
|---|
| 3 | --================================================================================================================
|
|---|
| 4 |
|
|---|
| 5 | -- 1. Конверзија на валути
|
|---|
| 6 | --Функцијата служи за конвертирање на паричен износ од една валута во друга според
|
|---|
| 7 | -- курсевите зачувани во табелата Exchange_rate за одреден датум.
|
|---|
| 8 | -- Доколку валутите се исти се враќа соодветниот износ,
|
|---|
| 9 | -- а доколку не постои курс за дадениот датум се фрла exception.
|
|---|
| 10 | CREATE OR REPLACE FUNCTION fn_convert_currency(
|
|---|
| 11 | p_from_curr INT,
|
|---|
| 12 | p_to_curr INT,
|
|---|
| 13 | p_amount DECIMAL(20, 2),
|
|---|
| 14 | p_date DATE DEFAULT CURRENT_DATE
|
|---|
| 15 | )
|
|---|
| 16 | RETURNS DECIMAL(20, 2) AS
|
|---|
| 17 | $$
|
|---|
| 18 | DECLARE
|
|---|
| 19 | v_rate DECIMAL(15, 6);
|
|---|
| 20 | BEGIN
|
|---|
| 21 | IF p_from_curr = p_to_curr THEN
|
|---|
| 22 | RETURN p_amount;
|
|---|
| 23 | END IF;
|
|---|
| 24 |
|
|---|
| 25 | SELECT (er_to.rate / er_from.rate)
|
|---|
| 26 | INTO v_rate
|
|---|
| 27 | FROM Exchange_rate er_from
|
|---|
| 28 | JOIN Exchange_rate er_to
|
|---|
| 29 | ON er_to.currency_id = p_to_curr
|
|---|
| 30 | AND er_to.date_updated = p_date
|
|---|
| 31 | WHERE er_from.currency_id = p_from_curr
|
|---|
| 32 | AND er_from.date_updated = p_date;
|
|---|
| 33 |
|
|---|
| 34 | IF v_rate IS NULL THEN
|
|---|
| 35 | RAISE EXCEPTION 'Nema kurs za %', p_date;
|
|---|
| 36 | END IF;
|
|---|
| 37 |
|
|---|
| 38 | RETURN ROUND(p_amount * v_rate, 2);
|
|---|
| 39 | END;
|
|---|
| 40 | $$ LANGUAGE plpgsql;
|
|---|
| 41 |
|
|---|
| 42 | SELECT fn_convert_currency(2, 3, 100.00, '2026-04-12');
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 | -- 2. Проверка дали сметка е активна
|
|---|
| 46 | --Функцијата проверува дали даден клиент и неговата сметка се активни.
|
|---|
| 47 | -- Се користи како безбедносна проверка пред извршување финансиски операции како трансфер, уплата или исплата.
|
|---|
| 48 | -- Доколку сметката не постои или статусот не е ACTIVE, враќа FALSE.
|
|---|
| 49 | CREATE OR REPLACE FUNCTION fn_is_account_eligible(p_account_id INT)
|
|---|
| 50 | RETURNS BOOLEAN AS
|
|---|
| 51 | $$
|
|---|
| 52 | DECLARE
|
|---|
| 53 | v_acc_status VARCHAR(20);
|
|---|
| 54 | v_client_status VARCHAR(20);
|
|---|
| 55 | BEGIN
|
|---|
| 56 | SELECT a.status, c.status
|
|---|
| 57 | INTO v_acc_status, v_client_status
|
|---|
| 58 | FROM Account a
|
|---|
| 59 | JOIN Client c ON a.client_id = c.client_id
|
|---|
| 60 | WHERE a.account_id = p_account_id;
|
|---|
| 61 |
|
|---|
| 62 | IF NOT FOUND THEN
|
|---|
| 63 | RETURN FALSE;
|
|---|
| 64 | END IF;
|
|---|
| 65 |
|
|---|
| 66 | RETURN (v_acc_status = 'ACTIVE' AND v_client_status = 'ACTIVE');
|
|---|
| 67 | END;
|
|---|
| 68 | $$ LANGUAGE plpgsql;
|
|---|
| 69 |
|
|---|
| 70 | SELECT fn_is_account_eligible(1003);
|
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 | -- 3. Враќа вкупен неплатен долг на клиент (сума на сите PENDING/LATE рати)
|
|---|
| 74 | --Функцијата го пресметува вкупниот неплатен долг на клиентот преку собирање
|
|---|
| 75 | -- на сите рати со статус PENDING или LATE од одобрени кредити. Нејзината намена е следење
|
|---|
| 76 | -- на задолженоста на клиентите, кредитна анализа.
|
|---|
| 77 | -- Доколку клиентот нема неплатени обврски функцијата враќа вредност 0.
|
|---|
| 78 | CREATE OR REPLACE FUNCTION fn_get_client_total_debt(p_client_id INT)
|
|---|
| 79 | RETURNS DECIMAL(20, 2) AS
|
|---|
| 80 | $$
|
|---|
| 81 | DECLARE
|
|---|
| 82 | v_total DECIMAL(20, 2);
|
|---|
| 83 | BEGIN
|
|---|
| 84 | SELECT COALESCE(SUM(li.amount), 0)
|
|---|
| 85 | INTO v_total
|
|---|
| 86 | FROM Loan_installment li
|
|---|
| 87 | JOIN Loan l ON li.loan_id = l.loan_id
|
|---|
| 88 | WHERE l.client_id = p_client_id
|
|---|
| 89 | AND li.status IN ('PENDING', 'LATE')
|
|---|
| 90 | AND l.status = 'APPROVED';
|
|---|
| 91 |
|
|---|
| 92 | RETURN v_total;
|
|---|
| 93 | END;
|
|---|
| 94 | $$ LANGUAGE plpgsql;
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 | SELECT fn_get_client_total_debt(318290);
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
|
|---|
| 101 | -- 4. Враќа преостаната сума за отплата на кредит
|
|---|
| 102 | --Функцијата враќа детален преглед за состојбата на конкретен кредит.
|
|---|
| 103 | -- Се прикажуваат вкупниот износ, веќе исплатениот дел, преостанатиот долг,
|
|---|
| 104 | -- како и бројот на доцнења и неплатени рати.
|
|---|
| 105 | -- Доколку кредитот не постои, се прикажува соодветна грешка.
|
|---|
| 106 | CREATE OR REPLACE FUNCTION fn_get_loan_remaining_amount(p_loan_id INT)
|
|---|
| 107 | RETURNS TABLE
|
|---|
| 108 | (
|
|---|
| 109 | loan_id INT,
|
|---|
| 110 | total_amount DECIMAL(15, 2),
|
|---|
| 111 | paid_amount DECIMAL(15, 2),
|
|---|
| 112 | remaining_amount DECIMAL(15, 2),
|
|---|
| 113 | late_count INT,
|
|---|
| 114 | pending_count INT
|
|---|
| 115 | )
|
|---|
| 116 | AS
|
|---|
| 117 | $$
|
|---|
| 118 | BEGIN
|
|---|
| 119 | RETURN QUERY
|
|---|
| 120 | SELECT l.loan_id,
|
|---|
| 121 | l.amount,
|
|---|
| 122 | COALESCE(SUM(li.amount) FILTER (WHERE li.status = 'PAID'), 0)::DECIMAL(15, 2),
|
|---|
| 123 | COALESCE(SUM(li.amount) FILTER (WHERE li.status != 'PAID'), 0)::DECIMAL(15, 2),
|
|---|
| 124 | COUNT(*) FILTER (WHERE li.status = 'LATE')::INT,
|
|---|
| 125 | COUNT(*) FILTER (WHERE li.status = 'PENDING')::INT
|
|---|
| 126 | FROM Loan l
|
|---|
| 127 | JOIN Loan_installment li ON l.loan_id = li.loan_id
|
|---|
| 128 | WHERE l.loan_id = p_loan_id
|
|---|
| 129 | GROUP BY l.loan_id, l.amount;
|
|---|
| 130 |
|
|---|
| 131 | IF NOT FOUND THEN
|
|---|
| 132 | RAISE EXCEPTION 'Кредитот со ID % не постои.', p_loan_id;
|
|---|
| 133 | END IF;
|
|---|
| 134 | END;
|
|---|
| 135 | $$ LANGUAGE plpgsql;
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 | SELECT *
|
|---|
| 139 | FROM fn_get_loan_remaining_amount(86961);
|
|---|
| 140 |
|
|---|
| 141 |
|
|---|
| 142 | -- 5. Го пресметува очекуваниот износ на камата за штедна сметка за даден период
|
|---|
| 143 | --Оваа функција пресметува очекувана камата за штедна сметка во зададен временски период.
|
|---|
| 144 | -- Поддржува едноставна (SIMPLE) и сложена (COMPOUND) камата, како и различни периоди на капитализација.
|
|---|
| 145 | -- Пресметката се базира на моменталното салдо и каматната стапка на сметката.
|
|---|
| 146 | CREATE OR REPLACE FUNCTION fn_calculate_savings_interest(
|
|---|
| 147 | p_account_id INT,
|
|---|
| 148 | p_period_start DATE,
|
|---|
| 149 | p_period_end DATE
|
|---|
| 150 | )
|
|---|
| 151 | RETURNS DECIMAL(20, 2) AS
|
|---|
| 152 | $$
|
|---|
| 153 | DECLARE
|
|---|
| 154 | v_balance DECIMAL(20, 2);
|
|---|
| 155 | v_interest_rate DECIMAL(5, 2);
|
|---|
| 156 | v_interest_period VARCHAR(20);
|
|---|
| 157 | v_capitalization VARCHAR(20);
|
|---|
| 158 | v_days INT;
|
|---|
| 159 | v_interest_result DECIMAL(20, 2);
|
|---|
| 160 |
|
|---|
| 161 | v_time_factor_years DECIMAL(20, 6);
|
|---|
| 162 | v_compounding_per_year INT;
|
|---|
| 163 | BEGIN
|
|---|
| 164 | SELECT a.balance, sa.interest_rate, sa.interest_period, sa.capitalization_type
|
|---|
| 165 | INTO v_balance, v_interest_rate, v_interest_period, v_capitalization
|
|---|
| 166 | FROM Account a
|
|---|
| 167 | JOIN SavingsAccount sa ON a.account_id = sa.account_id
|
|---|
| 168 | WHERE a.account_id = p_account_id;
|
|---|
| 169 |
|
|---|
| 170 | IF NOT FOUND THEN
|
|---|
| 171 | RAISE EXCEPTION 'Сметка % не е штедна сметка или не постои.', p_account_id;
|
|---|
| 172 | END IF;
|
|---|
| 173 |
|
|---|
| 174 | v_days := p_period_end - p_period_start;
|
|---|
| 175 |
|
|---|
| 176 | IF v_interest_period = 'DAILY' THEN
|
|---|
| 177 | v_time_factor_years := v_days / 365.0;
|
|---|
| 178 | v_compounding_per_year := 365;
|
|---|
| 179 |
|
|---|
| 180 | ELSIF v_interest_period = 'MONTHLY' THEN
|
|---|
| 181 | v_time_factor_years := v_days / 365.0;
|
|---|
| 182 | v_compounding_per_year := 12;
|
|---|
| 183 |
|
|---|
| 184 | ELSE
|
|---|
| 185 |
|
|---|
| 186 | v_time_factor_years := v_days / 365.0;
|
|---|
| 187 | v_compounding_per_year := 1;
|
|---|
| 188 | END IF;
|
|---|
| 189 |
|
|---|
| 190 | -- SIMPLE INTEREST
|
|---|
| 191 | IF v_capitalization = 'SIMPLE' THEN
|
|---|
| 192 |
|
|---|
| 193 | v_interest_result :=
|
|---|
| 194 | v_balance * (v_interest_rate / 100.0) * v_time_factor_years;
|
|---|
| 195 |
|
|---|
| 196 | -- COMPOUND INTEREST
|
|---|
| 197 | ELSE
|
|---|
| 198 |
|
|---|
| 199 | v_interest_result :=
|
|---|
| 200 | v_balance * (
|
|---|
| 201 | POWER(
|
|---|
| 202 | 1 + (v_interest_rate / 100.0) / v_compounding_per_year,
|
|---|
| 203 | v_compounding_per_year * v_time_factor_years
|
|---|
| 204 | ) - 1
|
|---|
| 205 | );
|
|---|
| 206 |
|
|---|
| 207 | END IF;
|
|---|
| 208 |
|
|---|
| 209 | RETURN ROUND(v_interest_result, 2);
|
|---|
| 210 | END;
|
|---|
| 211 | $$ LANGUAGE plpgsql;
|
|---|
| 212 |
|
|---|
| 213 |
|
|---|
| 214 | SELECT fn_calculate_savings_interest(201, '2026-04-01', '2026-04-30');
|
|---|
| 215 |
|
|---|