StabisoDocs

Accounting and finance

Year-end close

A fiscal year is closed by one entry of source close per entity and year: every profit and loss account with a balance is brought to zero against the account with the role annual_result (2979 Jahresgewinn oder Jahresverlust in the Swiss template, 3199 in the generic one). The books then continue into the next year with the balance sheet as it was. What the general meeting decides about the result is a second, later entry (source appropriation). The design is ADR-0023; the controls are in the control matrix (section "Year-end close", YC1 to YC9).

Everything below is a REST operation first (/v1, tag "year-end close" in the generated reference); the web UI (Finance → Period close → Year-end) and the MCP read tools call the same operations. The screen reads the year close (GET .../fiscal-years/{year}/close) only for a year whose fiscal year lists a close: a year never proposed is an empty state, not a 404 on every load.

What closing does

Before After
Profit and loss accounts (3xxx to 8xxx) the year's flows zero
annual_result (2979) nothing the result of the year (credit for a profit, debit for a loss)
Balance sheet totals assets = liabilities + equity + unclosed earnings the same totals; the unclosed earnings are now in 2979
Reports profit and loss, period profit and loss and the analytics leave out source close, so the year's income statement is what it was before the close; the trial balance and the general ledger show the closing lines (closing: true on a ledger line), and closing=exclude / include_close=false give the pre-closing view

Nothing is posted at the start of the new year. The opening balances of a year are the closing balance sheet of the year before, account by account (GET /v1/entities/{entity}/fiscal-years/{year}/opening-balances).

The entry is dated the last day of the year and posted in the year's last adjustment period when it has any, else in its last regular period; that period must be open. An entity needs an account with the role annual_result (both chart templates have one; otherwise 409 role_missing says how: creating the account with the role, POST /v1/entities/{entity}/accounts, takes one person; giving the role to an existing account, PATCH, is a change of a required role and needs a second person. The preview shows the chart template's code in result_account_suggestion: 2979 or 3199).

The proposal, the lock and the approval

  1. GET .../fiscal-years/{year}/close-preview shows what would be posted and what stands in the way (blockers): a draft journal entry, an entry or a document waiting for approval dated in the year, a last period that is not open, a missing annual-result account, a year that is already held, and a period-end run of the year's last regular period that is due and not done (depreciation, FX revaluation, deferred tax: the message names the run and its operation; the closing entry locks the year, so a run left for later could only be done after a reopening). It stores nothing, so an agent may call it.
  2. POST .../fiscal-years/{year}/close (a person; the policy action close_fiscal_year denies agents) refuses with 409 year_close_blocked while anything stands in the way and otherwise writes the entry and a fiscal_year_close row pending. From that moment the year is locked: no other entry is posted into the fiscal year, whatever its source, until the close is approved, cancelled or reopened. The application says why (409, "fiscal year 2026 of CH01 is being closed"); a database trigger refuses it even if the application is bypassed.
  3. By default a second person approves the entry (POST /v1/approvals/{approval_id}/approve; rule close_entries_need_a_second_person, which a one-person organization turns into allow for the sources close and appropriation in its approval policy). At approval the account totals are computed again and compared with a digest kept at the proposal: a difference is 409 year_changed and nothing changes, so a closing entry never posts over figures it was not made from. Proposals are serialised on the entity: a second one, simultaneous or later, gets 409 year_close_blocked ("already has a year close"), never a database error. A proposal locks the entity's chain sequence as every posting does, so a posting in flight finishes first and the totals read next include it, and it reads the totals once: the closing lines and the digest come from the same read. While a proposal waits for approval the close checklist fails the check year_close on the period the entry is posted in, so that period is not closed under it.
  4. POST .../close/cancel (a person, with a reason) withdraws a pending proposal: its approval and its entry are rejected and the lock is lifted. A rejected approval does the same. A closed year is not cancelled but reopened.

Reopening

POST .../fiscal-years/{year}/reopen-close (a person with the approve scope, a reason) is the decision to reopen. It reopens the period that holds the closing entry, reverses the closing entry itself (same date and period), and sets the close reopened. The reversal needs no second approval: the decision covers it, like the decision on an accrual covers its automatic reversal. Afterwards periods reopen as usual and the year can be closed again (a new close; both are kept).

  • A period of a closed year is reopened only this way (POST /v1/periods/{id}/reopen answers 409 while the close stands).
  • A year whose result is appropriated, or has an appropriation waiting for approval, is reopened after the appropriations are reversed.
  • The closing entry is reversed by this operation and by no other: the generic reversal of a journal entry does not accept a closing or appropriation entry (tests/architecture pins who may call the reversal engine).

Appropriating the result

