diff --git a/lib/sql_generator.js b/lib/sql_generator.js index c091e01..f4c3e48 100644 --- a/lib/sql_generator.js +++ b/lib/sql_generator.js @@ -84,7 +84,7 @@ ilog AS ( WHERE ${q(dateCol)} BETWEEN '{{date_from}}' AND '{{date_to}}' RETURNING * ) -SELECT count(*) AS rows_affected FROM ins`.trim(); +SELECT * FROM ins`.trim(); } function buildScale() { @@ -124,7 +124,7 @@ ilog AS ( FROM base RETURNING * ) -SELECT count(*) AS rows_affected FROM ins`.trim(); +SELECT * FROM ins`.trim(); } function buildRecode() { @@ -146,16 +146,16 @@ ilog AS ( SELECT ${dimsJoined}, ${q(dateCol)}, ${effectiveValue ? `-${q(effectiveValue)}` : '0'}, ${effectiveUnits ? `-${q(effectiveUnits)}` : '0'}, 'recode', (SELECT id FROM ilog), '{{pf_user}}', now() FROM src - RETURNING id + RETURNING * ) ,ins AS ( INSERT INTO {{fc_table}} (${insertCols}) SELECT {{set_clause}}, ${q(dateCol)}, ${effectiveValue ? q(effectiveValue) : '0'}, ${effectiveUnits ? q(effectiveUnits) : '0'}, 'recode', (SELECT id FROM ilog), '{{pf_user}}', now() FROM src - RETURNING id + RETURNING * ) -SELECT (SELECT count(*) FROM neg) + (SELECT count(*) FROM ins) AS rows_affected`.trim(); +SELECT * FROM neg UNION ALL SELECT * FROM ins`.trim(); } function buildClone() { @@ -179,7 +179,7 @@ ilog AS ( {{exclude_clause}} RETURNING * ) -SELECT count(*) AS rows_affected FROM ins`.trim(); +SELECT * FROM ins`.trim(); } function buildUndo() { diff --git a/public/app.js b/public/app.js index 76d7dcf..f2a75a9 100644 --- a/public/app.js +++ b/public/app.js @@ -451,6 +451,17 @@ function openForecast() { /* ============================================================ FORECAST VIEW — data loading ============================================================ */ +function parseNumericRows(rows) { + const numericCols = state.colMeta + .filter(c => c.role === 'value' || c.role === 'units') + .map(c => c.cname); + return rows.map(row => { + const r = { ...row }; + numericCols.forEach(col => { if (r[col] != null) r[col] = parseFloat(r[col]); }); + return r; + }); +} + async function loadForecastData() { if (!state.version) return; document.getElementById('forecast-label').textContent = @@ -462,14 +473,7 @@ async function loadForecastData() { } showStatus('Loading forecast data...', 'info'); const rawData = await api('GET', `/versions/${state.version.id}/data`); - const numericCols = state.colMeta - .filter(c => c.role === 'value' || c.role === 'units') - .map(c => c.cname); - const data = rawData.map(row => { - const r = { ...row }; - numericCols.forEach(col => { if (r[col] != null) r[col] = parseFloat(r[col]); }); - return r; - }); + const data = parseNumericRows(rawData); initPivotGrid(data); showStatus(`Loaded ${data.length.toLocaleString()} rows`, 'success'); } catch (err) { @@ -695,7 +699,7 @@ async function submitScale() { document.getElementById('scale-value-incr').value = ''; document.getElementById('scale-units-incr').value = ''; document.getElementById('scale-note').value = ''; - await loadForecastData(); + state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) }); } catch (err) { showStatus(err.message, 'error'); } @@ -721,7 +725,7 @@ async function submitRecode() { }); showStatus(`Recode applied — ${result.rows_affected} rows inserted`, 'success'); document.querySelectorAll('#recode-fields input[data-col], #recode-fields select[data-col]').forEach(i => { i.value = ''; }); - await loadForecastData(); + state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) }); } catch (err) { showStatus(err.message, 'error'); } @@ -750,7 +754,7 @@ async function submitClone() { showStatus(`Clone applied — ${result.rows_affected} rows inserted`, 'success'); document.querySelectorAll('#clone-fields input[data-col], #clone-fields select[data-col]').forEach(i => { i.value = ''; }); document.getElementById('clone-scale').value = '1'; - await loadForecastData(); + state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) }); } catch (err) { showStatus(err.message, 'error'); } diff --git a/routes/operations.js b/routes/operations.js index f3baba0..1418193 100644 --- a/routes/operations.js +++ b/routes/operations.js @@ -99,7 +99,7 @@ module.exports = function(pool) { }); const result = await runSQL(sql); - res.json(result.rows[0]); + res.json({ rows_affected: result.rows.length }); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); @@ -127,7 +127,7 @@ module.exports = function(pool) { }); const result = await runSQL(sql); - res.json(result.rows[0]); + res.json({ rows: result.rows, rows_affected: result.rows.length }); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); @@ -183,7 +183,7 @@ module.exports = function(pool) { }); const result = await runSQL(sql); - res.json(result.rows[0]); + res.json({ rows: result.rows, rows_affected: result.rows.length }); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); @@ -218,7 +218,7 @@ module.exports = function(pool) { }); const result = await runSQL(sql); - res.json(result.rows[0]); + res.json({ rows: result.rows, rows_affected: result.rows.length }); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); @@ -255,7 +255,7 @@ module.exports = function(pool) { }); const result = await runSQL(sql); - res.json(result.rows[0]); + res.json({ rows: result.rows, rows_affected: result.rows.length }); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message });