docs(contacts): state the email rule as per-organization, not per-account (#648) - #666
Merged
Merged
Conversation
…ount
`content/docs/sales/contacts.mdx` and its two translations promised that "an
email address must be unique within the same account" and that "the same person
can appear under multiple companies (a board member, for example)". Both halves
were wrong, and the second described the exact write the product refuses.
What ships, on two independent layers:
- `crm_contact.email` declares FIELD-level `unique: true`, which since
framework#3696 materializes as the tenant composite `(organization_id, email)`
— one address per ORGANIZATION, spanning every account in it. The object
deliberately declares no table-level index on `email`: a declared index is
taken verbatim (platform-wide) and would leave the composite unreachable
(framework#3991 `unique/double-declaration`).
- `contact_integrity` (`src/objects/contact.hook.ts`) lowercases the address and
looks it up with NO account scope, throwing `Another contact (…) with email …
already exists.` before the database index can. `test/hooks-runtime-sales.test.ts`
already carries the behavioural proof ("rejects a duplicate email GLOBALLY,
not just within one account").
So a reader following the board-member example got an error the documentation
said they would not get. The three rule lines now read like the `crm_account`
name rule corrected in #625 / PR #646: unique within your organization, another
organization on the same platform may hold its own contact at that address, the
address is normalised to lower case before it is stored and compared, and a
person you deal with at two companies needs a different address on each record.
`test/docs-contact-email-uniqueness.test.ts` pins both sides so the prose cannot
drift back: the rule line per language (organization scope present, per-account
claim and board-member example absent) plus the metadata and hook behaviour the
prose describes. Reverting any one of the three docs files turns 4 of its 15
assertions red.
Documentation only — no metadata, hook or constraint changed. Whether "one
person, two companies" SHOULD be supported is a product decision and a schema
change; it is deliberately out of scope here.
Fixes #648
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvtsew6XgsSjxVa59HRPRK
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #648
问题
content/docs/sales/contacts.mdx:62(及两份中文翻译)把联系人邮箱的唯一性规则写成:两句话都是错的,而且第二句描述的正是产品明确拒绝的写入 —— 读者照着文档去建"同一个人挂在两家公司下"的两条联系人,只会拿到
Another contact (…) with email … already exists.。当前实际生效的规则(已在 origin/main @ 5a11631,17.0.0-rc.2 上重新核对)
两层互相独立的约束,作用域都是组织而不是客户:
src/objects/contact.object.ts在字段上声明unique: true。自 framework#3696 起,字段级 unique 会落成租户复合唯一索引(organization_id, email)—— 一个组织内全局唯一,跨越该组织下的所有客户。对象里刻意没有表级indexes上的email条目:表级声明会被逐字materialize成平台级索引,反而让复合约束不可达(framework#3991unique/double-declaration)。src/objects/contact.hook.ts的contact_integrity先把邮箱转小写,再以不带客户作用域的查询查重并抛错,抢在数据库索引之前给出可读错误。test/hooks-runtime-sales.test.ts里那条名字就叫 "rejects a duplicate email GLOBALLY, not just within one account" 的用例记录了原因:按客户作用域查重会让跨客户重复溜过友好检查,然后在转换过程中撞上数据库全局唯一索引。改动
三份
contacts*.mdx的规则行改成实际生效的表述,措辞对齐 #625 / PR #646 修正crm_account名称规则时用的模板:组织内唯一;同一平台上的其他组织可以有自己使用该地址的联系人;邮箱在存储与比较前统一转小写,所以Ada@Example.com与ada@example.com视为同一个地址;同一个人同时在两家公司与你往来时,两条联系人记录需要各自不同的邮箱。董事会成员那个例子被删除 —— 文档里不再有任何描述"会被拒绝的写入"的示例。新增
test/docs-contact-email-uniqueness.test.ts把两侧都钉住,防止措辞漂回去:Contact.fields.email.unique === true、indexes里没有email条目、以及真实跑一遍contact_integrity(accA已有ada@example.com,往accB插Ada@Example.com被拒)。任意还原一份 docs 文件,该文件 15 条断言里有 4 条转红。
仅文档改动 —— 没有动任何 metadata、hook 或约束。"一个人、两家公司"到底应不应该被支持,是产品决策 + schema 变更(邮箱约束得挪位置),按 issue 的说明刻意不在本 PR 范围内。
验证
反向验证(临时 stash 掉
contacts.mdx后跑新测试):Changeset:
.changeset/contact-email-unique-per-org-docs.md(patch)。未触碰content/docs/releases/。🤖 Generated with Claude Code
https://claude.ai/code/session_01Rvtsew6XgsSjxVa59HRPRK
Generated by Claude Code