SET PATH *LIBL ; CREATE OR REPLACE FUNCTION RESTAPI.SFP_GET_HOURLY_OEE ( IN_PARM CLOB(2147483647) ) RETURNS CLOB(2147483647) LANGUAGE SQL SPECIFIC RESTAPI.SFP_GET_HOURLY_OEE NOT DETERMINISTIC MODIFIES SQL DATA CALLED ON NULL INPUT SET OPTION ALWBLK = *ALLREAD , ALWCPYDTA = *OPTIMIZE , COMMIT = *NONE , DECRESULT = (31, 31, 00) , 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 -- ========== RUN TRANSACTIONS (ON/OF) ========== RUN_ON AS ( SELECT T . ORDNO , T . OPSEQ , T . ITNBR , T . WKCTR , COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , T . TDATE AS ON_DATE , T . TTIME AS ON_TIME , T . RUNCD , M . STID , TO_DATE ( ZONED ( CISTOOLS . CYMD2DATE ( T . TDATE ) ) || LPAD ( T . TTIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) AS ON_TS , ROW_NUMBER ( ) OVER ( PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( T . EMPNO , T . BADGE ) , T . RUNCD , T . WKCTR ORDER BY T . TDATE , T . TTIME ) AS SEQ FROM MOTRAN T JOIN MOMAST M ON T . ORDNO = M . ORDNO WHERE T . TCODE = 'ON' AND T . RUNCD = 'R' 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 ) , RUN_OFF AS ( SELECT T . ORDNO , T . OPSEQ , T . WKCTR , COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , T . TDATE AS OFF_DATE , T . TTIME AS OFF_TIME , T . QTCOM , T . QTSCP , T . RUNCD , M . STID , TO_DATE ( ZONED ( CISTOOLS . CYMD2DATE ( T . TDATE ) ) || LPAD ( T . TTIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) AS OFF_TS , ROW_NUMBER ( ) OVER ( PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( T . EMPNO , T . BADGE ) , T . RUNCD , T . WKCTR ORDER BY T . TDATE , T . TTIME ) AS SEQ FROM MOTRAN T JOIN MOMAST M ON T . ORDNO = M . ORDNO WHERE T . TCODE = 'OF' AND T . RUNCD = 'R' 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_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 ) , RUN_MATCHED AS ( SELECT ON_T . ORDNO , ON_T . ITNBR , ON_T . WKCTR , ON_T . STID , ON_T . ON_TS AS START_TS , COALESCE ( OFF_T . OFF_TS , CE . DEFAULT_END_TS ) AS END_TS , COALESCE ( OFF_T . OFF_DATE , V_DATE_CY ) AS OFF_DATE , COALESCE ( OFF_T . OFF_TIME , CASE WHEN V_DATE = CURRENT_DATE THEN ZONED ( CURRENT_TIME ) ELSE 235959 END ) AS OFF_TIME , COALESCE ( OFF_T . QTCOM , 0 ) AS QTY_COMPLETE , COALESCE ( OFF_T . QTSCP , 0 ) AS QTY_SCRAP , CASE COALESCE ( R . TBCDE , ' ' ) WHEN ' ' THEN CASE WHEN COALESCE ( R . SRLHU , 0 ) > 0 THEN 1.0 / R . SRLHU / 60 ELSE 0 END WHEN 'P' THEN COALESCE ( R . SRLHU , 0 ) / 60 WHEN 'M' THEN CASE WHEN COALESCE ( R . SRLHU , 0 ) > 0 THEN 1.0 / R . SRLHU ELSE 0 END WHEN '1' THEN CASE WHEN COALESCE ( R . SRLHU , 0 ) > 0 THEN 10.0 / R . SRLHU / 60 ELSE 0 END WHEN '2' THEN CASE WHEN COALESCE ( R . SRLHU , 0 ) > 0 THEN 100.0 / R . SRLHU / 60 ELSE 0 END WHEN '3' THEN CASE WHEN COALESCE ( R . SRLHU , 0 ) > 0 THEN 1000.0 / R . SRLHU / 60 ELSE 0 END ELSE 0 END AS PCS_PER_MIN FROM RUN_ON ON_T CROSS JOIN CURRENT_END CE LEFT JOIN RUN_OFF OFF_T ON ON_T . ORDNO = OFF_T . ORDNO AND ON_T . OPSEQ = OFF_T . OPSEQ AND ON_T . EMP_KEY = OFF_T . EMP_KEY AND ON_T . RUNCD = OFF_T . RUNCD AND ON_T . SEQ = OFF_T . SEQ AND ON_T . WKCTR = OFF_T . WKCTR AND ON_T . STID = OFF_T . STID LEFT JOIN MOROUT R ON ON_T . ORDNO = R . ORDNO AND ON_T . OPSEQ = R . OPSEQ ) , -- ========== DOWNTIME TRANSACTIONS (DB/DE) ========== DOWN_BEGIN AS ( SELECT T . ORDNO , T . OPSEQ , CAST ( CAST ( T . RECD AS VARCHAR ( 6 ) CCSID 37 ) AS VARCHAR ( 6 ) CCSID 1208 ) AS REASON_CODE , T . WKCTR , COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , T . TDATE AS DB_DATE , T . TTIME AS DB_TIME , 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 ) , T . WKCTR 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 ) , DOWN_END AS ( SELECT T . ORDNO , T . OPSEQ , T . WKCTR , COALESCE ( T . EMPNO , T . BADGE ) AS EMP_KEY , T . TDATE AS DE_DATE , T . TTIME AS DE_TIME , 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 ) , T . WKCTR 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 ) , DOWN_MATCHED AS ( SELECT DB . ORDNO , DB . REASON_CODE , DB . WKCTR , DB . STID , COALESCE ( R . KDSCHED , 'N' ) AS IS_SCHEDULED , DB . DB_TS AS START_TS , COALESCE ( DE . DE_TS , CE . DEFAULT_END_TS ) AS END_TS 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 ) , -- ========== WORKCENTER/SITE LIST ========== WC_SITE_LIST AS ( SELECT DISTINCT WKCTR , STID FROM RUN_MATCHED UNION SELECT DISTINCT WKCTR , STID FROM DOWN_MATCHED ) , -- ========== HOURLY BREAKDOWN ========== 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 ) ) , -- Run time by hour per workcenter/site RUN_HOURLY AS ( SELECT H . HOUR_NUM , R . WKCTR , R . STID , GREATEST ( 0 , TIMESTAMPDIFF ( 2 , CHAR ( LEAST ( R . END_TS , H . HR_END_TS ) - GREATEST ( R . START_TS , H . HR_START_TS ) ) ) ) AS RUN_SECONDS , R . PCS_PER_MIN , R . QTY_COMPLETE , R . QTY_SCRAP , R . OFF_DATE , R . OFF_TIME , R . ITNBR , R . ORDNO FROM RUN_MATCHED R , ALL_HOURS H WHERE R . START_TS < H . HR_END_TS AND R . END_TS > H . HR_START_TS ) , RUN_BY_HOUR AS ( SELECT HOUR_NUM , WKCTR , STID , SUM ( RUN_SECONDS ) AS RUN_SEC , DEC ( SUM ( DEC ( RUN_SECONDS , 15 , 4 ) * DEC ( PCS_PER_MIN , 15 , 8 ) / 60 ) , 15 , 4 ) AS EXPECTED_QTY , SUM ( CASE WHEN HOUR ( TO_DATE ( ZONED ( CISTOOLS . CYMD2DATE ( OFF_DATE ) ) || LPAD ( OFF_TIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) ) = HOUR_NUM AND CISTOOLS . CYMD2DATE ( OFF_DATE ) = V_DATE THEN QTY_COMPLETE ELSE 0 END ) AS ACTUAL_QTY , SUM ( CASE WHEN HOUR ( TO_DATE ( ZONED ( CISTOOLS . CYMD2DATE ( OFF_DATE ) ) || LPAD ( OFF_TIME , 6 , '0' ) , 'YYYYMMDDHH24MISS' ) ) = HOUR_NUM AND CISTOOLS . CYMD2DATE ( OFF_DATE ) = V_DATE THEN QTY_SCRAP ELSE 0 END ) AS SCRAP_QTY FROM RUN_HOURLY WHERE RUN_SECONDS > 0 GROUP BY HOUR_NUM , WKCTR , STID ) , -- Items per hour/wkctr/stid HOURLY_ITEMS AS ( SELECT DISTINCT HOUR_NUM , WKCTR , STID , ORDNO , ITNBR FROM RUN_HOURLY WHERE RUN_SECONDS > 0 ) , HOURLY_ITEMS_AGG AS ( SELECT HOUR_NUM , WKCTR , STID , JSON_ARRAYAGG ( JSON_OBJECT ( 'orderNumber' : CAST ( CAST ( TRIM ( ORDNO ) AS VARCHAR ( 15 ) CCSID 37 ) AS VARCHAR ( 15 ) CCSID 1208 ) , 'itemNumber' : CAST ( CAST ( TRIM ( ITNBR ) AS VARCHAR ( 15 ) CCSID 37 ) AS VARCHAR ( 15 ) CCSID 1208 ) ) FORMAT JSON ) AS ITEMS FROM HOURLY_ITEMS GROUP BY HOUR_NUM , WKCTR , STID ) , -- Downtime by hour per workcenter/site DOWN_HOURLY AS ( SELECT H . HOUR_NUM , D . WKCTR , D . STID , D . IS_SCHEDULED , GREATEST ( 0 , TIMESTAMPDIFF ( 2 , CHAR ( LEAST ( D . END_TS , H . HR_END_TS ) - GREATEST ( D . START_TS , H . HR_START_TS ) ) ) ) AS DOWN_SECONDS FROM DOWN_MATCHED D , ALL_HOURS H WHERE D . START_TS < H . HR_END_TS AND D . END_TS > H . HR_START_TS ) , DOWN_BY_HOUR AS ( SELECT HOUR_NUM , WKCTR , STID , SUM ( CASE WHEN IS_SCHEDULED = 'Y' THEN DOWN_SECONDS ELSE 0 END ) AS SCHED_DOWN_SEC , SUM ( CASE WHEN IS_SCHEDULED <> 'Y' THEN DOWN_SECONDS ELSE 0 END ) AS UNSCHED_DOWN_SEC FROM DOWN_HOURLY WHERE DOWN_SECONDS > 0 GROUP BY HOUR_NUM , WKCTR , STID ) , -- ========== COMBINE AND CALCULATE OEE ========== HOURLY_COMBINED AS ( SELECT A . HOUR_NUM , W . WKCTR , W . STID , COALESCE ( R . RUN_SEC , 0 ) AS RUN_SEC , COALESCE ( R . EXPECTED_QTY , DEC ( 0 , 15 , 4 ) ) AS EXPECTED_QTY , COALESCE ( R . ACTUAL_QTY , DEC ( 0 , 15 , 4 ) ) AS ACTUAL_QTY , COALESCE ( R . SCRAP_QTY , DEC ( 0 , 15 , 4 ) ) AS SCRAP_QTY , COALESCE ( D . SCHED_DOWN_SEC , 0 ) AS SCHED_DOWN_SEC , COALESCE ( D . UNSCHED_DOWN_SEC , 0 ) AS UNSCHED_DOWN_SEC , I . ITEMS , LEAST ( COALESCE ( R . RUN_SEC , 0 ) + COALESCE ( D . SCHED_DOWN_SEC , 0 ) + COALESCE ( D . UNSCHED_DOWN_SEC , 0 ) , 3600 ) AS TOTAL_ACTIVE_SEC FROM ALL_HOURS A CROSS JOIN WC_SITE_LIST W LEFT JOIN RUN_BY_HOUR R ON A . HOUR_NUM = R . HOUR_NUM AND W . WKCTR = R . WKCTR AND W . STID = R . STID LEFT JOIN DOWN_BY_HOUR D ON A . HOUR_NUM = D . HOUR_NUM AND W . WKCTR = D . WKCTR AND W . STID = D . STID LEFT JOIN HOURLY_ITEMS_AGG I ON A . HOUR_NUM = I . HOUR_NUM AND W . WKCTR = I . WKCTR AND W . STID = I . STID ) , HOURLY_WITH_CUM AS ( SELECT H . * , SUM ( RUN_SEC ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_RUN , SUM ( EXPECTED_QTY ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_EXPECTED , SUM ( ACTUAL_QTY ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_ACTUAL , SUM ( SCRAP_QTY ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_SCRAP , SUM ( SCHED_DOWN_SEC ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_SCHED_DOWN , SUM ( UNSCHED_DOWN_SEC ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_UNSCHED_DOWN , SUM ( TOTAL_ACTIVE_SEC ) OVER ( PARTITION BY WKCTR , STID ORDER BY HOUR_NUM ROWS UNBOUNDED PRECEDING ) AS CUM_ACTIVE FROM HOURLY_COMBINED H ) , HOURLY_OEE AS ( SELECT H . * , CASE WHEN TOTAL_ACTIVE_SEC > 0 THEN INT ( ( CAST ( RUN_SEC AS DOUBLE ) / CAST ( TOTAL_ACTIVE_SEC AS DOUBLE ) ) * 100 + 0.5 ) ELSE 0 END AS HRLY_AVAIL , CASE WHEN EXPECTED_QTY > 0 THEN LEAST ( INT ( ( ACTUAL_QTY / EXPECTED_QTY ) * 100 + 0.5 ) , 999 ) ELSE 0 END AS HRLY_PERF , CASE WHEN ( ACTUAL_QTY + SCRAP_QTY ) > 0 THEN INT ( ( ACTUAL_QTY / ( ACTUAL_QTY + SCRAP_QTY ) ) * 100 + 0.5 ) ELSE 100 END AS HRLY_QUAL , CASE WHEN CUM_ACTIVE > 0 THEN INT ( ( CAST ( CUM_RUN AS DOUBLE ) / CAST ( CUM_ACTIVE AS DOUBLE ) ) * 100 + 0.5 ) ELSE 0 END AS CUM_AVAIL , CASE WHEN CUM_EXPECTED > 0 THEN LEAST ( INT ( ( CUM_ACTUAL / CUM_EXPECTED ) * 100 + 0.5 ) , 999 ) ELSE 0 END AS CUM_PERF , CASE WHEN ( CUM_ACTUAL + CUM_SCRAP ) > 0 THEN INT ( ( CUM_ACTUAL / ( CUM_ACTUAL + CUM_SCRAP ) ) * 100 + 0.5 ) ELSE 100 END AS CUM_QUAL FROM HOURLY_WITH_CUM H ) , HOURLY_FINAL AS ( SELECT H . * , CASE WHEN HRLY_AVAIL > 0 AND HRLY_PERF > 0 AND HRLY_QUAL > 0 THEN INT ( ( CAST ( HRLY_AVAIL AS DOUBLE ) * CAST ( HRLY_PERF AS DOUBLE ) * CAST ( HRLY_QUAL AS DOUBLE ) / 10000 ) + 0.5 ) ELSE 0 END AS HRLY_OEE , CASE WHEN CUM_AVAIL > 0 AND CUM_PERF > 0 AND CUM_QUAL > 0 THEN INT ( ( CAST ( CUM_AVAIL AS DOUBLE ) * CAST ( CUM_PERF AS DOUBLE ) * CAST ( CUM_QUAL AS DOUBLE ) / 10000 ) + 0.5 ) ELSE 0 END AS CUM_OEE FROM HOURLY_OEE H ) , -- ========== SHIFT TIMES PER WORKCENTER ========== SHIFT_TIMES AS ( SELECT W . WSWKCT AS WKCTR , W . WSSTID AS STID , W . WSSF1B AS SHIFT1_BEGIN , W . WSSF1E AS SHIFT1_END , W . WSSF2B AS SHIFT2_BEGIN , W . WSSF2E AS SHIFT2_END , W . WSSF3B AS SHIFT3_BEGIN , W . WSSF3E AS SHIFT3_END , W . WSDEND AS DAY_END FROM SFCCWKS W ) , -- ========== BUILD JSON ========== JSON_HOURS AS ( SELECT WKCTR , STID , HOUR_NUM , JSON_OBJECT ( 'hour' : HOUR_NUM , 'hourLabel' : CASE WHEN HOUR_NUM = 0 THEN '12AM' WHEN HOUR_NUM < 12 THEN TRIM ( CHAR ( HOUR_NUM ) ) || 'AM' WHEN HOUR_NUM = 12 THEN '12PM' ELSE TRIM ( CHAR ( HOUR_NUM - 12 ) ) || 'PM' END , 'items' : COALESCE ( ITEMS , JSON_ARRAY ( ) ) FORMAT JSON , 'runMinutes' : INT ( RUN_SEC / 60 ) , 'scheduledDownMinutes' : INT ( SCHED_DOWN_SEC / 60 ) , 'unscheduledDownMinutes' : INT ( UNSCHED_DOWN_SEC / 60 ) , 'expectedQty' : DEC ( EXPECTED_QTY , 10 , 2 ) , 'actualQty' : DEC ( ACTUAL_QTY , 10 , 2 ) , 'scrapQty' : DEC ( SCRAP_QTY , 10 , 2 ) , 'cumulativeExpected' : DEC ( CUM_EXPECTED , 10 , 2 ) , 'cumulativeActual' : DEC ( CUM_ACTUAL , 10 , 2 ) , 'cumulativeScrap' : DEC ( CUM_SCRAP , 10 , 2 ) , 'variance' : DEC ( CUM_ACTUAL - CUM_EXPECTED , 10 , 2 ) , 'hourlyAvailability' : HRLY_AVAIL , 'hourlyPerformance' : HRLY_PERF , 'hourlyQuality' : HRLY_QUAL , 'hourlyOee' : HRLY_OEE , 'cumulativeAvailability' : CUM_AVAIL , 'cumulativePerformance' : CUM_PERF , 'cumulativeQuality' : CUM_QUAL , 'cumulativeOee' : CUM_OEE ) AS HOUR_OBJ FROM HOURLY_FINAL ) , WC_TOTALS AS ( SELECT WKCTR , STID , MAX ( CUM_ACTUAL ) AS TOTAL_ACTUAL , MAX ( CUM_EXPECTED ) AS TOTAL_EXPECTED , MAX ( CUM_SCRAP ) AS TOTAL_SCRAP , MAX ( CUM_RUN ) AS TOTAL_RUN_SEC , MAX ( CUM_SCHED_DOWN ) AS TOTAL_SCHED_DOWN , MAX ( CUM_UNSCHED_DOWN ) AS TOTAL_UNSCHED_DOWN , MAX ( CUM_OEE ) AS FINAL_OEE , MAX ( CUM_AVAIL ) AS FINAL_AVAIL , MAX ( CUM_PERF ) AS FINAL_PERF , MAX ( CUM_QUAL ) AS FINAL_QUAL FROM HOURLY_FINAL GROUP BY WKCTR , STID ) , 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 , 'totals' : JSON_OBJECT ( 'actualQty' : DEC ( COALESCE ( T . TOTAL_ACTUAL , DEC ( 0 , 15 , 4 ) ) , 10 , 2 ) , 'expectedQty' : DEC ( COALESCE ( T . TOTAL_EXPECTED , DEC ( 0 , 15 , 4 ) ) , 10 , 2 ) , 'scrapQty' : DEC ( COALESCE ( T . TOTAL_SCRAP , DEC ( 0 , 15 , 4 ) ) , 10 , 2 ) , 'variance' : DEC ( COALESCE ( T . TOTAL_ACTUAL - T . TOTAL_EXPECTED , DEC ( 0 , 15 , 4 ) ) , 10 , 2 ) , 'runMinutes' : INT ( COALESCE ( T . TOTAL_RUN_SEC , 0 ) / 60 ) , 'scheduledDownMinutes' : INT ( COALESCE ( T . TOTAL_SCHED_DOWN , 0 ) / 60 ) , 'unscheduledDownMinutes' : INT ( COALESCE ( T . TOTAL_UNSCHED_DOWN , 0 ) / 60 ) , 'oeePercentage' : COALESCE ( T . FINAL_OEE , 0 ) , 'availabilityPercentage' : COALESCE ( T . FINAL_AVAIL , 0 ) , 'performancePercentage' : COALESCE ( T . FINAL_PERF , 0 ) , 'qualityPercentage' : COALESCE ( T . FINAL_QUAL , 0 ) ) , 'shiftTimes' : ( SELECT JSON_OBJECT ( 'shift1Begin' : CASE WHEN S . SHIFT1_BEGIN > 0 THEN SUBSTRING ( LPAD ( S . SHIFT1_BEGIN , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT1_BEGIN , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'shift1End' : CASE WHEN S . SHIFT1_END > 0 THEN SUBSTRING ( LPAD ( S . SHIFT1_END , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT1_END , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'shift2Begin' : CASE WHEN S . SHIFT2_BEGIN > 0 THEN SUBSTRING ( LPAD ( S . SHIFT2_BEGIN , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT2_BEGIN , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'shift2End' : CASE WHEN S . SHIFT2_END > 0 THEN SUBSTRING ( LPAD ( S . SHIFT2_END , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT2_END , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'shift3Begin' : CASE WHEN S . SHIFT3_BEGIN > 0 THEN SUBSTRING ( LPAD ( S . SHIFT3_BEGIN , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT3_BEGIN , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'shift3End' : CASE WHEN S . SHIFT3_END > 0 THEN SUBSTRING ( LPAD ( S . SHIFT3_END , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . SHIFT3_END , 6 , '0' ) , 3 , 2 ) ELSE NULL END , 'dayEnd' : CASE WHEN S . DAY_END > 0 THEN SUBSTRING ( LPAD ( S . DAY_END , 6 , '0' ) , 1 , 2 ) || ':' || SUBSTRING ( LPAD ( S . DAY_END , 6 , '0' ) , 3 , 2 ) ELSE NULL END ) FROM SHIFT_TIMES S WHERE S . WKCTR = T . WKCTR AND S . STID = T . STID FETCH FIRST 1 ROW ONLY ) FORMAT JSON ) 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_OEE TO AMAPICS WITH GRANT OPTION ; GRANT EXECUTE ON SPECIFIC FUNCTION RESTAPI.SFP_GET_HOURLY_OEE TO PUBLIC ;