Reconciliation
read-only · click a figure to scope everything below it · times in {{tzLabel}}
{{g.title}}
{{model.lastError}}
Pick an event to begin.
Loading…
{{model.data.eventTitle || ('Event #' + model.data.eventId)}}
with {{model.group.length}} other stage(s) · figures below count each person once
Showing
{{cohortLabel(model.data.appliedCohort)}} · {{model.data.appliedType}} only
Events {{model.data.groupEventIds}}
Could not be read
{{model.data.unavailable.join(', ')}} — those panels are blank because
the read failed, not because there is nothing to report. Every other figure here is
unaffected.
Produced {{model.data.generatedAtUtc | date:'dd.MM.yyyy HH:mm:ss'}} {{tzLabel}}
Every ticket classified
yes - {{partitionSum() | number}} = {{partitionTotal() | number}}
NO - {{partitionSum() | number}} ≠ {{partitionTotal() | number}}. Do not use these figures.
not available - the cohort figures did not come back, so the zeros below are not answers.
Known limits of this report
- Turned-away entries come from the scanners, not from the server. A scanner that already knows a ticket is used refuses it on screen and sends no scan, so nothing about the refusal ever reaches the server by itself. It is counted from the handsets' own record instead: a refusal made offline is included once that scanner catches up, and a scanner on a build older than 04.08.2026 keeps no such record and contributes nothing.
- The operator is missing on older scans. The person behind each handset has only been stored since 03.08.2026; earlier scans show under "(not recorded)".
- Gate history begins 02.08.2026. Which handset stood at which gate is recorded from that date; nothing reconstructs it for earlier events.
- An unknown code cannot name a gate or a scanner. The lookup that detects it carries only the code and the event, so a forged ticket is counted but not placed.
- Times are the handset's own clock at the moment the ticket was presented, not the moment the server received it.
Who they were — click to scope the figures below, except the per-gate table, which is never narrowed · the four exclusive groups sum to {{partitionTotal() | number}}
{{cohortCount(c.key) | number}}
{{c.label}}
{{c.sub}}
Cancelled, Every stage, Stopped partway and Never came are exclusive and add up to Everyone.
Came in is the two scanned groups together, so it deliberately overlaps them.
Cancelled, Came in and Never came are exclusive and add up to Everyone.
Add another stage above and the per-stage groups appear.
What happened at the door — attempts, not people; these do NOT belong to the groups above · and they report what the scanner decided at the time, not what the ticket is now
{{model.data.attempts.admittedAgain | number}}
Got in twice
same ticket let in more than once
{{model.data.attempts.preventedDuplicate | number}}
Turned away
same ticket refused the second time
{{model.data.attempts.unknownCode | number}}
Not our ticket
a code we do not know
whole event
{{model.data.attempts.admittedUnknown | number}}
Let in, not marked
the server failed to mark the ticket
whole event
{{model.data.attempts.cancelledAdmitted | number}}
Cancelled, let in
refunded ticket let in anyway
{{model.data.attempts.refusedWrongGate | number}}
Wrong gate
right event, wrong door
{{model.data.attempts.refusedCancelled | number}}
Refunded, refused
refunded ticket, correctly refused
{{model.data.attempts.refusedWrongSector | number}}
Wrong sector
refused by the sector limit
{{model.data.attempts.noEventRefused | number}}
No event named
the scan did not say which event
Click any card to compare it against the selected group in the chart below, per gate, sector or ticket type. The chart follows the same group and type as the cards, so its total for a card is the card's own figure.
Every refusal a handset can record now has a card here, so these account for the same total as the Refused column in the per-gate table below.
These cards are narrowed by the filter above; the Refused column in the per-gate table is not, so the two do not add up to the same total while a filter is on.
Per gate — the shifts at each gate, from the handsets' own record. This event only, and never narrowed by the cohort or type filter above: a shift is a period at a door, not a set of tickets
| Gate | Shifts | Scanners | Admitted | Refused | First shift | Last activity |
|---|---|---|---|---|---|---|
| {{g.gate}} | {{g.shifts | number}} | {{g.scanners | number}} | {{g.admitted | number}} | {{g.refused | number}} | {{g.firstOpenedAt ? (g.firstOpenedAt | date:'dd.MM HH:mm') : '—'}} | {{g.lastActivityAt ? (g.lastActivityAt | date:'dd.MM HH:mm') : '—'}} |
Times are {{tzLabel}}. any gate is a shift opened without a gate. A handset on a build older than 2026-08-04 keeps no shift and appears in no row.
ADMITTED counts every admission a
handset recorded, including the ones the server did not mark - a ticket already
used, or one cancelled since. Those are the got in twice and
cancelled, let in cards above. Scan Flow's per-gate figure counts only
admissions that marked a ticket, so it is the smaller of the two and omits a gate
where nothing was marked.
How they split — {{model.chart.labels.length || 0}} row(s) · compare by adding door events
{{d.label}}
{{t.label}}
Loading…
{{s.label}} · {{s.total | number}} {{s.kind === 'attempts' ? 'attempt(s)' : 'people'}}
the bar is the crowd; a door event is a count and a rate among the people who came in there - click a rate heading to put the worst gate on top
| {{model.dimension === 'TicketSector' ? 'Sector' : model.dimension === 'ScannedGate' ? 'Scanned at' : model.dimension === 'TicketType' ? 'Type' : 'Gate on ticket'}} | {{s.label}} ↕ | People — bar relative to the largest row |
|---|---|---|
| {{l}} | {{seriesVal(s, l) | number}} · {{seriesRate(s, l) | number:2}}% · no base | |
| Total | {{s.total | number}} | each column totals its own series |
{{u.label}}: {{u.total | number}}
cannot be split by gate, sector or type - these attempts belong to no ticket of ours, so there is nothing to attribute them to. Shown as a total on purpose rather than as zeros across every gate.
{{f.label}}
could not be computed, so it is left off the chart rather than drawn as zero. The rest of the comparison is unaffected. Try again, and if it persists the server log names the query.
Nothing in this selection.
Scanner activity — {{model.flow.bucketMinutes}}-minute buckets on one shared clock · {{model.flow.from | date:'dd.MM HH:mm'}} → {{model.flow.to | date:model.flow.stampFormat}} · busiest {{model.flow.allPeak | number}} in one bucket at {{model.flow.allPeakAt | date:model.flow.stampFormat}}
Everyone{{model.flow.allPeak | number}} peak
{{g.entrance}}{{g.total | number}} scans · peak {{g.peak | number}} · up to {{g.maxDevices}} scanner(s)
{{t.t | date:model.flow.stampFormat}}
Every bar is scaled to the busiest bucket of the busiest gate, so a tall bar means the same number everywhere.
A gap is a bucket in which that gate recorded nothing - which is a fact about the gate, not missing data.
The solid line marks the event's busiest bucket; the small marker on each row marks that gate's own.
A gate whose rate fell while its scanner count fell was understaffed, not congested, and the two are
indistinguishable in a scan count alone - hover any bar for both numbers.
While a group or type is picked, an admission whose ticket this event does not hold is not drawn here: it belongs to no group. The per-gate table above still counts it.
Presented again — {{repeatSummary().presentations | number}} presentation(s) of {{repeatSummary().tickets | number}} already-used ticket(s) · {{repeatSummary().letIn | number}} let in again · {{repeatSummary().turnedAway | number}} turned away
| Ticket | First presented | Presented again | Reached us after | What happened | |||||
|---|---|---|---|---|---|---|---|---|---|
| gate | scanner | time | gate | scanner | time | ||||
| {{r.ticketUnique}} | {{r.firstGate}} | {{r.firstDevice}} | {{r.firstScan | date:'dd.MM HH:mm:ss'}} | {{r.againGate}} | {{r.againDevice}} | {{r.againAt | date:'dd.MM HH:mm:ss'}} · {{r.reportedBy}}× | not known | {{r.delayMinutes}} min {{(r.delayMinutes / 60) | number:1}} h | {{r.outcome}} |
Times are when the ticket was presented, taken from the handset that scanned it, not when the server
learned of it. Those differ by hours for a scanner that was offline and drained later, and the last column is
exactly that gap. Two kinds of row appear, and What happened says which: a ticket let in again
because the scanner did not yet know it was used, and one turned away by a scanner that did. Each row is
one presentation, so a ticket offered three times is three rows.
Per operator — the person behind the handset, not the handset
| Operator | Scans | Scanners used | Gates worked | First | Last |
|---|---|---|---|---|---|
| {{o.operator}} | {{o.scannedCount | number}} | {{o.devices | number}} | {{o.entrances | number}} | {{o.firstScan | date:'dd.MM HH:mm'}} | {{o.lastScan | date:'dd.MM HH:mm'}} |
Times are {{tzLabel}}, by the handset's own clock. The operator is the one the shift was opened under, so a scan made on a shift opened before anyone signed in shows under (not recorded). That is a real state, not one person who scanned everything. A handset on a build older than 2026-08-04 keeps no shift and its scans appear in no row. While a group or type is picked, an admission whose ticket this event does not hold is left out: it belongs to no group.
{{model.forensics.open ? '▾' : '▸'}} What the scanners themselves recorded
How many people were let in and how many were turned away and why, who stood at which gate and for how long, and everything that happened on this event in order. Taken from the handsets' own record, not from the tickets.
Reading it…
This could not be read.
That is this page failing, not a statement about the event. Every other figure above is unaffected.
What the doors did
Nothing recorded for this event. Handsets on a build older than 2026-08-04 do not keep this record.
| What | Presentations | Tickets | Scanners | First | Last |
|---|---|---|---|---|---|
| {{events.activityLabel(a.kind)}} | {{a.activities | number}} | {{events.activityNamesTicket(a.kind) ? (a.tickets | number) : 'no ticket'}} | {{a.devices | number}} | {{a.firstAt | date:'dd.MM HH:mm'}} | {{a.lastAt | date:'dd.MM HH:mm'}} |
Times are {{tzLabel}}. {{events.activityTotal() | number}} presentations in total. A row counts PRESENTATIONS, so one visitor offering a used ticket three times is three of them, and Tickets beside it is how many different tickets that was. Scanners is the part worth reading: forty unknown codes from one handset is a door with a problem, forty from thirty handsets is an audience with one. Refusals appear only here, because a refused person never became a scanned ticket.
Shifts
No shift was recorded for this event. Handsets on a build older than 2026-08-04 do not keep one.
| Scanner | Operator | Gate | Sector limit | From | To | Admitted | Refused |
|---|---|---|---|---|---|---|---|
| {{s.deviceName || s.deviceGuid}} | {{s.operatorName || '(not recorded)'}} | {{s.entrance || 'any gate'}} | {{s.sectors || 'no limit'}} | {{s.openedAt | date:'dd.MM HH:mm'}} | {{s.closedAt ? (s.closedAt | date:'dd.MM HH:mm') : 'still open'}} | {{s.admitted | number}} | {{s.refused | number}} |
Times are {{tzLabel}}. A shift that admitted nobody is highlighted: it stood at a gate and did nothing, which is a finding rather than an empty row. Still open means the handset never closed that shift, usually because it was switched off rather than logged out.
What happened, in order
Nothing for this event apart from routine entries - the connection coming and going, the scan head being set up - which are left out here; the Device journal page shows them.
| Time | Scanner | What | Detail | Operator | Gate |
|---|---|---|---|---|---|
| {{it.first.occurredAt | date:'dd.MM HH:mm:ss'}} | {{it.first.deviceName || it.first.deviceGuid}} | {{it.first.kind}} | {{it.first.detail}}×{{it.count}} {{it.open ? '▾' : '▸'}} | {{it.first.operatorName || ''}} | {{it.first.entrance || ''}} |
| {{j.occurredAt | date:'dd.MM HH:mm:ss'}}+{{it.gapText[$index]}} | {{j.deviceName || j.deviceGuid}} | {{j.kind}} | {{j.detail}} | {{j.operatorName || ''}} | {{j.entrance || ''}} |
Newest first, as many as the server sends (its own default, currently 300 - JournalWindow.DefaultTop). Each line carries the time the DEVICE thought it was. A line with no operator against it is a boot or an event loaded before anyone signed in, which genuinely happened with nobody there. Routine entries - the connection coming and going, the scan head being set up - are left out before the window is counted; the Device journal page shows them. An entry repeated word for word by the same scanner is shown once, with how many times it came; click the row to see every one.