Adding "--value"/"-V" option for balancesheet/incomestatement/cashflow and register (#361)
* Added --value options for specialized balance ouputs balancesheet, incomestatement, cashflow * --value option for 'register' command lib: postingsValue to convert posting amounts to market value
This commit is contained in:
		
							parent
							
								
									419f5f2a2a
								
							
						
					
					
						commit
						b935cd2243
					
				| @ -9,6 +9,7 @@ module Hledger.Reports.PostingsReport ( | |||||||
|   PostingsReport, |   PostingsReport, | ||||||
|   PostingsReportItem, |   PostingsReportItem, | ||||||
|   postingsReport, |   postingsReport, | ||||||
|  |   postingsReportValue, | ||||||
|   mkpostingsReportItem, |   mkpostingsReportItem, | ||||||
| 
 | 
 | ||||||
|   -- * Tests |   -- * Tests | ||||||
| @ -28,6 +29,7 @@ import Test.HUnit | |||||||
| import Hledger.Data | import Hledger.Data | ||||||
| import Hledger.Query | import Hledger.Query | ||||||
| import Hledger.Utils | import Hledger.Utils | ||||||
|  | import Hledger.Reports.BalanceReport | ||||||
| import Hledger.Reports.ReportOptions | import Hledger.Reports.ReportOptions | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -87,6 +89,31 @@ postingsReport opts q j = (totallabel, items) | |||||||
| 
 | 
 | ||||||
| totallabel = "Total" | totallabel = "Total" | ||||||
| 
 | 
 | ||||||
|  | -- | Convert all the amounts in a postings report to | ||||||
|  | -- their value on the given date in their default valuation | ||||||
|  | -- commodities. | ||||||
|  | postingsReportValue :: Journal -> Day -> PostingsReport -> PostingsReport | ||||||
|  | postingsReportValue j d r = r' | ||||||
|  |   where | ||||||
|  |     (label,items) = r | ||||||
|  |     r' = dbg8 "postingsReportValue" $ | ||||||
|  |          (label,[(pb,pe,pd,postingValue j d pp,mixedAmountValue j d a) | (pb,pe,pd,pp,a) <- items]) | ||||||
|  | 
 | ||||||
|  | postingValue :: Journal -> Day -> Posting -> Posting | ||||||
|  | postingValue j d p = p' | ||||||
|  |   where | ||||||
|  |     Posting {pamount = pa, pbalanceassertion = pb, ptransaction = pt} = p | ||||||
|  |     p' = p{pamount = mixedAmountValue j d pa | ||||||
|  |           ,pbalanceassertion = mixedAmountValue j d <$> pb | ||||||
|  |           ,ptransaction = transactionValue j d <$> pt | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  | transactionValue :: Journal -> Day -> Transaction -> Transaction | ||||||
|  | transactionValue j d t = t' | ||||||
|  |   where | ||||||
|  |     Transaction {tpostings = tp} = t | ||||||
|  |     t' = t{tpostings = map (postingValue j d) tp} | ||||||
|  | 
 | ||||||
| -- | Adjust report start/end dates to more useful ones based on | -- | Adjust report start/end dates to more useful ones based on | ||||||
| -- journal data and report intervals. Ie: | -- journal data and report intervals. Ie: | ||||||
| -- 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) | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ module Hledger.Cli.Balancesheet ( | |||||||
|  ,tests_Hledger_Cli_Balancesheet |  ,tests_Hledger_Cli_Balancesheet | ||||||
| ) where | ) where | ||||||
| 
 | 
 | ||||||
