
   FAIL  Tests\Feature\ReportingTest
  ✓ the index lists only the reports the readers doors open                                                      2.68s  
  ✓ each report door is enforced per report with an audited refusal                                              2.93s  
  ✓ the export grants land on the roles that hold metrics export                                                 0.95s  
  ✓ finance reconciliation states its definitions and ties out                                                   1.08s  
  ✓ corrections move the finance figures and the books still tie out                                             0.95s  
  ✓ the cohort grid suppresses small cells and keeps true totals                                                 1.09s  
  ⨯ the workforce report derives hours and suppresses thin departments                                           0.97s  
  ✓ every report page states source refresh and denominator                                                      1.77s  
  ✓ the csv export needs the separate export door                                                                1.25s  
  ⨯ the csv carries definitions suppressed cells and the tie out                                                 0.96s  
  ✓ reads and exports write their own audit rows                                                                 1.12s  
  ✓ an impossible or malformed range is refused by name                                                          0.96s  
  ⨯ a future only range reports the rate as undefined not zero                                                   1.13s  
  ✓ the speculative metrics schema and models are gone                                                           0.94s  
  ✓ the module gate holds the section out of the pharmacy edition                                                1.21s  
  ✓ the metrics nav item follows the union of report doors                                                       1.87s  
  ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\ReportingTest > the workforce report derives hours and suppresses thi…  AssertionFailedError   
  The report no longer carries a figure named "People with rows".

  at tests\Feature\ReportingTest.php:82
     78▕                 }
     79▕             }
     80▕         }
     81▕ 
  ➜  82▕         $this->fail('The report no longer carries a figure named "'.$label.'".');
     83▕ 
     84▕         return 0.0;
     85▕     }
     86▕

  1   tests\Feature\ReportingTest.php:82
  2   tests\Feature\ReportingTest.php:398

  ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\ReportingTest > the csv carries definitions suppressed cells and the tie out                   
  Expected: Report,"Finance reconciliation"\n
"Generated at","2026-10-19 18:26:29"\n
Source,"invoices, payment_point_transactions (the till register), credit_notes, deposits — read live, no stored metrics"\n
Refresh,"On page load; nothing is cached or precomputed"\n
Denominator,"All non-voided invoices for this organisation (draft, issued, part-paid, paid), all time as at the moment the page opens — a period slice could not tie out, because collections and charges land in different months"\n
"Tie-out variance",0.00,"Ties out"\n
\n
"Reconciliation — charges, collections, corrections"\n
Figure,"Amount (NGN)",Note\n
"Charges invoiced (non-voided)",1456400.00,\n
"Collected on invoices (gross)",1270200.00,\n
"Refunds and reversals on invoices",0.00,"0 negative register row(s) — append-only, never edits"\n
"Net collected on invoices",1270200.00,\n
"Credit notes issued and active",0.00,"Reduces what is owed without touching money collected (DEC24)"\n
"Outstanding on non-voided invoices",186200.00,\n
"Variance — invoiced less net, credits and outstanding",0.00,"Zero means the books tie out"\n
\n
"Held, excluded and outside the reconciliation"\n
Figure,"Amount (NGN)",Note\n
"Collections outside invoices (net of refunds)",0.00,"0 register row(s) with no invoice behind them — deposit takings"\n
"Deposits held for patients",0.00,"0 deposit(s) still holding money — money kept, never revenue"\n
"Voided invoices (excluded from every figure above)",0.00,"0 voided invoice(s)"\n
\n


  To contain: Report,Finance reconciliation

  at tests\Feature\ReportingTest.php:465
    461▕             ->assertOk()
    462▕             ->streamedContent();
    463▕ 
    464▕         // Definitions first (F08) — same strings the screen prints.
  ➜ 465▕         $this->assertStringContainsString('Report,Finance reconciliation', $csv);
    466▕         $this->assertStringContainsString('Source,', $csv);
    467▕         $this->assertStringContainsString('Refresh,', $csv);
    468▕         $this->assertStringContainsString('Denominator,', $csv);
    469▕         $this->assertStringContainsString('payment_point_transactions', $csv);

  1   tests\Feature\ReportingTest.php:465

  ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\ReportingTest > a future only range reports the rate as undefined not zero                     
  Expected: <!DOCTYPE html>\n
