web: more account register fixes; heading shows "Balance" or "Total" appropriately

This commit is contained in:
Simon Michael 2011-06-13 19:49:34 +00:00
parent 06331c71a8
commit 1a88879734
3 changed files with 73 additions and 45 deletions

View File

@ -4,7 +4,7 @@
<th.description align=left>Description <th.description align=left>Description
<th.account align=left>Account <th.account align=left>Account
<th.amount align=right>Amount <th.amount align=right>Amount
<th.balance align=right>Balance <th.balance align=right>#{balancelabel}
$forall i <- numbered items $forall i <- numbered items
^{itemAsHtml vd i} ^{itemAsHtml vd i}

View File

@ -61,9 +61,9 @@ postJournalR = handlePost
-- | The main register view, with accounts sidebar. -- | The main register view, with accounts sidebar.
getRegisterR :: Handler RepHtml getRegisterR :: Handler RepHtml
getRegisterR = do getRegisterR = do
vd@VD{opts=opts,m=m,j=j} <- getViewData vd@VD{opts=opts,j=j} <- getViewData
let sidecontent = balanceReportAsHtml opts vd{q=""} $ balanceReport opts nullfilterspec j let sidecontent = balanceReportAsHtml opts vd{q=""} $ balanceReport opts nullfilterspec j
maincontent = registerReportAsHtml opts vd $ accountOrJournalRegisterReport opts m j maincontent = registerReportAsHtml opts vd $ accountOrJournalRegisterReport vd j
editform' = editform vd editform' = editform vd
defaultLayout $ do defaultLayout $ do
setTitle "hledger-web register" setTitle "hledger-web register"
@ -86,19 +86,19 @@ postJournalOnlyR = handlePost
-- | A simple postings view, like hledger register (with editing.) -- | A simple postings view, like hledger register (with editing.)
getRegisterOnlyR :: Handler RepHtml getRegisterOnlyR :: Handler RepHtml
getRegisterOnlyR = do getRegisterOnlyR = do
vd@VD{opts=opts,m=m,j=j} <- getViewData vd@VD{opts=opts,j=j} <- getViewData
defaultLayout $ do defaultLayout $ do
setTitle "hledger-web register only" setTitle "hledger-web register only"
addHamlet $ registerReportAsHtml opts vd $ accountOrJournalRegisterReport opts m j addHamlet $ registerReportAsHtml opts vd $ accountOrJournalRegisterReport vd j
postRegisterOnlyR :: Handler RepPlain postRegisterOnlyR :: Handler RepPlain
postRegisterOnlyR = handlePost postRegisterOnlyR = handlePost
-- temporary helper - use the new account register report when in:ACCT is specified. -- temporary helper - use the new account register report when in:ACCT is specified.
accountOrJournalRegisterReport :: [Opt] -> Matcher -> Journal -> RegisterReport accountOrJournalRegisterReport :: ViewData -> Journal -> RegisterReport
accountOrJournalRegisterReport opts m j = accountOrJournalRegisterReport VD{opts=opts,m=m,qopts=qopts} j =
case matcherInAccount m of Just a -> accountRegisterReport opts j m a case inAccount qopts of Just a -> accountRegisterReport opts j m a
Nothing -> registerReport opts nullfilterspec $ filterJournalPostings2 m j Nothing -> registerReport opts nullfilterspec $ filterJournalPostings2 m j
-- | A simple accounts view, like hledger balance. If the Accept header -- | A simple accounts view, like hledger balance. If the Accept header
-- specifies json, returns the chart of accounts as json. -- specifies json, returns the chart of accounts as json.
@ -126,10 +126,10 @@ accountUrl a = "inacct:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
-- | Render a balance report as HTML. -- | Render a balance report as HTML.
balanceReportAsHtml :: [Opt] -> ViewData -> BalanceReport -> Hamlet AppRoute balanceReportAsHtml :: [Opt] -> ViewData -> BalanceReport -> Hamlet AppRoute
balanceReportAsHtml _ vd@VD{here=here,q=q,m=m,j=j} (items,total) = $(Settings.hamletFile "balancereport") balanceReportAsHtml _ vd@VD{here=here,q=q,m=m,qopts=qopts,j=j} (items,total) = $(Settings.hamletFile "balancereport")
where where
filtering = not $ null q filtering = not $ null q
inacct = matcherInAccount m -- headMay $ filter (m `matchesInAccount`) $ journalAccountNames j inacct = inAccount qopts
itemAsHtml :: ViewData -> BalanceReportItem -> Hamlet AppRoute itemAsHtml :: ViewData -> BalanceReportItem -> Hamlet AppRoute
itemAsHtml VD{here=here,q=q} (acct, adisplay, aindent, abal) = $(Settings.hamletFile "balancereportitem") itemAsHtml VD{here=here,q=q} (acct, adisplay, aindent, abal) = $(Settings.hamletFile "balancereportitem")
where where
@ -152,7 +152,7 @@ journalReportAsHtml _ vd items = $(Settings.hamletFile "journalreport")
-- | Render a register report as HTML. -- | Render a register report as HTML.
registerReportAsHtml :: [Opt] -> ViewData -> RegisterReport -> Hamlet AppRoute registerReportAsHtml :: [Opt] -> ViewData -> RegisterReport -> Hamlet AppRoute
registerReportAsHtml _ vd items = $(Settings.hamletFile "registerreport") registerReportAsHtml _ vd (balancelabel,items) = $(Settings.hamletFile "registerreport")
where where
itemAsHtml :: ViewData -> (Int, RegisterReportItem) -> Hamlet AppRoute itemAsHtml :: ViewData -> (Int, RegisterReportItem) -> Hamlet AppRoute
itemAsHtml VD{here=here} (n, (ds, posting, b)) = $(Settings.hamletFile "registerreportitem") itemAsHtml VD{here=here} (n, (ds, posting, b)) = $(Settings.hamletFile "registerreportitem")
@ -385,13 +385,14 @@ nulltemplate = [$hamlet||]
-- | A bundle of data useful for handlers and their templates. -- | A bundle of data useful for handlers and their templates.
data ViewData = VD { data ViewData = VD {
opts :: [Opt] -- ^ command-line options at startup opts :: [Opt] -- ^ command-line options at startup
,q :: String -- ^ current q (query) parameter ,q :: String -- ^ current q parameter (the query expression for filtering transactions)
,m :: Matcher -- ^ a search/filter expression based on the above ,m :: Matcher -- ^ a matcher parsed from the query expr
,j :: Journal -- ^ the up-to-date parsed unfiltered journal ,qopts :: [QueryOpt] -- ^ query options parsed from the query expr
,today :: Day -- ^ the current day ,j :: Journal -- ^ the up-to-date parsed unfiltered journal
,here :: AppRoute -- ^ the current route ,today :: Day -- ^ the current day
,msg :: Maybe Html -- ^ the current UI message if any, possibly from the current request ,here :: AppRoute -- ^ the current route
,msg :: Maybe Html -- ^ the current UI message if any, possibly from the current request
} }
mkvd :: ViewData mkvd :: ViewData
@ -399,6 +400,7 @@ mkvd = VD {
opts = [] opts = []
,q = "" ,q = ""
,m = MatchAny ,m = MatchAny
,qopts = []
,j = nulljournal ,j = nulljournal
,today = ModifiedJulianDay 0 ,today = ModifiedJulianDay 0
,here = RootR ,here = RootR
@ -415,8 +417,8 @@ getViewData = do
Just here' <- getCurrentRoute Just here' <- getCurrentRoute
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
q <- getParameter "q" q <- getParameter "q"
let m = parseMatcher today q let (m,qopts) = parseQuery today q
return mkvd{opts=opts, q=q, m=m, j=j, today=today, here=here', msg=msg} return mkvd{opts=opts, q=q, m=m, qopts=qopts, j=j, today=today, here=here', msg=msg}
where where
-- | Update our copy of the journal if the file changed. If there is an -- | Update our copy of the journal if the file changed. If there is an
-- error while reloading, keep the old one and return the error, and set a -- error while reloading, keep the old one and return the error, and set a

View File

@ -37,7 +37,10 @@ import Hledger.Utils.UTF8 (putStr)
-- | A register report is a list of postings to an account or set of -- | A register report is a list of postings to an account or set of
-- accounts, with a running total. Postings may be actual postings, or -- accounts, with a running total. Postings may be actual postings, or
-- virtual postings aggregated over a reporting interval. -- virtual postings aggregated over a reporting interval.
type RegisterReport = [RegisterReportItem] -- line items, one per posting -- And also some heading info.
type RegisterReport = (String -- a possibly null label for the running balance column
,[RegisterReportItem] -- line items, one per posting
)
-- | The data for a single register report line item, representing one posting. -- | The data for a single register report line item, representing one posting.
type RegisterReportItem = (Maybe (Day, String) -- transaction date and description if this is the first posting type RegisterReportItem = (Maybe (Day, String) -- transaction date and description if this is the first posting
@ -53,7 +56,7 @@ register opts args j = do
-- | Render a register report as plain text suitable for console output. -- | Render a register report as plain text suitable for console output.
registerReportAsText :: [Opt] -> RegisterReport -> String registerReportAsText :: [Opt] -> RegisterReport -> String
registerReportAsText opts = unlines . map (registerReportItemAsText opts) registerReportAsText opts = unlines . map (registerReportItemAsText opts) . snd
-- | Render one register report line item as plain text. Eg: -- | Render one register report line item as plain text. Eg:
-- @ -- @
@ -84,7 +87,7 @@ showPostingWithBalanceForVty showtxninfo p b = registerReportItemAsText [] $ mki
-- ledger's register command; for an account-specific register see -- ledger's register command; for an account-specific register see
-- accountRegisterReport. -- accountRegisterReport.
registerReport :: [Opt] -> FilterSpec -> Journal -> RegisterReport registerReport :: [Opt] -> FilterSpec -> Journal -> RegisterReport
registerReport opts fspec j = postingsToRegisterReportItems ps nullposting startbal (+) registerReport opts fspec j = (totallabel,postingsToRegisterReportItems ps nullposting startbal (+))
where where
ps | interval == NoInterval = displayableps ps | interval == NoInterval = displayableps
| otherwise = summarisePostingsByInterval interval depth empty filterspan displayableps | otherwise = summarisePostingsByInterval interval depth empty filterspan displayableps
@ -100,30 +103,53 @@ registerReport opts fspec j = postingsToRegisterReportItems ps nullposting start
(interval, depth, empty) = (intervalFromOpts opts, depthFromOpts opts, Empty `elem` opts) (interval, depth, empty) = (intervalFromOpts opts, depthFromOpts opts, Empty `elem` opts)
-- | Get an account register report with the specified options for this -- | Get an account register report with the specified options for this
-- journal. An account register report is like a postings register report -- journal. An account register report is like the traditional account
-- except it is focussed on one account only, it shows the other postings -- register seen in bank statements and personal finance programs. It is
-- in the transactions for this account, and it shows the accurate -- focussed on one account only; it shows this account's transactions'
-- historic balance for this account. -- postings to other accounts; and if there is no transaction filtering in
-- Does not yet handle reporting intervals. -- effect other than a start date, it shows a historically-accurate
-- running balance for this account. Once additional filters are applied,
-- the running balance reverts to a running total starting at 0.
--
-- Does not handle reporting intervals.
--
accountRegisterReport :: [Opt] -> Journal -> Matcher -> AccountName -> RegisterReport accountRegisterReport :: [Opt] -> Journal -> Matcher -> AccountName -> RegisterReport
accountRegisterReport _ j m a = postingsToRegisterReportItems ps nullposting startbal (-) accountRegisterReport opts j m a = (label, postingsToRegisterReportItems displayps nullposting startbal (-))
where where
ps = displayps -- displayps' | interval == NoInterval = displayps
-- ps | interval == NoInterval = displayps -- | otherwise = summarisePostingsByInterval interval depth empty filterspan displayps
-- | otherwise = summarisePostingsByInterval interval depth empty filterspan displayps
-- transactions affecting this account
a' = accountNameToAccountOnlyRegex a a' = accountNameToAccountOnlyRegex a
-- XXX priorps and displayps not right due to inacct: still in matcher thisacctmatcher = MatchAcct True a'
-- postings to display: this account's transactions' "other" postings, filtered ts = filter (matchesTransaction thisacctmatcher) $ jtxns j
-- same matcher used on transactions then again on postings, ok I think
ts = filter (matchesTransaction (MatchInAcct True a')) $ jtxns j -- all postings in these transactions
displaymatcher = (MatchAnd [MatchAcct False a', m]) ps = transactionsPostings ts
displayps = filter (matchesPosting displaymatcher) $ transactionsPostings ts
-- starting balance: sum of this account's unfiltered postings prior to the specified start date, if any -- starting balance: if we are filtering by a start date and nothing else
priormatcher = case matcherStartDate m of -- else, the sum of postings to this account before it; otherwise zero.
Nothing -> MatchNone (startbal,label) | matcherIsNull m = (nullmixedamt,balancelabel)
d -> MatchAnd [MatchDate True (DateSpan Nothing d), MatchAcct True a'] | matcherIsStartDateOnly effective m = (sumPostings priorps,balancelabel)
priorps = filter (matchesPosting priormatcher) $ journalPostings j | otherwise = (nullmixedamt,totallabel)
startbal = sumPostings priorps where
priorps = -- ltrace "priorps" $
filter (matchesPosting
(-- ltrace "priormatcher" $
MatchAnd [thisacctmatcher, tostartdatematcher])) ps
tostartdatematcher = MatchDate True (DateSpan Nothing startdate)
startdate = matcherStartDate effective m
effective = Effective `elem` opts
-- postings to display: this account's transactions' "other" postings, with any additional filter applied
-- XXX would be better to collapse multiple postings from one txn into one (expandable) "split" item
displayps = -- ltrace "displayps" $
filter (matchesPosting $
-- ltrace "displaymatcher" $
(MatchAnd [negateMatcher thisacctmatcher, m])) ps
totallabel = "Total"
balancelabel = "Balance"
-- | Generate register report line items. -- | Generate register report line items.
postingsToRegisterReportItems :: [Posting] -> Posting -> MixedAmount -> (MixedAmount -> MixedAmount -> MixedAmount) -> [RegisterReportItem] postingsToRegisterReportItems :: [Posting] -> Posting -> MixedAmount -> (MixedAmount -> MixedAmount -> MixedAmount) -> [RegisterReportItem]