Spreadsheet Reports

A spreadsheet that refreshes

Finance does not want a dashboard. It wants a workbook — with its own columns, its own formulas, its own commentary — that is correct on the first working day of the month. Qrly ships a full spreadsheet engine bound directly to your database, where a refresh updates the data and leaves everything you built around it exactly where it was.

The workbook

A full workbook per report

  • Multiple sheets, default 200 rows × 26 columns
  • Column widths, row heights, frozen panes
  • Cells keyed A1 with value, formula and style
  • Every cell carries an owning-data-source marker
  • Stored as JSON in a side table
  • The report list stays light — workbooks are loaded on demand
The engine

Written, not wrapped

  • Own tokenizer, parser and evaluator, in the browser
  • Roughly 500 worksheet functions
  • Dynamic-array spilling and the full Excel error set
  • Cycle detection reported as #CIRC!
  • Excel number formats and date serials
  • Excel limits — column XFD, 1,048,576 rows
The binding

Four ways to fill a block

  • A saved question
  • Stored QQL, recompiled per run for the dialect
  • Ad-hoc SQL authored in Monaco
  • An AI prompt, resolved to SQL once
  • Per binding: sheet, anchor cell, headers, max rows, key columns
  • Refresh-safe: your columns and rows survive
01 / REFERENCES

Everything you would reach for by reflex

A1 references, ranges, whole-column references, cross-sheet references and named ranges — the vocabulary anyone who has used a spreadsheet already has. Workbook-aware functions such as ROW, INDIRECT and OFFSET behave as they should, which is what makes patterns copied from an existing model actually work rather than nearly work.

Evaluation is lazy where Excel is lazy: IF and IFERROR do not evaluate the branch they are not taking, so a guard around a division or a lookup does what a guard is supposed to do.

02 / CORRECTNESS

Cycles, errors and number formats

Circular references are detected and reported as #CIRC! rather than hanging the tab or silently producing a number. The complete Excel error set is implemented — #DIV/0!, #N/A, #NAME?, #NULL!, #NUM!, #REF!, #VALUE!, #CIRC! and #SPILL! — so errors propagate the way the person building the model expects them to.

Excel number formats and date serials are supported, which matters more than it sounds: a date that is secretly a string is the single most common reason a migrated model gives a different answer.

03 / FUNCTIONS

Roughly 500, with the awkward ones done properly

The function library covers the usual surface, and then explicitly overrides the ones where a naive implementation and Excel disagree: UNIQUE, SUBTOTAL and AGGREGATE, GROUPBY, TEXTBEFORE and TEXTAFTER, WRAPCOLS, PERCENTOF and the REGEX* family.

Those are the functions people notice. SUBTOTAL that ignores hidden rows, UNIQUE that spills correctly, TEXTAFTER that handles the delimiter-not-found case the way Excel does — get them wrong and every number downstream is wrong with them.

04 / STRUCTURE

Dynamic arrays and structural edits

Dynamic-array spilling is supported, with #SPILL! when the spill range is blocked — so modern array formulas behave rather than collapsing to their first cell.

Inserting and deleting rows and columns rewrites every dependent formula across the workbook. This is the least glamorous feature on the page and the one whose absence ruins a spreadsheet fastest: a grid where inserting a row breaks forty formulas is a grid nobody will maintain.

Grid capability
Behaviour
Why it matters
Virtualized rendering
Only visible cells are drawn
A large sheet scrolls instead of freezing the tab
Two-mode in-cell editing
Excel edit-vs-enter semantics, plus a formula bar as an alternate editor
Arrow keys move the cursor or the selection, as they should
Point mode
Arrows and clicks write references into a formula; Shift extends to a range; re-pointing replaces
Build a formula by pointing, never by typing coordinates
Autocomplete and reference outlining
Function autocomplete in the editor; live outlining of the ranges a formula touches
You can see what a formula reads before you commit it
Paste-special
All, values, formulas, formats — and transpose
Pasting values over formulas is a deliberate act, not an accident
Fill handle with series detection
Numbers, dates, weekday and month names, repeated text, reference-shifting formulas
Drag-fill guesses what you meant, the way it does in Excel
Structure
Frozen panes, merged cells, hidden rows and columns, insert, delete and drag-reorder with reference relocation
Reshaping a sheet does not break the formulas on it
Working with data in place
Sorting, filtering, find & replace, undo/redo, context menus, autofit, keyboard navigation
Nobody has to export to Excel to do the obvious thing
Formatting

Fonts, styling, borders, alignment

  • Undo, redo, paint format
  • Font family — Default, Sans, Serif, Mono — and size
  • Bold, italic, underline, strikethrough
  • Text colour and fill colour
  • Borders: all, outer, top, bottom, left, right, none
  • Horizontal and vertical alignment, wrap text, indent
  • Merge cells, merge across rows, unmerge
Number formats

Currency, percent, dates, presets

  • Currency, percent, increase and decrease decimals
  • General, #,##0, #,##0.00, red negatives
  • 0.00%, scientific, fractions
  • $#,##0.00 and €#,##0.00
  • yyyy-mm-dd, d mmm yyyy, yyyy-mm-dd hh:mm, hh:mm:ss
  • Plain text, and clear formatting
Insert & data

Structure, sorting, validation

  • Row above or below, column left or right, delete rows and columns
  • +100 rows, +10 columns, hide, unhide, fit columns to content
  • Sort A→Z and Z→A, create and remove filter
  • Freeze first row, first column, both, up to this cell, unfreeze
  • Data validation and named ranges
  • AutoSum (Alt+=): SUM, AVERAGE, COUNT, COUNTA, MAX, MIN — plus add a totals row or an averages row
01 / PRESETS

Seventeen one-click rules

Duplicates, unique values, top ten, bottom ten, above and below average, negatives in red, errors, blanks, dates today, dates in the last seven days, dates this month, a colour scale, data bars, and arrow, traffic-light and rating icon sets.

These exist because they are the rules people actually apply, and because making someone build "highlight duplicates" out of a custom formula is a small, daily insult.

02 / RULE TYPES

Cell, colour scale, data bar, icon set

The full dialog offers four rule types. Colour scales can be two- or three-colour; data bars take a bar colour; icon sets can be driven by percent, percentile or absolute number, with reverse order and custom thresholds.

Cell rules cover comparisons, between, text tests, duplicate, unique, top or bottom N or per cent, above and below average, empty, error, and an extensive date-period set.

03 / CUSTOM FORMULA

The escape hatch

Any rule can be a custom formula, evaluated by the same engine that evaluates the sheet. If you can express the condition as a formula, you can format on it — including conditions that read other columns, other sheets or named ranges.

That is what keeps the preset list from being a ceiling.

04 / STYLING

What a matching cell looks like

Matching cells can take a text colour, a fill, bold, italic, underline and strikethrough — the same styling vocabulary as manual formatting, so a conditionally formatted sheet and a hand-formatted one look like they belong to the same document.

Which matters when the workbook leaves the building as an XLSX attachment on a board pack.

Binding kind
What is stored
Use it when
QUESTION
A reference to a saved question
The number already exists and is governed elsewhere — edit it once, every workbook follows
VISUAL
Stored QQL, recompiled on every run
The block should follow the connection's dialect rather than being pinned to one engine's SQL
SQL
Ad-hoc raw SQL, authored in Monaco with schema completion
You want exactly this query, written by hand, and nothing between you and it
AI
A natural-language prompt, resolved to SQL once, then refreshed as plain SQL
Fastest way to a first block — with no token spend and no drift on later refreshes
Per binding Sheet Anchor cell Include headers Max rows Key columns

Why offset-based refresh destroys real work

A naive refresh writes the new result into the same rectangle, cell by cell, by position. That is fine on day one, when the block is all there is. It stops being fine the moment someone inserts a margin column beside it, adds a commentary row, or writes a note next to a specific customer — because on the next refresh the query returns its columns in a different order, or one fewer, or the rows come back sorted differently, and everything that was carefully placed is now beside the wrong record.

The predictable result is that people stop refreshing. They copy the block into a new tab, do their work there, and the "live" report becomes a manual one within two months. Qrly addresses this by addressing source columns by name, not by offset.

  1. Your columns are preserved and never written. Columns and rows you inserted inside a block stay where they are. Qrly writes the source columns it owns, and steps over everything else.
  2. A dropped source column is cleared, not shifted. When a column disappears from the query, its cells are cleared in place. Nothing slides left, so no formula that pointed at the column to its right suddenly points at something else.
  3. New source columns append past the block. A query that gains a column extends the block outward rather than pushing your own columns aside.
  4. Key columns put each record back on its row. With key columns configured, a record returns to the row it occupied last time, whatever order the query produced. Your hand-written note stays beside its customer across reorders — which is the whole reason people put notes in spreadsheets rather than in the database.
  5. Stale source cells are removed; your cells are never touched. Cells carry an owning-data-source marker. Cells Qrly wrote and no longer owns are cleaned up. Untagged cells — everything you typed — are left alone, permanently.
  6. The refresh reports what it did. Rows and columns written, columns and rows kept, columns dropped and rows skipped. You do not have to diff the sheet to find out whether the refresh did something you did not expect.
01 / ASSISTANT

A whitelist, not a free hand

The spreadsheet assistant works one-shot or as a multi-turn chat, and it can only propose operations from a fixed whitelist: setCells, setRange, addSheet, format and clearRange. It cannot invent an operation, and it is capped at 400 context cells and 200 operations per reply.

The cap is not only about cost. An assistant permitted to rewrite an unbounded region of a financial model is a category of accident you do not want available.

02 / PREVIEW

Nothing applies until you say so

What comes back is a preview draft, with an explanation of what it intends to do and a count of the cells it would affect. You apply it or you discard it.

Preview-before-apply is the same trust boundary Qrly uses for schema authoring: the model proposes a structured change, a human reads it, and only then does anything happen to the document.

03 / EXPORT

XLSX with real formulas — plus CSV, JSON, XML

XLSX export carries all sheets with real formulas and styles mapped to Excel, so the recipient gets a working model rather than a picture of one. CSV exports a single sheet as evaluated values, and JSON and XML are available for anything downstream that wants structure.

Formulas surviving the export is what makes the workbook forwardable. An accountant who receives values-only has received a screenshot with extra steps.

04 / ONE RENDERER

Same bytes, emailed or downloaded

Exports are rendered server-side. The file a scheduled subscription emails to a distribution list is byte-identical to the file a read-only viewer downloads from the report page.

That removes an entire class of argument — the one that starts with two people comparing the version they each received and discovering the numbers do not agree.

Is this a real formula engine or a thin wrapper around a library?

It is Qrly's own tokenizer, parser and evaluator, running in the browser. It handles A1 references, ranges, whole columns, cross-sheet references and named ranges; lazy IF and IFERROR; cycle detection reported as #CIRC!; Excel number formats and date serials; workbook-aware functions such as ROW, INDIRECT and OFFSET; dynamic-array spilling; and the full Excel error set.

Roughly 500 worksheet functions are implemented, with Excel-correct overrides for UNIQUE, SUBTOTAL and AGGREGATE, GROUPBY, TEXTBEFORE and TEXTAFTER, WRAPCOLS, PERCENTOF and the REGEX family, within Excel's own limits of column XFD and 1,048,576 rows.

What happens to my own columns when the data refreshes?

They survive. Data blocks address source columns by name rather than by offset, so columns and rows you inserted inside a block are preserved and never written over. A source column that disappears is cleared in place rather than shifting everything left, new source columns append past the block, and key columns return each record to the row it was on before.

Stale cells that Qrly itself wrote are removed; untagged cells you wrote are never touched. Each refresh reports rows and columns written, columns and rows kept, columns dropped and rows skipped.

Where does the data in a spreadsheet report come from?

From one of four binding kinds, per block: QUESTION binds a saved question; VISUAL stores QQL that is recompiled on every run so it follows the connection's dialect; SQL is ad-hoc raw SQL authored in Monaco with schema completion; and AI takes a natural-language prompt, resolves it to SQL once, and refreshes as plain SQL thereafter.

Each binding sets its sheet, anchor cell, whether headers are included, a max row count and its key columns.

Does an AI-backed block spend tokens every time it refreshes?

No. An AI binding resolves the natural-language prompt to SQL once. From then on the block refreshes as plain SQL, so scheduled refreshes and subscriptions never spend tokens — and never risk the model returning a different query than it did last month.

Can I export to Excel with the formulas intact?

Yes. XLSX export carries all sheets with real formulas and styles mapped to Excel. CSV exports a single sheet with evaluated values, and JSON and XML are also available.

All formats are rendered server-side, so the bytes a subscription emails out are the same bytes a read-only viewer downloads.

Does the AI assistant edit my sheet directly?

Not without you seeing it first. The assistant works one-shot or as a multi-turn chat and can only propose operations from a whitelist — setCells, setRange, addSheet, format and clearRange — capped at 400 context cells and 200 operations per reply.

What comes back is a preview draft with an explanation and an affected-cell count, which you apply or discard.

Stop rebuilding the same workbook every month

A real formula engine, bound to your database, where a refresh updates the data and leaves your work alone.