feat(contacts): backfill existing company industries to canonical set (PMS-602) #413

Merged
longjacksonle merged 4 commits from feat/PMS-602-backfill-company-industries into main 2026-06-30 19:30:42 +02:00

PMS-602 (follow-up to PMS-601)

PMS-582 standardized NEW company Industry entries; rows created earlier keep whatever free text was typed ("IT", "I.T.", "Information Technology"). This adds a one-time, re-runnable backfill that normalizes existing values to the canonical PMS-601 set so reporting/filtering on industry is clean across all data.

What it does

mokosh-bootstrap normalize-company-industries (cross-tenant operator task, privileged DATABASE_URL):

  • Rewrites companies.industry whose normalized form (lower(btrim(...))) matches a curated variant via a variant -> canonical map (e.g. it / i.t. / infotech / technology -> "Information Technology"; health care / medical -> "Healthcare"; etc.).
  • Leaves unmapped values untouched and reports them per tenant for manual review.
  • Idempotent / no-op-safe: skips rows already at the canonical value (a second run changes nothing) and also normalizes case-only differences (each canonical's lower-cased form is a variant key).

Decisions the ticket asked for

  • Runs against the static canonical map (whose targets are exactly the PMS-601 seed), not gated per-row on each tenant's lookup. Free text is still allowed, so a normalized value lands on a name the lookup suggests.
  • One cross-tenant pass via the BYPASSRLS pool, mirroring the qa-seed tooling.

Commits

  1. normalize_company_industries backfill function + variant map (contacts module).
  2. DB-free unit tests for map integrity (unique/normalized keys; every canonical has a self-variant).
  3. normalize-company-industries bootstrap subcommand + help.

Verified on the dev DB

Inserted messy rows; the command reported: normalized 2 ... across 1 tenant (I.T.->Information Technology, health care->Healthcare), left Fintech Widgets unmapped+reported, and a re-run was normalized 0 (idempotent). Test rows cleaned up. Compile + fmt + clippy clean; unit tests pass.

Operating

Run once per environment after deploy: mokosh-bootstrap normalize-company-industries (reads DATABASE_URL). Review any reported unmapped values and either add a mapping or fix them from Settings > Company Industries.

🤖 Generated with Claude Code

## PMS-602 (follow-up to PMS-601) PMS-582 standardized NEW company Industry entries; rows created earlier keep whatever free text was typed ("IT", "I.T.", "Information Technology"). This adds a one-time, re-runnable backfill that normalizes existing values to the canonical PMS-601 set so reporting/filtering on industry is clean across all data. ## What it does `mokosh-bootstrap normalize-company-industries` (cross-tenant operator task, privileged DATABASE_URL): - Rewrites `companies.industry` whose normalized form (`lower(btrim(...))`) matches a curated variant via a `variant -> canonical` map (e.g. it / i.t. / infotech / technology -> "Information Technology"; health care / medical -> "Healthcare"; etc.). - Leaves unmapped values untouched and reports them per tenant for manual review. - Idempotent / no-op-safe: skips rows already at the canonical value (a second run changes nothing) and also normalizes case-only differences (each canonical's lower-cased form is a variant key). ## Decisions the ticket asked for - Runs against the static canonical map (whose targets are exactly the PMS-601 seed), not gated per-row on each tenant's lookup. Free text is still allowed, so a normalized value lands on a name the lookup suggests. - One cross-tenant pass via the BYPASSRLS pool, mirroring the qa-seed tooling. ## Commits 1. `normalize_company_industries` backfill function + variant map (contacts module). 2. DB-free unit tests for map integrity (unique/normalized keys; every canonical has a self-variant). 3. `normalize-company-industries` bootstrap subcommand + help. ## Verified on the dev DB Inserted messy rows; the command reported: `normalized 2 ... across 1 tenant` (`I.T.`->`Information Technology`, `health care`->`Healthcare`), left `Fintech Widgets` unmapped+reported, and a re-run was `normalized 0` (idempotent). Test rows cleaned up. Compile + fmt + clippy clean; unit tests pass. ## Operating Run once per environment after deploy: `mokosh-bootstrap normalize-company-industries` (reads DATABASE_URL). Review any reported unmapped values and either add a mapping or fix them from Settings > Company Industries. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add normalize_company_industries(db): a cross-tenant, re-runnable pass that rewrites existing free-text companies.industry values to the canonical PMS-601 set using a curated variant->canonical map (e.g. IT / I.T. / Information Technology -> "Information Technology"). Idempotent (skips rows already canonical; also normalizes casing), leaves unmapped values untouched, and returns a report of per-tenant change counts plus the unmapped values for manual review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DB-free unit tests: variant keys are lower-cased, trimmed, and unique; every canonical has its own lower-cased self-variant so case-only differences normalize.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(bootstrap): normalize-company-industries subcommand (PMS-602)
Some checks failed
Check / fmt + clippy + build + tests (pull_request) Failing after 27s
E2E / Playwright against staging (pull_request) Successful in 1m34s
Integration / integration tests (pull_request) Has been cancelled
1969650833
mokosh-bootstrap normalize-company-industries runs the cross-tenant industry backfill against DATABASE_URL and prints the report (per-tenant change counts + unmapped values for review). Re-runnable; no --tenant needed. Added to the help text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
style(contacts): wrap long macro calls to satisfy CI rustfmt (PMS-602)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 1m10s
Check / fmt + clippy + build + tests (pull_request) Successful in 2m56s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
Integration / integration tests (pull_request) Successful in 10m8s
370160c73d
CI's rustfmt wraps these writeln!/assert_eq! calls (arg list exceeds fn_call_width); my local toolchain was an older rustfmt that left them single-line, failing the Check / formatting gate on #413. Reformatted with the current rustfmt to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/PMS-602-backfill-company-industries 2026-06-30 19:30:43 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!413
No description provided.