<html lang="en">\n
<head>\n
    <meta charset="utf-8">\n
    <meta name="viewport" content="width=device-width, initial-scale=1">\n
    <meta name="robots" content="noindex, nofollow">\n
    \n
    <meta name="csrf-token" content="kuylWrHxJVLaa13WfnMykLJiDS7EhrbiJ9RnIjmy">\n
    <title>Appointment outcomes — BITMED</title>\n
    <script>\n
        // Resolve the theme before first paint so there is no flash of the\n
        // wrong palette. An explicit choice is remembered; otherwise the\n
        // operating-system preference is honoured.\n
        (function () {\n
            try {\n
                var saved = window.localStorage.getItem('bitmed-theme');\n
                if (saved !== 'light' && saved !== 'dark') {\n
                    saved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n
                }\n
                document.documentElement.setAttribute('data-theme', saved);\n
            } catch (error) {\n
                document.documentElement.setAttribute('data-theme', 'light');\n
            }\n
        })();\n
    </script>\n
    <link rel="preload" href="/assets/fonts/InterVariable.woff2" as="font" type="font/woff2" crossorigin>\n
    <link rel="stylesheet" href="/assets/css/bitmed.css">\n
</head>\n
<body>\n
<a class="skip-link" href="#main-content">Skip to main content</a>\n
\n
<div class="app" data-nav="closed" id="app-shell">\n
    <aside class="app__sidebar" aria-label="Primary navigation">\n
        <a class="app__brand" href="http://127.0.0.1:8000/dashboard">\n
            <span class="app__brand-mark" aria-hidden="true">B</span>\n
            <span class="app__brand-text">\n
                BITMED\n
                <span class="app__brand-sub">\n
                    Lagos Family Clinic\n
                </span>\n
            </span>\n
        </a>\n
\n
        <nav class="app__nav">\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#home"></use></svg>\n
                                                Overview\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/dashboard"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#layout-dashboard"></use></svg>\n
                                                        <span>Dashboard</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/patients"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#users"></use></svg>\n
                                                        <span>Patients</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#heart-pulse"></use></svg>\n
                                                Care\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/appointments"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#calendar"></use></svg>\n
                                                        <span>Appointments</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/queue"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#list-check"></use></svg>\n
                                                        <span>Queue</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/calendar"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#calendar-days"></use></svg>\n
                                                        <span>Calendar</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/results/deliveries"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#send"></use></svg>\n
                                                        <span>Result deliveries</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/specimens"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#test-tube"></use></svg>\n
                                                        <span>Specimens</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/specimens/process"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#microscope"></use></svg>\n
                                                        <span>Processing &amp; QC</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bed"></use></svg>\n
                                                Admission\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/beds"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bed"></use></svg>\n
                                                        <span>Bed management</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/wards"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#building"></use></svg>\n
                                                        <span>Wards</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#credit-card"></use></svg>\n
                                                Finance\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/service-tariffs"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#file-text"></use></svg>\n
                                                        <span>Service catalogue</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/product-items"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#package"></use></svg>\n
                                                        <span>Product catalogue</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/invoices"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#file-invoice"></use></svg>\n
                                                        <span>Invoices</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/payment-points"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#credit-card"></use></svg>\n
                                                        <span>Payment points</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/wallets"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#wallet"></use></svg>\n
                                                        <span>Wallets</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/cards"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#credit-card"></use></svg>\n
                                                        <span>Cards</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/payment-adapters"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#terminal"></use></svg>\n
                                                        <span>Payment adapters</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/deposits"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#piggy-bank"></use></svg>\n
                                                        <span>Deposits</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/sponsorships"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#users"></use></svg>\n
                                                        <span>Sponsors</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/refunds"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#rotate-ccw"></use></svg>\n
                                                        <span>Refunds</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#cog"></use></svg>\n
                                                Operations\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/employees"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#users"></use></svg>\n
                                                        <span>Staff</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/leave"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#calendar-x"></use></svg>\n
                                                        <span>Leave</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/attendance"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#clock"></use></svg>\n
                                                        <span>Attendance</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/welfare"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#heart-handshake"></use></svg>\n
                                                        <span>Welfare</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/payroll"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#banknote"></use></svg>\n
                                                        <span>Payroll</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/payslips"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#receipt"></use></svg>\n
                                                        <span>My payslips</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/rosters"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#calendar"></use></svg>\n
                                                        <span>Rosters</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/credentials"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#award"></use></svg>\n
                                                        <span>Credentials</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bar-chart-2"></use></svg>\n
                                                Reporting\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/metrics"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bar-chart-2"></use></svg>\n
                                                        <span>Metrics</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#help-circle"></use></svg>\n
                                                Assistance\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/ask"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bot"></use></svg>\n
                                                        <span>Ask BITMED AI</span>\n
                                                    </a>\n
                                    </div>\n
                            <div class="app__nav-group">\n
                    <div class="app__nav-heading">\n
                                                    <svg class="app__nav-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#settings"></use></svg>\n
                                                Setup\n
                    </div>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/setup/modules"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#puzzle"></use></svg>\n
                                                        <span>Modules</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/setup/users"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#user-cog"></use></svg>\n
                                                        <span>Users and grants</span>\n
                                                    </a>\n
                                            <a class="app__nav-link"\n
                           href="http://127.0.0.1:8000/setup/audit"\n
                           >\n
                                                            <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#history"></use></svg>\n
                                                        <span>Audit trail</span>\n
                                                    </a>\n
                                    </div>\n
                    </nav>\n
\n
        <div class="app__sidebar-foot">\n
            <button class="theme-toggle" type="button" aria-pressed="false">\n
                <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#moon"></use></svg>\n
                <span class="theme-toggle__label">Dark mode</span>\n
            </button>\n
        </div>\n
    </aside>\n
\n
    <div class="app__main">\n
        <header class="app__topbar">\n
            <button class="app__nav-toggle" type="button" aria-expanded="false" aria-controls="app-shell">\n
                <svg class="app__nav-item-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#menu"></use></svg>\n
                <span>Menu</span>\n
            </button>\n
\n
                            <form class="topbar-search" role="search" method="get" action="http://127.0.0.1:8000/patients">\n
                    <svg class="topbar-search__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#search"></use></svg>\n
                    <label class="visually-hidden" for="topbar-search-input">Search patients</label>\n
                    <input id="topbar-search-input" type="search" name="q" value=""\n
                           placeholder="Search patients by name or MRN" autocomplete="off">\n
                </form>\n
            \n
            <div class="app__topbar-spacer"></div>\n
\n
                            <div class="topbar-actions">\n
                                            <details class="menu add-menu">\n
                            <summary class="btn btn--dark">\n
                                <svg class="btn-icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#plus"></use></svg>\n
                                <span>Add</span>\n
                            </summary>\n
                            <div class="menu__panel">\n
                                                                    <a class="menu__item" href="http://127.0.0.1:8000/patients">\n
                                        <svg class="menu__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#search"></use></svg>\n
                                        <span>Find a patient</span>\n
                                    </a>\n
                                                            </div>\n
                        </details>\n
                    \n
                    <button class="icon-btn" type="button" aria-disabled="true"\n
                            title="Direct messaging is not part of this build yet.">\n
                        <svg class="icon-btn__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#message"></use></svg>\n
                        <span class="visually-hidden">Messages — not available in this build</span>\n
                    </button>\n
\n
                    <button class="icon-btn" type="button" aria-disabled="true"\n
                            title="Notification centre is not part of this build yet.">\n
                        <svg class="icon-btn__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#bell"></use></svg>\n
                        <span class="visually-hidden">Notifications — not available in this build</span>\n
                    </button>\n
\n
                    <a class="icon-btn" href="http://127.0.0.1:8000/settings/two-step" title="Account settings">\n
                        <svg class="icon-btn__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#settings"></use></svg>\n
                        <span class="visually-hidden">Account settings</span>\n
                    </a>\n
\n
                                        <details class="menu user-menu">\n
                        <summary class="user-chip">\n
                            <span class="user-avatar" aria-hidden="true">AO</span>\n
                            <span class="user-chip__meta">\n
                                <strong>Amaka Okonkwo</strong>\n
                                <span>Facility administrator</span>\n
                            </span>\n
                            <svg class="user-chip__caret" aria-hidden="true"><use href="/assets/icons/sprite.svg#chevron-down"></use></svg>\n
                        </summary>\n
                        <div class="menu__panel menu__panel--right">\n
                            <div class="menu__header">\n
                                <strong>Amaka Okonkwo</strong>\n
                                <span>admin@lagosfamilyclinic.bitmed.test</span>\n
                                <span class="menu__role">Facility administrator</span>\n
                            </div>\n
                            <a class="menu__item" href="http://127.0.0.1:8000/settings/two-step">\n
                                <svg class="menu__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#shield"></use></svg>\n
                                <span>Two-step verification</span>\n
                            </a>\n
                            <a class="menu__item" href="http://127.0.0.1:8000/dashboard">\n
                                <svg class="menu__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#home"></use></svg>\n
                                <span>Dashboard</span>\n
                            </a>\n
                            <form method="POST" action="http://127.0.0.1:8000/logout">\n
                                <input type="hidden" name="_token" value="kuylWrHxJVLaa13WfnMykLJiDS7EhrbiJ9RnIjmy" autocomplete="off">                                <button class="menu__item menu__item--button" type="submit">\n
                                    <svg class="menu__icon" aria-hidden="true"><use href="/assets/icons/sprite.svg#log-out"></use></svg>\n
                                    <span>Sign out</span>\n
                                </button>\n
                            </form>\n
                        </div>\n
                    </details>\n
                </div>\n
                    </header>\n
\n
        <main class="app__content" id="main-content">\n
            \n
            \n
            <div class="container mt-4">\n
    \n
    <div class="page-head mb-3">\n
        <div>\n
            <h1>Appointment outcomes</h1>\n
            <p class="lede">What happened to appointments — kept, missed, cancelled, still to come — with every rate naming the denominator it divides by, and an age-band breakdown that suppresses small cohorts.</p>\n
        </div>\n
        <div class="row" style="gap: .5rem;">\n
            <a class="btn btn--outline" href="http://127.0.0.1:8000/metrics">All reports</a>\n
                            <a class="btn btn--primary"\n
                   href="http://127.0.0.1:8000/metrics/appointments.outcomes/export?from=2026-10-20&amp;to=2026-10-22">Export CSV</a>\n
                    </div>\n
    </div>\n
\n
    \n
    \n
    <section class="card">\n
        <div class="card__head"><h2>What this report is</h2></div>\n
        <div class="card__body">\n
            <p style="margin: 0 0 .5rem;"><strong>Source:</strong> appointments, patients (age read from date of birth at read time) — read live, no stored metrics</p>\n
            <p style="margin: 0 0 .5rem;"><strong>Refresh:</strong> On page load; nothing is cached or precomputed</p>\n
            <p style="margin: 0;"><strong>Denominator:</strong> All appointments with a scheduled date in the selected range. Rates split the range into slot-passed (the past, whose outcomes are settled) and still-to-come (the future); age bands estimate from partial dates of birth and show Unknown when none is recorded</p>\n
            <p class="muted" style="margin: .75rem 0 0;">\n
                                    <strong>Range:</strong> All dates ·\n
                                <strong>Computed:</strong> 2026-10-19 18:26:32\n
            </p>\n
\n
                    </div>\n
    </section>\n
\n
            <section class="card">\n
            <div class="card__body">\n
                <form method="GET" action="http://127.0.0.1:8000/metrics/appointments.outcomes"\n
                      class="row" style="gap: .75rem; align-items: flex-end; flex-wrap: wrap;">\n
                    <div class="field">\n
                        <label for="report-from">From</label>\n
                        <input type="date" name="from" id="report-from"\n
                               value="">\n
                    </div>\n
                    <div class="field">\n
                        <label for="report-to">To</label>\n
                        <input type="date" name="to" id="report-to"\n
                               value="">\n
                    </div>\n
                    <button class="btn btn--primary" type="submit">Apply range</button>\n
                                    </form>\n
            </div>\n
        </section>\n
    \n
    <div class="metric-grid mb-3">\n
                    <div class="card">\n
    <div class="card__body kpi">\n
        <span class="kpi__label">Appointments in range</span>\n
        <span class="kpi__value" data-metric="appointments-in-range">\n
            19\n
            <span class="kpi__unit"></span>        </span>\n
        <span class="kpi__context">Every status</span>\n
    </div>\n
</div>\n
                    <div class="card">\n
    <div class="card__body kpi">\n
        <span class="kpi__label">Kept</span>\n
        <span class="kpi__value" data-metric="kept">\n
            5\n
            <span class="kpi__unit"></span>        </span>\n
        <span class="kpi__context">Completed appointments</span>\n
    </div>\n
</div>\n
                    <div class="card">\n
    <div class="card__body kpi">\n
        <span class="kpi__label">No-shows</span>\n
        <span class="kpi__value" data-metric="no-shows">\n
            2\n
            <span class="kpi__unit"></span>        </span>\n
        <span class="kpi__context">Missed without notice</span>\n
    </div>\n
</div>\n
                    <div class="card">\n
    <div class="card__body kpi">\n
        <span class="kpi__label">Still to come</span>\n
        <span class="kpi__value" data-metric="still-to-come">\n
            9\n
            <span class="kpi__unit"></span>        </span>\n
        <span class="kpi__context">Slot time not reached yet</span>\n
    </div>\n
</div>\n
            </div>\n
