Skip to content

feat(database): add the RD organization columns and the territorial catalog - #612

Open
mrivas00 wants to merge 4 commits into
feat/mati/rd-terminologyfrom
feat/mati/rd-schema-additions
Open

feat(database): add the RD organization columns and the territorial catalog#612
mrivas00 wants to merge 4 commits into
feat/mati/rd-terminologyfrom
feat/mati/rd-schema-additions

Conversation

@mrivas00

@mrivas00 mrivas00 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Why

Two of MMARN's observations need columns that do not exist: a secondary economic activity, and a five-level territorial location. Nothing else in the ten needs schema — the rest is copy or seed data.

They land as one migration, early in the stack, so no later pull request carries one and no two compete for a timestamp. Nothing consumes these columns yet — PRs 7 and 8 do.

PR 3 of 8 · base feat/mati/rd-terminology (PR 2) · the only migration in the stack.

What

Column / table Shape Consumed by
organization_data.secondary_subsector_id BIGINT nullable → country_subsector, RESTRICT PR 7
organization_data.territory_id BIGINT nullable → territory, RESTRICT PR 7
territory new table: name, territory_level enum, self-referencing parent_id PR 7
199 rows in territory the ten planning regions, the 32 provinces and the 157 municipios of Ley 345-22, article 7 PR 7

Plus the matching Zod base schemas and a TerritoryBaseSchema.

Review notes

The migration carries data, which nothing in this repository did before. seed.ts skips entirely once a country exists, so the RD test environment — already populated — would never receive the territorial catalog by seeding. The migration inserts it; seedTerritories (PR 7) stays as the path for a database created without migrations and returns early when the table already holds rows. The branch is terminal and the target deployment is populated, which is what justifies the departure; flagging it because it should be a decision, not a surprise in review.

The insert is guarded on an empty table, not ON CONFLICT. The unique index spans (parent_id, level, name) and Postgres treats NULL parents as distinct, so it does not deduplicate the ten roots. The two child levels resolve their parent by name and use ON CONFLICT DO NOTHING; province names are unique across the country, so the municipal join needs no region.

The catalog is the law's, and it corrected five rows. Article 7 of the Ley Orgánica de Regiones Únicas de Planificación (núm. 345-22, G. O. 11077 del 2 de agosto de 2022) enumerates regions, provinces and municipios in one place. The first draft of this insert followed Decreto 710-04, which that law replaced: Azua sat under Valdesia instead of El Valle, and four rows carried wording the ministry does not use. The municipal level was going to wait on an ONE catalog it turns out not to need. docs/development/rd-territories-sources.md (PR 7) records the source, the ONE's corroborating counts and why the two levels below the municipality are still empty.

Applies to a populated database. Both columns are nullable and territory starts empty, so no existing row is rewritten: organizations get NULL for both new references.

Two decisions worth a look:

  • territory stores one reference per organization, not five columns. Ancestors are derived by walking parent_id. That makes an incoherent combination — a municipality outside the selected province — structurally impossible, at the cost of a recursive traversal when filtering by province. The consumer today is admin reporting; a closure table is the escape hatch if dashboards ever query it per request.
  • Both foreign keys are RESTRICT, not Prisma's default SET NULL. Nulling a deleted node's parent would promote its children to roots, where they would surface in the top-level selector as planning regions; nulling an organization's reference would rewrite a location it declared, which this repository's catalogs never do.

No maintainer, but an ordinary table. No CRUD endpoints and no maintainer screen: administrative boundaries change by law, not by administrative action. It is a normal catalog table, so a deployment that later needs to correct it can add a maintainer additively — endpoints, a screen, and a status column if it wants soft-delete. Those two columns are deliberately not added now: nothing reads them.

secondary_subsector needed both relations to CountrySubsector named (organization_data_subsector / organization_data_secondary_subsector). Relation names do not affect the FK constraint names Prisma generates, so the existing organization_data_subsector_id_fkey is untouched.

Verification

pnpm format && pnpm lint && pnpm type-check green, pnpm test:web 746 passing, full API suite 2,304 passing. prisma validate clean, and prisma generate produces the client the type-check ran against.

The migration has now been applied, twice, both ways.

Against an empty database — the API test harness deploys every migration and runs the whole seed into a fresh Postgres container on each run, so all 226 test files exercise a database this migration built.

Against a populated one, which is what task 4.6 actually asked and the harness cannot cover since it always starts empty: on a throwaway Postgres 18.6, every migration up to 20260819120000 was deployed, two organization_data rows inserted, and then this migration applied. Result:

  • both rows survived, with NULL in secondary_subsector_id and territory_id and every other column untouched;
  • the catalog landed 199 rows — 10 / 32 / 157 — with no province or municipio left parentless;
  • Azua resolves under El Valle (the Ley 345-22 correction), Santiago carries ten municipios, and the Distrito Nacional carries none, as the law has it;
  • re-running the three INSERT statements is a complete no-op, so the empty-table guard on the roots and the two ON CONFLICT DO NOTHING clauses below it both hold.

