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 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 }
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 }
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 }
1.5.2