Skip to content

docs(quotes): say what Generate Quote does: stage rule, no activity stamp, inbox and email notice - #2023

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-2021-quotes-docs
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-2021-quotes-docs

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #2021
Clause-②: no

The Quotes page's Generating a quote section (content/docs/sales/quotes.mdx, .zh-Hans.mdx, .zh-Hant.mdx) described a run the quote_generation flow does not perform. This PR rewrites that section only, in all three locales, so every sentence matches the flow on main 49fe305a (after #2020's approval-hold change). No src/** change: the flow is right, the page was wrong.

What the page said, measured against the flow before the fix

Page sentence (en; zh-Hans / zh-Hant said the same) What the flow does on 49fe305a
"open any opportunity and click Generate Quote" generate_quote is visible only while stage is neither closed_won nor closed_lost (src/sales/actions/opportunity.actions.ts:221)
"Then the system, in a single step:" separate writes in sequence (create the quote, then maybe update the opportunity, then notify), with no transaction across them (flow comment at :131-132, ADR-0077); now "When you submit it, the system:"
3. "Advances the opportunity to Proposal stage." advances only from prospecting / qualification / needs_analysis, and only when approval_status is not pending (edge e4a, :204-206); every other case keeps the stage (edge e4b, :207-209)
4. "Stamps the opportunity's last activity to today." the only opportunity write is fields: { stage: 'proposal' } (:155); the flow's own comment at :152-153: crm_opportunity has no last_activity_date field
5. "Sends you a confirmation email." notify_owner (:161-169): channels: ['inbox', 'email'], topic quote_created, template crm.quote_created, actionUrl: '/crm_quote/{quoteId.id}'. The email leaves the server only when outgoing mail is configured (measured below)
"attaches a PDF (or uses the built-in templating)" no quote template and no action on crm_quote exist anywhere in src/; the quote has enable.files: true (src/revenue/objects/quote.object.ts:280)

Steps 1 and 2 (Draft status, the opportunity's amount as subtotal, the discount applied) were right and stay, with the details create_quote sets added.

Measurements (pin @objectstack/* 17.7.0, on 49fe305a, through @objectstack/verify)

Two throwaway probes ran under the shared verify lock and were deleted afterwards (not committed). They booted the shipped artifact via hotcrmStack() and ran flows.run / flows.resume as a sales_rep with discount 10 and validity 30 days.

Stage rule, one $50K deal per stage:

stage before stage after run opportunity columns changed
prospecting proposal success stage, plus the stage-derived probability / forecast_category / expected_revenue, updated_at / updated_by
qualification proposal success same
needs_analysis proposal success same
proposal proposal success none
negotiation negotiation success none

The approval case is already pinned by test/flow-quote.test.ts (the #2015 block): a rep-created $200K deal with approval_status pending keeps qualification, and the quote is still drafted. Once the approval is decided (approved), the deal advances.

Quote fields written (all five runs): status draft, subtotal 50000, discount 10, discount_amount 5000, total_price 45000, quote_date today, expiration_date today + 30, payment_terms net_30, owner = the rep, tax / shipping_handling null.

No activity stamp: registeredFields('crm_opportunity') has no last_activity_date (crm_account has one). The account's last_activity_date was null before and after all five runs.

Notification:

  • Inbox: the delivery row is success after 1 attempt; the sys_inbox_message title is "Quote created: PQ-prospecting", action_url /crm_quote/ID.
  • Email, on the shared test boot (new EmailServicePlugin() with no options): every delivery failed 3 times with VALIDATION_FAILED: from address required, so no mail went out.
  • Email, on the options objectstack serve computes for a deployment that configured no mail (resolveEmailCapabilityArg({}, {}), called from the CLI, not hand-copied): provider log, sender no-reply@objectstack.local. The delivery row is success and the sys_email row reads sent, but the boot log says EmailServicePlugin: no transport configured — using LogTransport (mail will NOT be sent), and the send is [LogTransport] would send email … "subject":"Quote created: PQB-1". The settings binding also logs "Mail has never been configured — this is the out-of-the-box state."

So: inbox yes; email yes as a declaration, and on a default install nothing reaches a mailbox. The page now says that and points to the Email & Calendar guide, which already documents the log-only fallback.

The fix, sentence by sentence (en; the zh pages mirror it)

New sentence Matches
open an opportunity that is still open … not offered on a Closed Won or Closed Lost deal opportunity.actions.ts:221
Valid for (days) — default 30 flow :40
Discount % — default 0; the label shows the 60% ceiling flow :63-67 (defaultValue: 0, label interpolates QUOTE_DISCOUNT_CEILING)
1. Draft, the opportunity's amount as the subtotal; account and primary contact, you as owner, dated today, expires N days from today, Net 30; products not copied, no line items create_quote :79-88, :115; no node writes crm_quote_line_item
2. discount amount and total, to the cent :113-114 (rounded CEL envelopes)
3. Proposal only from Prospecting / Qualification / Needs Analysis; Proposal / Negotiation keep their stage; so does a deal whose Approval Status is Pending; you still get the quote e4a :204-206, e4b :207-209, update_opportunity :155
4. inbox message linking to the quote, and an email that reaches a mailbox only once outgoing mail is set up notify_owner :161-169, and the measurement above
no activity date stamped; the opportunity has no last-activity field; the account's Last Activity Date stays as it was :152-155, and the measurement above
attaches the quote document (a PDF) to the quote, and moves it to Presented when it goes to the customer quote.object.ts:280 (files: true)

UI nouns follow the page's existing usage and the language packs: the stages Prospecting / Qualification / Needs Analysis / Proposal / Negotiation in en, 寻找客户 / 资格审查 / 需求分析 / 提案 / 谈判 in zh-Hans with Traditional forms in zh-Hant; Approval Status Pending is 审批状态 审批中 (zh-CN pack objects.pipeline.ts:286-287).

Scope

  • Changed: content/docs/sales/quotes.mdx, quotes.zh-Hans.mdx, quotes.zh-Hant.mdx (§ Generating a quote only), and .changeset/2021-quotes-generating-a-quote.md (empty frontmatter; docs only, releases nothing).
  • The "built-in templating" sentence sits inside this section. Only its phantom half was corrected, worded so it stays consistent with the next section, which this PR leaves alone.

Gates

pnpm verify ran on a33872ab (git rev-parse --short HEAD, echoed by the run) under os-verify-lock.sh: VERDICT command-exit 0.

  • objectstack validate: ✓ Validation passed.
  • tsc --noEmit: clean.
  • objectstack lint --strict: 18 suggestions, no errors.
  • lint:i18n-gate: ✓ i18n lint gate: 0 i18n/missing-* issues.
  • hygiene: ✓ source hygiene clean.
  • hygiene:tokens: ✓ source token ratchet clean.
  • objectstack build: artifact written.
  • vitest run: Test Files 172 passed (172), Tests 3566 passed | 1 skipped (3567).

The docs checks that read these pages run inside vitest run. test/docs-anchor-links.test.ts parses every .mdx with remark-mdx and fumadocs' own remarkHeading, and resolves every link and anchor, including the two this PR adds: /docs/revenue/approvals and /docs/guides/email-and-calendar, plus their zh siblings.

Declared to CI, not run locally: the docs-app workflow's Fumadocs/Next build (apps/docs, which has its own lockfile and install). It is triggered by content/docs/**.

Acceptance notes (noted, not filed here)

  • src/revenue/data/revenue.seed.ts:123-125 says the seeded quote lines are "exactly what the quote_generation flow does when it clones opportunity lines onto a quote". The flow clones no lines. This is a source comment only, so the changeset and the page are unaffected.

Generated by Claude Code

The Quotes page's "Generating a quote" section, in en, zh-Hans and
zh-Hant, now matches the quote_generation flow: the deal moves to
Proposal only from Prospecting, Qualification or Needs Analysis and
keeps its stage while awaiting approval; no activity date is stamped;
the rep is notified in the in-app inbox and by email, and the email
leaves the server only once outgoing mail is configured. The phantom
"built-in templating" is dropped and the Generate Quote button's
open-deal-only visibility is stated.

Claude-Session: https://claude.ai/code/session_012zh91QzFgePbkmuHnugLN3
Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 8, 2026 3:27pm UTC

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(quotes): "Generating a quote" promises a last-activity stamp the flow never writes and an unconditional advance to Proposal (three locales)

2 participants