234 lines
7.4 KiB
PL/PgSQL
234 lines
7.4 KiB
PL/PgSQL
-- =============================================================
|
||
-- R__011_fn_effective_price.sql
|
||
-- EMS Platform – funkce pro výpočet efektivní ceny per site
|
||
-- Repeatable migration – nasazuje se při každé změně
|
||
-- =============================================================
|
||
|
||
CREATE OR REPLACE FUNCTION ems.fn_effective_buy_price(
|
||
p_site_id INT,
|
||
p_interval_start TIMESTAMPTZ
|
||
)
|
||
RETURNS NUMERIC(10,6)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $$
|
||
DECLARE
|
||
v_spot_price NUMERIC;
|
||
v_energy_czk NUMERIC;
|
||
v_dist_rate NUMERIC;
|
||
v_system_services NUMERIC;
|
||
v_ote_fee NUMERIC;
|
||
v_vat_rate NUMERIC;
|
||
v_buy_margin_fixed NUMERIC;
|
||
v_buy_margin_pct NUMERIC;
|
||
v_buy_margin NUMERIC;
|
||
v_is_vt BOOLEAN;
|
||
v_local_time TIME;
|
||
v_dow INT;
|
||
v_hdo_code_id INT;
|
||
v_tariff_id INT;
|
||
v_rate_type TEXT;
|
||
v_purchase_mode TEXT;
|
||
v_fixed_nt NUMERIC;
|
||
v_fixed_vt_sur NUMERIC;
|
||
BEGIN
|
||
SELECT
|
||
smc.purchase_pricing_mode,
|
||
smc.buy_fixed_energy_nt_czk_kwh,
|
||
smc.buy_fixed_vt_surcharge_czk_kwh,
|
||
smc.buy_margin_fixed_czk,
|
||
smc.buy_margin_percent,
|
||
smc.system_services_czk_kwh,
|
||
smc.ote_fee_czk_kwh,
|
||
smc.hdo_code_id,
|
||
smc.tariff_id,
|
||
dt.vat_rate
|
||
INTO
|
||
v_purchase_mode,
|
||
v_fixed_nt,
|
||
v_fixed_vt_sur,
|
||
v_buy_margin_fixed,
|
||
v_buy_margin_pct,
|
||
v_system_services,
|
||
v_ote_fee,
|
||
v_hdo_code_id,
|
||
v_tariff_id,
|
||
v_vat_rate
|
||
FROM ems.site_market_config smc
|
||
LEFT JOIN ems.distribution_tariff dt ON dt.id = smc.tariff_id
|
||
WHERE smc.site_id = p_site_id
|
||
AND smc.valid_from <= p_interval_start
|
||
AND (smc.valid_to IS NULL OR smc.valid_to > p_interval_start)
|
||
ORDER BY smc.valid_from DESC
|
||
LIMIT 1;
|
||
|
||
IF NOT FOUND THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
SELECT buy_raw_price_czk_kwh INTO v_spot_price
|
||
FROM ems.market_interval_price
|
||
WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
|
||
AND interval_start = p_interval_start
|
||
LIMIT 1;
|
||
|
||
v_local_time := (p_interval_start AT TIME ZONE 'Europe/Prague')::TIME;
|
||
v_dow := EXTRACT(DOW FROM p_interval_start AT TIME ZONE 'Europe/Prague');
|
||
-- 0=neděle, 6=sobota
|
||
|
||
IF v_hdo_code_id IS NOT NULL THEN
|
||
SELECT EXISTS (
|
||
SELECT 1
|
||
FROM ems.hdo_code_window w
|
||
WHERE w.hdo_code_id = v_hdo_code_id
|
||
AND (
|
||
w.day_type = 'all'
|
||
OR (w.day_type = 'workday' AND v_dow BETWEEN 1 AND 5)
|
||
OR (w.day_type = 'weekend' AND v_dow IN (0, 6))
|
||
)
|
||
AND w.rate_type = 'VT'
|
||
AND v_local_time >= w.window_from
|
||
AND v_local_time < w.window_to
|
||
) INTO v_is_vt;
|
||
ELSE
|
||
v_is_vt := false;
|
||
END IF;
|
||
|
||
v_rate_type := CASE WHEN v_is_vt THEN 'VT' ELSE 'NT' END;
|
||
|
||
IF v_tariff_id IS NOT NULL THEN
|
||
SELECT price_czk_kwh INTO v_dist_rate
|
||
FROM ems.distribution_tariff_rate
|
||
WHERE tariff_id = v_tariff_id
|
||
AND rate_type = v_rate_type
|
||
AND valid_from <= p_interval_start::DATE
|
||
AND (valid_to IS NULL OR valid_to > p_interval_start::DATE)
|
||
ORDER BY valid_from DESC
|
||
LIMIT 1;
|
||
END IF;
|
||
|
||
v_dist_rate := COALESCE(v_dist_rate, 0);
|
||
v_system_services := COALESCE(v_system_services, 0);
|
||
v_ote_fee := COALESCE(v_ote_fee, 0);
|
||
v_buy_margin_fixed := COALESCE(v_buy_margin_fixed, 0);
|
||
v_buy_margin_pct := COALESCE(v_buy_margin_pct, 0);
|
||
v_vat_rate := COALESCE(v_vat_rate, 0.21);
|
||
v_fixed_vt_sur := COALESCE(v_fixed_vt_sur, 0);
|
||
|
||
IF upper(trim(COALESCE(v_purchase_mode, ''))) = 'FIXED'
|
||
AND v_fixed_nt IS NOT NULL THEN
|
||
v_energy_czk := v_fixed_nt
|
||
+ CASE WHEN v_is_vt THEN v_fixed_vt_sur ELSE 0 END;
|
||
ELSIF v_spot_price IS NULL THEN
|
||
RETURN NULL;
|
||
ELSE
|
||
v_energy_czk := v_spot_price;
|
||
END IF;
|
||
|
||
v_buy_margin := v_buy_margin_fixed + (v_energy_czk * v_buy_margin_pct / 100.0);
|
||
|
||
RETURN ROUND(
|
||
(v_energy_czk + v_dist_rate + v_system_services + v_ote_fee + v_buy_margin)
|
||
* (1 + v_vat_rate),
|
||
6
|
||
);
|
||
END;
|
||
$$;
|
||
|
||
COMMENT ON FUNCTION ems.fn_effective_buy_price(INT, TIMESTAMPTZ) IS
|
||
'Efektivní nákupní cena elektřiny Kč/kWh včetně DPH.
|
||
Režim spot: energie = OTE buy_raw + distribuce NT/VT (dle HDO) + systémové služby + OTE poplatek + marže (fix + % z energie).
|
||
Režim fixed: energie = buy_fixed_energy_nt_czk_kwh (+ buy_fixed_vt_surcharge_czk_kwh ve VT oknech dle HDO), pak stejné příplatky a DPH.
|
||
DPH aplikováno na celou částku.';
|
||
|
||
-- ------------------------------------------------------------
|
||
|
||
CREATE OR REPLACE FUNCTION ems.fn_effective_sell_price(
|
||
p_site_id INT,
|
||
p_interval_start TIMESTAMPTZ
|
||
)
|
||
RETURNS NUMERIC(10,6)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $$
|
||
DECLARE
|
||
v_spot_price NUMERIC;
|
||
v_sell_margin_fixed NUMERIC;
|
||
v_sell_margin_pct NUMERIC;
|
||
BEGIN
|
||
SELECT sell_margin_fixed_czk, sell_margin_percent
|
||
INTO v_sell_margin_fixed, v_sell_margin_pct
|
||
FROM ems.site_market_config
|
||
WHERE site_id = p_site_id
|
||
AND valid_from <= p_interval_start
|
||
AND (valid_to IS NULL OR valid_to > p_interval_start)
|
||
ORDER BY valid_from DESC
|
||
LIMIT 1;
|
||
|
||
IF NOT FOUND THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
SELECT sell_raw_price_czk_kwh INTO v_spot_price
|
||
FROM ems.market_interval_price
|
||
WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
|
||
AND interval_start = p_interval_start
|
||
LIMIT 1;
|
||
|
||
IF v_spot_price IS NULL THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
RETURN ROUND(
|
||
v_spot_price
|
||
+ COALESCE(v_sell_margin_fixed, 0)
|
||
+ (v_spot_price * COALESCE(v_sell_margin_pct, 0) / 100.0),
|
||
6
|
||
);
|
||
END;
|
||
$$;
|
||
|
||
COMMENT ON FUNCTION ems.fn_effective_sell_price(INT, TIMESTAMPTZ) IS
|
||
'Efektivní prodejní cena elektřiny Kč/kWh bez DPH (neplátce DPH).
|
||
Složky: spot OTE + fixní/procentní prodejní marže (záporná = srážka).
|
||
Zelený bonus není součástí ceny – počítá se z výroby přes fn_green_bonus_revenue().
|
||
Záporná hodnota = platíme za export (záporné spotové ceny).';
|
||
|
||
-- ------------------------------------------------------------
|
||
|
||
CREATE OR REPLACE FUNCTION ems.fn_green_bonus_revenue(
|
||
p_pv_array_id INT,
|
||
p_interval_start TIMESTAMPTZ,
|
||
p_production_wh NUMERIC
|
||
)
|
||
RETURNS NUMERIC
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $$
|
||
DECLARE
|
||
v_bonus_rate NUMERIC;
|
||
BEGIN
|
||
SELECT green_bonus_czk_kwh INTO v_bonus_rate
|
||
FROM ems.asset_pv_array
|
||
WHERE id = p_pv_array_id
|
||
AND green_bonus_czk_kwh IS NOT NULL
|
||
AND green_bonus_valid_from <= p_interval_start::DATE
|
||
AND (green_bonus_valid_to IS NULL
|
||
OR green_bonus_valid_to > p_interval_start::DATE);
|
||
|
||
IF v_bonus_rate IS NULL OR p_production_wh IS NULL OR p_production_wh <= 0 THEN
|
||
RETURN 0;
|
||
END IF;
|
||
|
||
RETURN ROUND((p_production_wh / 1000.0) * v_bonus_rate, 6);
|
||
END;
|
||
$$;
|
||
|
||
COMMENT ON FUNCTION ems.fn_green_bonus_revenue(INT, TIMESTAMPTZ, NUMERIC) IS
|
||
'Příjem ze zeleného bonusu za výrobu FVE pole v daném intervalu.
|
||
Bonus plyne z celkové výroby bez ohledu na to kam energie šla
|
||
(interní spotřeba, baterie, EV, TČ i export do sítě).
|
||
Sazba se načítá dle platnosti (valid_from/valid_to) – ročně aktualizovatelné.
|
||
Vrátí 0 pokud pole nemá zelený bonus nebo výroba je nulová.
|
||
Použití: SELECT ems.fn_green_bonus_revenue(pv_array_id, interval_start, production_wh);';
|