support reporting intervals in period expressions and register report, and -W/-M/-D

This commit is contained in:
Simon Michael 2008-12-03 23:20:38 +00:00
parent 805c2dddd3
commit 41a3fb91d5
8 changed files with 290 additions and 81 deletions

View File

@ -8,8 +8,11 @@ We represent these as a triple of strings like ("2008","12",""),
("","","tomorrow"), ("","last","week"). ("","","tomorrow"), ("","last","week").
A 'DateSpan' is the span of time between two specific calendar dates, or A 'DateSpan' is the span of time between two specific calendar dates, or
possibly an open-ended span where one or both dates are missing. We use an open-ended span where one or both dates are unspecified. (A date span
this term since "period" and "interval" are ambiguous. with both ends unspecified matches all dates.)
An 'Interval' is ledger's "reporting interval" - weekly, monthly,
quarterly, etc.
-} -}
@ -53,10 +56,32 @@ elapsedSeconds t1 t2 = realToFrac $ diffUTCTime t1 t2
dayToUTC :: Day -> UTCTime dayToUTC :: Day -> UTCTime
dayToUTC d = localTimeToUTC utc (LocalTime d midnight) dayToUTC d = localTimeToUTC utc (LocalTime d midnight)
-- | Convert a period expression to a date span using the provided reference date. -- | Split a DateSpan into one or more consecutive spans at the specified interval.
spanFromPeriodExpr refdate = fromparse . parsewith (periodexpr refdate) splitSpan :: Interval -> DateSpan -> [DateSpan]
splitSpan i (DateSpan Nothing Nothing) = [DateSpan Nothing Nothing]
splitSpan NoInterval s = [s]
splitSpan Daily s = splitspan start next s where (start,next) = (startofday,nextday)
splitSpan Weekly s = splitspan start next s where (start,next) = (startofweek,nextweek)
splitSpan Monthly s = splitspan start next s where (start,next) = (startofmonth,nextmonth)
splitSpan Quarterly s = splitspan start next s where (start,next) = (startofquarter,nextquarter)
splitSpan Yearly s = splitspan start next s where (start,next) = (startofyear,nextyear)
-- | Convert a smart date string to a date span using the provided reference date. splitspan _ _ (DateSpan Nothing Nothing) = []
splitspan startof next (DateSpan Nothing (Just e)) = [DateSpan (Just $ startof e) (Just $ next $ startof e)]
splitspan startof next (DateSpan (Just b) Nothing) = [DateSpan (Just $ startof b) (Just $ next $ startof b)]
splitspan startof next (DateSpan (Just b) (Just e))
| b >= e = []
| otherwise = [DateSpan (Just $ startof b) (Just $ next $ startof b)]
++ splitspan startof next (DateSpan (Just $ next $ startof b) (Just e))
-- | Parse a period expression to an Interval and overall DateSpan using
-- the provided reference date.
parsePeriodExpr :: Day -> String -> (Interval, DateSpan)
parsePeriodExpr refdate expr = (interval,span)
where (interval,span) = fromparse $ parsewith (periodexpr refdate) expr
-- | Convert a single smart date string to a date span using the provided
-- reference date.
spanFromSmartDateString :: Day -> String -> DateSpan spanFromSmartDateString :: Day -> String -> DateSpan
spanFromSmartDateString refdate s = spanFromSmartDate refdate sdate spanFromSmartDateString refdate s = spanFromSmartDate refdate sdate
where where
@ -135,6 +160,7 @@ fixSmartDate refdate sdate = fix sdate
prevday :: Day -> Day prevday :: Day -> Day
prevday = addDays (-1) prevday = addDays (-1)
nextday = addDays 1 nextday = addDays 1
startofday = id
thisweek = startofweek thisweek = startofweek
prevweek = startofweek . addDays (-7) prevweek = startofweek . addDays (-7)
@ -292,21 +318,78 @@ lastthisnextthing = do
] ]
return ("",r,p) return ("",r,p)
periodexpr :: Day -> Parser DateSpan periodexpr :: Day -> Parser (Interval, DateSpan)
periodexpr rdate = try (doubledateperiod rdate) <|> (singledateperiod rdate) periodexpr rdate = choice $ map try [
intervalanddateperiodexpr rdate,
intervalperiodexpr,
dateperiodexpr rdate,
(return $ (NoInterval,DateSpan Nothing Nothing))
]
doubledateperiod :: Day -> Parser DateSpan intervalanddateperiodexpr :: Day -> Parser (Interval, DateSpan)
doubledateperiod rdate = do intervalanddateperiodexpr rdate = do
string "from"
many spacenonewline many spacenonewline
i <- periodexprinterval
many spacenonewline
s <- periodexprdatespan rdate
return (i,s)
intervalperiodexpr :: Parser (Interval, DateSpan)
intervalperiodexpr = do
many spacenonewline
i <- periodexprinterval
return (i, DateSpan Nothing Nothing)
dateperiodexpr :: Day -> Parser (Interval, DateSpan)
dateperiodexpr rdate = do
many spacenonewline
s <- periodexprdatespan rdate
return (NoInterval, s)
periodexprinterval :: Parser Interval
periodexprinterval =
choice $ map try [
tryinterval "day" "daily" Daily,
tryinterval "week" "weekly" Weekly,
tryinterval "month" "monthly" Monthly,
tryinterval "quarter" "quarterly" Quarterly,
tryinterval "year" "yearly" Yearly
]
where
tryinterval s1 s2 v =
choice [try (string $ "every "++s1), try (string s2)] >> return v
periodexprdatespan :: Day -> Parser DateSpan
periodexprdatespan rdate = choice $ map try [
doubledatespan rdate,
fromdatespan rdate,
todatespan rdate,
justdatespan rdate
]
doubledatespan :: Day -> Parser DateSpan
doubledatespan rdate = do
optional (string "from" >> many spacenonewline)
b <- smartdate b <- smartdate
many spacenonewline many spacenonewline
string "to" optional (string "to" >> many spacenonewline)
many spacenonewline
e <- smartdate e <- smartdate
let span = DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e) return $ DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e)
return span
singledateperiod :: Day -> Parser DateSpan fromdatespan :: Day -> Parser DateSpan
singledateperiod rdate = smartdate >>= return . spanFromSmartDate rdate fromdatespan rdate = do
string "from" >> many spacenonewline
b <- smartdate
return $ DateSpan (Just $ fixSmartDate rdate b) Nothing
todatespan :: Day -> Parser DateSpan
todatespan rdate = do
string "to" >> many spacenonewline
e <- smartdate
return $ DateSpan Nothing (Just $ fixSmartDate rdate e)
justdatespan :: Day -> Parser DateSpan
justdatespan rdate = do
optional (string "in" >> many spacenonewline)
d <- smartdate
return $ spanFromSmartDate rdate d

