GnuCash  5.6-117-g4a1abec97e+
Books / Accounting Periods

API: Book

Implementation Overview

       Linas Vepstas <linas@linas.org> December 2001
             Last Updated August 2003

A top, unimplemented request for GnuCash is the ability to 'close the books', that is, to add support for 'accounting periods'. Partial support for books has been added to the GnuCash engine; and a GUI is partially finished. This file reviews the implementation design choices and the current design status.

Definition

An accounting period or 'book' is a set of accounts and transactions that, once closed, must never be modified again. Books are typically closed once a quarter, or once a year. Generating a report from a closed book tells you how well you did for that year (or quarter). The provision against modifying a closed book helps ensure correctness, and discourages cheating. Note that the closing balances of a closed book must be carried forward as the opening balance in the next book. (But only for asset, liability and equity accounts. Opening balances are zeroed for income and expense accounts.)

Note that the correct handling of depreciation, capital gains and other similar accounting concepts requires 'Lots' to be correctly handled. Lots are a way of identifying that an item bought in one transaction is the same as that sold in another transaction. When a book is closed, the entire lot must be brought forward, and not just the account balance, because depreciation, capital gains, taxes, etc. depend on the dates of the originating transaction. See 'lots.txt' for details.

API: Lots: Core Function for AR/AP, Inventory, Stock Lots, Cap Gains

Requirements

Must have good performance (large data files usually mean poor performance). Use the idea of 'books' to prevent file bloat. Must have access to historical data. Must be able to create bar-charts, graphs, reports of multi-year data (i.e. create reports spanning multiple books).

Period

Status

The top-level structure that holds references to all of the data in a book is implemented in src/engine/qofbook.c. The routines to split one book into two, automatically setting account balances, etc. are implemented in src/engine/Period.c. The actual implementation used is "Plan A" from the list of choices below. The actual implementation is not yet complete, see "Implementation Notes" at bottom for the current status.

Possible Solutions

API: Query: Querying for Objects

Listed in order from worst to best:

Plan F:

Simply 'delete' old transactions, and adjust the equity to make up for this. More specifically: Split one file into two, with only 'old' transactions in one, and only 'new' transactions in the other.

I believe that this can be 'easily' coded by creating a second instance of a qofbook structure in memory, copying all the account info into it, and using Query.c to copy in only the 'old' transactions. (and v.v. using Query.c to delete the old transactions in the other copy.) Then, look up the ending balance on all asset/liability accounts in the 'old' book, and create new transactions in the 'new' book that transfers that balance amount to an equity account. The transfer description is, of course, 'opening balance'. Balances of income/expense accounts are zeroed out.

I believe this code would be easy to write in C or scheme. There may be a few bugs/difficulties lurking in qofbook that might trip things up. Also, at a minimum, there needs to be a GUI dialog, asking for the date on which to close the books.

(A fancy but optional GUI dialog/wizard might ask 'which equity account to transfer the opening balances, and what the description should say. This GUI is optional, since, after all, these can be tweaked by hand, and its only done once a year or once a quarter.)

(An even fancier GUI would remember how often the books should close: 1,2,3,4 times a year, 12 times a year, whatever, and 'remind' you when that happens.)

(Another 'fancy' feature might be to not allow user to close book until all 'old' transactions have been cleared/reconciled. But that might be a bit much for non-bank accounts).

API: Transaction, Split

Pros & Cons of plan F:

I think this last one is the Achilles heel, the torpedo in the rudder that sinks the boat.

Plan D

As above, but instead of deleting, add a kvp to each transaction stating '/book/closed-on=12.31.2000'. Then modify the default query for the registers so that the only displayed transactions are those that are not part of a closed book. Modify the Query GUI dialog to add 'book' as a query parameter.

We move on....

Plan C

As in plan F, but instead of creating two books, clone the account tree into two: 'old' and 'new'. The old and new accounts are identical, except that they get different guid's. Every account in the old tree gets a kvp in it: '/book/closed-on=12.31.2000'. We don't copy or delete any transactions; instead, we reclassify them: Old transactions are transfers between old accounts, new transactions are transfers between new accounts.

The account summary needs to be modified to show only 'new' accounts by default. The transfer-from pop-down needs to be modified to show only 'new' accounts only, and never the old accounts.

Transfers between closed and open accounts are never allowed (this is validated/forced in the engine). Opening balances are handled just as in plan 'F'. User can only view data in closed books, and not change it.
If we allow books to be re-opened, then the 'starting balance' equity transfers must be deleted. We can save 're-opening' for some future day.

The 'starting balance equity transfers' must have a kvp pair in them: '/book/closing-balance-of-account-guid=0xdeadbeef'. This way, we know that this transaction is associated with the closure of the book on some specific account, and that way, we can find this transaction someday in the future, if we ever need to.

Each new account needs to point back at the copy that is its 'old' self. (these don't have to be C pointers, they could be some suitably clever kvp: '/book/previous-guid=0xdeadbeef') This continuity is needed in order to be able to create reports that scan over multiple books. The Query.c interface needs to be modified so that it searches only new accounts, or it searches new accounts and their corresponding 'old' copies.

(There are three ways to deal with this account continuity issue:

I believe that if we can deal with the account-continuity issue in query.c or in a wrapper thereto, that there are no remaining issues with reporting. i.e., for any given report, we are either reporting data in closed books, or not. Different reports should have different defaults. e.g. income/expense pie chart never looks at old books. asset-value-over-time-bar-chart always looks at closed books.

But now we have enough info to propose the final solution:

Plan A:

The kvp markup of plan C coupled to the multi-file solution of plan F. In initial startup of GnuCash, only the 'current' book is loaded.
If user asks for a report that requires data from old books, then we have to pause to load one or more of the older books.
If the books are stored as separate files, then the 'current' book needs to somehow know the filenames of the old books. I recommend against storing the books as different sections of one file, for many reasons:

I recommend that every book get a unique guid. The current book would know the guid's if its closed book progeny. The filename would incorporate some compressed version of the guid (and/or the date of closure).

Optional: every book gets not only a unique guid, and also stores some meta-information (as book-level kvp's):

/book/title=some-user-supplied-name
/book/notes=user-supplied-descriptive-comments
/book/start-date=xxx
/book/end-date=xxx
/book/previous-book-guids=(list 0xa 0xb 0xc)
/book/accounting-period=enum {none, week, month, quarter, trimester, year}

Pro's & Con's

I am not aware of any con's to plan A at this point.

Implementation Overview

Plan A has been implemented in the engine. To quickly summarize:

Implementation Notes

ANNOUNCE: Book Closing Beta 2

Books AKA Accounting Periods can now be closed by going to the 'Action' menu on the main window, and selecting 'close books'. This will popup a druid that will allow you to select closing dates. Books are closed one at a time: after each one, you can go back and alter the close date.
A book is 'closed' by moving transactions and prices earlier than the indicated date to a separate file.

As a side-effect of book closing, capital gains (losses) will be automatically computed using a FIFO accounting method. Stocks or other non-cash assets that haven't been sold will remain in the currently open book, even if the purchasing transactions were made a long time ago.

The biggest drawback to the automatic computation of the gains/losses is that there is no GUI to specify alternate accounting methods (e.g. LIFO, or hand-picked lots, etc.) There is basically no practical way to 'undo' the results of the gain/loss computations.

The other main drawback to book closing is that this will prevent multi-year (multi-period) reports & graphs from being generated. The old data will be in separate files, and there is currently no way to load several files at once.

Open Issues/Questions that Need Discussion:

Open Issues / ToDo