Repository navigation
feat(actions): record pages offer only the actions a record can still take; quote and case status buttons (#2366) - #2374
Merged
Conversation
…d case lifecycle actions (#2366) [WIP] Converted lead and settled quote offer no Edit; a settled quote offers no line-item New; a quote cannot be created at a settled status; campaign metric counts and member response fields are read-only; quote lifecycle actions and Reopen Case; option kind on quote, campaign, campaign member and knowledge article status. Tests and docs follow. Claude-Session: https://claude.ai/code/session_01J8VVAmPx3rficEnw9LvZUQ Co-Authored-By: Claude <noreply@anthropic.com>
… counter changes; manual pages (#2366) - New tests: campaign-metric-fields-readonly, case-reopen-action, quote-lifecycle-actions, quote-settled-actions; status-option-kinds and converted-lead-guard extended; refusal-envelope 36 sites; refusal-labels Rejected; accepted-quote-frees-contact born-presented. - Case detail header lists reopen_case. - In-app manual (en + zh): quotes, cases, leads, campaigns; docs-site quotes page (en, zh-Hans, zh-Hant): Rejected is revised with Revise. - README / STATUS: 38 actions. Changeset. Claude-Session: https://claude.ai/code/session_01J8VVAmPx3rficEnw9LvZUQ Co-Authored-By: Claude <noreply@anthropic.com>
…t on refresh hooks (#2366) Claude-Session: https://claude.ai/code/session_01J8VVAmPx3rficEnw9LvZUQ Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
Author
席位核对Verdict: ACCEPT ·
Generated by Claude Code |
This was referenced Oct 29, 2026
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.
Requested by Irene · project thread
Fixes #2366
Clause-②: no
Metadata-protocol: none
维护者速读
quote_status_progression和 Show users the result of what they did, and say refusals in business words: enrolment counts, approval submission, stacked toasts, clone, double approval notices, quote and contract results, quote transitions, forecast validation, lead delete #2371 拥有的状态迁移拒绝块没动。行为变化:销售通过 API 以已接受/已拒绝/已过期创建报价单现在返回 400;已拒绝报价单在修订前只能改内部备注;用户写入统计字段会被丢弃。回滚即 revert 本 PR。src/revenue/actions/quote.actions.ts、src/revenue/objects/quote.hook.ts(新建拒绝与 Rejected 冻结)、src/revenue/objects/quote.object.ts(userActions与选项visibleWhen)、src/service/actions/case.actions.ts(reopen_case)。pnpm exec vitest run test/quote-settled-actions.test.ts test/quote-lifecycle-actions.test.ts test/case-reopen-action.test.ts test/campaign-metric-fields-readonly.test.ts,预期全部通过;或在控制台打开一张已提交的报价单,应看到“接受”“拒绝”按钮,接受后不再有“编辑”。What changes
Each row of the card, with its verdict:
crm_lead.userActions.edit.visibleWhenreadsis_converted. The lock inlead.hook.tsis unchanged.crm_quote.userActions.editandcrm_quote_line_item.userActions.create(the related list evaluates the host quote) hide both on Accepted, Rejected and Expired. Hiding Edit also removes the page's double-click inline editing (measured).quote_workflow's freeze now covers Rejected too, withstatusleft writable so Revise is the way out.visibleWhen: has(record.created_at): absent on the New form, present on every server insert (measured, seeds included).quote_workflowrefuses a user's create at any of them,VALIDATION_FAILED/ 400, with auserMessage. System writes (seed, import) still store them.actual_revenueand the sixnum_*fields oncrm_campaign, andhas_responded/response_dateoncrm_campaign_member, arereadonly.campaign_metrics_refreshnow runsrunAs: 'system'like its three siblings, so its write is not stripped.crm_userfloor:/auth/me/permissionsforservice.agentlistscrm_opportunityallowCreate: true, and a REST create answers 201. Changing that is a change to who may do what, out of scope.test/flow-quote.test.ts. The console already greys Edit on the locked deal. Hiding Generate Quote there needs a ruling.crm_userfloor), and the platform'seditInlinedefault is on. Any remaining gating is objectstack#27355 row 6.reopen_case: a declarative update toin_progress, offered on Resolved and Closed to callers who may edit cases (the grant Escalate and Close read). Listed incase_detail_page's header.submit_quote_for_review,mark_quote_presented,accept_quote(withconfirmText),reject_quote,revise_quote, each a declarative update ofstatusalone, offered only on the statuses its move leaves from and only to callers who may edit quotes.→ expiredstays the sweep's. The allowed transitions are unchanged.kindon quote, campaign-member and knowledge-article statuscrm_campaign.statusis included as the same defect class.reopen_casein en, zh-CN, es-ES and ja-JP.crm_revenue_quotes): the buttons, how a quote starts, and settled quotes.crm_service_cases): Reopen Case and what it does to the SLA.crm_sales_leads): a converted lead has no Edit.crm_marketing_campaigns): the numbers and the member's response fields are read-only.content/docs/sales/quotes.*(en, zh-Hans, zh-Hant): a Rejected quote is revised with Revise and is frozen until then.docs/STATUS.mdstate 38 actions (pnpm validate).Tests
test/quote-settled-actions.test.ts(new, 25):userActionspredicates and the option predicates are evaluated on the platform's CEL engine, settled and open statuses, plus the first render.userMessage, and nothing is stored.test/quote-lifecycle-actions.test.ts(new, 18):visibleand checked againstquote_status_progression. Every declared move but→ expiredhas an action./auth/me/permissions: a rep is offered them; a marketing user and a service agent are offered none.RECORD_NOT_FOUND/ 404 and nothing moves.test/case-reopen-action.test.ts(new, 7):in_progresswithis_closedfalse.sla_due_dateis kept, andis_sla_violatedstays true.test/campaign-metric-fields-readonly.test.ts(new, 14):readonly, and all four refresh hooks run elevated.num_sent,num_responsesandactual_revenueis dropped while the rest of the same write lands.has_responded/response_dateis dropped.test/status-option-kinds.test.ts: four new objects.test/converted-lead-guard.test.ts: the Edit predicate.test/refusal-labels.test.ts: the Rejected label and sentence.test/refusal-envelope.test.ts: 35 → 36 call sites.test/accepted-quote-frees-contact.test.ts: the born-without-contact case is now born Presented, since a user can no longer create a quote as Accepted.Acceptance notes
userActions.editbehaved the same way on quotes.closed_dateandresolution_time_hourskeep the first close when a reopened case is closed again. This is pre-existing and also reachable through Edit, so it is not changed here and is in the report.Verification
pnpm verify at 9599f1f
b4e6e01 only adds a 30 s budget to two describes in
test/campaign-metric-fields-readonly.test.ts(14/14 green,tsc --noEmitclean). CI runs coverage and e2e.Ablations (on the committed tree; exact-anchor replace, anchor count 1 → 0, restored with git checkout, blob hash equal to HEAD after each)
🤖 Generated with Claude Code
https://claude.ai/code/session_01J8VVAmPx3rficEnw9LvZUQ