StabisoDocs

Autopilot and routines

Recurring journals

A journal entry that repeats (the office rent accrued at every month end, a monthly cost allocation, the release of an insurance premium paid a year ahead) is written down once as a recurring journal: its description, its lines, how often, from when until when, and whether each entry is an accrual that reverses on the first day of the next period. A person activates it. After that a person posts the due journals on demand, and each run makes the entry of every due date that has come, as an ordinary journal entry through the approval policy. The design is ADR-0071; the controls are in the control matrix (section "Recurring journals", RJ1 to RJ7).

Everything below is a REST operation first (/v1, tag "recurring journals" in the generated reference); the web UI (General ledger → Recurring Journals) and the MCP read tools (list_recurring_journals, get_recurring_journal, recurring_journal_runs) call the same operations.

Runs are on demand. Nothing runs by itself. The Role Center cue Recurring journals due counts the recurring journals with a due date on or before today and opens that list; a person presses Post due journals (or calls POST /v1/recurring-journals/run). The close checklist warns when one is due through the period being closed (below).

The recurring journal

Field Meaning
entity The company whose books the entries are posted to
name What the recurring journal is called in lists; with the due date it is the entry's source_ref ("Office rent accrual 2026-09-30")
description The description of every entry
lines At least two: account, debit or credit (one side per line), currency (none: the company's functional currency; a foreign currency is converted at the entry's date as on any entry), description and dimensions (the line's values of cost centres, profit centres and projects, passed to the entry unchanged; checked when the recurring journal is saved as on a manual entry: active values usable on the next due date, and every dimension the account requires, else dimension_unknown, dimension_value_unknown or dimension_required). The lines must balance as an entry typed by hand must
frequency monthly, quarterly (every 3 months) or yearly
start_date The first due date, and the posting date of the first entry. It may lie in the past
end_date The last day an entry may fall due, inclusive; none for no end
reverse_next_period true: an accrual. Each entry is reversed on the first day of the period after the one it is posted in (auto_reverse_on of the entry, the end of its fiscal period plus one day)
next_due The next due date; the start date at first, then the date after each period made. None once it has ended
status draft, active, paused, ended
activated_by, activated_at Who activated it last, and when; none for a draft (also after a change returned it to draft)
entries_made How many due dates have an entry (a rejected entry does not count)

Creating a recurring journal builds its first entry without keeping it, so an unknown or blocked account, lines that do not balance or a closed period are refused at once (404, 409, 422), not at the first run. A change and an activation build the next entry the same way.

The due dates

The due dates are those of recurring invoices (the same functions, domain/schedule): the start date and every 1, 3 or 12 months after it, counted from the start date and keeping its day of the month, clamped to the last day of a shorter month. A recurring journal for every month end therefore starts on the 31st (31 Jan, 28 Feb, 31 Mar, ...). The end date is inclusive; after the last due date the recurring journal ends by itself (ended, no next_due), and an end date moved before the next due date ends it at once.

Activation

Anyone who may draft (an agent too) creates and changes a recurring journal; it starts as a draft, which makes no entries. Activating it is a person's step: an agent is refused (403 recurring_journal_human_only). There is no second-person approval of the recurring journal itself, unlike a recurring invoice's terms, because every entry it makes goes through the approval policy for the person who posts it (below): where the policy asks a second person to approve a manual entry, that person sees each entry. Pause (with a reason) stops an active one; Resume is an activation again. Once an entry was made, the frequency and the start date cannot change (409 recurring_journal_schedule_fixed): end it and create a new one. The name, the description, the lines, the end date and the reversal can change at any time and apply from the next entry.

A change of what the entries book needs a new activation. A change of the lines, the start date, the frequency, the end date or the reversal (reverse_next_period) returns an active or paused recurring journal to draft: its activation (activated_by, activated_at) is cleared, it makes no entries, and a person activates it again, whoever made the change. A person activated the amounts the entries book, not what someone changed them to since: without this, an agent could change an active rent accrual of 4,500.00 to 99,000.00 and the next run of a person whom the policy lets post would post it. The audit trail records recurring_journal.deactivated_by_change with the fields changed. A change of the name or the description keeps it active. The card shows the status the recurring journal has after the change.

Posting the due journals

POST /v1/recurring-journals/run with through (a day that has come for the company, usually today; 409 billing_through_future otherwise), and optionally entity or template (one recurring journal, which must be active). For every active recurring journal with a due date on or before through, in order of the due dates, the run makes the entry of each due date with journals.create_and_submit, the function an entry typed by hand in the General Journal goes through:

  • the entry is dated the due date, carries the description and lines, source_ref "name due date" and provenance.recurring_journal (template_id, due_date); it is the source manual;
  • the approval policy decides for the person (or agent) who runs it: the entry posts, or it waits for a second person (pending_approval), as an agent's entries do;
  • an accrual's entry has auto_reverse_on, and its reversal is posted when the entry posts (at once, or when the second person approves it).

A due date is made once. The recurring journal is locked and read again before it is worked, and the database refuses a second run row for one recurring journal and due date, so a repeated, retried or simultaneous run never makes a second entry. Idempotency-Key replays the answer of a repeated request. A run makes at most twelve due dates per recurring journal and says more_due when more are left.

A failure stops the recurring journal, not the run. A due date whose entry cannot be made (its period is closed, an account was blocked) is recorded as failed with its error, the recurring journal stays at that date, and the others carry on. The next run tries the failed date again. The card's Entries made lists every due date with its entry, its status, its reversal and any error.

A rejected entry is made again. Each entry names its run row as its document (provenance.document, type recurring_journal_run), so the decision on an entry that waited for a second person reaches the row: approved, the row becomes posted (and an accrual's reversal is posted); rejected (or withdrawn by a person), the row becomes rejected and the recurring journal's next due date moves back to that date. It is due again (the cue, the due list and the close check count it), and the next run makes the period again with the lines as they then stand, as it does a failed date. A recurring journal that had ended after that date is active again (a draft if no activation stands). If the amount was wrong, pause or change the recurring journal first: a change of the lines returns it to draft (above).

The answer lists what was made: made, failed, more_due and per due date the recurring journal, the status (posted, pending_approval, failed), the entry and its number. A run row that waited becomes posted or rejected once its entry is decided. GET /v1/recurring-journals/{recurring_journal_id}/runs lists the due dates made with the entry's number and status and its reversal.

The Role Center and the close

recurring_journals_due in GET /v1/entities/{entity}/overview is the cue on the Role Center; it opens /recurring-journals?due=1, the list GET /v1/recurring-journals?due_through=<today> returns, and the run posts exactly these. The close check recurring_journals_made warns (it never blocks) while a recurring journal is due through the end of the period being closed, and its fix opens the due list. It warns first (recurring_journals_rejected) while an entry made for a date in the period was rejected and has not been made again, whatever the recurring journal's status: a paused or changed one is not due, but the period still lacks its entry. It passes when the company has no active recurring journal or none is due through the period. The scheduled close job does not post recurring journals: a person does.

Audit, the database and the export

Every step is an audit event on the object type recurring_journal: recurring_journal.create, .update (the fields changed, whether it ended), .deactivated_by_change (a change returned it to draft), .activate (whether it resumed), .pause (the reason), .run (per due date: the status, the entry or the error), .rejected (a rejected entry moved it back to its date) and .end. The tables recurring_journal and recurring_journal_run (migration 0099) have forced row-level security; a recurring journal is never deleted and keeps its identity, an active or paused one names its activation and a draft none, a run row waiting for approval only follows the decision on its entry, a posted one never changes (a failed or rejected one is replaced by its retry), and neither table can be truncated. Both are in the organization export (group "ledger") and in test copies.

The General Journal: reverse on a date

Independent of recurring journals, the General Journal page has Reverse automatically (accrual). Ticked, it shows Reverse on, the first of the month after the posting date (it moves with the posting date until you change it), and the entry is posted with auto_reverse_on: its reversal is posted on that day. The date must be after the posting date.

Demo data

Demo AG (CH01) has two recurring journals, active and due from 30 September: Office rent accrual (monthly, 4,500.00 CHF, 6000 to 2300, reversed on the first of the next month) and Insurance prepayment release (quarterly, 1,200.00 EUR, 6500 to 1300, not reversed).

Stabiso, the Enterprise System of Record · generated from the code and its guides · release 6f03a5e846b3