From 4732ac577bf411112a18fb51f7a453c016f26c8b Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 1 Nov 1997 02:23:13 +0000 Subject: [PATCH] stock portfolio alpha-level fixes git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@17 57a11ea4-9604-0410-9ed3-97b8803252fd --- README | 21 +++++---- src/AccWindow.c | 2 + src/Account.c | 115 ++++++++++++++++++++++++++++++++++++++--------- src/MainWindow.c | 12 ++--- src/RegWindow.c | 87 ++++++++++++++++++++++------------- src/util.c | 7 +++ 6 files changed, 175 insertions(+), 69 deletions(-) diff --git a/README b/README index 2c7ae6844e..e68b98ee4e 100644 --- a/README +++ b/README @@ -39,16 +39,19 @@ sorry, no "make install" yet. Status: ------- -As of version 0.9f: +As of version 0.9g: --------------- -Mutual fund and Stock Portfolio handling -is implemented, but is in alpha state: many features are -missing, and others are broken. Transfers between funds and -bank accounts are not properly supported. Also, tabbing between -fields for mutual funds does not work correctly. One planned -addition is an indication of the type of transaction (Buy, -Cap Gain, Dividend Reinvest, etc.) But it is not clear when -this will be added. +Bare-bones Mutual Fund and Stock Portfolio handling +is implemented. +Working Features: +-- transfers between bank account & fund work +-- save to disk, restore from disk. + +Bugs: +-- tabbing between fields for mutual funds does not work correctly. + +Planned additions: +-- Indicate type of transaction (Buy, Cap Gain, Dividend Reinvest, etc.) -------- An alpha version of import from Quicken files is also present, diff --git a/src/AccWindow.c b/src/AccWindow.c index 9aef463e0e..e7a56027d0 100644 --- a/src/AccWindow.c +++ b/src/AccWindow.c @@ -594,6 +594,7 @@ createCB( Widget mw, XtPointer cd, XtPointer cb ) String name = XmTextGetString(accData->name); String desc = XmTextGetString(accData->desc); +#ifdef SHOULDNT_BE_BROKEN_ANYMORE { /* since portfolio & mutual not fully implemented, provide warning */ int warn = 0; @@ -615,6 +616,7 @@ Do you want to continue anyway?\n"); if (!do_it_anyway) return; } } +#endif /* SHOULDNT_BE_BROKEN_ANYMORE */ /* The account has to have a name! */ if( strcmp( name, "" ) == 0 ) { diff --git a/src/Account.c b/src/Account.c index 9a6c3000a7..7c26abcd36 100644 --- a/src/Account.c +++ b/src/Account.c @@ -275,6 +275,18 @@ insertTransaction( Account *acc, Transaction *trans ) \********************************************************************/ double xaccGetAmount (Account *acc, Transaction *trans) +{ + double themount; /* amount */ + + themount = xaccGetShareAmount (acc, trans); + themount *= trans->share_price; + return themount; +} + +/********************************************************************\ +\********************************************************************/ + +double xaccGetShareAmount (Account *acc, Transaction *trans) { double themount; /* amount */ if (NULL == trans) return 0.0; @@ -287,7 +299,7 @@ double xaccGetAmount (Account *acc, Transaction *trans) if ( trans->debit == ((struct _account *) acc) ) { themount = - (trans->damount); } else { - printf ("Internal Error: xaccGetAmount: missing double entry \n"); + printf ("Internal Error: xaccGetShareAmount: missing double entry \n"); printf ("this error should not occur. Please report the problem. \n"); themount = 0.0; /* punt */ } @@ -297,7 +309,7 @@ double xaccGetAmount (Account *acc, Transaction *trans) /********************************************************************\ \********************************************************************/ -void xaccSetAmount (Account *acc, Transaction *trans, double themount) +void xaccSetShareAmount (Account *acc, Transaction *trans, double themount) { /* for a double-entry, determine if this is a credit or a debit */ if ( trans->credit == ((struct _account *) acc) ) { @@ -315,6 +327,17 @@ void xaccSetAmount (Account *acc, Transaction *trans, double themount) /********************************************************************\ \********************************************************************/ +void xaccSetAmount (Account *acc, Transaction *trans, double themount) +{ + if (0.0 < trans->share_price) { + themount /= trans->share_price; + xaccSetShareAmount (acc, trans, themount); + } +} + +/********************************************************************\ +\********************************************************************/ + double xaccGetBalance (Account *acc, Transaction *trans) { double themount; /* amount */ @@ -379,11 +402,25 @@ double xaccGetShareBalance (Account *acc, Transaction *trans) * xaccRecomputeBalance * * recomputes the partial balances and the current balance for * * this account. * - -The current balance is always the current share balance times the -current share price. The share price for bank accounts is always 1.0, -so, for bank acccounts, the current balance is always equal to the current -share balance. + * + * The way the computation is done depends on whether the partial + * balances are for a monetary account (bank, cash, etc.) or a + * certificate account (stock portfolio, mutual fund). For bank + * accounts, the invarient amount is the dollar amount. For share + * accounts, the invarient amount is the number of shares. For + * share accounts, the share price fluctuates, and the current + * value of such an account is the number of shares times the current + * share price. + * + * Part of the complexity of this computatation stems from the fact + * xacc uses a double-entry system, meaning that one transaction + * appears in two accounts: one account is debited, and the other + * is credited. When the transaction represents a sale of shares, + * or a purchase of shares, some care must be taken to compute + * balances correctly. For a sale of shares, the stock account must + * be debited in shares, but the bank account must be credited + * in dollars. Thus, two different mechanisms must be used to + * compute balances, depending on account type. * * * Args: account -- the account for which to recompute balances * * Return: void * @@ -394,33 +431,67 @@ xaccRecomputeBalance( Account * acc ) int i; double dbalance = 0.0; double dcleared_balance = 0.0; - Transaction *trans; + double share_balance = 0.0; + double share_cleared_balance = 0.0; + double amt = 0.0; + Transaction *trans, *last_trans; + Account *tracc; if( NULL == acc ) return; for( i=0; (trans=getTransaction(acc,i)) != NULL; i++ ) { - dbalance += xaccGetAmount (acc, trans); + + /* compute both dollar and share balances */ + amt = xaccGetShareAmount (acc, trans); + share_balance += amt; + dbalance += amt * (trans->share_price); if( trans->reconciled != NREC ) { - dcleared_balance += xaccGetAmount (acc, trans); + share_cleared_balance += amt; + dcleared_balance += amt * (trans->share_price); } - if (acc == (Account *) trans->credit) { - trans -> credit_share_balance = dbalance; - trans -> credit_share_cleared_balance = dcleared_balance; - trans -> credit_balance = trans->share_price * dbalance; - trans -> credit_cleared_balance = trans->share_price * dcleared_balance; + tracc = (Account *) trans->credit; + if (tracc == acc) { + /* For bank accounts, the invarient subtotal is the dollar + * amount. For stock accoounts, the invarient is the share amount */ + if ( (PORTFOLIO == tracc->type) || ( MUTUAL == tracc->type) ) { + trans -> credit_share_balance = share_balance; + trans -> credit_share_cleared_balance = share_cleared_balance; + trans -> credit_balance = trans->share_price * share_balance; + trans -> credit_cleared_balance = trans->share_price * share_cleared_balance; + } else { + trans -> credit_share_balance = dbalance; + trans -> credit_share_cleared_balance = dcleared_balance; + trans -> credit_balance = dbalance; + trans -> credit_cleared_balance = dcleared_balance; + } } - if (acc == (Account *) trans->debit) { - trans -> debit_share_balance = dbalance; - trans -> debit_share_cleared_balance = dcleared_balance; - trans -> debit_balance = trans->share_price * dbalance; - trans -> debit_cleared_balance = trans->share_price * dcleared_balance; + tracc = (Account *) trans->debit; + if (tracc == acc) { + if ( (PORTFOLIO == tracc->type) || ( MUTUAL == tracc->type) ) { + trans -> debit_share_balance = share_balance; + trans -> debit_share_cleared_balance = share_cleared_balance; + trans -> debit_balance = trans->share_price * share_balance; + trans -> debit_cleared_balance = trans->share_price * share_cleared_balance; + } else { + trans -> debit_share_balance = dbalance; + trans -> debit_share_cleared_balance = dcleared_balance; + trans -> debit_balance = dbalance; + trans -> debit_cleared_balance = dcleared_balance; + } } + + last_trans = trans; } - acc -> balance = dbalance; - acc -> cleared_balance = dcleared_balance; + if ( (PORTFOLIO == acc->type) || ( MUTUAL == acc->type) ) { + acc -> balance = share_balance * (last_trans->share_price); + acc -> cleared_balance = share_cleared_balance * (last_trans->share_price); + } else { + acc -> balance = dbalance; + acc -> cleared_balance = dcleared_balance; + } return; } diff --git a/src/MainWindow.c b/src/MainWindow.c index 4e5adfb39d..52f7644ccd 100644 --- a/src/MainWindow.c +++ b/src/MainWindow.c @@ -79,7 +79,7 @@ Boolean havePixels = False; void refreshMainWindow( void ) { - int i,j,nrows; + int i,nrows; char buf[BUFSIZE]; XtVaGetValues( accountlist, XmNrows, &nrows, NULL ); @@ -91,14 +91,10 @@ refreshMainWindow( void ) String rows[3]; Transaction *trans=NULL; Account *acc = getAccount( data, i ); - double dbalance = 0.0; - double share_balance = 0.0; + double dbalance; - j=0; - while( (trans = getTransaction(acc,j++)) != NULL ) { - share_balance += xaccGetAmount (acc, trans); - dbalance = share_balance * trans->share_price; - } + xaccRecomputeBalance (acc); + dbalance = acc->balance; if( 0.0 > dbalance ) sprintf( buf,"-$%.2f\0", DABS(dbalance) ); diff --git a/src/RegWindow.c b/src/RegWindow.c index 92aedab72f..7cdc5ca23e 100644 --- a/src/RegWindow.c +++ b/src/RegWindow.c @@ -238,20 +238,47 @@ regRefresh( RegWindow *regData ) newData[row+1][RECN_CELL_C] = XtNewString(""); - themount = xaccGetAmount (acc, trans); - - /* hack alert -- share amounts should have three digits */ - if( 0.0 > themount ) + /* ----------------------------------- */ + /* display depends on account type */ + switch(acc->type) { - sprintf( buf, "%.2f ", -themount ); - newData[row][PAY_CELL_C] = XtNewString(buf); - newData[row][DEP_CELL_C] = XtNewString(""); - } - else - { - sprintf( buf, "%.2f ", themount ); - newData[row][PAY_CELL_C] = XtNewString(""); - newData[row][DEP_CELL_C] = XtNewString(buf); + case BANK: + case CASH: + case ASSET: + case CREDIT: + case LIABILITY: + themount = xaccGetAmount (acc, trans); + if( 0.0 > themount ) + { + sprintf( buf, "%.2f ", -themount ); + newData[row][PAY_CELL_C] = XtNewString(buf); + newData[row][DEP_CELL_C] = XtNewString(""); + } + else + { + sprintf( buf, "%.2f ", themount ); + newData[row][PAY_CELL_C] = XtNewString(""); + newData[row][DEP_CELL_C] = XtNewString(buf); + } + break; + case PORTFOLIO: + case MUTUAL: + themount = xaccGetShareAmount (acc, trans); + if( 0.0 > themount ) + { + sprintf( buf, "%.3f ", -themount ); + newData[row][PAY_CELL_C] = XtNewString(buf); + newData[row][DEP_CELL_C] = XtNewString(""); + } + else + { + sprintf( buf, "%.3f ", themount ); + newData[row][PAY_CELL_C] = XtNewString(""); + newData[row][DEP_CELL_C] = XtNewString(buf); + } + break; + default: + fprintf( stderr, "Ineternal Error: Account type: %d is unknown!\n", acc->type); } newData[row+1][PAY_CELL_C] = XtNewString(""); @@ -277,13 +304,11 @@ regRefresh( RegWindow *regData ) newData[row+1][PRIC_CELL_C] = XtNewString(""); newData[row][SHRS_CELL_C] = XtNewString(""); newData[row+1][SHRS_CELL_C] = XtNewString(""); -/* - newData[row][ACTN_CELL_C] = XtNewString(""); - newData[row+1][ACTN_CELL_C] = XtNewString(""); -*/ + /* newData[row][ACTN_CELL_C] = XtNewString(""); */ + /* newData[row+1][ACTN_CELL_C] = XtNewString(""); */ break; default: - fprintf( stderr, "Ineternal Error: Account type: %d is unknown!\n", regData->acc->type); + fprintf( stderr, "Ineternal Error: Account type: %d is unknown!\n", acc->type); } } @@ -384,9 +409,9 @@ regRecalculateBalance( RegWindow *regData ) (PORTFOLIO == acc->type) ) { #ifdef USE_NO_COLOR - sprintf( buf, "%.2f ", share_balance ); + sprintf( buf, "%.3f ", share_balance ); #else - sprintf( buf, "%.2f ", DABS(share_balance) ); + sprintf( buf, "%.3f ", DABS(share_balance) ); /* Set the color of the text, depending on whether the * balance is negative or positive */ @@ -552,7 +577,7 @@ regSaveTransaction( RegWindow *regData, int position ) sscanf( amount, "%f", &val ); themount -= val; - xaccSetAmount (acc, trans, themount); + xaccSetShareAmount (acc, trans, themount); /* Reset so there is only one field filled */ if( 0.0 > themount ) @@ -897,7 +922,7 @@ regWindow( Widget parent, Account *acc ) case MUTUAL: acc -> colWidths[PRIC_CELL_C] = 8; /* price */ acc -> colWidths[SHRS_CELL_C] = 8; /* share balance */ - /* acc -> colWidths[ACTN_CELL_C] = 6; /* action (category) */ + /* acc -> colWidths[ACTN_CELL_C] = 6; /* action (Buy/Sell)*/ break; } @@ -953,7 +978,7 @@ regWindow( Widget parent, Account *acc ) case MUTUAL: acc -> rows[0][PRIC_CELL_C] = "Price"; acc -> rows[0][SHRS_CELL_C] = "Tot Shrs"; - /* acc -> rows[0][ACTN_CELL_C] = "Cat"; /* action */ + /* acc -> rows[0][ACTN_CELL_C] = "Action"; /* action */ break; } @@ -1251,22 +1276,24 @@ deleteCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; Account *acc = regData->acc; + Transaction *trans; - if( getTransaction(acc,regData->lastTrans) != NULL ) + trans = getTransaction (acc, regData->lastTrans ); + if( NULL != trans) { char *msg = "Are you sure you want\nto delete this transaction?"; if( verifyBox( toplevel, msg ) ) { - Transaction *trans; - trans = getTransaction (acc, regData->lastTrans ); + Account * cred = (Account *) (trans->credit); + Account * deb = (Account *) (trans->debit); /* remove the transaction from both accounts */ - REMOVE_TRANS ((trans->credit), trans); - REMOVE_TRANS ((trans->debit), trans); + REMOVE_TRANS (cred, trans); + REMOVE_TRANS (deb, trans); - RECALC_BALANCE ((trans->debit)); - RECALC_BALANCE ((trans->credit)); + RECALC_BALANCE (deb); + RECALC_BALANCE (cred); /* Delete the transaction */ freeTransaction (trans); diff --git a/src/util.c b/src/util.c index 11ad1a0530..40da227fe3 100644 --- a/src/util.c +++ b/src/util.c @@ -108,6 +108,13 @@ dateCB( Widget mw, XtPointer cd, XtPointer cb ) cbs->doit = False; } break; + + case 0x0: + /* if delete key (for example) is hit, then input string */ + /* will be an empty string. In such a case, allow the input */ + cbs->doit = True; + break; + default: /* only accept the input if it is a number */ cbs->doit = isNum(input);