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
63static QofLogModule log_module = GNC_MOD_GUI_SX;
65static void sxftd_freq_combo_changed( GtkWidget *w, gpointer user_data );
66static void gnc_sx_trans_window_response_cb(GtkDialog *dialog, gint response, gpointer data);
68static void sxftd_destroy( GtkWidget *w, gpointer user_data );
70typedef 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;
109static 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 ();
279sxftd_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);
568sxftd_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);
616sxftd_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));
636gnc_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);
732sxftd_update_excal_adapt( GObject *o, gpointer ud )
735 sxftd_update_example_cal( sxfti );
743gnc_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));
Anchor Scheduled Transaction info in a book.
Scheduled Transactions public handling routines.
Date and Time handling routines.
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
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...
time64 gnc_time(time64 *tbuf)
get the current time
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...
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
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.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
#define DEBUG(format, args...)
Print a debugging message.
#define LEAVE(format, args...)
Print a function exit debugging message.
#define ENTER(format, args...)
Print a function entry debugging message.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
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_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
SchedXaction * xaccSchedXactionMalloc(QofBook *book)
Creates and initializes a scheduled transaction.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
Set to an invalid GDate to turn off 'end-date' definition.
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
void xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
Set to '0' to turn off number-of-occurrences definition.
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.