Normalize CDP field name before CRM↔CDP field-property matching
Jira Story: TF-3515 · Task: TF-3486 (linked via "Work item split" — direction currently backwards, pending fix; see bucket README) · Epic: TF-3477
Overview
Crm::BackfillCdpFieldPropertiesService decides whether a CRM field already has a matching CDP
field property by looking up the CRM-derived expected name (field.name, translated through
CRM_TO_CDP_NAME_MAP where applicable) against CDP's raw name value. CRM's own name is
always normalized via snake_case.chomp('_') before it is saved (additional_field.rb:101), but
CDP's name may not be stored in that same normalized form (different casing, spaces, trailing
underscores) — so an exact-string lookup can miss even when the two systems refer to the same
field, causing the backfill service to create a duplicate CDP field property instead of
linking to the existing one.
Fix: normalize CDP's name with the identical snake_case.chomp('_') transformation before
comparing (backfill_cdp_field_properties_service.rb:85-94, fetch_cdp_fields), rather than
introducing a new matching key or changing what CRM_TO_CDP_NAME_MAP is for. The CRM-side lookup
key (field.name / CRM_TO_CDP_NAME_MAP) is untouched — those values are already normalized
constants; only the CDP side needed fixing. Outbound payload construction (build_cdp_payload,
build_update_payload_for_field) is unaffected.
Scope Changes
- Backend —
Crm::BackfillCdpFieldPropertiesServicefield-name normalization on the CDP side of the match.
8. User Stories + Acceptance Criteria
| User Story | Importance | Mockup / Technical Notes | Acceptance Criteria |
|---|---|---|---|
| [FIELDMATCH-S01] — CRM/CDP field matching survives name-formatting differences As the platform, I want a CRM field to correctly match its existing CDP field property even when the two systems' stored name values differ only in casing/spacing/trailing characters, so that the backfill never creates a spurious duplicate field property. | Must Have | fetch_cdp_fields indexes CDP fields by a normalized key (name.to_s.snake_case.chomp('_')), matching how CRM already normalizes its own name on save. A select guard on non-blank name avoids collapsing multiple blank-name CDP fields onto one key. | — Happy Path — • AC-1: Given a CRM field whose derived CDP name matches an existing CDP field property's name only after normalization (e.g. CDP stores "Notes"/"notes_" where CRM expects notes), when the backfill service runs, then it links to the existing field property rather than creating a duplicate.• AC-2: Given a CRM field whose derived CDP name is already an exact match (no normalization needed), when the backfill service runs, then linking behavior is unchanged from before this fix. — Guard-rail — • NEG-1: Given a CDP field with a blank name, when fetch_cdp_fields builds its lookup index, then that field is not indexed/matched against any CRM field (guards against collapsing multiple blank-name fields onto the same key). |
Verification
Shipped and code-corroborated: merged in qontak.com (PR #12102, 1d0e99e38d) per the
2026-07-14 code-evidence.mjs check.