GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
window-reconcile.c
1 /********************************************************************\
2  * window-reconcile.c -- the reconcile window *
3  * Copyright (C) 1997 Robin D. Clark *
4  * Copyright (C) 1998-2000 Linas Vepstas *
5  * Copyright (C) 2002 Christian Stimming *
6  * Copyright (C) 2006 David Hampton *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA [email protected] *
24  * *
25  * Author: Rob Clark *
26  * Internet: [email protected] *
27  * Address: 609 8th Street *
28  * Huntington Beach, CA 92648-4632 *
29 \********************************************************************/
30 
31 #include "config.h"
32 
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35 #ifdef __G_IR_SCANNER__
36 #undef __G_IR_SCANNER__
37 #endif
38 #include <gdk/gdkkeysyms.h>
39 
40 #include "Scrub.h"
41 #include "Scrub3.h"
42 #include "dialog-account.h"
43 #include "dialog-transfer.h"
44 #include "dialog-utils.h"
45 #include "gnc-amount-edit.h"
46 #include "gnc-component-manager.h"
47 #include "gnc-date-edit.h"
48 #include "gnc-event.h"
49 #include "gnc-filepath-utils.h"
50 #include <gnc-gdate-utils.h>
51 #include "gnc-gnome-utils.h"
52 #include "gnc-main-window.h"
54 #include "gnc-prefs.h"
55 #include "gnc-ui.h"
56 #include "gnc-ui-balances.h"
57 #include "guile-util.h"
58 #include "reconcile-view.h"
59 #include "window-reconcile.h"
60 
61 #define WINDOW_RECONCILE_CM_CLASS "window-reconcile"
62 #define GNC_PREF_AUTO_INTEREST_TRANSFER "auto-interest-transfer"
63 #define GNC_PREF_AUTO_CC_PAYMENT "auto-cc-payment"
64 #define GNC_PREF_ALWAYS_REC_TO_TODAY "always-reconcile-to-today"
65 
66 
69 {
70  GncGUID account; /* The account that we are reconciling */
71  gnc_numeric new_ending; /* The new ending balance */
72  time64 statement_date; /* The statement date */
73 
74  gint component_id; /* id of component */
75 
76  GtkWidget *window; /* The reconcile window */
77 
78  GtkUIManager *ui_merge;
79  GtkActionGroup *action_group;
80 
81  GtkWidget *starting; /* The starting balance */
82  GtkWidget *ending; /* The ending balance */
83  GtkWidget *recn_date; /* The statement date */
84  GtkWidget *reconciled; /* The reconciled balance */
85  GtkWidget *difference; /* Text field, amount left to reconcile */
86 
87  GtkWidget *total_debit; /* Text field, total debit reconciled */
88  GtkWidget *total_credit; /* Text field, total credit reconciled */
89 
90  GtkWidget *debit; /* Debit matrix show unreconciled debit */
91  GtkWidget *credit; /* Credit matrix, shows credits... */
92 
93  GtkWidget *debit_frame; /* Frame around debit matrix */
94  GtkWidget *credit_frame; /* Frame around credit matrix */
95 
96  gboolean delete_refresh; /* do a refresh upon a window deletion */
97 };
98 
99 
100 /* This structure doesn't contain everything involved in the
101  * startRecnWindow, just pointers that have to be passed in to
102  * callbacks that need more than one piece of data to operate on.
103  * This is also used by the interest transfer dialog code.
104  */
105 typedef struct _startRecnWindowData
106 {
107  Account *account; /* the account being reconciled */
108  GNCAccountType account_type; /* the type of the account */
109 
110  GtkWidget *startRecnWindow; /* the startRecnWindow dialog */
111  GtkWidget *xfer_button; /* the dialog's interest transfer button */
112  GtkWidget *date_value; /* the dialog's ending date field */
113  GNCAmountEdit *end_value; /* the dialog's ending balance amount edit */
114  gnc_numeric original_value; /* the dialog's original ending balance */
115  gboolean user_set_value; /* the user changed the ending value */
116 
117  XferDialog *xferData; /* the interest xfer dialog (if it exists) */
118  gboolean include_children;
119 
120  time64 date; /* the interest xfer reconcile date */
122 
123 
124 /* Note: make sure to update the help text for this in prefs.scm if these
125  * change! These macros define the account types for which an auto interest
126  * xfer dialog could pop up, if the user's preferences allow it.
127  */
128 #define account_type_has_auto_interest_charge(type) (((type) == ACCT_TYPE_CREDIT) || \
129  ((type) == ACCT_TYPE_LIABILITY) ||\
130  ((type) == ACCT_TYPE_PAYABLE))
131 
132 #define account_type_has_auto_interest_payment(type) (((type) == ACCT_TYPE_BANK) || \
133  ((type) == ACCT_TYPE_ASSET) || \
134  ((type) == ACCT_TYPE_MUTUAL) || \
135  ((type) == ACCT_TYPE_RECEIVABLE))
136 
137 #define account_type_has_auto_interest_xfer(type) \
138  ( account_type_has_auto_interest_charge(type) || \
139  account_type_has_auto_interest_payment(type) )
140 
142 static gnc_numeric recnRecalculateBalance (RecnWindow *recnData);
143 
144 static void recn_destroy_cb (GtkWidget *w, gpointer data);
145 static void recn_cancel (RecnWindow *recnData);
146 static gboolean recn_delete_cb (GtkWidget *widget, GdkEvent *event, gpointer data);
147 static gboolean recn_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data);
148 static void recnFinishCB (GtkAction *action, RecnWindow *recnData);
149 static void recnPostponeCB (GtkAction *action, gpointer data);
150 static void recnCancelCB (GtkAction *action, gpointer data);
151 
152 void gnc_start_recn_children_changed (GtkWidget *widget, startRecnWindowData *data);
153 void gnc_start_recn_interest_clicked_cb (GtkButton *button, startRecnWindowData *data);
154 
155 static void gnc_reconcile_window_set_sensitivity (RecnWindow *recnData);
156 static char * gnc_recn_make_window_name (Account *account);
157 static void gnc_recn_set_window_name (RecnWindow *recnData);
158 static gboolean find_by_account (gpointer find_data, gpointer user_data);
159 
160 
162 /* This static indicates the debugging module that this .o belongs to. */
163 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
164 
165 static time64 gnc_reconcile_last_statement_date = 0;
166 
167 
174 static GtkActionEntry recnWindow_actions [];
176 static guint recnWindow_n_actions;
177 
178 /********************************************************************\
179  * recnRefresh *
180  * refreshes the transactions in the reconcile window *
181  * *
182  * Args: account - the account of the reconcile window to refresh *
183  * Return: none *
184 \********************************************************************/
185 static void
186 recnRefresh (RecnWindow *recnData)
187 {
188  if (recnData == NULL)
189  return;
190 
191  gnc_reconcile_view_refresh(GNC_RECONCILE_VIEW(recnData->debit));
192  gnc_reconcile_view_refresh(GNC_RECONCILE_VIEW(recnData->credit));
193 
194  gnc_reconcile_window_set_sensitivity(recnData);
195 
196  gnc_recn_set_window_name(recnData);
197 
198  recnRecalculateBalance(recnData);
199 
200  gtk_widget_queue_resize(recnData->window);
201 }
202 
203 
204 static Account *
205 recn_get_account (RecnWindow *recnData)
206 {
207  if (!recnData)
208  return NULL;
209 
210  return xaccAccountLookup (&recnData->account, gnc_get_current_book ());
211 }
212 
213 
214 /********************************************************************\
215  * recnRecalculateBalance *
216  * refreshes the balances in the reconcile window *
217  * *
218  * Args: recnData -- the reconcile window to refresh *
219  * Return: the difference between the nominal ending balance *
220  * and the 'effective' ending balance. *
221 \********************************************************************/
222 static gnc_numeric
223 recnRecalculateBalance (RecnWindow *recnData)
224 {
225  Account *account;
226  const char *amount;
227  gnc_numeric debit;
228  gnc_numeric credit;
229  gnc_numeric starting;
230  gnc_numeric ending;
231  gnc_numeric reconciled;
232  gnc_numeric diff;
233  GNCPrintAmountInfo print_info;
234  gboolean reverse_balance, include_children;
235  GtkAction *action;
236 
237  account = recn_get_account (recnData);
238  if (!account)
239  return gnc_numeric_zero ();
240 
241  reverse_balance = gnc_reverse_balance(account);
242 
243  /* update the starting balance */
244  include_children = xaccAccountGetReconcileChildrenStatus(account);
245  starting = gnc_ui_account_get_reconciled_balance(account, include_children);
246  print_info = gnc_account_print_info (account, TRUE);
247 
248  /*
249  * Do not reverse the balance here. It messes up the math in the
250  * reconciliation window. Also, the balance should show up as a
251  * positive number in the reconciliation window to match the positive
252  * number that shows in the register window.
253  */
254 
255  amount = xaccPrintAmount(starting, print_info);
256  gnc_set_label_color(recnData->starting, starting);
257  gtk_label_set_text(GTK_LABEL(recnData->starting), amount);
258  if (reverse_balance)
259  starting = gnc_numeric_neg (starting);
260 
261  /* update the statement date */
262  amount = qof_print_date(recnData->statement_date);
263  gtk_label_set_text(GTK_LABEL(recnData->recn_date), amount);
264 
265  /* update the ending balance */
266  ending = recnData->new_ending;
267  if (reverse_balance)
268  ending = gnc_numeric_neg (ending);
269  amount = xaccPrintAmount(ending, print_info);
270  gnc_set_label_color(recnData->ending, ending);
271  gtk_label_set_text(GTK_LABEL(recnData->ending), amount);
272  if (reverse_balance)
273  ending = gnc_numeric_neg (ending);
274 
275  debit = gnc_reconcile_view_reconciled_balance
276  (GNC_RECONCILE_VIEW(recnData->debit));
277 
278  credit = gnc_reconcile_view_reconciled_balance
279  (GNC_RECONCILE_VIEW(recnData->credit));
280 
281  /* Update the total debit and credit fields */
282  amount = xaccPrintAmount(debit, print_info);
283  gtk_label_set_text(GTK_LABEL(recnData->total_debit), amount);
284 
285  amount = xaccPrintAmount(credit, print_info);
286 
287  gtk_label_set_text(GTK_LABEL(recnData->total_credit), amount);
288 
289  /* update the reconciled balance */
290  reconciled = gnc_numeric_add_fixed (starting,
291  gnc_numeric_sub_fixed (debit, credit));
292  if (reverse_balance)
293  reconciled = gnc_numeric_neg (reconciled);
294  amount = xaccPrintAmount(reconciled, print_info);
295  gnc_set_label_color(recnData->reconciled, reconciled);
296  gtk_label_set_text(GTK_LABEL(recnData->reconciled), amount);
297  if (reverse_balance)
298  reconciled = gnc_numeric_neg (reconciled);
299 
300  /* update the difference */
301  diff = gnc_numeric_sub_fixed (ending, reconciled);
302  if (reverse_balance)
303  diff = gnc_numeric_neg (diff);
304  amount = xaccPrintAmount(diff, print_info);
305  gnc_set_label_color(recnData->difference, diff);
306  gtk_label_set_text(GTK_LABEL(recnData->difference), amount);
307  if (reverse_balance)
308  diff = gnc_numeric_neg (diff);
309 
310  action = gtk_action_group_get_action (recnData->action_group,
311  "RecnFinishAction");
312  gtk_action_set_sensitive(action, gnc_numeric_zero_p (diff));
313 
314  action = gtk_action_group_get_action (recnData->action_group,
315  "TransBalanceAction");
316  gtk_action_set_sensitive(action, !gnc_numeric_zero_p (diff));
317 
318  return diff;
319 }
320 
321 
322 static gboolean
323 gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event,
324  startRecnWindowData *data)
325 {
326  gnc_numeric value;
327 
328  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data->end_value));
329 
330  value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(data->end_value));
331  data->user_set_value = !gnc_numeric_equal(value, data->original_value);
332  return FALSE;
333 }
334 
335 
336 /* If the user changed the date edit widget, update the
337  * ending balance to reflect the ending balance of the account
338  * on the date that the date edit was changed to.
339  */
340 static void
341 gnc_start_recn_date_changed (GtkWidget *widget, startRecnWindowData *data)
342 {
343  GNCDateEdit *gde = GNC_DATE_EDIT (widget);
344  gnc_numeric new_balance;
345  time64 new_date;
346 
347  if (data->user_set_value)
348  return;
349  new_date = gnc_date_edit_get_date_end (gde);
350  /* get the balance for the account as of the new date */
351  new_balance = gnc_ui_account_get_balance_as_of_date (data->account, new_date,
352  data->include_children);
353  /* update the amount edit with the amount */
354  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value),
355  new_balance);
356 }
357 
358 
359 void
360 gnc_start_recn_children_changed (GtkWidget *widget, startRecnWindowData *data)
361 {
362  data->include_children =
363  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
364 
365  /* Force an update of the ending balance */
366  gnc_start_recn_date_changed (data->date_value, data);
367 }
368 
369 
370 /* For a given account, determine if an auto interest xfer dialog should be
371  * shown, based on both the per-account flag as well as the global reconcile
372  * option. The global option is the default that is used if there is no
373  * per-account option.
374  */
375 static gboolean
376 gnc_recn_interest_xfer_get_auto_interest_xfer_allowed( Account *account )
377 {
378  gboolean auto_xfer;
379 
380  auto_xfer = gnc_prefs_get_bool(GNC_PREFS_GROUP_RECONCILE, GNC_PREF_AUTO_INTEREST_TRANSFER);
381  return xaccAccountGetAutoInterestXfer( account, auto_xfer );
382 }
383 
384 
385 /********************************************************************\
386  * recnInterestXferWindow *
387  * opens up a window to prompt the user to enter an interest *
388  * charge or payment for an account prior to reconciling it. *
389  * Only to be called for some types of accounts, as defined *
390  * in the macros at the top of this file. *
391  * *
392  * NOTE: This function does not return until the user presses "Ok" *
393  * or "Cancel", which means that the transaction must be *
394  * resolved before the startRecnWindow will work. *
395  * *
396  * Args: data - jumbo structure containing info *
397  * about the start of the reconcile *
398  * process needed by this function. *
399  * Returns: none. *
400 \********************************************************************/
401 
402 /* helper function */
403 static char *
404 gnc_recn_make_interest_window_name(Account *account, char *text)
405 {
406  char *fullname;
407  char *title;
408 
409  fullname = gnc_account_get_full_name(account);
410  title = g_strconcat(fullname, " - ", text && *text ? _(text) : "", NULL);
411 
412  g_free(fullname);
413 
414  return title;
415 }
416 
417 
418 /* user clicked button in the interest xfer dialog entitled
419  * "No Auto Interest Payments for this Account".
420  */
421 static void
422 gnc_recn_interest_xfer_no_auto_clicked_cb(GtkButton *button,
423  startRecnWindowData *data)
424 {
425  /* Indicate that the user doesn't want
426  * an auto interest xfer for this account.
427  */
428  xaccAccountSetAutoInterestXfer( data->account, FALSE );
429 
430  /* shut down the interest xfer dialog */
431  gnc_xfer_dialog_close( data->xferData );
432 
433  /* make the button clickable again */
434  if ( data->xfer_button )
435  gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), TRUE);
436 }
437 
438 
439 static void
440 recnInterestXferWindow( startRecnWindowData *data)
441 {
442  gchar *title;
443 
444  if ( !account_type_has_auto_interest_xfer( data->account_type ) ) return;
445 
446  /* get a normal transfer dialog... */
447  data->xferData = gnc_xfer_dialog( GTK_WIDGET(data->startRecnWindow),
448  data->account );
449 
450  /* ...and start changing things: */
451 
452  /* change title */
453  if ( account_type_has_auto_interest_payment( data->account_type ) )
454  title = gnc_recn_make_interest_window_name( data->account,
455  _("Interest Payment") );
456  else
457  title = gnc_recn_make_interest_window_name( data->account,
458  _("Interest Charge") );
459 
460  gnc_xfer_dialog_set_title( data->xferData, title );
461  g_free( title );
462 
463 
464  /* change frame labels */
465  gnc_xfer_dialog_set_information_label( data->xferData,
466  _("Payment Information") );
467 
468  /* Interest accrued is a transaction from an income account
469  * to a bank account. Interest charged is a transaction from
470  * a credit account to an expense account. The user isn't allowed
471  * to change the account (bank or credit) being reconciled.
472  */
473  if ( account_type_has_auto_interest_payment( data->account_type ) )
474  {
475  gnc_xfer_dialog_set_from_account_label( data->xferData,
476  _("Payment From") );
477  gnc_xfer_dialog_set_from_show_button_active( data->xferData, TRUE );
478 
479  // XXX: Set "from" account from previous interest payment.
480 
481  gnc_xfer_dialog_set_to_account_label( data->xferData,
482  _("Reconcile Account") );
483  gnc_xfer_dialog_select_to_account( data->xferData, data->account );
484  gnc_xfer_dialog_lock_to_account_tree( data->xferData );
485 
486  /* Quickfill based on the reconcile account, which is the "To" acct. */
487  gnc_xfer_dialog_quickfill_to_account( data->xferData, TRUE );
488  }
489  else /* interest charged to account rather than paid to it */
490  {
491  gnc_xfer_dialog_set_from_account_label( data->xferData,
492  _("Reconcile Account") );
493  gnc_xfer_dialog_select_from_account( data->xferData, data->account );
494  gnc_xfer_dialog_lock_from_account_tree( data->xferData );
495 
496  gnc_xfer_dialog_set_to_account_label( data->xferData,
497  _("Payment To") );
498  gnc_xfer_dialog_set_to_show_button_active( data->xferData, TRUE );
499 
500  // XXX: Set "to" account from previous interest payment.
501 
502  /* Quickfill based on the reconcile account, which is the "From" acct. */
503  gnc_xfer_dialog_quickfill_to_account( data->xferData, FALSE );
504  }
505 
506 
507  /* add a button to disable auto interest payments for this account */
508  gnc_xfer_dialog_add_user_specified_button( data->xferData,
509  ( account_type_has_auto_interest_payment( data->account_type ) ?
510  _("No Auto Interest Payments for this Account")
511  : _("No Auto Interest Charges for this Account") ),
512  G_CALLBACK(gnc_recn_interest_xfer_no_auto_clicked_cb),
513  (gpointer) data );
514 
515  /* no currency frame */
516  gnc_xfer_dialog_toggle_currency_table( data->xferData, FALSE );
517 
518  /* set the reconcile date for the transaction date */
519  gnc_xfer_dialog_set_date( data->xferData, data->date );
520 
521  /* Now run the transfer dialog. This blocks until done.
522  * If the user hit Cancel, make the button clickable so that
523  * the user can retry if they want. We don't make the button
524  * clickable if they successfully entered a transaction, since
525  * the fact that the button was clickable again might make
526  * the user think that the transaction didn't actually go through.
527  */
528  if ( ! gnc_xfer_dialog_run_until_done( data->xferData ) )
529  if ( data->xfer_button )
530  gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), TRUE);
531 
532  /* done with the XferDialog */
533  data->xferData = NULL;
534 }
535 
536 
537 /* Set up for the interest xfer window, run the window, and update
538  * the startRecnWindow if the interest xfer changed anything that matters.
539  */
540 static void
541 gnc_reconcile_interest_xfer_run(startRecnWindowData *data)
542 {
543  GtkWidget *entry = gnc_amount_edit_gtk_entry(
544  GNC_AMOUNT_EDIT(data->end_value) );
545  gnc_numeric before = gnc_amount_edit_get_amount(
546  GNC_AMOUNT_EDIT(data->end_value) );
547  gnc_numeric after = gnc_numeric_zero();
548 
549  recnInterestXferWindow( data );
550 
551  /* recompute the ending balance */
552  after = xaccAccountGetBalanceAsOfDate(data->account, data->date);
553 
554  /* update the ending balance in the startRecnWindow if it has changed. */
555  if ( gnc_numeric_compare( before, after ) )
556  {
557  if (gnc_reverse_balance(data->account))
558  after = gnc_numeric_neg (after);
559 
560  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value), after);
561  gtk_widget_grab_focus(GTK_WIDGET(entry));
562  gtk_editable_select_region (GTK_EDITABLE(entry), 0, -1);
563  data->original_value = after;
564  data->user_set_value = FALSE;
565  }
566 }
567 
568 
569 void
570 gnc_start_recn_interest_clicked_cb(GtkButton *button, startRecnWindowData *data)
571 {
572  /* indicate in account that user wants
573  * an auto interest xfer for this account */
574  xaccAccountSetAutoInterestXfer( data->account, TRUE );
575 
576  /* make the button unclickable since we're popping up the window */
577  if ( data->xfer_button )
578  gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), FALSE);
579 
580  /* run the account window */
581  gnc_reconcile_interest_xfer_run( data );
582 }
583 
584 
585 static void
586 gnc_save_reconcile_interval(Account *account, time64 statement_date)
587 {
588  time64 prev_statement_date;
589  int days = 0, months = 0;
590  double seconds;
591 
592  if (!xaccAccountGetReconcileLastDate (account, &prev_statement_date))
593  return;
594 
595  /*
596  * Compute the number of days difference.
597  */
598  seconds = gnc_difftime (statement_date, prev_statement_date);
599  days = (int)(seconds / 60 / 60 / 24);
600 
601  /*
602  * See if we need to remember days(weeks) or months. The only trick
603  * value is 28 days which could be wither 4 weeks or 1 month.
604  */
605  if (days == 28)
606  {
607  int prev_days = 0, prev_months = 1;
608 
609  /* What was it last time? */
610  xaccAccountGetReconcileLastInterval (account, &prev_months, &prev_days);
611  if (prev_months == 1)
612  {
613  months = 1;
614  days = 0;
615  }
616  }
617  else if (days > 28)
618  {
619  struct tm current, prev;
620 
621  gnc_localtime_r (&statement_date, &current);
622  gnc_localtime_r (&prev_statement_date, &prev);
623  months = ((12 * current.tm_year + current.tm_mon) -
624  (12 * prev.tm_year + prev.tm_mon));
625  days = 0;
626  }
627 
628  /*
629  * Remember for next time unless it is negative.
630  */
631  if (months >= 0 && days >= 0)
632  xaccAccountSetReconcileLastInterval(account, months, days);
633 }
634 
635 
636 /********************************************************************\
637  * startRecnWindow *
638  * opens up the window to prompt the user to enter the ending *
639  * balance from bank statement *
640  * *
641  * NOTE: This function does not return until the user presses "Ok" *
642  * or "Cancel" *
643  * *
644  * Args: parent - the parent of this window *
645  * account - the account to reconcile *
646  * new_ending - returns the amount for ending balance *
647  * statement_date - returns date of the statement :) *
648  * Return: True, if the user presses "Ok", else False *
649 \********************************************************************/
650 static gboolean
651 startRecnWindow(GtkWidget *parent, Account *account,
652  gnc_numeric *new_ending, time64 *statement_date,
653  gboolean enable_subaccount)
654 {
655  GtkWidget *dialog, *end_value, *date_value, *include_children_button;
656  GtkBuilder *builder;
657  startRecnWindowData data = { NULL };
658  gboolean auto_interest_xfer_option;
659  GNCPrintAmountInfo print_info;
660  gnc_numeric ending;
661  char *title;
662  int result;
663 
664  /* Initialize the data structure that will be used for several callbacks
665  * throughout this file with the relevant info. Some initialization is
666  * done below as well. Note that local storage should be OK for this,
667  * since any callbacks using it will only work while the startRecnWindow
668  * is running.
669  */
670  data.account = account;
671  data.account_type = xaccAccountGetType(account);
672  data.date = *statement_date;
673 
674  /* whether to have an automatic interest xfer dialog or not */
675  auto_interest_xfer_option =
676  gnc_recn_interest_xfer_get_auto_interest_xfer_allowed( account );
677 
678  data.include_children = xaccAccountGetReconcileChildrenStatus(account);
679 
680  ending = gnc_ui_account_get_reconciled_balance(account,
681  data.include_children);
682  print_info = gnc_account_print_info (account, TRUE);
683 
684  /*
685  * Do not reverse the balance here. It messes up the math in the
686  * reconciliation window. Also, the balance should show up as a
687  * positive number in the reconciliation window to match the positive
688  * number that shows in the register window.
689  */
690 
691  /* Create the dialog box */
692  builder = gtk_builder_new();
693  gnc_builder_add_from_file (builder, "window-reconcile.glade", "Reconcile Start Dialog");
694 
695  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Reconcile Start Dialog"));
696 
697  title = gnc_recn_make_window_name (account);
698  gtk_window_set_title(GTK_WINDOW(dialog), title);
699  g_free (title);
700 
701  data.startRecnWindow = GTK_WIDGET(dialog);
702 
703  if (parent != NULL)
704  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
705 
706  {
707  GtkWidget *start_value, *box;
708  GtkWidget *entry, *label;
709  GtkWidget *interest = NULL;
710 
711  start_value = GTK_WIDGET(gtk_builder_get_object (builder, "start_value"));
712  gtk_label_set_text(GTK_LABEL(start_value), xaccPrintAmount (ending, print_info));
713 
714  include_children_button = GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_check"));
715  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(include_children_button),
716  data.include_children);
717  gtk_widget_set_sensitive(include_children_button, enable_subaccount);
718 
719  date_value = gnc_date_edit_new(*statement_date, FALSE, FALSE);
720  data.date_value = date_value;
721  box = GTK_WIDGET(gtk_builder_get_object (builder, "date_value_box"));
722  gtk_box_pack_start(GTK_BOX(box), date_value, TRUE, TRUE, 0);
723  label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
724  gnc_date_make_mnemonic_target(GNC_DATE_EDIT(date_value), label);
725 
726  end_value = gnc_amount_edit_new ();
727  data.end_value = GNC_AMOUNT_EDIT(end_value);
728  data.original_value = *new_ending;
729  data.user_set_value = FALSE;
730  box = GTK_WIDGET(gtk_builder_get_object (builder, "ending_value_box"));
731  gtk_box_pack_start(GTK_BOX(box), end_value, TRUE, TRUE, 0);
732  label = GTK_WIDGET(gtk_builder_get_object (builder, "end_label"));
733  gtk_label_set_mnemonic_widget(GTK_LABEL(label), end_value);
734 
735  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, &data);
736 
737  gnc_date_activates_default(GNC_DATE_EDIT(date_value), TRUE);
738 
739  /* need to get a callback on date changes to update the recn balance */
740  g_signal_connect ( G_OBJECT (date_value), "date_changed",
741  G_CALLBACK (gnc_start_recn_date_changed), (gpointer) &data );
742 
743  print_info.use_symbol = 0;
744  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (end_value), print_info);
745  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (end_value),
746  xaccAccountGetCommoditySCU (account));
747 
748  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (end_value), *new_ending);
749 
750  entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (end_value));
751  gtk_editable_select_region (GTK_EDITABLE(entry), 0, -1);
752  g_signal_connect(G_OBJECT(entry), "focus-out-event",
753  G_CALLBACK(gnc_start_recn_update_cb), (gpointer) &data);
754  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
755 
756  /* if it's possible to enter an interest payment or charge for this
757  * account, add a button so that the user can pop up the appropriate
758  * dialog if it isn't automatically popping up.
759  */
760  interest = GTK_WIDGET(gtk_builder_get_object (builder, "interest_button"));
761  if ( account_type_has_auto_interest_payment( data.account_type ) )
762  gtk_button_set_label(GTK_BUTTON(interest), _("Enter _Interest Payment...") );
763  else if ( account_type_has_auto_interest_charge( data.account_type ) )
764  gtk_button_set_label(GTK_BUTTON(interest), _("Enter _Interest Charge...") );
765  else
766  {
767  gtk_widget_destroy(interest);
768  interest = NULL;
769  }
770 
771  if ( interest )
772  {
773  data.xfer_button = interest;
774  if ( auto_interest_xfer_option )
775  gtk_widget_set_sensitive(GTK_WIDGET(interest), FALSE);
776  }
777 
778  gtk_widget_show_all(dialog);
779 
780  gtk_widget_grab_focus(gnc_amount_edit_gtk_entry
781  (GNC_AMOUNT_EDIT (end_value)));
782  }
783 
784  /* Allow the user to enter an interest payment
785  * or charge prior to reconciling */
786  if ( account_type_has_auto_interest_xfer( data.account_type )
787  && auto_interest_xfer_option )
788  {
789  gnc_reconcile_interest_xfer_run( &data );
790  }
791 
792  result = gtk_dialog_run(GTK_DIALOG(dialog));
793  if (result == GTK_RESPONSE_OK)
794  {
795  *new_ending = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (end_value));
796  *statement_date = gnc_date_edit_get_date_end(GNC_DATE_EDIT(date_value));
797 
798  if (gnc_reverse_balance(account))
799  *new_ending = gnc_numeric_neg (*new_ending);
800 
801  xaccAccountSetReconcileChildrenStatus(account, data.include_children);
802 
803  gnc_save_reconcile_interval(account, *statement_date);
804  }
805  gtk_widget_destroy (dialog);
806  g_object_unref(G_OBJECT(builder));
807 
808  return (result == GTK_RESPONSE_OK);
809 }
810 
811 
812 static void
813 gnc_reconcile_window_set_sensitivity(RecnWindow *recnData)
814 {
815  gboolean sensitive = FALSE;
816  GNCReconcileView *view;
817  GtkAction *action;
818 
819  view = GNC_RECONCILE_VIEW(recnData->debit);
820  if (gnc_reconcile_view_num_selected(view) == 1)
821  sensitive = TRUE;
822 
823  view = GNC_RECONCILE_VIEW(recnData->credit);
824  if (gnc_reconcile_view_num_selected(view) == 1)
825  sensitive = TRUE;
826 
827  action = gtk_action_group_get_action (recnData->action_group,
828  "TransEditAction");
829  gtk_action_set_sensitive(action, sensitive);
830  action = gtk_action_group_get_action (recnData->action_group,
831  "TransDeleteAction");
832  gtk_action_set_sensitive(action, sensitive);
833 
834  sensitive = FALSE;
835 
836  view = GNC_RECONCILE_VIEW(recnData->debit);
837  if (gnc_reconcile_view_num_selected(view) > 0)
838  sensitive = TRUE;
839 
840  view = GNC_RECONCILE_VIEW(recnData->credit);
841  if (gnc_reconcile_view_num_selected(view) > 0)
842  sensitive = TRUE;
843 
844  action = gtk_action_group_get_action (recnData->action_group,
845  "TransRecAction");
846  gtk_action_set_sensitive(action, sensitive);
847  action = gtk_action_group_get_action (recnData->action_group,
848  "TransUnRecAction");
849  gtk_action_set_sensitive(action, sensitive);
850 }
851 
852 
853 static void
854 gnc_reconcile_window_toggled_cb(GNCReconcileView *view, Split *split,
855  gpointer data)
856 {
857  RecnWindow *recnData = data;
858  gnc_reconcile_window_set_sensitivity(recnData);
859  recnRecalculateBalance(recnData);
860 }
861 
862 
863 static void
864 gnc_reconcile_window_row_cb(GNCReconcileView *view, gpointer item,
865  gpointer data)
866 {
867  RecnWindow *recnData = data;
868  gnc_reconcile_window_set_sensitivity(recnData);
869 }
870 
871 
884 static void
885 do_popup_menu(RecnWindow *recnData, GdkEventButton *event)
886 {
887  GtkWidget *menu;
888  int button, event_time;
889 
890  menu = gtk_ui_manager_get_widget(recnData->ui_merge, "/MainPopup");
891  if (!menu)
892  {
893  return;
894  }
895 
896  if (event)
897  {
898  button = event->button;
899  event_time = event->time;
900  }
901  else
902  {
903  button = 0;
904  event_time = gtk_get_current_event_time ();
905  }
906 
907  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
908 }
909 
910 
924 static gboolean
925 gnc_reconcile_window_popup_menu_cb (GtkWidget *widget,
926  RecnWindow *recnData)
927 {
928  do_popup_menu(recnData, NULL);
929  return TRUE;
930 }
931 
932 
933 /* Callback function invoked when the user clicks in the content of
934  * any Gnucash window. If this was a "right-click" then Gnucash will
935  * popup the contextual menu.
936  */
937 static gboolean
938 gnc_reconcile_window_button_press_cb (GtkWidget *widget,
939  GdkEventButton *event,
940  RecnWindow *recnData)
941 {
942  GNCQueryView *qview = GNC_QUERY_VIEW(widget);
943  GtkTreeModel *model;
944  GtkTreeSelection *selection;
945  GtkTreePath *path;
946 
947  model = gtk_tree_view_get_model(GTK_TREE_VIEW(qview));
948 
949  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
950  {
951 
952  /* Get tree path for row that was clicked */
953  gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(qview),
954  (gint) event->x,
955  (gint) event->y,
956  &path, NULL, NULL, NULL);
957 
958  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(qview));
959  gtk_tree_selection_select_path(selection, path);
960  gtk_tree_path_free(path);
961  do_popup_menu(recnData, event);
962  return TRUE;
963  }
964  return FALSE;
965 }
966 
967 
968 static GNCSplitReg *
969 gnc_reconcile_window_open_register(RecnWindow *recnData)
970 {
971  Account *account = recn_get_account (recnData);
972  GncPluginPage *page;
973  GNCSplitReg *gsr;
974  gboolean include_children;
975 
976  if (!account)
977  return(NULL);
978 
979  include_children = xaccAccountGetReconcileChildrenStatus (account);
980  page = gnc_plugin_page_register_new (account, include_children);
981  gnc_main_window_open_page (NULL, page);
983  gnc_split_reg_raise(gsr);
984  return gsr;
985 }
986 
987 
988 static void
989 gnc_reconcile_window_double_click_cb(GNCReconcileView *view, Split *split,
990  gpointer data)
991 {
992  RecnWindow *recnData = data;
993  GNCSplitReg *gsr;
994 
995  /* This should never be true, but be paranoid */
996  if (split == NULL)
997  return;
998 
999  gsr = gnc_reconcile_window_open_register(recnData);
1000  if (gsr == NULL)
1001  return;
1002  gnc_split_reg_jump_to_split( gsr, split );
1003 }
1004 
1005 
1006 static void
1007 gnc_reconcile_window_focus_cb(GtkWidget *widget, GdkEventFocus *event,
1008  gpointer data)
1009 {
1010  RecnWindow *recnData = data;
1011  GNCReconcileView *this_view, *other_view;
1012  GNCReconcileView *debit, *credit;
1013 
1014  this_view = GNC_RECONCILE_VIEW(widget);
1015 
1016  debit = GNC_RECONCILE_VIEW(recnData->debit);
1017  credit = GNC_RECONCILE_VIEW(recnData->credit);
1018 
1019  other_view = GNC_RECONCILE_VIEW(this_view == debit ? credit : debit);
1020 
1021  /* clear the *other* list so we always have no more than one selection */
1022  gnc_reconcile_view_unselect_all(other_view);
1023 }
1024 
1025 
1026 static gboolean
1027 gnc_reconcile_key_press_cb (GtkWidget *widget, GdkEventKey *event,
1028  gpointer data)
1029 {
1030  RecnWindow *recnData = data;
1031  GtkWidget *this_view, *other_view;
1032  GtkWidget *debit, *credit;
1033 
1034  switch (event->keyval)
1035  {
1036  case GDK_Tab:
1037  case GDK_ISO_Left_Tab:
1038  break;
1039 
1040  default:
1041  return FALSE;
1042  }
1043 
1044  g_signal_stop_emission_by_name (widget, "key_press_event");
1045 
1046  this_view = widget;
1047 
1048  debit = recnData->debit;
1049  credit = recnData->credit;
1050 
1051  other_view = (this_view == debit ? credit : debit);
1052 
1053  gtk_widget_grab_focus (other_view);
1054 
1055  return TRUE;
1056 }
1057 
1058 
1059 static void
1060 gnc_reconcile_window_set_titles(RecnWindow *recnData)
1061 {
1062  gboolean formal;
1063  gchar *title;
1064 
1065  formal = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS);
1066 
1067  if (formal)
1068  title = _("Debits");
1069  else
1070  title = gnc_get_debit_string(ACCT_TYPE_NONE);
1071 
1072  gtk_frame_set_label(GTK_FRAME(recnData->debit_frame), title);
1073 
1074  if (!formal)
1075  g_free(title);
1076 
1077  if (formal)
1078  title = _("Credits");
1079  else
1080  title = gnc_get_credit_string(ACCT_TYPE_NONE);
1081 
1082  gtk_frame_set_label(GTK_FRAME(recnData->credit_frame), title);
1083 
1084  if (!formal)
1085  g_free(title);
1086 }
1087 
1088 
1089 static GtkWidget *
1090 gnc_reconcile_window_create_view_box(Account *account,
1091  GNCReconcileViewType type,
1092  RecnWindow *recnData,
1093  GtkWidget **list_save,
1094  GtkWidget **total_save)
1095 {
1096  GtkWidget *frame, *scrollWin, *view, *vbox, *label, *hbox;
1097 
1098  frame = gtk_frame_new(NULL);
1099 
1100  if (type == RECLIST_DEBIT)
1101  recnData->debit_frame = frame;
1102  else
1103  recnData->credit_frame = frame;
1104 
1105  vbox = gtk_vbox_new(FALSE, 5);
1106 
1107  view = gnc_reconcile_view_new(account, type, recnData->statement_date);
1108  *list_save = view;
1109 
1110  g_signal_connect(view, "toggle_reconciled",
1111  G_CALLBACK(gnc_reconcile_window_toggled_cb),
1112  recnData);
1113  g_signal_connect(view, "line_selected",
1114  G_CALLBACK(gnc_reconcile_window_row_cb),
1115  recnData);
1116  g_signal_connect(view, "button_press_event",
1117  G_CALLBACK(gnc_reconcile_window_button_press_cb),
1118  recnData);
1119  g_signal_connect(view, "double_click_split",
1120  G_CALLBACK(gnc_reconcile_window_double_click_cb),
1121  recnData);
1122  g_signal_connect(view, "focus_in_event",
1123  G_CALLBACK(gnc_reconcile_window_focus_cb),
1124  recnData);
1125  g_signal_connect(view, "key_press_event",
1126  G_CALLBACK(gnc_reconcile_key_press_cb),
1127  recnData);
1128 
1129  scrollWin = gtk_scrolled_window_new (NULL, NULL);
1130  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrollWin),
1131  GTK_POLICY_AUTOMATIC,
1132  GTK_POLICY_AUTOMATIC);
1133  gtk_container_set_border_width(GTK_CONTAINER(scrollWin), 5);
1134 
1135  gtk_container_add(GTK_CONTAINER(frame), scrollWin);
1136  gtk_container_add(GTK_CONTAINER(scrollWin), view);
1137  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
1138 
1139  hbox = gtk_hbox_new(FALSE, 5);
1140  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1141 
1142  label = gtk_label_new(_("Total:"));
1143  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1144  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
1145 
1146  label = gtk_label_new("");
1147  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1148  *total_save = label;
1149 
1150  return vbox;
1151 }
1152 
1153 
1154 static Split *
1155 gnc_reconcile_window_get_current_split(RecnWindow *recnData)
1156 {
1157  GNCReconcileView *view;
1158  Split *split;
1159 
1160  view = GNC_RECONCILE_VIEW(recnData->debit);
1161  split = gnc_reconcile_view_get_current_split(view);
1162  if (split != NULL)
1163  return split;
1164 
1165  view = GNC_RECONCILE_VIEW(recnData->credit);
1166  split = gnc_reconcile_view_get_current_split(view);
1167 
1168  return split;
1169 }
1170 
1171 
1172 static void
1173 gnc_ui_reconcile_window_help_cb(GtkWidget *widget, gpointer data)
1174 {
1175  gnc_gnome_help(HF_HELP, HL_RECNWIN);
1176 }
1177 
1178 
1179 static void
1180 gnc_ui_reconcile_window_change_cb(GtkAction *action, gpointer data)
1181 {
1182  RecnWindow *recnData = data;
1183  Account *account = recn_get_account (recnData);
1184  gnc_numeric new_ending = recnData->new_ending;
1185  time64 statement_date = recnData->statement_date;
1186 
1187  if (gnc_reverse_balance (account))
1188  new_ending = gnc_numeric_neg (new_ending);
1189  if (startRecnWindow (recnData->window, account, &new_ending, &statement_date,
1190  FALSE))
1191  {
1192  recnData->new_ending = new_ending;
1193  recnData->statement_date = statement_date;
1194  recnRecalculateBalance (recnData);
1195  }
1196 }
1197 
1198 
1199 static void
1200 gnc_ui_reconcile_window_balance_cb(GtkButton *button, gpointer data)
1201 {
1202  RecnWindow *recnData = data;
1203  GNCSplitReg *gsr;
1204  Account *account;
1205  gnc_numeric balancing_amount;
1206  time64 statement_date;
1207 
1208 
1209  gsr = gnc_reconcile_window_open_register(recnData);
1210  if (gsr == NULL)
1211  return;
1212 
1213  account = recn_get_account(recnData);
1214  if (account == NULL)
1215  return;
1216 
1217  balancing_amount = recnRecalculateBalance(recnData);
1218  if (gnc_numeric_zero_p(balancing_amount))
1219  return;
1220 
1221  statement_date = recnData->statement_date;
1222  if (statement_date == 0)
1223  statement_date = gnc_time (NULL); // default to 'now'
1224 
1225  gnc_split_reg_balancing_entry(gsr, account, statement_date, balancing_amount);
1226 }
1227 
1228 
1229 static void
1230 gnc_ui_reconcile_window_rec_cb(GtkButton *button, gpointer data)
1231 {
1232  RecnWindow *recnData = data;
1233  GNCReconcileView *debit, *credit;
1234 
1235  debit = GNC_RECONCILE_VIEW(recnData->debit);
1236  credit = GNC_RECONCILE_VIEW(recnData->credit);
1237 
1238  gnc_reconcile_view_set_list (debit, TRUE);
1239  gnc_reconcile_view_set_list (credit, TRUE);
1240 }
1241 
1242 
1243 static void
1244 gnc_ui_reconcile_window_unrec_cb(GtkButton *button, gpointer data)
1245 {
1246  RecnWindow *recnData = data;
1247  GNCReconcileView *debit, *credit;
1248 
1249  debit = GNC_RECONCILE_VIEW(recnData->debit);
1250  credit = GNC_RECONCILE_VIEW(recnData->credit);
1251 
1252  gnc_reconcile_view_set_list (debit, FALSE);
1253  gnc_reconcile_view_set_list (credit, FALSE);
1254 }
1255 
1256 
1257 static void
1258 gnc_ui_reconcile_window_delete_cb(GtkButton *button, gpointer data)
1259 {
1260  RecnWindow *recnData = data;
1261  Transaction *trans;
1262  Split *split;
1263 
1264  split = gnc_reconcile_window_get_current_split(recnData);
1265  /* This should never be true, but be paranoid */
1266  if (split == NULL)
1267  return;
1268 
1269  {
1270  const char *message = _("Are you sure you want to delete the selected "
1271  "transaction?");
1272  gboolean result;
1273 
1274  result = gnc_verify_dialog(recnData->window, FALSE, "%s", message);
1275 
1276  if (!result)
1277  return;
1278  }
1279 
1280  gnc_suspend_gui_refresh ();
1281 
1282  trans = xaccSplitGetParent(split);
1283  xaccTransDestroy(trans);
1284 
1285  gnc_resume_gui_refresh ();
1286 }
1287 
1288 
1289 static void
1290 gnc_ui_reconcile_window_edit_cb(GtkButton *button, gpointer data)
1291 {
1292  RecnWindow *recnData = data;
1293  GNCSplitReg *gsr;
1294  Split *split;
1295 
1296  split = gnc_reconcile_window_get_current_split (recnData);
1297  /* This should never be true, but be paranoid */
1298  if (split == NULL)
1299  return;
1300 
1301  gsr = gnc_reconcile_window_open_register(recnData);
1302  if (gsr == NULL)
1303  return;
1304  gnc_split_reg_jump_to_split_amount( gsr, split );
1305 }
1306 
1307 
1308 static char *
1309 gnc_recn_make_window_name(Account *account)
1310 {
1311  char *fullname;
1312  char *title;
1313 
1314  fullname = gnc_account_get_full_name(account);
1315  title = g_strconcat(fullname, " - ", _("Reconcile"), NULL);
1316 
1317  g_free(fullname);
1318 
1319  return title;
1320 }
1321 
1322 
1323 static void
1324 gnc_recn_set_window_name(RecnWindow *recnData)
1325 {
1326  char *title;
1327 
1328  title = gnc_recn_make_window_name (recn_get_account (recnData));
1329 
1330  gtk_window_set_title (GTK_WINDOW (recnData->window), title);
1331 
1332  g_free (title);
1333 }
1334 
1335 
1336 static void
1337 gnc_recn_edit_account_cb(GtkAction *action, gpointer data)
1338 {
1339  RecnWindow *recnData = data;
1340  Account *account = recn_get_account (recnData);
1341 
1342  if (account == NULL)
1343  return;
1344 
1345  gnc_ui_edit_account_window (account);
1346 }
1347 
1348 
1349 static void
1350 gnc_recn_xfer_cb(GtkAction *action, gpointer data)
1351 {
1352  RecnWindow *recnData = data;
1353  Account *account = recn_get_account (recnData);
1354 
1355  if (account == NULL)
1356  return;
1357 
1358  gnc_xfer_dialog (recnData->window, account);
1359 }
1360 
1361 
1362 static void
1363 gnc_recn_scrub_cb(GtkAction *action, gpointer data)
1364 {
1365  RecnWindow *recnData = data;
1366  Account *account = recn_get_account (recnData);
1367 
1368  if (account == NULL)
1369  return;
1370 
1371  gnc_suspend_gui_refresh ();
1372 
1373  xaccAccountTreeScrubOrphans (account);
1374  xaccAccountTreeScrubImbalance (account);
1375 
1376  // XXX: Lots are disabled.
1377  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1378  xaccAccountTreeScrubLots(account);
1379 
1380  gnc_resume_gui_refresh ();
1381 }
1382 
1383 
1384 static void
1385 gnc_recn_open_cb(GtkAction *action, gpointer data)
1386 {
1387  RecnWindow *recnData = data;
1388 
1389  gnc_reconcile_window_open_register(recnData);
1390 }
1391 
1392 
1393 static void
1394 gnc_get_reconcile_info (Account *account,
1395  gnc_numeric *new_ending,
1396  time64 *statement_date)
1397 {
1398  gboolean always_today;
1399  GDate date;
1400  time64 today;
1401  struct tm tm;
1402 
1403  g_date_clear(&date, 1);
1404 
1405  always_today = gnc_prefs_get_bool(GNC_PREFS_GROUP_RECONCILE, GNC_PREF_ALWAYS_REC_TO_TODAY);
1406 
1407  if (!always_today &&
1408  xaccAccountGetReconcileLastDate (account, statement_date))
1409  {
1410  int months = 1, days = 0;
1411 
1412  gnc_gdate_set_time64(&date, *statement_date);
1413 
1414  xaccAccountGetReconcileLastInterval (account, &months, &days);
1415 
1416  if (months)
1417  {
1418  gboolean was_last_day_of_month = g_date_is_last_of_month(&date);
1419 
1420  g_date_add_months(&date, months);
1421 
1422  /* Track last day of the month, i.e. 1/31 -> 2/28 -> 3/31 */
1423  if (was_last_day_of_month)
1424  {
1425  g_date_set_day (&date, g_date_get_days_in_month(g_date_get_month(&date),
1426  g_date_get_year( &date)));
1427  }
1428  }
1429  else
1430  {
1431  g_date_add_days (&date, days);
1432  }
1433 
1434  *statement_date = gnc_time64_get_day_end_gdate (&date);
1435 
1436  today = gnc_time64_get_day_end (gnc_time (NULL));
1437  if (*statement_date > today)
1438  *statement_date = today;
1439  }
1440 
1441  xaccAccountGetReconcilePostponeDate (account, statement_date);
1442 
1443  if (xaccAccountGetReconcilePostponeBalance(account, new_ending))
1444  {
1445  if (gnc_reverse_balance(account))
1446  *new_ending = gnc_numeric_neg(*new_ending);
1447  }
1448  else
1449  {
1450  /* if the account wasn't previously postponed, try to predict
1451  * the statement balance based on the statement date.
1452  */
1453  *new_ending =
1454  gnc_ui_account_get_balance_as_of_date
1455  (account, *statement_date,
1457  }
1458 }
1459 
1460 
1461 static gboolean
1462 find_by_account (gpointer find_data, gpointer user_data)
1463 {
1464  Account *account = find_data;
1465  RecnWindow *recnData = user_data;
1466 
1467  if (!recnData)
1468  return FALSE;
1469 
1470  return guid_equal (&recnData->account, xaccAccountGetGUID (account));
1471 }
1472 
1473 
1474 static void
1475 recn_set_watches_one_account (gpointer data, gpointer user_data)
1476 {
1477  Account *account = (Account *)data;
1478  RecnWindow *recnData = (RecnWindow *)user_data;
1479  GList *node;
1480 
1481  for (node = xaccAccountGetSplitList (account); node; node = node->next)
1482  {
1483  Split *split = node->data;
1484  Transaction *trans;
1485  char recn;
1486 
1487  recn = xaccSplitGetReconcile (split);
1488  switch (recn)
1489  {
1490  case NREC:
1491  case CREC:
1492  trans = xaccSplitGetParent (split);
1493 
1494  gnc_gui_component_watch_entity (recnData->component_id,
1495  xaccTransGetGUID (trans),
1496  QOF_EVENT_MODIFY
1497  | QOF_EVENT_DESTROY
1498  | GNC_EVENT_ITEM_CHANGED);
1499  break;
1500 
1501  default:
1502  break;
1503  }
1504  }
1505 }
1506 
1507 
1508 static void
1509 recn_set_watches (RecnWindow *recnData)
1510 {
1511  gboolean include_children;
1512  Account *account;
1513  GList *accounts = NULL;
1514 
1515  gnc_gui_component_clear_watches (recnData->component_id);
1516 
1517  gnc_gui_component_watch_entity (recnData->component_id,
1518  &recnData->account,
1519  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
1520 
1521  account = recn_get_account (recnData);
1522 
1523  include_children = xaccAccountGetReconcileChildrenStatus(account);
1524  if (include_children)
1525  accounts = gnc_account_get_descendants(account);
1526 
1527  /* match the account */
1528  accounts = g_list_prepend (accounts, account);
1529 
1530  g_list_foreach(accounts, recn_set_watches_one_account, recnData);
1531 
1532  g_list_free (accounts);
1533 }
1534 
1535 
1536 static void
1537 refresh_handler (GHashTable *changes, gpointer user_data)
1538 {
1539  RecnWindow *recnData = user_data;
1540  const EventInfo *info;
1541  Account *account;
1542 
1543  account = recn_get_account (recnData);
1544  if (!account)
1545  {
1546  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1547  return;
1548  }
1549 
1550  if (changes)
1551  {
1552  info = gnc_gui_get_entity_events (changes, &recnData->account);
1553  if (info && (info->event_mask & QOF_EVENT_DESTROY))
1554  {
1555  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1556  return;
1557  }
1558  }
1559 
1560  gnc_reconcile_window_set_titles(recnData);
1561  recn_set_watches (recnData);
1562 
1563  recnRefresh (recnData);
1564 }
1565 
1566 
1567 static void
1568 close_handler (gpointer user_data)
1569 {
1570  RecnWindow *recnData = user_data;
1571 
1572  gnc_save_window_size(GNC_PREFS_GROUP_RECONCILE, GTK_WINDOW(recnData->window));
1573  gtk_widget_destroy (recnData->window);
1574 }
1575 
1576 
1577 /********************************************************************\
1578  * recnWindow *
1579  * opens up the window to reconcile an account *
1580  * *
1581  * Args: parent - the parent of this window *
1582  * account - the account to reconcile *
1583  * Return: recnData - the instance of this RecnWindow *
1584 \********************************************************************/
1585 RecnWindow *
1586 recnWindow (GtkWidget *parent, Account *account)
1587 {
1588  gnc_numeric new_ending;
1589  time64 statement_date;
1590 
1591  if (account == NULL)
1592  return NULL;
1593 
1594  /* The last time reconciliation was attempted during the current
1595  * execution of gnucash, the date was stored. Use that date if
1596  * possible. This helps with balancing multiple accounts for which
1597  * statements are issued at the same time, like multiple bank
1598  * accounts on a single statement. */
1599  if (!gnc_reconcile_last_statement_date)
1600  statement_date = gnc_time (NULL);
1601  else
1602  statement_date = gnc_reconcile_last_statement_date;
1603 
1604  gnc_get_reconcile_info (account, &new_ending, &statement_date);
1605 
1606  /* Popup a little window to prompt the user to enter the
1607  * ending balance for his/her bank statement */
1608  if (!startRecnWindow (parent, account, &new_ending, &statement_date, TRUE))
1609  return NULL;
1610 
1611  return recnWindowWithBalance (parent, account, new_ending, statement_date);
1612 }
1613 
1614 
1615 static void
1616 recnWindow_add_widget (GtkUIManager *merge,
1617  GtkWidget *widget,
1618  GtkVBox *dock)
1619 {
1620  gtk_box_pack_start (GTK_BOX (dock), widget, FALSE, FALSE, 0);
1621  gtk_widget_show (widget);
1622 }
1623 
1624 
1625 /********************************************************************\
1626  * recnWindowWithBalance
1627  *
1628  * Opens up the window to reconcile an account, but with ending
1629  * balance and statement date already given.
1630  *
1631  * Args: parent - The parent widget of the new window
1632  * account - The account to reconcile
1633  * new_ending - The amount for ending balance
1634  * statement_date - The date of the statement
1635  * Return: recnData - the instance of this RecnWindow
1636 \********************************************************************/
1637 RecnWindow *
1638 recnWindowWithBalance (GtkWidget *parent, Account *account,
1639  gnc_numeric new_ending, time64 statement_date)
1640 {
1641  RecnWindow *recnData;
1642  GtkWidget *statusbar;
1643  GtkWidget *vbox;
1644  GtkWidget *dock;
1645 
1646  if (account == NULL)
1647  return NULL;
1648 
1649  recnData = gnc_find_first_gui_component (WINDOW_RECONCILE_CM_CLASS,
1650  find_by_account, account);
1651  if (recnData)
1652  return recnData;
1653 
1654  recnData = g_new0 (RecnWindow, 1);
1655 
1656  recnData->account = *xaccAccountGetGUID (account);
1657 
1658 
1659  recnData->component_id =
1660  gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS,
1661  refresh_handler, close_handler,
1662  recnData);
1663 
1664  recn_set_watches (recnData);
1665 
1666  gnc_reconcile_last_statement_date = statement_date;
1667 
1668  recnData->new_ending = new_ending;
1669  recnData->statement_date = statement_date;
1670  recnData->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1671  recnData->delete_refresh = FALSE;
1672 
1673  gnc_recn_set_window_name(recnData);
1674 
1675  vbox = gtk_vbox_new(FALSE, 0);
1676  gtk_container_add(GTK_CONTAINER(recnData->window), vbox);
1677 
1678  dock = gtk_vbox_new (FALSE, 0);
1679  gtk_widget_show(dock);
1680  gtk_box_pack_start(GTK_BOX (vbox), dock, FALSE, TRUE, 0);
1681 
1682  {
1683  gchar *filename;
1684  gint merge_id;
1685  GtkAction *action;
1686  GtkActionGroup *action_group;
1687  GError *error = NULL;
1688 
1689  recnData->ui_merge = gtk_ui_manager_new ();
1690  g_signal_connect (recnData->ui_merge, "add_widget",
1691  G_CALLBACK (recnWindow_add_widget), dock);
1692 
1693  action_group = gtk_action_group_new ("ReconcileWindowActions");
1694  recnData->action_group = action_group;
1695  gnc_gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE);
1696  gtk_action_group_add_actions (action_group, recnWindow_actions,
1697  recnWindow_n_actions, recnData);
1698  action =
1699  gtk_action_group_get_action (action_group, "AccountOpenAccountAction");
1700  g_object_set (G_OBJECT(action), "short_label", _("Open"), NULL);
1701 
1702  gtk_ui_manager_insert_action_group (recnData->ui_merge, action_group, 0);
1703 
1704  filename = gnc_filepath_locate_ui_file("gnc-reconcile-window-ui.xml");
1705  /* Can't do much without a ui. */
1706  g_assert (filename);
1707 
1708  merge_id = gtk_ui_manager_add_ui_from_file (recnData->ui_merge,
1709  filename, &error);
1710  g_assert(merge_id || error);
1711  if (merge_id)
1712  {
1713  gtk_window_add_accel_group (GTK_WINDOW (recnData->window),
1714  gtk_ui_manager_get_accel_group(recnData->ui_merge));
1715  gtk_ui_manager_ensure_update (recnData->ui_merge);
1716  }
1717  else
1718  {
1719  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
1720  filename, error->message);
1721  g_error_free(error);
1722  g_assert(merge_id != 0);
1723  }
1724  g_free(filename);
1725  }
1726 
1727  g_signal_connect(recnData->window, "popup-menu",
1728  G_CALLBACK(gnc_reconcile_window_popup_menu_cb), recnData);
1729 
1730  statusbar = gtk_statusbar_new();
1731  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(statusbar), TRUE);
1732  gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
1733 
1734  g_signal_connect (recnData->window, "destroy",
1735  G_CALLBACK(recn_destroy_cb), recnData);
1736  g_signal_connect (recnData->window, "delete_event",
1737  G_CALLBACK(recn_delete_cb), recnData);
1738  g_signal_connect (recnData->window, "key_press_event",
1739  G_CALLBACK(recn_key_press_cb), recnData);
1740 
1741  /* The main area */
1742  {
1743  GtkWidget *frame = gtk_frame_new(NULL);
1744  GtkWidget *main_area = gtk_vbox_new(FALSE, 10);
1745  GtkWidget *debcred_area = gtk_table_new(1, 2, TRUE);
1746  GtkWidget *debits_box;
1747  GtkWidget *credits_box;
1748 
1749  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 10);
1750 
1751  /* Force a reasonable starting size */
1752  gtk_window_set_default_size(GTK_WINDOW(recnData->window), 800, 600);
1753  gnc_restore_window_size (GNC_PREFS_GROUP_RECONCILE, GTK_WINDOW(recnData->window));
1754 
1755  gtk_container_add(GTK_CONTAINER(frame), main_area);
1756  gtk_container_set_border_width(GTK_CONTAINER(main_area), 10);
1757 
1758  debits_box = gnc_reconcile_window_create_view_box
1759  (account, RECLIST_DEBIT, recnData,
1760  &recnData->debit, &recnData->total_debit);
1761 
1762  credits_box = gnc_reconcile_window_create_view_box
1763  (account, RECLIST_CREDIT, recnData,
1764  &recnData->credit, &recnData->total_credit);
1765 
1766  GNC_RECONCILE_VIEW(recnData->debit)->sibling = GNC_RECONCILE_VIEW(recnData->credit);
1767  GNC_RECONCILE_VIEW(recnData->credit)->sibling = GNC_RECONCILE_VIEW(recnData->debit);
1768 
1769  gtk_box_pack_start(GTK_BOX(main_area), debcred_area, TRUE, TRUE, 0);
1770  gtk_table_set_col_spacings(GTK_TABLE(debcred_area), 15);
1771  gtk_table_attach_defaults(GTK_TABLE(debcred_area), debits_box, 0, 1, 0, 1);
1772  gtk_table_attach_defaults(GTK_TABLE(debcred_area), credits_box, 1, 2, 0, 1);
1773 
1774  {
1775  GtkWidget *hbox, *title_vbox, *value_vbox;
1776  GtkWidget *totals_hbox, *frame, *title, *value;
1777 
1778  /* lower horizontal bar below reconcile lists */
1779  hbox = gtk_hbox_new(FALSE, 5);
1780  gtk_box_pack_start(GTK_BOX(main_area), hbox, FALSE, FALSE, 0);
1781 
1782  /* frame to hold totals */
1783  frame = gtk_frame_new(NULL);
1784  gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
1785 
1786  /* hbox to hold title/value vboxes */
1787  totals_hbox = gtk_hbox_new(FALSE, 3);
1788  gtk_container_add(GTK_CONTAINER(frame), totals_hbox);
1789  gtk_container_set_border_width(GTK_CONTAINER(totals_hbox), 5);
1790 
1791  /* vbox to hold titles */
1792  title_vbox = gtk_vbox_new(FALSE, 3);
1793  gtk_box_pack_start(GTK_BOX(totals_hbox), title_vbox, FALSE, FALSE, 0);
1794 
1795  /* vbox to hold values */
1796  value_vbox = gtk_vbox_new(FALSE, 3);
1797  gtk_box_pack_start(GTK_BOX(totals_hbox), value_vbox, TRUE, TRUE, 0);
1798 
1799  /* statement date title/value */
1800  title = gtk_label_new(_("Statement Date:"));
1801  gtk_misc_set_alignment(GTK_MISC(title), 1.0, 0.5);
1802  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
1803 
1804  value = gtk_label_new("");
1805  recnData->recn_date = value;
1806  gtk_misc_set_alignment(GTK_MISC(value), 1.0, 0.5);
1807  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
1808 
1809  /* starting balance title/value */
1810  title = gtk_label_new(_("Starting Balance:"));
1811  gtk_misc_set_alignment(GTK_MISC(title), 1.0, 0.5);
1812  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 3);
1813 
1814  value = gtk_label_new("");
1815  recnData->starting = value;
1816  gtk_misc_set_alignment(GTK_MISC(value), 1.0, 0.5);
1817  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 3);
1818 
1819  /* ending balance title/value */
1820  title = gtk_label_new(_("Ending Balance:"));
1821  gtk_misc_set_alignment(GTK_MISC(title), 1.0, 0.5);
1822  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
1823 
1824  value = gtk_label_new("");
1825  recnData->ending = value;
1826  gtk_misc_set_alignment(GTK_MISC(value), 1.0, 0.5);
1827  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
1828 
1829  /* reconciled balance title/value */
1830  title = gtk_label_new(_("Reconciled Balance:"));
1831  gtk_misc_set_alignment(GTK_MISC(title), 1.0, 0.5);
1832  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
1833 
1834  value = gtk_label_new("");
1835  recnData->reconciled = value;
1836  gtk_misc_set_alignment(GTK_MISC(value), 1.0, 0.5);
1837  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
1838 
1839  /* difference title/value */
1840  title = gtk_label_new(_("Difference:"));
1841  gtk_misc_set_alignment(GTK_MISC(title), 1.0, 0.5);
1842  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
1843 
1844  value = gtk_label_new("");
1845  recnData->difference = value;
1846  gtk_misc_set_alignment(GTK_MISC(value), 1.0, 0.5);
1847  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
1848  }
1849 
1850  /* Set up the data */
1851  recnRefresh (recnData);
1852  }
1853 
1854  /* Allow resize */
1855  gtk_window_set_resizable(GTK_WINDOW(recnData->window), TRUE);
1856 
1857  gtk_widget_show_all(recnData->window);
1858 
1859  gnc_reconcile_window_set_titles(recnData);
1860 
1861  recnRecalculateBalance(recnData);
1862 
1863  gnc_window_adjust_for_screen(GTK_WINDOW(recnData->window));
1864 
1865  /* Set the sort orders of the debit and credit tree views */
1866  gnc_query_sort_order(GNC_QUERY_VIEW(recnData->debit), 1, GTK_SORT_ASCENDING);
1867  gnc_query_sort_order(GNC_QUERY_VIEW(recnData->credit), 1, GTK_SORT_ASCENDING);
1868 
1869  gtk_widget_grab_focus (recnData->debit);
1870 
1871  return recnData;
1872 }
1873 
1874 
1875 /********************************************************************\
1876  * gnc_ui_reconile_window_raise *
1877  * shows and raises an account editing window *
1878  * *
1879  * Args: editAccData - the edit window structure *
1880 \********************************************************************/
1881 void
1882 gnc_ui_reconcile_window_raise(RecnWindow * recnData)
1883 {
1884  if (recnData == NULL)
1885  return;
1886 
1887  if (recnData->window == NULL)
1888  return;
1889 
1890  gtk_window_present(GTK_WINDOW(recnData->window));
1891 }
1892 
1893 
1894 /********************************************************************\
1895  * recn_destroy_cb *
1896  * frees memory allocated for an recnWindow, and other cleanup *
1897  * stuff *
1898  * *
1899  * Args: w - the widget that called us *
1900  * data - the data struct for this window *
1901  * Return: none *
1902 \********************************************************************/
1903 static void
1904 recn_destroy_cb (GtkWidget *w, gpointer data)
1905 {
1906  RecnWindow *recnData = data;
1907 
1908  gnc_unregister_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1909 
1910  if (recnData->delete_refresh)
1911  gnc_resume_gui_refresh ();
1912 
1913  g_free (recnData);
1914 }
1915 
1916 
1917 static void
1918 recn_cancel(RecnWindow *recnData)
1919 {
1920  gboolean changed = FALSE;
1921 
1922  if (gnc_reconcile_view_changed(GNC_RECONCILE_VIEW(recnData->credit)))
1923  changed = TRUE;
1924  if (gnc_reconcile_view_changed(GNC_RECONCILE_VIEW(recnData->debit)))
1925  changed = TRUE;
1926 
1927  if (changed)
1928  {
1929  const char *message = _("You have made changes to this reconcile "
1930  "window. Are you sure you want to cancel?");
1931  if (!gnc_verify_dialog(recnData->window, FALSE, "%s", message))
1932  return;
1933  }
1934 
1935  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1936 }
1937 
1938 
1939 static gboolean
1940 recn_delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
1941 {
1942  RecnWindow *recnData = data;
1943 
1944  recn_cancel(recnData);
1945  return TRUE;
1946 }
1947 
1948 
1949 static gboolean
1950 recn_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1951 {
1952  RecnWindow *recnData = data;
1953 
1954  if (event->keyval == GDK_Escape)
1955  {
1956  recn_cancel(recnData);
1957  return TRUE;
1958  }
1959  else
1960  {
1961  return FALSE;
1962  }
1963 }
1964 
1965 
1966 /********************************************************************\
1967  * find_payment_account *
1968  * find an account that 'looks like' a payment account for the *
1969  * given account. This really only makes sense for credit card *
1970  * accounts. *
1971  * *
1972  * Args: account - the account to look in *
1973  * Return: a candidate payment account or NULL if none was found *
1974 \********************************************************************/
1975 static Account *
1976 find_payment_account(Account *account)
1977 {
1978  GList *list;
1979  GList *node;
1980 
1981  if (account == NULL)
1982  return NULL;
1983 
1984  list = xaccAccountGetSplitList (account);
1985 
1986  /* Search backwards to find the latest payment */
1987  for (node = g_list_last (list); node; node = node->prev)
1988  {
1989  Transaction *trans;
1990  Split *split;
1991  GList *n;
1992 
1993  split = node->data;
1994  if (split == NULL)
1995  continue;
1996 
1997  /* ignore 'purchases' */
1999  continue;
2000 
2001  trans = xaccSplitGetParent(split);
2002  if (trans == NULL)
2003  continue;
2004 
2005  for (n = xaccTransGetSplitList (trans); n; n = n->next)
2006  {
2007  GNCAccountType type;
2008  Account *a;
2009  Split *s;
2010 
2011  s = n->data;
2012  if ((s == NULL) || (s == split))
2013  continue;
2014 
2015  a = xaccSplitGetAccount(s);
2016  if ((a == NULL) || (a == account))
2017  continue;
2018 
2019  type = xaccAccountGetType(a);
2020  if ((type == ACCT_TYPE_BANK) || (type == ACCT_TYPE_CASH) ||
2021  (type == ACCT_TYPE_ASSET))
2022  return a;
2023  }
2024  }
2025 
2026  return NULL;
2027 }
2028 
2029 
2030 /********************************************************************\
2031  * recnFinishCB *
2032  * saves reconcile information *
2033  * *
2034  * Args: w - the widget that called us *
2035  * data - the data struct for this window *
2036  * Return: none *
2037 \********************************************************************/
2038 static void
2039 recnFinishCB (GtkAction *action, RecnWindow *recnData)
2040 {
2041  gboolean auto_payment;
2042  Account *account;
2043  time64 date;
2044 
2045  if (!gnc_numeric_zero_p (recnRecalculateBalance(recnData)))
2046  {
2047  const char *message = _("The account is not balanced. "
2048  "Are you sure you want to finish?");
2049  if (!gnc_verify_dialog (recnData->window, FALSE, "%s", message))
2050  return;
2051  }
2052 
2053  date = recnData->statement_date;
2054 
2055  gnc_suspend_gui_refresh ();
2056 
2057  recnData->delete_refresh = TRUE;
2058 
2059  gnc_reconcile_view_commit(GNC_RECONCILE_VIEW(recnData->credit), date);
2060  gnc_reconcile_view_commit(GNC_RECONCILE_VIEW(recnData->debit), date);
2061 
2062  auto_payment = gnc_prefs_get_bool(GNC_PREFS_GROUP_RECONCILE, GNC_PREF_AUTO_CC_PAYMENT);
2063 
2064  account = recn_get_account (recnData);
2065 
2067  xaccAccountSetReconcileLastDate (account, date);
2068 
2069  if (auto_payment &&
2070  (xaccAccountGetType (account) == ACCT_TYPE_CREDIT) &&
2071  (gnc_numeric_negative_p (recnData->new_ending)))
2072  {
2073  Account *payment_account;
2074  XferDialog *xfer;
2075 
2076  xfer = gnc_xfer_dialog(NULL, account);
2077 
2078  gnc_xfer_dialog_set_amount(xfer, gnc_numeric_neg (recnData->new_ending));
2079 
2080  payment_account = find_payment_account (account);
2081  if (payment_account != NULL)
2082  gnc_xfer_dialog_select_from_account (xfer, payment_account);
2083  }
2084 
2085  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2086 }
2087 
2088 
2089 /********************************************************************\
2090  * recnPostponeCB *
2091  * saves reconcile information for later use *
2092  * *
2093  * Args: w - the widget that called us *
2094  * data - the data struct for this window *
2095  * Return: none *
2096 \********************************************************************/
2097 static void
2098 recnPostponeCB (GtkAction *action, gpointer data)
2099 {
2100  RecnWindow *recnData = data;
2101  Account *account;
2102 
2103  {
2104  const char *message = _("Do you want to postpone this reconciliation "
2105  "and finish it later?");
2106  if (!gnc_verify_dialog (recnData->window, FALSE, "%s", message))
2107  return;
2108  }
2109 
2110  gnc_suspend_gui_refresh ();
2111 
2112  recnData->delete_refresh = TRUE;
2113 
2114  gnc_reconcile_view_postpone (GNC_RECONCILE_VIEW(recnData->credit));
2115  gnc_reconcile_view_postpone (GNC_RECONCILE_VIEW(recnData->debit));
2116 
2117  account = recn_get_account (recnData);
2118 
2119  xaccAccountSetReconcilePostponeDate (account, recnData->statement_date);
2120  xaccAccountSetReconcilePostponeBalance (account, recnData->new_ending);
2121 
2122  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2123 }
2124 
2125 
2126 static void
2127 recnCancelCB (GtkAction *action, gpointer data)
2128 {
2129  RecnWindow *recnData = data;
2130  recn_cancel(recnData);
2131 }
2132 
2133 
2138 static GtkActionEntry recnWindow_actions [] =
2139 {
2140  /* Toplevel */
2141 
2142  { "ReconcileMenuAction", NULL, N_("_Reconcile"), NULL, NULL, NULL, },
2143  { "AccountMenuAction", NULL, N_("_Account"), NULL, NULL, NULL, },
2144  { "TransactionMenuAction", NULL, N_("_Transaction"), NULL, NULL, NULL, },
2145  { "HelpMenuAction", NULL, N_("_Help"), NULL, NULL, NULL, },
2146 
2147  /* Reconcile menu */
2148 
2149  {
2150  "RecnChangeInfoAction", NULL, N_("_Reconcile Information..."), NULL,
2151  N_("Change the reconcile information "
2152  "including statement date and ending balance."),
2153  G_CALLBACK (gnc_ui_reconcile_window_change_cb)
2154  },
2155  {
2156  "RecnFinishAction", GTK_STOCK_YES, N_("_Finish"), "<control>w",
2157  N_("Finish the reconciliation of this account"),
2158  G_CALLBACK(recnFinishCB)
2159  },
2160  {
2161  "RecnPostponeAction", GTK_STOCK_GO_BACK, N_("_Postpone"), "<control>p",
2162  N_("Postpone the reconciliation of this account"),
2163  G_CALLBACK(recnPostponeCB)
2164  },
2165  {
2166  "RecnCancelAction", GTK_STOCK_CANCEL, N_("_Cancel"), NULL,
2167  N_("Cancel the reconciliation of this account"),
2168  G_CALLBACK(recnCancelCB)
2169  },
2170 
2171  /* Account menu */
2172 
2173  {
2174  "AccountOpenAccountAction", GTK_STOCK_JUMP_TO, N_("_Open Account"), NULL,
2175  N_("Open the account"),
2176  G_CALLBACK(gnc_recn_open_cb)
2177  },
2178  {
2179  "AccountEditAccountAction", NULL, N_("_Edit Account"), NULL,
2180  N_("Edit the main account for this register"),
2181  G_CALLBACK(gnc_recn_edit_account_cb)
2182  },
2183  {
2184  "AccountTransferAction", NULL, N_("_Transfer..."), NULL,
2185  N_("Transfer funds from one account to another"),
2186  G_CALLBACK(gnc_recn_xfer_cb)
2187  },
2188  {
2189  "AccountCheckRepairAction", NULL, N_("_Check & Repair"), NULL,
2190  N_("Check for and repair unbalanced transactions and orphan splits "
2191  "in this account"),
2192  G_CALLBACK(gnc_recn_scrub_cb)
2193  },
2194 
2195  /* Transaction menu */
2196 
2197  {
2198  "TransBalanceAction", GTK_STOCK_NEW, N_("_Balance"), "<control>b",
2199  N_("Add a new balancing entry to the account"),
2200  G_CALLBACK(gnc_ui_reconcile_window_balance_cb)
2201  },
2202  {
2203  "TransEditAction", GTK_STOCK_PROPERTIES, N_("_Edit"), "<control>e",
2204  N_("Edit the current transaction"),
2205  G_CALLBACK(gnc_ui_reconcile_window_edit_cb)
2206  },
2207  {
2208  "TransDeleteAction", GTK_STOCK_DELETE, N_("_Delete"), "<control>d",
2209  N_("Delete the selected transaction"),
2210  G_CALLBACK(gnc_ui_reconcile_window_delete_cb)
2211  },
2212  {
2213  "TransRecAction", GTK_STOCK_APPLY, N_("_Reconcile Selection"), "<control>r",
2214  N_("Reconcile the selected transactions"),
2215  G_CALLBACK(gnc_ui_reconcile_window_rec_cb)
2216  },
2217  {
2218  "TransUnRecAction", GTK_STOCK_CLEAR, N_("_Unreconcile Selection"), "<control>u",
2219  N_("Unreconcile the selected transactions"),
2220  G_CALLBACK(gnc_ui_reconcile_window_unrec_cb)
2221  },
2222 
2223  /* Help menu */
2224 
2225  {
2226  "HelpHelpAction", NULL, N_("_Help"), NULL,
2227  N_("Open the GnuCash help window"),
2228  G_CALLBACK(gnc_ui_reconcile_window_help_cb)
2229  },
2230 };
2231 
2233 static guint recnWindow_n_actions = G_N_ELEMENTS (recnWindow_actions);
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
High-Level API for imposing Lot constraints.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
Dialog for create/edit an account.
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
int xaccAccountGetCommoditySCU(const Account *acc)
Definition: Account.c:2458
gnc_numeric gnc_numeric_neg(gnc_numeric a)
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
Definition: Account.c:4285
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
void xaccAccountSetReconcileLastInterval(Account *acc, int months, int days)
Definition: Account.c:4326
gboolean gnc_numeric_zero_p(gnc_numeric a)
gchar * gnc_filepath_locate_ui_file(const gchar *name)
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
gint gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
struct tm * gnc_localtime_r(const time64 *secs, struct tm *time)
fill out a time struct from a 64-bit time value adjusted for the current time zone.
time64 gnc_time64_get_day_end_gdate(const GDate *date)
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
void gnc_ui_edit_account_window(Account *account)
Functions for adding content to a window.
Definition: guid.h:65
gboolean gnc_numeric_negative_p(gnc_numeric a)
gboolean xaccAccountGetReconcilePostponeDate(const Account *acc, time64 *postpone_date)
Definition: Account.c:4348
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
char * qof_print_date(time64 secs)
#define xaccAccountGetGUID(X)
Definition: Account.h:239
convert single-entry accounts to clean double-entry
gchar * gnc_account_get_full_name(const Account *account)
Definition: Account.c:3038
Functions providing a register page for the GnuCash UI.
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
time64 gnc_time64_get_day_end(time64 time_val)
void xaccAccountClearReconcilePostpone(Account *acc)
Definition: Account.c:4423
void xaccAccountSetReconcilePostponeDate(Account *acc, time64 postpone_date)
Definition: Account.c:4368
Gnome specific utility functions.
Additional event handling code.
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
Definition: Account.c:4407
void gnc_gnome_help(const char *file_name, const char *anchor)
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Definition: Account.c:3288
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
Definition: Account.c:4264
#define CREC
Definition: Split.h:67
gdouble gnc_difftime(const time64 secs1, const time64 secs2)
Find the difference in seconds between two time values.
GNCAccountType
Definition: Account.h:96
gboolean gnc_numeric_positive_p(gnc_numeric a)
#define xaccTransGetGUID(X)
Definition: Transaction.h:755
Generic api to store and retrieve preferences.
GList * gnc_account_get_descendants(const Account *account)
Definition: Account.c:2755
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
Definition: Account.c:4660
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
Definition: Account.c:4300
GDate helper routines.
Definition: SplitP.h:71
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
time64 gnc_time(time64 *tbuf)
get the current local time
void xaccAccountSetAutoInterestXfer(Account *acc, gboolean option)
Definition: Account.c:4455
gint64 time64
Definition: gnc-date.h:83
void xaccAccountTreeScrubOrphans(Account *acc)
Definition: Scrub.c:62
File path resolution utility functions.
void gnc_gtk_action_group_set_translation_domain(GtkActionGroup *action_group, const gchar *domain)
GNCSplitReg * gnc_plugin_page_register_get_gsr(GncPluginPage *plugin_page)
SplitList * xaccTransGetSplitList(const Transaction *trans)
Definition: Transaction.c:2164
gboolean xaccAccountGetAutoInterestXfer(const Account *acc, gboolean default_value)
Definition: Account.c:4441
void xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
Definition: Account.c:4643
const gchar * QofLogModule
Definition: qofid.h:89
void gnc_gdate_set_time64(GDate *gd, time64 time)
#define NREC
Definition: Split.h:70
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987
gboolean xaccAccountGetReconcilePostponeBalance(const Account *acc, gnc_numeric *balance)
Definition: Account.c:4385
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1827