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
inferFromAssignment :: Posting -> CurrentBalancesModifier s Posting
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
-- | Adds a posting's amonut to the posting's account balance and

View File

@ -15,6 +15,7 @@ module Hledger.Data.Posting (
posting,
post,
-- * operations
originalPosting,
postingStatus,
isReal,
isVirtual,
@ -83,12 +84,16 @@ nullposting = Posting
,ptags=[]
,pbalanceassertion=Nothing
,ptransaction=Nothing
,porigin=Nothing
}
posting = nullposting
post :: AccountName -> Amount -> Posting
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
showPosting :: Posting -> String
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}
| not (hasAmount p) = updateAmount p bvsum
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)
-- | 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]}
| caninferprices && ptype p == pt && acommodity a == fromcommodity
= p{pamount=Mixed [a{aprice=conversionprice}]}
= p{pamount=Mixed [a{aprice=conversionprice}], porigin=Just $ originalPosting p}
where
fromcommodity = head $ filter (`elem` sumcommodities) pcommodities -- these heads are ugly but should be safe
conversionprice

View File

@ -199,8 +199,9 @@ data Posting = Posting {
ptype :: PostingType,
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
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.
porigin :: Maybe Posting -- ^ original posting if this one is result of any transformations (one level only)
} deriving (Typeable,Data,Generic)
instance NFData Posting
@ -208,7 +209,7 @@ instance NFData Posting
-- The equality test for postings ignores the parent transaction's
-- identity, to avoid infinite loops.
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.
-- File name, 1-based line number and 1-based column number.

View File

@ -34,7 +34,9 @@ printmode = (defCommandMode $ ["print"] ++ aliases) {
in
flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) 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
,groupHidden = []
@ -43,6 +45,17 @@ printmode = (defCommandMode $ ["print"] ++ 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' :: CliOpts -> Journal -> IO ()
print' opts j = do
@ -57,12 +70,15 @@ printEntries opts@CliOpts{reportopts_=ropts} j = do
fmt = outputFormatFromOpts opts
(render, ropts') = case fmt of
"csv" -> ((++"\n") . printCSV . entriesReportAsCsv, ropts{accountlistmode_=ALFlat})
_ -> (entriesReportAsText, ropts)
_ -> (entriesReportAsText' opts, ropts)
writeOutput opts $ render $ entriesReport ropts' q j
entriesReportAsText :: EntriesReport -> String
entriesReportAsText items = concatMap showTransactionUnelided items
entriesReportAsText' :: CliOpts -> EntriesReport -> String
entriesReportAsText' = concatMap . showTransaction'
-- XXX
-- tests_showTransactions = [
-- "showTransactions" ~: do

View File

@ -91,7 +91,7 @@ pivot tag j = j{jtxns = map pivotTrans . jtxns $ j}
where
pivotTrans t = t{tpostings = map pivotPosting . tpostings $ t}
pivotPosting p
| Just (_ , value) <- tagTuple = p{paccount = joinAccountNames tag value}
| Just (_ , value) <- tagTuple = p{paccount = joinAccountNames tag value, porigin = Just $ originalPosting p}
| _ <- tagTuple = 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
income:remuneration $-100
assets:bank $100
assets:bank
(liabilities:tax) $-33
2016/01/01 withdraw
assets:cash $20
assets:bank $-20
assets:bank
>>>2
>>>=0
# 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
assets:cash $20
assets:bank $-20
assets:bank
2016/01/01 gift
assets:cash $-15
expenses:gifts $15
expenses:gifts
(budget:gifts) $-15
>>>2
>>>=0
# 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
assets:cash $20
assets:bank $-20
assets:bank
expenses:fee $5
assets:bank $-5
2016/01/02 withdraw
assets:cash $30
assets:cash
assets:bank $-30
expenses:fee $5
assets:bank $-5
>>>2
>>>=0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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