Tasks 4.6 and 2.4 are closed.

@mrivas00 mrivas00 added priority: high High priority type: feature new user-facing capability or enhancement area: database packages/database (Prisma schema / migrations) area: packages shared packages/* labels Aug 21, 2026
@mrivas00
mrivas00 force-pushed the feat/mati/rd-schema-additions branch 2 times, most recently from a015d35 to fb37631 Compare August 24, 2026 15:10
@mrivas00
mrivas00 force-pushed the feat/mati/rd-terminology branch from b0887b8 to 8821621 Compare August 24, 2026 15:10
@mrivas00 mrivas00 changed the title feat(database): add the RD organization and catalog columns feat(database): add the RD organization and methodology columns Aug 24, 2026
@mrivas00
mrivas00 force-pushed the feat/mati/rd-terminology branch from 8821621 to a02887a Compare August 24, 2026 17:58
@mrivas00
mrivas00 force-pushed the feat/mati/rd-schema-additions branch from fb37631 to 144ca97 Compare August 24, 2026 17:58
@mrivas00 mrivas00 changed the title feat(database): add the RD organization and methodology columns feat(database): add the RD organization columns and the territorial catalog Aug 24, 2026
@mrivas00
mrivas00 force-pushed the feat/mati/rd-terminology branch from a02887a to 781cc21 Compare August 26, 2026 12:12
@mrivas00
mrivas00 force-pushed the feat/mati/rd-schema-additions branch from 6152fb0 to 80c901f Compare August 26, 2026 12:12
@mrivas00
mrivas00 force-pushed the feat/mati/rd-terminology branch from 781cc21 to 23ce2b3 Compare August 26, 2026 12:36
@mrivas00
mrivas00 force-pushed the feat/mati/rd-schema-additions branch from 80c901f to ee08ab5 Compare August 26, 2026 12:36
…atalog

One additive migration for the whole RD stack, so no later pull request
competes for a timestamp:

- secondary_subsector_id on organization_data: a second reference into
  the same activity catalog, allowed to sit in a different sector.
- territory: the administrative hierarchy as one self-referencing table,
  plus territory_id on organization_data pointing at the most specific
  node the registrant knows. Ancestors are derived by walking parent_id,
  never stored on the organization, which makes an incoherent
  combination (a municipality outside its province) impossible to
  represent.

Both columns are nullable and the new table starts empty, so the migration
applies to a populated database without rewriting a row.

It also loads the ten planning regions and the 32 provinces. seed.ts skips
entirely once a country exists, so an already-populated deployment — the RD
test environment — would never receive the catalog by seeding. The insert is
guarded on an empty table rather than ON CONFLICT, because the unique index
spans (parent_id, level, name) and Postgres treats NULL parents as distinct,
so it would not deduplicate the roots.

Both foreign keys are RESTRICT rather than Prisma's default SET NULL: nulling
a deleted node's parent would promote its children to roots, where they would
surface in the top-level selector as planning regions, and nulling an
organization's reference would rewrite a location it declared.

The hierarchy gets no CRUD endpoints and no maintainer screen — boundaries
change by law, not by administrative action — but it is an ordinary catalog
table, so adding one later is additive.
Extends the base Zod schemas with secondarySubsectorId and territoryId, adds
a TerritoryBaseSchema for the hierarchy, and re-exports the TerritoryLevel
enum.
The insert followed the regionalization of Decreto 710-04, which the Ley
Organica de Regiones Unicas de Planificacion (num. 345-22, G. O. 11077 del 2
de agosto de 2022) replaced. Azua moves from Valdesia to El Valle -- which
takes Valdesia from four provinces to three and El Valle from two to three
-- and four rows take the wording the ministry validates against: Baoruco
becomes Bahoruco, Monte Cristi becomes Montecristi, Higuamo becomes Higuamo
with the dieresis, and `Ozama o Metropolitana` becomes Ozama.

It also gains the 157 municipios. Article 7 of the same law lists them under
each province, so the level that was deferred waiting on the ONE catalog
turns out to need nothing external. Their join needs no region: province
names are unique across the country.

The migration has not been applied to the RD environment, so this edits the
insert in place rather than adding a corrective one.
@mrivas00
mrivas00 force-pushed the feat/mati/rd-terminology branch from 23ce2b3 to 56e56e4 Compare August 26, 2026 20:21
@mrivas00
mrivas00 force-pushed the feat/mati/rd-schema-additions branch from ee08ab5 to 87aaa3f Compare August 26, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: database packages/database (Prisma schema / migrations) area: packages shared packages/* priority: high High priority type: feature new user-facing capability or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant