Repository navigation
docs(contracts): 按视图、选项集、hook 与 profile 写实合同页其余四处 (#832) - #874
Merged
Merged
Conversation
…iews, picklists, hook and profiles (#832) The Contracts page still described five saved list views that do not exist, three picklist values that cannot be picked, a delete guard nobody built, and a tip addressed to the one persona the object refuses. - Standard list views: `src/views/contract.view.ts` saves one unfiltered list (`all_contracts`) offered in four visualisations via `allowedVisualizations` and `tabs[]`. Rebuilt as one dataset drawn four ways, per the #792 precedent, and the five ghost views are named as absent. - Billing frequency / payment terms: no `Custom`, no `Net 45`, no `Prepaid`. Both lists now match `contract.object.ts` and `_picklists.ts`, with the zh option labels taken from the locale pack. - Built-in rules: "Activated contracts cannot be deleted" has no implementation (no beforeDelete, no status check; deletion is profile-only and status-blind). Replaced with the rules that really reject a save — `end_after_start` and the two throws in `contract.hook.ts` — plus the two things that are not rules: the `min: 0` field constraint, and the state machine, which is declared at warning severity and therefore logs without blocking. - Tips for sales reps: `sales-rep.profile.ts` grants crm_contract read-only, and nothing pre-fills the opportunity link. The real path is `quote_on_accepted` drafting the contract from the accepted quote. Documentation only, all three languages. No metadata or behaviour changes.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
yinlianghui
marked this pull request as ready for review
August 6, 2026 01:18
This was referenced Aug 6, 2026
src/docs/crm_sales.md 两处仍称「通知负责人及其经理」,两条 flow 都只发负责人 —— 且该文件随 ADR-0046 打进 dist/objectstack.json
#875
Closed
Sales Manager 对 crm_contract 有 allowEdit 却没有写深度:报价自动起草的合同(挂在 rep 名下)只有 System Administrator 改得了
#880
Closed
activities.mdx 三语把「合同结束前 60 天的续约提醒」当成重复任务的用例:既不是 60 天(按合同自己的 renewal_notice_days,默认 30),也不是重复任务
#883
Closed
Closed
yinlianghui
added a commit
to yinlianghui/hotcrm
that referenced
this pull request
Aug 10, 2026
…files and the status machine (objectstack-ai#872) (objectstack-ai#878) Three sentences on the Contracts page contradicted what PR objectstack-ai#874 measured on the same page, in all three languages. - "The owner can change status to Terminated" is false for the most common owner: edit on crm_contract is object-level and ownership does not extend it. sales-rep.profile.ts grants allowEdit: false, and quote_on_accepted copies the quote's owner_id onto the contract it drafts, so the rep owns a record they can read and cannot touch. Sales Manager and System Administrator hold edit. - "Terminated contracts can't be reactivated" is a gate nobody built: contract_status_progression declares terminated a dead end at warning severity, and a non-error verdict is logged while the save proceeds. The one-way advice is kept, but the page now says nothing enforces it. - "The contract owner reads and edits their own contracts" conflated the two access layers: record-level sharing decides which records you reach, the profile's object-level CRUD decides whether you hold edit at all. Documentation only. No metadata, profile, permission or validation-rule changes. Co-authored-by: Claude <noreply@anthropic.com>
15 tasks done
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 #832
按 issue 列出的四处,以
src/为事实来源在最新 main 上逐条重定位、重核、改写,三语同步。仅动文档,src/**一行未改。前提复核(基线漂移)
issue 基线
4c127919,本 PR 基线ed6885e2,其间 #831(同页三处)已合并。四处在最新 main 上全部仍在,只是行号整体下移;#831 改的是到期扫描、续约提醒、记录字段表,与这四处无重叠。两处与 issue 正文/派发口径不一致,按实测写,不按模板写:
contract_status_progression是severity: 'warning'(src/objects/contract.object.ts:260)。实测引擎侧@objectstack/objectql的evaluateValidationRules:只有severity === 'error'才进errors并抛ValidationError,其余级别只logger.warn后放行(node_modules/@objectstack/objectql/dist/core.mjs:2434-2441)。所以「draft 不能直跳 activated / expired 终态」不是一道闸,是一条日志;页面按「建议不是闸门」写,而不是再造一个假护栏——本条要修的正是假护栏。另:该规则没有initialStates,insert分支直接返回 null(同文件 :2475-2485),所以新建时就是 activated 的记录根本不受检查。src/actions/、src/flows/;但src/objects/quote.hook.ts:95-152的quote_on_accepted在报价转accepted时直接 insert 一份合同,并把报价上的crm_opportunity、crm_account、crm_contact、owner_id、total_price一起抄过去。所以「链接被预填」的说法在人工建合同这条路上不成立(crm_opportunity只有dependsOn: ['crm_account'],无defaultValue),但整段前提并非全不成立:链接是从报价抄来的。改文按这个真实机制写。sales-rep 权限那半边完全成立(见下)。逐条证据与改文
1. 标准列表视图 — 五条幽灵 → 一份数据 + 四种可视化
src/views/contract.view.ts只存了一份列表all_contracts(:14-43,无任何过滤),另三条在listViews(:45-90),四者靠appearance.allowedVisualizations(:34-36)与tabs[](:37-42)拼成同一份数据的四种画法。按 #792 在 sales 列表视图上的先例改成一张「标签 / 它怎么画同一批合同」表,并写明四个标签都不过滤——读者要的「我的活跃合同」得自己在网格上排序过滤,替他盯日期的是每日续约提醒而不是某个视图。五个幽灵名字点名说明「不存在」,不静默删掉。zh 视图名按语言包(src/translations/zh-CN.ts:561-566):全部合同 / 续约日历 / 合同条款 / 合同时间线。2. 幽灵选项 — Custom / Net 45 / Prepaid
src/objects/contract.object.ts:131-140只有monthly(default) /quarterly/annually/one_time,无 Custom。src/objects/_picklists.ts:83-89的PAYMENT_TERMS_OPTIONS只有net_15/net_30(default) /net_60/net_90/due_on_receipt,无 Net 45、无 Prepaid。两处按包写实,并明说这三个值不存在、对应诉求写进「特殊条款」。zh 选项名按语言包(
zh-CN.ts:535-545):按月 / 按季度 / 按年 / 一次性;15 天账期 / 30 天账期 / 60 天账期 / 90 天账期 / 货到付款。3. 内置规则 — 删假护栏,补真会拦保存的规则
end_after_start(contract.object.ts:239-246,severity: 'error'),对象上唯一一条会把合同挡回去的校验规则。contract_value的min: 0字段约束(:122-129),不是校验规则;引擎侧数值字段的min在字段校验层拒绝(core.mjs:2081-2083)。顺带写上contract_term_months的min: 1。crm_contract没有beforeDelete,全仓beforeDelete只在contact.hook.ts/product.hook.ts/account.hook.ts上;也没有任何按status拦删除的校验。删除权限完全由 profile 决定且与状态无关:system-admin.profile.ts:28allowDelete: true(已激活的也能删)、sales-manager.profile.ts:24与sales-rep.profile.ts:52都是allowDelete: false(草稿也删不掉)。页面改为直说这件事,并把「要拦得管理员自己配」讲明。contract.hook.ts:54-61(end_date与start_date + contract_term_months相差超 1 个月抛错)、:63-73(激活后把end_date往前改抛错)。状态机按上面的实测写成「建议不是闸门」。4. 给销售代表的提示 — 按 profile 与 hook 实况改写
sales-rep.profile.ts:52:crm_contract是allowCreate: false, allowEdit: false, allowDelete: false, readScope: 'own'—— 销售代表既建不了也改不了合同,只读自己的。能建的是sales-manager.profile.ts:24与system-admin.profile.ts:28。quote.hook.ts:95-152的quote_on_accepted,在报价被接受时起草一份draft合同(12 个月、今天起算、contract_type: 'subscription'、金额取报价总价),商机链接是从报价抄的;owner_id也从报价抄,所以这份合同挂在这位代表名下,他读得到但改不了。contract.object.ts:74-79的crm_opportunity只有dependsOn: ['crm_account'](按已选客户过滤选择器),没有defaultValue。src/reports/、src/datasets/、src/dashboards/里crm_contract零命中,本仓没有任何合同报表或仪表盘。段名「给销售代表的提示」保留,三条提示改写为:合同不是你填出来的(接受报价才是起草动作)、商机链接是抄来的不是预填的、自己的合同你读别的事去找经理/管理员。
quotes 页核查(只核不改)
PAYMENT_TERMS_OPTIONS与 Quote 共用(quote.object.ts:178-183),所以顺带核了报价页:content/docs/sales/quotes.mdx:37写的是Net 15 · Net 30 (default) · Net 60 · Net 90 · Due on Receipt,与包完全一致,zh-Hans / zh-Hant 同(:37)——同批失真没有落在 quotes 页,本 PR 不改它。核查过程中另发现两处与本单无因果的残留,已按去重纪律处理,未在本 PR 内改动:报价页
:71的「接受即提升客户 / 60 天前排续约任务」两句;以及本页「手动终止」小节的两句(所有者能改状态、已终止不能复活)——后者与本 PR 第 3 条的实测结论直接冲突,留待单独处理。已分别立单:#871(报价页接受链路多写两步)、#872(本页「手动终止」两句)、#873(观察:共用词表的理由未被实现)。已知相关未修缺陷:#714(缺 contact 或 opportunity 的报价被接受时,同一个 hook 静默失败、合同不起草)。验证(共享锁 + 4G 堆,全部在本工作树)
pnpm validateData: 17 Objects 344 Fields/UI: 14 Views(5 条既有 author-time 警告,与本 PR 无关)pnpm typechecktsc --noEmit无输出pnpm lint13 warning(s), 14 suggestion(s)(全部既有)pnpm hygiene✓ no raw control bytes in first-party files/✓ source hygiene cleanpnpm build✓ Build complete/Artifact: dist/objectstack.json (1921.3 KB)pnpm test -- --maxWorkers=2Test Files 66 passed (66)/Tests 1587 passed · 1 skipped (1588)test 中 source-hygiene 元测试打印的「✗ ...」是它自己的预期 stderr。改动文件另做控制字节自扫(
grep -naP扫 0x00-0x08、0x0b、0x0c、0x0e-0x1f 这些区间),零命中。散文不加钉子测试(issue 明确要求),也没有升级任何
@objectstack/*。Generated by Claude Code