lib,cli,ui,web: Introduce ReportSpec, which holds ReportOpts, the day of
the report, and the parsed Query.
This commit is contained in:
		
							parent
							
								
									19ab222599
								
							
						
					
					
						commit
						260283e2f1
					
				| @ -82,8 +82,8 @@ type AccountTransactionsReportItem = | |||||||
| totallabel   = "Period Total" | totallabel   = "Period Total" | ||||||
| balancelabel = "Historical Total" | balancelabel = "Historical Total" | ||||||
| 
 | 
 | ||||||
| accountTransactionsReport :: ReportOpts -> Journal -> Query -> Query -> AccountTransactionsReport | accountTransactionsReport :: ReportSpec -> Journal -> Query -> Query -> AccountTransactionsReport | ||||||
| accountTransactionsReport ropts j reportq thisacctq = (label, items) | accountTransactionsReport rspec@ReportSpec{rsOpts=ropts} j reportq thisacctq = (label, items) | ||||||
|   where |   where | ||||||
|     -- a depth limit should not affect the account transactions report |     -- a depth limit should not affect the account transactions report | ||||||
|     -- seems unnecessary for some reason XXX |     -- seems unnecessary for some reason XXX | ||||||
| @ -115,11 +115,11 @@ accountTransactionsReport ropts j reportq thisacctq = (label, items) | |||||||
|     styles = journalCommodityStyles j |     styles = journalCommodityStyles j | ||||||
|     periodlast = |     periodlast = | ||||||
|       fromMaybe (error' "journalApplyValuation: expected a non-empty journal") $ -- XXX shouldn't happen |       fromMaybe (error' "journalApplyValuation: expected a non-empty journal") $ -- XXX shouldn't happen | ||||||
|       reportPeriodOrJournalLastDay ropts j |       reportPeriodOrJournalLastDay rspec j | ||||||
|     mreportlast = reportPeriodLastDay ropts |     mreportlast = reportPeriodLastDay rspec | ||||||
|     multiperiod = interval_ ropts /= NoInterval |     multiperiod = interval_ ropts /= NoInterval | ||||||
|     tval = case value_ ropts of |     tval = case value_ ropts of | ||||||
|              Just v  -> \t -> transactionApplyValuation prices styles periodlast mreportlast (today_ ropts) multiperiod t v |              Just v  -> \t -> transactionApplyValuation prices styles periodlast mreportlast (rsToday rspec) multiperiod t v | ||||||
|              Nothing -> id |              Nothing -> id | ||||||
|     ts4 = |     ts4 = | ||||||
|       ptraceAtWith 5 (("ts4:\n"++).pshowTransactions) $ |       ptraceAtWith 5 (("ts4:\n"++).pshowTransactions) $ | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ import Hledger.Data | |||||||
| import Hledger.Read (mamountp') | import Hledger.Read (mamountp') | ||||||
| import Hledger.Query | import Hledger.Query | ||||||
| import Hledger.Utils | import Hledger.Utils | ||||||
| import Hledger.Reports.MultiBalanceReport (multiBalanceReportWith) | import Hledger.Reports.MultiBalanceReport (multiBalanceReport) | ||||||
| import Hledger.Reports.ReportOptions | import Hledger.Reports.ReportOptions | ||||||
| import Hledger.Reports.ReportTypes | import Hledger.Reports.ReportTypes | ||||||
| 
 | 
 | ||||||
| @ -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 -> Journal -> BalanceReport | balanceReport :: ReportSpec -> Journal -> BalanceReport | ||||||
| balanceReport ropts j = (rows, total) | balanceReport rspec j = (rows, total) | ||||||
|   where |   where | ||||||
|     report = multiBalanceReportWith ropts j (journalPriceOracle (infer_value_ ropts) j) |     report = multiBalanceReport rspec 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 | ||||||
| @ -101,8 +101,8 @@ Right samplejournal2 = | |||||||
| tests_BalanceReport = tests "BalanceReport" [ | tests_BalanceReport = tests "BalanceReport" [ | ||||||
| 
 | 
 | ||||||
|   let |   let | ||||||
|     (opts,journal) `gives` r = do |     (rspec,journal) `gives` r = do | ||||||
|       let opts' = opts{query_=And [queryFromFlags opts, query_ opts]} |       let opts' = rspec{rsQuery=And [queryFromFlags $ rsOpts rspec, rsQuery rspec]} | ||||||
|           (eitems, etotal) = r |           (eitems, etotal) = r | ||||||
|           (aitems, atotal) = balanceReport opts' journal |           (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) | ||||||
| @ -112,10 +112,10 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|     tests "balanceReport" [ |     tests "balanceReport" [ | ||||||
| 
 | 
 | ||||||
|      test "no args, null journal" $ |      test "no args, null journal" $ | ||||||
|      (defreportopts, nulljournal) `gives` ([], 0) |      (defreportspec, nulljournal) `gives` ([], 0) | ||||||
| 
 | 
 | ||||||
|     ,test "no args, sample journal" $ |     ,test "no args, sample journal" $ | ||||||
|      (defreportopts, samplejournal) `gives` |      (defreportspec, samplejournal) `gives` | ||||||
|       ([ |       ([ | ||||||
|         ("assets:bank:checking","assets:bank:checking",0, mamountp' "$1.00") |         ("assets:bank:checking","assets:bank:checking",0, mamountp' "$1.00") | ||||||
|        ,("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00") |        ,("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00") | ||||||
| @ -128,7 +128,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with --tree" $ |     ,test "with --tree" $ | ||||||
|      (defreportopts{accountlistmode_=ALTree}, samplejournal) `gives` |      (defreportspec{rsOpts=defreportopts{accountlistmode_=ALTree}}, samplejournal) `gives` | ||||||
|       ([ |       ([ | ||||||
|         ("assets","assets",0, mamountp' "$0.00") |         ("assets","assets",0, mamountp' "$0.00") | ||||||
|        ,("assets:bank","bank",1, mamountp' "$2.00") |        ,("assets:bank","bank",1, mamountp' "$2.00") | ||||||
| @ -145,7 +145,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with --depth=N" $ |     ,test "with --depth=N" $ | ||||||
|      (defreportopts{depth_=Just 1}, samplejournal) `gives` |      (defreportspec{rsOpts=defreportopts{depth_=Just 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") | ||||||
| @ -153,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` |      (defreportspec{rsQuery=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") | ||||||
| @ -161,11 +161,11 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with date:" $ |     ,test "with date:" $ | ||||||
|      (defreportopts{query_=Date $ DateSpan (Just $ fromGregorian 2009 01 01) (Just $ fromGregorian 2010 01 01)}, samplejournal2) `gives` |      (defreportspec{rsQuery=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 $ DateSpan (Just $ fromGregorian 2009 01 01) (Just $ fromGregorian 2010 01 01)}, samplejournal2) `gives` |      (defreportspec{rsQuery=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") | ||||||
| @ -173,7 +173,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with desc:" $ |     ,test "with desc:" $ | ||||||
|      (defreportopts{query_=Desc $ toRegexCI' "income"}, samplejournal) `gives` |      (defreportspec{rsQuery=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") | ||||||
| @ -181,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 $ toRegexCI' "income"}, samplejournal) `gives` |      (defreportspec{rsQuery=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") | ||||||
| @ -192,7 +192,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|        Mixed [usd 0]) |        Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|     ,test "with period on a populated period" $ |     ,test "with period on a populated period" $ | ||||||
|       (defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2)}, samplejournal) `gives` |       (defreportspec{rsOpts=defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2)}}, samplejournal) `gives` | ||||||
|        ( |        ( | ||||||
|         [ |         [ | ||||||
|          ("assets:bank:checking","assets:bank:checking",0, mamountp' "$1.00") |          ("assets:bank:checking","assets:bank:checking",0, mamountp' "$1.00") | ||||||
| @ -201,7 +201,7 @@ tests_BalanceReport = tests "BalanceReport" [ | |||||||
|         Mixed [usd 0]) |         Mixed [usd 0]) | ||||||
| 
 | 
 | ||||||
|      ,test "with period on an unpopulated period" $ |      ,test "with period on an unpopulated period" $ | ||||||
|       (defreportopts{period_= PeriodBetween (fromGregorian 2008 1 2) (fromGregorian 2008 1 3)}, samplejournal) `gives` |       (defreportspec{rsOpts=defreportopts{period_= PeriodBetween (fromGregorian 2008 1 2) (fromGregorian 2008 1 3)}}, samplejournal) `gives` | ||||||
|        ([], 0) |        ([], 0) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -65,12 +65,12 @@ 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 -> Journal -> BudgetReport | budgetReport :: ReportSpec -> Bool -> DateSpan -> Journal -> BudgetReport | ||||||
| budgetReport ropts' assrt reportspan j = dbg1 "sortedbudgetreport" budgetreport | budgetReport rspec 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 | ||||||
|     ropts = ropts' { accountlistmode_ = ALTree } |     ropts = (rsOpts rspec){ accountlistmode_ = ALTree } | ||||||
|     showunbudgeted = empty_ ropts |     showunbudgeted = empty_ ropts | ||||||
|     budgetedaccts = |     budgetedaccts = | ||||||
|       dbg2 "budgetedacctsinperiod" $ |       dbg2 "budgetedacctsinperiod" $ | ||||||
| @ -83,9 +83,9 @@ budgetReport ropts' assrt reportspan j = dbg1 "sortedbudgetreport" budgetreport | |||||||
|     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 ropts{empty_=True} actualj |         dbg1 "actualreport" $ multiBalanceReport rspec{rsOpts=ropts{empty_=True}} actualj | ||||||
|     budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) = |     budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) = | ||||||
|         dbg1 "budgetgoalreport" $ multiBalanceReport ropts{empty_=True} budgetj |         dbg1 "budgetgoalreport" $ multiBalanceReport rspec{rsOpts=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,25 +32,25 @@ type EntriesReport = [EntriesReportItem] | |||||||
| type EntriesReportItem = Transaction | type EntriesReportItem = Transaction | ||||||
| 
 | 
 | ||||||
| -- | Select transactions for an entries report. | -- | Select transactions for an entries report. | ||||||
| entriesReport :: ReportOpts -> Journal -> EntriesReport | entriesReport :: ReportSpec -> Journal -> EntriesReport | ||||||
| entriesReport ropts@ReportOpts{..} j@Journal{..} = | entriesReport rspec@ReportSpec{rsOpts=ropts@ReportOpts{..}} j@Journal{..} = | ||||||
|   sortBy (comparing getdate) $ filter (query_ `matchesTransaction`) $ map tvalue jtxns |   sortBy (comparing getdate) $ filter (rsQuery rspec `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". | ||||||
|     tvalue t@Transaction{..} = t{tpostings=map pvalue tpostings} |     tvalue t@Transaction{..} = t{tpostings=map pvalue tpostings} | ||||||
|       where |       where | ||||||
|         pvalue p = maybe p |         pvalue p = maybe p | ||||||
|           (postingApplyValuation (journalPriceOracle infer_value_ j) (journalCommodityStyles j) periodlast mreportlast today_ False p) |           (postingApplyValuation (journalPriceOracle infer_value_ j) (journalCommodityStyles j) periodlast mreportlast (rsToday rspec) False p) | ||||||
|           value_ |           value_ | ||||||
|           where |           where | ||||||
|             periodlast  = fromMaybe today_ $ reportPeriodOrJournalLastDay ropts j |             periodlast  = fromMaybe (rsToday rspec) $ reportPeriodOrJournalLastDay rspec j | ||||||
|             mreportlast = reportPeriodLastDay ropts |             mreportlast = reportPeriodLastDay rspec | ||||||
| 
 | 
 | ||||||
| tests_EntriesReport = tests "EntriesReport" [ | tests_EntriesReport = tests "EntriesReport" [ | ||||||
|   tests "entriesReport" [ |   tests "entriesReport" [ | ||||||
|      test "not acct" $ (length $ entriesReport defreportopts{query_=Not . Acct $ toRegex' "bank"} samplejournal) @?= 1 |      test "not acct" $ (length $ entriesReport defreportspec{rsQuery=Not . Acct $ toRegex' "bank"} samplejournal) @?= 1 | ||||||
|     ,test "date" $ (length $ entriesReport defreportopts{query_=Date $ DateSpan (Just $ fromGregorian 2008 06 01) (Just $ fromGregorian 2008 07 01)} samplejournal) @?= 3 |     ,test "date" $ (length $ entriesReport defreportspec{rsQuery=Date $ DateSpan (Just $ fromGregorian 2008 06 01) (Just $ fromGregorian 2008 07 01)} samplejournal) @?= 3 | ||||||
|   ] |   ] | ||||||
|  ] |  ] | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -90,61 +90,61 @@ 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 :: ReportOpts -> Journal -> MultiBalanceReport | multiBalanceReport :: ReportSpec -> Journal -> MultiBalanceReport | ||||||
| multiBalanceReport ropts j = multiBalanceReportWith ropts j (journalPriceOracle infer j) | multiBalanceReport rspec j = multiBalanceReportWith rspec j (journalPriceOracle infer j) | ||||||
|   where infer = infer_value_ ropts |   where infer = infer_value_ $ rsOpts rspec | ||||||
| 
 | 
 | ||||||
| -- | A helper for multiBalanceReport. This one takes an extra argument, | -- | A helper for multiBalanceReport. This one takes an extra argument, | ||||||
| -- a PriceOracle to be used for looking up market prices. Commands which | -- a 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 -> Journal -> PriceOracle -> MultiBalanceReport | multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport | ||||||
| multiBalanceReportWith ropts' j priceoracle = report | multiBalanceReportWith rspec' j priceoracle = report | ||||||
|   where |   where | ||||||
|     -- Queries, report/column dates. |     -- Queries, report/column dates. | ||||||
|     reportspan = dbg "reportspan" $ calculateReportSpan ropts' j |     reportspan = dbg "reportspan" $ calculateReportSpan rspec' j | ||||||
|     ropts      = dbg "reportopts" $ makeReportQuery ropts' reportspan |     rspec      = dbg "reportopts" $ makeReportQuery rspec' reportspan | ||||||
|     valuation  = makeValuation ropts' j priceoracle  -- Must use ropts' instead of ropts, |     valuation  = makeValuation rspec' j priceoracle  -- Must use rspec' instead of rspec, | ||||||
|                                                      -- so the reportspan isn't used for valuation |                                                      -- so the reportspan isn't used for valuation | ||||||
| 
 | 
 | ||||||
|     -- Group postings into their columns. |     -- Group postings into their columns. | ||||||
|     colps    = dbg'' "colps"  $ getPostingsByColumn ropts j reportspan |     colps    = dbg'' "colps"  $ getPostingsByColumn rspec 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 j reportspan |     startbals = dbg' "startbals" $ startingBalances rspec 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 j valuation colspans colps startbals |       generateMultiBalanceReport rspec j valuation 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 :: ReportOpts -> Journal -> [CBCSubreportSpec] | compoundBalanceReport :: ReportSpec -> Journal -> [CBCSubreportSpec] | ||||||
|                       -> CompoundBalanceReport |                       -> CompoundBalanceReport | ||||||
| compoundBalanceReport ropts j = compoundBalanceReportWith ropts j (journalPriceOracle infer j) | compoundBalanceReport rspec j = compoundBalanceReportWith rspec j (journalPriceOracle infer j) | ||||||
|   where infer = infer_value_ ropts |   where infer = infer_value_ $ rsOpts rspec | ||||||
| 
 | 
 | ||||||
| -- | A helper for compoundBalanceReport, similar to multiBalanceReportWith. | -- | A helper for compoundBalanceReport, similar to multiBalanceReportWith. | ||||||
| compoundBalanceReportWith :: ReportOpts -> Journal -> PriceOracle | compoundBalanceReportWith :: ReportSpec -> Journal -> PriceOracle | ||||||
|                           -> [CBCSubreportSpec] -> CompoundBalanceReport |                           -> [CBCSubreportSpec] -> CompoundBalanceReport | ||||||
| compoundBalanceReportWith ropts' j priceoracle subreportspecs = cbr | compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr | ||||||
|   where |   where | ||||||
|     -- Queries, report/column dates. |     -- Queries, report/column dates. | ||||||
|     reportspan = dbg "reportspan" $ calculateReportSpan ropts' j |     reportspan = dbg "reportspan" $ calculateReportSpan rspec' j | ||||||
|     ropts      = dbg "reportopts" $ makeReportQuery ropts' reportspan |     rspec      = dbg "reportopts" $ makeReportQuery rspec' reportspan | ||||||
|     valuation  = makeValuation ropts' j priceoracle  -- Must use ropts' instead of ropts, |     valuation  = makeValuation rspec' j priceoracle  -- Must use ropts' instead of ropts, | ||||||
|                                                      -- so the reportspan isn't used for valuation |                                                      -- so the reportspan isn't used for valuation | ||||||
| 
 | 
 | ||||||
|     -- Group postings into their columns. |     -- Group postings into their columns. | ||||||
|     colps    = dbg'' "colps"  $ getPostingsByColumn ropts{empty_=True} j reportspan |     colps    = dbg'' "colps"  $ getPostingsByColumn rspec{rsOpts=(rsOpts rspec){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 j reportspan |     startbals = dbg' "startbals" $ startingBalances rspec j reportspan | ||||||
| 
 | 
 | ||||||
|     subreports = map generateSubreport subreportspecs |     subreports = map generateSubreport subreportspecs | ||||||
|       where |       where | ||||||
| @ -152,11 +152,12 @@ compoundBalanceReportWith ropts' 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' j valuation colspans colps' startbals' |                 generateMultiBalanceReport rspec' j valuation colspans colps' startbals' | ||||||
|             , cbcsubreportincreasestotal |             , cbcsubreportincreasestotal | ||||||
|             ) |             ) | ||||||
|           where |           where | ||||||
|             ropts'     = ropts{normalbalance_=Just cbcsubreportnormalsign} |             rspec'     = rspec{rsOpts=ropts} | ||||||
|  |             ropts      = (rsOpts rspec){normalbalance_=Just cbcsubreportnormalsign} | ||||||
|             -- Filter the column postings according to each subreport |             -- Filter the column postings according to each subreport | ||||||
|             colps'     = filter (matchesPosting $ cbcsubreportquery j) <$> colps |             colps'     = filter (matchesPosting $ cbcsubreportquery j) <$> colps | ||||||
|             startbals' = HM.filterWithKey (\k _ -> matchesAccount (cbcsubreportquery j) k) startbals |             startbals' = HM.filterWithKey (\k _ -> matchesAccount (cbcsubreportquery j) k) startbals | ||||||
| @ -182,19 +183,20 @@ compoundBalanceReportWith ropts' 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 -> Journal -> DateSpan -> HashMap AccountName Account | startingBalances :: ReportSpec -> Journal -> DateSpan -> HashMap AccountName Account | ||||||
| startingBalances ropts j reportspan = | startingBalances rspec@ReportSpec{rsQuery=query,rsOpts=ropts} j reportspan = | ||||||
|     acctChangesFromPostings ropts' . map fst $ getPostings ropts' j |     acctChangesFromPostings rspec' . map fst $ getPostings rspec' j | ||||||
|   where |   where | ||||||
|  |     rspec' = rspec{rsQuery=startbalq,rsOpts=ropts'} | ||||||
|     ropts' = case accountlistmode_ ropts of |     ropts' = case accountlistmode_ ropts of | ||||||
|         ALTree -> ropts{query_=startbalq, period_=precedingperiod, no_elide_=True} |         ALTree -> ropts{period_=precedingperiod, no_elide_=True} | ||||||
|         ALFlat -> ropts{query_=startbalq, period_=precedingperiod} |         ALFlat -> ropts{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) $ query_ ropts |     datelessq = dbg "datelessq" $ filterQuery (not . queryIsDateOrDate2) query | ||||||
| 
 | 
 | ||||||
|     precedingperiod = dateSpanAsPeriod . spanIntersect precedingspan . |     precedingperiod = dateSpanAsPeriod . spanIntersect precedingspan . | ||||||
|                          periodAsDateSpan $ period_ ropts |                          periodAsDateSpan $ period_ ropts | ||||||
| @ -204,11 +206,11 @@ startingBalances ropts j reportspan = | |||||||
|         a -> a |         a -> a | ||||||
| 
 | 
 | ||||||
| -- | Calculate the span of the report to be generated. | -- | Calculate the span of the report to be generated. | ||||||
| calculateReportSpan :: ReportOpts -> Journal -> DateSpan | calculateReportSpan :: ReportSpec -> Journal -> DateSpan | ||||||
| calculateReportSpan ropts j = reportspan | calculateReportSpan ReportSpec{rsQuery=query,rsOpts=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) $ query_ ropts |     requestedspan  = dbg "requestedspan" $ queryDateSpan (date2_ ropts) query | ||||||
|     -- 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'" $ | ||||||
| @ -227,37 +229,37 @@ calculateReportSpan ropts 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 -> ReportOpts | makeReportQuery :: ReportSpec -> DateSpan -> ReportSpec | ||||||
| makeReportQuery ropts reportspan | makeReportQuery rspec reportspan | ||||||
|     | reportspan == nulldatespan = ropts |     | reportspan == nulldatespan = rspec | ||||||
|     | otherwise = ropts{query_=query} |     | otherwise = rspec{rsQuery=query} | ||||||
|   where |   where | ||||||
|     query            = simplifyQuery $ And [dateless $ query_ ropts, reportspandatesq] |     query            = simplifyQuery $ And [dateless $ rsQuery rspec, 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_ (rsOpts rspec) then Date2 else Date | ||||||
| 
 | 
 | ||||||
| -- | Make a valuation function for valuating MixedAmounts and a given Day | -- | Make a valuation function for valuating MixedAmounts and a given Day | ||||||
| makeValuation :: ReportOpts -> Journal -> PriceOracle -> Day -> MixedAmount -> MixedAmount | makeValuation :: ReportSpec -> Journal -> PriceOracle -> (Day -> MixedAmount -> MixedAmount) | ||||||
| makeValuation ropts j priceoracle day = case value_ ropts of | makeValuation rspec j priceoracle day = case value_ (rsOpts rspec) of | ||||||
|     Nothing -> id |     Nothing -> id | ||||||
|     Just v  -> mixedAmountApplyValuation priceoracle styles day mreportlast (today_ ropts) multiperiod v |     Just v  -> mixedAmountApplyValuation priceoracle styles day mreportlast (rsToday rspec) multiperiod v | ||||||
|   where |   where | ||||||
|     -- Some things needed if doing valuation. |     -- Some things needed if doing valuation. | ||||||
|     styles = journalCommodityStyles j |     styles = journalCommodityStyles j | ||||||
|     mreportlast = reportPeriodLastDay ropts |     mreportlast = reportPeriodLastDay rspec | ||||||
|     multiperiod = interval_ ropts /= NoInterval |     multiperiod = interval_ (rsOpts rspec) /= NoInterval | ||||||
| 
 | 
 | ||||||
| -- | Group postings, grouped by their column | -- | Group postings, grouped by their column | ||||||
| getPostingsByColumn :: ReportOpts -> Journal -> DateSpan -> Map DateSpan [Posting] | getPostingsByColumn :: ReportSpec -> Journal -> DateSpan -> Map DateSpan [Posting] | ||||||
| getPostingsByColumn ropts j reportspan = columns | getPostingsByColumn rspec 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 j |     ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings rspec 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. | ||||||
|     colspans = calculateColSpans ropts reportspan days |     colspans = calculateColSpans (rsOpts rspec) reportspan days | ||||||
|     addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d |     addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d | ||||||
|     emptyMap = M.fromList . zip colspans $ repeat [] |     emptyMap = M.fromList . zip colspans $ repeat [] | ||||||
| 
 | 
 | ||||||
| @ -265,19 +267,18 @@ getPostingsByColumn ropts 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 -> Journal -> [(Posting, Day)] | getPostings :: ReportSpec -> Journal -> [(Posting, Day)] | ||||||
| getPostings ropts = | getPostings ReportSpec{rsQuery=query,rsOpts=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" query | ||||||
|     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 | ||||||
|     -- handles the hledger-ui+future txns case above). |     -- handles the hledger-ui+future txns case above). | ||||||
|     reportq = dbg "reportq" $ depthless q |     reportq = dbg "reportq" $ depthless query | ||||||
|     depthless = dbg "depthless" . filterQuery (not . queryIsDepth) |     depthless = dbg "depthless" . filterQuery (not . queryIsDepth) | ||||||
| 
 | 
 | ||||||
|     date = case whichDateFromOpts ropts of |     date = case whichDateFromOpts ropts of | ||||||
| @ -297,17 +298,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 -> [DateSpan] -> Map DateSpan [Posting] | calculateAccountChanges :: ReportSpec -> [DateSpan] -> Map DateSpan [Posting] | ||||||
|                         -> HashMap ClippedAccountName (Map DateSpan Account) |                         -> HashMap ClippedAccountName (Map DateSpan Account) | ||||||
| calculateAccountChanges ropts colspans colps | calculateAccountChanges rspec colspans colps | ||||||
|     | queryDepth (query_ ropts) == Just 0 = acctchanges <> elided |     | queryDepth (rsQuery rspec) == 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) colps |       dbg'' "colacctchanges" $ fmap (acctChangesFromPostings rspec) colps | ||||||
| 
 | 
 | ||||||
|     elided = HM.singleton "..." $ M.fromList [(span, nullacct) | span <- colspans] |     elided = HM.singleton "..." $ M.fromList [(span, nullacct) | span <- colspans] | ||||||
| 
 | 
 | ||||||
| @ -315,15 +316,16 @@ calculateAccountChanges ropts 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 -> [Posting] -> HashMap ClippedAccountName Account | acctChangesFromPostings :: ReportSpec -> [Posting] -> HashMap ClippedAccountName Account | ||||||
| acctChangesFromPostings ropts ps = HM.fromList [(aname a, a) | a <- as] | acctChangesFromPostings ReportSpec{rsQuery=query,rsOpts=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 $ query_ ropts |     depthq = dbg "depthq" $ filterQuery queryIsDepth query | ||||||
| 
 | 
 | ||||||
| -- | Accumulate and value amounts, as specified by the report options. | -- | Accumulate and value amounts, as specified by the report options. | ||||||
| -- | -- | ||||||
| @ -366,19 +368,20 @@ accumValueAmounts ropts valuation colspans startbals acctchanges =  -- PARTIAL: | |||||||
| -- | 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 -> Journal -> (Day -> MixedAmount -> MixedAmount) -> [DateSpan] | generateMultiBalanceReport :: ReportSpec -> Journal -> (Day -> MixedAmount -> MixedAmount) -> [DateSpan] | ||||||
|                            -> Map DateSpan [Posting] -> HashMap AccountName Account |                            -> Map DateSpan [Posting] -> HashMap AccountName Account | ||||||
|                            -> MultiBalanceReport |                            -> MultiBalanceReport | ||||||
| generateMultiBalanceReport ropts j valuation colspans colps startbals = report | generateMultiBalanceReport rspec@ReportSpec{rsOpts=ropts} j valuation 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 colspans colps |     acctchanges = dbg'' "acctchanges" $ calculateAccountChanges rspec 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 valuation colspans startbals acctchanges |     accumvalued = accumValueAmounts ropts valuation 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 accumvalued |     displaynames = dbg'' "displaynames" $ displayedAccounts rspec 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 | ||||||
| @ -417,9 +420,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 -> HashMap AccountName (Map DateSpan Account) | displayedAccounts :: ReportSpec -> HashMap AccountName (Map DateSpan Account) | ||||||
|                   -> HashMap AccountName DisplayName |                   -> HashMap AccountName DisplayName | ||||||
| displayedAccounts ropts valuedaccts | displayedAccounts ReportSpec{rsQuery=query,rsOpts=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 | ||||||
| @ -460,7 +463,7 @@ displayedAccounts ropts 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 $ query_ ropts |     depth = fromMaybe maxBound $  queryDepth query | ||||||
|     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. | ||||||
| @ -604,10 +607,10 @@ 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 |     (rspec,journal) `gives` r = do | ||||||
|       let opts' = opts{query_=And [queryFromFlags opts, query_ opts]} |       let rspec' = rspec{rsQuery=And [queryFromFlags $ rsOpts rspec, rsQuery rspec]} | ||||||
|           (eitems, etotal) = r |           (eitems, etotal) = r | ||||||
|           (PeriodicReport _ aitems atotal) = multiBalanceReport opts' journal |           (PeriodicReport _ aitems atotal) = multiBalanceReport rspec' 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) | ||||||
| @ -615,10 +618,10 @@ tests_MultiBalanceReport = tests "MultiBalanceReport" [ | |||||||
|   in |   in | ||||||
|    tests "multiBalanceReport" [ |    tests "multiBalanceReport" [ | ||||||
|       test "null journal"  $ |       test "null journal"  $ | ||||||
|       (defreportopts, nulljournal) `gives` ([], Mixed [nullamt]) |       (defreportspec, nulljournal) `gives` ([], Mixed [nullamt]) | ||||||
| 
 | 
 | ||||||
|      ,test "with -H on a populated period"  $ |      ,test "with -H on a populated period"  $ | ||||||
|       (defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2), balancetype_=HistoricalBalance}, samplejournal) `gives` |       (defreportspec{rsOpts=defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2), balancetype_=HistoricalBalance}}, samplejournal) `gives` | ||||||
|        ( |        ( | ||||||
|         [ PeriodicReportRow (flatDisplayName "assets:bank:checking") [mamountp' "$1.00"]  (mamountp' "$1.00")  (Mixed [amt0 {aquantity=1}]) |         [ PeriodicReportRow (flatDisplayName "assets:bank:checking") [mamountp' "$1.00"]  (mamountp' "$1.00")  (Mixed [amt0 {aquantity=1}]) | ||||||
|         , PeriodicReportRow (flatDisplayName "income:salary")        [mamountp' "$-1.00"] (mamountp' "$-1.00") (Mixed [amt0 {aquantity=(-1)}]) |         , PeriodicReportRow (flatDisplayName "income:salary")        [mamountp' "$-1.00"] (mamountp' "$-1.00") (Mixed [amt0 {aquantity=(-1)}]) | ||||||
|  | |||||||
| @ -65,19 +65,19 @@ 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 -> Journal -> PostingsReport | postingsReport :: ReportSpec -> Journal -> PostingsReport | ||||||
| postingsReport ropts@ReportOpts{..} j = | postingsReport rspec@ReportSpec{rsOpts=ropts@ReportOpts{..}} j = | ||||||
|   (totallabel, items) |   (totallabel, items) | ||||||
|     where |     where | ||||||
|       reportspan  = adjustReportDates ropts j |       reportspan  = adjustReportDates rspec j | ||||||
|       whichdate   = whichDateFromOpts ropts |       whichdate   = whichDateFromOpts ropts | ||||||
|       mdepth      = queryDepth query_ |       mdepth      = queryDepth $ rsQuery rspec | ||||||
|       styles      = journalCommodityStyles j |       styles      = journalCommodityStyles j | ||||||
|       priceoracle = journalPriceOracle infer_value_ j |       priceoracle = journalPriceOracle infer_value_ j | ||||||
|       multiperiod = interval_ /= NoInterval |       multiperiod = interval_ /= NoInterval | ||||||
| 
 | 
 | ||||||
|       -- 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 j reportspan |       (precedingps, reportps) = matchedPostingsBeforeAndDuring rspec 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)] | ||||||
| @ -89,12 +89,12 @@ postingsReport ropts@ReportOpts{..} j = | |||||||
|         where |         where | ||||||
|           showempty = empty_ || average_ |           showempty = empty_ || average_ | ||||||
|           -- 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". | ||||||
|           pvalue p periodlast = maybe p (postingApplyValuation priceoracle styles periodlast mreportlast today_ multiperiod p) value_ |           pvalue p periodlast = maybe p (postingApplyValuation priceoracle styles periodlast mreportlast (rsToday rspec) multiperiod p) value_ | ||||||
|             where |             where | ||||||
|               mreportlast = reportPeriodLastDay ropts |               mreportlast = reportPeriodLastDay rspec | ||||||
|           reportorjournallast = |           reportorjournallast = | ||||||
|             fromMaybe (error' "postingsReport: expected a non-empty journal") $  -- PARTIAL: shouldn't happen |             fromMaybe (error' "postingsReport: expected a non-empty journal") $  -- PARTIAL: shouldn't happen | ||||||
|             reportPeriodOrJournalLastDay ropts j |             reportPeriodOrJournalLastDay rspec j | ||||||
| 
 | 
 | ||||||
|       -- Posting report items ready for display. |       -- Posting report items ready for display. | ||||||
|       items = |       items = | ||||||
| @ -112,13 +112,13 @@ postingsReport ropts@ReportOpts{..} j = | |||||||
|               precedingsum = sumPostings precedingps |               precedingsum = sumPostings precedingps | ||||||
|               precedingavg | null precedingps = 0 |               precedingavg | null precedingps = 0 | ||||||
|                            | otherwise        = divideMixedAmount (fromIntegral $ length precedingps) precedingsum |                            | otherwise        = divideMixedAmount (fromIntegral $ length precedingps) precedingsum | ||||||
|               bvalue = maybe id (mixedAmountApplyValuation priceoracle styles daybeforereportstart Nothing today_ multiperiod) value_ |               bvalue = maybe id (mixedAmountApplyValuation priceoracle styles daybeforereportstart Nothing (rsToday rspec) multiperiod) value_ | ||||||
|                   -- XXX constrain valuation type to AtDate daybeforereportstart here ? |                   -- XXX constrain valuation type to AtDate daybeforereportstart here ? | ||||||
|                 where |                 where | ||||||
|                   daybeforereportstart = |                   daybeforereportstart = | ||||||
|                     maybe (error' "postingsReport: expected a non-empty journal")  -- PARTIAL: shouldn't happen |                     maybe (error' "postingsReport: expected a non-empty journal")  -- PARTIAL: shouldn't happen | ||||||
|                     (addDays (-1)) |                     (addDays (-1)) | ||||||
|                     $ reportPeriodOrJournalStart ropts j |                     $ reportPeriodOrJournalStart rspec j | ||||||
| 
 | 
 | ||||||
|           runningcalc = registerRunningCalculationFn ropts |           runningcalc = registerRunningCalculationFn ropts | ||||||
|           startnum = if historical then length precedingps + 1 else 1 |           startnum = if historical then length precedingps + 1 else 1 | ||||||
| @ -139,17 +139,17 @@ 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 -> Journal -> DateSpan | adjustReportDates :: ReportSpec -> Journal -> DateSpan | ||||||
| adjustReportDates opts j = reportspan | adjustReportDates rspec@ReportSpec{rsOpts=ropts} j = reportspan | ||||||
|   where |   where | ||||||
|     -- see also multiBalanceReport |     -- see also multiBalanceReport | ||||||
|     requestedspan       = dbg3 "requestedspan"       $ queryDateSpan' $ query_ opts                           -- span specified by -b/-e/-p options and query args |     requestedspan       = dbg3 "requestedspan"       $ queryDateSpan' $ rsQuery rspec                         -- 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 | ||||||
|         date2s = journalDateSpan True  j |         date2s = journalDateSpan True  j | ||||||
|     requestedspanclosed = dbg3 "requestedspanclosed" $ requestedspan `spanDefaultsFrom` journalspan           -- if open-ended, close it using the journal's dates (if any) |     requestedspanclosed = dbg3 "requestedspanclosed" $ requestedspan `spanDefaultsFrom` journalspan           -- if open-ended, close it using the journal's dates (if any) | ||||||
|     intervalspans       = dbg3 "intervalspans"       $ splitSpan (interval_ opts) requestedspanclosed  -- get the whole intervals enclosing that |     intervalspans       = dbg3 "intervalspans"       $ splitSpan (interval_ ropts) requestedspanclosed  -- get the whole intervals enclosing that | ||||||
|     mreportstart        = dbg3 "reportstart"         $ maybe Nothing spanStart $ headMay intervalspans        -- start of the first interval, or open ended |     mreportstart        = dbg3 "reportstart"         $ maybe Nothing spanStart $ headMay intervalspans        -- start of the first interval, or open ended | ||||||
|     mreportend          = dbg3 "reportend"           $ maybe Nothing spanEnd   $ lastMay intervalspans        -- end of the last interval, or open ended |     mreportend          = dbg3 "reportend"           $ maybe Nothing spanEnd   $ lastMay intervalspans        -- end of the last interval, or open ended | ||||||
|     reportspan          = dbg3 "reportspan"          $ DateSpan mreportstart mreportend                       -- the requested span enlarged to whole intervals if possible |     reportspan          = dbg3 "reportspan"          $ DateSpan mreportstart mreportend                       -- the requested span enlarged to whole intervals if possible | ||||||
| @ -158,30 +158,29 @@ adjustReportDates opts 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 -> Journal -> DateSpan -> ([Posting],[Posting]) | matchedPostingsBeforeAndDuring :: ReportSpec -> Journal -> DateSpan -> ([Posting],[Posting]) | ||||||
| matchedPostingsBeforeAndDuring opts j (DateSpan mstart mend) = | matchedPostingsBeforeAndDuring ReportSpec{rsOpts=ropts,rsQuery=q} 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 | ||||||
|       dbg5 "ps4" $ (if invert_ opts then map negatePostingAmount else id) $    -- with --invert, invert amounts |       dbg5 "ps4" $ (if invert_ ropts then map negatePostingAmount else id) $     -- with --invert, invert amounts | ||||||
|       dbg5 "ps3" $ map (filterPostingAmount symq) $                              -- remove amount parts which the query's cur: terms would exclude |       dbg5 "ps3" $ map (filterPostingAmount symq) $                              -- remove amount parts which the query's cur: terms would exclude | ||||||
|       dbg5 "ps2" $ (if related_ opts then concatMap relatedPostings else id) $ -- with -r, replace each with its sibling postings |       dbg5 "ps2" $ (if related_ ropts then concatMap relatedPostings else id) $  -- with -r, replace each with its sibling postings | ||||||
|       dbg5 "ps1" $ filter (beforeandduringq `matchesPosting`) $                  -- filter postings by the query, with no start date or depth limit |       dbg5 "ps1" $ filter (beforeandduringq `matchesPosting`) $                  -- filter postings by the query, with no start date or depth limit | ||||||
|                   journalPostings $ |                   journalPostings $ | ||||||
|                   journalSelectingAmountFromOpts opts j    -- maybe convert to cost early, will be seen by amt:. XXX what about converting to value ? |                   journalSelectingAmountFromOpts ropts j    -- maybe convert to cost early, will be seen by amt:. XXX what about converting to value ? | ||||||
|       where |       where | ||||||
|         beforeandduringq = dbg4 "beforeandduringq" $ And [depthless $ dateless q, beforeendq] |         beforeandduringq = dbg4 "beforeandduringq" $ And [depthless $ dateless q, beforeendq] | ||||||
|           where |           where | ||||||
|             depthless  = filterQuery (not . queryIsDepth) |             depthless  = filterQuery (not . queryIsDepth) | ||||||
|             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_ ropts then postingDate2 else postingDate | ||||||
|         symq = dbg4 "symq" . filterQuery queryIsSym $ query_ opts |         symq = dbg4 "symq" $ filterQuery queryIsSym q | ||||||
|     dateqtype |     dateqtype | ||||||
|       | queryIsDate2 dateq || (queryIsDate dateq && date2_ opts) = Date2 |       | queryIsDate2 dateq || (queryIsDate dateq && date2_ ropts) = Date2 | ||||||
|       | otherwise = Date |       | otherwise = Date | ||||||
|       where |       where | ||||||
|         dateq = dbg4 "dateq" $ filterQuery queryIsDateOrDate2 $ dbg4 "q" q  -- XXX confused by multiple date:/date2: ? |         dateq = dbg4 "dateq" $ filterQuery queryIsDateOrDate2 $ dbg4 "q" q  -- XXX confused by multiple date:/date2: ? | ||||||
| @ -270,7 +269,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_=query} journal) @?= n |     let (query, journal) `gives` n = (length $ snd $ postingsReport defreportspec{rsQuery=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 +278,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 samplejournal) @?= 13 |     (length $ snd $ postingsReport defreportspec samplejournal) @?= 13 | ||||||
|     (length $ snd $ postingsReport defreportopts{interval_=Months 1} samplejournal) @?= 11 |     (length $ snd $ postingsReport defreportspec{rsOpts=defreportopts{interval_=Months 1}} samplejournal) @?= 11 | ||||||
|     (length $ snd $ postingsReport defreportopts{interval_=Months 1, empty_=True} samplejournal) @?= 20 |     (length $ snd $ postingsReport defreportspec{rsOpts=defreportopts{interval_=Months 1, empty_=True}} samplejournal) @?= 20 | ||||||
|     (length $ snd $ postingsReport defreportopts{query_=Acct $ toRegex' "assets:bank:checking"} samplejournal) @?= 5 |     (length $ snd $ postingsReport defreportspec{rsQuery=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) | ||||||
|  | |||||||
| @ -10,12 +10,15 @@ Options common to most hledger reports. | |||||||
| 
 | 
 | ||||||
| module Hledger.Reports.ReportOptions ( | module Hledger.Reports.ReportOptions ( | ||||||
|   ReportOpts(..), |   ReportOpts(..), | ||||||
|  |   ReportSpec(..), | ||||||
|   BalanceType(..), |   BalanceType(..), | ||||||
|   AccountListMode(..), |   AccountListMode(..), | ||||||
|   ValuationType(..), |   ValuationType(..), | ||||||
|   defreportopts, |   defreportopts, | ||||||
|   rawOptsToReportOpts, |   rawOptsToReportOpts, | ||||||
|   regenerateReportOpts, |   defreportspec, | ||||||
|  |   reportOptsToSpec, | ||||||
|  |   rawOptsToReportSpec, | ||||||
|   flat_, |   flat_, | ||||||
|   tree_, |   tree_, | ||||||
|   reportOptsToggleStatus, |   reportOptsToggleStatus, | ||||||
| @ -78,10 +81,7 @@ instance Default AccountListMode where def = ALFlat | |||||||
| -- commands, as noted below. | -- commands, as noted below. | ||||||
| data ReportOpts = ReportOpts { | data ReportOpts = ReportOpts { | ||||||
|      -- for most reports: |      -- for most reports: | ||||||
|      today_          :: Day  -- ^ The current date. A late addition to ReportOpts. |      period_         :: Period | ||||||
|                              -- Reports use it when picking a -V valuation date. |  | ||||||
|                              -- This is not great, adds indeterminacy. |  | ||||||
|     ,period_         :: Period |  | ||||||
|     ,interval_       :: Interval |     ,interval_       :: Interval | ||||||
|     ,statuses_       :: [Status]  -- ^ Zero, one, or two statuses to be matched |     ,statuses_       :: [Status]  -- ^ Zero, one, or two statuses to be matched | ||||||
|     ,value_          :: Maybe ValuationType  -- ^ What value should amounts be converted to ? |     ,value_          :: Maybe ValuationType  -- ^ What value should amounts be converted to ? | ||||||
| @ -92,8 +92,6 @@ data ReportOpts = ReportOpts { | |||||||
|     ,no_elide_       :: Bool |     ,no_elide_       :: Bool | ||||||
|     ,real_           :: Bool |     ,real_           :: Bool | ||||||
|     ,format_         :: StringFormat |     ,format_         :: StringFormat | ||||||
|     ,query_          :: Query |  | ||||||
|     ,queryopts_      :: [QueryOpt] |  | ||||||
|     ,querystring_    :: T.Text |     ,querystring_    :: T.Text | ||||||
|     -- |     -- | ||||||
|     ,average_        :: Bool |     ,average_        :: Bool | ||||||
| @ -132,8 +130,7 @@ instance Default ReportOpts where def = defreportopts | |||||||
| 
 | 
 | ||||||
| defreportopts :: ReportOpts | defreportopts :: ReportOpts | ||||||
| defreportopts = ReportOpts | defreportopts = ReportOpts | ||||||
|     { today_           = nulldate |     { period_          = PeriodAll | ||||||
|     , period_          = PeriodAll |  | ||||||
|     , interval_        = NoInterval |     , interval_        = NoInterval | ||||||
|     , statuses_        = [] |     , statuses_        = [] | ||||||
|     , value_           = Nothing |     , value_           = Nothing | ||||||
| @ -144,8 +141,6 @@ defreportopts = ReportOpts | |||||||
|     , no_elide_        = False |     , no_elide_        = False | ||||||
|     , real_            = False |     , real_            = False | ||||||
|     , format_          = def |     , format_          = def | ||||||
|     , query_           = Any |  | ||||||
|     , queryopts_       = [] |  | ||||||
|     , querystring_     = "" |     , querystring_     = "" | ||||||
|     , average_         = False |     , average_         = False | ||||||
|     , related_         = False |     , related_         = False | ||||||
| @ -181,11 +176,8 @@ rawOptsToReportOpts rawopts = do | |||||||
|         Just (Right x)  -> return x |         Just (Right x)  -> return x | ||||||
|         Just (Left err) -> fail $ "could not parse format option: " ++ err |         Just (Left err) -> fail $ "could not parse format option: " ++ err | ||||||
| 
 | 
 | ||||||
|     (argsquery, queryopts) <- either fail return $ parseQuery d querystring |  | ||||||
| 
 |  | ||||||
|     let reportopts = defreportopts |     let reportopts = defreportopts | ||||||
|           {today_       = d |           {period_      = periodFromRawOpts d rawopts | ||||||
|           ,period_      = periodFromRawOpts d rawopts |  | ||||||
|           ,interval_    = intervalFromRawOpts rawopts |           ,interval_    = intervalFromRawOpts rawopts | ||||||
|           ,statuses_    = statusesFromRawOpts rawopts |           ,statuses_    = statusesFromRawOpts rawopts | ||||||
|           ,value_       = valuationTypeFromRawOpts rawopts |           ,value_       = valuationTypeFromRawOpts rawopts | ||||||
| @ -196,8 +188,6 @@ 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_       = simplifyQuery $ And [queryFromFlags reportopts, argsquery] |  | ||||||
|           ,queryopts_   = queryopts |  | ||||||
|           ,querystring_ = querystring |           ,querystring_ = querystring | ||||||
|           ,average_     = boolopt "average" rawopts |           ,average_     = boolopt "average" rawopts | ||||||
|           ,related_     = boolopt "related" rawopts |           ,related_     = boolopt "related" rawopts | ||||||
| @ -220,11 +210,40 @@ rawOptsToReportOpts rawopts = do | |||||||
|           } |           } | ||||||
|     return reportopts |     return reportopts | ||||||
| 
 | 
 | ||||||
| -- | Regenerate a ReportOpts on a different day with a different query string. | data ReportSpec = ReportSpec | ||||||
| regenerateReportOpts :: Day -> T.Text -> ReportOpts -> Either String ReportOpts |   { rsOpts      :: ReportOpts | ||||||
| regenerateReportOpts d querystring ropts = do |   , rsToday     :: Day | ||||||
|     (q,o) <- parseQuery d querystring |   , rsQuery     :: Query | ||||||
|     return ropts{today_=d, query_=q, queryopts_=o, querystring_=querystring} |   , rsQueryOpts :: [QueryOpt] | ||||||
|  |   } deriving (Show) | ||||||
|  | 
 | ||||||
|  | instance Default ReportSpec where def = defreportspec | ||||||
|  | 
 | ||||||
|  | defreportspec :: ReportSpec | ||||||
|  | defreportspec = ReportSpec | ||||||
|  |     { rsOpts      = def | ||||||
|  |     , rsToday     = nulldate | ||||||
|  |     , rsQuery     = Any | ||||||
|  |     , rsQueryOpts = [] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | -- | Generate a ReportSpec from a set of ReportOpts on a given day | ||||||
|  | reportOptsToSpec :: Day -> ReportOpts -> Either String ReportSpec | ||||||
|  | reportOptsToSpec day ropts = do | ||||||
|  |     (argsquery, queryopts) <- parseQuery day $ querystring_ ropts | ||||||
|  |     return ReportSpec | ||||||
|  |       { rsOpts = ropts | ||||||
|  |       , rsToday = day | ||||||
|  |       , rsQuery = simplifyQuery $ And [queryFromFlags ropts, argsquery] | ||||||
|  |       , rsQueryOpts = queryopts | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  | -- | Generate a ReportSpec from RawOpts and the current date. | ||||||
|  | rawOptsToReportSpec :: RawOpts -> IO ReportSpec | ||||||
|  | rawOptsToReportSpec rawopts = do | ||||||
|  |     d <- getCurrentDay | ||||||
|  |     ropts <- rawOptsToReportOpts rawopts | ||||||
|  |     either fail return $ reportOptsToSpec d ropts | ||||||
| 
 | 
 | ||||||
| accountlistmodeopt :: RawOpts -> AccountListMode | accountlistmodeopt :: RawOpts -> AccountListMode | ||||||
| accountlistmodeopt = | accountlistmodeopt = | ||||||
| @ -454,45 +473,45 @@ queryFromFlags ReportOpts{..} = simplifyQuery $ And flagsq | |||||||
| -- options or queries, or otherwise the earliest and latest transaction or | -- options or queries, or otherwise the earliest and latest transaction or | ||||||
| -- posting dates in the journal. If no dates are specified by options/queries | -- posting dates in the journal. If no dates are specified by options/queries | ||||||
| -- and the journal is empty, returns the null date span. | -- and the journal is empty, returns the null date span. | ||||||
| reportSpan :: Journal -> ReportOpts -> DateSpan | reportSpan :: Journal -> ReportSpec -> DateSpan | ||||||
| reportSpan j ropts = dbg3 "reportspan" $ DateSpan mstartdate menddate | reportSpan j ReportSpec{rsQuery=query} = dbg3 "reportspan" $ DateSpan mstartdate menddate | ||||||
|   where |   where | ||||||
|     DateSpan mjournalstartdate mjournalenddate = |     DateSpan mjournalstartdate mjournalenddate = | ||||||
|       dbg3 "journalspan" $ journalDateSpan False j  -- ignore secondary dates |       dbg3 "journalspan" $ journalDateSpan False j  -- ignore secondary dates | ||||||
|     mstartdate = queryStartDate False (query_ ropts) <|> mjournalstartdate |     mstartdate = queryStartDate False query <|> mjournalstartdate | ||||||
|     menddate   = queryEndDate   False (query_ ropts) <|> mjournalenddate |     menddate   = queryEndDate   False query <|> mjournalenddate | ||||||
| 
 | 
 | ||||||
| reportStartDate :: Journal -> ReportOpts -> Maybe Day | reportStartDate :: Journal -> ReportSpec -> Maybe Day | ||||||
| reportStartDate j ropts = spanStart $ reportSpan j ropts | reportStartDate j = spanStart . reportSpan j | ||||||
| 
 | 
 | ||||||
| reportEndDate :: Journal -> ReportOpts -> Maybe Day | reportEndDate :: Journal -> ReportSpec -> Maybe Day | ||||||
| reportEndDate j ropts = spanEnd $ reportSpan j ropts | reportEndDate j = spanEnd . reportSpan j | ||||||
| 
 | 
 | ||||||
| -- Some pure alternatives to the above. XXX review/clean up | -- Some pure alternatives to the above. XXX review/clean up | ||||||
| 
 | 
 | ||||||
| -- Get the report's start date. | -- Get the report's start date. | ||||||
| -- If no report period is specified, will be Nothing. | -- If no report period is specified, will be Nothing. | ||||||
| reportPeriodStart :: ReportOpts -> Maybe Day | reportPeriodStart :: ReportSpec -> Maybe Day | ||||||
| reportPeriodStart = queryStartDate False . query_ | reportPeriodStart = queryStartDate False . rsQuery | ||||||
| 
 | 
 | ||||||
| -- 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 | ||||||
| -- report period and nothing in the journal, will be Nothing. | -- report period and nothing in the journal, will be Nothing. | ||||||
| reportPeriodOrJournalStart :: ReportOpts -> Journal -> Maybe Day | reportPeriodOrJournalStart :: ReportSpec -> Journal -> Maybe Day | ||||||
| reportPeriodOrJournalStart ropts j = | reportPeriodOrJournalStart rspec j = | ||||||
|   reportPeriodStart ropts <|> journalStartDate False j |   reportPeriodStart rspec <|> journalStartDate False j | ||||||
| 
 | 
 | ||||||
| -- Get the last day of the overall report period. | -- Get the last day of the overall report period. | ||||||
| -- This the inclusive end date (one day before the | -- This the inclusive end date (one day before the | ||||||
| -- more commonly used, exclusive, report end date). | -- more commonly used, exclusive, report end date). | ||||||
| -- If no report period is specified, will be Nothing. | -- If no report period is specified, will be Nothing. | ||||||
| reportPeriodLastDay :: ReportOpts -> Maybe Day | reportPeriodLastDay :: ReportSpec -> Maybe Day | ||||||
| reportPeriodLastDay = fmap (addDays (-1)) . queryEndDate False . query_ | reportPeriodLastDay = fmap (addDays (-1)) . queryEndDate False . rsQuery | ||||||
| 
 | 
 | ||||||
| -- 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 | ||||||
| -- posting date). If there's no report period and nothing in the | -- posting date). If there's no report period and nothing in the | ||||||
| -- journal, will be Nothing. | -- journal, will be Nothing. | ||||||
| reportPeriodOrJournalLastDay :: ReportOpts -> Journal -> Maybe Day | reportPeriodOrJournalLastDay :: ReportSpec -> Journal -> Maybe Day | ||||||
| reportPeriodOrJournalLastDay ropts j = | reportPeriodOrJournalLastDay rspec j = | ||||||
|   reportPeriodLastDay ropts <|> journalEndDate False j |   reportPeriodLastDay rspec <|> journalEndDate False j | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ accountsScreen = AccountsScreen{ | |||||||
| 
 | 
 | ||||||
| asInit :: Day -> Bool -> UIState -> UIState | asInit :: Day -> Bool -> UIState -> UIState | ||||||
| asInit d reset ui@UIState{ | asInit d reset ui@UIState{ | ||||||
|   aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}, |   aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}, | ||||||
|   ajournal=j, |   ajournal=j, | ||||||
|   aScreen=s@AccountsScreen{} |   aScreen=s@AccountsScreen{} | ||||||
|   } = |   } = | ||||||
| @ -80,8 +80,8 @@ asInit d reset ui@UIState{ | |||||||
|                       where |                       where | ||||||
|                         as = map asItemAccountName displayitems |                         as = map asItemAccountName displayitems | ||||||
| 
 | 
 | ||||||
|     uopts' = uopts{cliopts_=copts{reportopts_=ropts'}} |     uopts' = uopts{cliopts_=copts{reportspec_=rspec'}} | ||||||
|     ropts' = ropts{query_=simplifyQuery $ And [query_ ropts, excludeforecastq (forecast_ ropts)]} |     rspec' = rspec{rsQuery=simplifyQuery $ And [rsQuery rspec, excludeforecastq (forecast_ ropts)]} | ||||||
|       where |       where | ||||||
|         -- Except in forecast mode, exclude future/forecast transactions. |         -- Except in forecast mode, exclude future/forecast transactions. | ||||||
|         excludeforecastq (Just _) = Any |         excludeforecastq (Just _) = Any | ||||||
| @ -92,13 +92,13 @@ asInit d reset ui@UIState{ | |||||||
|           ] |           ] | ||||||
| 
 | 
 | ||||||
|     -- run the report |     -- run the report | ||||||
|     (items,_total) = balanceReport ropts' j |     (items,_total) = balanceReport rspec' j | ||||||
| 
 | 
 | ||||||
|     -- pre-render the list items |     -- pre-render the list items | ||||||
|     displayitem (fullacct, shortacct, indent, bal) = |     displayitem (fullacct, shortacct, indent, bal) = | ||||||
|       AccountsScreenItem{asItemIndentLevel        = indent |       AccountsScreenItem{asItemIndentLevel        = indent | ||||||
|                         ,asItemAccountName        = fullacct |                         ,asItemAccountName        = fullacct | ||||||
|                         ,asItemDisplayAccountName = replaceHiddenAccountsNameWith "All" $ if tree_ ropts' then shortacct else fullacct |                         ,asItemDisplayAccountName = replaceHiddenAccountsNameWith "All" $ if tree_ ropts then shortacct else fullacct | ||||||
|                         ,asItemRenderedAmounts    = map (showAmountWithoutPrice False) amts |                         ,asItemRenderedAmounts    = map (showAmountWithoutPrice False) amts | ||||||
|                         } |                         } | ||||||
|       where |       where | ||||||
| @ -117,7 +117,7 @@ asInit d reset ui@UIState{ | |||||||
| asInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | asInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
| asDraw :: UIState -> [Widget Name] | asDraw :: UIState -> [Widget Name] | ||||||
| asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}} | ||||||
|               ,ajournal=j |               ,ajournal=j | ||||||
|               ,aScreen=s@AccountsScreen{} |               ,aScreen=s@AccountsScreen{} | ||||||
|               ,aMode=mode |               ,aMode=mode | ||||||
| @ -165,6 +165,7 @@ asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | |||||||
|       render $ defaultLayout toplabel bottomlabel $ renderList (asDrawItem colwidths) True (_asList s) |       render $ defaultLayout toplabel bottomlabel $ renderList (asDrawItem colwidths) True (_asList s) | ||||||
| 
 | 
 | ||||||
|       where |       where | ||||||
|  |         ropts = rsOpts rspec | ||||||
|         ishistorical = balancetype_ ropts == HistoricalBalance |         ishistorical = balancetype_ ropts == HistoricalBalance | ||||||
| 
 | 
 | ||||||
|         toplabel = |         toplabel = | ||||||
|  | |||||||
| @ -51,12 +51,12 @@ writeChan = BC.writeBChan | |||||||
| 
 | 
 | ||||||
| main :: IO () | main :: IO () | ||||||
| main = do | main = do | ||||||
|   opts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportopts_=ropts,rawopts_=rawopts}} <- getHledgerUIOpts |   opts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{rsOpts=ropts},rawopts_=rawopts}} <- getHledgerUIOpts | ||||||
|   -- when (debug_ $ cliopts_ opts) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts) |   -- when (debug_ $ cliopts_ opts) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts) | ||||||
| 
 | 
 | ||||||
|   -- always include forecasted periodic transactions when loading data; |   -- always include forecasted periodic transactions when loading data; | ||||||
|   -- they will be toggled on and off in the UI. |   -- they will be toggled on and off in the UI. | ||||||
|   let copts' = copts{reportopts_=ropts{forecast_=Just $ fromMaybe nulldatespan (forecast_ ropts)}} |   let copts' = copts{reportspec_=rspec{rsOpts=ropts{forecast_=Just $ fromMaybe nulldatespan (forecast_ ropts)}}} | ||||||
| 
 | 
 | ||||||
|   case True of |   case True of | ||||||
|     _ | "help"            `inRawOpts` rawopts -> putStr (showModeUsage uimode) |     _ | "help"            `inRawOpts` rawopts -> putStr (showModeUsage uimode) | ||||||
| @ -65,7 +65,7 @@ main = do | |||||||
|     _                                         -> withJournalDo copts' (runBrickUi opts) |     _                                         -> withJournalDo copts' (runBrickUi opts) | ||||||
| 
 | 
 | ||||||
| runBrickUi :: UIOpts -> Journal -> IO () | runBrickUi :: UIOpts -> Journal -> IO () | ||||||
| runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportopts_=ropts}} j = do | runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{rsOpts=ropts}}} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
| 
 | 
 | ||||||
|   let |   let | ||||||
| @ -79,12 +79,13 @@ runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportopts_=rop | |||||||
|     -- can be displayed independently. |     -- can be displayed independently. | ||||||
|     uopts' = uopts{ |     uopts' = uopts{ | ||||||
|       cliopts_=copts{ |       cliopts_=copts{ | ||||||
|          reportopts_= ropts{ |          reportspec_=rspec{ | ||||||
|  |             rsQuery=filteredQuery $ rsQuery rspec, -- as in ReportOptions, with same limitations | ||||||
|  |             rsOpts=ropts{ | ||||||
|                -- incorporate any depth: query args into depth_, |                -- incorporate any depth: query args into depth_, | ||||||
|                -- any date: query args into period_ |                -- any date: query args into period_ | ||||||
|             depth_ =queryDepth $ query_ ropts, |                depth_ =queryDepth $ rsQuery rspec, | ||||||
|                period_=periodfromoptsandargs, |                period_=periodfromoptsandargs, | ||||||
|             query_ =filteredQuery $ query_ ropts, -- as in ReportOptions, with same limitations |  | ||||||
|                -- always disable boring account name eliding, unlike the CLI, for a more regular tree |                -- always disable boring account name eliding, unlike the CLI, for a more regular tree | ||||||
|                no_elide_=True, |                no_elide_=True, | ||||||
|                -- flip the default for items with zero amounts, show them by default |                -- flip the default for items with zero amounts, show them by default | ||||||
| @ -94,8 +95,9 @@ runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportopts_=rop | |||||||
|                } |                } | ||||||
|             } |             } | ||||||
|          } |          } | ||||||
|  |       } | ||||||
|       where |       where | ||||||
|         datespanfromargs = queryDateSpan (date2_ ropts) $ query_ ropts |         datespanfromargs = queryDateSpan (date2_ ropts) $ rsQuery rspec | ||||||
|         periodfromoptsandargs = |         periodfromoptsandargs = | ||||||
|           dateSpanAsPeriod $ spansIntersect [periodAsDateSpan $ period_ ropts, datespanfromargs] |           dateSpanAsPeriod $ spansIntersect [periodAsDateSpan $ period_ ropts, datespanfromargs] | ||||||
|         filteredQuery q = simplifyQuery $ And [queryFromFlags ropts, filtered q] |         filteredQuery q = simplifyQuery $ And [queryFromFlags ropts, filtered q] | ||||||
|  | |||||||
| @ -59,17 +59,15 @@ rsSetAccount a forceinclusive scr@RegisterScreen{} = | |||||||
| rsSetAccount _ _ scr = scr | rsSetAccount _ _ scr = scr | ||||||
| 
 | 
 | ||||||
| rsInit :: Day -> Bool -> UIState -> UIState | rsInit :: Day -> Bool -> UIState -> UIState | ||||||
| rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajournal=j, aScreen=s@RegisterScreen{..}} = | rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}, ajournal=j, aScreen=s@RegisterScreen{..}} = | ||||||
|   ui{aScreen=s{rsList=newitems'}} |   ui{aScreen=s{rsList=newitems'}} | ||||||
|   where |   where | ||||||
|     -- gather arguments and queries |     -- gather arguments and queries | ||||||
|     -- XXX temp |     -- XXX temp | ||||||
|     inclusive = tree_ ropts || rsForceInclusive |     inclusive = tree_ ropts || rsForceInclusive | ||||||
|     thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) rsAccount |     thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) rsAccount | ||||||
|     ropts' = ropts{ |     rspec' = rspec{rsOpts=ropts{depth_=Nothing}} | ||||||
|                depth_=Nothing |     q = And [rsQuery rspec, excludeforecastq (forecast_ ropts)] | ||||||
|               } |  | ||||||
|     q = And [query_ ropts', excludeforecastq (forecast_ ropts)] |  | ||||||
|       where |       where | ||||||
|         -- Except in forecast mode, exclude future/forecast transactions. |         -- Except in forecast mode, exclude future/forecast transactions. | ||||||
|         excludeforecastq (Just _) = Any |         excludeforecastq (Just _) = Any | ||||||
| @ -79,8 +77,8 @@ rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{reportopts_=ropts | |||||||
|             ,Not generatedTransactionTag |             ,Not generatedTransactionTag | ||||||
|           ] |           ] | ||||||
| 
 | 
 | ||||||
|     (_label,items) = accountTransactionsReport ropts' j q thisacctq |     (_label,items) = accountTransactionsReport rspec' j q thisacctq | ||||||
|     items' = (if empty_ ropts' then id else filter (not . mixedAmountLooksZero . fifth6)) $  -- without --empty, exclude no-change txns |     items' = (if empty_ ropts then id else filter (not . mixedAmountLooksZero . fifth6)) $  -- without --empty, exclude no-change txns | ||||||
|              reverse  -- most recent last |              reverse  -- most recent last | ||||||
|              items |              items | ||||||
| 
 | 
 | ||||||
| @ -138,7 +136,7 @@ rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{reportopts_=ropts | |||||||
| rsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | rsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
| rsDraw :: UIState -> [Widget Name] | rsDraw :: UIState -> [Widget Name] | ||||||
| rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}} | ||||||
|               ,aScreen=RegisterScreen{..} |               ,aScreen=RegisterScreen{..} | ||||||
|               ,aMode=mode |               ,aMode=mode | ||||||
|               } = |               } = | ||||||
| @ -192,6 +190,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | |||||||
|       render $ defaultLayout toplabel bottomlabel $ renderList (rsDrawItem colwidths) True rsList |       render $ defaultLayout toplabel bottomlabel $ renderList (rsDrawItem colwidths) True rsList | ||||||
| 
 | 
 | ||||||
|       where |       where | ||||||
|  |         ropts = rsOpts rspec | ||||||
|         ishistorical = balancetype_ ropts == HistoricalBalance |         ishistorical = balancetype_ ropts == HistoricalBalance | ||||||
|         -- inclusive = tree_ ropts || rsForceInclusive |         -- inclusive = tree_ ropts || rsForceInclusive | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ transactionScreen = TransactionScreen{ | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| tsInit :: Day -> Bool -> UIState -> UIState | tsInit :: Day -> Bool -> UIState -> UIState | ||||||
| tsInit _d _reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=_ropts}} | tsInit _d _reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportspec_=_rspec}} | ||||||
|                            ,ajournal=_j |                            ,ajournal=_j | ||||||
|                            ,aScreen=TransactionScreen{} |                            ,aScreen=TransactionScreen{} | ||||||
|                            } = |                            } = | ||||||
| @ -58,7 +58,7 @@ tsInit _d _reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=_ropts}} | |||||||
| tsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | tsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
| tsDraw :: UIState -> [Widget Name] | tsDraw :: UIState -> [Widget Name] | ||||||
| tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}} | ||||||
|               ,ajournal=j |               ,ajournal=j | ||||||
|               ,aScreen=TransactionScreen{tsTransaction=(i,t) |               ,aScreen=TransactionScreen{tsTransaction=(i,t) | ||||||
|                                         ,tsTransactions=nts |                                         ,tsTransactions=nts | ||||||
| @ -77,14 +77,14 @@ tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | |||||||
|         styles = journalCommodityStyles j |         styles = journalCommodityStyles j | ||||||
|         periodlast = |         periodlast = | ||||||
|           fromMaybe (error' "TransactionScreen: expected a non-empty journal") $  -- PARTIAL: shouldn't happen |           fromMaybe (error' "TransactionScreen: expected a non-empty journal") $  -- PARTIAL: shouldn't happen | ||||||
|           reportPeriodOrJournalLastDay ropts j |           reportPeriodOrJournalLastDay rspec j | ||||||
|         mreportlast = reportPeriodLastDay ropts |         mreportlast = reportPeriodLastDay rspec | ||||||
|         multiperiod = interval_ ropts /= NoInterval |         multiperiod = interval_ ropts /= NoInterval | ||||||
| 
 | 
 | ||||||
|       render $ defaultLayout toplabel bottomlabel $ str $ |       render $ defaultLayout toplabel bottomlabel $ str $ | ||||||
|         showTransactionOneLineAmounts $ |         showTransactionOneLineAmounts $ | ||||||
|         (if valuationTypeIsCost ropts then transactionToCost (journalCommodityStyles j) else id) $ |         (if valuationTypeIsCost ropts then transactionToCost (journalCommodityStyles j) else id) $ | ||||||
|         (if valuationTypeIsDefaultValue ropts then (\t -> transactionApplyValuation prices styles periodlast mreportlast (today_ ropts) multiperiod t (AtDefault Nothing)) else id) $ |         (if valuationTypeIsDefaultValue ropts then (\t -> transactionApplyValuation prices styles periodlast mreportlast (rsToday rspec) multiperiod t (AtDefault Nothing)) else id) $ | ||||||
|         -- (if real_ ropts then filterTransactionPostings (Real True) else id) -- filter postings by --real |         -- (if real_ ropts then filterTransactionPostings (Real True) else id) -- filter postings by --real | ||||||
|         t |         t | ||||||
|       where |       where | ||||||
| @ -132,7 +132,7 @@ tsHandle ui@UIState{aScreen=s@TransactionScreen{tsTransaction=(i,t) | |||||||
|                                                ,tsTransactions=nts |                                                ,tsTransactions=nts | ||||||
|                                                ,tsAccount=acct |                                                ,tsAccount=acct | ||||||
|                                                } |                                                } | ||||||
|                    ,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} |                    ,aopts=UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}} | ||||||
|                    ,ajournal=j |                    ,ajournal=j | ||||||
|                    ,aMode=mode |                    ,aMode=mode | ||||||
|                    } |                    } | ||||||
| @ -172,7 +172,7 @@ tsHandle ui@UIState{aScreen=s@TransactionScreen{tsTransaction=(i,t) | |||||||
|             Right j' -> do |             Right j' -> do | ||||||
|               continue $ |               continue $ | ||||||
|                 regenerateScreens j' d $ |                 regenerateScreens j' d $ | ||||||
|                 regenerateTransactions ropts j' s acct i $   -- added (inline) 201512 (why ?) |                 regenerateTransactions rspec j' s acct i $   -- added (inline) 201512 (why ?) | ||||||
|                 clearCostValue $ |                 clearCostValue $ | ||||||
|                 ui |                 ui | ||||||
|         VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) |         VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) | ||||||
| @ -207,15 +207,12 @@ tsHandle _ _ = error "event handler called with wrong screen type, should not ha | |||||||
| 
 | 
 | ||||||
