Accounting Periods
[GnuCash Engine: Core, Non-GUI Accounting Functions]


Detailed Description

The architecture of the Accounting Period design is discussed in greater detail in the file "src/doc/books.txt"


Files

file  Period.h
 Implement accounting periods, as per design in src/doc/books.txt.

Functions

QofBookgnc_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)


Function Documentation

QofBook* gnc_book_close_period ( QofBook ,
Timespec  ,
Account equity_acct,
const char *  memo 
)

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 GUID'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 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 GUID of next book (the still-open book).

Implemented in still-open book: /book/open-date Earliest date in this book. /book/prev-book GUID of previous book (the closed book).

Implemented in the balancing transaction: /book/closed-acct GUID of account whose balance was brought forward /book/closed-book GUID of book whose balance was brought forward

Implemented in the closed account: /book/balancing-trans GUID of equity-balancing transaction. /book/next-book GUID of equity-balancing book. /book/next-acct GUID of twin of this account in the open book.

Implemented in the still-open account: /book/prev-acct GUID of twin of this account in the closed book. /book/prev-book GUID of previous book (the closed book)

Definition at line 810 of file Period.c.

00813 {
00814    QofQuery *txn_query, *prc_query;
00815    QofQueryPredData *pred_data;
00816    GSList *param_list;
00817    QofBook *closing_book;
00818    KvpFrame *exist_cwd, *partn_cwd;
00819    Timespec ts;
00820 
00821    if (!existing_book) return NULL;
00822    ENTER (" date=%s memo=%s", gnc_print_date(calve_date), memo);
00823 
00824    /* Setup closing book */
00825    closing_book = qof_book_new();
00826    qof_book_set_backend (closing_book, qof_book_get_backend(existing_book));
00827    qof_book_mark_closed(closing_book);
00828 
00829    period_begin_edit (existing_book, closing_book);
00830 
00831    /* Get all transactions that are *earlier* than the calve date,
00832     * and put them in the new book.  */
00833    txn_query = qof_query_create_for (GNC_ID_TRANS);
00834    pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
00835                                          QOF_DATE_MATCH_NORMAL,
00836                                          calve_date);
00837    param_list = qof_query_build_param_list (TRANS_DATE_POSTED, NULL);
00838    qof_query_add_term (txn_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
00839 
00840    gnc_book_partition_txn (closing_book, existing_book, txn_query);
00841    qof_query_destroy (txn_query);
00842 
00843    /* Move prices over too */
00844    prc_query = qof_query_create_for (GNC_ID_PRICE);
00845    pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
00846                                          QOF_DATE_MATCH_NORMAL,
00847                                          calve_date);
00848    param_list = qof_query_build_param_list (PRICE_DATE, NULL);
00849    qof_query_add_term (prc_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
00850 
00851    gnc_book_partition_pricedb (closing_book, existing_book, prc_query);
00852    qof_query_destroy (prc_query);
00853 
00854    /* Now add the various identifying kvp's */
00855    /* cwd == 'current working directory' */
00856    exist_cwd = qof_book_get_slots(existing_book);
00857    partn_cwd = qof_book_get_slots(closing_book);
00858    
00859    /* Mark the boundary date between the books */
00860    kvp_frame_set_timespec (exist_cwd, "/book/open-date", calve_date);
00861    kvp_frame_set_timespec (partn_cwd, "/book/close-date", calve_date);
00862 
00863    /* Mark partition as being closed */
00864    ts.tv_sec = time(0);
00865    ts.tv_nsec = 0;
00866    kvp_frame_set_timespec (partn_cwd, "/book/log-date", ts);
00867 
00868    /* Set up pointers to each book from the other. */
00869    kvp_frame_set_guid (partn_cwd, "/book/next-book", qof_book_get_guid(existing_book));
00870    kvp_frame_set_guid (exist_cwd, "/book/prev-book", qof_book_get_guid(closing_book));
00871 
00872    /* add in transactions to equity accounts that will
00873     * hold the colsing balances */
00874    add_closing_balances (gnc_book_get_root_account(closing_book), 
00875                         existing_book, closing_book,
00876                         equity_account,
00877                         &calve_date, &ts, memo);
00878 
00879    period_commit_edit (existing_book, closing_book);
00880 
00881    LEAVE (" ");
00882    return closing_book;
00883 }

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 GUID'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 GUID'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 130 of file Period.c.

00131 {
00132    QofCollection *col;
00133    QofBook *trans_book;
00134    GList *node;
00135 
00136    if (!trans || !book) return;
00137    
00138    /* If this is the same book, its a no-op. */
00139    trans_book = qof_instance_get_book(trans);
00140    if (trans_book == book) return;
00141 
00142    /* If the old and new book don't share backends, then clobber-copy;
00143     * i.e. destroy it in one backend, create it in another.  */
00144    if (qof_book_get_backend(book) != qof_book_get_backend(trans_book))
00145    {
00146       gnc_book_insert_trans_clobber (book, trans);
00147       return;
00148    }
00149    ENTER ("trans=%p %s", trans, trans->description);
00150 
00151    /* Fiddle the transaction into place in the new book */
00152    xaccTransBeginEdit (trans);
00153 
00154    col = qof_book_get_collection (book, GNC_ID_TRANS);
00155    qof_instance_set_book(trans, book);
00156    qof_collection_insert_entity (col, &trans->inst);
00157 
00158    col = qof_book_get_collection (book, GNC_ID_SPLIT);
00159    for (node = trans->splits; node; node = node->next)
00160    {
00161       Account *twin;
00162       Split *s = node->data;
00163 
00164       /* Move the splits over (only if they haven't already been moved). */
00165       if (qof_instance_get_book(s) != book)
00166       {
00167          qof_instance_set_book(s, book);
00168          qof_collection_insert_entity (col, &s->inst);
00169       }
00170 
00171       /* Find the twin account, and re-parent to that. */
00172       twin = xaccAccountLookupTwin (s->acc, book);
00173       if (!twin)
00174       {
00175          PERR ("near-fatal: twin account not found");
00176       }
00177       else
00178       {
00179         /* Move the split too, if it hasn't been moved already */
00180         if (s->acc != twin)
00181         {
00182            xaccAccountInsertSplit (twin, s);
00183            g_object_set(twin, "sort-dirty", TRUE, "balance-dirty", TRUE, NULL);
00184         }
00185       }
00186    }
00187 
00188    xaccTransCommitEdit (trans);
00189    qof_event_gen (&trans->inst, QOF_EVENT_MODIFY, NULL);
00190    LEAVE ("trans=%p %s", trans, trans->description);
00191 }

void gnc_book_partition_pricedb ( QofBook dest,
QofBook src,
QofQuery  
)

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.

00484 {
00485    GNCPriceDB *src_pdb, *dest_pdb;
00486    GList *price_list, *pnode;
00487 
00488    if (!src_book || !dest_book || !query) return;
00489    ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
00490 
00491    src_pdb = gnc_pricedb_get_db (src_book);
00492    dest_pdb = gnc_pricedb_get_db (dest_book);
00493 
00494    gnc_pricedb_begin_edit (src_pdb);
00495    gnc_pricedb_begin_edit (dest_pdb);
00496    gnc_pricedb_set_bulk_update (dest_pdb, TRUE);
00497 
00498    qof_query_set_book (query, src_book);
00499    price_list = qof_query_run (query);
00500 
00501 printf ("duude XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX prices\n");
00502    for (pnode = price_list; pnode; pnode=pnode->next)
00503    {
00504       GNCPrice *pr = pnode->data;
00505       gnc_book_insert_price (dest_book, pr);
00506    }
00507 
00508    gnc_pricedb_set_bulk_update (dest_pdb, FALSE);
00509    gnc_pricedb_commit_edit (dest_pdb);
00510    gnc_pricedb_commit_edit (src_pdb);
00511 
00512    LEAVE (" src_book=%p dest_book=%p", src_book, dest_book);
00513 }

