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
58 changes: 58 additions & 0 deletions .changeset/sla-tracking-fidelity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
'hotcrm': patch
---

Write the SLA-tracking half of `service/sla-and-escalation` to what the app
actually does, in all three locales.

Four claims on that page described behaviour this app does not have. Three were
confirmed against `origin/main` and rewritten; the fourth turned out to be true,
and is reported below rather than "fixed" into a new falsehood.

**Only Critical cases get an SLA due date.** The page opened with "Every case
gets an SLA due date the moment it's created", 45 lines above the sentence
#886/#890 had already written straight — that `case_sla_defaults`
(`src/objects/case.hook.ts`) stamps `sla_due_date` for `critical` only and
leaves every other priority blank. The page was contradicting itself; the lede
and the intro of *How SLA targets are calculated* now say what the hook does,
and the target table separates Critical's four hours (a deadline the system
keeps) from High / Medium / Low (a service commitment with no clock behind it).

**The High / Medium / Low "what breach looks like" cells described row colour,
not a breach.** There are no breach badges. The only colour declaration on a
case list is `rowColor` on the *All Cases* view, and it is keyed on
**priority**, not on violation — so it paints a High case orange (`#f97316`,
not the red the page promised) from the moment it is created, and Medium yellow
(`#eab308`) rather than amber. Those three rows now say that nothing fires at
all: `case_sla_monitor` only picks up cases whose `sla_due_date` has passed, and
they have none. What reports a breach in a list view is the **SLA Violated**
column.

**There is no live SLA countdown.** `countdown` and `remaining` appear nowhere
in the app's source, and the page header carries the case number, subject and
account — not an SLA timer with warning zones. The three-bullet
`2h 14m remaining` / `24m remaining` / `BREACHED – 1h 32m over` display is
replaced with what the case really shows: **SLA Due Date** and **SLA Violated**
as two ordinary fields in the **Key Information** strip, plus the two views that
do help (**SLA Calendar**, **SLA at Risk**) described for what they select on.
The *Tips for service agents* line telling agents to "watch your SLA countdown …
when you see the amber zone" pointed at the same missing feature and is
rewritten with it.

**The read-only claim was correct — it was the mechanism that was undocumented.**
"The breach flag is read-only for agents" is true: the Service Agent profile
masks `crm_case.is_sla_violated` as `readable: true, editable: false`
(`src/profiles/service-agent.profile.ts`), which is field-level security rather
than a `readonly` field — the field deliberately is not `readonly`, because
`case_sla_monitor` has to write it. The sentence keeps its claim and now names
where the lock lives. The neighbouring field bullet was wrong, though: the
checkbox is labelled **SLA Violated**, there is no *SLA Breached?* field, and it
does not compare resolution time against a target — the hourly sweep sets it on
open cases whose due date has already passed, which is why a case with no due
date is never a candidate and a case resolved late but before the next sweep is
never flagged.

Whether High / Medium / Low *should* have an SLA clock, and whether the breach
flag *should* be locked differently, stay open in #595 — this change only
records today's behaviour. No field definition, flow or view changed. Refs #903,
#886, #890.
37 changes: 21 additions & 16 deletions content/docs/service/sla-and-escalation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,42 @@ description: How priorities drive deadlines, when cases escalate automatically,

# SLA & Escalation

The SLA (Service-Level Agreement) system gives every case a **deadline** based on its priority, surfaces **breaches** in real time, and **escalates** stuck cases automatically — escalation flags the case and alerts its owner; it does not move the case to senior staff.
The SLA (Service-Level Agreement) system stamps a **deadline** on Critical cases, flags **breaches** on an hourly sweep, and **escalates** stuck cases automatically — escalation flags the case and alerts its owner; it does not move the case to senior staff.

## How SLA targets are calculated

Every case gets an **SLA due date** the moment it's created. The target is based on priority:
**Only Critical cases get an SLA due date automatically.** The `case_sla_defaults` hook (`src/objects/case.hook.ts`) fills `sla_due_date` with *now + 4 hours* when a case is created at — or edited to — Critical priority and the field is still empty. At **High**, **Medium** and **Low** it writes nothing, so the field stays blank unless a person types a date into it by hand.

| Priority | Default SLA target | What "breach" looks like |
That makes the four rows below two different kinds of thing: Critical's four hours is a deadline the system keeps, and the other three are the team's service commitment, with no clock behind them.

