From 52ff46a326e0edee5fbd5eb0ac7822a3b178ace4 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Sat, 22 Nov 2008 20:35:17 +0000 Subject: [PATCH] --basis/-B flag, to show all priced amounts on cost basis. Also a --cost alias. --- Ledger/RawLedger.hs | 40 ++++++++++++++++++++++------------------ NOTES | 1 - Options.hs | 2 ++ README | 10 +++++++--- Tests.hs | 19 ++++++++++++++++++- hledger.hs | 3 ++- 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Ledger/RawLedger.hs b/Ledger/RawLedger.hs index d7e77801d..ffcb0f42d 100644 --- a/Ledger/RawLedger.hs +++ b/Ledger/RawLedger.hs @@ -83,35 +83,39 @@ filterRawLedgerTransactionsByRealness True (RawLedger ms ps es f) = -- | Give all a ledger's amounts their canonical display settings. That -- is, in each commodity, amounts will use the display settings of the -- first amount detected, and the greatest precision of the amounts --- detected. -canonicaliseAmounts :: RawLedger -> RawLedger -canonicaliseAmounts l@(RawLedger ms ps es f) = RawLedger ms ps (map fixEntryAmounts es) f +-- detected. Also, amounts are converted to cost basis if that flag is +-- active. +canonicaliseAmounts :: Bool -> RawLedger -> RawLedger +canonicaliseAmounts costbasis l@(RawLedger ms ps es f) = RawLedger ms ps (map fixEntryAmounts es) f where fixEntryAmounts (Entry d s c de co ts pr) = Entry d s c de co (map fixRawTransactionAmounts ts) pr fixRawTransactionAmounts (RawTransaction ac a c t) = RawTransaction ac (fixMixedAmount a) c t fixMixedAmount (Mixed as) = Mixed $ map fixAmount as - fixAmount (Amount c q pri) = Amount (canonicalcommodity c) q pri - canonicalcommodity c@(Commodity {symbol=s}) = - (firstoccurrenceof c){precision=maximum $ map precision $ commoditieswithsymbol s} - firstoccurrenceof Commodity{symbol=s} = head $ commoditieswithsymbol s - -- Get ledger's amounts' commodities with a given symbol, in the order parsed. - -- Call with a good symbol or it will fail. - commoditieswithsymbol :: String -> [Commodity] - commoditieswithsymbol s = fromMaybe (error $ "no such commodity "++s) (Map.lookup s commoditiesmap) + fixAmount | costbasis = fixcommodity . costOfAmount + | otherwise = fixcommodity + fixcommodity a = a{commodity=canonicalcommodity $ commodity a} + canonicalcommodity c = (firstoccurrenceof c){precision=maxprecision c} where - commoditiesmap :: Map.Map String [Commodity] - commoditiesmap = Map.fromList [(symbol $ head cs,cs) | - cs <- groupBy samesymbol $ rawLedgerCommodities l] - samesymbol c1 c2 = symbol c1 == symbol c2 + firstoccurrenceof c = head $ rawLedgerCommoditiesWithSymbol l (symbol c) + maxprecision c = maximum $ map precision $ rawLedgerCommoditiesWithSymbol l (symbol c) --- | Get just the amounts from a ledger, in the order parsed. -rawLedgerAmounts :: RawLedger -> [MixedAmount] -rawLedgerAmounts = map amount . rawLedgerTransactions +-- | Get all amount commodities with a given symbol, in the order parsed. +-- Must be called with a good symbol or it will fail. +rawLedgerCommoditiesWithSymbol :: RawLedger -> String -> [Commodity] +rawLedgerCommoditiesWithSymbol l s = + fromMaybe (error $ "no such commodity "++s) (Map.lookup s map) + where + map = Map.fromList [(symbol $ head cs,cs) | cs <- groupBy same $ rawLedgerCommodities l] + same c1 c2 = symbol c1 == symbol c2 -- | Get just the ammount commodities from a ledger, in the order parsed. rawLedgerCommodities :: RawLedger -> [Commodity] rawLedgerCommodities = map commodity . concatMap amounts . rawLedgerAmounts +-- | Get just the amounts from a ledger, in the order parsed. +rawLedgerAmounts :: RawLedger -> [MixedAmount] +rawLedgerAmounts = map amount . rawLedgerTransactions + -- | Get just the amount precisions from a ledger, in the order parsed. rawLedgerPrecisions :: RawLedger -> [Int] rawLedgerPrecisions = map precision . rawLedgerCommodities diff --git a/NOTES b/NOTES index 7473fa60b..95d30478d 100644 --- a/NOTES +++ b/NOTES @@ -15,7 +15,6 @@ implementations were its consequences." --Niklaus Wirth *** flexible date expressions, for easier time reports **** more formats **** periods -*** commodity @ rate, for tracking client hours in main ledger *** actual/effective entry & txn dates, for ? *** --display, for reconciling recent transactions with real balance *** more ledger features from README diff --git a/Options.hs b/Options.hs index 047e8c2f6..cb38e939a 100644 --- a/Options.hs +++ b/Options.hs @@ -29,6 +29,7 @@ options = [ Option ['b'] ["begin"] (ReqArg Begin "YYYY/MM/DD") "report on entries on or after this date", Option ['e'] ["end"] (ReqArg End "YYYY/MM/DD") "report on entries prior to this date", Option ['C'] ["cleared"] (NoArg Cleared) "report only on cleared entries", + Option ['B'] ["cost","basis"] (NoArg CostBasis) "report cost basis of commodities", Option [] ["depth"] (ReqArg Depth "N") "balance report: maximum account depth to show", Option ['E'] ["empty"] (NoArg Empty) "balance report: show accounts with zero balance", Option ['R'] ["real"] (NoArg Real) "report only on real (non-virtual) transactions", @@ -44,6 +45,7 @@ data Opt = Begin String | End String | Cleared | + CostBasis | Depth String | Empty | Real | diff --git a/README b/README index 0f2086732..9509ffc6e 100644 --- a/README +++ b/README @@ -73,14 +73,18 @@ This version of hledger mimics a subset of ledger 2.6.1: -E, --empty balance report: show accounts with zero balance -n, --collapse balance report: no grand total + Commodity reporting: + -B, --basis, --cost report cost basis of commodities + Commands: balance [REGEXP]... show balance totals for matching accounts register [REGEXP]... show register of matching transactions print [REGEXP]... print all matching entries -hledger-only features: +hledger-specific features: -- --depth=N balance report: maximum account depth to show + --depth=N balance report: maximum account depth to show + --cost alias for basis ledger features not supported: @@ -146,7 +150,6 @@ ledger features not supported: -L, --price-exp MINS download quotes only if newer than MINS (def: 1440) -Q, --download download price information when needed -O, --quantity report commodity totals (this is the default) - -B, --basis report cost basis of commodities -V, --market report last known market value -g, --performance report gain/loss for each displayed transaction -G, --gain report net gain/loss @@ -162,3 +165,4 @@ Some other differences: - hledger talks about the entry and transaction "description", which ledger calls "note" - hledger always shows timelog balances in hours - hledger doesn't require a space after flags like -f +- hledger keeps differently-priced amounts of the same commodity separate \ No newline at end of file diff --git a/Tests.hs b/Tests.hs index f900bd289..35f13a11d 100644 --- a/Tests.hs +++ b/Tests.hs @@ -88,7 +88,7 @@ misc_tests = TestList [ , "canonicaliseAmounts" ~: do -- all amounts use the greatest precision - assertequal [2,2] (rawLedgerPrecisions $ canonicaliseAmounts $ rawLedgerWithAmounts ["1","2.00"]) + assertequal [2,2] (rawLedgerPrecisions $ canonicaliseAmounts False $ rawLedgerWithAmounts ["1","2.00"]) , "timeLog" ~: do assertparseequal timelog1 (parsewith timelog timelog1_str) @@ -239,6 +239,23 @@ balancecommand_tests = TestList [ " $-2 cash\n" ++ " $1 saving\n" ++ "") + , + "balance report with cost basis" ~: do + let l = cacheLedger [] $ + filterRawLedger Nothing Nothing [] False False $ + canonicaliseAmounts True $ -- enable cost basis adjustment + rawledgerfromstring + ("" ++ + "2008/1/1 test \n" ++ + " a:b 10h @ $50\n" ++ + " c:d \n" ++ + "\n") + assertequal + (" $500 a\n" ++ + " $-500 c\n" ++ + "" + ) + (showBalanceReport [] [] l) ] where gives (opts,pats) e = do l <- ledgerfromfile pats "sample.ledger" diff --git a/hledger.hs b/hledger.hs index 0c549911e..c5e2bd2a5 100644 --- a/hledger.hs +++ b/hledger.hs @@ -70,10 +70,11 @@ parseLedgerAndDo :: [Opt] -> [String] -> ([Opt] -> [String] -> Ledger -> IO ()) parseLedgerAndDo opts args cmd = ledgerFilePathFromOpts opts >>= parseLedgerFile >>= either printParseError runcmd where - runcmd = cmd opts args . cacheLedger apats . filterRawLedger b e dpats c r . canonicaliseAmounts + runcmd = cmd opts args . cacheLedger apats . filterRawLedger b e dpats c r . canonicaliseAmounts costbasis b = beginDateFromOpts opts e = endDateFromOpts opts (apats,dpats) = parseAccountDescriptionArgs args c = Cleared `elem` opts r = Real `elem` opts + costbasis = CostBasis `elem` opts