View File

@ -87,3 +87,11 @@ ledgerAccountTree depth l = treemap (ledgerAccount l) $ treeprune depth $ accoun
-- | Get a ledger's tree of accounts rooted at the specified account. -- | Get a ledger's tree of accounts rooted at the specified account.
ledgerAccountTreeAt :: Ledger -> Account -> Maybe (Tree Account) ledgerAccountTreeAt :: Ledger -> Account -> Maybe (Tree Account)
ledgerAccountTreeAt l acct = subtreeat acct $ ledgerAccountTree 9999 l ledgerAccountTreeAt l acct = subtreeat acct $ ledgerAccountTree 9999 l
-- | The (explicit) date span containing all the ledger's transactions,
-- or DateSpan Nothing Nothing if there are no transactions.
ledgerDateSpan l
| null ts = DateSpan Nothing Nothing
| otherwise = DateSpan (Just $ date $ head ts) (Just $ date $ last ts)
where
ts = sortBy (comparing date) $ ledgerTransactions l

View File

@ -16,6 +16,9 @@ type SmartDate = (String,String,String)
data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Show,Ord) data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Show,Ord)
data Interval = NoInterval | Daily | Weekly | Monthly | Quarterly | Yearly
deriving (Eq,Show,Ord)
type AccountName = String type AccountName = String
data Side = L | R deriving (Eq,Show,Ord) data Side = L | R deriving (Eq,Show,Ord)

34
NOTES
View File

