Preserve implicit amounts and prices the way user wrote them in output of print command (#471)

* Remember original postings during infer and pivot

This includes such functions like:
- inferFromAssignment
- inferBalancingAmount
- inferBalancingPrices
- pivotPosting

* Use original postings for hledger print

- Introduce "--explicit" option for "print" command which brings back
  old behavior when every inferred number being printed.
- Make "print" by default print original postings without inferred
  amounts. But use effective account name to have effect from aliases.
- Instruct shell tests with an new expected output or to use
  --explicit option when inferred amounts are checked.

Resolves simonmichael/hledger#442
This commit is contained in:
Mykola Orliuk 2017-01-13 17:25:44 +02:00 committed by Simon Michael
parent ec890b9455
commit 015b764d00
23 changed files with 190 additions and 52 deletions

View File

@ -657,7 +657,7 @@ checkInferAndRegisterAmounts (Right oldTx) = do
where where
inferFromAssignment :: Posting -> CurrentBalancesModifier s Posting inferFromAssignment :: Posting -> CurrentBalancesModifier s Posting
inferFromAssignment p = maybe (return p) inferFromAssignment p = maybe (return p)
(fmap (\a -> p { pamount = a }) . setBalance (paccount p)) (fmap (\a -> p { pamount = a, porigin = Just $ originalPosting p }) . setBalance (paccount p))
$ pbalanceassertion p $ pbalanceassertion p
-- | Adds a posting's amonut to the posting's account balance and -- | Adds a posting's amonut to the posting's account balance and

View File

@ -15,6 +15,7 @@ module Hledger.Data.Posting (
posting, posting,
post, post,
-- * operations -- * operations
originalPosting,
postingStatus, postingStatus,
isReal, isReal,
isVirtual, isVirtual,
@ -83,12 +84,16 @@ nullposting = Posting
,ptags=[] ,ptags=[]
,pbalanceassertion=Nothing ,pbalanceassertion=Nothing
,ptransaction=Nothing ,ptransaction=Nothing
,porigin=Nothing
} }
posting = nullposting posting = nullposting
post :: AccountName -> Amount -> Posting post :: AccountName -> Amount -> Posting
post acct amt = posting {paccount=acct, pamount=Mixed [amt]} post acct amt = posting {paccount=acct, pamount=Mixed [amt]}
originalPosting :: Posting -> Posting
originalPosting p = fromMaybe p $ porigin p
-- XXX once rendered user output, but just for debugging now; clean up -- XXX once rendered user output, but just for debugging now; clean up
showPosting :: Posting -> String showPosting :: Posting -> String
showPosting p@Posting{paccount=a,pamount=amt,ptype=t} = showPosting p@Posting{paccount=a,pamount=amt,ptype=t} =

View File

@ -402,7 +402,7 @@ inferBalancingAmount update t@Transaction{tpostings=ps}
inferamount p@Posting{ptype=BalancedVirtualPosting} inferamount p@Posting{ptype=BalancedVirtualPosting}
| not (hasAmount p) = updateAmount p bvsum | not (hasAmount p) = updateAmount p bvsum
inferamount p = return p inferamount p = return p
updateAmount p amt = update (paccount p) amt' >> return p { pamount=amt' } updateAmount p amt = update (paccount p) amt' >> return p { pamount=amt', porigin=Just $ originalPosting p }
where amt' = normaliseMixedAmount $ costOfMixedAmount (-amt) where amt' = normaliseMixedAmount $ costOfMixedAmount (-amt)
-- | Infer prices for this transaction's posting amounts, if needed to make -- | Infer prices for this transaction's posting amounts, if needed to make
@ -467,7 +467,7 @@ priceInferrerFor t pt = inferprice
inferprice p@Posting{pamount=Mixed [a]} inferprice p@Posting{pamount=Mixed [a]}
| caninferprices && ptype p == pt && acommodity a == fromcommodity | caninferprices && ptype p == pt && acommodity a == fromcommodity
= p{pamount=Mixed [a{aprice=conversionprice}]} = p{pamount=Mixed [a{aprice=conversionprice}], porigin=Just $ originalPosting p}
where where
fromcommodity = head $ filter (`elem` sumcommodities) pcommodities -- these heads are ugly but should be safe fromcommodity = head $ filter (`elem` sumcommodities) pcommodities -- these heads are ugly but should be safe
conversionprice conversionprice

View File

@ -199,8 +199,9 @@ data Posting = Posting {
ptype :: PostingType, ptype :: PostingType,
ptags :: [Tag], -- ^ tag names and values, extracted from the comment ptags :: [Tag], -- ^ tag names and values, extracted from the comment
pbalanceassertion :: Maybe Amount, -- ^ optional: the expected balance in this commodity in the account after this posting pbalanceassertion :: Maybe Amount, -- ^ optional: the expected balance in this commodity in the account after this posting
ptransaction :: Maybe Transaction -- ^ this posting's parent transaction (co-recursive types). ptransaction :: Maybe Transaction, -- ^ this posting's parent transaction (co-recursive types).
-- Tying this knot gets tedious, Maybe makes it easier/optional. -- Tying this knot gets tedious, Maybe makes it easier/optional.
porigin :: Maybe Posting -- ^ original posting if this one is result of any transformations (one level only)
} deriving (Typeable,Data,Generic) } deriving (Typeable,Data,Generic)
instance NFData Posting instance NFData Posting
@ -208,7 +209,7 @@ instance NFData Posting
-- The equality test for postings ignores the parent transaction's -- The equality test for postings ignores the parent transaction's
-- identity, to avoid infinite loops. -- identity, to avoid infinite loops.
instance Eq Posting where instance Eq Posting where
(==) (Posting a1 b1 c1 d1 e1 f1 g1 h1 i1 _) (Posting a2 b2 c2 d2 e2 f2 g2 h2 i2 _) = a1==a2 && b1==b2 && c1==c2 && d1==d2 && e1==e2 && f1==f2 && g1==g2 && h1==h2 && i1==i2 (==) (Posting a1 b1 c1 d1 e1 f1 g1 h1 i1 _ _) (Posting a2 b2 c2 d2 e2 f2 g2 h2 i2 _ _) = a1==a2 && b1==b2 && c1==c2 && d1==d2 && e1==e2 && f1==f2 && g1==g2 && h1==h2 && i1==i2
-- | The position of parse errors (eg), like parsec's SourcePos but generic. -- | The position of parse errors (eg), like parsec's SourcePos but generic.
-- File name, 1-based line number and 1-based column number. -- File name, 1-based line number and 1-based column number.

View File

@ -34,7 +34,9 @@ printmode = (defCommandMode $ ["print"] ++ aliases) {
in in
flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) matcharg flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) matcharg
("show the transaction whose description is most similar to "++matcharg ("show the transaction whose description is most similar to "++matcharg
++ ", and is most recent") ++ ", and is most recent"),
flagNone ["explicit","x"] (setboolopt "explicit")
"make output more explicit than original transactions"
] ]
++ outputflags ++ outputflags
,groupHidden = [] ,groupHidden = []
@ -43,6 +45,17 @@ printmode = (defCommandMode $ ["print"] ++ aliases) {
} }
where aliases = [] where aliases = []
showTransaction' :: CliOpts -> Transaction -> String
showTransaction' opts
| boolopt "explicit" $ rawopts_ opts = showTransactionUnelided
| otherwise = showTransactionUnelided . originalTransaction
originalTransaction :: Transaction -> Transaction
originalTransaction t = t { tpostings = map originalPosting' $ tpostings t } where
-- We don't want plain original postings because print wouldn't issue alias
-- directives. Thus we are going to print effective account name.
originalPosting' p = (originalPosting p) { paccount = paccount p }
-- | Print journal transactions in standard format. -- | Print journal transactions in standard format.
print' :: CliOpts -> Journal -> IO () print' :: CliOpts -> Journal -> IO ()
print' opts j = do print' opts j = do
@ -57,12 +70,15 @@ printEntries opts@CliOpts{reportopts_=ropts} j = do
fmt = outputFormatFromOpts opts fmt = outputFormatFromOpts opts
(render, ropts') = case fmt of (render, ropts') = case fmt of
"csv" -> ((++"\n") . printCSV . entriesReportAsCsv, ropts{accountlistmode_=ALFlat}) "csv" -> ((++"\n") . printCSV . entriesReportAsCsv, ropts{accountlistmode_=ALFlat})
_ -> (entriesReportAsText, ropts) _ -> (entriesReportAsText' opts, ropts)
writeOutput opts $ render $ entriesReport ropts' q j writeOutput opts $ render $ entriesReport ropts' q j
entriesReportAsText :: EntriesReport -> String entriesReportAsText :: EntriesReport -> String
entriesReportAsText items = concatMap showTransactionUnelided items entriesReportAsText items = concatMap showTransactionUnelided items
entriesReportAsText' :: CliOpts -> EntriesReport -> String
entriesReportAsText' = concatMap . showTransaction'
-- XXX -- XXX
-- tests_showTransactions = [ -- tests_showTransactions = [
-- "showTransactions" ~: do -- "showTransactions" ~: do

View File

@ -91,7 +91,7 @@ pivot tag j = j{jtxns = map pivotTrans . jtxns $ j}
where where
pivotTrans t = t{tpostings = map pivotPosting . tpostings $ t} pivotTrans t = t{tpostings = map pivotPosting . tpostings $ t}
pivotPosting p pivotPosting p
| Just (_ , value) <- tagTuple = p{paccount = joinAccountNames tag value} | Just (_ , value) <- tagTuple = p{paccount = joinAccountNames tag value, porigin = Just $ originalPosting p}
| _ <- tagTuple = p | _ <- tagTuple = p
where tagTuple = find ((tag ==) . fst) . ptags $ p where tagTuple = find ((tag ==) . fst) . ptags $ p

View File

@ -13,13 +13,14 @@ runghc ../../bin/hledger-rewrite.hs -f- ^income --add-posting '(liabilities:tax)
>>> >>>
2016/01/01 paycheck 2016/01/01 paycheck
income:remuneration $-100 income:remuneration $-100
assets:bank $100 assets:bank
(liabilities:tax) $-33 (liabilities:tax) $-33
2016/01/01 withdraw 2016/01/01 withdraw
assets:cash $20 assets:cash $20
assets:bank $-20 assets:bank
>>>2
>>>=0 >>>=0
# Duplicate posting for budgeting (from documentation) # Duplicate posting for budgeting (from documentation)
@ -35,13 +36,14 @@ runghc ../../bin/hledger-rewrite.hs -f- expenses:gifts --add-posting '(budget:gi
>>> >>>
2016/01/01 withdraw 2016/01/01 withdraw
assets:cash $20 assets:cash $20
assets:bank $-20 assets:bank
2016/01/01 gift 2016/01/01 gift
assets:cash $-15 assets:cash $-15
expenses:gifts $15 expenses:gifts
(budget:gifts) $-15 (budget:gifts) $-15
>>>2
>>>=0 >>>=0
# Add absolute bank processing fee # Add absolute bank processing fee
@ -62,14 +64,15 @@ runghc ../../bin/hledger-rewrite.hs -f- assets:bank and 'amt:<0' --add-posting '
>>> >>>
2016/01/01 withdraw 2016/01/01 withdraw
assets:cash $20 assets:cash $20
assets:bank $-20 assets:bank
expenses:fee $5 expenses:fee $5
assets:bank $-5 assets:bank $-5
2016/01/02 withdraw 2016/01/02 withdraw
assets:cash $30 assets:cash
assets:bank $-30 assets:bank $-30
expenses:fee $5 expenses:fee $5
assets:bank $-5 assets:bank $-5
>>>2
>>>=0 >>>=0

View File

@ -25,15 +25,15 @@ hledger print -f personal.journal -f business.journal -f alias.journal -f person
>>> >>>
2014/01/01 2014/01/01
expenses:office supplies $1 expenses:office supplies $1
assets:business checking $-1 assets:business checking
2014/01/02 2014/01/02
expenses:food $1 expenses:food $1
assets:cash $-1 assets:cash
2014/01/02 2014/01/02
expenses:food $1 expenses:food $1
assets:cash $-1 assets:cash
>>>2 >>>2
>>>=0 >>>=0
@ -47,7 +47,7 @@ hledger print -f personal.journal -f ../journal/a.timeclock -f ../journal/b.time
>>> >>>
2014/01/02 2014/01/02
expenses:food $1 expenses:food $1
assets:cash $-1 assets:cash
2016/01/01 * 12:00-16:00 2016/01/01 * 12:00-16:00
(a:aa) 4.00h (a:aa) 4.00h

View File

@ -6,6 +6,6 @@ hledger -f - print
>>> >>>
2009/01/01 проверка 2009/01/01 проверка
счёт:первый 1 счёт:первый 1
счёт:второй -1 счёт:второй
>>>=0 >>>=0

View File

@ -21,11 +21,11 @@ hledger -f - print
>>> >>>
2014/01/01 transaction 1 2014/01/01 transaction 1
㐀 㐃㐃1 @ 2 㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂 㐀 㐃㐃1 @ 2 㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂
㐀:㐁 -2 㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂㐂 ; 㐃㐃-1 㐀:㐁 ; 㐃㐃-1
2014/01/02 transaction 2 2014/01/02 transaction 2
㐀:㐁:㐂 USD 1 @@ EUR 1 㐀:㐁:㐂 USD 1
㐀:㐁:㐂:㐃 EUR -1 㐀:㐁:㐂:㐃 EUR -1
2014/01/03 transaction 3 2014/01/03 transaction 3
㐀:㐁:㐂:㐃:㐄 1 㐀:㐁:㐂:㐃:㐄 1

View File

@ -13,7 +13,7 @@ hledger -f - print
; transaction comment 1 ; transaction comment 1
; transaction comment 2 ; transaction comment 2
a 1 a 1
b -1 b
>>>=0 >>>=0
@ -27,7 +27,7 @@ hledger -f - print
>>> >>>
2009/01/01 x 2009/01/01 x
a 1 a 1
b -1 b
>>>=0 >>>=0
@ -51,7 +51,7 @@ hledger -f - print
; transaction new line comment ; transaction new line comment
a 1 ; posting 1 same line comment a 1 ; posting 1 same line comment
; posting 1 new line comment ; posting 1 new line comment
b -1 b
; posting 2 new line comment ; posting 2 new line comment
>>>2 >>>2

View File

@ -8,7 +8,7 @@ hledger -f- print
>>>2 /unexpected/ >>>2 /unexpected/
>>>= 1 >>>= 1
# 2. with quotes, ok; quotes appear in print output # 2. with quotes, ok; quotes appear in print output
hledger -f- print hledger -f- print --explicit
<<< <<<
2010-04-05 x 2010-04-05 x
a 10 "DE 0002 635307" a 10 "DE 0002 635307"
@ -34,7 +34,7 @@ hledger -f- balance
>>>=0 >>>=0
# 4. autobalance with prices # 4. autobalance with prices
hledger -f- print hledger -f- print --explicit
<<< <<<
2016/1/1 2016/1/1
saving-card $-105 saving-card $-105

View File

@ -24,7 +24,7 @@ hledger -f- print
>>> >>>
2000/02/29 x 2000/02/29 x
a 1 a 1
b -1 b
>>>= 0 >>>= 0
# 4. 29th feb on non-leap year should fail # 4. 29th feb on non-leap year should fail

View File

@ -12,7 +12,7 @@ hledger -f- print
>>> >>>
2010/01/01 2010/01/01
a 1000 a 1000
b -1000 b
>>>=0 >>>=0
@ -26,7 +26,7 @@ D £1000.00
>>> >>>
2010/01/01 2010/01/01
a £1000.00 a £1000.00
b £-1000.00 b
>>>=0 >>>=0
@ -40,7 +40,7 @@ D $1,000
>>> >>>
2010/01/01 2010/01/01
a $1000,000 a $1000,000
b $-1000,000 b
>>>=0 >>>=0

View File

@ -8,6 +8,6 @@ hledger -f - print
2009/01/01 x 2009/01/01 x
a 2 a 2
b (b) b -1 b (b) b -1
c -1 c
>>>=0 >>>=0

View File

@ -32,7 +32,7 @@ hledger -f - print
# 2. and here the price should be printed with its original precision, not # 2. and here the price should be printed with its original precision, not
# the canonical display precision # the canonical display precision
hledger -f - print hledger -f - print --explicit
<<< <<<
2010/1/1 2010/1/1
a $0.00 a $0.00
@ -130,7 +130,7 @@ D $1000.0
# the max precisions of the commodities being converted (#262). # the max precisions of the commodities being converted (#262).
# Here the (irrational) price should be displayed with just precision 4 # Here the (irrational) price should be displayed with just precision 4
# (C's precision 2 + D's precision 2). # (C's precision 2 + D's precision 2).
hledger -f- print hledger -f- print --explicit
<<< <<<
2015/1/1 2015/1/1
c C 10.00 c C 10.00
@ -147,7 +147,7 @@ hledger -f- print
## 8. Here the price should be displayed with precision 7 ## 8. Here the price should be displayed with precision 7
# (E's precision 4 + F's precision 3). # (E's precision 4 + F's precision 3).
hledger -f- print hledger -f- print --explicit
<<< <<<
2015/1/1 2015/1/1
e E 10.0000 e E 10.0000

View File

@ -1,6 +1,6 @@
# price-related tests # price-related tests
# 1. print a transaction with an explicit unit price # 1. print a transaction with an explicit unit price
hledger -f- print hledger -f- print --explicit
<<< <<<
2011/01/01 2011/01/01
expenses:foreign currency €100 @ $1.35 expenses:foreign currency €100 @ $1.35
@ -13,7 +13,7 @@ hledger -f- print
>>>=0 >>>=0
# 2. -B/--cost converts to the price's commodity ("cost") # 2. -B/--cost converts to the price's commodity ("cost")
hledger -f- print --cost hledger -f- print --explicit --cost
<<< <<<
2011/01/01 2011/01/01
expenses:foreign currency €100 @ $1.35 expenses:foreign currency €100 @ $1.35
@ -26,7 +26,7 @@ hledger -f- print --cost
>>>=0 >>>=0
# 3. print a transaction with a total price # 3. print a transaction with a total price
hledger -f - print hledger -f - print --explicit
<<< <<<
2011/01/01 2011/01/01
expenses:foreign currency €100 @@ $135 expenses:foreign currency €100 @@ $135
@ -40,7 +40,7 @@ hledger -f - print
# 4. when the balance has exactly two commodities, both unpriced, infer an # 4. 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 hledger -f - print --explicit
<<< <<<
2011/01/01 2011/01/01
expenses:foreign currency €100 expenses:foreign currency €100
@ -61,7 +61,7 @@ hledger -f - print
>>>=0 >>>=0
## 5. another, from ledger tests. Just one posting to price so uses @@. ## 5. another, from ledger tests. Just one posting to price so uses @@.
hledger -f - print hledger -f - print --explicit
<<< <<<
2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be
c56a21d23a6535184e7152ee138c28974f14280c 866.231000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c 866.231000 GGGGG

View File

@ -73,12 +73,12 @@ alias /A (.)/=\1
2011/01/01 2011/01/01
b b 1 b b 1
b b 2 b b 2
c -3 c
2011/01/01 2011/01/01
b 1 b 1
b 2 b 2
c -3 c
>>>=0 >>>=0
@ -98,7 +98,7 @@ hledger -f- print --alias '/A (.)/=a' --alias /a/=b
2011/01/01 2011/01/01
b 1 b 1
b 2 b 2
c -3 c
>>>=0 >>>=0
@ -117,7 +117,7 @@ alias E=F
>>> >>>
2011/01/01 2011/01/01
[E:x] 1 [E:x] 1
[x:A:x] -1 [x:A:x]
>>>2 >>>2
>>>=0 >>>=0

View File

@ -1,6 +1,6 @@
# amount layout tests, using default vertical layout # amount layout tests, using default vertical layout
# 1. print # 1. print
hledger -f - print hledger -f - print --explicit
<<< <<<
2010/1/1 2010/1/1
a EUR 1 ; a euro a EUR 1 ; a euro
@ -47,8 +47,7 @@ hledger -f - balance
>>>=0 >>>=0
# 4. a single-commodity zero amount's commodity/decimal places/price is preserved, when possible # 4. a single-commodity zero amount's commodity/decimal places/price is preserved, when possible
# hledger -f- print --explicit --empty
hledger -f- print --empty
<<< <<<
2010/3/1 x 2010/3/1 x
a $0.00 @ 3EUR a $0.00 @ 3EUR

View File

@ -17,11 +17,11 @@ hledger -f- print --cleared
>>> >>>
2010/01/02 * x 2010/01/02 * x
a 1 a 1
b -1 b
2010/01/03 * 2010/01/03 *
a 1 a 1
b -1 b
>>>=0 >>>=0
@ -42,7 +42,7 @@ hledger -f- print --uncleared
>>> >>>
2010/01/01 x 2010/01/01 x
a 1 a 1
b -1 b
>>>=0 >>>=0

114
tests/print/explicit.test Normal file
View File

@ -0,0 +1,114 @@
# Tests of --explicit option effect
# 1. implicit transaction balance w/o --explict
hledger -f - print
<<<
2017/1/1
expenses $5
assets
>>>
2017/01/01
expenses $5
assets
>>>2
>>>=0
# 2. implicit transaction balance w/ --explict
hledger -f - print --explicit
<<<
2017/1/1
expenses $5
assets
>>>
2017/01/01
expenses $5
assets $-5
>>>2
>>>=0
# 3. implicit commodity price w/o --explict
hledger -f - print
<<<
2017/1/1
expenses 4 EUR
assets $-5
>>>
2017/01/01
expenses 4 EUR
assets $-5
>>>2
>>>=0
# 4. implicit commodity price w/ --explict
hledger -f - print --explicit
<<<
2017/1/1
expenses 4 EUR
assets $-5
>>>
2017/01/01
expenses 4 EUR @@ $5
assets $-5
>>>2
>>>=0
# 5. implicit account balance w/o --explict
hledger -f - print
<<<
2017/1/1
assets = $100
equity
>>>
2017/01/01
assets = $100
equity
>>>2
>>>=0
# 6. implicit account balance w/ --explict
hledger -f - print --explicit
<<<
2017/1/1
assets = $100
equity
>>>
2017/01/01
assets $100 = $100
equity $-100
>>>2
>>>=0
# 7. default commodity always applied because print do not issue appropriate directive
hledger -f - print
<<<
D 1000.00 EUR
2017/1/1
expenses 100
assets
>>>
2017/01/01
expenses 100.00 EUR
assets
>>>2
>>>=0
# 8. option --explicit implies effect of --empty
hledger -f - print --explicit
<<<
2017/1/1
assets $0
equity
>>>
2017/01/01
assets 0
equity 0
>>>2
>>>=0

View File

@ -6,6 +6,6 @@ hledger -f - print
>>> >>>
2009/01/01 x 2009/01/01 x
aaaaabbbbbcccccdddddeeeeefffffggggghhhhh 1 aaaaabbbbbcccccdddddeeeeefffffggggghhhhh 1
b -1 b
>>>=0 >>>=0

View File

@ -11,6 +11,6 @@ hledger -f - print desc:x
>>> >>>
2009/01/01 x 2009/01/01 x
a 1 a 1
b -1 b
>>>=0 >>>=0