GnuCash  5.6-150-g038405b370+
window-reconcile.cpp
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 gnu@gnu.org *
24  * *
25  * Author: Rob Clark *
26  * Internet: rclark@cs.hmc.edu *
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 "Account.hpp"
41 #include "Scrub.h"
42 #include "Scrub3.h"
43 #include "dialog-account.h"
44 #include "dialog-transfer.h"
45 #include "dialog-utils.h"
46 #include "gnc-amount-edit.h"
47 #include "gnc-component-manager.h"
48 #include "gnc-date.h"
49 #include "gnc-date-edit.h"
50 #include "gnc-event.h"
51 #include "gnc-filepath-utils.h"
52 #include "gnc-gnome-utils.h"
53 #include "gnc-gtk-utils.h"
54 //#include "gnc-main-window.h"
56 #include "gnc-prefs.h"
57 #include "gnc-ui.h"
58 #include "gnc-ui-balances.h"
59 #include "gnc-window.h"
60 #include "reconcile-view.h"
61 #include "window-reconcile.h"
62 #include "gnc-session.h"
63 #ifdef MAC_INTEGRATION
64 #include <gtkmacintegration/gtkosxapplication.h>
65 #endif
66 
67 #define WINDOW_RECONCILE_CM_CLASS "window-reconcile"
68 #define GNC_PREF_AUTO_CC_PAYMENT "auto-cc-payment"
69 #define GNC_PREF_ALWAYS_REC_TO_TODAY "always-reconcile-to-today"
70 
71 
74 {
75  GncGUID account; /* The account that we are reconciling */
76  gnc_numeric new_ending; /* The new ending balance */
77  time64 statement_date; /* The statement date */
78 
79  gint component_id; /* id of component */
80 
81  GtkWidget *window; /* The reconcile window */
82 
83  GtkBuilder *builder; /* The builder object */
84  GSimpleActionGroup *simple_action_group; /* The action group for the window */
85  GtkAccelGroup *accel_group;
86 
87  GncPluginPage *page;
88 
89  GtkWidget *starting; /* The starting balance */
90  GtkWidget *ending; /* The ending balance */
91  GtkWidget *recn_date; /* The statement date */
92  GtkWidget *reconciled; /* The reconciled balance */
93  GtkWidget *difference; /* Text field, amount left to reconcile */
94 
95  GtkWidget *total_debit; /* Text field, total debit reconciled */
96  GtkWidget *total_credit; /* Text field, total credit reconciled */
97 
98  GtkWidget *debit; /* Debit matrix show unreconciled debit */
99  GtkWidget *credit; /* Credit matrix, shows credits... */
100 
101  GtkWidget *debit_frame; /* Frame around debit matrix */
102  GtkWidget *credit_frame; /* Frame around credit matrix */
103 
104  gboolean delete_refresh; /* do a refresh upon a window deletion */
105 };
106 
107 
108 /* This structure doesn't contain everything involved in the
109  * startRecnWindow, just pointers that have to be passed in to
110  * callbacks that need more than one piece of data to operate on.
111  * This is also used by the interest transfer dialog code.
112  */
113 typedef struct _startRecnWindowData
114 {
115  Account *account; /* the account being reconciled */
116  GNCAccountType account_type; /* the type of the account */
117 
118  GtkWidget *startRecnWindow; /* the startRecnWindow dialog */
119  GtkWidget *xfer_button; /* the dialog's interest transfer button */
120  GtkWidget *date_value; /* the dialog's ending date field */
121  GtkWidget *future_icon;
122  GtkWidget *future_text;
123  GNCAmountEdit *end_value; /* the dialog's ending balance amount edit */
124  gnc_numeric original_value; /* the dialog's original ending balance */
125  gboolean user_set_value; /* the user changed the ending value */
126 
127  XferDialog *xferData; /* the interest xfer dialog (if it exists) */
128  gboolean include_children;
129 
130  time64 date; /* the interest xfer reconcile date */
132 
134 static gnc_numeric recnRecalculateBalance (RecnWindow *recnData);
135 
136 static void recn_destroy_cb (GtkWidget *w, gpointer data);
137 static void recn_cancel (RecnWindow *recnData);
138 static gboolean recn_delete_cb (GtkWidget *widget, GdkEvent *event, gpointer data);
139 static gboolean recn_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data);
140 static void recnFinishCB (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
141 static void recnPostponeCB (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
142 static void recnCancelCB (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
143 
144 extern "C" {
145 void gnc_start_recn_children_changed (GtkWidget *widget, startRecnWindowData *data);
146 void gnc_start_recn_interest_clicked_cb (GtkButton *button, startRecnWindowData *data);
147 }
148 
149 static void gnc_reconcile_window_set_sensitivity (RecnWindow *recnData);
150 static char * gnc_recn_make_window_name (Account *account);
151 static void gnc_recn_set_window_name (RecnWindow *recnData);
152 static gboolean find_by_account (gpointer find_data, gpointer user_data);
153 
155 /* This static indicates the debugging module that this .o belongs to. */
156 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
157 
158 static time64 gnc_reconcile_last_statement_date = 0;
159 
162 static gpointer
163 commodity_compare(Account *account, gpointer user_data) {
164  gboolean equal = gnc_commodity_equiv (xaccAccountGetCommodity (account),
165  (gnc_commodity*) user_data);
166 
167  return equal ? NULL : account;
168 }
169 
170 
171 /********************************************************************\
172  * has_account_different_commodities *
173  * *
174  * Args: parent account - the account to look in *
175  * Return: true if there exists a subaccount with different *
176  * commodity then the parent account. *
177 \********************************************************************/
178 static gboolean
179 has_account_different_commodities(const Account *account)
180 {
181  gnc_commodity *parent_commodity;
182  gpointer result;
183 
184  if (account == NULL)
185  return FALSE;
186 
187  parent_commodity = xaccAccountGetCommodity (account);
188 
189  result = gnc_account_foreach_descendant_until (account,
190  commodity_compare,
191  parent_commodity);
192 
193  return result != NULL;
194 }
195 
196 
197 /********************************************************************\
198  * recnRefresh *
199  * refreshes the transactions in the reconcile window *
200  * *
201  * Args: account - the account of the reconcile window to refresh *
202  * Return: none *
203 \********************************************************************/
204 static void
205 recnRefresh (RecnWindow *recnData)
206 {
207  if (recnData == NULL)
208  return;
209 
210  gnc_reconcile_view_refresh(GNC_RECONCILE_VIEW(recnData->debit));
211  gnc_reconcile_view_refresh(GNC_RECONCILE_VIEW(recnData->credit));
212 
213  gnc_reconcile_window_set_sensitivity(recnData);
214 
215  gnc_recn_set_window_name(recnData);
216 
217  recnRecalculateBalance(recnData);
218 
219  gtk_widget_queue_resize(recnData->window);
220 }
221 
222 
223 static Account *
224 recn_get_account (RecnWindow *recnData)
225 {
226  if (!recnData)
227  return NULL;
228 
229  return xaccAccountLookup (&recnData->account, gnc_get_current_book ());
230 }
231 
232 
233 static void
234 gnc_add_colorized_amount (gpointer obj, gnc_numeric amt,
235  GNCPrintAmountInfo print_info, gboolean reverse)
236 {
237  if (!obj) return;
238  if (reverse) amt = gnc_numeric_neg (amt);
239  gnc_set_label_color (GTK_WIDGET (obj), amt);
240  gtk_label_set_text (GTK_LABEL (obj), xaccPrintAmount (amt, print_info));
241 }
242 
243 /********************************************************************\
244  * recnRecalculateBalance *
245  * refreshes the balances in the reconcile window *
246  * *
247  * Args: recnData -- the reconcile window to refresh *
248  * Return: the difference between the nominal ending balance *
249  * and the 'effective' ending balance. *
250 \********************************************************************/
251 static gnc_numeric
252 recnRecalculateBalance (RecnWindow *recnData)
253 {
254  Account *account;
255  gnc_numeric debit;
256  gnc_numeric credit;
257  gnc_numeric starting;
258  gnc_numeric ending;
259  gnc_numeric reconciled;
260  gnc_numeric diff;
261  gchar *datestr;
262  GNCPrintAmountInfo print_info;
263  gboolean reverse_balance, include_children;
264  GAction *action;
265 
266  account = recn_get_account (recnData);
267  if (!account)
268  return gnc_numeric_zero ();
269 
270  reverse_balance = gnc_reverse_balance(account);
271  include_children = xaccAccountGetReconcileChildrenStatus(account);
272  starting = gnc_ui_account_get_reconciled_balance(account, include_children);
273  print_info = gnc_account_print_info (account, TRUE);
274 
275  ending = recnData->new_ending;
276  debit = gnc_reconcile_view_reconciled_balance
277  (GNC_RECONCILE_VIEW(recnData->debit));
278  credit = gnc_reconcile_view_reconciled_balance
279  (GNC_RECONCILE_VIEW(recnData->credit));
280 
281  reconciled = gnc_numeric_sub_fixed (debit, credit);
282  if (reverse_balance)
283  reconciled = gnc_numeric_sub_fixed (reconciled, starting);
284  else
285  reconciled = gnc_numeric_add_fixed (reconciled, starting);
286 
287  diff = gnc_numeric_sub_fixed (ending, reconciled);
288 
289  datestr = qof_print_date (recnData->statement_date);
290  gtk_label_set_text (GTK_LABEL(recnData->recn_date), datestr);
291  g_free (datestr);
292 
293  gnc_add_colorized_amount (recnData->starting, starting, print_info, FALSE);
294  gnc_add_colorized_amount (recnData->ending, ending, print_info, reverse_balance);
295  gnc_add_colorized_amount (recnData->total_debit, debit, print_info, FALSE);
296  gnc_add_colorized_amount (recnData->total_credit, credit, print_info, FALSE);
297  gnc_add_colorized_amount (recnData->reconciled, reconciled, print_info, reverse_balance);
298  gnc_add_colorized_amount (recnData->difference, diff, print_info, reverse_balance);
299 
300  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
301  "RecnFinishAction");
302  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), gnc_numeric_zero_p (diff));
303 
304  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
305  "TransBalanceAction");
306  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), !gnc_numeric_zero_p (diff));
307 
308  return diff;
309 }
310 
311 /* amount_edit_cb
312  * Callback on activate event for statement Ending Balance.
313  * Sets the user_set_value flag true if the amount entered is
314  * different to the calculated Ending Balance as at the default
315  * Statement Date. This prevents the entered Ending Balance
316  * being recalculated if the Statement Date is changed.
317  *
318  * Args: widget - Ending Balance widget
319  * data - structure containing info about this
320  * reconciliation process.
321  */
322 static void
323 amount_edit_cb(GtkWidget *widget, startRecnWindowData *data)
324 {
325  gnc_numeric value;
326  gint result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(data->end_value),
327  &value, TRUE, NULL);
328 
329  data->user_set_value = FALSE;
330 
331  if (result < 1) // OK
332  {
333  if (result == -1) // blank entry is valid
334  {
335  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(data->end_value), value);
336  gnc_amount_edit_select_region (GNC_AMOUNT_EDIT(data->end_value), 0, -1);
337  }
338  data->user_set_value = !gnc_numeric_equal (value, data->original_value);
339  }
340 }
341 
342 /* amount_edit_focus_out_cb
343  * Callback on focus-out event for statement Ending Balance.
344  *
345  * Args: widget - Ending Balance widget
346  * event - event triggering this callback
347  * data - structure containing info about this
348  * reconciliation process.
349  * Returns: False - propagate the event to the widget's parent.
350  */
351 static gboolean
352 amount_edit_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
353  startRecnWindowData *data)
354 {
355  amount_edit_cb(widget, data);
356  return FALSE;
357 }
358 
359 
360 /* recn_date_changed_cb
361  * Callback on date_changed event for Statement Date.
362  * If the user changed the date edit widget, and the Ending
363  * Balance wasn't entered, update the Ending Balance to reflect
364  * the ending balance of the account as at Statement Date.
365  *
366  * Args: widget - Statement Date edit widget
367  * data - structure containing info about this
368  * reconciliation.
369  * Returns: none.
370  */
371 static void
372 recn_date_changed_cb (GtkWidget *widget, startRecnWindowData *data)
373 {
374  GNCDateEdit *gde = GNC_DATE_EDIT (widget);
375  gnc_numeric new_balance;
376  time64 new_date;
377 
378  gboolean show_warning = FALSE;
379  gint days_after_today;
380  static const time64 secs_per_day = 86400;
381  static const time64 secs_per_hour = 3600;
382 
383  new_date = gnc_date_edit_get_date_end (gde);
384 
385  /* Add secs_per_hour to the difference to compensate for the short
386  * day when transitioning from standard to daylight time.
387  */
388  days_after_today = (gnc_time64_get_day_end (new_date) -
390  secs_per_hour) / secs_per_day;
391 
392  if (days_after_today > 0)
393  {
394  gchar *str = g_strdup_printf
395  /* Translators: %d is the number of days in the future */
396  (ngettext ("Statement Date is %d day after today.",
397  "Statement Date is %d days after today.",
398  days_after_today),
399  days_after_today);
400 
401  gchar *tip_start = g_strdup_printf
402  /* Translators: %d is the number of days in the future */
403  (ngettext ("The statement date you have chosen is %d day in the future.",
404  "The statement date you have chosen is %d days in the future.",
405  days_after_today),
406  days_after_today);
407 
408  gchar *tip_end = g_strdup (_("This may cause issues for future reconciliation \
409 actions on this account. Please double-check this is the date you intended."));
410  gchar *tip = g_strdup_printf ("%s %s", tip_start, tip_end);
411 
412  show_warning = TRUE;
413 
414  gtk_label_set_text (GTK_LABEL(data->future_text), str);
415  gtk_widget_set_tooltip_text (GTK_WIDGET(data->future_text), tip);
416  g_free (str);
417  g_free (tip_end);
418  g_free (tip_start);
419  g_free (tip);
420  }
421  gtk_widget_set_visible (GTK_WIDGET(data->future_icon), show_warning);
422  gtk_widget_set_visible (GTK_WIDGET(data->future_text), show_warning);
423 
424  if (data->user_set_value)
425  return;
426 
427  /* get the balance for the account as of the new date */
428  new_balance = gnc_ui_account_get_balance_as_of_date (data->account, new_date,
429  data->include_children);
430  /* update the amount edit with the amount */
431  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value),
432  new_balance);
433 }
434 
435 
436 void
437 gnc_start_recn_children_changed (GtkWidget *widget, startRecnWindowData *data)
438 {
439  data->include_children =
440  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
441 
442  /* Force an update of the ending balance */
443  recn_date_changed_cb (data->date_value, data);
444 }
445 
446 
447 /********************************************************************\
448  * recnInterestXferWindow *
449  * opens up a window to prompt the user to enter an interest *
450  * charge or payment for an account prior to reconciling it. *
451  * Only to be called for some types of accounts, as defined *
452  * in the macros at the top of this file. *
453  * *
454  * NOTE: This function does not return until the user presses "Ok" *
455  * or "Cancel", which means that the transaction must be *
456  * resolved before the startRecnWindow will work. *
457  * *
458  * Args: data - jumbo structure containing info *
459  * about the start of the reconcile *
460  * process needed by this function. *
461  * Returns: none. *
462 \********************************************************************/
463 
464 /* helper function */
465 static char *
466 gnc_recn_make_interest_window_name(Account *account, char *text)
467 {
468  char *fullname;
469  char *title;
470 
471  fullname = gnc_account_get_full_name(account);
472  title = g_strconcat(fullname, " - ", text && *text ? _(text) : "", NULL);
473 
474  g_free(fullname);
475 
476  return title;
477 }
478 
479 
480 static void
481 recnInterestXferWindow( startRecnWindowData *data)
482 {
483  gchar *title;
484 
485  if ( !account_type_has_auto_interest_xfer( data->account_type ) )
486  return;
487 
488  /* get a normal transfer dialog... */
489  data->xferData = gnc_xfer_dialog( GTK_WIDGET(data->startRecnWindow),
490  data->account );
491 
492  /* ...and start changing things: */
493 
494  /* change title */
495  if ( account_type_has_auto_interest_payment( data->account_type ) )
496  title = gnc_recn_make_interest_window_name( data->account,
497  _("Interest Payment") );
498  else
499  title = gnc_recn_make_interest_window_name( data->account,
500  _("Interest Charge") );
501 
502  gnc_xfer_dialog_set_title( data->xferData, title );
503  g_free( title );
504 
505 
506  /* change frame labels */
507  gnc_xfer_dialog_set_information_label( data->xferData,
508  _("Payment Information") );
509 
510  /* Interest accrued is a transaction from an income account
511  * to a bank account. Interest charged is a transaction from
512  * a credit account to an expense account. The user isn't allowed
513  * to change the account (bank or credit) being reconciled.
514  */
515  if ( account_type_has_auto_interest_payment( data->account_type ) )
516  {
517  gnc_xfer_dialog_set_from_account_label( data->xferData,
518  _("Payment From") );
519  gnc_xfer_dialog_set_from_show_button_active( data->xferData, TRUE );
520 
521  // XXX: Set "from" account from previous interest payment.
522 
523  gnc_xfer_dialog_set_to_account_label( data->xferData,
524  _("Reconcile Account") );
525  gnc_xfer_dialog_select_to_account( data->xferData, data->account );
526  gnc_xfer_dialog_lock_to_account_tree( data->xferData );
527 
528  /* Quickfill based on the reconcile account, which is the "To" acct. */
529  gnc_xfer_dialog_quickfill_to_account( data->xferData, TRUE );
530  }
531  else /* interest charged to account rather than paid to it */
532  {
533  gnc_xfer_dialog_set_from_account_label( data->xferData,
534  _("Reconcile Account") );
535  gnc_xfer_dialog_select_from_account( data->xferData, data->account );
536  gnc_xfer_dialog_lock_from_account_tree( data->xferData );
537 
538  gnc_xfer_dialog_set_to_account_label( data->xferData,
539  _("Payment To") );
540  gnc_xfer_dialog_set_to_show_button_active( data->xferData, TRUE );
541 
542  // XXX: Set "to" account from previous interest payment.
543 
544  /* Quickfill based on the reconcile account, which is the "From" acct. */
545  gnc_xfer_dialog_quickfill_to_account( data->xferData, FALSE );
546  }
547 
548  /* no currency frame */
549  gnc_xfer_dialog_toggle_currency_table( data->xferData, FALSE );
550 
551  /* set the reconcile date for the transaction date */
552  gnc_xfer_dialog_set_date( data->xferData, data->date );
553 
554  /* Now run the transfer dialog. This blocks until done.
555  * If the user hit Cancel, make the button clickable so that
556  * the user can retry if they want. We don't make the button
557  * clickable if they successfully entered a transaction, since
558  * the fact that the button was clickable again might make
559  * the user think that the transaction didn't actually go through.
560  */
561  if ( ! gnc_xfer_dialog_run_until_done( data->xferData ) )
562  if ( data->xfer_button )
563  gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), TRUE);
564 
565  /* done with the XferDialog */
566  data->xferData = NULL;
567 }
568 
569 
570 /* Set up for the interest xfer window, run the window, and update
571  * the startRecnWindow if the interest xfer changed anything that matters.
572  */
573 static void
574 gnc_reconcile_interest_xfer_run(startRecnWindowData *data)
575 {
576  GtkWidget *entry = gnc_amount_edit_gtk_entry(
577  GNC_AMOUNT_EDIT(data->end_value) );
578  gnc_numeric before = gnc_amount_edit_get_amount(
579  GNC_AMOUNT_EDIT(data->end_value) );
580  gnc_numeric after;
581 
582  recnInterestXferWindow( data );
583 
584  /* recompute the ending balance */
585  after = xaccAccountGetBalanceAsOfDate(data->account, data->date);
586 
587  /* update the ending balance in the startRecnWindow if it has changed. */
588  if ( gnc_numeric_compare( before, after ) )
589  {
590  if (gnc_reverse_balance(data->account))
591  after = gnc_numeric_neg (after);
592 
593  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value), after);
594  gtk_widget_grab_focus(GTK_WIDGET(entry));
595  gtk_editable_select_region (GTK_EDITABLE(entry), 0, -1);
596  data->original_value = after;
597  data->user_set_value = FALSE;
598  }
599 }
600 
601 
602 void
603 gnc_start_recn_interest_clicked_cb(GtkButton *button, startRecnWindowData *data)
604 {
605  /* make the button unclickable since we're popping up the window */
606  if ( data->xfer_button )
607  gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), FALSE);
608 
609  /* run the account window */
610  gnc_reconcile_interest_xfer_run( data );
611 }
612 
613 
614 static void
615 gnc_save_reconcile_interval(Account *account, time64 statement_date)
616 {
617  time64 prev_statement_date;
618  int days = 0, months = 0;
619 
620  if (!xaccAccountGetReconcileLastDate (account, &prev_statement_date))
621  return;
622 
623  /*
624  * Compute the number of days difference.
625  */
626  auto seconds = statement_date - prev_statement_date;
627  days = seconds / 60 / 60 / 24;
628 
629  /*
630  * See if we need to remember days(weeks) or months. The only trick
631  * value is 28 days which could be either 4 weeks or 1 month.
632  */
633  if (days == 28)
634  {
635  int prev_days = 0, prev_months = 1;
636 
637  /* What was it last time? */
638  xaccAccountGetReconcileLastInterval (account, &prev_months, &prev_days);
639  if (prev_months == 1)
640  {
641  months = 1;
642  days = 0;
643  }
644  }
645  else if (days > 28)
646  {
647  struct tm current, prev;
648 
649  gnc_localtime_r (&statement_date, &current);
650  gnc_localtime_r (&prev_statement_date, &prev);
651  months = ((12 * current.tm_year + current.tm_mon) -
652  (12 * prev.tm_year + prev.tm_mon));
653  days = 0;
654  }
655 
656  /*
657  * Remember for next time unless it is negative.
658  */
659  if (months >= 0 && days >= 0)
660  xaccAccountSetReconcileLastInterval(account, months, days);
661 }
662 
663 
664 /********************************************************************\
665  * startRecnWindow *
666  * opens up the window to prompt the user to enter the ending *
667  * balance from bank statement *
668  * *
669  * NOTE: This function does not return until the user presses "Ok" *
670  * or "Cancel" *
671  * *
672  * Args: parent - the parent of this window *
673  * account - the account to reconcile *
674  * new_ending - returns the amount for ending balance *
675  * statement_date - returns date of the statement :) *
676  * Return: True, if the user presses "Ok", else False *
677 \********************************************************************/
678 static gboolean
679 startRecnWindow(GtkWidget *parent, Account *account,
680  gnc_numeric *new_ending, time64 *statement_date,
681  gboolean enable_subaccount)
682 {
683  GtkWidget *dialog, *end_value, *date_value, *include_children_button;
684  GtkBuilder *builder;
685  startRecnWindowData data = { NULL };
686  gboolean auto_interest_xfer_option;
687  GNCPrintAmountInfo print_info;
688  gnc_numeric ending;
689  GtkWidget *entry;
690  char *title;
691  int result = -6;
692  gulong fo_handler_id;
693 
694  /* Initialize the data structure that will be used for several callbacks
695  * throughout this file with the relevant info. Some initialization is
696  * done below as well. Note that local storage should be OK for this,
697  * since any callbacks using it will only work while the startRecnWindow
698  * is running.
699  */
700  data.account = account;
701  data.account_type = xaccAccountGetType(account);
702  data.date = *statement_date;
703 
704  /* whether to have an automatic interest xfer dialog or not */
705  auto_interest_xfer_option = xaccAccountGetAutoInterest (account);
706 
707  data.include_children = !has_account_different_commodities(account) &&
709 
710  ending = gnc_ui_account_get_reconciled_balance(account,
711  data.include_children);
712  print_info = gnc_account_print_info (account, TRUE);
713 
714  /*
715  * Do not reverse the balance here. It messes up the math in the
716  * reconciliation window. Also, the balance should show up as a
717  * positive number in the reconciliation window to match the positive
718  * number that shows in the register window.
719  */
720 
721  /* Create the dialog box */
722  builder = gtk_builder_new();
723  gnc_builder_add_from_file (builder, "window-reconcile.glade", "reconcile_start_dialog");
724 
725  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_start_dialog"));
726 
727  // Set the name for this dialog so it can be easily manipulated with css
728  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-reconcile-start");
729 
730  title = gnc_recn_make_window_name (account);
731  gtk_window_set_title(GTK_WINDOW(dialog), title);
732  g_free (title);
733 
734  data.startRecnWindow = GTK_WIDGET(dialog);
735 
736  if (parent != NULL)
737  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
738 
739  {
740  GtkWidget *start_value, *box;
741  GtkWidget *label;
742  GtkWidget *interest = NULL;
743 
744  start_value = GTK_WIDGET(gtk_builder_get_object (builder, "start_value"));
745  gtk_label_set_text(GTK_LABEL(start_value), xaccPrintAmount (ending, print_info));
746 
747  include_children_button = GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_check"));
748  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(include_children_button),
749  data.include_children);
750  gtk_widget_set_sensitive(include_children_button, enable_subaccount);
751 
752  date_value = gnc_date_edit_new(*statement_date, FALSE, FALSE);
753  data.date_value = date_value;
754  box = GTK_WIDGET(gtk_builder_get_object (builder, "date_value_box"));
755  gtk_box_pack_start(GTK_BOX(box), date_value, TRUE, TRUE, 0);
756  label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
757  gnc_date_make_mnemonic_target(GNC_DATE_EDIT(date_value), label);
758 
759  end_value = gnc_amount_edit_new ();
760  data.end_value = GNC_AMOUNT_EDIT(end_value);
761  data.original_value = *new_ending;
762  data.user_set_value = FALSE;
763 
764  data.future_icon = GTK_WIDGET(gtk_builder_get_object (builder, "future_icon"));
765  data.future_text = GTK_WIDGET(gtk_builder_get_object (builder, "future_text"));
766 
767  box = GTK_WIDGET(gtk_builder_get_object (builder, "ending_value_box"));
768  gtk_box_pack_start(GTK_BOX(box), end_value, TRUE, TRUE, 0);
769  label = GTK_WIDGET(gtk_builder_get_object (builder, "end_label"));
770  gnc_amount_edit_make_mnemonic_target (GNC_AMOUNT_EDIT(end_value), label);
771 
772  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, &data);
773 
774  gnc_date_activates_default(GNC_DATE_EDIT(date_value), TRUE);
775 
776  /* need to get a callback on date changes to update the recn balance */
777  g_signal_connect ( G_OBJECT (date_value), "date_changed",
778  G_CALLBACK (recn_date_changed_cb), (gpointer) &data );
779 
780  print_info.use_symbol = 0;
781  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (end_value), print_info);
782  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (end_value),
783  xaccAccountGetCommoditySCU (account));
784 
785  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (end_value), *new_ending);
786 
787  entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (end_value));
788  gtk_editable_select_region (GTK_EDITABLE(entry), 0, -1);
789  fo_handler_id = g_signal_connect (G_OBJECT(entry), "focus-out-event",
790  G_CALLBACK(amount_edit_focus_out_cb),
791  (gpointer) &data);
792  g_signal_connect (G_OBJECT(entry), "activate",
793  G_CALLBACK(amount_edit_cb),
794  (gpointer) &data);
795  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
796 
797  /* if it's possible to enter an interest payment or charge for this
798  * account, add a button so that the user can pop up the appropriate
799  * dialog if it isn't automatically popping up.
800  */
801  interest = GTK_WIDGET(gtk_builder_get_object (builder, "interest_button"));
802  if ( account_type_has_auto_interest_payment( data.account_type ) )
803  gtk_button_set_label(GTK_BUTTON(interest), _("Enter _Interest Payment…") );
804  else if ( account_type_has_auto_interest_charge( data.account_type ) )
805  gtk_button_set_label(GTK_BUTTON(interest), _("Enter _Interest Charge…") );
806  else
807  {
808  gtk_widget_destroy(interest);
809  interest = NULL;
810  }
811 
812  if ( interest )
813  {
814  data.xfer_button = interest;
815  if ( auto_interest_xfer_option )
816  gtk_widget_set_sensitive(GTK_WIDGET(interest), FALSE);
817  }
818 
819  gtk_widget_show_all(dialog);
820 
821  gtk_widget_hide (data.future_text);
822  gtk_widget_hide (data.future_icon);
823 
824  gtk_widget_grab_focus(gnc_amount_edit_gtk_entry
825  (GNC_AMOUNT_EDIT (end_value)));
826  }
827 
828  /* Allow the user to enter an interest payment
829  * or charge prior to reconciling */
830  if ( account_type_has_auto_interest_xfer( data.account_type )
831  && auto_interest_xfer_option )
832  {
833  gnc_reconcile_interest_xfer_run( &data );
834  }
835 
836  while (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
837  {
838  if (gnc_date_edit_get_date_end(GNC_DATE_EDIT(date_value)) != *statement_date)
839  recn_date_changed_cb(date_value, &data);
840 
841  /* If response is OK but end_value not valid, try again */
842  if (gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(end_value), NULL))
843  {
844  result = GTK_RESPONSE_OK;
845  break;
846  }
847  }
848 
849  if (result == GTK_RESPONSE_OK)
850  {
851  *new_ending = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (end_value));
852  *statement_date = gnc_date_edit_get_date_end(GNC_DATE_EDIT(date_value));
853 
854  if (gnc_reverse_balance(account))
855  *new_ending = gnc_numeric_neg (*new_ending);
856 
857  xaccAccountSetReconcileChildrenStatus(account, data.include_children);
858 
859  gnc_save_reconcile_interval(account, *statement_date);
860  }
861  // must remove the focus-out handler
862  g_signal_handler_disconnect (G_OBJECT(entry), fo_handler_id);
863  gtk_widget_destroy (dialog);
864  g_object_unref(G_OBJECT(builder));
865 
866  return (result == GTK_RESPONSE_OK);
867 }
868 
869 
870 static void
871 gnc_reconcile_window_set_sensitivity(RecnWindow *recnData)
872 {
873  gboolean sensitive = FALSE;
874  GNCReconcileView *view;
875  GAction *action;
876 
877  view = GNC_RECONCILE_VIEW(recnData->debit);
878  if (gnc_reconcile_view_num_selected(view) == 1)
879  sensitive = TRUE;
880 
881  view = GNC_RECONCILE_VIEW(recnData->credit);
882  if (gnc_reconcile_view_num_selected(view) == 1)
883  sensitive = TRUE;
884 
885  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
886  "TransEditAction");
887  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), sensitive);
888 
889  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
890  "TransDeleteAction");
891  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), sensitive);
892 
893  sensitive = FALSE;
894 
895  view = GNC_RECONCILE_VIEW(recnData->debit);
896  if (gnc_reconcile_view_num_selected(view) > 0)
897  sensitive = TRUE;
898 
899  view = GNC_RECONCILE_VIEW(recnData->credit);
900  if (gnc_reconcile_view_num_selected(view) > 0)
901  sensitive = TRUE;
902 
903  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
904  "TransRecAction");
905  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), sensitive);
906 
907  action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group),
908  "TransUnRecAction");
909  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), sensitive);
910 }
911 
912 
913 static void
914 gnc_reconcile_window_toggled_cb(GNCReconcileView *view, Split *split,
915  gpointer data)
916 {
917  auto recnData = static_cast<RecnWindow*>(data);
918  gnc_reconcile_window_set_sensitivity(recnData);
919  recnRecalculateBalance(recnData);
920 }
921 
922 
923 static void
924 gnc_reconcile_window_row_cb(GNCReconcileView *view, gpointer item,
925  gpointer data)
926 {
927  auto recnData = static_cast<RecnWindow*>(data);
928  gnc_reconcile_window_set_sensitivity(recnData);
929 }
930 
931 
944 static void
945 do_popup_menu(RecnWindow *recnData, GdkEventButton *event)
946 {
947  GMenuModel *menu_model = (GMenuModel *)gtk_builder_get_object (recnData->builder,
948  "recwin-popup");
949  GtkWidget *menu = gtk_menu_new_from_model (menu_model);
950 
951  if (!menu)
952  return;
953 
954  gtk_menu_attach_to_widget (GTK_MENU(menu), GTK_WIDGET(recnData->window), NULL);
955  gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
956 }
957 
958 
972 static gboolean
973 gnc_reconcile_window_popup_menu_cb (GtkWidget *widget,
974  RecnWindow *recnData)
975 {
976  do_popup_menu(recnData, NULL);
977  return TRUE;
978 }
979 
980 
981 /* Callback function invoked when the user clicks in the content of
982  * any Gnucash window. If this was a "right-click" then Gnucash will
983  * popup the contextual menu.
984  */
985 static gboolean
986 gnc_reconcile_window_button_press_cb (GtkWidget *widget,
987  GdkEventButton *event,
988  RecnWindow *recnData)
989 {
990  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
991  {
992  GNCQueryView *qview = GNC_QUERY_VIEW(widget);
993  GtkTreePath *path;
994 
995  /* Get tree path for row that was clicked */
996  gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(qview),
997  (gint) event->x,
998  (gint) event->y,
999  &path, NULL, NULL, NULL);
1000 
1001  if (path)
1002  {
1003  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
1004 
1005  if (!gtk_tree_selection_path_is_selected (selection, path))
1006  {
1007  gtk_tree_selection_unselect_all (selection);
1008  gtk_tree_selection_select_path (selection, path);
1009  }
1010  gtk_tree_path_free (path);
1011  }
1012  do_popup_menu (recnData, event);
1013  return TRUE;
1014  }
1015  return FALSE;
1016 }
1017 
1018 
1019 static GNCSplitReg *
1020 gnc_reconcile_window_open_register(RecnWindow *recnData)
1021 {
1022  Account *account = recn_get_account (recnData);
1023  GNCSplitReg *gsr;
1024  gboolean include_children;
1025 
1026  if (!account)
1027  return(NULL);
1028 
1029  include_children = xaccAccountGetReconcileChildrenStatus (account);
1030  recnData->page = gnc_plugin_page_register_new (account, include_children);
1031  gnc_main_window_open_page (NULL, recnData->page);
1032  gsr = gnc_plugin_page_register_get_gsr (recnData->page);
1033  gnc_split_reg_raise (gsr);
1034  return gsr;
1035 }
1036 
1037 
1038 static void
1039 gnc_reconcile_window_double_click_cb(GNCReconcileView *view, Split *split,
1040  gpointer data)
1041 {
1042  auto recnData = static_cast<RecnWindow*>(data);
1043  GNCSplitReg *gsr;
1044 
1045  /* This should never be true, but be paranoid */
1046  if (split == NULL)
1047  return;
1048 
1049  gsr = gnc_reconcile_window_open_register(recnData);
1050  if (gsr == NULL)
1051  return;
1052 
1053  /* Test for visibility of split */
1054  if (gnc_split_reg_clear_filter_for_split (gsr, split))
1055  gnc_plugin_page_register_clear_current_filter (GNC_PLUGIN_PAGE(recnData->page));
1056 
1057  gnc_split_reg_jump_to_split( gsr, split );
1058 }
1059 
1060 
1061 static void
1062 gnc_reconcile_window_focus_cb(GtkWidget *widget, GdkEventFocus *event,
1063  gpointer data)
1064 {
1065  auto recnData = static_cast<RecnWindow*>(data);
1066  GNCReconcileView *this_view, *other_view;
1067  GNCReconcileView *debit, *credit;
1068 
1069  this_view = GNC_RECONCILE_VIEW(widget);
1070 
1071  debit = GNC_RECONCILE_VIEW(recnData->debit);
1072  credit = GNC_RECONCILE_VIEW(recnData->credit);
1073 
1074  other_view = GNC_RECONCILE_VIEW(this_view == debit ? credit : debit);
1075 
1076  /* clear the *other* list so we always have no more than one selection */
1077  gnc_reconcile_view_unselect_all(other_view);
1078 }
1079 
1080 
1081 static gboolean
1082 gnc_reconcile_key_press_cb (GtkWidget *widget, GdkEventKey *event,
1083  gpointer data)
1084 {
1085  auto recnData = static_cast<RecnWindow*>(data);
1086  GtkWidget *this_view, *other_view;
1087  GtkWidget *debit, *credit;
1088 
1089  switch (event->keyval)
1090  {
1091  case GDK_KEY_Tab:
1092  case GDK_KEY_ISO_Left_Tab:
1093  break;
1094 
1095  default:
1096  return FALSE;
1097  }
1098 
1099  g_signal_stop_emission_by_name (widget, "key_press_event");
1100 
1101  this_view = widget;
1102 
1103  debit = recnData->debit;
1104  credit = recnData->credit;
1105 
1106  other_view = (this_view == debit ? credit : debit);
1107 
1108  gtk_widget_grab_focus (other_view);
1109 
1110  return TRUE;
1111 }
1112 
1113 
1114 static void
1115 gnc_reconcile_window_set_titles(RecnWindow *recnData)
1116 {
1117  const gchar *title;
1118 
1120  gtk_frame_set_label(GTK_FRAME(recnData->debit_frame), title);
1121 
1123  gtk_frame_set_label(GTK_FRAME(recnData->credit_frame), title);
1124 }
1125 
1126 
1127 static GtkWidget *
1128 gnc_reconcile_window_create_view_box(Account *account,
1129  GNCReconcileViewType type,
1130  RecnWindow *recnData,
1131  GtkWidget **list_save,
1132  GtkWidget **total_save)
1133 {
1134  GtkWidget *frame, *scrollWin, *view, *vbox, *label, *hbox;
1135  GtkWidget *vscroll;
1136  GtkRequisition nat_sb;
1137 
1138  frame = gtk_frame_new(NULL);
1139 
1140  if (type == RECLIST_DEBIT)
1141  recnData->debit_frame = frame;
1142  else
1143  recnData->credit_frame = frame;
1144 
1145  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
1146  gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
1147 
1148  view = gnc_reconcile_view_new(account, type, recnData->statement_date);
1149  *list_save = view;
1150 
1151  g_signal_connect(view, "toggle_reconciled",
1152  G_CALLBACK(gnc_reconcile_window_toggled_cb),
1153  recnData);
1154  g_signal_connect(view, "line_selected",
1155  G_CALLBACK(gnc_reconcile_window_row_cb),
1156  recnData);
1157  g_signal_connect(view, "button_press_event",
1158  G_CALLBACK(gnc_reconcile_window_button_press_cb),
1159  recnData);
1160  g_signal_connect(view, "double_click_split",
1161  G_CALLBACK(gnc_reconcile_window_double_click_cb),
1162  recnData);
1163  g_signal_connect(view, "focus_in_event",
1164  G_CALLBACK(gnc_reconcile_window_focus_cb),
1165  recnData);
1166  g_signal_connect(view, "key_press_event",
1167  G_CALLBACK(gnc_reconcile_key_press_cb),
1168  recnData);
1169 
1170  scrollWin = gtk_scrolled_window_new (NULL, NULL);
1171  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrollWin),
1172  GTK_POLICY_AUTOMATIC,
1173  GTK_POLICY_AUTOMATIC);
1174  gtk_container_set_border_width(GTK_CONTAINER(scrollWin), 5);
1175 
1176  gtk_container_add(GTK_CONTAINER(frame), scrollWin);
1177  gtk_container_add(GTK_CONTAINER(scrollWin), view);
1178  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
1179 
1180  // get the vertical scroll bar width
1181  vscroll = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (scrollWin));
1182  gtk_widget_get_preferred_size (vscroll, NULL, &nat_sb);
1183 
1184  // add xpadding to recn column so scrollbar does not cover
1185  gnc_reconcile_view_add_padding (GNC_RECONCILE_VIEW(view), REC_RECN, nat_sb.width);
1186 
1187  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1188  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
1189  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1190 
1191  label = gtk_label_new(_("Total"));
1192  gnc_label_set_alignment(label, 1.0, 0.5);
1193  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
1194 
1195  label = gtk_label_new("");
1196  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1197  *total_save = label;
1198  gtk_widget_set_margin_end (GTK_WIDGET(label), 10 + nat_sb.width);
1199 
1200  return vbox;
1201 }
1202 
1203 
1204 static Split *
1205 gnc_reconcile_window_get_current_split(RecnWindow *recnData)
1206 {
1207  GNCReconcileView *view;
1208  Split *split;
1209 
1210  view = GNC_RECONCILE_VIEW(recnData->debit);
1211  split = gnc_reconcile_view_get_current_split(view);
1212  if (split != NULL)
1213  return split;
1214 
1215  view = GNC_RECONCILE_VIEW(recnData->credit);
1216  split = gnc_reconcile_view_get_current_split(view);
1217 
1218  return split;
1219 }
1220 
1221 
1222 static void
1223 gnc_ui_reconcile_window_help_cb (GSimpleAction *simple,
1224  GVariant *parameter,
1225  gpointer user_data)
1226 {
1227  auto recnData = static_cast<RecnWindow*>(user_data);
1228  gnc_gnome_help (GTK_WINDOW(recnData->window), DF_MANUAL, DL_RECNWIN);
1229 }
1230 
1231 
1232 static void
1233 gnc_ui_reconcile_window_change_cb (GSimpleAction *simple,
1234  GVariant *parameter,
1235  gpointer user_data)
1236 {
1237  auto recnData = static_cast<RecnWindow*>(user_data);
1238  Account *account = recn_get_account (recnData);
1239  gnc_numeric new_ending = recnData->new_ending;
1240  time64 statement_date = recnData->statement_date;
1241 
1242  if (gnc_reverse_balance (account))
1243  new_ending = gnc_numeric_neg (new_ending);
1244  if (startRecnWindow (recnData->window, account, &new_ending, &statement_date,
1245  FALSE))
1246  {
1247  recnData->new_ending = new_ending;
1248  recnData->statement_date = statement_date;
1249  recnRecalculateBalance (recnData);
1250  }
1251 }
1252 
1253 
1254 static void
1255 gnc_ui_reconcile_window_balance_cb (GSimpleAction *simple,
1256  GVariant *parameter,
1257  gpointer user_data)
1258 {
1259  auto recnData = static_cast<RecnWindow*>(user_data);
1260  GNCSplitReg *gsr;
1261  Account *account;
1262  gnc_numeric balancing_amount;
1263  time64 statement_date;
1264 
1265 
1266  gsr = gnc_reconcile_window_open_register(recnData);
1267  if (gsr == NULL)
1268  return;
1269 
1270  account = recn_get_account(recnData);
1271  if (account == NULL)
1272  return;
1273 
1274  balancing_amount = recnRecalculateBalance(recnData);
1275  if (gnc_numeric_zero_p(balancing_amount))
1276  return;
1277 
1278  statement_date = recnData->statement_date;
1279  if (statement_date == 0)
1280  statement_date = gnc_time (NULL); // default to 'now'
1281 
1282  gnc_split_reg_balancing_entry(gsr, account, statement_date, balancing_amount);
1283 }
1284 
1285 
1286 static void
1287 gnc_ui_reconcile_window_rec_cb (GSimpleAction *simple,
1288  GVariant *parameter,
1289  gpointer user_data)
1290 {
1291  auto recnData = static_cast<RecnWindow*>(user_data);
1292  GNCReconcileView *debit, *credit;
1293 
1294  debit = GNC_RECONCILE_VIEW(recnData->debit);
1295  credit = GNC_RECONCILE_VIEW(recnData->credit);
1296 
1297  gnc_reconcile_view_set_list (debit, TRUE);
1298  gnc_reconcile_view_set_list (credit, TRUE);
1299 }
1300 
1301 
1302 static void
1303 gnc_ui_reconcile_window_unrec_cb (GSimpleAction *simple,
1304  GVariant *parameter,
1305  gpointer user_data)
1306 {
1307  auto recnData = static_cast<RecnWindow*>(user_data);
1308  GNCReconcileView *debit, *credit;
1309 
1310  debit = GNC_RECONCILE_VIEW(recnData->debit);
1311  credit = GNC_RECONCILE_VIEW(recnData->credit);
1312 
1313  gnc_reconcile_view_set_list (debit, FALSE);
1314  gnc_reconcile_view_set_list (credit, FALSE);
1315 }
1316 
1317 
1323 static GNCReconcileView *
1324 gnc_reconcile_window_get_selection_view (RecnWindow *recnData)
1325 {
1326  if (gnc_reconcile_view_num_selected (GNC_RECONCILE_VIEW (recnData->debit)) > 0)
1327  return GNC_RECONCILE_VIEW (recnData->debit);
1328 
1329  if (gnc_reconcile_view_num_selected (GNC_RECONCILE_VIEW (recnData->credit)) > 0)
1330  return GNC_RECONCILE_VIEW (recnData->credit);
1331 
1332  return NULL;
1333 }
1334 
1335 
1342 static void
1343 gnc_reconcile_window_delete_set_next_selection (RecnWindow *recnData, Split *split)
1344 {
1345  GNCReconcileView *view = gnc_reconcile_window_get_selection_view (recnData);
1346  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1347  Split *this_split = NULL;
1348  GtkTreeIter iter;
1349  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1350  GList *path_list, *node;
1351  GtkTreePath *save_del_path;
1352  Transaction* trans = xaccSplitGetParent (split); // parent transaction of the split to delete
1353 
1354  if (!view)
1355  return; // no selected split
1356 
1357  path_list = gtk_tree_selection_get_selected_rows (selection, &model);
1358  // get path of the first split selected - there should be only 1 selected
1359  node = g_list_first (path_list);
1360  if (!node)
1361  return;
1362  auto path = static_cast<GtkTreePath*>(node->data);
1363  save_del_path = gtk_tree_path_copy (path);
1364 
1365  gtk_tree_path_next (path);
1366  if (gtk_tree_model_get_iter (model, &iter, path))
1367  {
1368  do
1369  {
1370  gtk_tree_model_get (model, &iter, REC_POINTER, &this_split, -1);
1371  }
1372  while (xaccSplitGetParent (this_split) == trans && gtk_tree_model_iter_next (model, &iter));
1373  }
1374 
1375  if ((!this_split) || xaccSplitGetParent (this_split) == trans)
1376  {
1377  // There aren't any splits for a different transaction after the split to be deleted,
1378  // so find the previous split having a different parent transaction
1379  path = save_del_path; // split to be deleted
1380  if (gtk_tree_path_prev (path) && gtk_tree_model_get_iter (model, &iter, path))
1381  {
1382  do
1383  {
1384  gtk_tree_model_get (model, &iter, REC_POINTER, &this_split, -1);
1385  }
1386  while (xaccSplitGetParent (this_split) == trans && gtk_tree_model_iter_previous (model, &iter));
1387  }
1388  }
1389 
1390  gtk_tree_path_free (save_del_path);
1391  g_list_free_full (path_list, (GDestroyNotify) gtk_tree_path_free);
1392  if ((!this_split) || xaccSplitGetParent (this_split) == trans)
1393  return;
1394 
1395  gtk_tree_selection_select_iter (selection, &iter);
1396 }
1397 
1398 
1399 static void
1400 gnc_ui_reconcile_window_delete_cb (GSimpleAction *simple,
1401  GVariant *parameter,
1402  gpointer user_data)
1403 {
1404  auto recnData = static_cast<RecnWindow*>(user_data);
1405  Transaction *trans;
1406  Split *split;
1407 
1408  split = gnc_reconcile_window_get_current_split(recnData);
1409  /* This should never be true, but be paranoid */
1410  if (split == NULL)
1411  return;
1412 
1413  {
1414  const char *message = _("Are you sure you want to delete the selected "
1415  "transaction?");
1416  gboolean result;
1417 
1418  result = gnc_verify_dialog (GTK_WINDOW (recnData->window), FALSE, "%s", message);
1419 
1420  if (!result)
1421  return;
1422  }
1423 
1424  /* select the split that should be visible after the deletion */
1425  gnc_reconcile_window_delete_set_next_selection(recnData, split);
1426 
1427  gnc_suspend_gui_refresh ();
1428 
1429  trans = xaccSplitGetParent(split);
1430  xaccTransDestroy(trans);
1431 
1432  gnc_resume_gui_refresh ();
1433 }
1434 
1435 
1436 static void
1437 gnc_ui_reconcile_window_edit_cb (GSimpleAction *simple,
1438  GVariant *parameter,
1439  gpointer user_data)
1440 {
1441  auto recnData = static_cast<RecnWindow*>(user_data);
1442  GNCSplitReg *gsr;
1443  Split *split;
1444 
1445  split = gnc_reconcile_window_get_current_split (recnData);
1446  /* This should never be true, but be paranoid */
1447  if (split == NULL)
1448  return;
1449 
1450  gsr = gnc_reconcile_window_open_register(recnData);
1451  if (gsr == NULL)
1452  return;
1453 
1454  /* Test for visibility of split */
1455  if (gnc_split_reg_clear_filter_for_split (gsr, split))
1456  gnc_plugin_page_register_clear_current_filter (GNC_PLUGIN_PAGE(recnData->page));
1457 
1458  gnc_split_reg_jump_to_split_amount( gsr, split );
1459 }
1460 
1461 
1462 static char *
1463 gnc_recn_make_window_name(Account *account)
1464 {
1465  char *fullname;
1466  char *title;
1467 
1468  fullname = gnc_account_get_full_name(account);
1469  title = g_strconcat(fullname, " - ", _("Reconcile"), NULL);
1470 
1471  g_free(fullname);
1472 
1473  return title;
1474 }
1475 
1476 
1477 static void
1478 gnc_recn_set_window_name(RecnWindow *recnData)
1479 {
1480  char *title;
1481 
1482  title = gnc_recn_make_window_name (recn_get_account (recnData));
1483 
1484  gtk_window_set_title (GTK_WINDOW (recnData->window), title);
1485 
1486  g_free (title);
1487 }
1488 
1489 
1490 static void
1491 gnc_recn_edit_account_cb (GSimpleAction *simple,
1492  GVariant *parameter,
1493  gpointer user_data)
1494 {
1495  auto recnData = static_cast<RecnWindow*>(user_data);
1496  Account *account = recn_get_account (recnData);
1497 
1498  if (account == NULL)
1499  return;
1500 
1501  gnc_ui_edit_account_window (GTK_WINDOW (recnData->window), account);
1502 }
1503 
1504 
1505 static void
1506 gnc_recn_xfer_cb (GSimpleAction *simple,
1507  GVariant *parameter,
1508  gpointer user_data)
1509 {
1510  auto recnData = static_cast<RecnWindow*>(user_data);
1511  Account *account = recn_get_account (recnData);
1512 
1513  if (account == NULL)
1514  return;
1515 
1516  gnc_xfer_dialog (recnData->window, account);
1517 }
1518 
1519 
1520 static void
1521 gnc_recn_scrub_cb (GSimpleAction *simple,
1522  GVariant *parameter,
1523  gpointer user_data)
1524 {
1525  auto recnData = static_cast<RecnWindow*>(user_data);
1526  Account *account = recn_get_account (recnData);
1527 
1528  if (account == NULL)
1529  return;
1530 
1531  gnc_suspend_gui_refresh ();
1532 
1533  xaccAccountTreeScrubOrphans (account, gnc_window_show_progress);
1534  xaccAccountTreeScrubImbalance (account, gnc_window_show_progress);
1535 
1536  // XXX: Lots are disabled.
1537  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1538  xaccAccountTreeScrubLots(account);
1539 
1540  gnc_resume_gui_refresh ();
1541 }
1542 
1543 
1544 static void
1545 gnc_recn_open_cb (GSimpleAction *simple,
1546  GVariant *parameter,
1547  gpointer user_data)
1548 {
1549  auto recnData = static_cast<RecnWindow*>(user_data);
1550 
1551  gnc_reconcile_window_open_register(recnData);
1552 }
1553 
1554 
1555 static void
1556 gnc_get_reconcile_info (Account *account,
1557  gnc_numeric *new_ending,
1558  time64 *statement_date)
1559 {
1560  gboolean always_today;
1561  GDate date;
1562  time64 today;
1563 
1564  g_date_clear(&date, 1);
1565 
1566  always_today = gnc_prefs_get_bool(GNC_PREFS_GROUP_RECONCILE, GNC_PREF_ALWAYS_REC_TO_TODAY);
1567 
1568  if (!always_today &&
1569  xaccAccountGetReconcileLastDate (account, statement_date))
1570  {
1571  int months = 1, days = 0;
1572 
1573  gnc_gdate_set_time64(&date, *statement_date);
1574 
1575  xaccAccountGetReconcileLastInterval (account, &months, &days);
1576 
1577  if (months)
1578  {
1579  gboolean was_last_day_of_month = g_date_is_last_of_month(&date);
1580 
1581  g_date_add_months(&date, months);
1582 
1583  /* Track last day of the month, i.e. 1/31 -> 2/28 -> 3/31 */
1584  if (was_last_day_of_month)
1585  {
1586  g_date_set_day (&date, g_date_get_days_in_month(g_date_get_month(&date),
1587  g_date_get_year( &date)));
1588  }
1589  }
1590  else
1591  {
1592  g_date_add_days (&date, days);
1593  }
1594 
1595  *statement_date = gnc_time64_get_day_end_gdate (&date);
1596 
1597  today = gnc_time64_get_day_end (gnc_time (NULL));
1598  if (*statement_date > today)
1599  *statement_date = today;
1600  }
1601 
1602  xaccAccountGetReconcilePostponeDate (account, statement_date);
1603 
1604  if (xaccAccountGetReconcilePostponeBalance(account, new_ending))
1605  {
1606  if (gnc_reverse_balance(account))
1607  *new_ending = gnc_numeric_neg(*new_ending);
1608  }
1609  else
1610  {
1611  /* if the account wasn't previously postponed, try to predict
1612  * the statement balance based on the statement date.
1613  */
1614  *new_ending =
1615  gnc_ui_account_get_balance_as_of_date
1616  (account, *statement_date,
1618  }
1619 }
1620 
1621 
1622 static gboolean
1623 find_by_account (gpointer find_data, gpointer user_data)
1624 {
1625  auto account = GNC_ACCOUNT(find_data);
1626  auto recnData = static_cast<RecnWindow*>(user_data);
1627 
1628  if (!recnData)
1629  return FALSE;
1630 
1631  return guid_equal (&recnData->account, xaccAccountGetGUID (account));
1632 }
1633 
1634 
1635 static void
1636 recn_set_watches_one_account (gpointer data, gpointer user_data)
1637 {
1638  Account *account = (Account *)data;
1639  RecnWindow *recnData = (RecnWindow *)user_data;
1640 
1641  /* add a watch on the account */
1642  gnc_gui_component_watch_entity (recnData->component_id,
1643  xaccAccountGetGUID (account),
1644  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
1645 
1646  /* add a watch on each split for the account */
1647  for (auto split : xaccAccountGetSplits (account))
1648  {
1649  auto trans = xaccSplitGetParent (split);
1650  gnc_gui_component_watch_entity (recnData->component_id,
1651  xaccTransGetGUID (trans),
1652  QOF_EVENT_MODIFY
1653  | QOF_EVENT_DESTROY
1654  | GNC_EVENT_ITEM_CHANGED);
1655  }
1656 }
1657 
1658 
1659 static void
1660 recn_set_watches (RecnWindow *recnData)
1661 {
1662  gboolean include_children;
1663  Account *account;
1664  GList *accounts = NULL;
1665 
1666  gnc_gui_component_clear_watches (recnData->component_id);
1667 
1668  account = recn_get_account (recnData);
1669 
1670  include_children = xaccAccountGetReconcileChildrenStatus(account);
1671  if (include_children)
1672  accounts = gnc_account_get_descendants(account);
1673 
1674  /* match the account */
1675  accounts = g_list_prepend (accounts, account);
1676 
1677  g_list_foreach(accounts, recn_set_watches_one_account, recnData);
1678 
1679  g_list_free (accounts);
1680 }
1681 
1682 
1683 static void
1684 refresh_handler (GHashTable *changes, gpointer user_data)
1685 {
1686  auto recnData = static_cast<RecnWindow*>(user_data);
1687  const EventInfo *info;
1688  Account *account;
1689 
1690  account = recn_get_account (recnData);
1691  if (!account)
1692  {
1693  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1694  return;
1695  }
1696 
1697  if (changes)
1698  {
1699  info = gnc_gui_get_entity_events (changes, &recnData->account);
1700  if (info && (info->event_mask & QOF_EVENT_DESTROY))
1701  {
1702  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1703  return;
1704  }
1705  }
1706 
1707  gnc_reconcile_window_set_titles(recnData);
1708  recn_set_watches (recnData);
1709 
1710  recnRefresh (recnData);
1711 }
1712 
1713 
1714 static void
1715 close_handler (gpointer user_data)
1716 {
1717  auto recnData = static_cast<RecnWindow*>(user_data);
1718 
1719  gnc_save_window_size(GNC_PREFS_GROUP_RECONCILE, GTK_WINDOW(recnData->window));
1720  gtk_widget_destroy (recnData->window);
1721 }
1722 
1723 
1724 /********************************************************************\
1725  * recnWindow *
1726  * opens up the window to reconcile an account *
1727  * *
1728  * Args: parent - the parent of this window *
1729  * account - the account to reconcile *
1730  * Return: recnData - the instance of this RecnWindow *
1731 \********************************************************************/
1732 RecnWindow *
1733 recnWindow (GtkWidget *parent, Account *account)
1734 {
1735  gnc_numeric new_ending;
1736  gboolean enable_subaccounts;
1737  time64 statement_date;
1738 
1739  if (account == NULL)
1740  return NULL;
1741 
1742  /* The last time reconciliation was attempted during the current execution
1743  * of gnucash, the date was stored. Use that date if possible. This helps
1744  * with balancing multiple accounts for which statements are issued at the
1745  * same time, like multiple bank accounts on a single statement. Otherwise
1746  * use the end of today to ensure we include any transactions posted
1747  * today.
1748  */
1749  if (!gnc_reconcile_last_statement_date)
1750  statement_date = gnc_time64_get_day_end(gnc_time (NULL));
1751  else
1752  statement_date = gnc_reconcile_last_statement_date;
1753 
1754  gnc_get_reconcile_info (account, &new_ending, &statement_date);
1755 
1756  enable_subaccounts = !has_account_different_commodities(account);
1757  /* Popup a little window to prompt the user to enter the
1758  * ending balance for his/her bank statement */
1759  if (!startRecnWindow (parent, account, &new_ending, &statement_date,
1760  enable_subaccounts))
1761  return NULL;
1762 
1763  return recnWindowWithBalance (parent, account, new_ending, statement_date);
1764 }
1765 
1766 
1767 static GActionEntry recWindow_actions_entries [] =
1768 {
1769  { "RecnChangeInfoAction", gnc_ui_reconcile_window_change_cb, NULL, NULL, NULL },
1770  { "RecnFinishAction", recnFinishCB, NULL, NULL, NULL },
1771  { "RecnPostponeAction", recnPostponeCB, NULL, NULL, NULL },
1772  { "RecnCancelAction", recnCancelCB, NULL, NULL, NULL },
1773 
1774  { "AccountOpenAccountAction", gnc_recn_open_cb, NULL, NULL, NULL },
1775  { "AccountEditAccountAction", gnc_recn_edit_account_cb, NULL, NULL, NULL },
1776  { "AccountTransferAction", gnc_recn_xfer_cb, NULL, NULL, NULL },
1777  { "AccountCheckRepairAction", gnc_recn_scrub_cb, NULL, NULL, NULL },
1778 
1779  { "TransBalanceAction", gnc_ui_reconcile_window_balance_cb, NULL, NULL, NULL },
1780  { "TransEditAction", gnc_ui_reconcile_window_edit_cb, NULL, NULL, NULL },
1781  { "TransDeleteAction", gnc_ui_reconcile_window_delete_cb, NULL, NULL, NULL },
1782  { "TransRecAction", gnc_ui_reconcile_window_rec_cb, NULL, NULL, NULL },
1783  { "TransUnRecAction", gnc_ui_reconcile_window_unrec_cb, NULL, NULL, NULL },
1784 
1785  { "HelpHelpAction", gnc_ui_reconcile_window_help_cb, NULL, NULL, NULL },
1786 };
1788 static guint recnWindow_n_actions_entries = G_N_ELEMENTS(recWindow_actions_entries);
1789 
1790 #ifdef MAC_INTEGRATION
1791 /* Enable GtkMenuItem accelerators */
1792 static gboolean
1793 can_activate_cb(GtkWidget *widget, guint signal_id, gpointer data)
1794 {
1795  //return gtk_widget_is_sensitive (widget);
1796  return TRUE;
1797 }
1798 #endif
1799 
1800 /********************************************************************\
1801  * recnWindowWithBalance
1802  *
1803  * Opens up the window to reconcile an account, but with ending
1804  * balance and statement date already given.
1805  *
1806  * Args: parent - The parent widget of the new window
1807  * account - The account to reconcile
1808  * new_ending - The amount for ending balance
1809  * statement_date - The date of the statement
1810  * Return: recnData - the instance of this RecnWindow
1811 \********************************************************************/
1812 RecnWindow *
1813 recnWindowWithBalance (GtkWidget *parent, Account *account, gnc_numeric new_ending,
1814  time64 statement_date)
1815 {
1816  RecnWindow *recnData;
1817  GtkWidget *statusbar;
1818  GtkWidget *vbox;
1819  GtkWidget *dock;
1820 
1821  if (account == NULL)
1822  return NULL;
1823 
1824  recnData = static_cast<RecnWindow*>(gnc_find_first_gui_component (WINDOW_RECONCILE_CM_CLASS,
1825  find_by_account, account));
1826  if (recnData)
1827  return recnData;
1828 
1829  recnData = g_new0 (RecnWindow, 1);
1830 
1831  recnData->account = *xaccAccountGetGUID (account);
1832 
1833 
1834  recnData->component_id =
1835  gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS,
1836  refresh_handler, close_handler,
1837  recnData);
1838  gnc_gui_component_set_session (recnData->component_id, gnc_get_current_session());
1839 
1840  recn_set_watches (recnData);
1841 
1842  gnc_reconcile_last_statement_date = statement_date;
1843 
1844  recnData->new_ending = new_ending;
1845  recnData->statement_date = statement_date;
1846  recnData->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1847  recnData->delete_refresh = FALSE;
1848 
1849  gnc_recn_set_window_name(recnData);
1850 
1851  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1852  gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
1853  gtk_container_add(GTK_CONTAINER(recnData->window), vbox);
1854 
1855  // Set the name for this dialog so it can be easily manipulated with css
1856  gtk_widget_set_name (GTK_WIDGET(recnData->window), "gnc-id-reconcile");
1857 
1858  dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1859  gtk_box_set_homogeneous (GTK_BOX (dock), FALSE);
1860  gtk_widget_show(dock);
1861  gtk_box_pack_start(GTK_BOX (vbox), dock, FALSE, TRUE, 0);
1862 
1863  {
1864  GtkToolbar *tool_bar;
1865  GMenuModel *menu_model;
1866  GtkWidget *menu_bar;
1867  const gchar *ui = GNUCASH_RESOURCE_PREFIX "/gnc-reconcile-window.ui";
1868  GError *error = NULL;
1869 
1870  recnData->accel_group = gtk_accel_group_new ();
1871  recnData->builder = gtk_builder_new ();
1872 
1873  gtk_builder_add_from_resource (recnData->builder, ui, &error);
1874 
1875  gtk_builder_set_translation_domain (recnData->builder, PROJECT_NAME);
1876 
1877  if (error)
1878  {
1879  g_critical ("Failed to load ui resource %s, Error %s", ui, error->message);
1880  g_error_free (error);
1881  gnc_unregister_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
1882  g_free (recnData);
1883  return NULL;
1884  }
1885 
1886  menu_model = (GMenuModel *)gtk_builder_get_object (recnData->builder, "recwin-menu");
1887  menu_bar = gtk_menu_bar_new_from_model (menu_model);
1888  gtk_container_add (GTK_CONTAINER(vbox), menu_bar);
1889 #ifdef MAC_INTEGRATION
1890  auto theApp = static_cast<GtkosxApplication*>(g_object_new (GTKOSX_TYPE_APPLICATION, NULL));
1891  gtk_widget_hide (menu_bar);
1892  gtk_widget_set_no_show_all (menu_bar, TRUE);
1893  if (GTK_IS_MENU_ITEM (menu_bar))
1894  menu_bar = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_bar));
1895 
1896  gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL (menu_bar));
1897 #endif
1898  tool_bar = (GtkToolbar *)gtk_builder_get_object (recnData->builder, "recwin-toolbar");
1899 
1900  gtk_toolbar_set_style (GTK_TOOLBAR(tool_bar), GTK_TOOLBAR_BOTH);
1901  gtk_toolbar_set_icon_size (GTK_TOOLBAR(tool_bar),
1902  GTK_ICON_SIZE_SMALL_TOOLBAR);
1903 
1904  gtk_container_add (GTK_CONTAINER(vbox), GTK_WIDGET(tool_bar));
1905 
1906  gtk_window_add_accel_group (GTK_WINDOW(recnData->window), recnData->accel_group);
1907 
1908  // need to add the accelerator keys
1909  gnc_add_accelerator_keys_for_menu (menu_bar, menu_model, recnData->accel_group);
1910 
1911 #ifdef MAC_INTEGRATION
1912  gtkosx_application_sync_menubar (theApp);
1913  g_signal_connect (menu_bar, "can-activate-accel",
1914  G_CALLBACK(can_activate_cb), NULL);
1915  g_object_unref (theApp);
1916  theApp = NULL;
1917 #endif
1918 
1919  recnData->simple_action_group = g_simple_action_group_new ();
1920 
1921  g_action_map_add_action_entries (G_ACTION_MAP(recnData->simple_action_group),
1922  recWindow_actions_entries,
1923  recnWindow_n_actions_entries,
1924  recnData);
1925 
1926  gtk_widget_insert_action_group (GTK_WIDGET(recnData->window), "recwin",
1927  G_ACTION_GROUP(recnData->simple_action_group));
1928  }
1929 
1930  g_signal_connect(recnData->window, "popup-menu",
1931  G_CALLBACK(gnc_reconcile_window_popup_menu_cb), recnData);
1932 
1933  statusbar = gtk_statusbar_new();
1934  gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
1935 
1936  g_signal_connect (recnData->window, "destroy",
1937  G_CALLBACK(recn_destroy_cb), recnData);
1938  g_signal_connect (recnData->window, "delete_event",
1939  G_CALLBACK(recn_delete_cb), recnData);
1940  g_signal_connect (recnData->window, "key_press_event",
1941  G_CALLBACK(recn_key_press_cb), recnData);
1942 
1943 
1944  /* if account has a reconciled split where reconciled_date is
1945  later than statement_date, emit a warning into statusbar */
1946  {
1947  GtkStatusbar *bar = GTK_STATUSBAR (statusbar);
1948  guint context = gtk_statusbar_get_context_id (bar, "future_dates");
1949  GtkWidget *box = gtk_statusbar_get_message_area (bar);
1950  GtkWidget *image = gtk_image_new_from_icon_name
1951  ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
1952 
1953  // find an already reconciled split whose statement date
1954  // is after *this* reconciliation statement date.
1955  auto has_later_recn_statement_date = [statement_date](const Split *split)
1956  { return (xaccSplitGetReconcile (split) == YREC &&
1957  xaccSplitGetDateReconciled (split) > statement_date); };
1958 
1959  if (auto split = gnc_account_find_split (account, has_later_recn_statement_date, true))
1960  {
1961  auto datestr = qof_print_date (xaccTransGetDate (xaccSplitGetParent (split)));
1962  auto recnstr = qof_print_date (xaccSplitGetDateReconciled (split));
1963  PWARN ("split posting_date=%s, recn_date=%s", datestr, recnstr);
1964 
1965  gtk_statusbar_push (bar, context, _("WARNING! Account contains \
1966 splits whose reconcile date is after statement date. Reconciliation may be \
1967 difficult."));
1968 
1969  gtk_widget_set_tooltip_text (GTK_WIDGET (bar), _("This account \
1970 has splits whose Reconciled Date is after this reconciliation statement date. \
1971 These splits may make reconciliation difficult. If this is the case, you may \
1972 use Find Transactions to find them, unreconcile, and re-reconcile."));
1973 
1974  gtk_box_pack_start (GTK_BOX(box), image, FALSE, FALSE, 0);
1975  gtk_box_reorder_child (GTK_BOX(box), image, 0);
1976 
1977  g_free (datestr);
1978  g_free (recnstr);
1979  }
1980  }
1981 
1982  /* The main area */
1983  {
1984  GtkWidget *frame = gtk_frame_new(NULL);
1985  GtkWidget *main_area = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
1986  GtkWidget *debcred_area = gtk_grid_new ();
1987  GtkWidget *debits_box;
1988  GtkWidget *credits_box;
1989 
1990  gtk_box_set_homogeneous (GTK_BOX (main_area), FALSE);
1991  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 10);
1992 
1993  /* Force a reasonable starting size */
1994  gtk_window_set_default_size(GTK_WINDOW(recnData->window), 800, 600);
1995  gnc_restore_window_size (GNC_PREFS_GROUP_RECONCILE,
1996  GTK_WINDOW(recnData->window), GTK_WINDOW(parent));
1997 
1998  gtk_container_add(GTK_CONTAINER(frame), main_area);
1999  gtk_container_set_border_width(GTK_CONTAINER(main_area), 10);
2000 
2001  debits_box = gnc_reconcile_window_create_view_box
2002  (account, RECLIST_DEBIT, recnData,
2003  &recnData->debit, &recnData->total_debit);
2004 
2005  // Add a style context for this widget so it can be easily manipulated with css
2006  gnc_widget_style_context_add_class (GTK_WIDGET(debits_box), "gnc-class-debits");
2007 
2008  credits_box = gnc_reconcile_window_create_view_box
2009  (account, RECLIST_CREDIT, recnData,
2010  &recnData->credit, &recnData->total_credit);
2011 
2012  // Add a style context for this widget so it can be easily manipulated with css
2013  gnc_widget_style_context_add_class (GTK_WIDGET(credits_box), "gnc-class-credits");
2014 
2015  GNC_RECONCILE_VIEW(recnData->debit)->sibling = GNC_RECONCILE_VIEW(recnData->credit);
2016  GNC_RECONCILE_VIEW(recnData->credit)->sibling = GNC_RECONCILE_VIEW(recnData->debit);
2017 
2018  gtk_box_pack_start(GTK_BOX(main_area), debcred_area, TRUE, TRUE, 0);
2019 
2020  gtk_grid_set_column_homogeneous (GTK_GRID(debcred_area), TRUE);
2021  gtk_grid_set_column_spacing (GTK_GRID(debcred_area), 15);
2022  gtk_grid_attach (GTK_GRID(debcred_area), debits_box, 0, 0, 1, 1);
2023  gtk_widget_set_hexpand (debits_box, TRUE);
2024  gtk_widget_set_vexpand (debits_box, TRUE);
2025  gtk_widget_set_halign (debits_box, GTK_ALIGN_FILL);
2026  gtk_widget_set_valign (debits_box, GTK_ALIGN_FILL);
2027 
2028  gtk_grid_attach (GTK_GRID(debcred_area), credits_box, 1, 0, 1, 1);
2029  gtk_widget_set_hexpand (credits_box, TRUE);
2030  gtk_widget_set_vexpand (credits_box, TRUE);
2031  gtk_widget_set_halign (credits_box, GTK_ALIGN_FILL);
2032  gtk_widget_set_valign (credits_box, GTK_ALIGN_FILL);
2033 
2034  {
2035  GtkWidget *hbox, *title_vbox, *value_vbox;
2036  GtkWidget *totals_hbox, *frame, *title, *value;
2037 
2038  /* lower horizontal bar below reconcile lists */
2039  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
2040  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
2041  gtk_box_pack_start(GTK_BOX(main_area), hbox, FALSE, FALSE, 0);
2042 
2043  /* frame to hold totals */
2044  frame = gtk_frame_new(NULL);
2045  gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
2046 
2047  // Set the name for this widget so it can be easily manipulated with css
2048  gtk_widget_set_name (GTK_WIDGET(frame), "gnc-id-reconcile-totals");
2049 
2050  /* hbox to hold title/value vboxes */
2051  totals_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
2052  gtk_box_set_homogeneous (GTK_BOX (totals_hbox), FALSE);
2053  gtk_container_add(GTK_CONTAINER(frame), totals_hbox);
2054  gtk_container_set_border_width(GTK_CONTAINER(totals_hbox), 5);
2055 
2056  /* vbox to hold titles */
2057  title_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
2058  gtk_box_set_homogeneous (GTK_BOX (title_vbox), FALSE);
2059  gtk_box_pack_start(GTK_BOX(totals_hbox), title_vbox, FALSE, FALSE, 0);
2060 
2061  /* vbox to hold values */
2062  value_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
2063  gtk_box_set_homogeneous (GTK_BOX (value_vbox), FALSE);
2064  gtk_box_pack_start(GTK_BOX(totals_hbox), value_vbox, TRUE, TRUE, 0);
2065 
2066  /* statement date title/value */
2067  title = gtk_label_new(_("Statement Date"));
2068  gnc_label_set_alignment(title, 1.0, 0.5);
2069  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
2070 
2071  value = gtk_label_new("");
2072  recnData->recn_date = value;
2073  gnc_label_set_alignment(value, 1.0, 0.5);
2074  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
2075 
2076  /* starting balance title/value */
2077  title = gtk_label_new(_("Starting Balance"));
2078  gnc_label_set_alignment(title, 1.0, 0.5);
2079  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 3);
2080 
2081  value = gtk_label_new("");
2082  recnData->starting = value;
2083  gnc_label_set_alignment(value, 1.0, 0.5);
2084  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 3);
2085 
2086  /* ending balance title/value */
2087  title = gtk_label_new(_("Ending Balance"));
2088  gnc_label_set_alignment(title, 1.0, 0.5);
2089  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
2090 
2091  value = gtk_label_new("");
2092  recnData->ending = value;
2093  gnc_label_set_alignment(value, 1.0, 0.5);
2094  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
2095 
2096  /* reconciled balance title/value */
2097  title = gtk_label_new(_("Reconciled Balance"));
2098  gnc_label_set_alignment(title, 1.0, 0.5);
2099  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
2100 
2101  value = gtk_label_new("");
2102  recnData->reconciled = value;
2103  gnc_label_set_alignment(value, 1.0, 0.5);
2104  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
2105 
2106  /* difference title/value */
2107  title = gtk_label_new(_("Difference"));
2108  gnc_label_set_alignment(title, 1.0, 0.5);
2109  gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
2110 
2111  value = gtk_label_new("");
2112  recnData->difference = value;
2113  gnc_label_set_alignment(value, 1.0, 0.5);
2114  gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
2115  }
2116 
2117  /* Set up the data */
2118  recnRefresh (recnData);
2119  }
2120 
2121  /* Allow resize */
2122  gtk_window_set_resizable(GTK_WINDOW(recnData->window), TRUE);
2123  gtk_widget_show_all(recnData->window);
2124 
2125  gnc_reconcile_window_set_titles(recnData);
2126 
2127  recnRecalculateBalance(recnData);
2128 
2129  gnc_window_adjust_for_screen(GTK_WINDOW(recnData->window));
2130 
2131  /* Set the sort orders of the debit and credit tree views */
2132  gnc_query_sort_order(GNC_QUERY_VIEW(recnData->debit), REC_DATE, GTK_SORT_ASCENDING);
2133  gnc_query_sort_order(GNC_QUERY_VIEW(recnData->credit), REC_DATE, GTK_SORT_ASCENDING);
2134 
2135  gtk_widget_grab_focus (recnData->debit);
2136 
2137  { // align the Totals value with that of the amount column
2138  gint recn_widthc = gnc_reconcile_view_get_column_width (GNC_RECONCILE_VIEW(recnData->credit), REC_RECN);
2139  gint recn_widthd = gnc_reconcile_view_get_column_width (GNC_RECONCILE_VIEW(recnData->debit), REC_RECN);
2140 
2141  gtk_widget_set_margin_end (GTK_WIDGET(recnData->total_credit), 10 + recn_widthc);
2142  gtk_widget_set_margin_end (GTK_WIDGET(recnData->total_debit), 10 + recn_widthd);
2143  }
2144  return recnData;
2145 }
2146 
2147 
2148 /********************************************************************\
2149  * gnc_ui_reconcile_window_raise *
2150  * shows and raises an account editing window *
2151  * *
2152  * Args: editAccData - the edit window structure *
2153 \********************************************************************/
2154 void
2155 gnc_ui_reconcile_window_raise(RecnWindow * recnData)
2156 {
2157  if (recnData == NULL)
2158  return;
2159 
2160  if (recnData->window == NULL)
2161  return;
2162 
2163  gtk_window_present(GTK_WINDOW(recnData->window));
2164 }
2165 
2166 GtkWindow *
2167 gnc_ui_reconcile_window_get_window (RecnWindow * recnData)
2168 {
2169  if (recnData == NULL || recnData->window == NULL)
2170  return NULL;
2171  return GTK_WINDOW(recnData->window);
2172 }
2173 
2174 
2175 
2176 /********************************************************************\
2177  * recn_destroy_cb *
2178  * frees memory allocated for an recnWindow, and other cleanup *
2179  * stuff *
2180  * *
2181  * Args: w - the widget that called us *
2182  * data - the data struct for this window *
2183  * Return: none *
2184 \********************************************************************/
2185 static void
2186 recn_destroy_cb (GtkWidget *w, gpointer data)
2187 {
2188  auto recnData = static_cast<RecnWindow*>(data);
2189  gchar **actions = g_action_group_list_actions (G_ACTION_GROUP(recnData->simple_action_group));
2190  gint num_actions = g_strv_length (actions);
2191 
2192  gnc_unregister_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2193 
2194  if (recnData->delete_refresh)
2195  gnc_resume_gui_refresh ();
2196 
2197  if (recnData->builder)
2198  g_object_unref(recnData->builder);
2199 
2200  if (recnData->accel_group)
2201  g_object_unref(recnData->accel_group);
2202 
2203  //Disable the actions, the handlers try to access recnData
2204  for (gint i = 0; i < num_actions; i++)
2205  {
2206  GAction *action = g_action_map_lookup_action (G_ACTION_MAP(recnData->simple_action_group), actions[i]);
2207  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
2208  }
2209  g_strfreev (actions);
2210  g_free (recnData);
2211 }
2212 
2213 
2214 static void
2215 recn_cancel(RecnWindow *recnData)
2216 {
2217  gboolean changed = FALSE;
2218 
2219  if (gnc_reconcile_view_changed(GNC_RECONCILE_VIEW(recnData->credit)))
2220  changed = TRUE;
2221  if (gnc_reconcile_view_changed(GNC_RECONCILE_VIEW(recnData->debit)))
2222  changed = TRUE;
2223 
2224  if (changed)
2225  {
2226  const char *message = _("You have made changes to this reconcile "
2227  "window. Are you sure you want to cancel?");
2228  if (!gnc_verify_dialog (GTK_WINDOW (recnData->window), FALSE, "%s", message))
2229  return;
2230  }
2231 
2232  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2233 }
2234 
2235 
2236 static gboolean
2237 recn_delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
2238 {
2239  auto recnData = static_cast<RecnWindow*>(data);
2240 
2241  recn_cancel(recnData);
2242  return TRUE;
2243 }
2244 
2245 
2246 static gboolean
2247 recn_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2248 {
2249  auto recnData = static_cast<RecnWindow*>(data);
2250 
2251  if (event->keyval == GDK_KEY_Escape)
2252  {
2253  recn_cancel(recnData);
2254  return TRUE;
2255  }
2256  else
2257  {
2258  return FALSE;
2259  }
2260 }
2261 
2262 
2263 /********************************************************************\
2264  * find_payment_account *
2265  * find an account that 'looks like' a payment account for the *
2266  * given account. This really only makes sense for credit card *
2267  * accounts. *
2268  * *
2269  * Args: account - the account to look in *
2270  * Return: a candidate payment account or NULL if none was found *
2271 \********************************************************************/
2272 static Account *
2273 find_payment_account(Account *account)
2274 {
2275  if (account == nullptr)
2276  return nullptr;
2277 
2278  const auto& splits = xaccAccountGetSplits (account);
2279 
2280  /* Search backwards to find the latest payment */
2281  for (auto it = splits.rbegin(); it != splits.rend(); it++)
2282  {
2283  auto split = *it;
2284 
2285  /* ignore 'purchases' */
2287  continue;
2288 
2289  for (auto n = xaccTransGetSplitList (xaccSplitGetParent(split)); n; n = n->next)
2290  {
2291  auto s = GNC_SPLIT(n->data);
2292  if (s == split)
2293  continue;
2294 
2295  auto a = xaccSplitGetAccount(s);
2296  if (a == account)
2297  continue;
2298 
2299  auto type = xaccAccountGetType(a);
2300  if (type == ACCT_TYPE_BANK || type == ACCT_TYPE_CASH || type == ACCT_TYPE_ASSET)
2301  return a;
2302  }
2303  }
2304 
2305  return nullptr;
2306 }
2307 
2308 static void
2309 acct_traverse_descendants (Account *acct, std::function<void(Account*)> fn)
2310 {
2311  fn (acct);
2313  gnc_account_foreach_descendant (acct, fn);
2314 }
2315 
2316 /********************************************************************\
2317  * recnFinishCB *
2318  * saves reconcile information *
2319  * *
2320  * Args: w - the widget that called us *
2321  * data - the data struct for this window *
2322  * Return: none *
2323 \********************************************************************/
2324 static void
2325 recnFinishCB (GSimpleAction *simple,
2326  GVariant *parameter,
2327  gpointer user_data)
2328 {
2329  auto recnData = static_cast<RecnWindow*>(user_data);
2330  gboolean auto_payment;
2331  Account *account;
2332  time64 date;
2333 
2334  if (!gnc_numeric_zero_p (recnRecalculateBalance(recnData)))
2335  {
2336  const char *message = _("The account is not balanced. "
2337  "Are you sure you want to finish?");
2338  if (!gnc_verify_dialog (GTK_WINDOW (recnData->window), FALSE, "%s", message))
2339  return;
2340  }
2341 
2342  date = recnData->statement_date;
2343 
2344  gnc_suspend_gui_refresh ();
2345 
2346  recnData->delete_refresh = TRUE;
2347  account = recn_get_account (recnData);
2348 
2349  acct_traverse_descendants (account, xaccAccountBeginEdit);
2350  gnc_reconcile_view_commit(GNC_RECONCILE_VIEW(recnData->credit), date);
2351  gnc_reconcile_view_commit(GNC_RECONCILE_VIEW(recnData->debit), date);
2352  acct_traverse_descendants (account, xaccAccountCommitEdit);
2353 
2354  auto_payment = gnc_prefs_get_bool(GNC_PREFS_GROUP_RECONCILE, GNC_PREF_AUTO_CC_PAYMENT);
2355 
2357  xaccAccountSetReconcileLastDate (account, date);
2358 
2359  if (auto_payment &&
2360  (xaccAccountGetType (account) == ACCT_TYPE_CREDIT) &&
2361  (gnc_numeric_negative_p (recnData->new_ending)))
2362  {
2363  Account *payment_account;
2364  XferDialog *xfer;
2365 
2366  xfer = gnc_xfer_dialog (GTK_WIDGET (gnc_ui_get_main_window (recnData->window)), account);
2367 
2368  gnc_xfer_dialog_set_amount(xfer, gnc_numeric_neg (recnData->new_ending));
2369 
2370  payment_account = find_payment_account (account);
2371  if (payment_account != NULL)
2372  gnc_xfer_dialog_select_from_account (xfer, payment_account);
2373  }
2374 
2375  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2376 }
2377 
2378 
2379 /********************************************************************\
2380  * recnPostponeCB *
2381  * saves reconcile information for later use *
2382  * *
2383  * Args: w - the widget that called us *
2384  * data - the data struct for this window *
2385  * Return: none *
2386 \********************************************************************/
2387 static void
2388 recnPostponeCB (GSimpleAction *simple,
2389  GVariant *parameter,
2390  gpointer user_data)
2391 {
2392  auto recnData = static_cast<RecnWindow*>(user_data);
2393  Account *account;
2394 
2395  {
2396  const char *message = _("Do you want to postpone this reconciliation "
2397  "and finish it later?");
2398  if (!gnc_verify_dialog (GTK_WINDOW (recnData->window), FALSE, "%s", message))
2399  return;
2400  }
2401 
2402  gnc_suspend_gui_refresh ();
2403 
2404  recnData->delete_refresh = TRUE;
2405  account = recn_get_account (recnData);
2406 
2407  acct_traverse_descendants (account, xaccAccountBeginEdit);
2408  gnc_reconcile_view_postpone (GNC_RECONCILE_VIEW(recnData->credit));
2409  gnc_reconcile_view_postpone (GNC_RECONCILE_VIEW(recnData->debit));
2410  acct_traverse_descendants (account, xaccAccountCommitEdit);
2411 
2412  xaccAccountSetReconcilePostponeDate (account, recnData->statement_date);
2413  xaccAccountSetReconcilePostponeBalance (account, recnData->new_ending);
2414 
2415  gnc_close_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData);
2416 }
2417 
2418 
2419 static void
2420 recnCancelCB (GSimpleAction *simple,
2421  GVariant *parameter,
2422  gpointer user_data)
2423 {
2424  auto recnData = static_cast<RecnWindow*>(user_data);
2425  recn_cancel(recnData);
2426 }
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
Create a new "register" plugin page, given a pointer to an account.
High-Level API for imposing Lot constraints.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean xaccAccountGetAutoInterest(const Account *acc)
Get the "auto interest" flag for an account.
Definition: Account.cpp:4126
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
The instance data structure for a content plugin.
Date and Time handling routines.
This file contains the functions to present a gui to the user for creating a new account or editing a...
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3233
gtk helper routines.
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2712
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
DOCUMENT ME!
Definition: Account.cpp:4535
STRUCTS.
Functions that are supported by all types of windows.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
gpointer gnc_account_foreach_descendant_until(const Account *acc, AccountCb2 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:3210
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
void xaccAccountSetReconcileLastInterval(Account *acc, int months, int days)
DOCUMENT ME!
Definition: Account.cpp:4565
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gint gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
The cash account type is used to denote a shoe-box or pillowcase stuffed with * cash.
Definition: Account.h:110
const char * gnc_account_get_debit_string(GNCAccountType acct_type)
Get the debit string associated with this account type.
Definition: Account.cpp:4042
void gnc_ui_edit_account_window(GtkWindow *parent, Account *account)
Display a window for editing the attributes of an existing account.
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.
Definition: gnc-date.cpp:113
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gboolean xaccAccountGetReconcilePostponeDate(const Account *acc, time64 *postpone_date)
DOCUMENT ME!
Definition: Account.cpp:4575
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
#define xaccAccountGetGUID(X)
Definition: Account.h:252
convert single-entry accounts to clean double-entry
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:608
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3271
Functions providing a register page for the GnuCash UI.
Account public routines (C++ api)
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...
#define YREC
The Split has been reconciled.
Definition: Split.h:74
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition: guid.cpp:237
void xaccAccountTreeScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountTreeScrubOrphans() method performs this scrub for the indicated account and its childr...
Definition: Scrub.cpp:173
void xaccAccountClearReconcilePostpone(Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4625
The bank account type denotes a savings or checking account held at a bank.
Definition: Account.h:107
void xaccAccountSetReconcilePostponeDate(Account *acc, time64 postpone_date)
DOCUMENT ME!
Definition: Account.cpp:4590
Gnome specific utility functions.
Additional event handling code.
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
DOCUMENT ME!
Definition: Account.cpp:4615
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
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:3485
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition: Account.cpp:4517
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
#define xaccTransGetGUID(X)
Definition: Transaction.h:788
Generic api to store and retrieve preferences.
void gnc_add_accelerator_keys_for_menu(GtkWidget *menu, GMenuModel *model, GtkAccelGroup *accel_group)
Add accelerator keys for menu item widgets.
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition: Account.cpp:3010
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4849
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
DOCUMENT ME!
Definition: Account.cpp:4544
time64 xaccSplitGetDateReconciled(const Split *split)
Retrieve the date when the Split was reconciled.
Definition: Split.cpp:1823
const char * gnc_account_get_credit_string(GNCAccountType acct_type)
Get the credit string associated with this account type.
Definition: Account.cpp:4054
Split * gnc_account_find_split(const Account *acc, std::function< bool(const Split *)> predicate, bool reverse)
scans account split list (in forward or reverse order) until predicate split->bool returns true...
Definition: Account.cpp:1166
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1247
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1473
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
time64 gnc_time64_get_day_end_gdate(const GDate *date)
The gnc_time64_get_day_end() routine will take the given time in GLib GDate format and adjust it to t...
Definition: gnc-date.cpp:1435
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3367
time64 gnc_time64_get_today_end(void)
The gnc_time64_get_today_end() routine returns a time64 value corresponding to the last second of tod...
Definition: gnc-date.cpp:1359
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:260
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
void gnc_plugin_page_register_clear_current_filter(GncPluginPage *plugin_page)
This function clears the registers current filter.
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:1319
File path resolution utility functions.
Not a type.
Definition: Account.h:105
The type used to store guids in C.
Definition: guid.h:75
GNCSplitReg * gnc_plugin_page_register_get_gsr(GncPluginPage *plugin_page)
Get the GNCSplitReg data structure associated with this register page.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1514
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
void xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
DOCUMENT ME!
Definition: Account.cpp:4836
The Credit card account is used to denote credit (e.g.
Definition: Account.h:113
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
gboolean xaccAccountGetReconcilePostponeBalance(const Account *acc, gnc_numeric *balance)
DOCUMENT ME!
Definition: Account.cpp:4599
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048