You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#626 makes lead conversion match accounts on a normalized company name, so the conversion path no longer creates near-duplicates (Acme Corp / ACME Corp). It deliberately did not add a unique index on crm_account.name_normalized — the reasoning is in src/objects/account.object.ts, but the short version is that account-name uniqueness already lives per-tenant on name (#625), and a unique normalized column would subsume that constraint and hard-fail writes that succeed today.
So every non-conversion path can still create two accounts whose names differ only in case or spacing: the Console form, the record API, a CSV import, a future seed row. Nothing detects it and nothing merges it.
Two concrete consequences
No detection. A near-duplicate pair sits in the account list indefinitely. Contrast crm_lead, which already has the soft shape for the same class of problem: lead_duplicate_check flags a re-captured email as duplicate_status: 'suspected' and the suspected_duplicates view is the review queue. crm_account has neither half.
Non-deterministic reuse. When two accounts do share a normalized name, lead_conversion's find_account node reuses an arbitrary one — the built-in get_record executor accepts filter / fields / limit and has no sort option (measured on 17.0.0-rc.1), so there is no "oldest wins" to express. Reusing one of N is still better than creating the N+1th, which is what happened before Normalize account matching in lead conversion (split out of #598 scope 2) #626, but it is not a defined outcome.
Make name_normalized unique per tenant and retire the unique on name. Strictly cleaner constraint set (normalized-unique implies name-unique), but it re-decides crm_account.name uniqueness is platform-wide, not per-tenant — two orgs cannot both have an "Acme Corp" #625, hard-fails imports, and reports the violation against a hidden column. Note the measured upgrade hazard: create_indexfails on any deployment already holding both spellings, so this route needs a merge pass first on anything but a fresh install.
A merge action (reparent contacts/opportunities/cases, keep the survivor). Largest, and the only one that helps a database that already has the pairs.
Worth knowing before picking
The reason #626 could defer this at all is the maintainer's statement that this repo's deployment shape is fresh installs only. That premise is what bounds the create_index hazard and what makes "flow-level dedupe is enough" defensible today. If it ever stops holding, re-read this issue together with the index decision in account.object.ts and docs/MAINTENANCE.md §3.3 — both were written as conditional, not universal.
Found while implementing #626 (PR #654). Filed unassigned, out of scope there.
What #626 fixed, and what it left
#626 makes lead conversion match accounts on a normalized company name, so the conversion path no longer creates near-duplicates (
Acme Corp/ACME Corp). It deliberately did not add a unique index oncrm_account.name_normalized— the reasoning is insrc/objects/account.object.ts, but the short version is that account-name uniqueness already lives per-tenant onname(#625), and a unique normalized column would subsume that constraint and hard-fail writes that succeed today.So every non-conversion path can still create two accounts whose names differ only in case or spacing: the Console form, the record API, a CSV import, a future seed row. Nothing detects it and nothing merges it.
Two concrete consequences
crm_lead, which already has the soft shape for the same class of problem:lead_duplicate_checkflags a re-captured email asduplicate_status: 'suspected'and thesuspected_duplicatesview is the review queue.crm_accounthas neither half.lead_conversion'sfind_accountnode reuses an arbitrary one — the built-inget_recordexecutor acceptsfilter/fields/limitand has nosortoption (measured on 17.0.0-rc.1), so there is no "oldest wins" to express. Reusing one of N is still better than creating the N+1th, which is what happened before Normalize account matching in lead conversion (split out of #598 scope 2) #626, but it is not a defined outcome.Possible shapes (not a recommendation)
beforeInserthook oncrm_accountthat flags a colliding normalized name as suspected, plus a review view. Soft, consistent with Duplicate management: soft lead dedupe instead of the hard unique email constraint #598's doctrine, no write ever refused.name_normalizedunique per tenant and retire theuniqueonname. Strictly cleaner constraint set (normalized-unique implies name-unique), but it re-decides crm_account.name uniqueness is platform-wide, not per-tenant — two orgs cannot both have an "Acme Corp" #625, hard-fails imports, and reports the violation against a hidden column. Note the measured upgrade hazard:create_indexfails on any deployment already holding both spellings, so this route needs a merge pass first on anything but a fresh install.Worth knowing before picking
The reason #626 could defer this at all is the maintainer's statement that this repo's deployment shape is fresh installs only. That premise is what bounds the
create_indexhazard and what makes "flow-level dedupe is enough" defensible today. If it ever stops holding, re-read this issue together with the index decision inaccount.object.tsanddocs/MAINTENANCE.md§3.3 — both were written as conditional, not universal.