Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .changeset/decorative-field-sweep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
'hotcrm': minor
---

Enforce-or-remove, decided per field: nine inert fields are gone, and the
account hierarchy is now a real roll-up.

A consumer scan found ten declared fields that nothing in the product read or
wrote. Each got its own verdict rather than one verdict for the batch. The
common thread in the nine removals is that none of them looked absent — they
looked wired, which is the shape that misleads the next author into building on
them.

**Removed from `crm_product`:**

- **Quantity on Hand** and **Reorder Point**, with the **Low Stock** list view
and its **Low Stock** tab. Nothing ever decremented stock, and the view was
not the report it looked like: its filter compared quantity against a
hardcoded `10` rather than each product's own reorder point. HotCRM sells from
a catalog; stock belongs to the fulfilment system that owns it.
- **Taxable**. No quote or line item ever consulted it —
`crm_quote_line_item.tax_rate` defaults to `0` and is typed per line — so
clearing the flag on a zero-rated product changed no total anywhere.
- **Billing Type** and **Unit of Measure**, and the seeded values on all 13
demo products. The docs said billing type "drives how the quote calculates
totals"; no quote total, revenue report or line-item behaviour ever read it.

**Removed elsewhere:**

- `crm_case.customer_signature` — a signature pad on the resolution form that
no close-case step, SLA measure or export ever read back.
- `crm_case.parent_case` — a case hierarchy nothing traversed: no rollup, no
cascading close, no related-case handling.
- `crm_task.estimated_hours` / `actual_hours` — no rollup summed them, no
variance report compared them, nothing warned when actual overran estimate.
- `crm_contact.reports_to` — the org chart the contact docs described as "a
clickable tree on the account detail page" that the Copilot "uses when
summarising the account". Neither existed: no page rendered it and no skill
read it.
- `crm_contact.birthdate` — importable, read by nothing. Personal data held for
no stated purpose is a liability rather than a feature.
- `crm_campaign.parent_campaign` — campaign hierarchy, mentioned in no doc and
read by nothing. Its honest consumer would be a rolled-up ROI, which cannot
be one declaration: `roi` is a formula over each campaign's own cost and
revenue, so a hierarchy ROI would put two differently-scoped ROI numbers on
one record.

**Kept and enforced — `crm_account.parent_account`:**

The hierarchy stays, and now does something. `crm_account` gains **Child Account
Revenue** (`child_account_revenue`), a roll-up of the **Annual Revenue** of an
account's **direct** children, maintained by the platform: it moves when a
child's revenue is edited, when a child is re-parented, and when a child is
deleted. This is the roll-up the accounts documentation already promised and
never had. It is one level deep — a grandparent totals its own children, not the
whole tree — and it is on the account form's **Financials** section.

**Migration — what changes for users:**

- **Product → Low Stock** view and tab are gone, as are the *Inventory
tracking*, *Billing types* and *Units of measure* sections of the product
documentation. Values previously stored in the five removed product fields are
dropped with the columns; export them first if your org typed anything into
them by hand.
- The **contact import template** no longer accepts a `Birthdate` column. A file
that still carries one will have that column ignored — re-cut your template
from *Guides → Importing your data*. The **account** template is unchanged and
still carries `Parent Account`.
- The case form loses **Parent Case** (SLA tab) and **Customer Signature**
(Resolution tab); the task form loses **Estimated Hours** and **Actual
Hours**; the contact form loses **Reports To** and **Birthday**; the campaign
form loses **Parent Campaign**. Any values held in those columns are dropped
with them.
- Accounts gain a read-only **Child Account Revenue** on the *Financials*
section. It is computed, not typed, and is blank (zero) for an account with no
children.
- The account hierarchy still does **not** cascade sharing — it never did, and
the accounts page said otherwise until now. Use the sharing rules under
*Administration → Sharing* for that.

Authorized by the maintainer ruling of 2026-08-17, 「逐个 enforce-or-remove
(推荐)」: a declared-but-unenforced field ends the card either genuinely
enforced or genuinely gone.
102 changes: 51 additions & 51 deletions assets/import-templates/contacts.csv

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion content/docs/administration/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ What follows from that is on the SLA side: deadlines run on **calendar hours**.

### 9. Product catalog

