-- Poslední konec 15min intervalu s efektivní OTE cenou pro site (dynamický horizont plánu). create or replace function ems.fn_last_effective_ote(p_site_id int) returns timestamptz language sql stable as $$ select max(interval_end) from ems.vw_site_effective_price where site_id = p_site_id and effective_buy_price_czk_kwh is not null and effective_sell_price_czk_kwh is not null; $$; comment on function ems.fn_last_effective_ote(int) is 'Nejvzdálenější konec intervalu s efektivní nákupní i prodejní cenou (OTE přes vw_site_effective_price). Používá planning_engine pro dynamický horizont – jen reálná data, bez predikce.'; -- Konec horizontu pro LP: min(konec OTE, from + max_h), NULL pokud je k dispozici méně než min_h dat. create or replace function ems.fn_planning_horizon_end( p_site_id int, p_horizon_from timestamptz, p_max_hours numeric default 36, p_min_hours numeric default 1 ) returns timestamptz language plpgsql stable as $fn$ declare v_last timestamptz; v_cap timestamptz; v_min_end timestamptz; begin v_last := ems.fn_last_effective_ote(p_site_id); if v_last is null then return null; end if; v_cap := least(v_last, p_horizon_from + p_max_hours * interval '1 hour'); v_min_end := p_horizon_from + p_min_hours * interval '1 hour'; if v_cap < v_min_end then return null; end if; return v_cap; end; $fn$; comment on function ems.fn_planning_horizon_end(int, timestamptz, numeric, numeric) is 'Konec plánovacího okna: min(poslední OTE interval, p_horizon_from + p_max_hours). NULL pokud OTE končí dřív než p_min_hours od p_horizon_from (rolling se přeskočí). Výchozí strop 36 h, min 1 h – měnit přes volitelné argumenty nebo novou verzi funkce.';