diff --git a/MANUAL.md b/MANUAL.md index 69d302db6..73d0d65bc 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -953,6 +953,7 @@ A query term can be any of the following: - `REGEX` - match account names by this regular expression - `acct:REGEX` - same as above - `code:REGEX` - match by transaction code (eg check number) +- `comm:REGEX` - match by commodity symbol - `desc:REGEX` - match transaction descriptions by regular expression - `date:PERIODEXPR` - match dates within the specified [period](#period-expressions) - `date2:PERIODEXPR` - as above, but match secondary dates diff --git a/NEWS.md b/NEWS.md index bb23c0ab7..ae1bb8f92 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,7 +6,8 @@ title: hledger news ## unreleased -- parsing: amt queries use the = operator by default, eg amt:50 finds amounts equal to 50 +- queries: `comm:REGEXP` matches commodity symbols which match REGEXP +- queries: `amt` now uses the = operator by default, eg amt:50 finds amounts equal to 50 - don't break when there are non-ascii characters in CSV files - csv: add the `include` directive, useful for factoring out common rules used with multiple CSV files - balancesheet: don't bother showing equity, it won't be useful for most of us diff --git a/hledger-lib/Hledger/Query.hs b/hledger-lib/Hledger/Query.hs index b0f09f01b..cd33906f9 100644 --- a/hledger-lib/Hledger/Query.hs +++ b/hledger-lib/Hledger/Query.hs @@ -43,7 +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.Amount (nullamt, usd) import Hledger.Data.Dates import Hledger.Data.Posting import Hledger.Data.Transaction @@ -57,6 +57,7 @@ data Query = Any -- ^ always match | Or [Query] -- ^ match if any of these match | And [Query] -- ^ match if all of these match | Code String -- ^ match if code matches this regexp + | Comm String -- ^ match if the commodity symbol matches this regexp | Desc String -- ^ match if description matches this regexp | Acct String -- ^ match postings whose account matches this regexp | Date DateSpan -- ^ match if primary date in this date span @@ -174,7 +175,9 @@ tests_words'' = [ prefixes = map (++":") [ "inacctonly" ,"inacct" + ,"amt" ,"code" + ,"comm" ,"desc" ,"acct" ,"date" @@ -206,6 +209,7 @@ parseQueryTerm d ('n':'o':'t':':':s) = case parseQueryTerm d s of Left m -> Left $ Not m Right _ -> Left Any -- not:somequeryoption will be ignored parseQueryTerm _ ('c':'o':'d':'e':':':s) = Left $ Code s +parseQueryTerm _ ('c':'o':'m':'m':':':s) = Left $ Comm s parseQueryTerm _ ('d':'e':'s':'c':':':s) = Left $ Desc s parseQueryTerm _ ('a':'c':'c':'t':':':s) = Left $ Acct s parseQueryTerm d ('d':'a':'t':'e':':':s) = @@ -499,6 +503,7 @@ matchesPosting (None) _ = False matchesPosting (Or qs) p = any (`matchesPosting` p) qs matchesPosting (And qs) p = all (`matchesPosting` p) qs matchesPosting (Code r) p = regexMatchesCI r $ maybe "" tcode $ ptransaction p +matchesPosting (Comm r) Posting{pamount=Mixed as} = any (regexMatchesCI r) $ map acommodity as matchesPosting (Desc r) p = regexMatchesCI r $ maybe "" tdescription $ ptransaction p matchesPosting (Acct r) p = regexMatchesCI r $ paccount p matchesPosting (Date span) p = span `spanContainsDate` postingDate p @@ -549,6 +554,7 @@ tests_matchesPosting = [ assertBool "" $ not $ (Tag "foo foo" (Just " ar ba ")) `matchesPosting` nullposting{ptags=[("foo foo","bar bar")]} -- a tag match on a posting also sees inherited tags assertBool "" $ (Tag "txntag" Nothing) `matchesPosting` nullposting{ptransaction=Just nulltransaction{ttags=[("txntag","")]}} + assertBool "" $ (Comm "$") `matchesPosting` nullposting{pamount=Mixed [usd 1]} ] -- | Does the match expression match this transaction ? @@ -559,6 +565,7 @@ matchesTransaction (None) _ = False matchesTransaction (Or qs) t = any (`matchesTransaction` t) qs matchesTransaction (And qs) t = all (`matchesTransaction` t) qs matchesTransaction (Code r) t = regexMatchesCI r $ tcode t +matchesTransaction q@(Comm _) t = any (q `matchesPosting`) $ tpostings t matchesTransaction (Desc r) t = regexMatchesCI r $ tdescription t matchesTransaction q@(Acct _) t = any (q `matchesPosting`) $ tpostings t matchesTransaction (Date span) t = spanContainsDate span $ tdate t diff --git a/hledger-web/Handler/Common.hs b/hledger-web/Handler/Common.hs index 8e7326e42..431d37c84 100644 --- a/hledger-web/Handler/Common.hs +++ b/hledger-web/Handler/Common.hs @@ -95,6 +95,7 @@ searchform VD{..} = [hamlet| Transactions/postings may additionally be filtered by acct:REGEXP (target account), # code:REGEXP (transaction code), # + comm:REGEXP (commodity symbol), # desc:REGEXP (description), # date:PERIODEXP (date), # date2:PERIODEXP (secondary date), # @@ -103,7 +104,7 @@ searchform VD{..} = [hamlet| status:*, status:!, status: (cleared status), # real:BOOL (real/virtual-ness), # empty:BOOL (is amount zero), # - amt:N (test magnitude of single-commodity amount). + amt:N, amt:N (test magnitude of single-commodity amount).
Prepend not: to negate, enclose multi-word patterns in quotes, multiple search terms are AND'ed. |]