SET PATH *LIBL ; 

CREATE OR REPLACE FUNCTION RESTAPI.SFP_GET_HOURLY_PRODUCTION ( 
	IN_PARM CLOB(2147483647) ) 
	RETURNS CLOB(2147483647)   
	LANGUAGE SQL 
	SPECIFIC RESTAPI.SFP_GET_HOURLY_PRODUCTION 
	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 ON transactions (job starts) 
ON_TRANSACTIONS AS ( 
SELECT T . ORDNO , T . CLDT , T . OPSEQ , T . EMPNO , T . BADGE , T . WKCTR , 
T . TDATE AS ON_DATE , T . TTIME AS ON_TIME , T . RUNCD , 
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 ON_TS , 
ROW_NUMBER ( ) OVER ( 
PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( 
T . EMPNO , T . BADGE ) , T . RUNCD 
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 . 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 OFF transactions (job ends with quantities) 
OFF_TRANSACTIONS AS ( 
SELECT T . ORDNO , T . CLDT , T . OPSEQ , T . EMPNO , T . BADGE , 
T . TDATE AS OFF_DATE , T . TTIME AS OFF_TIME , T . QTCOM , 
T . QTSCP , T . RUNCD , 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 OFF_TS , 
ROW_NUMBER ( ) OVER ( 
PARTITION BY T . ORDNO , T . OPSEQ , COALESCE ( 
T . EMPNO , T . BADGE ) , T . RUNCD 
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 . 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 ON with OF by order/operation/employee/runcode 
MATCHED_TRANS AS ( 
SELECT ON_T . ORDNO , ON_T . ITNBR , ON_T . RUNCD , ON_T . ON_DATE , 
ON_T . ON_TIME , ON_T . ON_TS AS START_TS , ON_T . WKCTR , 
ON_T . STID , 
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 , 
COALESCE ( R . SRLHU , 0 ) AS STD_RUN_HRS , 
COALESCE ( R . TBCDE , ' ' ) AS TIME_BASIS 
FROM ON_TRANSACTIONS ON_T 
CROSS JOIN CURRENT_END CE 
LEFT JOIN OFF_TRANSACTIONS 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 
WHERE ON_T . RUNCD = 'R'  -- Run transactions only 
) , 
-- Calculate pieces per minute 
TRANS_WITH_RATE AS ( 
SELECT M . * , 
DEC ( 
CASE 
TIME_BASIS 
WHEN 
' ' 
THEN 
CASE 
WHEN 
STD_RUN_HRS > 0 
THEN 1.0 / STD_RUN_HRS / 60 
ELSE 0 
END 
WHEN 'P' THEN STD_RUN_HRS / 60 
WHEN 
'M' 
THEN 
CASE 
WHEN 
STD_RUN_HRS > 0 
THEN 1.0 / STD_RUN_HRS 
ELSE 0 
END 
WHEN 
'1' 
THEN 
CASE 
WHEN 
STD_RUN_HRS > 0 
THEN 10.0 / STD_RUN_HRS / 60 
ELSE 0 
END 
WHEN 
'2' 
THEN 
CASE 
WHEN 
STD_RUN_HRS > 0 
THEN 100.0 / STD_RUN_HRS / 60 
ELSE 0 
END 
WHEN 
'3' 
THEN 
CASE 
WHEN 
STD_RUN_HRS > 0 
THEN 1000.0 / STD_RUN_HRS / 60 
ELSE 0 
END 
ELSE 0 
END , 15 , 8 ) AS PCS_PER_MIN 
FROM MATCHED_TRANS M 
) , 
-- Get distinct workcenter/site combinations 
WC_SITE_LIST AS ( 
SELECT DISTINCT WKCTR , STID 
FROM TRANS_WITH_RATE 
) , 
-- 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 ) 
) , 
-- Calculate time per hour for each transaction 
HOURLY_BREAKDOWN AS ( 
SELECT T . ORDNO , T . ITNBR , T . QTY_COMPLETE , T . QTY_SCRAP , 
T . PCS_PER_MIN , T . START_TS , T . END_TS , T . OFF_DATE , 
T . OFF_TIME , T . WKCTR , T . STID , H . HOUR_NUM , 
GREATEST ( 
0 , TIMESTAMPDIFF ( 
2 , CHAR ( 
LEAST ( T . END_TS , H . HR_END_TS ) - 
GREATEST ( T . START_TS , H . HR_START_TS ) ) ) ) 
AS SECONDS_IN_HR 
FROM TRANS_WITH_RATE T , ALL_HOURS H 
WHERE T . START_TS < H . HR_END_TS 
AND T . END_TS > H . HR_START_TS 
) , 
-- Aggregate by hour, workcenter, site 
HOURLY_AGG AS ( 
SELECT HOUR_NUM , WKCTR , STID , 
SUM ( SECONDS_IN_HR ) AS TOTAL_SECONDS , 
DEC ( 
SUM ( DEC ( SECONDS_IN_HR , 15 , 4 ) * PCS_PER_MIN / 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 , 
COUNT ( DISTINCT ORDNO ) AS ORDER_COUNT 
FROM HOURLY_BREAKDOWN 
WHERE SECONDS_IN_HR > 0 
GROUP BY HOUR_NUM , WKCTR , STID 
) , 
-- Get distinct order/item per hour/wkctr/stid 
HOURLY_ITEMS AS ( 
SELECT DISTINCT HOUR_NUM , WKCTR , STID , ORDNO , ITNBR 
FROM HOURLY_BREAKDOWN 
WHERE SECONDS_IN_HR > 0 
) , 
-- Aggregate items into JSON array 
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 
) , 
-- 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 . TOTAL_SECONDS , 0 ) AS TOTAL_SECONDS , 
COALESCE ( H . EXPECTED_QTY , DEC ( 0 , 15 , 4 ) ) 
AS EXPECTED_QTY , 
COALESCE ( H . ACTUAL_QTY , DEC ( 0 , 15 , 4 ) ) AS ACTUAL_QTY , 
COALESCE ( H . SCRAP_QTY , DEC ( 0 , 15 , 4 ) ) AS SCRAP_QTY , 
COALESCE ( H . ORDER_COUNT , 0 ) AS ORDER_COUNT , 
I . ITEMS AS ITEMS , 
SUM ( COALESCE ( H . EXPECTED_QTY , DEC ( 0 , 15 , 4 ) ) ) OVER ( 
PARTITION BY W . WKCTR , W . STID 
ORDER BY A . HOUR_NUM ROWS UNBOUNDED PRECEDING 
) AS CUM_EXPECTED , 
SUM ( COALESCE ( H . ACTUAL_QTY , DEC ( 0 , 15 , 4 ) ) ) OVER ( 
PARTITION BY W . WKCTR , W . STID 
ORDER BY A . HOUR_NUM ROWS UNBOUNDED PRECEDING 
) AS CUM_ACTUAL , 
SUM ( COALESCE ( H . SCRAP_QTY , DEC ( 0 , 15 , 4 ) ) ) OVER ( 
PARTITION BY W . WKCTR , W . STID 
ORDER BY A . HOUR_NUM ROWS UNBOUNDED PRECEDING 
) AS CUM_SCRAP 
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_ITEMS_AGG I 
ON A . HOUR_NUM = I . HOUR_NUM 
AND W . WKCTR = I . WKCTR 
AND W . STID = I . 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 , 'runMinutes' : INT ( TOTAL_SECONDS / 60 ) , 
'items' : COALESCE ( ITEMS , JSON_ARRAY ( ) ) FORMAT JSON , 
'orderCount' : ORDER_COUNT , '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 ) , 
'performancePercentage' : 
CASE 
WHEN 
CUM_EXPECTED > 0 
THEN 
INT ( 
( CUM_ACTUAL / CUM_EXPECTED * 100 ) + 
0.5 ) 
ELSE 0 
END 
) AS HOUR_OBJ , HOUR_NUM 
FROM HOURLY_FULL 
) , 
-- Calculate totals per workcenter/site 
WC_TOTALS AS ( 
SELECT WKCTR , STID , MAX ( CUM_ACTUAL ) AS TOTAL_ACTUAL , 
MAX ( CUM_EXPECTED ) AS TOTAL_EXPECTED , 
MAX ( CUM_SCRAP ) AS TOTAL_SCRAP 
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 , 
'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 ) , 
'performancePercentage' : 
CASE 
WHEN 
T . TOTAL_EXPECTED > 0 
THEN 
INT ( 
( T . TOTAL_ACTUAL / T . TOTAL_EXPECTED * 
100 ) + 0.5 ) 
ELSE 0 
END 
) 
) 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_PRODUCTION 
TO AMAPICS WITH GRANT OPTION ; 

GRANT EXECUTE   
ON SPECIFIC FUNCTION RESTAPI.SFP_GET_HOURLY_PRODUCTION 
TO PUBLIC ; 

