Pular para o conteúdo

Scheduled delivery

A schedule delivers saved reports and datasets by email on a recurrence. It is the difference between reporting someone has to remember to run and reporting that simply arrives.

The Scheduler is not a feature of reports — it is their sibling. A schedule carries reports, datasets, or both, so a bare dataset can be mailed as a spreadsheet with no report existing anywhere. It lives under Logging & Reports → Reporting → Scheduler, and nothing ships pre-scheduled: a fresh install starts with an empty list.

The scheduler The Scheduler lists every delivery with its recurrence, recipient count, next run and status, plus per-row Send now, Edit and Delete. Searchable by name, sortable by name and next run.

Name — unique across schedules, up to 128 characters.

Deliverables — the reports and datasets to send; at least one is required. A schedule can carry several of each, so one Monday-morning email holds the whole operations pack rather than four separate messages. Reports are always delivered as PDF — no choice is offered. For each dataset you pick the format from what its template allows: a raw-table dataset offers CSV, Excel or PDF, a preset offers PDF only. The picker shows each dataset’s formats before you add it, and the per-row select is limited to them afterwards.

Recurrencedaily, weekly (pick the weekday) or monthly (pick the day of the month), each with a time of day in 24-hour HH:MM. A new schedule opens as daily at 09:00. In a month shorter than the chosen day the monthly delivery moves to the last day rather than skipping the month — the 31st fires on 28 or 29 February.

Active window — a start and an optional end. Nothing fires before the start, and a start set in the future delays the first delivery to the window opening instead of firing straight away. Leave the end empty and the schedule never expires.

Recipients — user groups, literal email addresses, or both; at least one of the two.

Enabled — a disabled schedule keeps all its settings and never fires. Use it to pause a delivery without rebuilding it.

Recipients are resolved at send time, not at save time. Each recipient group is expanded to its members’ email addresses, unioned with the addresses typed into the form, and then deduplicated by address, case-insensitively: two people in two different groups who share a mailbox get one copy, not two. A member with no address, or with one that does not parse, is skipped — group membership is not a promise of a working mailbox.

Because the expansion happens per delivery, adding someone to a recipient group puts them on the next email without anyone touching the schedule.

Each recipient gets their own message, addressed individually. Nobody is Cc’d, and the recipient list is not visible in the mail.

This is the part worth understanding, because it is what makes a schedule set-and-forget.

A schedule does not freeze the period. It stores nothing about the data and no date range of its own; it triggers exactly the same generation path as the export button, and the window is resolved at the moment each delivery is produced. The relative period lives on the dataset, which is why it never has to be rolled forward.

So a dataset scoped to “last 7 days” means the last seven days of every delivery. Configure it once in July and the January email is still about the week that just ended. Nothing decays, and nobody has to go back and roll the dates forward.

The corollary: if you need a fixed window — a specific audit quarter that must not move — set an absolute global period on the report itself, and schedule that.

One background job — Report Delivery Scheduler, listed under Settings → Workers — wakes about once a minute, asks the database which schedules are due, delivers them, and advances each to its next run. There are no per-schedule timers anywhere: the database is the source of truth, so schedules survive a restart, an upgrade and an edit without anything needing to be re-registered. Disabling that one job on the Workers page stops every delivery at once.

Missed occurrences do not stack. If the platform is down for three days, a daily schedule is simply overdue when it comes back: it delivers once, then advances to the next occurrence from the current time. The same applies to a schedule left disabled for a month — re-enabling it does not re-plan the next run, so it fires on the following tick and settles back into its recurrence from there.

Times are wall-clock times in the system timezone — the zone Taranac reads from the host at start-up, not a per-schedule or per-user setting. Because it is a wall-clock time, 09:00 stays 09:00 across a daylight-saving change: the UTC offset moves, the delivery does not. In a high-availability deployment the zone is one cluster-wide value and only the leader runs the tick, so a report is generated and sent once, not once per node.

Everything a delivery produces is packed into as few emails as possible, under a 20 MiB per-message ceiling measured the way a mail server sees it — base64-encoded on the wire, not raw bytes. Past that the delivery splits into several emails, and the subject picks up a (part 1 of 2) marker so the pack is still readable as one thing.

