pipekit/config/modules/poi.sql
Paul Trowbridge d5618e9514 Sync purchase orders incrementally
poh and poi were full truncate-reloads: 187k rows / 30 s and 529k / 75 s.
Both now pull only changed POs -- 1,201 rows / 5.5 s and 3,660 / 10 s --
and reconcile.py --quick reads IN SYNC on all 115 / 120 metrics.

POH turns out to carry its own change stamp in its "future" fields:
KAFUT12 is Date Created and KAFUT20 is Date Updated, both CHAR(10) ISO
text. Predicate is those two against the watermark, plus the open hot set
KACRCM = '1' -- 1,035 of 187k headers, and KACRCM leads most of POH's
logicals, so it is a keyed read. POI has no change stamp of its own, so
it joins the header's changed set at PO grain; its merge key is kbpo#, so
staging only the changed lines would drop the rest of the PO.

CMS logs this family (POHL, POIL), but both are redundant for change
detection: over 7 days POHL and the KAFUT20/12 window agree exactly, and
POIL reports no PO that POHL does not, so a line edit always stamps the
header. The logs' one unique contribution is deletes, which an
incremental merge structurally cannot see -- delete-by-key only touches
staged keys, so a purged PO would sit in the dest forever (~88 POs/yr).

poh_deleted closes that: DQ0ACTN = '3' is the delete action, and the
NOT EXISTS against live POH makes the list exact rather than a guess
about action ordering, since a re-used PO number is back in POH and drops
out on its own. Three dest hooks, in order -- clearing the tombstone of
any PO that is live again must happen before the deletes, or the second
hook could remove a row poh had just refreshed.

Whenever these are scheduled, run order must be poh -> poi ->
poh_deleted: poi's watermark resolves off cms.poh, and poh_deleted's
first hook reads cms.poh expecting it to be fresh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 22:44:24 -04:00

114 lines
3.3 KiB
SQL

