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
47 changes: 47 additions & 0 deletions .changeset/account-registration-category-and-approval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
'hotcrm': minor
---

An account now records **who it is on paper**, **what it spends**, **what it may be sold**,
and **whether it has been signed off**. Six new fields on `crm_account`, a new **Business
Profile** section, a capability gate on new opportunities, and an account approval flow
(REQ-0003 — the `crm_account` half of REQ-0002's steps 1, 3, 4 and 5).

**Registration Number.** The counterparty's registered identity as issued by its own
government registry. Until now nothing on the account carried one: `Account Number` is a
sequence *we* issue and already spends itself as half the record title, so invoicing,
credit checks and entity resolution had no key to work from. The field is deliberately
country-neutral — the requirement arrived as 社会信用代码 because that customer registers
in China, but a Japanese install writes 法人番号 here, a German one a
Handelsregisternummer and a US one an EIN, and a field named after one country's registry
could not be renamed by any of them. It is searchable, so the value is findable from
global search and the lookup picker.

**Commercial Capability, and the gate that reads it.** Some counterparties are payable but
not sellable — tender agencies, resellers-of-record, intra-group billing entities, dormant
accounts. An account set to *Settlement Only* stays fully usable for invoicing, payments
and collections, but **a new opportunity can no longer be opened against it**: the save is
refused, naming the account and the remedy. The restriction is a **transition gate, not an
invariant** — opportunities that already point at the account keep working and keep being
editable, including after someone reclassifies it, and only the new link is refused. The
gate sits beside `Account Type` rather than inside it, because type is a relationship
stage that the system itself advances (a won deal promotes an account to *Customer*), and
a restriction that a won deal could silently overwrite would not be a restriction.

**Business Profile** — **Incumbent Vendor**, **Annual Purchasing Budget** and **Payment
Cycle**, in their own new section. Generic B2B sales intelligence: who holds the account
today, how much there is to win this year, and how they pay. The existing `Annual Revenue`
answers none of the three — it is what the counterparty *earns*, not what it will *spend*
with vendors.

**Approval Status**, and the **Account Approval** flow behind it. A newly created account
now starts at *Pending*, appears in the approval inbox HotCRM already mounts, and reaches
*Approved* only through a decision on that request. The shape is the one
`crm_opportunity.approval_status` already uses — `readonly`, with a real field-level
default — and not a second boolean beside `Active`, which answers a different question
("is this account live at all", rather than "has this record been signed off"). An install
that wants no account sign-off changes that one default to *Approved*, whereupon the flow's
start condition is false for every record and the gate is off but still switchable.

Ships in all four locale packs (en, zh-CN, es-ES, ja-JP), with the account, automation and
approval documentation updated in English, Simplified and Traditional Chinese.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
# HotCRM

> **The reference app for AI-written enterprise software.** A complete CRM —
> 18 objects, 30 flows, 5 dashboards, 6 AI skills, 4 languages — built as four
> 18 objects, 31 flows, 5 dashboards, 6 AI skills, 4 languages — built as four
> packages (sales, service, revenue, marketing) that compile to one artifact.
> The **sales package**, the one a customer installs, carries its whole
> business semantics (objects, flows, actions, hooks) in **~50k tokens**
> business semantics (objects, flows, actions, hooks) in **~54k tokens**
> of typed [ObjectStack](https://github.com/objectstack-ai/objectstack) metadata,
> and its whole interaction layer (views, pages, dashboards, app shell) in
> another **~29k**. An agent holds every business rule of a real enterprise CRM
Expand Down Expand Up @@ -69,7 +69,7 @@ HotCRM is a complete, opinionated CRM built as the **first official application*
| `crm_event` | | | |
| `crm_event_attendee` | | | |

Plus **6 AI skills** (a skills-only surface — HotCRM defines no agents of its own; the skills attach to the platform `ask` assistant), **5 dashboards**, **30 flows**, **31 actions**, **9 datasets**, **4 language bundles** (en, zh-CN, es-ES, ja-JP), **6 permission profiles**, **12 positions**, and **9 sharing rules**.
Plus **6 AI skills** (a skills-only surface — HotCRM defines no agents of its own; the skills attach to the platform `ask` assistant), **5 dashboards**, **31 flows**, **31 actions**, **9 datasets**, **4 language bundles** (en, zh-CN, es-ES, ja-JP), **6 permission profiles**, **12 positions**, and **9 sharing rules**.

> **Business reader?** The ObjectStack docs tour every one of these capabilities in plain business language — [What Can It Do?](https://objectstack.ai/docs/capabilities) — with HotCRM as the running example on every page.

Expand Down
3 changes: 2 additions & 1 deletion content/docs/administration/automation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ A flow fires one of three ways, set by its start node:

> **Auto-launch needs the `triggers` capability.** Record-change and scheduled flows only fire when the stack's `requires` list includes `triggers` — it installs the record-change + schedule trigger providers (schedule triggers also use the job service). Screen flows are always launched manually.

**Built-in flows in HotCRM** (30). Each row carries the flow's own label — the name listed in **Studio → Automation → Flows**, and the name you pick from in **Studio → Developer → Flow Runs**, so a run you are chasing can be looked up here verbatim:
**Built-in flows in HotCRM** (31). Each row carries the flow's own label — the name listed in **Studio → Automation → Flows**, and the name you pick from in **Studio → Developer → Flow Runs**, so a run you are chasing can be looked up here verbatim:

| Flow | Trigger | What it does |
| --- | --- | --- |
Expand All @@ -70,6 +70,7 @@ A flow fires one of three ways, set by its start node:
| **Large Deal Approval** | Record change (update) | Tiered sign-off via approval nodes — Sales Manager ≥ $100K, Sales Director > $500K |
| **Large Deal Approval (on create)** | Record change (insert) | The same intake for opportunities *created* at or above the threshold |
| **Lead Conversion Approval** | Record change (insert) | Sign-off a lead needs before it can be converted. Ships switched OFF — it opens nothing until an install arms the gate on the lead's **Conversion Approval** field |
| **Account Approval** | Record change (insert) | Sign-off a newly created account needs before it counts as established data. Ships switched **ON** — a new account starts *Pending* and is decided in the approval inbox; an install that wants no account sign-off changes the default on the account's **Approval Status** field |
| **Large Deal Won Alert** | Record change (update) | When an opportunity of $100K or more turns *Closed Won*, notify the owner — the owner alone, not their manager |
| **Billing Hand-off: Closed Won** | Record change (update) | On the transition into *Closed Won*, enqueue one durable POST of the deal + account + line items to the billing endpoint |
| **Billing Hand-off: Contract Activated** | Record change (update) | On the transition into *Activated*, enqueue one durable POST of the contract + account + the originating deal's line items |
Expand Down
3 changes: 2 additions & 1 deletion content/docs/administration/automation.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ description: 验证规则、流程、计划作业与审批 —— 无需你动

> **自动触发需要 `triggers` 能力。** 记录变更与计划类流程,只有当 stack 的 `requires` 列表包含 `triggers` 时才会触发 —— 它会安装记录变更与计划触发器提供方(计划触发器还依赖 job 服务)。屏幕类流程始终为手动启动。

**HotCRM 中的内置流程**(30 个)。每一行用的都是流程自身的标签 —— 也就是 **Studio → Automation → Flows** 里列出、并在 **Studio → Developer → Flow Runs** 里供你挑选的那个名字,因此一次运行可以逐字回到这张表里查:
**HotCRM 中的内置流程**(31 个)。每一行用的都是流程自身的标签 —— 也就是 **Studio → Automation → Flows** 里列出、并在 **Studio → Developer → Flow Runs** 里供你挑选的那个名字,因此一次运行可以逐字回到这张表里查:

| 流程 | 触发 | 它做什么 |
| --- | --- | --- |
Expand All @@ -70,6 +70,7 @@ description: 验证规则、流程、计划作业与审批 —— 无需你动
| **大额商机审批** | 记录变更(更新) | 通过 approval 节点分级签核 —— 销售经理 ≥ $100K,销售总监 > $500K |
| **大额商机审批(新建时)** | 记录变更(插入) | 同一套受理逻辑,用于*创建时*金额已达到阈值的商机 |
| **线索转化审批** | 记录变更(插入) | 线索转化前需要的签核。出厂默认是**关闭**的:在线索的**转化审批**字段上打开闸门之前,它不会发起任何审批 |
| **客户审批** | 记录变更(插入) | 新建客户在成为正式数据之前需要的签核。出厂默认是**打开**的:新客户以**待审批**状态创建,并在审批收件箱中裁定;不需要客户签核的安装,改掉客户**审批状态**字段的默认值即可 |
| **大额商机赢单提醒** | 记录变更(更新) | 金额 $100K 及以上的商机转为 *Closed Won* 时,通知负责人 —— 只通知负责人本人,不通知其经理 |
| **计费交接:赢单** | 记录变更(更新) | 商机转入 *Closed Won* 的那一刻,向计费端点投递一次带重试的 POST:商机 + 客户 + 行项目 |
| **计费交接:合同激活** | 记录变更(更新) | 合同转入 *Activated* 的那一刻,向计费端点投递一次带重试的 POST:合同 + 客户 + 来源商机的行项目 |
Expand Down
3 changes: 2 additions & 1 deletion content/docs/administration/automation.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ description: 驗證規則、流程、排程作業與審批 —— 無需你動

> **自動觸發需要 `triggers` 能力。** 記錄變更與排程類流程,只有當 stack 的 `requires` 列表包含 `triggers` 時才會觸發 —— 它會安裝記錄變更與排程觸發器提供方(排程觸發器還依賴 job 服務)。螢幕類流程始終為手動啟動。

**HotCRM 中的內建流程**(30 個)。每一行用的都是流程自身的標籤 —— 也就是 **Studio → Automation → Flows** 裡列出、並在 **Studio → Developer → Flow Runs** 裡供你挑選的那個名字,因此一次執行可以逐字回到這張表裡查:
**HotCRM 中的內建流程**(31 個)。每一行用的都是流程自身的標籤 —— 也就是 **Studio → Automation → Flows** 裡列出、並在 **Studio → Developer → Flow Runs** 裡供你挑選的那個名字,因此一次執行可以逐字回到這張表裡查:

| 流程 | 觸發 | 它做什麼 |
| --- | --- | --- |
Expand All @@ -72,6 +72,7 @@ description: 驗證規則、流程、排程作業與審批 —— 無需你動
| **大額商機審批** | 記錄變更(更新) | 透過 approval 節點分級簽核 —— 銷售經理 ≥ $100K,銷售總監 > $500K |
| **大額商機審批(新建時)** | 記錄變更(插入) | 同一套受理邏輯,用於*建立時*金額已達到閾值的商機 |
| **線索轉化審批** | 記錄變更(插入) | 線索轉化前需要的簽核。出廠預設是**關閉**的:在線索的**轉化審批**欄位上打開閘門之前,它不會發起任何審批 |
| **客戶審批** | 記錄變更(插入) | 新建客戶在成為正式資料之前需要的簽核。出廠預設是**打開**的:新客戶以**待審批**狀態建立,並在審批收件匣中裁定;不需要客戶簽核的安裝,改掉客戶**審批狀態**欄位的預設值即可 |
| **大額商機贏單提醒** | 記錄變更(更新) | 金額 $100K 及以上的商機轉為 *Closed Won* 時,通知負責人 —— 只通知負責人本人,不通知其經理 |
| **計費交接:贏單** | 記錄變更(更新) | 商機轉入 *Closed Won* 的那一刻,向計費端點投遞一次帶重試的 POST:商機 + 客戶 + 行項目 |
| **計費交接:合約啟用** | 記錄變更(更新) | 合約轉入 *Activated* 的那一刻,向計費端點投遞一次帶重試的 POST:合約 + 客戶 + 來源商機的行項目 |
Expand Down
40 changes: 37 additions & 3 deletions content/docs/sales/accounts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,52 @@ Every account has a **type** that drives how the rest of the system treats it:

When a contract is activated for a prospect, the system automatically promotes the account to **Customer**. You don't have to remember to update it.

## Commercial capability — who you may open deals with

**Type** says where an account stands in its relationship with you. **Commercial Capability** answers a different question: *may we book new business against it at all?*

| Commercial Capability | What it means |
| --- | --- |
| 🟢 **Full** | Business as usual. *(Default for new accounts.)* |
| 🟠 **Settlement Only** | Keep the record, refuse the deal. The account stays fully usable for invoicing, payments and collections, but **no new opportunity may be opened against it**. |

Tender agencies, resellers-of-record, intra-group billing entities and dormant accounts are the usual *Settlement Only* cases: you still pay them and get paid through them, you just do not sell to them.

This is a **gate, not a label**. Pick an account marked *Settlement Only* on a new opportunity and the save is refused, with a message naming the account and telling you what to do instead. Two things it deliberately does **not** do:

- **It never breaks history.** Opportunities that already point at the account keep working and stay editable, including after someone reclassifies the account. Only the *new* link is refused.
- **It is not a compliance lock.** It is a classification a person writes down, and the same person can set it back to *Full* when the relationship changes.

The two axes are independent: a *Settlement Only* account can be a prospect, a customer or a former customer, and winning a deal never quietly un-restricts one.

## Account approval

A newly created account starts at **Pending**. It shows up in the approval inbox, and a Sales Manager approves or rejects it. **Approval Status** is read-only on the form, so it only ever moves through a decision on that request — nobody can type their way to *Approved*. A rejected account stays on file, so its details can be corrected and sent back for another look.

**Approval Status** answers *"has this record been signed off?"*. **Active** answers *"is this account live at all?"*. They are deliberately separate: an approved account can later be deactivated, and a pending account is not the same thing as an inactive one.

If your organisation does not want to sign accounts off, an admin changes the default on **Approval Status** to *Approved* and no new account enters the queue again — see [Administration › Automation](/docs/administration/automation).

## What each account record stores

The account detail screen is organised into 6 sections:
The account detail screen is organised into 7 sections:

| Section | Fields |
| --- | --- |
| **Basic Information** | Account number, name, type, industry, description |
| **Basic Information** | Account number, registration number, name, type, commercial capability, industry, description |
| **Financials** | Annual revenue, number of employees, child account revenue |
| **Business Profile** | Incumbent vendor, annual purchasing budget, payment cycle |
| **Contact Information** | Phone, website, billing address, office location (with map) |
| **Ownership & Status** | Owner, parent account, active flag |
| **Ownership & Status** | Owner, parent account, active flag, approval status |
| **Branding** | Brand color (used as the avatar accent), company logo (uploaded image — PNG, JPEG, SVG or WebP, up to 2 MB) |
| **System** | Timestamps, audit trail |

Two of those need a word of explanation.

**Registration Number** is the counterparty's registered identity as issued by *its own* government registry — whichever registry that is. It is deliberately not named after any one country's scheme, because the right answer differs by where the company is registered, and the field has to hold all of them. It is what invoicing, credit checks and matching duplicate companies key on, and it is searchable, so you can find an account by it from global search and from any account picker. It is **not** the **Account Number**, which is the sequence *we* issue and which already appears in the record title.

**Business Profile** is sales intelligence about how this account buys: who currently holds it (**Incumbent Vendor**), how much it plans to spend with suppliers this year (**Annual Purchasing Budget**), and how it pays (**Payment Cycle**). None of that is the same as **Annual Revenue**, which is what the account *earns* — the budget is what it will *spend*.

## Account hierarchy

Use **Parent Account** to model global companies with subsidiaries:
Expand All @@ -63,6 +96,7 @@ Two things the hierarchy deliberately does **not** do. It does **not** cascade s
- The account **name must be unique within your organization**. You'll get an error if you try to create a second account with a name you already use. Another organization on the same platform is free to have its own "Acme Corp" — the rule is scoped to your data, not the whole platform. The uniqueness check compares names exactly as typed, so `Acme Corp` and `ACME Corp` can both exist as separate accounts. Lead conversion is more forgiving: it ignores capitalisation and extra spaces when looking for an account to reuse (see [Leads](./leads)), so converting a lead for `ACME Corp` attaches it to your existing `Acme Corp` rather than creating a near-duplicate.
- **Annual revenue must be zero or positive** — no negative numbers allowed.
- You **cannot delete** an account that has open opportunities or active contracts. Set it to *Inactive* instead (`is_active = false`).
- You **cannot open a new opportunity** against an account whose **Commercial Capability** is *Settlement Only*. The opportunities it already has are untouched and stay editable.

## Automatic updates

Expand Down
Loading
Loading