A single attachment larger than the ceiling cannot be split, so it gets an email to itself and is sent anyway. That is best-effort: your relay may still refuse it. If one report regularly lands near the limit, narrow its period or split it across two schedules rather than relying on that.

Send now delivers a schedule immediately without waiting for its next run, and without disturbing the recurrence — the next scheduled run is left exactly where it was. It is the right way to check that recipients and formats are correct before leaving a schedule to run on its own, and it is audited under your name rather than the system’s.

The result is reported back plainly: how many recipients it went to, and — if anything could not be produced — how many items were skipped and why. A delivery that partly fails does not fail silently.

Nothing here retries a delivery. Each occurrence stands alone, and the schedule advances to its next run whether it succeeded or not — a failure never turns into a delivery that re-fires every minute, and it is never queued for later. What varies is how far the attempt gets before it stops:

  • No usable recipient. Every group was empty or every address unparseable: the delivery is recorded as failed and nothing is rendered.
  • Email is not configured. With SMTP disabled the delivery stops before any report is generated, and is recorded as failed. There is no outbox.
  • A deliverable was deleted. A schedule references its reports and datasets by id and holds no hard link to them, so a target deleted after the schedule was saved is skipped with a reason while the rest of the pack still goes out. Saving is stricter than delivering: you cannot create or edit a schedule that points at something missing, or ask for a format the dataset’s template does not offer.
  • The PDF renderer is unavailable. PDFs are printed by the taranac-renderer container. If it is merely busy the item is retried twice, one and two seconds apart; if it is down or unreachable the item is skipped immediately. CSV and Excel never touch the renderer, so a mixed schedule still mails its spreadsheets — but a PDF-only schedule sends nothing at all.
  • The mail server rejects a message. Sending is per address, so a single bad mailbox does not stop the others. If at least one message got out the delivery counts as delivered; if none did, it is recorded as failed.

Anything skipped is carried in the delivery’s audit event, so a half-empty pack is explainable after the fact rather than a mystery.

Every change is audited — REPORT_SCHEDULE_CREATED, REPORT_SCHEDULE_UPDATED, REPORT_SCHEDULE_DELETED — and so is every delivery attempt: REPORT_DELIVERED when something went out, carrying the recipients, the attachment filenames, how many emails it took and anything skipped; REPORT_DELIVERY_FAILED when nothing did, carrying the reason. Both record what triggered them, so a scheduled run and a Send now are distinguishable afterwards. The mailer adds its own EMAIL_SENT / EMAIL_FAILED per message.

There is deliberately no separate delivery-history table: the Activity Log is the history. “Who set this up and when did it last go out” is answerable after the fact rather than a matter of memory.

Managing schedules needs reports.schedule, and that permission gates the whole surface — every schedule endpoint and the sidebar entry. An account with reports.view sees Reports and Datasets and never learns that the Scheduler exists, let alone that its data is being mailed out nightly.

reports.schedule neither implies nor is implied by reports.export; the two are independent on purpose. Treat them the same way, because they are the two doors through which data leaves the product.

If the API rejects a dataset deliverable, it says the item must be “a table dataset exportable as CSV or XLSX”. That message is out of date. PDF is a permitted delivery format, for raw-table datasets and presets alike, and the code accepts it — the UI even offers it. Read the message as “this dataset’s template does not offer the format you asked for” and check the format select, not the letter of the text.

Weekly operations digest. One schedule, weekly on Monday at 08:00, carrying one report with a global relative period of 7 days.

Monthly compliance pack. Monthly on the 1st, carrying the review report. If the pack must cover an exact month rather than a rolling 30 days, give the report an absolute period and update it each cycle — or accept the rolling window and say so on the cover.

A spreadsheet for someone who lives in Excel. A dataset delivered as XLSX, daily, with no report attached at all. Schedules are not report-only.

A delivery that stops on its own. A migration window you want watched for six weeks: set an end date on the active window and the schedule expires by itself instead of quietly mailing a stale pack forever.

  • Reports — the documents a schedule delivers
  • Exports — the same formats, produced by hand
  • Datasets — why the saved period is relative