\n
            <section class="card">\n
            <div class="card__head"><h2>Appointments and outcomes in range</h2></div>\n
            <div class="card__body">\n
                                    <div class="table-wrap">\n
                        <table class="table">\n
                            <caption class="sr-only">Appointments and outcomes in range — Appointment outcomes</caption>\n
                            <thead>\n
                                <tr>\n
                                    <th scope="col">Measure</th>\n
                                    <th scope="col" class="tnum">Value</th>\n
                                </tr>\n
                            </thead>\n
                            <tbody>\n
                                                                    <tr>\n
                                        <td>\n
                                            Appointments scheduled in range\n
                                                                                    </td>\n
                                        <td class="tnum">19</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Booked\n
                                                                                    </td>\n
                                        <td class="tnum">9</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Confirmed\n
                                                                                    </td>\n
                                        <td class="tnum">0</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Checked in\n
                                                                                    </td>\n
                                        <td class="tnum">0</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            In progress\n
                                                                                    </td>\n
                                        <td class="tnum">0</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Completed\n
                                                                                    </td>\n
                                        <td class="tnum">5</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Cancelled\n
                                                                                    </td>\n
                                        <td class="tnum">3</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            No-show\n
                                                                                    </td>\n
                                        <td class="tnum">2</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Rescheduled\n
                                                                                    </td>\n
                                        <td class="tnum">0</td>\n
                                    </tr>\n
                                                            </tbody>\n
                        </table>\n
                    </div>\n
                \n
                            </div>\n
        </section>\n
            <section class="card">\n
            <div class="card__head"><h2>Rates, with the denominator each one divides by</h2></div>\n
            <div class="card__body">\n
                                    <div class="table-wrap">\n
                        <table class="table">\n
                            <caption class="sr-only">Rates, with the denominator each one divides by — Appointment outcomes</caption>\n
                            <thead>\n
                                <tr>\n
                                    <th scope="col">Measure</th>\n
                                    <th scope="col" class="tnum">Value</th>\n
                                </tr>\n
                            </thead>\n
                            <tbody>\n
                                                                    <tr>\n
                                        <td>\n
                                            Slot-passed appointments (denominator for the rates below)\n
                                                                                    </td>\n
                                        <td class="tnum">9</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Still to come\n
                                                                                    </td>\n
                                        <td class="tnum">9</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Kept (completed ÷ slot-passed)\n
                                                                                            <div class="muted">5 of 9 slot-passed appointments</div>\n
                                                                                    </td>\n
                                        <td class="tnum">55.6 %</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            No-show rate (no-shows ÷ slot-passed)\n
                                                                                            <div class="muted">2 of 9 slot-passed appointments</div>\n
                                                                                    </td>\n
                                        <td class="tnum">22.2 %</td>\n
                                    </tr>\n
                                                                    <tr>\n
                                        <td>\n
                                            Cancellation rate (cancelled ÷ slot-passed)\n
                                                                                            <div class="muted">2 of 9 slot-passed appointments</div>\n
                                                                                    </td>\n
                                        <td class="tnum">22.2 %</td>\n
                                    </tr>\n
                                                            </tbody>\n
                        </table>\n
                    </div>\n
                \n
                            </div>\n
        </section>\n
            <section class="card">\n
            <div class="card__head"><h2>Age band by outcome</h2></div>\n
            <div class="card__body">\n
                                    <div class="table-wrap">\n
                        <table class="table">\n
                            <caption class="sr-only">Age band by outcome — Appointment outcomes</caption>\n
                            <thead>\n
                                <tr>\n
                                                                            <th scope="col" >Age band</th>\n
                                                                            <th scope="col"  class="tnum" >Kept</th>\n
                                                                            <th scope="col"  class="tnum" >No-show</th>\n
                                                                            <th scope="col"  class="tnum" >Cancelled</th>\n
                                                                            <th scope="col"  class="tnum" >Rescheduled</th>\n
                                                                            <th scope="col"  class="tnum" >Booked or confirmed</th>\n
                                                                            <th scope="col"  class="tnum" >Checked in / in progress</th>\n
                                                                            <th scope="col"  class="tnum" >Total</th>\n
                                                                    </tr>\n
                            </thead>\n
                            <tbody>\n
                                                                    <tr>\n
                                                                                    <td >0-17</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >1</td>\n
                                                                            </tr>\n
                                                                    <tr>\n
                                                                                    <td >18-29</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >3</td>\n
                                                                            </tr>\n
                                                                    <tr>\n
                                                                                    <td >30-44</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >2</td>\n
                                                                            </tr>\n
                                                                    <tr>\n
                                                                                    <td >45-59</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >2</td>\n
                                                                            </tr>\n
                                                                    <tr>\n
                                                                                    <td >60+</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >1</td>\n
                                                                            </tr>\n
                                                                    <tr>\n
                                                                                    <td >Unknown</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >&lt;5</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >9</td>\n
                                                                                    <td  class="tnum" >0</td>\n
                                                                                    <td  class="tnum" >10</td>\n
                                                                            </tr>\n
                                                            </tbody>\n
                                                            <tfoot>\n
                                    <tr>\n
                                                                                    <th scope="col" >Total</th>\n
                                                                                    <th scope="col"  class="tnum" >5</th>\n
                                                                                    <th scope="col"  class="tnum" >2</th>\n
                                                                                    <th scope="col"  class="tnum" >3</th>\n
                                                                                    <th scope="col"  class="tnum" >0</th>\n
                                                                                    <th scope="col"  class="tnum" >9</th>\n
                                                                                    <th scope="col"  class="tnum" >0</th>\n
                                                                                    <th scope="col"  class="tnum" >19</th>\n
                                                                            </tr>\n
                                </tfoot>\n
                                                    </table>\n
                    </div>\n
                \n
                            </div>\n
        </section>\n
    \n
            <p class="muted" style="margin-top: 1rem;">\n
            <strong>Small-cohort rule:</strong> cells covering 1&ndash;4 people show as &lt;5,\n
            a true zero stays 0, and totals show their true values — so suppressed components\n
            may not add up. The CSV carries the same rule.\n
        </p>\n
    </div>\n
        </main>\n