void gnc_book_partition_txn ( QofBook dest,
QofBook src,
QofQuery  
)

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 GUID. The GUID 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 GUID. 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.

00519 {
00520    gnc_commodity_table *src_tbl, *dst_tbl;
00521    Account *src_root, *dst_root;
00522    time_t now;
00523    TransList *trans_list, *tnode;
00524    LotList *lot_list, *lnode;
00525    QofInstance *book_inst;
00526 
00527    if (!src_book || !dest_book || !query) return;
00528    ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
00529 
00530    /* First, copy the book's KVP tree */
00531    /* hack alert -- FIXME -- this should really be a merge, not a
00532     * clobber copy, but I am too lazy to write a kvp merge routine,
00533     * and it is not needed for the current usage. */
00534    kvp_frame_delete (qof_book_get_slots(dest_book));
00535    book_inst = (QofInstance*)dest_book;
00536    book_inst->kvp_data = kvp_frame_copy (qof_book_get_slots(src_book));
00537 
00538    /* Next, copy the commodity tables */
00539    src_tbl = gnc_commodity_table_get_table (src_book);
00540    dst_tbl = gnc_commodity_table_get_table (dest_book);
00541    gnc_commodity_table_copy (dst_tbl, src_tbl, dest_book);
00542 
00543    /* Next, copy all of the accounts */
00544    /* hack alert -- FIXME -- this should really be a merge, not a
00545     * clobber copy, but I am too lazy to write an account-group merge 
00546     * routine, and it is not needed for the current usage. */
00547    src_root = gnc_book_get_root_account (src_book);
00548    dst_root = gnc_book_get_root_account (dest_book);
00549    gnc_account_copy_children (dst_root, src_root);
00550 
00551    /* Next, run the query */
00552    xaccAccountBeginEdit (dst_root);
00553    xaccAccountBeginEdit (src_root);
00554    qof_query_set_book (query, src_book);
00555    trans_list = qof_query_run (query);
00556 
00557    /* Preen: remove open lots/ open trnasactions */
00558    gnc_account_foreach_descendant(src_root, clear_markers, NULL);
00559    trans_list = trans_list_preen_open_lots (trans_list);
00560    lot_list = create_lot_list_from_trans_list (trans_list);
00561    lot_list = lot_list_preen_open_lots (lot_list);
00562 
00563    /* Move closed lots over to destination. Do this before moving 
00564     * the txn's, so that the lots don't get trashed.  */
00565    for (lnode = lot_list; lnode; lnode = lnode->next)
00566    {
00567       GNCLot *lot = lnode->data;
00568       gnc_book_insert_lot (dest_book, lot);
00569    }
00570 
00571    /* Move the transactions over to the destination book. */
00572    for (tnode = trans_list; tnode; tnode=tnode->next)
00573    {
00574       Transaction *trans = tnode->data;
00575       gnc_book_insert_trans (dest_book, trans);
00576    }
00577 
00578    xaccAccountCommitEdit (src_root);
00579    xaccAccountCommitEdit (dst_root);
00580 
00581    /* Make note of the sibling books */
00582    now = time(0);
00583    gnc_kvp_bag_add (qof_book_get_slots(src_book), "gemini", now, 
00584                           "book_guid", qof_book_get_guid(dest_book),
00585                            NULL);
00586    gnc_kvp_bag_add (qof_book_get_slots(dest_book), "gemini", now, 
00587                           "book_guid", qof_book_get_guid(src_book),
00588                            NULL);
00589    LEAVE (" ");
00590 }


Generated on Thu Jul 3 05:07:17 2008 for GnuCash by  doxygen 1.5.2