WITH changed AS (
-- Same predicate as poh (module 138) -- POI carries no change stamp of its
-- own (every KBFUT* is either blank or a business attribute), so it learns
-- of change only through its header. Verified 2026-08-18 against
-- LGDAT.POIL (CMS's own item audit log): over a 7-day window POIL reports
-- 0 POs that POHL does not already report, i.e. a line edit always stamps
-- the header.
SELECT "KAPO#" AS po FROM LGDAT.POH WHERE KAFUT20 >= '{poi_wm}'
UNION
SELECT "KAPO#" FROM LGDAT.POH WHERE KAFUT12 >= '{poi_wm}'
UNION
SELECT "KAPO#" FROM LGDAT.POH WHERE KACRCM = '1'
UNION
-- 4 open lines hang off headers already marked complete; without this they
-- would never refresh.
SELECT "KBPO#" FROM LGDAT.POI WHERE KBCRCM = '1'
)
SELECT
"KBPO#" AS "kbpo#",
"KBITM#" AS "kbitm#",
KBQTYO AS kbqtyo,
KBQTYR AS kbqtyr,
KBQTYA AS kbqtya,
KBQTYJ AS kbqtyj,
RTRIM(KBOUNT) AS kbount,
RTRIM("KBPT#") AS "kbpt#",
RTRIM("KBVPT#") AS "kbvpt#",
KBUPRC AS kbuprc,
RTRIM(KBPUNT) AS kbpunt,
CASE WHEN KBRDAT IN (DATE('0001-01-01'), DATE('9999-12-31')) THEN NULL ELSE KBRDAT END AS kbrdat,
CASE WHEN KBCDAT IN (DATE('0001-01-01'), DATE('9999-12-31')) THEN NULL ELSE KBCDAT END AS kbcdat,
RTRIM(KBREQR) AS kbreqr,
"KBREQ#" AS "kbreq#",
"KBGL#" AS "kbgl#",
RTRIM("KBJOB#") AS "kbjob#",
"KBSEQ#" AS "kbseq#",
RTRIM("KBPRJ#") AS "kbprj#",
RTRIM(KBDEPT) AS kbdept,
RTRIM(KBCGST) AS kbcgst,
RTRIM(KBCPST) AS kbcpst,
RTRIM(KBISTS) AS kbists,
RTRIM(KBITYP) AS kbityp,
RTRIM(KBISTR) AS kbistr,
RTRIM(KBOSTR) AS kbostr,
KBEXTN AS kbextn,
KBGSTA AS kbgsta,
KBPSTA AS kbpsta,
RTRIM(KBPRGR) AS kbprgr,
RTRIM(KBCRCM) AS kbcrcm,
RTRIM(KBOTYP) AS kbotyp,
RTRIM(KBTOS) AS kbtos,
RTRIM(KBTAXG) AS kbtaxg,
RTRIM(KBTAXR) AS kbtaxr,
RTRIM(KBDPRC) AS kbdprc,
RTRIM(KBPLNT) AS kbplnt,
RTRIM(KBFUT1) AS kbfut1,
RTRIM(KBFUT2) AS kbfut2,
RTRIM(KBFUT3) AS kbfut3,
RTRIM(KBFUT4) AS kbfut4,
RTRIM(KBFUT5) AS kbfut5,
RTRIM(KBFUT6) AS kbfut6,
RTRIM(KBFUT7) AS kbfut7,
RTRIM(KBFUT8) AS kbfut8,
RTRIM(KBFUT9) AS kbfut9,
RTRIM(KBFUT10) AS kbfut10,
RTRIM(KBFUT11) AS kbfut11,
RTRIM(KBFUT12) AS kbfut12,
RTRIM(KBFUT13) AS kbfut13,
RTRIM(KBFUT14) AS kbfut14,
RTRIM(KBFUT15) AS kbfut15,
RTRIM(KBFUT16) AS kbfut16,
RTRIM(KBFUT17) AS kbfut17,
RTRIM(KBFUT18) AS kbfut18,
RTRIM(KBFUT19) AS kbfut19,
RTRIM(KBFUT20) AS kbfut20,
RTRIM(KBFUT21) AS kbfut21,
RTRIM(KBFUT22) AS kbfut22,
RTRIM(KBFUT23) AS kbfut23,
RTRIM(KBFUT24) AS kbfut24,
RTRIM(KBFUT25) AS kbfut25,
RTRIM(KBFUT26) AS kbfut26,
RTRIM(KBFUT27) AS kbfut27,
RTRIM(KBFUT28) AS kbfut28,
KBFUT29 AS kbfut29,
KBFUT30 AS kbfut30,
KBFUT31 AS kbfut31,
KBFUT32 AS kbfut32,
KBFUT33 AS kbfut33,
KBFUT34 AS kbfut34,
KBFUT35 AS kbfut35,
KBFUT36 AS kbfut36,
RTRIM(KBUSR1) AS kbusr1,
RTRIM(KBUSR2) AS kbusr2,
RTRIM(KBUSR3) AS kbusr3,
RTRIM(KBUSR4) AS kbusr4,
RTRIM(KBUSR5) AS kbusr5,
RTRIM(KBUSR6) AS kbusr6,
RTRIM(KBUSR7) AS kbusr7,
RTRIM(KBCORG) AS kbcorg,
RTRIM(KBROHD) AS kbrohd,
KBORDN AS kbordn,
KBOITM AS kboitm,
RTRIM(KBPPRT) AS kbpprt,
RTRIM(KBCSTC) AS kbcstc,
RTRIM(KBCSTT) AS kbcstt,
RTRIM(KBSRIN) AS kbsrin,
KBUNTFR AS kbuntfr,
KBSRGPC AS kbsrgpc,
KBFFRTC AS kbffrtc,
RTRIM(KBPGRP) AS kbpgrp
FROM LGDAT.POI i
INNER JOIN changed c
ON c.po = i."KBPO#"