diff --git a/.changeset/first-response-and-lead-reports-to-source.md b/.changeset/first-response-and-lead-reports-to-source.md new file mode 100644 index 000000000..313c6c489 --- /dev/null +++ b/.changeset/first-response-and-lead-reports-to-source.md @@ -0,0 +1,61 @@ +--- +'hotcrm': patch +--- + +Write the "first-response SLA", the Lead reports section and two enumeration-free +behaviour claims to source, in all three locales. + +**A measurable first-response target never existed.** Three pages presented one as +if the app kept it: the setup checklist's §11 SLA matrix carried an entire *First +response* column (1 hour at Critical through 1 business day at Low), the reports +page promised *"% first-response on time"*, and the Service Cube listed both a +*First-response time (minutes)* measure and an *SLA met %*. `first_response_date` +does have a writer — `logActivityAction` in `src/actions/global.actions.ts` stamps +it the first time a call or meeting that already took place is logged on a case — +but nothing in the repo compares that stamp against a target: `case_metrics` +(`src/datasets/case.dataset.ts`) declares no first-response measure, no report or +tile reads it, and no flow alerts on it. The four numbers themselves appear +nowhere, and `content/docs/service/sla-and-escalation`, which the checklist links +to for them, does not mention a first response at all. All three pages now name +the stamp, name what is absent, and state the first-response promise for what it +is — the team's own service commitment. The SLA matrix keeps its resolution +column, with Critical marked as the one row `case_sla_defaults` turns into a +deadline. The cube's SLA measure is named as what `case_metrics` really declares: +an **SLA Violation Rate**, not a compliance percentage. + +**The Service reports table listed six reports where `src/reports/case.report.ts` +publishes three.** *Cases by Status and Priority*, the SLA report and *Cases +Opened by Priority × Day* are real; *Case Volume by Origin*, *Case Resolution +Time*, *Top Accounts by Case Volume*, *Reopened Cases* and *CSAT by Agent* are +not published anywhere, and most of them ask `case_metrics` for something it +does not carry — there is no agent dimension, no account dimension, no reopen +marker on the case and no measure over **Customer Satisfaction**, so three of +those five cannot be built as custom reports either. The section now lists the +three real reports with their real grouping, and says of each absent one why it +is absent. Two subscription examples that named a report from that list now name +a published one. + +**The Lead reports section listed three reports that do not exist**, and omitted +the one that does. `src/reports/` publishes exactly one lead report — **Lead +Engagement by Month × Source** (`src/reports/lead.report.ts`), a matrix of +contacted-lead volume by source and month — while the page listed *Lead Conversion +Funnel*, *Lead Source ROI* and *Aged Leads*, none of which is published anywhere. +Two of them also spelled a lead status that does not exist: *Working* is a row in +the import alias table (`src/mappings/lead_import.mapping.ts`) that maps a legacy +value onto **Contacted** at import time, not a value of `crm_lead.status`, whose +route is *New → Contacted → Qualified → Unqualified → Converted*. The section now +lists the real report, names the three absent ones, and says what `lead_metrics` +can and cannot answer. + +**Two behavioural claims outside any enumeration.** The setup checklist sent +admins to a **Setup → Opportunity → Stages** screen that does not exist — the +stages are the `stage` field's options in `src/objects/_picklists.ts` and the +probabilities are the `STAGE_PROBABILITY` map in `src/objects/opportunity.hook.ts`, +which re-derives probability from the stage on every save. And the state-machines +page advised hanging automation off a transition because it is *"much more +performant"*; there is no transition to hang it on (the table is a warning-severity +validation rule that logs and emits nothing), and the comparison had no basis. The +tip now describes what the app actually does — a `record_change` flow that narrows +itself in its start condition, as `opportunity_won_alert` does. + +Documentation only; `src/` unchanged. Fixes #936, #951, #952. diff --git a/content/docs/administration/setup.mdx b/content/docs/administration/setup.mdx index e232b1c34..b8460f43a 100644 --- a/content/docs/administration/setup.mdx +++ b/content/docs/administration/setup.mdx @@ -93,20 +93,24 @@ Confirm the 7-stage default pipeline matches your sales process. Stages and prob | Closed Won | 100% | | Closed Lost | 0% | -If your process differs, adjust in **Setup → Opportunity → Stages**. +If your process differs, that is a source edit rather than a screen: **there is no Setup → Opportunity → Stages page**. The seven stages are the `stage` field's own options (`OPPORTUNITY_STAGE_OPTIONS` in `src/objects/_picklists.ts`), and the probabilities are the `STAGE_PROBABILITY` map in `src/objects/opportunity.hook.ts` — which re-derives **Probability** and **Expected Revenue** from the stage on every save, so a probability typed onto a deal by hand does not survive the next one. Changing either is a code change plus a redeploy. ### 11. Case SLAs Confirm the default [SLA matrix](/docs/service/sla-and-escalation): -| Priority | First response | Resolution | -| --- | --- | --- | -| Critical | 1 hour | 4 hours | -| High | 2 hours | 8 hours | -| Medium | 4 hours | 2 business days | -| Low | 1 business day | 7 business days | +| Priority | Resolution | +| --- | --- | +| Critical | 4 hours | +| High | 8 hours | +| Medium | 2 business days | +| Low | 7 business days | + +**Only the Critical row is a deadline the app keeps.** The `case_sla_defaults` hook (`src/objects/case.hook.ts`) stamps `sla_due_date` at *now + 4 calendar hours* on a Critical case and writes nothing at the other three priorities — and the hourly `case_sla_monitor` sweep only picks up cases whose due date has passed, so below Critical there is no date to pass. Those three rows are your team's service commitment, not a clock the app runs. + +**There is no first-response target to confirm.** This checklist used to carry a whole *First response* column — 1 hour at Critical through 1 business day at Low — and none of those four numbers exists anywhere in the app; the SLA page this section links to does not mention a first response at all. What does exist is the stamp: **First Response Date** is written the first time a call or meeting that already took place is logged on the case (`logActivityAction`, `src/actions/global.actions.ts`). It is never compared against anything — `case_metrics` declares no first-response measure, so no report or dashboard tile reports on it, and no flow alerts on it. Promise your customers a first response time by all means; the app records when it happened and nothing else. -Adjust if your support promise differs. +Adjust if your support promise differs — the Critical four hours lives in `src/objects/case.hook.ts`, not on a Setup screen. ### 12. Approval process diff --git a/content/docs/administration/setup.zh-Hans.mdx b/content/docs/administration/setup.zh-Hans.mdx index 92de4cd40..cb613e705 100644 --- a/content/docs/administration/setup.zh-Hans.mdx +++ b/content/docs/administration/setup.zh-Hans.mdx @@ -93,20 +93,24 @@ description: 让 HotCRM 为你的团队做好生产准备的第一周任务。 | Closed Won | 100% | | Closed Lost | 0% | -如果你的流程不同,请在 **设置 → 商机 → 阶段** 中调整。 +如果你的流程不同,那是一次源码修改,不是在某个屏上调整:**并不存在「设置 → 商机 → 阶段」这个页面**。七个阶段是 `stage` 字段自己的选项(`src/objects/_picklists.ts` 里的 `OPPORTUNITY_STAGE_OPTIONS`),概率则是 `src/objects/opportunity.hook.ts` 里的 `STAGE_PROBABILITY` 映射 —— 它在每次保存时都按阶段重新推导 **Probability** 与 **Expected Revenue**,所以手工填在商机上的概率撑不过下一次保存。改动其中任何一项都是代码改动加一次重新部署。 ### 11. 案例 SLA 确认默认的 [SLA 矩阵](/zh-Hans/docs/service/sla-and-escalation): -| 优先级 | 首次响应 | 解决 | -| --- | --- | --- | -| Critical | 1 小时 | 4 小时 | -| High | 2 小时 | 8 小时 | -| Medium | 4 小时 | 2 个工作日 | -| Low | 1 个工作日 | 7 个工作日 | +| 优先级 | 解决 | +| --- | --- | +| Critical | 4 小时 | +| High | 8 小时 | +| Medium | 2 个工作日 | +| Low | 7 个工作日 | + +**只有 Critical 这一行是应用真正在守的期限。** `case_sla_defaults` 钩子(`src/objects/case.hook.ts`)在 Critical 工单上把 `sla_due_date` 盖成 *当前时间 + 4 个自然小时*,其余三个优先级什么都不写 —— 而每小时跑一次的 `case_sla_monitor` 只捞到期日已过的工单,所以 Critical 以下根本没有一个日期可以过期。那三行是你们团队的服务承诺,不是应用替你跑的钟。 + +**这里没有「首次响应目标」可确认。** 本清单曾经带着整整一列 *首次响应*(Critical 1 小时一直到 Low 1 个工作日),这四个数字在应用里哪儿都不存在;本节链过去的那一页 SLA 文档也根本没提过首次响应。真正存在的是那个时间戳:**First Response Date** 在工单上第一次记录 *已经发生过* 的通话或会议时被写入(`logActivityAction`,`src/actions/global.actions.ts`)。它从不与任何目标比较 —— `case_metrics` 没有声明任何首次响应度量,因此没有报表、没有仪表盘磁贴会报它,也没有任何流程会为它告警。你当然可以对客户承诺一个首次响应时长;应用只是记下它何时发生,别的什么都不做。 -如果你的支持承诺不同,请进行调整。 +如果你的支持承诺不同,请自行调整 —— Critical 的这 4 小时写在 `src/objects/case.hook.ts` 里,不在任何设置屏上。 ### 12. 审批流程 diff --git a/content/docs/administration/setup.zh-Hant.mdx b/content/docs/administration/setup.zh-Hant.mdx index 7e61d0d12..29b98268e 100644 --- a/content/docs/administration/setup.zh-Hant.mdx +++ b/content/docs/administration/setup.zh-Hant.mdx @@ -93,20 +93,24 @@ description: 讓 HotCRM 為你的團隊做好生產準備的第一週任務。 | Closed Won | 100% | | Closed Lost | 0% | -如果你的流程不同,請在 **設定 → 商機 → 階段** 中調整。 +如果你的流程不同,那是一次原始碼修改,不是在某個畫面上調整:**並不存在「設定 → 商機 → 階段」這個頁面**。七個階段是 `stage` 欄位自己的選項(`src/objects/_picklists.ts` 裡的 `OPPORTUNITY_STAGE_OPTIONS`),機率則是 `src/objects/opportunity.hook.ts` 裡的 `STAGE_PROBABILITY` 對應表 —— 它在每次儲存時都按階段重新推導 **Probability** 與 **Expected Revenue**,所以手工填在商機上的機率撐不過下一次儲存。改動其中任何一項都是程式碼改動加一次重新部署。 ### 11. 案例 SLA 確認預設的 [SLA 矩陣](/zh-Hant/docs/service/sla-and-escalation): -| 優先級 | 首次回應 | 解決 | -| --- | --- | --- | -| Critical | 1 小時 | 4 小時 | -| High | 2 小時 | 8 小時 | -| Medium | 4 小時 | 2 個工作日 | -| Low | 1 個工作日 | 7 個工作日 | +| 優先級 | 解決 | +| --- | --- | +| Critical | 4 小時 | +| High | 8 小時 | +| Medium | 2 個工作日 | +| Low | 7 個工作日 | + +**只有 Critical 這一行是應用真正在守的期限。** `case_sla_defaults` 鉤子(`src/objects/case.hook.ts`)在 Critical 工單上把 `sla_due_date` 蓋成 *目前時間 + 4 個自然小時*,其餘三個優先級什麼都不寫 —— 而每小時跑一次的 `case_sla_monitor` 只撈到期日已過的工單,所以 Critical 以下根本沒有一個日期可以過期。那三行是你們團隊的服務承諾,不是應用替你跑的鐘。 + +**這裡沒有「首次回應目標」可確認。** 本清單曾經帶著整整一欄 *首次回應*(Critical 1 小時一直到 Low 1 個工作日),這四個數字在應用裡哪兒都不存在;本節連過去的那一頁 SLA 文件也根本沒提過首次回應。真正存在的是那個時間戳:**First Response Date** 在工單上第一次記錄 *已經發生過* 的通話或會議時被寫入(`logActivityAction`,`src/actions/global.actions.ts`)。它從不與任何目標比較 —— `case_metrics` 沒有宣告任何首次回應度量,因此沒有報表、沒有儀表板磁貼會報它,也沒有任何流程會為它告警。你當然可以對客戶承諾一個首次回應時長;應用只是記下它何時發生,別的什麼都不做。 -如果你的支援承諾不同,請進行調整。 +如果你的支援承諾不同,請自行調整 —— Critical 的這 4 小時寫在 `src/objects/case.hook.ts` 裡,不在任何設定畫面上。 ### 12. 審批流程 diff --git a/content/docs/administration/state-machines.mdx b/content/docs/administration/state-machines.mdx index 990d02d89..7514f040f 100644 --- a/content/docs/administration/state-machines.mdx +++ b/content/docs/administration/state-machines.mdx @@ -137,7 +137,7 @@ What the tables do give an agent is what they give a report author: the route, d - ✅ **Don't over-constrain** — too many transition rules become a friction tax on the team. - ✅ When adding a new status, **decide the entry and exit conditions first** — the rest follows. -- ✅ Use state-machine transitions to **trigger automation** rather than running automation on every save — much more performant. +- ✅ **Don't hang automation off a transition** — there is nothing to hang it on. The table is a validation rule, evaluated on the save that makes the move; it writes a log line and emits no event for automation to subscribe to. Automation that should run on one move is a `record_change` flow that fires on the save and narrows *itself* in its start condition, comparing `record.stage` against `previous.stage` — `opportunity_won_alert` (`src/flows/opportunity-won-alert.flow.ts`) does exactly that, so the congratulations blast goes out on the move into *Closed Won* and not on every later edit of a won deal. - ✅ **Test the wizard flows** (convert lead, close opportunity) after every change. ## Tips for users diff --git a/content/docs/administration/state-machines.zh-Hans.mdx b/content/docs/administration/state-machines.zh-Hans.mdx index 11e34cb2c..20152495b 100644 --- a/content/docs/administration/state-machines.zh-Hans.mdx +++ b/content/docs/administration/state-machines.zh-Hans.mdx @@ -137,7 +137,7 @@ Prospecting / Qualification / Needs Analysis / Proposal / Negotiation ──▶ - ✅ **不要过度约束** —— 太多转换规则会成为团队的摩擦税。 - ✅ 添加新状态时,**先决定进入和退出条件** —— 其余的自然就有了。 -- ✅ 使用状态机转换来 **触发自动化**,而非在每次保存时运行自动化 —— 性能要好得多。 +- ✅ **不要把自动化挂在转换上** —— 根本没有可挂的东西。转换表是一条验证规则,在做出这次移动的那一次保存里被求值;它写下一行日志,不产生任何可供自动化订阅的事件。只应在某一次移动上跑的自动化,是一条 `record_change` 流程:它在那次保存上触发,并在自己的起始条件里把 `record.stage` 与 `previous.stage` 比对来收窄 **自己** —— `opportunity_won_alert`(`src/flows/opportunity-won-alert.flow.ts`)正是这么写的,所以那条祝贺通知只在移入 *Closed Won* 的那一次发出,而不是在这笔已赢商机之后的每一次编辑上都发。 - ✅ 每次变更后 **测试向导流程**(转化潜在客户、关闭商机)。 ## 给用户的提示 diff --git a/content/docs/administration/state-machines.zh-Hant.mdx b/content/docs/administration/state-machines.zh-Hant.mdx index 223bd64a6..fb0ff621f 100644 --- a/content/docs/administration/state-machines.zh-Hant.mdx +++ b/content/docs/administration/state-machines.zh-Hant.mdx @@ -137,7 +137,7 @@ Prospecting / Qualification / Needs Analysis / Proposal / Negotiation ──▶ - ✅ **不要過度約束** —— 太多轉換規則會成為團隊的摩擦稅。 - ✅ 新增狀態時,**先決定進入和退出條件** —— 其餘的自然就有了。 -- ✅ 使用狀態機轉換來 **觸發自動化**,而非在每次儲存時執行自動化 —— 效能要好得多。 +- ✅ **不要把自動化掛在轉換上** —— 根本沒有可掛的東西。轉換表是一條驗證規則,在做出這次移動的那一次儲存裡被求值;它寫下一行日誌,不產生任何可供自動化訂閱的事件。只應在某一次移動上跑的自動化,是一條 `record_change` 流程:它在那次儲存上觸發,並在自己的起始條件裡把 `record.stage` 與 `previous.stage` 比對來收窄 **自己** —— `opportunity_won_alert`(`src/flows/opportunity-won-alert.flow.ts`)正是這麼寫的,所以那條祝賀通知只在移入 *Closed Won* 的那一次發出,而不是在這筆已贏商機之後的每一次編輯上都發。 - ✅ 每次變更後 **測試精靈流程**(轉化潛在客戶、關閉商機)。 ## 給使用者的提示 diff --git a/content/docs/analytics/cubes.mdx b/content/docs/analytics/cubes.mdx index 90ddd2395..ba2ab33fa 100644 --- a/content/docs/analytics/cubes.mdx +++ b/content/docs/analytics/cubes.mdx @@ -81,11 +81,12 @@ The cube behind every case-related dashboard and SLA report. - Open case count - New case count - Resolved case count -- First-response time (minutes) - Resolution time (hours) -- SLA met % (first response and resolution) +- **SLA Violation Rate** — the average of the **SLA Violated** flag (`avg_sla_violated` in `case_metrics`), which is a breach rate and not a compliance percentage - CSAT score +**There is no first-response measure here**, and no "SLA met %". `case_metrics` (`src/datasets/case.dataset.ts`) declares neither, so nothing in analytics can aggregate, compare or chart a first response. The **First Response Date** stamp on the case is real — see [Cases](/docs/service/cases) for when it is written — but it stops there: nothing measures it, and nothing alerts on it. + **Dimensions**: - Status, priority, category, origin - Agent (and team) @@ -94,7 +95,7 @@ The cube behind every case-related dashboard and SLA report. - Period **Sample questions**: -- *"SLA met % by priority by month."* +- *"SLA Violation Rate by priority."* - *"Cases by category by product — which products generate the most support load?"* - *"Reopened cases by agent — quality signal."* diff --git a/content/docs/analytics/cubes.zh-Hans.mdx b/content/docs/analytics/cubes.zh-Hans.mdx index 90886a3e7..e85256e4e 100644 --- a/content/docs/analytics/cubes.zh-Hans.mdx +++ b/content/docs/analytics/cubes.zh-Hans.mdx @@ -81,11 +81,12 @@ description: 为 HotCRM 分析提供支持的数据层——四个内置多维 - 未结工单数 - 新建工单数 - 已解决工单数 -- 首次响应时间(分钟) - 解决时间(小时) -- SLA 达标 %(首次响应和解决) +- **SLA 违约率** —— **SLA Violated** 标志的平均值(`case_metrics` 里的 `avg_sla_violated`),它是违约率,不是达标率 - CSAT 评分 +**这里没有首次响应度量**,也没有「SLA 达标 %」。`case_metrics`(`src/datasets/case.dataset.ts`)两者都没有声明,所以分析侧根本无法对首次响应做聚合、比较或出图。工单上的 **First Response Date** 时间戳是真的 —— 它何时被写入见 [工单](/zh-Hans/docs/service/cases) —— 但也就到此为止:没有任何东西度量它,也没有任何东西为它告警。 + **维度**: - 状态、优先级、类别、来源 - 客服(及团队) @@ -94,7 +95,7 @@ description: 为 HotCRM 分析提供支持的数据层——四个内置多维 - 周期 **示例问题**: -- *"按优先级按月的 SLA 达标 %。"* +- *"按优先级的 SLA 违约率。"* - *"按类别按产品的工单——哪些产品产生的支持负荷最大?"* - *"按客服的重开工单——质量信号。"* diff --git a/content/docs/analytics/cubes.zh-Hant.mdx b/content/docs/analytics/cubes.zh-Hant.mdx index 15b8732fa..e2d23d9f2 100644 --- a/content/docs/analytics/cubes.zh-Hant.mdx +++ b/content/docs/analytics/cubes.zh-Hant.mdx @@ -81,11 +81,12 @@ description: 為 HotCRM 分析提供支援的資料層——四個內建多維 - 未結工單數 - 新建工單數 - 已解決工單數 -- 首次回應時間(分鐘) - 解決時間(小時) -- SLA 達標 %(首次回應和解決) +- **SLA 違約率** —— **SLA Violated** 標誌的平均值(`case_metrics` 裡的 `avg_sla_violated`),它是違約率,不是達標率 - CSAT 評分 +**這裡沒有首次回應度量**,也沒有「SLA 達標 %」。`case_metrics`(`src/datasets/case.dataset.ts`)兩者都沒有宣告,所以分析側根本無法對首次回應做彙總、比較或出圖。工單上的 **First Response Date** 時間戳是真的 —— 它何時被寫入見 [工單](/zh-Hant/docs/service/cases) —— 但也就到此為止:沒有任何東西度量它,也沒有任何東西為它告警。 + **維度**: - 狀態、優先順序、類別、來源 - 客服(及團隊) @@ -94,7 +95,7 @@ description: 為 HotCRM 分析提供支援的資料層——四個內建多維 - 週期 **範例問題**: -- *"按優先順序按月的 SLA 達標 %。"* +- *"按優先順序的 SLA 違約率。"* - *"按類別按產品的工單——哪些產品產生的支援負荷最大?"* - *"按客服的重開工單——品質訊號。"* diff --git a/content/docs/analytics/reports.mdx b/content/docs/analytics/reports.mdx index e365fff9e..1932ed48d 100644 --- a/content/docs/analytics/reports.mdx +++ b/content/docs/analytics/reports.mdx @@ -26,22 +26,38 @@ HotCRM ships with these reports out of the box: ### 🎯 Lead reports +`src/reports/lead.report.ts` publishes **one** lead report: + | Report | What it shows | | --- | --- | -| **Lead Conversion Funnel** | New → Working → Qualified → Converted | -| **Lead Source ROI** | Volume, conversion rate, and revenue by source | -| **Aged Leads** | Leads in *New* or *Working* status > 30 days | +| **Lead Engagement by Month × Source** | A matrix of contacted-lead volume — lead source down the rows, the month of **Last Contacted** across the columns. Leads nobody has contacted are excluded; they have no engagement month | + +The three reports this section used to list are published nowhere, and two of them named a lead status that does not exist: + +- **Lead Conversion Funnel** and **Aged Leads** do not exist in any form. Both leaned on a *Working* status, and `crm_lead.status` has no such value — the route is *New → Contacted → Qualified → Unqualified → Converted* (`src/objects/lead.object.ts`), with *Unqualified* reachable from any open status. *Working* is a row in the import alias table (`src/mappings/lead_import.mapping.ts`), which maps that legacy spelling onto **Contacted** as records come in; the product never displays the word. [State machines](/docs/administration/state-machines) documents the declared route. +- **Lead Source ROI** does not exist either, and the semantic layer could not answer it as written: `lead_metrics` (`src/datasets/lead.dataset.ts`) declares a single measure — the lead count — over four dimensions (status, source, created date, last-contacted month). Volume by source comes straight off that, a conversion rate is arithmetic you do on top of a count grouped by status, and revenue is not in this dataset at all — it never crosses over to opportunities. + +The lead count is the one lead number anything else reads: the **Open Leads** tile on the [Executive Overview](/docs/analytics/dashboards) dashboard counts unconverted leads off that same dataset. For the rest, build the report you need — see *Custom reports* below. ### 🎧 Service reports +`src/reports/case.report.ts` publishes **three** case reports: + | Report | What it shows | | --- | --- | -| **Case Volume by Origin** | New cases by Email / Phone / Web / Chat / Portal | -| **SLA Performance** | % first-response on time, % resolved on time, by priority | -| **Case Resolution Time** | Average time-to-resolve, by category and agent | -| **Top Accounts by Case Volume** | Accounts with the most cases | -| **Reopened Cases** | Cases reopened in the period (quality signal) | -| **CSAT by Agent** | Average customer satisfaction by agent | +| **Cases by Status and Priority** | Case count and average resolution time, grouped by status and then priority, with a bar chart of cases by status | +| **SLA Performance Report** | Case count, **SLA Violation Rate** and average resolution time, by priority — over closed cases only | +| **Cases Opened by Priority × Day** | A matrix of daily inflow: priority down the rows, the day each case was opened across the columns | + +The five other reports this section used to list are published nowhere, and most of them ask the semantic layer for something it does not carry. `case_metrics` (`src/datasets/case.dataset.ts`) reads `crm_case` alone, through five dimensions — **Status**, **Priority**, **Origin**, **Type**, **Created** (bucketed by day) — and three measures: case count, average resolution time, SLA Violation Rate. + +- **Case Volume by Origin** is the one of the five you could build yourself: **Origin** is a dimension, so a count of cases by Email / Phone / Web / Chat / Portal is a report away. It just is not one of the shipped ones. +- **Case Resolution Time** *by category and agent* — the measure is real and rides in both summary reports above, but **agent** is not reachable at all: `owner_id` is a field on the case and no dimension exposes it. The nearest thing to a category is **Type** (*Question / Problem / Feature Request / Bug*). +- **Top Accounts by Case Volume** cannot be built either — there is no account dimension, and the dataset never crosses over to `crm_account`. +- **Reopened Cases** counts something nothing records. Cases do reopen — the state machine allows *Resolved* and *Closed* back to *In Progress* — but no field marks a case as having been reopened, and no measure counts it. +- **CSAT by Agent** fails on both halves: **Customer Satisfaction** (`customer_rating`) is a real 1–5 field on the case, yet `case_metrics` declares no measure over it, and there is no agent to group by. + +**SLA Performance Report carries no first-response figure either**, and its SLA number is a violation *rate* — the average of the **SLA Violated** flag — rather than an on-time percentage. Nothing in this app measures first response at all: **First Response Date** is stamped on a case the first time a call or meeting that already took place is logged on it, and it is then never compared against a target; `case_metrics` declares no first-response measure, so no report and no dashboard tile can report one. See [SLA & Escalation](/docs/service/sla-and-escalation). ### 💰 Revenue reports @@ -105,8 +121,8 @@ The most common way to consume reports is by **email subscription**: A common pattern: - *Pipeline by Stage* → Mondays 8 AM to the sales team. -- *SLA Performance* → daily 6 PM to the service manager. -- *Top Accounts by Case Volume* → Fridays 4 PM to the customer success team. +- *SLA Performance Report* → daily 6 PM to the service manager. +- *Cases Opened by Priority × Day* → Fridays 4 PM to the customer success team. ## Tips for users diff --git a/content/docs/analytics/reports.zh-Hans.mdx b/content/docs/analytics/reports.zh-Hans.mdx index 4ae5eaf9a..090937d6a 100644 --- a/content/docs/analytics/reports.zh-Hans.mdx +++ b/content/docs/analytics/reports.zh-Hans.mdx @@ -26,22 +26,38 @@ HotCRM 开箱即用地提供以下报表: ### 🎯 线索报表 +`src/reports/lead.report.ts` 发布的线索报表只有 **一份**: + | 报表 | 显示内容 | | --- | --- | -| **Lead Conversion Funnel** | New → Working → Qualified → Converted | -| **Lead Source ROI** | 按来源的数量、转化率和收入 | -| **Aged Leads** | 处于 *New* 或 *Working* 状态超过 30 天的线索 | +| **Lead Engagement by Month × Source** | 已联系线索数量的矩阵 —— 行是线索来源,列是 **Last Contacted** 所在的月份。从未联系过的线索被排除在外:它们没有互动月份 | + +本节曾经列出的三份报表,一份都没有发布,其中两份还用了一个并不存在的线索状态: + +- **Lead Conversion Funnel** 与 **Aged Leads** 以任何形式都不存在。两者都依赖一个 *Working* 状态,而 `crm_lead.status` 根本没有这个取值 —— 真实路线是 *New → Contacted → Qualified → Unqualified → Converted*(`src/objects/lead.object.ts`),其中 *Unqualified* 从任何开放状态都能进入。*Working* 是导入别名表(`src/mappings/lead_import.mapping.ts`)里的一行,它在记录导入时把这个历史写法映射到 **Contacted**;产品从不显示这个词。声明出来的路线见 [状态机](/zh-Hans/docs/administration/state-machines)。 +- **Lead Source ROI** 同样不存在,而且按它写的口径,语义层也答不出来:`lead_metrics`(`src/datasets/lead.dataset.ts`)只声明了一个度量 —— 线索计数 —— 外加四个维度(状态、来源、创建日期、最后联系月份)。「按来源的数量」可以直接从它得到;转化率要你自己在按状态分组的计数上做除法;而收入根本不在这个数据集里 —— 它从不跨到商机那一侧。 + +线索计数也是别处唯一会读的线索数字:[Executive Overview](/zh-Hans/docs/analytics/dashboards) 仪表盘上的 **Open Leads** 磁贴正是用同一个数据集数未转化线索。其余的请自建 —— 见下文《自定义报表》。 ### 🎧 服务报表 +`src/reports/case.report.ts` 发布的工单报表共 **三份**: + | 报表 | 显示内容 | | --- | --- | -| **Case Volume by Origin** | 新工单按 Email / Phone / Web / Chat / Portal | -| **SLA Performance** | 首次响应准时 %、按时解决 %,按优先级 | -| **Case Resolution Time** | 平均解决时间,按类别和客服 | -| **Top Accounts by Case Volume** | 工单最多的客户 | -| **Reopened Cases** | 周期内重开的工单(质量信号) | -| **CSAT by Agent** | 按客服的平均客户满意度 | +| **Cases by Status and Priority** | 工单数与平均解决时长,先按状态、再按优先级分组,并带一张「按状态的工单数」柱状图 | +| **SLA Performance Report** | 工单数、**SLA 违约率** 与平均解决时长,按优先级 —— 只统计已关闭的工单 | +| **Cases Opened by Priority × Day** | 每日新增工单的矩阵:行是优先级,列是工单开出来的那一天 | + +本节曾经列出的另外五份报表哪儿都没有发布,而且其中多数是在向语义层要它根本没有的东西。`case_metrics`(`src/datasets/case.dataset.ts`)只读 `crm_case` 一个对象,五个维度 —— **Status**、**Priority**、**Origin**、**Type**、**Created**(按天分桶)—— 外加三个度量:工单数、平均解决时长、SLA 违约率。 + +- **Case Volume by Origin** 是这五份里唯一你自己建得出来的:**Origin** 是维度,所以「按 Email / Phone / Web / Chat / Portal 数工单」只差一份自建报表。它只是不在随应用发布的那几份里。 +- **Case Resolution Time** *按类别和客服* —— 度量本身是真的,上面两份汇总报表都带着它;但 **客服** 这一维根本够不着:`owner_id` 是工单上的字段,没有任何维度把它暴露出来。最接近「类别」的是 **Type**(*Question / Problem / Feature Request / Bug*)。 +- **Top Accounts by Case Volume** 同样建不出来 —— 没有客户维度,这个数据集也从不跨到 `crm_account`。 +- **Reopened Cases** 数的是没有任何东西在记的事。工单确实会重开 —— 状态机允许 *Resolved* 与 *Closed* 回到 *In Progress* —— 但没有任何字段标记一张工单曾被重开,也没有任何度量在数它。 +- **CSAT by Agent** 两头都不成立:**Customer Satisfaction**(`customer_rating`)是工单上真实的 1–5 星字段,但 `case_metrics` 没有为它声明任何度量,也没有「客服」这一维可分组。 + +**SLA Performance Report 里同样没有任何首次响应数字**,而且它那个 SLA 数字是 **SLA Violated** 标志的平均值,也就是违约 *率*,不是准时率。本应用根本不度量首次响应:**First Response Date** 只在工单上第一次记录 *已经发生过* 的通话或会议时被盖上,此后从不与任何目标比较;`case_metrics` 没有声明任何首次响应度量,所以没有报表、也没有仪表盘磁贴能报出一个来。参见 [SLA 与升级](/zh-Hans/docs/service/sla-and-escalation)。 ### 💰 收入报表 @@ -105,8 +121,8 @@ HotCRM 开箱即用地提供以下报表: 一种常见模式: - *Pipeline by Stage* → 周一早上 8 点发给销售团队。 -- *SLA Performance* → 每日下午 6 点发给服务经理。 -- *Top Accounts by Case Volume* → 周五下午 4 点发给客户成功团队。 +- *SLA Performance Report* → 每日下午 6 点发给服务经理。 +- *Cases Opened by Priority × Day* → 周五下午 4 点发给客户成功团队。 ## 给用户的提示 diff --git a/content/docs/analytics/reports.zh-Hant.mdx b/content/docs/analytics/reports.zh-Hant.mdx index a34d8927c..c7c97aafe 100644 --- a/content/docs/analytics/reports.zh-Hant.mdx +++ b/content/docs/analytics/reports.zh-Hant.mdx @@ -26,22 +26,38 @@ HotCRM 開箱即用地提供以下報表: ### 🎯 潛在客戶報表 +`src/reports/lead.report.ts` 發布的潛在客戶報表只有 **一份**: + | 報表 | 顯示內容 | | --- | --- | -| **Lead Conversion Funnel** | New → Working → Qualified → Converted | -| **Lead Source ROI** | 按來源的數量、轉化率和收入 | -| **Aged Leads** | 處於 *New* 或 *Working* 狀態超過 30 天的潛在客戶 | +| **Lead Engagement by Month × Source** | 已聯絡潛在客戶數量的矩陣 —— 橫列是潛在客戶來源,直欄是 **Last Contacted** 所在的月份。從未聯絡過的潛在客戶被排除在外:它們沒有互動月份 | + +本節曾經列出的三份報表,一份都沒有發布,其中兩份還用了一個並不存在的潛在客戶狀態: + +- **Lead Conversion Funnel** 與 **Aged Leads** 以任何形式都不存在。兩者都依賴一個 *Working* 狀態,而 `crm_lead.status` 根本沒有這個取值 —— 真實路線是 *New → Contacted → Qualified → Unqualified → Converted*(`src/objects/lead.object.ts`),其中 *Unqualified* 從任何開放狀態都能進入。*Working* 是匯入別名表(`src/mappings/lead_import.mapping.ts`)裡的一行,它在記錄匯入時把這個歷史寫法對應到 **Contacted**;產品從不顯示這個詞。宣告出來的路線見 [狀態機](/zh-Hant/docs/administration/state-machines)。 +- **Lead Source ROI** 同樣不存在,而且按它寫的口徑,語義層也答不出來:`lead_metrics`(`src/datasets/lead.dataset.ts`)只宣告了一個度量 —— 潛在客戶計數 —— 外加四個維度(狀態、來源、建立日期、最後聯絡月份)。「按來源的數量」可以直接從它得到;轉化率要你自己在按狀態分組的計數上做除法;而收入根本不在這個資料集裡 —— 它從不跨到商機那一側。 + +潛在客戶計數也是別處唯一會讀的潛在客戶數字:[Executive Overview](/zh-Hant/docs/analytics/dashboards) 儀表板上的 **Open Leads** 磁貼正是用同一個資料集數未轉化的潛在客戶。其餘的請自建 —— 見下文《自訂報表》。 ### 🎧 服務報表 +`src/reports/case.report.ts` 發布的工單報表共 **三份**: + | 報表 | 顯示內容 | | --- | --- | -| **Case Volume by Origin** | 新工單按 Email / Phone / Web / Chat / Portal | -| **SLA Performance** | 首次回應準時 %、按時解決 %,按優先順序 | -| **Case Resolution Time** | 平均解決時間,按類別和客服 | -| **Top Accounts by Case Volume** | 工單最多的客戶 | -| **Reopened Cases** | 週期內重開的工單(品質訊號) | -| **CSAT by Agent** | 按客服的平均客戶滿意度 | +| **Cases by Status and Priority** | 工單數與平均解決時長,先按狀態、再按優先順序分組,並帶一張「按狀態的工單數」長條圖 | +| **SLA Performance Report** | 工單數、**SLA 違約率** 與平均解決時長,按優先順序 —— 只統計已關閉的工單 | +| **Cases Opened by Priority × Day** | 每日新增工單的矩陣:橫列是優先順序,直欄是工單開出來的那一天 | + +本節曾經列出的另外五份報表哪兒都沒有發布,而且其中多數是在向語義層要它根本沒有的東西。`case_metrics`(`src/datasets/case.dataset.ts`)只讀 `crm_case` 一個物件,五個維度 —— **Status**、**Priority**、**Origin**、**Type**、**Created**(按天分桶)—— 外加三個度量:工單數、平均解決時長、SLA 違約率。 + +- **Case Volume by Origin** 是這五份裡唯一你自己建得出來的:**Origin** 是維度,所以「按 Email / Phone / Web / Chat / Portal 數工單」只差一份自建報表。它只是不在隨應用發布的那幾份裡。 +- **Case Resolution Time** *按類別和客服* —— 度量本身是真的,上面兩份彙總報表都帶著它;但 **客服** 這一維根本搆不著:`owner_id` 是工單上的欄位,沒有任何維度把它暴露出來。最接近「類別」的是 **Type**(*Question / Problem / Feature Request / Bug*)。 +- **Top Accounts by Case Volume** 同樣建不出來 —— 沒有客戶維度,這個資料集也從不跨到 `crm_account`。 +- **Reopened Cases** 數的是沒有任何東西在記的事。工單確實會重開 —— 狀態機允許 *Resolved* 與 *Closed* 回到 *In Progress* —— 但沒有任何欄位標記一張工單曾被重開,也沒有任何度量在數它。 +- **CSAT by Agent** 兩頭都不成立:**Customer Satisfaction**(`customer_rating`)是工單上真實的 1–5 星欄位,但 `case_metrics` 沒有為它宣告任何度量,也沒有「客服」這一維可分組。 + +**SLA Performance Report 裡同樣沒有任何首次回應數字**,而且它那個 SLA 數字是 **SLA Violated** 標誌的平均值,也就是違約 *率*,不是準時率。本應用根本不度量首次回應:**First Response Date** 只在工單上第一次記錄 *已經發生過* 的通話或會議時被蓋上,此後從不與任何目標比較;`case_metrics` 沒有宣告任何首次回應度量,所以沒有報表、也沒有儀表板磁貼能報出一個來。參見 [SLA 與升級](/zh-Hant/docs/service/sla-and-escalation)。 ### 💰 收入報表 @@ -105,8 +121,8 @@ HotCRM 開箱即用地提供以下報表: 一種常見模式: - *Pipeline by Stage* → 週一早上 8 點發給銷售團隊。 -- *SLA Performance* → 每日下午 6 點發給服務經理。 -- *Top Accounts by Case Volume* → 週五下午 4 點發給客戶成功團隊。 +- *SLA Performance Report* → 每日下午 6 點發給服務經理。 +- *Cases Opened by Priority × Day* → 週五下午 4 點發給客戶成功團隊。 ## 給使用者的提示