Report Packs Starters Blocks Developer Guide - edmozley/freeitsm GitHub Wiki

Report Packs: Starters & leavers blocks โ€” Developer Guide

5.2.0 ยท #324 ยท User guide: Report Packs - the Starters & leavers blocks ยท Module: Starters and leavers ยท Engine: Report Packs Developer Guide

๐Ÿ”‘ The one-line version

Four block handlers in includes/report_packs/blocks_starters.php read jml_cases and the case's steps (the subtasks of its task) the same way the Starters register does. A case is in the period by its date (effective_date), never by when it was opened.

๐Ÿ“ Files

File What
includes/report_packs/blocks_starters.php The four data functions and their helpers
includes/report_packs/blocks.php require_once of the file; the handlers starters.kpis, starters.breakdown, starters.list, starters.overdue in rpHandlers(); the toolbox items starters_kpis, starters_by_kind, starters_list, starters_overdue in rpToolboxItems()
assets/js/report-packs/designer.js (v=5) ['starters', 'designer.area.starters'] in AREAS, so the toolbox shows the area
lang/en/reporting.php packs.starters.* (labels), packs.tool.starters_* (toolbox titles and descriptions), packs.designer.area.starters, help.packs_area_starters(_body)
reporting/help.php starters added to the list of areas in the Report Packs help
includes/feature_bingo/cards/starters.php Card starters.report_pack - a pack with a "handler":"starters. block
tests/report-packs-starters.php 17 checks against seeded cases (below)

๐Ÿ—„๏ธ The data it reads

Nothing new in the database. A case and its steps:

jml_cases  (id, tenant_id, kind 'starter|mover|leaver', person_name, job_title,
            effective_date DATE,          -- first day, move date or last day
            status 'open|done|cancelled', template_id, parent_task_id, owner_analyst_id)
tasks      (id, parent_task_id, status_id, due_date DATE, completed_datetime DATETIME,
            assigned_analyst_id, assigned_team_id)   -- a step = a subtask of the case's task
task_statuses (id, is_closed)                         -- a step is done when its status is closed

Progress is never stored on the case. It is counted from the steps every time, exactly as StartersService::listCases() counts it:

function rpStartersProgressCols(): string
{
    return "(SELECT COUNT(*) FROM tasks s WHERE s.parent_task_id = c.parent_task_id) AS steps_total,
            (SELECT COUNT(*) FROM tasks s JOIN task_statuses ss ON ss.id = s.status_id
              WHERE s.parent_task_id = c.parent_task_id AND ss.is_closed = 1) AS steps_done";
}

๐Ÿ“… "In the period" and the company

One helper builds the WHERE clause for every period block:

function rpStartersWhere(PDO $conn, int $analystId, array $range, $tenant,
                         string $kind = '', bool $withCancelled = false): array
{
    [$tSql, $tArgs] = rpTenantClause($conn, $analystId, $tenant, 'c.tenant_id');  // NULL = Default company
    $sql  = ' WHERE c.effective_date BETWEEN ? AND ?';                              // the person's date
    $args = [$range['from_date'], $range['to_date']];
    if (!$withCancelled) $sql .= " AND c.status <> 'cancelled'";
    if ($kind !== '' && $kind !== 'all' && JmlKind::tryFrom($kind)) { $sql .= ' AND c.kind = ?'; $args[] = $kind; }
    return [$sql . $tSql, array_merge($args, $tArgs)];
}
  • ๐Ÿ”‘ The date, not the opening. "Starters this month" in a pack means the people starting this month. A case opened in January for a March start is a March figure.
  • Cancelled cases are left out of everything except the chart grouped by status, which is the one place they belong ($withCancelled = true there only).
  • The company comes from rpTenantClause(), like every other block, so a pack set to one company never shows another's cases.
  • Access: each handler declares 'module' => 'starters'. The engine refuses the block for a viewer without Starters & leavers (analystCanAccessModule() in rpBlockData()), and a shared pack shows that viewer a placeholder instead of the figures.

โœ… Ready on the day

