|
GnuCash 2.3.0
|
Files | |
| file | Period.h |
Implement accounting periods, as per design in src/doc/books.txt. | |
Functions | |
| QofBook * | gnc_book_close_period (QofBook *, Timespec, Account *equity_acct, const char *memo) |
| void | gnc_book_partition_txn (QofBook *dest, QofBook *src, QofQuery *) |
| void | gnc_book_partition_pricedb (QofBook *dest, QofBook *src, QofQuery *) |
| void | gnc_book_insert_trans (QofBook *book, Transaction *trans) |
| void | gnc_book_insert_trans_clobber (QofBook *book, Transaction *trans) |
| void | gnc_book_insert_lot (QofBook *book, GNCLot *lot) |
| void | gnc_book_insert_lot_clobber (QofBook *book, GNCLot *lot) |
| void | gnc_book_insert_price (QofBook *book, GNCPrice *prc) |
| void | gnc_book_insert_price_clobber (QofBook *book, GNCPrice *prc) |
The architecture of the Accounting Period design is discussed in greater detail in the file "src/doc/books.txt"
The gnc_book_close_period() routine will 'close' a book at the indicated date. It returns a pointer to the closed book, while the argument remains open. This routine will move all of the older transactions from the open book to the closed book. The guid's of the old transactions will not be changed in the move. Note, however, that the closed book will have a copy of every account in the open book, and that these copies will have new GncGUID's issued to them. Thus, every account has a 'twin' in the other book.
This routine will also create 'equity transactions' in order to preserve the balances on accounts. For any account that is not of income, expense, trading or equity type, this routine wil find the closing balance of each account in the closed book. It will then create an 'equity transaction' in the open book, creating an opening balance between an equity account and the twin account to the closed account. The 'memo' field will be used to set the description in the equity transaction. Typically, you will want to set this field to _("Opening Balance").
The equity_account argument is supposed to indicate the equity account in the open book into which the opening balances will be placed. This argument may be NULL, if it is NULL, then a search algorithm will be used to find a suitable equity account. If NULL, this routine searches for the 'nearest' account of GNCAccountType ACCT_TYPE_EQUITY among its siblings, or the siblings of its parents. It does not search downwards. If it does not find such an account, it will create one, hanging off the top-most group.
This routine also populates a number of KVP values in order to make a log of the closing. In principle, the stored KVP's should be enough to locate everything needed to safely re-open and re-close a closed book. In particular, if a closed book is re-opened, the 'equity transaction' would need to be adjusted.
The kvp values that are set are:
Implemented in the closed book: /book/close-date Latest date in this book. Must not change. /book/log-date Date on which user called this routine. /book/next-book GncGUID of next book (the still-open book).
Implemented in still-open book: /book/open-date Earliest date in this book. /book/prev-book GncGUID of previous book (the closed book).
Implemented in the balancing transaction: /book/closed-acct GncGUID of account whose balance was brought forward /book/closed-book GncGUID of book whose balance was brought forward
Implemented in the closed account: /book/balancing-trans GncGUID of equity-balancing transaction. /book/next-book GncGUID of equity-balancing book. /book/next-acct GncGUID of twin of this account in the open book.
Implemented in the still-open account: /book/prev-acct GncGUID of twin of this account in the closed book. /book/prev-book GncGUID of previous book (the closed book)
Definition at line 813 of file Period.c.
{
QofQuery *txn_query, *prc_query;
QofQueryPredData *pred_data;
GSList *param_list;
QofBook *closing_book;
KvpFrame *exist_cwd, *partn_cwd;
Timespec ts;
if (!existing_book) return NULL;
ENTER (" date=%s memo=%s", gnc_print_date(calve_date), memo);
/* Setup closing book */
closing_book = qof_book_new();
qof_book_set_backend (closing_book, qof_book_get_backend(existing_book));
qof_book_mark_closed(closing_book);
period_begin_edit (existing_book, closing_book);
/* Get all transactions that are *earlier* than the calve date,
* and put them in the new book. */
txn_query = qof_query_create_for (GNC_ID_TRANS);
pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
QOF_DATE_MATCH_NORMAL,
calve_date);
param_list = qof_query_build_param_list (TRANS_DATE_POSTED, NULL);
qof_query_add_term (txn_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
gnc_book_partition_txn (closing_book, existing_book, txn_query);
qof_query_destroy (txn_query);
/* Move prices over too */
prc_query = qof_query_create_for (GNC_ID_PRICE);
pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
QOF_DATE_MATCH_NORMAL,
calve_date);
param_list = qof_query_build_param_list (PRICE_DATE, NULL);
qof_query_add_term (prc_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
gnc_book_partition_pricedb (closing_book, existing_book, prc_query);
qof_query_destroy (prc_query);
/* Now add the various identifying kvp's */
/* cwd == 'current working directory' */
exist_cwd = qof_book_get_slots(existing_book);
partn_cwd = qof_book_get_slots(closing_book);
/* Mark the boundary date between the books */
kvp_frame_set_timespec (exist_cwd, "/book/open-date", calve_date);
kvp_frame_set_timespec (partn_cwd, "/book/close-date", calve_date);
/* Mark partition as being closed */
ts.tv_sec = time(0);
ts.tv_nsec = 0;
kvp_frame_set_timespec (partn_cwd, "/book/log-date", ts);
/* Set up pointers to each book from the other. */
kvp_frame_set_guid (partn_cwd, "/book/next-book", qof_book_get_guid(existing_book));
kvp_frame_set_guid (exist_cwd, "/book/prev-book", qof_book_get_guid(closing_book));
/* add in transactions to equity accounts that will
* hold the colsing balances */
add_closing_balances (gnc_book_get_root_account(closing_book),
existing_book, closing_book,
equity_account,
&calve_date, &ts, memo);
period_commit_edit (existing_book, closing_book);
LEAVE (" ");
return closing_book;
}
| void gnc_book_insert_trans | ( | QofBook * | book, |
| Transaction * | trans | ||
| ) |
The gnc_book_insert_trans_clobber() routine takes an existing transaction that is located in one book, and moves it to another book. It moves all of the splits as well. In the course of the move, the transaction is literally deleted from the first book as its placed into the second. The transaction and split GncGUID's are not changed in the move. This routine assumes that twin accounts already exist in both books (and can be located with the standard twining proceedure).
Note that this routine does *not* move the lots that any of the splits might belong to. These must be moved sepearately. Note that one must take care when moving a transaction, so that any associated lots don't end up hamstrung across two different books.
The gnc_book_insert_trans() routine does the same as the above, except that it doesn't actually clobber the transaction: it merely moves the transaction and split GncGUID's to the new books' entity tables, and not much else.
The gnc_book_insert_lot() routine, as above, but for lots ...
Definition at line 129 of file Period.c.
{
QofCollection *col;
QofBook *trans_book;
GList *node;
if (!trans || !book) return;
/* If this is the same book, its a no-op. */
trans_book = qof_instance_get_book(trans);
if (trans_book == book) return;
/* If the old and new book don't share backends, then clobber-copy;
* i.e. destroy it in one backend, create it in another. */
if (qof_book_get_backend(book) != qof_book_get_backend(trans_book))
{
gnc_book_insert_trans_clobber (book, trans);
return;
}
ENTER ("trans=%p %s", trans, trans->description);
/* Fiddle the transaction into place in the new book */
xaccTransBeginEdit (trans);
col = qof_book_get_collection (book, GNC_ID_TRANS);
qof_instance_set_book(trans, book);
qof_collection_insert_entity (col, &trans->inst);
col = qof_book_get_collection (book, GNC_ID_SPLIT);
for (node = trans->splits; node; node = node->next)
{
Account *twin;
Split *s = node->data;
/* Move the splits over (only if they haven't already been moved). */
if (qof_instance_get_book(s) != book)
{
qof_instance_set_book(s, book);
qof_collection_insert_entity (col, &s->inst);
}
/* Find the twin account, and re-parent to that. */
twin = xaccAccountLookupTwin (s->acc, book);
if (!twin)
{
PERR ("near-fatal: twin account not found");
}
else
{
/* Move the split too, if it hasn't been moved already */
if (s->acc != twin)
{
xaccAccountInsertSplit (twin, s);
g_object_set(twin, "sort-dirty", TRUE, "balance-dirty", TRUE, NULL);
}
}
}
xaccTransCommitEdit (trans);
qof_event_gen (&trans->inst, QOF_EVENT_MODIFY, NULL);
LEAVE ("trans=%p %s", trans, trans->description);
}
The gnc_book_partition_pricedb() routine uses te result of the indicated query to move a set of prices from the "src" book to the "dest" book. The query passed into it must be set up to return a list of prices.
Definition at line 483 of file Period.c.
{
GNCPriceDB *src_pdb, *dest_pdb;
GList *price_list, *pnode;
if (!src_book || !dest_book || !query) return;
ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
src_pdb = gnc_pricedb_get_db (src_book);
dest_pdb = gnc_pricedb_get_db (dest_book);
gnc_pricedb_begin_edit (src_pdb);
gnc_pricedb_begin_edit (dest_pdb);
gnc_pricedb_set_bulk_update (dest_pdb, TRUE);
qof_query_set_book (query, src_book);
price_list = qof_query_run (query);
printf ("duude XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX prices\n");
for (pnode = price_list; pnode; pnode = pnode->next)
{
GNCPrice *pr = pnode->data;
gnc_book_insert_price (dest_book, pr);
}
gnc_pricedb_set_bulk_update (dest_pdb, FALSE);
gnc_pricedb_commit_edit (dest_pdb);
gnc_pricedb_commit_edit (src_pdb);
LEAVE (" src_book=%p dest_book=%p", src_book, dest_book);
}
The gnc_book_partition_txn() uses the result of the indicated query to move a set of transactions from the "src" book to the "dest" book. Before moving the transactions, it will first place a copy of all of the accounts in "src" into "dest". This is done in order to ensure that all of the moved transactions will have the corrrect set of accounts to reference. The transactions that will be moved are precisely those specified by the query. Any query that returns a list of transactions will work to partition a book; however, its expected that this routine will mostly serve as a utility to break up a book into accounting periods.
This routine intentionally does not copy scheduled/recurring transactions.
This routine will also copy closed lots to the destination book. NOTICE: It will not copy open lots, nor will it copy lots that have lead to transactions that contains splits in other open lots. Leaving behind open lots is exactly what is needed for closing books, but it means that gnc_book_partition() is not really a 'general purpose' function. The way to fix this would be to weed out open lots by constructing the query correctly.
When an account is copied, the copy is issued a new GncGUID. The GncGUID of its sibling is placed in the 'gemini' KVP value (See kvp_doc.txt for more detail). Transactions and splits are moved without reassigning them a new GncGUID. Note they are removed from one book's entity table and placed into the other book: Once moved, they won't be findable in the entity table of the old book.
Known Bugs: When this routine copies accounts, it does not check to see if they already exist in the 'dest' book; it should. For the current usage, this bug aint important, and I'm too lazy to fix it.
Definition at line 518 of file Period.c.
{
gnc_commodity_table *src_tbl, *dst_tbl;
Account *src_root, *dst_root;
time_t now;
TransList *trans_list, *tnode;
LotList *lot_list, *lnode;
QofInstance *book_inst;
if (!src_book || !dest_book || !query) return;
ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
/* First, copy the book's KVP tree */
/* hack alert -- FIXME -- this should really be a merge, not a
* clobber copy, but I am too lazy to write a kvp merge routine,
* and it is not needed for the current usage. */
kvp_frame_delete (qof_book_get_slots(dest_book));
book_inst = (QofInstance*)dest_book;
book_inst->kvp_data = kvp_frame_copy (qof_book_get_slots(src_book));
/* Next, copy the commodity tables */
src_tbl = gnc_commodity_table_get_table (src_book);
dst_tbl = gnc_commodity_table_get_table (dest_book);
gnc_commodity_table_copy (dst_tbl, src_tbl, dest_book);
/* Next, copy all of the accounts */
/* hack alert -- FIXME -- this should really be a merge, not a
* clobber copy, but I am too lazy to write an account-group merge
* routine, and it is not needed for the current usage. */
src_root = gnc_book_get_root_account (src_book);
dst_root = gnc_book_get_root_account (dest_book);
gnc_account_copy_children (dst_root, src_root);
/* Next, run the query */
xaccAccountBeginEdit (dst_root);
xaccAccountBeginEdit (src_root);
qof_query_set_book (query, src_book);
trans_list = qof_query_run (query);
/* Preen: remove open lots/ open trnasactions */
gnc_account_foreach_descendant(src_root, clear_markers, NULL);
trans_list = trans_list_preen_open_lots (trans_list);
lot_list = create_lot_list_from_trans_list (trans_list);
lot_list = lot_list_preen_open_lots (lot_list);
/* Move closed lots over to destination. Do this before moving
* the txn's, so that the lots don't get trashed. */
for (lnode = lot_list; lnode; lnode = lnode->next)
{
GNCLot *lot = lnode->data;
gnc_book_insert_lot (dest_book, lot);
}
/* Move the transactions over to the destination book. */
for (tnode = trans_list; tnode; tnode = tnode->next)
{
Transaction *trans = tnode->data;
gnc_book_insert_trans (dest_book, trans);
}
xaccAccountCommitEdit (src_root);
xaccAccountCommitEdit (dst_root);
/* Make note of the sibling books */
now = time(0);
gnc_kvp_bag_add (qof_book_get_slots(src_book), "gemini", now,
"book_guid", qof_book_get_guid(dest_book),
NULL);
gnc_kvp_bag_add (qof_book_get_slots(dest_book), "gemini", now,
"book_guid", qof_book_get_guid(src_book),
NULL);
LEAVE (" ");
}
1.7.4