--source-side extract for cms.terms (pipekit CMS module) --CMS terms live in the generic CODE table (A2 = 'NN'); the structured attributes are --packed into the fixed-width CHAR column A215 at fixed byte offsets. Parse them HERE on --DB2 where A215 is intact -- do NOT sync raw A215 and parse in Postgres: the transfer --strips leading spaces on discount-bearing rows, which shifts every offset. --offsets verified against the working query in sql/db2/CMS/AR/terms/Terms Codes.sql --REPLACE(...,' ','0') guards each numeric field: unpopulated term types leave those --positions blank, and a bare INT()/FLOAT() over a space would fail the whole extract. SELECT LTRIM(RTRIM(A9)) AS term ,LTRIM(RTRIM(A30)) AS descr --discount % offered (e.g. 1.00 = 1%); A215 pos 102-105, scaled by 10000 ,ROUND(FLOAT(REPLACE(SUBSTR(A215,102,4),' ','0')) / FLOAT(10000),2) AS disc_pct --days within which the discount may be taken; A215 pos 84-86 ,INT(REPLACE(SUBSTR(A215,84,3),' ','0')) AS disc_days --net pay days; A215 pos 64-66 ,INT(REPLACE(SUBSTR(A215,64,3),' ','0')) AS pay_days --pay percent (rarely used split-term %); A215 pos 10-12 ,FLOAT(REPLACE(SUBSTR(A215,10,3),' ','0')) AS pay_perc --fixed pay date (seasonal/dated terms): A215 pos 28-33 packed MMDDYY -> YYYY-MM-DD --emitted as ISO text (null when the date field is zero) so a malformed packed value --cannot fail the extract; cast to date in Postgres on read ,CASE WHEN INT(REPLACE(SUBSTR(A215,28,6),' ','0')) = 0 THEN CAST(NULL AS CHAR(10)) ELSE '20' || SUBSTR(A215,32,2) || '-' || SUBSTR(A215,28,2) || '-' || SUBSTR(A215,30,2) END AS pay_date FROM LGDAT.CODE WHERE A2 = 'NN'