POST /v1/entities/{entity}/appropriations books the shareholders' resolution as one entry dated the day of the resolution (a dividend is a liability only from then, IAS 10.12-13): for a profit Dr annual_result / Cr the destinations, for a loss the mirror image. Destinations: retained_earnings (the account defaults to the one with that role), legal_profit_reserve, voluntary_reserve (equity accounts you name), dividend_payable (a liability account you name, for a profit only; a loss is carried forward or covered from reserves) and group_dividend (a dividend to a shareholder that is another entity of the organization: the line goes to ic_other_payable, and the posting drafts the shareholder's entry, intercompany; such an appropriation is not reversed). The year must be closed. A second person approves it by default. The resolution date must fall in a fiscal year that no close holds: a date in a year that is closed or being closed (the next year may already be closed), or before a catch-up that stands, is refused at the request by name, so nobody is asked to approve what the lock would refuse; reopen that year first or date the resolution in an open one.

  • The remainder is tracked. fiscal_year_close.appropriated is kept by a database trigger on the append-only appropriation table, which also refuses an appropriation beyond what is left; a partial resolution leaves the rest visible (remainder). The application refuses it first, by name (409 appropriation_exceeds_remainder, counting resolutions that wait for approval). For a loss the remainder is negative (what is left to cover), and the destinations' amounts are positive: the amounts that cover it. The screen says so: "Loss still to cover: -2,000.00 CHF", and "Covered" for the total typed, where a profit has its "remainder".
  • A resolution is withdrawn by a reversal of its own (POST .../appropriations/{id}/reverse): the opposite entry through the same approval, a row with the opposite sign.
  • Swiss entities get warnings and withholding_tax: 5 % of the annual profit, after a loss carried forward (a debit balance of retained earnings) is eliminated, must go to the legal profit reserve (Art. 672 para. 1 OR; the system does not know the share capital, so it only warns), and a dividend bears 35 % withholding tax (Verrechnungssteuer, below). No other jurisdiction has a rule until one is cited.

Withholding tax on a Swiss dividend

A Swiss company deducts 35 % from the dividend it pays (Art. 4 para. 1 lit. b, Art. 13 para. 1 lit. a, Art. 14 para. 1 VStG) and pays it to the Federal Tax Administration within 30 days of the claim arising, which is when the dividend falls due (Art. 12 para. 1, Art. 16 para. 1 lit. c VStG). A destination of kind dividend_payable or group_dividend says how with withholding_tax:

withholding_tax Entry at the resolution date When
deduct Dr annual result (gross) / Cr the destination (65 %) / Cr withholding_tax_payable (35 %) the normal case
notification Dr annual result / Cr the destination (gross), nothing withheld a group dividend to a Swiss shareholder that may report instead of paying (Art. 26a VStV); refused otherwise
not set as before: the gross dividend, and a warning that names the 35 % and the 30 days to book the tax by hand

The answer carries withholding_tax (the amount) and withholding_tax_due (the resolution date plus 30 days; the system assumes the dividend falls due on the day of the resolution, and a later due date in the resolution moves the deadline with it). The tax is paid like any other liability: the bank line of the payment is posted to the account with the role withholding_tax_payable (POST /v1/bank-lines/{id}/post). The close check withholding_tax_due (order 180, a warning) names the amount still owed past its due date at the period's end. The notification procedure needs a participation of at least 10 % and the notification form; the system does not know the participation, so it states the condition in a warning and does not check it. A group shareholder's side holds the net dividend as a receivable and the 35 % as a claim on the tax administration (withholding_tax_receivable, recovered by the shareholder's refund application, Art. 29 VStG); see intercompany. The roles are optional: ch_kmu 2206 and 1176 Verrechnungssteuer, the generic chart 2230 and 1410 (409 role_missing names them for an existing entity). Foreign recipients and treaty refunds are not built.

  • The general meeting is due six months after the year end (Art. 699 para. 2 OR). A Swiss entity's closed year with a remainder after that day is appropriation_overdue; the close checklist warns in every later period (appropriation_pending), GET .../fiscal-years?unappropriated=true lists the years, and the Role Center shows the count as a cue that opens Period close.

Catching up

An entity that has been posting for years has profit and loss accounts that accumulate over all of them. POST /v1/entities/{entity}/year-close/catch-up with first_clean_year posts one entry (source close) dated the first day of that year, in its first period (which must be open: 409 catch_up_period_not_open names it): the balance of every profit and loss account of everything before that date is brought to zero against retained_earnings, the earlier results being treated as carried forward (the system does not know the resolutions of those years). The most recent year before may still be waiting for its general meeting: leave_latest_year_unappropriated (default: true when that year ended less than six months before) puts its result on annual_result instead, with a catch_up close of its own, so appropriation tracking and the six-month warning apply to it. The same lock, digest, approval and cancellation apply, and it is refused (409 year_close_blocked) while any earlier period is open. While it stands, waiting or posted, no entry is dated before its date and no period ending before it is reopened. The years carried forward share one close, filed under the latest of them; their periods are normally closed already, and the lock holds that year.

Permissions

Operation Needs Policy action
preview, get, opening balances, list read none
propose, cancel, catch-up, appropriate, reverse an appropriation draft (a person) close_fiscal_year, denied to agents
approve the entry approve, another person than the proposer approve
reopen approve (a person) reopen_period, denied to agents

MCP tools (read only): year_close_preview, year_close_status, opening_balances, list_appropriations, unappropriated_years.

Not built (yet)

  • A dividend to a group shareholder (an intercompany receivable) waits for intercompany (ADR-0025).
  • The withholding tax of a foreign shareholder (a treaty rate, a refund) and the notification procedure's Form 106 itself.
  • Interim dividends and the Bilanzgewinn presentation of § 268 HGB.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 6f03a5e846b3