| Priority | Target | What "breach" looks like |
| --- | --- | --- |
| **Critical** | 4 hours | The SLA monitor flags **SLA Violated**, escalates the case, and alerts the **case owner** by inbox + email. No red banner on the case detail page — this app has no banner mechanism — and no alert to a support manager: the notify node's recipient list is `{currentCase.owner_id}` alone |
| **High** | 8 hours | Red badge in list views |
| **Medium** | 2 business days | Amber badge |
| **Low** | 7 business days | Grey badge |
| **Critical** | 4 hours, stamped as `sla_due_date` | The SLA monitor flags **SLA Violated**, escalates the case, and alerts the **case owner** by inbox + email. No red banner on the case detail page — this app has no banner mechanism — and no alert to a support manager: the notify node's recipient list is `{currentCase.owner_id}` alone |
| **High** | 8 hours — a service commitment; nothing stamps `sla_due_date` | Nothing fires. `case_sla_monitor` only picks up cases whose `sla_due_date` has passed, so a case with no due date is never a candidate and **SLA Violated** stays `false`. There is no red badge either: the only colour rule on a case list is the **row tint** in *All Cases*, which is keyed on **priority** (`#f97316`, orange) and paints every High case from the moment it is created, breach or no breach |
| **Medium** | 2 business days — a service commitment; nothing stamps `sla_due_date` | Nothing fires, for the same reason. The amber-looking rows are that same priority row tint (`#eab308`, yellow), not a breach badge |
| **Low** | 7 business days — a service commitment; nothing stamps `sla_due_date` | Nothing fires, for the same reason. The grey rows are the priority row tint (`#94a3b8`) |

In a list view, the thing that actually reports a breach is the **SLA Violated** column, not the row colour.

The SLA target is configurable per priority — see the *Tips for admins* section below.
Critical's four hours is the one target the system keeps, and it lives in the hook rather than on a Setup screen — see the *Tips for admins* section below.

> **Business hours** — the system can be configured to count only business hours (e.g., 9-5 Mon-Fri) rather than calendar hours. See [Administration › Setup](/docs/administration/setup).

## How the SLA is tracked

Two fields on every case:

- **SLA Due Date** — when the case must be resolved by.
- **SLA Breached?** — `true` when the resolution time exceeds the target.
- **SLA Due Date** (`sla_due_date`) — when the case must be resolved by. Blank on every case the Critical hook did not stamp, unless somebody filled it in by hand.
- **SLA Violated** (`is_sla_violated`) — there is no field called *SLA Breached?*; the checkbox agents see is labelled **SLA Violated**. It does not measure resolution time against the target. The hourly `case_sla_monitor` sweep sets it to `true` on cases that are still open — status is neither *Resolved* nor *Closed* — and whose **SLA Due Date** is already in the past. Two consequences worth knowing: a case with no due date is never a candidate, and a case resolved late but before the next sweep is never flagged.

The breach flag is **read-only for agents** — they can't fudge it, and the value itself is written by `case_sla_monitor`, which runs as the system rather than as a user. The lock is field-level security rather than a read-only field: the Service Agent profile masks `crm_case.is_sla_violated` as readable but not editable, which is why an agent sees the checkbox on the case — on the form's **SLA** tab and in the detail page's **Status & SLA** section — but cannot change it. The shipped masks are listed under [Profiles › Field-level security](/docs/administration/profiles#field-level-security).

The breach flag is **read-only for agents** — they can't fudge it. It's auto-calculated by the system.
**There is no live SLA countdown.** Nothing in this app computes time remaining or time over — no threshold colouring, no warning zone, and no ⏱ / ⚠️ / 🚨 treatment anywhere on the case. The page header carries the case number, subject and account and nothing else; **SLA Due Date** and **SLA Violated** are rendered as two ordinary fields in the **Key Information** strip below it, next to Status, Priority, Owner and Account. Working out how much time is left is the agent's own arithmetic.

The case detail page shows a live SLA countdown in the header:
Two views help with that, neither of them a timer:

