Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

periods package

Overview

Package periods turns a cumulative double-entry book into a period report: per-account movement over a date window, the period income statement, and the closing entry that sweeps nominal accounts into equity.

p/journal is cumulative by construction and says so. Its Close is a backdating seal — it refuses entries dated at or before a cutoff and does not sweep anything — so revenue and expense accumulate from the book's first posting forever, and p/statements names its three figures CumulativeRevenue, CumulativeExpenses and CumulativeNet for that reason. Its package doc states the gap in one sentence: "A period income statement needs entry-level data and is out of scope." This package is that entry-level consumer. It reads no chain, holds no state, and writes nothing.

Example
1w := Window{From: q1Start, Until: q2Start}
2rep, err := Derive(chart, entries, w)
3// rep.Activity, rep.PeriodNet, rep.CrossWindowReversals
4post, err := rep.ClosingEntry("3100")   // a PROPOSAL, not a write

Why the chart is a parameter

journal.Posting is {Account string, Side, Amount int64}: it carries the account's CODE and never its Kind. So revenue cannot be told from expense from postings alone, and no income statement is derivable without the chart. That single fact fixes this package's signature. Only Code, Name and Kind are read; Debits and Credits are the book's CUMULATIVE columns and are deliberately neither read nor validated here, because period movement comes from the entries.

The window is closed-open

Window is [From, Until): From is included, Until is not. Consecutive periods therefore TILE — Window.Tiles is the predicate — with no overlap and no gap, which is what makes a sequence of period reports add up to the cumulative figures. A closed-closed window double-counts exactly one timestamp per boundary, and that error is invisible in any single report.

Two inputs, two ordering decisions, opposite answers

This package imposes an ordering precondition on one input and refuses one on the other, and the rule separating them is what journal GUARANTEES:

  • chart must be strictly ascending by Code (ErrChartNotOrdered). journal.Book.IterateAccounts is "ascending code order", so every producer meets it. It buys duplicate-code detection, chart-order output with no sort, and a binary-search code lookup instead of a map.
  • entries must be strictly ascending by ID (ErrEntriesNotOrdered). journal.Book.IterateEntries and journal.Book.Entries both guarantee it. It buys duplicate-ID detection, the binary search that resolves ReversalOf, and ID >= 1 for free, 0 being ReversalOf's sentinel.
  • entries must NOT be required in ascending At order, and this package must never require it. At is caller-supplied and backdating is legal until Close seals it, so journal guarantees nothing about it. Such a precondition would be one no producer in this portfolio can meet — a gate that can never pass, in an API signature rather than in a checker.

The reversal census has three buckets, and the third is the point

A reversal dated inside the window whose original is dated outside it changes what the period means: the window carries a correction to activity it never contained. The census reports three things and never two:

  • [Report.Reversals] — in-window reversal, original also in-window: ordinary, classified.
  • [Report.CrossWindowReversals] — in-window reversal, original dated outside: classified, and material.
  • [Report.UnresolvedReversals] — in-window reversal whose original is ABSENT from the entries slice, so the classification COULD NOT BE MADE.

Collapsing the third into either of the first two would read "could not evaluate" as assent. It is a census, not a verdict — the same posture as statements.Statement.Abnormal — and it is a function of what the caller passed, not of the book: pass a window-filtered slice and every reversal reaching back before it is unresolved by construction.

What this package does not do

  • **It never writes.** Report.ClosingEntry returns postings for the caller to Post. It sweeps PERIOD MOVEMENT, not cumulative balance, so running it twice over one window and posting both results double-sweeps — and this package never sees the Book, so it cannot detect that. The caller owns idempotence.
  • **No paging, no Merge.** MaxEntries is the hard bound. Merging reports across pages would make UnresolvedReversals an artifact of the paging rather than of the data; fixing that needs a two-pass design over the whole entry set and is a different package.
  • **No cash-flow statement.** The indirect method needs a cash-vs-accrual classification journal.Posting does not carry. [Report.Activity] is the balance-sheet delta half it would need.
  • **No audit.** Every figure is derived from what the caller supplied. A chart from one book and entries from another produce a plausible report and nothing here detects it.

p/journal: /p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/journal p/statements: /p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/statements

Functions

Derive

func Derive(chart []journal.Account, entries []journal.Entry, w Window) (Report, error)
Ask AI Claude ChatGPT

Derive computes the period report.

It is a pure function of its three arguments: the same inputs always give the same Report, and nothing about the caller, the chain or the time enters it. On any error the returned Report is the zero value, so a caller that ignores the error reads zeros rather than a half-summed period.

Preconditions, each for a reason given in the package doc: w must be valid; chart must be strictly ascending by Code and no longer than MaxAccounts, with every Kind one of journal's five; entries must be strictly ascending by ID and no longer than MaxEntries, with at most journal.MaxPostingsPerEntry postings each.

Entries outside w are skipped, not refused — a caller passing the whole book is the expected case — and their postings are therefore NOT content-checked, because no figure in the report depends on them. ErrInvalidPosting and ErrUnknownAccount are scoped to the window for that reason; the postings-per-entry cap is not, since it is what makes MaxEntries' cost ceiling a bound rather than a hope.

Params

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/periods.Derive(,,)"

Result

MustDerive

func MustDerive(chart []journal.Account, entries []journal.Entry, w Window) Report
Ask AI Claude ChatGPT

MustDerive is Derive and panics instead of returning an error. It is for a realm's mutating path, where an error return does not revert the transaction and only a panic does.

Params

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/periods.MustDerive(,,)"

Result