| -- Got to redo the register screen's transactions report, to get the latest transactions list for this screen. | -- Got to redo the register screen's transactions report, to get the latest transactions list for this screen. | ||||||
| -- XXX Duplicates rsInit. Why do we have to do this as well as regenerateScreens ? | -- XXX Duplicates rsInit. Why do we have to do this as well as regenerateScreens ? | ||||||
| regenerateTransactions :: ReportOpts -> Journal -> Screen -> AccountName -> Integer -> UIState -> UIState | regenerateTransactions :: ReportSpec -> Journal -> Screen -> AccountName -> Integer -> UIState -> UIState | ||||||
| regenerateTransactions ropts j s acct i ui = | regenerateTransactions rspec j s acct i ui = | ||||||
|   let |   let | ||||||
|     ropts' = ropts {depth_=Nothing |     q = filterQuery (not . queryIsDepth) $ rsQuery rspec | ||||||
|                    ,balancetype_=HistoricalBalance |  | ||||||
|                    } |  | ||||||
|     q = filterQuery (not . queryIsDepth) $ query_ ropts' |  | ||||||
|     thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs |     thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs | ||||||
|     items = reverse $ snd $ accountTransactionsReport ropts j q thisacctq |     items = reverse $ snd $ accountTransactionsReport rspec j q thisacctq | ||||||
|     ts = map first6 items |     ts = map first6 items | ||||||
|     numberedts = zip [1..] ts |     numberedts = zip [1..] ts | ||||||
|     -- select the best current transaction from the new list |     -- select the best current transaction from the new list | ||||||
|  | |||||||
| @ -21,18 +21,18 @@ import Hledger.UI.UIOptions | |||||||
| 
 | 
 | ||||||
