Put the territory column in the Setup editor
The flag was enforced everywhere and settable nowhere but psql, so the one piece of configuration a second account depends on was invisible. A radio rather than a checkbox, because exactly one column per source can be the territory -- the control should say so rather than leaving it to an error on save. Clicking the chosen one again clears it, which a radio has no other way to express. The save still refuses two, since the UI is not the only caller, and two would mean whichever a .find() reached first -- the trap is_key already fell into. Restricted to dimension columns: a territory is something rows are divided by, and scoping on a date or a measure is not a thing to offer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
712d114dc5
commit
e60dd3ad96
@ -87,13 +87,23 @@ module.exports = function(pool) {
|
||||
if (!Array.isArray(cols)) {
|
||||
return res.status(400).json({ error: 'body must be an array' });
|
||||
}
|
||||
// Exactly one per source: the scope is a single IN list against a single
|
||||
// column, and two flagged would silently mean whichever one a .find()
|
||||
// reached first -- the trap is_key already fell into (see CLAUDE.md).
|
||||
const territoryCols = cols.filter(c => c.is_territory).map(c => c.cname);
|
||||
if (territoryCols.length > 1) {
|
||||
return res.status(400).json({
|
||||
error: `Only one column can be the territory. Flagged: ${territoryCols.join(', ')}`
|
||||
});
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
for (const col of cols) {
|
||||
await client.query(`
|
||||
INSERT INTO pf.col_meta (source_id, cname, label, role, is_key, dim_group, dim_period_col, in_grain, opos)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
INSERT INTO pf.col_meta (source_id, cname, label, role, is_key, dim_group, dim_period_col, in_grain, is_territory, opos)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
ON CONFLICT (source_id, cname) DO UPDATE SET
|
||||
label = EXCLUDED.label,
|
||||
role = EXCLUDED.role,
|
||||
@ -101,6 +111,7 @@ module.exports = function(pool) {
|
||||
dim_group = EXCLUDED.dim_group,
|
||||
dim_period_col = EXCLUDED.dim_period_col,
|
||||
in_grain = EXCLUDED.in_grain,
|
||||
is_territory = EXCLUDED.is_territory,
|
||||
opos = EXCLUDED.opos
|
||||
`, [
|
||||
sourceId,
|
||||
@ -111,6 +122,7 @@ module.exports = function(pool) {
|
||||
col.dim_group || null,
|
||||
col.dim_period_col || null,
|
||||
col.in_grain || false,
|
||||
col.is_territory || false,
|
||||
col.opos || null
|
||||
]);
|
||||
}
|
||||
|
||||
@ -354,6 +354,7 @@ export default function Setup({ refreshSources }) {
|
||||
<th className="px-3 py-1.5 font-medium">role</th>
|
||||
<th className="px-3 py-1.5 font-medium text-center">key</th>
|
||||
<th className="px-3 py-1.5 font-medium text-center" title="Include this column in the display grain — the load is pre-aggregated to the flagged columns">grain</th>
|
||||
<th className="px-3 py-1.5 font-medium text-center" title="The column an account's territory is expressed in. Accounts see and change only rows whose value here is on their list. One per source.">territory</th>
|
||||
<th className="px-3 py-1.5 font-medium">group</th>
|
||||
<th className="px-3 py-1.5 font-medium">period col</th>
|
||||
<th className="px-3 py-1.5 font-medium">label</th>
|
||||
@ -390,6 +391,25 @@ export default function Setup({ refreshSources }) {
|
||||
className="cursor-pointer disabled:opacity-20"
|
||||
/>
|
||||
</td>
|
||||
{/* Radio, not a checkbox: exactly one column per source,
|
||||
and the shape of the control should say so rather than
|
||||
leaving it to a save-time error. */}
|
||||
<td className="px-3 py-1.5 text-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="pf-territory-col"
|
||||
checked={!!col.is_territory}
|
||||
onChange={() => {}}
|
||||
onClick={() => setEditedCols(prev => {
|
||||
// clicking the chosen one again clears it, since a
|
||||
// radio otherwise has no way back to "no territory"
|
||||
const already = !!prev[i].is_territory
|
||||
return prev.map((c, x) => ({ ...c, is_territory: !already && x === i }))
|
||||
})}
|
||||
disabled={col.role !== 'dimension'}
|
||||
className="cursor-pointer disabled:opacity-20"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-1.5">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user