GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-book-close.c
1 /********************************************************************\
2  * dialog-book-close.c -- dialog for helping the user close the *
3  * book at the end of the year by adding *
4  * zero-izing splits to all Income and *
5  * Expense accounts *
6  * *
7  * Copyright (C) 2007-8 Derek Atkins <[email protected]> *
8  * *
9  * This program is free software; you can redistribute it and/or *
10  * modify it under the terms of the GNU General Public License as *
11  * published by the Free Software Foundation; either version 2 of *
12  * the License, or (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License*
20  * along with this program; if not, contact: *
21  * *
22  * Free Software Foundation Voice: +1-617-542-5942 *
23  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24  * Boston, MA 02110-1301, USA [email protected] *
25 \********************************************************************/
26 
27 #include "config.h"
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 
32 #include "dialog-utils.h"
33 #include "gnc-engine.h"
34 #include "Transaction.h"
35 #include "Split.h"
36 #include "Account.h"
37 #include "gnc-ui.h"
38 #include "gnc-gui-query.h"
39 #include "dialog-book-close.h"
40 #include "gnc-account-sel.h"
41 #include "gnc-component-manager.h"
42 #include "gnc-date-edit.h"
43 #include "gnc-session.h"
44 #include "app-utils/gnc-ui-util.h"
45 
46 #define DIALOG_BOOK_CLOSE_CM_CLASS "dialog-book-close"
47 
48 void gnc_book_close_response_cb(GtkDialog *, gint, GtkDialog *);
49 
50 /* This static indicates the debugging module that this .o belongs to. */
51 static QofLogModule log_module = GNC_MOD_GUI;
52 
54 {
55  /* Passed in by the creator */
56  QofBook* book;
57 
58  /* Parts of the dialog */
59  GtkWidget* dialog;
60  GtkWidget* close_date_widget;
61  GtkWidget* income_acct_widget;
62  GtkWidget* expense_acct_widget;
63  GtkWidget* desc_widget;
64 
65  /* The final settings */
66  time64 close_date;
67  const char* desc;
68 
69  /* Component registration */
70  gint component_manager_id;
71 };
72 
74 {
75  struct CloseBookWindow* cbw;
76  Account* base_acct;
77  GNCAccountType acct_type;
78  GHashTable* txns;
79  guint hash_size;
80 };
81 
83 {
84  gnc_commodity* cmdty;
85  Transaction* txn;
86  gnc_numeric total;
87 };
88 
89 static struct CACBTransactionList*
90 find_or_create_txn(struct CloseAccountsCB* cacb, gnc_commodity* cmdty)
91 {
92  struct CACBTransactionList* txn;
93 
94  g_return_val_if_fail(cacb, NULL);
95  g_return_val_if_fail(cmdty, NULL);
96 
97  txn = g_hash_table_lookup(cacb->txns, cmdty);
98  if (!txn)
99  {
100  txn = g_new0(struct CACBTransactionList, 1);
101  txn->cmdty = cmdty;
102  txn->total = gnc_numeric_zero();
103  txn->txn = xaccMallocTransaction(cacb->cbw->book);
104  xaccTransBeginEdit(txn->txn);
105  xaccTransSetDateEnteredSecs(txn->txn, gnc_time (NULL));
106 
107  /* Watch out: The book-closing txn currently assume that their
108  posted-date is the end date plus 12 hours, so that the closing txn can
109  be distinguished from normal txns of the last day. This is the only
110  case within GnuCash where the PostedDate is a different time-of-day
111  that what the GDate normally says as a normalized date. */
112  xaccTransSetDatePostedSecs(txn->txn, cacb->cbw->close_date);
113 
114  xaccTransSetDescription(txn->txn, cacb->cbw->desc);
115  xaccTransSetCurrency(txn->txn, cmdty);
116  xaccTransSetIsClosingTxn(txn->txn, TRUE);
117  g_hash_table_insert(cacb->txns, cmdty, txn);
118  }
119 
120  return txn;
121 }
122 
123 /* Make sure that the account is of the correct type.
124  * then make sure the account has a balance as of the closing date.
125  * then get the account commodity and find the appropriate
126  * balancing transaction for that commodity and add this balance
127  * to it.
128  */
129 static void close_accounts_cb(Account *a, gpointer data)
130 {
131  struct CloseAccountsCB* cacb = data;
132  struct CACBTransactionList* txn;
133  gnc_commodity* acct_commodity;
134  Split* split;
135  gnc_numeric bal;
136 
137  g_return_if_fail(a);
138  g_return_if_fail(cacb);
139  g_return_if_fail(cacb->cbw);
140  g_return_if_fail(cacb->txns);
141 
142  if (cacb->acct_type != xaccAccountGetType(a))
143  return;
144 
145  bal = xaccAccountGetBalanceAsOfDate(a, cacb->cbw->close_date + 1);
146  if (gnc_numeric_zero_p(bal))
147  return;
148 
149  acct_commodity = gnc_account_or_default_currency(a, NULL);
150  g_assert(acct_commodity);
151 
152  txn = find_or_create_txn(cacb, acct_commodity);
153  g_assert(txn);
154 
155  split = xaccMallocSplit(cacb->cbw->book);
156  xaccSplitSetParent(split, txn->txn);
158  xaccAccountInsertSplit(a, split);
159  xaccSplitSetBaseValue(split, gnc_numeric_neg(bal), acct_commodity);
161  txn->total = gnc_numeric_add(txn->total, bal, GNC_DENOM_AUTO,
163 }
164 
165 
166 static void finish_txn_cb(gnc_commodity* cmdty,
167  struct CACBTransactionList* txn,
168  struct CloseAccountsCB* cacb)
169 {
170  Account* acc;
171  Split* split;
172 
173  g_return_if_fail(cmdty);
174  g_return_if_fail(txn);
175  g_return_if_fail(cacb);
176  g_return_if_fail(cacb->hash_size);
177 
178  /* If we only have one currency and the base account uses
179  * that currency, then we can use that account. Otherwise,
180  * create a subaccount for each currency.
181  */
182  if (cacb->hash_size == 1 &&
183  gnc_commodity_equal(cmdty, xaccAccountGetCommodity(cacb->base_acct)))
184  acc = cacb->base_acct;
185  else
186  {
187  /* See if we already have an account by that name */
188  acc = gnc_account_lookup_by_name(cacb->base_acct,
190 
191  /* If not, then create one */
192  if (!acc)
193  {
194  acc = xaccMallocAccount(cacb->cbw->book);
199  xaccAccountSetCommodity(acc, cmdty);
200  gnc_account_append_child(cacb->base_acct, acc);
202  }
203  }
204  /* Make sure the account exists and is of the correct commodity */
205  g_assert(acc);
206  g_assert(gnc_commodity_equal(cmdty, xaccAccountGetCommodity(acc)));
207 
208  /* Create the split for the Equity account to balance out
209  * all the accounts of this. Use the "total".
210  */
211  split = xaccMallocSplit(cacb->cbw->book);
212  xaccSplitSetParent(split, txn->txn);
214  xaccAccountInsertSplit(acc, split);
215  xaccSplitSetBaseValue(split, txn->total, cmdty);
217  xaccTransCommitEdit(txn->txn);
218 }
219 
220 static void close_accounts_of_type(struct CloseBookWindow* cbw,
221  Account* acct,
222  GNCAccountType acct_type)
223 {
224  struct CloseAccountsCB cacb;
225  Account* root_acct;
226 
227  g_return_if_fail(cbw);
228  g_return_if_fail(acct);
229 
230  cacb.cbw = cbw;
231  cacb.base_acct = acct;
232  cacb.acct_type = acct_type;
233  cacb.txns = g_hash_table_new_full(g_direct_hash,
234  (GEqualFunc)gnc_commodity_equal,
235  NULL, g_free);
236 
237  /* Iterate through all accounts and set up the balancing splits */
238  root_acct = gnc_book_get_root_account(cbw->book);
239  gnc_account_foreach_descendant(root_acct, close_accounts_cb, &cacb);
240 
241  /* now iterate through the transactions and handle each currency */
242  cacb.hash_size = g_hash_table_size(cacb.txns);
243  if (cacb.hash_size)
244  g_hash_table_foreach(cacb.txns, (GHFunc)finish_txn_cb, &cacb);
245 
246  /* Destroy the table, freeing the used memory */
247  g_hash_table_destroy(cacb.txns);
248 }
249 
250 static void close_handler(gpointer data)
251 {
252  GtkWidget *dialog = data;
253 
254  gtk_widget_destroy(dialog);
255 }
256 
257 static void destroy_cb(GtkObject *object, gpointer data)
258 {
259  struct CloseBookWindow *cbw;
260 
261  cbw = g_object_get_data(G_OBJECT(object), "CloseBookWindow");
262 
263  if (cbw->component_manager_id)
264  {
265  gnc_unregister_gui_component(cbw->component_manager_id);
266  cbw->component_manager_id = 0;
267  }
268 }
269 
270 
271 void
272 gnc_book_close_response_cb(GtkDialog *dialog, gint response, GtkDialog *unused)
273 {
274  struct CloseBookWindow* cbw;
275  Account* income_acct;
276  Account* expense_acct;
277 
278  ENTER("dialog %p, response %d, unused %p", dialog, response, unused);
279 
280  g_return_if_fail(dialog);
281 
282  cbw = g_object_get_data(G_OBJECT(dialog), "CloseBookWindow");
283  g_return_if_fail(cbw);
284 
285  switch (response)
286  {
287  case GTK_RESPONSE_HELP:
288  gnc_gnome_help(HF_HELP, HL_CLOSE_BOOK);
289  break;
290  case GTK_RESPONSE_OK:
291  cbw->close_date = gnc_date_edit_get_date(GNC_DATE_EDIT(cbw->close_date_widget));
292  cbw->close_date += (3600 * 12); /* Add 12 hours to the timestamp */
293  cbw->desc = gtk_entry_get_text(GTK_ENTRY(cbw->desc_widget));
294 
295  income_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->income_acct_widget));
296  expense_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->expense_acct_widget));
297 
298  if (!income_acct)
299  {
300  gnc_error_dialog(cbw->dialog, "%s",
301  _("Please select an Equity account to hold the total Period Income."));
302  break;
303  }
304 
305  if (!expense_acct)
306  {
307  gnc_error_dialog(cbw->dialog, "%s",
308  _("Please select an Equity account to hold the total Period Expense."));
309  break;
310  }
311 
312  gnc_suspend_gui_refresh();
313  close_accounts_of_type(cbw, income_acct, ACCT_TYPE_INCOME);
314  close_accounts_of_type(cbw, expense_acct, ACCT_TYPE_EXPENSE);
315  gnc_resume_gui_refresh();
316 
317  /* FALL THROUGH */
318  default:
319  gtk_widget_destroy(GTK_WIDGET(dialog));
320  break;
321  }
322  LEAVE("");
323 }
324 
326 {
327  struct CloseBookWindow *cbw;
328  GtkBuilder* builder;
329  GtkWidget* box;
330  GList* equity_list = NULL;
331 
332  g_return_if_fail(book);
333 
334  cbw = g_new0(struct CloseBookWindow, 1);
335  g_return_if_fail(cbw);
336  cbw->book = book;
337 
338  /* Open the dialog */
339  builder = gtk_builder_new();
340  gnc_builder_add_from_file (builder, "dialog-book-close.glade", "Close Book");
341  cbw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Close Book"));
342 
343  PINFO("Closed Book Window is %p, Dialog is %p", cbw, cbw->dialog);
344 
345  /* close date */
346  box = GTK_WIDGET(gtk_builder_get_object (builder, "date_box"));
347  cbw->close_date_widget = gnc_date_edit_new(gnc_time (NULL), FALSE, FALSE);
348  gtk_box_pack_start(GTK_BOX(box), cbw->close_date_widget, TRUE, TRUE, 0);
349 
350  /* income acct */
351  equity_list = g_list_prepend(equity_list, GINT_TO_POINTER(ACCT_TYPE_EQUITY));
352  box = GTK_WIDGET(gtk_builder_get_object (builder, "income_acct_box"));
353  cbw->income_acct_widget = gnc_account_sel_new();
354  gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->income_acct_widget),
355  equity_list, NULL);
356  gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->income_acct_widget), TRUE);
357  gtk_box_pack_start(GTK_BOX(box), cbw->income_acct_widget, TRUE, TRUE, 0);
358 
359  /* expense acct */
360  box = GTK_WIDGET(gtk_builder_get_object (builder, "expense_acct_box"));
361  cbw->expense_acct_widget = gnc_account_sel_new();
362  gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(cbw->expense_acct_widget),
363  equity_list, NULL);
364  gnc_account_sel_set_new_account_ability(GNC_ACCOUNT_SEL(cbw->expense_acct_widget), TRUE);
365  gtk_box_pack_start(GTK_BOX(box), cbw->expense_acct_widget, TRUE, TRUE, 0);
366 
367  /* desc */
368  cbw->desc_widget = GTK_WIDGET(gtk_builder_get_object (builder, "desc_entry"));
369 
370  /* Autoconnect signals */
371  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, cbw->dialog);
372 
373  /* Register dialog with component manager */
374  cbw->component_manager_id =
375  gnc_register_gui_component(DIALOG_BOOK_CLOSE_CM_CLASS, NULL, close_handler,
376  cbw->dialog);
377  gnc_gui_component_set_session(cbw->component_manager_id,
378  gnc_get_current_session());
379  g_signal_connect(cbw->dialog, "destroy", G_CALLBACK(destroy_cb), NULL);
380 
381  /* Clean up the data structure when the dialog is destroyed */
382  g_object_set_data_full(G_OBJECT(cbw->dialog), "CloseBookWindow", cbw, g_free);
383 
384  g_object_unref(G_OBJECT(builder));
385 
386  /* Run the dialog */
387  gtk_widget_show_all(cbw->dialog);
388 
389  g_list_free(equity_list);
390 }
391 
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Definition: Account.c:2208
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:513
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Definition: Split.c:1341
void gnc_account_append_child(Account *new_parent, Account *child)
Definition: Account.c:2525
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
Definition: Account.c:2958
utility functions for the GnuCash UI
#define PINFO(format, args...)
Definition: qoflog.h:249
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
gnc_numeric gnc_numeric_neg(gnc_numeric a)
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
void xaccTransSetDescription(Transaction *trans, const char *desc)
Definition: Transaction.c:2085
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gboolean gnc_numeric_zero_p(gnc_numeric a)
API for Transactions and Splits (journal entries)
#define ENTER(format, args...)
Definition: qoflog.h:261
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
Definition: Account.c:2803
void xaccTransSetIsClosingTxn(Transaction *trans, gboolean is_closing)
Definition: Transaction.c:2126
Account handling public routines.
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
void gnc_gnome_help(const char *file_name, const char *anchor)
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Definition: Account.c:3288
All type declarations for the whole Gnucash engine.
GNCAccountType
Definition: Account.h:96
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
Definition: gnc-ui-util.c:944
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
Definition: Transaction.c:1911
Definition: SplitP.h:71
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
#define LEAVE(format, args...)
Definition: qoflog.h:271
Account * xaccMallocAccount(QofBook *book)
Definition: Account.c:1083
time64 gnc_time(time64 *tbuf)
get the current local time
gint64 time64
Definition: gnc-date.h:83
void xaccAccountSetDescription(Account *acc, const char *str)
Definition: Account.c:2268
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Definition: Transaction.c:1951
void gnc_ui_close_book(QofBook *book)
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
void xaccAccountSetName(Account *acc, const char *str)
Definition: Account.c:2229
const gchar * QofLogModule
Definition: qofid.h:89
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Definition: Account.c:2389