| -- | Toggle between showing only unmarked items or all items. | -- | Toggle between showing only unmarked items or all items. | ||||||
| toggleUnmarked :: UIState -> UIState | toggleUnmarked :: UIState -> UIState | ||||||
| toggleUnmarked ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleUnmarked ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Unmarked copts ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatusSomehow Unmarked copts rspec}}} | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing only pending items or all items. | -- | Toggle between showing only pending items or all items. | ||||||
| togglePending :: UIState -> UIState | togglePending :: UIState -> UIState | ||||||
| togglePending ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | togglePending ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Pending copts ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatusSomehow Pending copts rspec}}} | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing only cleared items or all items. | -- | Toggle between showing only cleared items or all items. | ||||||
| toggleCleared :: UIState -> UIState | toggleCleared :: UIState -> UIState | ||||||
| toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Cleared copts ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatusSomehow Cleared copts rspec}}} | ||||||
| 
 | 
 | ||||||
| -- TODO testing different status toggle styles | -- TODO testing different status toggle styles | ||||||
| 
 | 
 | ||||||
| @ -52,9 +52,10 @@ uiShowStatus copts ss = | |||||||
|     showstatus Pending  = "pending" |     showstatus Pending  = "pending" | ||||||
|     showstatus Unmarked = "unmarked" |     showstatus Unmarked = "unmarked" | ||||||
| 
 | 
 | ||||||
