lib,cli: Store parsed Query in ReportOpts, rather than an unparsed
String.
This commit is contained in:
		
							parent
							
								
									103308e795
								
							
						
					
					
						commit
						c45663d41d
					
				| @ -59,6 +59,7 @@ module Hledger.Query ( | |||||||
| where | where | ||||||
| 
 | 
 | ||||||
| import Control.Applicative ((<|>), many, optional) | import Control.Applicative ((<|>), many, optional) | ||||||
|  | import Data.Default (Default(..)) | ||||||
| import Data.Either (partitionEithers) | import Data.Either (partitionEithers) | ||||||
| import Data.List (partition) | import Data.List (partition) | ||||||
| import Data.Maybe (fromMaybe, isJust, mapMaybe) | import Data.Maybe (fromMaybe, isJust, mapMaybe) | ||||||
| @ -105,6 +106,8 @@ data Query = Any              -- ^ always match | |||||||
|                                         -- matching the regexp if provided, exists |                                         -- matching the regexp if provided, exists | ||||||
|     deriving (Eq,Show) |     deriving (Eq,Show) | ||||||
| 
 | 
 | ||||||
|  | instance Default Query where def = Any | ||||||
|  | 
 | ||||||
| -- | Construct a payee tag | -- | Construct a payee tag | ||||||
| payeeTag :: Maybe String -> Either RegexError Query | payeeTag :: Maybe String -> Either RegexError Query | ||||||
| payeeTag = fmap (Tag (toRegexCI' "payee")) . maybe (pure Nothing) (fmap Just . toRegexCI) | payeeTag = fmap (Tag (toRegexCI' "payee")) . maybe (pure Nothing) (fmap Just . toRegexCI) | ||||||
|  | |||||||
| @ -41,5 +41,4 @@ tests_Reports = tests "Reports" [ | |||||||
|   ,tests_EntriesReport |   ,tests_EntriesReport | ||||||
|   ,tests_MultiBalanceReport |   ,tests_MultiBalanceReport | ||||||
|   ,tests_PostingsReport |   ,tests_PostingsReport | ||||||
|   ,tests_ReportOptions |  | ||||||
|   ] |   ] | ||||||
|  | |||||||
| @ -61,10 +61,10 @@ flatShowsExclusiveBalance    = True | |||||||
| -- their balances (change of balance) during the specified period. | -- their balances (change of balance) during the specified period. | ||||||
| -- If the normalbalance_ option is set, it adjusts the sorting and sign of | -- If the normalbalance_ option is set, it adjusts the sorting and sign of | ||||||
| -- amounts (see ReportOpts and CompoundBalanceCommand). | -- amounts (see ReportOpts and CompoundBalanceCommand). | ||||||
| balanceReport :: ReportOpts -> Query -> Journal -> BalanceReport | balanceReport :: ReportOpts -> Journal -> BalanceReport | ||||||
| balanceReport ropts q j = (rows, total) | balanceReport ropts j = (rows, total) | ||||||
|   where |   where | ||||||
|     report = multiBalanceReportWith ropts q j (journalPriceOracle (infer_value_ ropts) j) |     report = multiBalanceReportWith ropts j (journalPriceOracle (infer_value_ ropts) j) | ||||||
|     rows = [( prrFullName row |     rows = [( prrFullName row | ||||||
|             , prrDisplayName row |             , prrDisplayName row | ||||||
|             , prrDepth row - 1  -- BalanceReport uses 0-based account depths |             , prrDepth row - 1  -- BalanceReport uses 0-based account depths | ||||||
| @ -102,8 +102,9 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
| 
 | 
 | ||||||
|   let |   let | ||||||
|     (opts,journal) `gives` r = do |     (opts,journal) `gives` r = do | ||||||
|       let (eitems, etotal) = r |       let opts' = opts{query_=And [queryFromFlags opts, query_ opts]} | ||||||
|           (aitems, atotal) = balanceReport opts (queryFromOpts nulldate opts) journal |           (eitems, etotal) = r | ||||||
|  |           (aitems, atotal) = balanceReport opts' journal | ||||||
|           showw (acct,acct',indent,amt) = (acct, acct', indent, showMixedAmountDebug amt) |           showw (acct,acct',indent,amt) = (acct, acct', indent, showMixedAmountDebug amt) | ||||||
|       (map showw aitems) @?= (map showw eitems) |       (map showw aitems) @?= (map showw eitems) | ||||||
|       (showMixedAmountDebug atotal) @?= (showMixedAmountDebug etotal) |       (showMixedAmountDebug atotal) @?= (showMixedAmountDebug etotal) | ||||||
| @ -152,7 +153,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with depth:N" $ |     ,test "with depth:N" $ | ||||||
|      (defreportopts{query_="depth:1"}, samplejournal) `gives` |      (defreportopts{query_=Depth 1}, samplejournal) `gives` | ||||||
|       ([ |       ([ | ||||||
|        ("expenses",    "expenses",    0, mamountp'  "$2.00") |        ("expenses",    "expenses",    0, mamountp'  "$2.00") | ||||||
|        ,("income",      "income",      0, mamountp' "$-2.00") |        ,("income",      "income",      0, mamountp' "$-2.00") | ||||||
| @ -160,11 +161,11 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with date:" $ |     ,test "with date:" $ | ||||||
|      (defreportopts{query_="date:'in 2009'"}, samplejournal2) `gives` |      (defreportopts{query_=Date $ DateSpan (Just $ fromGregorian 2009 01 01) (Just $ fromGregorian 2010 01 01)}, samplejournal2) `gives` | ||||||
|       ([], 0) |       ([], 0) | ||||||
| 
 | 
 | ||||||
|     ,test "with date2:" $ |     ,test "with date2:" $ | ||||||
|      (defreportopts{query_="date2:'in 2009'"}, samplejournal2) `gives` |      (defreportopts{query_=Date2 $ DateSpan (Just $ fromGregorian 2009 01 01) (Just $ fromGregorian 2010 01 01)}, samplejournal2) `gives` | ||||||
|       ([ |       ([ | ||||||
|         ("assets:bank:checking","assets:bank:checking",0,mamountp' "$1.00") |         ("assets:bank:checking","assets:bank:checking",0,mamountp' "$1.00") | ||||||
|        ,("income:salary","income:salary",0,mamountp' "$-1.00") |        ,("income:salary","income:salary",0,mamountp' "$-1.00") | ||||||
| @ -172,7 +173,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with desc:" $ |     ,test "with desc:" $ | ||||||
|      (defreportopts{query_="desc:income"}, samplejournal) `gives` |      (defreportopts{query_=Desc $ toRegexCI' "income"}, samplejournal) `gives` | ||||||
|       ([ |       ([ | ||||||
|         ("assets:bank:checking","assets:bank:checking",0,mamountp' "$1.00") |         ("assets:bank:checking","assets:bank:checking",0,mamountp' "$1.00") | ||||||
|        ,("income:salary","income:salary",0, mamountp' "$-1.00") |        ,("income:salary","income:salary",0, mamountp' "$-1.00") | ||||||
| @ -180,7 +181,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with not:desc:" $ |     ,test "with not:desc:" $ | ||||||
|      (defreportopts{query_="not:desc:income"}, samplejournal) `gives` |      (defreportopts{query_=Not . Desc $ toRegexCI' "income"}, samplejournal) `gives` | ||||||
|       ([ |       ([ | ||||||
|         ("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00") |         ("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00") | ||||||
|        ,("assets:cash","assets:cash",0, mamountp' "$-2.00") |        ,("assets:cash","assets:cash",0, mamountp' "$-2.00") | ||||||
|  | |||||||
| @ -33,7 +33,6 @@ import Data.Maybe | |||||||
| #if !(MIN_VERSION_base(4,11,0)) | #if !(MIN_VERSION_base(4,11,0)) | ||||||
| import Data.Monoid ((<>)) | import Data.Monoid ((<>)) | ||||||
| #endif | #endif | ||||||
| import Data.Time.Calendar |  | ||||||
| import Safe | import Safe | ||||||
| --import Data.List | --import Data.List | ||||||
| --import Data.Maybe | --import Data.Maybe | ||||||
| @ -66,8 +65,8 @@ type BudgetReport    = PeriodicReport    DisplayName BudgetCell | |||||||
| -- actual balance changes from the regular transactions, | -- actual balance changes from the regular transactions, | ||||||
| -- and compare these to get a 'BudgetReport'. | -- and compare these to get a 'BudgetReport'. | ||||||
| -- Unbudgeted accounts may be hidden or renamed (see budgetRollup). | -- Unbudgeted accounts may be hidden or renamed (see budgetRollup). | ||||||
| budgetReport :: ReportOpts -> Bool -> DateSpan -> Day -> Journal -> BudgetReport | budgetReport :: ReportOpts -> Bool -> DateSpan -> Journal -> BudgetReport | ||||||
| budgetReport ropts' assrt reportspan d j = dbg1 "sortedbudgetreport" budgetreport | budgetReport ropts' assrt reportspan j = dbg1 "sortedbudgetreport" budgetreport | ||||||
|   where |   where | ||||||
|     -- Budget report demands ALTree mode to ensure subaccounts and subaccount budgets are properly handled |     -- Budget report demands ALTree mode to ensure subaccounts and subaccount budgets are properly handled | ||||||
|     -- and that reports with and without --empty make sense when compared side by side |     -- and that reports with and without --empty make sense when compared side by side | ||||||
| @ -84,9 +83,9 @@ budgetReport ropts' assrt reportspan d j = dbg1 "sortedbudgetreport" budgetrepor | |||||||
|     actualj = dbg1With (("actualj"++).show.jtxns)  $ budgetRollUp budgetedaccts showunbudgeted j |     actualj = dbg1With (("actualj"++).show.jtxns)  $ budgetRollUp budgetedaccts showunbudgeted j | ||||||
|     budgetj = dbg1With (("budgetj"++).show.jtxns)  $ budgetJournal assrt ropts reportspan j |     budgetj = dbg1With (("budgetj"++).show.jtxns)  $ budgetJournal assrt ropts reportspan j | ||||||
|     actualreport@(PeriodicReport actualspans _ _) = |     actualreport@(PeriodicReport actualspans _ _) = | ||||||
|         dbg1 "actualreport" $ multiBalanceReport d ropts{empty_=True} actualj |         dbg1 "actualreport" $ multiBalanceReport ropts{empty_=True} actualj | ||||||
|     budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) = |     budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) = | ||||||
|         dbg1 "budgetgoalreport" $ multiBalanceReport d ropts{empty_=True} budgetj |         dbg1 "budgetgoalreport" $ multiBalanceReport ropts{empty_=True} budgetj | ||||||
|     budgetgoalreport' |     budgetgoalreport' | ||||||
|       -- If no interval is specified: |       -- If no interval is specified: | ||||||
|       -- budgetgoalreport's span might be shorter actualreport's due to periodic txns; |       -- budgetgoalreport's span might be shorter actualreport's due to periodic txns; | ||||||
|  | |||||||
| @ -32,9 +32,9 @@ type EntriesReport = [EntriesReportItem] | |||||||
| type EntriesReportItem = Transaction | type EntriesReportItem = Transaction | ||||||
| 
 | 
 | ||||||
| -- | Select transactions for an entries report. | -- | Select transactions for an entries report. | ||||||
| entriesReport :: ReportOpts -> Query -> Journal -> EntriesReport | entriesReport :: ReportOpts -> Journal -> EntriesReport | ||||||
| entriesReport ropts@ReportOpts{..} q j@Journal{..} = | entriesReport ropts@ReportOpts{..} j@Journal{..} = | ||||||
|   sortBy (comparing getdate) $ filter (q `matchesTransaction`) $ map tvalue jtxns |   sortBy (comparing getdate) $ filter (query_ `matchesTransaction`) $ map tvalue jtxns | ||||||
|   where |   where | ||||||
|     getdate = transactionDateFn ropts |     getdate = transactionDateFn ropts | ||||||
|     -- We may be converting posting amounts to value, per hledger_options.m4.md "Effect of --value on reports". |     -- We may be converting posting amounts to value, per hledger_options.m4.md "Effect of --value on reports". | ||||||
| @ -50,8 +50,8 @@ entriesReport ropts@ReportOpts{..} q j@Journal{..} = | |||||||
| 
 | 
 | ||||||
| tests_EntriesReport = tests "EntriesReport" [ | tests_EntriesReport = tests "EntriesReport" [ | ||||||
|   tests "entriesReport" [ |   tests "entriesReport" [ | ||||||
|      test "not acct" $ (length $ entriesReport defreportopts (Not . Acct $ toRegex' "bank") samplejournal) @?= 1 |      test "not acct" $ (length $ entriesReport defreportopts{query_=Not . Acct $ toRegex' "bank"} samplejournal) @?= 1 | ||||||
|     ,test "date" $ (length $ entriesReport defreportopts (Date $ DateSpan (Just $ fromGregorian 2008 06 01) (Just $ fromGregorian 2008 07 01)) samplejournal) @?= 3 |     ,test "date" $ (length $ entriesReport defreportopts{query_=Date $ DateSpan (Just $ fromGregorian 2008 06 01) (Just $ fromGregorian 2008 07 01)} samplejournal) @?= 3 | ||||||
|   ] |   ] | ||||||
|  ] |  ] | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -91,64 +91,57 @@ type ClippedAccountName = AccountName | |||||||
| -- CompoundBalanceCommand). hledger's most powerful and useful report, used | -- CompoundBalanceCommand). hledger's most powerful and useful report, used | ||||||
| -- by the balance command (in multiperiod mode) and (via compoundBalanceReport) | -- by the balance command (in multiperiod mode) and (via compoundBalanceReport) | ||||||
| -- by the bs/cf/is commands. | -- by the bs/cf/is commands. | ||||||
| multiBalanceReport :: Day -> ReportOpts -> Journal -> MultiBalanceReport | multiBalanceReport :: ReportOpts -> Journal -> MultiBalanceReport | ||||||
| multiBalanceReport today ropts j = | multiBalanceReport ropts j = multiBalanceReportWith ropts j (journalPriceOracle infer j) | ||||||
|     multiBalanceReportWith ropts q j (journalPriceOracle infer j) |   where infer = infer_value_ ropts | ||||||
|   where |  | ||||||
|     q = queryFromOpts today ropts |  | ||||||
|     infer = infer_value_ ropts |  | ||||||
| 
 | 
 | ||||||
| -- | A helper for multiBalanceReport. This one takes an explicit Query | -- | A helper for multiBalanceReport. This one takes an extra argument, | ||||||
| -- instead of deriving one from ReportOpts, and an extra argument, a | -- a PriceOracle to be used for looking up market prices. Commands which | ||||||
| -- PriceOracle to be used for looking up market prices. Commands which |  | ||||||
| -- run multiple reports (bs etc.) can generate the price oracle just | -- run multiple reports (bs etc.) can generate the price oracle just | ||||||
| -- once for efficiency, passing it to each report by calling this | -- once for efficiency, passing it to each report by calling this | ||||||
| -- function directly. | -- function directly. | ||||||
| multiBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle -> MultiBalanceReport | multiBalanceReportWith :: ReportOpts -> Journal -> PriceOracle -> MultiBalanceReport | ||||||
| multiBalanceReportWith ropts q j priceoracle = report | multiBalanceReportWith ropts' j priceoracle = report | ||||||
|   where |   where | ||||||
|     -- Queries, report/column dates. |     -- Queries, report/column dates. | ||||||
|     reportspan = dbg "reportspan" $ calculateReportSpan ropts q j |     reportspan = dbg "reportspan" $ calculateReportSpan ropts' j | ||||||
|     reportq    = dbg "reportq"    $ makeReportQuery ropts reportspan q |     ropts      = dbg "reportopts" $ makeReportQuery ropts' reportspan | ||||||
| 
 | 
 | ||||||
|     -- Group postings into their columns. |     -- Group postings into their columns. | ||||||
|     colps    = dbg'' "colps"  $ getPostingsByColumn ropts reportq j reportspan |     colps    = dbg'' "colps"  $ getPostingsByColumn ropts j reportspan | ||||||
|     colspans = dbg "colspans" $ M.keys colps |     colspans = dbg "colspans" $ M.keys colps | ||||||
| 
 | 
 | ||||||
|     -- The matched accounts with a starting balance. All of these should appear |     -- The matched accounts with a starting balance. All of these should appear | ||||||
|     -- in the report, even if they have no postings during the report period. |     -- in the report, even if they have no postings during the report period. | ||||||
|     startbals = dbg' "startbals" $ startingBalances ropts reportq j reportspan |     startbals = dbg' "startbals" $ startingBalances ropts j reportspan | ||||||
| 
 | 
 | ||||||
|     -- Generate and postprocess the report, negating balances and taking percentages if needed |     -- Generate and postprocess the report, negating balances and taking percentages if needed | ||||||
|     report = dbg' "report" $ |     report = dbg' "report" $ | ||||||
|       generateMultiBalanceReport ropts reportq j priceoracle colspans colps startbals |       generateMultiBalanceReport ropts j priceoracle colspans colps startbals | ||||||
| 
 | 
 | ||||||
| -- | Generate a compound balance report from a list of CBCSubreportSpec. This | -- | Generate a compound balance report from a list of CBCSubreportSpec. This | ||||||
| -- shares postings between the subreports. | -- shares postings between the subreports. | ||||||
| compoundBalanceReport :: Day -> ReportOpts -> Journal -> [CBCSubreportSpec] | compoundBalanceReport :: ReportOpts -> Journal -> [CBCSubreportSpec] | ||||||
|                       -> CompoundBalanceReport |                       -> CompoundBalanceReport | ||||||
| compoundBalanceReport today ropts j = | compoundBalanceReport ropts j = compoundBalanceReportWith ropts j (journalPriceOracle infer j) | ||||||
|     compoundBalanceReportWith ropts q j (journalPriceOracle infer j) |   where infer = infer_value_ ropts | ||||||
|   where |  | ||||||
|     q = queryFromOpts today ropts |  | ||||||
|     infer = infer_value_ ropts |  | ||||||
| 
 | 
 | ||||||
| -- | A helper for compoundBalanceReport, similar to multiBalanceReportWith. | -- | A helper for compoundBalanceReport, similar to multiBalanceReportWith. | ||||||
| compoundBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle | compoundBalanceReportWith :: ReportOpts -> Journal -> PriceOracle | ||||||
|                           -> [CBCSubreportSpec] -> CompoundBalanceReport |                           -> [CBCSubreportSpec] -> CompoundBalanceReport | ||||||
| compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr | compoundBalanceReportWith ropts' j priceoracle subreportspecs = cbr | ||||||
|   where |   where | ||||||
|     -- Queries, report/column dates. |     -- Queries, report/column dates. | ||||||
|     reportspan = dbg "reportspan" $ calculateReportSpan ropts q j |     reportspan = dbg "reportspan" $ calculateReportSpan ropts' j | ||||||
|     reportq    = dbg "reportq"    $ makeReportQuery ropts reportspan q |     ropts      = dbg "reportopts" $ makeReportQuery ropts' reportspan | ||||||
| 
 | 
 | ||||||
|     -- Group postings into their columns. |     -- Group postings into their columns. | ||||||
|     colps    = dbg'' "colps"  $ getPostingsByColumn ropts{empty_=True} reportq j reportspan |     colps    = dbg'' "colps"  $ getPostingsByColumn ropts{empty_=True} j reportspan | ||||||
|     colspans = dbg "colspans" $ M.keys colps |     colspans = dbg "colspans" $ M.keys colps | ||||||
| 
 | 
 | ||||||
|     -- The matched accounts with a starting balance. All of these should appear |     -- The matched accounts with a starting balance. All of these should appear | ||||||
|     -- in the report, even if they have no postings during the report period. |     -- in the report, even if they have no postings during the report period. | ||||||
|     startbals = dbg' "startbals" $ startingBalances ropts reportq j reportspan |     startbals = dbg' "startbals" $ startingBalances ropts j reportspan | ||||||
| 
 | 
 | ||||||
|     subreports = map generateSubreport subreportspecs |     subreports = map generateSubreport subreportspecs | ||||||
|       where |       where | ||||||
| @ -156,7 +149,7 @@ compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr | |||||||
|             ( cbcsubreporttitle |             ( cbcsubreporttitle | ||||||
|             -- Postprocess the report, negating balances and taking percentages if needed |             -- Postprocess the report, negating balances and taking percentages if needed | ||||||
|             , prNormaliseSign cbcsubreportnormalsign $ |             , prNormaliseSign cbcsubreportnormalsign $ | ||||||
|                 generateMultiBalanceReport ropts' reportq j priceoracle colspans colps' startbals' |                 generateMultiBalanceReport ropts' j priceoracle colspans colps' startbals' | ||||||
|             , cbcsubreportincreasestotal |             , cbcsubreportincreasestotal | ||||||
|             ) |             ) | ||||||
|           where |           where | ||||||
| @ -186,21 +179,19 @@ compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr | |||||||
| -- TODO: Do we want to check whether to bother calculating these? isHistorical | -- TODO: Do we want to check whether to bother calculating these? isHistorical | ||||||
| -- and startDate is not nothing, otherwise mempty? This currently gives a | -- and startDate is not nothing, otherwise mempty? This currently gives a | ||||||
| -- failure with some totals which are supposed to be 0 being blank. | -- failure with some totals which are supposed to be 0 being blank. | ||||||
| startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> HashMap AccountName Account | startingBalances :: ReportOpts -> Journal -> DateSpan -> HashMap AccountName Account | ||||||
| startingBalances ropts q j reportspan = acctchanges | startingBalances ropts j reportspan = | ||||||
|  |     acctChangesFromPostings ropts' . map fst $ getPostings ropts' j | ||||||
|   where |   where | ||||||
|     acctchanges = acctChangesFromPostings ropts' startbalq . map fst $ |     ropts' = case accountlistmode_ ropts of | ||||||
|         getPostings ropts' startbalq j |         ALTree -> ropts{query_=startbalq, period_=precedingperiod, no_elide_=True} | ||||||
|  |         ALFlat -> ropts{query_=startbalq, period_=precedingperiod} | ||||||
| 
 | 
 | ||||||
|     -- q projected back before the report start date. |     -- q projected back before the report start date. | ||||||
|     -- When there's no report start date, in case there are future txns (the hledger-ui case above), |     -- When there's no report start date, in case there are future txns (the hledger-ui case above), | ||||||
|     -- we use emptydatespan to make sure they aren't counted as starting balance. |     -- we use emptydatespan to make sure they aren't counted as starting balance. | ||||||
|     startbalq = dbg'' "startbalq" $ And [datelessq, precedingspanq] |     startbalq = dbg'' "startbalq" $ And [datelessq, precedingspanq] | ||||||
|     datelessq = dbg "datelessq" $ filterQuery (not . queryIsDateOrDate2) q |     datelessq = dbg "datelessq" . filterQuery (not . queryIsDateOrDate2) $ query_ ropts | ||||||
| 
 |  | ||||||
|     ropts' = case accountlistmode_ ropts of |  | ||||||
|         ALTree -> ropts{no_elide_=True, period_=precedingperiod} |  | ||||||
|         ALFlat -> ropts{period_=precedingperiod} |  | ||||||
| 
 | 
 | ||||||
|     precedingperiod = dateSpanAsPeriod . spanIntersect precedingspan . |     precedingperiod = dateSpanAsPeriod . spanIntersect precedingspan . | ||||||
|                          periodAsDateSpan $ period_ ropts |                          periodAsDateSpan $ period_ ropts | ||||||
| @ -210,11 +201,11 @@ startingBalances ropts q j reportspan = acctchanges | |||||||
|         a -> a |         a -> a | ||||||
| 
 | 
 | ||||||
| -- | Calculate the span of the report to be generated. | -- | Calculate the span of the report to be generated. | ||||||
| calculateReportSpan :: ReportOpts -> Query -> Journal -> DateSpan | calculateReportSpan :: ReportOpts -> Journal -> DateSpan | ||||||
| calculateReportSpan ropts q j = reportspan | calculateReportSpan ropts j = reportspan | ||||||
|   where |   where | ||||||
|     -- The date span specified by -b/-e/-p options and query args if any. |     -- The date span specified by -b/-e/-p options and query args if any. | ||||||
|     requestedspan  = dbg "requestedspan" $ queryDateSpan (date2_ ropts) q |     requestedspan  = dbg "requestedspan" $ queryDateSpan (date2_ ropts) $ query_ ropts | ||||||
|     -- If the requested span is open-ended, close it using the journal's end dates. |     -- If the requested span is open-ended, close it using the journal's end dates. | ||||||
|     -- This can still be the null (open) span if the journal is empty. |     -- This can still be the null (open) span if the journal is empty. | ||||||
|     requestedspan' = dbg "requestedspan'" $ |     requestedspan' = dbg "requestedspan'" $ | ||||||
| @ -233,21 +224,22 @@ calculateReportSpan ropts q j = reportspan | |||||||
| -- The user's query expanded to the report span | -- The user's query expanded to the report span | ||||||
| -- if there is one (otherwise any date queries are left as-is, which | -- if there is one (otherwise any date queries are left as-is, which | ||||||
| -- handles the hledger-ui+future txns case above). | -- handles the hledger-ui+future txns case above). | ||||||
| makeReportQuery :: ReportOpts -> DateSpan -> Query -> Query | makeReportQuery :: ReportOpts -> DateSpan -> ReportOpts | ||||||
| makeReportQuery ropts reportspan q | makeReportQuery ropts reportspan | ||||||
|     | reportspan == nulldatespan = q |     | reportspan == nulldatespan = ropts | ||||||
|     | otherwise = And [dateless q, reportspandatesq] |     | otherwise = ropts{query_=query} | ||||||
|   where |   where | ||||||
|  |     query            = simplifyQuery $ And [dateless $ query_ ropts, reportspandatesq] | ||||||
|     reportspandatesq = dbg "reportspandatesq" $ dateqcons reportspan |     reportspandatesq = dbg "reportspandatesq" $ dateqcons reportspan | ||||||
|     dateless         = dbg "dateless" . filterQuery (not . queryIsDateOrDate2) |     dateless         = dbg "dateless" . filterQuery (not . queryIsDateOrDate2) | ||||||
|     dateqcons        = if date2_ ropts then Date2 else Date |     dateqcons        = if date2_ ropts then Date2 else Date | ||||||
| 
 | 
 | ||||||
| -- | Group postings, grouped by their column | -- | Group postings, grouped by their column | ||||||
| getPostingsByColumn :: ReportOpts -> Query -> Journal -> DateSpan -> Map DateSpan [Posting] | getPostingsByColumn :: ReportOpts -> Journal -> DateSpan -> Map DateSpan [Posting] | ||||||
| getPostingsByColumn ropts q j reportspan = columns | getPostingsByColumn ropts j reportspan = columns | ||||||
|   where |   where | ||||||
|     -- Postings matching the query within the report period. |     -- Postings matching the query within the report period. | ||||||
|     ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts q j |     ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts j | ||||||
|     days = map snd ps |     days = map snd ps | ||||||
| 
 | 
 | ||||||
|     -- The date spans to be included as report columns. |     -- The date spans to be included as report columns. | ||||||
| @ -259,13 +251,14 @@ getPostingsByColumn ropts q j reportspan = columns | |||||||
|     columns = foldr addPosting emptyMap ps |     columns = foldr addPosting emptyMap ps | ||||||
| 
 | 
 | ||||||
| -- | Gather postings matching the query within the report period. | -- | Gather postings matching the query within the report period. | ||||||
| getPostings :: ReportOpts -> Query -> Journal -> [(Posting, Day)] | getPostings :: ReportOpts -> Journal -> [(Posting, Day)] | ||||||
| getPostings ropts q = | getPostings ropts = | ||||||
|     map (\p -> (p, date p)) . |     map (\p -> (p, date p)) . | ||||||
|     journalPostings . |     journalPostings . | ||||||
|     filterJournalAmounts symq .    -- remove amount parts excluded by cur: |     filterJournalAmounts symq .    -- remove amount parts excluded by cur: | ||||||
|     filterJournalPostings reportq  -- remove postings not matched by (adjusted) query |     filterJournalPostings reportq  -- remove postings not matched by (adjusted) query | ||||||
|   where |   where | ||||||
|  |     q = query_ ropts | ||||||
|     symq = dbg "symq" . filterQuery queryIsSym $ dbg "requested q" q |     symq = dbg "symq" . filterQuery queryIsSym $ dbg "requested q" q | ||||||
|     -- The user's query with no depth limit, and expanded to the report span |     -- The user's query with no depth limit, and expanded to the report span | ||||||
|     -- if there is one (otherwise any date queries are left as-is, which |     -- if there is one (otherwise any date queries are left as-is, which | ||||||
| @ -290,18 +283,17 @@ calculateColSpans ropts reportspan days = | |||||||
| 
 | 
 | ||||||
| -- | Gather the account balance changes into a regular matrix | -- | Gather the account balance changes into a regular matrix | ||||||
| -- including the accounts from all columns. | -- including the accounts from all columns. | ||||||
| calculateAccountChanges :: ReportOpts -> Query -> [DateSpan] | calculateAccountChanges :: ReportOpts -> [DateSpan] -> Map DateSpan [Posting] | ||||||
|                         -> Map DateSpan [Posting] |  | ||||||
|                         -> HashMap ClippedAccountName (Map DateSpan Account) |                         -> HashMap ClippedAccountName (Map DateSpan Account) | ||||||
| calculateAccountChanges ropts q colspans colps | calculateAccountChanges ropts colspans colps | ||||||
|     | queryDepth q == Just 0 = acctchanges <> elided |     | queryDepth (query_ ropts) == Just 0 = acctchanges <> elided | ||||||
|     | otherwise = acctchanges |     | otherwise = acctchanges | ||||||
|   where |   where | ||||||
|     -- Transpose to get each account's balance changes across all columns. |     -- Transpose to get each account's balance changes across all columns. | ||||||
|     acctchanges = transposeMap colacctchanges |     acctchanges = transposeMap colacctchanges | ||||||
| 
 | 
 | ||||||
|     colacctchanges :: Map DateSpan (HashMap ClippedAccountName Account) = |     colacctchanges :: Map DateSpan (HashMap ClippedAccountName Account) = | ||||||
|       dbg'' "colacctchanges" $ fmap (acctChangesFromPostings ropts q) colps |       dbg'' "colacctchanges" $ fmap (acctChangesFromPostings ropts) colps | ||||||
| 
 | 
 | ||||||
|     elided = HM.singleton "..." $ M.fromList [(span, nullacct) | span <- colspans] |     elided = HM.singleton "..." $ M.fromList [(span, nullacct) | span <- colspans] | ||||||
| 
 | 
 | ||||||
| @ -309,15 +301,15 @@ calculateAccountChanges ropts q colspans colps | |||||||
| -- the accounts that have postings and calculate the change amount for | -- the accounts that have postings and calculate the change amount for | ||||||
| -- each. Accounts and amounts will be depth-clipped appropriately if | -- each. Accounts and amounts will be depth-clipped appropriately if | ||||||
| -- a depth limit is in effect. | -- a depth limit is in effect. | ||||||
| acctChangesFromPostings :: ReportOpts -> Query -> [Posting] -> HashMap ClippedAccountName Account | acctChangesFromPostings :: ReportOpts -> [Posting] -> HashMap ClippedAccountName Account | ||||||
| acctChangesFromPostings ropts q ps = HM.fromList [(aname a, a) | a <- as] | acctChangesFromPostings ropts ps = HM.fromList [(aname a, a) | a <- as] | ||||||
|   where |   where | ||||||
|     as = filterAccounts . drop 1 $ accountsFromPostings ps |     as = filterAccounts . drop 1 $ accountsFromPostings ps | ||||||
|     filterAccounts = case accountlistmode_ ropts of |     filterAccounts = case accountlistmode_ ropts of | ||||||
|         ALTree -> filter ((depthq `matchesAccount`) . aname)      -- exclude deeper balances |         ALTree -> filter ((depthq `matchesAccount`) . aname)      -- exclude deeper balances | ||||||
|         ALFlat -> clipAccountsAndAggregate (queryDepth depthq) .  -- aggregate deeper balances at the depth limit. |         ALFlat -> clipAccountsAndAggregate (queryDepth depthq) .  -- aggregate deeper balances at the depth limit. | ||||||
|                       filter ((0<) . anumpostings) |                       filter ((0<) . anumpostings) | ||||||
|     depthq = dbg "depthq" $ filterQuery queryIsDepth q |     depthq = dbg "depthq" . filterQuery queryIsDepth $ query_ ropts | ||||||
| 
 | 
 | ||||||
| -- | Accumulate and value amounts, as specified by the report options. | -- | Accumulate and value amounts, as specified by the report options. | ||||||
| -- | -- | ||||||
| @ -370,21 +362,19 @@ accumValueAmounts ropts j priceoracle colspans startbals acctchanges =  -- PARTI | |||||||
| -- | Lay out a set of postings grouped by date span into a regular matrix with rows | -- | 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 | -- given by AccountName and columns by DateSpan, then generate a MultiBalanceReport | ||||||
| -- from the columns. | -- from the columns. | ||||||
| generateMultiBalanceReport :: ReportOpts -> Query -> Journal -> PriceOracle | generateMultiBalanceReport :: ReportOpts -> Journal -> PriceOracle -> [DateSpan] | ||||||
|                            -> [DateSpan] |                            -> Map DateSpan [Posting] -> HashMap AccountName Account | ||||||
|                            -> Map DateSpan [Posting] |  | ||||||
|                            -> HashMap AccountName Account |  | ||||||
|                            -> MultiBalanceReport |                            -> MultiBalanceReport | ||||||
| generateMultiBalanceReport ropts q j priceoracle colspans colps startbals = report | generateMultiBalanceReport ropts j priceoracle colspans colps startbals = report | ||||||
|   where |   where | ||||||
|     -- Each account's balance changes across all columns. |     -- Each account's balance changes across all columns. | ||||||
|     acctchanges = dbg'' "acctchanges" $ calculateAccountChanges ropts q colspans colps |     acctchanges = dbg'' "acctchanges" $ calculateAccountChanges ropts colspans colps | ||||||
| 
 | 
 | ||||||
|     -- Process changes into normal, cumulative, or historical amounts, plus value them |     -- Process changes into normal, cumulative, or historical amounts, plus value them | ||||||
|     accumvalued = accumValueAmounts ropts j priceoracle colspans startbals acctchanges |     accumvalued = accumValueAmounts ropts j priceoracle colspans startbals acctchanges | ||||||
| 
 | 
 | ||||||
|     -- All account names that will be displayed, possibly depth-clipped. |     -- All account names that will be displayed, possibly depth-clipped. | ||||||
|     displaynames = dbg'' "displaynames" $ displayedAccounts ropts q accumvalued |     displaynames = dbg'' "displaynames" $ displayedAccounts ropts accumvalued | ||||||
| 
 | 
 | ||||||
|     -- All the rows of the report. |     -- All the rows of the report. | ||||||
|     rows = dbg'' "rows" $ buildReportRows ropts displaynames accumvalued |     rows = dbg'' "rows" $ buildReportRows ropts displaynames accumvalued | ||||||
| @ -423,10 +413,9 @@ buildReportRows ropts displaynames = toList . HM.mapMaybeWithKey mkRow | |||||||
| 
 | 
 | ||||||
| -- | Calculate accounts which are to be displayed in the report, as well as | -- | Calculate accounts which are to be displayed in the report, as well as | ||||||
| -- their name and depth | -- their name and depth | ||||||
| displayedAccounts :: ReportOpts -> Query | displayedAccounts :: ReportOpts -> HashMap AccountName (Map DateSpan Account) | ||||||
|                   -> HashMap AccountName (Map DateSpan Account) |  | ||||||
|                   -> HashMap AccountName DisplayName |                   -> HashMap AccountName DisplayName | ||||||
| displayedAccounts ropts q valuedaccts | displayedAccounts ropts valuedaccts | ||||||
|     | depth == 0 = HM.singleton "..." $ DisplayName "..." "..." 1 |     | depth == 0 = HM.singleton "..." $ DisplayName "..." "..." 1 | ||||||
|     | otherwise  = HM.mapWithKey (\a _ -> displayedName a) displayedAccts |     | otherwise  = HM.mapWithKey (\a _ -> displayedName a) displayedAccts | ||||||
|   where |   where | ||||||
| @ -467,7 +456,7 @@ displayedAccounts ropts q valuedaccts | |||||||
|         minSubs = if no_elide_ ropts then 1 else 2 |         minSubs = if no_elide_ ropts then 1 else 2 | ||||||
| 
 | 
 | ||||||
|     isZeroRow balance = all (mixedAmountLooksZero . balance) |     isZeroRow balance = all (mixedAmountLooksZero . balance) | ||||||
|     depth = fromMaybe maxBound $ queryDepth q |     depth = fromMaybe maxBound . queryDepth $ query_ ropts | ||||||
|     numSubs = subaccountTallies . HM.keys $ HM.filterWithKey isInteresting valuedaccts |     numSubs = subaccountTallies . HM.keys $ HM.filterWithKey isInteresting valuedaccts | ||||||
| 
 | 
 | ||||||
| -- | Sort the rows by amount or by account declaration order. | -- | Sort the rows by amount or by account declaration order. | ||||||
| @ -612,8 +601,9 @@ tests_MultiBalanceReport = tests "MultiBalanceReport" [ | |||||||
|   let |   let | ||||||
|     amt0 = Amount {acommodity="$", aquantity=0, aprice=Nothing, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asprecision = Precision 2, asdecimalpoint = Just '.', asdigitgroups = Nothing}, aismultiplier=False} |     amt0 = Amount {acommodity="$", aquantity=0, aprice=Nothing, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asprecision = Precision 2, asdecimalpoint = Just '.', asdigitgroups = Nothing}, aismultiplier=False} | ||||||
|     (opts,journal) `gives` r = do |     (opts,journal) `gives` r = do | ||||||
|       let (eitems, etotal) = r |       let opts' = opts{query_=And [queryFromFlags opts, query_ opts]} | ||||||
|           (PeriodicReport _ aitems atotal) = multiBalanceReport nulldate opts journal |           (eitems, etotal) = r | ||||||
|  |           (PeriodicReport _ aitems atotal) = multiBalanceReport opts' journal | ||||||
|           showw (PeriodicReportRow a lAmt amt amt') |           showw (PeriodicReportRow a lAmt amt amt') | ||||||
|               = (displayFull a, displayName a, displayDepth a, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt') |               = (displayFull a, displayName a, displayDepth a, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt') | ||||||
|       (map showw aitems) @?= (map showw eitems) |       (map showw aitems) @?= (map showw eitems) | ||||||
|  | |||||||
| @ -65,20 +65,20 @@ type SummaryPosting = (Posting, Day) | |||||||
| 
 | 
 | ||||||
| -- | Select postings from the journal and add running balance and other | -- | Select postings from the journal and add running balance and other | ||||||
| -- information to make a postings report. Used by eg hledger's register command. | -- information to make a postings report. Used by eg hledger's register command. | ||||||
| postingsReport :: ReportOpts -> Query -> Journal -> PostingsReport | postingsReport :: ReportOpts -> Journal -> PostingsReport | ||||||
| postingsReport ropts@ReportOpts{..} q j = | postingsReport ropts@ReportOpts{..} j = | ||||||
|   (totallabel, items) |   (totallabel, items) | ||||||
|     where |     where | ||||||
|       reportspan  = adjustReportDates ropts q j |       reportspan  = adjustReportDates ropts j | ||||||
|       whichdate   = whichDateFromOpts ropts |       whichdate   = whichDateFromOpts ropts | ||||||
|       mdepth      = queryDepth q |       mdepth      = queryDepth query_ | ||||||
|       styles      = journalCommodityStyles j |       styles      = journalCommodityStyles j | ||||||
|       priceoracle = journalPriceOracle infer_value_ j |       priceoracle = journalPriceOracle infer_value_ j | ||||||
|       multiperiod = interval_ /= NoInterval |       multiperiod = interval_ /= NoInterval | ||||||
|       today       = fromMaybe (error' "postingsReport: could not pick a valuation date, ReportOpts today_ is unset") today_  -- PARTIAL: |       today       = fromMaybe (error' "postingsReport: could not pick a valuation date, ReportOpts today_ is unset") today_  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
|       -- postings to be included in the report, and similarly-matched postings before the report start date |       -- postings to be included in the report, and similarly-matched postings before the report start date | ||||||
|       (precedingps, reportps) = matchedPostingsBeforeAndDuring ropts q j reportspan |       (precedingps, reportps) = matchedPostingsBeforeAndDuring ropts j reportspan | ||||||
| 
 | 
 | ||||||
|       -- Postings, or summary postings with their subperiod's end date, to be displayed. |       -- Postings, or summary postings with their subperiod's end date, to be displayed. | ||||||
|       displayps :: [(Posting, Maybe Day)] |       displayps :: [(Posting, Maybe Day)] | ||||||
| @ -140,11 +140,11 @@ totallabel = "Total" | |||||||
| -- 1. If the start date is unspecified, use the earliest date in the journal (if any) | -- 1. If the start date is unspecified, use the earliest date in the journal (if any) | ||||||
| -- 2. If the end date is unspecified, use the latest date in the journal (if any) | -- 2. If the end date is unspecified, use the latest date in the journal (if any) | ||||||
| -- 3. If a report interval is specified, enlarge the dates to enclose whole intervals | -- 3. If a report interval is specified, enlarge the dates to enclose whole intervals | ||||||
| adjustReportDates :: ReportOpts -> Query -> Journal -> DateSpan | adjustReportDates :: ReportOpts -> Journal -> DateSpan | ||||||
| adjustReportDates opts q j = reportspan | adjustReportDates opts j = reportspan | ||||||
|   where |   where | ||||||
|     -- see also multiBalanceReport |     -- see also multiBalanceReport | ||||||
|     requestedspan       = dbg3 "requestedspan"       $ queryDateSpan' q                                       -- span specified by -b/-e/-p options and query args |     requestedspan       = dbg3 "requestedspan"       $ queryDateSpan' $ query_ opts                           -- span specified by -b/-e/-p options and query args | ||||||
|     journalspan         = dbg3 "journalspan"         $ dates `spanUnion` date2s                               -- earliest and latest dates (or date2s) in the journal |     journalspan         = dbg3 "journalspan"         $ dates `spanUnion` date2s                               -- earliest and latest dates (or date2s) in the journal | ||||||
|       where |       where | ||||||
|         dates  = journalDateSpan False j |         dates  = journalDateSpan False j | ||||||
| @ -159,10 +159,11 @@ adjustReportDates opts q j = reportspan | |||||||
| -- and also any similarly-matched postings before that date span. | -- and also any similarly-matched postings before that date span. | ||||||
| -- Date restrictions and depth restrictions in the query are ignored. | -- Date restrictions and depth restrictions in the query are ignored. | ||||||
| -- A helper for the postings report. | -- A helper for the postings report. | ||||||
| matchedPostingsBeforeAndDuring :: ReportOpts -> Query -> Journal -> DateSpan -> ([Posting],[Posting]) | matchedPostingsBeforeAndDuring :: ReportOpts -> Journal -> DateSpan -> ([Posting],[Posting]) | ||||||
| matchedPostingsBeforeAndDuring opts q j (DateSpan mstart mend) = | matchedPostingsBeforeAndDuring opts j (DateSpan mstart mend) = | ||||||
|   dbg5 "beforeps, duringps" $ span (beforestartq `matchesPosting`) beforeandduringps |   dbg5 "beforeps, duringps" $ span (beforestartq `matchesPosting`) beforeandduringps | ||||||
|   where |   where | ||||||
|  |     q = query_ opts | ||||||
|     beforestartq = dbg3 "beforestartq" $ dateqtype $ DateSpan Nothing mstart |     beforestartq = dbg3 "beforestartq" $ dateqtype $ DateSpan Nothing mstart | ||||||
|     beforeandduringps = |     beforeandduringps = | ||||||
|       dbg5 "ps5" $ sortOn sortdate $                                           -- sort postings by date or date2 |       dbg5 "ps5" $ sortOn sortdate $                                           -- sort postings by date or date2 | ||||||
| @ -179,7 +180,7 @@ matchedPostingsBeforeAndDuring opts q j (DateSpan mstart mend) = | |||||||
|             dateless   = filterQuery (not . queryIsDateOrDate2) |             dateless   = filterQuery (not . queryIsDateOrDate2) | ||||||
|             beforeendq = dateqtype $ DateSpan Nothing mend |             beforeendq = dateqtype $ DateSpan Nothing mend | ||||||
|         sortdate = if date2_ opts then postingDate2 else postingDate |         sortdate = if date2_ opts then postingDate2 else postingDate | ||||||
|         symq = dbg4 "symq" $ filterQuery queryIsSym q |         symq = dbg4 "symq" . filterQuery queryIsSym $ query_ opts | ||||||
|     dateqtype |     dateqtype | ||||||
|       | queryIsDate2 dateq || (queryIsDate dateq && date2_ opts) = Date2 |       | queryIsDate2 dateq || (queryIsDate dateq && date2_ opts) = Date2 | ||||||
|       | otherwise = Date |       | otherwise = Date | ||||||
| @ -270,7 +271,7 @@ negatePostingAmount p = p { pamount = negate $ pamount p } | |||||||
| tests_PostingsReport = tests "PostingsReport" [ | tests_PostingsReport = tests "PostingsReport" [ | ||||||
| 
 | 
 | ||||||
|    test "postingsReport" $ do |    test "postingsReport" $ do | ||||||
|     let (query, journal) `gives` n = (length $ snd $ postingsReport defreportopts query journal) @?= n |     let (query, journal) `gives` n = (length $ snd $ postingsReport defreportopts{query_=query} journal) @?= n | ||||||
|     -- with the query specified explicitly |     -- with the query specified explicitly | ||||||
|     (Any, nulljournal) `gives` 0 |     (Any, nulljournal) `gives` 0 | ||||||
|     (Any, samplejournal) `gives` 13 |     (Any, samplejournal) `gives` 13 | ||||||
| @ -279,10 +280,10 @@ tests_PostingsReport = tests "PostingsReport" [ | |||||||
|     (And [Depth 1, StatusQ Cleared, Acct (toRegex' "expenses")], samplejournal) `gives` 2 |     (And [Depth 1, StatusQ Cleared, Acct (toRegex' "expenses")], samplejournal) `gives` 2 | ||||||
|     (And [And [Depth 1, StatusQ Cleared], Acct (toRegex' "expenses")], samplejournal) `gives` 2 |     (And [And [Depth 1, StatusQ Cleared], Acct (toRegex' "expenses")], samplejournal) `gives` 2 | ||||||
|     -- with query and/or command-line options |     -- with query and/or command-line options | ||||||
|     (length $ snd $ postingsReport defreportopts Any samplejournal) @?= 13 |     (length $ snd $ postingsReport defreportopts samplejournal) @?= 13 | ||||||
|     (length $ snd $ postingsReport defreportopts{interval_=Months 1} Any samplejournal) @?= 11 |     (length $ snd $ postingsReport defreportopts{interval_=Months 1} samplejournal) @?= 11 | ||||||
|     (length $ snd $ postingsReport defreportopts{interval_=Months 1, empty_=True} Any samplejournal) @?= 20 |     (length $ snd $ postingsReport defreportopts{interval_=Months 1, empty_=True} samplejournal) @?= 20 | ||||||
|     (length $ snd $ postingsReport defreportopts (Acct (toRegex' "assets:bank:checking")) samplejournal) @?= 5 |     (length $ snd $ postingsReport defreportopts{query_=Acct $ toRegex' "assets:bank:checking"} samplejournal) @?= 5 | ||||||
| 
 | 
 | ||||||
|      -- (defreportopts, And [Acct "a a", Acct "'b"], samplejournal2) `gives` 0 |      -- (defreportopts, And [Acct "a a", Acct "'b"], samplejournal2) `gives` 0 | ||||||
|      -- [(Just (fromGregorian 2008 01 01,"income"),assets:bank:checking             $1,$1) |      -- [(Just (fromGregorian 2008 01 01,"income"),assets:bank:checking             $1,$1) | ||||||
|  | |||||||
| @ -23,9 +23,7 @@ module Hledger.Reports.ReportOptions ( | |||||||
|   journalSelectingAmountFromOpts, |   journalSelectingAmountFromOpts, | ||||||
|   intervalFromRawOpts, |   intervalFromRawOpts, | ||||||
|   forecastPeriodFromRawOpts, |   forecastPeriodFromRawOpts, | ||||||
|   queryFromOpts, |   queryFromFlags, | ||||||
|   queryFromOptsOnly, |  | ||||||
|   queryOptsFromOpts, |  | ||||||
|   transactionDateFn, |   transactionDateFn, | ||||||
|   postingDateFn, |   postingDateFn, | ||||||
|   reportSpan, |   reportSpan, | ||||||
| @ -40,8 +38,6 @@ module Hledger.Reports.ReportOptions ( | |||||||
|   reportPeriodOrJournalLastDay, |   reportPeriodOrJournalLastDay, | ||||||
|   valuationTypeIsCost, |   valuationTypeIsCost, | ||||||
|   valuationTypeIsDefaultValue, |   valuationTypeIsDefaultValue, | ||||||
| 
 |  | ||||||
|   tests_ReportOptions |  | ||||||
| ) | ) | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
| @ -49,7 +45,7 @@ import Control.Applicative ((<|>)) | |||||||
| import Data.List.Extra (nubSort) | import Data.List.Extra (nubSort) | ||||||
| import Data.Maybe (fromMaybe, isJust) | import Data.Maybe (fromMaybe, isJust) | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| import Data.Time.Calendar (Day, addDays, fromGregorian) | import Data.Time.Calendar (Day, addDays) | ||||||
| import Data.Default (Default(..)) | import Data.Default (Default(..)) | ||||||
| import Safe (lastDef, lastMay) | import Safe (lastDef, lastMay) | ||||||
| 
 | 
 | ||||||
| @ -99,8 +95,8 @@ data ReportOpts = ReportOpts { | |||||||
|     ,no_elide_       :: Bool |     ,no_elide_       :: Bool | ||||||
|     ,real_           :: Bool |     ,real_           :: Bool | ||||||
|     ,format_         :: StringFormat |     ,format_         :: StringFormat | ||||||
|     ,query_          :: String -- ^ All query arguments space sepeareted |     ,query_          :: Query | ||||||
|                                --   and quoted if needed (see 'quoteIfNeeded') |     ,queryopts_      :: [QueryOpt] | ||||||
|     -- |     -- | ||||||
|     ,average_        :: Bool |     ,average_        :: Bool | ||||||
|     -- for posting reports (register) |     -- for posting reports (register) | ||||||
| @ -167,21 +163,28 @@ defreportopts = ReportOpts | |||||||
|     def |     def | ||||||
|     def |     def | ||||||
|     def |     def | ||||||
|  |     def | ||||||
| 
 | 
 | ||||||
| rawOptsToReportOpts :: RawOpts -> IO ReportOpts | rawOptsToReportOpts :: RawOpts -> IO ReportOpts | ||||||
| rawOptsToReportOpts rawopts = do | rawOptsToReportOpts rawopts = do | ||||||
|     d <- getCurrentDay |     d <- getCurrentDay | ||||||
|     no_color <- isJust <$> lookupEnv "NO_COLOR" |     no_color <- isJust <$> lookupEnv "NO_COLOR" | ||||||
|     supports_color <- hSupportsANSIColor stdout |     supports_color <- hSupportsANSIColor stdout | ||||||
|     let colorflag = stringopt "color" rawopts |  | ||||||
| 
 | 
 | ||||||
|     format <- case parseStringFormat <$> maybestringopt "format" rawopts of |     let colorflag    = stringopt "color" rawopts | ||||||
|  |         formatstring = maybestringopt "format" rawopts | ||||||
|  |         querystring  = T.pack . unwords . map quoteIfNeeded $ | ||||||
|  |                         listofstringopt "args" rawopts  -- doesn't handle an arg like "" right | ||||||
|  | 
 | ||||||
|  |     format <- case parseStringFormat <$> formatstring of | ||||||
|         Nothing         -> return defaultBalanceLineFormat |         Nothing         -> return defaultBalanceLineFormat | ||||||
|         Just (Right x)  -> return x |         Just (Right x)  -> return x | ||||||
|          Just (Left err) -> usageError $ "could not parse format option: " ++ err |         Just (Left err) -> fail $ "could not parse format option: " ++ err | ||||||
| 
 | 
 | ||||||
|     return defreportopts{ |     (argsquery, queryopts) <- either fail return $ parseQuery d querystring | ||||||
|        today_       = Just d | 
 | ||||||
|  |     let reportopts = defreportopts | ||||||
|  |           {today_       = Just d | ||||||
|           ,period_      = periodFromRawOpts d rawopts |           ,period_      = periodFromRawOpts d rawopts | ||||||
|           ,interval_    = intervalFromRawOpts rawopts |           ,interval_    = intervalFromRawOpts rawopts | ||||||
|           ,statuses_    = statusesFromRawOpts rawopts |           ,statuses_    = statusesFromRawOpts rawopts | ||||||
| @ -193,7 +196,8 @@ rawOptsToReportOpts rawopts = do | |||||||
|           ,no_elide_    = boolopt "no-elide" rawopts |           ,no_elide_    = boolopt "no-elide" rawopts | ||||||
|           ,real_        = boolopt "real" rawopts |           ,real_        = boolopt "real" rawopts | ||||||
|           ,format_      = format |           ,format_      = format | ||||||
|       ,query_       = unwords . map quoteIfNeeded $ listofstringopt "args" rawopts -- doesn't handle an arg like "" right |           ,query_       = simplifyQuery $ And [queryFromFlags reportopts, argsquery] | ||||||
|  |           ,queryopts_   = queryopts | ||||||
|           ,average_     = boolopt "average" rawopts |           ,average_     = boolopt "average" rawopts | ||||||
|           ,related_     = boolopt "related" rawopts |           ,related_     = boolopt "related" rawopts | ||||||
|           ,txn_dates_   = boolopt "txn-dates" rawopts |           ,txn_dates_   = boolopt "txn-dates" rawopts | ||||||
| @ -214,6 +218,8 @@ rawOptsToReportOpts rawopts = do | |||||||
|           ,transpose_   = boolopt "transpose" rawopts |           ,transpose_   = boolopt "transpose" rawopts | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|  |     return reportopts | ||||||
|  | 
 | ||||||
| accountlistmodeopt :: RawOpts -> AccountListMode | accountlistmodeopt :: RawOpts -> AccountListMode | ||||||
| accountlistmodeopt = | accountlistmodeopt = | ||||||
|   fromMaybe ALFlat . choiceopt parse where |   fromMaybe ALFlat . choiceopt parse where | ||||||
| @ -423,17 +429,9 @@ journalSelectingAmountFromOpts opts = | |||||||
|     Just (AtCost _) -> journalToCost |     Just (AtCost _) -> journalToCost | ||||||
|     _               -> id |     _               -> id | ||||||
| 
 | 
 | ||||||
| -- | Convert report options and arguments to a query. |  | ||||||
| -- If there is a parsing problem, this function calls error. |  | ||||||
| queryFromOpts :: Day -> ReportOpts -> Query |  | ||||||
| queryFromOpts d ropts = simplifyQuery . And $ [flagsq, argsq] |  | ||||||
|   where |  | ||||||
|     flagsq = queryFromOptsOnly d ropts |  | ||||||
|     argsq = fst $ either error' id $ parseQuery d (T.pack $ query_ ropts)  -- PARTIAL: |  | ||||||
| 
 |  | ||||||
| -- | Convert report options to a query, ignoring any non-flag command line arguments. | -- | Convert report options to a query, ignoring any non-flag command line arguments. | ||||||
| queryFromOptsOnly :: Day -> ReportOpts -> Query | queryFromFlags :: ReportOpts -> Query | ||||||
| queryFromOptsOnly _d ReportOpts{..} = simplifyQuery $ And flagsq | queryFromFlags ReportOpts{..} = simplifyQuery $ And flagsq | ||||||
|   where |   where | ||||||
|     flagsq = consIf   Real  real_ |     flagsq = consIf   Real  real_ | ||||||
|            . consIf   Empty empty_ |            . consIf   Empty empty_ | ||||||
| @ -444,11 +442,6 @@ queryFromOptsOnly _d ReportOpts{..} = simplifyQuery $ And flagsq | |||||||
|     consIf f b = if b then (f True:) else id |     consIf f b = if b then (f True:) else id | ||||||
|     consJust f = maybe id ((:) . f) |     consJust f = maybe id ((:) . f) | ||||||
| 
 | 
 | ||||||
| -- | Convert report options and arguments to query options. |  | ||||||
| -- If there is a parsing problem, this function calls error. |  | ||||||
| queryOptsFromOpts :: Day -> ReportOpts -> [QueryOpt] |  | ||||||
| queryOptsFromOpts d = snd . either error' id . parseQuery d . T.pack . query_  -- PARTIAL: |  | ||||||
| 
 |  | ||||||
| -- Report dates. | -- Report dates. | ||||||
| 
 | 
 | ||||||
| -- | The effective report span is the start and end dates specified by | -- | The effective report span is the start and end dates specified by | ||||||
| @ -477,9 +470,8 @@ reportEndDate j ropts = spanEnd <$> reportSpan j ropts | |||||||
| -- Needs IO to parse smart dates in options/queries. | -- Needs IO to parse smart dates in options/queries. | ||||||
| specifiedStartEndDates :: ReportOpts -> IO (Maybe Day, Maybe Day) | specifiedStartEndDates :: ReportOpts -> IO (Maybe Day, Maybe Day) | ||||||
| specifiedStartEndDates ropts = do | specifiedStartEndDates ropts = do | ||||||
|   today <- getCurrentDay |  | ||||||
|   let |   let | ||||||
|     q = queryFromOpts today ropts |     q = query_ ropts | ||||||
|     mspecifiedstartdate = queryStartDate False q |     mspecifiedstartdate = queryStartDate False q | ||||||
|     mspecifiedenddate   = queryEndDate   False q |     mspecifiedenddate   = queryEndDate   False q | ||||||
|   return (mspecifiedstartdate, mspecifiedenddate) |   return (mspecifiedstartdate, mspecifiedenddate) | ||||||
| @ -498,9 +490,7 @@ specifiedEndDate ropts = snd <$> specifiedStartEndDates ropts | |||||||
| -- since we need that to get the report period robustly | -- since we need that to get the report period robustly | ||||||
| -- (unlike reportStartDate, which looks up the date with IO.) | -- (unlike reportStartDate, which looks up the date with IO.) | ||||||
| reportPeriodStart :: ReportOpts -> Maybe Day | reportPeriodStart :: ReportOpts -> Maybe Day | ||||||
| reportPeriodStart ropts@ReportOpts{..} = do | reportPeriodStart = queryStartDate False . query_ | ||||||
|   t <- today_ |  | ||||||
|   queryStartDate False $ queryFromOpts t ropts |  | ||||||
| 
 | 
 | ||||||
| -- Get the report's start date, or if no report period is specified, | -- Get the report's start date, or if no report period is specified, | ||||||
| -- the journal's start date (the earliest posting date). If there's no | -- the journal's start date (the earliest posting date). If there's no | ||||||
| @ -517,11 +507,7 @@ reportPeriodOrJournalStart ropts j = | |||||||
| -- since we need that to get the report period robustly | -- since we need that to get the report period robustly | ||||||
| -- (unlike reportEndDate, which looks up the date with IO.) | -- (unlike reportEndDate, which looks up the date with IO.) | ||||||
| reportPeriodLastDay :: ReportOpts -> Maybe Day | reportPeriodLastDay :: ReportOpts -> Maybe Day | ||||||
| reportPeriodLastDay ropts@ReportOpts{..} = do | reportPeriodLastDay = fmap (addDays (-1)) . queryEndDate False . query_ | ||||||
|   t <- today_ |  | ||||||
|   let q = queryFromOpts t ropts |  | ||||||
|   qend <- queryEndDate False q |  | ||||||
|   return $ addDays (-1) qend |  | ||||||
| 
 | 
 | ||||||
| -- Get the last day of the overall report period, or if no report | -- Get the last day of the overall report period, or if no report | ||||||
| -- period is specified, the last day of the journal (ie the latest | -- period is specified, the last day of the journal (ie the latest | ||||||
| @ -530,22 +516,3 @@ reportPeriodLastDay ropts@ReportOpts{..} = do | |||||||
| reportPeriodOrJournalLastDay :: ReportOpts -> Journal -> Maybe Day | reportPeriodOrJournalLastDay :: ReportOpts -> Journal -> Maybe Day | ||||||
| reportPeriodOrJournalLastDay ropts j = | reportPeriodOrJournalLastDay ropts j = | ||||||
|   reportPeriodLastDay ropts <|> journalEndDate False j |   reportPeriodLastDay ropts <|> journalEndDate False j | ||||||
| 
 |  | ||||||
| -- tests |  | ||||||
| 
 |  | ||||||
| tests_ReportOptions = tests "ReportOptions" [ |  | ||||||
|    test "queryFromOpts" $ do |  | ||||||
|        queryFromOpts nulldate defreportopts @?= Any |  | ||||||
|        queryFromOpts nulldate defreportopts{query_="a"} @?= Acct (toRegexCI' "a") |  | ||||||
|        queryFromOpts nulldate defreportopts{query_="desc:'a a'"} @?= Desc (toRegexCI' "a a") |  | ||||||
|        queryFromOpts nulldate defreportopts{period_=PeriodFrom (fromGregorian 2012 01 01),query_="date:'to 2013'" } |  | ||||||
|          @?= (Date $ DateSpan (Just $ fromGregorian 2012 01 01) (Just $ fromGregorian 2013 01 01)) |  | ||||||
|        queryFromOpts nulldate defreportopts{query_="date2:'in 2012'"} @?= (Date2 $ DateSpan (Just $ fromGregorian 2012 01 01) (Just $ fromGregorian 2013 01 01)) |  | ||||||
|        queryFromOpts nulldate defreportopts{query_="'a a' 'b"} @?= Or [Acct $ toRegexCI' "a a", Acct $ toRegexCI' "'b"] |  | ||||||
| 
 |  | ||||||
|   ,test "queryOptsFromOpts" $ do |  | ||||||
|       queryOptsFromOpts nulldate defreportopts @?= [] |  | ||||||
|       queryOptsFromOpts nulldate defreportopts{query_="a"} @?= [] |  | ||||||
|       queryOptsFromOpts nulldate defreportopts{period_=PeriodFrom (fromGregorian 2012 01 01) |  | ||||||
|                                               ,query_="date:'to 2013'"} @?= [] |  | ||||||
|  ] |  | ||||||
|  | |||||||
| @ -518,8 +518,7 @@ getHledgerCliOpts' mode' args' = do | |||||||
|         putStrLn $ "running: " ++ progname' |         putStrLn $ "running: " ++ progname' | ||||||
|         putStrLn $ "raw args: " ++ show args' |         putStrLn $ "raw args: " ++ show args' | ||||||
|         putStrLn $ "processed opts:\n" ++ show opts |         putStrLn $ "processed opts:\n" ++ show opts | ||||||
|         d <- getCurrentDay |         putStrLn $ "search query: " ++ show (query_ $ reportopts_ opts) | ||||||
|         putStrLn $ "search query: " ++ show (queryFromOpts d $ reportopts_ opts) |  | ||||||
| 
 | 
 | ||||||
| getHledgerCliOpts :: Mode RawOpts -> IO CliOpts | getHledgerCliOpts :: Mode RawOpts -> IO CliOpts | ||||||
| getHledgerCliOpts mode' = do | getHledgerCliOpts mode' = do | ||||||
|  | |||||||
| @ -277,7 +277,7 @@ testmode = hledgerCommandMode | |||||||
| -- | -- | ||||||
| testcmd :: CliOpts -> Journal -> IO () | testcmd :: CliOpts -> Journal -> IO () | ||||||
| testcmd opts _undefined = do | testcmd opts _undefined = do | ||||||
|   withArgs (words' $ query_ $ reportopts_ opts) $ |   withArgs (listofstringopt "args" $ rawopts_ opts) $ | ||||||
|     Test.Tasty.defaultMain $ tests "hledger" [ |     Test.Tasty.defaultMain $ tests "hledger" [ | ||||||
|        tests_Hledger |        tests_Hledger | ||||||
|       ,tests_Hledger_Cli |       ,tests_Hledger_Cli | ||||||
|  | |||||||
| @ -51,11 +51,10 @@ accounts :: CliOpts -> Journal -> IO () | |||||||
| accounts CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | accounts CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | ||||||
| 
 | 
 | ||||||
|   -- 1. identify the accounts we'll show |   -- 1. identify the accounts we'll show | ||||||
|   d <- getCurrentDay |  | ||||||
|   let tree     = tree_ ropts |   let tree     = tree_ ropts | ||||||
|       declared = boolopt "declared" rawopts |       declared = boolopt "declared" rawopts | ||||||
|       used     = boolopt "used"     rawopts |       used     = boolopt "used"     rawopts | ||||||
|       q        = queryFromOpts d ropts |       q        = query_ ropts | ||||||
|       -- a depth limit will clip and exclude account names later, but we don't want to exclude accounts at this stage |       -- a depth limit will clip and exclude account names later, but we don't want to exclude accounts at this stage | ||||||
|       nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q |       nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q | ||||||
|       -- just the acct: part of the query will be reapplied later, after clipping |       -- just the acct: part of the query will be reapplied later, after clipping | ||||||
|  | |||||||
| @ -30,17 +30,15 @@ barchar = '*' | |||||||
| 
 | 
 | ||||||
| -- | Print a bar chart of number of postings per report interval. | -- | Print a bar chart of number of postings per report interval. | ||||||
| activity :: CliOpts -> Journal -> IO () | activity :: CliOpts -> Journal -> IO () | ||||||
| activity CliOpts{reportopts_=ropts} j = do | activity CliOpts{reportopts_=ropts} j = putStr $ showHistogram ropts j | ||||||
|   d <- getCurrentDay |  | ||||||
|   putStr $ showHistogram ropts (queryFromOpts d ropts) j |  | ||||||
| 
 | 
 | ||||||
| showHistogram :: ReportOpts -> Query -> Journal -> String | showHistogram :: ReportOpts -> Journal -> String | ||||||
| showHistogram opts q j = concatMap (printDayWith countBar) spanps | showHistogram ReportOpts{query_=q,interval_=i,date2_=date2} j = | ||||||
|  |     concatMap (printDayWith countBar) spanps | ||||||
|   where |   where | ||||||
|       i = interval_ opts |  | ||||||
|     interval | i == NoInterval = Days 1 |     interval | i == NoInterval = Days 1 | ||||||
|              | otherwise = i |              | otherwise = i | ||||||
|       span' = queryDateSpan (date2_ opts) q `spanDefaultsFrom` journalDateSpan (date2_ opts) j |     span' = queryDateSpan date2 q `spanDefaultsFrom` journalDateSpan date2 j | ||||||
|     spans = filter (DateSpan Nothing Nothing /=) $ splitSpan interval span' |     spans = filter (DateSpan Nothing Nothing /=) $ splitSpan interval span' | ||||||
|     spanps = [(s, filter (isPostingInDateSpan s) ps) | s <- spans] |     spanps = [(s, filter (isPostingInDateSpan s) ps) | s <- spans] | ||||||
|     -- same as Register |     -- same as Register | ||||||
|  | |||||||
| @ -255,7 +255,7 @@ confirmedTransactionWizard prevInput es@EntryState{..} stack@(currentStage : _) | |||||||
| -- Identify the closest recent match for this description in past transactions. | -- Identify the closest recent match for this description in past transactions. | ||||||
| similarTransaction :: EntryState -> Text -> Maybe Transaction | similarTransaction :: EntryState -> Text -> Maybe Transaction | ||||||
| similarTransaction EntryState{..} desc = | similarTransaction EntryState{..} desc = | ||||||
|   let q = queryFromOptsOnly esToday $ reportopts_ esOpts |   let q = queryFromFlags $ reportopts_ esOpts | ||||||
|       historymatches = transactionsSimilarTo esJournal q desc |       historymatches = transactionsSimilarTo esJournal q desc | ||||||
|       bestmatch | null historymatches = Nothing |       bestmatch | null historymatches = Nothing | ||||||
|                 | otherwise           = Just $ snd $ head historymatches |                 | otherwise           = Just $ snd $ head historymatches | ||||||
| @ -461,9 +461,8 @@ ensureOneNewlineTerminated = (++"\n") . reverse . dropWhile (=='\n') . reverse | |||||||
| -- | Convert a string of journal data into a register report. | -- | Convert a string of journal data into a register report. | ||||||
| registerFromString :: String -> IO String | registerFromString :: String -> IO String | ||||||
| registerFromString s = do | registerFromString s = do | ||||||
|   d <- getCurrentDay |  | ||||||
|   j <- readJournal' $ T.pack s |   j <- readJournal' $ T.pack s | ||||||
|   return $ postingsReportAsText opts $ postingsReport ropts (queryFromOpts d ropts) j |   return . postingsReportAsText opts $ postingsReport ropts j | ||||||
|       where |       where | ||||||
|         ropts = defreportopts{empty_=True} |         ropts = defreportopts{empty_=True} | ||||||
|         opts = defcliopts{reportopts_=ropts} |         opts = defcliopts{reportopts_=ropts} | ||||||
|  | |||||||
| @ -19,7 +19,6 @@ module Hledger.Cli.Commands.Aregister ( | |||||||
|  ,tests_Aregister |  ,tests_Aregister | ||||||
| ) where | ) where | ||||||
| 
 | 
 | ||||||
| import Control.Monad (when) |  | ||||||
| import Data.Aeson (toJSON) | import Data.Aeson (toJSON) | ||||||
| import Data.Aeson.Text (encodeToLazyText) | import Data.Aeson.Text (encodeToLazyText) | ||||||
| import Data.List | import Data.List | ||||||
| @ -75,10 +74,11 @@ aregister :: CliOpts -> Journal -> IO () | |||||||
| aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   -- the first argument specifies the account, any remaining arguments are a filter query |   -- the first argument specifies the account, any remaining arguments are a filter query | ||||||
|   let args' = listofstringopt "args" rawopts |   (apat,querystring) <- case listofstringopt "args" rawopts of | ||||||
|   when (null args') $ error' "aregister needs an account, please provide an account name or pattern"  -- PARTIAL: |       []     -> fail "aregister needs an account, please provide an account name or pattern" | ||||||
|  |       (a:as) -> return (a, T.pack . unwords $ map quoteIfNeeded as) | ||||||
|  |   argsquery <- either fail (return . fst) $ parseQuery d querystring | ||||||
|   let |   let | ||||||
|     (apat:queryargs) = args' |  | ||||||
|     acct = headDef (error' $ show apat++" did not match any account")   -- PARTIAL: |     acct = headDef (error' $ show apat++" did not match any account")   -- PARTIAL: | ||||||
|            . filterAccts $ journalAccountNames j |            . filterAccts $ journalAccountNames j | ||||||
|     filterAccts = case toRegexCI apat of |     filterAccts = case toRegexCI apat of | ||||||
| @ -88,13 +88,13 @@ aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | |||||||
|     inclusive = True  -- tree_ ropts |     inclusive = True  -- tree_ ropts | ||||||
|     thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) acct |     thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) acct | ||||||
|     ropts' = ropts{ |     ropts' = ropts{ | ||||||
|        query_=unwords $ map quoteIfNeeded $ queryargs |        query_=simplifyQuery $ And [queryFromFlags ropts, argsquery] | ||||||
|        -- remove a depth limit for reportq, as in RegisterScreen, I forget why XXX |        -- remove a depth limit for reportq, as in RegisterScreen, I forget why XXX | ||||||
|       ,depth_=Nothing |       ,depth_=Nothing | ||||||
|        -- always show historical balance |        -- always show historical balance | ||||||
|       ,balancetype_= HistoricalBalance |       ,balancetype_= HistoricalBalance | ||||||
|       } |       } | ||||||
|     reportq = And [queryFromOpts d ropts', excludeforecastq (isJust $ forecast_ ropts)] |     reportq = And [query_ ropts', excludeforecastq (isJust $ forecast_ ropts)] | ||||||
|       where |       where | ||||||
|         -- As in RegisterScreen, why ? XXX |         -- As in RegisterScreen, why ? XXX | ||||||
|         -- Except in forecast mode, exclude future/forecast transactions. |         -- Except in forecast mode, exclude future/forecast transactions. | ||||||
|  | |||||||
| @ -305,14 +305,13 @@ balancemode = hledgerCommandMode | |||||||
| -- | The balance command, prints a balance report. | -- | The balance command, prints a balance report. | ||||||
| balance :: CliOpts -> Journal -> IO () | balance :: CliOpts -> Journal -> IO () | ||||||
| balance opts@CliOpts{rawopts_=rawopts,reportopts_=ropts@ReportOpts{..}} j = do | balance opts@CliOpts{rawopts_=rawopts,reportopts_=ropts@ReportOpts{..}} j = do | ||||||
|     d <- getCurrentDay |  | ||||||
|     let budget      = boolopt "budget" rawopts |     let budget      = boolopt "budget" rawopts | ||||||
|         multiperiod = interval_ /= NoInterval |         multiperiod = interval_ /= NoInterval | ||||||
|         fmt         = outputFormatFromOpts opts |         fmt         = outputFormatFromOpts opts | ||||||
| 
 | 
 | ||||||
|     if budget then do  -- single or multi period budget report |     if budget then do  -- single or multi period budget report | ||||||
|       reportspan <- reportSpan j ropts |       reportspan <- reportSpan j ropts | ||||||
|       let budgetreport = dbg4 "budgetreport" $ budgetReport ropts assrt reportspan d j |       let budgetreport = dbg4 "budgetreport" $ budgetReport ropts assrt reportspan j | ||||||
|             where |             where | ||||||
|               assrt = not $ ignore_assertions_ $ inputopts_ opts |               assrt = not $ ignore_assertions_ $ inputopts_ opts | ||||||
|           render = case fmt of |           render = case fmt of | ||||||
| @ -323,7 +322,7 @@ balance opts@CliOpts{rawopts_=rawopts,reportopts_=ropts@ReportOpts{..}} j = do | |||||||
| 
 | 
 | ||||||
|     else |     else | ||||||
|       if multiperiod then do  -- multi period balance report |       if multiperiod then do  -- multi period balance report | ||||||
|         let report = multiBalanceReport d ropts j |         let report = multiBalanceReport ropts j | ||||||
|             render = case fmt of |             render = case fmt of | ||||||
|               "txt"  -> multiBalanceReportAsText ropts |               "txt"  -> multiBalanceReportAsText ropts | ||||||
|               "csv"  -> (++"\n") . printCSV . multiBalanceReportAsCsv ropts |               "csv"  -> (++"\n") . printCSV . multiBalanceReportAsCsv ropts | ||||||
| @ -333,7 +332,7 @@ balance opts@CliOpts{rawopts_=rawopts,reportopts_=ropts@ReportOpts{..}} j = do | |||||||
|         writeOutput opts $ render report |         writeOutput opts $ render report | ||||||
| 
 | 
 | ||||||
|       else do  -- single period simple balance report |       else do  -- single period simple balance report | ||||||
|         let report = balanceReport ropts (queryFromOpts d ropts) j -- simple Ledger-style balance report |         let report = balanceReport ropts j -- simple Ledger-style balance report | ||||||
|             render = case fmt of |             render = case fmt of | ||||||
|               "txt"  -> balanceReportAsText |               "txt"  -> balanceReportAsText | ||||||
|               "csv"  -> \ropts r -> (++ "\n") $ printCSV $ balanceReportAsCsv ropts r |               "csv"  -> \ropts r -> (++ "\n") $ printCSV $ balanceReportAsCsv ropts r | ||||||
| @ -622,7 +621,7 @@ tests_Balance = tests "Balance" [ | |||||||
|     test "unicode in balance layout" $ do |     test "unicode in balance layout" $ do | ||||||
|       j <- readJournal' "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n" |       j <- readJournal' "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n" | ||||||
|       let opts = defreportopts |       let opts = defreportopts | ||||||
|       balanceReportAsText opts (balanceReport opts (queryFromOpts (fromGregorian 2008 11 26) opts) j) |       balanceReportAsText opts (balanceReport opts{today_=Just $ fromGregorian 2008 11 26} j) | ||||||
|         @?= |         @?= | ||||||
|         unlines |         unlines | ||||||
|         ["                -100  актив:наличные" |         ["                -100  актив:наличные" | ||||||
|  | |||||||
| @ -22,10 +22,8 @@ checkdatesmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| checkdates :: CliOpts -> Journal -> IO () | checkdates :: CliOpts -> Journal -> IO () | ||||||
| checkdates CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | checkdates CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |  | ||||||
|   let ropts_ = ropts{accountlistmode_=ALFlat} |   let ropts_ = ropts{accountlistmode_=ALFlat} | ||||||
|   let q = queryFromOpts d ropts_ |   let ts = filter (query_ ropts_ `matchesTransaction`) $ | ||||||
|   let ts = filter (q `matchesTransaction`) $ |  | ||||||
|            jtxns $ journalSelectingAmountFromOpts ropts j |            jtxns $ journalSelectingAmountFromOpts ropts j | ||||||
|   let strict = boolopt "strict" rawopts |   let strict = boolopt "strict" rawopts | ||||||
|   let date = transactionDateFn ropts |   let date = transactionDateFn ropts | ||||||
|  | |||||||
| @ -73,7 +73,7 @@ close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | |||||||
| 
 | 
 | ||||||
|     -- dates of the closing and opening transactions |     -- dates of the closing and opening transactions | ||||||
|     ropts_ = ropts{balancetype_=HistoricalBalance, accountlistmode_=ALFlat} |     ropts_ = ropts{balancetype_=HistoricalBalance, accountlistmode_=ALFlat} | ||||||
|     q = queryFromOpts today ropts_ |     q = query_ ropts_ | ||||||
|     openingdate = fromMaybe today $ queryEndDate False q |     openingdate = fromMaybe today $ queryEndDate False q | ||||||
|     closingdate = addDays (-1) openingdate |     closingdate = addDays (-1) openingdate | ||||||
| 
 | 
 | ||||||
| @ -86,7 +86,7 @@ close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | |||||||
|                   False -> normaliseMixedAmount . mixedAmountStripPrices |                   False -> normaliseMixedAmount . mixedAmountStripPrices | ||||||
| 
 | 
 | ||||||
|     -- the balances to close |     -- the balances to close | ||||||
|     (acctbals,_) = balanceReport ropts_ q j |     (acctbals,_) = balanceReport ropts_ j | ||||||
|     totalamt = sum $ map (\(_,_,_,b) -> normalise b) acctbals |     totalamt = sum $ map (\(_,_,_,b) -> normalise b) acctbals | ||||||
| 
 | 
 | ||||||
|     -- since balance assertion amounts are required to be exact, the |     -- since balance assertion amounts are required to be exact, the | ||||||
|  | |||||||
| @ -33,10 +33,7 @@ codesmode = hledgerCommandMode | |||||||
| -- | The codes command. | -- | The codes command. | ||||||
| codes :: CliOpts -> Journal -> IO () | codes :: CliOpts -> Journal -> IO () | ||||||
| codes CliOpts{reportopts_=ropts@ReportOpts{empty_}} j = do | codes CliOpts{reportopts_=ropts@ReportOpts{empty_}} j = do | ||||||
|   d <- getCurrentDay |   let ts = entriesReport ropts j | ||||||
|   let q  = queryFromOpts d ropts |  | ||||||
|       ts = entriesReport ropts q j |  | ||||||
|       codes = (if empty_ then id else filter (not . T.null)) $ |       codes = (if empty_ then id else filter (not . T.null)) $ | ||||||
|               map tcode ts |               map tcode ts | ||||||
| 
 |  | ||||||
|   mapM_ T.putStrLn codes |   mapM_ T.putStrLn codes | ||||||
|  | |||||||
| @ -32,9 +32,7 @@ descriptionsmode = hledgerCommandMode | |||||||
| -- | The descriptions command. | -- | The descriptions command. | ||||||
| descriptions :: CliOpts -> Journal -> IO () | descriptions :: CliOpts -> Journal -> IO () | ||||||
| descriptions CliOpts{reportopts_=ropts} j = do | descriptions CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   let ts = entriesReport ropts j | ||||||
|   let q  = queryFromOpts d ropts |  | ||||||
|       ts = entriesReport ropts q j |  | ||||||
|       descriptions = nubSort $ map tdescription ts |       descriptions = nubSort $ map tdescription ts | ||||||
| 
 | 
 | ||||||
|   mapM_ T.putStrLn descriptions |   mapM_ T.putStrLn descriptions | ||||||
|  | |||||||
| @ -102,11 +102,11 @@ unmatchedtxns s pp m = | |||||||
| 
 | 
 | ||||||
| -- | The diff command. | -- | The diff command. | ||||||
| diff :: CliOpts -> Journal -> IO () | diff :: CliOpts -> Journal -> IO () | ||||||
| diff CliOpts{file_=[f1, f2], reportopts_=ReportOpts{query_=acctName}} _ = do | diff CliOpts{file_=[f1, f2], reportopts_=ReportOpts{query_=Acct acctRe}} _ = do | ||||||
|   j1 <- readJournalFile' f1 |   j1 <- readJournalFile' f1 | ||||||
|   j2 <- readJournalFile' f2 |   j2 <- readJournalFile' f2 | ||||||
| 
 | 
 | ||||||
|   let acct = T.pack acctName |   let acct = T.pack $ reString acctRe | ||||||
|   let pp1 = matchingPostings acct j1 |   let pp1 = matchingPostings acct j1 | ||||||
|   let pp2 = matchingPostings acct j2 |   let pp2 = matchingPostings acct j2 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,9 +33,6 @@ notesmode = hledgerCommandMode | |||||||
| -- | The notes command. | -- | The notes command. | ||||||
| notes :: CliOpts -> Journal -> IO () | notes :: CliOpts -> Journal -> IO () | ||||||
| notes CliOpts{reportopts_=ropts} j = do | notes CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   let ts = entriesReport ropts j | ||||||
|   let q  = queryFromOpts d ropts |  | ||||||
|       ts = entriesReport ropts q j |  | ||||||
|       notes = nubSort $ map transactionNote ts |       notes = nubSort $ map transactionNote ts | ||||||
| 
 |  | ||||||
|   mapM_ T.putStrLn notes |   mapM_ T.putStrLn notes | ||||||
|  | |||||||
| @ -33,9 +33,6 @@ payeesmode = hledgerCommandMode | |||||||
| -- | The payees command. | -- | The payees command. | ||||||
| payees :: CliOpts -> Journal -> IO () | payees :: CliOpts -> Journal -> IO () | ||||||
| payees CliOpts{reportopts_=ropts} j = do | payees CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   let ts = entriesReport ropts j | ||||||
|   let q  = queryFromOpts d ropts |  | ||||||
|       ts = entriesReport ropts q j |  | ||||||
|       payees = nubSort $ map transactionPayee ts |       payees = nubSort $ map transactionPayee ts | ||||||
| 
 |  | ||||||
|   mapM_ T.putStrLn payees |   mapM_ T.putStrLn payees | ||||||
|  | |||||||
| @ -25,10 +25,9 @@ pricesmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- XXX the original hledger-prices script always ignored assertions | -- XXX the original hledger-prices script always ignored assertions | ||||||
| prices opts j = do | prices opts j = do | ||||||
|   d <- getCurrentDay |  | ||||||
|   let |   let | ||||||
|     styles     = journalCommodityStyles j |     styles     = journalCommodityStyles j | ||||||
|     q          = queryFromOpts d (reportopts_ opts) |     q          = query_ $ reportopts_ opts | ||||||
|     ps         = filter (matchesPosting q) $ allPostings j |     ps         = filter (matchesPosting q) $ allPostings j | ||||||
|     mprices    = jpricedirectives j |     mprices    = jpricedirectives j | ||||||
|     cprices    = map (stylePriceDirectiveExceptPrecision styles) $ concatMap postingsPriceDirectivesFromCosts ps |     cprices    = map (stylePriceDirectiveExceptPrecision styles) $ concatMap postingsPriceDirectivesFromCosts ps | ||||||
|  | |||||||
| @ -54,16 +54,14 @@ print' opts j = do | |||||||
| 
 | 
 | ||||||
| printEntries :: CliOpts -> Journal -> IO () | printEntries :: CliOpts -> Journal -> IO () | ||||||
| printEntries opts@CliOpts{reportopts_=ropts} j = do | printEntries opts@CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   let fmt = outputFormatFromOpts opts | ||||||
|   let q = queryFromOpts d ropts |  | ||||||
|       fmt = outputFormatFromOpts opts |  | ||||||
|       render = case fmt of |       render = case fmt of | ||||||
|         "txt"  -> entriesReportAsText opts |         "txt"  -> entriesReportAsText opts | ||||||
|         "csv"  -> (++"\n") . printCSV . entriesReportAsCsv |         "csv"  -> (++"\n") . printCSV . entriesReportAsCsv | ||||||
|         "json" -> (++"\n") . TL.unpack . toJsonText |         "json" -> (++"\n") . TL.unpack . toJsonText | ||||||
|         "sql"  -> entriesReportAsSql |         "sql"  -> entriesReportAsSql | ||||||
|         _      -> const $ error' $ unsupportedOutputFormatError fmt  -- PARTIAL: |         _      -> const $ error' $ unsupportedOutputFormatError fmt  -- PARTIAL: | ||||||
|   writeOutput opts $ render $ entriesReport ropts q j |   writeOutput opts $ render $ entriesReport ropts j | ||||||
| 
 | 
 | ||||||
| entriesReportAsText :: CliOpts -> EntriesReport -> String | entriesReportAsText :: CliOpts -> EntriesReport -> String | ||||||
| entriesReportAsText opts = concatMap (showTransaction . whichtxn) | entriesReportAsText opts = concatMap (showTransaction . whichtxn) | ||||||
| @ -185,9 +183,7 @@ postingToCSV p = | |||||||
| -- (and the query, if any). | -- (and the query, if any). | ||||||
| printMatch :: CliOpts -> Journal -> Text -> IO () | printMatch :: CliOpts -> Journal -> Text -> IO () | ||||||
| printMatch CliOpts{reportopts_=ropts} j desc = do | printMatch CliOpts{reportopts_=ropts} j desc = do | ||||||
|   d <- getCurrentDay |   case similarTransaction' j (query_ ropts) desc of | ||||||
|   let q = queryFromOpts d ropts |  | ||||||
|   case similarTransaction' j q desc of |  | ||||||
|       Nothing -> putStrLn "no matches found." |       Nothing -> putStrLn "no matches found." | ||||||
|       Just t  -> putStr $ showTransaction t |       Just t  -> putStr $ showTransaction t | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,7 +23,6 @@ import Data.Maybe | |||||||
| -- import Data.Text (Text) | -- import Data.Text (Text) | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| import qualified Data.Text.Lazy as TL | import qualified Data.Text.Lazy as TL | ||||||
| import Data.Time (fromGregorian) |  | ||||||
| import System.Console.CmdArgs.Explicit | import System.Console.CmdArgs.Explicit | ||||||
| import Hledger.Read.CsvReader (CSV, CsvRecord, printCSV) | import Hledger.Read.CsvReader (CSV, CsvRecord, printCSV) | ||||||
| 
 | 
 | ||||||
| @ -60,13 +59,12 @@ registermode = hledgerCommandMode | |||||||
| -- | Print a (posting) register report. | -- | Print a (posting) register report. | ||||||
| register :: CliOpts -> Journal -> IO () | register :: CliOpts -> Journal -> IO () | ||||||
| register opts@CliOpts{reportopts_=ropts} j = do | register opts@CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |  | ||||||
|   let fmt = outputFormatFromOpts opts |   let fmt = outputFormatFromOpts opts | ||||||
|       render | fmt=="txt"  = postingsReportAsText |       render | fmt=="txt"  = postingsReportAsText | ||||||
|              | fmt=="csv"  = const ((++"\n") . printCSV . postingsReportAsCsv) |              | fmt=="csv"  = const ((++"\n") . printCSV . postingsReportAsCsv) | ||||||
|              | fmt=="json" = const ((++"\n") . TL.unpack . toJsonText) |              | fmt=="json" = const ((++"\n") . TL.unpack . toJsonText) | ||||||
|              | otherwise   = const $ error' $ unsupportedOutputFormatError fmt  -- PARTIAL: |              | otherwise   = const $ error' $ unsupportedOutputFormatError fmt  -- PARTIAL: | ||||||
|   writeOutput opts $ render opts $ postingsReport ropts (queryFromOpts d ropts) j |   writeOutput opts . render opts $ postingsReport ropts j | ||||||
| 
 | 
 | ||||||
| postingsReportAsCsv :: PostingsReport -> CSV | postingsReportAsCsv :: PostingsReport -> CSV | ||||||
| postingsReportAsCsv (_,is) = | postingsReportAsCsv (_,is) = | ||||||
| @ -201,7 +199,7 @@ tests_Register = tests "Register" [ | |||||||
|     test "unicode in register layout" $ do |     test "unicode in register layout" $ do | ||||||
|       j <- readJournal' "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n" |       j <- readJournal' "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n" | ||||||
|       let opts = defreportopts |       let opts = defreportopts | ||||||
|       (postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (fromGregorian 2008 11 26) opts) j) |       (postingsReportAsText defcliopts $ postingsReport opts j) | ||||||
|         @?= |         @?= | ||||||
|         unlines |         unlines | ||||||
|         ["2009-01-01 медвежья шкура       расходы:покупки                100           100" |         ["2009-01-01 медвежья шкура       расходы:покупки                100           100" | ||||||
|  | |||||||
| @ -22,13 +22,10 @@ registermatchmode = hledgerCommandMode | |||||||
|   ([], Just $ argsFlag "DESC") |   ([], Just $ argsFlag "DESC") | ||||||
| 
 | 
 | ||||||
| registermatch :: CliOpts -> Journal -> IO () | registermatch :: CliOpts -> Journal -> IO () | ||||||
| registermatch opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | registermatch opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = | ||||||
|   let args' = listofstringopt "args" rawopts |   case listofstringopt "args" rawopts of | ||||||
|   case args' of |  | ||||||
|     [desc] -> do |     [desc] -> do | ||||||
|         d <- getCurrentDay |         let (_,pris) = postingsReport ropts j | ||||||
|         let q  = queryFromOptsOnly d ropts |  | ||||||
|             (_,pris) = postingsReport ropts q j |  | ||||||
|             ps = [p | (_,_,_,p,_) <- pris] |             ps = [p | (_,_,_,p,_) <- pris] | ||||||
|         case similarPosting ps desc of |         case similarPosting ps desc of | ||||||
|           Nothing -> putStrLn "no matches found." |           Nothing -> putStrLn "no matches found." | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ module Hledger.Cli.Commands.Rewrite ( | |||||||
| where | where | ||||||
| 
 | 
 | ||||||
| #if !(MIN_VERSION_base(4,11,0)) | #if !(MIN_VERSION_base(4,11,0)) | ||||||
| import Control.Monad.Writer | import Control.Monad.Writer hiding (Any) | ||||||
| #endif | #endif | ||||||
| import Data.Functor.Identity | import Data.Functor.Identity | ||||||
| import Data.List (sortOn, foldl') | import Data.List (sortOn, foldl') | ||||||
| @ -42,15 +42,15 @@ rewrite opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j@Journal{jtxns=ts} = d | |||||||
|   let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j |   let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j | ||||||
|   let j' = j{jtxns=either error' id $ modifyTransactions d modifiers ts}  -- PARTIAL: |   let j' = j{jtxns=either error' id $ modifyTransactions d modifiers ts}  -- PARTIAL: | ||||||
|   -- run the print command, showing all transactions, or show diffs |   -- run the print command, showing all transactions, or show diffs | ||||||
|   printOrDiff rawopts opts{reportopts_=ropts{query_=""}} j j' |   printOrDiff rawopts opts{reportopts_=ropts{query_=Any}} j j' | ||||||
| 
 | 
 | ||||||
| -- | Build a 'TransactionModifier' from any query arguments and --add-posting flags | -- | Build a 'TransactionModifier' from any query arguments and --add-posting flags | ||||||
| -- provided on the command line, or throw a parse error. | -- provided on the command line, or throw a parse error. | ||||||
| transactionModifierFromOpts :: CliOpts -> TransactionModifier | transactionModifierFromOpts :: CliOpts -> TransactionModifier | ||||||
| transactionModifierFromOpts CliOpts{rawopts_=rawopts,reportopts_=ropts} = | transactionModifierFromOpts CliOpts{rawopts_=rawopts} = | ||||||
|     TransactionModifier{tmquerytxt=q, tmpostingrules=ps} |     TransactionModifier{tmquerytxt=q, tmpostingrules=ps} | ||||||
|   where |   where | ||||||
|     q = T.pack $ query_ ropts |     q = T.pack . unwords . map quoteIfNeeded $ listofstringopt "args" rawopts | ||||||
|     ps = map (parseposting . T.pack) $ listofstringopt "add-posting" rawopts |     ps = map (parseposting . T.pack) $ listofstringopt "add-posting" rawopts | ||||||
|     parseposting t = either (error' . errorBundlePretty) id ep  -- PARTIAL: |     parseposting t = either (error' . errorBundlePretty) id ep  -- PARTIAL: | ||||||
|       where |       where | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ import Data.Function (on) | |||||||
| import Data.List | import Data.List | ||||||
| import Numeric.RootFinding | import Numeric.RootFinding | ||||||
| import Data.Decimal | import Data.Decimal | ||||||
|  | import qualified Data.Text as T | ||||||
| import System.Console.CmdArgs.Explicit as CmdArgs | import System.Console.CmdArgs.Explicit as CmdArgs | ||||||
| 
 | 
 | ||||||
| import Text.Tabular as Tbl | import Text.Tabular as Tbl | ||||||
| @ -54,11 +55,16 @@ roi ::  CliOpts -> Journal -> IO () | |||||||
| roi CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | roi CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let |   let | ||||||
|     investmentsQuery = queryFromOpts d $ ropts{query_ = stringopt "investment" rawopts,period_=PeriodAll} |  | ||||||
|     pnlQuery         = queryFromOpts d $ ropts{query_ = stringopt "pnl" rawopts,period_=PeriodAll} |  | ||||||
|     showCashFlow = boolopt "cashflow" rawopts |     showCashFlow = boolopt "cashflow" rawopts | ||||||
|     prettyTables = pretty_tables_ ropts |     prettyTables = pretty_tables_ ropts | ||||||
|  |     makeQuery flag = do | ||||||
|  |         q <- either usageError (return . fst) . parseQuery d . T.pack $ stringopt flag rawopts | ||||||
|  |         return . simplifyQuery $ And [queryFromFlags ropts{period_=PeriodAll}, q] | ||||||
| 
 | 
 | ||||||
|  |   investmentsQuery <- makeQuery "investment" | ||||||
|  |   pnlQuery         <- makeQuery "pnl" | ||||||
|  | 
 | ||||||
|  |   let | ||||||
|     trans = dbg3 "investments" $ jtxns $ filterJournalTransactions investmentsQuery j |     trans = dbg3 "investments" $ jtxns $ filterJournalTransactions investmentsQuery j | ||||||
| 
 | 
 | ||||||
|     journalSpan = |     journalSpan = | ||||||
|  | |||||||
| @ -42,12 +42,11 @@ statsmode = hledgerCommandMode | |||||||
| -- like Register.summarisePostings | -- like Register.summarisePostings | ||||||
| -- | Print various statistics for the journal. | -- | Print various statistics for the journal. | ||||||
| stats :: CliOpts -> Journal -> IO () | stats :: CliOpts -> Journal -> IO () | ||||||
| stats opts@CliOpts{reportopts_=reportopts_} j = do | stats opts@CliOpts{reportopts_=ReportOpts{query_=q, interval_=interval}} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let q = queryFromOpts d reportopts_ |   let l = ledgerFromJournal q j | ||||||
|       l = ledgerFromJournal q j |  | ||||||
|       reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q) |       reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q) | ||||||
|       intervalspans = splitSpan (interval_ reportopts_) reportspan |       intervalspans = splitSpan interval reportspan | ||||||
|       showstats = showLedgerStats l d |       showstats = showLedgerStats l d | ||||||
|       s = intercalate "\n" $ map showstats intervalspans |       s = intercalate "\n" $ map showstats intervalspans | ||||||
|   writeOutput opts s |   writeOutput opts s | ||||||
|  | |||||||
| @ -28,15 +28,17 @@ tagsmode = hledgerCommandMode | |||||||
| tags :: CliOpts -> Journal -> IO () | tags :: CliOpts -> Journal -> IO () | ||||||
| tags CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | tags CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let |   let args = listofstringopt "args" rawopts | ||||||
|     args      = listofstringopt "args" rawopts |  | ||||||
|   mtagpat <- mapM (either Fail.fail pure . toRegexCI) $ headMay args |   mtagpat <- mapM (either Fail.fail pure . toRegexCI) $ headMay args | ||||||
|   let |   let | ||||||
|     queryargs = drop 1 args |     querystring = T.pack . unwords . map quoteIfNeeded $ drop 1 args | ||||||
|     values      = boolopt "values" rawopts |     values      = boolopt "values" rawopts | ||||||
|     parsed      = boolopt "parsed" rawopts |     parsed      = boolopt "parsed" rawopts | ||||||
|     empty       = empty_ ropts |     empty       = empty_ ropts | ||||||
|     q = queryFromOpts d $ ropts{query_ = unwords $ map quoteIfNeeded queryargs} | 
 | ||||||
|  |   argsquery <- either usageError (return . fst) $ parseQuery d querystring | ||||||
|  |   let | ||||||
|  |     q = simplifyQuery $ And [queryFromFlags ropts, argsquery] | ||||||
|     txns = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts ropts j |     txns = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts ropts j | ||||||
|     tagsorvalues = |     tagsorvalues = | ||||||
|       (if parsed then id else nubSort) |       (if parsed then id else nubSort) | ||||||
|  | |||||||
| @ -89,7 +89,6 @@ compoundBalanceCommandMode CompoundBalanceCommandSpec{..} = | |||||||
| -- | Generate a runnable command from a compound balance command specification. | -- | Generate a runnable command from a compound balance command specification. | ||||||
| compoundBalanceCommand :: CompoundBalanceCommandSpec -> (CliOpts -> Journal -> IO ()) | compoundBalanceCommand :: CompoundBalanceCommandSpec -> (CliOpts -> Journal -> IO ()) | ||||||
| compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=ropts@ReportOpts{..}, rawopts_=rawopts} j = do | compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=ropts@ReportOpts{..}, rawopts_=rawopts} j = do | ||||||
|     today <- getCurrentDay |  | ||||||
|     let |     let | ||||||
|       -- use the default balance type for this report, unless the user overrides |       -- use the default balance type for this report, unless the user overrides | ||||||
|       mBalanceTypeOverride = |       mBalanceTypeOverride = | ||||||
| @ -121,7 +120,7 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=r | |||||||
|               _                 -> showDateSpan requestedspan |               _                 -> showDateSpan requestedspan | ||||||
|             where |             where | ||||||
|               enddates = map (addDays (-1)) . mapMaybe spanEnd $ cbrDates cbr  -- these spans will always have a definite end date |               enddates = map (addDays (-1)) . mapMaybe spanEnd $ cbrDates cbr  -- these spans will always have a definite end date | ||||||
|               requestedspan = queryDateSpan date2_ (queryFromOpts today ropts') |               requestedspan = queryDateSpan date2_ query_ | ||||||
|                                   `spanDefaultsFrom` journalDateSpan date2_ j |                                   `spanDefaultsFrom` journalDateSpan date2_ j | ||||||
| 
 | 
 | ||||||
|           -- when user overrides, add an indication to the report title |           -- when user overrides, add an indication to the report title | ||||||
| @ -143,7 +142,7 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=r | |||||||
|             where multiperiod = interval_ /= NoInterval |             where multiperiod = interval_ /= NoInterval | ||||||
| 
 | 
 | ||||||
|       -- make a CompoundBalanceReport. |       -- make a CompoundBalanceReport. | ||||||
|       cbr' = compoundBalanceReport today ropts' j cbcqueries |       cbr' = compoundBalanceReport ropts' j cbcqueries | ||||||
|       cbr  = cbr'{cbrTitle=title} |       cbr  = cbr'{cbrTitle=title} | ||||||
| 
 | 
 | ||||||
|     -- render appropriately |     -- render appropriately | ||||||
|  | |||||||
| @ -151,10 +151,9 @@ main = do | |||||||
|   dbgIO "isInternalCommand" isInternalCommand |   dbgIO "isInternalCommand" isInternalCommand | ||||||
|   dbgIO "isExternalCommand" isExternalCommand |   dbgIO "isExternalCommand" isExternalCommand | ||||||
|   dbgIO "isBadCommand" isBadCommand |   dbgIO "isBadCommand" isBadCommand | ||||||
|   d <- getCurrentDay |  | ||||||
|   dbgIO "period from opts" (period_ $ reportopts_ opts) |   dbgIO "period from opts" (period_ $ reportopts_ opts) | ||||||
|   dbgIO "interval from opts" (interval_ $ reportopts_ opts) |   dbgIO "interval from opts" (interval_ $ reportopts_ opts) | ||||||
|   dbgIO "query from opts & args" (queryFromOpts d $ reportopts_ opts) |   dbgIO "query from opts & args" (query_ $ reportopts_ opts) | ||||||
|   let |   let | ||||||
|     journallesserror = error "journal-less command tried to use the journal" |     journallesserror = error "journal-less command tried to use the journal" | ||||||
|     runHledgerCommand |     runHledgerCommand | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user