Skip to content

docs(sales): 按对象/流程/语言包重写联系人退订、线索评分与 zh 版 presented 状态名 (#765) - #794

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-765-sales-docs-ghost-fields
Aug 5, 2026
Merged

yinlianghui merged 2 commits into
mainfrom
claude/issue-765-sales-docs-ghost-fields

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #765

三处 sales 文档描述了产品里不存在的表面。以 src/objects/*.object.ts、src/flows/*.flow.ts、src/objects/lead.hook.ts、src/translations/zh-CN.ts 为唯一事实来源重写,纯文档改动,不动 src/**,不为散文加钉子测试(#765 明确要求)。

前提复核(基线已过期,逐条在 cdbd863 上重核)

立单基线是 c55aaf5,leads.mdx 当天已被 #768、#774 改过。逐条结论:

issue 断言 复核结果
§1 传真字段/传真退订不存在 成立。全仓 grep fax 只命中 contacts.mdx 三行文档,src/** 零命中
§1「Do Not Call 阻止记录通话任务」 成立。crm_contact.do_not_call 只被对象声明、表单区块(contact.view.ts:97)和四个语言包引用,无 hook / flow / validation 读它
§1「仅管理员可取消」 成立。src/profiles/ 的四处 fields: 字段级权限里没有这两个字段
§2 Qualification 行的 score / budget / timeline 成立。lead.object.ts 只有 rating: Field.rating(5)(:200,label 是 Lead Score),无 score / budget / timeline
§2 评分是 0-100 且创建/更新都重算 成立。lead.hook.ts:130-149 的 computeRating 加权后 Math.round 到 1-5 整星,且只在 beforeInsert 且 typeof input.rating !== 'number' 时写入(:181)
§2 四星「下次跟进设为今天」 成立。lead-assignment.flow.ts:67 是 {TODAY() + 1}(明天),另一支 :91 是 {TODAY() + 3}
§2 五星「立即发邮件」 成立。通知由边 e2 的 record.rating >= 4 触发(:127),走 inbox + email 两通道,与满五星无关
§2 SLA 提示句(旧 :132) 已被 #768 修复,本 PR 不再改(现文案「四星及以上是明天,其余是三天后」与 flow 一致)
§2 Hot Leads / High Priority 两行 已被 #774 按 rating ≥ 4 订正,保留不动
§3 presented 译作「已呈现」 成立,但行号与范围与 issue 不同:现在是 :16(不是 :14),且同页另有 5 处句子把该状态称作「已呈现」(:21/:89/:93/:104/:128)。只改状态表会让同一页出现两种叫法,故 6 处一并对齐

按 #785 先例,实测与断言不符的地方以实测为准,已在上表标注。

改了什么

1. contacts.mdx ×3

  • 字段区块表:Contact Information 删「传真」,Communication Preferences 删「传真退订」。是删不是补 —— 是否需要传真字段是产品决定,不该由一篇文档倒推。
  • 「Communication preferences (GDPR / CCPA)」小节按落地程度重写为两个开关:
    • Email Opt Out —— 已强制执行:contact.actions.ts 的 send_email 带 visible: record.email_opt_out == false(locations: record_header / list_item),contact-welcome.flow.ts 的 start 条件也以它为门。
    • Do Not Call —— 仅是标记位:如实说明产品不阻止任何通话或 Call 任务。
    • 「仅管理员可取消」整句改为如实描述:这两个字段没有任何字段级权限规则,谁能编辑联系人谁就能勾选/取消。
  • 顺带删掉同段里同样无落地的两处(都在 issue 点名的 :48-58 区间内,重写该段时无法保留):「每个标志以红色徽章显示在界面顶部」(contact.view.ts 无任何 badge 声明)、「营销活动会将其排除」(campaign-enrollment.flow.ts:80 过滤的是 crm_lead.email_opt_out,联系人不进这条链路)。
  • zh 页的两个开关名改用语言包写法(zh-CN.ts 的「拒绝邮件」「禁止致电」),与 §3 同一条 contract-first 规则。

第二个 commit 是对我自己第一版措辞的订正:contact_welcome 不是邮件模板,是 flow 名与 notify topic,通知发给联系人的负责人("Reach out to welcome them"),不是发给联系人本人。所以 email_opt_out 挡掉的是「给负责人的打招呼提醒」。同页「Built-in rules」小节把它写成「自动向对方发送 contact_welcome 邮件模板」,三点都不对,已立 #796,本 PR 不顺手改。

2. leads.mdx ×3

  • Qualification 行按 qualification group 的真实成员重写。这连带要求改一格:lead_source 与 next_followup_date 声明在 qualification(lead.object.ts:206/299)而非 assignment,若只改一行,两行会在同一张表里互相打架,故 Assignment 行收敛为「负责人」。
  • 「Lead scoring」整节按 computeRating 重写:真实六项权重、上限 5 下限 1 四舍五入到整星、只在创建时运行、不覆盖调用方给的 rating。删掉「互动信号——近期网页访问、网络研讨会出席」(无任何对应实现)。
  • SLA 表按 lead-assignment.flow.ts:四星及以上 → 明天 + inbox 与 email 双通道告警;其余(含无评级)→ 三天后 + 仅 inbox。并写明满五星不额外触发任何事。
  • 销售代表提示里「使用星级评级——评分并不能捕捉一切」把 rating 与 score 当成两个东西,是本节订正的同一处混淆,改为说明「你填的评级会保留,自动评分只在留空时补上」。
  • 「管理员可调整评分权重 → Administration › Automation」改为指向本页末尾已有的管理员提示(那里已正确写明权重在 src/objects/lead.hook.ts)。

3. quotes.zh-Hans.mdx / quotes.zh-Hant.mdx

presented 的译名跟 zh-CN.ts:471 的「已提交」,状态表加正文共 6 处。en 页无此问题,未改。:8 的「呈现时刻」与 :113 的「以看板呈现」是动词用法,不是状态名,保留。

越界发现(已单独立单,本 PR 不修)

验证

全量跑(docs 改动,确认零测试影响):

pnpm hygiene    ✓ source hygiene clean(含 no raw control bytes;两次 commit 后各跑一次)
pnpm typecheck  TYPECHECK_EXIT=0
pnpm validate   ✓ Validation passed (1516ms) —— 5 条 warning 均为既有项(approval / campaign_member),与本改动无关
pnpm lint       13 warning(s), 14 suggestion(s),0 error,均为既有项
pnpm test       Test Files 63 passed (63) · Tests 1521 passed | 1 skipped (1522)

控制字符自扫全部改动文件 + changeset:grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' 零命中。

本 PR 未新增任何链接(只删除了一处 /docs/administration/automation 链接),link-check 无新增面。changeset:.changeset/sales-docs-ghost-fields.md(hotcrm, patch)。

…d` sections against the shipped metadata (#765)

Three sections of the sales pages (and their zh-Hans / zh-Hant siblings)
described a surface that does not exist, so a reader following them looked for
fields and behaviour they could never find.

Contacts advertised a fax field and a Fax Opt-out flag; `crm_contact` declares
neither `fax` nor `fax_opt_out`, so both rows are removed rather than answered
with new fields — whether HotCRM should carry a fax number is a product
decision, not something a docs page settles. The two flags that do exist are
now split by how far they are actually enforced: `email_opt_out` hides the
Send Email action (`contact.actions.ts` `visible: record.email_opt_out ==
false`) and gates the welcome email (`contact-welcome.flow.ts`), while
`do_not_call` is read by nothing outside the object and the form section — the
page had claimed it blocks logging a Call task. The "admin-only, reps can only
request a change" sentence goes with it: no profile in `src/profiles/`
declares field-level permissions on either flag.

Leads described a 0-100 score plus budget and timeline fields. There is one
quality field, `rating` — labelled Lead Score, `Field.rating(5)` — and the
scoring section is rewritten against `computeRating` in `lead.hook.ts`: the
real weights, the rounding to whole stars, and the two limits the old text hid
(it runs on beforeInsert only, and never overwrites a rating the caller
supplied). The SLA table follows `lead-assignment.flow.ts`: rating >= 4 is
`TODAY() + 1` with an inbox + email alert, everything else `TODAY() + 3` with
an inbox alert; nothing extra happens at five stars.

The Qualification row is written from the object's real `qualification` group,
which forced one adjacent cell: `lead_source` and `next_followup_date` are
declared there, not in `assignment`, so the Assignment row is trimmed to Owner
rather than left contradicting the row above it. The rep tip contrasting "the
star rating" with "the score" is reworded for the same reason — they are one
field. The rest of that table's rows diverge too and are filed as #792.

The Chinese quote pages translated `presented` as 已呈现 / 已呈現 while the
shipped zh-CN locale pack calls it 已提交, so a Chinese reader could not match
the docs to the screen. Every sentence naming the status follows the locale
pack, not just the status table.

Fixes #765

Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 5, 2026 8:22pm

Request Review

…ot an email to the contact

Self-correction on the previous commit. `contact_welcome` is a flow name and a
notify topic, not an email template: `contact-welcome.flow.ts` notifies the
contact's OWNER ("Reach out to welcome them"), so `email_opt_out` suppresses
that prompt rather than an email sent to the person. The opt-out row said the
latter, repeating a claim the page's "Built-in rules" section makes further
down — that claim is wrong on three counts and is filed as #796, not fixed
here.

Refs #765

Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
@yinlianghui
yinlianghui marked this pull request as ready for review August 5, 2026 20:27
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 881dd67 Aug 5, 2026
9 checks passed
This was referenced Aug 5, 2026
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…es on the zh-Hant page (objectstack-ai#793) (objectstack-ai#803)

`content/docs/sales/quotes.zh-Hant.mdx` called the `expired` quote status
「已到期」 in its status table and again in the daily-sweep bullet, while
`crm_quote.status.options.expired` in `src/translations/zh-CN.ts` is 「已过期」 —
「已過期」 in traditional script. 到期 and 過期 are not the same word (a deadline
arriving vs having lapsed), so a reader matching the page against the screen
looked for a status the product never shows.

Both status-name occurrences now follow the locale pack. The page's third
mention — the standard-list-view section describing the nightly sweep — already
said 已過期, so the page was contradicting itself as well as the product.

Verb and field usages stay as they are, on the criterion PR objectstack-ai#794 set when it
aligned `presented` (it deliberately left 「擷取其呈現時刻的定價」 alone): 「自動到期」
in the frontmatter names the expiry mechanism, 「每日到期掃描」 names the nightly
sweep, and 「到期日期」 is the shipped label of the `expiration_date` field
(`src/translations/zh-CN.ts`), not a status name.

Docs follow the locale pack rather than the reverse, so nothing under `src/`
changed. The leads half of the issue is delivered by objectstack-ai#792's PR, per the scope
split recorded on both issues.

Fixes objectstack-ai#793


Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
….ts (objectstack-ai#805) (objectstack-ai#823)

The Contracts page's "On contract activation" list promised four things and the
hook that runs on activation does two, neither of which was the first item's
match. `contract_on_activation` (src/objects/contract.hook.ts:77-126) stamps
`signed_date` on the CONTRACT when neither the update nor the stored record
carries one (:104-109), and sets the account's `type` to `customer` when the
account is not already one (:111-119). That is the whole of it.

Of the four documented steps, three have no implementation anywhere:

- "Customer Since date stamped on the account if blank" — `customer_since` is a
  ghost field. `grep -rn customer_since src/` is empty, and `crm_account`
  declares no field of any name for when an account became a customer, so the
  cohort analysis a reader would build on it has nothing to read.
- "Welcome email sent to the primary contact (configurable)" — the contract
  domain ships two flows, `contract-expiration` and `contract-renewal`, both
  scheduled and both notifying the OWNER. No outbound customer mail is sent at
  activation and there is no configuration surface behind "(configurable)". Same
  promise, same shape as objectstack-ai#796 on the Contacts page.
- "Notification to the account owner" — the hook raises no notification at all.
  The two contract `notify` nodes hang off the expiration and renewal sweeps.

The signed-date stamp, which does happen, was the one step the page never
mentioned; it is now step 1, with the condition stated from the code — set
Signed Date in the same edit that activates and your value is kept.

The surviving step is stated more tightly than "if it was a Prospect". The gate
is `account.type !== 'customer'`, so a *Partner* and a *Former Customer* are
promoted exactly as a *Prospect* is; documenting only the Prospect case hid a
promotion that a partner-account owner would not expect. Two mechanics that
decide whether anything happens are written down too: the hook is `async: true`
with `onError: 'log'` (:82-83), so failures are logged rather than raised, and it
is an `afterUpdate` hook (:80) with a `previous?.status === 'activated'` guard
(:88) — a contract created already *Activated* by an import, by seed data or by
an integration writing the record in one shot triggers neither write, and its
account stays a *Prospect*.

Same-page consistency (the objectstack-ai#794 precedent): the admin tip called activation a
"flow" and sent the reader to the flow list to customize it. It is an object
hook and does not appear there, so the tip now names the file and keeps the
pointer for the expiration and renewal sweeps, which really are scheduled flows.

Documentation only — no metadata, behaviour or field changes. Whether HotCRM
should send a welcome email or carry a `customer_since` field is a product
decision and is deliberately left open.

Fixes objectstack-ai#805

Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
os-sales pushed a commit that referenced this pull request Sep 3, 2026
…abels

#837 landed the mechanism scoped to object names. This extends `TERMS` to
`status.options.*` and field `label`s — the four defects #802 was filed on —
and answers that card's open question 2: mechanism prose is spared by ledger
granularity (the complete term), not by syntactic position.

Measured, not assumed: 71 lines carry 到期 and 14 carry 呈现 across the 134
Chinese pages; zero carry 已到期, 过期日期 or 已呈现. The narrowing the card
proposed (status-table cells plus `**bold**` refs) would have missed six of the
nineteen historical defect sites, which carried no markup at all.

The extension found one live defect: `crm_quote.crm_contact.help` in the zh-CN
pack quoted 「已呈现」, a status the console does not show — introduced by
#1017 (landed as #1068) after #794 had swept the repo clean of that spelling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hUuCQStzXGMFSX4dzww5t
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

同一缺陷类:contacts.mdx 的传真字段/传真退订、leads.mdx 的 0-100 评分与 budget/timeline 字段,在对象声明里都不存在

2 participants