- [ ] Import [products](/docs/revenue/products) — at minimum: code, name, list price, billing type
- [ ] Import [products](/docs/revenue/products) — at minimum: code, name, list price
- [ ] Set up product manager (the contact for each product line)
- [ ] Upload datasheets — a PDF file field on the product record, there for people to open; no knowledge base ingests it

Expand Down
2 changes: 1 addition & 1 deletion content/docs/administration/setup.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ description: 让 HotCRM 为你的团队做好生产准备的第一周任务。

### 9. 产品目录

- [ ] 导入 [产品](/zh-Hans/docs/revenue/products) —— 至少:代码、名称、标价、计费类型
- [ ] 导入 [产品](/zh-Hans/docs/revenue/products) —— 至少:代码、名称、标价
- [ ] 设置产品经理(每条产品线的联系人)
- [ ] 上传数据表 —— 产品记录上的一个 PDF 文件字段,供人打开查看;没有任何知识库会摄取它

Expand Down
2 changes: 1 addition & 1 deletion content/docs/administration/setup.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ description: 讓 HotCRM 為你的團隊做好生產準備的第一週任務。

### 9. 產品目錄

- [ ] 匯入 [產品](/zh-Hant/docs/revenue/products) —— 至少:代碼、名稱、標價、計費類型
- [ ] 匯入 [產品](/zh-Hant/docs/revenue/products) —— 至少:代碼、名稱、標價
- [ ] 設置產品經理(每條產品線的聯絡人)
- [ ] 上傳資料表 —— 產品記錄上的一個 PDF 檔案欄位,供人開啟檢視;沒有任何知識庫會攝取它

Expand Down
2 changes: 1 addition & 1 deletion content/docs/guides/importing-your-data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ file. *Website* must be a full URL (`https://…`) or the row is rejected.

`Salutation` · `First Name` · `Last Name` · `Account Name` · `Title` ·
`Department` · `Email` · `Phone` · `Mobile` · `Mailing Street` · `Mailing City` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` · `Birthdate` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` ·
`Lead Source` · `Contact Owner Email` · `Description`

*First Name*, *Last Name*, *Email* and *Account Name* are required.
Expand Down
2 changes: 1 addition & 1 deletion content/docs/guides/importing-your-data.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ curl -X POST "$HOTCRM/api/v1/data/import/jobs/$JOB_ID/undo" -H "Authorization: B

`Salutation` · `First Name` · `Last Name` · `Account Name` · `Title` ·
`Department` · `Email` · `Phone` · `Mobile` · `Mailing Street` · `Mailing City` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` · `Birthdate` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` ·
`Lead Source` · `Contact Owner Email` · `Description`

*First Name*、*Last Name*、*Email* 和 *Account Name* 为必填。
Expand Down
2 changes: 1 addition & 1 deletion content/docs/guides/importing-your-data.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ curl -X POST "$HOTCRM/api/v1/data/import/jobs/$JOB_ID/undo" -H "Authorization: B

