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