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
37 changes: 37 additions & 0 deletions .changeset/1992-case-created-at.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
'hotcrm': patch
---

Cases you create now count in "Cases Opened by Priority × Day" and in the Customer Service dashboard's date range

A case created in the app or through the REST API never showed up in the
**Cases Opened by Priority × Day** report or inside the **Customer Service**
dashboard's date range. Both read the case's **Created Date** field, and nothing
filled that field in on a real case: only the demo data set it. So a new case
stored no Created Date, the report left it out, and the dashboard range skipped
it. Managers saw the 38 demo cases and none of their own.

**FROM → TO.** Cases now use one creation timestamp, the platform's own
`created_at`, which is recorded on every case however it is raised. This is the
same change opportunities got earlier (#575).

- `crm_case.created_date` is removed. Read `created_at` instead, the field every
object already carries. An integration that read `created_date` through the
API should switch to `created_at`.
- The `case_metrics` dataset's day dimension is now `created_at` (label
**Created**, still bucketed by day). It was `created_date`. A saved query or
widget that grouped by `created_date` should group by `created_at`.
- **Cases Opened by Priority × Day** buckets on `created_at` and no longer
filters anything out. The Customer Service dashboard's date range and its
**Daily Case Volume** chart use `created_at`. So does the **Case Timeline**
view, whose bars now start on the day each case was created. Before, a case
created in the app had no start date there.
- **Resolution Time (Hours)** is measured from `created_at` to **Closed Date**.
- The **SLA & Priority** group on a case has six fields instead of seven.
**Created Date** is no longer one of them, and the four language packs no
longer translate it.

The demo data keeps its history. The seeded cases now set `created_at` to the
day each one was opened. Since `@objectstack/*` 17.7.0 the platform keeps that
value when a fresh database is seeded, so the report and the dashboard spread
the demo cases over their own days, not the day you ran the demo.
12 changes: 6 additions & 6 deletions content/docs/service/cases.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ This drives reporting (which channels generate the most volume) and is auto-fill

## What a case record stores

`crm_case` declares **25 fields** (`src/service/objects/case.object.ts`), and three different things organise them: the object's own field groups, the case detail screen, and the case form. Only the first of the three has six sections, and no two of them agree — so it is worth knowing which one you are being shown.
`crm_case` declares **24 fields** (`src/service/objects/case.object.ts`), and three different things organise them: the object's own field groups, the case detail screen, and the case form. Only the first of the three has six sections, and no two of them agree — so it is worth knowing which one you are being shown.

### The object's field groups

Expand All @@ -62,7 +62,7 @@ The six names below are real, but not one of them is a screen: they are `crm_cas
| --- | --- |
| **Case Information** | Case Number, Subject, Display Title, Description, Account, Contact, Status, Case Type |
| **Origin & Routing** | Case Owner, Case Origin |
| **SLA & Priority** | Priority, Created Date, Closed Date, First Response Date, Resolution Time (Hours), SLA Due Date, SLA Violated |
| **SLA & Priority** | Priority, Closed Date, First Response Date, Resolution Time (Hours), SLA Due Date, SLA Violated |
| **Resolution** | Resolution, Resolved by Article |
| **Escalation** *(collapsed by default)* | Escalated, Escalated Date, Escalation Reason |
| **System** *(collapsed by default)* | Internal Notes, Is Closed |
Expand All @@ -71,7 +71,7 @@ Five of those six rows used to be written differently on this page, and the diff

### The detail screen

The **Details** tab of the case detail page (`src/service/pages/case_detail.page.ts`) declares **three** sections, holding **10** of the 25 fields:
The **Details** tab of the case detail page (`src/service/pages/case_detail.page.ts`) declares **three** sections, holding **10** of the 24 fields:

| Section | Fields |
| --- | --- |
Expand All @@ -85,7 +85,7 @@ The tab is shorter than its section names suggest, and that is deliberate: a sec

### The form

The case form (`src/service/views/case.view.ts`) is **not** tabbed. It is a single section, holding **9** of the 25 fields:
The case form (`src/service/views/case.view.ts`) is **not** tabbed. It is a single section, holding **9** of the 24 fields:

| Section | Fields |
| --- | --- |
Expand All @@ -101,7 +101,7 @@ The two are not one screen wearing different chrome — they overlap in only fou
- **First response date** (`first_response_date`) — stamped the first time an interaction that **already took place** is recorded against the case. Its single writer is the `event_activity_bubble` hook (`src/sales/objects/event.hook.ts`), which fires on any `crm_event` reaching **Held** status with the case as its related record — so **Log a Call** and **Log a Meeting** stamp it, and so does an interaction entered any other way, including straight onto the case's activity list. It reads the stored value back before writing, so a second interaction never overwrites the first. Two things are deliberately *not* a first response: a **status change** (an agent can move a case to *In Progress* and investigate for an hour while the customer hears nothing) and a meeting merely *scheduled*, which is `planned` rather than held. On a case worked entirely through comments the field stays empty.
- **Closed flag** — set automatically when status changes to *Closed*.
- **Closed date** — stamped to NOW when status changes to *Closed*.
- **Resolution time (hours)** — calculated as the difference between *Created date* and *Closed date*.
- **Resolution time (hours)** — calculated as the time between the moment the case was created and its *Closed date*. The creation moment is the platform's own timestamp, written on every case however it was raised; a case has no separate *Created Date* field of its own.
- **SLA due date** — stamped on **every** case with a recognised priority. `case_sla_defaults` looks up the case's priority against its account's **Customer Tier** and fills the field with *now + the matching number of calendar hours*, when a case is created and whenever the field is still empty. It is written once and never recomputed, and a date somebody typed in by hand is never overwritten.
- **SLA Violated** (`is_sla_violated`) — there is no field called *SLA Breached?*. 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** has already passed. It never compares resolution time against the target, so a case with no due date is never a candidate, and a case resolved late but before the next sweep is never flagged.
- **Account's last activity date** — bumped to today when the case is updated.
Expand Down Expand Up @@ -197,7 +197,7 @@ When you open a case, you'll see:
- **Status path** — a Salesforce-style horizontal flow showing New → In Progress → Waiting on Customer → Escalated → Resolved → Closed, so an agent can see at a glance where the case stands and what the next stop is.
- **Customer panel** — there is no such panel. The page (`src/service/pages/case_detail.page.ts`) declares two regions and nothing else: a header holding the three components above, and a main region holding the tab strip below. What customer context there is sits in those: the **account** appears twice over — the header subtitle and the Key Information strip — and the **primary contact** is a field in the *Details* tab's *Case Information* section. **Contract tier** and **open cases this month** are on no case surface at all. A case links to no contract, and `crm_contract` carries no tier field either; the nearest real field is **Customer Tier** on the account record, which this page does not show. Nothing in this app counts a customer's cases by month.
- **Three tabs:**
- **Details** — the case description, resolution and internal notes in full, but not "all metadata fields": **10** of the object's **25**, in the three sections listed under *What a case record stores* above. Of the fifteen that are not on the tab, seven are elsewhere on this same screen: **Status**, **Priority**, **SLA Due Date**, **SLA Violated**, **Case Owner** and **Account** are on the **Key Information** strip above the tabs, and **Subject** is half the page title. The remaining eight are on no case screen at all — **Created Date**, **Closed Date**, **Escalated Date**, **First Response Date**, **Is Closed**, **Priority Rank**, **Display Title** and **Resolved by Article**. **Created Date**, **Closed Date** and **Is Closed** are stamped as the case opens and closes and are read by the *Case Timeline* and *Service Workflow* views rather than by this screen, **Escalated Date** is written by the escalation flows and displayed nowhere, **Priority Rank** is the sort key behind *My Open Cases*, **Display Title** is the object's `nameField`, the title formula that names a case wherever one is referenced rather than a field you read here, and **Resolved by Article** is read by the deflection measures rather than by a screen. **First Response Date** is the omission worth knowing about: *What happens automatically* above explains at length how it gets stamped, and this is not the tab you can read it on.
- **Details** — the case description, resolution and internal notes in full, but not "all metadata fields": **10** of the object's **24**, in the three sections listed under *What a case record stores* above. Of the fourteen that are not on the tab, seven are elsewhere on this same screen: **Status**, **Priority**, **SLA Due Date**, **SLA Violated**, **Case Owner** and **Account** are on the **Key Information** strip above the tabs, and **Subject** is half the page title. The remaining seven are on no case screen at all — **Closed Date**, **Escalated Date**, **First Response Date**, **Is Closed**, **Priority Rank**, **Display Title** and **Resolved by Article**. **Closed Date** and **Is Closed** are stamped as the case closes and are read by the *Case Timeline* and *Service Workflow* views rather than by this screen, **Escalated Date** is written by the escalation flows and displayed nowhere, **Priority Rank** is the sort key behind *My Open Cases*, **Display Title** is the object's `nameField`, the title formula that names a case wherever one is referenced rather than a field you read here, and **Resolved by Article** is read by the deflection measures rather than by a screen. **First Response Date** is the omission worth knowing about: *What happens automatically* above explains at length how it gets stamped, and this is not the tab you can read it on.
- **Related** — one list, not four: **Open Tasks** — the `crm_task` records pointing at this case through **Related Case** (`related_to_case`), filtered to those whose status is not *Completed*, ten at a time. That is the whole tab. There is no attachments list: files can be attached to a case (the object enables them), but no component on this page lists them. There is no linked-opportunity list, because `crm_case` has no opportunity relationship in either direction. And there is no milestone list — the case's three milestones, *escalated*, *resolved* and *closed*, are emitted as entries in the **Activity** timeline, not as records here.
- **Activity** — a unified timeline (more on this below).
- **AI Reference Rail** — there is no rail on this page, of any kind. The main region holds the tab strip and nothing else, and not one component on the page is AI-driven. The service skills are real — see *How the AI assistant helps* below — but you reach them by asking, not from a panel beside the case. The one reference rail this app does render sits on the opportunity detail page, and even there it lists related records (quotes, products, open tasks) rather than suggestions.
Expand Down
Loading
Loading