\n
        <footer class="app__footer">\n
            Synthetic demonstration data only. No real patient records, and no real\n
            payments or notifications are generated by this build.\n
        </footer>\n
    </div>\n
</div>\n
\n
<script>\n
    (function () {\n
        var shell = document.getElementById('app-shell');\n
        var toggle = document.querySelector('.app__nav-toggle');\n
        if (shell && toggle) {\n
            toggle.addEventListener('click', function () {\n
                var open = shell.getAttribute('data-nav') === 'open';\n
                shell.setAttribute('data-nav', open ? 'closed' : 'open');\n
                toggle.setAttribute('aria-expanded', open ? 'false' : 'true');\n
            });\n
        }\n
\n
        // Only one top-bar menu is open at a time.\n
        var menus = Array.prototype.slice.call(document.querySelectorAll('details.menu'));\n
        menus.forEach(function (menu) {\n
            menu.addEventListener('toggle', function () {\n
                if (!menu.open) return;\n
                menus.forEach(function (other) {\n
                    if (other !== menu) other.open = false;\n
                });\n
            });\n
        });\n
\n
        document.addEventListener('click', function (event) {\n
            menus.forEach(function (menu) {\n
                if (menu.open && !menu.contains(event.target)) menu.open = false;\n
            });\n
        });\n
\n
        document.addEventListener('keydown', function (event) {\n
            if (event.key !== 'Escape') return;\n
            if (shell && toggle) {\n
                shell.setAttribute('data-nav', 'closed');\n
                toggle.setAttribute('aria-expanded', 'false');\n
            }\n
            menus.forEach(function (menu) { menu.open = false; });\n
        });\n
\n
        var themeButton = document.querySelector('.theme-toggle');\n
\n
        function syncThemeButton() {\n
            if (!themeButton) return;\n
            var dark = document.documentElement.getAttribute('data-theme') === 'dark';\n
            themeButton.setAttribute('aria-pressed', dark ? 'true' : 'false');\n
            var label = themeButton.querySelector('.theme-toggle__label');\n
            if (label) label.textContent = dark ? 'Light mode' : 'Dark mode';\n
        }\n
\n
        if (themeButton) {\n
            syncThemeButton();\n
            themeButton.addEventListener('click', function () {\n
                var dark = document.documentElement.getAttribute('data-theme') === 'dark';\n
                var next = dark ? 'light' : 'dark';\n
                document.documentElement.setAttribute('data-theme', next);\n
                try {\n
                    window.localStorage.setItem('bitmed-theme', next);\n
                } catch (error) {\n
                    /* Storage may be unavailable; the choice simply does not persist. */\n
                }\n
                syncThemeButton();\n
            });\n
        }\n
    })();\n
</script>\n
</body>\n
</html>\n


  To contain: From 20 Oct 2026 to 22 Oct 2026

  at tests\Feature\ReportingTest.php:559
    555▕             'to' => $to->toDateString(),
    556▕         ]));
    557▕ 
    558▕         $response->assertOk()
  ➜ 559▕             ->assertSee('From '.$from->format('d M Y').' to '.$to->format('d M Y'))
    560▕             ->assertSee('No slot has passed in this range, so the rate is undefined rather than zero.');
    561▕ 
    562▕         // The rate rows must not print a bare zero — no denominator means
    563▕         // no fraction, not a fraction of nothing.


  Tests:    3 failed, 13 passed (150 assertions)
  Duration: 22.24s

