imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings. --cost will override --infer-equity. This means there will no longer be unbalanced transactions, but will be offsetting conversion postings to balance things out. For example. 2000-01-01 a 1 AAA @@ 2 BBB b -2 BBB When converting to cost, this is treated the same as before. When used with --infer-equity, this is now treated as: 2000-01-01 a 1 AAA equity:conversion:AAA-BBB:AAA -1 AAA equity:conversion:AAA-BBB:BBB 2 BBB b -2 BBB There is a new account type, Conversion/V, which is a subtype of Equity/E. The first account declared with this type, if any, is used as the base account for inferred equity postings in conversion transactions, overriding the default "equity:conversion". API changes: Costing has been changed to ConversionOp with three options: NoConversionOp, ToCost, and InferEquity. The first correspond to the previous NoCost and Cost options, while the third corresponds to the --infer-equity flag. This converts transactions with costs (one or more transaction prices) to transactions with equity:conversion postings. It is in ConversionOp because converting to cost with -B/--cost and inferring conversion equity postings with --infer-equity are mutually exclusive. Correspondingly, the cost_ record of ReportOpts has been changed to conversionop_. This also removes show_costs_ option in ReportOpts, as its functionality has been replaced by the richer cost_ option.
This commit is contained in:
		
							parent
							
								
									bf063f719d
								
							
						
					
					
						commit
						8eedbbbe87
					
				| @ -80,6 +80,7 @@ module Hledger.Data.Amount ( | |||||||
|   amountUnstyled, |   amountUnstyled, | ||||||
|   showAmountB, |   showAmountB, | ||||||
|   showAmount, |   showAmount, | ||||||
|  |   showAmountPrice, | ||||||
|   cshowAmount, |   cshowAmount, | ||||||
|   showAmountWithZeroCommodity, |   showAmountWithZeroCommodity, | ||||||
|   showAmountDebug, |   showAmountDebug, | ||||||
|  | |||||||
| @ -80,6 +80,7 @@ module Hledger.Data.Journal ( | |||||||
|   journalLiabilityAccountQuery, |   journalLiabilityAccountQuery, | ||||||
|   journalEquityAccountQuery, |   journalEquityAccountQuery, | ||||||
|   journalCashAccountQuery, |   journalCashAccountQuery, | ||||||
|  |   journalConversionAccount, | ||||||
|   -- * Misc |   -- * Misc | ||||||
|   canonicalStyleFrom, |   canonicalStyleFrom, | ||||||
|   nulljournal, |   nulljournal, | ||||||
| @ -120,9 +121,10 @@ import Hledger.Utils | |||||||
| import Hledger.Data.Types | import Hledger.Data.Types | ||||||
| import Hledger.Data.AccountName | import Hledger.Data.AccountName | ||||||
| import Hledger.Data.Amount | import Hledger.Data.Amount | ||||||
|  | import Hledger.Data.Posting | ||||||
| import Hledger.Data.Transaction | import Hledger.Data.Transaction | ||||||
| import Hledger.Data.TransactionModifier | import Hledger.Data.TransactionModifier | ||||||
| import Hledger.Data.Posting | import Hledger.Data.Valuation | ||||||
| import Hledger.Query | import Hledger.Query | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -395,7 +397,8 @@ letterPairs _ = [] | |||||||
| -- queries for standard account types | -- queries for standard account types | ||||||
| 
 | 
 | ||||||
| -- | Get a query for accounts of the specified types in this journal.  | -- | Get a query for accounts of the specified types in this journal.  | ||||||
| -- Account types include Asset, Liability, Equity, Revenue, Expense, Cash. | -- Account types include: | ||||||
|  | -- Asset, Liability, Equity, Revenue, Expense, Cash, Conversion. | ||||||
| -- For each type, if no accounts were declared with this type, the query  | -- For each type, if no accounts were declared with this type, the query  | ||||||
| -- will instead match accounts with names matched by the case-insensitive  | -- will instead match accounts with names matched by the case-insensitive  | ||||||
| -- regular expression provided as a fallback. | -- regular expression provided as a fallback. | ||||||
| @ -506,6 +509,13 @@ journalProfitAndLossAccountQuery j = Or [journalRevenueAccountQuery j | |||||||
|                                         ,journalExpenseAccountQuery j |                                         ,journalExpenseAccountQuery j | ||||||
|                                         ] |                                         ] | ||||||
| 
 | 
 | ||||||
|  | -- | The 'AccountName' to use for automatically generated conversion postings. | ||||||
|  | journalConversionAccount :: Journal -> AccountName | ||||||
|  | journalConversionAccount = | ||||||
|  |     headDef (T.pack "equity:conversion") | ||||||
|  |     . M.findWithDefault [] Conversion | ||||||
|  |     . jdeclaredaccounttypes | ||||||
|  | 
 | ||||||
| -- Various kinds of filtering on journals. We do it differently depending | -- Various kinds of filtering on journals. We do it differently depending | ||||||
| -- on the command. | -- on the command. | ||||||
| 
 | 
 | ||||||
| @ -870,10 +880,11 @@ postingInferredmarketPrice p@Posting{pamount} = | |||||||
| 
 | 
 | ||||||
| -- | Convert all this journal's amounts to cost using the transaction prices, if any. | -- | Convert all this journal's amounts to cost using the transaction prices, if any. | ||||||
| -- The journal's commodity styles are applied to the resulting amounts. | -- The journal's commodity styles are applied to the resulting amounts. | ||||||
| journalToCost :: Journal -> Journal | journalToCost :: ConversionOp -> Journal -> Journal | ||||||
| journalToCost j@Journal{jtxns=ts} = j{jtxns=map (transactionToCost styles) ts} | journalToCost cost j@Journal{jtxns=ts} = | ||||||
|     where |     j{jtxns=map (transactionToCost (journalConversionAccount j) styles cost) ts} | ||||||
|       styles = journalCommodityStyles j |   where | ||||||
|  |     styles = journalCommodityStyles j | ||||||
| 
 | 
 | ||||||
| -- -- | Get this journal's unique, display-preference-canonicalised commodities, by symbol. | -- -- | Get this journal's unique, display-preference-canonicalised commodities, by symbol. | ||||||
| -- journalCanonicalCommodities :: Journal -> M.Map String CommoditySymbol | -- journalCanonicalCommodities :: Journal -> M.Map String CommoditySymbol | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ import Data.Foldable (asum) | |||||||
| import qualified Data.Map as M | import qualified Data.Map as M | ||||||
| import Data.Maybe (fromMaybe, isJust) | import Data.Maybe (fromMaybe, isJust) | ||||||
| import Data.MemoUgly (memo) | import Data.MemoUgly (memo) | ||||||
| import Data.List (foldl') | import Data.List (foldl', sort) | ||||||
| import qualified Data.Set as S | import qualified Data.Set as S | ||||||
| import Data.Text (Text) | import Data.Text (Text) | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| @ -480,9 +480,43 @@ postingApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day | |||||||
| postingApplyValuation priceoracle styles periodlast today v p = | postingApplyValuation priceoracle styles periodlast today v p = | ||||||
|     postingTransformAmount (mixedAmountApplyValuation priceoracle styles periodlast today (postingDate p) v) p |     postingTransformAmount (mixedAmountApplyValuation priceoracle styles periodlast today (postingDate p) v) p | ||||||
| 
 | 
 | ||||||
| -- | Convert this posting's amount to cost, and apply the appropriate amount styles. | -- | Maybe convert this 'Posting's amount to cost, and apply apply appropriate | ||||||
| postingToCost :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting | -- amount styles; or, in --infer-equity mode, remove its cost price and add an | ||||||
| postingToCost styles = postingTransformAmount (styleMixedAmount styles . mixedAmountCost) | -- appropriate pair of equity postings. | ||||||
|  | postingToCost :: Text -> M.Map CommoditySymbol AmountStyle -> ConversionOp -> Posting -> [Posting] | ||||||
|  | postingToCost _          _      NoConversionOp p = [p] | ||||||
|  | postingToCost _          styles ToCost         p = [postingTransformAmount (styleMixedAmount styles . mixedAmountCost) p] | ||||||
|  | postingToCost equityAcct styles InferEquity    p = taggedPosting : concatMap conversionPostings priceAmounts | ||||||
|  |   where | ||||||
|  |     taggedPosting | ||||||
|  |       | null priceAmounts = p | ||||||
|  |       | otherwise         = p{ pcomment = pcomment p `commentAddTag` priceTag | ||||||
|  |                              , ptags = priceTag : ptags p | ||||||
|  |                              } | ||||||
|  |     conversionPostings amt = case aprice amt of | ||||||
|  |         Nothing -> [] | ||||||
|  |         Just _  -> [ cp{ paccount = accountPrefix <> amtCommodity | ||||||
|  |                        , pamount = mixedAmount . negate $ amountStripPrices amt | ||||||
|  |                        } | ||||||
|  |                    , cp{ paccount = accountPrefix <> costCommodity | ||||||
|  |                        , pamount = styleMixedAmount styles $ mixedAmount cost | ||||||
|  |                        } | ||||||
|  |                    ] | ||||||
|  |       where | ||||||
|  |         cost = amountCost amt | ||||||
|  |         amtCommodity  = commodity amt | ||||||
|  |         costCommodity = commodity cost | ||||||
|  |         cp = p{ pcomment = pcomment p `commentAddTag` ("generated-posting","") | ||||||
|  |               , ptags = [("generated-posting", ""), ("_generated-posting", "")] | ||||||
|  |               , pbalanceassertion = Nothing | ||||||
|  |               , poriginal = Nothing | ||||||
|  |               } | ||||||
|  |         accountPrefix = mconcat [ equityAcct, ":", T.intercalate "-" $ sort [amtCommodity, costCommodity], ":"] | ||||||
|  |         -- Take the commodity of an amount and collapse consecutive spaces to a single space | ||||||
|  |         commodity = T.unwords . filter (not . T.null) . T.words . acommodity | ||||||
|  | 
 | ||||||
|  |     priceTag = ("cost", T.strip . wbToText $ foldMap showAmountPrice priceAmounts) | ||||||
|  |     priceAmounts = filter (isJust . aprice) . amountsRaw $ pamount p | ||||||
| 
 | 
 | ||||||
| -- | Apply a transform function to this posting's amount. | -- | Apply a transform function to this posting's amount. | ||||||
| postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting | postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting | ||||||
|  | |||||||
| @ -202,9 +202,11 @@ transactionApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> | |||||||
| transactionApplyValuation priceoracle styles periodlast today v = | transactionApplyValuation priceoracle styles periodlast today v = | ||||||
|   transactionTransformPostings (postingApplyValuation priceoracle styles periodlast today v) |   transactionTransformPostings (postingApplyValuation priceoracle styles periodlast today v) | ||||||
| 
 | 
 | ||||||
| -- | Convert this transaction's amounts to cost, and apply the appropriate amount styles. | -- | Maybe convert this 'Transaction's amounts to cost and apply the | ||||||
| transactionToCost :: M.Map CommoditySymbol AmountStyle -> Transaction -> Transaction | -- appropriate amount styles; or in --infer-equity mode, replace any | ||||||
| transactionToCost styles = transactionTransformPostings (postingToCost styles) | -- transaction prices by a pair of equity postings. | ||||||
|  | transactionToCost :: Text -> M.Map CommoditySymbol AmountStyle -> ConversionOp -> Transaction -> Transaction | ||||||
|  | transactionToCost equityAcct styles cost t = t{tpostings=concatMap (postingToCost equityAcct styles cost) $ tpostings t} | ||||||
| 
 | 
 | ||||||
| -- | Apply some account aliases to all posting account names in the transaction, as described by accountNameApplyAliases. | -- | Apply some account aliases to all posting account names in the transaction, as described by accountNameApplyAliases. | ||||||
| -- This can fail due to a bad replacement pattern in a regular expression alias. | -- This can fail due to a bad replacement pattern in a regular expression alias. | ||||||
|  | |||||||
| @ -151,6 +151,7 @@ data AccountType = | |||||||
|   | Revenue |   | Revenue | ||||||
|   | Expense |   | Expense | ||||||
|   | Cash  -- ^ a subtype of Asset - liquid assets to show in cashflow report |   | Cash  -- ^ a subtype of Asset - liquid assets to show in cashflow report | ||||||
|  |   | Conversion -- ^ a subtype of Equity - account in which to generate conversion postings for transaction prices | ||||||
|   deriving (Show,Eq,Ord,Generic) |   deriving (Show,Eq,Ord,Generic) | ||||||
| 
 | 
 | ||||||
| -- not worth the trouble, letters defined in accountdirectivep for now | -- not worth the trouble, letters defined in accountdirectivep for now | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ looking up historical market prices (exchange rates) between commodities. | |||||||
| {-# LANGUAGE DeriveGeneric #-} | {-# LANGUAGE DeriveGeneric #-} | ||||||
| 
 | 
 | ||||||
| module Hledger.Data.Valuation ( | module Hledger.Data.Valuation ( | ||||||
|    Costing(..) |    ConversionOp(..) | ||||||
|   ,ValuationType(..) |   ,ValuationType(..) | ||||||
|   ,PriceOracle |   ,PriceOracle | ||||||
|   ,journalPriceOracle |   ,journalPriceOracle | ||||||
| @ -51,8 +51,8 @@ import Text.Printf (printf) | |||||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ||||||
| -- Types | -- Types | ||||||
| 
 | 
 | ||||||
| -- | Whether to convert amounts to cost. | -- | Which operation to perform on conversion transactions. | ||||||
| data Costing = Cost | NoCost | data ConversionOp = NoConversionOp | InferEquity | ToCost | ||||||
|   deriving (Show,Eq) |   deriving (Show,Eq) | ||||||
| 
 | 
 | ||||||
| -- | What kind of value conversion should be done on amounts ? | -- | What kind of value conversion should be done on amounts ? | ||||||
| @ -98,8 +98,8 @@ priceDirectiveToMarketPrice PriceDirective{..} = | |||||||
| -- Converting things to value | -- Converting things to value | ||||||
| 
 | 
 | ||||||
| -- | Convert all component amounts to cost/selling price if requested, and style them. | -- | Convert all component amounts to cost/selling price if requested, and style them. | ||||||
| mixedAmountToCost :: Costing -> M.Map CommoditySymbol AmountStyle -> MixedAmount -> MixedAmount | mixedAmountToCost :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> MixedAmount -> MixedAmount | ||||||
| mixedAmountToCost cost styles = mapMixedAmount (amountToCost cost styles) | mixedAmountToCost styles cost = mapMixedAmount (amountToCost styles cost) | ||||||
| 
 | 
 | ||||||
| -- | Apply a specified valuation to this mixed amount, using the | -- | Apply a specified valuation to this mixed amount, using the | ||||||
| -- provided price oracle, commodity styles, and reference dates. | -- provided price oracle, commodity styles, and reference dates. | ||||||
| @ -109,9 +109,10 @@ mixedAmountApplyValuation priceoracle styles periodlast today postingdate v = | |||||||
|   mapMixedAmount (amountApplyValuation priceoracle styles periodlast today postingdate v) |   mapMixedAmount (amountApplyValuation priceoracle styles periodlast today postingdate v) | ||||||
| 
 | 
 | ||||||
| -- | Convert an Amount to its cost if requested, and style it appropriately. | -- | Convert an Amount to its cost if requested, and style it appropriately. | ||||||
| amountToCost :: Costing -> M.Map CommoditySymbol AmountStyle -> Amount -> Amount | amountToCost :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> Amount -> Amount | ||||||
| amountToCost NoCost _      = id | amountToCost styles ToCost         = styleAmount styles . amountCost | ||||||
| amountToCost Cost   styles = styleAmount styles . amountCost | amountToCost _      InferEquity    = amountStripPrices | ||||||
|  | amountToCost _      NoConversionOp = id | ||||||
| 
 | 
 | ||||||
| -- | Apply a specified valuation to this amount, using the provided | -- | Apply a specified valuation to this amount, using the provided | ||||||
| -- price oracle, and reference dates. Also fix up its display style | -- price oracle, and reference dates. Also fix up its display style | ||||||
|  | |||||||
| @ -351,7 +351,7 @@ accountdirectivep = do | |||||||
|   -- XXX added in 1.11, deprecated in 1.13, remove in 1.14 |   -- XXX added in 1.11, deprecated in 1.13, remove in 1.14 | ||||||
|   mtypecode :: Maybe Char <- lift $ optional $ try $ do |   mtypecode :: Maybe Char <- lift $ optional $ try $ do | ||||||
|     skipNonNewlineSpaces1 -- at least one more space in addition to the one consumed by modifiedaccountp |     skipNonNewlineSpaces1 -- at least one more space in addition to the one consumed by modifiedaccountp | ||||||
|     choice $ map char "ALERX" |     choice $ map char "ALERXV" | ||||||
| 
 | 
 | ||||||
|   -- maybe a comment, on this and/or following lines |   -- maybe a comment, on this and/or following lines | ||||||
|   (cmt, tags) <- lift transactioncommentp |   (cmt, tags) <- lift transactioncommentp | ||||||
| @ -378,22 +378,24 @@ accountTypeTagName = "type" | |||||||
| parseAccountTypeCode :: Text -> Either String AccountType | parseAccountTypeCode :: Text -> Either String AccountType | ||||||
| parseAccountTypeCode s = | parseAccountTypeCode s = | ||||||
|   case T.toLower s of |   case T.toLower s of | ||||||
|     "asset"     -> Right Asset |     "asset"      -> Right Asset | ||||||
|     "a"         -> Right Asset |     "a"          -> Right Asset | ||||||
|     "liability" -> Right Liability |     "liability"  -> Right Liability | ||||||
|     "l"         -> Right Liability |     "l"          -> Right Liability | ||||||
|     "equity"    -> Right Equity |     "equity"     -> Right Equity | ||||||
|     "e"         -> Right Equity |     "e"          -> Right Equity | ||||||
|     "revenue"   -> Right Revenue |     "revenue"    -> Right Revenue | ||||||
|     "r"         -> Right Revenue |     "r"          -> Right Revenue | ||||||
|     "expense"   -> Right Expense |     "expense"    -> Right Expense | ||||||
|     "x"         -> Right Expense |     "x"          -> Right Expense | ||||||
|     "cash"      -> Right Cash |     "cash"       -> Right Cash | ||||||
|     "c"         -> Right Cash |     "c"          -> Right Cash | ||||||
|     _           -> Left err |     "conversion" -> Right Conversion | ||||||
|  |     "v"          -> Right Conversion | ||||||
|  |     _            -> Left err | ||||||
|   where |   where | ||||||
|     err = T.unpack $ "invalid account type code "<>s<>", should be one of " <> |     err = T.unpack $ "invalid account type code "<>s<>", should be one of " <> | ||||||
|             T.intercalate ", " ["A","L","E","R","X","C","Asset","Liability","Equity","Revenue","Expense","Cash"] |             T.intercalate ", " ["A","L","E","R","X","C","V","Asset","Liability","Equity","Revenue","Expense","Cash","Conversion"] | ||||||
| 
 | 
 | ||||||
| -- Add an account declaration to the journal, auto-numbering it. | -- Add an account declaration to the journal, auto-numbering it. | ||||||
| addAccountDeclaration :: (AccountName,Text,[Tag]) -> JournalParser m () | addAccountDeclaration :: (AccountName,Text,[Tag]) -> JournalParser m () | ||||||
|  | |||||||
| @ -313,7 +313,7 @@ tests_BalanceReport = testGroup "BalanceReport" [ | |||||||
|                 ,"  a:b          10h @ $50" |                 ,"  a:b          10h @ $50" | ||||||
|                 ,"  c:d                   " |                 ,"  c:d                   " | ||||||
|                 ]) >>= either error' return |                 ]) >>= either error' return | ||||||
|          let j' = journalCanonicaliseAmounts $ journalToCost j -- enable cost basis adjustment |          let j' = journalCanonicaliseAmounts $ journalToCost ToCost j -- enable cost basis adjustment | ||||||
|          balanceReportAsText defreportopts (balanceReport defreportopts Any j') `is` |          balanceReportAsText defreportopts (balanceReport defreportopts Any j') `is` | ||||||
|            ["                $500  a:b" |            ["                $500  a:b" | ||||||
|            ,"               $-500  c:d" |            ,"               $-500  c:d" | ||||||
|  | |||||||
| @ -218,9 +218,9 @@ budgetReportAsText ropts@ReportOpts{..} budgetr = TB.toLazyText $ | |||||||
|       balanceReportTableAsText ropts (budgetReportAsTable ropts budgetr) |       balanceReportTableAsText ropts (budgetReportAsTable ropts budgetr) | ||||||
|   where |   where | ||||||
|     title = "Budget performance in " <> showDateSpan (periodicReportSpan budgetr) |     title = "Budget performance in " <> showDateSpan (periodicReportSpan budgetr) | ||||||
|            <> (case cost_ of |            <> (case conversionop_ of | ||||||
|                  Cost   -> ", converted to cost" |                  Just ToCost -> ", converted to cost" | ||||||
|                  NoCost -> "") |                  _           -> "") | ||||||
|            <> (case value_ of |            <> (case value_ of | ||||||
|                  Just (AtThen _mc)   -> ", valued at posting date" |                  Just (AtThen _mc)   -> ", valued at posting date" | ||||||
|                  Just (AtEnd _mc)    -> ", valued at period ends" |                  Just (AtEnd _mc)    -> ", valued at period ends" | ||||||
| @ -386,9 +386,9 @@ budgetReportAsTable | |||||||
|         _   -> -- trace (pshow $ (maybecost actual, maybecost budget))  -- debug missing percentage |         _   -> -- trace (pshow $ (maybecost actual, maybecost budget))  -- debug missing percentage | ||||||
|                Nothing |                Nothing | ||||||
|       where |       where | ||||||
|         costedAmounts = case cost_ of |         costedAmounts = case conversionop_ of | ||||||
|             Cost   -> amounts . mixedAmountCost |             Just ToCost -> amounts . mixedAmountCost | ||||||
|             NoCost -> amounts |             _           -> amounts | ||||||
| 
 | 
 | ||||||
|     -- | Calculate the percentage of actual change to budget goal for a particular commodity |     -- | Calculate the percentage of actual change to budget goal for a particular commodity | ||||||
|     percentage' :: Change -> BudgetGoal -> CommoditySymbol -> Maybe Percentage |     percentage' :: Change -> BudgetGoal -> CommoditySymbol -> Maybe Percentage | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ type EntriesReportItem = Transaction | |||||||
| entriesReport :: ReportSpec -> Journal -> EntriesReport | entriesReport :: ReportSpec -> Journal -> EntriesReport | ||||||
| entriesReport rspec@ReportSpec{_rsReportOpts=ropts} = | entriesReport rspec@ReportSpec{_rsReportOpts=ropts} = | ||||||
|     sortBy (comparing $ transactionDateFn ropts) . jtxns |     sortBy (comparing $ transactionDateFn ropts) . jtxns | ||||||
|     . journalApplyValuationFromOpts rspec{_rsReportOpts=ropts{show_costs_=True}} |     . journalApplyValuationFromOpts (setDefaultConversionOp NoConversionOp rspec) | ||||||
|     . filterJournalTransactions (_rsQuery rspec) |     . filterJournalTransactions (_rsQuery rspec) | ||||||
| 
 | 
 | ||||||
| tests_EntriesReport = testGroup "EntriesReport" [ | tests_EntriesReport = testGroup "EntriesReport" [ | ||||||
|  | |||||||
| @ -257,7 +257,7 @@ getPostings rspec@ReportSpec{_rsQuery=query, _rsReportOpts=ropts} j priceoracle | |||||||
|   where |   where | ||||||
|     rspec' = rspec{_rsQuery=depthless, _rsReportOpts = ropts'} |     rspec' = rspec{_rsQuery=depthless, _rsReportOpts = ropts'} | ||||||
|     ropts' = if isJust (valuationAfterSum ropts) |     ropts' = if isJust (valuationAfterSum ropts) | ||||||
|         then ropts{value_=Nothing, cost_=NoCost}  -- If we're valuing after the sum, don't do it now |         then ropts{value_=Nothing, conversionop_=Just NoConversionOp}  -- If we're valuing after the sum, don't do it now | ||||||
|         else ropts |         else ropts | ||||||
| 
 | 
 | ||||||
|     -- 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 | ||||||
| @ -432,7 +432,7 @@ displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} valuedaccts | |||||||
|         balance = maybeStripPrices . case accountlistmode_ ropts of |         balance = maybeStripPrices . case accountlistmode_ ropts of | ||||||
|             ALTree | d == depth -> aibalance |             ALTree | d == depth -> aibalance | ||||||
|             _                   -> aebalance |             _                   -> aebalance | ||||||
|           where maybeStripPrices = if show_costs_ ropts then id else mixedAmountStripPrices |           where maybeStripPrices = if conversionop_ ropts == Just NoConversionOp then id else mixedAmountStripPrices | ||||||
| 
 | 
 | ||||||
|     -- Accounts interesting because they are a fork for interesting subaccounts |     -- Accounts interesting because they are a fork for interesting subaccounts | ||||||
|     interestingParents = dbg5 "interestingParents" $ case accountlistmode_ ropts of |     interestingParents = dbg5 "interestingParents" $ case accountlistmode_ ropts of | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ module Hledger.Reports.ReportOptions ( | |||||||
|   defreportopts, |   defreportopts, | ||||||
|   rawOptsToReportOpts, |   rawOptsToReportOpts, | ||||||
|   defreportspec, |   defreportspec, | ||||||
|  |   setDefaultConversionOp, | ||||||
|   reportOptsToSpec, |   reportOptsToSpec, | ||||||
|   updateReportSpec, |   updateReportSpec, | ||||||
|   updateReportSpecWith, |   updateReportSpecWith, | ||||||
| @ -69,7 +70,7 @@ import Data.Either (fromRight) | |||||||
| import Data.Either.Extra (eitherToMaybe) | import Data.Either.Extra (eitherToMaybe) | ||||||
| import Data.Functor.Identity (Identity(..)) | import Data.Functor.Identity (Identity(..)) | ||||||
| import Data.List.Extra (find, isPrefixOf, nubSort) | import Data.List.Extra (find, isPrefixOf, nubSort) | ||||||
| import Data.Maybe (fromMaybe, mapMaybe) | import Data.Maybe (fromMaybe, isJust) | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| import Data.Time.Calendar (Day, addDays) | import Data.Time.Calendar (Day, addDays) | ||||||
| import Data.Default (Default(..)) | import Data.Default (Default(..)) | ||||||
| @ -124,7 +125,7 @@ data ReportOpts = ReportOpts { | |||||||
|      period_           :: Period |      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 | ||||||
|     ,cost_             :: Costing  -- ^ Should we convert amounts to cost, when present? |     ,conversionop_     :: Maybe ConversionOp  -- ^ Which operation should we apply to conversion transactions? | ||||||
|     ,value_            :: Maybe ValuationType  -- ^ What value should amounts be converted to ? |     ,value_            :: Maybe ValuationType  -- ^ What value should amounts be converted to ? | ||||||
|     ,infer_prices_     :: Bool      -- ^ Infer market prices from transactions ? |     ,infer_prices_     :: Bool      -- ^ Infer market prices from transactions ? | ||||||
|     ,depth_            :: Maybe Int |     ,depth_            :: Maybe Int | ||||||
| @ -180,7 +181,7 @@ defreportopts = ReportOpts | |||||||
|     { period_           = PeriodAll |     { period_           = PeriodAll | ||||||
|     , interval_         = NoInterval |     , interval_         = NoInterval | ||||||
|     , statuses_         = [] |     , statuses_         = [] | ||||||
|     , cost_             = NoCost |     , conversionop_     = Nothing | ||||||
|     , value_            = Nothing |     , value_            = Nothing | ||||||
|     , infer_prices_     = False |     , infer_prices_     = False | ||||||
|     , depth_            = Nothing |     , depth_            = Nothing | ||||||
| @ -223,7 +224,6 @@ rawOptsToReportOpts d rawopts = | |||||||
| 
 | 
 | ||||||
|     let formatstring = T.pack <$> maybestringopt "format" rawopts |     let formatstring = T.pack <$> maybestringopt "format" rawopts | ||||||
|         querystring  = map T.pack $ listofstringopt "args" rawopts  -- doesn't handle an arg like "" right |         querystring  = map T.pack $ listofstringopt "args" rawopts  -- doesn't handle an arg like "" right | ||||||
|         (costing, valuation) = valuationTypeFromRawOpts rawopts |  | ||||||
|         pretty = fromMaybe False $ alwaysneveropt "pretty" rawopts |         pretty = fromMaybe False $ alwaysneveropt "pretty" rawopts | ||||||
| 
 | 
 | ||||||
|         format = case parseStringFormat <$> formatstring of |         format = case parseStringFormat <$> formatstring of | ||||||
| @ -235,8 +235,8 @@ rawOptsToReportOpts d rawopts = | |||||||
|           {period_           = periodFromRawOpts d rawopts |           {period_           = periodFromRawOpts d rawopts | ||||||
|           ,interval_         = intervalFromRawOpts rawopts |           ,interval_         = intervalFromRawOpts rawopts | ||||||
|           ,statuses_         = statusesFromRawOpts rawopts |           ,statuses_         = statusesFromRawOpts rawopts | ||||||
|           ,cost_             = costing |           ,conversionop_     = conversionOpFromRawOpts rawopts | ||||||
|           ,value_            = valuation |           ,value_            = valuationTypeFromRawOpts rawopts | ||||||
|           ,infer_prices_     = boolopt "infer-market-prices" rawopts |           ,infer_prices_     = boolopt "infer-market-prices" rawopts | ||||||
|           ,depth_            = maybeposintopt "depth" rawopts |           ,depth_            = maybeposintopt "depth" rawopts | ||||||
|           ,date2_            = boolopt "date2" rawopts |           ,date2_            = boolopt "date2" rawopts | ||||||
| @ -290,6 +290,11 @@ defreportspec = ReportSpec | |||||||
|     , _rsQueryOpts  = [] |     , _rsQueryOpts  = [] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | -- | Set the default ConversionOp. | ||||||
|  | setDefaultConversionOp :: ConversionOp -> ReportSpec -> ReportSpec | ||||||
|  | setDefaultConversionOp def rspec@ReportSpec{_rsReportOpts=ropts} = | ||||||
|  |     rspec{_rsReportOpts=ropts{conversionop_=conversionop_ ropts <|> Just def}} | ||||||
|  | 
 | ||||||
| accountlistmodeopt :: RawOpts -> AccountListMode | accountlistmodeopt :: RawOpts -> AccountListMode | ||||||
| accountlistmodeopt = | accountlistmodeopt = | ||||||
|   fromMaybe ALFlat . choiceopt parse where |   fromMaybe ALFlat . choiceopt parse where | ||||||
| @ -469,39 +474,32 @@ reportOptsToggleStatus s ropts@ReportOpts{statuses_=ss} | |||||||
|   | s `elem` ss = ropts{statuses_=filter (/= s) ss} |   | s `elem` ss = ropts{statuses_=filter (/= s) ss} | ||||||
|   | otherwise   = ropts{statuses_=simplifyStatuses (s:ss)} |   | otherwise   = ropts{statuses_=simplifyStatuses (s:ss)} | ||||||
| 
 | 
 | ||||||
| -- | Parse the type of valuation and costing to be performed, if any, | -- | Parse the type of valuation to be performed, if any, specified by -V, | ||||||
| -- specified by -B/--cost, -V, -X/--exchange, or --value flags. It is | -- -X/--exchange, or --value flags. If there's more than one valuation type, | ||||||
| -- allowed to combine -B/--cost with any other valuation type. If | -- the rightmost flag wins. This will fail with a usage error if an invalid | ||||||
| -- there's more than one valuation type, the rightmost flag wins. | -- argument is passed to --value, or if --valuechange is called with a | ||||||
| -- This will fail with a usage error if an invalid argument is passed | -- valuation type other than -V/--value=end. | ||||||
| -- to --value, or if --valuechange is called with a valuation type | valuationTypeFromRawOpts :: RawOpts -> Maybe ValuationType | ||||||
| -- other than -V/--value=end. | valuationTypeFromRawOpts rawopts = case (balancecalcopt rawopts, directval) of | ||||||
| valuationTypeFromRawOpts :: RawOpts -> (Costing, Maybe ValuationType) |     (CalcValueChange, Nothing       ) -> Just $ AtEnd Nothing  -- If no valuation requested for valuechange, use AtEnd | ||||||
| valuationTypeFromRawOpts rawopts = case (balancecalcopt rawopts, directcost, directval) of |     (CalcValueChange, Just (AtEnd _)) -> directval             -- If AtEnd valuation requested, use it | ||||||
|     (CalcValueChange, _,      Nothing       ) -> (directcost, Just $ AtEnd Nothing)  -- If no valuation requested for valuechange, use AtEnd |     (CalcValueChange, _             ) -> usageError "--valuechange only produces sensible results with --value=end" | ||||||
|     (CalcValueChange, _,      Just (AtEnd _)) -> (directcost, directval)             -- If AtEnd valuation requested, use it |     (CalcGain,        Nothing       ) -> Just $ AtEnd Nothing  -- If no valuation requested for gain, use AtEnd | ||||||
|     (CalcValueChange, _,      _             ) -> usageError "--valuechange only produces sensible results with --value=end" |     (_,               _             ) -> directval             -- Otherwise, use requested valuation | ||||||
|     (CalcGain,        Cost,   _             ) -> usageError "--gain cannot be combined with --cost" |  | ||||||
|     (CalcGain,        NoCost, Nothing       ) -> (directcost, Just $ AtEnd Nothing)  -- If no valuation requested for gain, use AtEnd |  | ||||||
|     (_,               _,      _             ) -> (directcost, directval)             -- Otherwise, use requested valuation |  | ||||||
|   where |   where | ||||||
|     directcost = if Cost `elem` map fst valuationopts then Cost else NoCost |     directval = lastMay $ collectopts valuationfromrawopt rawopts | ||||||
|     directval  = lastMay $ mapMaybe snd valuationopts |  | ||||||
| 
 |  | ||||||
|     valuationopts = collectopts valuationfromrawopt rawopts |  | ||||||
|     valuationfromrawopt (n,v)  -- option name, value |     valuationfromrawopt (n,v)  -- option name, value | ||||||
|       | n == "B"     = Just (Cost,   Nothing)  -- keep supporting --value=cost for now |       | n == "V"     = Just $ AtEnd Nothing | ||||||
|       | n == "V"     = Just (NoCost, Just $ AtEnd Nothing) |       | n == "X"     = Just $ AtEnd (Just $ T.pack v) | ||||||
|       | n == "X"     = Just (NoCost, Just $ AtEnd (Just $ T.pack v)) |       | n == "value" = valueopt v | ||||||
|       | n == "value" = Just $ valueopt v |  | ||||||
|       | otherwise    = Nothing |       | otherwise    = Nothing | ||||||
|     valueopt v |     valueopt v | ||||||
|       | t `elem` ["cost","c"]  = (Cost,   AtEnd . Just <$> mc)  -- keep supporting --value=cost,COMM for now |       | t `elem` ["cost","c"]  = AtEnd . Just <$> mc  -- keep supporting --value=cost,COMM for now | ||||||
|       | t `elem` ["then" ,"t"] = (NoCost, Just $ AtThen mc) |       | t `elem` ["then" ,"t"] = Just $ AtThen mc | ||||||
|       | t `elem` ["end" ,"e"]  = (NoCost, Just $ AtEnd  mc) |       | t `elem` ["end" ,"e"]  = Just $ AtEnd  mc | ||||||
|       | t `elem` ["now" ,"n"]  = (NoCost, Just $ AtNow  mc) |       | t `elem` ["now" ,"n"]  = Just $ AtNow  mc | ||||||
|       | otherwise = case parsedateM t of |       | otherwise = case parsedateM t of | ||||||
|             Just d  -> (NoCost, Just $ AtDate d mc) |             Just d  -> Just $ AtDate d mc | ||||||
|             Nothing -> usageError $ "could not parse \""++t++"\" as valuation type, should be: then|end|now|t|e|n|YYYY-MM-DD" |             Nothing -> usageError $ "could not parse \""++t++"\" as valuation type, should be: then|end|now|t|e|n|YYYY-MM-DD" | ||||||
|       where |       where | ||||||
|         -- parse --value's value: TYPE[,COMM] |         -- parse --value's value: TYPE[,COMM] | ||||||
| @ -510,6 +508,22 @@ valuationTypeFromRawOpts rawopts = case (balancecalcopt rawopts, directcost, dir | |||||||
|                    "" -> Nothing |                    "" -> Nothing | ||||||
|                    c  -> Just $ T.pack c |                    c  -> Just $ T.pack c | ||||||
| 
 | 
 | ||||||
|  | -- | Parse the type of costing to be performed, if any, specified by -B/--cost | ||||||
|  | -- or --value flags. If there's more than one costing type, the rightmost flag | ||||||
|  | -- wins. This will fail with a usage error if an invalid argument is passed to | ||||||
|  | -- --cost or if a costing type is requested with --gain. | ||||||
|  | conversionOpFromRawOpts :: RawOpts -> Maybe ConversionOp | ||||||
|  | conversionOpFromRawOpts rawopts | ||||||
|  |     | isJust costFlag && balancecalcopt rawopts == CalcGain = usageError "--gain cannot be combined with --cost" | ||||||
|  |     | boolopt "infer-equity" rawopts = costFlag <|> Just InferEquity | ||||||
|  |     | otherwise = costFlag | ||||||
|  |   where | ||||||
|  |     costFlag = lastMay $ collectopts conversionopfromrawopt rawopts | ||||||
|  |     conversionopfromrawopt (n,v)  -- option name, value | ||||||
|  |       | n == "B"                                    = Just ToCost | ||||||
|  |       | n == "value", takeWhile (/=',') v `elem` ["cost", "c"] = Just ToCost  -- keep supporting --value=cost for now | ||||||
|  |       | otherwise                                   = Nothing | ||||||
|  | 
 | ||||||
| -- | Select the Transaction date accessor based on --date2. | -- | Select the Transaction date accessor based on --date2. | ||||||
| transactionDateFn :: ReportOpts -> (Transaction -> Day) | transactionDateFn :: ReportOpts -> (Transaction -> Day) | ||||||
| transactionDateFn ReportOpts{..} = if date2_ then transactionDate2 else tdate | transactionDateFn ReportOpts{..} = if date2_ then transactionDate2 else tdate | ||||||
| @ -578,9 +592,7 @@ journalApplyValuationFromOptsWith rspec@ReportSpec{_rsReportOpts=ropts} j priceo | |||||||
|   where |   where | ||||||
|     valuation p = maybe id (mixedAmountApplyValuation priceoracle styles (periodEnd p) (_rsDay rspec) (postingDate p)) (value_ ropts) |     valuation p = maybe id (mixedAmountApplyValuation priceoracle styles (periodEnd p) (_rsDay rspec) (postingDate p)) (value_ ropts) | ||||||
|     gain      p = maybe id (mixedAmountApplyGain      priceoracle styles (periodEnd p) (_rsDay rspec) (postingDate p)) (value_ ropts) |     gain      p = maybe id (mixedAmountApplyGain      priceoracle styles (periodEnd p) (_rsDay rspec) (postingDate p)) (value_ ropts) | ||||||
|     costing = case cost_ ropts of |     costing     = journalToCost (fromMaybe NoConversionOp $ conversionop_ ropts) | ||||||
|         Cost   -> journalToCost |  | ||||||
|         NoCost -> id |  | ||||||
| 
 | 
 | ||||||
|     -- Find the end of the period containing this posting |     -- Find the end of the period containing this posting | ||||||
|     periodEnd  = addDays (-1) . fromMaybe err . mPeriodEnd . postingDate |     periodEnd  = addDays (-1) . fromMaybe err . mPeriodEnd . postingDate | ||||||
| @ -605,9 +617,10 @@ mixedAmountApplyValuationAfterSumFromOptsWith ropts j priceoracle = | |||||||
|   where |   where | ||||||
|     valuation mc span = mixedAmountValueAtDate priceoracle styles mc (maybe err (addDays (-1)) $ spanEnd span) |     valuation mc span = mixedAmountValueAtDate priceoracle styles mc (maybe err (addDays (-1)) $ spanEnd span) | ||||||
|     gain mc span = mixedAmountGainAtDate priceoracle styles mc (maybe err (addDays (-1)) $ spanEnd span) |     gain mc span = mixedAmountGainAtDate priceoracle styles mc (maybe err (addDays (-1)) $ spanEnd span) | ||||||
|     costing = case cost_ ropts of |     costing = case fromMaybe NoConversionOp $ conversionop_ ropts of | ||||||
|         Cost   -> styleMixedAmount styles . mixedAmountCost |         NoConversionOp -> id | ||||||
|         NoCost -> id |         InferEquity    -> mixedAmountStripPrices | ||||||
|  |         ToCost         -> styleMixedAmount styles . mixedAmountCost | ||||||
|     styles = journalCommodityStyles j |     styles = journalCommodityStyles j | ||||||
|     err = error "mixedAmountApplyValuationAfterSumFromOptsWith: expected all spans to have an end date" |     err = error "mixedAmountApplyValuationAfterSumFromOptsWith: expected all spans to have an end date" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -285,7 +285,7 @@ asHandle ui0@UIState{ | |||||||
|         VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui |         VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui | ||||||
|         VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui |         VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui | ||||||
|         VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui |         VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui | ||||||
|         VtyEvent (EvKey (KChar 'B') []) -> continue $ regenerateScreens j d $ toggleCost ui |         VtyEvent (EvKey (KChar 'B') []) -> continue $ regenerateScreens j d $ toggleConversionOp ui | ||||||
|         VtyEvent (EvKey (KChar 'V') []) -> continue $ regenerateScreens j d $ toggleValue ui |         VtyEvent (EvKey (KChar 'V') []) -> continue $ regenerateScreens j d $ toggleValue ui | ||||||
|         VtyEvent (EvKey (KChar '0') []) -> continue $ regenerateScreens j d $ setDepth (Just 0) ui |         VtyEvent (EvKey (KChar '0') []) -> continue $ regenerateScreens j d $ setDepth (Just 0) ui | ||||||
|         VtyEvent (EvKey (KChar '1') []) -> continue $ regenerateScreens j d $ setDepth (Just 1) ui |         VtyEvent (EvKey (KChar '1') []) -> continue $ regenerateScreens j d $ setDepth (Just 1) ui | ||||||
|  | |||||||
| @ -335,7 +335,7 @@ rsHandle ui@UIState{ | |||||||
|                           rsItemTransaction=Transaction{tsourcepos=(SourcePos f l c,_)}}) -> (Just (unPos l, Just $ unPos c),f) |                           rsItemTransaction=Transaction{tsourcepos=(SourcePos f l c,_)}}) -> (Just (unPos l, Just $ unPos c),f) | ||||||
| 
 | 
 | ||||||
|         -- display mode/query toggles |         -- display mode/query toggles | ||||||
|         VtyEvent (EvKey (KChar 'B') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCost ui |         VtyEvent (EvKey (KChar 'B') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleConversionOp ui | ||||||
|         VtyEvent (EvKey (KChar 'V') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleValue ui |         VtyEvent (EvKey (KChar 'V') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleValue ui | ||||||
|         VtyEvent (EvKey (KChar 'H') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui |         VtyEvent (EvKey (KChar 'H') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui | ||||||
|         VtyEvent (EvKey (KChar 't') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleTree ui |         VtyEvent (EvKey (KChar 't') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleTree ui | ||||||
|  | |||||||
| @ -63,12 +63,10 @@ tsInit _ _ _ = error "init function called with wrong screen type, should not ha | |||||||
| -- Render a transaction suitably for the transaction screen. | -- Render a transaction suitably for the transaction screen. | ||||||
| showTxn :: ReportOpts -> ReportSpec -> Journal -> Transaction -> T.Text | showTxn :: ReportOpts -> ReportSpec -> Journal -> Transaction -> T.Text | ||||||
| showTxn ropts rspec j t = | showTxn ropts rspec j t = | ||||||
|     showTransactionOneLineAmounts |       showTransactionOneLineAmounts | ||||||
|   $ maybe id (transactionApplyValuation prices styles periodlast (_rsDay rspec)) (value_ ropts) |     $ maybe id (transactionApplyValuation prices styles periodlast (_rsDay rspec)) (value_ ropts) | ||||||
|   $ case cost_ ropts of |     $ maybe id (transactionToCost (journalConversionAccount j) styles) (conversionop_ ropts) t | ||||||
|         Cost   -> transactionToCost styles t |     -- (if real_ ropts then filterTransactionPostings (Real True) else id) -- filter postings by --real | ||||||
|         NoCost -> t |  | ||||||
|   -- (if real_ ropts then filterTransactionPostings (Real True) else id) -- filter postings by --real |  | ||||||
|   where |   where | ||||||
|     prices = journalPriceOracle (infer_prices_ ropts) j |     prices = journalPriceOracle (infer_prices_ ropts) j | ||||||
|     styles = journalCommodityStyles j |     styles = journalCommodityStyles j | ||||||
| @ -187,7 +185,7 @@ tsHandle ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransaction | |||||||
|         -- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui |         -- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui | ||||||
|         -- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui |         -- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui | ||||||
|         -- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui |         -- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui | ||||||
|         VtyEvent (EvKey (KChar 'B') []) -> continue . regenerateScreens j d $ toggleCost ui |         VtyEvent (EvKey (KChar 'B') []) -> continue . regenerateScreens j d $ toggleConversionOp ui | ||||||
|         VtyEvent (EvKey (KChar 'V') []) -> continue . regenerateScreens j d $ toggleValue ui |         VtyEvent (EvKey (KChar 'V') []) -> continue . regenerateScreens j d $ toggleValue ui | ||||||
| 
 | 
 | ||||||
|         VtyEvent e | e `elem` moveUpEvents   -> continue $ tsSelect iprev tprev ui |         VtyEvent e | e `elem` moveUpEvents   -> continue $ tsSelect iprev tprev ui | ||||||
|  | |||||||
| @ -97,11 +97,13 @@ toggleEmpty :: UIState -> UIState | |||||||
| toggleEmpty = over empty__ not | toggleEmpty = over empty__ not | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing the primary amounts or costs. | -- | Toggle between showing the primary amounts or costs. | ||||||
| toggleCost :: UIState -> UIState | toggleConversionOp :: UIState -> UIState | ||||||
| toggleCost = over cost toggleCostMode | toggleConversionOp = over conversionop toggleCostMode | ||||||
|   where |   where | ||||||
|     toggleCostMode Cost   = NoCost |     toggleCostMode Nothing               = Just ToCost | ||||||
|     toggleCostMode NoCost = Cost |     toggleCostMode (Just NoConversionOp) = Just ToCost | ||||||
|  |     toggleCostMode (Just InferEquity)    = Just ToCost | ||||||
|  |     toggleCostMode (Just ToCost)         = Just NoConversionOp | ||||||
| 
 | 
 | ||||||
| -- | Toggle between showing primary amounts or default valuation. | -- | Toggle between showing primary amounts or default valuation. | ||||||
| toggleValue :: UIState -> UIState | toggleValue :: UIState -> UIState | ||||||
|  | |||||||
| @ -181,6 +181,8 @@ reportflags = [ | |||||||
|      ,"'now':  convert to current market value, in default valuation commodity or COMM" |      ,"'now':  convert to current market value, in default valuation commodity or COMM" | ||||||
|      ,"YYYY-MM-DD: convert to market value on the given date, in default valuation commodity or COMM" |      ,"YYYY-MM-DD: convert to market value on the given date, in default valuation commodity or COMM" | ||||||
|      ]) |      ]) | ||||||
|  |   ,flagNone ["infer-equity"] (setboolopt "infer-equity") | ||||||
|  |     "in conversion transactions, replace costs (transaction prices) with equity postings, to keep the transactions balanced" | ||||||
|    |    | ||||||
|   -- history of this flag so far, lest we be confused: |   -- history of this flag so far, lest we be confused: | ||||||
|   --  originally --infer-value |   --  originally --infer-value | ||||||
|  | |||||||
| @ -652,9 +652,9 @@ multiBalanceReportAsText ropts@ReportOpts{..} r = TB.toLazyText $ | |||||||
|         (_,               Cumulative ) -> "Ending balances (cumulative)" |         (_,               Cumulative ) -> "Ending balances (cumulative)" | ||||||
|         (_,               Historical)  -> "Ending balances (historical)" |         (_,               Historical)  -> "Ending balances (historical)" | ||||||
|     valuationdesc = |     valuationdesc = | ||||||
|         (case cost_ of |         (case conversionop_ of | ||||||
|             Cost   -> ", converted to cost" |             Just ToCost -> ", converted to cost" | ||||||
|             NoCost -> "") |             _           -> "") | ||||||
|         <> (case value_ of |         <> (case value_ of | ||||||
|             Just (AtThen _mc)    -> ", valued at posting date" |             Just (AtThen _mc)    -> ", valued at posting date" | ||||||
|             Just (AtEnd _mc) | changingValuation -> "" |             Just (AtEnd _mc) | changingValuation -> "" | ||||||
|  | |||||||
| @ -48,9 +48,8 @@ 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 hledger/test/close.test. | -- tests are in hledger/test/close.test. | ||||||
| close CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | close CliOpts{rawopts_=rawopts, reportspec_=rspec'} j = do | ||||||
|   let |   let | ||||||
|     today = _rsDay rspec |  | ||||||
|     -- show opening entry, closing entry, or (default) both ? |     -- show opening entry, closing entry, or (default) both ? | ||||||
|     (opening, closing) = |     (opening, closing) = | ||||||
|       case (boolopt "open" rawopts, boolopt "close" rawopts) of |       case (boolopt "open" rawopts, boolopt "close" rawopts) of | ||||||
| @ -72,6 +71,9 @@ close CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | |||||||
|         (Nothing, Just o)  -> (o, o) |         (Nothing, Just o)  -> (o, o) | ||||||
|         (Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct) |         (Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct) | ||||||
| 
 | 
 | ||||||
|  |     ropts = (_rsReportOpts rspec'){balanceaccum_=Historical, accountlistmode_=ALFlat} | ||||||
|  |     rspec = setDefaultConversionOp (if show_costs then NoConversionOp else InferEquity) rspec'{_rsReportOpts=ropts} | ||||||
|  | 
 | ||||||
|     -- dates of the closing and opening transactions |     -- dates of the closing and opening transactions | ||||||
|     -- |     -- | ||||||
|     -- Close.md: |     -- Close.md: | ||||||
| @ -90,7 +92,7 @@ close CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | |||||||
|     -- - `-e 2021`" |     -- - `-e 2021`" | ||||||
|     -- |     -- | ||||||
|     q = _rsQuery rspec |     q = _rsQuery rspec | ||||||
|     yesterday = addDays (-1) today |     yesterday = addDays (-1) $ _rsDay rspec | ||||||
|     yesterdayorjournalend = case journalLastDay False j of |     yesterdayorjournalend = case journalLastDay False j of | ||||||
|       Just journalend -> max yesterday journalend |       Just journalend -> max yesterday journalend | ||||||
|       Nothing         -> yesterday |       Nothing         -> yesterday | ||||||
| @ -100,12 +102,11 @@ close CliOpts{rawopts_=rawopts, reportspec_=rspec} j = do | |||||||
| 
 | 
 | ||||||
|     -- should we show the amount(s) on the equity posting(s) ? |     -- should we show the amount(s) on the equity posting(s) ? | ||||||
|     explicit = boolopt "explicit" rawopts |     explicit = boolopt "explicit" rawopts | ||||||
|  |     show_costs = boolopt "show-costs" rawopts | ||||||
| 
 | 
 | ||||||
|     -- the balances to close |     -- the balances to close | ||||||
|     ropts = (_rsReportOpts rspec){balanceaccum_=Historical, accountlistmode_=ALFlat} |     (acctbals',_) = balanceReport rspec j | ||||||
|     rspec_ = rspec{_rsReportOpts=ropts} |     acctbals = map (\(a,_,_,b) -> (a, if show_costs then b else mixedAmountStripPrices b)) acctbals' | ||||||
|     (acctbals',_) = balanceReport rspec_ j |  | ||||||
|     acctbals = map (\(a,_,_,b) -> (a, if show_costs_ ropts then b else mixedAmountStripPrices b)) acctbals' |  | ||||||
|     totalamt = maSum $ map snd acctbals |     totalamt = maSum $ map snd acctbals | ||||||
| 
 | 
 | ||||||
|     -- since balance assertion amounts are required to be exact, the |     -- since balance assertion amounts are required to be exact, the | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ import qualified Data.Text as T | |||||||
| import qualified Data.Text.IO as T | import qualified Data.Text.IO as T | ||||||
| import qualified Data.Text.Lazy as TL | import qualified Data.Text.Lazy as TL | ||||||
| import qualified Data.Text.Lazy.Builder as TB | import qualified Data.Text.Lazy.Builder as TB | ||||||
| import Lens.Micro (_Just, has) | import Lens.Micro ((^.), _Just, has) | ||||||
| import System.Console.CmdArgs.Explicit | import System.Console.CmdArgs.Explicit | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| @ -37,6 +37,8 @@ printmode = hledgerCommandMode | |||||||
|     ("show the transaction whose description is most similar to "++arg++", and is most recent") |     ("show the transaction whose description is most similar to "++arg++", and is most recent") | ||||||
|   ,flagNone ["explicit","x"] (setboolopt "explicit") |   ,flagNone ["explicit","x"] (setboolopt "explicit") | ||||||
|     "show all amounts explicitly" |     "show all amounts explicitly" | ||||||
|  |   ,flagNone ["show-costs"] (setboolopt "show-costs") | ||||||
|  |     "show transaction prices even with conversion postings" | ||||||
|   ,flagNone ["new"] (setboolopt "new") |   ,flagNone ["new"] (setboolopt "new") | ||||||
|     "show only newer-dated transactions added in each file since last run" |     "show only newer-dated transactions added in each file since last run" | ||||||
|   ,outputFormatFlag ["txt","csv","json","sql"] |   ,outputFormatFlag ["txt","csv","json","sql"] | ||||||
| @ -72,7 +74,8 @@ printEntries opts@CliOpts{reportspec_=rspec} j = | |||||||
|            | otherwise   = error' $ unsupportedOutputFormatError fmt  -- PARTIAL: |            | otherwise   = error' $ unsupportedOutputFormatError fmt  -- PARTIAL: | ||||||
| 
 | 
 | ||||||
| entriesReportAsText :: CliOpts -> EntriesReport -> TL.Text | entriesReportAsText :: CliOpts -> EntriesReport -> TL.Text | ||||||
| entriesReportAsText opts = TB.toLazyText . foldMap (TB.fromText . showTransaction . whichtxn) | entriesReportAsText opts = | ||||||
|  |     TB.toLazyText . foldMap (TB.fromText . showTransaction . maybeStripPrices . whichtxn) | ||||||
|   where |   where | ||||||
|     whichtxn |     whichtxn | ||||||
|       -- With -x, use the fully-inferred txn with all amounts & txn prices explicit. |       -- With -x, use the fully-inferred txn with all amounts & txn prices explicit. | ||||||
| @ -84,6 +87,11 @@ entriesReportAsText opts = TB.toLazyText . foldMap (TB.fromText . showTransactio | |||||||
|       | has (value . _Just) opts = id |       | has (value . _Just) 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 | ||||||
|  |     maybeStripPrices | ||||||
|  |       -- Strip prices when inferring equity, unless the show-costs option is set | ||||||
|  |       | opts ^. conversionop == Just InferEquity && not (boolopt "show-costs" $ rawopts_ opts) = | ||||||
|  |           transactionTransformPostings postingStripPrices | ||||||
|  |       | otherwise = id | ||||||
| 
 | 
 | ||||||
| -- Replace this transaction's postings with the original postings if any, but keep the | -- Replace this transaction's postings with the original postings if any, but keep the | ||||||
| -- current possibly rewritten account names, and the inferred values of any auto postings | -- current possibly rewritten account names, and the inferred values of any auto postings | ||||||
|  | |||||||
| @ -66,7 +66,7 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{_rsReportOpts=ReportO | |||||||
|     styles = journalCommodityStyles j |     styles = journalCommodityStyles j | ||||||
|     mixedAmountValue periodlast date = |     mixedAmountValue periodlast date = | ||||||
|         maybe id (mixedAmountApplyValuation priceOracle styles periodlast today date) value_ |         maybe id (mixedAmountApplyValuation priceOracle styles periodlast today date) value_ | ||||||
|         . mixedAmountToCost cost_ styles |       . maybe id (mixedAmountToCost styles) conversionop_ | ||||||
| 
 | 
 | ||||||
|   let |   let | ||||||
|     ropts = _rsReportOpts rspec |     ropts = _rsReportOpts rspec | ||||||
|  | |||||||
| @ -152,9 +152,9 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportspec_=r | |||||||
|             _                                              -> Nothing |             _                                              -> Nothing | ||||||
| 
 | 
 | ||||||
|         valuationdesc = |         valuationdesc = | ||||||
|           (case cost_ of |           (case conversionop_ of | ||||||
|                Cost   -> ", converted to cost" |                Just ToCost -> ", converted to cost" | ||||||
|                NoCost -> "") |                _           -> "") | ||||||
|           <> (case value_ of |           <> (case value_ of | ||||||
|                Just (AtThen _mc)       -> ", valued at posting date" |                Just (AtThen _mc)       -> ", valued at posting date" | ||||||
|                Just (AtEnd _mc) | changingValuation -> "" |                Just (AtEnd _mc) | changingValuation -> "" | ||||||
|  | |||||||
| @ -917,15 +917,22 @@ see the discussion at [#1625](https://github.com/simonmichael/hledger/issues/162 | |||||||
| 
 | 
 | ||||||
| # COSTING | # COSTING | ||||||
| 
 | 
 | ||||||
| The `-B/--cost` flag converts amounts to their cost or sale amount at transaction time, | The `-B/--cost` flag converts amounts to their cost or sale amount at | ||||||
| if they have a [transaction price](#transaction-prices) specified. | transaction time, if they have a [transaction price](#transaction-prices) | ||||||
| If this flag is supplied, hledger will perform cost conversion first, and will apply | specified. | ||||||
| any market price valuations (if requested) afterwards. | 
 | ||||||
|  | The `--infer-equity` flag generates conversion postings within equity to | ||||||
|  | balance any transaction prices. | ||||||
|  | The account used is "equity:conversion" by default, but this can be customised | ||||||
|  | with an account declaration: `account <conversion_account>  Conversion`. | ||||||
|  | 
 | ||||||
|  | If either of these flags are supplied, hledger will perform cost conversion | ||||||
|  | first, and will apply any market price valuations (if requested) afterwards. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # VALUATION | # VALUATION | ||||||
| 
 | 
 | ||||||
| Instead of reporting amounts in their original commodity, | Instead of reporting amounts in their original commodity, hledger can convert them to | ||||||
| hledger can convert them to |  | ||||||
| cost/sale amount (using the conversion rate recorded in the transaction), | cost/sale amount (using the conversion rate recorded in the transaction), | ||||||
| and/or to market value (using some market price on a certain date). | and/or to market value (using some market price on a certain date). | ||||||
| This is controlled by the `--value=TYPE[,COMMODITY]` option, which will be described below. | This is controlled by the `--value=TYPE[,COMMODITY]` option, which will be described below. | ||||||
| @ -2914,8 +2921,8 @@ account ACCTNAME  [ACCTTYPE] [;COMMENT] | |||||||
| 
 | 
 | ||||||
| By adding a `type` tag to the [account directive], | By adding a `type` tag to the [account directive], | ||||||
| with value | with value | ||||||
| `A`, `L`, `E`, `R`, `X`, `C` | `A`, `L`, `E`, `R`, `X`, `C`, `V` | ||||||
| (or if you prefer: `Asset`, `Liability`, `Equity`, `Revenue`, `Expense`, `Cash`), | (or if you prefer: `Asset`, `Liability`, `Equity`, `Revenue`, `Expense`, `Cash`, `Conversion`), | ||||||
| you can declare hledger accounts to be of a certain type: | you can declare hledger accounts to be of a certain type: | ||||||
| 
 | 
 | ||||||
| - **asset**,  | - **asset**,  | ||||||
| @ -2928,6 +2935,9 @@ you can declare hledger accounts to be of a certain type: | |||||||
| - **cash**\ | - **cash**\ | ||||||
|   a subtype of asset, used for [liquid assets][CCE]. |   a subtype of asset, used for [liquid assets][CCE]. | ||||||
| 
 | 
 | ||||||
|  | - **conversion**\ | ||||||
|  |   a subtype of equity, used for [conversion postings](#costing) | ||||||
|  | 
 | ||||||
| Declaring account types is a good idea, since it helps enable the easy  | Declaring account types is a good idea, since it helps enable the easy  | ||||||
| [balancesheet], [balancesheetequity], [incomestatement] and [cashflow] reports,  | [balancesheet], [balancesheetequity], [incomestatement] and [cashflow] reports,  | ||||||
| and probably other things in future.  | and probably other things in future.  | ||||||
|  | |||||||
| @ -25,7 +25,50 @@ hledger -f- print --explicit --cost | |||||||
| 
 | 
 | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 3. print a transaction with a total price | # 3. --infer-equity generates conversion postings | ||||||
|  | hledger -f- print --infer-equity | ||||||
|  | <<< | ||||||
|  | 2011/01/01 | ||||||
|  |     expenses:foreign currency       €100 @ $1.35 | ||||||
|  |     assets | ||||||
|  | >>> | ||||||
|  | 2011-01-01 | ||||||
|  |     expenses:foreign currency            €100  ; cost: @ $1.35 | ||||||
|  |     equity:conversion:$-€:€             €-100  ; generated-posting: | ||||||
|  |     equity:conversion:$-€:$           $135.00  ; generated-posting: | ||||||
|  |     assets | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 4. With --infer-equity and --show-costs, the cost is still shown | ||||||
|  | hledger -f- print --infer-equity --show-costs | ||||||
|  | <<< | ||||||
|  | 2011/01/01 | ||||||
|  |     expenses:foreign currency       €100 @ $1.35 | ||||||
|  |     assets | ||||||
|  | >>> | ||||||
|  | 2011-01-01 | ||||||
|  |     expenses:foreign currency    €100 @ $1.35  ; cost: @ $1.35 | ||||||
|  |     equity:conversion:$-€:€             €-100  ; generated-posting: | ||||||
|  |     equity:conversion:$-€:$           $135.00  ; generated-posting: | ||||||
|  |     assets | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 5. With --cost, --infer-equity is ignored | ||||||
|  | hledger -f- print --explicit --cost --infer-equity | ||||||
|  | <<< | ||||||
|  | 2011/01/01 | ||||||
|  |     expenses:foreign currency       €100 @ $1.35 | ||||||
|  |     assets | ||||||
|  | >>> | ||||||
|  | 2011-01-01 | ||||||
|  |     expenses:foreign currency         $135.00 | ||||||
|  |     assets                           $-135.00 | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 6. print a transaction with a total price | ||||||
| hledger -f - print --explicit | hledger -f - print --explicit | ||||||
| <<< | <<< | ||||||
| 2011/01/01 | 2011/01/01 | ||||||
| @ -38,7 +81,7 @@ hledger -f - print --explicit | |||||||
| 
 | 
 | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 4. when the balance has exactly two commodities, both unpriced, infer an | # 7. when the balance has exactly two commodities, both unpriced, infer an | ||||||
| # implicit conversion price for the first one in terms of the second. | # implicit conversion price for the first one in terms of the second. | ||||||
| hledger -f - print --explicit | hledger -f - print --explicit | ||||||
| <<< | <<< | ||||||
| @ -60,7 +103,7 @@ hledger -f - print --explicit | |||||||
| 
 | 
 | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| ## 5. another, from ledger tests. Just one posting to price so uses @@. | # 8. another, from ledger tests. Just one posting to price so uses @@. | ||||||
| hledger -f - print --explicit | hledger -f - print --explicit | ||||||
| <<< | <<< | ||||||
| 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be | 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be | ||||||
| @ -73,7 +116,7 @@ hledger -f - print --explicit | |||||||
| 
 | 
 | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 6. when the balance has more than two commodities, don't bother | # 9. when the balance has more than two commodities, don't bother | ||||||
| hledger -f - print | hledger -f - print | ||||||
| <<< | <<< | ||||||
| 2011/01/01 | 2011/01/01 | ||||||
| @ -82,7 +125,7 @@ hledger -f - print | |||||||
|     expenses:other                    £200 |     expenses:other                    £200 | ||||||
| >>>= !0 | >>>= !0 | ||||||
| 
 | 
 | ||||||
| # 7. another | # 10. another | ||||||
| hledger -f - balance -B | hledger -f - balance -B | ||||||
| <<< | <<< | ||||||
| 2011/01/01 | 2011/01/01 | ||||||
| @ -97,7 +140,7 @@ hledger -f - balance -B | |||||||
|                    0   |                    0   | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 8. transaction in two commodities should balance out properly | # 11. transaction in two commodities should balance out properly | ||||||
| hledger -f - balance --cost | hledger -f - balance --cost | ||||||
| <<< | <<< | ||||||
| 2011/01/01 x | 2011/01/01 x | ||||||
| @ -110,23 +153,62 @@ hledger -f - balance --cost | |||||||
|                    0   |                    0   | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 9. When commodity price is specified implicitly, transaction should | # 12. --value=cost,XXX is deprecated, but should still work (for now) | ||||||
| #    be considered balanced out even when first amount is negative | hledger -f - balance --value=cost,XXX | ||||||
| #    (that is, price for it should be determined properly, with proper sign) | <<< | ||||||
|  | 2011/01/01 x | ||||||
|  |   a  10£ @@ 16$ | ||||||
|  |   b | ||||||
|  | >>> | ||||||
|  |                  16$  a | ||||||
|  |                 -16$  b | ||||||
|  | -------------------- | ||||||
|  |                    0   | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 13. conversion postings should be generated when called --infer-equity | ||||||
|  | hledger -f - balance --infer-equity | ||||||
|  | <<< | ||||||
|  | 2011/01/01 x | ||||||
|  |   a  10£ @@ 16$ | ||||||
|  |   b | ||||||
|  | >>> | ||||||
|  |                  10£  a | ||||||
|  |                 -16$  b | ||||||
|  |                  16$  equity:conversion:$-£:$ | ||||||
|  |                 -10£  equity:conversion:$-£:£ | ||||||
|  | -------------------- | ||||||
|  |                    0   | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 14. transaction should be left unbalanced when called without --cost or --infer-equity | ||||||
| hledger -f - balance | hledger -f - balance | ||||||
| <<< | <<< | ||||||
|  | 2011/01/01 x | ||||||
|  |   a  10£ @@ 16$ | ||||||
|  |   b | ||||||
|  | >>> | ||||||
|  |                  10£  a | ||||||
|  |                 -16$  b | ||||||
|  | -------------------- | ||||||
|  |                 -16$ | ||||||
|  |                  10£   | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 15. When commodity price is specified implicitly, transaction should | ||||||
|  | #     be considered balanced out even when first amount is negative | ||||||
|  | #     (that is, price for it should be determined properly, with proper sign) | ||||||
|  | hledger -f - balance -N | ||||||
|  | <<< | ||||||
| 2011/01/01 x | 2011/01/01 x | ||||||
|   a  -10£ |   a  -10£ | ||||||
|   b  16$ |   b  16$ | ||||||
| >>> | >>> | ||||||
|                 -10£  a |                 -10£  a | ||||||
|                  16$  b |                  16$  b | ||||||
| -------------------- |  | ||||||
|                  16$ |  | ||||||
|                 -10£   |  | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 10. Should not infer prices when --strict is specified | # 16. Should not infer prices when --strict is specified | ||||||
| hledger -f - balance --strict | hledger -f - balance --strict | ||||||
| <<< | <<< | ||||||
| 2011/01/01 x | 2011/01/01 x | ||||||
| @ -135,7 +217,7 @@ hledger -f - balance --strict | |||||||
| >>> | >>> | ||||||
| >>>=1 | >>>=1 | ||||||
| 
 | 
 | ||||||
| # 11. When commodity price is specified implicitly, transaction should | # 17. When commodity price is specified implicitly, transaction should | ||||||
| #     NOT be considered balanced out when BOTH amounts are negative | #     NOT be considered balanced out when BOTH amounts are negative | ||||||
| hledger -f - balance | hledger -f - balance | ||||||
| <<< | <<< | ||||||
| @ -145,7 +227,7 @@ hledger -f - balance | |||||||
| >>> | >>> | ||||||
| >>>=1 | >>>=1 | ||||||
| 
 | 
 | ||||||
| # 12. Differently-priced lots of a commodity should be merged in balance report | # 18. Differently-priced lots of a commodity should be merged in balance report | ||||||
| hledger -f - balance | hledger -f - balance | ||||||
| <<< | <<< | ||||||
| 2011/1/1 | 2011/1/1 | ||||||
| @ -159,7 +241,7 @@ hledger -f - balance | |||||||
|                   £2   |                   £2   | ||||||
| >>>=0 | >>>=0 | ||||||
| 
 | 
 | ||||||
| # 13. this should balance | # 19. this should balance | ||||||
| hledger -f - balance | hledger -f - balance | ||||||
| <<< | <<< | ||||||
| 2011/1/1 | 2011/1/1 | ||||||
| @ -168,7 +250,7 @@ hledger -f - balance | |||||||
|     c  $-30 |     c  $-30 | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 14. these balance because of the unit prices, and should parse successfully | # 20. these balance because of the unit prices, and should parse successfully | ||||||
| hledger -f - balance --no-total | hledger -f - balance --no-total | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -178,7 +260,7 @@ hledger -f - balance --no-total | |||||||
|                  -1X  a |                  -1X  a | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 15. | # 21. | ||||||
| hledger -f - balance --no-total -B | hledger -f - balance --no-total -B | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -187,7 +269,7 @@ hledger -f - balance --no-total -B | |||||||
| >>> | >>> | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 16. likewise with total prices. Note how the primary amount's sign is used. | # 22. likewise with total prices. Note how the primary amount's sign is used. | ||||||
| hledger -f - balance --no-total | hledger -f - balance --no-total | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -197,7 +279,7 @@ hledger -f - balance --no-total | |||||||
|                  -1X  a |                  -1X  a | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 17. | # 23. | ||||||
| hledger -f - balance --no-total -B | hledger -f - balance --no-total -B | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -206,7 +288,7 @@ hledger -f - balance --no-total -B | |||||||
| >>> | >>> | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 18. here, a's primary amount is 0, and its cost is 1Y; b is the assigned auto-balancing amount of -1Y (per issue 69) | # 24. here, a's primary amount is 0, and its cost is 1Y; b is the assigned auto-balancing amount of -1Y (per issue 69) | ||||||
| hledger -f - balance --no-total -E | hledger -f - balance --no-total -E | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -219,7 +301,7 @@ hledger -f - balance --no-total -E | |||||||
|                  -1Y  b |                  -1Y  b | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 19. Without -E, a should be hidden because its balance is zero, even though it has a non-zero cost. | # 25. Without -E, a should be hidden because its balance is zero, even though it has a non-zero cost. | ||||||
| hledger -f - balance --no-total | hledger -f - balance --no-total | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -231,7 +313,7 @@ hledger -f - balance --no-total | |||||||
|                  -1Y  b |                  -1Y  b | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
| # 20. the above with -B | # 26. the above with -B | ||||||
| hledger -f - balance --no-total -E -B | hledger -f - balance --no-total -E -B | ||||||
| <<< | <<< | ||||||
| 1/1 | 1/1 | ||||||
| @ -244,6 +326,23 @@ hledger -f - balance --no-total -E -B | |||||||
|                  -1Y  b |                  -1Y  b | ||||||
| >>>= 0 | >>>= 0 | ||||||
| 
 | 
 | ||||||
|  | # 27. The equity account used by --infer-equity can be customised | ||||||
|  | hledger -f- print --infer-equity | ||||||
|  | <<< | ||||||
|  | account  equity:trades   V | ||||||
|  | 
 | ||||||
|  | 2011/01/01 | ||||||
|  |     expenses:foreign currency       €100 @ $1.35 | ||||||
|  |     assets | ||||||
|  | >>> | ||||||
|  | 2011-01-01 | ||||||
|  |     expenses:foreign currency            €100  ; cost: @ $1.35 | ||||||
|  |     equity:trades:$-€:€                 €-100  ; generated-posting: | ||||||
|  |     equity:trades:$-€:$               $135.00  ; generated-posting: | ||||||
|  |     assets | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
| # # when the *cost-basis* balance has exactly two commodities, both | # # when the *cost-basis* balance has exactly two commodities, both | ||||||
| # # unpriced, infer an implicit conversion price for the first one in terms | # # unpriced, infer an implicit conversion price for the first one in terms | ||||||
| # # of the second. | # # of the second. | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user