| reportOptsToggleStatusSomehow :: Status -> CliOpts -> ReportOpts -> ReportOpts | reportSpecToggleStatusSomehow :: Status -> CliOpts -> ReportSpec -> ReportSpec | ||||||
| reportOptsToggleStatusSomehow s copts ropts = | reportSpecToggleStatusSomehow s copts rspec = rspec{rsOpts=ropts} | ||||||
|   case maybeposintopt "status-toggles" $ rawopts_ copts of |   where | ||||||
|  |     ropts = case maybeposintopt "status-toggles" $ rawopts_ copts of | ||||||
|       Just 2 -> reportOptsToggleStatus2 s ropts |       Just 2 -> reportOptsToggleStatus2 s ropts | ||||||
|       Just 3 -> reportOptsToggleStatus3 s ropts |       Just 3 -> reportOptsToggleStatus3 s ropts | ||||||
| --      Just 4 -> reportOptsToggleStatus4 s ropts | --      Just 4 -> reportOptsToggleStatus4 s ropts | ||||||
| @ -102,26 +103,26 @@ complement = ([minBound..maxBound] \\) | |||||||
| 
 | 
 | ||||||
| -- | Toggle between showing all and showing only nonempty (more precisely, nonzero) items. | -- | Toggle between showing all and showing only nonempty (more precisely, nonzero) items. | ||||||
| toggleEmpty :: UIState -> UIState | toggleEmpty :: UIState -> UIState | ||||||
| toggleEmpty ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleEmpty ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=toggleEmpty ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=toggleEmpty ropts}}}} | ||||||
|   where |   where | ||||||
|     toggleEmpty ropts = ropts{empty_=not $ empty_ ropts} |     toggleEmpty ropts = ropts{empty_=not $ empty_ ropts} | ||||||
| 
 | 
 | ||||||