@ -11,30 +11,7 @@ clever tricks like the plague." --Edsger Dijkstra
* to do * to do
** errors ** errors
** features ** features
*** period expressions, for ease of use *** show empty reporting intervals with the -E option, for clarity/consistency/graphing
**** from and/or to
**** reporting intervals
***** design types
***** parse intervals
every day
every week
every monthly
every quarter
every year
every N days # N is any integer
every N weeks
every N months
every N quarters
every N years
daily
weekly
biweekly
monthly
bimonthly
quarterly
yearly
***** implement in register report
***** -W/-M/-Y
*** actual/effective entry & txn dates, for completeness *** actual/effective entry & txn dates, for completeness
*** speed *** speed
**** easy profiling **** easy profiling
@ -43,6 +20,15 @@ clever tricks like the plague." --Edsger Dijkstra
*** ~/.hledgerrc, for setting defaults *** ~/.hledgerrc, for setting defaults
*** more ledger features from README (?) *** more ledger features from README (?)
*** new features *** new features
**** support complete period syntax ?
every N days # N is any integer
every N weeks
every N months
every N quarters
every N years
biweekly
bimonthly
**** -Q quarterly interval option, for consistency/convenience ?
**** easy timelog queries when called as hours **** easy timelog queries when called as hours
**** curses gui **** curses gui
**** web gui **** web gui

View File

