fix: cost: Add inferred equity postings during journal finalisation, so
they can be matched by queries.
This commit is contained in:
parent
f7a3f510be
commit
57d055b643
@ -24,6 +24,7 @@ module Hledger.Data.Journal (
|
|||||||
commodityStylesFromAmounts,
|
commodityStylesFromAmounts,
|
||||||
journalCommodityStyles,
|
journalCommodityStyles,
|
||||||
journalToCost,
|
journalToCost,
|
||||||
|
journalAddInferredEquityPostings,
|
||||||
journalReverse,
|
journalReverse,
|
||||||
journalSetLastReadTime,
|
journalSetLastReadTime,
|
||||||
journalPivot,
|
journalPivot,
|
||||||
@ -881,11 +882,16 @@ 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 :: ConversionOp -> Journal -> Journal
|
journalToCost :: ConversionOp -> Journal -> Journal
|
||||||
journalToCost cost j@Journal{jtxns=ts} =
|
journalToCost cost j@Journal{jtxns=ts} = j{jtxns=map (transactionToCost styles cost) ts}
|
||||||
j{jtxns=map (transactionToCost (journalConversionAccount j) styles cost) ts}
|
|
||||||
where
|
where
|
||||||
styles = journalCommodityStyles j
|
styles = journalCommodityStyles j
|
||||||
|
|
||||||
|
-- | Add inferred equity postings to a 'Journal' using transaction prices.
|
||||||
|
journalAddInferredEquityPostings :: Journal -> Journal
|
||||||
|
journalAddInferredEquityPostings j = journalMapTransactions (transactionAddInferredEquityPostings equityAcct) j
|
||||||
|
where
|
||||||
|
equityAcct = journalConversionAccount 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
|
||||||
-- journalCanonicalCommodities j = canonicaliseCommodities $ journalAmountCommodities j
|
-- journalCanonicalCommodities j = canonicaliseCommodities $ journalAmountCommodities j
|
||||||
|
|||||||
@ -71,6 +71,7 @@ module Hledger.Data.Posting (
|
|||||||
postingTransformAmount,
|
postingTransformAmount,
|
||||||
postingApplyValuation,
|
postingApplyValuation,
|
||||||
postingToCost,
|
postingToCost,
|
||||||
|
postingAddInferredEquityPostings,
|
||||||
tests_Posting
|
tests_Posting
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
@ -481,12 +482,14 @@ 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
|
||||||
|
|
||||||
-- | Maybe convert this 'Posting's amount to cost, and apply apply appropriate
|
-- | Maybe convert this 'Posting's amount to cost, and apply apply appropriate
|
||||||
-- amount styles; or, in --infer-equity mode, remove its cost price and add an
|
-- amount styles.
|
||||||
-- appropriate pair of equity postings.
|
postingToCost :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> Posting -> Posting
|
||||||
postingToCost :: Text -> M.Map CommoditySymbol AmountStyle -> ConversionOp -> Posting -> [Posting]
|
postingToCost _ NoConversionOp p = p
|
||||||
postingToCost _ _ NoConversionOp p = [p]
|
postingToCost styles ToCost p = postingTransformAmount (styleMixedAmount styles . mixedAmountCost) p
|
||||||
postingToCost _ styles ToCost p = [postingTransformAmount (styleMixedAmount styles . mixedAmountCost) p]
|
|
||||||
postingToCost equityAcct styles InferEquity p = taggedPosting : concatMap conversionPostings priceAmounts
|
-- | Generate inferred equity postings from a 'Posting' using transaction prices.
|
||||||
|
postingAddInferredEquityPostings :: Text -> Posting -> [Posting]
|
||||||
|
postingAddInferredEquityPostings equityAcct p = taggedPosting : concatMap conversionPostings priceAmounts
|
||||||
where
|
where
|
||||||
taggedPosting
|
taggedPosting
|
||||||
| null priceAmounts = p
|
| null priceAmounts = p
|
||||||
@ -499,7 +502,7 @@ postingToCost equityAcct styles InferEquity p = taggedPosting : concatMap con
|
|||||||
, pamount = mixedAmount . negate $ amountStripPrices amt
|
, pamount = mixedAmount . negate $ amountStripPrices amt
|
||||||
}
|
}
|
||||||
, cp{ paccount = accountPrefix <> costCommodity
|
, cp{ paccount = accountPrefix <> costCommodity
|
||||||
, pamount = styleMixedAmount styles $ mixedAmount cost
|
, pamount = mixedAmount cost
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
|
|||||||
@ -27,6 +27,7 @@ module Hledger.Data.Transaction
|
|||||||
, transactionTransformPostings
|
, transactionTransformPostings
|
||||||
, transactionApplyValuation
|
, transactionApplyValuation
|
||||||
, transactionToCost
|
, transactionToCost
|
||||||
|
, transactionAddInferredEquityPostings
|
||||||
, transactionApplyAliases
|
, transactionApplyAliases
|
||||||
, transactionMapPostings
|
, transactionMapPostings
|
||||||
, transactionMapPostingAmounts
|
, transactionMapPostingAmounts
|
||||||
@ -203,10 +204,14 @@ transactionApplyValuation priceoracle styles periodlast today v =
|
|||||||
transactionTransformPostings (postingApplyValuation priceoracle styles periodlast today v)
|
transactionTransformPostings (postingApplyValuation priceoracle styles periodlast today v)
|
||||||
|
|
||||||
-- | Maybe convert this 'Transaction's amounts to cost and apply the
|
-- | Maybe convert this 'Transaction's amounts to cost and apply the
|
||||||
-- appropriate amount styles; or in --infer-equity mode, replace any
|
-- appropriate amount styles.
|
||||||
-- transaction prices by a pair of equity postings.
|
transactionToCost :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> Transaction -> Transaction
|
||||||
transactionToCost :: Text -> M.Map CommoditySymbol AmountStyle -> ConversionOp -> Transaction -> Transaction
|
transactionToCost styles cost = transactionMapPostings (postingToCost styles cost)
|
||||||
transactionToCost equityAcct styles cost t = t{tpostings=concatMap (postingToCost equityAcct styles cost) $ tpostings t}
|
|
||||||
|
-- | Add inferred equity postings to a 'Transaction' using transaction prices.
|
||||||
|
transactionAddInferredEquityPostings :: AccountName -> Transaction -> Transaction
|
||||||
|
transactionAddInferredEquityPostings equityAcct t =
|
||||||
|
t{tpostings=concatMap (postingAddInferredEquityPostings equityAcct) $ 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.
|
||||||
|
|||||||
@ -52,7 +52,7 @@ import Text.Printf (printf)
|
|||||||
-- Types
|
-- Types
|
||||||
|
|
||||||
-- | Which operation to perform on conversion transactions.
|
-- | Which operation to perform on conversion transactions.
|
||||||
data ConversionOp = NoConversionOp | InferEquity | ToCost
|
data ConversionOp = NoConversionOp | 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 ?
|
||||||
@ -111,7 +111,6 @@ mixedAmountApplyValuation 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 :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> Amount -> Amount
|
amountToCost :: M.Map CommoditySymbol AmountStyle -> ConversionOp -> Amount -> Amount
|
||||||
amountToCost styles ToCost = styleAmount styles . amountCost
|
amountToCost styles ToCost = styleAmount styles . amountCost
|
||||||
amountToCost _ InferEquity = amountStripPrices
|
|
||||||
amountToCost _ NoConversionOp = id
|
amountToCost _ NoConversionOp = id
|
||||||
|
|
||||||
-- | Apply a specified valuation to this amount, using the provided
|
-- | Apply a specified valuation to this amount, using the provided
|
||||||
|
|||||||
@ -213,6 +213,7 @@ rawOptsToInputOpts day rawopts =
|
|||||||
,forecast_ = forecastPeriodFromRawOpts day rawopts
|
,forecast_ = forecastPeriodFromRawOpts day rawopts
|
||||||
,reportspan_ = DateSpan (queryStartDate False datequery) (queryEndDate False datequery)
|
,reportspan_ = DateSpan (queryStartDate False datequery) (queryEndDate False datequery)
|
||||||
,auto_ = boolopt "auto" rawopts
|
,auto_ = boolopt "auto" rawopts
|
||||||
|
,infer_equity_ = boolopt "infer-equity" rawopts && not (conversionop_ ropts == Just ToCost)
|
||||||
,balancingopts_ = defbalancingopts{
|
,balancingopts_ = defbalancingopts{
|
||||||
ignore_assertions_ = boolopt "ignore-assertions" rawopts
|
ignore_assertions_ = boolopt "ignore-assertions" rawopts
|
||||||
, infer_transaction_prices_ = not noinferprice
|
, infer_transaction_prices_ = not noinferprice
|
||||||
@ -302,7 +303,7 @@ parseAndFinaliseJournal' parser iopts f txt = do
|
|||||||
-- - infer transaction-implied market prices from transaction prices
|
-- - infer transaction-implied market prices from transaction prices
|
||||||
--
|
--
|
||||||
journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal
|
journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal
|
||||||
journalFinalise iopts@InputOpts{auto_,balancingopts_,strict_} f txt pj = do
|
journalFinalise iopts@InputOpts{auto_,infer_equity_,balancingopts_,strict_} f txt pj = do
|
||||||
t <- liftIO getPOSIXTime
|
t <- liftIO getPOSIXTime
|
||||||
-- Infer and apply canonical styles for each commodity (or throw an error).
|
-- Infer and apply canonical styles for each commodity (or throw an error).
|
||||||
-- This affects transaction balancing/assertions/assignments, so needs to be done early.
|
-- This affects transaction balancing/assertions/assignments, so needs to be done early.
|
||||||
@ -325,6 +326,8 @@ journalFinalise iopts@InputOpts{auto_,balancingopts_,strict_} f txt pj = do
|
|||||||
& (if auto_ && not (null $ jtxnmodifiers j) then journalAddAutoPostings d balancingopts_ else pure)
|
& (if auto_ && not (null $ jtxnmodifiers j) then journalAddAutoPostings d balancingopts_ else pure)
|
||||||
-- Balance all transactions and maybe check balance assertions.
|
-- Balance all transactions and maybe check balance assertions.
|
||||||
>>= journalBalanceTransactions balancingopts_
|
>>= journalBalanceTransactions balancingopts_
|
||||||
|
-- Add inferred equity postings, after balancing transactions and generating auto postings
|
||||||
|
<&> (if infer_equity_ then journalAddInferredEquityPostings else id)
|
||||||
-- infer market prices from commodity-exchanging transactions
|
-- infer market prices from commodity-exchanging transactions
|
||||||
<&> journalInferMarketPricesFromTransactions
|
<&> journalInferMarketPricesFromTransactions
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ data InputOpts = InputOpts {
|
|||||||
,forecast_ :: Maybe DateSpan -- ^ span in which to generate forecast transactions
|
,forecast_ :: Maybe DateSpan -- ^ span in which to generate forecast transactions
|
||||||
,reportspan_ :: DateSpan -- ^ a dirty hack keeping the query dates in InputOpts. This rightfully lives in ReportSpec, but is duplicated here.
|
,reportspan_ :: DateSpan -- ^ a dirty hack keeping the query dates in InputOpts. This rightfully lives in ReportSpec, but is duplicated here.
|
||||||
,auto_ :: Bool -- ^ generate automatic postings when journal is parsed
|
,auto_ :: Bool -- ^ generate automatic postings when journal is parsed
|
||||||
|
,infer_equity_ :: Bool -- ^ generate automatic equity postings from transaction prices
|
||||||
,balancingopts_ :: BalancingOpts -- ^ options for balancing transactions
|
,balancingopts_ :: BalancingOpts -- ^ options for balancing transactions
|
||||||
,strict_ :: Bool -- ^ do extra error checking (eg, all posted accounts are declared, no prices are inferred)
|
,strict_ :: Bool -- ^ do extra error checking (eg, all posted accounts are declared, no prices are inferred)
|
||||||
,_ioDay :: Day -- ^ today's date, for use with forecast transactions XXX this duplicates _rsDay, and should eventually be removed when it's not needed anymore.
|
,_ioDay :: Day -- ^ today's date, for use with forecast transactions XXX this duplicates _rsDay, and should eventually be removed when it's not needed anymore.
|
||||||
@ -53,6 +54,7 @@ definputopts = InputOpts
|
|||||||
, forecast_ = Nothing
|
, forecast_ = Nothing
|
||||||
, reportspan_ = nulldatespan
|
, reportspan_ = nulldatespan
|
||||||
, auto_ = False
|
, auto_ = False
|
||||||
|
, infer_equity_ = False
|
||||||
, balancingopts_ = defbalancingopts
|
, balancingopts_ = defbalancingopts
|
||||||
, strict_ = False
|
, strict_ = False
|
||||||
, _ioDay = nulldate
|
, _ioDay = nulldate
|
||||||
|
|||||||
@ -515,7 +515,6 @@ valuationTypeFromRawOpts rawopts = case (balancecalcopt rawopts, directval) of
|
|||||||
conversionOpFromRawOpts :: RawOpts -> Maybe ConversionOp
|
conversionOpFromRawOpts :: RawOpts -> Maybe ConversionOp
|
||||||
conversionOpFromRawOpts rawopts
|
conversionOpFromRawOpts rawopts
|
||||||
| isJust costFlag && balancecalcopt rawopts == CalcGain = usageError "--gain cannot be combined with --cost"
|
| isJust costFlag && balancecalcopt rawopts == CalcGain = usageError "--gain cannot be combined with --cost"
|
||||||
| boolopt "infer-equity" rawopts = costFlag <|> Just InferEquity
|
|
||||||
| otherwise = costFlag
|
| otherwise = costFlag
|
||||||
where
|
where
|
||||||
costFlag = lastMay $ collectopts conversionopfromrawopt rawopts
|
costFlag = lastMay $ collectopts conversionopfromrawopt rawopts
|
||||||
@ -619,7 +618,6 @@ mixedAmountApplyValuationAfterSumFromOptsWith ropts j priceoracle =
|
|||||||
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 fromMaybe NoConversionOp $ conversionop_ ropts of
|
costing = case fromMaybe NoConversionOp $ conversionop_ ropts of
|
||||||
NoConversionOp -> id
|
NoConversionOp -> id
|
||||||
InferEquity -> mixedAmountStripPrices
|
|
||||||
ToCost -> styleMixedAmount styles . mixedAmountCost
|
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"
|
||||||
|
|||||||
@ -65,7 +65,7 @@ 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)
|
||||||
$ maybe id (transactionToCost (journalConversionAccount j) styles) (conversionop_ ropts) t
|
$ maybe id (transactionToCost styles) (conversionop_ ropts) t
|
||||||
-- (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
|
||||||
where
|
where
|
||||||
prices = journalPriceOracle (infer_prices_ ropts) j
|
prices = journalPriceOracle (infer_prices_ ropts) j
|
||||||
|
|||||||
@ -102,7 +102,6 @@ toggleConversionOp = over conversionop toggleCostMode
|
|||||||
where
|
where
|
||||||
toggleCostMode Nothing = Just ToCost
|
toggleCostMode Nothing = Just ToCost
|
||||||
toggleCostMode (Just NoConversionOp) = Just ToCost
|
toggleCostMode (Just NoConversionOp) = Just ToCost
|
||||||
toggleCostMode (Just InferEquity) = Just ToCost
|
|
||||||
toggleCostMode (Just ToCost) = Just NoConversionOp
|
toggleCostMode (Just ToCost) = Just NoConversionOp
|
||||||
|
|
||||||
-- | Toggle between showing primary amounts or default valuation.
|
-- | Toggle between showing primary amounts or default valuation.
|
||||||
|
|||||||
@ -72,7 +72,7 @@ close CliOpts{rawopts_=rawopts, reportspec_=rspec'} j = do
|
|||||||
(Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct)
|
(Nothing, Nothing) -> (T.pack defclosingacct, T.pack defopeningacct)
|
||||||
|
|
||||||
ropts = (_rsReportOpts rspec'){balanceaccum_=Historical, accountlistmode_=ALFlat}
|
ropts = (_rsReportOpts rspec'){balanceaccum_=Historical, accountlistmode_=ALFlat}
|
||||||
rspec = setDefaultConversionOp (if show_costs then NoConversionOp else InferEquity) rspec'{_rsReportOpts=ropts}
|
rspec = setDefaultConversionOp NoConversionOp rspec'{_rsReportOpts=ropts}
|
||||||
|
|
||||||
-- dates of the closing and opening transactions
|
-- dates of the closing and opening transactions
|
||||||
--
|
--
|
||||||
|
|||||||
@ -89,7 +89,7 @@ entriesReportAsText opts =
|
|||||||
| otherwise = originalTransaction
|
| otherwise = originalTransaction
|
||||||
maybeStripPrices
|
maybeStripPrices
|
||||||
-- Strip prices when inferring equity, unless the show-costs option is set
|
-- Strip prices when inferring equity, unless the show-costs option is set
|
||||||
| opts ^. conversionop == Just InferEquity && not (boolopt "show-costs" $ rawopts_ opts) =
|
| opts ^. infer_equity && not (boolopt "show-costs" $ rawopts_ opts) =
|
||||||
transactionTransformPostings postingStripPrices
|
transactionTransformPostings postingStripPrices
|
||||||
| otherwise = id
|
| otherwise = id
|
||||||
|
|
||||||
|
|||||||
@ -343,6 +343,18 @@ account equity:trades V
|
|||||||
|
|
||||||
>>>=0
|
>>>=0
|
||||||
|
|
||||||
|
# 28. Inferred equity postings are generated early enough to match filters
|
||||||
|
hledger -f- areg --infer-equity equity:conversion
|
||||||
|
<<<
|
||||||
|
2011/01/01
|
||||||
|
expenses:foreign currency €100 @ $1.35
|
||||||
|
assets
|
||||||
|
>>>
|
||||||
|
Transactions in equity:conversion and subaccounts:
|
||||||
|
2011-01-01 ex:foreign currenc.. $135.00 $135.00
|
||||||
|
€-100 €-100
|
||||||
|
>>>=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