{-| A ledger-compatible @register@ command. -} module Commands.Register where import Prelude hiding (putStr) import Ledger import Options import System.IO.UTF8 -- | Print a register report. register :: [Opt] -> [String] -> Ledger -> IO () register opts args l = do t <- getCurrentLocalTime putStr $ showRegisterReport opts (optsToFilterSpec opts args t) l -- | Generate the register report, which is a list of postings with transaction -- info and a running balance. showRegisterReport :: [Opt] -> FilterSpec -> Ledger -> String showRegisterReport opts filterspec l | interval == NoInterval = showpostings displayedps nullposting startbal | otherwise = showpostings summaryps nullposting startbal where startbal = sumPostings precedingps (displayedps, _) = span displayExprMatches restofps (precedingps, restofps) = break displayExprMatches sortedps sortedps = sortBy (comparing postingDate) ps ps = journalPostings $ filterJournalPostings filterspec $ journal l summaryps = concatMap summarisespan spans summarisespan s = summarisePostingsInDateSpan s depth empty (postingsinspan s) postingsinspan s = filter (isPostingInDateSpan s) displayedps spans = splitSpan interval (postingsDateSpan displayedps) interval = intervalFromOpts opts empty = Empty `elem` opts depth = depthFromOpts opts dispexpr = displayExprFromOpts opts displayExprMatches p = case dispexpr of Nothing -> True Just e -> (fromparse $ parsewith datedisplayexpr e) p -- | Given a date span (representing a reporting interval) and a list of -- postings within it: aggregate the postings so there is only one per -- account, and adjust their date/description so that they will render -- as a summary for this interval. -- -- As usual with date spans the end date is exclusive, but for display -- purposes we show the previous day as end date, like ledger. -- -- When a depth argument is present, postings to accounts of greater -- depth are aggregated where possible. -- -- The showempty flag forces the display of a zero-posting span -- and also zero-posting accounts within the span. summarisePostingsInDateSpan :: DateSpan -> Maybe Int -> Bool -> [Posting] -> [Posting] summarisePostingsInDateSpan (DateSpan b e) depth showempty ps | null ps && showempty = [p] | null ps = [] | otherwise = summaryps' where postingwithinfo date desc = nullposting{ptransaction=Just nulltransaction{tdate=date,tdescription=desc}} p = postingwithinfo b' ("- "++ showDate (addDays (-1) e')) b' = fromMaybe (postingDate $ head ps) b e' = fromMaybe (postingDate $ last ps) e summaryps' | showempty = summaryps | otherwise = filter (not . isZeroMixedAmount . pamount) summaryps anames = sort $ nub $ map paccount ps -- aggregate balances by account, like cacheLedger, then do depth-clipping (_,_,exclbalof,inclbalof) = groupPostings ps clippedanames = nub $ map (clipAccountName d) anames isclipped a = accountNameLevel a >= d d = fromMaybe 99999 $ depth balancetoshowfor a = (if isclipped a then inclbalof else exclbalof) (if null a then "top" else a) summaryps = [p{paccount=a,pamount=balancetoshowfor a} | a <- clippedanames] {- | Show postings one per line, plus transaction info for the first posting of each transaction, and a running balance. Eg: @ date (10) description (20) account (22) amount (11) balance (12) DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAA AAAAAAAAAAAA aaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAA AAAAAAAAAAAA @ -} showpostings :: [Posting] -> Posting -> MixedAmount -> String showpostings [] _ _ = "" showpostings (p:ps) pprev bal = this ++ showpostings ps p bal' where this = showposting isfirst p bal' isfirst = ptransaction p /= ptransaction pprev bal' = bal + pamount p -- | Show one posting and running balance, with or without transaction info. showposting :: Bool -> Posting -> MixedAmount -> String showposting withtxninfo p b = concatBottomPadded [txninfo ++ pstr ++ " ", bal] ++ "\n" where ledger3ishlayout = False datedescwidth = if ledger3ishlayout then 34 else 32 txninfo = if withtxninfo then printf "%s %s " date desc else replicate datedescwidth ' ' date = showDate da datewidth = 10 descwidth = datedescwidth - datewidth - 2 desc = printf ("%-"++(show descwidth)++"s") $ elideRight descwidth de :: String pstr = showPostingWithoutPrice p bal = padleft 12 (showMixedAmountOrZeroWithoutPrice b) (da,de) = case ptransaction p of Just (Transaction{tdate=da',tdescription=de'}) -> (da',de') Nothing -> (nulldate,"")