| Version 2 (modified by , 13 days ago) ( diff ) |
|---|
Фаза 4 — Функции, Процедури, Тригери
Тригери
1. trg_validate_ballot
Тип: BEFORE INSERT на табела ballot
Се активира пред секое внесување на гласачко ливче и ги проверува три услови: дали изборот е активен (status=1), дали гласачкото место е назначено на тој избор, и дали entity_id учествувал во изборот. Ја имплементира бизнис логиката за интегритет на гласање — спречува невалидни или фалсификувани ливчиња да влезат во системот.
CREATE OR REPLACE FUNCTION public.fn_validate_ballot()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
v_status SMALLINT;
v_station_ok BOOLEAN;
v_entity_ok BOOLEAN;
BEGIN
-- Check (a): election must be active
SELECT status
INTO v_status
FROM election
WHERE election_id = NEW.election_id;
IF NOT FOUND THEN
RAISE EXCEPTION
'Election % does not exist', NEW.election_id;
END IF;
IF v_status <> 1 THEN
RAISE EXCEPTION
'Election % is not active (status=%). Ballots cannot be cast.',
NEW.election_id, v_status;
END IF;
-- Check (b): station must be in this election
SELECT EXISTS (
SELECT 1
FROM station_election
WHERE station_id = NEW.station_id
AND election_id = NEW.election_id
) INTO v_station_ok;
IF NOT v_station_ok THEN
RAISE EXCEPTION
'Polling station % is not assigned to election %.',
NEW.station_id, NEW.election_id;
END IF;
-- Check (c/d): entity validation for valid ballots
IF NEW.is_valid = TRUE THEN
IF NEW.entity_id IS NULL AND NEW.candidate_id IS NULL THEN
RAISE EXCEPTION
'A valid ballot must have either an entity_id or candidate_id.';
END IF;
IF NEW.entity_id IS NOT NULL THEN
SELECT EXISTS (
SELECT 1
FROM election_participant ep
JOIN electoral_district ed
ON ed.district_id = ep.district_id
WHERE ep.entity_id = NEW.entity_id
AND ed.election_id = NEW.election_id
) INTO v_entity_ok;
IF NOT v_entity_ok THEN
RAISE EXCEPTION
'Entity % did not participate in election %.',
NEW.entity_id, NEW.election_id;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$function$;
-- Trigger definition
CREATE TRIGGER trg_validate_ballot
BEFORE INSERT ON public.ballot
FOR EACH ROW EXECUTE FUNCTION fn_validate_ballot();
2. trg_after_insert_ballot_live_count
Тип: AFTER INSERT на табела ballot
Се активира по секое внесување на валидно ливче и автоматски го ажурира бројачот во vote_result преку UPSERT — ако записот постои го зголемува за 1, инаку создава нов. Ја имплементира логиката за live бројање на гласови во реално време, без потреба од рачна агрегација на резултати.
CREATE OR REPLACE FUNCTION public.fn_trigger_live_vote_counter()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
v_result_id_exists BIGINT;
v_max_id BIGINT;
BEGIN
IF NEW.is_valid = TRUE THEN
SELECT result_id INTO v_result_id_exists
FROM public.vote_result
WHERE election_id = NEW.election_id
AND station_id = NEW.station_id
AND entity_id = NEW.entity_id
AND (candidate_id = NEW.candidate_id
OR (candidate_id IS NULL AND NEW.candidate_id IS NULL));
IF v_result_id_exists IS NOT NULL THEN
UPDATE public.vote_result
SET votes = votes + 1
WHERE result_id = v_result_id_exists;
ELSE
SELECT COALESCE(MAX(result_id), 0) + 1
INTO v_max_id
FROM public.vote_result;
INSERT INTO public.vote_result
(result_id, election_id, station_id, entity_id, candidate_id, votes)
VALUES
(v_max_id, NEW.election_id, NEW.station_id, NEW.entity_id, NEW.candidate_id, 1);
END IF;
END IF;
RETURN NEW;
END;
$function$;
-- Trigger definition
CREATE TRIGGER trg_after_insert_ballot_live_count
AFTER INSERT ON public.ballot
FOR EACH ROW EXECUTE FUNCTION fn_trigger_live_vote_counter();
3. trg_before_insert_voter_age
Тип: BEFORE INSERT на табела voter
Се активира пред регистрација на нов гласач и проверува дали лицето е полнолетно (≥18 години), фрлајќи детален исклучок со името на лицето ако не е. Директно ја имплементира законската забрана за гласање на малолетни лица на ниво на базата.
CREATE OR REPLACE FUNCTION public.fn_check_voter_age_limit()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
v_birth_date DATE;
v_full_name TEXT;
BEGIN
SELECT date_of_birth, (name || ' ' || surname)
INTO v_birth_date, v_full_name
FROM public.person
WHERE person_id = NEW.person_id;
IF NOT FOUND THEN
RAISE EXCEPTION
'Грешка: Лицето со ID % не постои во матичната евиденција (табела person).',
NEW.person_id;
END IF;
IF v_birth_date > CURRENT_DATE - INTERVAL '18 years' THEN
RAISE EXCEPTION
'Критична грешка: Лицето "%" (ID: %) има % години и е малолетно. '
'Регистрацијата во базата на гласачи е ОДБИЕНА.',
v_full_name,
NEW.person_id,
EXTRACT(YEAR FROM AGE(CURRENT_DATE, v_birth_date));
END IF;
RETURN NEW;
END;
$function$;
-- Trigger definition
CREATE TRIGGER trg_before_insert_voter_age
BEFORE INSERT ON public.voter
FOR EACH ROW EXECUTE FUNCTION fn_check_voter_age_limit();
4. trg_after_voter_change_sync_stats
Тип: AFTER INSERT OR DELETE на табела voter
Автоматски го ажурира registered_voter бројачот во polling_station при секоја промена на гласачкиот список — +1 при регистрација, -1 при бришење. Ја одржува конзистентноста на статистиките за регистрирани гласачи без рачно одржување.
CREATE OR REPLACE FUNCTION public.fn_trigger_sync_registered_voters()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE public.polling_station
SET registered_voter = COALESCE(registered_voter, 0) + 1
WHERE station_id = NEW.station_id;
ELSIF TG_OP = 'DELETE' THEN
UPDATE public.polling_station
SET registered_voter = COALESCE(registered_voter, 0) - 1
WHERE station_id = OLD.station_id;
END IF;
RETURN NULL;
END;
$function$;
-- Trigger definition
CREATE TRIGGER trg_after_voter_change_sync_stats
AFTER INSERT OR DELETE ON public.voter
FOR EACH ROW EXECUTE FUNCTION fn_trigger_sync_registered_voters();
5. trg_prevent_duplicate_voter_checkin
Тип: BEFORE INSERT на табела voter_election
Проверува три услови: изборот е активен, гласачот не се пријавил веќе на тој избор, и check-in е на неговата матична станица. Ја имплементира основната изборна контрола против двојно гласање и гласање надвор од матичното место.
CREATE OR REPLACE FUNCTION public.fn_prevent_duplicate_voter_checkin()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
v_registered_station BIGINT;
v_election_status SMALLINT;
v_election_name VARCHAR;
v_voter_name TEXT;
BEGIN
-- (1) Изборот мора да биде активен
SELECT e.status, e.name
INTO v_election_status, v_election_name
FROM election e
WHERE e.election_id = NEW.election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Election % does not exist.', NEW.election_id;
END IF;
IF v_election_status <> 1 THEN
RAISE EXCEPTION
'Election "%" is not active. Voter check-in is not allowed.',
v_election_name;
END IF;
-- (2) Гласачот не смее двапати да се пријави
IF EXISTS (
SELECT 1
FROM voter_election ve
WHERE ve.voter_id = NEW.voter_id
AND ve.election_id = NEW.election_id
) THEN
SELECT (p.name || ' ' || p.surname)
INTO v_voter_name
FROM voter v
JOIN person p ON p.person_id = v.person_id
WHERE v.voter_id = NEW.voter_id;
RAISE EXCEPTION
'Voter "%" (id: %) has already checked in for election "%".',
v_voter_name, NEW.voter_id, v_election_name;
END IF;
-- (3) Check-in мора да биде на матичната станица
SELECT v.station_id
INTO v_registered_station
FROM voter v
WHERE v.voter_id = NEW.voter_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Voter % does not exist.', NEW.voter_id;
END IF;
IF v_registered_station <> NEW.station_id THEN
RAISE EXCEPTION
'Voter % must check in at their registered station (%). '
'Attempted check-in at station %.',
NEW.voter_id, v_registered_station, NEW.station_id;
END IF;
RETURN NEW;
END;
$function$;
-- Trigger definition
CREATE TRIGGER trg_prevent_duplicate_voter_checkin
BEFORE INSERT ON public.voter_election
FOR EACH ROW EXECUTE FUNCTION fn_prevent_duplicate_voter_checkin();
Функции
6. fn_calculate_dhondt
Сигнатура: fn_calculate_dhondt(p_election_id bigint, p_district_id bigint)
Враќа: TABLE(round, entity_id, entity_name, votes, divisor, quotient, seat_awarded)
Ја пресметува распределбата на мандати по D'Hondt метод за даден избор и изборна единица — генерира табела со сите квоциенти, дивизори, позиции и флаг дали е доделен мандат. Ја имплементира пропорционалната изборна математика која е законски задолжителна за парламентарни избори во Македонија и Србија, со вграден 5% праг на влез.
CREATE OR REPLACE FUNCTION public.fn_calculate_dhondt(p_election_id bigint, p_district_id bigint)
RETURNS TABLE(round bigint, entity_id bigint, entity_name character varying,
votes bigint, divisor integer, quotient numeric, seat_awarded boolean)
LANGUAGE plpgsql
AS $function$
DECLARE
v_seats INTEGER;
v_method_id BIGINT;
v_election_name VARCHAR;
v_region_name VARCHAR;
BEGIN
SELECT e.winner_method_id, e.name
INTO v_method_id, v_election_name
FROM election e
WHERE e.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Election % does not exist', p_election_id;
END IF;
IF v_method_id <> 8 THEN
RAISE EXCEPTION
'Election "%" does not use D''Hondt method. '
'This function only works for proportional elections.',
v_election_name;
END IF;
SELECT ed.seats_available, r.name
INTO v_seats, v_region_name
FROM electoral_district ed
JOIN region r ON r.region_id = ed.region_id
WHERE ed.district_id = p_district_id
AND ed.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION
'District % does not exist for election %',
p_district_id, p_election_id;
END IF;
IF v_seats IS NULL OR v_seats = 0 THEN
RAISE EXCEPTION 'District % has no seats defined', p_district_id;
END IF;
RAISE NOTICE 'Calculating D''Hondt for: % — % (% seats)',
v_election_name, v_region_name, v_seats;
RETURN QUERY
WITH
-- Чекор 1: гласови по ентитет во дистриктот
raw_votes AS (
SELECT
COALESCE(pe2.entity_id, vr.entity_id) AS entity_id,
SUM(vr.votes) AS district_votes
FROM vote_result vr
JOIN polling_station ps ON ps.station_id = vr.station_id
JOIN region r2 ON r2.region_id = ps.municipality_id
JOIN political_entity pe ON pe.entity_id = vr.entity_id
LEFT JOIN coalition_member cm ON cm.party_id = pe.party_id
LEFT JOIN party_coalition pc ON pc.coalition_id = cm.coalition_id
AND pc.election_id = vr.election_id
LEFT JOIN political_entity pe2 ON pe2.coalition_id = pc.coalition_id
WHERE vr.election_id = p_election_id
AND vr.candidate_id IS NULL
AND (
r2.region_id = (SELECT region_id FROM electoral_district
WHERE district_id = p_district_id)
OR
r2.parent_region_id = (SELECT region_id FROM electoral_district
WHERE district_id = p_district_id)
)
GROUP BY COALESCE(pe2.entity_id, vr.entity_id)
),
-- Чекор 2: филтрирање на ентитети над 5% праг
district_total AS (
SELECT SUM(district_votes) AS total FROM raw_votes
),
eligible AS (
SELECT rv.entity_id, rv.district_votes
FROM raw_votes rv
CROSS JOIN district_total dt
WHERE rv.district_votes * 100.0 / NULLIF(dt.total, 0) >= 5.0
),
-- Чекор 3: генерирање на сите квоциенти
quotients AS (
SELECT
e.entity_id,
e.district_votes,
d.divisor,
ROUND(e.district_votes::NUMERIC / d.divisor, 4) AS quotient
FROM eligible e
CROSS JOIN generate_series(1, v_seats) AS d(divisor)
),
-- Чекор 4: рангирање на сите квоциенти
ranked AS (
SELECT
q.*,
RANK() OVER (ORDER BY q.quotient DESC) AS global_rank
FROM quotients q
)
-- Чекор 5: враќање со флаг за доделен мандат
SELECT
r.global_rank AS round,
r.entity_id,
pe.name AS entity_name,
r.district_votes AS votes,
r.divisor,
r.quotient,
(r.global_rank <= v_seats) AS seat_awarded
FROM ranked r
JOIN political_entity pe ON pe.entity_id = r.entity_id
ORDER BY r.global_rank, r.entity_id;
END;
$function$;
7. fn_get_candidate_list_for_entity
Сигнатура: fn_get_candidate_list_for_entity(p_entity_id bigint, p_election_id bigint)
Враќа: TABLE(list_position, candidate_id, full_name, date_of_birth, gender, municipality, party_name, entity_name, election_name)
Ја враќа целосната листа на кандидати за даден политички ентитет на даден избор, подредена по позиција, со лични податоци, партиска припадност и општина. Ја имплементира логиката за јавна транспарентност — граѓаните и медиумите можат да видат кој е на каква позиција на изборната листа.
CREATE OR REPLACE FUNCTION public.fn_get_candidate_list_for_entity(p_entity_id bigint, p_election_id bigint)
RETURNS TABLE(list_position integer, candidate_id bigint, full_name text,
date_of_birth date, gender character, municipality character varying,
party_name character varying, entity_name character varying,
election_name character varying)
LANGUAGE plpgsql
AS $function$
DECLARE
v_entity_name VARCHAR;
v_election_name VARCHAR;
v_list_id BIGINT;
BEGIN
SELECT pe.name INTO v_entity_name
FROM political_entity pe
WHERE pe.entity_id = p_entity_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Political entity % does not exist.', p_entity_id;
END IF;
SELECT e.name INTO v_election_name
FROM election e
WHERE e.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Election % does not exist.', p_election_id;
END IF;
SELECT cl.list_id INTO v_list_id
FROM candidate_list cl
WHERE cl.entity_id = p_entity_id
AND cl.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION
'No candidate list found for entity "%" on election "%".',
v_entity_name, v_election_name;
END IF;
RAISE NOTICE 'Candidate list for "%" — Election: "%" (list_id: %)',
v_entity_name, v_election_name, v_list_id;
RETURN QUERY
SELECT
cli.position AS list_position,
c.candidate_id,
(p.name || ' ' || p.surname)::TEXT AS full_name,
p.date_of_birth,
p.gender,
COALESCE(r.name, 'No municipality data') AS municipality,
COALESCE(
(
SELECT pp.name
FROM candidate_party cp
JOIN political_party pp ON pp.party_id = cp.party_id
WHERE cp.candidate_id = c.candidate_id
AND cp.election_id = p_election_id
ORDER BY cp.candidate_party_id
LIMIT 1
),
v_entity_name
) AS party_name,
v_entity_name AS entity_name,
v_election_name AS election_name
FROM candidate_list_item cli
JOIN candidate c ON c.candidate_id = cli.candidate_id
JOIN person p ON p.person_id = c.person_id
LEFT JOIN region r ON r.region_id = p.municipality_id
WHERE cli.list_id = v_list_id
ORDER BY cli.position ASC;
END;
$function$;
8. fn_station_anomaly_score
Сигнатура: fn_station_anomaly_score(p_station_id bigint, p_election_id bigint)
Враќа: TABLE(station_id, station_name, election_name, registered_voters, ballots_cast, valid_ballots, invalid_ballots, turnout_pct, invalid_pct, total_votes_vr, anomaly_score, flag_high_turnout, flag_impossible_turnout, flag_high_invalid, flag_no_ballots, flag_impossible_votes, recommendation)
Ја анализира статистиката на гласачко место и пресметува anomaly_score врз основа на 5 флагови: висока излезност (>95%), невозможна излезност (>100%), многу невалидни ливчиња (>10%), нула ливчиња, и гласови поголеми од регистрираните гласачи. Ја имплементира автоматската детекција на потенцијални изборни измами или грешки во внесот со конкретна препорака за акција.
| Anomaly Score | Препорака |
|---|---|
| 0 | OK — No anomalies detected. |
| 1–2 | MINOR — Monitor this station. |
| 3–5 | WARNING — Manual review recommended. |
| >5 | CRITICAL — Immediate investigation required. |
CREATE OR REPLACE FUNCTION public.fn_station_anomaly_score(p_station_id bigint, p_election_id bigint)
RETURNS TABLE(station_id bigint, station_name character varying, election_name character varying,
registered_voters integer, ballots_cast bigint, valid_ballots bigint,
invalid_ballots bigint, turnout_pct numeric, invalid_pct numeric,
total_votes_vr bigint, anomaly_score integer,
flag_high_turnout boolean, flag_impossible_turnout boolean,
flag_high_invalid boolean, flag_no_ballots boolean,
flag_impossible_votes boolean, recommendation text)
LANGUAGE plpgsql
AS $function$
DECLARE
v_station_name VARCHAR;
v_election_name VARCHAR;
v_registered INT;
v_ballots BIGINT;
v_valid BIGINT;
v_invalid BIGINT;
v_votes_vr BIGINT;
v_turnout NUMERIC;
v_invalid_pct NUMERIC;
v_score INT := 0;
v_flag_turnout BOOLEAN := FALSE;
v_flag_impossible BOOLEAN := FALSE;
v_flag_invalid BOOLEAN := FALSE;
v_flag_no_ballot BOOLEAN := FALSE;
v_flag_imp_votes BOOLEAN := FALSE;
v_recommendation TEXT;
BEGIN
SELECT ps.name, ps.registered_voter
INTO v_station_name, v_registered
FROM polling_station ps
WHERE ps.station_id = p_station_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Polling station % does not exist.', p_station_id;
END IF;
SELECT e.name INTO v_election_name
FROM election e
WHERE e.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Election % does not exist.', p_election_id;
END IF;
IF NOT EXISTS (
SELECT 1 FROM station_election se
WHERE se.station_id = p_station_id
AND se.election_id = p_election_id
) THEN
RAISE EXCEPTION 'Station % is not assigned to election %.',
p_station_id, p_election_id;
END IF;
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE b.is_valid = TRUE),
COUNT(*) FILTER (WHERE b.is_valid = FALSE)
INTO v_ballots, v_valid, v_invalid
FROM ballot b
WHERE b.station_id = p_station_id
AND b.election_id = p_election_id;
SELECT COALESCE(SUM(vr.votes), 0)
INTO v_votes_vr
FROM vote_result vr
WHERE vr.station_id = p_station_id
AND vr.election_id = p_election_id;
v_turnout := ROUND(
v_ballots::NUMERIC * 100.0 / NULLIF(COALESCE(v_registered, 0), 0), 2);
v_invalid_pct := ROUND(
v_invalid::NUMERIC * 100.0 / NULLIF(v_ballots, 0), 2);
-- Flag 1: Излезност > 95%
IF v_turnout > 95.0 THEN
v_flag_turnout := TRUE;
v_score := v_score + 3;
END IF;
-- Flag 2: Излезност > 100% (невозможно)
IF v_turnout > 100.0 THEN
v_flag_impossible := TRUE;
v_score := v_score + 10;
END IF;
-- Flag 3: Невалидни > 10%
IF v_invalid_pct > 10.0 THEN
v_flag_invalid := TRUE;
v_score := v_score + 2;
END IF;
-- Flag 4: Нула балоти
IF v_ballots = 0 THEN
v_flag_no_ballot := TRUE;
v_score := v_score + 2;
END IF;
-- Flag 5: Гласови во vote_result > регистрирани гласачи
IF v_votes_vr > COALESCE(v_registered, 0) THEN
v_flag_imp_votes := TRUE;
v_score := v_score + 5;
END IF;
v_recommendation := CASE
WHEN v_score = 0 THEN 'OK — No anomalies detected.'
WHEN v_score BETWEEN 1 AND 2 THEN 'MINOR — Monitor this station.'
WHEN v_score BETWEEN 3 AND 5 THEN 'WARNING — Manual review recommended.'
ELSE 'CRITICAL — Immediate investigation required.'
END;
RETURN QUERY
SELECT
p_station_id, v_station_name, v_election_name,
v_registered, v_ballots, v_valid, v_invalid,
COALESCE(v_turnout, 0), COALESCE(v_invalid_pct, 0),
v_votes_vr, v_score,
v_flag_turnout, v_flag_impossible, v_flag_invalid,
v_flag_no_ballot, v_flag_imp_votes, v_recommendation;
END;
$function$;
Процедури
9. sp_close_election
Сигнатура: sp_close_election(IN p_election_id bigint)
Ги верифицира резултатите, го наоѓа победникот со процент на гласови, го затвора изборот (status=0) и логира детален извештај. Ја имплементира официјалната процедура за завршување на изборен циклус — само избори со внесени резултати можат да се затворат.
CREATE OR REPLACE PROCEDURE public.sp_close_election(IN p_election_id bigint)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_election_name VARCHAR;
v_election_type VARCHAR;
v_status SMALLINT;
v_station_count INT;
v_total_votes BIGINT;
v_winner_name VARCHAR;
v_winner_votes BIGINT;
v_winner_pct NUMERIC;
BEGIN
-- (1) Провери дали изборот постои
SELECT e.name, e.status, et.type_name
INTO v_election_name, v_status, v_election_type
FROM election e
JOIN election_type et ON et.election_type_id = e.election_type_id
WHERE e.election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Election % does not exist.', p_election_id;
END IF;
-- (2) Провери дали е активен
IF v_status = 0 THEN
RAISE EXCEPTION 'Election "%" is already closed.', v_election_name;
END IF;
-- (3) Провери дали постојат vote_result записи
SELECT COUNT(DISTINCT vr.station_id), SUM(vr.votes)
INTO v_station_count, v_total_votes
FROM vote_result vr
WHERE vr.election_id = p_election_id
AND vr.candidate_id IS NULL;
IF v_total_votes IS NULL OR v_total_votes = 0 THEN
RAISE EXCEPTION
'Election "%" has no vote results. Cannot close an election without results.',
v_election_name;
END IF;
-- Најди победник
SELECT pe.name, SUM(vr.votes),
ROUND(SUM(vr.votes)::NUMERIC * 100.0 / NULLIF(v_total_votes, 0), 2)
INTO v_winner_name, v_winner_votes, v_winner_pct
FROM vote_result vr
JOIN political_entity pe ON pe.entity_id = vr.entity_id
WHERE vr.election_id = p_election_id
AND vr.candidate_id IS NULL
GROUP BY pe.name
ORDER BY SUM(vr.votes) DESC
LIMIT 1;
-- Затвори го изборот
UPDATE election SET status = 0 WHERE election_id = p_election_id;
RAISE NOTICE '════════════════════════════════════════';
RAISE NOTICE 'Election closed: % (%)', v_election_name, v_election_type;
RAISE NOTICE 'Stations reported : %', v_station_count;
RAISE NOTICE 'Total votes : %', v_total_votes;
RAISE NOTICE 'Winner : %', v_winner_name;
RAISE NOTICE 'Winner votes : %', v_winner_votes;
RAISE NOTICE 'Winner share : % pct', v_winner_pct;
RAISE NOTICE '════════════════════════════════════════';
END;
$procedure$;
10. sp_open_election
Сигнатура: sp_open_election(IN p_election_id bigint)
Го активира изборот (status=1) по верификација дека не постои веќе активен избор од ист тип во системот. Ја имплементира бизнис правилото дека не можат да се одржуваат два истовидни избори истовремено.
CREATE OR REPLACE PROCEDURE public.sp_open_election(IN p_election_id bigint)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_election_name VARCHAR;
v_type_id BIGINT;
v_status SMALLINT;
v_same_type_active BOOLEAN;
BEGIN
SELECT name, status, election_type_id
INTO v_election_name, v_status, v_type_id
FROM public.election
WHERE election_id = p_election_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Грешка: Изборот со ID % не постои.', p_election_id;
END IF;
IF v_status = 1 THEN
RAISE EXCEPTION
'Забелешка: Изборот "%" е веќе отворен и активен.', v_election_name;
END IF;
SELECT EXISTS (
SELECT 1 FROM public.election
WHERE status = 1 AND election_type_id = v_type_id
) INTO v_same_type_active;
IF v_same_type_active THEN
RAISE EXCEPTION
'Предупредување: Веќе постојат активни избори од овој тип во системот. '
'Мора прво да ги затворите претходните од истата категорија.';
END IF;
UPDATE public.election SET status = 1 WHERE election_id = p_election_id;
RAISE NOTICE '========================================';
RAISE NOTICE 'Изборот "%" е УСПЕШНО ОТВОРЕН!', v_election_name;
RAISE NOTICE 'Овозможено е истовремено гласање со други активни различни типови избори.';
RAISE NOTICE '========================================';
END;
$procedure$;
11. sp_register_newly_eligible_voters
Сигнатура: sp_register_newly_eligible_voters()
Ги регистрира сите лица кои наполниле 18 години а сè уште не се во гласачкиот список, и ги распоредува на најмалку оптоварената станица во batch-ови од 10,000. Ја автоматизира периодичната ажурирање на гласачкиот список која државните институции ја вршат пред секои избори.
CREATE OR REPLACE PROCEDURE public.sp_register_newly_eligible_voters()
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_count INT := 0;
v_max_voter_id BIGINT;
v_station_id BIGINT;
v_region_id BIGINT;
rec RECORD;
BEGIN
SELECT COALESCE(MAX(voter_id), 0)
INTO v_max_voter_id
FROM voter;
-- Најди најмалку оптоварена станица
SELECT ps.station_id, ps.municipality_id
INTO v_station_id, v_region_id
FROM polling_station ps
LEFT JOIN voter v ON v.station_id = ps.station_id
GROUP BY ps.station_id, ps.municipality_id, ps.registered_voter
ORDER BY COUNT(v.voter_id) * 1.0 / NULLIF(ps.registered_voter, 1) ASC
LIMIT 1;
IF v_station_id IS NULL THEN
RAISE EXCEPTION 'No polling stations found in the system';
END IF;
FOR rec IN
SELECT p.person_id
FROM person p
WHERE p.date_of_birth <= CURRENT_DATE - INTERVAL '18 years'
AND p.person_id NOT IN (SELECT person_id FROM voter)
ORDER BY p.person_id
LOOP
v_max_voter_id := v_max_voter_id + 1;
INSERT INTO voter (voter_id, person_id, region_id, station_id)
VALUES (v_max_voter_id, rec.person_id, v_region_id, v_station_id)
ON CONFLICT (person_id) DO NOTHING;
v_count := v_count + 1;
-- Commit во batch-ови од 10,000
IF v_count % 10000 = 0 THEN
RAISE NOTICE 'Registered % voters so far...', v_count;
END IF;
END LOOP;
RAISE NOTICE 'Done. Total newly registered voters: %', v_count;
END;
$procedure$;
