GnuCash  5.6-150-g038405b370+
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 <derek@ihtfp.com> *
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 gnu@gnu.org *
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 "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  xaccTransSetDatePostedSecsNormalized(txn->txn, cacb->cbw->close_date);
107 
108  xaccTransSetDescription(txn->txn, cacb->cbw->desc);
109  xaccTransSetCurrency(txn->txn, cmdty);
110  xaccTransSetIsClosingTxn(txn->txn, TRUE);
111  g_hash_table_insert(cacb->txns, cmdty, txn);
112  }
113 
114  return txn;
115 }
116 
117 /* Make sure that the account is of the correct type.
118  * then make sure the account has a balance as of the closing date.
119  * then get the account commodity and find the appropriate
120  * balancing transaction for that commodity and add this balance
121  * to it.
122  */
123 static void close_accounts_cb(Account *a, gpointer data)
124 {
125  struct CloseAccountsCB* cacb = data;
126  struct CACBTransactionList* txn;
127  gnc_commodity* acct_commodity;
128  Split* split;
129  gnc_numeric bal;
130 
131  g_return_if_fail(a);
132  g_return_if_fail(cacb);
133  g_return_if_fail(cacb->cbw);
134  g_return_if_fail(cacb->txns);
135 
136  if (cacb->acct_type != xaccAccountGetType(a))
137  return;
138 
139  bal = xaccAccountGetBalanceAsOfDate(a, gnc_time64_get_day_end (cacb->cbw->close_date));
140  if (gnc_numeric_zero_p(bal))
141  return;
142 
143  acct_commodity = gnc_account_or_default_currency(a, NULL);
144  g_assert(acct_commodity);
145 
146  txn = find_or_create_txn(cacb, acct_commodity);
147  g_assert(txn);
148 
149  split = xaccMallocSplit(cacb->cbw->book);
150  xaccSplitSetParent(split, txn->txn);
152  xaccAccountInsertSplit(a, split);
153  xaccSplitSetBaseValue(split, gnc_numeric_neg(bal), acct_commodity);
155  txn->total = gnc_numeric_add(txn->total, bal, GNC_DENOM_AUTO,
157 }
158 
159 
160 static void finish_txn_cb(gnc_commodity* cmdty,
161  struct CACBTransactionList* txn,
162  struct CloseAccountsCB* cacb)
163 {
164  Account* acc;
165  Split* split;
166 
167  g_return_if_fail(cmdty);
168  g_return_if_fail(txn);
169  g_return_if_fail(cacb);
170  g_return_if_fail(cacb->hash_size);
171 
172  /* If we only have one currency and the base account uses
173  * that currency, then we can use that account. Otherwise,
174  * create a subaccount for each currency.
175  */
176  if (cacb->hash_size == 1 &&
177  gnc_commodity_equal(cmdty, xaccAccountGetCommodity(cacb->base_acct)))
178  acc = cacb->base_acct;
179  else
180  {
181  /* See if we already have an account by that name */
182  acc = gnc_account_lookup_by_name(cacb->base_acct,
184 
185  /* If not, then create one */
186  if (!acc)
187  {
188  acc = xaccMallocAccount(cacb->cbw->book);
193  xaccAccountSetCommodity(acc, cmdty);
194  gnc_account_append_child(cacb->base_acct, acc);
196  }
197  }
198  /* Make sure the account exists and is of the correct commodity */
199  g_assert(acc);
200  g_assert(gnc_commodity_equal(cmdty, xaccAccountGetCommodity(acc)));
201 
202  /* Create the split for the Equity account to balance out
203  * all the accounts of this. Use the "total".
204  */
205  split = xaccMallocSplit(cacb->cbw->book);
206  xaccSplitSetParent(split, txn->txn);
208  xaccAccountInsertSplit(acc, split);
209  xaccSplitSetBaseValue(split, txn->total, cmdty);
211  xaccTransCommitEdit(txn->txn);
212 }
213 
214 static void close_accounts_of_type(struct CloseBookWindow* cbw,
215  Account* acct,
216  GNCAccountType acct_type)
217 {
218  struct CloseAccountsCB cacb;
219  Account* root_acct;
220 
221  g_return_if_fail(cbw);
222  g_return_if_fail(acct);
223 
224  cacb.cbw = cbw;
225  cacb.base_acct = acct;
226  cacb.acct_type = acct_type;
227  cacb.txns = g_hash_table_new_full(g_direct_hash,
228  (GEqualFunc)gnc_commodity_equal,
229  NULL, g_free);
230 
231  /* Iterate through all accounts and set up the balancing splits */
232  root_acct = gnc_book_get_root_account(cbw->book);
233  gnc_account_foreach_descendant(root_acct, close_accounts_cb, &cacb);
234 
235  /* now iterate through the transactions and handle each currency */
236  cacb.hash_size = g_hash_table_size(cacb.txns);
237  if (cacb.hash_size)
238  g_hash_table_foreach(cacb.txns, (GHFunc)finish_txn_cb, &cacb);
239 
240  /* Destroy the table, freeing the used memory */
241  g_hash_table_destroy(cacb.txns);
242 }
243 
244 static void close_handler(gpointer data)
245 {
246  GtkWidget *dialog = data;
247 
248  gtk_widget_destroy(dialog);
249 }
250 
251 static void destroy_cb(GObject *object, gpointer data)
252 {
253  struct CloseBookWindow *cbw;
254 
255  cbw = g_object_get_data(G_OBJECT(object), "CloseBookWindow");
256 
257  if (cbw->component_manager_id)
258  {
259  gnc_unregister_gui_component(cbw->component_manager_id);
260  cbw->component_manager_id = 0;
261  }
262 }
263 
264 
265 void
266 gnc_book_close_response_cb(GtkDialog *dialog, gint response, GtkDialog *unused)
267 {
268  struct CloseBookWindow* cbw;
269  Account* income_acct;
270  Account* expense_acct;
271 
272  ENTER("dialog %p, response %d, unused %p", dialog, response, unused);
273 
274  g_return_if_fail(dialog);
275 
276  cbw = g_object_get_data(G_OBJECT(dialog), "CloseBookWindow");
277  g_return_if_fail(cbw);
278 
279  switch (response)
280  {
281  case GTK_RESPONSE_HELP:
282  gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_CLOSE_BOOK);
283  break;
284  case GTK_RESPONSE_OK:
285  cbw->close_date = gnc_date_edit_get_date(GNC_DATE_EDIT(cbw->close_date_widget));
286  cbw->desc = gtk_entry_get_text(GTK_ENTRY(cbw->desc_widget));
287 
288  income_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->income_acct_widget));
289  expense_acct = gnc_account_sel_get_account(GNC_ACCOUNT_SEL(cbw->expense_acct_widget));
290 
291  if (!income_acct)
292  {
293  gnc_error_dialog(GTK_WINDOW (cbw->dialog), "%s",
294  _("Please select an Equity account to hold the total Period Income."));
295  break;
296  }
297 
298  if (!expense_acct)
299  {
300  gnc_error_dialog(GTK_WINDOW (cbw->dialog), "%s",
301  _("Please select an Equity account to hold the total Period Expense."));
302  break;
303  }
304 
305  gnc_suspend_gui_refresh();
306  close_accounts_of_type(cbw, income_acct, ACCT_TYPE_INCOME);
307  close_accounts_of_type(cbw, expense_acct, ACCT_TYPE_EXPENSE);
308  gnc_resume_gui_refresh();
309 
310  /* FALL THROUGH */
311  default:
312  gtk_widget_destroy(GTK_WIDGET(dialog));
313  break;
314  }
315  LEAVE("");
316 }
317 
318 void gnc_ui_close_book (QofBook* book, GtkWindow *parent)
319 {
320  struct CloseBookWindow *cbw;
321  GtkBuilder* builder;
322  GtkWidget* box;
323  GList* equity_list = NULL;
324 
325  g_return_if_fail(book);
326 
327  cbw = g_new0(struct CloseBookWindow, 1);
328  g_return_if_fail(cbw);
329  cbw->book = book;
330 
331  /* Open the dialog */
332  builder = gtk_builder_new();
333  gnc_builder_add_from_file (builder, "dialog-book-close.glade", "close_book_dialog");
334  cbw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "close_book_dialog"));
335 
336  // Set the name for this dialog so it can be easily manipulated with css
337  gtk_widget_set_name (GTK_WIDGET(cbw->dialog), "gnc-id-book-close");
338 
339  /* parent */
340  if (parent != NULL)
341  gtk_window_set_transient_for (GTK_WINDOW(cbw->dialog), GTK_WINDOW(parent));
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)
Set the account&#39;s type.
Definition: Account.cpp:2418
Never round at all, and signal an error if there is a fractional result in a computation.
Definition: gnc-numeric.h:177
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Depending on the base_currency, set either the value or the amount of this split or both: If the base...
Definition: Split.cpp:1320
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship, and will then add the account as a child of the new parent.
Definition: Account.cpp:2776
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3197
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:143
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3228
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
This file contains the functions to present a GUI to select a book closing date and accounts into whi...
All arguments are required to have the same denominator, that denominator is to be used in the output...
Definition: gnc-numeric.h:206
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
API for Transactions and Splits (journal entries)
void gnc_ui_close_book(QofBook *book, GtkWindow *parent)
Create and run the dialog to close the book.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
Definition: Account.cpp:3054
void xaccTransSetIsClosingTxn(Transaction *trans, gboolean is_closing)
Sets whether or not this transaction is a "closing transaction".
Account handling public routines.
Income accounts are used to denote income.
Definition: Account.h:140
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Get the balance of the account at the end of the day before the date specified.
Definition: Account.cpp:3553
All type declarations for the whole Gnucash engine.
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1479
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3413
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1048
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition: Account.cpp:1275
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account&#39;s description.
Definition: Account.cpp:2478
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
time64 gnc_time64_get_day_end(time64 time_val)
The gnc_time64_get_day_end() routine will take the given time in seconds and adjust it to the last se...
Definition: gnc-date.cpp:1315
Equity account is used to balance the balance sheet.
Definition: Account.h:146
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1520
void xaccAccountSetName(Account *acc, const char *str)
Set the account&#39;s name.
Definition: Account.cpp:2439
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account&#39;s commodity.
Definition: Account.cpp:2618