dialog-book-close.c

00001 /********************************************************************\
00002  * dialog-book-close.c -- dialog for helping the user close the     *
00003  *                        book at the end of the year by adding     *
00004  *                        zero-izing splits to all Income and       *
00005  *                        Expense accounts                          *
00006  *                                                                  *
00007  * Copyright (C) 2007-8 Derek Atkins <derek@ihtfp.com>              *
00008  *                                                                  *
00009  * This program is free software; you can redistribute it and/or    *
00010  * modify it under the terms of the GNU General Public License as   *
00011  * published by the Free Software Foundation; either version 2 of   *
00012  * the License, or (at your option) any later version.              *
00013  *                                                                  *
00014  * This program is distributed in the hope that it will be useful,  *
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00017  * GNU General Public License for more details.                     *
00018  *                                                                  *
00019  * You should have received a copy of the GNU General Public License*
00020  * along with this program; if not, contact:                        *
00021  *                                                                  *
00022  * Free Software Foundation           Voice:  +1-617-542-5942       *
00023  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00024  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00025 \********************************************************************/
00026 
00027 #include "config.h"
00028 
00029 #include <gtk/gtk.h>
00030 #include <glib/gi18n.h>
00031 #include <glade/glade.h>
00032 
00033 #include "dialog-utils.h"
00034 #include "gnc-engine.h"
00035 #include "Transaction.h"
00036 #include "Split.h"
00037 #include "Account.h"
00038 #include "gnc-ui.h"
00039 #include "gnc-gui-query.h"
00040 #include "dialog-book-close.h"
00041 #include "gnc-account-sel.h"
00042 #include "gnc-component-manager.h"
00043 #include "gnc-date-edit.h"
00044 #include "gnc-session.h"
00045 
00046 #define DIALOG_BOOK_CLOSE_CM_CLASS "dialog-book-close"
00047 
00048 void gnc_book_close_response_cb(GtkDialog *, gint, GtkDialog *);
00049 
00050 struct CloseBookWindow
00051 {
00052   /* Passed in by the creator */
00053   QofBook* book;
00054 
00055   /* Parts of the dialog */
00056   GtkWidget* dialog;
00057   GtkWidget* close_date_widget;
00058   GtkWidget* income_acct_widget;
00059   GtkWidget* expense_acct_widget;
00060   GtkWidget* desc_widget;
00061 
00062   /* The final settings */
00063   time_t close_date;
00064   const char* desc;
00065 
00066   /* Component registration */
00067   gint component_manager_id;
00068 };
00069 
00070 struct CloseAccountsCB
00071 {
00072   struct CloseBookWindow* cbw;
00073   Account* base_acct;
00074   GNCAccountType acct_type;
00075   GHashTable* txns;
00076   guint hash_size;
00077 };
00078 
00079 struct CACBTransactionList
00080 {
00081   gnc_commodity* cmdty;
00082   Transaction* txn;
00083   gnc_numeric total;
00084 };
00085 
00086 static struct CACBTransactionList*
00087 find_or_create_txn(struct CloseAccountsCB* cacb, gnc_commodity* cmdty)
00088 {
00089   struct CACBTransactionList* txn;
00090 
00091   g_return_val_if_fail(cacb, NULL);
00092   g_return_val_if_fail(cmdty, NULL);
00093 
00094   txn = g_hash_table_lookup(cacb->txns, cmdty);
00095   if (!txn)
00096   {
00097     txn = g_new0(struct CACBTransactionList, 1);
00098     txn->cmdty = cmdty;
00099     txn->total = gnc_numeric_zero();
00100     txn->txn = xaccMallocTransaction(cacb->cbw->book);
00101     xaccTransBeginEdit(txn->txn);
00102     xaccTransSetDateEnteredSecs(txn->txn, time(NULL));
00103     xaccTransSetDatePostedSecs(txn->txn, cacb->cbw->close_date);
00104     xaccTransSetDescription(txn->txn, cacb->cbw->desc);
00105     xaccTransSetCurrency(txn->txn, cmdty);
00106 
00107     g_hash_table_insert(cacb->txns, cmdty, txn);
00108   }
00109 
00110   return txn;
00111 }
00112 
00113 /* Make sure that the account is of the correct type.
00114  * then make sure the account has a balance as of the closing date.
00115  * then get the account commodity and find the appropriate
00116  * balancing transaction for that commodity and add this balance
00117  * to it.
00118  */
00119 static void close_accounts_cb(Account *a, gpointer data)
00120 {
00121   struct CloseAccountsCB* cacb = data;
00122   struct CACBTransactionList* txn;
00123   gnc_commodity* acct_commodity;
00124   Split* split;
00125   gnc_numeric bal;
00126 
00127   g_return_if_fail(a);
00128   g_return_if_fail(cacb);
00129   g_return_if_fail(cacb->cbw);
00130   g_return_if_fail(cacb->txns);
00131 
00132   if (cacb->acct_type != xaccAccountGetType(a))
00133     return;
00134 
00135   bal = xaccAccountGetBalanceAsOfDate(a, cacb->cbw->close_date+1);
00136   if (gnc_numeric_zero_p(bal))
00137     return;
00138 
00139   acct_commodity = xaccAccountGetCommodity(a);
00140   g_assert(acct_commodity);
00141 
00142   txn = find_or_create_txn(cacb, acct_commodity);
00143   g_assert(txn);
00144   
00145   split = xaccMallocSplit(cacb->cbw->book);
00146   xaccSplitSetParent(split, txn->txn);
00147   xaccAccountBeginEdit(a);
00148   xaccAccountInsertSplit(a, split);
00149   xaccSplitSetBaseValue(split, gnc_numeric_neg(bal), acct_commodity);
00150   xaccAccountCommitEdit(a);
00151   txn->total = gnc_numeric_add(txn->total, bal, GNC_DENOM_AUTO,
00152                                GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER);
00153 }
00154 
00155 
00156 static void finish_txn_cb(gnc_commodity* cmdty,
00157                           struct CACBTransactionList* txn,
00158                           struct CloseAccountsCB* cacb)
00159 {
00160   Account* acc;
00161   Split* split;
00162 
00163   g_return_if_fail(cmdty);
00164   g_return_if_fail(txn);
00165   g_return_if_fail(cacb);
00166   g_return_if_fail(cacb->hash_size);
00167 
00168   /* If we only have one currency and the base account uses
00169    * that currency, then we can use that account.  Otherwise,
00170    * create a subaccount for each currency.
00171    */
00172   if (cacb->hash_size == 1 &&
00173       gnc_commodity_equal(cmdty, xaccAccountGetCommodity(cacb->base_acct)))
00174     acc = cacb->base_acct;
00175   else
00176   {
00177     /* See if we already have an account by that name */
00178     acc = gnc_account_lookup_by_name(cacb->base_acct,
00179                                      gnc_commodity_get_mnemonic(cmdty));
00180 
00181     /* If not, then create one */
00182     if (!acc)
00183     {
00184       acc = xaccMallocAccount(cacb->cbw->book);
00185       xaccAccountBeginEdit(acc);
00186       xaccAccountSetType(acc, ACCT_TYPE_EQUITY);
00187       xaccAccountSetName(acc, gnc_commodity_get_mnemonic(cmdty));
00188       xaccAccountSetDescription(acc, gnc_commodity_get_mnemonic(cmdty));
00189       xaccAccountSetCommodity(acc, cmdty);
00190       gnc_account_append_child(cacb->base_acct, acc);
00191       xaccAccountCommitEdit(acc);
00192     }
00193   }
00194   /* Make sure the account exists and is of the correct commodity */
00195   g_assert(acc);
00196   g_assert(gnc_commodity_equal(cmdty, xaccAccountGetCommodity(acc)));
00197 
00198   /* Create the split for the Equity account to balance out
00199    * all the accounts of this.  Use the "total".
00200    */
00201   split = xaccMallocSplit(cacb->cbw->book);
00202   xaccSplitSetParent(split, txn->txn);
00203   xaccAccountBeginEdit(acc);
00204   xaccAccountInsertSplit(acc, split);
00205   xaccSplitSetBaseValue(split, txn->total, cmdty);
00206   xaccAccountCommitEdit(acc);
00207   xaccTransCommitEdit(txn->txn);
00208 }
00209 
00210 static void close_accounts_of_type(struct CloseBookWindow* cbw,
00211                                    Account* acct,
00212                                    GNCAccountType acct_type)
00213 {
00214   struct CloseAccountsCB cacb;
00215   Account* root_acct;
00216 
00217   g_return_if_fail(cbw);
00218   g_return_if_fail(acct);
00219 
00220   cacb.cbw = cbw;
00221   cacb.base_acct = acct;
00222   cacb.acct_type = acct_type;
00223   cacb.txns = g_hash_table_new_full(g_direct_hash,
00224                                     (GEqualFunc)gnc_commodity_equal,
00225                                     NULL, g_free);
00226 
00227   /* Iterate through all accounts and set up the balancing splits */
00228   root_acct = gnc_book_get_root_account(cbw->book);
00229   gnc_account_foreach_descendant(root_acct, close_accounts_cb, &cacb);
00230 
00231   /* now iterate through the transactions and handle each currency */
00232   cacb.hash_size = g_hash_table_size(cacb.txns);
00233   if (cacb.hash_size)
00234     g_hash_table_foreach(cacb.txns, (GHFunc)finish_txn_cb, &cacb);
00235 
00236   /* Destroy the table, freeing the used memory */
00237   g_hash_table_destroy(cacb.txns);
00238 }
00239 
00240 static void close_handler(gpointer data)
00241 {
00242   GtkWidget *dialog = data;
00243 
00244   gtk_widget_destroy(dialog);
00245 }
00246 
00247 static void destroy_cb(GtkObject *object, gpointer data)
00248 {
00249   struct CloseBookWindow *cbw;
00250 
00251   cbw = g_object_get_data(G_OBJECT(object), "CloseBookWindow");
00252 
00253   if (cbw->component_manager_id) {
00254     gnc_unregister_gui_component(cbw->component_manager_id);
00255     cbw->component_manager_id = 0;
00256   }
00257 }
00258 
00259 
00260 void
00261 gnc_book_close_response_cb(GtkDialog *dialog, gint response, GtkDialog *unused)
00262 {
00263   struct CloseBookWindow* cbw;
00264   Account* income_acct;
00265   Account* expense_acct;
00266 
00267   g_return_if_fail(dialog);
00268 
00269   cbw = g_object_get_data(G_OBJECT(dialog), "CloseBookWindow");
00270   g_return_if_fail(cbw);
00271 
00272   switch (response)
00273   {
00274   case GTK_RESPONSE_HELP:
00275      gnc_gnome_help(HF_HELP, HL_GLOBPREFS);
00276      break;
00277   case GTK_RESPONSE_OK:
00278     cbw->close_date = gnc_date_edit_get_date(GNC_DATE_EDIT(cbw->close_date_widget));
00279     cbw->close_date += (3600 * 12);  /* Add 12 hours to the timestamp */
00280     cbw->desc = gtk_entry_get_text(GTK_ENTRY(cbw->desc_widget));
00281 
00282     income_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->income_acct_widget));
00283     expense_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->expense_acct_widget));
00284 
00285     if (!income_acct)
00286     {
00287       gnc_error_dialog(cbw->dialog, "%s",
00288                        _("Please select an Equity account to hold the total Period Income."));
00289       break;
00290     }
00291 
00292     if (!expense_acct)
00293     {
00294       gnc_error_dialog(cbw->dialog, "%s",
00295                        _("Please select an Equity account to hold the total Period Expense."));
00296       break;
00297     }
00298 
00299     close_accounts_of_type(cbw, income_acct, ACCT_TYPE_INCOME);
00300     close_accounts_of_type(cbw, expense_acct, ACCT_TYPE_EXPENSE);
00301 
00302     /* FALLTHROUGH */ 
00303   default:
00304      gtk_widget_destroy(GTK_WIDGET(dialog));
00305      break;
00306   }
00307 }
00308 
00309 void gnc_ui_close_book (QofBook* book)
00310 {
00311   struct CloseBookWindow *cbw;
00312   GladeXML* xml;
00313   GtkWidget* box;
00314   GList* equity_list = NULL;
00315 
00316   g_return_if_fail(book);
00317 
00318   cbw = g_new0(struct CloseBookWindow, 1);
00319   g_return_if_fail(cbw);
00320   cbw->book = book;
00321 
00322   /* Open the dialog */
00323   xml = gnc_glade_xml_new("dialog-book-close.glade", "Close Book");
00324   cbw->dialog = glade_xml_get_widget(xml, "Close Book");
00325 
00326   /* close date */
00327   box = glade_xml_get_widget(xml, "date_box");
00328   cbw->close_date_widget = gnc_date_edit_new(time(NULL), FALSE, FALSE);
00329   gtk_box_pack_start(GTK_BOX(box), cbw->close_date_widget, TRUE, TRUE, 0);
00330 
00331   /* income acct */
00332   equity_list = g_list_prepend(equity_list, GINT_TO_POINTER(ACCT_TYPE_EQUITY));
00333   box = glade_xml_get_widget(xml, "income_acct_box");
00334   cbw->income_acct_widget = gnc_account_sel_new();
00335   gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->income_acct_widget),
00336                                    equity_list);
00337   gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->income_acct_widget), TRUE);
00338   gtk_box_pack_start(GTK_BOX(box), cbw->income_acct_widget, TRUE, TRUE, 0);
00339 
00340   /* expense acct */
00341   box = glade_xml_get_widget(xml, "expense_acct_box");
00342   cbw->expense_acct_widget = gnc_account_sel_new();
00343   gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->expense_acct_widget),
00344                                    equity_list);
00345   gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->expense_acct_widget), TRUE);
00346   gtk_box_pack_start(GTK_BOX(box), cbw->expense_acct_widget, TRUE, TRUE, 0);
00347 
00348   /* desc */
00349   cbw->desc_widget = glade_xml_get_widget(xml, "desc_entry");
00350 
00351   /* Autoconnect signals */
00352   glade_xml_signal_autoconnect_full(xml, gnc_glade_autoconnect_full_func,
00353                                     cbw->dialog);
00354 
00355   /* Register dialog with component manager */
00356   cbw->component_manager_id =
00357     gnc_register_gui_component(DIALOG_BOOK_CLOSE_CM_CLASS, NULL, close_handler,
00358                                cbw->dialog);
00359   gnc_gui_component_set_session(cbw->component_manager_id,
00360                                 gnc_get_current_session());
00361   g_signal_connect(cbw->dialog, "destroy", G_CALLBACK(destroy_cb), NULL);
00362 
00363   /* Clean up the xml data structure when the dialog is destroyed */
00364   g_object_set_data_full(G_OBJECT(cbw->dialog), "dialog-book-close.glade",
00365                          xml, g_object_unref);
00366   g_object_set_data_full(G_OBJECT(cbw->dialog), "CloseBookWindow", cbw,
00367                          g_free);
00368 
00369   /* Run the dialog */
00370   gtk_widget_show_all(cbw->dialog);
00371 
00372   g_list_free(equity_list);
00373 }
00374 

Generated on Fri Jul 25 05:05:12 2008 for GnuCash by  doxygen 1.5.2