|  | import Data.Maybe (fromMaybe) | ||||||
| import qualified Data.Text.Lazy.IO as LT | import qualified Data.Text.Lazy.IO as LT | ||||||
| import System.Console.CmdArgs.Explicit | import System.Console.CmdArgs.Explicit | ||||||
| import Test.HUnit | import Test.HUnit | ||||||
| @ -28,6 +29,7 @@ balancesheetmode = (defCommandMode $ ["balancesheet"]++aliases) { | |||||||
|      groupUnnamed = [ |      groupUnnamed = [ | ||||||
|       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" |       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" | ||||||
|      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" |      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" | ||||||
|  |      ,flagNone ["value","V"] (setboolopt "value") "show amounts as their current market value in their default valuation commodity" | ||||||
|      ] |      ] | ||||||
|     ,groupHidden = [] |     ,groupHidden = [] | ||||||
|     ,groupNamed = [generalflagsgroup1] |     ,groupNamed = [generalflagsgroup1] | ||||||
| @ -41,18 +43,23 @@ balancesheet CliOpts{reportopts_=ropts} j = do | |||||||
|   -- let lines = case lineFormatFromOpts ropts of Left err, Right ... |   -- let lines = case lineFormatFromOpts ropts of Left err, Right ... | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let q = queryFromOpts d (withoutBeginDate ropts) |   let q = queryFromOpts d (withoutBeginDate ropts) | ||||||
|  |       valuedate = fromMaybe d $ queryEndDate False $ queryFromOpts d ropts | ||||||
|       assetreport@(_,assets)          = balanceReport ropts (And [q, journalAssetAccountQuery j]) j |       assetreport@(_,assets)          = balanceReport ropts (And [q, journalAssetAccountQuery j]) j | ||||||
|       liabilityreport@(_,liabilities) = balanceReport ropts (And [q, journalLiabilityAccountQuery j]) j |       liabilityreport@(_,liabilities) = balanceReport ropts (And [q, journalLiabilityAccountQuery j]) j | ||||||
|       total = assets + liabilities |       total = assets + liabilities | ||||||
|  |       convertReport | value_ ropts = balanceReportValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|  |       convertTotal  | value_ ropts = mixedAmountValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|   LT.putStr $ [lt|Balance Sheet |   LT.putStr $ [lt|Balance Sheet | ||||||
| 
 | 
 | ||||||
| Assets: | Assets: | ||||||
| #{balanceReportAsText ropts assetreport} | #{balanceReportAsText ropts (convertReport assetreport)} | ||||||
| Liabilities: | Liabilities: | ||||||
| #{balanceReportAsText ropts liabilityreport} | #{balanceReportAsText ropts (convertReport liabilityreport)} | ||||||
| Total: | Total: | ||||||
| -------------------- | -------------------- | ||||||
| #{padleft 20 $ showMixedAmountWithoutPrice total} | #{padleft 20 $ showMixedAmountWithoutPrice (convertTotal total)} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| withoutBeginDate :: ReportOpts -> ReportOpts | withoutBeginDate :: ReportOpts -> ReportOpts | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ module Hledger.Cli.Cashflow ( | |||||||
|  ,tests_Hledger_Cli_Cashflow |  ,tests_Hledger_Cli_Cashflow | ||||||
| ) where | ) where | ||||||
| 
 | 
 | ||||||
|  | import Data.Maybe (fromMaybe) | ||||||
| import qualified Data.Text.Lazy.IO as LT | import qualified Data.Text.Lazy.IO as LT | ||||||
| import System.Console.CmdArgs.Explicit | import System.Console.CmdArgs.Explicit | ||||||
| import Test.HUnit | import Test.HUnit | ||||||
| @ -31,6 +32,7 @@ cashflowmode = (defCommandMode ["cashflow","cf"]) { | |||||||
|      groupUnnamed = [ |      groupUnnamed = [ | ||||||
|       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" |       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" | ||||||
|      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" |      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" | ||||||
|  |      ,flagNone ["value","V"] (setboolopt "value") "show amounts as their current market value in their default valuation commodity" | ||||||
|      ] |      ] | ||||||
|     ,groupHidden = [] |     ,groupHidden = [] | ||||||
|     ,groupNamed = [generalflagsgroup1] |     ,groupNamed = [generalflagsgroup1] | ||||||
| @ -43,18 +45,23 @@ cashflow CliOpts{reportopts_=ropts} j = do | |||||||
|   -- let lines = case lineFormatFromOpts ropts of Left err, Right ... |   -- let lines = case lineFormatFromOpts ropts of Left err, Right ... | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let q = queryFromOpts d ropts |   let q = queryFromOpts d ropts | ||||||
|  |       valuedate = fromMaybe d $ queryEndDate False $ queryFromOpts d ropts | ||||||
|       cashreport@(_,total) = balanceReport ropts (And [q, journalCashAccountQuery j]) j |       cashreport@(_,total) = balanceReport ropts (And [q, journalCashAccountQuery j]) j | ||||||
|       -- operatingreport@(_,operating) = balanceReport ropts (And [q, journalOperatingAccountMatcher j]) j |       -- operatingreport@(_,operating) = balanceReport ropts (And [q, journalOperatingAccountMatcher j]) j | ||||||
|       -- investingreport@(_,investing) = balanceReport ropts (And [q, journalInvestingAccountMatcher j]) j |       -- investingreport@(_,investing) = balanceReport ropts (And [q, journalInvestingAccountMatcher j]) j | ||||||
|       -- financingreport@(_,financing) = balanceReport ropts (And [q, journalFinancingAccountMatcher j]) j |       -- financingreport@(_,financing) = balanceReport ropts (And [q, journalFinancingAccountMatcher j]) j | ||||||
|       -- total = operating + investing + financing |       -- total = operating + investing + financing | ||||||
|  |       convertReport | value_ ropts = balanceReportValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|  |       convertTotal  | value_ ropts = mixedAmountValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|   LT.putStr $ [lt|Cashflow Statement |   LT.putStr $ [lt|Cashflow Statement | ||||||
| 
 | 
 | ||||||
| Cash flows: | Cash flows: | ||||||
| #{balanceReportAsText ropts cashreport} | #{balanceReportAsText ropts (convertReport cashreport)} | ||||||
| Total: | Total: | ||||||
| -------------------- | -------------------- | ||||||
| #{padleft 20 $ showMixedAmountWithoutPrice total} | #{padleft 20 $ showMixedAmountWithoutPrice (convertTotal total)} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| tests_Hledger_Cli_Cashflow :: Test | tests_Hledger_Cli_Cashflow :: Test | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ module Hledger.Cli.Incomestatement ( | |||||||
|  ,tests_Hledger_Cli_Incomestatement |  ,tests_Hledger_Cli_Incomestatement | ||||||
| ) where | ) where | ||||||
| 
 | 
 | ||||||
|  | import Data.Maybe (fromMaybe) | ||||||
| import qualified Data.Text.Lazy.IO as LT | import qualified Data.Text.Lazy.IO as LT | ||||||
| import System.Console.CmdArgs.Explicit | import System.Console.CmdArgs.Explicit | ||||||
| import Test.HUnit | import Test.HUnit | ||||||
| @ -28,6 +29,7 @@ incomestatementmode = (defCommandMode $ ["incomestatement"]++aliases) { | |||||||
|      groupUnnamed = [ |      groupUnnamed = [ | ||||||
|       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" |       flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list" | ||||||
|      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" |      ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts" | ||||||
|  |      ,flagNone ["value","V"] (setboolopt "value") "show amounts as their current market value in their default valuation commodity" | ||||||
|      ] |      ] | ||||||
|     ,groupHidden = [] |     ,groupHidden = [] | ||||||
|     ,groupNamed = [generalflagsgroup1] |     ,groupNamed = [generalflagsgroup1] | ||||||
| @ -40,18 +42,23 @@ incomestatement :: CliOpts -> Journal -> IO () | |||||||
| incomestatement CliOpts{reportopts_=ropts} j = do | incomestatement CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let q = queryFromOpts d ropts |   let q = queryFromOpts d ropts | ||||||
|  |       valuedate = fromMaybe d $ queryEndDate False $ queryFromOpts d ropts | ||||||
|       incomereport@(_,income)    = balanceReport ropts (And [q, journalIncomeAccountQuery j]) j |       incomereport@(_,income)    = balanceReport ropts (And [q, journalIncomeAccountQuery j]) j | ||||||
|       expensereport@(_,expenses) = balanceReport ropts (And [q, journalExpenseAccountQuery j]) j |       expensereport@(_,expenses) = balanceReport ropts (And [q, journalExpenseAccountQuery j]) j | ||||||
|       total = income + expenses |       total = income + expenses | ||||||
|  |       convertReport | value_ ropts = balanceReportValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|  |       convertTotal  | value_ ropts = mixedAmountValue j valuedate | ||||||
|  |                     | otherwise    = id | ||||||
|   LT.putStr $ [lt|Income Statement |   LT.putStr $ [lt|Income Statement | ||||||
| 
 | 
 | ||||||
| Revenues: | Revenues: | ||||||
| #{balanceReportAsText ropts incomereport} | #{balanceReportAsText ropts (convertReport incomereport)} | ||||||
| Expenses: | Expenses: | ||||||
| #{balanceReportAsText ropts expensereport} | #{balanceReportAsText ropts (convertReport expensereport)} | ||||||
| Total: | Total: | ||||||
| -------------------- | -------------------- | ||||||
| #{padleft 20 $ showMixedAmountWithoutPrice total} | #{padleft 20 $ showMixedAmountWithoutPrice (convertTotal total)} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| tests_Hledger_Cli_Incomestatement :: Test | tests_Hledger_Cli_Incomestatement :: Test | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ registermode = (defCommandMode $ ["register"] ++ aliases) { | |||||||
|      ,flagNone ["average","A"] (\opts -> setboolopt "average" opts) |      ,flagNone ["average","A"] (\opts -> setboolopt "average" opts) | ||||||
|         "show running average of posting amounts instead of total (implies --empty)" |         "show running average of posting amounts instead of total (implies --empty)" | ||||||
|      ,flagNone ["related","r"] (\opts -> setboolopt "related" opts) "show postings' siblings instead" |      ,flagNone ["related","r"] (\opts -> setboolopt "related" opts) "show postings' siblings instead" | ||||||
|  |      ,flagNone ["value","V"] (setboolopt "value") "show amounts as their current market value in their default valuation commodity" | ||||||
|      ,flagReq  ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N" |      ,flagReq  ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N" | ||||||
|       ("set output width (default: " ++ |       ("set output width (default: " ++ | ||||||
| #ifdef mingw32_HOST_OS | #ifdef mingw32_HOST_OS | ||||||
| @ -61,9 +62,12 @@ register :: CliOpts -> Journal -> IO () | |||||||
| register opts@CliOpts{reportopts_=ropts} j = do | register opts@CliOpts{reportopts_=ropts} j = do | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   let fmt = outputFormatFromOpts opts |   let fmt = outputFormatFromOpts opts | ||||||
|       render | fmt=="csv" = const ((++"\n") . printCSV . postingsReportAsCsv) |       valuedate = fromMaybe d $ queryEndDate False $ queryFromOpts d ropts | ||||||
|              | otherwise  = postingsReportAsText |       convert | value_ ropts = postingsReportValue j valuedate | ||||||
|   writeOutput opts $ render opts $ postingsReport ropts (queryFromOpts d ropts) j |               | otherwise    = id | ||||||
|  |       render  | fmt=="csv" = const ((++"\n") . printCSV . postingsReportAsCsv) | ||||||
|  |               | otherwise  = postingsReportAsText | ||||||
|  |   writeOutput opts $ render opts $ convert $ postingsReport ropts (queryFromOpts d ropts) j | ||||||
| 
 | 
 | ||||||
| postingsReportAsCsv :: PostingsReport -> CSV | postingsReportAsCsv :: PostingsReport -> CSV | ||||||
| postingsReportAsCsv (_,is) = | postingsReportAsCsv (_,is) = | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user