Skip to content

feat(product): remove crm_product.tax_rate — declared, read by nothing, ruled removed 2026-08-17 (#1198) - #1588

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-1198-product-tax-rate-removal-2
Sep 5, 2026
Merged

feat(product): remove crm_product.tax_rate — declared, read by nothing, ruled removed 2026-08-17 (#1198)#1588
os-zhuang merged 1 commit into
mainfrom
claude/issue-1198-product-tax-rate-removal-2

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #1198

Executes the maintainer ruling of 2026-08-17 (#1198 (comment), verbatim 「其他接受你的所有建议」; restored on 2026-08-31 in #1198 (comment) after the 08-22 re-escalation was found to have been made without reading the record): remove crm_product.tax_rate — the field, its four crm_product locale rows, its docs surface. Published-field deletion is authorised explicitly by that ruling.

The blocker that stopped the R32 round is gone: #1580 retired test/field-consumer-scan.test.ts and scripts/scan-field-consumers.ts on 2026-09-05, so the fixture collision no longer exists. This branch is cut from 021db549, the merge commit of that retirement.

The third option is not taken and not foreclosed. Making a product rate drive the quote-level tax figure remains a possible future design; it gets one sentence in the changeset and nothing else here.

Why the field goes

Declared on an apiEnabled object, absent from the product form (so only the REST API could ever set it), and read by nothing — no view column, no formula, no hook, no flow. Enforcing it was measured wrong on the real engine: stamping the product rate onto a line makes the quote's total 200 lower than the sum of its own line totals, and double-taxes a rep who also fills in the quote's manual tax amount. The card carries the full measurement.

What changed

Metadata (1 field, 4 locale rows)

  • src/objects/product.object.ts — the tax_rate: Field.percent({...}) block and its // Tax & billing comment, which covered only this field. The pricing field group survives on list_price / cost.
  • src/translations/{en,es-ES,ja-JP,zh-CN}.ts — one row each. Each deletion was made by line number after printing the enclosing object key, so the four crm_quote_line_item.tax_rate rows (a different object's live field, read by the line's total_price formula) are untouched. Verified after the fact: each pack went from 2 tax_rate occurrences to 1, and the survivor is inside crm_quote_line_item.

Docs — the 12 sites across the three faces (products.mdx, products.zh-Hans.mdx, products.zh-Hant.mdx, 4 sites each)

  1. the frontmatter description, which ended "SKUs and tax rates" / 「SKU 和税率」 / 「SKU 和稅率」;
  2. the Pricing field-group summary row (| **Pricing** | List price, cost, SKU, tax rate |);
  3. the Default Tax Rate % row in the Pricing fields table;
  4. the prose paragraph about the rate not being applied. It was there only to explain the inert field, so it is replaced by one sentence that keeps the still-true business concept — tax is a quote-level figure, the line item has its own rate, the catalog stores nothing about tax.

⛔ Not touched, and still true: the sales-rep tip "Tax is not filled in: enter it on the quote" is about quote-level tax. Each face keeps its own locale convention — the zh-Hant page continues to spell console navigation in English per the #1368 ruling, and nothing unrelated was "fixed" on these pages.

Two comment cross-references this deletion makes false

  • src/objects/quote_line_item.object.ts:60 — "and crm_product keeps tax_rate there too" is dropped; the rest of the field-group rationale is unchanged.
  • src/objects/opportunity_line_item.object.ts:63 — "crm_quote and crm_product both keep every money field — tax included — in pricing" attributed a tax field to crm_product that no longer exists. Re-attributed to crm_quote's tax, which is where it is true. This one is outside the dispatched surface and is declared as an in-place fix: same defect class as the clause above, mechanical, comment-only, no new verification surface, and no other claim holds the file.

One documented figure, protected by a pin

docs/STATUS.md:22 transcribes what pnpm validate prints; test/docs-declared-versions.test.ts (#1011) compares every figure in that transcript against the registered stack. Data: 18 Objects 334 Fields becomes 333 Fields. That is the pin doing its job, not a pin being adjusted — proven by ablation: with the figure left at 334 the test fails with "docs/STATUS.md transcribes figures that are not the current ones" (Tests 1 failed | 18 passed); restored to 333 it passes, and the restore was verified by blob hash against HEAD, not by an exit code.

Changeset.changeset/remove-product-default-tax-rate.md, minor. Not major, and the title drops the !: the comparable precedent in this repo is the enforce-or-remove sweep that deleted nine inert published fields, released under Minor Changes (540e488 in CHANGELOG.md), and the only major entry in the current changelog is a behaviour-breaking data-model change. It states that any value written to crm_product.tax_rate through the REST API is dropped, that no seeded catalog product ever carried one (all 13 sit at the 0 default), and that the product-driven quote tax remains a possible future design.

Gates — the repo's own chain, each command run separately, exit code captured by redirect before any pipe

Command Exit Verdict line
pnpm validate 0 ✓ Validation passed · Data: 18 Objects 333 Fields
pnpm typecheck 0 tsc --noEmit, no output
pnpm lint 0 90 warning(s), 12 suggestion(s) — no errors
pnpm lint:i18n-gate 0 ✓ i18n lint gate: 0 i18n/missing-* issues
pnpm hygiene 0 ✓ source hygiene clean
pnpm hygiene:tokens 0 ✓ source token ratchet clean — business semantics ~84,018 / ceiling ~85,000; authored total ~135,789 / ceiling ~140,000. No ceiling raised; the deletion lowers the count.
pnpm build 0 Artifact: dist/objectstack.json (2011.4 KB)
pnpm test 0 Test Files 159 passed (159) · Tests 3380 passed, 1 skipped (3381)

Reverse verification. Before the edit, on 021db549: pnpm validate exit 0 at Data: 18 Objects 334 Fields, pnpm test exit 0 at 159 passed / 3380 passed, 1 skipped. After: validate exit 0 at 333 Fields — exactly one field fewer, the positive control that validate re-read the tree — and the same 159 files / 3380 tests green with the one documented figure updated. No test needed adjusting; no pin went red for any reason other than the figure above.

The four locale packs keep identical crm_product field key sets after the deletion (13 direct field keys each, symmetric difference against en empty), which is why the i18n gate stays at 0 missing-key issues.


Generated by Claude Code

The field was declared on an apiEnabled object, absent from the product form,
and read by nothing: no view column, no formula, no hook, no flow. Enforcing it
was measured wrong (a stamped rate makes a quote total disagree with the sum of
its own line totals and double-taxes a rep who also fills the quote's Tax
amount), so the maintainer ruled removal on 2026-08-17.

Removes the field, its four crm_product locale rows, and the twelve product-doc
sites across the three faces. Fixes two comment cross-references the deletion
makes false, and updates the validator transcript in docs/STATUS.md from 334 to
333 fields, which test/docs-declared-versions.test.ts pins against the stack.

The quote line item's own tax_rate and the quote's manual Tax amount are
untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TezFG8ZMrNH6n5VTNpPpdH
@vercel

vercel Bot commented Sep 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
hotcrm Ignored Ignored Sep 5, 2026 3:17pm UTC

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation metadata Declarative metadata — schema, security posture, UI surfaces labels Sep 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review September 5, 2026 15:20
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 1486ec0 Sep 5, 2026
11 checks passed
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 metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] crm_product.tax_rate: enforcing it is measurably wrong, so the remaining option is removal — which needs a ruling

2 participants