- ⏱ *2h 14m remaining* — fine
- ⚠️ *24m remaining* — warning zone
- 🚨 *BREACHED – 1h 32m over* — escalation territory
- **SLA Calendar** lays cases out on their **SLA Due Date**, so in practice it shows the Critical ones and whatever else was dated by hand.
- **SLA at Risk** lists open **High and Critical** cases sorted by due date, soonest first. It selects on priority, not on how close the deadline is, so a High case with no due date sits in it from the day it opens.

## SLA performance reporting

Expand Down Expand Up @@ -121,7 +126,7 @@ If priority resolves to **Critical**, the Copilot immediately recommends escalat

## Tips for service agents

- ✅ Watch your SLA countdown — when you see the amber zone, ask for help.
- ✅ Watch **SLA Due Date** yourself — there is no countdown and no amber zone to warn you, so keep **SLA at Risk** or **SLA Calendar** open during your shift and ask for help before the date passes.
- ✅ Use **Waiting on Customer** when you're genuinely blocked — depending on your config, this can pause the SLA clock.
- ✅ Don't artificially down-prioritise to extend your SLA — managers see priority change history.

Expand Down
37 changes: 21 additions & 16 deletions content/docs/service/sla-and-escalation.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,42 @@ description: 优先级如何驱动截止时间、工单何时自动升级,以

# SLA 与升级

SLA(服务级别协议)系统根据每个工单的优先级为其赋予一个**截止时间**,实时呈现**违约**,并自动**升级**卡住的工单——升级会打上标记并提醒工单负责人,但不会把工单转交给资深员工。
SLA(服务级别协议)系统为 Critical 工单打上一个**截止时间**,靠每小时一轮的扫描标记**违约**,并自动**升级**卡住的工单——升级会打上标记并提醒工单负责人,但不会把工单转交给资深员工。

## SLA 目标如何计算

每个工单在创建的那一刻就会获得一个 **SLA 截止日期**。该目标基于优先级:
**只有 Critical 工单会自动获得 SLA 截止日期。** `case_sla_defaults` 钩子(`src/objects/case.hook.ts`)在工单以 Critical 优先级创建、或被改为 Critical 且该字段仍为空时,把 `sla_due_date` 填成*当前时间 + 4 小时*。在 **High**、**Medium**、**Low** 三个优先级上它什么都不写,所以除非有人手工填一个日期进去,这个字段一直是空的。

| 优先级 | 默认 SLA 目标 | "违约"是什么样子 |
因此下表的四行其实是两类东西:Critical 的 4 小时是系统真会盯的截止时间,另外三个是团队的服务承诺,背后没有任何时钟。

| 优先级 | 目标 | "违约"是什么样子 |
| --- | --- | --- |
| **Critical** | 4 小时 | SLA 监控流程把它标记为 **SLA 已违约**并升级,然后通过站内消息 + 邮件提醒**工单负责人**。工单详情页上没有红色横幅——本应用没有任何横幅机制——也不会向支持经理发出警报:该 notify 节点的收件人列表只有 `{currentCase.owner_id}` 一项 |
| **High** | 8 小时 | 列表视图中的红色徽章 |
| **Medium** | 2 个工作日 | 琥珀色徽章 |
| **Low** | 7 个工作日 | 灰色徽章 |
| **Critical** | 4 小时,会被写入 `sla_due_date` | SLA 监控流程把它标记为 **SLA 已违约**并升级,然后通过站内消息 + 邮件提醒**工单负责人**。工单详情页上没有红色横幅——本应用没有任何横幅机制——也不会向支持经理发出警报:该 notify 节点的收件人列表只有 `{currentCase.owner_id}` 一项 |
| **High** | 8 小时——只是服务承诺;没有任何东西会写 `sla_due_date` | 什么都不会发生。`case_sla_monitor` 只捞 `sla_due_date` 已经过去的工单,所以一个没有截止日期的工单永远不会入选,**SLA 已违约**始终是 `false`。也没有红色徽章:工单列表上唯一的颜色规则是 *All Cases* 视图的**行底色**,它键在**优先级**上(`#f97316`,橙色),从工单创建的那一刻起就一直是这个颜色,与违不违约无关 |
| **Medium** | 2 个工作日——只是服务承诺;没有任何东西会写 `sla_due_date` | 同样什么都不会发生。看上去像琥珀色的那些行,就是同一条按优先级着色的行底色(`#eab308`,黄色),不是违约徽章 |
| **Low** | 7 个工作日——只是服务承诺;没有任何东西会写 `sla_due_date` | 同样什么都不会发生。灰色的行是按优先级着色的行底色(`#94a3b8`) |

