29 #include "dialog-sx-editor.h" 30 #include "dialog-sx-from-trans.h" 31 #include "dialog-utils.h" 32 #include "gnc-component-manager.h" 34 #include "gnc-date-edit.h" 35 #include "gnc-dense-cal-store.h" 36 #include "gnc-dense-cal.h" 38 #include "engine-helpers.h" 43 #include "Recurrence.h" 45 #include "SchedXaction.hpp" 47 #include "SX-ttinfo.hpp" 48 #include <glib/gi18n.h> 52 #define SXFTD_ERRNO_UNBALANCED_XACTION 3 53 #define SXFTD_ERRNO_OPEN_XACTION -3 55 #define SXFTD_EXCAL_NUM_MONTHS 4 56 #define SXFTD_EXCAL_MONTHS_PER_COL 4 58 #define SXFTD_RESPONSE_ADVANCED 100 61 #define G_LOG_DOMAIN GNC_MOD_GUI_SX 63 static QofLogModule log_module = GNC_MOD_GUI_SX;
65 static void sxftd_freq_combo_changed( GtkWidget *w, gpointer user_data );
66 static void gnc_sx_trans_window_response_cb(GtkDialog *dialog, gint response, gpointer data);
68 static void sxftd_destroy( GtkWidget *w, gpointer user_data );
70 typedef enum { FREQ_DAILY = 0,
84 GtkComboBox *freq_combo;
86 GtkToggleButton *ne_but;
87 GtkToggleButton *ed_but;
88 GtkToggleButton *oc_but;
89 GtkEntry *n_occurences;
94 GncDenseCalStore *dense_cal_model;
95 GncDenseCal *example_cal;
109 static void sxftd_update_excal_adapt( GObject *o, gpointer ud );
115 void (*handlerFn)(GObject*,gpointer);
129 {
"never_end_button",
"clicked", sxftd_update_excal_adapt },
130 {
"end_on_date_button",
"clicked", sxftd_update_excal_adapt },
131 {
"n_occurrences_button",
"clicked", sxftd_update_excal_adapt },
132 {
"n_occurrences_entry",
"changed", sxftd_update_excal_adapt },
139 for (i = 0; callbacks[i].name != NULL; i++)
141 w = GTK_WIDGET(gtk_builder_get_object(sxfti->builder, callbacks[i].name));
143 g_signal_connect (G_OBJECT(w), callbacks[i].signal,
144 G_CALLBACK(callbacks[i].handlerFn),
148 g_signal_connect (G_OBJECT(sxfti->dialog),
"response",
149 G_CALLBACK (gnc_sx_trans_window_response_cb),
159 retval.type = BAD_END;
160 g_date_clear( &(retval.end_date), 1 );
161 retval.n_occurrences = 0;
163 if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->ne_but)))
165 retval.type = NEVER_END;
169 if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->ed_but)))
172 retval.type = END_ON_DATE;
173 g_date_clear( &(retval.end_date), 1 );
174 end_tt = gnc_date_edit_get_date(sxfti->endDateGDE);
179 if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->oc_but) ))
183 const gchar *text = gtk_entry_get_text (GTK_ENTRY(sxfti->n_occurences));
184 if (!text || !text[0])
191 n_occs = strtoul(text, &endptr, 10);
198 retval.type = END_AFTER_N_OCCS;
199 retval.n_occurrences = n_occs;
210 Transaction *tr = sxfti->trans;
211 GList *splits = NULL;
212 TTInfoPtr tti = std::make_shared<TTInfo>();
214 gnc_numeric runningBalance;
215 gnc_numeric split_value;
218 runningBalance = gnc_numeric_zero();
221 tti->set_num(gnc_get_num_action(tr, NULL));
225 tti->clear_template_splits ();
229 auto sp = GNC_SPLIT(splits->data);
230 ttsi = std::make_shared<TTSplitInfo>();
231 ttsi->set_action (gnc_get_num_action(NULL, sp));
241 gnc_default_print_info(FALSE) );
242 ttsi->set_debit_formula (tmpStr);
248 gnc_default_print_info(FALSE) );
249 ttsi->set_credit_formula (tmpStr);
255 tti->append_template_split (ttsi);
259 && !gnc_verify_dialog (GTK_WINDOW (sxfti->dialog),
261 _(
"The Scheduled Transaction Editor " 262 "cannot automatically balance " 264 "Should it still be " 267 return SXFTD_ERRNO_UNBALANCED_XACTION;
270 gnc_suspend_gui_refresh ();
271 xaccSchedXactionSetTemplateTrans (sxfti->sx, { tti }, gnc_get_current_book ());
272 gnc_resume_gui_refresh ();
279 sxftd_update_schedule(
SXFromTransInfo *sxfti, GDate *date, GList **recurrences)
286 index = gtk_combo_box_get_active(GTK_COMBO_BOX(sxfti->freq_combo));
293 recurrenceSet(r, 1, PERIOD_DAY, date, WEEKEND_ADJ_NONE);
294 *recurrences = g_list_append(*recurrences, r);
302 int mult = (index == FREQ_BIWEEKLY ? 2 : 1);
303 recurrenceSet(r, mult, PERIOD_WEEK, date, WEEKEND_ADJ_NONE);
304 *recurrences = g_list_append(*recurrences, r);
313 int mult = (index == FREQ_MONTHLY
315 : (index == FREQ_QUARTERLY
318 recurrenceSet(r, mult, PERIOD_MONTH, date, recurrenceGetWeekendAdjust(r));
319 *recurrences = g_list_append(*recurrences, r);
324 g_critical(
"nonexistent frequency selected");
334 const char *transName;
336 GList *schedule = NULL;
338 GDate date, nextDate;
344 if ( ! sxfti->trans )
350 return SXFTD_ERRNO_OPEN_XACTION;
355 sxfti->ne_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder,
"never_end_button"));
356 sxfti->ed_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder,
"end_on_date_button"));
357 sxfti->oc_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder,
"n_occurrences_button"));
358 sxfti->n_occurences = GTK_ENTRY(gtk_builder_get_object(sxfti->builder,
"n_occurrences_entry"));
365 sxfti->name = GTK_ENTRY(gtk_builder_get_object(sxfti->builder,
"name_entry" ));
367 gtk_editable_insert_text( GTK_EDITABLE(sxfti->name), transName,
368 (strlen(transName) *
sizeof(
char)), &pos );
370 sxfti_attach_callbacks(sxfti);
374 int num_marks = SXFTD_EXCAL_NUM_MONTHS * 31;
376 w = GTK_WIDGET(gtk_builder_get_object(sxfti->builder,
"ex_cal_frame" ));
377 sxfti->dense_cal_model = gnc_dense_cal_store_new(num_marks);
378 sxfti->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (GTK_WINDOW(sxfti->dialog),
379 GNC_DENSE_CAL_MODEL(sxfti->dense_cal_model)));
380 g_object_ref_sink(sxfti->example_cal);
382 g_assert(sxfti->example_cal);
383 gnc_dense_cal_set_num_months( sxfti->example_cal, SXFTD_EXCAL_NUM_MONTHS );
384 gnc_dense_cal_set_months_per_col( sxfti->example_cal, SXFTD_EXCAL_MONTHS_PER_COL );
385 gtk_container_add( GTK_CONTAINER(w), GTK_WIDGET(sxfti->example_cal) );
390 GtkWidget *paramTable = GTK_WIDGET(gtk_builder_get_object(sxfti->builder,
"param_table" ));
391 sxfti->startDateGDE = GNC_DATE_EDIT( gnc_date_edit_new (
gnc_time (NULL), FALSE, FALSE));
393 gtk_grid_attach (GTK_GRID(paramTable), GTK_WIDGET(sxfti->startDateGDE), 1, 2, 1, 1);
394 gtk_widget_set_halign (GTK_WIDGET(sxfti->startDateGDE), GTK_ALIGN_FILL);
395 gtk_widget_set_valign (GTK_WIDGET(sxfti->startDateGDE), GTK_ALIGN_FILL);
396 gtk_widget_set_hexpand (GTK_WIDGET(sxfti->startDateGDE), TRUE);
397 gtk_widget_set_vexpand (GTK_WIDGET(sxfti->startDateGDE), FALSE);
398 g_object_set (GTK_WIDGET(sxfti->startDateGDE),
"margin", 0, NULL);
400 g_signal_connect( sxfti->startDateGDE,
"date-changed",
401 G_CALLBACK( sxftd_update_excal_adapt ),
405 GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object(sxfti->builder,
"end_date_hbox" ));
407 GNC_DATE_EDIT( gnc_date_edit_new (
gnc_time (NULL),
409 gtk_box_pack_start( GTK_BOX( endDateBox ),
410 GTK_WIDGET( sxfti->endDateGDE ),
412 g_signal_connect( sxfti->endDateGDE,
"date-changed",
413 G_CALLBACK( sxftd_update_excal_adapt ),
421 sxfti->freq_combo = GTK_COMBO_BOX(gtk_builder_get_object(sxfti->builder,
"freq_combo_box"));
422 gtk_combo_box_set_active(GTK_COMBO_BOX(sxfti->freq_combo), 0);
423 g_signal_connect( sxfti->freq_combo,
"changed",
424 G_CALLBACK(sxftd_freq_combo_changed),
426 sxftd_update_schedule( sxfti, &date, &schedule);
427 recurrenceListNextInstance(schedule, &date, &nextDate);
428 recurrenceListFree(&schedule);
430 gnc_date_edit_set_time( sxfti->startDateGDE, start_tt );
432 g_signal_connect( G_OBJECT(sxfti->name),
"destroy",
433 G_CALLBACK(sxftd_destroy),
436 sxftd_update_example_cal( sxfti );
446 GList *schedule = NULL;
448 guint sxftd_errno = 0;
450 SchedXaction *sx = sxfti->sx;
457 sxftd_update_schedule(sxfti, &date, &schedule);
458 if (sxftd_errno == 0)
461 xaccSchedXactionSetStartDate( sx, &date );
464 end_info = sxftd_get_end_info(sxfti);
466 switch (end_info.type)
475 case END_AFTER_N_OCCS:
489 gboolean autoCreateState, notifyState;
496 xaccSchedXactionSetAutoCreate( sx,
498 (autoCreateState & notifyState) );
502 xaccSchedXactionSetAdvanceCreation( sx, daysInAdvance );
506 xaccSchedXactionSetAdvanceReminder( sx, daysInAdvance );
509 if ( sxftd_add_template_trans( sxfti ) != 0 )
511 sxftd_errno = SXFTD_ERRNO_UNBALANCED_XACTION;
521 if ( sxfti->sx && delete_sx )
523 gnc_sx_begin_edit(sxfti->sx);
528 gtk_widget_destroy (GTK_WIDGET (sxfti->dialog));
537 guint sx_error = sxftd_compute_sx(sxfti);
540 && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION)
542 g_critical(
"sxftd_compute_sx after ok_clicked [%d]", sx_error);
546 if ( sx_error == SXFTD_ERRNO_UNBALANCED_XACTION )
548 gnc_error_dialog (GTK_WINDOW (sxfti->dialog),
"%s",
549 _(
"The Scheduled Transaction is unbalanced. " 550 "You are strongly encouraged to correct this situation." ) );
552 book = gnc_get_current_book ();
553 sxes = gnc_book_get_schedxactions(book);
554 gnc_sxes_add_sx(sxes, sxfti->sx);
557 sxftd_close(sxfti, FALSE);
568 sxftd_freq_combo_changed( GtkWidget *w, gpointer user_data )
571 GDate date, nextDate;
573 GList *schedule = NULL;
578 g_date_clear(&nextDate, 1);
579 sxftd_update_schedule(sxfti, &date, &schedule);
580 recurrenceListNextInstance(schedule, &date, &nextDate);
582 gnc_date_edit_set_time( sxfti->startDateGDE, tmp_tt );
584 recurrenceListFree(&schedule);
585 sxftd_update_example_cal( sxfti );
592 guint sx_error = sxftd_compute_sx(sxfti);
593 GMainContext *context;
595 if ( sx_error != 0 && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION )
599 g_warning(
"something bad happened in sxftd_compute_sx [%d]", sx_error);
602 gtk_widget_hide( sxfti->dialog );
604 context = g_main_context_default();
605 while (g_main_context_iteration(context, FALSE));
611 sxftd_close(sxfti, FALSE);
616 sxftd_destroy( GtkWidget *w, gpointer user_data )
622 gnc_sx_begin_edit(sxfti->sx);
626 if (sxfti->dense_cal_model)
627 g_object_unref(G_OBJECT(sxfti->dense_cal_model));
628 if (sxfti->example_cal)
629 g_object_unref(G_OBJECT(sxfti->example_cal));
636 gnc_sx_trans_window_response_cb (GtkDialog *dialog,
642 ENTER(
" dialog %p, response %d, sx %p", dialog, response, sxfti);
645 case GTK_RESPONSE_OK:
647 sxftd_ok_clicked(sxfti);
649 case SXFTD_RESPONSE_ADVANCED:
651 sxftd_advanced_clicked(sxfti);
653 case GTK_RESPONSE_CANCEL:
656 sxftd_close(sxfti, TRUE);
672 GDate date, startDate, nextDate;
673 GList *schedule = NULL;
676 get = sxftd_get_end_info( sxfti );
678 tmp_tt = gnc_date_edit_get_date( sxfti->startDateGDE );
681 sxftd_update_schedule(sxfti, &date, &schedule);
686 g_date_subtract_days(&date, 1);
687 g_date_clear(&nextDate, 1);
688 recurrenceListNextInstance(schedule, &date, &nextDate);
690 gnc_dense_cal_store_update_name (sxfti->dense_cal_model,
691 gtk_entry_get_text (sxfti->name));
694 gchar *schedule_desc;
695 schedule_desc = recurrenceListToCompactString(schedule);
696 gnc_dense_cal_store_update_info(sxfti->dense_cal_model, schedule_desc);
697 g_free(schedule_desc);
701 gtk_widget_set_sensitive( GTK_WIDGET(sxfti->endDateGDE), (
get.type == END_ON_DATE) );
702 gtk_widget_set_sensitive( GTK_WIDGET(sxfti->n_occurences), (
get.type == END_AFTER_N_OCCS) );
708 gnc_dense_cal_store_update_recurrences_no_end(sxfti->dense_cal_model, &date, schedule);
711 gnc_dense_cal_store_update_recurrences_date_end(sxfti->dense_cal_model, &date, schedule, &
get.end_date);
713 case END_AFTER_N_OCCS:
714 gnc_dense_cal_store_update_recurrences_count_end(sxfti->dense_cal_model, &date, schedule,
get.n_occurrences);
717 g_warning(
"unknown get.type [%d]\n",
get.type);
721 gnc_dense_cal_set_month( sxfti->example_cal, g_date_get_month( &startDate ) );
722 gnc_dense_cal_set_year( sxfti->example_cal, g_date_get_year( &startDate ) );
724 recurrenceListFree(&schedule);
732 sxftd_update_excal_adapt( GObject *o, gpointer ud )
735 sxftd_update_example_cal( sxfti );
743 gnc_sx_create_from_trans( GtkWindow *parent, Transaction *trans )
750 builder = gtk_builder_new();
752 gnc_builder_add_from_file (builder ,
"dialog-sx.glade",
"freq_liststore");
754 gnc_builder_add_from_file (builder ,
"dialog-sx.glade",
"sx_from_real_trans_dialog");
755 dialog = GTK_WIDGET(gtk_builder_get_object (builder,
"sx_from_real_trans_dialog"));
758 gtk_widget_set_name (GTK_WIDGET(dialog),
"gnc-id-sx-from-trans");
759 gnc_widget_style_context_add_class (GTK_WIDGET(dialog),
"gnc-class-sx");
761 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
763 sxfti->builder = builder;
764 sxfti->dialog = dialog;
765 sxfti->trans = trans;
769 if ( (sx_error = sxftd_init( sxfti )) < 0 )
771 if ( sx_error == SXFTD_ERRNO_OPEN_XACTION )
773 gnc_error_dialog (NULL,
"%s",
774 _(
"Cannot create a Scheduled Transaction " 775 "from a Transaction currently " 776 "being edited. Please Enter the " 777 "Transaction before Scheduling." ) );
778 sxftd_destroy (NULL, sxfti);
783 g_error(
"sxftd_init: %d", sx_error);
787 gtk_widget_show_all(GTK_WIDGET(sxfti->dialog));
789 gtk_builder_connect_signals(builder, sxfti);
790 g_object_unref(G_OBJECT(builder));
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
Date and Time handling routines.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing. ...
utility functions for the GnuCash UI
void xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
Set to '0' to turn off number-of-occurrences definition.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
#define DEBUG(format, args...)
Print a debugging message.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
#define ENTER(format, args...)
Print a function entry debugging message.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
Find the least common multiple of the arguments' denominators and use that as the denominator of the ...
Anchor Scheduled Transaction info in a book.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
All type declarations for the whole Gnucash engine.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
Generic api to store and retrieve preferences.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
time64 gnc_time(time64 *tbuf)
get the current time
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
SchedXaction * xaccSchedXactionMalloc(QofBook *book)
Creates and initializes a scheduled transaction.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Scheduled Transactions public handling routines.
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
Set to an invalid GDate to turn off 'end-date' definition.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
time64 gnc_time64_get_day_start_gdate(const GDate *date)
The gnc_time64_get_day_start() routine will take the given time in GLib GDate format and adjust it to...
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.