-- ============================================================= -- R__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_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; BEGIN SELECT 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_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; IF v_spot_price IS NULL THEN RETURN NULL; END IF; 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_buy_margin := v_buy_margin_fixed + (v_spot_price * v_buy_margin_pct / 100.0); v_vat_rate := COALESCE(v_vat_rate, 0.21); RETURN ROUND( (v_spot_price + 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. Složky: spot OTE + distribuce NT/VT (dle HDO) + systémové služby + OTE poplatek + marže (fix + % ze spotu). DPH aplikováno na celou částku. Distribuce závisí na HDO kódu site.'; -- ------------------------------------------------------------ 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);';