basic querying by amount: "amt:<0", "amt:=100", etc.
The syntax is "amt:ON", where O is "<", "=" or ">" and N is a number. For simple (single-commodity) amounts, this matches if the amount's quantity has the specified relationship to N. For multi-commodity amounts, it always matches. If parsing fails, an error is raised. This has not been tested for floating-point precision.
This commit is contained in:
parent
1adc583975
commit
c39e424642
@ -43,6 +43,7 @@ import Text.ParserCombinators.Parsec
|
||||
import Hledger.Utils
|
||||
import Hledger.Data.Types
|
||||
import Hledger.Data.AccountName
|
||||
import Hledger.Data.Amount (nullamt)
|
||||
import Hledger.Data.Dates
|
||||
import Hledger.Data.Posting
|
||||
import Hledger.Data.Transaction
|
||||
@ -61,6 +62,7 @@ data Query = Any -- ^ always match
|
||||
| Date2 DateSpan -- ^ match if secondary date in this date span
|
||||
| Status Bool -- ^ match if cleared status has this value
|
||||
| Real Bool -- ^ match if "realness" (involves a real non-virtual account ?) has this value
|
||||
| Amt Ordering Quantity -- ^ match if the amount's numeric quantity is less than/greater than/equal to some value
|
||||
| Empty Bool -- ^ if true, show zero-amount postings/accounts which are usually not shown
|
||||
-- more of a query option than a query criteria ?
|
||||
| Depth Int -- ^ match if account depth is less than or equal to this value
|
||||
@ -211,6 +213,7 @@ parseQueryTerm d ('e':'d':'a':'t':'e':':':s) =
|
||||
Right (_,span) -> Left $ Date2 span
|
||||
parseQueryTerm _ ('s':'t':'a':'t':'u':'s':':':s) = Left $ Status $ parseStatus s
|
||||
parseQueryTerm _ ('r':'e':'a':'l':':':s) = Left $ Real $ parseBool s
|
||||
parseQueryTerm _ ('a':'m':'t':':':s) = Left $ Amt op q where (op, q) = parseAmountTest s
|
||||
parseQueryTerm _ ('e':'m':'p':'t':'y':':':s) = Left $ Empty $ parseBool s
|
||||
parseQueryTerm _ ('d':'e':'p':'t':'h':':':s) = Left $ Depth $ readDef 0 s
|
||||
parseQueryTerm _ ('t':'a':'g':':':s) = Left $ Tag n v where (n,v) = parseTag s
|
||||
@ -232,6 +235,28 @@ tests_parseQueryTerm = [
|
||||
"inacct:a" `gives` (Right $ QueryOptInAcct "a")
|
||||
"tag:a" `gives` (Left $ Tag "a" Nothing)
|
||||
"tag:a=some value" `gives` (Left $ Tag "a" (Just "some value"))
|
||||
-- "amt:<0" `gives` (Left $ Amt LT 0)
|
||||
-- "amt:=.23" `gives` (Left $ Amt EQ 0.23)
|
||||
-- "amt:>10000.10" `gives` (Left $ Amt GT 10000.1)
|
||||
]
|
||||
|
||||
-- can fail
|
||||
parseAmountTest :: String -> (Ordering, Quantity)
|
||||
parseAmountTest s =
|
||||
case s of
|
||||
"" -> err
|
||||
'<':s' -> (LT, readDef err s')
|
||||
'=':s' -> (EQ, readDef err s')
|
||||
'>':s' -> (GT, readDef err s')
|
||||
_ -> err
|
||||
where err = error' $ "could not parse as operator followed by numeric quantity: "++s
|
||||
|
||||
tests_parseAmountTest = [
|
||||
"parseAmountTest" ~: do
|
||||
let s `gives` r = parseAmountTest s `is` r
|
||||
"<0" `gives` (LT,0)
|
||||
"=0.23" `gives` (EQ,0.23)
|
||||
">10000.10" `gives` (GT,10000.1)
|
||||
]
|
||||
|
||||
parseTag :: String -> (String, Maybe String)
|
||||
@ -475,6 +500,7 @@ matchesPosting (Date2 span) p = span `spanContainsDate` postingDate2 p
|
||||
matchesPosting (Status v) p = v == postingCleared p
|
||||
matchesPosting (Real v) p = v == isReal p
|
||||
matchesPosting (Depth d) Posting{paccount=a} = Depth d `matchesAccount` a
|
||||
matchesPosting (Amt op n) Posting{pamount=a} = compareMixedAmount op n a
|
||||
-- matchesPosting (Empty v) Posting{pamount=a} = v == isZeroMixedAmount a
|
||||
-- matchesPosting (Empty False) Posting{pamount=a} = True
|
||||
-- matchesPosting (Empty True) Posting{pamount=a} = isZeroMixedAmount a
|
||||
@ -483,6 +509,14 @@ matchesPosting (Tag n Nothing) p = isJust $ lookupTagByName n $ postingAllTags p
|
||||
matchesPosting (Tag n (Just v)) p = isJust $ lookupTagByNameAndValue (n,v) $ postingAllTags p
|
||||
-- matchesPosting _ _ = False
|
||||
|
||||
-- | Is this simple mixed amount's quantity less than, equal to, or greater than this number ?
|
||||
-- For complext mixed amounts (with multiple commodities), this is always true.
|
||||
compareMixedAmount :: Ordering -> Quantity -> MixedAmount -> Bool
|
||||
compareMixedAmount op q (Mixed []) = compareMixedAmount op q (Mixed [nullamt])
|
||||
-- compareMixedAmount op q (Mixed [a]) = strace (compare (strace $ aquantity a) (strace q)) == op
|
||||
compareMixedAmount op q (Mixed [a]) = compare (aquantity a) q == op
|
||||
compareMixedAmount _ _ _ = True
|
||||
|
||||
tests_matchesPosting = [
|
||||
"matchesPosting" ~: do
|
||||
-- matching posting status..
|
||||
@ -524,6 +558,7 @@ matchesTransaction (Date span) t = spanContainsDate span $ tdate t
|
||||
matchesTransaction (Date2 span) t = spanContainsDate span $ transactionDate2 t
|
||||
matchesTransaction (Status v) t = v == tstatus t
|
||||
matchesTransaction (Real v) t = v == hasRealPostings t
|
||||
matchesTransaction q@(Amt _ _) t = any (q `matchesPosting`) $ tpostings t
|
||||
matchesTransaction (Empty _) _ = True
|
||||
matchesTransaction (Depth d) t = any (Depth d `matchesPosting`) $ tpostings t
|
||||
matchesTransaction (Tag n Nothing) t = isJust $ lookupTagByName n $ transactionAllTags t
|
||||
@ -563,6 +598,7 @@ tests_Hledger_Query = TestList $
|
||||
++ tests_words''
|
||||
++ tests_filterQuery
|
||||
++ tests_parseQueryTerm
|
||||
++ tests_parseAmountTest
|
||||
++ tests_parseQuery
|
||||
++ tests_matchesAccount
|
||||
++ tests_matchesPosting
|
||||
|
||||
Loading…
Reference in New Issue
Block a user