`Salutation` · `First Name` · `Last Name` · `Account Name` · `Title` ·
`Department` · `Email` · `Phone` · `Mobile` · `Mailing Street` · `Mailing City` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` · `Birthdate` ·
`Mailing State` · `Mailing Postal Code` · `Mailing Country` ·
`Lead Source` · `Contact Owner Email` · `Description`

*First Name*、*Last Name*、*Email* 與 *Account Name* 為必填。
Expand Down
2 changes: 1 addition & 1 deletion content/docs/revenue/billing-handoff.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ That is a deliberate boundary, not a gap. Billing, tax, collections and revenue

Two consequences worth stating plainly, because people look for them:

- **`Quantity on Hand` on a product is display-only.** Closing a deal does not decrement it. Stock movement belongs to the fulfilment system.
- **HotCRM holds no stock figure at all.** A product carries no quantity on hand, and closing a deal decrements nothing. Inventory is the fulfilment system’s record, and this app deliberately keeps no second copy of it.
- **Payment status is never mirrored back.** Nothing in HotCRM tells you whether an invoice was paid. A future inbound integration could, but it does not exist today, and no field pretends otherwise.

## The two events
Expand Down
2 changes: 1 addition & 1 deletion content/docs/revenue/billing-handoff.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ HotCRM 负责营收的**商务**一侧:找到客户、谈定价格、签下合

有两点值得明说,因为总有人来找:

- **产品上的「库存数量」只用于展示。** 赢单不会扣减它。库存变动属于履约系统。
- **HotCRM 根本不保存库存数字。** 产品上没有库存数量字段,赢单也不会扣减任何东西。库存是履约系统的记录,本应用刻意不保留第二份副本。
- **回款状态永远不会写回来。** HotCRM 里没有任何字段告诉你发票是否已付。未来的入站集成可以做到这件事,但今天它不存在,也没有字段假装它存在。

## 两个事件
Expand Down
2 changes: 1 addition & 1 deletion content/docs/revenue/billing-handoff.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ HotCRM 負責營收的**商務**一側:找到客戶、談定價格、簽下合

有兩點值得明說,因為總有人來找:

- **產品上的「庫存數量」只用於顯示。** 贏單不會扣減它。庫存異動屬於履約系統。
- **HotCRM 根本不保存庫存數字。** 產品上沒有庫存數量欄位,贏單也不會扣減任何東西。庫存是履約系統的記錄,本應用刻意不保留第二份副本。
- **收款狀態永遠不會寫回來。** HotCRM 裡沒有任何欄位告訴你發票是否已付。未來的入站整合可以做到這件事,但今天它不存在,也沒有欄位假裝它存在。

## 兩個事件
Expand Down
4 changes: 2 additions & 2 deletions content/docs/revenue/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The Revenue module covers everything that happens **after sales says "we won"**:

| Area | What it does |
| --- | --- |
| [**Products**](/docs/revenue/products) | The catalog of what you sell — names, codes, list prices, billing types. |
| [**Products**](/docs/revenue/products) | The catalog of what you sell — names, codes, list prices, SKUs. |
| [**Contracts**](/docs/revenue/contracts) | Signed agreements with customers — terms, value, renewals. |
| [**Approvals**](/docs/revenue/approvals) | Multi-step approval for high-value discounts. |
| [**Billing Hand-off**](/docs/revenue/billing-handoff) | Where CRM scope ends — the two outbound events that hand a won deal and an activated contract to billing. |
Expand All @@ -31,7 +31,7 @@ Contract (Activated)
Renewal task ──► Renewal opportunity
```

1. **Set up the catalog** — Products with codes, list prices, billing types.
1. **Set up the catalog** — Products with codes, list prices and SKUs.
2. **Build quotes** with line items from the catalog (see [Quotes](/docs/sales/quotes)).
3. **On acceptance** the system creates a contract with the quote's terms.
4. **Contract is activated** — the account is promoted to *Customer*.
Expand Down
4 changes: 2 additions & 2 deletions content/docs/revenue/index.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: 产品目录、合同生命周期、续约,以及高价值折扣

| 领域 | 作用 |
| --- | --- |
| [**产品**](/zh-Hans/docs/revenue/products) | 你所销售内容的目录——名称、编码、标价、计费类型。 |
| [**产品**](/zh-Hans/docs/revenue/products) | 你所销售内容的目录——名称、编码、标价、SKU。 |
| [**合同**](/zh-Hans/docs/revenue/contracts) | 与客户签署的协议——条款、价值、续约。 |
| [**审批**](/zh-Hans/docs/revenue/approvals) | 针对高价值折扣的多步骤审批。 |
| [**计费交接**](/zh-Hans/docs/revenue/billing-handoff) | CRM 范围的终点——把赢单商机与已激活合同交给计费系统的两个出站事件。 |
Expand All @@ -31,7 +31,7 @@ Contract (Activated)
Renewal task ──► Renewal opportunity
```

1. **建立目录**——带编码、标价、计费类型的产品。
1. **建立目录**——带编码、标价、SKU 的产品。
2. **构建报价**——使用目录中的明细行(参见 [报价](/zh-Hans/docs/sales/quotes))。
3. **接受时**系统会以报价的条款创建一份合同。
4. **合同被激活**——账户被提升为 *Customer*。
Expand Down
4 changes: 2 additions & 2 deletions content/docs/revenue/index.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: 產品目錄、合約生命週期、續約,以及高價值折扣

| 領域 | 作用 |
| --- | --- |
| [**產品**](/zh-Hant/docs/revenue/products) | 你所銷售內容的目錄——名稱、編碼、標價、計費類型。 |
| [**產品**](/zh-Hant/docs/revenue/products) | 你所銷售內容的目錄——名稱、編碼、標價、SKU。 |
| [**合約**](/zh-Hant/docs/revenue/contracts) | 與客戶簽署的協議——條款、價值、續約。 |
| [**審批**](/zh-Hant/docs/revenue/approvals) | 針對高價值折扣的多步驟審批。 |
| [**計費交接**](/zh-Hant/docs/revenue/billing-handoff) | CRM 範圍的終點——把贏單商機與已啟用合約交給計費系統的兩個出站事件。 |
Expand All @@ -31,7 +31,7 @@ Contract (Activated)
Renewal task ──► Renewal opportunity
```

