GnuCash 2.3.0
druid-loan.c
00001 /********************************************************************\
00002  * druid-loan.c : A Gnome Druid for setting up loan-repayment       *
00003  *     scheduled transactions.                                      *
00004  * Copyright (C) 2002,2007 Joshua Sled <jsled@asynchronous.org>     *
00005  * Copyright (C) 2006 David Hampton <hampton@employees.org>         *
00006  *                                                                  *
00007  * This program is free software; you can redistribute it and/or    *
00008  * modify it under the terms of the GNU General Public License as   *
00009  * published by the Free Software Foundation; either version 2 of   *
00010  * the License, or (at your option) any later version.              *
00011  *                                                                  *
00012  * This program is distributed in the hope that it will be useful,  *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00015  * GNU General Public License for more details.                     *
00016  *                                                                  *
00017  * You should have received a copy of the GNU General Public License*
00018  * along with this program; if not, contact:                        *
00019  *                                                                  *
00020  * Free Software Foundation           Voice:  +1-617-542-5942       *
00021  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00022  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <gnome.h>
00028 #include <glib/gi18n.h>
00029 #include <string.h>
00030 #include <glade/glade.h>
00031 #include <math.h>
00032 
00033 #include "druid-loan.h"
00034 
00035 #include "SchedXaction.h"
00036 #include "SX-book.h"
00037 #include "SX-ttinfo.h"
00038 #include "druid-utils.h"
00039 #include "gnc-amount-edit.h"
00040 #include "gnc-account-sel.h"
00041 #include "gnc-date.h"
00042 #include "gnc-exp-parser.h"
00043 #include "gnc-component-manager.h"
00044 #include "dialog-utils.h"
00045 #include "Account.h"
00046 #include "gnc-ui.h"
00047 #include "gnc-gdate-utils.h"
00048 #include "gnc-gui-query.h"
00049 #include "gnc-ui-util.h"
00050 #include "gnc-frequency.h"
00051 #include "gnc-engine.h"
00052 
00053 #define DIALOG_LOAN_DRUID_CM_CLASS "druid-loan-setup"
00054 
00055 #define SX_GLADE_FILE "sched-xact.glade"
00056 #define LOAN_DRUID_WIN_GLADE_NAME "loan_druid_win"
00057 #define LOAN_DRUID_GLADE_NAME     "loan_druid"
00058 
00059 #define PG_INTRO "loan_intro_pg"
00060 #define PG_INFO "loan_info_pg"
00061 #  define PARAM_TABLE      "param_table"
00062 #  define ORIG_PRINC_ENTRY "orig_princ_ent"
00063 #  define IRATE_SPIN       "irate_spin"
00064 #  define IRATE_TYPE_COMBOBOX "irate_type_combobox"
00065 #  define TYPE_COMBOBOX    "type_combobox"
00066 #  define VAR_CONTAINER    "type_freq_frame"
00067 #  define LENGTH_SPIN      "len_spin"
00068 #  define LENGTH_OPT       "len_opt"
00069 #  define REMAIN_SPIN      "rem_spin"
00070 #define PG_OPTS "loan_opts_pg"
00071 #  define OPT_CONTAINER        "opt_vbox"
00072 #  define OPT_ESCROW           "opt_escrow_cb"
00073 #  define OPT_ESCROW_CONTAINER "opt_escrow_hbox"
00074 #define PG_REPAYMENT "repayment_pg"
00075 #  define TXN_NAME       "txn_title"
00076 #  define REPAY_TABLE    "repay_table"
00077 #  define AMOUNT_ENTRY   "amount_ent"
00078 #  define FREQ_CONTAINER "freq_frame"
00079 #define PG_PAYMENT "payment_pg"
00080 #  define PAY_TXN_TITLE      "pay_txn_title"
00081 #  define PAY_AMT_ENTRY      "pay_amt_ent"
00082 #  define PAY_TABLE          "pay_table"
00083 #  define PAY_USE_ESCROW     "pay_use_escrow"
00084 #  define PAY_SPEC_SRC_ACCT  "pay_specify_source"
00085 #  define PAY_FROM_ACCT_LBL  "pay_from_account_label"
00086 #  define PAY_ESC_TO_LBL     "pay_escrow_to_label"
00087 #  define PAY_ESC_FROM_LBL   "pay_escrow_from_label"
00088 #  define PAY_TXN_PART_RB    "pay_txn_part_rb"
00089 #  define PAY_UNIQ_FREQ_RB   "pay_uniq_freq_rb"
00090 #  define PAY_FREQ_CONTAINER "pay_freq_align"
00091 #define PG_REVIEW "review_pg"
00092 #  define REV_SCROLLWIN      "rev_scrollwin"
00093 #  define REV_DATE_FRAME     "rev_date_frame"
00094 #  define REV_RANGE_OPT      "rev_range_opt"
00095 #  define REV_RANGE_TABLE    "rev_date_range_table"
00096 #define PG_COMMIT "commit_pg"
00097 
00098 #define OPT_VBOX_SPACING 2
00099 
00100 enum loan_cols
00101 {
00102     LOAN_COL_DATE = 0,
00103     LOAN_COL_PAYMENT,
00104     LOAN_COL_PRINCIPAL,
00105     LOAN_COL_INTEREST,
00106     NUM_LOAN_COLS
00107 };
00108 
00109 typedef enum
00110 {
00111     CURRENT_YEAR,
00112     NOW_PLUS_ONE,
00113     WHOLE_LOAN,
00114     CUSTOM
00115 } REV_RANGE_OPTS;
00116 
00117 static QofLogModule log_module = GNC_MOD_SX;
00118 
00128 struct LoanDruidData_;
00129 
00134 typedef struct RepayOptData_
00135 {
00136     gboolean enabled;
00137     char *name; /* { "insurance", "pmi", "taxes", ... } */
00138     char *txnMemo;
00139     float amount;
00140     gboolean throughEscrowP;
00141     gboolean specSrcAcctP;
00142     Account *to;
00143     Account *from; /* If NULL { If throughEscrowP, then through escrowAcct };
00144                         * else: undefined. */
00145     GList *schedule;
00146     /* If NULL, part of repayment; otherwise: defined
00147      * here. */
00148     GDate *startDate;
00149 } RepayOptData;
00150 
00154 typedef struct RepayOptDataDefault_
00155 {
00156     char *name;
00157     char *defaultTxnMemo;
00158     gboolean escrowDefault;
00159     gboolean specSrcAcctDefault;
00160 } RepayOptDataDefault;
00161 
00162 static RepayOptDataDefault REPAY_DEFAULTS[] =
00163 {
00164     /* { name, default txn memo, throughEscrowP, specSrcAcctP } */
00165     { N_("Taxes"),         N_("Tax Payment"),           TRUE,  FALSE },
00166     { N_("Insurance"),     N_("Insurance Payment"),     TRUE,  FALSE  },
00167     /* Translators: PMI stands for Private Mortgage Insurance. */
00168     { N_("PMI"),           N_("PMI Payment"),           TRUE,  FALSE  },
00169     { N_("Other Expense"), N_("Miscellaneous Payment"), FALSE, FALSE },
00170     { NULL }
00171 };
00172 
00176 typedef struct RepayOptUI_
00177 {
00178     /* must be stated this way [instead of 'LoanDruidData*'] because of
00179      * forward decl. */
00180     struct LoanDruidData_ *ldd;
00181     GtkCheckButton *optCb;
00182     GtkCheckButton *escrowCb;
00183     RepayOptData *optData;
00184 } RepayOptUIData;
00185 
00186 typedef enum
00187 {
00188     GNC_FIXED = 0,
00189     GNC_VARIABLE,
00190     GNC_VARIABLE_3_1 = GNC_VARIABLE,
00191     GNC_VARIABLE_5_1,
00192     GNC_VARIABLE_7_1,
00193     GNC_VARIABLE_10_1,
00194     /* ... FIXME */
00195 } LoanType;
00196 
00197 typedef enum
00198 {
00199     GNC_MONTHS = 0,
00200     GNC_YEARS
00201 } PeriodSize;
00202 /*type of interest rate entered*/
00203 typedef enum
00204 {
00205     GNC_IRATE_SIMPLE,
00206     GNC_IRATE_APR_DAILY,
00207     GNC_IRATE_APR_WEEKLY,
00208     GNC_IRATE_APR_MONTHLY,
00209     GNC_IRATE_APR_QUARTERLY,
00210     GNC_IRATE_APR_ANNUALLY
00211 } IRateType;
00212 
00218 typedef struct rev_repayment_row
00219 {
00220     GDate date;
00221     gnc_numeric *numCells;
00222 } RevRepaymentRow;
00223 
00227 typedef struct LoanData_
00228 {
00229     Account *primaryAcct;
00230     gnc_numeric principal;
00231     float interestRate;
00232     IRateType rateType;
00233     LoanType type;
00234     GList *loan_schedule;
00235     GDate *startDate;
00236     GDate *varStartDate;
00237     int numPer;
00238     PeriodSize perSize;
00239     int numMonRemain;
00240 
00241     char *repMemo;
00242     char *repAmount;
00243     Account *repFromAcct;
00244     Account *repPriAcct;
00245     Account *repIntAcct;
00246     Account *escrowAcct;
00247     GList *repayment_schedule;
00248     GDate *repStartDate;
00249 
00250     int repayOptCount;
00251     RepayOptData **repayOpts;
00252 
00253     /* Data concerning the review of repayments. */
00254     int revNumPmts;
00255     int revRepayOptToColMap[ (sizeof(REPAY_DEFAULTS)
00256                               / sizeof(RepayOptDataDefault))
00257                              - 1 ];
00258     GList *revSchedule;
00259 } LoanData;
00260 
00264 typedef struct LoanDruidData_
00265 {
00266     GladeXML *gxml;
00267     GtkWidget *dialog;
00268     GnomeDruid *druid;
00269 
00270     LoanData ld;
00271     /* The UI-side storage of repayment data; this is 1:1 with the array
00272      * in LoanData */
00273     RepayOptUIData **repayOptsUI;
00274 
00275     /* Current index of the payment opt for multiplexing the 'payment'
00276      * page. */
00277     int currentIdx;
00278 
00279     /* widgets */
00280     /* prm = params */
00281     GtkTable      *prmTable;
00282     GNCAccountSel *prmAccountGAS;
00283     GNCAmountEdit *prmOrigPrincGAE;
00284     GtkSpinButton *prmIrateSpin;
00285     GtkComboBox   *prmType;
00286     GtkFrame      *prmVarFrame;
00287     GncFrequency  *prmVarGncFreq;
00288     GNCDateEdit   *prmStartDateGDE;
00289     GtkSpinButton *prmLengthSpin;
00290     GtkComboBox   *prmLengthType;
00291     GtkSpinButton *prmRemainSpin;
00292     GtkComboBox   *prmIrateType;
00293 
00294     /* opt = options */
00295     GtkVBox        *optVBox;
00296     GtkCheckButton *optEscrowCb;
00297     GtkHBox        *optEscrowHBox;
00298     GNCAccountSel  *optEscrowGAS;
00299 
00300     /* rep = repayment */
00301     GtkEntry      *repTxnName;
00302     GtkTable      *repTable;
00303     GtkEntry      *repAmtEntry;
00304     GNCAccountSel *repAssetsFromGAS;
00305     GNCAccountSel *repPrincToGAS;
00306     GNCAccountSel *repIntToGAS;
00307     GtkFrame      *repFreqFrame;
00308     GncFrequency  *repGncFreq;
00309 
00310     /* pay = payment[s] */
00311     GtkEntry         *payTxnName;
00312     GtkEntry         *payAmtEntry;
00313     GNCAccountSel    *payAcctFromGAS;
00314     GNCAccountSel    *payAcctEscToGAS;
00315     GNCAccountSel    *payAcctEscFromGAS;
00316     GNCAccountSel    *payAcctToGAS;
00317     GtkTable         *payTable;
00318     GtkCheckButton   *payUseEscrow;
00319     GtkCheckButton   *paySpecSrcAcct;
00320     GtkLabel         *payAcctFromLabel;
00321     GtkLabel         *payEscToLabel;
00322     GtkLabel         *payEscFromLabel;
00323     GtkRadioButton   *payTxnFreqPartRb;
00324     GtkRadioButton   *payTxnFreqUniqRb;
00325     GtkAlignment     *payFreqAlign;
00326     GncFrequency     *payGncFreq;
00327 
00328     /* rev = review */
00329     GtkComboBox       *revRangeOpt;
00330     GtkFrame          *revDateFrame;
00331     GtkTable          *revTable;
00332     GNCDateEdit       *revStartDate;
00333     GNCDateEdit       *revEndDate;
00334     GtkScrolledWindow *revScrollWin;
00335     GtkTreeView       *revView;
00336 } LoanDruidData;
00337 
00341 typedef struct toCreateSX_
00342 {
00344     gchar *name;
00346     GDate start, last, end;
00348     GList *schedule;
00350     gint instNum;
00352     TTInfo *mainTxn;
00354     TTInfo *escrowTxn;
00355 } toCreateSX;
00356 
00357 static void gnc_loan_druid_data_init( LoanDruidData *ldd );
00358 static void gnc_loan_druid_get_widgets( LoanDruidData *ldd );
00359 
00360 static void ld_close_handler( LoanDruidData *ldd );
00361 static void ld_destroy( GtkObject *o, gpointer ud );
00362 
00363 static void ld_cancel_check( GnomeDruid *gd, LoanDruidData *ldd );
00364 static void ld_prm_type_changed( GtkWidget *w, gpointer ud );
00365 static void ld_calc_upd_rem_payments( GtkWidget *w, gpointer ud );
00366 
00367 static void ld_escrow_toggle( GtkToggleButton *tb, gpointer ud );
00368 static void ld_opt_toggled( GtkToggleButton *tb, gpointer ud );
00369 static void ld_opt_consistency( GtkToggleButton *tb, gpointer ud );
00370 static void ld_escrow_toggled( GtkToggleButton *tb, gpointer ud );
00371 
00372 static void ld_pay_freq_toggle( GtkToggleButton *tb, gpointer ud );
00373 
00374 static void ld_pay_use_esc_setup( LoanDruidData *ldd, gboolean newState );
00375 static void ld_pay_use_esc_toggle( GtkToggleButton *tb, gpointer ud );
00376 static void ld_pay_spec_src_setup( LoanDruidData *ldd, gboolean newState );
00377 static void ld_pay_spec_src_toggle( GtkToggleButton *tb, gpointer ud );
00378 
00379 static void ld_get_loan_range( LoanDruidData *ldd,
00380                                GDate *start,
00381                                GDate *end );
00382 
00383 static void ld_rev_recalc_schedule( LoanDruidData *ldd );
00384 static void ld_rev_range_opt_changed( GtkComboBox *combo, gpointer ud );
00385 static void ld_rev_range_changed( GNCDateEdit *gde, gpointer ud );
00386 static void ld_rev_get_dates( LoanDruidData *ldd,
00387                               GDate *start,
00388                               GDate *end );
00389 static void ld_rev_update_view( LoanDruidData *ldd,
00390                                 GDate *start,
00391                                 GDate *end );
00392 static void ld_rev_sched_list_free( gpointer data, gpointer user_data );
00393 static void ld_rev_hash_to_list( gpointer key,
00394                                  gpointer val,
00395                                  gpointer user_data );
00396 static void ld_rev_hash_free_date_keys( gpointer key,
00397                                         gpointer val,
00398                                         gpointer user_data );
00399 
00400 static void ld_get_pmt_formula( LoanDruidData *ldd, GString *gstr );
00401 static void ld_get_ppmt_formula( LoanDruidData *ldd, GString *gstr );
00402 static void ld_get_ipmt_formula( LoanDruidData *ldd, GString *gstr );
00403 
00404 static gboolean ld_info_save( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00405 static void     ld_info_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00406 static gboolean ld_opts_tran( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00407 static void     ld_opts_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00408 static gboolean ld_rep_next ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00409 static void     ld_rep_prep ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00410 static gboolean ld_rep_back ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00411 static gboolean ld_pay_next ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00412 static void     ld_pay_prep ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00413 static gboolean ld_pay_back ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00414 static gboolean ld_rev_next ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00415 static void     ld_rev_prep ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00416 static gboolean ld_rev_back ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00417 static gboolean ld_com_next ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00418 static void     ld_com_prep ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00419 static gboolean ld_com_back ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00420 static void     ld_com_fin  ( GnomeDruidPage *gdp, gpointer arg1, gpointer ud );
00421 
00422 static void ld_create_sxes( LoanDruidData *ldd );
00423 static gint ld_find_ttsplit_with_acct( gconstpointer elt,
00424                                        gconstpointer crit );
00425 static void ld_create_sx_from_tcSX( LoanDruidData *ldd, toCreateSX *tcSX );
00426 static void ld_tcSX_free( gpointer data, gpointer user_data );
00427 static float ld_apr_to_simple_formula (float rate, float compounding_periods);
00428 
00429 struct LoanDruidData_*
00430 gnc_ui_sx_loan_druid_create(void)
00431 {
00432     int i;
00433     LoanDruidData *ldd;
00434 
00435     ldd = g_new0( LoanDruidData, 1 );
00436 
00437     gnc_loan_druid_data_init( ldd );
00438 
00439     ldd->gxml   = gnc_glade_xml_new( SX_GLADE_FILE, LOAN_DRUID_WIN_GLADE_NAME );
00440     ldd->dialog = glade_xml_get_widget( ldd->gxml, LOAN_DRUID_WIN_GLADE_NAME );
00441     ldd->druid  = GNOME_DRUID(glade_xml_get_widget( ldd->gxml,
00442                               LOAN_DRUID_GLADE_NAME ));
00443     gnc_druid_set_colors (ldd->druid);
00444 
00445     /* get pointers to the various widgets */
00446     gnc_loan_druid_get_widgets( ldd );
00447 
00448     /* non-gladeable widget setup */
00449     {
00450         int i;
00451         // ACCT_TYPE_LIABILITY
00452         GList *liabilityAcct;
00453         // ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CREDIT,
00454         // ACCT_TYPE_ASSET + ACCT_TYPE_LIABILITY
00455         GList *paymentFromAccts;
00456         // ACCT_TYPE_EXPENSE, ACCT_TYPE_LIABILITY, + payment-froms.
00457         GList *paymentToAccts;
00458         int fromLen = 5;
00459         GNCAccountType paymentFroms[] = { ACCT_TYPE_BANK, ACCT_TYPE_CASH,
00460                                           ACCT_TYPE_CREDIT, ACCT_TYPE_ASSET,
00461                                           ACCT_TYPE_LIABILITY
00462                                         };
00463         int toLen = 1;
00464         GNCAccountType paymentTos[] = { ACCT_TYPE_EXPENSE };
00465 
00466         liabilityAcct = NULL;
00467         paymentFromAccts = NULL;
00468         paymentToAccts = NULL;
00469 
00470         liabilityAcct = g_list_append( liabilityAcct,
00471                                        GINT_TO_POINTER( ACCT_TYPE_LIABILITY ) );
00472         for ( i = 0; i < fromLen; i++ )
00473         {
00474             paymentFromAccts
00475             = g_list_append( paymentFromAccts,
00476                              GINT_TO_POINTER( paymentFroms[i] ) );
00477             paymentToAccts
00478             = g_list_append( paymentToAccts,
00479                              GINT_TO_POINTER( paymentFroms[i] ) );
00480         }
00481 
00482         for ( i = 0; i < toLen; i++ )
00483         {
00484             paymentToAccts = g_list_append( paymentToAccts,
00485                                             GINT_TO_POINTER( paymentTos[i] ) );
00486         }
00487 
00488         /* All of the GncAccountSel[ectors]... */
00489         {
00490             int i;
00491             GtkAlignment *a;
00492             /* "gas" == GncAccountSel */
00493             struct gas_in_tables_data
00494             {
00495                 GNCAccountSel **loc;
00496                 GtkTable *table;
00497                 gboolean newAcctAbility;
00498                 int left, right, top, bottom;
00499                 GList *allowableAccounts;
00500             } gas_data[] =
00501             {
00502                 /* These ints are the GtkTable boundries */
00503                 { &ldd->prmAccountGAS,     ldd->prmTable, TRUE,  1, 4, 0, 1, liabilityAcct },
00504                 { &ldd->repAssetsFromGAS,  ldd->repTable, TRUE,  1, 4, 2, 3, paymentFromAccts },
00505                 { &ldd->repPrincToGAS,     ldd->repTable, TRUE,  1, 2, 3, 4, paymentToAccts  },
00506                 { &ldd->repIntToGAS,       ldd->repTable, TRUE,  3, 4, 3, 4, paymentToAccts },
00507                 { &ldd->payAcctFromGAS,    ldd->payTable, TRUE,  1, 2, 4, 5, paymentFromAccts },
00508                 { &ldd->payAcctEscToGAS,   ldd->payTable, FALSE, 3, 4, 4, 5, paymentToAccts },
00509                 { &ldd->payAcctEscFromGAS, ldd->payTable, FALSE, 1, 2, 5, 6, paymentFromAccts },
00510                 { &ldd->payAcctToGAS,      ldd->payTable, TRUE,  3, 4, 5, 6, paymentToAccts },
00511                 { NULL }
00512             };
00513 
00514             /* left-aligned, 25%-width */
00515             a = GTK_ALIGNMENT(gtk_alignment_new( 0.0, 0.5, 0.25, 1.0 ));
00516             ldd->prmOrigPrincGAE = GNC_AMOUNT_EDIT(gnc_amount_edit_new());
00517             gtk_container_add( GTK_CONTAINER(a), GTK_WIDGET(ldd->prmOrigPrincGAE) );
00518             gtk_table_attach( ldd->prmTable, GTK_WIDGET(a),
00519                               1, 4, 1, 2,
00520                               GTK_EXPAND | GTK_FILL,
00521                               GTK_EXPAND | GTK_FILL, 2, 2 );
00522 
00523             for ( i = 0; gas_data[i].loc != NULL; i++ )
00524             {
00525                 GNCAccountSel *gas;
00526 
00527                 a = GTK_ALIGNMENT(gtk_alignment_new( 0.0, 0.5, 0.25, 1.0 ));
00528                 gas = GNC_ACCOUNT_SEL(gnc_account_sel_new());
00529                 gnc_account_sel_set_new_account_ability(
00530                     gas, gas_data[i].newAcctAbility );
00531                 if ( gas_data[i].allowableAccounts != NULL )
00532                 {
00533                     gnc_account_sel_set_acct_filters(
00534                         gas, gas_data[i].allowableAccounts, NULL );
00535                 }
00536                 gtk_container_add( GTK_CONTAINER(a),
00537                                    GTK_WIDGET(gas) );
00538                 gtk_table_attach( gas_data[i].table,
00539                                   GTK_WIDGET(a),
00540                                   gas_data[i].left,
00541                                   gas_data[i].right,
00542                                   gas_data[i].top,
00543                                   gas_data[i].bottom,
00544                                   GTK_EXPAND | GTK_FILL,
00545                                   GTK_EXPAND | GTK_FILL, 2, 2 );
00546                 *(gas_data[i].loc) = gas;
00547             }
00548         }
00549 
00550         /* Setup the payment page always-insensitive GASes */
00551         gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctEscToGAS),   FALSE );
00552         gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctEscFromGAS), FALSE );
00553 
00554         /* The GNCDateEdit[s] */
00555         {
00556             /* "gde" == GNCDateEdit */
00557             struct gde_in_tables_data
00558             {
00559                 GNCDateEdit **loc;
00560                 GtkTable *table;
00561                 int left, right, top, bottom;
00562             } gde_data[] =
00563             {
00564                 /* These ints are the GtkTable boundries */
00565                 { &ldd->prmStartDateGDE, ldd->prmTable, 1, 2, 4, 5 },
00566                 { &ldd->revStartDate,    ldd->revTable, 1, 2, 0, 1 },
00567                 { &ldd->revEndDate,      ldd->revTable, 1, 2, 1, 2 },
00568                 { NULL }
00569             };
00570 
00571             for ( i = 0; gde_data[i].loc != NULL; i++ )
00572             {
00573                 *gde_data[i].loc =
00574                     GNC_DATE_EDIT(
00575                         gnc_date_edit_new( time(NULL),
00576                                            FALSE, FALSE ) );
00577                 gtk_table_attach( gde_data[i].table,
00578                                   GTK_WIDGET( *gde_data[i].loc ),
00579                                   gde_data[i].left,
00580                                   gde_data[i].right,
00581                                   gde_data[i].top,
00582                                   gde_data[i].bottom,
00583                                   (GTK_EXPAND | GTK_FILL),
00584                                   GTK_FILL, 0, 0 );
00585             }
00586 
00587         }
00588 
00589         gtk_widget_set_sensitive( GTK_WIDGET(ldd->prmVarFrame), FALSE );
00590         {
00591             g_signal_connect( ldd->prmType, "changed",
00592                               G_CALLBACK( ld_prm_type_changed ),
00593                               ldd );
00594         }
00595 
00596         {
00597             GtkAdjustment *a;
00598 
00599             /* 8.0 [%], range of 0.005..100.0 with ticks at 0.001[%]. */
00600             a = GTK_ADJUSTMENT(gtk_adjustment_new( 8.0, 0.001,
00601                                                    100.0, 0.001,
00602                                                    1.0, 1.0 ));
00603             gtk_spin_button_set_adjustment( ldd->prmIrateSpin, a );
00604             gtk_spin_button_set_value( ldd->prmIrateSpin, 8.00 );
00605             gtk_spin_button_set_snap_to_ticks( ldd->prmIrateSpin,
00606                                                TRUE );
00607 
00608             a = GTK_ADJUSTMENT(gtk_adjustment_new( 360, 1,
00609                                                    9999, 1,
00610                                                    12, 12 ));
00611             gtk_spin_button_set_adjustment( ldd->prmLengthSpin, a );
00612             g_signal_connect( ldd->prmLengthSpin, "changed",
00613                               G_CALLBACK( ld_calc_upd_rem_payments ),
00614                               ldd );
00615             g_signal_connect( ldd->prmStartDateGDE, "date-changed",
00616                               G_CALLBACK( ld_calc_upd_rem_payments ),
00617                               ldd );
00618             g_signal_connect( ldd->prmLengthSpin, "changed",
00619                               G_CALLBACK( ld_calc_upd_rem_payments ),
00620                               ldd );
00621             g_signal_connect( ldd->prmLengthType, "changed",
00622                               G_CALLBACK( ld_calc_upd_rem_payments ),
00623                               ldd );
00624 
00625             a = GTK_ADJUSTMENT(gtk_adjustment_new( 360, 1,
00626                                                    9999, 1,
00627                                                    12, 12 ));
00628             gtk_spin_button_set_adjustment( ldd->prmRemainSpin, a );
00629         }
00630 
00631         g_signal_connect( ldd->optEscrowCb, "toggled",
00632                           G_CALLBACK(ld_escrow_toggle), ldd );
00633         gtk_widget_set_sensitive( GTK_WIDGET(ldd->optEscrowHBox), FALSE );
00634         ldd->optEscrowGAS = GNC_ACCOUNT_SEL(gnc_account_sel_new());
00635         gnc_account_sel_set_new_account_ability( ldd->optEscrowGAS, TRUE );
00636         gtk_container_add( GTK_CONTAINER(ldd->optEscrowHBox),
00637                            GTK_WIDGET(ldd->optEscrowGAS) );
00638 
00639         {
00640             /* . Each RepayOpt gets an "entry" in the optContainer.
00641              * . Each "entry" is a 2-line vbox containing:
00642              *   . The checkbox for the option itself
00643              *   . an alignment-contained sub-checkbox for "through the
00644              *     escrow account".
00645              *   . Hook up each to bit-twiddling the appropriate line.
00646              */
00647 
00648             RepayOptUIData *rouid;
00649             GtkVBox *vb;
00650             GtkAlignment *optAlign, *subOptAlign;
00651             GString *str;
00652 
00653             str = g_string_sized_new( 32 );
00654 
00655             for ( i = 0; i < ldd->ld.repayOptCount; i++ )
00656             {
00657                 rouid = ldd->repayOptsUI[i];
00658                 vb = GTK_VBOX(gtk_vbox_new( FALSE, OPT_VBOX_SPACING ));
00659 
00660                 /* Add payment checkbox. */
00661 
00662                 /* Translators: %s is "Taxes",
00663                  * "Insurance", or similar. */
00664                 g_string_printf( str, _("... pay \"%s\"?"),
00665                                  rouid->optData->name );
00666                 rouid->optCb =
00667                     GTK_CHECK_BUTTON(
00668                         gtk_check_button_new_with_label(
00669                             str->str ));
00670                 gtk_box_pack_start( GTK_BOX(vb),
00671                                     GTK_WIDGET(rouid->optCb),
00672                                     FALSE, FALSE, 2 );
00673                 rouid->escrowCb =
00674                     GTK_CHECK_BUTTON(
00675                         gtk_check_button_new_with_label(
00676                             _("via Escrow account?") ));
00677                 gtk_widget_set_sensitive(
00678                     GTK_WIDGET(rouid->escrowCb),
00679                     FALSE );
00680                 subOptAlign =
00681                     GTK_ALIGNMENT(
00682                         gtk_alignment_new(
00683                             0.5, 0.5, 0.75, 1.0 ));
00684                 gtk_container_add( GTK_CONTAINER(subOptAlign),
00685                                    GTK_WIDGET(rouid->escrowCb) );
00686                 gtk_box_pack_start( GTK_BOX(vb), GTK_WIDGET(subOptAlign),
00687                                     FALSE, FALSE, 2 );
00688 
00689                 g_signal_connect( rouid->optCb, "toggled",
00690                                   G_CALLBACK(ld_opt_toggled),
00691                                   rouid );
00692                 g_signal_connect( rouid->optCb, "toggled",
00693                                   G_CALLBACK(ld_opt_consistency),
00694                                   rouid );
00695                 g_signal_connect( rouid->escrowCb, "toggled",
00696                                   G_CALLBACK(ld_escrow_toggled),
00697                                   rouid );
00698 
00699                 optAlign = GTK_ALIGNMENT(gtk_alignment_new( 0.5, 0.5, 0.75, 1.0 ));
00700                 gtk_container_add( GTK_CONTAINER(optAlign),
00701                                    GTK_WIDGET(vb) );
00702                 gtk_box_pack_start( GTK_BOX(ldd->optVBox), GTK_WIDGET(optAlign),
00703                                     FALSE, FALSE, 2 );
00704                 gtk_widget_show_all( GTK_WIDGET(optAlign) );
00705             }
00706             g_string_free( str, TRUE );
00707         }
00708 
00709         g_signal_connect( ldd->payUseEscrow, "toggled",
00710                           G_CALLBACK(ld_pay_use_esc_toggle), ldd );
00711         g_signal_connect( ldd->paySpecSrcAcct, "toggled",
00712                           G_CALLBACK(ld_pay_spec_src_toggle), ldd );
00713         g_signal_connect( ldd->payTxnFreqPartRb, "toggled",
00714                           G_CALLBACK(ld_pay_freq_toggle), ldd );
00715         g_signal_connect( ldd->payTxnFreqUniqRb, "toggled",
00716                           G_CALLBACK(ld_pay_freq_toggle), ldd );
00717 
00718         ldd->prmVarGncFreq =
00719             GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
00720         gtk_container_add( GTK_CONTAINER(ldd->prmVarFrame),
00721                            GTK_WIDGET(ldd->prmVarGncFreq) );
00722 
00723         ldd->repGncFreq =
00724             GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
00725         gtk_container_add( GTK_CONTAINER(ldd->repFreqFrame),
00726                            GTK_WIDGET(ldd->repGncFreq) );
00727 
00728         ldd->payGncFreq =
00729             GNC_FREQUENCY(gnc_frequency_new( NULL, NULL ));
00730         gtk_container_add( GTK_CONTAINER(ldd->payFreqAlign),
00731                            GTK_WIDGET(ldd->payGncFreq) );
00732     }
00733 
00734     /* Review page widget setup. */
00735     {
00736         gtk_combo_box_set_active( ldd->revRangeOpt, 0 );
00737         g_signal_connect( ldd->revRangeOpt, "changed",
00738                           G_CALLBACK( ld_rev_range_opt_changed ),
00739                           ldd );
00740         g_signal_connect( ldd->revStartDate, "date-changed",
00741                           G_CALLBACK( ld_rev_range_changed ),
00742                           ldd );
00743         g_signal_connect( ldd->revEndDate, "date-changed",
00744                           G_CALLBACK( ld_rev_range_changed ),
00745                           ldd );
00746     }
00747 
00748     {
00749         static struct
00750         {
00751             char *pageName;
00752             gboolean (*nextFn)();
00753             void     (*prepFn)();
00754             gboolean (*backFn)();
00755             void     (*finishFn)();
00756             /* cancel is handled by the druid itself. */
00757         } DRUID_HANDLERS[] =
00758         {
00759             { PG_INFO,      ld_info_save, ld_info_prep, ld_info_save, NULL },
00760             { PG_OPTS,      ld_opts_tran, ld_opts_prep, ld_opts_tran, NULL },
00761             { PG_REPAYMENT, ld_rep_next,  ld_rep_prep,  ld_rep_back,  NULL },
00762             { PG_PAYMENT,   ld_pay_next,  ld_pay_prep,  ld_pay_back,  NULL },
00763             { PG_REVIEW,    ld_rev_next,  ld_rev_prep,  ld_rev_back,  NULL },
00764             { PG_COMMIT,    ld_com_next,  ld_com_prep,  ld_com_back,  ld_com_fin },
00765             { NULL }
00766         };
00767 
00768         /* setup page-transition handlers */
00769         /* setup druid-global handler for cancel */
00770         g_signal_connect( ldd->druid, "cancel",
00771                           G_CALLBACK(ld_cancel_check), ldd );
00772         /* FIXME: this is substantially similar to the code in
00773          * dialog-sxsincelast.c ... it should probably be factored out
00774          * somewhere. */
00775         for ( i = 0; DRUID_HANDLERS[i].pageName != NULL; i++ )
00776         {
00777             GtkWidget *pg;
00778 
00779             pg = glade_xml_get_widget( ldd->gxml,
00780                                        DRUID_HANDLERS[i].pageName );
00781             if ( DRUID_HANDLERS[i].prepFn )
00782             {
00783                 g_signal_connect( pg, "prepare",
00784                                   G_CALLBACK(DRUID_HANDLERS[i].
00785                                              prepFn),
00786                                   ldd);
00787             }
00788             if ( DRUID_HANDLERS[i].backFn )
00789             {
00790                 g_signal_connect( pg, "back",
00791                                   G_CALLBACK(DRUID_HANDLERS[i].
00792                                              backFn),
00793                                   ldd);
00794             }
00795             if ( DRUID_HANDLERS[i].nextFn )
00796             {
00797                 g_signal_connect( pg, "next",
00798                                   G_CALLBACK(DRUID_HANDLERS[i].
00799                                              nextFn),
00800                                   ldd);
00801             }
00802             if ( DRUID_HANDLERS[i].finishFn )
00803             {
00804                 g_signal_connect( pg, "finish",
00805                                   G_CALLBACK(DRUID_HANDLERS[i].
00806                                              finishFn),
00807                                   ldd);
00808             }
00809         }
00810     }
00811 
00812     gnc_register_gui_component( DIALOG_LOAN_DRUID_CM_CLASS,
00813                                 NULL, /* no refresh handler */
00814                                 (GNCComponentCloseHandler)ld_close_handler,
00815                                 ldd );
00816 
00817     g_signal_connect( ldd->dialog, "destroy",
00818                       G_CALLBACK(ld_destroy),
00819                       ldd );
00820 
00821     gtk_widget_show_all( ldd->dialog );
00822     return ldd;
00823 }
00824 
00825 static
00826 void
00827 gnc_loan_druid_data_init( LoanDruidData *ldd )
00828 {
00829     int i, optCount;
00830     RepayOptData *optData;
00831 
00832     /* get the count of repayment defaults. */
00833     for ( optCount = 0; REPAY_DEFAULTS[optCount].name != NULL; optCount++ )
00834         ;
00835 
00836     ldd->currentIdx = -1;
00837 
00838     ldd->ld.principal = gnc_numeric_zero();
00839     ldd->ld.startDate = g_date_new();
00840     ldd->ld.varStartDate = g_date_new();
00841     g_date_set_time_t( ldd->ld.startDate, time(NULL) );
00842     ldd->ld.loan_schedule = NULL;
00843     ldd->ld.repayment_schedule = NULL;
00844     {
00845         Recurrence *r = g_new0(Recurrence, 1);
00846         recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
00847         ldd->ld.repayment_schedule = g_list_append(ldd->ld.repayment_schedule, r);
00848     }
00849 
00850     ldd->ld.repMemo = g_strdup( _("Loan") );
00851     ldd->ld.repAmount = NULL;
00852     ldd->ld.repStartDate = g_date_new();
00853     ldd->ld.repayOptCount = optCount;
00854     ldd->ld.repayOpts = g_new0( RepayOptData*, optCount );
00855     /* copy all the default lines into the LDD */
00856     ldd->repayOptsUI = g_new0( RepayOptUIData*, optCount );
00857     for ( i = 0; i < optCount; i++ )
00858     {
00859         g_assert( REPAY_DEFAULTS[i].name != NULL );
00860 
00861         ldd->repayOptsUI[i] = g_new0( RepayOptUIData, 1 );
00862         ldd->repayOptsUI[i]->ldd = ldd;
00863 
00864         optData = ldd->ld.repayOpts[i]
00865                   = ldd->repayOptsUI[i]->optData
00866                     = g_new0( RepayOptData, 1 );
00867 
00868         optData->enabled        = FALSE;
00869         optData->name           = g_strdup( _(REPAY_DEFAULTS[i].name) );
00870         optData->txnMemo        = g_strdup( _(REPAY_DEFAULTS[i].
00871                                               defaultTxnMemo) );
00872         optData->amount         = 0.0;
00873         optData->throughEscrowP = REPAY_DEFAULTS[i].escrowDefault;
00874         optData->specSrcAcctP   = REPAY_DEFAULTS[i].specSrcAcctDefault;
00875         optData->schedule       = NULL;
00876         optData->startDate      = NULL;
00877     }
00878 }
00879 
00880 static
00881 void
00882 gnc_loan_druid_get_widgets( LoanDruidData *ldd )
00883 {
00884     g_assert( ldd != NULL );
00885     g_assert( ldd->gxml != NULL );
00886 
00887     /* Get all widgets */
00888 #define GET_CASTED_WIDGET( cast, name ) \
00889         (cast( glade_xml_get_widget( ldd->gxml, name ) ))
00890 
00891     /* prm = params */
00892     ldd->prmTable =
00893         GET_CASTED_WIDGET( GTK_TABLE,          PARAM_TABLE );
00894     ldd->prmIrateSpin =
00895         GET_CASTED_WIDGET( GTK_SPIN_BUTTON,    IRATE_SPIN );
00896     ldd->prmType =
00897         GET_CASTED_WIDGET( GTK_COMBO_BOX,      TYPE_COMBOBOX );
00898     ldd->prmVarFrame =
00899         GET_CASTED_WIDGET( GTK_FRAME,          VAR_CONTAINER );
00900     /* ldd->prmStartDateGDE */
00901     ldd->prmLengthSpin =
00902         GET_CASTED_WIDGET( GTK_SPIN_BUTTON,    LENGTH_SPIN );
00903     ldd->prmLengthType =
00904         GET_CASTED_WIDGET( GTK_COMBO_BOX,      LENGTH_OPT );
00905     ldd->prmRemainSpin =
00906         GET_CASTED_WIDGET( GTK_SPIN_BUTTON,    REMAIN_SPIN );
00907     ldd->prmIrateType =
00908         GET_CASTED_WIDGET( GTK_COMBO_BOX,      IRATE_TYPE_COMBOBOX );
00909 
00910     /* opt = options */
00911     ldd->optVBox =
00912         GET_CASTED_WIDGET( GTK_VBOX,           OPT_CONTAINER );
00913     ldd->optEscrowCb =
00914         GET_CASTED_WIDGET( GTK_CHECK_BUTTON,   OPT_ESCROW );
00915     ldd->optEscrowHBox =
00916         GET_CASTED_WIDGET( GTK_HBOX,           OPT_ESCROW_CONTAINER );
00917 
00918     /* rep = repayment */
00919     ldd->repTxnName =
00920         GET_CASTED_WIDGET( GTK_ENTRY,          TXN_NAME );
00921     ldd->repTable =
00922         GET_CASTED_WIDGET( GTK_TABLE,          REPAY_TABLE );
00923     ldd->repAmtEntry =
00924         GET_CASTED_WIDGET( GTK_ENTRY,          AMOUNT_ENTRY );
00925     ldd->repFreqFrame =
00926         GET_CASTED_WIDGET( GTK_FRAME,          FREQ_CONTAINER );
00927 
00928     /* pay = payment[s] */
00929     ldd->payTxnName =
00930         GET_CASTED_WIDGET( GTK_ENTRY,          PAY_TXN_TITLE );
00931     ldd->payAmtEntry =
00932         GET_CASTED_WIDGET( GTK_ENTRY,          PAY_AMT_ENTRY );
00933     ldd->payTable =
00934         GET_CASTED_WIDGET( GTK_TABLE,          PAY_TABLE );
00935     ldd->payUseEscrow =
00936         GET_CASTED_WIDGET( GTK_CHECK_BUTTON,   PAY_USE_ESCROW );
00937     ldd->paySpecSrcAcct =
00938         GET_CASTED_WIDGET( GTK_CHECK_BUTTON,   PAY_SPEC_SRC_ACCT );
00939     ldd->payAcctFromLabel =
00940         GET_CASTED_WIDGET( GTK_LABEL,          PAY_FROM_ACCT_LBL );
00941     ldd->payEscToLabel =
00942         GET_CASTED_WIDGET( GTK_LABEL,          PAY_ESC_TO_LBL );
00943     ldd->payEscFromLabel =
00944         GET_CASTED_WIDGET( GTK_LABEL,          PAY_ESC_FROM_LBL );
00945     ldd->payTxnFreqPartRb =
00946         GET_CASTED_WIDGET( GTK_RADIO_BUTTON,   PAY_TXN_PART_RB );
00947     ldd->payTxnFreqUniqRb =
00948         GET_CASTED_WIDGET( GTK_RADIO_BUTTON,   PAY_UNIQ_FREQ_RB );
00949     ldd->payFreqAlign =
00950         GET_CASTED_WIDGET( GTK_ALIGNMENT,      PAY_FREQ_CONTAINER );
00951 
00952     /* rev = review */
00953     ldd->revRangeOpt =
00954         GET_CASTED_WIDGET( GTK_COMBO_BOX,      REV_RANGE_OPT );
00955     ldd->revDateFrame =
00956         GET_CASTED_WIDGET( GTK_FRAME,          REV_DATE_FRAME );
00957     ldd->revTable =
00958         GET_CASTED_WIDGET( GTK_TABLE,          REV_RANGE_TABLE );
00959     /* GNCDateEdit       *revStartDate */
00960     /* GNCDateEdit       *revEndDate   */
00961     ldd->revScrollWin =
00962         GET_CASTED_WIDGET( GTK_SCROLLED_WINDOW, REV_SCROLLWIN );
00963 
00964 }
00965 
00966 /* convert APR rate to simple rate based on formula r=q((1+i)^(1/q)-1) (r=interest rate, i=apr, q=compounding periods) */
00967 
00968 gfloat ld_apr_to_simple_formula (gfloat rate, gfloat compounding_periods)
00969 {
00970     /* float percent_to_frac; - redundant */
00971     gfloat simple_rate;
00972     /* percent_to_frac= compounding_periods/100; - redundant */
00973     simple_rate = compounding_periods * ((pow((1 + rate), (1 / compounding_periods))) - 1);
00974     return (simple_rate);
00975 }
00976 
00977 
00978 static
00979 void
00980 ld_get_pmt_formula( LoanDruidData *ldd, GString *gstr )
00981 {
00982     gint rate_case;
00983     gfloat pass_thru_rate;
00984     g_assert( ldd != NULL );
00985     g_assert( gstr != NULL );
00986     rate_case = ldd->ld.rateType;
00987     pass_thru_rate = ldd->ld.interestRate / 100;
00988     switch (rate_case)
00989     {
00990     case GNC_IRATE_SIMPLE:
00991     {
00992         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
00993                                 pass_thru_rate, 12.0,
00994                                 ( ldd->ld.numPer
00995                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
00996                                 gnc_numeric_to_double(ldd->ld.principal) );
00997     }
00998     break;
00999     case GNC_IRATE_APR_DAILY:
01000     {
01001         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01002                                 ld_apr_to_simple_formula(pass_thru_rate, 365),
01003                                 12.0,
01004                                 ( ldd->ld.numPer
01005                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01006                                 gnc_numeric_to_double(ldd->ld.principal) );
01007     }
01008     break;
01009     case GNC_IRATE_APR_WEEKLY:
01010     {
01011         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01012                                 ld_apr_to_simple_formula(pass_thru_rate, 52),
01013                                 12.0,
01014                                 ( ldd->ld.numPer
01015                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01016                                 gnc_numeric_to_double(ldd->ld.principal) );
01017     }
01018     break;
01019     case GNC_IRATE_APR_MONTHLY:
01020     {
01021         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01022                                 ld_apr_to_simple_formula(pass_thru_rate, 12),
01023                                 12.0,
01024                                 ( ldd->ld.numPer
01025                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01026                                 gnc_numeric_to_double(ldd->ld.principal) );
01027     }
01028     break;
01029     case GNC_IRATE_APR_QUARTERLY:
01030     {
01031         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01032                                 ld_apr_to_simple_formula(pass_thru_rate, 4),
01033                                 12.0,
01034                                 ( ldd->ld.numPer
01035                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01036                                 gnc_numeric_to_double(ldd->ld.principal) );
01037     }
01038     break;
01039     case GNC_IRATE_APR_ANNUALLY:
01040     {
01041         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01042                                 ld_apr_to_simple_formula(pass_thru_rate, 1),
01043                                 12.0,
01044                                 ( ldd->ld.numPer
01045                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01046                                 gnc_numeric_to_double(ldd->ld.principal) );
01047     }
01048     break;
01049     default:
01050         g_string_append_printf( gstr, "pmt( %.5f / %0.2f : %0.2f : %0.2f : 0 : 0 )",
01051                                 (ldd->ld.interestRate / 100),
01052                                 12.0,
01053                                 ( ldd->ld.numPer
01054                                   * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01055                                 gnc_numeric_to_double(ldd->ld.principal) );
01056         break;
01057     }
01058 }
01059 
01060 static
01061 void
01062 ld_get_ppmt_formula( LoanDruidData *ldd, GString *gstr )
01063 {
01064     gint rate_case;
01065     gfloat pass_thru_rate;
01066     g_assert( ldd != NULL );
01067     g_assert( gstr != NULL );
01068     rate_case = ldd->ld.rateType;
01069     pass_thru_rate = ldd->ld.interestRate / 100;
01070     switch (rate_case)
01071     {
01072     case GNC_IRATE_SIMPLE:
01073     {
01074         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01075                          pass_thru_rate,
01076                          12.0,
01077                          ( ldd->ld.numPer
01078                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01079                          gnc_numeric_to_double(ldd->ld.principal));
01080     }
01081     break;
01082     case GNC_IRATE_APR_DAILY:
01083     {
01084         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01085                          ld_apr_to_simple_formula(pass_thru_rate, 365),
01086                          12.0,
01087                          ( ldd->ld.numPer
01088                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01089                          gnc_numeric_to_double(ldd->ld.principal));
01090     }
01091     break;
01092     case GNC_IRATE_APR_WEEKLY:
01093     {
01094         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01095                          ld_apr_to_simple_formula(pass_thru_rate, 52),
01096                          12.0,
01097                          ( ldd->ld.numPer
01098                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01099                          gnc_numeric_to_double(ldd->ld.principal));
01100     }
01101     break;
01102     case GNC_IRATE_APR_MONTHLY:
01103     {
01104         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01105                          ld_apr_to_simple_formula(pass_thru_rate, 12),
01106                          12.0,
01107                          ( ldd->ld.numPer
01108                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01109                          gnc_numeric_to_double(ldd->ld.principal));
01110     }
01111     break;
01112     case GNC_IRATE_APR_QUARTERLY:
01113     {
01114         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01115                          ld_apr_to_simple_formula(pass_thru_rate, 4),
01116                          12.0,
01117                          ( ldd->ld.numPer
01118                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01119                          gnc_numeric_to_double(ldd->ld.principal));
01120     }
01121     break;
01122     case GNC_IRATE_APR_ANNUALLY:
01123     {
01124         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01125                          ld_apr_to_simple_formula(pass_thru_rate, 1),
01126                          12.0,
01127                          ( ldd->ld.numPer
01128                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01129                          gnc_numeric_to_double(ldd->ld.principal));
01130     }
01131     break;
01132     default:
01133         g_string_printf( gstr, "ppmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01134                          (ldd->ld.interestRate / 100),
01135                          12.0,
01136                          ( ldd->ld.numPer
01137                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01138                          gnc_numeric_to_double(ldd->ld.principal));
01139         break;
01140     }
01141 }
01142 
01143 static
01144 void
01145 ld_get_ipmt_formula( LoanDruidData *ldd, GString *gstr )
01146 {
01147     gint rate_case;
01148     gfloat pass_thru_rate;
01149     g_assert( ldd != NULL );
01150     g_assert( gstr != NULL );
01151     rate_case = ldd->ld.rateType;
01152     pass_thru_rate = ldd->ld.interestRate / 100;
01153     switch (rate_case)
01154     {
01155     case GNC_IRATE_SIMPLE:
01156     {
01157         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01158                          pass_thru_rate,
01159                          12.0,
01160                          ( ldd->ld.numPer
01161                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01162                          gnc_numeric_to_double(ldd->ld.principal));
01163     }
01164     break;
01165     case GNC_IRATE_APR_DAILY:
01166     {
01167         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01168                          ld_apr_to_simple_formula(pass_thru_rate, 365),
01169                          12.0,
01170                          ( ldd->ld.numPer
01171                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01172                          gnc_numeric_to_double(ldd->ld.principal));
01173     }
01174     break;
01175     case GNC_IRATE_APR_WEEKLY:
01176     {
01177         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01178                          ld_apr_to_simple_formula(pass_thru_rate, 52),
01179                          12.0,
01180                          ( ldd->ld.numPer
01181                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01182                          gnc_numeric_to_double(ldd->ld.principal));
01183     }
01184     break;
01185     case GNC_IRATE_APR_MONTHLY:
01186     {
01187         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01188                          ld_apr_to_simple_formula(pass_thru_rate, 12),
01189                          12.0,
01190                          ( ldd->ld.numPer
01191                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01192                          gnc_numeric_to_double(ldd->ld.principal));
01193     }
01194     break;
01195     case GNC_IRATE_APR_QUARTERLY:
01196     {
01197         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01198                          ld_apr_to_simple_formula(pass_thru_rate, 4),
01199                          12.0,
01200                          ( ldd->ld.numPer
01201                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01202                          gnc_numeric_to_double(ldd->ld.principal));
01203     }
01204     break;
01205     case GNC_IRATE_APR_ANNUALLY:
01206     {
01207         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01208                          ld_apr_to_simple_formula(pass_thru_rate, 1),
01209                          12.0,
01210                          ( ldd->ld.numPer
01211                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01212                          gnc_numeric_to_double(ldd->ld.principal));
01213     }
01214     break;
01215     default:
01216         g_string_printf( gstr, "ipmt( %.5f / %0.2f : i : %0.2f : %0.2f : 0 : 0 )",
01217                          (ldd->ld.interestRate / 100),
01218                          12.0,
01219                          ( ldd->ld.numPer
01220                            * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) ) * 1.,
01221                          gnc_numeric_to_double(ldd->ld.principal));
01222         break;
01223     }
01224 }
01225 
01226 static
01227 void
01228 ld_close_handler( LoanDruidData *ldd )
01229 {
01230     gtk_widget_hide( ldd->dialog );
01231     gtk_widget_destroy( ldd->dialog );
01232 }
01233 
01234 static
01235 void
01236 ld_destroy( GtkObject *o, gpointer ud )
01237 {
01238     LoanDruidData *ldd;
01239 
01240     ldd = (LoanDruidData*)ud;
01241 
01242     g_assert( ldd );
01243 
01244     gnc_unregister_gui_component_by_data
01245     (DIALOG_LOAN_DRUID_CM_CLASS, ldd);
01246 
01247     /* free alloc'd mem; cleanup */
01248 
01249     /* repay opts */
01250     {
01251         int i;
01252 
01253         g_date_free( ldd->ld.startDate );
01254         g_date_free( ldd->ld.varStartDate );
01255         recurrenceListFree(&ldd->ld.loan_schedule);
01256 
01257         if ( ldd->ld.repMemo )
01258             g_free( ldd->ld.repMemo );
01259 
01260         for ( i = 0; i < ldd->ld.repayOptCount; i++ )
01261         {
01262             RepayOptData *rod = ldd->ld.repayOpts[i];
01263             if ( rod->name )
01264                 g_free( rod->name );
01265             if ( rod->txnMemo )
01266                 g_free( rod->txnMemo );
01267 
01268             if ( rod->startDate )
01269                 g_date_free( rod->startDate );
01270 
01271             if (rod->schedule != NULL)
01272                 recurrenceListFree(&rod->schedule);
01273 
01274             g_free( ldd->ld.repayOpts[i] );
01275             g_free( ldd->repayOptsUI[i] );
01276         }
01277         g_free( ldd->ld.repayOpts );
01278         g_free( ldd->repayOptsUI );
01279 
01280         if ( ldd->ld.repAmount )
01281             g_free( ldd->ld.repAmount );
01282 
01283         g_date_free( ldd->ld.repStartDate );
01284     }
01285 
01286     /* review */
01287     {
01288         if ( ldd->ld.revSchedule )
01289         {
01290             g_list_foreach( ldd->ld.revSchedule,
01291                             ld_rev_sched_list_free,
01292                             NULL );
01293             g_list_free( ldd->ld.revSchedule );
01294             ldd->ld.revSchedule = NULL;
01295         }
01296     }
01297 
01298     g_free( ldd );
01299 }
01300 
01301 
01302 static
01303 void
01304 ld_cancel_check( GnomeDruid *gd, LoanDruidData *ldd )
01305 {
01306     const char *cancelMsg = _( "Are you sure you want to cancel "
01307                                "the Mortgage/Loan Setup Assistant?" );
01308     if ( gnc_verify_dialog( ldd->dialog, FALSE, "%s", cancelMsg ) )
01309     {
01310         gnc_close_gui_component_by_data( DIALOG_LOAN_DRUID_CM_CLASS,
01311                                          ldd );
01312     }
01313 }
01314 
01315 static
01316 void
01317 ld_prm_type_changed( GtkWidget *w, gpointer ud )
01318 {
01319     LoanDruidData *ldd;
01320     gint index;
01321 
01322     ldd = (LoanDruidData*)ud;
01323     index = gtk_combo_box_get_active( ldd->prmType );
01324     gtk_widget_set_sensitive( GTK_WIDGET(ldd->prmVarFrame),
01325                               index != GNC_FIXED );
01326 }
01327 
01328 static
01329 void
01330 ld_escrow_toggle( GtkToggleButton *tb, gpointer ud )
01331 {
01332     int i;
01333     gboolean newState;
01334     RepayOptUIData *rouid;
01335     LoanDruidData *ldd;
01336 
01337     ldd = (LoanDruidData*)ud;
01338     newState = gtk_toggle_button_get_active(tb);
01339     gtk_widget_set_sensitive( GTK_WIDGET(ldd->optEscrowHBox), newState );
01340     /* deal with escrow options. */
01341     for ( i = 0; i < ldd->ld.repayOptCount; i++ )
01342     {
01343         rouid = ldd->repayOptsUI[i];
01344         /* If we're going off, then uncheck and desensitize all escrow opts. */
01345         /* If we're going on, then sensitize all escrow opts. */
01346 
01347         /* prevent the toggle handler from running and "trashing" the
01348          * state of the throughEscrowP selection */
01349         g_signal_handlers_block_by_func( rouid->escrowCb,
01350                                          ld_escrow_toggled,
01351                                          rouid );
01352         gtk_toggle_button_set_active(
01353             GTK_TOGGLE_BUTTON(rouid->escrowCb),
01354             ( newState
01355               && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rouid->optCb) )
01356               && rouid->optData->throughEscrowP ) );
01357         gtk_widget_set_sensitive(
01358             GTK_WIDGET(rouid->escrowCb),
01359             newState
01360             && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rouid->optCb) ) );
01361         g_signal_handlers_unblock_by_func( rouid->escrowCb,
01362                                            ld_escrow_toggled,
01363                                            rouid );
01364         if ( newState )
01365         {
01366             rouid->optData->from = ldd->ld.escrowAcct;
01367         }
01368         else
01369         {
01370             rouid->optData->from = NULL;
01371         }
01372     }
01373 }
01374 
01375 static
01376 void
01377 ld_opt_toggled( GtkToggleButton *tb, gpointer ud )
01378 {
01379     RepayOptUIData *rouid;
01380 
01381     rouid = (RepayOptUIData*)ud;
01382     rouid->optData->enabled = gtk_toggle_button_get_active(tb);
01383 }
01384 
01385 static
01386 void
01387 ld_opt_consistency( GtkToggleButton *tb, gpointer ud )
01388 {
01389     GtkToggleButton *escrowCb;
01390     RepayOptUIData *rouid;
01391 
01392     rouid = (RepayOptUIData*)ud;
01393     escrowCb = GTK_TOGGLE_BUTTON(rouid->escrowCb);
01394     /* make sure the escrow option is only selected if we're active. */
01395     gtk_toggle_button_set_active( escrowCb,
01396                                   gtk_toggle_button_get_active(
01397                                       GTK_TOGGLE_BUTTON(
01398                                           rouid->ldd->optEscrowCb) )
01399                                   && rouid->optData->throughEscrowP
01400                                   && gtk_toggle_button_get_active(tb) );
01401     /* make sure the escrow option is only sensitive if we're active, and
01402      * the escrow account is enabled  */
01403     gtk_widget_set_sensitive( GTK_WIDGET(escrowCb),
01404                               gtk_toggle_button_get_active(tb)
01405                               && gtk_toggle_button_get_active(
01406                                   GTK_TOGGLE_BUTTON(rouid->ldd->optEscrowCb)) );
01407 }
01408 
01409 static
01410 void
01411 ld_escrow_toggled( GtkToggleButton *tb, gpointer ud )
01412 {
01413     RepayOptUIData *rouid;
01414 
01415     rouid = (RepayOptUIData*)ud;
01416     rouid->optData->throughEscrowP = gtk_toggle_button_get_active( tb );
01417 }
01418 
01419 static
01420 gboolean
01421 ld_info_save( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01422 {
01423     LoanDruidData *ldd;
01424 
01425     ldd = (LoanDruidData*)ud;
01426 
01427     ldd->ld.primaryAcct = gnc_account_sel_get_account( ldd->prmAccountGAS );
01428     if ( ldd->ld.primaryAcct == NULL )
01429     {
01430         gnc_info_dialog( ldd->dialog, "%s",
01431                          _("Please select a valid loan account.") );
01432         return TRUE;
01433     }
01434     if ( ! ldd->ld.repPriAcct )
01435     {
01436         ldd->ld.repPriAcct = ldd->ld.primaryAcct;
01437     }
01438     ldd->ld.principal = gnc_amount_edit_get_amount( ldd->prmOrigPrincGAE );
01439     ldd->ld.interestRate = gtk_spin_button_get_value( ldd->prmIrateSpin );
01440     ldd->ld.rateType = gtk_combo_box_get_active (ldd->prmIrateType );
01441     ldd->ld.type = gtk_combo_box_get_active( ldd->prmType );
01442     if ( ldd->ld.type != GNC_FIXED )
01443     {
01444         recurrenceListFree(&ldd->ld.loan_schedule);
01445         gnc_frequency_save_to_recurrence(ldd->prmVarGncFreq,
01446                                          &ldd->ld.loan_schedule,
01447                                          ldd->ld.varStartDate);
01448     }
01449 
01450     /* start date */
01451     {
01452         time_t tmpTT;
01453         struct tm *tmpTm;
01454 
01455         tmpTT = gnc_date_edit_get_date( ldd->prmStartDateGDE );
01456         tmpTm = localtime( &tmpTT );
01457         g_date_set_dmy( ldd->ld.startDate,
01458                         tmpTm->tm_mday,
01459                         (tmpTm->tm_mon + 1),
01460                         (1900 + tmpTm->tm_year) );
01461     }
01462 
01463     /* len / periods */
01464     {
01465         ldd->ld.perSize =
01466             (gtk_combo_box_get_active( ldd->prmLengthType )
01467              == GNC_MONTHS) ? GNC_MONTHS : GNC_YEARS;
01468         ldd->ld.numPer =
01469             gtk_spin_button_get_value_as_int( ldd->prmLengthSpin );
01470         ldd->ld.numMonRemain =
01471             gtk_spin_button_get_value_as_int( ldd->prmRemainSpin );
01472     }
01473     return FALSE;
01474 }
01475 
01476 static
01477 void
01478 ld_info_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01479 {
01480     LoanDruidData *ldd;
01481 
01482     ldd = (LoanDruidData*)ud;
01483     gnc_amount_edit_set_amount( ldd->prmOrigPrincGAE, ldd->ld.principal );
01484     gtk_spin_button_set_value( ldd->prmIrateSpin, ldd->ld.interestRate );
01485     gtk_combo_box_set_active( ldd->prmIrateType, ldd ->ld.rateType );
01486     gtk_combo_box_set_active( ldd->prmType, ldd->ld.type );
01487     if ( ldd->ld.type != GNC_FIXED )
01488     {
01489         gnc_frequency_setup_recurrence(ldd->prmVarGncFreq,
01490                                        ldd->ld.loan_schedule,
01491                                        ldd->ld.varStartDate);
01492     }
01493 
01494     /* start date */
01495     {
01496         struct tm *tmpTm;
01497 
01498         tmpTm = g_new0( struct tm, 1 );
01499 
01500         g_date_to_struct_tm( ldd->ld.startDate, tmpTm );
01501         gnc_date_edit_set_time( ldd->prmStartDateGDE,
01502                                 mktime(tmpTm) );
01503         g_free( tmpTm );
01504     }
01505 
01506     /* length: total and remaining */
01507     {
01508         gtk_spin_button_set_value( ldd->prmLengthSpin, ldd->ld.numPer );
01509         gtk_combo_box_set_active( ldd->prmLengthType, ldd->ld.perSize );
01510         gtk_spin_button_set_value( ldd->prmRemainSpin, ldd->ld.numMonRemain );
01511     }
01512 }
01513 
01514 static
01515 gboolean
01516 ld_opts_save_state( LoanDruidData *ldd )
01517 {
01518     if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(ldd->optEscrowCb) ) )
01519     {
01520         ldd->ld.escrowAcct =
01521             gnc_account_sel_get_account( ldd->optEscrowGAS );
01522         if ( ldd->ld.escrowAcct == NULL )
01523         {
01524             gnc_info_dialog( ldd->dialog, "%s",
01525                              _("Please select a valid "
01526                                "Escrow Account.") );
01527             return TRUE;
01528         }
01529 
01530     }
01531     else
01532     {
01533         ldd->ld.escrowAcct = NULL;
01534     }
01535     return FALSE;
01536 }
01537 
01538 static
01539 gboolean
01540 ld_opts_tran( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01541 {
01542     return ld_opts_save_state( (LoanDruidData*)ud );
01543 }
01544 
01545 static
01546 void
01547 ld_opts_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01548 {
01549     int i;
01550     RepayOptUIData *rouid;
01551     LoanDruidData *ldd;
01552 
01553     ldd = (LoanDruidData*)ud;
01554     if ( ldd->ld.escrowAcct )
01555     {
01556         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->optEscrowCb),
01557                                       TRUE );
01558         gnc_account_sel_set_account( ldd->optEscrowGAS, ldd->ld.escrowAcct, FALSE );
01559     }
01560     for ( i = 0; i < ldd->ld.repayOptCount; i++ )
01561     {
01562         rouid = ldd->repayOptsUI[i];
01563         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(rouid->optCb),
01564                                       rouid->optData->enabled );
01565         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(rouid->escrowCb),
01566                                       rouid->optData->throughEscrowP
01567                                       && rouid->optData->enabled
01568                                       && ldd->ld.escrowAcct );
01569         gtk_widget_set_sensitive( GTK_WIDGET(rouid->escrowCb),
01570                                   rouid->optData->enabled
01571                                   && ldd->ld.escrowAcct );
01572     }
01573 }
01574 
01575 static
01576 gboolean
01577 ld_rep_save( LoanDruidData *ldd )
01578 {
01579     int i;
01580 
01581     if ( ldd->ld.repMemo )
01582         g_free( ldd->ld.repMemo );
01583     ldd->ld.repMemo =
01584         gtk_editable_get_chars( GTK_EDITABLE(ldd->repTxnName), 0, -1 );
01585 
01586     if ( ldd->ld.repAmount )
01587         g_free( ldd->ld.repAmount );
01588     ldd->ld.repAmount =
01589         gtk_editable_get_chars( GTK_EDITABLE(ldd->repAmtEntry), 0, -1 );
01590 
01591     ldd->ld.repFromAcct =
01592         gnc_account_sel_get_account( ldd->repAssetsFromGAS );
01593     if ( ldd->ld.repFromAcct == NULL )
01594     {
01595         gnc_info_dialog( ldd->dialog, "%s",
01596                          _("Please select a valid \"from\" account."));
01597         return TRUE;
01598     }
01599     ldd->ld.repPriAcct =
01600         gnc_account_sel_get_account( ldd->repPrincToGAS );
01601     if ( ldd->ld.repPriAcct == NULL )
01602     {
01603         gnc_info_dialog( ldd->dialog, "%s",
01604                          _("Please select a valid \"to\" account.") );
01605         return TRUE;
01606     }
01607     ldd->ld.repIntAcct =
01608         gnc_account_sel_get_account( ldd->repIntToGAS );
01609     if ( ldd->ld.repIntAcct == NULL )
01610     {
01611         gnc_info_dialog( ldd->dialog, "%s",
01612                          _("Please select a valid "
01613                            "\"interest\" account.") );
01614         return TRUE;
01615     }
01616     recurrenceListFree(&ldd->ld.repayment_schedule);
01617     gnc_frequency_save_to_recurrence(ldd->repGncFreq,
01618                                      &ldd->ld.repayment_schedule,
01619                                      ldd->ld.repStartDate);
01620 
01621     /* Set the 'from' accounts of the various options to be the
01622      * Assets-From account, if they're not already something else. */
01623     for ( i = 0; i < ldd->ld.repayOptCount; i++ )
01624     {
01625         RepayOptData *rod = ldd->ld.repayOpts[i];
01626         if ( ! rod->from )
01627         {
01628             rod->from = ldd->ld.repFromAcct;
01629         }
01630     }
01631     return FALSE;
01632 }
01633 
01634 static
01635 gboolean
01636 ld_rep_next( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01637 {
01638     LoanDruidData *ldd;
01639 
01640     ldd = (LoanDruidData*)ud;
01641 
01642     if ( ld_rep_save( ldd ) != FALSE )
01643     {
01644         DEBUG( "Couldn't save, stopping here." );
01645         return TRUE;
01646     }
01647 
01648     {
01649         int i;
01650         for ( i = 0; // we can always start at 0, here.
01651                 (i < ldd->ld.repayOptCount)
01652                 && !ldd->ld.repayOpts[i]->enabled;
01653                 i++ )
01654             ;
01655         if ( i < ldd->ld.repayOptCount )
01656         {
01657             ldd->currentIdx = i;
01658             return FALSE;
01659         }
01660     }
01661     gnome_druid_set_page( ldd->druid,
01662                           GNOME_DRUID_PAGE(
01663                               glade_xml_get_widget( ldd->gxml,
01664                                       PG_REVIEW ) ) );
01665     return TRUE;
01666 }
01667 
01668 static
01669 gboolean
01670 ld_rep_back( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01671 {
01672     LoanDruidData *ldd;
01673 
01674     ldd = (LoanDruidData*)ud;
01675     return ld_rep_save(ldd);
01676 }
01677 
01678 static
01679 void
01680 ld_rep_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01681 {
01682     LoanDruidData *ldd;
01683     GString *str;
01684 
01685 
01686     ldd = (LoanDruidData*)ud;
01687 
01688     if ( ldd->ld.repAmount )
01689     {
01690         g_free( ldd->ld.repAmount );
01691     }
01692 
01693     str = g_string_sized_new( 64 );
01694     ld_get_pmt_formula( ldd, str);
01695     ldd->ld.repAmount = str->str;
01696     g_string_free( str, FALSE );
01697 
01698     if ( ldd->ld.repMemo )
01699         gtk_entry_set_text( ldd->repTxnName, ldd->ld.repMemo );
01700 
01701     if ( ldd->ld.repAmount )
01702         gtk_entry_set_text( ldd->repAmtEntry, ldd->ld.repAmount );
01703 
01704     gnc_account_sel_set_account( ldd->repAssetsFromGAS,
01705                                  ldd->ld.repFromAcct, FALSE );
01706     gnc_account_sel_set_account( ldd->repPrincToGAS,
01707                                  ldd->ld.repPriAcct, FALSE );
01708     gnc_account_sel_set_account( ldd->repIntToGAS,
01709                                  ldd->ld.repIntAcct, FALSE );
01710     gnc_frequency_setup_recurrence(ldd->repGncFreq,
01711                                    ldd->ld.repayment_schedule,
01712                                    ldd->ld.repStartDate);
01713 }
01714 
01715 static
01716 void
01717 ld_pay_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01718 {
01719     LoanDruidData *ldd;
01720     RepayOptData *rod;
01721     GString *str;
01722     gboolean uniq;
01723 
01724     ldd = (LoanDruidData*)ud;
01725     g_assert( ldd->currentIdx >= 0 );
01726     g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
01727 
01728     rod = ldd->ld.repayOpts[ldd->currentIdx];
01729     str = g_string_sized_new( 32 );
01730     /* Translators: %s is "Taxes", or "Insurance", or similar */
01731     g_string_printf( str, _("Payment: \"%s\""), rod->name );
01732     gnome_druid_page_standard_set_title( GNOME_DRUID_PAGE_STANDARD(gdp),
01733                                          str->str );
01734     /* copy in the relevant data from the currently-indexed
01735      * option. */
01736     gtk_entry_set_text( ldd->payTxnName, rod->txnMemo );
01737     g_string_printf( str, "%0.2f", rod->amount );
01738     gtk_entry_set_text( ldd->payAmtEntry, str->str );
01739 
01740     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payUseEscrow),
01741                               (ldd->ld.escrowAcct != NULL) );
01742 
01743     {
01744         g_signal_handlers_block_by_func( ldd->payUseEscrow,
01745                                          ld_pay_use_esc_toggle,
01746                                          ldd );
01747 
01748         ld_pay_use_esc_setup( ldd,
01749                               (ldd->ld.escrowAcct != NULL)
01750                               && rod->throughEscrowP );
01751         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payUseEscrow),
01752                                       (rod->throughEscrowP
01753                                        && ldd->ld.escrowAcct != NULL) );
01754 
01755         g_signal_handlers_unblock_by_func( ldd->payUseEscrow,
01756                                            ld_pay_use_esc_toggle,
01757                                            ldd );
01758     }
01759 
01760     {
01761         g_signal_handlers_block_by_func( ldd->paySpecSrcAcct,
01762                                          ld_pay_spec_src_toggle,
01763                                          ldd );
01764 
01765         ld_pay_spec_src_setup( ldd, rod->specSrcAcctP );
01766         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->paySpecSrcAcct),
01767                                       rod->specSrcAcctP );
01768 
01769         g_signal_handlers_unblock_by_func( ldd->paySpecSrcAcct,
01770                                            ld_pay_spec_src_toggle,
01771                                            ldd );
01772     }
01773 
01774     gnc_account_sel_set_account( ldd->payAcctToGAS,   rod->to, FALSE );
01775 
01776     uniq = (rod->schedule != NULL);
01777     gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payTxnFreqPartRb),
01778                                   !uniq );
01779     gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ldd->payTxnFreqUniqRb),
01780                                   uniq );
01781     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payFreqAlign),
01782                               uniq );
01783     if ( uniq )
01784     {
01785         gnc_frequency_setup_recurrence( ldd->payGncFreq, rod->schedule, rod->startDate );
01786     }
01787 
01788     g_string_free( str, TRUE );
01789 }
01790 
01791 static
01792 gboolean
01793 ld_pay_save_current( LoanDruidData *ldd )
01794 {
01795     gchar *tmpStr;
01796     RepayOptData *rod;
01797 
01798     g_assert( ldd->currentIdx >= 0 );
01799     g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
01800     rod = ldd->ld.repayOpts[ ldd->currentIdx ];
01801 
01802     tmpStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->payTxnName),
01803                                      0, -1 );
01804     if ( rod->txnMemo != NULL )
01805     {
01806         g_free( rod->txnMemo );
01807     }
01808     rod->txnMemo = tmpStr;
01809     tmpStr = NULL;
01810 
01811     tmpStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->payAmtEntry),
01812                                      0, -1 );
01813     rod->amount = (float)strtod( tmpStr, NULL );
01814     g_free( tmpStr );
01815 
01816     rod->specSrcAcctP =
01817         gtk_toggle_button_get_active(
01818             GTK_TOGGLE_BUTTON(ldd->paySpecSrcAcct) );
01819 
01820     rod->from = NULL;
01821     if ( rod->specSrcAcctP )
01822     {
01823         rod->from = gnc_account_sel_get_account( ldd->payAcctFromGAS );
01824         if ( rod->from == NULL )
01825         {
01826             gnc_info_dialog( ldd->dialog, "%s",
01827                              _("Please select a valid "
01828                                "\"from\" account.") );
01829             return TRUE;
01830         }
01831     }
01832 
01833     rod->to   = gnc_account_sel_get_account( ldd->payAcctToGAS );
01834     if ( rod->to == NULL )
01835     {
01836         gnc_info_dialog( ldd->dialog, "%s",
01837                          _("Please select a valid "
01838                            "\"to\" account.") );
01839         return TRUE;
01840     }
01841 
01842     /* if ( rb toggled )
01843      *   ensure schedule/startdate setup
01844      *   save
01845      * else
01846      *   if (freqspec setup)
01847      *     remove schedule/startdate
01848      */
01849 
01850     /* neither of these should happen. */
01851     g_assert( ! (rod->schedule && !rod->startDate) );
01852     g_assert( ! (!rod->schedule && rod->startDate) );
01853 
01854     if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ldd->payTxnFreqUniqRb)) )
01855     {
01856         if ( rod->startDate == NULL )
01857         {
01858             rod->startDate = g_date_new();
01859         }
01860         recurrenceListFree(&rod->schedule);
01861         gnc_frequency_save_to_recurrence(ldd->payGncFreq,
01862                                          &rod->schedule,
01863                                          rod->startDate);
01864     }
01865     else
01866     {
01867         if (rod->schedule)
01868         {
01869             recurrenceListFree(&rod->schedule);
01870         }
01871         if ( rod->startDate )
01872         {
01873             g_date_free( rod->startDate );
01874             rod->startDate = NULL;
01875         }
01876     }
01877     return FALSE;
01878 }
01879 
01880 static
01881 gboolean
01882 ld_pay_next( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01883 {
01884     int i;
01885     LoanDruidData *ldd;
01886 
01887     ldd = (LoanDruidData*)ud;
01888     /* save current data */
01889     if ( ld_pay_save_current( ldd ) != FALSE )
01890     {
01891         return TRUE;
01892     }
01893 
01894     /* Go through opts list and select next enabled option. */
01895     for ( i = ldd->currentIdx + 1;
01896             (i < ldd->ld.repayOptCount)
01897             && !ldd->ld.repayOpts[i]->enabled; i++ )
01898         ;
01899 
01900     if ( i < ldd->ld.repayOptCount )
01901     {
01902         ldd->currentIdx = i;
01903         ld_pay_prep( gdp, arg1, ud );
01904         return TRUE;
01905     }
01906 
01907     /* If there's no repayment options enabled, then go immediately to
01908      * the review page. */
01909     gnome_druid_set_page( ldd->druid,
01910                           GNOME_DRUID_PAGE(
01911                               glade_xml_get_widget( ldd->gxml,
01912                                       PG_REVIEW ) ) );
01913     return TRUE;
01914 }
01915 
01916 static
01917 gboolean
01918 ld_pay_back( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
01919 {
01920     int i;
01921     LoanDruidData *ldd;
01922 
01923     ldd = (LoanDruidData*)ud;
01924 
01925     /* save current data */
01926     if ( ld_pay_save_current( ldd ) != FALSE )
01927     {
01928         return TRUE;
01929     }
01930 
01931     /* go back through opts list and select next enabled options. */
01932     for ( i = ldd->currentIdx - 1;
01933             (i > -1) && !ldd->ld.repayOpts[i]->enabled;
01934             i-- )
01935         ;
01936     if ( i >= 0 )
01937     {
01938         ldd->currentIdx = i;
01939         ld_pay_prep( gdp, arg1, ud );
01940         return TRUE;
01941     }
01942     ldd->currentIdx = -1;
01943     return FALSE;
01944 }
01945 
01946 static
01947 void
01948 ld_pay_freq_toggle( GtkToggleButton *tb, gpointer ud )
01949 {
01950     LoanDruidData *ldd;
01951     gboolean uniq;
01952 
01953     ldd = (LoanDruidData*)ud;
01954 
01955     g_assert( ldd->currentIdx >= 0 );
01956     g_assert( ldd->currentIdx <= ldd->ld.repayOptCount );
01957 
01958     uniq = gtk_toggle_button_get_active(
01959                GTK_TOGGLE_BUTTON(ldd->payTxnFreqUniqRb) );
01960     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payFreqAlign), uniq );
01961 
01962     if ( uniq )
01963     {
01964         RepayOptData *rod;
01965         rod = ldd->ld.repayOpts[ ldd->currentIdx ];
01966 
01967         if ( rod->schedule == NULL )
01968         {
01969             Recurrence *r = g_new0(Recurrence, 1);
01970             recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
01971             rod->schedule = g_list_append(rod->schedule, r);
01972         }
01973         if ( rod->startDate == NULL )
01974         {
01975             rod->startDate = g_date_new();
01976             *rod->startDate = *ldd->ld.startDate;
01977         }
01978         gnc_frequency_setup_recurrence(ldd->payGncFreq,
01979                                        rod->schedule,
01980                                        rod->startDate);
01981     }
01982 }
01983 
01984 static
01985 void
01986 ld_pay_use_esc_setup( LoanDruidData *ldd, gboolean newState )
01987 {
01988     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payEscToLabel), newState );
01989     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payEscFromLabel), newState );
01990     if ( newState )
01991     {
01992         gnc_account_sel_set_account( ldd->payAcctEscToGAS,
01993                                      ldd->ld.escrowAcct, FALSE );
01994         gnc_account_sel_set_account( ldd->payAcctEscFromGAS,
01995                                      ldd->ld.escrowAcct, FALSE );
01996     }
01997 }
01998 
01999 static
02000 void
02001 ld_pay_use_esc_toggle( GtkToggleButton *tb, gpointer ud )
02002 {
02003     gboolean newState;
02004     LoanDruidData *ldd = (LoanDruidData*)ud;
02005 
02006     newState = gtk_toggle_button_get_active( tb );
02007     ld_pay_use_esc_setup( ldd, newState );
02008 }
02009 
02010 static
02011 void
02012 ld_pay_spec_src_setup( LoanDruidData *ldd, gboolean newState )
02013 {
02014     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctFromLabel), newState );
02015     gtk_widget_set_sensitive( GTK_WIDGET(ldd->payAcctFromGAS), newState );
02016     if ( newState )
02017     {
02018         gnc_account_sel_set_account( ldd->payAcctFromGAS,
02019                                      ldd->ld.repayOpts[ldd->currentIdx]
02020                                      ->from, FALSE );
02021     }
02022 }
02023 
02024 static
02025 void
02026 ld_pay_spec_src_toggle( GtkToggleButton *tb, gpointer ud )
02027 {
02028     gboolean newState;
02029     LoanDruidData *ldd = (LoanDruidData*)ud;
02030 
02031     newState = gtk_toggle_button_get_active( tb );
02032     ld_pay_spec_src_setup( ldd, newState );
02033 }
02034 
02035 static
02036 gboolean
02037 ld_rev_next( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02038 {
02039     LoanDruidData *ldd;
02040 
02041     ldd = (LoanDruidData*)ud;
02042     gnome_druid_set_page( ldd->druid,
02043                           GNOME_DRUID_PAGE(
02044                               glade_xml_get_widget( ldd->gxml,
02045                                       PG_COMMIT ) ) );
02046     return TRUE;
02047 }
02048 
02049 static
02050 void
02051 ld_rev_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02052 {
02053     /* 3, here, does not include the Date column. */
02054     const static int BASE_COLS = 3;
02055     LoanDruidData *ldd;
02056     GtkListStore *store;
02057     GtkCellRenderer *renderer;
02058     GtkTreeViewColumn *column;
02059     GType *types;
02060     int i;
02061 
02062     ldd = (LoanDruidData*)ud;
02063 
02064     /* Cleanup old view */
02065     if ( ldd->revView != NULL )
02066     {
02067         gtk_widget_destroy( GTK_WIDGET(ldd->revView) );
02068         ldd->revView = NULL;
02069     }
02070 
02071     ldd->ld.revNumPmts = BASE_COLS;
02072     /* Get the correct number of repayment columns. */
02073     for ( i = 0; i < ldd->ld.repayOptCount; i++ )
02074     {
02075         ldd->ld.revRepayOptToColMap[i] = -1;
02076         if ( ! ldd->ld.repayOpts[i]->enabled )
02077         {
02078             continue;
02079         }
02080         /* not '+1' = there is no date column to be accounted for in
02081          * the mapping. */
02082         ldd->ld.revRepayOptToColMap[i] = ldd->ld.revNumPmts;
02083         ldd->ld.revNumPmts += 1;
02084     }
02085 
02086     /* '+1' for leading date col */
02087     types = g_new( GType, ldd->ld.revNumPmts + 1 );
02088     for ( i = 0; i < ldd->ld.revNumPmts + 1; i++ )
02089         types[i] = G_TYPE_STRING;
02090     store = gtk_list_store_newv(ldd->ld.revNumPmts + 1, types);
02091     g_free(types);
02092 
02093     ldd->revView = GTK_TREE_VIEW(
02094                        gtk_tree_view_new_with_model( GTK_TREE_MODEL(store) ));
02095     g_object_unref(store);
02096 
02097     renderer = gtk_cell_renderer_text_new();
02098     column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer,
02099              "text", LOAN_COL_DATE,
02100              NULL);
02101     gtk_tree_view_append_column(ldd->revView, column);
02102 
02103     renderer = gtk_cell_renderer_text_new();
02104     column = gtk_tree_view_column_new_with_attributes(_("Payment"), renderer,
02105              "text", LOAN_COL_PAYMENT,
02106              NULL);
02107     gtk_tree_view_append_column(ldd->revView, column);
02108 
02109     renderer = gtk_cell_renderer_text_new();
02110     column = gtk_tree_view_column_new_with_attributes(_("Principal"), renderer,
02111              "text", LOAN_COL_PRINCIPAL,
02112              NULL);
02113     gtk_tree_view_append_column(ldd->revView, column);
02114 
02115     renderer = gtk_cell_renderer_text_new();
02116     column = gtk_tree_view_column_new_with_attributes(_("Interest"), renderer,
02117              "text", LOAN_COL_INTEREST,
02118              NULL);
02119     gtk_tree_view_append_column(ldd->revView, column);
02120 
02121     /* move the appropriate names over into the title array */
02122     {
02123         for ( i = 0; i < ldd->ld.repayOptCount; i++ )
02124         {
02125             if ( ldd->ld.revRepayOptToColMap[i] == -1 )
02126             {
02127                 continue;
02128             }
02129 
02130             renderer = gtk_cell_renderer_text_new();
02131             column = gtk_tree_view_column_new_with_attributes
02132                      (ldd->ld.repayOpts[i]->name, renderer,
02133                       "text", LOAN_COL_INTEREST + i,
02134                       NULL);
02135             gtk_tree_view_append_column(ldd->revView, column);
02136         }
02137     }
02138 
02139     gtk_container_add( GTK_CONTAINER(ldd->revScrollWin),
02140                        GTK_WIDGET(ldd->revView) );
02141     gtk_widget_show( GTK_WIDGET(ldd->revView) );
02142 
02143     ld_rev_recalc_schedule( ldd );
02144 
02145     {
02146         GDate start, end;
02147         g_date_clear( &start, 1 );
02148         g_date_clear( &end, 1 );
02149         ld_rev_get_dates( ldd, &start, &end );
02150         ld_rev_update_view( ldd, &start, &end );
02151     }
02152 }
02153 
02154 static
02155 gboolean
02156 ld_rev_back( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02157 {
02158     LoanDruidData *ldd = (LoanDruidData*)ud;
02159     int i;
02160 
02161     /* Get the correct page based on the repayment state. */
02162     /* go back through opts list and select next enabled options. */
02163     for ( i = ldd->currentIdx;
02164             (i > -1) && !ldd->ld.repayOpts[i]->enabled;
02165             i-- )
02166         ;
02167     if ( i >= 0 )
02168     {
02169         ldd->currentIdx = i;
02170         /* natural transition to the payments page */
02171         return FALSE;
02172     }
02173 
02174     /* If there are no payment options, then go directly to the main
02175      * repayment page. */
02176     gnome_druid_set_page( ldd->druid,
02177                           GNOME_DRUID_PAGE(
02178                               glade_xml_get_widget( ldd->gxml,
02179                                       PG_REPAYMENT ) ) );
02180     return TRUE;
02181 }
02182 
02183 static
02184 gboolean
02185 ld_com_next( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02186 {
02187     g_assert( FALSE );
02188     return TRUE;
02189 }
02190 
02191 static
02192 void
02193 ld_com_prep( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02194 {
02195 }
02196 
02197 static
02198 gboolean
02199 ld_com_back( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02200 {
02201     LoanDruidData *ldd = (LoanDruidData*)ud;
02202     gnome_druid_set_page( ldd->druid,
02203                           GNOME_DRUID_PAGE(
02204                               glade_xml_get_widget( ldd->gxml,
02205                                       PG_REVIEW ) ) );
02206     return TRUE;
02207 }
02208 
02209 static
02210 void
02211 ld_com_fin( GnomeDruidPage *gdp, gpointer arg1, gpointer ud )
02212 {
02213     LoanDruidData *ldd = (LoanDruidData*)ud;
02214     ld_create_sxes( ldd );
02215     ld_close_handler( ldd );
02216 
02217 }
02218 
02219 static int
02220 ld_calc_sx_instance_num(GDate *start_date, GList *schedule)
02221 {
02222     int instance_count;
02223     GDate next_date, today;
02224 
02225     g_date_clear(&next_date, 1);
02226     g_date_clear(&today, 1);
02227     g_date_set_time_t(&today, time(NULL));
02228 
02229     if (g_date_compare(start_date, &today) > 0)
02230         return 0;
02231 
02232     instance_count = -1;
02233     do
02234     {
02235         instance_count++;
02236         recurrenceListNextInstance(schedule, start_date, &next_date);
02237     }
02238     while (g_date_compare(&next_date, &today) < 0);
02239 
02240     return instance_count;
02241 }
02242 
02243 static
02244 void
02245 ld_tcSX_free( gpointer data, gpointer user_data )
02246 {
02247     toCreateSX *tcSX = (toCreateSX*)data;
02248     g_free( tcSX->name );
02249     if ( tcSX->mainTxn )
02250         gnc_ttinfo_free( tcSX->mainTxn );
02251     if ( tcSX->escrowTxn )
02252         gnc_ttinfo_free( tcSX->escrowTxn );
02253     g_free( tcSX );
02254 }
02255 
02261 static
02262 gint
02263 ld_find_ttsplit_with_acct( gconstpointer elt,
02264                            gconstpointer crit )
02265 {
02266     TTSplitInfo *ttsi = (TTSplitInfo*)elt;
02267     return ( (gnc_ttsplitinfo_get_account( ttsi )
02268               == (Account*)crit) ? 0 : 1 );
02269 }
02270 
02274 static
02275 void
02276 ld_create_sx_from_tcSX( LoanDruidData *ldd, toCreateSX *tcSX )
02277 {
02278     SchedXaction *sx;
02279     SchedXactions *sxes;
02280     GList *ttxnList;
02281 
02282     sx = xaccSchedXactionMalloc( gnc_get_current_book() );
02283     xaccSchedXactionSetName( sx, tcSX->name );
02284     gnc_sx_set_schedule(sx, tcSX->schedule);
02285     xaccSchedXactionSetStartDate( sx, &tcSX->start );
02286     xaccSchedXactionSetLastOccurDate( sx, &tcSX->last );
02287     xaccSchedXactionSetEndDate( sx, &tcSX->end );
02288     gnc_sx_set_instance_count( sx, tcSX->instNum );
02289 
02290     ttxnList = NULL;
02291     if ( tcSX->mainTxn )
02292         ttxnList = g_list_append( ttxnList, tcSX->mainTxn );
02293     if ( tcSX->escrowTxn )
02294         ttxnList = g_list_append( ttxnList, tcSX->escrowTxn );
02295 
02296     g_assert( ttxnList != NULL );
02297 
02298     xaccSchedXactionSetTemplateTrans( sx, ttxnList,
02299                                       gnc_get_current_book() );
02300 
02301     sxes = gnc_book_get_schedxactions(gnc_get_current_book());
02302     gnc_sxes_add_sx(sxes, sx);
02303 
02304     g_list_free( ttxnList );
02305     ttxnList = NULL;
02306 }
02307 
02313 static
02314 void
02315 ld_setup_repayment_sx( LoanDruidData *ldd,
02316                        RepayOptData *rod,
02317                        toCreateSX *paymentSX,
02318                        toCreateSX *tcSX )
02319 {
02320     /* In DoubleEntryAccounting-ease, this is what we're going to do,
02321      * below...
02322      *
02323      * if ( rep->escrow ) {
02324      *   if ( rep->from ) {
02325      *      a: paymentSX.main.splits += split( rep->fromAcct, repAmt )
02326      *      b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
02327      *         tcSX.escrow.split( rep->escrow ).debCred += repAmt
02328      *     c1: tcSX.escrow.splits += split( rep->toAcct, +repAmt )
02329      *   } else {
02330      *      d: paymentSX.main.split( ldd->ld.repFromAcct ).debcred += -repAmt
02331      *      b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
02332      *         tcSX.escrow.splits += split( rep->escrow, -repAmt )
02333      *     c1: tcSX.escrow.splits += split( rep->toAcct, +repAmt )
02334      *   }
02335      * } else {
02336      *   if ( rep->from ) {
02337      *      a: paymentSX.main.splits += split( rep->fromAcct, -repAmt )
02338      *     c2: paymentSX.main.splits += split( rep->toAcct,   +repAmt )
02339      *   } else {
02340      *      d: paymentSX.main.split( ldd->ld.payFromAcct ).debcred += -repAmt
02341      *     c2: paymentSX.main.splits += split( rep->toAcct, +repAmt )
02342      *   }
02343      * }
02344      */
02345 
02346     /* Now, we refactor the common operations from the above out...
02347      *
02348      * fromSplit = NULL;
02349      * if ( rep->escrow ) {
02350      *   b: paymentSX.main.split( ldd->ld.escrowAcct ).debCred += repAmt
02351      *  c1: ( toTTI = tcSX.escrow )
02352      *   if ( rep->from ) {
02353      *     a1: (fromSplit = NULL) paymentSX.main.splits += split( rep->fromAcct, repAmt )
02354      *      b:
02355      *         tcSX.escrow.split( rep->escrow ).debCred += repAmt
02356      *     c1:
02357      *   } else {
02358      *     a2: (fromSplit = paymentSX.main.split( ldd->ld.repFromAcct )) .debcred += -repAmt
02359      *      b:
02360      *         tcSX.escrow.splits += split( rep->escrow, -repAmt )
02361      *     c1:
02362      *   }
02363      * } else {
02364      *   c2: ( toTTI = paymentSX.main )
02365      *   if ( rep->from ) {
02366      *     a1: (fromSplit = NULL) paymentSX.main.splits += split( rep->fromAcct, -repAmt )
02367      *     c2:
02368      *   } else {
02369      *     a2: (fromSplit = paymentSX.main.split( ldd->ld.payFromAcct )).debcred += -repAmt
02370      *     c2:
02371      *   }
02372      * }
02373      * if ( fromSplit ) {
02374      *   fromSplit.debCred += (-repAmt);
02375      * } else {
02376      *   fromSplit = split( rep->fromAcct, -repAmt )
02377      *   paymentSX.main.splits += fromSplit
02378      * }
02379      * toTTI.splits += split( rep->toAcct, +repAmt );
02380      */
02381 
02384     GString *gstr;
02385     GList *elt;
02386     TTSplitInfo *fromSplit = NULL;
02387     TTSplitInfo *ttsi;
02388     TTInfo *toTxn = NULL;
02389     GNCPrintAmountInfo pricePAI = gnc_default_price_print_info();
02390 #define AMTBUF_LEN 64
02391     gchar amtBuf[AMTBUF_LEN];
02392     gint GNCN_HOW = (GNC_HOW_DENOM_SIGFIGS(2) | GNC_HOW_RND_ROUND_HALF_UP);
02393 
02394     /* We're going to use this a lot, below, so just create it once. */
02395     xaccSPrintAmount( amtBuf,
02396                       double_to_gnc_numeric( rod->amount, 100,
02397                               GNCN_HOW ),
02398                       pricePAI );
02399 
02400     if ( rod->throughEscrowP && ldd->ld.escrowAcct )
02401     {
02402         toTxn = tcSX->escrowTxn;
02403 
02404         /* Add the repayment amount into the string of the existing
02405          * ttsplit. */
02406         {
02407             elt = g_list_find_custom(
02408                       gnc_ttinfo_get_template_splits( paymentSX->mainTxn ),
02409                       ldd->ld.escrowAcct,
02410                       ld_find_ttsplit_with_acct );
02411             g_assert( elt );
02412             ttsi = (TTSplitInfo*)elt->data;
02413             g_assert( ttsi );
02414             gstr = g_string_new( gnc_ttsplitinfo_get_debit_formula( ttsi ) );
02415             g_string_append_printf( gstr, " + %s", amtBuf );
02416             gnc_ttsplitinfo_set_debit_formula( ttsi, gstr->str );
02417             g_string_free( gstr, TRUE );
02418             gstr = NULL;
02419             ttsi = NULL;
02420         }
02421 
02422         if ( rod->from != NULL )
02423         {
02424             gchar *str;
02425 
02426             fromSplit = NULL;
02427 
02428             /* tcSX.escrow.split( rep->escrow ).debCred += repAmt */
02429             elt = g_list_find_custom(
02430                       gnc_ttinfo_get_template_splits( tcSX->escrowTxn ),
02431                       ldd->ld.escrowAcct,
02432                       ld_find_ttsplit_with_acct );
02433             ttsi = NULL;
02434             if ( elt )
02435             {
02436                 ttsi = (TTSplitInfo*)elt->data;
02437             }
02438             if ( !ttsi )
02439             {
02440                 /* create split */
02441                 ttsi = gnc_ttsplitinfo_malloc();
02442                 gnc_ttsplitinfo_set_memo( ttsi, rod->txnMemo );
02443                 gnc_ttsplitinfo_set_account( ttsi, ldd->ld.escrowAcct );
02444                 gnc_ttinfo_append_template_split( tcSX->escrowTxn, ttsi );
02445             }
02446             if ( (str = (gchar*)gnc_ttsplitinfo_get_credit_formula( ttsi ))
02447                     == NULL )
02448             {
02449                 gstr = g_string_sized_new( 16 );
02450             }
02451             else
02452             {
02453                 /* If we did get a split/didn't have to
02454                  * create a split, then we need to add our
02455                  * amount in rather than replace. */
02456                 gstr = g_string_new( str );
02457                 g_string_append_printf( gstr, " + " );
02458             }
02459             g_string_append_printf( gstr, "%s", amtBuf );
02460             gnc_ttsplitinfo_set_credit_formula( ttsi, gstr->str );
02461             g_string_free( gstr, TRUE );
02462             gstr = NULL;
02463             ttsi = NULL;
02464         }
02465         else
02466         {
02467             /* (fromSplit = paymentSX.main.split( ldd->ld.repFromAcct )) */
02468             elt = g_list_find_custom(
02469                       gnc_ttinfo_get_template_splits( paymentSX->mainTxn ),
02470                       ldd->ld.repFromAcct,
02471                       ld_find_ttsplit_with_acct );
02472             g_assert( elt );
02473             fromSplit = (TTSplitInfo*)elt->data;
02474 
02475             /* tcSX.escrow.splits += split( rep->escrow, -repAmt ) */
02476             ttsi = gnc_ttsplitinfo_malloc();
02477             gnc_ttsplitinfo_set_memo( ttsi, rod->txnMemo );
02478             gnc_ttsplitinfo_set_account( ttsi, ldd->ld.escrowAcct );
02479             gnc_ttsplitinfo_set_credit_formula( ttsi, amtBuf );
02480             gnc_ttinfo_append_template_split( tcSX->escrowTxn, ttsi );
02481             ttsi = NULL;
02482         }
02483     }
02484     else
02485     {
02486         toTxn = tcSX->mainTxn;
02487 
02488         if ( rod->from != NULL )
02489         {
02490             fromSplit = NULL;
02491         }
02492         else
02493         {
02494             /* (fromSplit = paymentSX.main.split( ldd->ld.repFromAcct )) */
02495             elt = g_list_find_custom(
02496                       gnc_ttinfo_get_template_splits( tcSX->mainTxn ),
02497                       ldd->ld.repFromAcct,
02498                       ld_find_ttsplit_with_acct );
02499             fromSplit = NULL;
02500             if ( elt )
02501             {
02502                 /* This is conditionally true in the case of
02503                  * a repayment on it's own schedule. */
02504                 fromSplit = (TTSplitInfo*)elt->data;
02505             }
02506         }
02507     }
02508 
02509     if ( fromSplit != NULL )
02510     {
02511         /* Update the existing from-split. */
02512         gstr = g_string_new( gnc_ttsplitinfo_get_credit_formula( fromSplit ) );
02513         g_string_append_printf( gstr, " + %s", amtBuf );
02514         gnc_ttsplitinfo_set_credit_formula( fromSplit, gstr->str );
02515         g_string_free( gstr, TRUE );
02516         gstr = NULL;
02517 
02518     }
02519     else
02520     {
02521         TTInfo *tti;
02522         /* Create a new from-split. */
02523         ttsi = gnc_ttsplitinfo_malloc();
02524         gnc_ttsplitinfo_set_memo( ttsi, rod->txnMemo );
02525         if ( rod->from )
02526         {
02527             gnc_ttsplitinfo_set_account( ttsi, rod->from );
02528         }
02529         else
02530         {
02531             gnc_ttsplitinfo_set_account( ttsi, ldd->ld.repFromAcct );
02532         }
02533         gnc_ttsplitinfo_set_credit_formula( ttsi, amtBuf );
02534         tti = tcSX->mainTxn;
02535         if ( rod->throughEscrowP )
02536         {
02537             tti = paymentSX->mainTxn;
02538         }
02539         gnc_ttinfo_append_template_split( tti, ttsi );
02540         ttsi = NULL;
02541         tti  = NULL;
02542     }
02543 
02544     /* Add to-account split. */
02545     {
02546         ttsi = gnc_ttsplitinfo_malloc();
02547         gnc_ttsplitinfo_set_memo( ttsi, rod->txnMemo );
02548         gnc_ttsplitinfo_set_account( ttsi, rod->to );
02549         gnc_ttsplitinfo_set_debit_formula( ttsi, amtBuf );
02550         gnc_ttinfo_append_template_split( toTxn, ttsi );
02551         ttsi = NULL;
02552     }
02553 }
02554 
02569 static
02570 void
02571 ld_create_sxes( LoanDruidData *ldd )
02572 {
02573     /* The main loan-payment SX.*/
02574     toCreateSX *paymentSX = NULL;
02575     /* A GList of any other repayment SXes with different schedule. */
02576     GList *repaySXes = NULL;
02577     /* The currently-being-referenced toCreateSX. */
02578     toCreateSX *tcSX;
02579     int i;
02580     TTInfo *ttxn;
02581     TTSplitInfo *ttsi;
02582     GString *gstr;
02583 
02584     paymentSX = g_new0( toCreateSX, 1 );
02585     paymentSX->name  = g_strdup(ldd->ld.repMemo);
02586     paymentSX->start = *ldd->ld.startDate;
02587     paymentSX->last  = *ldd->ld.repStartDate;
02588     g_date_subtract_months( &paymentSX->last, 1 );
02589     {
02590         paymentSX->end = *ldd->ld.repStartDate;
02591         g_date_add_months( &paymentSX->end, ldd->ld.numMonRemain - 1);
02592     }
02593     paymentSX->schedule = ldd->ld.repayment_schedule;
02594     /* Figure out the correct current instance-count for the txns in the
02595      * SX. */
02596     paymentSX->instNum =
02597         (ldd->ld.numPer * ( ldd->ld.perSize == GNC_YEARS ? 12 : 1 ))
02598         - ldd->ld.numMonRemain + 1;
02599 
02600     paymentSX->mainTxn = gnc_ttinfo_malloc();
02601     gnc_ttinfo_set_currency( paymentSX->mainTxn,
02602                              gnc_default_currency() );
02603     {
02604         GString *payMainTxnDesc = g_string_sized_new( 32 );
02605         g_string_printf( payMainTxnDesc,
02606                          "%s - %s",
02607                          ldd->ld.repMemo,
02608                          ( ldd->ld.escrowAcct == NULL
02609                            ? _("Payment")
02610                            : _("Escrow Payment") )
02611                        );
02612 
02613         gnc_ttinfo_set_description( paymentSX->mainTxn,
02614                                     payMainTxnDesc->str );
02615         g_string_free( payMainTxnDesc, TRUE );
02616     }
02617 
02618     /* Create the basic txns and splits...
02619      *
02620      * ttxn <- mainTxn
02621      * srcAcct <- assets
02622      * if ( escrow ) {
02623      *  realSrcAcct <- srcAcct
02624      *  srcAcct     <- escrow;
02625      *  ttxn <- escrowTxn
02626      *  main.splits += split( realSrcAcct, -pmt )
02627      *  main.splits += split( escrow,       pmt )
02628      * }
02629      * ttxn.splits += split( escrow,            -pmt)
02630      * ttxn.splits += split( liability,          ppmt )
02631      * ttxn.splits += split( expenses:interest,  ipmt ) */
02632 
02633     {
02634         Account *srcAcct;
02635 
02636         ttxn = paymentSX->mainTxn;
02637         srcAcct = ldd->ld.repFromAcct;
02638 
02639         if ( ldd->ld.escrowAcct != NULL )
02640         {
02641             Account *realSrcAcct = srcAcct;
02642             srcAcct = ldd->ld.escrowAcct;
02643 
02644             gstr = g_string_sized_new( 32 );
02645             ld_get_pmt_formula( ldd, gstr );
02646             /* ttxn.splits += split( realSrcAcct, -pmt ); */
02647             {
02648                 ttsi = gnc_ttsplitinfo_malloc();
02649                 gnc_ttsplitinfo_set_memo( ttsi, ldd->ld.repMemo );
02650                 gnc_ttsplitinfo_set_account( ttsi, realSrcAcct );
02651                 gnc_ttsplitinfo_set_credit_formula( ttsi, gstr->str );
02652                 gnc_ttinfo_append_template_split( ttxn, ttsi );
02653                 ttsi = NULL;
02654             }
02655 
02656             /* ttxn.splits += split( escrowAcct, +pmt ); */
02657             {
02658                 ttsi = gnc_ttsplitinfo_malloc();
02659                 gnc_ttsplitinfo_set_memo( ttsi, ldd->ld.repMemo );
02660                 gnc_ttsplitinfo_set_account( ttsi, ldd->ld.escrowAcct );
02661                 gnc_ttsplitinfo_set_debit_formula( ttsi, gstr->str );
02662                 gnc_ttinfo_append_template_split( ttxn, ttsi );
02663                 ttsi = NULL;
02664             }
02665             g_string_free( gstr, TRUE );
02666             gstr = NULL;
02667 
02668             paymentSX->escrowTxn = gnc_ttinfo_malloc();
02669             gnc_ttinfo_set_currency( paymentSX->escrowTxn,
02670                                      gnc_default_currency() );
02671 
02672             {
02673                 GString *escrowTxnDesc;
02674                 escrowTxnDesc = g_string_new( ldd->ld.repMemo );
02675                 g_string_append_printf( escrowTxnDesc, " - %s", _("Payment") );
02676                 gnc_ttinfo_set_description( paymentSX->escrowTxn,
02677                                             escrowTxnDesc->str );
02678                 g_string_free( escrowTxnDesc, TRUE );
02679             }
02680             ttxn = paymentSX->escrowTxn;
02681         }
02682 
02683         /* ttxn.splits += split( srcAcct, -pmt ); */
02684         {
02685             ttsi = gnc_ttsplitinfo_malloc();
02686             {
02687                 gstr = g_string_new( ldd->ld.repMemo );
02688                 g_string_append_printf( gstr, " - %s",
02689                                         _("Payment") );
02690                 gnc_ttsplitinfo_set_memo( ttsi, gstr->str );
02691                 g_string_free( gstr, TRUE );
02692                 gstr = NULL;
02693             }
02694             gnc_ttsplitinfo_set_account( ttsi, srcAcct );
02695             gstr = g_string_sized_new( 32 );
02696             ld_get_pmt_formula( ldd, gstr );
02697             gnc_ttsplitinfo_set_credit_formula( ttsi, gstr->str );
02698             gnc_ttinfo_append_template_split( ttxn, ttsi );
02699             g_string_free( gstr, TRUE );
02700             gstr = NULL;
02701             ttsi = NULL;
02702         }
02703 
02704         /* ttxn.splits += split( ldd->ld.repPriAcct, +ppmt ); */
02705         {
02706             ttsi = gnc_ttsplitinfo_malloc();
02707             {
02708                 gstr = g_string_new( ldd->ld.repMemo );
02709                 g_string_append_printf( gstr, " - %s",
02710                                         _("Principal") );
02711                 gnc_ttsplitinfo_set_memo( ttsi, gstr->str );
02712                 g_string_free( gstr, TRUE );
02713                 gstr = NULL;
02714             }
02715             gnc_ttsplitinfo_set_account( ttsi, ldd->ld.repPriAcct );
02716             gstr = g_string_sized_new( 32 );
02717             ld_get_ppmt_formula( ldd, gstr );
02718             gnc_ttsplitinfo_set_debit_formula( ttsi, gstr->str );
02719             gnc_ttinfo_append_template_split( ttxn, ttsi );
02720             g_string_free( gstr, TRUE );
02721             gstr = NULL;
02722             ttsi = NULL;
02723         }
02724 
02725         /* ttxn.splits += split( ldd->ld.repIntAcct, +ipmt ); */
02726         {
02727             ttsi = gnc_ttsplitinfo_malloc();
02728             {
02729                 gstr = g_string_new( ldd->ld.repMemo );
02730                 g_string_append_printf( gstr, " - %s",
02731                                         _("Interest") );
02732                 gnc_ttsplitinfo_set_memo( ttsi, gstr->str );
02733                 g_string_free( gstr, TRUE );
02734                 gstr = NULL;
02735             }
02736             gnc_ttsplitinfo_set_account( ttsi, ldd->ld.repIntAcct );
02737             gstr = g_string_sized_new( 32 );
02738             ld_get_ipmt_formula( ldd, gstr );
02739             gnc_ttsplitinfo_set_debit_formula( ttsi, gstr->str );
02740             gnc_ttinfo_append_template_split( ttxn, ttsi );
02741             g_string_free( gstr, TRUE );
02742             gstr = NULL;
02743             ttsi = NULL;
02744         }
02745     }
02746 
02747     for ( i = 0; i < ldd->ld.repayOptCount; i++ )
02748     {
02749         RepayOptData *rod = ldd->ld.repayOpts[i];
02750         if ( ! rod->enabled )
02751             continue;
02752 
02753         tcSX = paymentSX;
02754         if ( rod->schedule != NULL )
02755         {
02756             tcSX = g_new0( toCreateSX, 1 );
02757             gstr = g_string_new( ldd->ld.repMemo );
02758             g_string_append_printf( gstr, " - %s",
02759                                     rod->name );
02760             tcSX->name    = g_strdup(gstr->str);
02761             tcSX->start   = *ldd->ld.startDate;
02762             tcSX->last    = *ldd->ld.repStartDate;
02763             {
02764                 tcSX->end = tcSX->last;
02765                 g_date_add_months( &tcSX->end, ldd->ld.numMonRemain );
02766             }
02767             tcSX->schedule = rod->schedule;
02768             /* So it won't get destroyed when the close the
02769              * Druid. */
02770             tcSX->instNum =
02771                 ld_calc_sx_instance_num(&tcSX->start, rod->schedule);
02772             rod->schedule = NULL;
02773             tcSX->mainTxn = gnc_ttinfo_malloc();
02774             gnc_ttinfo_set_currency( tcSX->mainTxn,
02775                                      gnc_default_currency() );
02776             gnc_ttinfo_set_description( tcSX->mainTxn,
02777                                         gstr->str );
02778             tcSX->escrowTxn = gnc_ttinfo_malloc();
02779             gnc_ttinfo_set_currency( tcSX->escrowTxn,
02780                                      gnc_default_currency() );
02781             gnc_ttinfo_set_description( tcSX->escrowTxn,
02782                                         gstr->str );
02783 
02784             g_string_free( gstr, TRUE );
02785             gstr = NULL;
02786 
02787             repaySXes = g_list_append( repaySXes, tcSX );
02788 
02789         }
02790 
02791         /* repayment */
02792         ld_setup_repayment_sx( ldd, rod, paymentSX, tcSX );
02793     }
02794 
02795     /* Create the SXes */
02796     {
02797         GList *l;
02798 
02799         ld_create_sx_from_tcSX( ldd, paymentSX );
02800 
02801         for ( l = repaySXes; l; l = l->next )
02802         {
02803             ld_create_sx_from_tcSX( ldd, (toCreateSX*)l->data );
02804         }
02805     }
02806 
02807     /* Clean up. */
02808     ld_tcSX_free( paymentSX, NULL );
02809     g_list_foreach( repaySXes, ld_tcSX_free, NULL );
02810     g_list_free( repaySXes );
02811 }
02812 
02813 static
02814 void
02815 ld_calc_upd_rem_payments( GtkWidget *w, gpointer ud )
02816 {
02817     LoanDruidData *ldd = (LoanDruidData*)ud;
02818     GDate start, now;
02819     int i, totalVal, total, remain;
02820 
02821     g_date_clear( &start, 1 );
02822     g_date_clear( &now, 1 );
02823     g_date_set_time_t( &start, gnc_date_edit_get_date( ldd->prmStartDateGDE ) );
02824     g_date_set_time_t( &now, time(NULL) );
02825     for ( i = 0; g_date_compare( &start, &now ) < 0; i++ )
02826     {
02827         g_date_add_months( &start, 1 );
02828     }
02829 
02830     /* Get the correct, current value of the length spin. */
02831     {
02832         gchar *valueStr = gtk_editable_get_chars( GTK_EDITABLE(ldd->prmLengthSpin),
02833                           0, -1 );
02834         totalVal = strtol( valueStr, NULL, 10 );
02835         g_free( valueStr );
02836     }
02837     total = totalVal
02838             * ( gtk_combo_box_get_active( ldd->prmLengthType )
02839                 == 1 ? 12 : 1 );
02840     remain = total - i;
02841     gtk_spin_button_set_value( ldd->prmRemainSpin, remain );
02842     gtk_widget_show( GTK_WIDGET(ldd->prmRemainSpin) );
02843 }
02844 
02845 static
02846 void
02847 ld_rev_range_opt_changed( GtkComboBox *combo, gpointer ud )
02848 {
02849     LoanDruidData *ldd = (LoanDruidData*)ud;
02850     int opt;
02851 
02852     opt = gtk_combo_box_get_active( ldd->revRangeOpt );
02853     gtk_widget_set_sensitive( GTK_WIDGET(ldd->revDateFrame),
02854                               (opt == CUSTOM) );
02855     {
02856         GDate start, end;
02857         g_date_clear( &start, 1 );
02858         g_date_clear( &end, 1 );
02859         ld_rev_get_dates( ldd, &start, &end );
02860         ld_rev_update_view( ldd, &start, &end );
02861     }
02862 }
02863 
02864 static
02865 void
02866 ld_rev_range_changed( GNCDateEdit *gde, gpointer ud )
02867 {
02868     LoanDruidData *ldd = (LoanDruidData*)ud;
02869     {
02870         GDate start, end;
02871         g_date_clear( &start, 1 );
02872         g_date_clear( &end, 1 );
02873         ld_rev_get_dates( ldd, &start, &end );
02874         ld_rev_update_view( ldd, &start, &end );
02875     }
02876 }
02877 
02878 static
02879 void
02880 ld_get_loan_range( LoanDruidData *ldd, GDate *start, GDate *end )
02881 {
02882     int monthsTotal;
02883     struct tm *endDateMath;
02884 
02885     *start = *ldd->ld.startDate;
02886 
02887     endDateMath = g_new0( struct tm, 1 );
02888     g_date_to_struct_tm( ldd->ld.startDate, endDateMath );
02889     monthsTotal = ( (ldd->ld.numPer - 1)
02890                     * ( ldd->ld.perSize == GNC_MONTHS ? 1 : 12 ) );
02891     endDateMath->tm_mon += monthsTotal;
02892     g_date_set_time_t( end, mktime( endDateMath ) );
02893     g_free( endDateMath );
02894 }
02895 
02896 static
02897 void
02898 ld_rev_get_dates( LoanDruidData *ldd, GDate *start, GDate *end )
02899 {
02900     int range = gtk_combo_box_get_active( ldd->revRangeOpt );
02901     switch ( range )
02902     {
02903     case CURRENT_YEAR:
02904         g_date_set_time_t( start, time(NULL) );
02905         g_date_set_dmy( start, 1, G_DATE_JANUARY, g_date_get_year( start ) );
02906         g_date_set_dmy( end, 31, G_DATE_DECEMBER, g_date_get_year( start ) );
02907         break;
02908     case NOW_PLUS_ONE:
02909         g_date_set_time_t( start, time(NULL) );
02910         *end = *start;
02911         g_date_add_years( end, 1 );
02912         break;
02913     case WHOLE_LOAN:
02914         ld_get_loan_range( ldd, start, end );
02915         break;
02916     case CUSTOM:
02917         g_date_set_time_t( start,
02918                            gnc_date_edit_get_date( ldd->revStartDate ) );
02919         g_date_set_time_t( end,
02920                            gnc_date_edit_get_date( ldd->revEndDate ) );
02921         break;
02922     default:
02923         PERR( "Unknown review date range option %d", range );
02924         break;
02925     }
02926 
02927 }
02928 
02929 static
02930 void
02931 ld_rev_sched_list_free( gpointer data, gpointer user_data )
02932 {
02933     RevRepaymentRow *rrr = (RevRepaymentRow*)data;
02934     g_free( rrr->numCells );
02935     g_free( rrr );
02936 }
02937 
02938 static
02939 void
02940 ld_rev_hash_to_list( gpointer key, gpointer val, gpointer user_data )
02941 {
02942     GList **l = (GList**)user_data;
02943     RevRepaymentRow *rrr = g_new0( RevRepaymentRow, 1 );
02944     if ( !key || !val )
02945     {
02946         DEBUG( "%.8x, %.8x",
02947                GPOINTER_TO_UINT(key),
02948                GPOINTER_TO_UINT(val));
02949         return;
02950     }
02951     rrr->date = *(GDate*)key;
02952     rrr->numCells = (gnc_numeric*)val;
02953     *l = g_list_append( *l, (gpointer)rrr );
02954 }
02955 
02956 static
02957 void
02958 ld_rev_hash_free_date_keys( gpointer key, gpointer val, gpointer user_data )
02959 {
02960     g_free( (GDate*)key );
02961 }
02962 
02963 static
02964 void
02965 ld_rev_recalc_schedule( LoanDruidData *ldd )
02966 {
02967     GDate start, end;
02968     gnc_numeric *rowNumData;
02969     GHashTable *repayment_schedule;
02970 
02971     g_date_clear( &start, 1 );
02972     g_date_clear( &end, 1 );
02973     ld_get_loan_range( ldd, &start, &end );
02974 
02975     /* The repayment_schedule is a hash of GDates to
02976      * row-of-gnc_numeric[N] data, where N is the number of columns as
02977      * determined by the _prep function, and stored in
02978      * LoanData::revNumPmts. */
02979     repayment_schedule = g_hash_table_new(gnc_gdate_hash, gnc_gdate_equal);
02980 
02981     /* Do the master repayment */
02982     {
02983         GDate curDate, nextDate;
02984         GString *pmtFormula, *ppmtFormula, *ipmtFormula;
02985         int i;
02986         GHashTable *ivar;
02987 
02988         pmtFormula = g_string_sized_new( 64 );
02989         ld_get_pmt_formula( ldd, pmtFormula );
02990         ppmtFormula = g_string_sized_new( 64 );
02991         ld_get_ppmt_formula( ldd, ppmtFormula );
02992         ipmtFormula = g_string_sized_new( 64 );
02993         ld_get_ipmt_formula( ldd, ipmtFormula );
02994 
02995         ivar = g_hash_table_new( g_str_hash, g_str_equal );
02996         g_date_clear( &curDate, 1 );
02997         curDate = start;
02998         g_date_subtract_days( &curDate, 1 );
02999         g_date_clear(&nextDate, 1);
03000         recurrenceListNextInstance(ldd->ld.repayment_schedule, &curDate, &nextDate);
03001         for ( i = 1;
03002                 g_date_valid( &nextDate )
03003                 && g_date_compare( &nextDate, &end ) <= 0 ;
03004                 i++,
03005                 curDate = nextDate,
03006                 recurrenceListNextInstance(ldd->ld.repayment_schedule,
03007                                            &curDate, &nextDate))
03008         {
03009             gnc_numeric ival;
03010             gnc_numeric val;
03011             char *eloc;
03012             rowNumData =
03013                 (gnc_numeric*)g_hash_table_lookup( repayment_schedule,
03014                                                    &curDate );
03015             if ( rowNumData == NULL)
03016             {
03017                 int j;
03018                 GDate *dateKeyCopy = g_date_new();
03019 
03020                 *dateKeyCopy = curDate;
03021                 rowNumData = g_new0( gnc_numeric, ldd->ld.revNumPmts );
03022                 g_assert( rowNumData != NULL );
03023                 for ( j = 0; j < ldd->ld.revNumPmts; j++ )
03024                 {
03025                     rowNumData[j] = gnc_numeric_error( GNC_ERROR_ARG );
03026                 }
03027                 g_hash_table_insert( repayment_schedule,
03028                                      (gpointer)dateKeyCopy,
03029                                      (gpointer)rowNumData );
03030             }
03031 
03032             /* evaluate the expressions given the correct
03033              * sequence number i */
03034             ival = gnc_numeric_create( i, 1 );
03035             g_hash_table_insert( ivar, "i", &ival );
03036 
03037             if ( ! gnc_exp_parser_parse_separate_vars(
03038                         pmtFormula->str, &val, &eloc, ivar ) )
03039             {
03040                 PERR( "pmt Parsing error at %s", eloc );
03041                 continue;
03042             }
03043             val = gnc_numeric_convert( val, 100, GNC_HOW_RND_ROUND_HALF_UP );
03044             rowNumData[0] = val;
03045 
03046             if ( ! gnc_exp_parser_parse_separate_vars(
03047                         ppmtFormula->str, &val, &eloc, ivar ) )
03048             {
03049                 PERR( "ppmt Parsing error at %s", eloc );
03050                 continue;
03051             }
03052             val = gnc_numeric_convert( val, 100, GNC_HOW_RND_ROUND_HALF_UP );
03053             rowNumData[1] = val;
03054 
03055             if ( ! gnc_exp_parser_parse_separate_vars(
03056                         ipmtFormula->str, &val, &eloc, ivar ) )
03057             {
03058                 PERR( "ipmt Parsing error at %s", eloc );
03059                 continue;
03060             }
03061             val = gnc_numeric_convert( val, 100, GNC_HOW_RND_ROUND_HALF_UP );
03062             rowNumData[2] = val;
03063         }
03064 
03065         g_string_free( ipmtFormula, TRUE );
03066         g_string_free( ppmtFormula, TRUE );
03067         g_string_free( pmtFormula, TRUE );
03068 
03069         g_hash_table_destroy( ivar );
03070     }
03071 
03072     /* Process any other enabled payments. */
03073     {
03074         int i;
03075         GDate curDate, nextDate;
03076         GList *schedule;
03077 
03078         for ( i = 0; i < ldd->ld.repayOptCount; i++ )
03079         {
03080             if ( ! ldd->ld.repayOpts[i]->enabled )
03081                 continue;
03082 
03083             schedule
03084             = ( ldd->ld.repayOpts[i]->schedule != NULL
03085                 ? ldd->ld.repayOpts[i]->schedule
03086                 : ldd->ld.repayment_schedule );
03087 
03088             g_date_clear( &curDate, 1 );
03089             curDate = start;
03090             g_date_subtract_days( &curDate, 1 );
03091             g_date_clear(&nextDate, 1);
03092             recurrenceListNextInstance(schedule, &curDate, &nextDate );
03093             for ( ; g_date_valid( &nextDate )
03094                     && g_date_compare( &nextDate, &end ) <= 0;
03095                     curDate = nextDate,
03096                     recurrenceListNextInstance(
03097                         schedule, &curDate, &nextDate ) )
03098             {
03099                 gint gncn_how =
03100                     GNC_HOW_DENOM_SIGFIGS(2)
03101                     | GNC_HOW_RND_ROUND_HALF_UP;
03102                 gnc_numeric val;
03103                 rowNumData = (gnc_numeric*)g_hash_table_lookup( repayment_schedule,
03104                              &curDate );
03105                 if ( rowNumData == NULL )
03106                 {
03107                     int j;
03108                     GDate *dateKeyCopy = g_date_new();
03109 
03110                     *dateKeyCopy = curDate;
03111                     rowNumData = g_new0( gnc_numeric, ldd->ld.revNumPmts );
03112                     g_assert( rowNumData != NULL );
03113                     for ( j = 0; j < ldd->ld.revNumPmts; j++ )
03114                     {
03115                         rowNumData[j] = gnc_numeric_error( GNC_ERROR_ARG );
03116                     }
03117                     g_hash_table_insert( repayment_schedule,
03118                                          (gpointer)dateKeyCopy,
03119                                          (gpointer)rowNumData );
03120                 }
03121 
03122                 val = double_to_gnc_numeric( (double)ldd->ld
03123                                              .repayOpts[i]
03124                                              ->amount,
03125                                              100, gncn_how );
03126                 rowNumData[ ldd->ld.revRepayOptToColMap[i] ]
03127                 = val;
03128             }
03129         }
03130     }
03131 
03132     /* Convert the GHashTable into a sorted GList in the LoanData */
03133     {
03134         if ( ldd->ld.revSchedule != NULL )
03135         {
03136             g_list_foreach( ldd->ld.revSchedule,
03137                             ld_rev_sched_list_free,
03138                             NULL );
03139             g_list_free( ldd->ld.revSchedule );
03140             ldd->ld.revSchedule = NULL;
03141         }
03142         g_hash_table_foreach( repayment_schedule, ld_rev_hash_to_list,
03143                               &ldd->ld.revSchedule );
03144         g_hash_table_foreach( repayment_schedule, ld_rev_hash_free_date_keys,
03145                               NULL );
03146         g_hash_table_destroy( repayment_schedule );
03147         ldd->ld.revSchedule =
03148             g_list_sort( ldd->ld.revSchedule, (GCompareFunc)g_date_compare );
03149     }
03150 }
03151 
03152 static
03153 void
03154 ld_rev_update_view( LoanDruidData *ldd, GDate *start, GDate *end )
03155 {
03156     static gchar *NO_AMT_CELL_TEXT = " ";
03157     GList *l;
03158     GNCPrintAmountInfo pai;
03159     GtkListStore *store;
03160     GtkTreeIter iter;
03161 
03162     pai = gnc_default_price_print_info();
03163     pai.min_decimal_places = 2;
03164 
03165     store = GTK_LIST_STORE(gtk_tree_view_get_model( ldd->revView ));
03166 
03167     gtk_list_store_clear( store );
03168 
03169     for ( l = ldd->ld.revSchedule; l != NULL; l = l->next )
03170     {
03171         int i;
03172         gchar tmpBuf[50];
03173         RevRepaymentRow *rrr = (RevRepaymentRow*)l->data;
03174 
03175         if ( g_date_compare( &rrr->date, start ) < 0 )
03176             continue;
03177         if ( g_date_compare( &rrr->date, end ) > 0 )
03178             continue; /* though we can probably return, too. */
03179 
03180         gtk_list_store_append(store, &iter);
03181 
03182         qof_print_gdate( tmpBuf, MAX_DATE_LENGTH, &rrr->date );
03183         gtk_list_store_set( store, &iter, LOAN_COL_DATE, tmpBuf, -1 );
03184 
03185         for ( i = 0; i < ldd->ld.revNumPmts; i++ )
03186         {
03187             int numPrinted;
03188             if ( gnc_numeric_check( rrr->numCells[i] )
03189                     == GNC_ERROR_ARG )
03190             {
03191                 /* '+1' for the date cell */
03192                 gtk_list_store_set( store, &iter,
03193                                     i + 1, NO_AMT_CELL_TEXT,
03194                                     -1);
03195                 continue;
03196             }
03197 
03198             numPrinted = xaccSPrintAmount( tmpBuf, rrr->numCells[i], pai );
03199             g_assert( numPrinted < 50 );
03200             /* '+1' for the date cell */
03201             gtk_list_store_set( store, &iter,
03202                                 i + 1, tmpBuf,
03203                                 -1);
03204         }
03205 
03206     }
03207 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines