SET PATH *LIBL ; 

CREATE OR REPLACE FUNCTION RESTAPI.SFP_GET_WORKCENTERS_DETAILS ( 
	IN_PARM CLOB(2147483647) ) 
	RETURNS CLOB(2147483647)   
	LANGUAGE SQL 
	SPECIFIC RESTAPI.SFP_GET_WORKCENTERS_DETAILS 
	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_SITE VARCHAR ( 3 ) DEFAULT '' ; 
DECLARE V_WC VARCHAR ( 5 ) DEFAULT '' ; 
DECLARE V_DEPT VARCHAR ( 5 ) DEFAULT '' ; 
DECLARE V_POSITION VARCHAR ( 5 ) DEFAULT '' ; 
DECLARE V_SHOW_ALL VARCHAR ( 5 ) DEFAULT 'false' ; 
DECLARE V_INDIRECT VARCHAR ( 5 ) DEFAULT 'false' ; 
DECLARE LL VARCHAR ( 1024 ) DEFAULT '' ; 
DECLARE V_IN_PARM CLOB ( 2 G ) DEFAULT '' ; 
DECLARE V_TODAY DECIMAL ( 7 , 0 ) DEFAULT 0 ; 
DECLARE V_CUTOFF DECIMAL ( 7 , 0 ) DEFAULT 9999999 ; 
-- 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 
SET V_SITE = COALESCE ( JSON_VALUE ( IN_PARM , '$.site' ) , '' ) ; 
SET V_WC = COALESCE ( JSON_VALUE ( IN_PARM , '$.workcenter' ) , '' ) ; 
SET V_DEPT = COALESCE ( JSON_VALUE ( IN_PARM , '$.department' ) , '' ) ; 
SET V_POSITION = COALESCE ( JSON_VALUE ( IN_PARM , '$.positionTo' ) , '' ) ; 
SET V_SHOW_ALL = COALESCE ( JSON_VALUE ( IN_PARM , '$.showAll' ) , 'false' ) ;SET V_INDIRECT = COALESCE ( 
JSON_VALUE ( IN_PARM , '$.indirectOnly' ) , 'false' ) ; 
-- 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 today's date in CYYMMDD 
SET V_TODAY = ZONED ( CURRENT_DATE ) - 19000000 ; 
RETURN 
WITH 
-- Base workcenter list from FACMST (EPDM) joined with SITMST 
-- Excludes simulation sites (sims = '0') 
WC_BASE AS ( 
SELECT F . WKCTR , F . WCDSC , F . FRMAN , F . DPTNO , F . STID , 
TRIM ( F . WKGRP ) AS WKGRP , F . WLNCD 
FROM FACMST F 
JOIN SITMST S 
ON F . STID = S . STID 
WHERE S . SIMS = '0' 
AND F . STID = COALESCE ( NULLIF ( V_SITE , '' ) , F . STID ) 
AND F . WKCTR = COALESCE ( NULLIF ( V_WC , '' ) , F . WKCTR ) 
AND F . DPTNO = COALESCE ( NULLIF ( V_DEPT , '' ) , F . DPTNO ) 
AND F . WKCTR >= COALESCE ( 
NULLIF ( V_POSITION , '' ) , F . WKCTR ) 
AND ( V_INDIRECT <> 'true' 
OR F . WKGRP = 'IND' ) 
AND ( V_INDIRECT = 'true' 
OR F . WKGRP <> 'IND' 
OR V_SHOW_ALL = 'true' ) 
) , 
-- Down status from SFCCWKS joined with SFCDWR for reason description 
DOWN_STATUS AS ( 
SELECT W . WSWKCT AS WKCTR , W . WSSTID AS STID , 
TRIM ( W . WSDWNR ) AS DOWN_REASON , 
TRIM ( D . KDRDESC ) AS DOWN_DESCRIPTION , 
D . KDSCHED AS SCHEDULED_DOWN , W . WSSF1B , W . WSSF1E , 
W . WSSF2B , W . WSSF2E , W . WSSF3B , W . WSSF3E , W . WSDEND , 
W . WSCCRW AS CURRENT_CREW 
FROM SFCCWKS W 
LEFT JOIN SFCDWR D 
ON TRIM ( W . WSDWNR ) = TRIM ( D . KDREASN ) 
) , 
-- Latest transaction per badge/ordno/opseq to determine ON status 
-- Mimics SFP002R get_oncnt logic 
LATEST_TRANS AS ( 
SELECT T . BADGE , T . ORDNO , T . OPSEQ , T . TRFMT , 
ROW_NUMBER ( ) OVER ( 
PARTITION BY T . BADGE , T . ORDNO , T . OPSEQ 
ORDER BY T . TDATE DESC , T . TTIME DESC 
) AS RN 
FROM TRDATA T 
WHERE T . TRFMT IN ( 'ON' , 'OF' ) 
AND T . TSTAT < 5 
AND T . ACREC = 'Y' 
) , 
-- Active employees per workcenter/site 
ACTIVE_EMPS AS ( 
SELECT R . AWRKC AS WKCTR , M . STID , COUNT ( * ) AS ACTIVE_OPS , 
COUNT ( DISTINCT L . BADGE ) AS UNIQUE_EMPS 
FROM LATEST_TRANS L 
JOIN MOMAST M 
ON L . ORDNO = M . ORDNO 
JOIN MOROUT R 
ON L . ORDNO = R . ORDNO 
AND L . OPSEQ = R . OPSEQ 
WHERE L . RN = 1 
AND L . TRFMT = 'ON' 
GROUP BY R . AWRKC , M . STID 
) , 
-- Open order count per workcenter/site 
-- Orders with opstc > '00' and < '40', ostat > '00' and < '45' 
OPEN_ORDERS AS ( 
SELECT R . AWRKC AS WKCTR , M . STID , 
COUNT ( DISTINCT R . ORDNO ) AS OPEN_COUNT 
FROM MOROUT R 
JOIN MOMAST M 
ON R . ORDNO = M . ORDNO 
WHERE R . OPSTC > '00' 
AND R . OPSTC < '40' 
AND M . OSTAT > '00' 
AND M . OSTAT < '45' 
AND M . ODUDT <= V_CUTOFF 
GROUP BY R . AWRKC , M . STID 
) , 
-- Planned order count per workcenter/site (from LRP) 
PLANNED_ORDERS AS ( 
SELECT W . RPWKCT AS WKCTR , W . RPSTID AS STID , 
COUNT ( DISTINCT W . RPORTK ) AS PLANNED_COUNT 
FROM LRPWKCT W 
JOIN ORDREV O 
ON W . RPORTK = O . TKEN 
WHERE W . RPODUD <= V_CUTOFF 
AND W . RPORTK <> ' ' 
AND O . NNUMB = ' ' 
GROUP BY W . RPWKCT , W . RPSTID 
) , 
-- Combine all data 
WC_COMBINED AS ( 
SELECT B . WKCTR , B . WCDSC , B . FRMAN , B . DPTNO , B . STID , B . WKGRP , 
B . WLNCD , COALESCE ( D . DOWN_REASON , '' ) AS DOWN_REASON , 
COALESCE ( D . DOWN_DESCRIPTION , '' ) AS DOWN_DESCRIPTION , 
COALESCE ( D . SCHEDULED_DOWN , '' ) AS SCHEDULED_DOWN , 
COALESCE ( D . CURRENT_CREW , '' ) AS CURRENT_CREW , 
COALESCE ( D . WSSF1B , 0 ) AS SF1_BEGIN , 
COALESCE ( D . WSSF1E , 0 ) AS SF1_END , 
COALESCE ( D . WSSF2B , 0 ) AS SF2_BEGIN , 
COALESCE ( D . WSSF2E , 0 ) AS SF2_END , 
COALESCE ( D . WSSF3B , 0 ) AS SF3_BEGIN , 
COALESCE ( D . WSSF3E , 0 ) AS SF3_END , 
COALESCE ( D . WSDEND , 0 ) AS DAY_END , 
COALESCE ( A . ACTIVE_OPS , 0 ) AS ACTIVE_OPS , 
COALESCE ( A . UNIQUE_EMPS , 0 ) AS UNIQUE_EMPS , 
COALESCE ( O . OPEN_COUNT , 0 ) AS OPEN_ORDERS , 
COALESCE ( P . PLANNED_COUNT , 0 ) AS PLANNED_ORDERS 
FROM WC_BASE B 
LEFT JOIN DOWN_STATUS D 
ON B . WKCTR = D . WKCTR 
AND B . STID = D . STID 
LEFT JOIN ACTIVE_EMPS A 
ON B . WKCTR = A . WKCTR 
AND B . STID = A . STID 
LEFT JOIN OPEN_ORDERS O 
ON B . WKCTR = O . WKCTR 
AND B . STID = O . STID 
LEFT JOIN PLANNED_ORDERS P 
ON B . WKCTR = P . WKCTR 
AND B . STID = P . STID 
) , 
-- Build JSON for each workcenter 
JSON_ROWS AS ( 
SELECT 
JSON_OBJECT ( 
'workcenterId' : TRIM ( 
CAST ( WKCTR AS VARCHAR ( 5 ) CCSID 37 ) ) , 
'workcenterDescription' : TRIM ( 
CAST ( WCDSC AS VARCHAR ( 50 ) CCSID 37 ) ) , 
'site' : TRIM ( CAST ( STID AS VARCHAR ( 5 ) CCSID 37 ) ) , 
'department' : TRIM ( 
CAST ( DPTNO AS VARCHAR ( 5 ) CCSID 37 ) ) , 
'foreman' : TRIM ( 
CAST ( FRMAN AS VARCHAR ( 5 ) CCSID 37 ) ) , 
'workGroup' : 
CASE 
WHEN WKGRP = '' THEN NULL 
ELSE CAST ( WKGRP AS VARCHAR ( 50 ) CCSID 37 ) 
END , 'isProductionLine' : 
CASE 
WHEN WLNCD = '1' THEN 'true' 
ELSE 'false' 
END FORMAT JSON , 'isIndirect' : 
CASE 
WHEN WKGRP = 'IND' THEN 'true' 
ELSE 'false' 
END FORMAT JSON , 'isDown' : 
CASE 
WHEN DOWN_REASON <> '' THEN 'true' 
ELSE 'false' 
END FORMAT JSON , 'downReason' : 
CASE 
WHEN DOWN_REASON = '' THEN NULL 
ELSE 
JSON_OBJECT ( 
'reasonCode' : DOWN_REASON , 
'description' : DOWN_DESCRIPTION , 
'scheduled' : 
CASE 
WHEN 
SCHEDULED_DOWN = 'Y' THEN 'true' 
ELSE 'false' 
END FORMAT JSON 
) 
END FORMAT JSON , 'currentCrew' : 
CASE 
WHEN CURRENT_CREW = '' THEN NULL 
ELSE CURRENT_CREW 
END , 'activeOperations' : ACTIVE_OPS , 
'uniqueEmployees' : UNIQUE_EMPS , 
'employeeDisplay' : 
CASE 
WHEN 
ACTIVE_OPS > 0 
THEN 
TRIM ( CHAR ( UNIQUE_EMPS ) ) || ' / ' || 
TRIM ( CHAR ( ACTIVE_OPS ) ) 
ELSE NULL 
END , 'openOrders' : OPEN_ORDERS , 
'plannedOrders' : PLANNED_ORDERS , 
'shiftTimes' : JSON_OBJECT ( 
'shift1Begin' : 
CASE 
WHEN 
SF1_BEGIN > 0 
THEN 
SUBSTRING ( 
LPAD ( SF1_BEGIN , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF1_BEGIN , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'shift1End' : 
CASE 
WHEN 
SF1_END > 0 
THEN 
SUBSTRING ( 
LPAD ( SF1_END , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF1_END , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'shift2Begin' : 
CASE 
WHEN 
SF2_BEGIN > 0 
THEN 
SUBSTRING ( 
LPAD ( SF2_BEGIN , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF2_BEGIN , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'shift2End' : 
CASE 
WHEN 
SF2_END > 0 
THEN 
SUBSTRING ( 
LPAD ( SF2_END , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF2_END , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'shift3Begin' : 
CASE 
WHEN 
SF3_BEGIN > 0 
THEN 
SUBSTRING ( 
LPAD ( SF3_BEGIN , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF3_BEGIN , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'shift3End' : 
CASE 
WHEN 
SF3_END > 0 
THEN 
SUBSTRING ( 
LPAD ( SF3_END , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( SF3_END , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END , 'dayEnd' : 
CASE 
WHEN 
DAY_END > 0 
THEN 
SUBSTRING ( 
LPAD ( DAY_END , 6 , '0' ) , 1 , 2 ) || 
':' || 
SUBSTRING ( 
LPAD ( DAY_END , 6 , '0' ) , 3 , 2 ) 
ELSE NULL 
END 
) 
) AS WC_OBJ , ACTIVE_OPS , UNIQUE_EMPS 
FROM WC_COMBINED 
ORDER BY WKCTR ) , 
-- Calculate totals 
TOTALS AS ( 
SELECT SUM ( ACTIVE_OPS ) AS TOTAL_ACTIVE_OPS , 
SUM ( UNIQUE_EMPS ) AS TOTAL_UNIQUE_EMPS , 
COUNT ( * ) AS WORKCENTER_COUNT 
FROM WC_COMBINED 
) SELECT JSON_OBJECT ( 
'workcenters' : ( SELECT JSON_ARRAYAGG ( 
WC_OBJ FORMAT JSON 
) 
FROM JSON_ROWS ) FORMAT JSON , 'totals' : JSON_OBJECT ( 
'workcenterCount' : T . WORKCENTER_COUNT , 
'totalActiveOperations' : COALESCE ( T . TOTAL_ACTIVE_OPS , 0 ) , 
'totalUniqueEmployees' : COALESCE ( T . TOTAL_UNIQUE_EMPS , 0 ) , 
'employeeDisplay' : TRIM ( 
CHAR ( COALESCE ( T . TOTAL_UNIQUE_EMPS , 0 ) ) ) || ' / ' || 
TRIM ( CHAR ( COALESCE ( T . TOTAL_ACTIVE_OPS , 0 ) ) ) 
) , 'filters' : JSON_OBJECT ( 
'site' : 
CASE 
WHEN V_SITE = '' THEN NULL 
ELSE V_SITE 
END , 'workcenter' : 
CASE 
WHEN V_WC = '' THEN NULL 
ELSE V_WC 
END , 'department' : 
CASE 
WHEN V_DEPT = '' THEN NULL 
ELSE V_DEPT 
END , 'showAll' : 
CASE 
WHEN V_SHOW_ALL = 'true' THEN 'true' 
ELSE 'false' 
END FORMAT JSON , 'indirectOnly' : 
CASE 
WHEN V_INDIRECT = 'true' THEN 'true' 
ELSE 'false' 
END FORMAT JSON 
) , 'generatedAt' : TO_CHAR ( 
CURRENT_TIMESTAMP , 'YYYY-MM-DD HH24:MI:SS' ) 
) 
FROM TOTALS T ; 
END  ; 

GRANT ALTER , EXECUTE   
ON SPECIFIC FUNCTION RESTAPI.SFP_GET_WORKCENTERS_DETAILS 
TO AMAPICS WITH GRANT OPTION ; 

GRANT EXECUTE   
ON SPECIFIC FUNCTION RESTAPI.SFP_GET_WORKCENTERS_DETAILS 
TO PUBLIC ; 

