|
GnuCash 2.3.0
|
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 }
1.7.4