GnuCash  5.6-150-g038405b370+
assistant-loan.cpp
1 /********************************************************************\
2  * assistant-loan.c : An Assistant for setting up loan-repayment *
3  * scheduled transactions. *
4  * Copyright (C) 2002,2007 Joshua Sled <jsled@asynchronous.org> *
5  * Copyright (C) 2006 David Hampton <hampton@employees.org> *
6  * Copyright (C) 2011 Robert Fewell *
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 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
29 
30 #include <config.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <math.h>
34 #include "assistant-loan.h"
35 #include "SchedXaction.h"
36 #include "SchedXaction.hpp"
37 #include "SX-book.h"
38 #include "SX-ttinfo.hpp"
39 #include "gnc-amount-edit.h"
40 #include "gnc-account-sel.h"
41 #include "gnc-date.h"
42 #include "gnc-exp-parser.h"
43 #include "gnc-component-manager.h"
44 #include "dialog-utils.h"
45 #include "Account.h"
46 #include "gnc-ui.h"
47 #include "gnc-gui-query.h"
48 #include "gnc-ui-util.h"
49 #include "gnc-frequency.h"
50 #include "gnc-engine.h"
51 #ifdef __MINGW32__
52 #include <Windows.h>
53 #endif
54 
55 #include <gnc-locale-utils.hpp>
56 #include <boost/locale.hpp>
57 #include <string>
58 #include <sstream>
59 #include <iomanip>
60 #ifdef __MINGW32__
61 #include <codecvt>
62 #endif
63 
64 namespace bl = boost::locale;
65 
66 #define DIALOG_LOAN_ASSISTANT_CM_CLASS "assistant-loan-setup"
67 
68 static QofLogModule log_module = GNC_MOD_ASSISTANT;
69 
70 enum loan_cols
71 {
72  LOAN_COL_DATE = 0,
73  LOAN_COL_PAYMENT,
74  LOAN_COL_PRINCIPAL,
75  LOAN_COL_INTEREST,
76  NUM_LOAN_COLS
77 };
78 
79 typedef enum
80 {
81  CURRENT_YEAR,
82  NOW_PLUS_ONE,
83  WHOLE_LOAN,
84  CUSTOM
85 } REV_RANGE_OPTS;
86 
87 struct LoanAssistantData_;
88 
93 typedef struct RepayOptData_
94 {
95  gboolean enabled;
96  gboolean optValid;
97  gboolean FreqUniq;
98  char *name; /* { "insurance", "pmi", "taxes", ... } */
99  char *txnMemo;
100  float amount;
101  gboolean throughEscrowP;
102  gboolean specSrcAcctP;
103  Account *to;
104  Account *from; /* If NULL { If throughEscrowP, then through escrowAcct };
105  * else: undefined. */
106  GList *schedule;
107  /* If NULL, part of repayment; otherwise: defined
108  * here. */
109  GDate *startDate;
110 } RepayOptData;
111 
115 typedef struct RepayOptDataDefault_
116 {
117  const char *name;
118  const char *defaultTxnMemo;
119  gboolean escrowDefault;
120  gboolean specSrcAcctDefault;
122 
123 static RepayOptDataDefault REPAY_DEFAULTS[] =
124 {
125  /* { name, default txn memo, throughEscrowP, specSrcAcctP } */
126  { N_("Taxes"), N_("Tax Payment"), TRUE, FALSE },
127  { N_("Insurance"), N_("Insurance Payment"), TRUE, FALSE },
128  /* Translators: PMI stands for Private Mortgage Insurance. */
129  { N_("PMI"), N_("PMI Payment"), TRUE, FALSE },
130  { N_("Other Expense"), N_("Miscellaneous Payment"), FALSE, FALSE },
131  { NULL }
132 };
133 
137 typedef struct RepayOptUI_
138 {
139  /* must be stated this way [instead of 'LoanAssistantData*'] because of
140  * forward decl. */
141  struct LoanAssistantData_ *ldd;
142  GtkCheckButton *optCb;
143  GtkCheckButton *escrowCb;
144  RepayOptData *optData;
146 
147 typedef enum
148 {
149  GNC_FIXED = 0,
150  GNC_VARIABLE,
151  GNC_VARIABLE_3_1 = GNC_VARIABLE,
152  GNC_VARIABLE_5_1,
153  GNC_VARIABLE_7_1,
154  GNC_VARIABLE_10_1,
155  /* ... FIXME */
156 } LoanType;
157 
158 typedef enum
159 {
160  GNC_MONTHS = 0,
161  GNC_YEARS
162 } PeriodSize;
163 
164 /*type of interest rate entered*/
165 typedef enum
166 {
167  GNC_IRATE_SIMPLE,
168  GNC_IRATE_APR_DAILY,
169  GNC_IRATE_APR_WEEKLY,
170  GNC_IRATE_APR_MONTHLY,
171  GNC_IRATE_APR_QUARTERLY,
172  GNC_IRATE_APR_ANNUALLY
173 } IRateType;
174 
180 typedef struct rev_repayment_row
181 {
182  GDate date;
183  gnc_numeric *numCells;
185 
189 typedef struct LoanData_
190 {
191  Account *primaryAcct;
192  gnc_numeric principal;
193  float interestRate;
194  IRateType rateType;
195  LoanType type;
196  GList *loan_schedule;
197  GDate *startDate;
198  GDate *varStartDate;
199  int numPer;
200  PeriodSize perSize;
201  int numMonRemain;
202 
203  char *repMemo;
204  char *repAmount;
205  Account *repFromAcct;
206  Account *repPriAcct;
207  Account *repIntAcct;
208  Account *escrowAcct;
209  GList *repayment_schedule;
210  GDate *repStartDate;
211 
212  int repayOptCount;
213  RepayOptData **repayOpts;
214 
215  /* Data concerning the review of repayments. */
216  int revNumPmts;
217  int revRepayOptToColMap[ (sizeof(REPAY_DEFAULTS)
218  / sizeof(RepayOptDataDefault))
219  - 1 ];
220  GList *revSchedule;
221 } LoanData;
222 
226 typedef struct LoanAssistantData_
227 {
228  GtkWidget *window;
229  GtkWidget *assistant;
230 
231  LoanData ld;
232  /* The UI-side storage of repayment data; this is 1:1 with the array
233  * in LoanData */
234  RepayOptUIData **repayOptsUI;
235 
236  /* Current index of the payment opt for multiplexing the 'payment'
237  * page. */
238  int currentIdx;
239 
240  /* widgets */
241  /* prm = params */
242  GtkGrid *prmTable;
243  GNCAccountSel *prmAccountGAS;
244  GNCAmountEdit *prmOrigPrincGAE;
245  GtkSpinButton *prmIrateSpin;
246  GtkComboBox *prmType;
247  GtkFrame *prmVarFrame;
248  GncFrequency *prmVarGncFreq;
249  GNCDateEdit *prmStartDateGDE;
250  GtkSpinButton *prmLengthSpin;
251  GtkComboBox *prmLengthType;
252  GtkSpinButton *prmRemainSpin;
253  GtkComboBox *prmIrateType;
254 
255  /* opt = options */
256  GtkBox *optVBox;
257  GtkCheckButton *optEscrowCb;
258  GtkBox *optEscrowHBox;
259  GNCAccountSel *optEscrowGAS;
260 
261  /* rep = repayment */
262  GtkEntry *repTxnName;
263  GtkGrid *repTable;
264  GtkEntry *repAmtEntry;
265  GNCAccountSel *repAssetsFromGAS;
266  GNCAccountSel *repPrincToGAS;
267  GNCAccountSel *repIntToGAS;
268  GtkFrame *repFreqFrame;
269  GncFrequency *repGncFreq;
270 
271  /* pay = payment[s] */
272  GtkEntry *payTxnName;
273  GtkEntry *payAmtEntry;
274  GNCAccountSel *payAcctFromGAS;
275  GNCAccountSel *payAcctEscToGAS;
276  GNCAccountSel *payAcctEscFromGAS;
277  GNCAccountSel *payAcctToGAS;
278  GtkGrid *payTable;
279  GtkCheckButton *payUseEscrow;
280  GtkCheckButton *paySpecSrcAcct;
281  GtkLabel *payAcctFromLabel;
282  GtkLabel *payEscToLabel;
283  GtkLabel *payEscFromLabel;
284  GtkRadioButton *payTxnFreqPartRb;
285  GtkRadioButton *payTxnFreqUniqRb;
286  GtkBox *payFreqHBox;
287  GncFrequency *payGncFreq;
288 
289  /* rev = review */
290  GtkComboBox *revRangeOpt;
291  GtkFrame *revDateFrame;
292  GtkGrid *revTable;
293  GNCDateEdit *revStartDate;
294  GNCDateEdit *revEndDate;
295  GtkScrolledWindow *revScrollWin;
296  GtkTreeView *revView;
298 
302 typedef struct toCreateSX_
303 {
305  gchar *name;
307  GDate start, last, end;
309  GList *schedule;
311  gint instNum;
313  TTInfoPtr mainTxn;
315  TTInfoPtr escrowTxn;
316 } toCreateSX;
317 
318 /**************************************************************************/
319 
320 static void loan_assistant_window_destroy_cb( GtkWidget *object, gpointer user_data );
321 static void loan_assistant_close_handler( gpointer user_data );
322 static void loan_assistant_data_init( LoanAssistantData *ldd );
323 
324 static void loan_info_prep( GtkAssistant *assistant, gpointer user_data );
325 static void loan_info_prm_type_cb( GtkWidget *w, gpointer user_data );
326 static void loan_info_calc_update_cb( GtkWidget *widget, gpointer user_data );
327 void loan_info_page_valid_cb( GtkWidget *widget, gpointer user_data );
328 static gboolean loan_info_page_complete( GtkAssistant *assistant, gpointer user_data );
329 static void loan_info_page_save( GtkAssistant *assistant, gpointer user_data );
330 
331 static void loan_opt_prep( GtkAssistant *assistant, gpointer user_data );
332 static void loan_opt_toggled_cb( GtkToggleButton *tb, gpointer user_data );
333 static void loan_opt_consistency_cb( GtkToggleButton *tb, gpointer user_data );
334 static void loan_opt_escrow_toggle_cb( GtkToggleButton *tb, gpointer user_data );
335 static void loan_opt_escrow_toggled_cb( GtkToggleButton *tb, gpointer user_data );
336 void loan_opt_page_valid_cb (GtkWidget *widget, gpointer user_data );
337 static gboolean loan_opt_page_complete( GtkAssistant *assistant, gpointer user_data );
338 
339 static void loan_rep_prep ( GtkAssistant *assistant, gpointer user_data );
340 void loan_rep_page_valid_cb (GtkWidget *widget, gpointer user_data );
341 static gboolean loan_rep_page_complete( GtkAssistant *assistant, gpointer user_data );
342 static void loan_rep_page_save( GtkAssistant *assistant, gpointer user_data );
343 
344 static void loan_pay_prep ( GtkAssistant *assistant, gpointer user_data );
345 static void loan_pay_use_esc_setup( LoanAssistantData *ldd, gboolean newState );
346 static void loan_pay_use_esc_toggle_cb( GtkToggleButton *tb, gpointer user_data );
347 static void loan_pay_spec_src_setup( LoanAssistantData *ldd, gboolean newState );
348 static void loan_pay_spec_src_toggle_cb( GtkToggleButton *tb, gpointer user_data );
349 static void loan_pay_freq_toggle_cb( GtkToggleButton *tb, gpointer user_data );
350 static void loan_pay_page_valid_cb (GtkWidget *widget, gpointer user_data );
351 static gboolean loan_pay_complete( GtkAssistant *assistant, gpointer user_data );
352 static gboolean loan_pay_all_opt_valid ( GtkAssistant *assistant, gpointer user_data );
353 static void loan_pay_back_button_cb( GtkButton *button, gpointer user_data );
354 static void loan_pay_next_button_cb( GtkButton *button, gpointer user_data );
355 
356 static void loan_rev_prep ( GtkAssistant *assistant, gpointer user_data );
357 static void loan_rev_recalc_schedule( LoanAssistantData *ldd );
358 static void loan_rev_range_opt_changed_cb( GtkComboBox *combo, gpointer user_data );
359 static void loan_rev_range_changed_cb( GNCDateEdit *gde, gpointer user_data );
360 static void loan_rev_get_loan_range( LoanAssistantData *ldd, GDate *start, GDate *end );
361 static void loan_rev_get_dates( LoanAssistantData *ldd, GDate *start, GDate *end );
362 static void loan_rev_update_view( LoanAssistantData *ldd, GDate *start, GDate *end );
363 static void loan_rev_sched_list_free( gpointer data, gpointer user_data );
364 static void loan_rev_hash_to_list( gpointer key, gpointer val, gpointer user_data );
365 static void loan_rev_hash_free_date_keys( gpointer key, gpointer val, gpointer user_data );
366 
367 static void loan_get_pmt_formula( LoanAssistantData *ldd, GString *gstr );
368 static void loan_get_ppmt_formula( LoanAssistantData *ldd, GString *gstr );
369 static void loan_get_ipmt_formula( LoanAssistantData *ldd, GString *gstr );
370 static float loan_apr_to_simple_formula (float rate, float compounding_periods);
371 
372 static void loan_create_sxes( LoanAssistantData *ldd );
373 static void loan_create_sx_from_tcSX( LoanAssistantData *ldd, toCreateSX *tcSX );
374 static void loan_tcSX_free( gpointer data, gpointer user_data );
375 
376 extern "C" {
377 void loan_assistant_prepare( GtkAssistant *assistant, GtkWidget *page, gpointer user_data );
378 void loan_assistant_finish( GtkAssistant *gtkassistant, gpointer user_data );
379 void loan_assistant_cancel( GtkAssistant *gtkassistant, gpointer user_data );
380 void loan_assistant_close( GtkAssistant *gtkassistant, gpointer user_data );
381 }
382 
383 /*****************************************************************************/
384 
385 
386 static
387 void
388 loan_assistant_close_handler( gpointer user_data )
389 {
390  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
391  gtk_widget_destroy( ldd->window );
392 }
393 
394 
395 static
396 void
397 loan_assistant_window_destroy_cb( GtkWidget *object, gpointer user_data )
398 {
399  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
400 
401  g_assert( ldd );
402 
403  gnc_unregister_gui_component_by_data (DIALOG_LOAN_ASSISTANT_CM_CLASS, ldd);
404 
405  /* free alloc'd mem; cleanup */
406 
407  /* repay opts */
408  {
409  int i;
410 
411  g_date_free( ldd->ld.startDate );
412  g_date_free( ldd->ld.varStartDate );
413  recurrenceListFree(&ldd->ld.loan_schedule);
414 
415  if ( ldd->ld.repMemo )
416  g_free( ldd->ld.repMemo );
417 
418  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
419  {
420  RepayOptData *rod = ldd->ld.repayOpts[i];
421  if ( rod->name )
422  g_free( rod->name );
423  if ( rod->txnMemo )
424  g_free( rod->txnMemo );
425 
426  if ( rod->startDate )
427  g_date_free( rod->startDate );
428 
429  if (rod->schedule != NULL)
430  recurrenceListFree(&rod->schedule);
431 
432  g_free( ldd->ld.repayOpts[i] );
433  g_free( ldd->repayOptsUI[i] );
434  }
435  g_free( ldd->ld.repayOpts );
436  g_free( ldd->repayOptsUI );
437 
438  if ( ldd->ld.repAmount )
439  g_free( ldd->ld.repAmount );
440 
441  g_date_free( ldd->ld.repStartDate );
442  }
443 
444  /* review */
445  {
446  if ( ldd->ld.revSchedule )
447  {
448  g_list_foreach( ldd->ld.revSchedule,
449  loan_rev_sched_list_free,
450  NULL );
451  g_list_free( ldd->ld.revSchedule );
452  ldd->ld.revSchedule = NULL;
453  }
454  }
455 
456  g_free( ldd );
457 }
458 
459 
460 static GtkWidget *
461 gnc_loan_assistant_create( LoanAssistantData *ldd )
462 {
463  GtkBuilder *builder;
464  GtkWidget *window;
465 
466  loan_assistant_data_init( ldd );
467 
468  builder = gtk_builder_new();
469 
470  gnc_builder_add_from_file (builder , "assistant-loan.glade", "len_liststore");
471  gnc_builder_add_from_file (builder , "assistant-loan.glade", "range_liststore");
472  gnc_builder_add_from_file (builder , "assistant-loan.glade", "type_liststore");
473  gnc_builder_add_from_file (builder , "assistant-loan.glade", "rate_liststore");
474 
475  gnc_builder_add_from_file (builder , "assistant-loan.glade", "loan_mortgage_assistant");
476  window = GTK_WIDGET(gtk_builder_get_object (builder, "loan_mortgage_assistant"));
477  ldd->window = window;
478 
479  // Set the name for this assistant so it can be easily manipulated with css
480  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-assistant-loan");
481 
482  /* Enable buttons on complete pages. */
483  gtk_assistant_set_page_complete (GTK_ASSISTANT (window),
484  GTK_WIDGET(gtk_builder_get_object(builder, "loan_intro_page")),
485  TRUE);
486  gtk_assistant_set_page_complete (GTK_ASSISTANT (window),
487  GTK_WIDGET(gtk_builder_get_object(builder, "loan_options_page")),
488  TRUE);
489  gtk_assistant_set_page_complete (GTK_ASSISTANT (window),
490  GTK_WIDGET(gtk_builder_get_object(builder, "loan_review_page")),
491  TRUE);
492 
493  /* Information Page */
494  {
495  ldd->prmTable = GTK_GRID(gtk_builder_get_object(builder, "param_table"));
496  ldd->prmVarFrame = GTK_FRAME(gtk_builder_get_object(builder, "type_freq_frame"));
497  ldd->prmIrateSpin = GTK_SPIN_BUTTON (gtk_builder_get_object(builder, "irate_spin"));
498  ldd->prmType = GTK_COMBO_BOX (gtk_builder_get_object(builder, "type_combobox"));
499  gtk_combo_box_set_active( GTK_COMBO_BOX( ldd->prmType), FALSE );
500  ldd->prmLengthSpin = GTK_SPIN_BUTTON (gtk_builder_get_object(builder, "len_spin"));
501  ldd->prmLengthType = GTK_COMBO_BOX (gtk_builder_get_object(builder, "len_opt"));
502  gtk_combo_box_set_active( GTK_COMBO_BOX( ldd->prmLengthType), FALSE );
503  ldd->prmRemainSpin = GTK_SPIN_BUTTON (gtk_builder_get_object(builder, "rem_spin"));
504  ldd->prmIrateType = GTK_COMBO_BOX (gtk_builder_get_object(builder, "irate_type_combobox"));
505  gtk_combo_box_set_active( GTK_COMBO_BOX( ldd->prmIrateType), FALSE );
506  /* ldd->prmStartDateGDE */
507  }
508  /* Repayment Page */
509  {
510  ldd->repTable = GTK_GRID(gtk_builder_get_object(builder, "repay_table"));
511  ldd->repTxnName = GTK_ENTRY(gtk_builder_get_object(builder, "txn_title"));
512  ldd->repAmtEntry = GTK_ENTRY(gtk_builder_get_object(builder, "amount_ent"));
513  ldd->repFreqFrame = GTK_FRAME(gtk_builder_get_object(builder, "freq_frame"));
514  }
515  /* Options Page */
516  {
517  ldd->optVBox = GTK_BOX(gtk_builder_get_object(builder, "loan_options_page"));
518  ldd->optEscrowCb = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, "opt_escrow_cb"));
519  ldd->optEscrowHBox = GTK_BOX(gtk_builder_get_object(builder, "opt_escrow_hbox"));
520  }
521  /* Payment Page */
522  {
523  ldd->payTable = GTK_GRID(gtk_builder_get_object(builder, "pay_table"));
524  ldd->payTxnName = GTK_ENTRY(gtk_builder_get_object(builder, "pay_txn_title"));
525  ldd->payAmtEntry = GTK_ENTRY(gtk_builder_get_object(builder, "pay_amt_ent"));
526  ldd->payUseEscrow = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, "pay_use_escrow"));
527  ldd->paySpecSrcAcct = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, "pay_specify_source"));
528  ldd->payAcctFromLabel = GTK_LABEL(gtk_builder_get_object(builder, "pay_from_account_label"));
529  ldd->payEscToLabel = GTK_LABEL(gtk_builder_get_object(builder, "pay_escrow_to_label"));
530  ldd->payEscFromLabel = GTK_LABEL(gtk_builder_get_object(builder, "pay_escrow_from_label"));
531  ldd->payTxnFreqPartRb = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "pay_txn_part_rb"));
532  ldd->payTxnFreqUniqRb = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "pay_uniq_freq_rb"));
533  ldd->payFreqHBox = GTK_BOX(gtk_builder_get_object(builder, "pay_freq_hbox"));
534  }
535  /* Review Page */
536  {
537  ldd->revTable = GTK_GRID(gtk_builder_get_object(builder, "rev_date_range_table"));
538  ldd->revRangeOpt = GTK_COMBO_BOX(gtk_builder_get_object(builder, "rev_range_opt"));
539  ldd->revDateFrame = GTK_FRAME(gtk_builder_get_object(builder, "rev_date_frame"));
540  ldd->revScrollWin = GTK_SCROLLED_WINDOW(gtk_builder_get_object(builder, "rev_scrollwin"));
541  /* GNCDateEdit *revStartDate */
542  /* GNCDateEdit *revEndDate */
543  }
544 
545  /* non-buildable widget setup */
546  {
547  GtkWidget *butt;
548  int i;
549  // ACCT_TYPE_LIABILITY
550  GList *liabilityAcct;
551  // ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CREDIT,
552  // ACCT_TYPE_ASSET + ACCT_TYPE_LIABILITY
553  GList *paymentFromAccts;
554  // ACCT_TYPE_EXPENSE, ACCT_TYPE_LIABILITY, + payment-froms.
555  GList *paymentToAccts;
556  int fromLen = 5;
557  GNCAccountType paymentFroms[] = { ACCT_TYPE_BANK, ACCT_TYPE_CASH,
560  };
561  int toLen = 1;
562  GNCAccountType paymentTos[] = { ACCT_TYPE_EXPENSE };
563 
564  liabilityAcct = NULL;
565  paymentFromAccts = NULL;
566  paymentToAccts = NULL;
567 
568  liabilityAcct = g_list_append( liabilityAcct,
569  GINT_TO_POINTER( ACCT_TYPE_LIABILITY ) );
570  for ( i = 0; i < fromLen; i++ )
571  {
572  paymentFromAccts
573  = g_list_append( paymentFromAccts,
574  GINT_TO_POINTER( paymentFroms[i] ) );
575  paymentToAccts
576  = g_list_append( paymentToAccts,
577  GINT_TO_POINTER( paymentFroms[i] ) );
578  }
579 
580  for ( i = 0; i < toLen; i++ )
581  {
582  paymentToAccts = g_list_append( paymentToAccts,
583  GINT_TO_POINTER( paymentTos[i] ) );
584  }
585 
586  /* All of the GncAccountSel[ectors]... */
587  {
588  int i;
589  /* "gas" == GncAccountSel */
590  struct gas_in_tables_data
591  {
592  GNCAccountSel **loc;
593  GtkGrid *table;
594  gboolean newAcctAbility;
595  int left, top, width, height;
596  GList *allowableAccounts;
597  } gas_data[] =
598  {
599  /* These ints are the GtkGrid boundaries */
600  { &ldd->prmAccountGAS, ldd->prmTable, TRUE, 1, 0, 1, 1, liabilityAcct },
601  { &ldd->repAssetsFromGAS, ldd->repTable, TRUE, 1, 2, 1, 1, paymentFromAccts },
602  { &ldd->repPrincToGAS, ldd->repTable, TRUE, 1, 3, 1, 1, paymentToAccts },
603  { &ldd->repIntToGAS, ldd->repTable, TRUE, 3, 3, 1, 1, paymentToAccts },
604  { &ldd->payAcctFromGAS, ldd->payTable, TRUE, 1, 4, 1, 1, paymentFromAccts },
605  { &ldd->payAcctEscToGAS, ldd->payTable, FALSE, 3, 4, 1, 1, paymentToAccts },
606  { &ldd->payAcctEscFromGAS, ldd->payTable, FALSE, 1, 5, 1, 1, paymentFromAccts },
607  { &ldd->payAcctToGAS, ldd->payTable, TRUE, 3, 5, 1, 1, paymentToAccts },
608  { NULL }
609  };
610 
611  ldd->prmOrigPrincGAE = GNC_AMOUNT_EDIT(gnc_amount_edit_new());
612  gtk_grid_attach (GTK_GRID(ldd->prmTable), GTK_WIDGET(ldd->prmOrigPrincGAE), 1, 1, 1, 1);
613 
614  gtk_widget_set_halign (GTK_WIDGET(ldd->prmOrigPrincGAE), GTK_ALIGN_FILL);
615  gtk_widget_set_hexpand (GTK_WIDGET(ldd->prmOrigPrincGAE), FALSE);
616  g_object_set (GTK_WIDGET(ldd->prmOrigPrincGAE), "margin", 2, nullptr);
617 
618  g_signal_connect (G_OBJECT(ldd->prmOrigPrincGAE), "changed",
619  G_CALLBACK(loan_info_page_valid_cb), ldd);
620 
621  for ( i = 0; gas_data[i].loc != NULL; i++ )
622  {
623  GNCAccountSel *gas = GNC_ACCOUNT_SEL(gnc_account_sel_new());
624 
625  gnc_account_sel_set_new_account_ability(
626  gas, gas_data[i].newAcctAbility );
627  if ( gas_data[i].allowableAccounts != NULL )
628  {
629  gnc_account_sel_set_acct_filters(
630  gas, gas_data[i].allowableAccounts, NULL );
631  }
632 
633  gtk_grid_attach (GTK_GRID(gas_data[i].table),
634  GTK_WIDGET(gas),
635  gas_data[i].left,
636  gas_data[i].top,
637  gas_data[i].width,
638  gas_data[i].height);
639 
640  gtk_widget_set_halign (GTK_WIDGET(gas), GTK_ALIGN_FILL);
641  gnc_account_sel_set_new_account_modal (GNC_ACCOUNT_SEL(gas), true);
642  g_object_set (GTK_WIDGET(gas), "margin", 2, nullptr);
643  *(gas_data[i].loc) = gas;
644  }
645  }
646 
647  /* Setup the payment page always-insensitive GASes */
648  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctEscToGAS), FALSE );
649  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctEscFromGAS), FALSE );
650 
651  /* The GNCDateEdit[s] */
652  {
653  /* "gde" == GNCDateEdit */
654  struct gde_in_tables_data
655  {
656  GNCDateEdit **loc;
657  GtkGrid *table;
658  int left, top, width, height;
659  } gde_data[] =
660  {
661  /* These ints are the GtkGrid boundaries */
662  { &ldd->prmStartDateGDE, ldd->prmTable, 1, 4, 1, 1 },
663  { &ldd->revStartDate, ldd->revTable, 1, 0, 1, 1 },
664  { &ldd->revEndDate, ldd->revTable, 1, 1, 1, 1 },
665  { NULL }
666  };
667 
668  for ( i = 0; gde_data[i].loc != NULL; i++ )
669  {
670  *gde_data[i].loc =
671  GNC_DATE_EDIT(
672  gnc_date_edit_new( gnc_time (NULL),
673  FALSE, FALSE ) );
674 
675  gtk_grid_attach (GTK_GRID(gde_data[i].table),
676  GTK_WIDGET( *gde_data[i].loc ),
677  gde_data[i].left,
678  gde_data[i].top,
679  gde_data[i].width,
680  gde_data[i].height);
681 
682  gtk_widget_set_halign (GTK_WIDGET( *gde_data[i].loc ), GTK_ALIGN_START);
683  gtk_widget_set_hexpand (GTK_WIDGET( *gde_data[i].loc ), FALSE);
684  g_object_set (GTK_WIDGET( *gde_data[i].loc ), "margin", 0, nullptr);
685  }
686 
687  }
688 
689  gtk_widget_set_sensitive( GTK_WIDGET(ldd->prmVarFrame), FALSE );
690  {
691  g_signal_connect( ldd->prmType, "changed",
692  G_CALLBACK( loan_info_prm_type_cb ),
693  ldd );
694  }
695 
696  {
697  GtkAdjustment *a;
698 
699  /* 8.0 [%], range of 0.005..100.0 with ticks at 0.001[%]. */
700  a = GTK_ADJUSTMENT(gtk_adjustment_new( 8.0, 0.001,
701  100.0, 0.001,
702  1.0, 0.0 ));
703  gtk_spin_button_set_adjustment( ldd->prmIrateSpin, a );
704  gtk_spin_button_set_value( ldd->prmIrateSpin, 8.00 );
705  gtk_spin_button_set_snap_to_ticks( ldd->prmIrateSpin, TRUE );
706 
707  a = GTK_ADJUSTMENT(gtk_adjustment_new( 360, 1,
708  9999, 1,
709  12, 0.0 ));
710  gtk_spin_button_set_adjustment( ldd->prmLengthSpin, a );
711  g_signal_connect( ldd->prmLengthSpin, "changed",
712  G_CALLBACK( loan_info_calc_update_cb ),
713  ldd );
714  g_signal_connect( ldd->prmStartDateGDE, "date-changed",
715  G_CALLBACK( loan_info_calc_update_cb ),
716  ldd );
717  g_signal_connect( ldd->prmLengthSpin, "value-changed",
718  G_CALLBACK( loan_info_calc_update_cb ),
719  ldd );
720  g_signal_connect( ldd->prmLengthType, "changed",
721  G_CALLBACK( loan_info_calc_update_cb ),
722  ldd );
723 
724  a = GTK_ADJUSTMENT(gtk_adjustment_new( 360, 1,
725  9999, 1,
726  12, 0.0 ));
727  gtk_spin_button_set_adjustment( ldd->prmRemainSpin, a );
728  }
729 
730  g_signal_connect( ldd->optEscrowCb, "toggled",
731  G_CALLBACK(loan_opt_escrow_toggle_cb), ldd );
732  gtk_widget_set_sensitive( GTK_WIDGET(ldd->optEscrowHBox), FALSE );
733  ldd->optEscrowGAS = GNC_ACCOUNT_SEL(gnc_account_sel_new());
734  g_object_set (ldd->optEscrowGAS, "entry-width", 50, NULL);
735  gnc_account_sel_set_new_account_modal (GNC_ACCOUNT_SEL(ldd->optEscrowGAS), true);
736  gnc_account_sel_set_new_account_ability( ldd->optEscrowGAS, TRUE );
737  gtk_container_add( GTK_CONTAINER(ldd->optEscrowHBox),
738  GTK_WIDGET(ldd->optEscrowGAS) );
739 
740  {
741  /* . Each RepayOpt gets an "entry" in the optContainer.
742  * . Each "entry" is a 2-line vbox containing:
743  * . The checkbox for the option itself
744  * . an alignment-contained sub-checkbox for "through the
745  * escrow account".
746  * . Hook up each to bit-twiddling the appropriate line.
747  */
748 
749  RepayOptUIData *rouid;
750  GtkWidget *vb;
751  GString *str;
752 
753  str = g_string_sized_new( 32 );
754 
755  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
756  {
757  rouid = ldd->repayOptsUI[i];
758  vb = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
759  gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
760  gtk_widget_set_margin_start (GTK_WIDGET(vb), 12);
761 
762  /* Add payment checkbox. */
763 
764  /* Translators: %s is "Taxes",
765  "Insurance", or similar. */
766  g_string_printf( str, _("… pay \"%s\"?"),
767  rouid->optData->name );
768  rouid->optCb =
769  GTK_CHECK_BUTTON(
770  gtk_check_button_new_with_label(
771  str->str ));
772  gtk_box_pack_start( GTK_BOX(vb),
773  GTK_WIDGET(rouid->optCb),
774  FALSE, FALSE, 2 );
775  rouid->escrowCb =
776  GTK_CHECK_BUTTON(
777  gtk_check_button_new_with_label(
778  _("via Escrow account?") ));
779  gtk_widget_set_sensitive(
780  GTK_WIDGET(rouid->escrowCb),
781  FALSE );
782 
783  gtk_box_pack_start( GTK_BOX(vb), GTK_WIDGET(rouid->escrowCb), FALSE, FALSE, 2 );
784  gtk_widget_set_margin_start (GTK_WIDGET(rouid->escrowCb), 12);
785 
786  g_signal_connect( rouid->optCb, "toggled",
787  G_CALLBACK(loan_opt_toggled_cb),
788  rouid );
789  g_signal_connect( rouid->optCb, "toggled",
790  G_CALLBACK(loan_opt_consistency_cb),
791  rouid );
792  g_signal_connect( rouid->escrowCb, "toggled",
793  G_CALLBACK(loan_opt_escrow_toggled_cb),
794  rouid );
795 
796  gtk_box_pack_start( GTK_BOX(ldd->optVBox), GTK_WIDGET(vb), FALSE, FALSE, 2 );
797  gtk_widget_show_all( GTK_WIDGET(ldd->optVBox) );
798  }
799  g_string_free( str, TRUE );
800  }
801 
802  g_signal_connect( ldd->payUseEscrow, "toggled",
803  G_CALLBACK(loan_pay_use_esc_toggle_cb), ldd );
804  g_signal_connect( ldd->paySpecSrcAcct, "toggled",
805  G_CALLBACK(loan_pay_spec_src_toggle_cb), ldd );
806  g_signal_connect( ldd->payTxnFreqUniqRb, "toggled",
807  G_CALLBACK(loan_pay_freq_toggle_cb), ldd );
808 
809  {
810  GtkBox *hbox;
811  hbox = GTK_BOX(gtk_builder_get_object(builder, "type_freq_hbox"));
812  ldd->prmVarGncFreq = GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
813  gtk_box_pack_start( GTK_BOX(hbox) , GTK_WIDGET(ldd->prmVarGncFreq), TRUE, FALSE, 0 );
814  g_signal_connect (ldd->prmVarGncFreq, "changed",
815  G_CALLBACK (loan_info_page_valid_cb), ldd);
816  }
817  {
818  GtkBox *hbox;
819  hbox = GTK_BOX(gtk_builder_get_object(builder, "freq_frame_hbox"));
820  ldd->repGncFreq = GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
821  gtk_box_pack_start( GTK_BOX(hbox) , GTK_WIDGET(ldd->repGncFreq), TRUE, FALSE, 0 );
822  g_signal_connect (ldd->repGncFreq, "changed",
823  G_CALLBACK (loan_rep_page_valid_cb), ldd);
824  }
825 
826  ldd->payGncFreq = GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
827  gtk_container_add( GTK_CONTAINER(ldd->payFreqHBox), GTK_WIDGET(ldd->payGncFreq) );
828 
829  g_signal_connect (ldd->payGncFreq, "changed",
830  G_CALLBACK (loan_pay_page_valid_cb), ldd);
831 
832  butt = GTK_WIDGET(gtk_builder_get_object(builder, "pay_back_button"));
833  g_signal_connect (butt, "clicked",
834  G_CALLBACK (loan_pay_back_button_cb), ldd);
835 
836  butt = GTK_WIDGET(gtk_builder_get_object(builder, "pay_next_button"));
837  g_signal_connect (butt, "clicked",
838  G_CALLBACK (loan_pay_next_button_cb), ldd);
839 
840  }
841 
842  /* Info page Call Back */
843  {
844  g_signal_connect (ldd->prmAccountGAS, "account_sel_changed",
845  G_CALLBACK (loan_info_page_valid_cb), ldd);
846  g_signal_connect( ldd->prmIrateType, "changed",
847  G_CALLBACK( loan_info_page_valid_cb ), ldd );
848  }
849  /* Opts page Call Back */
850  {
851  g_signal_connect (ldd->optEscrowGAS, "account_sel_changed",
852  G_CALLBACK (loan_opt_page_valid_cb), ldd);
853  }
854  /* Rep page Call Back */
855  {
856  g_signal_connect (ldd->repAssetsFromGAS, "account_sel_changed",
857  G_CALLBACK (loan_rep_page_valid_cb), ldd);
858  g_signal_connect (ldd->repIntToGAS, "account_sel_changed",
859  G_CALLBACK (loan_rep_page_valid_cb), ldd);
860  g_signal_connect (ldd->repPrincToGAS, "account_sel_changed",
861  G_CALLBACK (loan_rep_page_valid_cb), ldd);
862  }
863  /* Pay page Call Back */
864  {
865  g_signal_connect (ldd->payAcctFromGAS, "account_sel_changed",
866  G_CALLBACK (loan_pay_page_valid_cb), ldd);
867  g_signal_connect (ldd->payAcctToGAS, "account_sel_changed",
868  G_CALLBACK (loan_pay_page_valid_cb), ldd);
869  g_signal_connect (ldd->payAcctEscFromGAS, "account_sel_changed",
870  G_CALLBACK (loan_pay_page_valid_cb), ldd);
871  g_signal_connect (ldd->payAcctEscToGAS, "account_sel_changed",
872  G_CALLBACK (loan_pay_page_valid_cb), ldd);
873  }
874  /* Review page Call Back */
875  {
876  gtk_combo_box_set_active( ldd->revRangeOpt, 0 );
877  g_signal_connect( ldd->revRangeOpt, "changed",
878  G_CALLBACK( loan_rev_range_opt_changed_cb ),
879  ldd );
880  g_signal_connect( ldd->revStartDate, "date-changed",
881  G_CALLBACK( loan_rev_range_changed_cb ),
882  ldd );
883  g_signal_connect( ldd->revEndDate, "date-changed",
884  G_CALLBACK( loan_rev_range_changed_cb ),
885  ldd );
886  }
887 
888  g_signal_connect( ldd->window, "destroy",
889  G_CALLBACK(loan_assistant_window_destroy_cb),
890  ldd );
891 
892  gtk_builder_connect_signals(builder, ldd);
893  g_object_unref(G_OBJECT(builder));
894 
895  gtk_widget_show_all( ldd->window );
896  return window;
897 }
898 
899 
900 static
901 void
902 loan_assistant_data_init( LoanAssistantData *ldd )
903 {
904  int i, optCount;
905  RepayOptData *optData;
906 
907  /* get the count of repayment defaults. */
908  for ( optCount = 0; REPAY_DEFAULTS[optCount].name != NULL; optCount++ )
909  ;
910 
911  ldd->currentIdx = -1;
912 
913  ldd->ld.principal = gnc_numeric_zero();
914  ldd->ld.startDate = g_date_new();
915  ldd->ld.varStartDate = g_date_new();
916  gnc_gdate_set_time64( ldd->ld.startDate, gnc_time (NULL) );
917  ldd->ld.loan_schedule = NULL;
918  ldd->ld.repayment_schedule = NULL;
919  {
920  Recurrence *r = g_new0(Recurrence, 1);
921  recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
922  ldd->ld.repayment_schedule = g_list_append(ldd->ld.repayment_schedule, r);
923  }
924 
925  ldd->ld.repMemo = g_strdup( _("Loan") );
926  ldd->ld.repAmount = NULL;
927  ldd->ld.repStartDate = g_date_new();
928  ldd->ld.repayOptCount = optCount;
929  ldd->ld.repayOpts = g_new0( RepayOptData*, optCount );
930  /* copy all the default lines into the LDD */
931  ldd->repayOptsUI = g_new0( RepayOptUIData*, optCount );
932  for ( i = 0; i < optCount; i++ )
933  {
934  g_assert( REPAY_DEFAULTS[i].name != NULL );
935 
936  ldd->repayOptsUI[i] = g_new0( RepayOptUIData, 1 );
937  ldd->repayOptsUI[i]->ldd = ldd;
938 
939  optData = ldd->ld.repayOpts[i]
940  = ldd->repayOptsUI[i]->optData
941  = g_new0( RepayOptData, 1 );
942 
943  optData->enabled = FALSE;
944  optData->optValid = FALSE;
945  optData->FreqUniq = FALSE;
946  optData->name = g_strdup( _(REPAY_DEFAULTS[i].name) );
947  optData->txnMemo = g_strdup( _(REPAY_DEFAULTS[i].
948  defaultTxnMemo) );
949  optData->amount = 0.0;
950  optData->throughEscrowP = REPAY_DEFAULTS[i].escrowDefault;
951  optData->specSrcAcctP = REPAY_DEFAULTS[i].specSrcAcctDefault;
952  optData->schedule = NULL;
953  optData->startDate = NULL;
954  }
955 }
956 
957 /************************ Page functions in order ***********************/
958 
959 static
960 void
961 loan_info_prep( GtkAssistant *assistant, gpointer user_data )
962 {
963  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
964 
965  gnc_amount_edit_set_amount( ldd->prmOrigPrincGAE, ldd->ld.principal );
966  gtk_spin_button_set_value( ldd->prmIrateSpin, ldd->ld.interestRate );
967  gtk_combo_box_set_active( ldd->prmIrateType, ldd->ld.rateType );
968  gtk_combo_box_set_active( ldd->prmType, ldd->ld.type );
969  if ( ldd->ld.type != GNC_FIXED )
970  {
971  g_signal_handlers_block_by_func( GNC_FREQUENCY( ldd->prmVarGncFreq),
972  (gpointer) loan_info_page_valid_cb, ldd );
973  gnc_frequency_setup_recurrence(ldd->prmVarGncFreq, ldd->ld.loan_schedule, ldd->ld.varStartDate);
974  g_signal_handlers_unblock_by_func( GNC_FREQUENCY( ldd->prmVarGncFreq),
975  (gpointer) loan_info_page_valid_cb, ldd );
976  }
977 
978  /* start date */
979  {
980  struct tm *tmpTm;
981 
982  tmpTm = g_new0( struct tm, 1 );
983 
984  g_date_to_struct_tm (ldd->ld.startDate, tmpTm);
985  gnc_date_edit_set_time (ldd->prmStartDateGDE,
986  gnc_mktime (tmpTm));
987  g_free (tmpTm);
988  }
989 
990  /* length: total and remaining */
991  {
992  gtk_spin_button_set_value( ldd->prmLengthSpin, ldd->ld.numPer );
993  gtk_combo_box_set_active( ldd->prmLengthType, ldd->ld.perSize );
994  gtk_spin_button_set_value( ldd->prmRemainSpin, ldd->ld.numMonRemain );
995  }
996 }
997 
998 
999 static
1000 void
1001 loan_info_prm_type_cb( GtkWidget *w, gpointer user_data )
1002 {
1003  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1004  gint index;
1005 
1006  index = gtk_combo_box_get_active( ldd->prmType );
1007  gtk_widget_set_sensitive( GTK_WIDGET(ldd->prmVarFrame),
1008  index != GNC_FIXED );
1009 }
1010 
1011 
1012 static
1013 void
1014 loan_info_calc_update_cb( GtkWidget *w, gpointer user_data )
1015 {
1016  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1017  GDate start, now;
1018  int i, totalVal, total, remain;
1019 
1020  g_date_clear( &start, 1 );
1021  g_date_clear( &now, 1 );
1022  gnc_gdate_set_time64( &start, gnc_date_edit_get_date( ldd->prmStartDateGDE ) );
1023  gnc_gdate_set_time64( &now, gnc_time (NULL) );
1024  for ( i = 0; g_date_compare( &start, &now ) < 0; i++ )
1025  {
1026  g_date_add_months( &start, 1 );
1027  }
1028 
1029  /* Get the correct, current value of the length spin. */
1030  {
1031  gchar *valueStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->prmLengthSpin),
1032  0, -1 );
1033  totalVal = strtol( valueStr, NULL, 10 );
1034  g_free( valueStr );
1035  }
1036  total = totalVal
1037  * ( gtk_combo_box_get_active( ldd->prmLengthType )
1038  == 1 ? 12 : 1 );
1039  remain = total - i;
1040  gtk_spin_button_set_value( ldd->prmRemainSpin, remain );
1041  gtk_widget_show( GTK_WIDGET(ldd->prmRemainSpin) );
1042 }
1043 
1044 
1045 void
1046 loan_info_page_valid_cb (GtkWidget *widget, gpointer user_data )
1047 {
1048  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1049  GtkAssistant *assistant = GTK_ASSISTANT(ldd->window);
1050  gint num = gtk_assistant_get_current_page (assistant);
1051  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1052 
1053  gtk_assistant_set_page_complete (assistant, page,
1054  loan_info_page_complete (assistant, ldd));
1055 }
1056 
1057 
1058 static
1059 gboolean
1060 loan_info_page_complete( GtkAssistant *assistant, gpointer user_data )
1061 {
1062  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1063  GNCPrintAmountInfo print_info;
1064  gnc_commodity *currency;
1065  gint result;
1066  gnc_numeric value;
1067 
1068  ldd->ld.primaryAcct = gnc_account_sel_get_account( ldd->prmAccountGAS );
1069  /* Test for valid Account */
1070  if ( ldd->ld.primaryAcct == NULL )
1071  return FALSE;
1072 
1073  /* Test for loan amount */
1074  currency = xaccAccountGetCommodity (ldd->ld.primaryAcct);
1075  print_info = gnc_commodity_print_info (currency, FALSE);
1076  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE), print_info);
1077  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE),
1078  gnc_commodity_get_fraction (currency));
1079 
1080  result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE),
1081  &value, FALSE, nullptr);
1082  if (result == 1)
1083  return FALSE;
1084 
1085  return TRUE;
1086 }
1087 
1088 
1089 static
1090 void
1091 loan_info_page_save( GtkAssistant *assistant, gpointer user_data )
1092 {
1093  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1094 
1095  ldd->ld.primaryAcct = gnc_account_sel_get_account( ldd->prmAccountGAS );
1096 
1097  if ( ! ldd->ld.repPriAcct )
1098  {
1099  ldd->ld.repPriAcct = ldd->ld.primaryAcct;
1100  }
1101  ldd->ld.principal = gnc_amount_edit_get_amount( ldd->prmOrigPrincGAE );
1102  ldd->ld.interestRate = gtk_spin_button_get_value( ldd->prmIrateSpin );
1103  ldd->ld.rateType = static_cast<IRateType>( gtk_combo_box_get_active (ldd->prmIrateType ));
1104  ldd->ld.type = static_cast<LoanType>( gtk_combo_box_get_active( ldd->prmType ));
1105 
1106  if ( ldd->ld.type != GNC_FIXED )
1107  {
1108  recurrenceListFree(&ldd->ld.loan_schedule);
1109  gnc_frequency_save_to_recurrence(ldd->prmVarGncFreq,
1110  &ldd->ld.loan_schedule,
1111  ldd->ld.varStartDate);
1112  }
1113 
1114  /* start date */
1115  {
1116  time64 tmpTT;
1117  struct tm *tmpTm;
1118 
1119  tmpTT = gnc_date_edit_get_date( ldd->prmStartDateGDE );
1120  tmpTm = gnc_localtime ( &tmpTT );
1121  if (tmpTm)
1122  {
1123  g_date_set_dmy( ldd->ld.startDate,
1124  tmpTm->tm_mday,
1125  static_cast<GDateMonth>(tmpTm->tm_mon + 1),
1126  (1900 + tmpTm->tm_year) );
1127  gnc_tm_free (tmpTm);
1128  }
1129  }
1130 
1131  /* len / periods */
1132  {
1133  ldd->ld.perSize =
1134  (gtk_combo_box_get_active( ldd->prmLengthType )
1135  == GNC_MONTHS) ? GNC_MONTHS : GNC_YEARS;
1136  ldd->ld.numPer =
1137  gtk_spin_button_get_value_as_int( ldd->prmLengthSpin );
1138  ldd->ld.numMonRemain =
1139  gtk_spin_button_get_value_as_int( ldd->prmRemainSpin );
1140  }
1141 }
1142 
1143 /************************************************************************/
1144 
1145 static
1146 void
1147 loan_opt_prep( GtkAssistant *assistant, gpointer user_data )
1148 {
1149  int i;
1150  RepayOptUIData *rouid;
1151  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1152 
1153  /* Save Previous Page ( Information ) */
1154  loan_info_page_save(assistant, ldd);
1155 
1156  if ( ldd->ld.escrowAcct )
1157  {
1158  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->optEscrowCb), TRUE );
1159  gnc_account_sel_set_account( ldd->optEscrowGAS, ldd->ld.escrowAcct, FALSE );
1160  }
1161  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
1162  {
1163  rouid = ldd->repayOptsUI[i];
1164  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(rouid->optCb),
1165  rouid->optData->enabled );
1166  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(rouid->escrowCb),
1167  rouid->optData->throughEscrowP
1168  && rouid->optData->enabled
1169  && ldd->ld.escrowAcct );
1170  gtk_widget_set_sensitive( GTK_WIDGET(rouid->escrowCb),
1171  rouid->optData->enabled
1172  && ldd->ld.escrowAcct );
1173  }
1174 }
1175 
1176 
1177 static
1178 void
1179 loan_opt_toggled_cb( GtkToggleButton *tb, gpointer user_data )
1180 {
1181  RepayOptUIData *rouid;
1182 
1183  rouid = (RepayOptUIData*)user_data;
1184  rouid->optData->enabled = gtk_toggle_button_get_active(tb);
1185 }
1186 
1187 
1188 static
1189 void
1190 loan_opt_consistency_cb( GtkToggleButton *tb, gpointer user_data )
1191 {
1192  GtkToggleButton *escrowCb;
1193  RepayOptUIData *rouid;
1194 
1195  rouid = (RepayOptUIData*)user_data;
1196  escrowCb = GTK_TOGGLE_BUTTON(rouid->escrowCb);
1197  /* make sure the escrow option is only selected if we're active. */
1198  gtk_toggle_button_set_active( escrowCb,
1199  gtk_toggle_button_get_active(
1200  GTK_TOGGLE_BUTTON(
1201  rouid->ldd->optEscrowCb) )
1202  && rouid->optData->throughEscrowP
1203  && gtk_toggle_button_get_active(tb) );
1204  /* make sure the escrow option is only sensitive if we're active, and
1205  * the escrow account is enabled */
1206  gtk_widget_set_sensitive( GTK_WIDGET(escrowCb),
1207  gtk_toggle_button_get_active(tb)
1208  && gtk_toggle_button_get_active(
1209  GTK_TOGGLE_BUTTON(rouid->ldd->optEscrowCb)) );
1210 }
1211 
1212 
1213 static
1214 void
1215 loan_opt_escrow_toggle_cb( GtkToggleButton *tb, gpointer user_data )
1216 {
1217  int i;
1218  gboolean newState;
1219  RepayOptUIData *rouid;
1220  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1221  GtkAssistant *assistant = GTK_ASSISTANT(ldd->window);
1222  gint num = gtk_assistant_get_current_page (assistant);
1223  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1224 
1225  newState = gtk_toggle_button_get_active(tb);
1226  gtk_widget_set_sensitive( GTK_WIDGET(ldd->optEscrowHBox), newState );
1227  /* Check for Valid Account if enabled */
1228  if (newState)
1229  {
1230  if (GNC_ACCOUNT_SEL( ldd->ld.escrowAcct) == NULL)
1231  gtk_assistant_set_page_complete (assistant, page, FALSE);
1232  }
1233  else
1234  {
1235  ldd->ld.escrowAcct = NULL;
1236  gnc_account_sel_set_account( GNC_ACCOUNT_SEL( ldd->optEscrowGAS), NULL , FALSE );
1237  gtk_assistant_set_page_complete (assistant, page, TRUE);
1238  }
1239 
1240 
1241  /* deal with escrow options. */
1242  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
1243  {
1244  rouid = ldd->repayOptsUI[i];
1245  /* If we're going off, then uncheck and desensitize all escrow opts. */
1246  /* If we're going on, then sensitize all escrow opts. */
1247 
1248  /* prevent the toggle handler from running and "trashing" the
1249  * state of the throughEscrowP selection */
1250  g_signal_handlers_block_by_func( rouid->escrowCb,
1251  (gpointer) loan_opt_escrow_toggled_cb,
1252  rouid );
1253  gtk_toggle_button_set_active(
1254  GTK_TOGGLE_BUTTON(rouid->escrowCb),
1255  ( newState
1256  && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rouid->optCb) )
1257  && rouid->optData->throughEscrowP ) );
1258  gtk_widget_set_sensitive(
1259  GTK_WIDGET(rouid->escrowCb),
1260  newState
1261  && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rouid->optCb) ) );
1262  g_signal_handlers_unblock_by_func( rouid->escrowCb,
1263  (gpointer) loan_opt_escrow_toggled_cb,
1264  rouid );
1265  if ( newState )
1266  {
1267  rouid->optData->from = ldd->ld.escrowAcct;
1268  }
1269  else
1270  {
1271  rouid->optData->from = NULL;
1272  }
1273  }
1274 }
1275 
1276 
1277 static
1278 void
1279 loan_opt_escrow_toggled_cb( GtkToggleButton *tb, gpointer user_data )
1280 {
1281  RepayOptUIData *rouid;
1282 
1283  rouid = (RepayOptUIData*)user_data;
1284  rouid->optData->throughEscrowP = gtk_toggle_button_get_active( tb );
1285 }
1286 
1287 
1288 void
1289 loan_opt_page_valid_cb (GtkWidget *widget, gpointer user_data )
1290 {
1291  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1292 
1293  GtkAssistant *assistant = GTK_ASSISTANT(ldd->window);
1294  gint num = gtk_assistant_get_current_page (assistant);
1295  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1296 
1297  gtk_assistant_set_page_complete (assistant, page,
1298  loan_opt_page_complete (assistant, ldd));
1299 }
1300 
1301 
1302 static
1303 gboolean
1304 loan_opt_page_complete( GtkAssistant *assistant, gpointer user_data )
1305 {
1306  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1307 
1308  if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(ldd->optEscrowCb) ) )
1309  {
1310  ldd->ld.escrowAcct =
1311  gnc_account_sel_get_account( ldd->optEscrowGAS );
1312  /* Test for valid Account */
1313  if ( ldd->ld.escrowAcct == NULL )
1314  return FALSE;
1315  }
1316  else
1317  {
1318  ldd->ld.escrowAcct = NULL;
1319  }
1320  return TRUE;
1321 }
1322 
1323 /************************************************************************/
1324 
1325 static
1326 void
1327 loan_rep_prep( GtkAssistant *assistant, gpointer user_data )
1328 {
1329  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1330  GString *str;
1331 
1332  if ( ldd->ld.repAmount )
1333  {
1334  g_free( ldd->ld.repAmount );
1335  }
1336 
1337  str = g_string_sized_new( 64 );
1338  loan_get_pmt_formula( ldd, str);
1339  ldd->ld.repAmount = g_string_free (str, false);
1340 
1341  if ( ldd->ld.repMemo )
1342  gtk_entry_set_text( ldd->repTxnName, ldd->ld.repMemo );
1343 
1344  if ( ldd->ld.repAmount )
1345  gtk_entry_set_text( ldd->repAmtEntry, ldd->ld.repAmount );
1346 
1347  gnc_account_sel_set_account( ldd->repAssetsFromGAS, ldd->ld.repFromAcct, FALSE );
1348  gnc_account_sel_set_account( ldd->repPrincToGAS, ldd->ld.repPriAcct, FALSE );
1349  gnc_account_sel_set_account( ldd->repIntToGAS, ldd->ld.repIntAcct, FALSE );
1350 
1351  g_signal_handlers_block_by_func( ldd->repGncFreq,
1352  (gpointer) loan_rep_page_valid_cb, ldd );
1353  gnc_frequency_setup_recurrence(ldd->repGncFreq, ldd->ld.repayment_schedule, ldd->ld.repStartDate);
1354  g_signal_handlers_unblock_by_func( ldd->repGncFreq,
1355  (gpointer) loan_rep_page_valid_cb, ldd );
1356 
1357  /* Find the first enabled option */
1358  {
1359  int i;
1360  for ( i = 0; // we can always start at 0, here.
1361  (i < ldd->ld.repayOptCount)
1362  && !ldd->ld.repayOpts[i]->enabled;
1363  i++ )
1364  ;
1365  if ( i < ldd->ld.repayOptCount )
1366  ldd->currentIdx = i;
1367  else
1368  ldd->currentIdx = -1;
1369  }
1370 }
1371 
1372 
1373 void
1374 loan_rep_page_valid_cb (GtkWidget *widget, gpointer user_data )
1375 {
1376  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1377  GtkAssistant *assistant = GTK_ASSISTANT(ldd->window);
1378  gint num = gtk_assistant_get_current_page (assistant);
1379  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1380 
1381  gtk_assistant_set_page_complete (assistant, page,
1382  loan_rep_page_complete (assistant, ldd));
1383 }
1384 
1385 
1386 static
1387 gboolean
1388 loan_rep_page_complete( GtkAssistant *assistant, gpointer user_data )
1389 {
1390  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1391 
1392  ldd->ld.repFromAcct =
1393  gnc_account_sel_get_account( ldd->repAssetsFromGAS );
1394  /* Test for valid Account */
1395  if ( ldd->ld.repFromAcct == NULL )
1396  return FALSE;
1397 
1398  ldd->ld.repPriAcct =
1399  gnc_account_sel_get_account( ldd->repPrincToGAS );
1400  /* Test for valid Account */
1401  if ( ldd->ld.repPriAcct == NULL )
1402  return FALSE;
1403 
1404  ldd->ld.repIntAcct =
1405  gnc_account_sel_get_account( ldd->repIntToGAS );
1406  /* Test for valid Account */
1407  if ( ldd->ld.repIntAcct == NULL )
1408  return FALSE;
1409 
1410  return TRUE;
1411 }
1412 
1413 
1414 static
1415 void
1416 loan_rep_page_save( GtkAssistant *assistant, gpointer user_data )
1417 {
1418  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1419 
1420  if ( ldd->ld.repMemo )
1421  g_free( ldd->ld.repMemo );
1422  ldd->ld.repMemo =
1423  gtk_editable_get_chars( GTK_EDITABLE(ldd->repTxnName), 0, -1 );
1424 
1425  if ( ldd->ld.repAmount )
1426  g_free( ldd->ld.repAmount );
1427  ldd->ld.repAmount =
1428  gtk_editable_get_chars( GTK_EDITABLE(ldd->repAmtEntry), 0, -1 );
1429 
1430  ldd->ld.repFromAcct =
1431  gnc_account_sel_get_account( ldd->repAssetsFromGAS );
1432 
1433  ldd->ld.repPriAcct =
1434  gnc_account_sel_get_account( ldd->repPrincToGAS );
1435 
1436  ldd->ld.repIntAcct =
1437  gnc_account_sel_get_account( ldd->repIntToGAS );
1438 
1439  recurrenceListFree(&ldd->ld.repayment_schedule);
1440  gnc_frequency_save_to_recurrence(ldd->repGncFreq,
1441  &ldd->ld.repayment_schedule,
1442  ldd->ld.repStartDate);
1443 }
1444 
1445 /************************************************************************/
1446 
1447 static
1448 void
1449 loan_pay_prep( GtkAssistant *assistant, gpointer user_data )
1450 {
1451  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1452  RepayOptData *rod;
1453  GString *str;
1454 
1455 
1456  gint num = gtk_assistant_get_current_page (assistant);
1457  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1458 
1459  /* Save Previous Page (Repayment) */
1460  loan_rep_page_save(assistant, ldd);
1461 
1462  /* Step over page if no options enabled */
1463  if (ldd->currentIdx == -1 )
1464  {
1465  gtk_assistant_set_current_page (assistant, num + 1);
1466  }
1467  else
1468  {
1469  g_assert( ldd->currentIdx >= 0 );
1470  g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
1471 
1472  rod = ldd->ld.repayOpts[ldd->currentIdx];
1473  str = g_string_sized_new( 32 );
1474  /* Translators: %s is "Taxes", or "Insurance", or similar */
1475  g_string_printf( str, _("Loan Repayment Option: \"%s\""), rod->name );
1476  gtk_assistant_set_page_title (assistant, page, str->str );
1477 
1478  /* copy in the relevant data from the currently-indexed option. */
1479  gtk_entry_set_text( ldd->payTxnName, rod->txnMemo );
1480  g_string_printf( str, "%0.2f", rod->amount );
1481  gtk_entry_set_text( ldd->payAmtEntry, str->str );
1482 
1483  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payUseEscrow),
1484  (ldd->ld.escrowAcct != NULL) );
1485 
1486  {
1487  g_signal_handlers_block_by_func( ldd->payUseEscrow,
1488  (gpointer) loan_pay_use_esc_toggle_cb,
1489  ldd );
1490 
1491  loan_pay_use_esc_setup( ldd,
1492  (ldd->ld.escrowAcct != NULL)
1493  && rod->throughEscrowP );
1494  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payUseEscrow),
1495  (rod->throughEscrowP
1496  && ldd->ld.escrowAcct != NULL) );
1497 
1498  g_signal_handlers_unblock_by_func( ldd->payUseEscrow,
1499  (gpointer) loan_pay_use_esc_toggle_cb,
1500  ldd );
1501  }
1502 
1503  {
1504  g_signal_handlers_block_by_func( ldd->paySpecSrcAcct,
1505  (gpointer) loan_pay_spec_src_toggle_cb,
1506  ldd );
1507  loan_pay_spec_src_setup( ldd, rod->specSrcAcctP );
1508  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->paySpecSrcAcct),
1509  rod->specSrcAcctP );
1510 
1511  g_signal_handlers_unblock_by_func( ldd->paySpecSrcAcct,
1512  (gpointer) loan_pay_spec_src_toggle_cb,
1513  ldd );
1514  }
1515 
1516  g_signal_handlers_block_by_func(ldd->payAcctToGAS,
1517  (gpointer) loan_pay_page_valid_cb, ldd );
1518  gnc_account_sel_set_account( ldd->payAcctToGAS, rod->to, FALSE );
1519  g_signal_handlers_unblock_by_func(ldd->payAcctToGAS,
1520  (gpointer) loan_pay_page_valid_cb, ldd );
1521 
1522 
1523  g_signal_handlers_block_by_func(ldd->payTxnFreqUniqRb,
1524  (gpointer) loan_pay_freq_toggle_cb, ldd );
1525  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payTxnFreqPartRb), !rod->FreqUniq );
1526  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payTxnFreqUniqRb), rod->FreqUniq );
1527  g_signal_handlers_unblock_by_func(ldd->payTxnFreqUniqRb,
1528  (gpointer) loan_pay_freq_toggle_cb, ldd );
1529 
1530  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payFreqHBox), rod->FreqUniq );
1531 
1532  if ( rod->FreqUniq )
1533  {
1534  g_signal_handlers_disconnect_by_func( ldd->payGncFreq,
1535  (gpointer) loan_pay_page_valid_cb, ldd );
1536  gtk_container_remove( GTK_CONTAINER(ldd->payFreqHBox), GTK_WIDGET(ldd->payGncFreq) );
1537  ldd->payGncFreq = NULL;
1538  ldd->payGncFreq = GNC_FREQUENCY(gnc_frequency_new_from_recurrence( rod->schedule, rod->startDate ));
1539  gtk_container_add( GTK_CONTAINER(ldd->payFreqHBox), GTK_WIDGET(ldd->payGncFreq) );
1540  g_signal_connect (ldd->payGncFreq, "changed",
1541  G_CALLBACK (loan_pay_page_valid_cb), ldd);
1542  }
1543  g_string_free( str, TRUE );
1544  loan_pay_page_valid_cb(GTK_WIDGET(ldd->window), ldd);
1545  }
1546 }
1547 
1548 
1549 void
1550 loan_pay_page_valid_cb (GtkWidget *widget, gpointer user_data )
1551 {
1552  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1553  GtkAssistant *assistant = GTK_ASSISTANT(ldd->window);
1554  gint num = gtk_assistant_get_current_page (assistant);
1555  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
1556 
1557  gtk_assistant_set_page_complete (assistant, page,
1558  ( loan_pay_complete (assistant, ldd) &&
1559  loan_pay_all_opt_valid (assistant, ldd )));
1560 }
1561 
1562 
1563 static
1564 void
1565 loan_pay_use_esc_setup( LoanAssistantData *ldd, gboolean newState )
1566 {
1567  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payEscToLabel), newState );
1568  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payEscFromLabel), newState );
1569  if ( newState )
1570  {
1571  g_signal_handlers_block_by_func( ldd->payAcctEscToGAS,
1572  (gpointer) loan_pay_page_valid_cb, ldd );
1573  g_signal_handlers_block_by_func( ldd->payAcctEscFromGAS,
1574  (gpointer) loan_pay_page_valid_cb, ldd );
1575  gnc_account_sel_set_account( ldd->payAcctEscToGAS, ldd->ld.escrowAcct, FALSE );
1576  gnc_account_sel_set_account( ldd->payAcctEscFromGAS, ldd->ld.escrowAcct, FALSE );
1577  g_signal_handlers_unblock_by_func( ldd->payAcctEscToGAS,
1578  (gpointer) loan_pay_page_valid_cb, ldd );
1579  g_signal_handlers_unblock_by_func( ldd->payAcctEscFromGAS,
1580  (gpointer) loan_pay_page_valid_cb, ldd );
1581  }
1582 }
1583 
1584 
1585 static
1586 void
1587 loan_pay_use_esc_toggle_cb( GtkToggleButton *tb, gpointer user_data )
1588 {
1589  gboolean newState;
1590  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1591 
1592  newState = gtk_toggle_button_get_active( tb );
1593  loan_pay_use_esc_setup( ldd, newState );
1594 }
1595 
1596 
1597 static
1598 void
1599 loan_pay_spec_src_setup( LoanAssistantData *ldd, gboolean newState )
1600 {
1601  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctFromLabel), newState );
1602  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctFromGAS), newState );
1603  if ( newState )
1604  {
1605  g_signal_handlers_block_by_func( ldd->payAcctFromGAS,
1606  (gpointer) loan_pay_page_valid_cb, ldd );
1607  gnc_account_sel_set_account( ldd->payAcctFromGAS, ldd->ld.repayOpts[ldd->currentIdx]->from, FALSE );
1608  g_signal_handlers_unblock_by_func( ldd->payAcctFromGAS,
1609  (gpointer) loan_pay_page_valid_cb, ldd );
1610  }
1611  else
1612  {
1613  g_signal_handlers_block_by_func( ldd->payAcctFromGAS,
1614  (gpointer) loan_pay_page_valid_cb, ldd );
1615  gnc_account_sel_set_account( ldd->payAcctFromGAS, NULL, FALSE );
1616  ldd->ld.repayOpts[ldd->currentIdx]->from = NULL;
1617  g_signal_handlers_unblock_by_func( ldd->payAcctFromGAS,
1618  (gpointer) loan_pay_page_valid_cb, ldd );
1619  }
1620 }
1621 
1622 
1623 static
1624 void
1625 loan_pay_spec_src_toggle_cb( GtkToggleButton *tb, gpointer user_data )
1626 {
1627  gboolean newState;
1628  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1629 
1630  newState = gtk_toggle_button_get_active( tb );
1631  loan_pay_spec_src_setup( ldd, newState );
1632 }
1633 
1634 
1635 static
1636 void
1637 loan_pay_freq_toggle_cb( GtkToggleButton *tb, gpointer user_data )
1638 {
1639  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1640  RepayOptData *rod;
1641 
1642  g_assert( ldd->currentIdx >= 0 );
1643  g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
1644 
1645  rod = ldd->ld.repayOpts[ldd->currentIdx];
1646 
1647  rod->FreqUniq = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(ldd->payTxnFreqUniqRb) );
1648  gtk_widget_set_sensitive( GTK_WIDGET(ldd->payFreqHBox), rod->FreqUniq );
1649 
1650  if ( rod->FreqUniq )
1651  {
1652  if ( rod->schedule == NULL )
1653  {
1654  Recurrence *r = g_new0(Recurrence, 1);
1655 
1656  recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
1657  rod->schedule = g_list_append(rod->schedule, r);
1658  }
1659  if ( rod->startDate == NULL )
1660  {
1661  rod->startDate = g_date_new();
1662  *rod->startDate = *ldd->ld.startDate;
1663  }
1664  g_signal_handlers_block_by_func( ldd->payGncFreq,
1665  (gpointer) loan_pay_page_valid_cb, ldd );
1666  gnc_frequency_setup_recurrence(ldd->payGncFreq, rod->schedule, rod->startDate);
1667  g_signal_handlers_unblock_by_func( ldd->payGncFreq,
1668  (gpointer) loan_pay_page_valid_cb, ldd );
1669  }
1670  else
1671  {
1672  if (rod->schedule)
1673  {
1674  recurrenceListFree(&rod->schedule);
1675  }
1676  if ( rod->startDate )
1677  {
1678  g_date_free( rod->startDate );
1679  rod->startDate = NULL;
1680  }
1681  }
1682 }
1683 
1684 
1685 static
1686 void
1687 loan_pay_next_button_cb( GtkButton *button, gpointer user_data )
1688 {
1689  int i;
1690  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1691 
1692  /* save current data */
1693  if ( loan_pay_complete ( GTK_ASSISTANT(ldd->window), user_data ) != FALSE )
1694  {
1695  /* Go through opts list and select next enabled option. */
1696  for ( i = ldd->currentIdx + 1;
1697  (i < ldd->ld.repayOptCount)
1698  && !ldd->ld.repayOpts[i]->enabled; i++ )
1699  ;
1700  if ( i < ldd->ld.repayOptCount )
1701  {
1702  ldd->currentIdx = i;
1703  loan_pay_prep( GTK_ASSISTANT(ldd->window), user_data );
1704  }
1705  }
1706 }
1707 
1708 
1709 static
1710 void
1711 loan_pay_back_button_cb( GtkButton *button, gpointer user_data )
1712 {
1713  int i;
1714  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1715 
1716  /* save current data */
1717  if ( loan_pay_complete ( GTK_ASSISTANT(ldd->window), user_data ) != FALSE)
1718  {
1719  /* go back through opts list and select next enabled options. */
1720  for ( i = ldd->currentIdx - 1;
1721  (i > -1) && !ldd->ld.repayOpts[i]->enabled;
1722  i-- )
1723  ;
1724  if ( i >= 0 )
1725  {
1726  ldd->currentIdx = i;
1727  loan_pay_prep( GTK_ASSISTANT(ldd->window), user_data );
1728  }
1729  }
1730 }
1731 
1732 
1733 static
1734 gboolean
1735 loan_pay_all_opt_valid ( GtkAssistant *assistant, gpointer user_data )
1736 {
1737  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1738  int i;
1739  gboolean all_valid;
1740  all_valid = FALSE;
1741 
1742  /* Go through all option pages checking for valid enabled pages */
1743  for ( i = 0; (i < ldd->ld.repayOptCount); i++ )
1744  {
1745  if (ldd->ld.repayOpts[i]->enabled)
1746  {
1747  if (ldd->ld.repayOpts[i]->optValid)
1748  all_valid = TRUE;
1749  else
1750  all_valid = FALSE;
1751  }
1752  }
1753  return all_valid;
1754 }
1755 
1756 
1757 static
1758 gboolean
1759 loan_pay_complete( GtkAssistant *assistant, gpointer user_data )
1760 {
1761  gchar *tmpStr;
1762  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1763  RepayOptData *rod;
1764 
1765  g_assert( ldd->currentIdx >= 0 );
1766  g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
1767  rod = ldd->ld.repayOpts[ ldd->currentIdx ];
1768 
1769  tmpStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->payTxnName),
1770  0, -1 );
1771  if ( rod->txnMemo != NULL )
1772  {
1773  g_free( rod->txnMemo );
1774  }
1775  rod->txnMemo = tmpStr;
1776  tmpStr = NULL;
1777 
1778  tmpStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->payAmtEntry),
1779  0, -1 );
1780  rod->amount = (float)strtod( tmpStr, NULL );
1781  g_free( tmpStr );
1782 
1783  rod->specSrcAcctP =
1784  gtk_toggle_button_get_active(
1785  GTK_TOGGLE_BUTTON(ldd->paySpecSrcAcct) );
1786 
1787  /* Test for Valid From Account */
1788  if ( rod->specSrcAcctP )
1789  {
1790  rod->from = gnc_account_sel_get_account( ldd->payAcctFromGAS );
1791  if ( rod->from == NULL )
1792  return FALSE;
1793  }
1794 
1795  /* Test for Valid To Account */
1796  rod->to = gnc_account_sel_get_account( ldd->payAcctToGAS );
1797  if ( rod->to == NULL )
1798  return FALSE;
1799 
1800  /* Set Page Valid */
1801  rod->optValid = TRUE;
1802 
1803  /* If Uniq Freq, then save to recurrence */
1804  if ( rod->FreqUniq )
1805  {
1806  if ( rod->startDate == NULL )
1807  {
1808  rod->startDate = g_date_new();
1809  }
1810  recurrenceListFree(&rod->schedule);
1811  gnc_frequency_save_to_recurrence(ldd->payGncFreq, &rod->schedule, rod->startDate);
1812 
1813  if (! rod->schedule )
1814  {
1815  return FALSE;
1816  }
1817  }
1818  return TRUE;
1819 }
1820 
1821 /************************************************************************/
1822 
1823 static
1824 void
1825 loan_rev_prep( GtkAssistant *assistant, gpointer user_data )
1826 {
1827  /* 3, here, does not include the Date column. */
1828  static const int BASE_COLS = 3;
1829  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1830  GtkListStore *store;
1831  GtkCellRenderer *renderer;
1832  GtkTreeViewColumn *column;
1833  GType *types;
1834  int i;
1835  int col = 1;
1836 
1837  /* Make sure we saved last Payment Option */
1838  if (ldd->currentIdx != -1)
1839  loan_pay_complete(assistant, ldd);
1840 
1841  /* Cleanup old view */
1842  if ( ldd->revView != NULL )
1843  {
1844  gtk_widget_destroy( GTK_WIDGET(ldd->revView) );
1845  ldd->revView = NULL;
1846  }
1847 
1848  ldd->ld.revNumPmts = BASE_COLS;
1849  /* Get the correct number of repayment columns. */
1850  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
1851  {
1852  ldd->ld.revRepayOptToColMap[i] = -1;
1853  if ( ! ldd->ld.repayOpts[i]->enabled )
1854  {
1855  continue;
1856  }
1857  /* not '+1' = there is no date column to be accounted for in
1858  * the mapping. */
1859  ldd->ld.revRepayOptToColMap[i] = ldd->ld.revNumPmts;
1860  ldd->ld.revNumPmts += 1;
1861  }
1862  /* '+1' for leading date col */
1863  types = g_new( GType, ldd->ld.revNumPmts + 1 );
1864  for ( i = 0; i < ldd->ld.revNumPmts + 1; i++ )
1865  types[i] = G_TYPE_STRING;
1866  store = gtk_list_store_newv(ldd->ld.revNumPmts + 1, types);
1867  g_free(types);
1868 
1869  ldd->revView = GTK_TREE_VIEW(
1870  gtk_tree_view_new_with_model( GTK_TREE_MODEL(store) ));
1871  g_object_unref(store);
1872 
1873  // Set grid lines option to preference
1874  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(ldd->revView), gnc_tree_view_get_grid_lines_pref ());
1875 
1876  renderer = gtk_cell_renderer_text_new();
1877  column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer,
1878  "text", LOAN_COL_DATE,
1879  nullptr);
1880  gtk_tree_view_append_column(ldd->revView, column);
1881 
1882  renderer = gtk_cell_renderer_text_new();
1883  column = gtk_tree_view_column_new_with_attributes(_("Payment"), renderer,
1884  "text", LOAN_COL_PAYMENT,
1885  nullptr);
1886  gtk_tree_view_append_column(ldd->revView, column);
1887 
1888  renderer = gtk_cell_renderer_text_new();
1889  column = gtk_tree_view_column_new_with_attributes(_("Principal"), renderer,
1890  "text", LOAN_COL_PRINCIPAL,
1891  nullptr);
1892  gtk_tree_view_append_column(ldd->revView, column);
1893 
1894  renderer = gtk_cell_renderer_text_new();
1895  column = gtk_tree_view_column_new_with_attributes(_("Interest"), renderer,
1896  "text", LOAN_COL_INTEREST,
1897  nullptr);
1898  gtk_tree_view_append_column(ldd->revView, column);
1899 
1900  /* move the appropriate names over into the title array */
1901  {
1902  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
1903  {
1904  if ( ldd->ld.revRepayOptToColMap[i] == -1 )
1905  {
1906  continue;
1907  }
1908  renderer = gtk_cell_renderer_text_new();
1909  column = gtk_tree_view_column_new_with_attributes
1910  (ldd->ld.repayOpts[i]->name, renderer,
1911  "text", LOAN_COL_INTEREST + col,
1912  nullptr);
1913  gtk_tree_view_append_column(ldd->revView, column);
1914  col++;
1915  }
1916  }
1917 
1918  gtk_container_add( GTK_CONTAINER(ldd->revScrollWin),
1919  GTK_WIDGET(ldd->revView) );
1920  gtk_widget_show( GTK_WIDGET(ldd->revView) );
1921 
1922  loan_rev_recalc_schedule( ldd );
1923 
1924  {
1925  GDate start, end;
1926  g_date_clear( &start, 1 );
1927  g_date_clear( &end, 1 );
1928  loan_rev_get_dates( ldd, &start, &end );
1929  loan_rev_update_view( ldd, &start, &end );
1930  }
1931 }
1932 
1933 
1934 static
1935 void
1936 loan_rev_range_opt_changed_cb( GtkComboBox *combo, gpointer user_data )
1937 {
1938  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1939  int opt;
1940 
1941  opt = gtk_combo_box_get_active( ldd->revRangeOpt );
1942  gtk_widget_set_sensitive( GTK_WIDGET(ldd->revDateFrame),
1943  (opt == CUSTOM) );
1944  {
1945  GDate start, end;
1946  g_date_clear( &start, 1 );
1947  g_date_clear( &end, 1 );
1948  loan_rev_get_dates( ldd, &start, &end );
1949  loan_rev_update_view( ldd, &start, &end );
1950  }
1951 }
1952 
1953 
1954 static
1955 void
1956 loan_rev_range_changed_cb( GNCDateEdit *gde, gpointer user_data )
1957 {
1958  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
1959  {
1960  GDate start, end;
1961  g_date_clear( &start, 1 );
1962  g_date_clear( &end, 1 );
1963  loan_rev_get_dates( ldd, &start, &end );
1964  loan_rev_update_view( ldd, &start, &end );
1965  }
1966 }
1967 
1968 
1969 static
1970 void
1971 loan_rev_get_loan_range( LoanAssistantData *ldd, GDate *start, GDate *end )
1972 {
1973  int monthsTotal;
1974  struct tm *endDateMath;
1975 
1976  *start = *ldd->ld.startDate;
1977 
1978  endDateMath = g_new0( struct tm, 1 );
1979  g_date_to_struct_tm (ldd->ld.startDate, endDateMath);
1980  monthsTotal = ( ldd->ld.numPer
1981  * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) );
1982  endDateMath->tm_mon += monthsTotal;
1983  gnc_gdate_set_time64 (end, gnc_mktime (endDateMath));
1984  g_date_subtract_days( end, 1 );
1985  g_free (endDateMath);
1986 }
1987 
1988 
1989 static
1990 void
1991 loan_rev_get_dates( LoanAssistantData *ldd, GDate *start, GDate *end )
1992 {
1993  int range = gtk_combo_box_get_active( ldd->revRangeOpt );
1994  switch ( range )
1995  {
1996  case CURRENT_YEAR:
1997  gnc_gdate_set_time64( start, gnc_time (NULL) );
1998  g_date_set_dmy( start, 1, G_DATE_JANUARY, g_date_get_year( start ) );
1999  g_date_set_dmy( end, 31, G_DATE_DECEMBER, g_date_get_year( start ) );
2000  break;
2001  case NOW_PLUS_ONE:
2002  gnc_gdate_set_time64( start, gnc_time (NULL) );
2003  *end = *start;
2004  g_date_add_years( end, 1 );
2005  break;
2006  case WHOLE_LOAN:
2007  loan_rev_get_loan_range( ldd, start, end );
2008  break;
2009  case CUSTOM:
2010  gnc_gdate_set_time64( start,
2011  gnc_date_edit_get_date( ldd->revStartDate ) );
2012  gnc_gdate_set_time64( end,
2013  gnc_date_edit_get_date( ldd->revEndDate ) );
2014  break;
2015  default:
2016  PERR( "Unknown review date range option %d", range );
2017  break;
2018  }
2019 
2020 }
2021 
2022 
2023 static
2024 void
2025 loan_rev_sched_list_free( gpointer data, gpointer user_data )
2026 {
2027  RevRepaymentRow *rrr = (RevRepaymentRow*)data;
2028  g_free( rrr->numCells );
2029  g_free( rrr );
2030 }
2031 
2032 
2033 static
2034 void
2035 loan_rev_hash_to_list( gpointer key, gpointer val, gpointer user_data )
2036 {
2037  GList **l = (GList**)user_data;
2038  RevRepaymentRow *rrr;
2039  if ( !key || !val )
2040  {
2041  DEBUG( "%.8x, %.8x",
2042  GPOINTER_TO_UINT(key),
2043  GPOINTER_TO_UINT(val));
2044  return;
2045  }
2046  rrr = g_new0( RevRepaymentRow, 1 );
2047  rrr->date = *(GDate*)key;
2048  rrr->numCells = (gnc_numeric*)val;
2049  *l = g_list_append( *l, (gpointer)rrr );
2050 }
2051 
2052 
2053 static
2054 void
2055 loan_rev_hash_free_date_keys( gpointer key, gpointer val, gpointer user_data )
2056 {
2057  g_free( (GDate*)key );
2058 }
2059 
2060 
2061 static
2062 void
2063 loan_rev_recalc_schedule( LoanAssistantData *ldd )
2064 {
2065  GDate start, end;
2066  gnc_numeric *rowNumData;
2067  GHashTable *repayment_schedule;
2068 
2069  g_date_clear( &start, 1 );
2070  g_date_clear( &end, 1 );
2071  loan_rev_get_loan_range( ldd, &start, &end );
2072 
2073  /* The repayment_schedule is a hash of GDates to
2074  * row-of-gnc_numeric[N] data, where N is the number of columns as
2075  * determined by the _prep function, and stored in
2076  * LoanData::revNumPmts. */
2077  repayment_schedule = g_hash_table_new(gnc_gdate_hash, gnc_gdate_equal);
2078 
2079  /* Do the master repayment */
2080  {
2081  GDate curDate, nextDate;
2082  GString *pmtFormula, *ppmtFormula, *ipmtFormula;
2083  int i;
2084  GHashTable *ivar;
2085 
2086  pmtFormula = g_string_sized_new( 64 );
2087  loan_get_pmt_formula( ldd, pmtFormula );
2088  ppmtFormula = g_string_sized_new( 64 );
2089  loan_get_ppmt_formula( ldd, ppmtFormula );
2090  ipmtFormula = g_string_sized_new( 64 );
2091  loan_get_ipmt_formula( ldd, ipmtFormula );
2092 
2093  ivar = g_hash_table_new( g_str_hash, g_str_equal );
2094  g_date_clear( &curDate, 1 );
2095  curDate = start;
2096  g_date_subtract_days( &curDate, 1 );
2097  g_date_clear(&nextDate, 1);
2098  recurrenceListNextInstance(ldd->ld.repayment_schedule, &curDate, &nextDate);
2099  for ( i = 1;
2100  g_date_valid( &nextDate )
2101  && g_date_compare( &nextDate, &end ) <= 0 ;
2102  i++,
2103  curDate = nextDate,
2104  recurrenceListNextInstance(ldd->ld.repayment_schedule,
2105  &curDate, &nextDate))
2106  {
2107  gnc_numeric ival;
2108  gnc_numeric val;
2109  char *eloc;
2110  rowNumData =
2111  (gnc_numeric*)g_hash_table_lookup( repayment_schedule,
2112  &nextDate );
2113  if ( rowNumData == NULL)
2114  {
2115  int j;
2116  GDate *dateKeyCopy = g_date_new();
2117 
2118  *dateKeyCopy = nextDate;
2119  rowNumData = g_new0( gnc_numeric, ldd->ld.revNumPmts );
2120  g_assert( rowNumData != NULL );
2121  for ( j = 0; j < ldd->ld.revNumPmts; j++ )
2122  {
2123  rowNumData[j] = gnc_numeric_error( GNC_ERROR_ARG );
2124  }
2125  g_hash_table_insert( repayment_schedule,
2126  (gpointer)dateKeyCopy,
2127  (gpointer)rowNumData );
2128  }
2129 
2130  /* evaluate the expressions given the correct
2131  * sequence number i */
2132  ival = gnc_numeric_create( i, 1 );
2133  g_hash_table_insert( ivar, (gpointer) "i", &ival );
2134 
2135  if ( ! gnc_exp_parser_parse_separate_vars(
2136  pmtFormula->str, &val, &eloc, ivar ) )
2137  {
2138  PERR( "pmt Parsing error at %s", eloc );
2139  continue;
2140  }
2142  rowNumData[0] = val;
2143 
2144  if ( ! gnc_exp_parser_parse_separate_vars(
2145  ppmtFormula->str, &val, &eloc, ivar ) )
2146  {
2147  PERR( "ppmt Parsing error at %s", eloc );
2148  continue;
2149  }
2151  rowNumData[1] = val;
2152 
2153  if ( ! gnc_exp_parser_parse_separate_vars(
2154  ipmtFormula->str, &val, &eloc, ivar ) )
2155  {
2156  PERR( "ipmt Parsing error at %s", eloc );
2157  continue;
2158  }
2160  rowNumData[2] = val;
2161  }
2162 
2163  g_string_free( ipmtFormula, TRUE );
2164  g_string_free( ppmtFormula, TRUE );
2165  g_string_free( pmtFormula, TRUE );
2166 
2167  g_hash_table_destroy( ivar );
2168  }
2169 
2170  /* Process any other enabled payments. */
2171  {
2172  int i;
2173  GDate curDate, nextDate;
2174  GList *schedule;
2175 
2176  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
2177  {
2178  if ( ! ldd->ld.repayOpts[i]->enabled )
2179  continue;
2180 
2181  schedule
2182  = ( ldd->ld.repayOpts[i]->schedule != NULL
2183  ? ldd->ld.repayOpts[i]->schedule
2184  : ldd->ld.repayment_schedule );
2185 
2186  g_date_clear( &curDate, 1 );
2187  curDate = start;
2188  g_date_subtract_days( &curDate, 1 );
2189  g_date_clear(&nextDate, 1);
2190  recurrenceListNextInstance(schedule, &curDate, &nextDate );
2191  for ( ; g_date_valid( &nextDate )
2192  && g_date_compare( &nextDate, &end ) <= 0;
2193  curDate = nextDate,
2194  recurrenceListNextInstance(
2195  schedule, &curDate, &nextDate ) )
2196  {
2197  gint gncn_how =
2200  gnc_numeric val;
2201  rowNumData = (gnc_numeric*)g_hash_table_lookup( repayment_schedule,
2202  &nextDate );
2203  if ( rowNumData == NULL )
2204  {
2205  int j;
2206  GDate *dateKeyCopy = g_date_new();
2207 
2208  *dateKeyCopy = nextDate;
2209  rowNumData = g_new0( gnc_numeric, ldd->ld.revNumPmts );
2210  g_assert( rowNumData != NULL );
2211  for ( j = 0; j < ldd->ld.revNumPmts; j++ )
2212  {
2213  rowNumData[j] = gnc_numeric_error( GNC_ERROR_ARG );
2214  }
2215  g_hash_table_insert( repayment_schedule,
2216  (gpointer)dateKeyCopy,
2217  (gpointer)rowNumData );
2218  }
2219 
2220  val = double_to_gnc_numeric( (double)ldd->ld
2221  .repayOpts[i]
2222  ->amount,
2223  100, gncn_how );
2224  rowNumData[ ldd->ld.revRepayOptToColMap[i] ]
2225  = val;
2226  }
2227  }
2228  }
2229 
2230  /* Convert the GHashTable into a sorted GList in the LoanData */
2231  {
2232  if ( ldd->ld.revSchedule != NULL )
2233  {
2234  g_list_foreach( ldd->ld.revSchedule,
2235  loan_rev_sched_list_free,
2236  NULL );
2237  g_list_free( ldd->ld.revSchedule );
2238  ldd->ld.revSchedule = NULL;
2239  }
2240  g_hash_table_foreach( repayment_schedule, loan_rev_hash_to_list,
2241  &ldd->ld.revSchedule );
2242  g_hash_table_foreach( repayment_schedule, loan_rev_hash_free_date_keys,
2243  NULL );
2244  g_hash_table_destroy( repayment_schedule );
2245  ldd->ld.revSchedule =
2246  g_list_sort( ldd->ld.revSchedule, (GCompareFunc)g_date_compare );
2247  }
2248 }
2249 
2250 
2251 static
2252 void
2253 loan_rev_update_view( LoanAssistantData *ldd, GDate *start, GDate *end )
2254 {
2255  static const gchar *NO_AMT_CELL_TEXT = " ";
2256  GList *l;
2257  GNCPrintAmountInfo pai;
2258  GtkListStore *store;
2259  GtkTreeIter iter;
2260 
2261  pai = gnc_default_price_print_info(NULL);
2262  pai.min_decimal_places = 2;
2263 
2264  store = GTK_LIST_STORE(gtk_tree_view_get_model( ldd->revView ));
2265 
2266  gtk_list_store_clear( store );
2267 
2268  for ( l = ldd->ld.revSchedule; l != NULL; l = l->next )
2269  {
2270  int i;
2271  gchar tmpBuf[50];
2272  RevRepaymentRow *rrr = (RevRepaymentRow*)l->data;
2273 
2274  if ( g_date_compare( &rrr->date, start ) < 0 )
2275  continue;
2276  if ( g_date_compare( &rrr->date, end ) > 0 )
2277  continue; /* though we can probably return, too. */
2278 
2279  gtk_list_store_append(store, &iter);
2280 
2281  qof_print_gdate( tmpBuf, MAX_DATE_LENGTH, &rrr->date );
2282  gtk_list_store_set( store, &iter, LOAN_COL_DATE, tmpBuf, -1 );
2283 
2284  for ( i = 0; i < ldd->ld.revNumPmts; i++ )
2285  {
2286  int numPrinted;
2287  if ( gnc_numeric_check( rrr->numCells[i] )
2288  == GNC_ERROR_ARG )
2289  {
2290  /* '+1' for the date cell */
2291  gtk_list_store_set( store, &iter,
2292  i + 1, NO_AMT_CELL_TEXT,
2293  -1);
2294  continue;
2295  }
2296 
2297  numPrinted = xaccSPrintAmount( tmpBuf, rrr->numCells[i], pai );
2298  g_assert( numPrinted < 50 );
2299  /* '+1' for the date cell */
2300  gtk_list_store_set( store, &iter,
2301  i + 1, tmpBuf,
2302  -1);
2303  }
2304 
2305  }
2306 }
2307 
2308 /************************* Worker functions *****************************/
2309 
2310 /* convert APR rate to simple rate based on formula r=q((1+i)^(1/q)-1) (r=interest rate, i=apr, q=compounding periods) */
2311 
2312 gfloat loan_apr_to_simple_formula (gfloat rate, gfloat compounding_periods)
2313 {
2314  /* float percent_to_frac; - redundant */
2315  gfloat simple_rate;
2316  /* percent_to_frac= compounding_periods/100; - redundant */
2317  simple_rate = compounding_periods * ((pow((1 + rate), (1 / compounding_periods))) - 1);
2318  return (simple_rate);
2319 }
2320 
2321 using boost::locale::conv::utf_to_utf;
2322 
2323 // Define a monetary facet template for printing monetary values
2324 // with a custom precision <prec>
2325 // This will be used to generate the loan formulas, which use 5 decimal places
2326 // for the loan percentage and 2 decimal places for all other numbers.
2327 
2328 // Note this facet is wchar_t based: some locales have non-ascii thousands
2329 // separators and these can't be handled by a char based moneypunct facet.
2330 
2331 template<int prec>
2332 struct cust_prec_punct : std::moneypunct_byname<wchar_t, false> {
2333  cust_prec_punct(const char* name) : moneypunct_byname(name) {}
2334  int do_frac_digits() const { return prec; }
2335 };
2336 
2337 // Convert a double to a string hardcoding <prec> decimal places
2338 template<int prec>
2339 std::string to_str_with_prec (const gdouble val)
2340 {
2341 #ifdef __MINGW32__
2342  NUMBERFMTW numfmt;
2343  LCID lcid = GetThreadLocale();
2344  DWORD numval;
2345 
2346  numfmt.NumDigits = prec;
2347  GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR)&numval,
2348  sizeof(numval)/sizeof(wchar_t));
2349  numfmt.LeadingZero = numval;
2350  wchar_t grouping[10];
2351  GetLocaleInfoW(lcid, LOCALE_SGROUPING, grouping,
2352  sizeof(grouping)/sizeof(wchar_t));
2353  auto semi = wcschr(grouping, ';');
2354  *semi = 0;
2355  numfmt.Grouping = _wtoi(grouping);
2356  wchar_t decsep[4];
2357  GetLocaleInfoW(lcid, LOCALE_SDECIMAL, decsep,
2358  sizeof(decsep)/sizeof(wchar_t) );
2359  numfmt.lpDecimalSep = decsep;
2360  wchar_t thousep[4];
2361  GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousep,
2362  sizeof(thousep)/sizeof(wchar_t));
2363  numfmt.lpThousandSep = thousep;
2364  GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER,
2365  (LPWSTR)&numval, sizeof(numval)/sizeof(wchar_t));
2366  numfmt.NegativeOrder = numval;
2367 //Can't use std::to_wstring, it localizes with a C function.
2368  std::wstringstream valstr;
2369  valstr << val;
2370  int size = GetNumberFormatW(lcid, 0, valstr.str().c_str(),
2371  &numfmt, nullptr, 0);
2372  wchar_t* buf = static_cast<wchar_t*>(malloc(sizeof(wchar_t) * size));
2373  GetNumberFormatW(lcid, 0, valstr.str().c_str(), &numfmt, buf, size);
2374  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conv;
2375  std::string result = conv.to_bytes(buf);
2376  free(buf);
2377  return result;
2378 #else
2379  auto loc = std::locale(gnc_get_locale(), new cust_prec_punct<prec>(""));
2380  std::wstringstream valstr;
2381  valstr.imbue(loc);
2382  valstr << std::put_money(val * pow(10, prec));
2383  return utf_to_utf<char>(valstr.str());
2384 #endif
2385 }
2386 
2387 static
2388 void
2389 loan_get_formula_internal( LoanAssistantData *ldd, GString *gstr, const gchar *tpl )
2390 {
2391  g_assert( ldd != NULL );
2392  g_assert( gstr != NULL );
2393 
2394  gdouble pass_thru_rate = ldd->ld.interestRate / 100.0;
2395  gdouble periods = (ldd->ld.numPer * (ldd->ld.perSize == GNC_MONTHS ? 1 : 12)) * 1.;
2396  auto principal = gnc_numeric_to_double(ldd->ld.principal);
2397 
2398  gdouble period_rate;
2399  auto rate_case = ldd->ld.rateType;
2400  switch (rate_case)
2401  {
2402  case GNC_IRATE_SIMPLE:
2403  period_rate = pass_thru_rate;
2404  break;
2405  case GNC_IRATE_APR_DAILY:
2406  period_rate = loan_apr_to_simple_formula (pass_thru_rate, 365);
2407  break;
2408  case GNC_IRATE_APR_WEEKLY:
2409  period_rate = loan_apr_to_simple_formula (pass_thru_rate, 52);
2410  break;
2411  case GNC_IRATE_APR_MONTHLY:
2412  period_rate = loan_apr_to_simple_formula (pass_thru_rate, 12);
2413  break;
2414  case GNC_IRATE_APR_QUARTERLY:
2415  period_rate = loan_apr_to_simple_formula (pass_thru_rate, 4);
2416  break;
2417  case GNC_IRATE_APR_ANNUALLY:
2418  period_rate = loan_apr_to_simple_formula (pass_thru_rate, 1);
2419  break;
2420  default:
2421  period_rate = ldd->ld.interestRate / 100;
2422  break;
2423  }
2424  auto period_rate_str = to_str_with_prec<5> (period_rate);
2425  auto period_base_str = to_str_with_prec<2> (12.0);
2426  auto periods_str = to_str_with_prec<2> (periods);
2427  auto principal_str = to_str_with_prec<2> (principal);
2428 
2429  // Using boost::locale::format here to merge a template
2430  // with plain strings. We can't use bl::format directly on the double
2431  // values as it will use numeric punctuation instead of monetary punctuation.
2432  // This is different in several locales (like nl_BE and ru_RU)
2433  // and our parsing function does expect monetary punctuation to work properly.
2434  // So instead of bl::format we could also have used boost::format.
2435  // However at the time of this writing that sublibrary is not yet a requirement
2436  // for gnucash. So I stuck with bl::format, which is.
2437  auto formula = (bl::format (tpl) % period_rate_str %
2438  period_base_str % periods_str % principal_str).str();
2439  g_string_append (gstr, formula.c_str());
2440 }
2441 
2442 static
2443 void
2444 loan_get_pmt_formula( LoanAssistantData *ldd, GString *gstr )
2445 {
2446  loan_get_formula_internal (ldd, gstr, "pmt( {1} / {2} : {3} : {4} : 0 : 0 )");
2447 }
2448 
2449 
2450 static
2451 void
2452 loan_get_ppmt_formula( LoanAssistantData *ldd, GString *gstr )
2453 {
2454  loan_get_formula_internal (ldd, gstr, "ppmt( {1} / {2} : i : {3} : {4} : 0 : 0 )");
2455 }
2456 
2457 
2458 static
2459 void
2460 loan_get_ipmt_formula( LoanAssistantData *ldd, GString *gstr )
2461 {
2462  loan_get_formula_internal (ldd, gstr, "ipmt( {1} / {2} : i : {3} : {4} : 0 : 0 )");
2463 }
2464 
2465 /******************* Scheduled Transaction Functions ********************/
2466 
2467 static int
2468 ld_calc_sx_instance_num(GDate *start_date, GList *schedule)
2469 {
2470  int instance_count;
2471  GDate next_date, today;
2472 
2473  g_date_clear(&next_date, 1);
2474  g_date_clear(&today, 1);
2475  gnc_gdate_set_time64 (&today, gnc_time (NULL));
2476 
2477  if (g_date_compare(start_date, &today) > 0)
2478  return 0;
2479 
2480  instance_count = -1;
2481  do
2482  {
2483  instance_count++;
2484  recurrenceListNextInstance(schedule, start_date, &next_date);
2485  }
2486  while (g_date_compare(&next_date, &today) < 0);
2487 
2488  return instance_count;
2489 }
2490 
2491 
2492 static
2493 void
2494 loan_tcSX_free( gpointer data, gpointer user_data )
2495 {
2496  toCreateSX *tcSX = (toCreateSX*)data;
2497  g_free( tcSX->name );
2498  tcSX->mainTxn.~TTInfoPtr();
2499  tcSX->escrowTxn.~TTInfoPtr();
2500  g_free( tcSX );
2501 }
2502 
2503 
2507 static
2508 void
2509 loan_create_sx_from_tcSX( LoanAssistantData *ldd, toCreateSX *tcSX )
2510 {
2511  SchedXaction *sx;
2512  SchedXactions *sxes;
2513  TTInfoVec ttxn_vec;
2514 
2515  sx = xaccSchedXactionMalloc( gnc_get_current_book() );
2516  xaccSchedXactionSetName( sx, tcSX->name );
2517  gnc_sx_set_schedule(sx, tcSX->schedule);
2518  xaccSchedXactionSetStartDate( sx, &tcSX->start );
2519  xaccSchedXactionSetLastOccurDate( sx, &tcSX->last );
2520  xaccSchedXactionSetEndDate( sx, &tcSX->end );
2521  gnc_sx_set_instance_count( sx, tcSX->instNum );
2522 
2523  if ( tcSX->mainTxn )
2524  ttxn_vec.push_back (tcSX->mainTxn);
2525  if ( tcSX->escrowTxn )
2526  ttxn_vec.push_back (tcSX->escrowTxn);
2527 
2528  g_assert (!ttxn_vec.empty());
2529 
2530  xaccSchedXactionSetTemplateTrans (sx, ttxn_vec, gnc_get_current_book());
2531 
2532  sxes = gnc_book_get_schedxactions(gnc_get_current_book());
2533  gnc_sxes_add_sx(sxes, sx);
2534 }
2535 
2536 
2537 static TTSplitInfoPtr
2538 find_account_from_template_splits (const TTInfoPtr& txn, const Account* account)
2539 {
2540  auto& splits{txn->get_template_splits ()};
2541  auto has_acct = [account](auto ttinfo){ return ttinfo->get_account() == account; };
2542  auto it = std::find_if (splits.begin(), splits.end(), has_acct);
2543  return it == splits.end() ? nullptr : *it;
2544 }
2545 
2551 static
2552 void
2553 ld_setup_repayment_sx( LoanAssistantData *ldd,
2554  RepayOptData *rod,
2555  toCreateSX *paymentSX,
2556  toCreateSX *tcSX )
2557 {
2558  /* In DoubleEntryAccounting-ease, this is what we're going to do,
2559  * below...
2560  *
2561  * if ( rep->escrow ) {
2562  * if ( rep->from ) {
2563  * a: paymentSX.main.splits += split( rep->fromAcct, repAmt )
2564  * b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
2565  * tcSX.escrow.split( rep->escrow ).debCred += repAmt
2566  * c1: tcSX.escrow.splits += split( rep->toAcct, +repAmt )
2567  * } else {
2568  * d: paymentSX.main.split( ldd->ld.repFromAcct ).debcred += -repAmt
2569  * b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
2570  * tcSX.escrow.splits += split( rep->escrow, -repAmt )
2571  * c1: tcSX.escrow.splits += split( rep->toAcct, +repAmt )
2572  * }
2573  * } else {
2574  * if ( rep->from ) {
2575  * a: paymentSX.main.splits += split( rep->fromAcct, -repAmt )
2576  * c2: paymentSX.main.splits += split( rep->toAcct, +repAmt )
2577  * } else {
2578  * d: paymentSX.main.split( ldd->ld.payFromAcct ).debcred += -repAmt
2579  * c2: paymentSX.main.splits += split( rep->toAcct, +repAmt )
2580  * }
2581  * }
2582  */
2583 
2584  /* Now, we refactor the common operations from the above out...
2585  *
2586  * fromSplit = NULL;
2587  * if ( rep->escrow ) {
2588  * b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
2589  * c1: ( toTTI = tcSX.escrow )
2590  * if ( rep->from ) {
2591  * a1: (fromSplit = NULL) paymentSX.main.splits += split( rep->fromAcct, repAmt )
2592  * b:
2593  * tcSX.escrow.split( rep->escrow ).debCred += repAmt
2594  * c1:
2595  * } else {
2596  * a2: (fromSplit = paymentSX.main.split( ldd->ld.repFromAcct )) .debcred += -repAmt
2597  * b:
2598  * tcSX.escrow.splits += split( rep->escrow, -repAmt )
2599  * c1:
2600  * }
2601  * } else {
2602  * c2: ( toTTI = paymentSX.main )
2603  * if ( rep->from ) {
2604  * a1: (fromSplit = NULL) paymentSX.main.splits += split( rep->fromAcct, -repAmt )
2605  * c2:
2606  * } else {
2607  * a2: (fromSplit = paymentSX.main.split( ldd->ld.payFromAcct )).debcred += -repAmt
2608  * c2:
2609  * }
2610  * }
2611  * if ( fromSplit ) {
2612  * fromSplit.debCred += (-repAmt);
2613  * } else {
2614  * fromSplit = split( rep->fromAcct, -repAmt )
2615  * paymentSX.main.splits += fromSplit
2616  * }
2617  * toTTI.splits += split( rep->toAcct, +repAmt );
2618  */
2619 
2622  GString *gstr;
2623  TTSplitInfoPtr fromSplit;
2624  TTSplitInfoPtr ttsi;
2625  TTInfoPtr toTxn;
2626  GNCPrintAmountInfo pricePAI = gnc_default_price_print_info(NULL);
2627 #define AMTBUF_LEN 64
2628  gchar amtBuf[AMTBUF_LEN];
2629  gint GNCN_HOW = (GNC_HOW_DENOM_SIGFIGS(2) | GNC_HOW_RND_ROUND_HALF_UP);
2630 
2631  /* We're going to use this a lot, below, so just create it once. */
2632  xaccSPrintAmount( amtBuf,
2633  double_to_gnc_numeric( rod->amount, 100,
2634  GNCN_HOW ),
2635  pricePAI );
2636 
2637  if ( rod->throughEscrowP && ldd->ld.escrowAcct )
2638  {
2639  toTxn = tcSX->escrowTxn;
2640 
2641  /* Add the repayment amount into the string of the existing
2642  * ttsplit. */
2643  {
2644  auto ttsi = find_account_from_template_splits (paymentSX->mainTxn, ldd->ld.escrowAcct);
2645  g_assert (ttsi);
2646  gstr = g_string_new (ttsi->get_debit_formula());
2647  g_string_append_printf( gstr, " + %s", amtBuf );
2648  ttsi->set_debit_formula (gstr->str);
2649  g_string_free( gstr, TRUE );
2650  gstr = NULL;
2651  ttsi = NULL;
2652  }
2653 
2654  if ( rod->from != NULL )
2655  {
2656  gchar *str;
2657 
2658  fromSplit = NULL;
2659 
2660  /* tcSX.escrow.split( rep->escrow ).debCred += repAmt */
2661  auto ttsi = find_account_from_template_splits (tcSX->escrowTxn, ldd->ld.escrowAcct);
2662  if ( !ttsi )
2663  {
2664  /* create split */
2665  ttsi = std::make_shared<TTSplitInfo>();
2666  ttsi->set_memo (rod->txnMemo);
2667  ttsi->set_account (ldd->ld.escrowAcct);
2668  tcSX->escrowTxn->append_template_split (ttsi);
2669  }
2670  if ( (str = (gchar*)ttsi->get_credit_formula ())
2671  == NULL )
2672  {
2673  gstr = g_string_sized_new( 16 );
2674  }
2675  else
2676  {
2677  /* If we did get a split/didn't have to
2678  * create a split, then we need to add our
2679  * amount in rather than replace. */
2680  gstr = g_string_new( str );
2681  g_string_append_printf( gstr, " + " );
2682  }
2683  g_string_append_printf( gstr, "%s", amtBuf );
2684  ttsi->set_credit_formula (gstr->str);
2685  g_string_free( gstr, TRUE );
2686  gstr = NULL;
2687  ttsi = NULL;
2688  }
2689  else
2690  {
2691  /* (fromSplit = paymentSX.main.split( ldd->ld.repFromAcct )) */
2692  fromSplit = find_account_from_template_splits (paymentSX->mainTxn, ldd->ld.repFromAcct);
2693  g_assert (fromSplit);
2694 
2695  /* tcSX.escrow.splits += split( rep->escrow, -repAmt ) */
2696  ttsi = std::make_shared<TTSplitInfo>();
2697  ttsi->set_memo (rod->txnMemo);
2698  ttsi->set_account (ldd->ld.escrowAcct);
2699  ttsi->set_credit_formula (amtBuf);
2700  tcSX->escrowTxn->append_template_split (ttsi);
2701  ttsi = NULL;
2702  }
2703  }
2704  else
2705  {
2706  toTxn = tcSX->mainTxn;
2707 
2708  if ( rod->from != NULL )
2709  {
2710  fromSplit = NULL;
2711  }
2712  else
2713  {
2714  fromSplit = find_account_from_template_splits (tcSX->mainTxn, ldd->ld.repFromAcct);
2715  }
2716  }
2717 
2718  if ( fromSplit != NULL )
2719  {
2720  /* Update the existing from-split. */
2721  gstr = g_string_new (fromSplit->get_credit_formula ());
2722  g_string_append_printf( gstr, " + %s", amtBuf );
2723  fromSplit->set_credit_formula (gstr->str);
2724  g_string_free( gstr, TRUE );
2725  gstr = NULL;
2726 
2727  }
2728  else
2729  {
2730  TTInfoPtr tti;
2731  /* Create a new from-split. */
2732  ttsi = std::make_shared<TTSplitInfo>();
2733  ttsi->set_memo (rod->txnMemo);
2734  ttsi->set_account (rod->from ? rod->from : ldd->ld.repFromAcct);
2735  ttsi->set_credit_formula (amtBuf);
2736  tti = tcSX->mainTxn;
2737  if ( rod->throughEscrowP )
2738  {
2739  tti = paymentSX->mainTxn;
2740  }
2741  tti->append_template_split (ttsi);
2742  ttsi = NULL;
2743  tti = NULL;
2744  }
2745 
2746  /* Add to-account split. */
2747  {
2748  ttsi = std::make_shared<TTSplitInfo>();
2749  ttsi->set_memo (rod->txnMemo);
2750  ttsi->set_account (rod->to);
2751  ttsi->set_debit_formula (amtBuf);
2752  toTxn->append_template_split (ttsi);
2753  ttsi = NULL;
2754  }
2755 }
2756 
2757 
2772 static
2773 void
2774 loan_create_sxes( LoanAssistantData *ldd )
2775 {
2776  /* The main loan-payment SX.*/
2777  toCreateSX *paymentSX = NULL;
2778  /* A GList of any other repayment SXes with different schedule. */
2779  GList *repaySXes = NULL;
2780  /* The currently-being-referenced toCreateSX. */
2781  toCreateSX *tcSX;
2782  int i;
2783  TTInfoPtr ttxn;
2784  TTSplitInfoPtr ttsi;
2785  GString *gstr;
2786 
2787  paymentSX = g_new0( toCreateSX, 1 );
2788  paymentSX->name = g_strdup(ldd->ld.repMemo);
2789  paymentSX->start = *ldd->ld.startDate;
2790  paymentSX->last = *ldd->ld.repStartDate;
2791  g_date_subtract_months( &paymentSX->last, 1 );
2792  {
2793  paymentSX->end = *ldd->ld.repStartDate;
2794  g_date_add_months( &paymentSX->end, ldd->ld.numMonRemain - 1);
2795  }
2796 
2797  paymentSX->schedule = ldd->ld.repayment_schedule;
2798  /* Figure out the correct current instance-count for the txns in the
2799  * SX. */
2800  paymentSX->instNum =
2801  (ldd->ld.numPer * ( ldd->ld.perSize == GNC_YEARS ? 12 : 1 ))
2802  - ldd->ld.numMonRemain + 1;
2803 
2804  paymentSX->mainTxn = std::make_shared<TTInfo>();
2805  paymentSX->mainTxn->set_currency (gnc_default_currency());
2806 
2807  {
2808  GString *payMainTxnDesc = g_string_sized_new( 32 );
2809  g_string_printf( payMainTxnDesc,
2810  "%s - %s",
2811  ldd->ld.repMemo,
2812  ( ldd->ld.escrowAcct == NULL
2813  ? _("Payment")
2814  : _("Escrow Payment") )
2815  );
2816 
2817  paymentSX->mainTxn->set_description(payMainTxnDesc->str);
2818  g_string_free( payMainTxnDesc, TRUE );
2819  }
2820 
2821  /* Create the basic txns and splits...
2822  *
2823  * ttxn <- mainTxn
2824  * srcAcct <- assets
2825  * if ( escrow ) {
2826  * realSrcAcct <- srcAcct
2827  * srcAcct <- escrow;
2828  * ttxn <- escrowTxn
2829  * main.splits += split( realSrcAcct, -pmt )
2830  * main.splits += split( escrow, pmt )
2831  * }
2832  * ttxn.splits += split( escrow, -pmt)
2833  * ttxn.splits += split( liability, ppmt )
2834  * ttxn.splits += split( expenses:interest, ipmt ) */
2835 
2836  {
2837  Account *srcAcct;
2838 
2839  ttxn = paymentSX->mainTxn;
2840  srcAcct = ldd->ld.repFromAcct;
2841  if ( ldd->ld.escrowAcct != NULL )
2842  {
2843  Account *realSrcAcct = srcAcct;
2844  srcAcct = ldd->ld.escrowAcct;
2845  gstr = g_string_sized_new( 32 );
2846  loan_get_pmt_formula( ldd, gstr );
2847  /* ttxn.splits += split( realSrcAcct, -pmt ); */
2848  {
2849  ttsi = std::make_shared<TTSplitInfo>();
2850  ttsi->set_memo (ldd->ld.repMemo);
2851  ttsi->set_account (realSrcAcct);
2852  ttsi->set_credit_formula (gstr->str);
2853  ttxn->append_template_split (ttsi);
2854  ttsi = NULL;
2855  }
2856  /* ttxn.splits += split( escrowAcct, +pmt ); */
2857  {
2858  ttsi = std::make_shared<TTSplitInfo>();
2859  ttsi->set_memo (ldd->ld.repMemo);
2860  ttsi->set_account (ldd->ld.escrowAcct);
2861  ttsi->set_debit_formula (gstr->str);
2862  ttxn->append_template_split (ttsi);
2863  ttsi = NULL;
2864  }
2865  g_string_free( gstr, TRUE );
2866  gstr = NULL;
2867  paymentSX->escrowTxn = std::make_shared<TTInfo>();
2868  paymentSX->escrowTxn->set_currency (gnc_default_currency());
2869 
2870  {
2871  GString *escrowTxnDesc;
2872  escrowTxnDesc = g_string_new( ldd->ld.repMemo );
2873  g_string_append_printf( escrowTxnDesc, " - %s", _("Payment") );
2874  paymentSX->escrowTxn->set_description (escrowTxnDesc->str);
2875  g_string_free( escrowTxnDesc, TRUE );
2876  }
2877  ttxn = paymentSX->escrowTxn;
2878  }
2879  /* ttxn.splits += split( srcAcct, -pmt ); */
2880  {
2881  ttsi = std::make_shared<TTSplitInfo>();
2882  {
2883  gstr = g_string_new( ldd->ld.repMemo );
2884  g_string_append_printf( gstr, " - %s",
2885  _("Payment") );
2886  ttsi->set_memo (gstr->str);
2887  g_string_free( gstr, TRUE );
2888  gstr = NULL;
2889  }
2890  ttsi->set_account (srcAcct);
2891  gstr = g_string_sized_new( 32 );
2892  loan_get_pmt_formula( ldd, gstr );
2893  ttsi->set_credit_formula (gstr->str);
2894  ttxn->append_template_split (ttsi);
2895  g_string_free( gstr, TRUE );
2896  gstr = NULL;
2897  ttsi = NULL;
2898  }
2899  /* ttxn.splits += split( ldd->ld.repPriAcct, +ppmt ); */
2900  {
2901  ttsi = std::make_shared<TTSplitInfo>();
2902  {
2903  gstr = g_string_new( ldd->ld.repMemo );
2904  g_string_append_printf( gstr, " - %s",
2905  _("Principal") );
2906  ttsi->set_memo (gstr->str);
2907  g_string_free( gstr, TRUE );
2908  gstr = NULL;
2909  }
2910  ttsi->set_account (ldd->ld.repPriAcct);
2911  gstr = g_string_sized_new( 32 );
2912  loan_get_ppmt_formula( ldd, gstr );
2913  ttsi->set_debit_formula (gstr->str);
2914  ttxn->append_template_split (ttsi);
2915  g_string_free( gstr, TRUE );
2916  gstr = NULL;
2917  ttsi = NULL;
2918  }
2919  /* ttxn.splits += split( ldd->ld.repIntAcct, +ipmt ); */
2920  {
2921  ttsi = std::make_shared<TTSplitInfo>();
2922  {
2923  gstr = g_string_new( ldd->ld.repMemo );
2924  g_string_append_printf( gstr, " - %s",
2925  _("Interest") );
2926  ttsi->set_memo (gstr->str);
2927  g_string_free( gstr, TRUE );
2928  gstr = NULL;
2929  }
2930  ttsi->set_account (ldd->ld.repIntAcct);
2931  gstr = g_string_sized_new( 32 );
2932  loan_get_ipmt_formula( ldd, gstr );
2933  ttsi->set_debit_formula (gstr->str);
2934  ttxn->append_template_split (ttsi);
2935  g_string_free( gstr, TRUE );
2936  gstr = NULL;
2937  ttsi = NULL;
2938  }
2939  }
2940  for ( i = 0; i < ldd->ld.repayOptCount; i++ )
2941  {
2942  RepayOptData *rod = ldd->ld.repayOpts[i];
2943  if ( ! rod->enabled )
2944  continue;
2945 
2946  tcSX = paymentSX;
2947  if ( rod->schedule != NULL )
2948  {
2949  tcSX = g_new0( toCreateSX, 1 );
2950  gstr = g_string_new( ldd->ld.repMemo );
2951  g_string_append_printf( gstr, " - %s",
2952  rod->name );
2953  tcSX->name = g_strdup(gstr->str);
2954  tcSX->start = *ldd->ld.startDate;
2955  tcSX->last = *ldd->ld.repStartDate;
2956  {
2957  tcSX->end = tcSX->last;
2958  g_date_add_months( &tcSX->end, ldd->ld.numMonRemain );
2959  }
2960  tcSX->schedule = rod->schedule;
2961  /* So it won't get destroyed when the close the
2962  * Assistant. */
2963  tcSX->instNum =
2964  ld_calc_sx_instance_num(&tcSX->start, rod->schedule);
2965  rod->schedule = NULL;
2966  tcSX->mainTxn = std::make_shared<TTInfo>();
2967  tcSX->mainTxn->set_currency(gnc_default_currency());
2968  tcSX->mainTxn->set_description (gstr->str);
2969  tcSX->escrowTxn = std::make_shared<TTInfo>();
2970  tcSX->escrowTxn->set_currency (gnc_default_currency());
2971  tcSX->escrowTxn->set_description(gstr->str);
2972 
2973  g_string_free( gstr, TRUE );
2974  gstr = NULL;
2975 
2976  repaySXes = g_list_prepend (repaySXes, tcSX);
2977 
2978  }
2979 
2980  /* repayment */
2981  ld_setup_repayment_sx( ldd, rod, paymentSX, tcSX );
2982  }
2983 
2984  repaySXes = g_list_reverse (repaySXes);
2985  /* Create the SXes */
2986  {
2987  GList *l;
2988 
2989  loan_create_sx_from_tcSX( ldd, paymentSX );
2990 
2991  for ( l = repaySXes; l; l = l->next )
2992  {
2993  loan_create_sx_from_tcSX( ldd, (toCreateSX*)l->data );
2994  }
2995  }
2996  /* Clean up. */
2997  loan_tcSX_free( paymentSX, NULL );
2998  g_list_foreach( repaySXes, loan_tcSX_free, NULL );
2999  g_list_free( repaySXes );
3000 }
3001 
3002 /************************ Assistant Functions ***************************/
3003 
3004 void
3005 loan_assistant_finish ( GtkAssistant *gtkassistant, gpointer user_data )
3006 {
3007  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
3008  loan_create_sxes( ldd );
3009 
3010 }
3011 
3012 
3013 void
3014 loan_assistant_cancel( GtkAssistant *gtkassistant, gpointer user_data )
3015 {
3016  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
3017  gnc_close_gui_component_by_data( DIALOG_LOAN_ASSISTANT_CM_CLASS, ldd );
3018 }
3019 
3020 
3021 void
3022 loan_assistant_close( GtkAssistant *gtkassistant, gpointer user_data )
3023 {
3024  LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
3025  gnc_close_gui_component_by_data( DIALOG_LOAN_ASSISTANT_CM_CLASS, ldd );
3026 }
3027 
3028 
3029 void
3030 loan_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
3031  gpointer user_data)
3032 {
3033  gint currentpage = gtk_assistant_get_current_page(assistant);
3034 
3035  switch (currentpage)
3036  {
3037  case 1:
3038  /* Current page is info page */
3039  loan_info_prep (assistant, user_data);
3040  break;
3041  case 2:
3042  /* Current page is Options page */
3043  loan_opt_prep (assistant, user_data);
3044  break;
3045  case 3:
3046  /* Current page is Repayments page */
3047  loan_rep_prep (assistant, user_data);
3048  break;
3049  case 4:
3050  /* Current page is Repayments Options page */
3051  loan_pay_prep (assistant, user_data);
3052  break;
3053  case 5:
3054  /* Current page is Review page */
3055  loan_rev_prep (assistant, user_data);
3056  break;
3057  }
3058 }
3059 
3060 
3061 /********************************************************************\
3062  * gnc_ui_sx_loan_assistant_create *
3063  * opens up a window to start the loan Assistant *
3064  * *
3065 \********************************************************************/
3066 void
3067 gnc_ui_sx_loan_assistant_create (void)
3068 {
3069  LoanAssistantData *ldd;
3070  gint component_id;
3071 
3072  ldd = g_new0 (LoanAssistantData, 1);
3073 
3074  gnc_loan_assistant_create (ldd);
3075 
3076  component_id = gnc_register_gui_component (DIALOG_LOAN_ASSISTANT_CM_CLASS,
3077  NULL, loan_assistant_close_handler,
3078  ldd);
3079 
3080  gnc_gui_component_watch_entity_type (component_id,
3081  GNC_ID_ACCOUNT,
3082  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
3083 
3084  gtk_widget_show_all (ldd->window);
3085 
3086  gnc_window_adjust_for_screen (GTK_WINDOW(ldd->window));
3087 }
A transient struct used to collate the GDate and the gnc_numeric row-data for the repayment review sc...
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.
TTInfoPtr mainTxn
The main/source transaction being created.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
Date and Time handling routines.
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
gint gnc_gdate_equal(gconstpointer gda, gconstpointer gdb)
Compares two GDate*&#39;s for equality; useful for using GDate*&#39;s as GHashTable keys. ...
Definition: gnc-date.cpp:1401
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:143
gchar * name
The name of the SX.
guint gnc_gdate_hash(gconstpointer gd)
Provides a "hash" of a GDate* value; useful for using GDate*&#39;s as GHashTable keys.
Definition: gnc-date.cpp:1407
STRUCTS.
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:597
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
The data relating to a single "repayment option" – a potential [sub-]transaction in the repayment...
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
The cash account type is used to denote a shoe-box or pillowcase stuffed with * cash.
Definition: Account.h:110
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
void gnc_tm_free(struct tm *time)
free a struct tm* created with gnc_localtime() or gnc_gmtime()
Definition: gnc-date.cpp:96
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
The UI-side storage of the loan assistant data.
The default repayment options data.
Account handling public routines.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments &#39;...
gint instNum
The current &#39;instance-num&#39; count.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
Anchor Scheduled Transaction info in a book.
A transient structure to contain SX details during the creation process.
GDate start
The start, last-occurred and end dates.
The bank account type denotes a savings or checking account held at a bank.
Definition: Account.h:107
The UI-side storage of the repayment options.
Argument is not a valid number.
Definition: gnc-numeric.h:224
time64 gnc_mktime(struct tm *time)
calculate seconds from the epoch given a time struct
Definition: gnc-date.cpp:218
GList * schedule
The SX schedule.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
struct tm * gnc_localtime(const time64 *secs)
fill out a time struct from a 64-bit time value.
Definition: gnc-date.cpp:102
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
All type declarations for the whole Gnucash engine.
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1244
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3371
TTInfoPtr escrowTxn
The optional escrow transaction being created.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.
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...
Definition: gnc-date.h:87
Scheduled Transactions public handling routines.
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
Set to an invalid GDate to turn off &#39;end-date&#39; definition.
#define GNC_HOW_DENOM_SIGFIGS(n)
Build a &#39;how&#39; value that will generate a denominator that will keep at least n significant figures in...
Definition: gnc-numeric.h:217
Implementations.
Data about a loan repayment.
The Credit card account is used to denote credit (e.g.
Definition: Account.h:113