1. **建立目錄**——帶編碼、標價、計費類型的產品。
1. **建立目錄**——帶編碼、標價、SKU 的產品。
2. **構建報價**——使用目錄中的明細行(參見 [報價](/zh-Hant/docs/sales/quotes))。
3. **接受時**系統會以報價的條款建立一份合約。
4. **合約被啟用**——帳戶被提升為 *Customer*。
Expand Down
43 changes: 6 additions & 37 deletions content/docs/revenue/products.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Products
description: The catalog of what you sell — codes, list prices, billing types and SKUs.
description: The catalog of what you sell — codes, list prices, SKUs and tax rates.
---

# Products
Expand All @@ -14,7 +14,7 @@ The detail screen has 3 sections:
| Section | Fields |
| --- | --- |
| **Product Information** | Product code *(auto)*, name, description, category, family |
| **Pricing & Billing** | List price, cost, SKU, billing type, unit of measure, tax rate, taxable flag |
| **Pricing** | List price, cost, SKU, tax rate |
| **Resources** | Product image, datasheet, product manager |

## Categories and families
Expand All @@ -38,48 +38,19 @@ Categorise products two ways:

Categories and families let you slice reports by product mix — *"how much SMB-tier subscription revenue did we book this quarter?"*

## Billing types

| Billing type | Use for |
| --- | --- |
| **One-Time** *(default)* | Perpetual licenses, one-off purchases |
| **Monthly** | Monthly subscriptions |
| **Quarterly** | Quarterly subscriptions |
| **Annual** | Annual subscriptions |
| **Usage** | Consumption-based (per API call, per GB, etc.) |

The billing type drives how the quote calculates totals (one-time fee vs. recurring) and feeds into revenue recognition reporting.

## Units of measure

Specify how the product is sold:

- **Each** *(default)* — one unit per quantity
- **License** — per software license
- **Seat** — per named user
- **Hour** — for services
- **Day** — for services
- **Month** — for time-bounded subscriptions

## Pricing fields

| Field | Purpose |
| --- | --- |
| **List Price** | Standard published price — what shows up on a quote by default |
| **Cost** | Your cost — used for margin calculation |
| **Default Tax Rate %** | Auto-applied to quote line items |
| **Taxable** | If unchecked, no tax is added (zero-rated products) |
| **Default Tax Rate %** | The rate carried on the product record |

The system warns you if Cost is greater than List Price (which would be a losing sale).

## Inventory tracking

For physical products:

- **Quantity on Hand** — current inventory.
- **Reorder Point** — when to reorder.
## No inventory, no billing schedule

These fields are optional — leave them empty for non-inventory items (software, services).
A product record is a catalog entry and nothing more. HotCRM holds **no stock figure** — no quantity on hand, no reorder point, and nothing decrements on a closed deal — and **no billing schedule or unit of measure**: how a subscription is billed lives in the contract and the quote, not on the catalog row. Those fields used to be declared here and were read by nothing, which made the catalog look like an inventory and billing system it never was. Stock belongs to your fulfilment system; see [Billing Handoff](/docs/revenue/billing-handoff).

## The product image and datasheet

Expand All @@ -105,8 +76,6 @@ The product catalog is **public read-only** for the whole organisation. Sales re
- **Active Products** — `is_active = true`.
- **By Category** — grouped.
- **By Family** — grouped.
- **Subscriptions** — `category = subscription OR billing_type IN [monthly, quarterly, annual]`.
- **Low Stock** — `quantity_on_hand < reorder_point` (for inventory products).

## Tips for product managers

Expand All @@ -121,5 +90,5 @@ The product catalog is **public read-only** for the whole organisation. Sales re

## Tips for admins

- The category, family, billing type and unit-of-measure picklists can be extended. See [Customization › Extending Objects](/docs/customization/extending-objects).
- The category and family picklists can be extended. See [Customization › Extending Objects](/docs/customization/extending-objects).
- A product can be linked to multiple opportunities and quotes — deactivation doesn't break historical records.
Loading
Loading