mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 12:14:31 -06:00
16b4976381
Found via `codespell -q 3 -L ans,ba,parm,parms,numer`
428 lines
18 KiB
Plaintext
428 lines
18 KiB
Plaintext
/** \page bookperiods Books / Accounting Periods
|
|
|
|
API: \ref Book\n
|
|
|
|
\section periodsintro 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.
|
|
|
|
\subsection periodsdefines 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: \ref Lot
|
|
|
|
\subsection periodsrequired 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).
|
|
|
|
\ref Period
|
|
|
|
\subsection periodsstatus 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.
|
|
|
|
\section periodssolution Possible Solutions
|
|
|
|
API: \ref Query
|
|
|
|
Listed in order from worst to best:
|
|
|
|
\subsection periodsdelete 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: \ref Transaction
|
|
|
|
\subsection periodsf Pros & Cons of plan F:
|
|
|
|
- pro: simple. The simplest option.
|
|
- pro: truncated file loads much faster
|
|
- pro: old/irrelevant accounts can be safely deleted from newest file
|
|
(while still being preserved in old files).
|
|
|
|
- con: impossible to generate 5 year reports, multi-year graphs. This
|
|
would really hurt, esp, when tracking stocks/mutual funds/retirement
|
|
accounts over a number of years.
|
|
|
|
I think this last one is the Achilles heel, the torpedo in the rudder
|
|
that sinks the boat.
|
|
|
|
\subsection periodspland 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.
|
|
|
|
- pro: easy access to historical record
|
|
|
|
- con: slow loads; file size not reduced.
|
|
- con: dealing with opening balances, equity, is icky.
|
|
- con: can't delete/hide old/stale accounts.
|
|
|
|
We move on....
|
|
|
|
\subsection periodsplanc 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:
|
|
- don't deal with it in query.c: force various GUI dialogs to explicitly
|
|
formulate queries involving the /book/previous-guid string.
|
|
but this gets messy in the GUI's. May lead to excess cut-n-paste
|
|
of similar code between different GUI's.
|
|
|
|
- 'hide' the distinction between 'old' and 'new' in query.c:
|
|
the users of query.c need only to specify a boolean flag: search
|
|
closed books: yes/no. However, this is conceptually ugly, and
|
|
prevents query from doing low-level queries on specific
|
|
books.
|
|
|
|
- create query utility wrapper/pre-processor that takes a query,
|
|
and then modifies it to search through closed books as well.
|
|
This is the 'cleanest' solution. ??
|
|
|
|
- All these are delicate, and need a little more thought and
|
|
exploration. Goal is to simplify queries, not burden the system
|
|
with cryptic, complex code.
|
|
)
|
|
|
|
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.
|
|
|
|
|
|
- pro: safer than plan F, since we really can enforce the 'you aren't
|
|
allowed to edit closed books' rule.
|
|
- pro: solves the old-account/new-account problem, since new accounts
|
|
can be edited/deleted without damaging old account.
|
|
- pro: solves the historical reporting problem.
|
|
|
|
- con: queries are potentially slow, loading of file is potentially slow.
|
|
|
|
But now we have enough info to propose the final solution:
|
|
|
|
\subsection periodsplana 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:
|
|
- risk of corruption of old books
|
|
- bloated file size
|
|
- the single-file solution would need to invent a 'directory' so
|
|
that the location of the old books in the file can be quickly
|
|
found and lseek()'ed or mmap()'ed. But why invent a directory?
|
|
Unix already provides directories!
|
|
|
|
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\n
|
|
/book/notes=user-supplied-descriptive-comments\n
|
|
/book/start-date=xxx\n
|
|
/book/end-date=xxx\n
|
|
/book/previous-book-guids=(list 0xa 0xb 0xc)\n
|
|
/book/accounting-period=enum {none, week, month, quarter, trimester, year}
|
|
|
|
\subsection periodsaprocon Pro's & Con's
|
|
|
|
I am not aware of any con's to plan A at this point.
|
|
|
|
\section periodsoverview Implementation Overview
|
|
|
|
Plan A has been implemented in the engine. To quickly summarize:
|
|
- Partitioning involves splitting one book into two, called the
|
|
"old, closing book", and the "current open book".
|
|
|
|
- Accounts are copied between old and new books. One of the copies
|
|
is issued new GUID's, but the rest of the account data is copied.
|
|
KVP pairs are then added so that each copy points at the other,
|
|
and can thus be easily found. The "gemini" KVP keyword is used.
|
|
The opening balance is zeroed for income accounts. A transaction
|
|
is created to set the correct opening balance on asset accounts.
|
|
The transaction is a transfer from equity. An equity account is
|
|
created automagically, if needed.
|
|
|
|
- Transactions. Transactions are partitioned, and end up either
|
|
in the old or the new book. Splits move with transactions.
|
|
Note that some transactions, associated with open lots, may be
|
|
kept in the new book (See below).
|
|
|
|
- Lots. If a transaction has a split in an open lot, then that
|
|
transaction is not moved to the closed book. It needs to stay
|
|
with the open book. If a lot is closed, and all of the other
|
|
lots associated with all of the transactions in this lot are
|
|
also closed, then the lot may be moved to the closed book
|
|
(and all of the other associated lots must then also be moved).
|
|
|
|
- Prices. Prices are sorted into the new and old books according
|
|
to the date on the price.
|
|
|
|
- Scheduled transactions/recurring transactions. These are left
|
|
in the new book, untouched. They are not copied into the old
|
|
book, and no trace of their existence is left in the old book.
|
|
|
|
- Business Objects. Not implemented.
|
|
|
|
\section periodsnotes Implementation Notes
|
|
|
|
- src/engine/Period.[ch]
|
|
Implements the main logic to split one book into two, and populate
|
|
it with the appropriate keys, markup, etc. and to carry balances
|
|
forward, etc.
|
|
|
|
- src/engine/gnc-lot.[ch]
|
|
Implements accounting Lots.
|
|
|
|
- src/engine/Scrub2.[ch]
|
|
Implements simple FIFO for lots. Data is scrubbed with respect to
|
|
this FIFO before the books are closed. Commodity accounts (e.g.
|
|
stock accounts) must have a coherent Lots structure before books
|
|
can be closed. The scrubber makes sure there's nothing hanging
|
|
out.
|
|
|
|
- src/gnome/druid-acct-period.[ch]
|
|
Implements a druid interface to allow user to specify book closing
|
|
dates, add a title and notes to a book, and walk through the process.
|
|
Uses FreqSpec.[ch] and the widget in gnc-frequency.[ch] to allow
|
|
the user to specify the frequency of book closings.
|
|
|
|
- The XML-file backend can store multiple books in one file. There
|
|
is currently minimal support for writing out multiple books,
|
|
one per file (this enables faster load, by not loading old books).
|
|
There is currently no support for loading multiple books from multiple
|
|
files.
|
|
|
|
- (Details about the dbi backend are missing.)
|
|
|
|
\section periodsannounce 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.
|
|
|
|
\section periodsissues Open Issues/Questions that Need Discussion:
|
|
|
|
- How to handle business objects? e.g. vendors, customers should be
|
|
copied into both old and new books. But invoices should be in one
|
|
or the other. Need to document which is which.
|
|
Copy:
|
|
customer -- ok, has guid, has for-each
|
|
employee\n
|
|
No-op:
|
|
address -- do nothing
|
|
|
|
- Discussion Q: What should the naming convention be for the different
|
|
books? When the XML file backend is used, different books need to be
|
|
stored as different files (in order to avoid the performance
|
|
penalty of a large file load). Currently, what's implemented is\n
|
|
book-1dc750aa3e6fd045c13ac8afb1a9ac03-my-gnucash-file.xac.gml\n
|
|
where the number is the GUID of the closed book (needed to be able to
|
|
quickly find the book/file) and 'my-gnucash-file.xac' is the name of
|
|
the original file whose books were closed.\n
|
|
Need to change the name to include the word "archive".
|
|
|
|
- Discussion Q: When saving books, make the book title part of the
|
|
book name (user convenience).
|
|
|
|
- Should closed books be allowed to have unreconciled transactions?
|
|
Answer: probably. Should there be a warning?
|
|
|
|
\section periodstodo Open Issues / ToDo
|
|
|
|
- Change GUI to allow user to specify a default equity account
|
|
for dealing with opening balances. Should be done with kvp markup.
|
|
|
|
- Fix crash when exiting gnucash after closing books.
|
|
|
|
- The filename of the old, closed books should probably be saved
|
|
in the KVP tree of the current open book. This need be done only
|
|
from the file backend.
|
|
|
|
- Need to mark closed book as unalterable, and respect that markup.
|
|
I think there's a 'closed' flag in the book, but I don't think its
|
|
respected.
|
|
|
|
- The book closing GUI (druid-acct-period.c) needs to save/restore
|
|
period end date (the FreqSpec) to KVP on open book. This would be
|
|
easy once we have a freq-spec-to-kvp and freq-spec-from-kvp routines.
|
|
|
|
- Handling of lots in book closing is implemented but is poorly tested.
|
|
Need to write test cases. Also test cases for prices in book
|
|
closing.
|
|
|
|
- price-saving in the SQL backend is probably broken, its certainly
|
|
untested. Need to remove old deprecated price-lookup mechanism,
|
|
and replace w/ qofquery.
|
|
|
|
- Need to provide for loading of closed books, because this is needed
|
|
for reports.
|
|
|
|
- Handling of multi-book reports ??? Need to work out the recommended way
|
|
of making this happen....
|
|
|
|
- Have some way of remembering the quickfill text from older books.
|
|
|
|
- Possibly neat idea:
|
|
Book closing, as currently implemented in the GUI, is driven entirely
|
|
by the date-posted. There is no (planned) interface to allow you to
|
|
exclude some certain transactions from a particular closing (although
|
|
it would be 'easy' to add this: right before I close a book, I have a
|
|
list of transactions which can be added to/removed from).
|
|
|
|
- add 13-period support to FreqSpec and the FreqSpec widget.
|
|
e.g. from the mailing list:
|
|
One of the calendars my company uses is like this:
|
|
13 periods
|
|
1st period begins jan 1st, partial first week plus 4 weeks.
|
|
2nd - 13th period begins on a sunday every four weeks.
|
|
13th period - 4th week may be less than full week because it ends on 12/31.\n
|
|
For 2003:\n
|
|
01. 01/01 - 02/01\n
|
|
02. 02/02 - 03/01\n
|
|
03. 03/02 - 03/29\n
|
|
04. 03/30 - 04/26\n
|
|
05. 04/27 - 05/24\n
|
|
06. 05/25 - 06/21\n
|
|
07. 06/22 - 07/19\n
|
|
08. 07/20 - 08/16\n
|
|
09. 08/17 - 09/13\n
|
|
10. 09/14 - 10/11\n
|
|
11. 10/12 - 11/08\n
|
|
12. 11/09 - 12/06\n
|
|
13. 12/07 - 12/31\n
|
|
|
|
*/
|
|
=========================== end of file ========================
|