@ -50,6 +50,9 @@ options = [
Option [] ["options-anywhere"] (NoArg OptionsAnywhere) "allow options anywhere, use ^ to negate patterns", Option [] ["options-anywhere"] (NoArg OptionsAnywhere) "allow options anywhere, use ^ to negate patterns",
Option ['n'] ["collapse"] (NoArg Collapse) "balance report: no grand total", Option ['n'] ["collapse"] (NoArg Collapse) "balance report: no grand total",
Option ['s'] ["subtotal"] (NoArg SubTotal) "balance report: show subaccounts", Option ['s'] ["subtotal"] (NoArg SubTotal) "balance report: show subaccounts",
Option ['W'] ["weekly"] (NoArg WeeklyOpt) "register report: show weekly summary",
Option ['M'] ["monthly"] (NoArg MonthlyOpt) "register report: show monthly summary",
Option ['Y'] ["yearly"] (NoArg YearlyOpt) "register report: show yearly summary",
Option ['h'] ["help"] (NoArg Help) "show this help", Option ['h'] ["help"] (NoArg Help) "show this help",
Option ['v'] ["verbose"] (NoArg Verbose) "verbose test output", Option ['v'] ["verbose"] (NoArg Verbose) "verbose test output",
Option ['V'] ["version"] (NoArg Version) "show version" Option ['V'] ["version"] (NoArg Version) "show version"
@ -73,15 +76,24 @@ data Opt =
OptionsAnywhere | OptionsAnywhere |
Collapse | Collapse |
SubTotal | SubTotal |
WeeklyOpt |
MonthlyOpt |
YearlyOpt |
Help | Help |
Verbose | Verbose |
Version Version
deriving (Show,Eq) deriving (Show,Eq)
-- yow.. -- yow..
optsWithConstructor f opts = concatMap get opts
where get o = if f v == o then [o] else [] where v = value o
optValuesForConstructor f opts = concatMap get opts optValuesForConstructor f opts = concatMap get opts
where get o = if f v == o then [v] else [] where v = value o where get o = if f v == o then [v] else [] where v = value o
optValuesForConstructors fs opts = concatMap get opts
where get o = if any (\f -> f v == o) fs then [v] else [] where v = value o
-- | Parse the command-line arguments into ledger options, ledger command -- | Parse the command-line arguments into ledger options, ledger command
-- name, and ledger command arguments. Also any dates in the options are -- name, and ledger command arguments. Also any dates in the options are
-- converted to full YYYY/MM/DD format, while we are in the IO monad -- converted to full YYYY/MM/DD format, while we are in the IO monad
@ -109,22 +121,37 @@ fixOptDates opts = do
where fixbracketeddatestr s = "[" ++ (fixSmartDateStr t $ init $ tail s) ++ "]" where fixbracketeddatestr s = "[" ++ (fixSmartDateStr t $ init $ tail s) ++ "]"
fixopt _ o = o fixopt _ o = o
-- | Figure out the date span we should report on, based on any -- | Figure out the overall date span we should report on, based on any
-- begin/end/period options provided. This could be really smart but I'm -- begin/end/period options provided. If there is a period option, the
-- just going to look for 1. the first Period or 2. the first Begin and -- others are ignored.
-- first End.
dateSpanFromOpts :: Day -> [Opt] -> DateSpan dateSpanFromOpts :: Day -> [Opt] -> DateSpan
dateSpanFromOpts refdate opts dateSpanFromOpts refdate opts
| not $ null ps = spanFromPeriodExpr refdate $ head ps | not $ null popts = snd $ parsePeriodExpr refdate $ head popts
| otherwise = DateSpan firstb firste | otherwise = DateSpan firstb firste
where where
ps = optValuesForConstructor Period opts popts = optValuesForConstructor Period opts
firstb = listtomaybeday $ optValuesForConstructor Begin opts bopts = optValuesForConstructor Begin opts
firste = listtomaybeday $ optValuesForConstructor End opts eopts = optValuesForConstructor End opts
listtomaybeday [] = Nothing firstb = listtomaybeday bopts
listtomaybeday vs = Just $ parse $ head vs firste = listtomaybeday eopts
parse s = parsedate $ printf "%04s/%02s/%02s" y m d listtomaybeday vs = if null vs then Nothing else Just $ parse $ head vs
where (y,m,d) = fromparse $ parsewith smartdate $ s where parse = parsedate . fixSmartDateStr refdate
-- | Figure out the reporting interval, if any, specified by the options.
-- If there is a period option, the others are ignored.
intervalFromOpts :: [Opt] -> Interval
intervalFromOpts opts
| not $ null popts = fst $ parsePeriodExpr refdate $ head popts
| otherwise = case otheropts of
[] -> NoInterval
(WeeklyOpt:_) -> Weekly
(MonthlyOpt:_) -> Monthly
(YearlyOpt:_) -> Yearly
where
popts = optValuesForConstructor Period opts
otheropts = filter (`elem` [WeeklyOpt,MonthlyOpt,YearlyOpt]) opts
-- doesn't affect the interval, but parsePeriodExpr needs something
refdate = parsedate "0001/01/01"
-- | Get the value of the (first) depth option, if any. -- | Get the value of the (first) depth option, if any.
depthFromOpts :: [Opt] -> Maybe Int depthFromOpts :: [Opt] -> Maybe Int

8
README
View File

@ -158,8 +158,12 @@ Other differences
* hledger keeps differently-priced amounts of the same commodity separate, at the moment * hledger keeps differently-priced amounts of the same commodity separate, at the moment
* hledger refers to the entry and transaction "description", ledger calls it "note" * hledger refers to the entry and transaction "description", ledger calls it "note"
* hledger doesn't require a space after flags like -f * hledger doesn't require a space after flags like -f
* hledger always shows timelog balances in hours
* hledger doesn't parse all ledger file constructs (and may choke ? please report) * hledger doesn't parse all ledger file constructs (and may choke ? please report)
* hledger doesn't count an unfinished timelog session
* hledger interprets "last/this/next week" as weeks beginning on monday * hledger interprets "last/this/next week" as weeks beginning on monday
* hledger provides "--cost" as a synonym for "--basis" * hledger provides "--cost" as a synonym for "--basis"
* hledger's "weekly" reporting intervals always start on mondays
* hledger shows start and end dates of full intervals, not just the span containing data
* hledger period expressions don't support "biweekly", "bimonthly", or "every N days/weeks/..."
* hledger always shows timelog balances in hours
* hledger doesn't count an unfinished timelog session
* (disabled for now: hledger counts timelog sessions on the day they end, ledger on the day they start)

View File

@ -6,6 +6,8 @@ A ledger-compatible @register@ command.
module RegisterCommand module RegisterCommand
where where
import qualified Data.Map as Map
import Data.Map ((!))
import Ledger import Ledger
import Options import Options
@ -26,27 +28,80 @@ DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAA AAAAAAAAAAAA
@ @
-} -}
showRegisterReport :: [Opt] -> [String] -> Ledger -> String showRegisterReport :: [Opt] -> [String] -> Ledger -> String
showRegisterReport opts args l = showtxns ts nulltxn nullmixedamt showRegisterReport opts args l
| interval == NoInterval = showtxns ts nulltxn nullmixedamt
| otherwise = showtxns summaryts nulltxn nullmixedamt
where where
ts = filter matchapats $ ledgerTransactions l interval = intervalFromOpts opts
ts = filter (not . isZeroMixedAmount . amount) $ filter (matchdisplayopt dopt) $ filter matchapats $ ledgerTransactions l
matchapats t = matchpats apats $ account t matchapats t = matchpats apats $ account t
apats = fst $ parseAccountDescriptionArgs opts args apats = fst $ parseAccountDescriptionArgs opts args
matchdisplayopt Nothing t = True matchdisplayopt Nothing t = True
matchdisplayopt (Just e) t = (fromparse $ parsewith datedisplayexpr e) t matchdisplayopt (Just e) t = (fromparse $ parsewith datedisplayexpr e) t
dopt = displayFromOpts opts dopt = displayFromOpts opts
empty = Empty `elem` opts
summaryts = concat $ map (\(n,s) -> summarise n s (filter (isTransactionInDateSpan s) ts)) $ zip [1..] spans
spans = splitSpan interval (ledgerDateSpan l)
-- generate a grouped set of summary transactions for this date span
summarise :: Int -> DateSpan -> [Transaction] -> [Transaction]
summarise _ _ [] = []
summarise n (DateSpan b e) ts = summarytxns (b',e') n empty ts
where
b' = fromMaybe (date $ head ts) b
e' = fromMaybe (date $ last ts) e
-- show display-filtered transactions, one per line, with a running balance -- | Does the given transaction fall within the given date span ?
showtxns [] _ _ = "" isTransactionInDateSpan :: DateSpan -> Transaction -> Bool
showtxns (t@Transaction{amount=a}:ts) tprev bal = isTransactionInDateSpan (DateSpan Nothing Nothing) _ = True
(if (isZeroMixedAmount a || (not $ matchdisplayopt dopt t)) then "" else this) ++ showtxns ts t bal' isTransactionInDateSpan (DateSpan Nothing (Just e)) (Transaction{date=d}) = d<e
isTransactionInDateSpan (DateSpan (Just b) Nothing) (Transaction{date=d}) = d>=b
isTransactionInDateSpan (DateSpan (Just b) (Just e)) (Transaction{date=d}) = d>=b && d<e
-- | Convert a date span and a list of transactions within that date span
-- to a new list of transactions aggregated by account, which when
-- rendered by showtxns will display a summary for the date span. Both
-- ends of the date span must be specified so we pass a tuple of dates.
-- As usual with date spans the second date is exclusive, but when
-- rendering we will show the previous (inclusive) date.
-- A unique entryno value is provided so that these dummy transactions
-- will be rendered as one entry. Also the showempty flag is provided to
-- control display of zero-balance accounts.
summarytxns :: (Day,Day) -> Int -> Bool -> [Transaction] -> [Transaction]
summarytxns (b,e) entryno showempty ts = summaryts'
where
summaryts'
| showempty = summaryts
| otherwise = filter (not . isZeroMixedAmount . amount) summaryts
summaryts = [templtxn{account=a,amount=balmap ! a} | a <- anames]
templtxn = nulltxn{entryno=entryno,date=b,description="- "++(showDate eprev)}
eprev = addDays (-1) e
anames = sort $ nub $ map account ts
-- from cacheLedger:
sortedts = sortBy (comparing account) ts
groupedts = groupBy (\t1 t2 -> account t1 == account t2) sortedts
txnmap = Map.union
(Map.fromList [(account $ head g, g) | g <- groupedts])
(Map.fromList [(a,[]) | a <- anames])
txnsof = (txnmap !)
subacctsof a = filter (a `isAccountNamePrefixOf`) anames
subtxnsof a = concat [txnsof a | a <- [a] ++ subacctsof a]
balmap = Map.union
(Map.fromList [(a,(sumTransactions $ subtxnsof a)) | a <- anames])
(Map.fromList [(a,Mixed []) | a <- anames])
--
-- | Show transactions one per line, with each date/description appearing
-- only once, and a running balance.
showtxns [] _ _ = ""
showtxns (t@Transaction{amount=a}:ts) tprev bal = this ++ showtxns ts t bal'
where where
this = showtxn (t `issame` tprev) t bal' this = showtxn (t `issame` tprev) t bal'
issame t1 t2 = entryno t1 == entryno t2 issame t1 t2 = entryno t1 == entryno t2
bal' = bal + amount t bal' = bal + amount t
-- show one transaction line, with or without the entry details -- | Show one transaction line and balance with or without the entry details.
showtxn :: Bool -> Transaction -> MixedAmount -> String showtxn :: Bool -> Transaction -> MixedAmount -> String
showtxn omitdesc t b = concatBottomPadded [entrydesc ++ txn ++ " ", bal] ++ "\n" showtxn omitdesc t b = concatBottomPadded [entrydesc ++ txn ++ " ", bal] ++ "\n"
where where
entrydesc = if omitdesc then replicate 32 ' ' else printf "%s %s " date desc entrydesc = if omitdesc then replicate 32 ' ' else printf "%s %s " date desc
date = showDate $ da date = showDate $ da

