d537144ab1
## Summary When a custom object is renamed after creation, the relation fields on `noteTarget`, `taskTarget`, `attachment`, and `timelineActivity` keep their old names but point to the renamed object. For example: - User creates custom object `solution` - System creates field `solution` on NoteTarget → pointing to Solution - User renames object from `solution` to `productCatalog` - Field name stays as `solution` but points to `productCatalog` - Migration runs: `solution` → `targetSolution` - Now `targetSolution` points to `productCatalog` ❌ This causes the morph name computation to fail: - `getMorphNameFromMorphFieldMetadataName("targetSolution", "productCatalog")` - Tries to remove `ProductCatalog` from `targetSolution` → no match - Name stays as `targetSolution` instead of becoming `target` - Frontend computes: `targetSolution` + `Company` = `targetSolutionCompany` 💥 ## The Fix This command finds and fixes all such mismatches by: 1. Finding MORPH_RELATION fields where `field.name != target${capitalize(targetObject.nameSingular)}` 2. Renaming the column in the workspace schema 3. Updating the field metadata (name + joinColumnName in settings) 4. Invalidating the cache ## Usage **Dry run (to see what would be fixed):** ```bash npx nx run twenty-server:command upgrade:1-17:fix-morph-relation-field-names -- --workspace-id <id> --dry-run ``` **Fix a specific workspace:** ```bash npx nx run twenty-server:command upgrade:1-17:fix-morph-relation-field-names -- --workspace-id <id> ``` **Fix all workspaces:** ```bash npx nx run twenty-server:command upgrade:1-17:fix-morph-relation-field-names ``` ## SQL to find affected workspaces ```sql SELECT fm."workspaceId", COUNT(*) AS mismatched_fields FROM core."fieldMetadata" fm JOIN core."objectMetadata" target_om ON fm."relationTargetObjectMetadataId" = target_om.id JOIN core."objectMetadata" source_om ON fm."objectMetadataId" = source_om.id WHERE fm.type = 'MORPH_RELATION' AND fm.name LIKE 'target%' AND source_om."nameSingular" IN ('noteTarget', 'taskTarget', 'attachment', 'timelineActivity') GROUP BY fm."workspaceId" HAVING COUNT(*) FILTER ( WHERE fm.name != 'target' || initcap(replace(target_om."nameSingular", '_', '')) ) > 0; ```