| -- | Show primary amounts, not cost or value. | -- | Show primary amounts, not cost or value. | ||||||
| clearCostValue :: UIState -> UIState | clearCostValue :: UIState -> UIState | ||||||
| clearCostValue ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | clearCostValue ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{value_ = plog "clearing value mode" Nothing}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{value_ = plog "clearing value mode" Nothing}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing the primary amounts or costs. | -- | Toggle between showing the primary amounts or costs. | ||||||
| toggleCost :: UIState -> UIState | toggleCost :: UIState -> UIState | ||||||
| toggleCost ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleCost ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{value_ = valuationToggleCost $ value_ ropts}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{value_ = valuationToggleCost $ value_ ropts}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing primary amounts or default valuation. | -- | Toggle between showing primary amounts or default valuation. | ||||||
| toggleValue :: UIState -> UIState | toggleValue :: UIState -> UIState | ||||||
| toggleValue ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleValue ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{ |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{ | ||||||
|     value_ = plog "toggling value mode to" $ valuationToggleValue $ value_ ropts}}}} |     value_ = plog "toggling value mode to" $ valuationToggleValue $ value_ ropts}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Basic toggling of -B/cost, for hledger-ui. | -- | Basic toggling of -B/cost, for hledger-ui. | ||||||
| valuationToggleCost :: Maybe ValuationType -> Maybe ValuationType | valuationToggleCost :: Maybe ValuationType -> Maybe ValuationType | ||||||
| @ -135,18 +136,18 @@ valuationToggleValue _                    = Just $ AtDefault Nothing | |||||||
| 
 | 
 | ||||||
| -- | Set hierarchic account tree mode. | -- | Set hierarchic account tree mode. | ||||||
| setTree :: UIState -> UIState | setTree :: UIState -> UIState | ||||||
| setTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | setTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{accountlistmode_=ALTree}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{accountlistmode_=ALTree}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Set flat account list mode. | -- | Set flat account list mode. | ||||||
| setList :: UIState -> UIState | setList :: UIState -> UIState | ||||||
| setList ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | setList ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{accountlistmode_=ALFlat}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{accountlistmode_=ALFlat}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Toggle between flat and tree mode. If current mode is unspecified/default, assume it's flat. | -- | Toggle between flat and tree mode. If current mode is unspecified/default, assume it's flat. | ||||||
| toggleTree :: UIState -> UIState | toggleTree :: UIState -> UIState | ||||||
| toggleTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=toggleTreeMode ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=toggleTreeMode ropts}}}} | ||||||
|   where |   where | ||||||
|     toggleTreeMode ropts |     toggleTreeMode ropts | ||||||
|       | accountlistmode_ ropts == ALTree = ropts{accountlistmode_=ALFlat} |       | accountlistmode_ ropts == ALTree = ropts{accountlistmode_=ALFlat} | ||||||
| @ -154,8 +155,8 @@ toggleTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropt | |||||||
| 
 | 
 | ||||||
| -- | Toggle between historical balances and period balances. | -- | Toggle between historical balances and period balances. | ||||||
| toggleHistorical :: UIState -> UIState | toggleHistorical :: UIState -> UIState | ||||||
| toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{balancetype_=b}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{balancetype_=b}}}}} | ||||||
|   where |   where | ||||||
|     b | balancetype_ ropts == HistoricalBalance = PeriodChange |     b | balancetype_ ropts == HistoricalBalance = PeriodChange | ||||||
|       | otherwise                               = HistoricalBalance |       | otherwise                               = HistoricalBalance | ||||||
| @ -174,10 +175,10 @@ toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts | |||||||
| -- transactions with a query for their special tag. | -- transactions with a query for their special tag. | ||||||
| -- | -- | ||||||
| toggleForecast :: Day -> UIState -> UIState | toggleForecast :: Day -> UIState -> UIState | ||||||
| toggleForecast d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleForecast d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts'}} |   ui{aopts=uopts{cliopts_=copts'}} | ||||||
|   where |   where | ||||||
|     copts' = copts{reportopts_=ropts{forecast_=forecast'}} |     copts' = copts{reportspec_=rspec{rsOpts=ropts{forecast_=forecast'}}} | ||||||
|     forecast' = |     forecast' = | ||||||
|       case forecast_ ropts of |       case forecast_ ropts of | ||||||
|         Just _  -> Nothing |         Just _  -> Nothing | ||||||
| @ -185,8 +186,8 @@ toggleForecast d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts | |||||||
| 
 | 
 | ||||||
| -- | Toggle between showing all and showing only real (non-virtual) items. | -- | Toggle between showing all and showing only real (non-virtual) items. | ||||||
| toggleReal :: UIState -> UIState | toggleReal :: UIState -> UIState | ||||||
| toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=toggleReal ropts}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=toggleReal ropts}}}} | ||||||
|   where |   where | ||||||
|     toggleReal ropts = ropts{real_=not $ real_ ropts} |     toggleReal ropts = ropts{real_=not $ real_ ropts} | ||||||
| 
 | 
 | ||||||
| @ -197,41 +198,41 @@ toggleIgnoreBalanceAssertions ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOp | |||||||
| 
 | 
 | ||||||
| -- | Step through larger report periods, up to all. | -- | Step through larger report periods, up to all. | ||||||
| growReportPeriod :: Day -> UIState -> UIState | growReportPeriod :: Day -> UIState -> UIState | ||||||
| growReportPeriod _d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | growReportPeriod _d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodGrow $ period_ ropts}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=periodGrow $ period_ ropts}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Step through smaller report periods, down to a day. | -- | Step through smaller report periods, down to a day. | ||||||
| shrinkReportPeriod :: Day -> UIState -> UIState | shrinkReportPeriod :: Day -> UIState -> UIState | ||||||
| shrinkReportPeriod d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | shrinkReportPeriod d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodShrink d $ period_ ropts}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=periodShrink d $ period_ ropts}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Step the report start/end dates to the next period of same duration, | -- | Step the report start/end dates to the next period of same duration, | ||||||
| -- remaining inside the given enclosing span. | -- remaining inside the given enclosing span. | ||||||
| nextReportPeriod :: DateSpan -> UIState -> UIState | nextReportPeriod :: DateSpan -> UIState -> UIState | ||||||
| nextReportPeriod enclosingspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{period_=p}}}} = | nextReportPeriod enclosingspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts@ReportOpts{period_=p}}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodNextIn enclosingspan p}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=periodNextIn enclosingspan p}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Step the report start/end dates to the next period of same duration, | -- | Step the report start/end dates to the next period of same duration, | ||||||
| -- remaining inside the given enclosing span. | -- remaining inside the given enclosing span. | ||||||
| previousReportPeriod :: DateSpan -> UIState -> UIState | previousReportPeriod :: DateSpan -> UIState -> UIState | ||||||
| previousReportPeriod enclosingspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{period_=p}}}} = | previousReportPeriod enclosingspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts@ReportOpts{period_=p}}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodPreviousIn enclosingspan p}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=periodPreviousIn enclosingspan p}}}}} | ||||||
| 
 | 
 | ||||||
| -- | If a standard report period is set, step it forward/backward if needed so that | -- | If a standard report period is set, step it forward/backward if needed so that | ||||||
| -- it encloses the given date. | -- it encloses the given date. | ||||||
| moveReportPeriodToDate :: Day -> UIState -> UIState | moveReportPeriodToDate :: Day -> UIState -> UIState | ||||||
| moveReportPeriodToDate d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{period_=p}}}} = | moveReportPeriodToDate d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts@ReportOpts{period_=p}}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodMoveTo d p}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=periodMoveTo d p}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Get the report period. | -- | Get the report period. | ||||||
| reportPeriod :: UIState -> Period | reportPeriod :: UIState -> Period | ||||||
| reportPeriod UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ReportOpts{period_=p}}}} = | reportPeriod UIState{aopts=UIOpts{cliopts_=CliOpts{reportspec_=ReportSpec{rsOpts=ReportOpts{period_=p}}}}} = | ||||||
|   p |   p | ||||||
| 
 | 
 | ||||||
| -- | Set the report period. | -- | Set the report period. | ||||||
| setReportPeriod :: Period -> UIState -> UIState | setReportPeriod :: Period -> UIState -> UIState | ||||||
| setReportPeriod p ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | setReportPeriod p ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=p}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{period_=p}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Clear any report period limits. | -- | Clear any report period limits. | ||||||
| resetReportPeriod :: UIState -> UIState | resetReportPeriod :: UIState -> UIState | ||||||
| @ -239,21 +240,24 @@ resetReportPeriod = setReportPeriod PeriodAll | |||||||
| 
 | 
 | ||||||
| -- | Apply a new filter query. | -- | Apply a new filter query. | ||||||
| setFilter :: String -> UIState -> UIState | setFilter :: String -> UIState -> UIState | ||||||
| setFilter s ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | setFilter s ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|     ui{aopts=uopts{cliopts_=copts{reportopts_=newRopts}}} |     ui{aopts=uopts{cliopts_=copts{reportspec_=newrspec}}} | ||||||
|   where |   where | ||||||
|     newRopts = either (const ropts) id $ regenerateReportOpts (today_ ropts) (T.pack s) ropts |     newrspec = either (const rspec) id $ reportOptsToSpec (rsToday rspec) ropts{querystring_=T.pack s} | ||||||
| 
 | 
 | ||||||
| -- | Reset some filters & toggles. | -- | Reset some filters & toggles. | ||||||
| resetFilter :: UIState -> UIState | resetFilter :: UIState -> UIState | ||||||
| resetFilter ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | resetFilter ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{ |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{ | ||||||
|  |      rsQuery=Any | ||||||
|  |     ,rsQueryOpts=[] | ||||||
|  |     ,rsOpts=ropts{ | ||||||
|        empty_=True |        empty_=True | ||||||
|       ,statuses_=[] |       ,statuses_=[] | ||||||
|       ,real_=False |       ,real_=False | ||||||
|     ,query_=Any |       ,querystring_="" | ||||||
|       --,period_=PeriodAll |       --,period_=PeriodAll | ||||||
|     }}}} |     }}}}} | ||||||
| 
 | 
 | ||||||