View File

@ -129,6 +129,33 @@ misc_tests = TestList [
"this year" `gives` "2008/01/01" "this year" `gives` "2008/01/01"
"last year" `gives` "2007/01/01" "last year" `gives` "2007/01/01"
"next year" `gives` "2009/01/01" "next year" `gives` "2009/01/01"
,
"dateSpanFromOpts" ~: do
let todaysdate = parsedate "2008/11/26"
let opts `gives` spans = assertequal spans (show $ dateSpanFromOpts todaysdate opts)
[] `gives` "DateSpan Nothing Nothing"
[Begin "2008", End "2009"] `gives` "DateSpan (Just 2008-01-01) (Just 2009-01-01)"
[Period "in 2008"] `gives` "DateSpan (Just 2008-01-01) (Just 2009-01-01)"
[Begin "2005", End "2007",Period "in 2008"] `gives` "DateSpan (Just 2008-01-01) (Just 2009-01-01)"
,
"intervalFromOpts" ~: do
let opts `gives` interval = assertequal interval (intervalFromOpts opts)
[] `gives` NoInterval
[WeeklyOpt] `gives` Weekly
[MonthlyOpt] `gives` Monthly
[YearlyOpt] `gives` Yearly
[Period "weekly"] `gives` Weekly
[Period "monthly"] `gives` Monthly
[WeeklyOpt, Period "yearly"] `gives` Yearly
,
"period expressions" ~: do
let todaysdate = parsedate "2008/11/26"
let str `gives` result = assertequal ("Right "++result) (show $ parsewith (periodexpr todaysdate) str)
"from aug to oct" `gives` "(NoInterval,DateSpan (Just 2008-08-01) (Just 2008-10-01))"
"aug to oct" `gives` "(NoInterval,DateSpan (Just 2008-08-01) (Just 2008-10-01))"
"every day from aug to oct" `gives` "(Daily,DateSpan (Just 2008-08-01) (Just 2008-10-01))"
"daily from aug" `gives` "(Daily,DateSpan (Just 2008-08-01) Nothing)"
"every week to 2009" `gives` "(Weekly,DateSpan Nothing (Just 2009-01-01))"
] ]
balancereportacctnames_tests = TestList balancereportacctnames_tests = TestList
@ -356,8 +383,24 @@ registercommand_tests = TestList [
do do
"" `periodexprgives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"] "" `periodexprgives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
"2008" `periodexprgives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"] "2008" `periodexprgives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
-- need to get datespan into ledgerFromString, or preconvert period expressions "2007" `periodexprgives` []
-- "2007" `periodexprgives` [] "june" `periodexprgives` ["2008/06/01","2008/06/02","2008/06/03"]
let l = ledgerfromstring [] sample_ledger_str
assertequal (
"2008/01/01 - 2008/12/31 assets:cash $-2 $-2\n" ++
" assets:saving $1 $-1\n" ++
" expenses:food $1 0\n" ++
" expenses:supplies $1 $1\n" ++
" income:gifts $-1 0\n" ++
" income:salary $-1 $-1\n" ++
" liabilities:debts $1 0\n" ++
"")
(showRegisterReport [Period "yearly"] [] l)
assertequal ["2008/01/01","2008/04/01","2008/10/01"] (datesfromregister $ showRegisterReport [Period "quarterly"] [] l)
-- assertequal ["2008/01/01","2008/04/01","2008/07/01","2008/10/01"] (datesfromregister $ showRegisterReport [Period "quarterly"] [] l)
] ]
where where
expr `displayexprgives` dates = expr `displayexprgives` dates =
@ -369,9 +412,9 @@ registercommand_tests = TestList [
assertequal dates (datesfromregister r) assertequal dates (datesfromregister r)
where where
r = showRegisterReport [Period expr] [] l r = showRegisterReport [Period expr] [] l
l = ledgerfromstring [] sample_ledger_str l = ledgerfromstringwithopts [Period expr] [] refdate sample_ledger_str
refdate = parsedate "2008/11/26"
datesfromregister = filter (not . null) . map (strip . take 10) . lines datesfromregister = filter (not . null) . map (strip . take 10) . lines
------------------------------------------------------------------------------ ------------------------------------------------------------------------------