From 81c2324220d2df31ac4a62f4fc7bf2dac68360c6 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Wed, 16 Sep 2026 17:42:22 -0400 Subject: [PATCH] Expose load segments and adjustment notes as separate pivot columns The label on a baseline or reference load ("Open Orders", "Prior Year") lived only on the pf.log row; the data stream was a straight dump of the forecast table, so Perspective saw pf_logid and never the name. The stream now joins pf.log and emits two columns rather than one, because commingling them makes neither useful: pf_segment names the load a row came from, and is '(adjustment)' for everything else; pf_note carries the free text on scale/recode/clone and is null on loads. The operation routes stamp the same two fields on the rows they push back incrementally. The tag column those labels belong in is declared in 01_schema.sql but predates some installs, so the backfill seeding it from note joins the ALTER already there. Adjustment notes are free text, not labels, and are left alone. Co-Authored-By: Claude Opus 5 (1M context) --- routes/operations.js | 20 ++++++++++++++++---- setup_sql/01_schema.sql | 11 +++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/routes/operations.js b/routes/operations.js index fc88b58..a2b0909 100644 --- a/routes/operations.js +++ b/routes/operations.js @@ -267,7 +267,16 @@ module.exports = function(pool) { await client.query('BEGIN'); await client.query(` DECLARE pf_cur CURSOR FOR - SELECT * FROM ${tbl} + SELECT t.* + ,CASE WHEN l.operation IN ('baseline','reference') + THEN COALESCE(NULLIF(l.tag, ''), NULLIF(l.note, ''), '(unlabeled load)') + ELSE '(adjustment)' END AS pf_segment + ,CASE WHEN l.operation IN ('baseline','reference') + THEN NULL + ELSE COALESCE(NULLIF(l.tag, ''), NULLIF(l.note, '')) END AS pf_note + FROM ${tbl} t + LEFT JOIN pf.log l + ON l.id = t.pf_logid `); // Accumulate into column arrays (not row objects) to avoid allocating one JS @@ -550,7 +559,8 @@ module.exports = function(pool) { await client.query('COMMIT'); committed = true; - const rows = allRows.map(r => ({ ...r, pf_note: note || null, pf_op: 'scale' })); + const opLabel = (req.body.tag || '').trim() || note || null; + const rows = allRows.map(r => ({ ...r, pf_segment: '(adjustment)', pf_note: opLabel, pf_op: 'scale' })); res.json({ rows, rows_affected: rows.length, @@ -609,7 +619,8 @@ module.exports = function(pool) { } await client.query('COMMIT'); committed = true; - const rows = allRows.map(r => ({ ...r, pf_note: note || null, pf_op: 'recode' })); + const opLabel = (req.body.tag || '').trim() || note || null; + const rows = allRows.map(r => ({ ...r, pf_segment: '(adjustment)', pf_note: opLabel, pf_op: 'recode' })); res.json({ rows, rows_affected: rows.length, slices_applied: units.length }); } finally { if (!committed) try { await client.query('ROLLBACK'); } catch {} @@ -665,7 +676,8 @@ module.exports = function(pool) { } await client.query('COMMIT'); committed = true; - const rows = allRows.map(r => ({ ...r, pf_note: note || null, pf_op: 'clone' })); + const opLabel = (req.body.tag || '').trim() || note || null; + const rows = allRows.map(r => ({ ...r, pf_segment: '(adjustment)', pf_note: opLabel, pf_op: 'clone' })); res.json({ rows, rows_affected: rows.length, slices_applied: units.length }); } finally { if (!committed) try { await client.query('ROLLBACK'); } catch {} diff --git a/setup_sql/01_schema.sql b/setup_sql/01_schema.sql index cb53514..3a26b8f 100644 --- a/setup_sql/01_schema.sql +++ b/setup_sql/01_schema.sql @@ -64,6 +64,17 @@ CREATE TABLE IF NOT EXISTS pf.log ( ALTER TABLE pf.log ADD COLUMN IF NOT EXISTS tag text; CREATE INDEX IF NOT EXISTS log_tag_idx ON pf.log (tag) WHERE tag IS NOT NULL; +-- seed tags for loads that predate the column: a baseline/reference note is the +-- segment's name ('Open Orders', 'Prior Year'), which is exactly what tag holds. +-- Adjustment notes are free text, not labels, so they are left alone. +UPDATE pf.log +SET tag = note +WHERE TRUE + AND tag IS NULL + AND note IS NOT NULL + AND note <> '' + AND operation IN ('baseline', 'reference'); + -- generated operation SQL per source, stored after col_meta is configured CREATE TABLE IF NOT EXISTS pf.sql ( id serial PRIMARY KEY,