From 76867c98a38bea4117a9e5bc3b98ea3a7326054c Mon Sep 17 00:00:00 2001 From: Mykola Orliuk Date: Fri, 13 Jan 2017 18:02:11 +0200 Subject: [PATCH] Feature/pivot implicit tags (#460) * Add implicit tags code/desc/payee for --pivot Additionally allow using of transaction inherited tags. * Use original posting in query by account name To be able to query on individual postings and by account name it is useful to have access to original account name (before pivot). Especially this is useful when all postings within transaction gets the same pivot name due. As a side effect we'll match by alias. Note: to query on amt it usually expected to see matches with inferred amounts. --- hledger-lib/Hledger/Data/Posting.hs | 13 +++++ hledger-lib/Hledger/Query.hs | 5 +- hledger/Hledger/Cli/Utils.hs | 2 +- tests/journal/tags.test | 39 ++++++++++++++ tests/misc/account-aliases.test | 13 +++++ tests/misc/pivot.test | 84 +++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 2 deletions(-) diff --git a/hledger-lib/Hledger/Data/Posting.hs b/hledger-lib/Hledger/Data/Posting.hs index d320c0c64..3f045a4e3 100644 --- a/hledger-lib/Hledger/Data/Posting.hs +++ b/hledger-lib/Hledger/Data/Posting.hs @@ -25,6 +25,7 @@ module Hledger.Data.Posting ( hasAmount, postingAllTags, transactionAllTags, + postingAllImplicitTags, relatedPostings, removePrices, -- * date operations @@ -168,6 +169,18 @@ postingStatus Posting{pstatus=s, ptransaction=mt} Nothing -> Uncleared | otherwise = s +-- | Implicit tags for this transaction. +transactionImplicitTags :: Transaction -> [Tag] +transactionImplicitTags t = filter (not . T.null . snd) [("code", tcode t) + ,("desc", tdescription t) + ,("payee", tdescription t) + ] + +-- | Tags for this posting including implicit and any inherited from its parent transaction. +postingAllImplicitTags :: Posting -> [Tag] +postingAllImplicitTags p = ptags p ++ maybe [] transactionTags (ptransaction p) + where transactionTags t = ttags t ++ transactionImplicitTags t + -- | Tags for this posting including any inherited from its parent transaction. postingAllTags :: Posting -> [Tag] postingAllTags p = ptags p ++ maybe [] ttags (ptransaction p) diff --git a/hledger-lib/Hledger/Query.hs b/hledger-lib/Hledger/Query.hs index b35a65cb1..9437b68de 100644 --- a/hledger-lib/Hledger/Query.hs +++ b/hledger-lib/Hledger/Query.hs @@ -659,6 +659,8 @@ compareAmount ord q Amount{aquantity=aq} = case ord of Lt -> aq < q AbsEq -> abs aq == abs q -- | Does the match expression match this posting ? +-- +-- Note that for account match we try both original and effective account matchesPosting :: Query -> Posting -> Bool matchesPosting (Not q) p = not $ q `matchesPosting` p matchesPosting (Any) _ = True @@ -667,7 +669,8 @@ matchesPosting (Or qs) p = any (`matchesPosting` p) qs matchesPosting (And qs) p = all (`matchesPosting` p) qs matchesPosting (Code r) p = regexMatchesCI r $ maybe "" (T.unpack . tcode) $ ptransaction p matchesPosting (Desc r) p = regexMatchesCI r $ maybe "" (T.unpack . tdescription) $ ptransaction p -matchesPosting (Acct r) p = regexMatchesCI r $ T.unpack $ paccount p -- XXX pack +matchesPosting (Acct r) p = matchesPosting p || matchesPosting (originalPosting p) + where matchesPosting p = regexMatchesCI r $ T.unpack $ paccount p -- XXX pack matchesPosting (Date span) p = span `spanContainsDate` postingDate p matchesPosting (Date2 span) p = span `spanContainsDate` postingDate2 p matchesPosting (Status Uncleared) p = postingStatus p /= Cleared diff --git a/hledger/Hledger/Cli/Utils.hs b/hledger/Hledger/Cli/Utils.hs index b61b692cc..aebd8fbfd 100644 --- a/hledger/Hledger/Cli/Utils.hs +++ b/hledger/Hledger/Cli/Utils.hs @@ -93,7 +93,7 @@ pivot tag j = j{jtxns = map pivotTrans . jtxns $ j} pivotPosting p | Just (_ , value) <- tagTuple = p{paccount = joinAccountNames tag value, porigin = Just $ originalPosting p} | _ <- tagTuple = p - where tagTuple = find ((tag ==) . fst) . ptags $ p + where tagTuple = find ((tag ==) . fst) . postingAllImplicitTags $ p -- | Apply the anonymisation transformation on a journal, if option is present anonymiseByOpts :: CliOpts -> Journal -> Journal diff --git a/tests/journal/tags.test b/tests/journal/tags.test index b616da23a..fc940d4bb 100644 --- a/tests/journal/tags.test +++ b/tests/journal/tags.test @@ -93,3 +93,42 @@ hledger -f - register tag:foo=bar >>>2 >>>=0 +# 5. look for transactions without tags +hledger -f - print not:tag:. +<<< +2010/01/01 desc + a 1 + b -1 + +2010/01/02 + ; foo:some tag + c 2 + d -2 + +2010/01/03 + e 3 + f -3 + +2010/01/04 (code) + g 4 + h -4 +>>> +2010/01/01 desc + a 1 + b -1 + +2010/01/03 + e 3 + f -3 + +2010/01/04 (code) + g 4 + h -4 + +>>>=0 + +# 6. query is not affected by implicit tags +hledger -f ../../examples/sample.journal reg tag:d +>>> +>>>2 +>>>=0 diff --git a/tests/misc/account-aliases.test b/tests/misc/account-aliases.test index 5d17d4076..2fb61a530 100644 --- a/tests/misc/account-aliases.test +++ b/tests/misc/account-aliases.test @@ -121,3 +121,16 @@ alias E=F >>>2 >>>=0 + +# query will search both origin and substitution in alias +hledger -f- reg '^a$' '^b$' +<<< +alias a = b +1/1 + a 1 + b -1 +>>> +2017/01/01 b 1 1 + b -1 0 +>>>2 +>>>=0 diff --git a/tests/misc/pivot.test b/tests/misc/pivot.test index a5b638ff5..9c4c2ab26 100644 --- a/tests/misc/pivot.test +++ b/tests/misc/pivot.test @@ -38,3 +38,87 @@ hledger -f- --pivot budget bal --no-total 2 EUR assets:bank account -2 EUR budget:Freifunk >>>=0 + +# query on account will find either effective name or original one +hledger -f- --pivot TAG reg '^Account2$' '^TAG:fun$' not:hidden +<<< +2016/02/16 Test Transaction + Account1 2 EUR + ; TAG: fun + Account2 -1 EUR + ; TAG: value + Account2 -1 EUR + ; TAG: hidden +>>> +2016/02/16 Test Transaction TAG:fun 2 EUR 2 EUR + TAG:value -1 EUR 1 EUR +>>>=0 + +# pivot for implicit tag desc (technical sample) +hledger -f- --pivot desc reg -M +<<< +2016/02/16 Freifunk + assets:bank account 2 EUR ; date:03/01 + income:donations -2 EUR +>>> +2016/02 desc:Freifunk -2 EUR -2 EUR +2016/03 desc:Freifunk 2 EUR 0 +>>>=0 + +# pivot for implicit tag code (technical sample) +hledger -f- --pivot code reg -M +<<< +2016/02/16 (Freifunk) Donation Freifunk + assets:bank account 2 EUR ; date:03/01 + income:donations -2 EUR +>>> +2016/02 code:Freifunk -2 EUR -2 EUR +2016/03 code:Freifunk 2 EUR 0 +>>>=0 + +# use of pivot with code-based budgeting +hledger -f- --pivot code reg ^income +<<< +2016/02/16 (Freifunk) Donation Freifunk + assets:bank account 2 EUR + income:donations -2 EUR +>>> +2016/02/16 Donation Freifunk code:Freifunk -2 EUR -2 EUR +>>>=0 + +# get expenses balance by description/payee +hledger -f- --pivot desc bal --no-total --flat ^expense +<<< +2016/02/16 (1138) Auchan + assets:bank account + expense:snacks 2 EUR + expense:grocery 20 EUR + +2016/02/16 StarBars + assets:bank account + expense:coffee 5 EUR +>>> + 22 EUR desc:Auchan + 5 EUR desc:StarBars +>>>=0 + +# get daily expenses by description/payee +hledger -f- --pivot desc reg -D ^expense +<<< +2016/02/16 (1138) Auchan + assets:bank account + expense:snacks 2 EUR + expense:grocery 20 EUR + +2016/02/16 StarBars + assets:bank account + expense:coffee 5 EUR + +2016/02/17 (1139) Auchan + assets:bank account + expense:grocery 30 EUR +>>> +2016/02/16 desc:Auchan 22 EUR 22 EUR + desc:StarBars 5 EUR 27 EUR +2016/02/17 desc:Auchan 30 EUR 57 EUR +>>>=0