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