在列表视图里,真正报告违约的是 **SLA 已违约**这一列,而不是行底色。

SLA 目标可按优先级配置——参见下方的*给管理员的提示*部分。
Critical 的 4 小时是系统唯一会盯的目标,而且它写在钩子里,不在任何设置页面上——参见下方的*给管理员的提示*部分。

> **营业时间**——系统可以配置为只计算营业时间(例如周一至周五 9-5),而非日历时间。参见 [管理 › 设置](/zh-Hans/docs/administration/setup)。

## SLA 如何被跟踪

每个工单上有两个字段:

- **SLA 截止日期**——工单必须在何时之前解决。
- **SLA 已违约?**——当解决时间超过目标时为 `true`。
- **SLA 截止日期**(`sla_due_date`)——工单必须在何时之前解决。凡是 Critical 钩子没有打过戳的工单,这个字段都是空的,除非有人手工填过。
- **SLA 已违约**(`is_sla_violated`)——并不存在一个叫 *SLA 已违约?* 的字段;客服看到的那个勾选框,标签就是 **SLA 已违约**。它并不拿解决时间去和目标做比较:每小时一轮的 `case_sla_monitor` 扫描,会把那些仍然打开(状态既不是 *Resolved* 也不是 *Closed*)、且 **SLA 截止日期**已经过去的工单置为 `true`。由此有两个值得知道的后果:没有截止日期的工单永远不会入选;在下一轮扫描之前就被解决掉的超时工单,也不会被标记。

违约标记对客服是**只读的**——他们无法篡改,值本身由 `case_sla_monitor` 写入,该流程以系统身份运行而不是以用户身份运行。不过这道门是字段级安全,不是字段上的 readonly:服务专员权限档案把 `crm_case.is_sla_violated` 遮罩成可读不可编辑,所以客服能在工单上看到这个勾选框(表单的 **SLA** 页签、详情页的 **Status & SLA** 分区),却改不动它。随产品提供的遮罩清单见[权限档案](/zh-Hans/docs/administration/profiles)。

违约标记对客服是**只读的**——他们无法篡改。它由系统自动计算。
**并不存在实时 SLA 倒计时。** 本应用里没有任何东西会计算"还剩多久"或"超时多久"——没有阈值配色,没有警告区,工单上任何地方都没有 ⏱ / ⚠️ / 🚨 这套呈现。页眉里只有工单编号、主题和账户,别的什么都没有;**SLA 截止日期**与 **SLA 已违约**是渲染在页眉下方**关键信息**摘要栏里的两个普通字段,和状态、优先级、负责人、账户并排。还剩多少时间,得客服自己算。

工单详情页在页眉中显示实时 SLA 倒计时:
有两个视图可以帮上忙,但它们都不是计时器:

- ⏱ *剩余 2h 14m*——正常
- ⚠️ *剩余 24m*——警告区
- 🚨 *已违约 – 超时 1h 32m*——升级范畴
- **SLA Calendar** 按 **SLA 截止日期**把工单铺在日历上,所以实际上它显示的是 Critical 工单,外加那些被手工填过日期的工单。
- **SLA at Risk** 列出打开的 **High 与 Critical** 工单,按截止日期升序排列。它筛的是优先级,不是"离截止还有多近",所以一个没有截止日期的 High 工单从建单那天起就一直待在里面。

## SLA 表现报表

Expand Down Expand Up @@ -121,7 +126,7 @@ SLA 目标可按优先级配置——参见下方的*给管理员的提示*部

## 给服务客服的提示

- ✅ 关注你的 SLA 倒计时——当你看到琥珀色区域时,请求帮助。
- ✅ 自己盯着 **SLA 截止日期**——没有倒计时,也没有琥珀色区域会来提醒你,所以当班时把 **SLA at Risk** 或 **SLA Calendar** 视图开着,赶在日期过去之前求助。
- ✅ 当你确实被阻塞时使用 **Waiting on Customer**——取决于你的配置,这可以暂停 SLA 计时。
- ✅ 不要人为降低优先级来延长你的 SLA——经理可以看到优先级变更历史。

Expand Down
Loading
Loading