| -- | Reset all options state to exactly what it was at startup | -- | Reset all options state to exactly what it was at startup | ||||||
| -- (preserving any command-line options/arguments). | -- (preserving any command-line options/arguments). | ||||||
| @ -261,8 +265,8 @@ resetOpts :: UIState -> UIState | |||||||
| resetOpts ui@UIState{astartupopts} = ui{aopts=astartupopts} | resetOpts ui@UIState{astartupopts} = ui{aopts=astartupopts} | ||||||
| 
 | 
 | ||||||
| resetDepth :: UIState -> UIState | resetDepth :: UIState -> UIState | ||||||
| resetDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | resetDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} = | ||||||
|   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=Nothing}}}} |   ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{depth_=Nothing}}}}} | ||||||
| 
 | 
 | ||||||
| -- | Get the maximum account depth in the current journal. | -- | Get the maximum account depth in the current journal. | ||||||
| maxDepth :: UIState -> Int | maxDepth :: UIState -> Int | ||||||
| @ -271,8 +275,8 @@ maxDepth UIState{ajournal=j} = maximum $ map accountNameLevel $ journalAccountNa | |||||||
| -- | Decrement the current depth limit towards 0. If there was no depth limit, | -- | Decrement the current depth limit towards 0. If there was no depth limit, | ||||||
| -- set it to one less than the maximum account depth. | -- set it to one less than the maximum account depth. | ||||||
| decDepth :: UIState -> UIState | decDepth :: UIState -> UIState | ||||||
| decDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}} | decDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts@ReportOpts{..}}}}} | ||||||
|   = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=dec depth_}}}} |   = ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{depth_=dec depth_}}}}} | ||||||
|   where |   where | ||||||
|     dec (Just d) = Just $ max 0 (d-1) |     dec (Just d) = Just $ max 0 (d-1) | ||||||
|     dec Nothing  = Just $ maxDepth ui - 1 |     dec Nothing  = Just $ maxDepth ui - 1 | ||||||
| @ -280,8 +284,8 @@ decDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ | |||||||
| -- | Increment the current depth limit. If this makes it equal to the | -- | Increment the current depth limit. If this makes it equal to the | ||||||
| -- the maximum account depth, remove the depth limit. | -- the maximum account depth, remove the depth limit. | ||||||
| incDepth :: UIState -> UIState | incDepth :: UIState -> UIState | ||||||
| incDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}} | incDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts@ReportOpts{..}}}}} | ||||||
|   = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=inc depth_}}}} |   = ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{depth_=inc depth_}}}}} | ||||||
|   where |   where | ||||||
|     inc (Just d) | d < (maxDepth ui - 1) = Just $ d+1 |     inc (Just d) | d < (maxDepth ui - 1) = Just $ d+1 | ||||||
|     inc _ = Nothing |     inc _ = Nothing | ||||||
| @ -291,8 +295,8 @@ incDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ | |||||||
| -- maximum account depth. If the specified depth is negative, reset the depth limit | -- maximum account depth. If the specified depth is negative, reset the depth limit | ||||||
| -- to whatever was specified at uiartup. | -- to whatever was specified at uiartup. | ||||||
| setDepth :: Maybe Int -> UIState -> UIState | setDepth :: Maybe Int -> UIState -> UIState | ||||||
| setDepth mdepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} | setDepth mdepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} | ||||||
|   = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=mdepth'}}}} |   = ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{depth_=mdepth'}}}}} | ||||||
|   where |   where | ||||||
|     mdepth' = case mdepth of |     mdepth' = case mdepth of | ||||||
|                 Nothing                   -> Nothing |                 Nothing                   -> Nothing | ||||||
| @ -301,14 +305,14 @@ setDepth mdepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_ | |||||||
|                        | otherwise        -> mdepth |                        | otherwise        -> mdepth | ||||||
| 
 | 
 | ||||||
| getDepth :: UIState -> Maybe Int | getDepth :: UIState -> Maybe Int | ||||||
| getDepth UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}} = depth_ ropts | getDepth UIState{aopts=UIOpts{cliopts_=CliOpts{reportspec_=rspec}}} = depth_ $ rsOpts rspec | ||||||
| 
 | 
 | ||||||
| -- | Open the minibuffer, setting its content to the current query with the cursor at the end. | -- | Open the minibuffer, setting its content to the current query with the cursor at the end. | ||||||
| showMinibuffer :: UIState -> UIState | showMinibuffer :: UIState -> UIState | ||||||
| showMinibuffer ui = setMode (Minibuffer e) ui | showMinibuffer ui = setMode (Minibuffer e) ui | ||||||
|   where |   where | ||||||
|     e = applyEdit gotoEOL $ editor MinibufferEditor (Just 1) oldq |     e = applyEdit gotoEOL $ editor MinibufferEditor (Just 1) oldq | ||||||
|     oldq = T.unpack . querystring_ . reportopts_ . cliopts_ $ aopts ui |     oldq = T.unpack . querystring_ . rsOpts . reportspec_ . cliopts_ $ aopts ui | ||||||
| 
 | 
 | ||||||
| -- | Close the minibuffer, discarding any edit in progress. | -- | Close the minibuffer, discarding any edit in progress. | ||||||
| closeMinibuffer :: UIState -> UIState | closeMinibuffer :: UIState -> UIState | ||||||
|  | |||||||
| @ -123,14 +123,16 @@ instance Yesod App where | |||||||
|     showSidebar <- shouldShowSidebar |     showSidebar <- shouldShowSidebar | ||||||
|     hideEmptyAccts <- (== Just "1") . lookup "hideemptyaccts" . reqCookies <$> getRequest |     hideEmptyAccts <- (== Just "1") . lookup "hideemptyaccts" . reqCookies <$> getRequest | ||||||
| 
 | 
 | ||||||
|     let ropts = reportopts_ (cliopts_ opts) |     let rspec = reportspec_ (cliopts_ opts) | ||||||
|         ropts' = ropts |         ropts = rsOpts rspec | ||||||
|  |         ropts' = (rsOpts rspec) | ||||||
|           {accountlistmode_ = ALTree  -- force tree mode for sidebar |           {accountlistmode_ = ALTree  -- force tree mode for sidebar | ||||||
|           ,empty_           = not (empty_ ropts)  -- show zero items by default |           ,empty_           = not (empty_ ropts)  -- show zero items by default | ||||||
|           } |           } | ||||||
|  |         rspec' = rspec{rsQuery=m,rsOpts=ropts'} | ||||||
|         accounts = |         accounts = | ||||||
|           balanceReportAsHtml (JournalR, RegisterR) here hideEmptyAccts j q qopts $ |           balanceReportAsHtml (JournalR, RegisterR) here hideEmptyAccts j q qopts $ | ||||||
|           balanceReport ropts'{query_=m} j |           balanceReport rspec' j | ||||||
| 
 | 
 | ||||||
|         topShowmd = if showSidebar then "col-md-4" else "col-any-0" :: Text |         topShowmd = if showSidebar then "col-md-4" else "col-any-0" :: Text | ||||||
|         topShowsm = if showSidebar then "col-sm-4" else "" :: Text |         topShowsm = if showSidebar then "col-sm-4" else "" :: Text | ||||||
| @ -206,12 +208,14 @@ getViewData = do | |||||||
|   App {appOpts = opts, appJournal} <- getYesod |   App {appOpts = opts, appJournal} <- getYesod | ||||||
|   today <- liftIO getCurrentDay |   today <- liftIO getCurrentDay | ||||||
|   let copts = cliopts_ opts |   let copts = cliopts_ opts | ||||||
|  |       rspec = (reportspec_ copts){rsOpts=ropts} | ||||||
|  |       ropts = (rsOpts rspec){no_elide_ = True} | ||||||
| 
 | 
 | ||||||
|   -- try to read the latest journal content, keeping the old content |   -- try to read the latest journal content, keeping the old content | ||||||
|   -- if there's an error |   -- if there's an error | ||||||
|   (j, mjerr) <- getCurrentJournal |   (j, mjerr) <- getCurrentJournal | ||||||
|                 appJournal |                 appJournal | ||||||
|                 copts {reportopts_ = (reportopts_ copts) {no_elide_ = True}} |                 copts {reportspec_ = rspec} | ||||||
|                 today |                 today | ||||||
| 
 | 
 | ||||||
|   -- try to parse the query param, assuming no query if there's an error |   -- try to parse the query param, assuming no query if there's an error | ||||||
| @ -265,7 +269,7 @@ getCurrentJournal jref opts d = do | |||||||
|   j <- liftIO (readIORef jref) |   j <- liftIO (readIORef jref) | ||||||
|   (ej, changed) <- liftIO $ journalReloadIfChanged opts d j |   (ej, changed) <- liftIO $ journalReloadIfChanged opts d j | ||||||
|   -- re-apply any initial filter specified at startup |   -- re-apply any initial filter specified at startup | ||||||
|   let initq = query_ $ reportopts_ opts |   let initq = rsQuery $ reportspec_ opts | ||||||
|   case (changed, filterJournalTransactions initq <$> ej) of |   case (changed, filterJournalTransactions initq <$> ej) of | ||||||
|     (False, _) -> return (j, Nothing) |     (False, _) -> return (j, Nothing) | ||||||
|     (True, Right j') -> do |     (True, Right j') -> do | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ getJournalR = do | |||||||
|         Just (a, inclsubs) -> "Transactions in " <> a <> if inclsubs then "" else " (excluding subaccounts)" |         Just (a, inclsubs) -> "Transactions in " <> a <> if inclsubs then "" else " (excluding subaccounts)" | ||||||
|       title' = title <> if m /= Any then ", filtered" else "" |       title' = title <> if m /= Any then ", filtered" else "" | ||||||
|       acctlink a = (RegisterR, [("q", replaceInacct q $ accountQuery a)]) |       acctlink a = (RegisterR, [("q", replaceInacct q $ accountQuery a)]) | ||||||
|       (_, items) = transactionsReport (reportopts_ $ cliopts_ opts) j m |       (_, items) = transactionsReport (rsOpts . reportspec_ $ cliopts_ opts) j m | ||||||
|       transactionFrag = transactionFragment j |       transactionFrag = transactionFragment j | ||||||
| 
 | 
 | ||||||
|   defaultLayout $ do |   defaultLayout $ do | ||||||
|  | |||||||
| @ -102,9 +102,9 @@ getAccounttransactionsR a = do | |||||||
|   VD{caps, j} <- getViewData |   VD{caps, j} <- getViewData | ||||||
|   when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") |   when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") | ||||||
|   let |   let | ||||||
|     ropts = defreportopts |     rspec = defreportspec | ||||||
|     q = Any --filterQuery (not . queryIsDepth) $ queryFromOpts d ropts' |     q = Any --filterQuery (not . queryIsDepth) $ queryFromOpts d ropts' | ||||||
|     thisacctq = Acct $ accountNameToAccountRegex a -- includes subs |     thisacctq = Acct $ accountNameToAccountRegex a -- includes subs | ||||||
|   selectRep $ do |   selectRep $ do | ||||||
|     provideJson $ accountTransactionsReport ropts j q thisacctq |     provideJson $ accountTransactionsReport rspec j q thisacctq | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ getRegisterR = do | |||||||
|       s2 = if m /= Any then ", filtered" else "" |       s2 = if m /= Any then ", filtered" else "" | ||||||
|       header = a <> s1 <> s2 |       header = a <> s1 <> s2 | ||||||
| 
 | 
 | ||||||
|   let ropts = reportopts_ (cliopts_ opts) |   let rspec = reportspec_ (cliopts_ opts) | ||||||
|       acctQuery = fromMaybe Any (inAccountQuery qopts) |       acctQuery = fromMaybe Any (inAccountQuery qopts) | ||||||
|       acctlink acc = (RegisterR, [("q", replaceInacct q $ accountQuery acc)]) |       acctlink acc = (RegisterR, [("q", replaceInacct q $ accountQuery acc)]) | ||||||
|       otherTransAccounts = |       otherTransAccounts = | ||||||
| @ -44,7 +44,7 @@ getRegisterR = do | |||||||
|           zip xs $ |           zip xs $ | ||||||
|           zip (map (T.unpack . accountSummarisedName . paccount) xs) $ |           zip (map (T.unpack . accountSummarisedName . paccount) xs) $ | ||||||
|           tail $ (", "<$xs) ++ [""] |           tail $ (", "<$xs) ++ [""] | ||||||
|       r@(balancelabel,items) = accountTransactionsReport ropts j m acctQuery |       r@(balancelabel,items) = accountTransactionsReport rspec j m acctQuery | ||||||
|       balancelabel' = if isJust (inAccount qopts) then balancelabel else "Total" |       balancelabel' = if isJust (inAccount qopts) then balancelabel else "Total" | ||||||
|       transactionFrag = transactionFragment j |       transactionFrag = transactionFragment j | ||||||
|   defaultLayout $ do |   defaultLayout $ do | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ runWith opts | |||||||
| -- | The web command. | -- | The web command. | ||||||
| web :: WebOpts -> Journal -> IO () | web :: WebOpts -> Journal -> IO () | ||||||
| web opts j = do | web opts j = do | ||||||
|   let initq = query_ . reportopts_ $ cliopts_ opts |   let initq = rsQuery . reportspec_ $ cliopts_ opts | ||||||
|       j' = filterJournalTransactions initq j |       j' = filterJournalTransactions initq j | ||||||
|       h = host_ opts |       h = host_ opts | ||||||
|       p = port_ opts |       p = port_ opts | ||||||
|  | |||||||
| @ -399,7 +399,7 @@ data CliOpts = CliOpts { | |||||||
|     ,command_         :: String |     ,command_         :: String | ||||||
|     ,file_            :: [FilePath] |     ,file_            :: [FilePath] | ||||||
|     ,inputopts_       :: InputOpts |     ,inputopts_       :: InputOpts | ||||||
|     ,reportopts_      :: ReportOpts |     ,reportspec_      :: ReportSpec | ||||||
|     ,output_file_     :: Maybe FilePath |     ,output_file_     :: Maybe FilePath | ||||||
|     ,output_format_   :: Maybe String |     ,output_format_   :: Maybe String | ||||||
|     ,debug_           :: Int            -- ^ debug level, set by @--debug[=N]@. See also 'Hledger.Utils.debugLevel'. |     ,debug_           :: Int            -- ^ debug level, set by @--debug[=N]@. See also 'Hledger.Utils.debugLevel'. | ||||||
| @ -419,7 +419,7 @@ defcliopts = CliOpts | |||||||
|     , command_         = "" |     , command_         = "" | ||||||
|     , file_            = [] |     , file_            = [] | ||||||
|     , inputopts_       = def |     , inputopts_       = def | ||||||
|     , reportopts_      = def |     , reportspec_      = def | ||||||
|     , output_file_     = Nothing |     , output_file_     = Nothing | ||||||
|     , output_format_   = Nothing |     , output_format_   = Nothing | ||||||
|     , debug_           = 0 |     , debug_           = 0 | ||||||
| @ -447,7 +447,7 @@ replaceNumericFlags = map replace | |||||||
| rawOptsToCliOpts :: RawOpts -> IO CliOpts | rawOptsToCliOpts :: RawOpts -> IO CliOpts | ||||||
| rawOptsToCliOpts rawopts = do | rawOptsToCliOpts rawopts = do | ||||||
|   let iopts = rawOptsToInputOpts rawopts |   let iopts = rawOptsToInputOpts rawopts | ||||||
|   ropts <- rawOptsToReportOpts rawopts |   rspec <- rawOptsToReportSpec rawopts | ||||||
|   mcolumns <- readMay <$> getEnvSafe "COLUMNS" |   mcolumns <- readMay <$> getEnvSafe "COLUMNS" | ||||||
|   mtermwidth <- |   mtermwidth <- | ||||||
| #ifdef mingw32_HOST_OS | #ifdef mingw32_HOST_OS | ||||||
| @ -462,7 +462,7 @@ rawOptsToCliOpts rawopts = do | |||||||
|              ,command_         = stringopt "command" rawopts |              ,command_         = stringopt "command" rawopts | ||||||
|              ,file_            = listofstringopt "file" rawopts |              ,file_            = listofstringopt "file" rawopts | ||||||
|              ,inputopts_       = iopts |              ,inputopts_       = iopts | ||||||
|              ,reportopts_      = ropts |              ,reportspec_      = rspec | ||||||
|              ,output_file_     = maybestringopt "output-file" rawopts |              ,output_file_     = maybestringopt "output-file" rawopts | ||||||
|              ,output_format_   = maybestringopt "output-format" rawopts |              ,output_format_   = maybestringopt "output-format" rawopts | ||||||
|              ,debug_           = posintopt "debug" rawopts |              ,debug_           = posintopt "debug" rawopts | ||||||
| @ -519,7 +519,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 | ||||||
|         putStrLn $ "search query: " ++ show (query_ $ reportopts_ opts) |         putStrLn $ "search query: " ++ show (rsQuery $ reportspec_ opts) | ||||||
| 
 | 
 | ||||||
| getHledgerCliOpts :: Mode RawOpts -> IO CliOpts | getHledgerCliOpts :: Mode RawOpts -> IO CliOpts | ||||||
| getHledgerCliOpts mode' = do | getHledgerCliOpts mode' = do | ||||||
|  | |||||||
| @ -48,18 +48,17 @@ accountsmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- | The accounts command. | -- | The accounts command. | ||||||
| accounts :: CliOpts -> Journal -> IO () | accounts :: CliOpts -> Journal -> IO () | ||||||
| accounts CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | accounts CliOpts{rawopts_=rawopts, reportspec_=ReportSpec{rsQuery=query,rsOpts=ropts}} j = do | ||||||
| 
 | 
 | ||||||
|   -- 1. identify the accounts we'll show |   -- 1. identify the accounts we'll show | ||||||
|   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        = 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) query | ||||||
|       -- 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 | ||||||
|       acctq    = dbg1 "acctq" $ filterQuery queryIsAcct q |       acctq    = dbg1 "acctq" $ filterQuery queryIsAcct query | ||||||
|       depth    = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth q |       depth    = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth query | ||||||
|       matcheddeclaredaccts = dbg1 "matcheddeclaredaccts" $ filter (matchesAccount nodepthq) $ map fst $ jdeclaredaccounts j |       matcheddeclaredaccts = dbg1 "matcheddeclaredaccts" $ filter (matchesAccount nodepthq) $ map fst $ jdeclaredaccounts j | ||||||
|       matchedusedaccts     = dbg5 "matchedusedaccts" $ map paccount $ journalPostings $ filterJournalPostings nodepthq j |       matchedusedaccts     = dbg5 "matchedusedaccts" $ map paccount $ journalPostings $ filterJournalPostings nodepthq j | ||||||
|       accts                = dbg5 "accts to show" $ -- no need to nub/sort, accountTree will |       accts                = dbg5 "accts to show" $ -- no need to nub/sort, accountTree will | ||||||
|  | |||||||
| @ -30,10 +30,10 @@ 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 = putStr $ showHistogram ropts j | activity CliOpts{reportspec_=rspec} j = putStr $ showHistogram rspec j | ||||||
| 
 | 
 | ||||||
