pipekit/config/modules/poh.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

109 lines
3.2 KiB
SQL

SELECT
"KAPO#" AS "kapo#",
RTRIM(KAOTYP) AS kaotyp,
RTRIM(KARELD) AS kareld,
CASE WHEN KAODAT IN (DATE('0001-01-01'), DATE('9999-12-31')) THEN NULL ELSE KAODAT END AS kaodat,
RTRIM(KAOVND) AS kaovnd,
RTRIM(KAOVNM) AS kaovnm,
RTRIM(KABVND) AS kabvnd,
RTRIM(KASVND) AS kasvnd,
RTRIM(KASVNM) AS kasvnm,
RTRIM(KASAD1) AS kasad1,
RTRIM(KASAD2) AS kasad2,
RTRIM(KASAD3) AS kasad3,
RTRIM(KASAD4) AS kasad4,
RTRIM(KASAD5) AS kasad5,
RTRIM(KASAD6) AS kasad6,
RTRIM(KASAD7) AS kasad7,
RTRIM(KASAD8) AS kasad8,
RTRIM(KASAD9) AS kasad9,
RTRIM(KASAD10) AS kasad10,
RTRIM(KASVPC) AS kasvpc,
RTRIM(KACNTC) AS kacntc,
RTRIM(KAPHON) AS kaphon,
RTRIM(KATRMC) AS katrmc,
RTRIM(KASVIA) AS kasvia,
RTRIM(KAPCOL) AS kapcol,
RTRIM(KACONF) AS kaconf,
RTRIM(KAFOBC) AS kafobc,
RTRIM(KAOSTS) AS kaosts,
RTRIM(KAPSTS) AS kapsts,
KAOGST AS kaogst,
KAOPST AS kaopst,
KAOTOT AS kaotot,
KABVAL AS kabval,
RTRIM(KACURR) AS kacurr,
RTRIM(KABUYR) AS kabuyr,
"KALRL#" AS "kalrl#",
KAQNUM AS kaqnum,
RTRIM(KACRCM) AS kacrcm,
RTRIM(KACARC) AS kacarc,
KAFRAM AS kafram,
RTRIM(KACBRC) AS kacbrc,
KADUAM AS kaduam,
RTRIM(KAHREA) AS kahrea,
RTRIM(KAFAX) AS kafax,
RTRIM(KAIUSE) AS kaiuse,
RTRIM(KAPURP) AS kapurp,
RTRIM(KAESTS) AS kaests,
RTRIM(KAASTS) AS kaasts,
RTRIM(KAPLNT) AS kaplnt,
RTRIM(KASCTY) AS kascty,
RTRIM(KASPOV) AS kaspov,
RTRIM(KASCRY) AS kascry,
RTRIM(KAFUT1) AS kafut1,
RTRIM(KAFUT2) AS kafut2,
RTRIM(KAFUT3) AS kafut3,
RTRIM(KAFUT4) AS kafut4,
RTRIM(KAFUT5) AS kafut5,
RTRIM(KAFUT6) AS kafut6,
RTRIM(KAFUT7) AS kafut7,
RTRIM(KAFUT8) AS kafut8,
RTRIM(KAFUT9) AS kafut9,
RTRIM(KAFUT10) AS kafut10,
RTRIM(KAFUT11) AS kafut11,
RTRIM(KAFUT12) AS kafut12,
RTRIM(KAFUT13) AS kafut13,
RTRIM(KAFUT14) AS kafut14,
RTRIM(KAFUT15) AS kafut15,
RTRIM(KAFUT16) AS kafut16,
RTRIM(KAFUT17) AS kafut17,
RTRIM(KAFUT18) AS kafut18,
RTRIM(KAFUT19) AS kafut19,
RTRIM(KAFUT20) AS kafut20,
RTRIM(KAFUT21) AS kafut21,
RTRIM(KAFUT22) AS kafut22,
RTRIM(KAFUT23) AS kafut23,
RTRIM(KAFUT24) AS kafut24,
RTRIM(KAFUT25) AS kafut25,
RTRIM(KAFUT26) AS kafut26,
RTRIM(KAFUT27) AS kafut27,
RTRIM(KAFUT28) AS kafut28,
KAFUT29 AS kafut29,
KAFUT30 AS kafut30,
KAFUT31 AS kafut31,
KAFUT32 AS kafut32,
KAFUT33 AS kafut33,
KAFUT34 AS kafut34,
KAFUT35 AS kafut35,
KAFUT36 AS kafut36,
RTRIM(KAUSR1) AS kausr1,
RTRIM(KAUSR2) AS kausr2,
RTRIM(KAUSR3) AS kausr3,
RTRIM(KAUSR4) AS kausr4,
RTRIM(KAUSR5) AS kausr5,
RTRIM(KAUSR6) AS kausr6,
RTRIM(KAUSR7) AS kausr7,
RTRIM(KAEDIRS) AS kaedirs
FROM LGDAT.POH
-- DB2 for i has no boolean literal, so no `WHERE TRUE` base condition here.
-- POH's "future" fields are really an audit pair: KAFUT12 = Date Created,
-- KAFUT20 = Date Updated, both CHAR(10) ISO 'YYYY-MM-DD', so the compares
-- below are lexical and blanks sort below any watermark.
WHERE ( KAFUT20 >= '{poh_wm}'
OR KAFUT12 >= '{poh_wm}'
-- Open-PO hot set: KACRCM 1-Current / 2-Complete, and it leads most of
-- POH's logicals, so this is a keyed read of ~1,035 rows. Insurance
-- against a status flip that writes no date, the way OCRH's DCSTAT does.
OR KACRCM = '1')