diff --git a/RESTAPI/Functions/SFP_GET_HOURLY_DOWNTIME.sql b/RESTAPI/Functions/SFP_GET_HOURLY_DOWNTIME.sql
index 0c556b6..0717977 100644
--- a/RESTAPI/Functions/SFP_GET_HOURLY_DOWNTIME.sql
+++ b/RESTAPI/Functions/SFP_GET_HOURLY_DOWNTIME.sql
@@ -1 +1,437 @@
-âÅã@×ÁãÈ@\ÓÉÂÓ@^@
%
%ÃÙÅÁãÅ@ÖÙ@ÙÅ×ÓÁÃÅ@ÆäÕÃãÉÖÕ@ÙÅâãÁ×ÉKâÆ×mÇÅãmÈÖäÙÓèmÄÖæÕãÉÔÅ@M@
%ÉÕm×ÁÙÔ@ÃÓÖÂMòñô÷ôøóöô÷]@]@
%ÙÅãäÙÕâ@ÃÓÖÂMòñô÷ôøóöô÷]@@@
%ÓÁÕÇäÁÇÅ@âØÓ@
%â×ÅÃÉÆÉÃ@ÙÅâãÁ×ÉKâÆ×mÇÅãmÈÖäÙÓèmÄÖæÕãÉÔÅ@
%ÕÖã@ÄÅãÅÙÔÉÕÉâãÉÃ@
%ÔÖÄÉÆÉÅâ@âØÓ@ÄÁãÁ@
%ÃÁÓÓÅÄ@ÖÕ@ÕäÓÓ@ÉÕ×äã@
%âÅã@Ö×ãÉÖÕ@@ÁÓæÂÓÒ@~@\ÁÓÓÙÅÁÄ@k@
%ÁÓæÃ×èÄãÁ@~@\Ö×ãÉÔÉéÅ@k@
%ÃÖÔÔÉã@~@\ÕÖÕÅ@k@
%ÄÅÃÙÅâäÓã@~@Móñk@óñk@ðð]@k@
%ÄÓè×Ù×@~@\ÕÖ@k@
%ÄèÕÄÆãÃÖÓ@~@\ÕÖ@k@
%ÄèÕäâÙ×ÙÆ@~@\äâÅÙ@k@
%âÙãâÅØ@~@\ÈÅç@@@
%ÂÅÇÉÕ@
%ÄÅÃÓÁÙÅ@ÅÕå@åÁÙÃÈÁÙ@M@òð@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmÄÁãÅ@ÄÁãÅ@^@
%ÄÅÃÓÁÙÅ@åmÄÁãÅmâãÙ@åÁÙÃÈÁÙ@M@ñð@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmæÃ@åÁÙÃÈÁÙ@M@õ@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmâÉãÅ@åÁÙÃÈÁÙ@M@ó@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmÄÁãÅmÃè@ÄÅÃÉÔÁÓ@M@÷@k@ð@]@ÄÅÆÁäÓã@ð@^@
%ÄÅÃÓÁÙÅ@åmÄÁãÅm×ÙÉÖÙmÃè@ÄÅÃÉÔÁÓ@M@÷@k@ð@]@ÄÅÆÁäÓã@ð@^@
%ÄÅÃÓÁÙÅ@ÓÓ@åÁÙÃÈÁÙ@M@ñðòô@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmÉÕm×ÁÙÔ@ÃÓÖÂ@M@ò@Ç@]@ÄÅÆÁäÓã@}}@^@
%``@Å™™–™@ˆ�•„“‰•‡@¥�™‰�‚“…¢@
%ÄÅÃÓÁÙÅ@åmâØÓâãÁãÅ@ÃÈÁÙ@M@õ@]@ÄÅÆÁäÓã@}ððððð}@^@
%ÄÅÃÓÁÙÅ@åmâØÓÃÖÄÅ@ÉÕãÅÇÅÙ@ÄÅÆÁäÓã@ð@^@
%ÄÅÃÓÁÙÅ@åmÅÙÙÔâÇ@åÁÙÃÈÁÙ@M@õðð@]@ÄÅÆÁäÓã@}}@^@
%ÄÅÃÓÁÙÅ@åmÈãã×mâãÁãäâ@ÉÕãÅÇÅÙ@ÄÅÆÁäÓã@òðð@^@
%ÄÅÃÓÁÙÅ@åmÈÁâmÅÙÙÖÙ@ÉÕãÅÇÅÙ@ÄÅÆÁäÓã@ð@^@
%``@Ã–•£‰•¤…@ˆ�•„“…™¢@†–™@…™™–™@ƒ�—£¤™…@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓâãÁãÅ@}òòðð÷}@@``@É•¥�“‰„@„�£…£‰”…@
%ÂÅÇÉÕ@
%âÅã@åmâØÓâãÁãÅ@~@}òòðð÷}@^@
%ÇÅã@ÄÉÁÇÕÖâãÉÃâ@ÃÖÕÄÉãÉÖÕ@ñ@åmÅÙÙÔâÇ@~@ÔÅââÁÇÅmãÅçã@^@
%âÅã@åmÈãã×mâãÁãäâ@~@ôðð@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓâãÁãÅ@}òòððó}@@``@Õ¤”…™‰ƒ@–¤£@–†@™�•‡…@
%ÂÅÇÉÕ@
%âÅã@åmâØÓâãÁãÅ@~@}òòððó}@^@
%ÇÅã@ÄÉÁÇÕÖâãÉÃâ@ÃÖÕÄÉãÉÖÕ@ñ@åmÅÙÙÔâÇ@~@ÔÅââÁÇÅmãÅçã@^@
%âÅã@åmÈãã×mâãÁãäâ@~@ôðð@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓâãÁãÅ@}ðòððð}@@``@Õ–@„�£�@†–¤•„@
%ÂÅÇÉÕ@
%âÅã@åmâØÓâãÁãÅ@~@}ðòððð}@^@
%âÅã@åmÅÙÙÔâÇ@~@}Õ–@„�£�@†–¤•„@†–™@£ˆ…@¢—…ƒ‰†‰…„@ƒ™‰£…™‰�}@^@
%âÅã@åmÈãã×mâãÁãäâ@~@ôðô@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓâãÁãÅ@}ôòõðñ}@@``@Õ–£@�¤£ˆ–™‰©…„@
%ÂÅÇÉÕ@
%âÅã@åmâØÓâãÁãÅ@~@}ôòõðñ}@^@
%ÇÅã@ÄÉÁÇÕÖâãÉÃâ@ÃÖÕÄÉãÉÖÕ@ñ@åmÅÙÙÔâÇ@~@ÔÅââÁÇÅmãÅçã@^@
%âÅã@åmÈãã×mâãÁãäâ@~@ôðó@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓâãÁãÅ@}ôò÷ðô}@@``@Ö‚‘…ƒ£@•–£@†–¤•„@
%ÂÅÇÉÕ@
%âÅã@åmâØÓâãÁãÅ@~@}ôò÷ðô}@^@
%ÇÅã@ÄÉÁÇÕÖâãÉÃâ@ÃÖÕÄÉãÉÖÕ@ñ@åmÅÙÙÔâÇ@~@ÔÅââÁÇÅmãÅçã@^@
%âÅã@åmÈãã×mâãÁãäâ@~@ôðô@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%ÄÅÃÓÁÙÅ@ÃÖÕãÉÕäÅ@ÈÁÕÄÓÅÙ@ÆÖÙ@âØÓÅçÃÅ×ãÉÖÕ@
%ÂÅÇÉÕ@
%ÇÅã@ÄÉÁÇÕÖâãÉÃâ@ÃÖÕÄÉãÉÖÕ@ñ@
%åmâØÓâãÁãÅ@~@ÙÅãäÙÕÅÄmâØÓâãÁãÅ@k@
%åmâØÓÃÖÄÅ@~@ÄÂòmÙÅãäÙÕÅÄmâØÓÃÖÄÅ@k@åmÅÙÙÔâÇ@~@ÔÅââÁÇÅmãÅçã@^@
%âÅã@åmÈãã×mâãÁãäâ@~@õðð@^@
%âÅã@åmÈÁâmÅÙÙÖÙ@~@ñ@^@
%ÅÕÄ@^@
%``@×�™¢…@‰•—¤£@—�™�”…£…™¢@†‰™¢£@M‚…†–™…@…•¥@…§£™�ƒ£‰–•@”–„‰†‰…¢@É•m×�™”]@
%âÅã@åmÄÁãÅmâãÙ@~@ÃÖÁÓÅâÃÅ@M@ÑâÖÕmåÁÓäÅ@M@ÉÕm×ÁÙÔ@k@}[K„�£…}@]@k@}}@]@^@
%âÅã@åmæÃ@~@ÃÖÁÓÅâÃÅ@M@ÑâÖÕmåÁÓäÅ@M@ÉÕm×ÁÙÔ@k@}[K¦–™’ƒ…•£…™}@]@k@
\ No newline at end of file
+SET PATH *LIBL ; 
+
+CREATE OR REPLACE FUNCTION RESTAPI.SFP_GET_HOURLY_DOWNTIME ( 
+	IN_PARM CLOB(2147483647) ) 
+	RETURNS CLOB(2147483647)   
+	LANGUAGE SQL 
+	SPECIFIC RESTAPI.SFP_GET_HOURLY_DOWNTIME 
+	NOT DETERMINISTIC 
+	MODIFIES SQL DATA 
+	CALLED ON NULL INPUT 
+	SET OPTION  ALWBLK = *ALLREAD , 
+	ALWCPYDTA = *OPTIMIZE , 
+	COMMIT = *NONE , 
+	DECRESULT = (31, 31, 00) , 
+	DLYPRP = *NO , 
+	DYNDFTCOL = *NO , 
+	DYNUSRPRF = *USER , 
+	SRTSEQ = *HEX   
+	BEGIN 
+DECLARE ENV VARCHAR ( 20 ) DEFAULT '' ; 
+DECLARE V_DATE DATE ; 
+DECLARE V_DATE_STR VARCHAR ( 10 ) DEFAULT '' ; 
+DECLARE V_WC VARCHAR ( 5 ) DEFAULT '' ; 
+DECLARE V_SITE VARCHAR ( 3 ) DEFAULT '' ; 
+DECLARE V_DATE_CY DECIMAL ( 7 , 0 ) DEFAULT 0 ; 
+DECLARE V_DATE_PRIOR_CY DECIMAL ( 7 , 0 ) DEFAULT 0 ; 
+DECLARE LL VARCHAR ( 1024 ) DEFAULT '' ; 
+DECLARE V_IN_PARM CLOB ( 2 G ) DEFAULT '' ; 
+-- Error handling variables 
+DECLARE V_SQLSTATE CHAR ( 5 ) DEFAULT '00000' ; 
+DECLARE V_SQLCODE INTEGER DEFAULT 0 ; 
+DECLARE V_ERRMSG VARCHAR ( 500 ) DEFAULT '' ; 
+DECLARE V_HTTP_STATUS INTEGER DEFAULT 200 ; 
+DECLARE V_HAS_ERROR INTEGER DEFAULT 0 ; 
+-- Continue handlers for error capture 
+DECLARE CONTINUE HANDLER FOR SQLSTATE '22007'  -- Invalid datetime 
+BEGIN 
+SET V_SQLSTATE = '22007' ; 
+GET DIAGNOSTICS CONDITION 1 V_ERRMSG = MESSAGE_TEXT ; 
+SET V_HTTP_STATUS = 400 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+DECLARE CONTINUE HANDLER FOR SQLSTATE '22003'  -- Numeric out of range 
+BEGIN 
+SET V_SQLSTATE = '22003' ; 
+GET DIAGNOSTICS CONDITION 1 V_ERRMSG = MESSAGE_TEXT ; 
+SET V_HTTP_STATUS = 400 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'  -- No data found 
+BEGIN 
+SET V_SQLSTATE = '02000' ; 
+SET V_ERRMSG = 'No data found for the specified criteria' ; 
+SET V_HTTP_STATUS = 404 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+DECLARE CONTINUE HANDLER FOR SQLSTATE '42501'  -- Not authorized 
+BEGIN 
+SET V_SQLSTATE = '42501' ; 
+GET DIAGNOSTICS CONDITION 1 V_ERRMSG = MESSAGE_TEXT ; 
+SET V_HTTP_STATUS = 403 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+DECLARE CONTINUE HANDLER FOR SQLSTATE '42704'  -- Object not found 
+BEGIN 
+SET V_SQLSTATE = '42704' ; 
+GET DIAGNOSTICS CONDITION 1 V_ERRMSG = MESSAGE_TEXT ; 
+SET V_HTTP_STATUS = 404 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION 
+BEGIN 
+GET DIAGNOSTICS CONDITION 1 
+V_SQLSTATE = RETURNED_SQLSTATE , 
+V_SQLCODE = DB2_RETURNED_SQLCODE , V_ERRMSG = MESSAGE_TEXT ; 
+SET V_HTTP_STATUS = 500 ; 
+SET V_HAS_ERROR = 1 ; 
+END ; 
+-- Parse input parameters first (before env extraction modifies In_Parm) 
+SET V_DATE_STR = COALESCE ( JSON_VALUE ( IN_PARM , '$.date' ) , '' ) ; 
+SET V_WC = COALESCE ( JSON_VALUE ( IN_PARM , '$.workcenter' ) , '' ) ; 
+SET V_SITE = COALESCE ( JSON_VALUE ( IN_PARM , '$.site' ) , '' ) ; 
+-- Parse date - set to Current_Date if not provided 
+IF V_DATE_STR <> '' THEN 
+SET V_DATE = DATE ( V_DATE_STR ) ; 
+ELSE 
+SET V_DATE = CURRENT_DATE ; 
+END IF ; 
+-- Extract env handling all input formats (object, array, string) 
+SET V_IN_PARM = IN_PARM ; 
+WHILE CISTOOLS . JSON_TYPE ( V_IN_PARM ) IN ( 'object' , 'array' , 'string' ) 
+DO 
+SET V_IN_PARM = 
+CASE CISTOOLS . JSON_TYPE ( V_IN_PARM ) 
+WHEN 'object' THEN JSON_VALUE ( V_IN_PARM , '$.env' ) 
+WHEN 'array' THEN JSON_VALUE ( V_IN_PARM , '$[0]' ) 
+WHEN 'string' THEN REPLACE ( V_IN_PARM , '"' , '' ) 
+ELSE TRIM ( V_IN_PARM ) 
+END ; 
+END WHILE ; 
+SET ENV = V_IN_PARM ; 
+SET LL = CISTOOLS . SET_LIBRARY_LIST ( ENV ) ; 
+-- Validate environment was set 
+IF ENV = '' 
+OR ENV IS NULL THEN 
+RETURN 
+JSON_OBJECT ( 
+'error' : JSON_OBJECT ( 
+'http_status' : 400 , 'sqlstate' : '22001' , 
+'message' : 'Environment parameter is required' 
+) 
+) ; 
+END IF ; 
+-- Check for errors from library list setup 
+IF V_HAS_ERROR = 1 THEN 
+RETURN 
+JSON_OBJECT ( 
+'error' : JSON_OBJECT ( 
+'http_status' : V_HTTP_STATUS , 'sqlstate' : V_SQLSTATE , 
+'message' : V_ERRMSG 
+) 
+) ; 
+END IF ; 
+-- Calculate CYYMMDD dates 
+SET V_DATE_CY = ZONED ( V_DATE ) - 19000000 ; 
+SET V_DATE_PRIOR_CY = ZONED ( V_DATE - 1 DAY ) - 19000000 ; 
+RETURN 
+WITH 
+-- Get Down Begin transactions 
+DOWN_BEGIN AS ( 
+SELECT T . ORDNO , T . CLDT , T . OPSEQ , T . EMPNO , T . BADGE , T . WKCTR , 
+T . TDATE AS DB_DATE , T . TTIME AS DB_TIME , 
+CAST ( 
+CAST ( T . RECD AS VARCHAR ( 6 ) CCSID 37 ) AS VARCHAR ( 6 ) 
+CCSID 1208 ) AS REASON_CODE , T . ITNBR , 
+COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , M . STID , 
+TO_DATE ( 
+ZONED ( CISTOOLS . CYMD2DATE ( T . TDATE ) ) || 
+LPAD ( T . TTIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) 
+AS DB_TS , 
+ROW_NUMBER ( ) OVER ( 
+PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( 
+T . EMPNO , T . BADGE ) 
+ORDER BY T . TDATE , T . TTIME 
+) AS SEQ 
+FROM MOTRAN T 
+JOIN MOMAST M 
+ON T . ORDNO = M . ORDNO 
+WHERE T . TCODE = 'DB' 
+AND T . WKCTR = COALESCE ( NULLIF ( V_WC , '' ) , T . WKCTR ) 
+AND M . STID = COALESCE ( NULLIF ( V_SITE , '' ) , M . STID ) 
+AND T . ACREC = 'A' 
+AND T . TDATE BETWEEN V_DATE_PRIOR_CY AND V_DATE_CY 
+) , 
+-- Get Down End transactions 
+DOWN_END AS ( 
+SELECT T . ORDNO , T . CLDT , T . OPSEQ , T . EMPNO , T . BADGE , 
+T . TDATE AS DE_DATE , T . TTIME AS DE_TIME , T . WKCTR , 
+COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , M . STID , 
+TO_DATE ( 
+ZONED ( CISTOOLS . CYMD2DATE ( T . TDATE ) ) || 
+LPAD ( T . TTIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) 
+AS DE_TS , 
+ROW_NUMBER ( ) OVER ( 
+PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( 
+T . EMPNO , T . BADGE ) 
+ORDER BY T . TDATE , T . TTIME 
+) AS SEQ 
+FROM MOTRAN T 
+JOIN MOMAST M 
+ON T . ORDNO = M . ORDNO 
+WHERE T . TCODE = 'DE' 
+AND T . WKCTR = COALESCE ( NULLIF ( V_WC , '' ) , T . WKCTR ) 
+AND M . STID = COALESCE ( NULLIF ( V_SITE , '' ) , M . STID ) 
+AND T . ACREC = 'A' 
+AND T . TDATE = V_DATE_CY 
+) , 
+-- Current time as default end time 
+CURRENT_END AS ( 
+SELECT 
+CASE 
+WHEN V_DATE = CURRENT_DATE THEN CURRENT_TIMESTAMP 
+ELSE 
+TO_DATE ( 
+ZONED ( V_DATE ) || '235959' , 'YYYYMMDDHH24MISS' ) 
+END AS DEFAULT_END_TS 
+FROM SYSIBM . SYSDUMMY1 
+) , 
+-- Match DB with DE 
+MATCHED_DOWN AS ( 
+SELECT DB . ORDNO , DB . ITNBR , DB . REASON_CODE , DB . WKCTR , DB . STID , 
+DB . DB_DATE , DB . DB_TIME , DB . DB_TS AS START_TS , 
+COALESCE ( DE . DE_TS , CE . DEFAULT_END_TS ) AS END_TS , 
+COALESCE ( DE . DE_DATE , V_DATE_CY ) AS DE_DATE , 
+COALESCE ( 
+DE . DE_TIME , 
+CASE 
+WHEN 
+V_DATE = CURRENT_DATE 
+THEN ZONED ( CURRENT_TIME ) 
+ELSE 235959 
+END ) AS DE_TIME , 
+COALESCE ( R . KDSCHED , 'N' ) AS IS_SCHEDULED , 
+COALESCE ( R . KDRDESC , 'Unknown' ) AS REASON_DESC 
+FROM DOWN_BEGIN DB 
+CROSS JOIN CURRENT_END CE 
+LEFT JOIN DOWN_END DE 
+ON DB . ORDNO = DE . ORDNO 
+AND DB . OPSEQ = DE . OPSEQ 
+AND DB . EMP_KEY = DE . EMP_KEY 
+AND DB . SEQ = DE . SEQ 
+AND DB . WKCTR = DE . WKCTR 
+AND DB . STID = DE . STID 
+LEFT JOIN SFCDWR R 
+ON DB . REASON_CODE = R . KDREASN 
+) , 
+-- Get distinct workcenter/site combinations 
+WC_SITE_LIST AS ( 
+SELECT DISTINCT WKCTR , STID 
+FROM MATCHED_DOWN 
+) , 
+-- Generate hours for the date (0-23) with proper timestamps 
+ALL_HOURS AS ( 
+SELECT HR AS HOUR_NUM , 
+TO_DATE ( 
+ZONED ( V_DATE ) || LPAD ( HR * 10000 , 6 , '0' ) , 
+'YYYYMMDDHH24MISS' ) AS HR_START_TS , 
+CASE 
+WHEN 
+HR = 23 
+THEN 
+TO_DATE ( 
+ZONED ( V_DATE + 1 DAY ) || '000000' , 
+'YYYYMMDDHH24MISS' ) 
+ELSE 
+TO_DATE ( 
+ZONED ( V_DATE ) || 
+LPAD ( ( HR + 1 ) * 10000 , 6 , '0' ) , 
+'YYYYMMDDHH24MISS' ) 
+END AS HR_END_TS 
+FROM ( 
+VALUES ( 0 ) , ( 1 ) , ( 2 ) , ( 3 ) , ( 4 ) , ( 5 ) , ( 6 ) , ( 7 ) , ( 8 ) ,( 9 ) , ( 10 ) , ( 11 ) , ( 12 ) , ( 13 ) , ( 14 ) , ( 15 ) , 
+( 16 ) , ( 17 ) , ( 18 ) , ( 19 ) , ( 20 ) , ( 21 ) , ( 22 ) , 
+( 23 ) 
+) T ( HR ) 
+) , 
+-- Break down by hour 
+HOURLY_DOWN AS ( 
+SELECT D . REASON_CODE , D . REASON_DESC , D . IS_SCHEDULED , D . ORDNO , 
+D . ITNBR , D . WKCTR , D . STID , H . HOUR_NUM , 
+GREATEST ( 
+0 , TIMESTAMPDIFF ( 
+2 , CHAR ( 
+LEAST ( D . END_TS , H . HR_END_TS ) - 
+GREATEST ( D . START_TS , H . HR_START_TS ) ) ) ) 
+AS SECONDS_IN_HR 
+FROM MATCHED_DOWN D , ALL_HOURS H 
+WHERE D . START_TS < H . HR_END_TS 
+AND D . END_TS > H . HR_START_TS 
+) , 
+-- Aggregate by hour, workcenter, site 
+HOURLY_AGG AS ( 
+SELECT HOUR_NUM , WKCTR , STID , SUM ( 
+CASE 
+WHEN IS_SCHEDULED = 'Y' THEN SECONDS_IN_HR 
+ELSE 0 
+END ) AS SCHED_DOWN_SEC , SUM ( 
+CASE 
+WHEN IS_SCHEDULED <> 'Y' THEN SECONDS_IN_HR 
+ELSE 0 
+END ) AS UNSCHED_DOWN_SEC , 
+SUM ( SECONDS_IN_HR ) AS TOTAL_DOWN_SEC , 
+COUNT ( DISTINCT REASON_CODE ) AS REASON_COUNT 
+FROM HOURLY_DOWN 
+WHERE SECONDS_IN_HR > 0 
+GROUP BY HOUR_NUM , WKCTR , STID 
+) , 
+-- Get distinct reasons per hour/wkctr/stid 
+HOURLY_REASONS AS ( 
+SELECT DISTINCT HOUR_NUM , WKCTR , STID , REASON_CODE , 
+REASON_DESC , IS_SCHEDULED 
+FROM HOURLY_DOWN 
+WHERE SECONDS_IN_HR > 0 
+) , 
+-- Aggregate reasons into JSON array 
+HOURLY_REASONS_AGG AS ( 
+SELECT HOUR_NUM , WKCTR , STID , 
+JSON_ARRAYAGG ( 
+JSON_OBJECT ( 
+'reasonCode' : TRIM ( REASON_CODE ) , 
+'description' : TRIM ( REASON_DESC ) , 'isScheduled' : 
+CASE 
+WHEN IS_SCHEDULED = 'Y' THEN 'true' 
+ELSE 'false' 
+END FORMAT JSON 
+) FORMAT JSON 
+) AS REASONS 
+FROM HOURLY_REASONS 
+GROUP BY HOUR_NUM , WKCTR , STID 
+) , 
+-- Cross join hours with workcenter/site list, then left join aggregated data 
+HOURLY_FULL AS ( 
+SELECT A . HOUR_NUM , W . WKCTR , W . STID , 
+COALESCE ( H . SCHED_DOWN_SEC , 0 ) AS SCHED_DOWN_SEC , 
+COALESCE ( H . UNSCHED_DOWN_SEC , 0 ) AS UNSCHED_DOWN_SEC , 
+COALESCE ( H . TOTAL_DOWN_SEC , 0 ) AS TOTAL_DOWN_SEC , 
+COALESCE ( H . REASON_COUNT , 0 ) AS REASON_COUNT , 
+R . REASONS AS REASONS , 
+SUM ( COALESCE ( H . SCHED_DOWN_SEC , 0 ) ) OVER ( 
+PARTITION BY W . WKCTR , W . STID 
+ORDER BY A . HOUR_NUM ROWS UNBOUNDED PRECEDING 
+) AS CUM_SCHED , 
+SUM ( COALESCE ( H . UNSCHED_DOWN_SEC , 0 ) ) OVER ( 
+PARTITION BY W . WKCTR , W . STID 
+ORDER BY A . HOUR_NUM ROWS UNBOUNDED PRECEDING 
+) AS CUM_UNSCHED 
+FROM ALL_HOURS A 
+CROSS JOIN WC_SITE_LIST W 
+LEFT JOIN HOURLY_AGG H 
+ON A . HOUR_NUM = H . HOUR_NUM 
+AND W . WKCTR = H . WKCTR 
+AND W . STID = H . STID 
+LEFT JOIN HOURLY_REASONS_AGG R 
+ON A . HOUR_NUM = R . HOUR_NUM 
+AND W . WKCTR = R . WKCTR 
+AND W . STID = R . STID 
+) , 
+-- Reason summary per workcenter/site 
+REASON_SUMMARY AS ( 
+SELECT WKCTR , STID , REASON_CODE , REASON_DESC , IS_SCHEDULED , 
+SUM ( SECONDS_IN_HR ) AS TOTAL_SECONDS , 
+INT ( SUM ( SECONDS_IN_HR ) / 60 ) AS TOTAL_MINUTES 
+FROM HOURLY_DOWN 
+WHERE SECONDS_IN_HR > 0 
+GROUP BY WKCTR , STID , REASON_CODE , REASON_DESC , 
+IS_SCHEDULED 
+) , 
+-- Aggregate reason summary into JSON 
+REASON_SUMMARY_AGG AS ( 
+SELECT WKCTR , STID , 
+JSON_ARRAYAGG ( 
+JSON_OBJECT ( 
+'reasonCode' : TRIM ( REASON_CODE ) , 
+'description' : TRIM ( REASON_DESC ) , 'isScheduled' : 
+CASE 
+WHEN IS_SCHEDULED = 'Y' THEN 'true' 
+ELSE 'false' 
+END FORMAT JSON , 'totalMinutes' : TOTAL_MINUTES 
+) FORMAT JSON 
+) AS REASONS 
+FROM REASON_SUMMARY 
+GROUP BY WKCTR , STID 
+) , 
+-- Build hour JSON objects 
+JSON_HOURS AS ( 
+SELECT WKCTR , STID , 
+JSON_OBJECT ( 
+'hour' : HOUR_NUM , 'hourLabel' : 
+CASE 
+WHEN HOUR_NUM = 0 THEN '12AM' 
+WHEN 
+HOUR_NUM < 12 THEN VARCHAR ( HOUR_NUM ) || 'AM' 
+WHEN HOUR_NUM = 12 THEN '12PM' 
+ELSE VARCHAR ( HOUR_NUM - 12 ) || 'PM' 
+END , 'scheduledDownMinutes' : INT ( 
+SCHED_DOWN_SEC / 60 ) , 
+'unscheduledDownMinutes' : INT ( 
+UNSCHED_DOWN_SEC / 60 ) , 'totalDownMinutes' : INT ( 
+TOTAL_DOWN_SEC / 60 ) , 
+'cumulativeScheduledMinutes' : INT ( CUM_SCHED / 60 ) , 
+'cumulativeUnscheduledMinutes' : INT ( 
+CUM_UNSCHED / 60 ) , 'reasonCount' : REASON_COUNT , 
+'reasons' : COALESCE ( REASONS , JSON_ARRAY ( ) ) FORMAT 
+JSON 
+) AS HOUR_OBJ , HOUR_NUM 
+FROM HOURLY_FULL 
+) , 
+-- Calculate totals per workcenter/site 
+WC_TOTALS AS ( 
+SELECT WKCTR , STID , MAX ( CUM_SCHED ) AS TOTAL_SCHED_SEC , 
+MAX ( CUM_UNSCHED ) AS TOTAL_UNSCHED_SEC , 
+MAX ( CUM_SCHED ) + MAX ( CUM_UNSCHED ) AS TOTAL_DOWN_SEC 
+FROM HOURLY_FULL 
+GROUP BY WKCTR , STID 
+) , 
+-- Build workcenter JSON objects with nested hours array 
+JSON_WORKCENTERS AS ( 
+SELECT 
+JSON_OBJECT ( 
+'workcenter' : CAST ( 
+CAST ( T . WKCTR AS VARCHAR ( 5 ) CCSID 37 ) AS VARCHAR ( 
+5 ) CCSID 1208 ) , 'site' : CAST ( 
+CAST ( T . STID AS VARCHAR ( 3 ) CCSID 37 ) AS VARCHAR ( 3 ) 
+CCSID 1208 ) , 'hours' : ( SELECT JSON_ARRAYAGG ( 
+H . HOUR_OBJ FORMAT JSON 
+ORDER BY H . HOUR_NUM 
+) 
+FROM JSON_HOURS H 
+WHERE H . WKCTR = T . WKCTR 
+AND H . STID = T . STID ) FORMAT JSON , 
+'reasons' : COALESCE ( ( SELECT RS . REASONS 
+FROM REASON_SUMMARY_AGG RS 
+WHERE RS . WKCTR = T . WKCTR 
+AND RS . STID = T . STID ) , JSON_ARRAY ( ) ) 
+FORMAT JSON , 'totals' : JSON_OBJECT ( 
+'scheduledDownMinutes' : INT ( 
+COALESCE ( T . TOTAL_SCHED_SEC , 0 ) / 60 ) , 
+'unscheduledDownMinutes' : INT ( 
+COALESCE ( T . TOTAL_UNSCHED_SEC , 0 ) / 60 ) , 
+'totalDownMinutes' : INT ( 
+COALESCE ( T . TOTAL_DOWN_SEC , 0 ) / 60 ) , 
+'availableMinutes' : 1440 - 
+INT ( COALESCE ( T . TOTAL_DOWN_SEC , 0 ) / 60 ) 
+) 
+) AS WC_OBJ 
+FROM WC_TOTALS T 
+ORDER BY T . WKCTR , T . STID ) SELECT JSON_ARRAY ( 
+JSON_OBJECT ( 
+'date' : TO_CHAR ( V_DATE , 'YYYY-MM-DD' ) , 'workcenters' : ( 
+SELECT JSON_ARRAYAGG ( 
+WC_OBJ FORMAT JSON 
+) 
+FROM JSON_WORKCENTERS ) FORMAT JSON , 
+'dataSource' : 'MOTRAN (Live)' , 'generatedAt' : TO_CHAR ( 
+CURRENT_TIMESTAMP , 'YYYY-MM-DD HH24:MI:SS' ) 
+) ) 
+FROM SYSIBM . SYSDUMMY1 ; 
+END  ; 
+
+GRANT ALTER , EXECUTE   
+ON SPECIFIC FUNCTION RESTAPI.SFP_GET_HOURLY_DOWNTIME 
+TO AMAPICS WITH GRANT OPTION ; 
+
+GRANT EXECUTE   
+ON SPECIFIC FUNCTION RESTAPI.SFP_GET_HOURLY_DOWNTIME 
+TO PUBLIC ; 
+