| showHistogram :: ReportOpts -> Journal -> String | showHistogram :: ReportSpec -> Journal -> String | ||||||
| showHistogram ReportOpts{query_=q,interval_=i,date2_=date2} j = | showHistogram ReportSpec{rsQuery=q,rsOpts=ReportOpts{interval_=i,date2_=date2}} j = | ||||||
|     concatMap (printDayWith countBar) spanps |     concatMap (printDayWith countBar) spanps | ||||||
|   where |   where | ||||||
|     interval | i == NoInterval = Days 1 |     interval | i == NoInterval = Days 1 | ||||||
|  | |||||||
| @ -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 = queryFromFlags $ reportopts_ esOpts |   let q = queryFromFlags . rsOpts $ reportspec_ 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 | ||||||
| @ -462,10 +462,11 @@ ensureOneNewlineTerminated = (++"\n") . reverse . dropWhile (=='\n') . reverse | |||||||
| registerFromString :: String -> IO String | registerFromString :: String -> IO String | ||||||
| registerFromString s = do | registerFromString s = do | ||||||
|   j <- readJournal' $ T.pack s |   j <- readJournal' $ T.pack s | ||||||
|   return . postingsReportAsText opts $ postingsReport ropts j |   return . postingsReportAsText opts $ postingsReport rspec j | ||||||
|       where |       where | ||||||
|         ropts = defreportopts{empty_=True} |         ropts = defreportopts{empty_=True} | ||||||
|         opts = defcliopts{reportopts_=ropts} |         rspec = defreportspec{rsOpts=ropts} | ||||||
|  |         opts = defcliopts{reportspec_=rspec} | ||||||
| 
 | 
 | ||||||
