diff --git a/hledger-lib/Hledger/Data/Account.hs b/hledger-lib/Hledger/Data/Account.hs index 1f4813b21..d766ed8e2 100644 --- a/hledger-lib/Hledger/Data/Account.hs +++ b/hledger-lib/Hledger/Data/Account.hs @@ -48,13 +48,20 @@ nullacct = Account , asubs = [] , anumpostings = 0 , aebalance = nullmixedamt + , aestartbalance = nullmixedamt + , aeperiodchange = nullmixedamt + , aeendbalance = nullmixedamt , aibalance = nullmixedamt + , aistartbalance = nullmixedamt + , aiperiodchange = nullmixedamt + , aiendbalance = nullmixedamt , aboring = False } --- | Derive 1. an account tree and 2. their balances from a list of postings. +-- | Derive 1. an account tree and 2. their total changes from a list of postings. -- (ledger's core feature). The accounts are returned in a list, but --- retain their tree structure; the first one is the root of the tree. +-- also reference each other as a tree structure; the first account is +-- the root of the tree. accountsFromPostings :: [Posting] -> [Account] accountsFromPostings ps = let diff --git a/hledger-lib/Hledger/Data/Ledger.hs b/hledger-lib/Hledger/Data/Ledger.hs index 7b275232b..96a44639c 100644 --- a/hledger-lib/Hledger/Data/Ledger.hs +++ b/hledger-lib/Hledger/Data/Ledger.hs @@ -18,9 +18,11 @@ import Text.Printf import Hledger.Data.Types import Hledger.Data.Account +import Hledger.Data.Dates import Hledger.Data.Journal import Hledger.Data.Posting import Hledger.Query +import Hledger.Utils.Debug instance Show Ledger where @@ -39,16 +41,33 @@ nullledger = Ledger { -- | Filter a journal's transactions with the given query, then derive -- a ledger containing the chart of accounts and balances. If the --- query includes a depth limit, that will affect the this ledger's +-- query includes a depth limit, that will affect the ledger's -- journal but not the ledger's account tree. ledgerFromJournal :: Query -> Journal -> Ledger -ledgerFromJournal q j = nullledger{ljournal=j'', laccounts=as} +ledgerFromJournal q j = nullledger{ljournal=depthclippedperiodj, laccounts=as} where - (q',depthq) = (filterQuery (not . queryIsDepth) q, filterQuery queryIsDepth q) - j' = filterJournalAmounts (filterQuery queryIsSym q) $ -- remove amount parts which the query's sym: terms would exclude - filterJournalPostings q' j - as = accountsFromPostings $ journalPostings j' - j'' = filterJournalPostings depthq j' + symq = filterQuery queryIsSym q + depthq = filterQuery queryIsDepth q + periodq = filterQuery (not . queryIsDepth) q + -- get account totals before and during report period + datelessq = filterQuery (not . queryIsDateOrDate2) periodq + dateqcons = Date -- if date2_ opts then Date2 else Date -- import cycle, don't bother supporting date2 + reportspan = queryDateSpan False {-(date2_ opts)-} q -- date span specified by -b/-e/-p options and query args + mstartdate = dbg1 "mstartdate" $ spanStart reportspan + menddate = dbg1 "menddate" $ spanEnd reportspan + precedingq = dbg1 "precedingq" $ + case mstartdate of + Just _ -> And [datelessq, dateqcons $ DateSpan Nothing mstartdate] + Nothing -> None + + -- remove amount parts which the query's sym: terms would exclude + precedingj = filterJournalAmounts symq $ filterJournalPostings precedingq j + periodj = filterJournalAmounts symq $ filterJournalPostings periodq j + precedingas = accountsFromPostings $ journalPostings precedingj + as = accountsFromPostings $ journalPostings periodj + depthclippedperiodj = filterJournalPostings depthq periodj + +-- j' = journalSelectingAmountFromOpts opts j -- | List a ledger's account names. ledgerAccountNames :: Ledger -> [AccountName] diff --git a/hledger-lib/Hledger/Data/Types.hs b/hledger-lib/Hledger/Data/Types.hs index a8f78dca9..5a83085ff 100644 --- a/hledger-lib/Hledger/Data/Types.hs +++ b/hledger-lib/Hledger/Data/Types.hs @@ -281,8 +281,8 @@ instance NFData MarketPrice -- data Journal = Journal { -- parsing-related data - jparsedefaultyear :: (Maybe Year) -- ^ the current default year, specified by the most recent Y directive (or current date) - ,jparsedefaultcommodity :: (Maybe (CommoditySymbol,AmountStyle)) -- ^ the current default commodity and its format, specified by the most recent D directive + jparsedefaultyear :: Maybe Year -- ^ the current default year, specified by the most recent Y directive (or current date) + ,jparsedefaultcommodity :: Maybe (CommoditySymbol,AmountStyle) -- ^ the current default commodity and its format, specified by the most recent D directive ,jparseparentaccounts :: [AccountName] -- ^ the current stack of parent account names, specified by apply account directives ,jparsealiases :: [AccountAlias] -- ^ the current account name aliases in effect, specified by alias directives (& options ?) ,jparsetransactioncount :: Integer -- ^ the current count of transactions parsed so far (only journal format txns, currently) @@ -329,15 +329,23 @@ data Reader = Reader { instance Show Reader where show r = rFormat r ++ " reader" --- | An account, with name, balances and links to parent/subaccounts --- which let you walk up or down the account tree. +-- | An account, with name, links to parent/subaccounts +-- which let you walk up or down the account tree, and +-- and start/end balances and balance change amount relative to +-- some report period. data Account = Account { aname :: AccountName, -- ^ this account's full name aebalance :: MixedAmount, -- ^ this account's balance, excluding subaccounts + aestartbalance :: MixedAmount, -- ^ this account's balance at start of report period, excluding subaccounts + aeperiodchange :: MixedAmount, -- ^ the change in this account's balance during the report period, excluding subaccounts + aeendbalance :: MixedAmount, -- ^ this account's balance at end of report period, excluding subaccounts asubs :: [Account], -- ^ sub-accounts anumpostings :: Int, -- ^ number of postings to this account -- derived from the above : aibalance :: MixedAmount, -- ^ this account's balance, including subaccounts + aistartbalance :: MixedAmount, -- ^ this account's balance at start of report period, excluding subaccounts + aiperiodchange :: MixedAmount, -- ^ the change in this account's balance during the report period, excluding subaccounts + aiendbalance :: MixedAmount, -- ^ this account's balance at end of report period, excluding subaccounts aparent :: Maybe Account, -- ^ parent account aboring :: Bool -- ^ used in the accounts report to label elidable parents } deriving (Typeable, Data, Generic) diff --git a/hledger-lib/Hledger/Reports/BalanceHistoryReport.hs b/hledger-lib/Hledger/Reports/BalanceHistoryReport.hs index c9719e045..bbd7eb053 100644 --- a/hledger-lib/Hledger/Reports/BalanceHistoryReport.hs +++ b/hledger-lib/Hledger/Reports/BalanceHistoryReport.hs @@ -22,7 +22,7 @@ import Hledger.Reports.ReportOptions import Hledger.Reports.TransactionsReports --- | Get the historical running inclusive balance of a particular account, +-- | Get the historical inclusive balance of a particular account over time, -- from earliest to latest posting date. accountBalanceHistory :: ReportOpts -> Journal -> Account -> [(Day, MixedAmount)] accountBalanceHistory ropts j a = [(getdate t, bal) | (t,_,_,_,_,bal) <- items] diff --git a/hledger-lib/Hledger/Reports/BalanceReport.hs b/hledger-lib/Hledger/Reports/BalanceReport.hs index 05cec8cd7..d3b2ce87a 100644 --- a/hledger-lib/Hledger/Reports/BalanceReport.hs +++ b/hledger-lib/Hledger/Reports/BalanceReport.hs @@ -5,6 +5,13 @@ Balance report, used by the balance command. -} + + + + + + + module Hledger.Reports.BalanceReport ( BalanceReport, BalanceReportItem, @@ -31,6 +38,12 @@ import Hledger.Utils import Hledger.Reports.ReportOptions + + + + + + -- | A simple single-column balance report. It has: -- -- 1. a list of rows, each containing a renderable account name and a corresponding amount @@ -141,8 +154,8 @@ mixedAmountValue :: Journal -> Day -> MixedAmount -> MixedAmount mixedAmountValue j d (Mixed as) = Mixed $ map (amountValue j d) as -- | Find the market value of this amount on the given date, in it's --- default valuation commodity, based on historical prices. If no --- default valuation commodity can be found, the amount is left +-- default valuation commodity, based on recorded market prices. +-- If no default valuation commodity can be found, the amount is left -- unchanged. amountValue :: Journal -> Day -> Amount -> Amount amountValue j d a = @@ -155,7 +168,7 @@ amountValue j d a = -- | Find the market value, if known, of one unit of this commodity on -- the given date, in the commodity in which it has most recently been -- market-priced (ie the commodity mentioned in the most recent --- applicable historical price directive before this date). +-- applicable market price directive before this date). commodityValue :: Journal -> Day -> CommoditySymbol -> Maybe Amount commodityValue j d c | null applicableprices = Nothing @@ -163,6 +176,13 @@ commodityValue j d c where applicableprices = [p | p <- sort $ jmarketprices j, mpcommodity p == c, mpdate p <= d] + + + + + + + tests_balanceReport = let (opts,journal) `gives` r = do diff --git a/hledger/Hledger/Cli/Add.hs b/hledger/Hledger/Cli/Add.hs index c7cad5d3c..fc342e9ac 100644 --- a/hledger/Hledger/Cli/Add.hs +++ b/hledger/Hledger/Cli/Add.hs @@ -260,6 +260,7 @@ accountWizard EntryState{..} = do | otherwise = Just t dbg1 = id -- strace +amountAndCommentWizard :: EntryState -> Wizard Haskeline (Amount, Text) amountAndCommentWizard EntryState{..} = do let pnum = length esPostings + 1 (mhistoricalp,followedhistoricalsofar) = @@ -270,7 +271,7 @@ amountAndCommentWizard EntryState{..} = do def = case (esArgs, mhistoricalp, followedhistoricalsofar) of (d:_,_,_) -> d (_,Just hp,True) -> showamt $ pamount hp - _ | pnum > 1 && not (isZeroMixedAmount balancingamt) -> showamt balancingamt + _ | pnum > 1 && not (isZeroMixedAmount balancingamt) -> showamt balancingamtfirstcommodity _ -> "" retryMsg "A valid hledger amount is required. Eg: 1, $2, 3 EUR, \"4 red apples\"." $ parser parseAmountAndComment $ @@ -293,7 +294,9 @@ amountAndCommentWizard EntryState{..} = do -- eof return (a,c) balancingamt = negate $ sum $ map pamount realps where realps = filter isReal esPostings - showamt = showMixedAmountWithPrecision + balancingamtfirstcommodity = Mixed $ take 1 $ amounts balancingamt + showamt = + showMixedAmountWithPrecision -- what should this be ? -- 1 maxprecision (show all decimal places or none) ? -- 2 maxprecisionwithpoint (show all decimal places or .0 - avoids some but not all confusion with thousands separators) ?