Of the period's cases whose date has come (effective_date <= today) and that have steps, how many had every step closed by the end of their date. It is worked out in one query as "no step that is still open, has no completion time, or was completed on or after the day after the date":

SELECT COUNT(*) AS total,
       SUM(CASE WHEN NOT EXISTS (
               SELECT 1 FROM tasks s LEFT JOIN task_statuses ss ON ss.id = s.status_id
                WHERE s.parent_task_id = c.parent_task_id
                  AND (COALESCE(ss.is_closed, 0) = 0 OR s.completed_datetime IS NULL
                       OR s.completed_datetime >= DATE_ADD(c.effective_date, INTERVAL 1 DAY))
           ) THEN 1 ELSE 0 END) AS ready
  FROM jml_cases c
 WHERE ...period... AND c.effective_date <= ?            -- today
   AND EXISTS (SELECT 1 FROM tasks s2 WHERE s2.parent_task_id = c.parent_task_id)
  • A case with no steps is counted neither way - it would otherwise always be "ready".
  • A case dated in the future is not counted yet - it has not had its chance.
  • โš ๏ธ completed_datetime is UTC and effective_date is the organisation's calendar day, so a step closed in the last hour or two of the day can land either side. That is accepted for a monthly figure; do not "fix" it by converting one row at a time in PHP.

โฐ Overdue steps

Open steps (COALESCE(ss.is_closed, 0) = 0) of open cases with due_date < today. This is a snapshot of now, so the block sets 'snapshot' => true and the pack says so under its title. The summary's Steps overdue tile uses the same rule, so the tile and the table always agree. Days late are worked out in PHP from the two dates. The pill is amber up to a week late and red after that.

๐Ÿงฑ The handlers

Handler Kind Options Function
starters.kpis kpi - rpStartersKpis() - starters, movers, leavers (in the period), Ready on the day (rpStartersReadyCounts()), open cases and steps overdue (now)
starters.breakdown chart by kind / status / template, chart, limit rpStartersBreakdown() - through the shared rpChartFromRows(); kind and status get fixed colours (RP_STARTERS_KIND_COLOURS, RP_STARTERS_STATUS_COLOURS) and are translated after the query, so a German pack says Eintritte rather than starter
starters.list table kind, status all / open, owner, limit rpStartersList() - one row per case; job title and template in the _detail line; limit + 1 rows fetched so it can say the list was cut short
starters.overdue table kind, limit rpStartersOverdue() - snapshot

To add a fifth block, follow Report Packs Internals 3 - adding a block: a data function here, a handler in rpHandlers(), a toolbox item in rpToolboxItems() with 'area' => 'starters', and packs.tool.<key>.title/desc in lang/en/reporting.php. tests/report-packs-blocks.php then runs it with every option automatically.

โš ๏ธ Traps

  • TRAP: filter by effective_date, never created_datetime. Every figure here is about when people join or leave. A case opened early would otherwise count in the wrong month.
  • TRAP: never store progress on the case. Count it from the subtasks, as the register does. A stored count would drift the first time someone closes a step from the Tasks module.
  • TRAP: the summary's overdue tile and the overdue table must use the same rule (open step, open case, due before today). If one changes, change both.

๐Ÿงช Tests

  • tests/report-packs-starters.php (17, prtest only: FREEITSM_TEST_DATABASE=freeitsm_prtest). It seeds six cases dated in 2001, so no real case falls in its period, inside one transaction that is always rolled back. It checks:
    • the kind counts, with a cancelled case left out;
    • Ready on the day as 1 of 3: one step closed on the day counts as ready, a step closed the next day does not, and a case with no steps is not counted;
    • overdue rising by exactly 2: the cancelled case's step and a step not yet due are not counted;
    • the chart by kind, by status (with the cancelled case) and by template ("No template");
    • the list: its order, progress 50% (1/2) and -, Open only, the kind filter and the row limit;
    • the overdue table: 10 days late, the kind filter, and nothing left behind.
  • tests/report-packs-blocks.php (45, read-only) runs every handler, including these four, with every option value, and checks every label resolves.

See also: Report Packs Developer Guide ยท Starters and leavers

โš ๏ธ **GitHub.com Fallback** โš ๏ธ