| capitalize :: String -> String | capitalize :: String -> String | ||||||
| capitalize "" = "" | capitalize "" = "" | ||||||
|  | |||||||
| @ -71,7 +71,7 @@ aregistermode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- | Print an account register report for a specified account. | -- | Print an account register report for a specified account. | ||||||
| aregister :: CliOpts -> Journal -> IO () | aregister :: CliOpts -> Journal -> IO () | ||||||
| aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | aregister opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} 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 | ||||||
|   (apat,querystring) <- case listofstringopt "args" rawopts of |   (apat,querystring) <- case listofstringopt "args" rawopts of | ||||||
| @ -87,14 +87,17 @@ aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | |||||||
|     -- gather report options |     -- gather report options | ||||||
|     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{ |     rspec' = rspec{ rsQuery=simplifyQuery $ And [queryFromFlags ropts, argsquery] | ||||||
|        query_=simplifyQuery $ And [queryFromFlags ropts, argsquery] |                   , rsOpts=ropts' | ||||||
|        -- remove a depth limit for reportq, as in RegisterScreen, I forget why XXX |                   } | ||||||
|       ,depth_=Nothing |     ropts' = ropts | ||||||
|  |       { -- remove a depth limit for reportq, as in RegisterScreen, I forget why XXX | ||||||
|  |         depth_=Nothing | ||||||
|         -- always show historical balance |         -- always show historical balance | ||||||
|       , balancetype_= HistoricalBalance |       , balancetype_= HistoricalBalance | ||||||
|       } |       } | ||||||
|     reportq = And [query_ ropts', excludeforecastq (isJust $ forecast_ ropts)] |     ropts = rsOpts rspec | ||||||
|  |     reportq = And [rsQuery rspec', 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. | ||||||
| @ -106,7 +109,7 @@ aregister opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | |||||||
|           ] |           ] | ||||||
|     -- run the report |     -- run the report | ||||||
|     -- TODO: need to also pass the queries so we can choose which date to render - move them into the report ? |     -- TODO: need to also pass the queries so we can choose which date to render - move them into the report ? | ||||||
|     (balancelabel,items) = accountTransactionsReport ropts' j reportq thisacctq |     (balancelabel,items) = accountTransactionsReport rspec' j reportq thisacctq | ||||||
|     items' = (if empty_ ropts then id else filter (not . mixedAmountLooksZero . fifth6)) $ |     items' = (if empty_ ropts then id else filter (not . mixedAmountLooksZero . fifth6)) $ | ||||||
|              reverse items |              reverse items | ||||||
|     -- select renderer |     -- select renderer | ||||||
| @ -140,7 +143,7 @@ accountTransactionsReportItemAsCsvRecord | |||||||
| -- | Render a register report as plain text suitable for console output. | -- | Render a register report as plain text suitable for console output. | ||||||
| accountTransactionsReportAsText :: CliOpts -> Query -> Query -> AccountTransactionsReport -> String | accountTransactionsReportAsText :: CliOpts -> Query -> Query -> AccountTransactionsReport -> String | ||||||
| accountTransactionsReportAsText | accountTransactionsReportAsText | ||||||
|   copts@CliOpts{reportopts_=ReportOpts{no_elide_}} reportq thisacctq (_balancelabel,items) |   copts@CliOpts{reportspec_=ReportSpec{rsOpts=ReportOpts{no_elide_}}} reportq thisacctq (_balancelabel,items) | ||||||
|   = unlines $ title : |   = unlines $ title : | ||||||
|     map (accountTransactionsReportItemAsText copts reportq thisacctq amtwidth balwidth) items |     map (accountTransactionsReportItemAsText copts reportq thisacctq amtwidth balwidth) items | ||||||
|   where |   where | ||||||
| @ -173,7 +176,7 @@ accountTransactionsReportAsText | |||||||
| -- | -- | ||||||
| accountTransactionsReportItemAsText :: CliOpts -> Query -> Query -> Int -> Int -> AccountTransactionsReportItem -> String | accountTransactionsReportItemAsText :: CliOpts -> Query -> Query -> Int -> Int -> AccountTransactionsReportItem -> String | ||||||
| accountTransactionsReportItemAsText | accountTransactionsReportItemAsText | ||||||
|   copts@CliOpts{reportopts_=ReportOpts{color_,no_elide_}} |   copts@CliOpts{reportspec_=ReportSpec{rsOpts=ReportOpts{color_,no_elide_}}} | ||||||
|   reportq thisacctq preferredamtwidth preferredbalwidth |   reportq thisacctq preferredamtwidth preferredbalwidth | ||||||
|   (t@Transaction{tdescription}, _, _issplit, otheracctsstr, change, balance) |   (t@Transaction{tdescription}, _, _issplit, otheracctsstr, change, balance) | ||||||
|     -- Transaction -- the transaction, unmodified |     -- Transaction -- the transaction, unmodified | ||||||
|  | |||||||
| @ -304,14 +304,15 @@ 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,reportspec_=rspec} j = do | ||||||
|     let budget      = boolopt "budget" rawopts |     let ropts@ReportOpts{..} = rsOpts rspec | ||||||
|  |         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 | ||||||
|       let reportspan = reportSpan j ropts |       let reportspan = reportSpan j rspec | ||||||
|           budgetreport = dbg4 "budgetreport" $ budgetReport ropts assrt reportspan j |           budgetreport = dbg4 "budgetreport" $ budgetReport rspec 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 | ||||||
| @ -322,7 +323,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 ropts j |         let report = multiBalanceReport rspec 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 | ||||||
| @ -332,7 +333,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 j -- simple Ledger-style balance report |         let report = balanceReport rspec 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 | ||||||
| @ -620,8 +621,8 @@ tests_Balance = tests "Balance" [ | |||||||
|    tests "balanceReportAsText" [ |    tests "balanceReportAsText" [ | ||||||
|     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 rspec = defreportspec | ||||||
|       balanceReportAsText opts (balanceReport opts{today_=fromGregorian 2008 11 26} j) |       balanceReportAsText (rsOpts rspec) (balanceReport rspec{rsToday=fromGregorian 2008 11 26} j) | ||||||
|         @?= |         @?= | ||||||
|         unlines |         unlines | ||||||
|         ["                -100  актив:наличные" |         ["                -100  актив:наличные" | ||||||
|  | |||||||
| @ -21,9 +21,9 @@ checkdatesmode = hledgerCommandMode | |||||||
|   ([], Just $ argsFlag "[QUERY]") |   ([], Just $ argsFlag "[QUERY]") | ||||||
| 
 | 
 | ||||||
| checkdates :: CliOpts -> Journal -> IO () | checkdates :: CliOpts -> Journal -> IO () | ||||||
| checkdates CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | checkdates CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do | ||||||
|   let ropts_ = ropts{accountlistmode_=ALFlat} |   let ropts = (rsOpts rspec){accountlistmode_=ALFlat} | ||||||
|   let ts = filter (query_ ropts_ `matchesTransaction`) $ |   let ts = filter (rsQuery rspec `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 | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ closemode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- debugger, beware: close is incredibly devious. simple rules combine to make a horrid maze. | -- debugger, beware: close is incredibly devious. simple rules combine to make a horrid maze. | ||||||
| -- tests are in tests/close.test. | -- tests are in tests/close.test. | ||||||
| close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | close CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | ||||||
|   today <- getCurrentDay |   today <- getCurrentDay | ||||||
|   let |   let | ||||||
|     -- show opening entry, closing entry, or (default) both ? |     -- show opening entry, closing entry, or (default) both ? | ||||||
| @ -72,8 +72,9 @@ close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | |||||||
|         (Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct) |         (Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct) | ||||||
| 
 | 
 | ||||||
|     -- dates of the closing and opening transactions |     -- dates of the closing and opening transactions | ||||||
|     ropts_ = ropts{balancetype_=HistoricalBalance, accountlistmode_=ALFlat} |     rspec_ = rspec{rsOpts=ropts} | ||||||
|     q = query_ ropts_ |     ropts = (rsOpts rspec){balancetype_=HistoricalBalance, accountlistmode_=ALFlat} | ||||||
|  |     q = rsQuery rspec | ||||||
|     openingdate = fromMaybe today $ queryEndDate False q |     openingdate = fromMaybe today $ queryEndDate False q | ||||||
|     closingdate = addDays (-1) openingdate |     closingdate = addDays (-1) openingdate | ||||||
| 
 | 
 | ||||||
| @ -86,7 +87,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_ j |     (acctbals,_) = balanceReport rspec_ 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 | ||||||
|  | |||||||
| @ -32,8 +32,8 @@ 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{reportspec_=rspec} j = do | ||||||
|   let ts = entriesReport ropts j |   let ts = entriesReport rspec j | ||||||
|       codes = (if empty_ then id else filter (not . T.null)) $ |       codes = (if empty_ (rsOpts rspec) then id else filter (not . T.null)) $ | ||||||
|               map tcode ts |               map tcode ts | ||||||
|   mapM_ T.putStrLn codes |   mapM_ T.putStrLn codes | ||||||
|  | |||||||
| @ -31,8 +31,8 @@ descriptionsmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- | The descriptions command. | -- | The descriptions command. | ||||||
| descriptions :: CliOpts -> Journal -> IO () | descriptions :: CliOpts -> Journal -> IO () | ||||||
| descriptions CliOpts{reportopts_=ropts} j = do | descriptions CliOpts{reportspec_=rspec} j = do | ||||||
|   let ts = entriesReport ropts j |   let ts = entriesReport rspec j | ||||||
|       descriptions = nubSort $ map tdescription ts |       descriptions = nubSort $ map tdescription ts | ||||||
| 
 | 
 | ||||||
|   mapM_ T.putStrLn descriptions |   mapM_ T.putStrLn descriptions | ||||||
|  | |||||||
| @ -102,7 +102,7 @@ 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_=Acct acctRe}} _ = do | diff CliOpts{file_=[f1, f2], reportspec_=ReportSpec{rsQuery=Acct acctRe}} _ = do | ||||||
|   j1 <- readJournalFile' f1 |   j1 <- readJournalFile' f1 | ||||||
|   j2 <- readJournalFile' f2 |   j2 <- readJournalFile' f2 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ notesmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- | The notes command. | -- | The notes command. | ||||||
| notes :: CliOpts -> Journal -> IO () | notes :: CliOpts -> Journal -> IO () | ||||||
| notes CliOpts{reportopts_=ropts} j = do | notes CliOpts{reportspec_=rspec} j = do | ||||||
|   let ts = entriesReport ropts j |   let ts = entriesReport rspec j | ||||||
|       notes = nubSort $ map transactionNote ts |       notes = nubSort $ map transactionNote ts | ||||||
|   mapM_ T.putStrLn notes |   mapM_ T.putStrLn notes | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ payeesmode = hledgerCommandMode | |||||||
| 
 | 
 | ||||||
| -- | The payees command. | -- | The payees command. | ||||||
| payees :: CliOpts -> Journal -> IO () | payees :: CliOpts -> Journal -> IO () | ||||||
| payees CliOpts{reportopts_=ropts} j = do | payees CliOpts{reportspec_=rspec} j = do | ||||||
|   let ts = entriesReport ropts j |   let ts = entriesReport rspec j | ||||||
|       payees = nubSort $ map transactionPayee ts |       payees = nubSort $ map transactionPayee ts | ||||||
|   mapM_ T.putStrLn payees |   mapM_ T.putStrLn payees | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ pricesmode = hledgerCommandMode | |||||||
| prices opts j = do | prices opts j = do | ||||||
|   let |   let | ||||||
|     styles     = journalCommodityStyles j |     styles     = journalCommodityStyles j | ||||||
|     q          = query_ $ reportopts_ opts |     q          = rsQuery $ reportspec_ 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 | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ print' opts j = do | |||||||
|     Just desc -> printMatch opts j $ T.pack desc |     Just desc -> printMatch opts j $ T.pack desc | ||||||
| 
 | 
 | ||||||
| printEntries :: CliOpts -> Journal -> IO () | printEntries :: CliOpts -> Journal -> IO () | ||||||
| printEntries opts@CliOpts{reportopts_=ropts} j = do | printEntries opts@CliOpts{reportspec_=rspec} j = do | ||||||
|   let fmt = outputFormatFromOpts opts |   let fmt = outputFormatFromOpts opts | ||||||
|       render = case fmt of |       render = case fmt of | ||||||
|         "txt"  -> entriesReportAsText opts |         "txt"  -> entriesReportAsText opts | ||||||
| @ -61,7 +61,7 @@ printEntries opts@CliOpts{reportopts_=ropts} j = do | |||||||
|         "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 j |   writeOutput opts $ render $ entriesReport rspec j | ||||||
| 
 | 
 | ||||||
| entriesReportAsText :: CliOpts -> EntriesReport -> String | entriesReportAsText :: CliOpts -> EntriesReport -> String | ||||||
| entriesReportAsText opts = concatMap (showTransaction . whichtxn) | entriesReportAsText opts = concatMap (showTransaction . whichtxn) | ||||||
| @ -73,7 +73,7 @@ entriesReportAsText opts = concatMap (showTransaction . whichtxn) | |||||||
|         -- Because of #551, and because of print -V valuing only one |         -- Because of #551, and because of print -V valuing only one | ||||||
|         -- posting when there's an implicit txn price. |         -- posting when there's an implicit txn price. | ||||||
|         -- So -B/-V/-X/--value implies -x. Is this ok ? |         -- So -B/-V/-X/--value implies -x. Is this ok ? | ||||||
|         || (isJust $ value_ $ reportopts_ opts) = id |         || (isJust . value_ . rsOpts $ reportspec_ opts) = id | ||||||
|       -- By default, use the original as-written-in-the-journal txn. |       -- By default, use the original as-written-in-the-journal txn. | ||||||
|       | otherwise = originalTransaction |       | otherwise = originalTransaction | ||||||
| 
 | 
 | ||||||
| @ -182,8 +182,8 @@ postingToCSV p = | |||||||
| -- | Print the transaction most closely and recently matching a description | -- | Print the transaction most closely and recently matching a description | ||||||
| -- (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{reportspec_=rspec} j desc = do | ||||||
|   case similarTransaction' j (query_ ropts) desc of |   case similarTransaction' j (rsQuery rspec) desc of | ||||||
|       Nothing -> putStrLn "no matches found." |       Nothing -> putStrLn "no matches found." | ||||||
|       Just t  -> putStr $ showTransaction t |       Just t  -> putStr $ showTransaction t | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -58,13 +58,13 @@ 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{reportspec_=rspec} j = do | ||||||
|   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 j |   writeOutput opts . render opts $ postingsReport rspec j | ||||||
| 
 | 
 | ||||||
| postingsReportAsCsv :: PostingsReport -> CSV | postingsReportAsCsv :: PostingsReport -> CSV | ||||||
| postingsReportAsCsv (_,is) = | postingsReportAsCsv (_,is) = | ||||||
| @ -178,7 +178,7 @@ postingsReportItemAsText opts preferredamtwidth preferredbalwidth (mdate, mendda | |||||||
|               BalancedVirtualPosting -> (\s -> "["++s++"]", acctwidth-2) |               BalancedVirtualPosting -> (\s -> "["++s++"]", acctwidth-2) | ||||||
|               VirtualPosting         -> (\s -> "("++s++")", acctwidth-2) |               VirtualPosting         -> (\s -> "("++s++")", acctwidth-2) | ||||||
|               _                      -> (id,acctwidth) |               _                      -> (id,acctwidth) | ||||||
|       showamt = showMixedAmountWithoutPrice (color_ $ reportopts_ opts) |       showamt = showMixedAmountWithoutPrice (color_ . rsOpts $ reportspec_ opts) | ||||||
|       amt = showamt $ pamount p |       amt = showamt $ pamount p | ||||||
|       bal = showamt b |       bal = showamt b | ||||||
|       -- alternate behaviour, show null amounts as 0 instead of blank |       -- alternate behaviour, show null amounts as 0 instead of blank | ||||||
| @ -198,8 +198,8 @@ tests_Register = tests "Register" [ | |||||||
|    tests "postingsReportAsText" [ |    tests "postingsReportAsText" [ | ||||||
|     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 rspec = defreportspec | ||||||
|       (postingsReportAsText defcliopts $ postingsReport opts j) |       (postingsReportAsText defcliopts $ postingsReport rspec j) | ||||||
|         @?= |         @?= | ||||||
|         unlines |         unlines | ||||||
|         ["2009-01-01 медвежья шкура       расходы:покупки                100           100" |         ["2009-01-01 медвежья шкура       расходы:покупки                100           100" | ||||||
|  | |||||||
| @ -22,10 +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 = | registermatch opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} j = | ||||||
|   case listofstringopt "args" rawopts of |   case listofstringopt "args" rawopts of | ||||||
|     [desc] -> do |     [desc] -> do | ||||||
|         let (_,pris) = postingsReport ropts j |         let (_,pris) = postingsReport rspec 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." | ||||||
|  | |||||||
| @ -36,13 +36,13 @@ rewritemode = hledgerCommandMode | |||||||
| -- TODO interpolating match groups in replacement | -- TODO interpolating match groups in replacement | ||||||
| -- TODO allow using this on unbalanced entries, eg to rewrite while editing | -- TODO allow using this on unbalanced entries, eg to rewrite while editing | ||||||
| 
 | 
 | ||||||
| rewrite opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j@Journal{jtxns=ts} = do | rewrite opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} j@Journal{jtxns=ts} = do | ||||||
|   -- rewrite matched transactions |   -- rewrite matched transactions | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   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_=Any}} j j' |   printOrDiff rawopts opts{reportspec_=rspec{rsQuery=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. | ||||||
|  | |||||||
| @ -52,9 +52,10 @@ data OneSpan = OneSpan | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| roi ::  CliOpts -> Journal -> IO () | roi ::  CliOpts -> Journal -> IO () | ||||||
| roi CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do | roi CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let |   let | ||||||
|  |     ropts = rsOpts rspec | ||||||
|     showCashFlow = boolopt "cashflow" rawopts |     showCashFlow = boolopt "cashflow" rawopts | ||||||
|     prettyTables = pretty_tables_ ropts |     prettyTables = pretty_tables_ ropts | ||||||
|     makeQuery flag = do |     makeQuery flag = do | ||||||
|  | |||||||
| @ -42,11 +42,12 @@ 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{query_=q, interval_=interval}} j = do | stats opts@CliOpts{reportspec_=rspec} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let l = ledgerFromJournal q j |   let q = rsQuery rspec | ||||||
|  |       l = ledgerFromJournal q j | ||||||
|       reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q) |       reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q) | ||||||
|       intervalspans = splitSpan interval reportspan |       intervalspans = splitSpan (interval_ $ rsOpts rspec) 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 | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ tagsmode = hledgerCommandMode | |||||||
|   ([], Just $ argsFlag "[TAGREGEX [QUERY...]]") |   ([], Just $ argsFlag "[TAGREGEX [QUERY...]]") | ||||||
| 
 | 
 | ||||||
| tags :: CliOpts -> Journal -> IO () | tags :: CliOpts -> Journal -> IO () | ||||||
| tags CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | tags CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let args = listofstringopt "args" rawopts |   let args = listofstringopt "args" rawopts | ||||||
|   mtagpat <- mapM (either Fail.fail pure . toRegexCI) $ headMay args |   mtagpat <- mapM (either Fail.fail pure . toRegexCI) $ headMay args | ||||||
| @ -34,12 +34,12 @@ tags CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do | |||||||
|     querystring = T.pack . unwords . map quoteIfNeeded $ 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_ $ rsOpts rspec | ||||||
| 
 | 
 | ||||||
|   argsquery <- either usageError (return . fst) $ parseQuery d querystring |   argsquery <- either usageError (return . fst) $ parseQuery d querystring | ||||||
|   let |   let | ||||||
|     q = simplifyQuery $ And [queryFromFlags ropts, argsquery] |     q = simplifyQuery $ And [queryFromFlags $ rsOpts rspec, argsquery] | ||||||
|     txns = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts ropts j |     txns = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts (rsOpts rspec) j | ||||||
|     tagsorvalues = |     tagsorvalues = | ||||||
|       (if parsed then id else nubSort) |       (if parsed then id else nubSort) | ||||||
|       [ r |       [ r | ||||||
|  | |||||||
| @ -88,8 +88,9 @@ 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{reportspec_=rspec, rawopts_=rawopts} j = do | ||||||
|     let |     let | ||||||
|  |       ropts@ReportOpts{..} = rsOpts rspec | ||||||
|       -- 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 = | ||||||
|         choiceopt parse rawopts where |         choiceopt parse rawopts where | ||||||
| @ -120,7 +121,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_ query_ |               requestedspan = queryDateSpan date2_ (rsQuery rspec) | ||||||
|                                   `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 | ||||||
| @ -142,7 +143,7 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=r | |||||||
|             where multiperiod = interval_ /= NoInterval |             where multiperiod = interval_ /= NoInterval | ||||||
| 
 | 
 | ||||||
|       -- make a CompoundBalanceReport. |       -- make a CompoundBalanceReport. | ||||||
|       cbr' = compoundBalanceReport ropts' j cbcqueries |       cbr' = compoundBalanceReport rspec{rsOpts=ropts'} j cbcqueries | ||||||
|       cbr  = cbr'{cbrTitle=title} |       cbr  = cbr'{cbrTitle=title} | ||||||
| 
 | 
 | ||||||
|     -- render appropriately |     -- render appropriately | ||||||
|  | |||||||
| @ -151,9 +151,9 @@ main = do | |||||||
|   dbgIO "isInternalCommand" isInternalCommand |   dbgIO "isInternalCommand" isInternalCommand | ||||||
|   dbgIO "isExternalCommand" isExternalCommand |   dbgIO "isExternalCommand" isExternalCommand | ||||||
|   dbgIO "isBadCommand" isBadCommand |   dbgIO "isBadCommand" isBadCommand | ||||||
|   dbgIO "period from opts" (period_ $ reportopts_ opts) |   dbgIO "period from opts" (period_ . rsOpts $ reportspec_ opts) | ||||||
|   dbgIO "interval from opts" (interval_ $ reportopts_ opts) |   dbgIO "interval from opts" (interval_ . rsOpts $ reportspec_ opts) | ||||||
|   dbgIO "query from opts & args" (query_ $ reportopts_ opts) |   dbgIO "query from opts & args" (rsQuery $ reportspec_ 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 | ||||||
|  | |||||||
| @ -30,7 +30,6 @@ module Hledger.Cli.Utils | |||||||
|     ) |     ) | ||||||
| where | where | ||||||
| import Control.Exception as C | import Control.Exception as C | ||||||
| import Control.Monad |  | ||||||
| 
 | 
 | ||||||
| import Data.List | import Data.List | ||||||
| import Data.Maybe | import Data.Maybe | ||||||
| @ -71,9 +70,9 @@ withJournalDo opts cmd = do | |||||||
|   -- it's stdin, or it doesn't exist and we are adding. We read it strictly |   -- it's stdin, or it doesn't exist and we are adding. We read it strictly | ||||||
|   -- to let the add command work. |   -- to let the add command work. | ||||||
|   journalpaths <- journalFilePathFromOpts opts |   journalpaths <- journalFilePathFromOpts opts | ||||||
|   readJournalFiles (inputopts_ opts) journalpaths |   files <- readJournalFiles (inputopts_ opts) journalpaths | ||||||
|   >>= mapM (journalTransform opts) |   let transformed = journalTransform opts <$> files | ||||||
|   >>= either error' cmd  -- PARTIAL: |   either error' cmd transformed  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
| -- | Apply some extra post-parse transformations to the journal, if | -- | Apply some extra post-parse transformations to the journal, if | ||||||
| -- specified by options. These happen after journal validation, but | -- specified by options. These happen after journal validation, but | ||||||
| @ -83,13 +82,13 @@ withJournalDo opts cmd = do | |||||||
| -- - pivoting account names (--pivot) | -- - pivoting account names (--pivot) | ||||||
| -- - anonymising (--anonymise). | -- - anonymising (--anonymise). | ||||||
| -- | -- | ||||||
| journalTransform :: CliOpts -> Journal -> IO Journal | journalTransform :: CliOpts -> Journal -> Journal | ||||||
| journalTransform opts@CliOpts{reportopts_=_ropts} = | journalTransform opts = | ||||||
|       journalAddForecast opts |     anonymiseByOpts opts | ||||||
|   -- - converting amounts to market value (--value) |   -- - converting amounts to market value (--value) | ||||||
|   -- >=> journalApplyValue ropts |   -- . journalApplyValue ropts | ||||||
|   >=> return . pivotByOpts opts |   . pivotByOpts opts | ||||||
|   >=> return . anonymiseByOpts opts |   . journalAddForecast opts | ||||||
| 
 | 
 | ||||||
| -- | Apply the pivot transformation on a journal, if option is present. | -- | Apply the pivot transformation on a journal, if option is present. | ||||||
| pivotByOpts :: CliOpts -> Journal -> Journal | pivotByOpts :: CliOpts -> Journal -> Journal | ||||||
| @ -115,20 +114,22 @@ anonymiseByOpts opts = | |||||||
| -- The start & end date for generated periodic transactions are determined in | -- The start & end date for generated periodic transactions are determined in | ||||||
| -- a somewhat complicated way; see the hledger manual -> Periodic transactions. | -- a somewhat complicated way; see the hledger manual -> Periodic transactions. | ||||||
| -- | -- | ||||||
| journalAddForecast :: CliOpts -> Journal -> IO Journal | journalAddForecast :: CliOpts -> Journal -> Journal | ||||||
| journalAddForecast CliOpts{inputopts_=iopts, reportopts_=ropts} j = | journalAddForecast CliOpts{inputopts_=iopts, reportspec_=rspec} j = | ||||||
|     case forecast_ ropts of |     case forecast_ ropts of | ||||||
|     Nothing -> return j |         Nothing -> j | ||||||
|     Just _ -> do |         Just _  -> either (error') id . journalApplyCommodityStyles $  -- PARTIAL: | ||||||
|       today <- getCurrentDay |                      journalBalanceTransactions' iopts j{ jtxns = concat [jtxns j, forecasttxns'] } | ||||||
|  |   where | ||||||
|  |     today = rsToday rspec | ||||||
|  |     ropts = rsOpts rspec | ||||||
| 
 | 
 | ||||||
|     -- "They can start no earlier than: the day following the latest normal transaction in the journal (or today if there are none)." |     -- "They can start no earlier than: the day following the latest normal transaction in the journal (or today if there are none)." | ||||||
|       let |  | ||||||
|     mjournalend   = dbg2 "journalEndDate" $ journalEndDate False j  -- ignore secondary dates |     mjournalend   = dbg2 "journalEndDate" $ journalEndDate False j  -- ignore secondary dates | ||||||
|     forecastbeginDefault = dbg2 "forecastbeginDefault" $ fromMaybe today mjournalend |     forecastbeginDefault = dbg2 "forecastbeginDefault" $ fromMaybe today mjournalend | ||||||
| 
 | 
 | ||||||
|     -- "They end on or before the specified report end date, or 180 days from today if unspecified." |     -- "They end on or before the specified report end date, or 180 days from today if unspecified." | ||||||
|         mspecifiedend = dbg2 "specifieddates" $ reportPeriodLastDay ropts |     mspecifiedend = dbg2 "specifieddates" $ reportPeriodLastDay rspec | ||||||
|     forecastendDefault = dbg2 "forecastendDefault" $ fromMaybe (addDays 180 today) mspecifiedend |     forecastendDefault = dbg2 "forecastendDefault" $ fromMaybe (addDays 180 today) mspecifiedend | ||||||
| 
 | 
 | ||||||
|     forecastspan = dbg2 "forecastspan" $ |     forecastspan = dbg2 "forecastspan" $ | ||||||
| @ -146,14 +147,10 @@ journalAddForecast CliOpts{inputopts_=iopts, reportopts_=ropts} j = | |||||||
|       (if auto_ iopts then either error' id . modifyTransactions today (jtxnmodifiers j) else id)  -- PARTIAL: |       (if auto_ iopts then either error' id . modifyTransactions today (jtxnmodifiers j) else id)  -- PARTIAL: | ||||||
|       forecasttxns |       forecasttxns | ||||||
| 
 | 
 | ||||||
|         j' = either error' id $ journalBalanceTransactions (not . ignore_assertions_ $ iopts)  -- PARTIAL: |     journalBalanceTransactions' iopts j = | ||||||
|           j{jtxns=concat [jtxns j, forecasttxns']} |       let assrt = not . ignore_assertions_ $ iopts | ||||||
| 
 |       in | ||||||
|         -- Display styles were applied early.. apply them again to ensure the forecasted |        either error' id $ journalBalanceTransactions assrt j  -- PARTIAL: | ||||||
|         -- transactions are also styled. XXX Possible optimisation: style just the forecasttxns. |  | ||||||
|         j'' = either error' id $ journalApplyCommodityStyles j'  -- PARTIAL: |  | ||||||
| 
 |  | ||||||
|       return j'' |  | ||||||
| 
 | 
 | ||||||
| -- | Write some output to stdout or to a file selected by --output-file. | -- | Write some output to stdout or to a file selected by --output-file. | ||||||
| -- If the file exists it will be overwritten. | -- If the file exists it will be overwritten. | ||||||
| @ -172,8 +169,8 @@ writeOutput opts s = do | |||||||
| journalReload :: CliOpts -> IO (Either String Journal) | journalReload :: CliOpts -> IO (Either String Journal) | ||||||
| journalReload opts = do | journalReload opts = do | ||||||
|   journalpaths <- journalFilePathFromOpts opts |   journalpaths <- journalFilePathFromOpts opts | ||||||
|   readJournalFiles (inputopts_ opts) journalpaths |   files <- readJournalFiles (inputopts_ opts) journalpaths | ||||||
|   >>= mapM (journalTransform opts) |   return $ journalTransform opts <$> files | ||||||
| 
 | 
 | ||||||
| -- | Re-read the option-specified journal file(s), but only if any of | -- | Re-read the option-specified journal file(s), but only if any of | ||||||
| -- them has changed since last read. (If the file is standard input, | -- them has changed since last read. (If the file is standard input, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user