Skip to content

docs(activities): 把「重复任务」用例里的续约提醒按机制写实(#883) - #900

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-883-activities-recurring-example
Aug 6, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-883-activities-recurring-example

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #883

content/docs/sales/activities.mdx:73 及其 zh-Hans / zh-Hant 双胞胎,在 Recurring tasks / 重复任务一节的用例列表里写着「合同结束前 60 天的续约提醒——由合同流程自动设置」。两个半句都与源码不符,这个 PR 按实测三语改写这一条用例所在的句段。

前提复核(在最新 origin/main 上)

  • 原文仍在,行号未变:三语均在 :73。activities 页最近两次改动是 docs(sales): 把 activities 页的活动量指标指向真正报告它的仪表盘 (#747) #772(指向真实仪表板)与 fix(home): bind the Sales Home tabs to real views, and document the task views that ship (#771) #784(Sales Home 标签绑定),都没碰这一节。
  • 逐行复核事实,两处不符都成立:
    • 不是 60 天。 src/objects/contract.hook.ts:121-124 的注释写明写死 60 天的激活期任务已被删除("The activation-time task this hook used to create hardcoded a 60-day notice and duplicated the flow's task.")。今天排续约提醒的是 contract_renewal 定时流程(src/flows/contract-renewal.flow.ts):start 节点 schedule: '0 8 * * *',query_contracts 的 filter 是 { status: 'activated', end_date: { $gte: '{TODAY()}', $lte: '{TODAY() + 120}' } }(120 天只是宽预过滤),逐份合同的窗口判定在出边 b1 上,读的是 vars.currentContract.renewal_notice_days;该字段定义在 src/objects/contract.object.ts,defaultValue: 30,逐份可设。
    • 不是重复任务。 create_renewal_task(contract-renewal.flow.ts:86-98)写入的字段只有 subject / type: 'follow_up' / priority: 'high' / status: 'not_started' / due_date / owner_id / related_to_type / related_to_account——没有任何重复字段。它每天重现靠 find_existing_task + 出边 b3(existingRenewalTask == null)这个幂等闸门,即「扫描 + 当天已处理则跳过」,与 recurrence 机制无关。

recurrence 用例测查结论(决定改法的那一步)

按 PM 裁定,先测本仓是否存在真实的 recurrence 用例,有则换真例、没有则如实写、不编造第二个假例。测查结果分两层:

机制是真的,因此本节的开头段(「标记为重复→系统在你完成当前实例时生成新实例,直到重复结束日期」)不需要改:

  • 字段面:crm_task 的 is_recurring / recurrence_type(daily/weekly/monthly/yearly)/ recurrence_interval / recurrence_end_date(src/objects/task.object.ts:178-206);
  • 表单面:src/views/task.view.ts:279-292 的 Recurrence & Effort 分组把四个字段都摆了出来;
  • 规则面:recurrence_fields_required(task.object.ts:292-296);
  • 实现面:src/objects/task.hook.ts 的 task_recurrence 钩子,在「进入已完成」的那次转换上克隆下一实例,按 recurrence_type × interval 推进 due_date / reminder_date(月/年步长对月末做 clamp),越过 recurrence_end_date 即停。

但没有任何自动化会创建重复任务。 is_recurring 在全仓的命中只有 task.object.ts / task.hook.ts / task.view.ts 加四个语言包;src/flows/、src/actions/、src/data/(种子)下零命中。也就是说:这个应用里每一条重复任务都由人工勾选发起,钩子只负责把已存在的系列往下续。

所以没有真例可换——列表里剩下的两条(季度回访、每日站会)本来就是人工设定的示例,是准确的;假的那一条被移除,改为如实写明「本应用不会自动创建重复任务」,再按真实机制交代续约提醒,并指向 content/docs/revenue/contracts.mdx 的每日——续约提醒一节(那里已按实测写实,#823 / #874)。口径与 PR #884(quotes 接受链路)一致:同一句「只读已激活合同、用每份合同自己的续约通知(天)、默认 30、不是 60」,不重复不矛盾;「说明本应用不发布/不使用某能力」的写法沿用 #864。

改文三语对照

en — 改前:- *Renewal reminder 60 days before contract end* — set automatically by the contract flow.
改后为两段散文:① 两条用例都是手工设定,且 nothing in HotCRM marks a task as recurring for you(无 flow / action / 种子数据);② The renewal reminder is not one of them — 每日续约扫描建的是普通高优先级 follow-up、身上没有重复字段,针对 Activated 合同、按各自的 Renewal Notice (Days)(默认 30,不是固定 60),每早重现是「扫描又跑一遍 + 跳过还挂着未完成提醒的合同」,并链到 Contracts。

zh-Hans — 改前:- *合同结束前 60 天的续约提醒*——由合同流程自动设置。
改后同结构:「HotCRM 不会替你把任何任务标记成重复任务……你不勾,这个应用里就一条重复任务都没有。」+「续约提醒不在其列。……结束日期已经进入这份合同自己的**续约通知(天)**窗口的已激活合同,默认 30 天、逐份可设,不是固定的 60 天……」链到合同页。

zh-Hant — 改前:- *合約結束前 60 天的續約提醒*——由合約流程自動設定。
改后同上,繁体:「續約提醒不在其列。……**續約通知(天)**視窗的已啟用合約,預設 30 天、逐份可設,不是固定的 60 天……」链到合約頁。

zh 术语按语言包:renewal_notice_days → 「续约通知(天)」(src/translations/zh-CN.ts:547),is_recurring → 「重复任务」(:707),合同 status.activated → 「已激活」(:523);zh-Hant 沿用本仓既有的「啟用 / 視窗 / 欄位」译法与 #884 落地文本一致。

只动了该用例所在的句段——本节开头的机制段、其余两条用例、以及前后两节都未改;未触碰 src/**、content/docs/releases/;未加任何守卫;未升级 @objectstack/*。

验证输出

全量六项在共享锁 flock -w 7200 /tmp/os-heavy-verify.lock 下、NODE_OPTIONS=--max-old-space-size=4096 依次跑完,退出码全为 0:

命令 退出码 关键行
pnpm validate 0 Data: 17 Objects 344 Fields / Logic: 24 Flows;5 条 author-time 警告为既有(approval approvers、campaign_member 分组),与本改动无关
pnpm typecheck 0 tsc --noEmit 无输出
pnpm build 0 ✓ Build complete (1444ms) · Artifact: dist/objectstack.json (1921.4 KB)
pnpm test -- --maxWorkers=2 0 Test Files 66 passed (66) · Tests 1587 passed, 1 skipped (1588) · Duration 47.64s
pnpm lint 0 13 warning(s), 14 suggestion(s) (1221ms),全部为既有的 approval-approvers 提示
pnpm hygiene 0 Source hygiene — 245 files under src, test, e2e, scripts; the control-byte scan adds 409 under content, .changeset · ✓ no raw control bytes in first-party files · ✓ source hygiene clean

test 输出里的 ✗ source hygiene: scanned director(y|ies) missing: .changeset 是 source-hygiene-scan-surface.test.ts 元测试自身刻意触发的负例 stderr,属预期,该文件计入上面 66 passed。

控制字节自扫(grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]')覆盖三个 mdx 与 changeset,退出码 1(零命中)。

changeset:.changeset/activities-recurring-example.md('hotcrm': patch,站内路径用反引号)。

…ecurrence (#883)

`content/docs/sales/activities.mdx:73` and its zh-Hans / zh-Hant twins listed
"Renewal reminder 60 days before contract end — set automatically by the
contract flow" as a **recurring task** use case. Both halves are wrong:

- Not 60 days. The hardcoded 60-day activation task was deleted from
  `src/objects/contract.hook.ts` (reason at :121-124). Renewal reminders come
  from the `contract_renewal` scheduled flow, which runs daily at 08:00, reads
  `status: 'activated'` contracts only, and applies each contract's own
  `renewal_notice_days` (`src/objects/contract.object.ts`, defaultValue 30).
- Not a recurring task. `create_renewal_task` writes subject / type follow_up /
  priority / status / due_date / owner_id / related_to_* and no recurrence
  field at all. It reappears daily through scan + idempotency gate
  (`find_existing_task` + edge `b3`), not through recurrence.

Tested for a real recurrence use case before rewriting. The mechanism is real
(`is_recurring` / `recurrence_type` / `recurrence_interval` /
`recurrence_end_date` on `crm_task`, the form's Recurrence group, the
`recurrence_fields_required` rule, and `task_recurrence` in
`src/objects/task.hook.ts` spawning the next occurrence on completion), but no
automation ever creates one — `is_recurring` has zero hits under `src/flows/`,
`src/actions/` and `src/data/`. So the false example is removed rather than
swapped for an invented one, and the section says plainly that every recurring
series here starts with a person ticking the box, then points the renewal
reminder at the daily sweep and the Contracts page.

Documentation only; no metadata, hook or flow changes.

Co-authored-by: yinlianghui <yinlianghui@users.noreply.github.com>
@vercel

vercel Bot commented Aug 6, 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 6, 2026 5:40am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 6, 2026 05:43
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 3ec9feb Aug 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant