handle pending status correctly, add --pending (#250)
A transaction/posting status of ! (pending) was effectively equivalent to * (cleared). Now it's a separate state, not matched by --cleared. The new Ledger-compatible --pending flag matches it, and so does --uncleared. The equivalent search queries are now status:*, status:! and status: (the old status:1 and status:0 spellings are deprecated). Since we interpret --uncleared and status: as "any state except cleared", it's not currently possible to match things which are neither cleared nor pending.
This commit is contained in:
parent
6fb0910996
commit
d1f63334ee
@ -840,7 +840,8 @@ General flags:
|
|||||||
all at once (overrides the flags above)
|
all at once (overrides the flags above)
|
||||||
--date2 --aux-date use postings/txns' secondary dates instead
|
--date2 --aux-date use postings/txns' secondary dates instead
|
||||||
-C --cleared include only cleared postings/txns
|
-C --cleared include only cleared postings/txns
|
||||||
-U --uncleared include only uncleared postings/txns
|
--pending include only pending postings/txns
|
||||||
|
-U --uncleared include only uncleared (and pending) postings/txns
|
||||||
-R --real include only non-virtual postings
|
-R --real include only non-virtual postings
|
||||||
--depth=N hide accounts/postings deeper than N
|
--depth=N hide accounts/postings deeper than N
|
||||||
-E --empty show empty/zero things which are normally omitted
|
-E --empty show empty/zero things which are normally omitted
|
||||||
@ -962,7 +963,7 @@ A query term can be any of the following:
|
|||||||
- `date2:PERIODEXPR` - as above, but match secondary dates
|
- `date2:PERIODEXPR` - as above, but match secondary dates
|
||||||
- `tag:NAME[=REGEX]` - match by (exact, case sensitive) [tag](#tags) name, and optionally match the tag value by regular expression. Note `tag:` will match a transaction if it or any its postings have the tag, and will match posting if it or its parent transaction has the tag.
|
- `tag:NAME[=REGEX]` - match by (exact, case sensitive) [tag](#tags) name, and optionally match the tag value by regular expression. Note `tag:` will match a transaction if it or any its postings have the tag, and will match posting if it or its parent transaction has the tag.
|
||||||
- `depth:N` - match (or display, depending on command) accounts at or above this [depth](#depth-limiting)
|
- `depth:N` - match (or display, depending on command) accounts at or above this [depth](#depth-limiting)
|
||||||
- `status:1` or `status:0` - match pending/cleared or uncleared transactions respectively
|
- `status:*` or `status:!` or `status:` - match cleared, pending, or uncleared/pending transactions respectively
|
||||||
- `real:1` or `real:0` - match real/virtual-ness
|
- `real:1` or `real:0` - match real/virtual-ness
|
||||||
- `empty:1` or `empty:0` - match if amount is/is not zero
|
- `empty:1` or `empty:0` - match if amount is/is not zero
|
||||||
- `amt:N`, `amt:<N`, `amt:<=N`, `amt:>N`, `amt:>=N` - match postings with a single-commodity
|
- `amt:N`, `amt:<N`, `amt:<=N`, `amt:>N`, `amt:>=N` - match postings with a single-commodity
|
||||||
|
|||||||
@ -674,7 +674,7 @@ Right samplejournal = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/01/01",
|
tdate=parsedate "2008/01/01",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="income",
|
tdescription="income",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -690,7 +690,7 @@ Right samplejournal = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/06/01",
|
tdate=parsedate "2008/06/01",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="gift",
|
tdescription="gift",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -706,7 +706,7 @@ Right samplejournal = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/06/02",
|
tdate=parsedate "2008/06/02",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="save",
|
tdescription="save",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -722,7 +722,7 @@ Right samplejournal = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/06/03",
|
tdate=parsedate "2008/06/03",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=True,
|
tstatus=Cleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="eat & shop",
|
tdescription="eat & shop",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -738,7 +738,7 @@ Right samplejournal = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/12/31",
|
tdate=parsedate "2008/12/31",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="pay off",
|
tdescription="pay off",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
|
|||||||
@ -13,7 +13,7 @@ module Hledger.Data.Posting (
|
|||||||
posting,
|
posting,
|
||||||
post,
|
post,
|
||||||
-- * operations
|
-- * operations
|
||||||
postingCleared,
|
postingStatus,
|
||||||
isReal,
|
isReal,
|
||||||
isVirtual,
|
isVirtual,
|
||||||
isBalancedVirtual,
|
isBalancedVirtual,
|
||||||
@ -67,7 +67,7 @@ nullposting, posting :: Posting
|
|||||||
nullposting = Posting
|
nullposting = Posting
|
||||||
{pdate=Nothing
|
{pdate=Nothing
|
||||||
,pdate2=Nothing
|
,pdate2=Nothing
|
||||||
,pstatus=False
|
,pstatus=Uncleared
|
||||||
,paccount=""
|
,paccount=""
|
||||||
,pamount=nullmixedamt
|
,pamount=nullmixedamt
|
||||||
,pcomment=""
|
,pcomment=""
|
||||||
@ -137,14 +137,28 @@ postingDate2 p = headDef nulldate $ catMaybes dates
|
|||||||
,maybe Nothing (Just . tdate) $ ptransaction p
|
,maybe Nothing (Just . tdate) $ ptransaction p
|
||||||
]
|
]
|
||||||
|
|
||||||
-- |Is this posting cleared? If this posting was individually marked
|
-- | Get a posting's cleared status: cleared or pending if explicitly set,
|
||||||
-- as cleared, returns True. Otherwise, return the parent
|
-- otherwise the cleared status of its parent transaction, or uncleared
|
||||||
-- transaction's cleared status or, if there is no parent
|
-- if there is no parent transaction.
|
||||||
-- transaction, return False.
|
-- (Note Uncleared's ambiguity, can mean "uncleared" or "don't know".
|
||||||
postingCleared :: Posting -> Bool
|
postingStatus :: Posting -> ClearedStatus
|
||||||
postingCleared p = if pstatus p
|
postingStatus Posting{pstatus=s, ptransaction=mt}
|
||||||
then True
|
| s == Uncleared = case mt of Just t -> tstatus t
|
||||||
else maybe False tstatus $ ptransaction p
|
Nothing -> Uncleared
|
||||||
|
| otherwise = s
|
||||||
|
|
||||||
|
-- -- | Is this posting cleared? True if the posting is explicitly marked
|
||||||
|
-- -- cleared, false if it is marked pending, otherwise true if the
|
||||||
|
-- -- parent transaction is marked cleared or false if there is no parent
|
||||||
|
-- -- transaction.
|
||||||
|
-- -- (Note Uncleared's ambiguity, can mean "uncleared" or "don't know".
|
||||||
|
-- postingIsCleared :: Posting -> Bool
|
||||||
|
-- postingIsCleared p
|
||||||
|
-- | pstatus p == Cleared = True
|
||||||
|
-- | pstatus p == Pending = False
|
||||||
|
-- | otherwise = case ptransaction p of
|
||||||
|
-- Just t -> tstatus t == Cleared
|
||||||
|
-- Nothing -> False
|
||||||
|
|
||||||
-- | Tags for this posting including any inherited from its parent transaction.
|
-- | Tags for this posting including any inherited from its parent transaction.
|
||||||
postingAllTags :: Posting -> [Tag]
|
postingAllTags :: Posting -> [Tag]
|
||||||
|
|||||||
@ -82,7 +82,7 @@ entryFromTimeLogInOut i o
|
|||||||
tsourcepos = tlsourcepos i,
|
tsourcepos = tlsourcepos i,
|
||||||
tdate = idate,
|
tdate = idate,
|
||||||
tdate2 = Nothing,
|
tdate2 = Nothing,
|
||||||
tstatus = True,
|
tstatus = Cleared,
|
||||||
tcode = "",
|
tcode = "",
|
||||||
tdescription = desc,
|
tdescription = desc,
|
||||||
tcomment = "",
|
tcomment = "",
|
||||||
|
|||||||
@ -64,7 +64,7 @@ nulltransaction = Transaction {
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=nulldate,
|
tdate=nulldate,
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="",
|
tdescription="",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -102,14 +102,14 @@ tests_showTransactionUnelided = [
|
|||||||
nulltransaction{
|
nulltransaction{
|
||||||
tdate=parsedate "2012/05/14",
|
tdate=parsedate "2012/05/14",
|
||||||
tdate2=Just $ parsedate "2012/05/15",
|
tdate2=Just $ parsedate "2012/05/15",
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="code",
|
tcode="code",
|
||||||
tdescription="desc",
|
tdescription="desc",
|
||||||
tcomment="tcomment1\ntcomment2\n",
|
tcomment="tcomment1\ntcomment2\n",
|
||||||
ttags=[("ttag1","val1")],
|
ttags=[("ttag1","val1")],
|
||||||
tpostings=[
|
tpostings=[
|
||||||
nullposting{
|
nullposting{
|
||||||
pstatus=True,
|
pstatus=Cleared,
|
||||||
paccount="a",
|
paccount="a",
|
||||||
pamount=Mixed [usd 1, hrs 2],
|
pamount=Mixed [usd 1, hrs 2],
|
||||||
pcomment="\npcomment2\n",
|
pcomment="\npcomment2\n",
|
||||||
@ -140,7 +140,9 @@ showTransaction' elide t =
|
|||||||
date = showdate (tdate t) ++ maybe "" showedate (tdate2 t)
|
date = showdate (tdate t) ++ maybe "" showedate (tdate2 t)
|
||||||
showdate = printf "%-10s" . showDate
|
showdate = printf "%-10s" . showDate
|
||||||
showedate = printf "=%s" . showdate
|
showedate = printf "=%s" . showdate
|
||||||
status = if tstatus t then " *" else ""
|
status | tstatus t == Cleared = " *"
|
||||||
|
| tstatus t == Pending = " !"
|
||||||
|
| otherwise = ""
|
||||||
code = if length (tcode t) > 0 then printf " (%s)" $ tcode t else ""
|
code = if length (tcode t) > 0 then printf " (%s)" $ tcode t else ""
|
||||||
desc = if null d then "" else " " ++ d where d = tdescription t
|
desc = if null d then "" else " " ++ d where d = tdescription t
|
||||||
(samelinecomment, newlinecomments) =
|
(samelinecomment, newlinecomments) =
|
||||||
@ -184,7 +186,7 @@ postingAsLines elideamount ps p =
|
|||||||
showacct p =
|
showacct p =
|
||||||
indent $ showstatus p ++ printf (printf "%%-%ds" w) (showAccountName Nothing (ptype p) (paccount p))
|
indent $ showstatus p ++ printf (printf "%%-%ds" w) (showAccountName Nothing (ptype p) (paccount p))
|
||||||
where
|
where
|
||||||
showstatus p = if pstatus p then "* " else ""
|
showstatus p = if pstatus p == Cleared then "* " else ""
|
||||||
w = maximum $ map (length . paccount) ps
|
w = maximum $ map (length . paccount) ps
|
||||||
showamt =
|
showamt =
|
||||||
padleft 12 . showMixedAmount
|
padleft 12 . showMixedAmount
|
||||||
@ -194,7 +196,7 @@ tests_postingAsLines = [
|
|||||||
let p `gives` ls = assertEqual "" ls (postingAsLines False [p] p)
|
let p `gives` ls = assertEqual "" ls (postingAsLines False [p] p)
|
||||||
posting `gives` [" 0"]
|
posting `gives` [" 0"]
|
||||||
posting{
|
posting{
|
||||||
pstatus=True,
|
pstatus=Cleared,
|
||||||
paccount="a",
|
paccount="a",
|
||||||
pamount=Mixed [usd 1, hrs 2],
|
pamount=Mixed [usd 1, hrs 2],
|
||||||
pcomment="pcomment1\npcomment2\n tag3: val3 \n",
|
pcomment="pcomment1\npcomment2\n tag3: val3 \n",
|
||||||
@ -382,7 +384,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
," assets:checking"
|
," assets:checking"
|
||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "coopportunity" "" []
|
(let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
@ -396,7 +398,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
," assets:checking $-47.18"
|
," assets:checking $-47.18"
|
||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "coopportunity" "" []
|
(let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
@ -412,7 +414,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(showTransaction
|
(showTransaction
|
||||||
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "coopportunity" "" []
|
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.19)]}
|
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.19)]}
|
||||||
] ""))
|
] ""))
|
||||||
@ -425,7 +427,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(showTransaction
|
(showTransaction
|
||||||
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "coopportunity" "" []
|
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
@ -437,7 +439,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(showTransaction
|
(showTransaction
|
||||||
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "coopportunity" "" []
|
(txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []
|
||||||
[posting{paccount="expenses:food:groceries", pamount=missingmixedamt}
|
[posting{paccount="expenses:food:groceries", pamount=missingmixedamt}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
@ -450,7 +452,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
,""
|
,""
|
||||||
])
|
])
|
||||||
(showTransaction
|
(showTransaction
|
||||||
(txnTieKnot $ Transaction nullsourcepos (parsedate "2010/01/01") Nothing False "" "x" "" []
|
(txnTieKnot $ Transaction nullsourcepos (parsedate "2010/01/01") Nothing Uncleared "" "x" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [num 1 `at` (usd 2 `withPrecision` 0)]}
|
[posting{paccount="a", pamount=Mixed [num 1 `at` (usd 2 `withPrecision` 0)]}
|
||||||
,posting{paccount="b", pamount= missingmixedamt}
|
,posting{paccount="b", pamount= missingmixedamt}
|
||||||
] ""))
|
] ""))
|
||||||
@ -458,19 +460,19 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
,"balanceTransaction" ~: do
|
,"balanceTransaction" ~: do
|
||||||
assertBool "detect unbalanced entry, sign error"
|
assertBool "detect unbalanced entry, sign error"
|
||||||
(isLeft $ balanceTransaction Nothing
|
(isLeft $ balanceTransaction Nothing
|
||||||
(Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "test" "" []
|
(Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1]}
|
[posting{paccount="a", pamount=Mixed [usd 1]}
|
||||||
,posting{paccount="b", pamount=Mixed [usd 1]}
|
,posting{paccount="b", pamount=Mixed [usd 1]}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
assertBool "detect unbalanced entry, multiple missing amounts"
|
assertBool "detect unbalanced entry, multiple missing amounts"
|
||||||
(isLeft $ balanceTransaction Nothing
|
(isLeft $ balanceTransaction Nothing
|
||||||
(Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "test" "" []
|
(Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []
|
||||||
[posting{paccount="a", pamount=missingmixedamt}
|
[posting{paccount="a", pamount=missingmixedamt}
|
||||||
,posting{paccount="b", pamount=missingmixedamt}
|
,posting{paccount="b", pamount=missingmixedamt}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2007/01/28") Nothing False "" "" "" []
|
let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1]}
|
[posting{paccount="a", pamount=Mixed [usd 1]}
|
||||||
,posting{paccount="b", pamount=missingmixedamt}
|
,posting{paccount="b", pamount=missingmixedamt}
|
||||||
] "")
|
] "")
|
||||||
@ -481,7 +483,7 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
Right e' -> (pamount $ last $ tpostings e')
|
Right e' -> (pamount $ last $ tpostings e')
|
||||||
Left _ -> error' "should not happen")
|
Left _ -> error' "should not happen")
|
||||||
|
|
||||||
let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing False "" "" "" []
|
let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1.35]}
|
[posting{paccount="a", pamount=Mixed [usd 1.35]}
|
||||||
,posting{paccount="b", pamount=Mixed [eur (-1)]}
|
,posting{paccount="b", pamount=Mixed [eur (-1)]}
|
||||||
] "")
|
] "")
|
||||||
@ -493,49 +495,49 @@ tests_Hledger_Data_Transaction = TestList $ concat [
|
|||||||
Left _ -> error' "should not happen")
|
Left _ -> error' "should not happen")
|
||||||
|
|
||||||
assertBool "balanceTransaction balances based on cost if there are unit prices" (isRight $
|
assertBool "balanceTransaction balances based on cost if there are unit prices" (isRight $
|
||||||
balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing False "" "" "" []
|
balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1 `at` eur 2]}
|
[posting{paccount="a", pamount=Mixed [usd 1 `at` eur 2]}
|
||||||
,posting{paccount="a", pamount=Mixed [usd (-2) `at` eur 1]}
|
,posting{paccount="a", pamount=Mixed [usd (-2) `at` eur 1]}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
assertBool "balanceTransaction balances based on cost if there are total prices" (isRight $
|
assertBool "balanceTransaction balances based on cost if there are total prices" (isRight $
|
||||||
balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing False "" "" "" []
|
balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1 @@ eur 1]}
|
[posting{paccount="a", pamount=Mixed [usd 1 @@ eur 1]}
|
||||||
,posting{paccount="a", pamount=Mixed [usd (-2) @@ eur 1]}
|
,posting{paccount="a", pamount=Mixed [usd (-2) @@ eur 1]}
|
||||||
] ""))
|
] ""))
|
||||||
|
|
||||||
,"isTransactionBalanced" ~: do
|
,"isTransactionBalanced" ~: do
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "detect balanced" (isTransactionBalanced Nothing t)
|
assertBool "detect balanced" (isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.01)], ptransaction=Just t}
|
,posting{paccount="c", pamount=Mixed [usd (-1.01)], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "detect unbalanced" (not $ isTransactionBalanced Nothing t)
|
assertBool "detect unbalanced" (not $ isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "detect unbalanced, one posting" (not $ isTransactionBalanced Nothing t)
|
assertBool "detect unbalanced, one posting" (not $ isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 0], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 0], ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "one zero posting is considered balanced for now" (isTransactionBalanced Nothing t)
|
assertBool "one zero posting is considered balanced for now" (isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=VirtualPosting, ptransaction=Just t}
|
,posting{paccount="d", pamount=Mixed [usd 100], ptype=VirtualPosting, ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "virtual postings don't need to balance" (isTransactionBalanced Nothing t)
|
assertBool "virtual postings don't need to balance" (isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}
|
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}
|
||||||
] ""
|
] ""
|
||||||
assertBool "balanced virtual postings need to balance among themselves" (not $ isTransactionBalanced Nothing t)
|
assertBool "balanced virtual postings need to balance among themselves" (not $ isTransactionBalanced Nothing t)
|
||||||
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing False "" "a" "" []
|
let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
[posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}
|
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ Most data types are defined here to avoid import cycles.
|
|||||||
Here is an overview of the hledger data model:
|
Here is an overview of the hledger data model:
|
||||||
|
|
||||||
> Journal -- a journal is read from one or more data files. It contains..
|
> Journal -- a journal is read from one or more data files. It contains..
|
||||||
> [Transaction] -- journal transactions (aka entries), which have date, status, code, description and..
|
> [Transaction] -- journal transactions (aka entries), which have date, cleared status, code, description and..
|
||||||
> [Posting] -- multiple account postings, which have account name and amount
|
> [Posting] -- multiple account postings, which have account name and amount
|
||||||
> [HistoricalPrice] -- historical commodity prices
|
> [HistoricalPrice] -- historical commodity prices
|
||||||
>
|
>
|
||||||
@ -116,10 +116,18 @@ data PostingType = RegularPosting | VirtualPosting | BalancedVirtualPosting
|
|||||||
|
|
||||||
type Tag = (String, String) -- ^ A tag name and (possibly empty) value.
|
type Tag = (String, String) -- ^ A tag name and (possibly empty) value.
|
||||||
|
|
||||||
|
data ClearedStatus = Uncleared | Pending | Cleared
|
||||||
|
deriving (Eq,Ord,Typeable,Data)
|
||||||
|
|
||||||
|
instance Show ClearedStatus where -- custom show
|
||||||
|
show Uncleared = "" -- a bad idea
|
||||||
|
show Pending = "!" -- don't do it
|
||||||
|
show Cleared = "*"
|
||||||
|
|
||||||
data Posting = Posting {
|
data Posting = Posting {
|
||||||
pdate :: Maybe Day, -- ^ this posting's date, if different from the transaction's
|
pdate :: Maybe Day, -- ^ this posting's date, if different from the transaction's
|
||||||
pdate2 :: Maybe Day, -- ^ this posting's secondary date, if different from the transaction's
|
pdate2 :: Maybe Day, -- ^ this posting's secondary date, if different from the transaction's
|
||||||
pstatus :: Bool,
|
pstatus :: ClearedStatus,
|
||||||
paccount :: AccountName,
|
paccount :: AccountName,
|
||||||
pamount :: MixedAmount,
|
pamount :: MixedAmount,
|
||||||
pcomment :: String, -- ^ this posting's comment lines, as a single non-indented multi-line string
|
pcomment :: String, -- ^ this posting's comment lines, as a single non-indented multi-line string
|
||||||
@ -139,7 +147,7 @@ data Transaction = Transaction {
|
|||||||
tsourcepos :: SourcePos,
|
tsourcepos :: SourcePos,
|
||||||
tdate :: Day,
|
tdate :: Day,
|
||||||
tdate2 :: Maybe Day,
|
tdate2 :: Maybe Day,
|
||||||
tstatus :: Bool, -- XXX tcleared ?
|
tstatus :: ClearedStatus,
|
||||||
tcode :: String,
|
tcode :: String,
|
||||||
tdescription :: String,
|
tdescription :: String,
|
||||||
tcomment :: String, -- ^ this transaction's comment lines, as a single non-indented multi-line string
|
tcomment :: String, -- ^ this transaction's comment lines, as a single non-indented multi-line string
|
||||||
|
|||||||
@ -73,7 +73,7 @@ data Query = Any -- ^ always match
|
|||||||
| Acct String -- ^ match postings whose account matches this regexp
|
| Acct String -- ^ match postings whose account matches this regexp
|
||||||
| Date DateSpan -- ^ match if primary date in this date span
|
| Date DateSpan -- ^ match if primary date in this date span
|
||||||
| Date2 DateSpan -- ^ match if secondary date in this date span
|
| Date2 DateSpan -- ^ match if secondary date in this date span
|
||||||
| Status Bool -- ^ match if cleared status has this value
|
| Status ClearedStatus -- ^ match if cleared status has this value
|
||||||
| Real Bool -- ^ match if "realness" (involves a real non-virtual account ?) has this value
|
| Real Bool -- ^ match if "realness" (involves a real non-virtual account ?) has this value
|
||||||
| Amt OrdPlus Quantity -- ^ match if the amount's numeric quantity is less than/greater than/equal to/unsignedly equal to some value
|
| Amt OrdPlus Quantity -- ^ match if the amount's numeric quantity is less than/greater than/equal to/unsignedly equal to some value
|
||||||
| Sym String -- ^ match if the entire commodity symbol is matched by this regexp
|
| Sym String -- ^ match if the entire commodity symbol is matched by this regexp
|
||||||
@ -265,9 +265,11 @@ tests_parseQueryTerm = [
|
|||||||
"a" `gives` (Left $ Acct "a")
|
"a" `gives` (Left $ Acct "a")
|
||||||
"acct:expenses:autres d\233penses" `gives` (Left $ Acct "expenses:autres d\233penses")
|
"acct:expenses:autres d\233penses" `gives` (Left $ Acct "expenses:autres d\233penses")
|
||||||
"not:desc:a b" `gives` (Left $ Not $ Desc "a b")
|
"not:desc:a b" `gives` (Left $ Not $ Desc "a b")
|
||||||
"status:1" `gives` (Left $ Status True)
|
"status:1" `gives` (Left $ Status Cleared)
|
||||||
"status:0" `gives` (Left $ Status False)
|
"status:*" `gives` (Left $ Status Cleared)
|
||||||
"status:" `gives` (Left $ Status False)
|
"status:!" `gives` (Left $ Status Pending)
|
||||||
|
"status:0" `gives` (Left $ Status Uncleared)
|
||||||
|
"status:" `gives` (Left $ Status Uncleared)
|
||||||
"real:1" `gives` (Left $ Real True)
|
"real:1" `gives` (Left $ Real True)
|
||||||
"date:2008" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2008/01/01") (Just $ parsedate "2009/01/01"))
|
"date:2008" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2008/01/01") (Just $ parsedate "2009/01/01"))
|
||||||
"date:from 2012/5/17" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2012/05/17") Nothing)
|
"date:from 2012/5/17" `gives` (Left $ Date $ DateSpan (Just $ parsedate "2012/05/17") Nothing)
|
||||||
@ -333,8 +335,10 @@ parseTag s | '=' `elem` s = (n, Just $ tail v)
|
|||||||
|
|
||||||
-- -- , treating "*" or "!" as synonyms for "1".
|
-- -- , treating "*" or "!" as synonyms for "1".
|
||||||
-- | Parse the boolean value part of a "status:" query.
|
-- | Parse the boolean value part of a "status:" query.
|
||||||
parseStatus :: String -> Bool
|
parseStatus :: String -> ClearedStatus
|
||||||
parseStatus s = s `elem` (truestrings) -- ++ ["*","!"])
|
parseStatus s | s `elem` ["1","*"] = Cleared
|
||||||
|
| s == "!" = Pending
|
||||||
|
| otherwise = Uncleared
|
||||||
|
|
||||||
-- | Parse the boolean value part of a "status:" query. "1" means true,
|
-- | Parse the boolean value part of a "status:" query. "1" means true,
|
||||||
-- anything else will be parsed as false without error.
|
-- anything else will be parsed as false without error.
|
||||||
@ -399,7 +403,7 @@ tests_filterQuery = [
|
|||||||
let (q,p) `gives` r = assertEqual "" r (filterQuery p q)
|
let (q,p) `gives` r = assertEqual "" r (filterQuery p q)
|
||||||
(Any, queryIsDepth) `gives` Any
|
(Any, queryIsDepth) `gives` Any
|
||||||
(Depth 1, queryIsDepth) `gives` Depth 1
|
(Depth 1, queryIsDepth) `gives` Depth 1
|
||||||
(And [And [Status True,Depth 1]], not . queryIsDepth) `gives` Status True
|
(And [And [Status Cleared,Depth 1]], not . queryIsDepth) `gives` Status Cleared
|
||||||
-- (And [Date nulldatespan, Not (Or [Any, Depth 1])], queryIsDepth) `gives` And [Not (Or [Depth 1])]
|
-- (And [Date nulldatespan, Not (Or [Any, Depth 1])], queryIsDepth) `gives` And [Not (Or [Depth 1])]
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -643,7 +647,8 @@ matchesPosting (Desc r) p = regexMatchesCI r $ maybe "" tdescription $ ptransact
|
|||||||
matchesPosting (Acct r) p = regexMatchesCI r $ paccount p
|
matchesPosting (Acct r) p = regexMatchesCI r $ paccount p
|
||||||
matchesPosting (Date span) p = span `spanContainsDate` postingDate p
|
matchesPosting (Date span) p = span `spanContainsDate` postingDate p
|
||||||
matchesPosting (Date2 span) p = span `spanContainsDate` postingDate2 p
|
matchesPosting (Date2 span) p = span `spanContainsDate` postingDate2 p
|
||||||
matchesPosting (Status v) p = v == postingCleared p
|
matchesPosting (Status Uncleared) p = postingStatus p /= Cleared
|
||||||
|
matchesPosting (Status s) p = postingStatus p == s
|
||||||
matchesPosting (Real v) p = v == isReal p
|
matchesPosting (Real v) p = v == isReal p
|
||||||
matchesPosting q@(Depth _) Posting{paccount=a} = q `matchesAccount` a
|
matchesPosting q@(Depth _) Posting{paccount=a} = q `matchesAccount` a
|
||||||
matchesPosting q@(Amt _ _) Posting{pamount=amt} = q `matchesMixedAmount` amt
|
matchesPosting q@(Amt _ _) Posting{pamount=amt} = q `matchesMixedAmount` amt
|
||||||
@ -660,16 +665,16 @@ matchesPosting (Tag n (Just v)) p = isJust $ lookupTagByNameAndValue (n,v) $ pos
|
|||||||
tests_matchesPosting = [
|
tests_matchesPosting = [
|
||||||
"matchesPosting" ~: do
|
"matchesPosting" ~: do
|
||||||
-- matching posting status..
|
-- matching posting status..
|
||||||
assertBool "positive match on true posting status" $
|
assertBool "positive match on cleared posting status" $
|
||||||
(Status True) `matchesPosting` nullposting{pstatus=True}
|
(Status Cleared) `matchesPosting` nullposting{pstatus=Cleared}
|
||||||
assertBool "negative match on true posting status" $
|
assertBool "negative match on cleared posting status" $
|
||||||
not $ (Not $ Status True) `matchesPosting` nullposting{pstatus=True}
|
not $ (Not $ Status Cleared) `matchesPosting` nullposting{pstatus=Cleared}
|
||||||
assertBool "positive match on false posting status" $
|
assertBool "positive match on unclered posting status" $
|
||||||
(Status False) `matchesPosting` nullposting{pstatus=False}
|
(Status Uncleared) `matchesPosting` nullposting{pstatus=Uncleared}
|
||||||
assertBool "negative match on false posting status" $
|
assertBool "negative match on unclered posting status" $
|
||||||
not $ (Not $ Status False) `matchesPosting` nullposting{pstatus=False}
|
not $ (Not $ Status Uncleared) `matchesPosting` nullposting{pstatus=Uncleared}
|
||||||
assertBool "positive match on true posting status acquired from transaction" $
|
assertBool "positive match on true posting status acquired from transaction" $
|
||||||
(Status True) `matchesPosting` nullposting{pstatus=False,ptransaction=Just nulltransaction{tstatus=True}}
|
(Status Cleared) `matchesPosting` nullposting{pstatus=Uncleared,ptransaction=Just nulltransaction{tstatus=Cleared}}
|
||||||
assertBool "real:1 on real posting" $ (Real True) `matchesPosting` nullposting{ptype=RegularPosting}
|
assertBool "real:1 on real posting" $ (Real True) `matchesPosting` nullposting{ptype=RegularPosting}
|
||||||
assertBool "real:1 on virtual posting fails" $ not $ (Real True) `matchesPosting` nullposting{ptype=VirtualPosting}
|
assertBool "real:1 on virtual posting fails" $ not $ (Real True) `matchesPosting` nullposting{ptype=VirtualPosting}
|
||||||
assertBool "real:1 on balanced virtual posting fails" $ not $ (Real True) `matchesPosting` nullposting{ptype=BalancedVirtualPosting}
|
assertBool "real:1 on balanced virtual posting fails" $ not $ (Real True) `matchesPosting` nullposting{ptype=BalancedVirtualPosting}
|
||||||
@ -701,7 +706,8 @@ matchesTransaction (Desc r) t = regexMatchesCI r $ tdescription t
|
|||||||
matchesTransaction q@(Acct _) t = any (q `matchesPosting`) $ tpostings t
|
matchesTransaction q@(Acct _) t = any (q `matchesPosting`) $ tpostings t
|
||||||
matchesTransaction (Date span) t = spanContainsDate span $ tdate t
|
matchesTransaction (Date span) t = spanContainsDate span $ tdate t
|
||||||
matchesTransaction (Date2 span) t = spanContainsDate span $ transactionDate2 t
|
matchesTransaction (Date2 span) t = spanContainsDate span $ transactionDate2 t
|
||||||
matchesTransaction (Status v) t = v == tstatus t
|
matchesTransaction (Status Uncleared) t = tstatus t /= Cleared
|
||||||
|
matchesTransaction (Status s) t = tstatus t == s
|
||||||
matchesTransaction (Real v) t = v == hasRealPostings t
|
matchesTransaction (Real v) t = v == hasRealPostings t
|
||||||
matchesTransaction q@(Amt _ _) t = any (q `matchesPosting`) $ tpostings t
|
matchesTransaction q@(Amt _ _) t = any (q `matchesPosting`) $ tpostings t
|
||||||
matchesTransaction (Empty _) _ = True
|
matchesTransaction (Empty _) _ = True
|
||||||
|
|||||||
@ -51,7 +51,7 @@ import Text.Printf (hPrintf,printf)
|
|||||||
import Hledger.Data
|
import Hledger.Data
|
||||||
import Hledger.Utils.UTF8IOCompat (getContents)
|
import Hledger.Utils.UTF8IOCompat (getContents)
|
||||||
import Hledger.Utils
|
import Hledger.Utils
|
||||||
import Hledger.Read.JournalReader (amountp)
|
import Hledger.Read.JournalReader (amountp, statusp)
|
||||||
|
|
||||||
|
|
||||||
reader :: Reader
|
reader :: Reader
|
||||||
@ -602,7 +602,15 @@ transactionFromCsvRecord sourcepos rules record = t
|
|||||||
++"or "++maybe "add a" (const "change your") mskip++" skip rule"
|
++"or "++maybe "add a" (const "change your") mskip++" skip rule"
|
||||||
,"for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y"
|
,"for m/d/y or d/m/y dates, use date-format %-m/%-d/%Y or date-format %-d/%-m/%Y"
|
||||||
]
|
]
|
||||||
status = maybe False ((=="*") . render) $ mfieldtemplate "status"
|
status =
|
||||||
|
case mfieldtemplate "status" of
|
||||||
|
Nothing -> Uncleared
|
||||||
|
Just str -> either statuserror id $ runParser (statusp <* eof) nullctx "" $ render str
|
||||||
|
where
|
||||||
|
statuserror err = error' $ unlines
|
||||||
|
["error: could not parse \""++str++"\" as a cleared status (should be *, ! or empty)"
|
||||||
|
,"the parse error is: "++show err
|
||||||
|
]
|
||||||
code = maybe "" render $ mfieldtemplate "code"
|
code = maybe "" render $ mfieldtemplate "code"
|
||||||
description = maybe "" render $ mfieldtemplate "description"
|
description = maybe "" render $ mfieldtemplate "description"
|
||||||
comment = maybe "" render $ mfieldtemplate "comment"
|
comment = maybe "" render $ mfieldtemplate "comment"
|
||||||
|
|||||||
@ -36,6 +36,7 @@ module Hledger.Read.JournalReader (
|
|||||||
amountp',
|
amountp',
|
||||||
mamountp',
|
mamountp',
|
||||||
numberp,
|
numberp,
|
||||||
|
statusp,
|
||||||
emptyorcommentlinep,
|
emptyorcommentlinep,
|
||||||
followingcommentp,
|
followingcommentp,
|
||||||
accountaliasp
|
accountaliasp
|
||||||
@ -367,7 +368,7 @@ transaction = do
|
|||||||
date <- datep <?> "transaction"
|
date <- datep <?> "transaction"
|
||||||
edate <- optionMaybe (secondarydatep date) <?> "secondary date"
|
edate <- optionMaybe (secondarydatep date) <?> "secondary date"
|
||||||
lookAhead (spacenonewline <|> newline) <?> "whitespace or newline"
|
lookAhead (spacenonewline <|> newline) <?> "whitespace or newline"
|
||||||
status <- status <?> "cleared flag"
|
status <- statusp <?> "cleared status"
|
||||||
code <- codep <?> "transaction code"
|
code <- codep <?> "transaction code"
|
||||||
description <- descriptionp >>= return . strip
|
description <- descriptionp >>= return . strip
|
||||||
comment <- try followingcommentp <|> (newline >> return "")
|
comment <- try followingcommentp <|> (newline >> return "")
|
||||||
@ -407,14 +408,14 @@ test_transaction = do
|
|||||||
nulltransaction{
|
nulltransaction{
|
||||||
tdate=parsedate "2012/05/14",
|
tdate=parsedate "2012/05/14",
|
||||||
tdate2=Just $ parsedate "2012/05/15",
|
tdate2=Just $ parsedate "2012/05/15",
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="code",
|
tcode="code",
|
||||||
tdescription="desc",
|
tdescription="desc",
|
||||||
tcomment=" tcomment1\n tcomment2\n ttag1: val1\n",
|
tcomment=" tcomment1\n tcomment2\n ttag1: val1\n",
|
||||||
ttags=[("ttag1","val1")],
|
ttags=[("ttag1","val1")],
|
||||||
tpostings=[
|
tpostings=[
|
||||||
nullposting{
|
nullposting{
|
||||||
pstatus=True,
|
pstatus=Cleared,
|
||||||
paccount="a",
|
paccount="a",
|
||||||
pamount=Mixed [usd 1],
|
pamount=Mixed [usd 1],
|
||||||
pcomment=" pcomment1\n pcomment2\n ptag1: val1\n ptag2: val2\n",
|
pcomment=" pcomment1\n pcomment2\n ptag1: val1\n ptag2: val2\n",
|
||||||
@ -535,8 +536,14 @@ secondarydatep primarydate = do
|
|||||||
edate <- withDefaultYear primarydate datep
|
edate <- withDefaultYear primarydate datep
|
||||||
return edate
|
return edate
|
||||||
|
|
||||||
status :: Stream [Char] m Char => ParsecT [Char] JournalContext m Bool
|
statusp :: Stream [Char] m Char => ParsecT [Char] JournalContext m ClearedStatus
|
||||||
status = try (do { many spacenonewline; (char '*' <|> char '!') <?> "status"; return True } ) <|> return False
|
statusp =
|
||||||
|
choice'
|
||||||
|
[ many spacenonewline >> char '*' >> return Cleared
|
||||||
|
, many spacenonewline >> char '!' >> return Pending
|
||||||
|
, return Uncleared
|
||||||
|
]
|
||||||
|
<?> "cleared status"
|
||||||
|
|
||||||
codep :: Stream [Char] m Char => ParsecT [Char] JournalContext m String
|
codep :: Stream [Char] m Char => ParsecT [Char] JournalContext m String
|
||||||
codep = try (do { many1 spacenonewline; char '(' <?> "codep"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
|
codep = try (do { many1 spacenonewline; char '(' <?> "codep"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
|
||||||
@ -555,7 +562,7 @@ postings = many1 (try postingp) <?> "postings"
|
|||||||
postingp :: Stream [Char] m Char => ParsecT [Char] JournalContext m Posting
|
postingp :: Stream [Char] m Char => ParsecT [Char] JournalContext m Posting
|
||||||
postingp = do
|
postingp = do
|
||||||
many1 spacenonewline
|
many1 spacenonewline
|
||||||
status <- status
|
status <- statusp
|
||||||
many spacenonewline
|
many spacenonewline
|
||||||
account <- modifiedaccountname
|
account <- modifiedaccountname
|
||||||
let (ptype, account') = (accountNamePostingType account, unbracket account)
|
let (ptype, account') = (accountNamePostingType account, unbracket account)
|
||||||
|
|||||||
@ -332,7 +332,7 @@ Right samplejournal2 = journalBalanceTransactions $
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2008/01/01",
|
tdate=parsedate "2008/01/01",
|
||||||
tdate2=Just $ parsedate "2009/01/01",
|
tdate2=Just $ parsedate "2009/01/01",
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="",
|
tcode="",
|
||||||
tdescription="income",
|
tdescription="income",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
|
|||||||
@ -228,8 +228,8 @@ tests_postingsReport = [
|
|||||||
(Any, samplejournal) `gives` 11
|
(Any, samplejournal) `gives` 11
|
||||||
-- register --depth just clips account names
|
-- register --depth just clips account names
|
||||||
(Depth 2, samplejournal) `gives` 11
|
(Depth 2, samplejournal) `gives` 11
|
||||||
(And [Depth 1, Status True, Acct "expenses"], samplejournal) `gives` 2
|
(And [Depth 1, Status Cleared, Acct "expenses"], samplejournal) `gives` 2
|
||||||
(And [And [Depth 1, Status True], Acct "expenses"], samplejournal) `gives` 2
|
(And [And [Depth 1, Status Cleared], Acct "expenses"], samplejournal) `gives` 2
|
||||||
|
|
||||||
-- with query and/or command-line options
|
-- with query and/or command-line options
|
||||||
assertEqual "" 11 (length $ snd $ postingsReport defreportopts Any samplejournal)
|
assertEqual "" 11 (length $ snd $ postingsReport defreportopts Any samplejournal)
|
||||||
|
|||||||
@ -63,6 +63,7 @@ data ReportOpts = ReportOpts {
|
|||||||
,end_ :: Maybe Day
|
,end_ :: Maybe Day
|
||||||
,period_ :: Maybe (Interval,DateSpan)
|
,period_ :: Maybe (Interval,DateSpan)
|
||||||
,cleared_ :: Bool
|
,cleared_ :: Bool
|
||||||
|
,pending_ :: Bool
|
||||||
,uncleared_ :: Bool
|
,uncleared_ :: Bool
|
||||||
,cost_ :: Bool
|
,cost_ :: Bool
|
||||||
,depth_ :: Maybe Int
|
,depth_ :: Maybe Int
|
||||||
@ -119,6 +120,7 @@ defreportopts = ReportOpts
|
|||||||
def
|
def
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
def
|
||||||
|
|
||||||
rawOptsToReportOpts :: RawOpts -> IO ReportOpts
|
rawOptsToReportOpts :: RawOpts -> IO ReportOpts
|
||||||
rawOptsToReportOpts rawopts = do
|
rawOptsToReportOpts rawopts = do
|
||||||
@ -128,6 +130,7 @@ rawOptsToReportOpts rawopts = do
|
|||||||
,end_ = maybesmartdateopt d "end" rawopts
|
,end_ = maybesmartdateopt d "end" rawopts
|
||||||
,period_ = maybeperiodopt d rawopts
|
,period_ = maybeperiodopt d rawopts
|
||||||
,cleared_ = boolopt "cleared" rawopts
|
,cleared_ = boolopt "cleared" rawopts
|
||||||
|
,pending_ = boolopt "pending" rawopts
|
||||||
,uncleared_ = boolopt "uncleared" rawopts
|
,uncleared_ = boolopt "uncleared" rawopts
|
||||||
,cost_ = boolopt "cost" rawopts
|
,cost_ = boolopt "cost" rawopts
|
||||||
,depth_ = maybeintopt "depth" rawopts
|
,depth_ = maybeintopt "depth" rawopts
|
||||||
@ -226,9 +229,10 @@ intervalFromOpts ReportOpts{..} =
|
|||||||
| otherwise = NoInterval
|
| otherwise = NoInterval
|
||||||
|
|
||||||
-- | Get a maybe boolean representing the last cleared/uncleared option if any.
|
-- | Get a maybe boolean representing the last cleared/uncleared option if any.
|
||||||
clearedValueFromOpts :: ReportOpts -> Maybe Bool
|
clearedValueFromOpts :: ReportOpts -> Maybe ClearedStatus
|
||||||
clearedValueFromOpts ReportOpts{..} | cleared_ = Just True
|
clearedValueFromOpts ReportOpts{..} | cleared_ = Just Cleared
|
||||||
| uncleared_ = Just False
|
| pending_ = Just Pending
|
||||||
|
| uncleared_ = Just Uncleared
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
|
|
||||||
-- depthFromOpts :: ReportOpts -> Int
|
-- depthFromOpts :: ReportOpts -> Int
|
||||||
|
|||||||
@ -81,7 +81,7 @@ $newline never
|
|||||||
<li> <b><tt>desc:REGEXP</tt></b> - filter on description
|
<li> <b><tt>desc:REGEXP</tt></b> - filter on description
|
||||||
<li> <b><tt>date:PERIODEXP</tt></b>, <b><tt>date2:PERIODEXP</tt></b> - filter on date or secondary date
|
<li> <b><tt>date:PERIODEXP</tt></b>, <b><tt>date2:PERIODEXP</tt></b> - filter on date or secondary date
|
||||||
<li> <b><tt>code:REGEXP</tt></b> - filter on transaction's code (eg check number)
|
<li> <b><tt>code:REGEXP</tt></b> - filter on transaction's code (eg check number)
|
||||||
<li> <b><tt>status:*</tt></b>, <b><tt>status:!</tt></b>, <b><tt>status:</tt></b> - filter on transaction's status flag (eg cleared status)
|
<li> <b><tt>status:*</tt></b>, <b><tt>status:!</tt></b>, <b><tt>status:</tt></b> - filter on transaction's cleared status (cleared, pending, uncleared)
|
||||||
<!-- <li> <b><tt>empty:BOOL</tt></b> - filter on whether amount is zero -->
|
<!-- <li> <b><tt>empty:BOOL</tt></b> - filter on whether amount is zero -->
|
||||||
<li> <b><tt>amt:N</tt></b>, <b><tt>amt:<N</tt></b>, <b><tt>amt:>N</tt></b> - filter on the unsigned amount magnitude. Or with a sign before N, filter on the signed value. (Single-commodity amounts only.)
|
<li> <b><tt>amt:N</tt></b>, <b><tt>amt:<N</tt></b>, <b><tt>amt:>N</tt></b> - filter on the unsigned amount magnitude. Or with a sign before N, filter on the signed value. (Single-commodity amounts only.)
|
||||||
<li> <b><tt>cur:REGEXP</tt></b> - filter on the currency/commodity symbol (must match all of it). Dollar sign must be written as <tt>\$</tt>
|
<li> <b><tt>cur:REGEXP</tt></b> - filter on the currency/commodity symbol (must match all of it). Dollar sign must be written as <tt>\$</tt>
|
||||||
|
|||||||
@ -346,7 +346,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/01/01",
|
tdate=parsedate "2007/01/01",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="opening balance",
|
tdescription="opening balance",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -362,7 +362,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/02/01",
|
tdate=parsedate "2007/02/01",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="ayres suites",
|
tdescription="ayres suites",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -378,7 +378,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/01/02",
|
tdate=parsedate "2007/01/02",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="auto transfer to savings",
|
tdescription="auto transfer to savings",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -394,7 +394,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/01/03",
|
tdate=parsedate "2007/01/03",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="poquito mas",
|
tdescription="poquito mas",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -410,7 +410,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/01/03",
|
tdate=parsedate "2007/01/03",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="verizon",
|
tdescription="verizon",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
@ -426,7 +426,7 @@ journal7 = nulljournal {jtxns =
|
|||||||
tsourcepos=nullsourcepos,
|
tsourcepos=nullsourcepos,
|
||||||
tdate=parsedate "2007/01/03",
|
tdate=parsedate "2007/01/03",
|
||||||
tdate2=Nothing,
|
tdate2=Nothing,
|
||||||
tstatus=False,
|
tstatus=Uncleared,
|
||||||
tcode="*",
|
tcode="*",
|
||||||
tdescription="discover",
|
tdescription="discover",
|
||||||
tcomment="",
|
tcomment="",
|
||||||
|
|||||||
@ -147,7 +147,7 @@ transactionWizard es@EntryState{..} = do
|
|||||||
balancedPostingsWizard = do
|
balancedPostingsWizard = do
|
||||||
ps <- postingsWizard es2{esPostings=[]}
|
ps <- postingsWizard es2{esPostings=[]}
|
||||||
let t = nulltransaction{tdate=date
|
let t = nulltransaction{tdate=date
|
||||||
,tstatus=False
|
,tstatus=Uncleared
|
||||||
,tcode=code
|
,tcode=code
|
||||||
,tdescription=desc
|
,tdescription=desc
|
||||||
,tcomment=comment
|
,tcomment=comment
|
||||||
|
|||||||
@ -121,8 +121,9 @@ reportflags = [
|
|||||||
,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "set start date, end date, and/or reporting interval all at once (overrides the flags above)"
|
,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "set start date, end date, and/or reporting interval all at once (overrides the flags above)"
|
||||||
,flagNone ["date2","aux-date"] (setboolopt "date2") "use postings/txns' secondary dates instead"
|
,flagNone ["date2","aux-date"] (setboolopt "date2") "use postings/txns' secondary dates instead"
|
||||||
|
|
||||||
,flagNone ["cleared","C"] (setboolopt "cleared") "include only pending/cleared postings/txns"
|
,flagNone ["cleared","C"] (setboolopt "cleared") "include only cleared postings/txns"
|
||||||
,flagNone ["uncleared","U"] (setboolopt "uncleared") "include only uncleared postings/txns"
|
,flagNone ["pending"] (setboolopt "pending") "include only pending postings/txns"
|
||||||
|
,flagNone ["uncleared","U"] (setboolopt "uncleared") "include only uncleared (and pending) postings/txns"
|
||||||
,flagNone ["real","R"] (setboolopt "real") "include only non-virtual postings"
|
,flagNone ["real","R"] (setboolopt "real") "include only non-virtual postings"
|
||||||
,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/postings deeper than N"
|
,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/postings deeper than N"
|
||||||
,flagNone ["empty","E"] (setboolopt "empty") "show empty/zero things which are normally omitted"
|
,flagNone ["empty","E"] (setboolopt "empty") "show empty/zero things which are normally omitted"
|
||||||
|
|||||||
@ -100,7 +100,7 @@ transactionToCSV n t =
|
|||||||
description = tdescription t
|
description = tdescription t
|
||||||
date = showDate (tdate t)
|
date = showDate (tdate t)
|
||||||
date2 = maybe "" showDate (tdate2 t)
|
date2 = maybe "" showDate (tdate2 t)
|
||||||
status = if tstatus t then "*" else ""
|
status = show $ tstatus t
|
||||||
code = tcode t
|
code = tcode t
|
||||||
comment = chomp $ strip $ tcomment t
|
comment = chomp $ strip $ tcomment t
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ postingToCSV p =
|
|||||||
amounts
|
amounts
|
||||||
where
|
where
|
||||||
Mixed amounts = pamount p
|
Mixed amounts = pamount p
|
||||||
status = if pstatus p then "*" else ""
|
status = show $ pstatus p
|
||||||
account = showAccountName Nothing (ptype p) (paccount p)
|
account = showAccountName Nothing (ptype p) (paccount p)
|
||||||
comment = chomp $ strip $ pcomment p
|
comment = chomp $ strip $ pcomment p
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user