packages feed

hledger-lib 1.20.2 → 1.20.3

raw patch · 17 files changed

+107/−80 lines, 17 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Hledger.Data.Commodity: showCommoditySymbol :: Text -> Text
+ Hledger.Reports.BudgetReport: combineBudgetAndActual :: ReportOpts -> Journal -> MultiBalanceReport -> MultiBalanceReport -> BudgetReport
+ Hledger.Reports.ReportOptions: changingValuation :: ReportOpts -> Bool

Files

CHANGES.md view
@@ -1,6 +1,10 @@ Internal/api/developer-ish changes in the hledger-lib (and hledger) packages. For user-visible changes, see the hledger package changelog. +# 1.20.3 2021-01-14++- See hledger.+ # 1.20.2 2020-12-28  - Fix the info manuals' node structure.
Hledger/Data/Commodity.hs view
@@ -25,6 +25,8 @@ import Hledger.Data.Types import Hledger.Utils +-- Show space-containing commodity symbols quoted, as they are in a journal.+showCommoditySymbol = textQuoteIfNeeded  -- characters that may not be used in a non-quoted commodity symbol isNonsimpleCommodityChar :: Char -> Bool
Hledger/Data/Valuation.hs view
@@ -5,6 +5,7 @@  -} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}@@ -28,9 +29,8 @@ where  import Control.Applicative ((<|>))-import Data.Foldable (asum) import Data.Function ((&), on)-import Data.List ( (\\), sortBy )+import Data.List (partition, intercalate, sortBy) import Data.List.Extra (nubSortBy) import qualified Data.Map as M import qualified Data.Set as S@@ -39,12 +39,13 @@ import Data.Time.Calendar (Day, fromGregorian) import Data.MemoUgly (memo) import GHC.Generics (Generic)-import Safe (lastMay)+import Safe (headMay, lastMay)  import Hledger.Utils import Hledger.Data.Types import Hledger.Data.Amount import Hledger.Data.Dates (nulldate)+import Hledger.Data.Commodity (showCommoditySymbol)   ------------------------------------------------------------------------------@@ -219,9 +220,13 @@       Just to            ->         -- We have a commodity to convert to. Find the most direct price available,         -- according to the rules described in makePriceGraph.-        case -          pricesShortestPath forwardprices from to <|> -          pricesShortestPath allprices     from to +        let msg = "seeking " ++ pshowedge' "" from to ++ " price"+        in case +          (traceAt 2 (msg++" using forward prices") $ +            pricesShortestPath from to forwardprices)+          <|> +          (traceAt 2 (msg++" using forward and reverse prices") $ +            pricesShortestPath from to allprices)         of           Nothing -> Nothing           Just [] -> Nothing@@ -246,6 +251,7 @@  ------------------------------------------------------------------------------ -- Market price graph+-- built directly with MarketPrices for now, probably space-inefficient  type Edge = MarketPrice type Path = [Edge]@@ -277,34 +283,56 @@ -- form the edges of a directed graph. There should be at most one edge -- between each directed pair of commodities, eg there can be one -- USD->EUR price and one EUR->USD price.-pricesShortestPath :: [Edge] -> CommoditySymbol -> CommoditySymbol -> Maybe Path-pricesShortestPath edges start end =-  dbg1 ("shortest price path for "++T.unpack start++" -> "++T.unpack end) $ -  asum $ map (findPath end edgesremaining) initialpaths+pricesShortestPath :: CommoditySymbol -> CommoditySymbol -> [Edge] -> Maybe Path+pricesShortestPath start end edges =+  dbg2 ("shortest "++pshowedge' "" start end++" price path") $+  find [([],edges)]   where-    initialpaths = dbg9 "initial price paths" $ [[p] | p <- edges, mpfrom p == start]-    edgesremaining = dbg9 "initial edges remaining" $ edges \\ concat initialpaths+    -- Find the first and shortest complete path using a breadth-first search.+    find :: [(Path,[Edge])] -> Maybe Path+    find paths =+      case concatMap extend paths of+        [] -> Nothing +        _ | iteration > maxiterations -> +          trace ("gave up searching for a price chain after "++show maxiterations++" iterations, please report a bug")+          Nothing+          where +            iteration = 1 + maybe 0 (length . fst) (headMay paths)+            maxiterations = 1000+        paths' -> +          case completepaths of+                p:_ -> Just p  -- the left-most complete path at this length+                []  -> find paths'+          where completepaths = [p | (p,_) <- paths', (mpto <$> lastMay p) == Just end] --- Helper: breadth-first search for a continuation of the given path--- using zero or more of the given edges, to the specified end commodity.--- Returns the first & shortest complete path found, or Nothing.-findPath :: CommoditySymbol -> [Edge] -> Path -> Maybe Path-findPath end _ path | mpathend == Just end = Just path  -- path is complete-  where -    mpathend = mpto <$> lastMay path-findPath _ [] _ = Nothing   -- no more edges are available-findPath end edgesremaining path =   -- try continuing with all the remaining edges-  asum [ -      findPath end edgesremaining' path'-    | e <- nextedges-    , let path' = path++[e]-    , let edgesremaining' = filter (/=e) edgesremaining-    ]-  where-    nextedges = [ e | e <- edgesremaining, Just (mpfrom e) == mpathend ]-      where-        mpathend = mpto <$> lastMay path+    -- Use all applicable edges from those provided to extend this path by one step,+    -- returning zero or more new (path, remaining edges) pairs.+    extend :: (Path,[Edge]) -> [(Path,[Edge])]+    extend (path,unusededges) =+      let+        pathnodes = start : map mpto path+        pathend = fromMaybe start $ mpto <$> lastMay path+        (nextedges,remainingedges) = partition ((==pathend).mpfrom) unusededges+      in+        [ (path', remainingedges')+        | e <- nextedges+        , let path' = dbgpath "trying" $ path ++ [e]  -- PERF prepend ?+        , let pathnodes' = mpto e : pathnodes+        , let remainingedges' = [r | r <- remainingedges, not $ mpto r `elem` pathnodes' ]+        ] +-- debug helpers+dbgpath  label = dbg2With (pshowpath label)+-- dbgedges label = dbg2With (pshowedges label)+pshowpath label = \case+  []      -> prefix label ""+  p@(e:_) -> prefix label $ pshownode (mpfrom e) ++ ">" ++ intercalate ">" (map (pshownode . mpto) p)+-- pshowedges label = prefix label . intercalate ", " . map (pshowedge "")+-- pshowedge label MarketPrice{..} = pshowedge' label mpfrom mpto+pshowedge' label from to = prefix label $ pshownode from ++ ">" ++ pshownode to+pshownode = T.unpack . showCommoditySymbol+prefix l = if null l then (""++) else ((l++": ")++)+ -- | A snapshot of the known exchange rates between commodity pairs at a given date. -- This is a home-made version, more tailored to our needs. -- | Build the graph of commodity conversion prices for a given day.@@ -367,12 +395,12 @@     }   where     -- prices in effect on date d, either declared or inferred-    visibledeclaredprices = dbg2 "visibledeclaredprices" $ filter ((<=d).mpdate) alldeclaredprices-    visibleinferredprices = dbg2 "visibleinferredprices" $ filter ((<=d).mpdate) allinferredprices+    visibledeclaredprices = dbg9 "visibledeclaredprices" $ filter ((<=d).mpdate) alldeclaredprices+    visibleinferredprices = dbg9 "visibleinferredprices" $ filter ((<=d).mpdate) allinferredprices     forwardprices = effectiveMarketPrices visibledeclaredprices visibleinferredprices      -- infer any additional reverse prices not already declared or inferred-    reverseprices = dbg2 "additional reverse prices" $+    reverseprices = dbg9 "additional reverse prices" $       [p | p@MarketPrice{..} <- map marketPriceReverse forwardprices          , not $ (mpfrom,mpto) `S.member` forwardpairs       ]@@ -384,7 +412,7 @@     -- somewhat but not quite like effectiveMarketPrices     defaultdests = M.fromList [(mpfrom,mpto) | MarketPrice{..} <- pricesfordefaultcomms]       where-        pricesfordefaultcomms = dbg2 "prices for choosing default valuation commodities, by date then parse order" $+        pricesfordefaultcomms = dbg9 "prices for choosing default valuation commodities, by date then parse order" $           ps           & zip [1..]  -- label items with their parse order           & sortBy (compare `on` (\(parseorder,MarketPrice{..})->(mpdate,parseorder)))  -- sort by increasing date then increasing parse order@@ -407,7 +435,7 @@     declaredprices' = [(1, i, p) | (i,p) <- zip [1..] declaredprices]     inferredprices' = [(0, i, p) | (i,p) <- zip [1..] inferredprices]   in-    dbg2 "effective forward prices" $+    dbg9 "effective forward prices" $     -- combine     declaredprices' ++ inferredprices'     -- sort by decreasing date then decreasing precedence then decreasing parse order
Hledger/Read/Common.hs view
@@ -401,7 +401,7 @@         mfirstundeclaredcomm =            headMay $ filter (not . (`elem` cs)) $ catMaybes $           (acommodity . baamount <$> pbalanceassertion) :-          (map (Just . acommodity) $ amounts pamount)+          (map (Just . acommodity) . filter (/= missingamt) $ amounts pamount)         cs = journalCommoditiesDeclared j  setYear :: Year -> JournalParser m ()@@ -788,9 +788,12 @@         return $ nullamt{acommodity=c, aquantity=sign q, aismultiplier=mult, astyle=s, aprice=Nothing}       -- no symbol amount       Nothing -> do-        mdecmarkStyle <- getDecimalMarkStyle-        mcommodityStyle <- getDefaultAmountStyle-        let msuggestedStyle = mdecmarkStyle <|> mcommodityStyle+        -- look for a number style to use when parsing, based on+        -- these things we've already parsed, in this order of preference:+        mdecmarkStyle   <- getDecimalMarkStyle   -- a decimal-mark CSV rule+        mcommodityStyle <- getAmountStyle ""     -- a commodity directive for the no-symbol commodity+        mdefaultStyle   <- getDefaultAmountStyle -- a D default commodity directive+        let msuggestedStyle = mdecmarkStyle <|> mcommodityStyle <|> mdefaultStyle         (q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion msuggestedStyle ambiguousRawNum mExponent         -- if a default commodity has been set, apply it and its style to this amount         -- (unless it's a multiplier in an automated posting)
Hledger/Reports/BudgetReport.hs view
@@ -21,6 +21,7 @@   budgetReportAsCsv,   -- * Helpers   reportPeriodName,+  combineBudgetAndActual,   -- * Tests   tests_BudgetReport )
Hledger/Reports/MultiBalanceReport.hs view
@@ -51,7 +51,7 @@ #endif import Data.Semigroup (sconcat) import Data.Time.Calendar (Day, addDays, fromGregorian)-import Safe (headMay, lastDef, lastMay, minimumMay)+import Safe (headMay, lastDef, lastMay)  import Hledger.Data import Hledger.Query@@ -358,39 +358,20 @@         HistoricalBalance -> historical       where         historical = cumulativeSum startingBalance-        cumulative | fixedValuationDate = cumulativeSum nullacct-                   | otherwise          = fmap (`subtractAcct` valuedStart) historical-        changeamts | fixedValuationDate = M.mapWithKey valueAcct changes-                   | otherwise          = M.fromDistinctAscList . zip dates $-                                            zipWith subtractAcct histamts (valuedStart:histamts)-          where (dates, histamts) = unzip $ M.toAscList historical+        cumulative = cumulativeSum nullacct+        changeamts = M.mapWithKey valueAcct changes          cumulativeSum start = snd $ M.mapAccumWithKey accumValued start changes           where accumValued startAmt date newAmt = (s, valueAcct date s)                   where s = sumAcct startAmt newAmt -        -- Whether the market price is measured at the same date for all report-        -- periods, and we can therefore use the simpler calculations for-        -- cumulative and change reports.-        fixedValuationDate = case value_ ropts of-            Just (AtCost (Just _)) -> singleperiod-            Just (AtEnd  _)        -> singleperiod-            Just (AtDefault _)     -> singleperiod-            _                      -> True-          where singleperiod = interval_ ropts == NoInterval-         startingBalance = HM.lookupDefault nullacct name startbals-        valuedStart = valueAcct (DateSpan Nothing historicalDate) startingBalance      -- Add the values of two accounts. Should be right-biased, since it's used     -- in scanl, so other properties (such as anumpostings) stay in the right place     sumAcct Account{aibalance=i1,aebalance=e1} a@Account{aibalance=i2,aebalance=e2} =         a{aibalance = i1 + i2, aebalance = e1 + e2} -    -- Subtract the values in one account from another. Should be left-biased.-    subtractAcct a@Account{aibalance=i1,aebalance=e1} Account{aibalance=i2,aebalance=e2} =-        a{aibalance = i1 - i2, aebalance = e1 - e2}-     -- We may be converting amounts to value, per hledger_options.m4.md "Effect of --value on reports".     valueAcct (DateSpan _ (Just end)) acct =         acct{aibalance = value (aibalance acct), aebalance = value (aebalance acct)}@@ -398,8 +379,6 @@     valueAcct _ _ = error "multiBalanceReport: expected all spans to have an end date"  -- XXX should not happen      zeros = M.fromList [(span, nullacct) | span <- colspans]-    historicalDate = minimumMay $ mapMaybe spanStart colspans-  -- | Lay out a set of postings grouped by date span into a regular matrix with rows -- given by AccountName and columns by DateSpan, then generate a MultiBalanceReport
Hledger/Reports/PostingsReport.hs view
@@ -81,15 +81,15 @@        -- Postings, or summary postings with their subperiod's end date, to be displayed.       displayps :: [(Posting, Maybe Day)]-        | multiperiod =-            let summaryps = summarisePostingsByInterval interval_ whichdate mdepth showempty reportspan reportps-            in [(pvalue p lastday, Just periodend) | (p, periodend) <- summaryps, let lastday = addDays (-1) periodend]-        | otherwise =-            [(pvalue p reportorjournallast, Nothing) | p <- reportps]+        | multiperiod && changingValuation ropts = [(pvalue lastday p, Just periodend) | (p, periodend) <- summariseps reportps, let lastday = addDays (-1) periodend]+        | multiperiod                            = [(p, Just periodend) | (p, periodend) <- summariseps valuedps]+        | otherwise                              = [(p, Nothing)        | p <- valuedps]         where+          summariseps = summarisePostingsByInterval interval_ whichdate mdepth showempty reportspan+          valuedps = map (pvalue reportorjournallast) reportps           showempty = empty_ || average_           -- We may be converting posting amounts to value, per hledger_options.m4.md "Effect of --value on reports".-          pvalue p periodlast = maybe p (postingApplyValuation priceoracle styles periodlast mreportlast (rsToday rspec) multiperiod p) value_+          pvalue periodlast p = maybe p (postingApplyValuation priceoracle styles periodlast mreportlast (rsToday rspec) multiperiod p) value_             where               mreportlast = reportPeriodLastDay rspec           reportorjournallast =
Hledger/Reports/ReportOptions.hs view
@@ -23,6 +23,7 @@   rawOptsToReportSpec,   flat_,   tree_,+  changingValuation,   reportOptsToggleStatus,   simplifyStatuses,   whichDateFromOpts,@@ -484,6 +485,15 @@                ]     consIf f b = if b then (f True:) else id     consJust f = maybe id ((:) . f)++-- | Whether the market price for postings might change when reported in+-- different report periods.+changingValuation :: ReportOpts -> Bool+changingValuation ropts = case value_ ropts of+    Just (AtCost (Just _)) -> True+    Just (AtEnd  _)        -> True+    Just (AtDefault _)     -> interval_ ropts /= NoInterval+    _                      -> False  -- Report dates. 
hledger-lib.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: f99ed8ff188d98ffb43ea739b382591b37b4e66b2adbabc5345e75d4e8e69a56+-- hash: 84b9a4a7bf3049275178ede9a44418e46548ddce8bdbd182d32caccb5cb51f3f  name:           hledger-lib-version:        1.20.2+version:        1.20.3 synopsis:       A reusable library providing the core functionality of hledger description:    A reusable library containing hledger's core functionality.                 This is used by most hledger* packages so that they support the same
hledger_csv.5 view
@@ -1,6 +1,6 @@ .\"t -.TH "HLEDGER_CSV" "5" "December 2020" "hledger-lib-1.20.1 " "hledger User Manuals"+.TH "HLEDGER_CSV" "5" "December 2020" "hledger-lib-1.20.3 " "hledger User Manuals"   
hledger_csv.txt view
@@ -958,4 +958,4 @@   -hledger-lib-1.20.1               December 2020                  HLEDGER_CSV(5)+hledger-lib-1.20.3               December 2020                  HLEDGER_CSV(5)
hledger_journal.5 view
@@ -1,6 +1,6 @@ .\"t -.TH "HLEDGER_JOURNAL" "5" "December 2020" "hledger-lib-1.20.1 " "hledger User Manuals"+.TH "HLEDGER_JOURNAL" "5" "December 2020" "hledger-lib-1.20.3 " "hledger User Manuals"   
hledger_journal.txt view
@@ -1575,4 +1575,4 @@   -hledger-lib-1.20.1               December 2020              HLEDGER_JOURNAL(5)+hledger-lib-1.20.3               December 2020              HLEDGER_JOURNAL(5)
hledger_timeclock.5 view
@@ -1,5 +1,5 @@ -.TH "HLEDGER_TIMECLOCK" "5" "December 2020" "hledger-lib-1.20.1 " "hledger User Manuals"+.TH "HLEDGER_TIMECLOCK" "5" "December 2020" "hledger-lib-1.20.3 " "hledger User Manuals"   
hledger_timeclock.txt view
@@ -77,4 +77,4 @@   -hledger-lib-1.20.1               December 2020            HLEDGER_TIMECLOCK(5)+hledger-lib-1.20.3               December 2020            HLEDGER_TIMECLOCK(5)
hledger_timedot.5 view
@@ -1,5 +1,5 @@ -.TH "HLEDGER_TIMEDOT" "5" "December 2020" "hledger-lib-1.20.1 " "hledger User Manuals"+.TH "HLEDGER_TIMEDOT" "5" "December 2020" "hledger-lib-1.20.3 " "hledger User Manuals"   
hledger_timedot.txt view
@@ -160,4 +160,4 @@   -hledger-lib-1.20.1               December 2020              HLEDGER_TIMEDOT(5)+hledger-lib-1.20.3               December 2020              HLEDGER_TIMEDOT(5)