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