web: make journal register work like account registers; show multiple postings
This commit is contained in:
		
							parent
							
								
									1fe21904eb
								
							
						
					
					
						commit
						ec426d620c
					
				@ -14,6 +14,7 @@ $(document).ready(function() {
 | 
				
			|||||||
    /* set up various show/hide toggles */
 | 
					    /* set up various show/hide toggles */
 | 
				
			||||||
    $('#search-help-link').click(function() { $('#search-help').slideToggle('fast'); });
 | 
					    $('#search-help-link').click(function() { $('#search-help').slideToggle('fast'); });
 | 
				
			||||||
    $('#accounts-toggle-link').click(function() { $('#accounts').slideToggle('fast'); });
 | 
					    $('#accounts-toggle-link').click(function() { $('#accounts').slideToggle('fast'); });
 | 
				
			||||||
 | 
					    $('.postings-toggle-link').click(function() { $(this).parent().parent().nextUntil(':not(.posting)').toggle(); event.preventDefault(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -112,6 +112,9 @@ table.registerreport        { border-spacing:0; }
 | 
				
			|||||||
table.registerreport tr     { vertical-align:top; }
 | 
					table.registerreport tr     { vertical-align:top; }
 | 
				
			||||||
table.registerreport td     { padding-bottom:0.2em; }
 | 
					table.registerreport td     { padding-bottom:0.2em; }
 | 
				
			||||||
table.registerreport .date  { white-space:nowrap; }
 | 
					table.registerreport .date  { white-space:nowrap; }
 | 
				
			||||||
 | 
					table.registerreport tr.posting { display:none; font-size:smaller; }
 | 
				
			||||||
 | 
					table.registerreport tr.posting .account  { padding-left:1.5em; }
 | 
				
			||||||
 | 
					table.registerreport tr.posting .amount  { padding-right:0.5em; }
 | 
				
			||||||
tr.firstposting td          { }
 | 
					tr.firstposting td          { }
 | 
				
			||||||
tr.newday td                { border-top: 1px solid black; }
 | 
					tr.newday td                { border-top: 1px solid black; }
 | 
				
			||||||
tr.newmonth td              { border-top: 2px solid black; }
 | 
					tr.newmonth td              { border-top: 2px solid black; }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,24 @@
 | 
				
			|||||||
<tr.item.#{evenodd}.#{firstposting}.#{datetransition}
 | 
					<tr.item.#{evenodd}.#{firstposting}.#{datetransition}
 | 
				
			||||||
 <td.date>#{date}
 | 
					 <td.date>#{date}
 | 
				
			||||||
 <td.description title="#{desc}">#{elideRight 30 desc}
 | 
					 <td.description title="#{show t}">#{elideRight 30 desc}
 | 
				
			||||||
 <td.account><a href="@?{accturl}" title="#{acct}">#{elideRight 40 acct}
 | 
					 <td.account>
 | 
				
			||||||
 <td.amount align=right>#{mixedAmountAsHtml amt}
 | 
					  $if split
 | 
				
			||||||
 | 
					   <a title="#{acct}"
 | 
				
			||||||
 | 
					    #{elideRight 40 acct}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					   <a.postings-toggle-link.togglelink href="#" title="Toggle postings"
 | 
				
			||||||
 | 
					    [+/-]
 | 
				
			||||||
 | 
					  $else
 | 
				
			||||||
 | 
					   <a href="@?{acctquery}" title="Go to #{acct}">#{elideRight 40 acct}
 | 
				
			||||||
 | 
					 <td.amount align=right>
 | 
				
			||||||
 | 
					  $if showamt
 | 
				
			||||||
 | 
					   #{mixedAmountAsHtml amt}
 | 
				
			||||||
 <td.balance align=right>#{mixedAmountAsHtml bal}
 | 
					 <td.balance align=right>#{mixedAmountAsHtml bal}
 | 
				
			||||||
 | 
					$if split
 | 
				
			||||||
 | 
					 $forall p <- tpostings t'
 | 
				
			||||||
 | 
					  <tr.item.#{evenodd}.posting
 | 
				
			||||||
 | 
					   <td.date
 | 
				
			||||||
 | 
					   <td.description
 | 
				
			||||||
 | 
					   <td.account> <a href="@?{accountUrl here $ paccount p}" title="#{stringIfLongerThan 40 $ paccount p}">#{elideRight 40 $ paccount p}
 | 
				
			||||||
 | 
					   <td.amount align=right>#{mixedAmountAsHtml $ pamount p}
 | 
				
			||||||
 | 
					   <td.balance align=right>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,13 @@
 | 
				
			|||||||
<tr.item.#{inacctclass}
 | 
					<tr.item.#{inacctclass}
 | 
				
			||||||
 <td.account.#{depthclass}
 | 
					 <td.account.#{depthclass}
 | 
				
			||||||
  #{indent}
 | 
					  #{indent}
 | 
				
			||||||
  <a href="@?{accturl}">#{adisplay}
 | 
					  <a href="@?{acctquery}" title="Focus on this account">#{adisplay}
 | 
				
			||||||
  <span.accountextralinks
 | 
					  <span.accountextralinks
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
   <a href="@?{acctsurl}">+subs
 | 
					   <a href="@?{acctsquery}" title="Focus on this account and sub-accounts">+subs
 | 
				
			||||||
 | 
					   <!--
 | 
				
			||||||
     
 | 
					     
 | 
				
			||||||
   <a href="@?{acctsonlyurl}">-others
 | 
					    <a href="@?{acctsonlyquery}" title="Focus on this account and sub-accounts and hide others">-others -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 <td.balance align=right>#{mixedAmountAsHtml abal}
 | 
					 <td.balance align=right>#{mixedAmountAsHtml abal}
 | 
				
			||||||
 <td.numpostings align=right>(#{numpostingsinacct acct})
 | 
					 <td.numpostings align=right>(#{numpostingsinacct acct})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
<tr.item.#{evenodd}.#{firstposting}.#{datetransition}
 | 
					<tr.item.#{evenodd}.#{firstposting}.#{datetransition}
 | 
				
			||||||
 <td.date>#{date}
 | 
					 <td.date>#{date}
 | 
				
			||||||
 <td.description title="#{desc}">#{elideRight 30 desc}
 | 
					 <td.description title="#{desc}">#{elideRight 30 desc}
 | 
				
			||||||
 <td.account><a href="@?{accturl}" title="#{acct}">#{elideRight 40 acct}
 | 
					 <td.account><a href="@?{acctquery}" title="#{acct}">#{elideRight 40 acct}
 | 
				
			||||||
 <td.amount align=right>#{mixedAmountAsHtml $ pamount posting}
 | 
					 <td.amount align=right>#{mixedAmountAsHtml $ pamount posting}
 | 
				
			||||||
 <td.balance align=right>#{mixedAmountAsHtml b}
 | 
					 <td.balance align=right>#{mixedAmountAsHtml b}
 | 
				
			||||||
 | 
				
			|||||||
@ -65,7 +65,7 @@ getRegisterR = do
 | 
				
			|||||||
  let sidecontent = balanceReportAsHtml  opts vd{q=""} $ balanceReport opts nullfilterspec j
 | 
					  let sidecontent = balanceReportAsHtml  opts vd{q=""} $ balanceReport opts nullfilterspec j
 | 
				
			||||||
      maincontent =
 | 
					      maincontent =
 | 
				
			||||||
          case inAccountMatcher qopts of Just m' -> accountRegisterReportAsHtml opts vd $ accountRegisterReport opts j m m'
 | 
					          case inAccountMatcher qopts of Just m' -> accountRegisterReportAsHtml opts vd $ accountRegisterReport opts j m m'
 | 
				
			||||||
                                         Nothing -> postingRegisterReportAsHtml opts vd $ postingRegisterReport opts nullfilterspec $ filterJournalPostings2 m j
 | 
					                                         Nothing -> accountRegisterReportAsHtml opts vd $ journalRegisterReport opts j m
 | 
				
			||||||
      editform' = editform vd
 | 
					      editform' = editform vd
 | 
				
			||||||
  defaultLayout $ do
 | 
					  defaultLayout $ do
 | 
				
			||||||
      setTitle "hledger-web register"
 | 
					      setTitle "hledger-web register"
 | 
				
			||||||
@ -93,7 +93,7 @@ getRegisterOnlyR = do
 | 
				
			|||||||
      setTitle "hledger-web register only"
 | 
					      setTitle "hledger-web register only"
 | 
				
			||||||
      addHamlet $
 | 
					      addHamlet $
 | 
				
			||||||
          case inAccountMatcher qopts of Just m' -> accountRegisterReportAsHtml opts vd $ accountRegisterReport opts j m m'
 | 
					          case inAccountMatcher qopts of Just m' -> accountRegisterReportAsHtml opts vd $ accountRegisterReport opts j m m'
 | 
				
			||||||
                                         Nothing -> postingRegisterReportAsHtml opts vd $ postingRegisterReport opts nullfilterspec $ filterJournalPostings2 m j
 | 
					                                         Nothing -> accountRegisterReportAsHtml opts vd $ journalRegisterReport opts j m
 | 
				
			||||||
 | 
					
 | 
				
			||||||
postRegisterOnlyR :: Handler RepPlain
 | 
					postRegisterOnlyR :: Handler RepPlain
 | 
				
			||||||
postRegisterOnlyR = handlePost
 | 
					postRegisterOnlyR = handlePost
 | 
				
			||||||
@ -119,34 +119,37 @@ getAccountsJsonR = do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- helpers
 | 
					-- helpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
accountUrl :: String -> String
 | 
					accountQuery :: AccountName -> String
 | 
				
			||||||
accountUrl a = "inacct:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
					accountQuery a = "inacct:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
accountsUrl :: String -> String
 | 
					accountsQuery :: AccountName -> String
 | 
				
			||||||
accountsUrl a = "inaccts:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
					accountsQuery a = "inaccts:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
accountsOnlyUrl :: String -> String
 | 
					accountsOnlyQuery :: AccountName -> String
 | 
				
			||||||
accountsOnlyUrl a = "inacctsonly:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
					accountsOnlyQuery a = "inacctsonly:" ++ quoteIfSpaced a -- (accountNameToAccountRegex a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- accountUrl :: AppRoute -> AccountName -> (AppRoute,[(String,ByteString)])
 | 
				
			||||||
 | 
					accountUrl r a = (r, [("q",pack $ accountQuery 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,qopts=qopts,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
 | 
				
			||||||
   l = journalToLedger nullfilterspec j
 | 
					   l = journalToLedger nullfilterspec j
 | 
				
			||||||
   numpostingsinacct = length . apostings . ledgerAccount l
 | 
					 | 
				
			||||||
   inacctmatcher = inAccountMatcher qopts
 | 
					   inacctmatcher = inAccountMatcher qopts
 | 
				
			||||||
   allaccts = isNothing inacctmatcher
 | 
					   allaccts = isNothing inacctmatcher
 | 
				
			||||||
   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
 | 
				
			||||||
 | 
					       numpostings = length $ apostings $ ledgerAccount l acct
 | 
				
			||||||
       depthclass = "depth"++show aindent
 | 
					       depthclass = "depth"++show aindent
 | 
				
			||||||
       inacctclass = case inacctmatcher of
 | 
					       inacctclass = case inacctmatcher of
 | 
				
			||||||
                       Just m -> if m `matchesAccount` acct then "inacct" else "notinacct"
 | 
					                       Just m -> if m `matchesAccount` acct then "inacct" else "notinacct"
 | 
				
			||||||
                       Nothing -> "" :: String
 | 
					                       Nothing -> "" :: String
 | 
				
			||||||
       indent = preEscapedString $ concat $ replicate (2 * aindent) " "
 | 
					       indent = preEscapedString $ concat $ replicate (2 * aindent) " "
 | 
				
			||||||
       accturl = (here, [("q", pack $ accountUrl acct)])
 | 
					       acctquery = (here, [("q", pack $ accountQuery acct)])
 | 
				
			||||||
       acctsurl = (here, [("q", pack $ accountsUrl acct)])
 | 
					       acctsquery = (here, [("q", pack $ accountsQuery acct)])
 | 
				
			||||||
       acctsonlyurl = (here, [("q", pack $ accountsOnlyUrl acct)])
 | 
					       acctsonlyquery = (here, [("q", pack $ accountsOnlyQuery acct)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Render a journal report as HTML.
 | 
					-- | Render a journal report as HTML.
 | 
				
			||||||
journalReportAsHtml :: [Opt] -> ViewData -> JournalReport -> Hamlet AppRoute
 | 
					journalReportAsHtml :: [Opt] -> ViewData -> JournalReport -> Hamlet AppRoute
 | 
				
			||||||
@ -158,54 +161,23 @@ journalReportAsHtml _ vd items = $(Settings.hamletFile "journalreport")
 | 
				
			|||||||
       evenodd = if even n then "even" else "odd" :: String
 | 
					       evenodd = if even n then "even" else "odd" :: String
 | 
				
			||||||
       txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
 | 
					       txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Render a register report as HTML.
 | 
					 | 
				
			||||||
-- Journal-wide postings register, when no account has focus.
 | 
					 | 
				
			||||||
postingRegisterReportAsHtml :: [Opt] -> ViewData -> PostingRegisterReport -> Hamlet AppRoute
 | 
					 | 
				
			||||||
postingRegisterReportAsHtml _ vd (balancelabel,items) = $(Settings.hamletFile "postingregisterreport")
 | 
					 | 
				
			||||||
 where
 | 
					 | 
				
			||||||
   itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, PostingRegisterReportItem) -> Hamlet AppRoute
 | 
					 | 
				
			||||||
   itemAsHtml VD{here=here} (n, newd, newm, newy, (ds, posting, b)) = $(Settings.hamletFile "postingregisterreportitem")
 | 
					 | 
				
			||||||
     where
 | 
					 | 
				
			||||||
       evenodd = if even n then "even" else "odd" :: String
 | 
					 | 
				
			||||||
       datetransition | newm = "newmonth"
 | 
					 | 
				
			||||||
                      | newd = "newday"
 | 
					 | 
				
			||||||
                      | otherwise = "" :: String
 | 
					 | 
				
			||||||
       (firstposting, date, desc) = case ds of Just (da, de) -> ("firstposting", show da, de)
 | 
					 | 
				
			||||||
                                               Nothing -> ("", "", "") :: (String,String,String)
 | 
					 | 
				
			||||||
       acct = paccount posting
 | 
					 | 
				
			||||||
       accturl = (here, [("q", pack $ accountUrl acct)])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- Add incrementing transaction numbers to a list of register report items
 | 
					 | 
				
			||||||
-- starting at 1.  Also add three flags that are true if the date, month,
 | 
					 | 
				
			||||||
-- and year is different from the previous item's.
 | 
					 | 
				
			||||||
numberPostingRegisterReportItems :: [PostingRegisterReportItem] -> [(Int,Bool,Bool,Bool,PostingRegisterReportItem)]
 | 
					 | 
				
			||||||
numberPostingRegisterReportItems [] = []
 | 
					 | 
				
			||||||
numberPostingRegisterReportItems is = number 0 nulldate is
 | 
					 | 
				
			||||||
  where
 | 
					 | 
				
			||||||
    number :: Int -> Day -> [PostingRegisterReportItem] -> [(Int,Bool,Bool,Bool,PostingRegisterReportItem)]
 | 
					 | 
				
			||||||
    number _ _ [] = []
 | 
					 | 
				
			||||||
    number n prevd (i@(Nothing, _, _)   :is)  = (n,False,False,False,i)    :(number n prevd is)
 | 
					 | 
				
			||||||
    number n prevd (i@(Just (d,_), _, _):is)  = (n+1,newday,newmonth,newyear,i):(number (n+1) d is)
 | 
					 | 
				
			||||||
        where
 | 
					 | 
				
			||||||
          newday = d/=prevd
 | 
					 | 
				
			||||||
          newmonth = dm/=prevdm || dy/=prevdy
 | 
					 | 
				
			||||||
          newyear = dy/=prevdy
 | 
					 | 
				
			||||||
          (dy,dm,_) = toGregorian d
 | 
					 | 
				
			||||||
          (prevdy,prevdm,_) = toGregorian prevd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- Account-specific transaction register, when an account is focussed.
 | 
					-- Account-specific transaction register, when an account is focussed.
 | 
				
			||||||
accountRegisterReportAsHtml :: [Opt] -> ViewData -> AccountRegisterReport -> Hamlet AppRoute
 | 
					accountRegisterReportAsHtml :: [Opt] -> ViewData -> AccountRegisterReport -> Hamlet AppRoute
 | 
				
			||||||
accountRegisterReportAsHtml _ vd (balancelabel,items) = $(Settings.hamletFile "accountregisterreport")
 | 
					accountRegisterReportAsHtml _ vd (balancelabel,items) = $(Settings.hamletFile "accountregisterreport")
 | 
				
			||||||
 where
 | 
					 where
 | 
				
			||||||
   itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, AccountRegisterReportItem) -> Hamlet AppRoute
 | 
					   itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, AccountRegisterReportItem) -> Hamlet AppRoute
 | 
				
			||||||
   itemAsHtml VD{here=here} (n, newd, newm, newy, (t, acct, amt, bal)) = $(Settings.hamletFile "accountregisterreportitem")
 | 
					   itemAsHtml VD{here=here} (n, newd, newm, newy, (t, t', split, acct, amt, bal)) = $(Settings.hamletFile "accountregisterreportitem")
 | 
				
			||||||
     where
 | 
					     where
 | 
				
			||||||
       evenodd = if even n then "even" else "odd" :: String
 | 
					       evenodd = if even n then "even" else "odd" :: String
 | 
				
			||||||
       datetransition | newm = "newmonth"
 | 
					       datetransition | newm = "newmonth"
 | 
				
			||||||
                      | newd = "newday"
 | 
					                      | newd = "newday"
 | 
				
			||||||
                      | otherwise = "" :: String
 | 
					                      | otherwise = "" :: String
 | 
				
			||||||
       (firstposting, date, desc) = (False, show $ tdate t, tdescription t)
 | 
					       (firstposting, date, desc) = (False, show $ tdate t, tdescription t)
 | 
				
			||||||
       accturl = (here, [("q", pack $ accountUrl acct)])
 | 
					       acctquery = (here, [("q", pack $ accountQuery acct)])
 | 
				
			||||||
 | 
					       showamt = not split || not (isZeroMixedAmount amt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stringIfLongerThan :: Int -> String -> String
 | 
				
			||||||
 | 
					stringIfLongerThan n s = if length s > n then s else ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
numberAccountRegisterReportItems :: [AccountRegisterReportItem] -> [(Int,Bool,Bool,Bool,AccountRegisterReportItem)]
 | 
					numberAccountRegisterReportItems :: [AccountRegisterReportItem] -> [(Int,Bool,Bool,Bool,AccountRegisterReportItem)]
 | 
				
			||||||
numberAccountRegisterReportItems [] = []
 | 
					numberAccountRegisterReportItems [] = []
 | 
				
			||||||
@ -213,7 +185,7 @@ numberAccountRegisterReportItems is = number 0 nulldate is
 | 
				
			|||||||
  where
 | 
					  where
 | 
				
			||||||
    number :: Int -> Day -> [AccountRegisterReportItem] -> [(Int,Bool,Bool,Bool,AccountRegisterReportItem)]
 | 
					    number :: Int -> Day -> [AccountRegisterReportItem] -> [(Int,Bool,Bool,Bool,AccountRegisterReportItem)]
 | 
				
			||||||
    number _ _ [] = []
 | 
					    number _ _ [] = []
 | 
				
			||||||
    number n prevd (i@(Transaction{tdate=d},_,_,_):is)  = (n+1,newday,newmonth,newyear,i):(number (n+1) d is)
 | 
					    number n prevd (i@(Transaction{tdate=d},_,_,_,_,_):is)  = (n+1,newday,newmonth,newyear,i):(number (n+1) d is)
 | 
				
			||||||
        where
 | 
					        where
 | 
				
			||||||
          newday = d/=prevd
 | 
					          newday = d/=prevd
 | 
				
			||||||
          newmonth = dm/=prevdm || dy/=prevdy
 | 
					          newmonth = dm/=prevdm || dy/=prevdy
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ module Hledger.Cli.Register (
 | 
				
			|||||||
 ,register
 | 
					 ,register
 | 
				
			||||||
 ,postingRegisterReport
 | 
					 ,postingRegisterReport
 | 
				
			||||||
 ,accountRegisterReport
 | 
					 ,accountRegisterReport
 | 
				
			||||||
 | 
					 ,journalRegisterReport
 | 
				
			||||||
 ,postingRegisterReportAsText
 | 
					 ,postingRegisterReportAsText
 | 
				
			||||||
 ,showPostingWithBalanceForVty
 | 
					 ,showPostingWithBalanceForVty
 | 
				
			||||||
 ,tests_Hledger_Cli_Register
 | 
					 ,tests_Hledger_Cli_Register
 | 
				
			||||||
@ -58,6 +59,8 @@ type AccountRegisterReport = (String                      -- label for the balan
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- | A single account register line item, representing one transaction to/from the focussed account.
 | 
					-- | A single account register line item, representing one transaction to/from the focussed account.
 | 
				
			||||||
type AccountRegisterReportItem = (Transaction -- the corresponding transaction
 | 
					type AccountRegisterReportItem = (Transaction -- the corresponding transaction
 | 
				
			||||||
 | 
					                                 ,Transaction -- the transaction with postings to the focussed account removed
 | 
				
			||||||
 | 
					                                 ,Bool        -- is this a split (more than one other-account posting) ?
 | 
				
			||||||
                                 ,String      -- the (possibly aggregated) account info to display
 | 
					                                 ,String      -- the (possibly aggregated) account info to display
 | 
				
			||||||
                                 ,MixedAmount -- the (possibly aggregated) amount to display (sum of the other-account postings)
 | 
					                                 ,MixedAmount -- the (possibly aggregated) amount to display (sum of the other-account postings)
 | 
				
			||||||
                                 ,MixedAmount -- the running balance for the focussed account after this transaction
 | 
					                                 ,MixedAmount -- the running balance for the focussed account after this transaction
 | 
				
			||||||
@ -103,7 +106,7 @@ balancelabel = "Balance"
 | 
				
			|||||||
-- | Get a ledger-style posting register report, with the specified options,
 | 
					-- | Get a ledger-style posting register report, with the specified options,
 | 
				
			||||||
-- for the whole journal. See also "accountRegisterReport".
 | 
					-- for the whole journal. See also "accountRegisterReport".
 | 
				
			||||||
postingRegisterReport :: [Opt] -> FilterSpec -> Journal -> PostingRegisterReport
 | 
					postingRegisterReport :: [Opt] -> FilterSpec -> Journal -> PostingRegisterReport
 | 
				
			||||||
postingRegisterReport opts fspec j = (totallabel,postingRegisterItems ps nullposting startbal (+))
 | 
					postingRegisterReport opts fspec j = (totallabel, postingRegisterItems 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
 | 
				
			||||||
@ -173,10 +176,19 @@ datedisplayexpr = do
 | 
				
			|||||||
 where
 | 
					 where
 | 
				
			||||||
  compareop = choice $ map (try . string) ["<=",">=","==","<","=",">"]
 | 
					  compareop = choice $ map (try . string) ["<=",">=","==","<","=",">"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Get a quicken/gnucash-style account register report, with the
 | 
					-- | Get a ledger-style register report showing all matched transactions and postings.
 | 
				
			||||||
-- specified options, for the currently focussed account (or possibly the
 | 
					-- Similar to "postingRegisterReport" except it uses matchers and
 | 
				
			||||||
-- focussed account plus sub-accounts.) This differs from
 | 
					-- per-transaction report items like "accountRegisterReport".
 | 
				
			||||||
-- "postingRegisterReport" in several ways:
 | 
					journalRegisterReport :: [Opt] -> Journal -> Matcher -> AccountRegisterReport
 | 
				
			||||||
 | 
					journalRegisterReport opts j@Journal{jtxns=ts} m = (totallabel, items)
 | 
				
			||||||
 | 
					   where
 | 
				
			||||||
 | 
					     ts' = sortBy (comparing tdate) $ filter (not . null . tpostings) $ map (filterTransactionPostings m) ts
 | 
				
			||||||
 | 
					     items = reverse $ accountRegisterReportItems m MatchAny nullmixedamt (+) ts'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- | Get a conventional account register report, with the specified
 | 
				
			||||||
 | 
					-- options, for the currently focussed account (or possibly the focussed
 | 
				
			||||||
 | 
					-- account plus sub-accounts.) This differs from "postingRegisterReport"
 | 
				
			||||||
 | 
					-- in several ways:
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
-- 1. it shows transactions, from the point of view of the focussed
 | 
					-- 1. it shows transactions, from the point of view of the focussed
 | 
				
			||||||
--    account. The other account's name and posted amount is displayed,
 | 
					--    account. The other account's name and posted amount is displayed,
 | 
				
			||||||
@ -195,7 +207,6 @@ accountRegisterReport opts j m thisacctmatcher = (label, items)
 | 
				
			|||||||
 where
 | 
					 where
 | 
				
			||||||
     -- transactions affecting this account, in date order
 | 
					     -- transactions affecting this account, in date order
 | 
				
			||||||
     ts = sortBy (comparing tdate) $ filter (matchesTransaction thisacctmatcher) $ jtxns j
 | 
					     ts = sortBy (comparing tdate) $ filter (matchesTransaction thisacctmatcher) $ jtxns j
 | 
				
			||||||
 | 
					 | 
				
			||||||
     -- starting balance: if we are filtering by a start date and nothing else,
 | 
					     -- starting balance: if we are filtering by a start date and nothing else,
 | 
				
			||||||
     -- the sum of postings to this account before that date; otherwise zero.
 | 
					     -- the sum of postings to this account before that date; otherwise zero.
 | 
				
			||||||
     (startbal,label, sumfn) | matcherIsNull m = (nullmixedamt,balancelabel,(-))
 | 
					     (startbal,label, sumfn) | matcherIsNull m = (nullmixedamt,balancelabel,(-))
 | 
				
			||||||
@ -210,35 +221,45 @@ accountRegisterReport opts j m thisacctmatcher = (label, items)
 | 
				
			|||||||
                        tostartdatematcher = MatchDate True (DateSpan Nothing startdate)
 | 
					                        tostartdatematcher = MatchDate True (DateSpan Nothing startdate)
 | 
				
			||||||
                        startdate = matcherStartDate effective m
 | 
					                        startdate = matcherStartDate effective m
 | 
				
			||||||
                        effective = Effective `elem` opts
 | 
					                        effective = Effective `elem` opts
 | 
				
			||||||
 | 
					     items = reverse $ accountRegisterReportItems m thisacctmatcher startbal sumfn ts
 | 
				
			||||||
     displaymatcher = -- ltrace "displaymatcher" $
 | 
					 | 
				
			||||||
                      MatchAnd [negateMatcher thisacctmatcher, m]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     items = reverse $ accountRegisterReportItems ts displaymatcher nulltransaction startbal sumfn
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Generate account register line items from a list of transactions,
 | 
					-- | Generate account register line items from a list of transactions,
 | 
				
			||||||
-- using the provided matcher (postings not matching this will not affect
 | 
					-- using the provided query and "this account" matchers, starting balance,
 | 
				
			||||||
-- the displayed item), starting transaction, starting balance, and
 | 
					-- and balance summing function.
 | 
				
			||||||
-- balance summing function.
 | 
					accountRegisterReportItems :: Matcher -> Matcher -> MixedAmount -> (MixedAmount -> MixedAmount -> MixedAmount) -> [Transaction] -> [AccountRegisterReportItem]
 | 
				
			||||||
accountRegisterReportItems :: [Transaction] -> Matcher -> Transaction -> MixedAmount -> (MixedAmount -> MixedAmount -> MixedAmount) -> [AccountRegisterReportItem]
 | 
					accountRegisterReportItems _ _ _ _ [] = []
 | 
				
			||||||
accountRegisterReportItems [] _ _ _ _ = []
 | 
					accountRegisterReportItems matcher thisacctmatcher bal sumfn (t@Transaction{tpostings=ps}:ts) =
 | 
				
			||||||
accountRegisterReportItems (t@Transaction{tpostings=ps}:ts) displaymatcher _ bal sumfn =
 | 
					 | 
				
			||||||
    case i of Just i' -> i':is
 | 
					    case i of Just i' -> i':is
 | 
				
			||||||
              Nothing -> is
 | 
					              Nothing -> is
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
      (i,bal'') = case filter (displaymatcher `matchesPosting`) ps of
 | 
					      thisacctps = tpostings $ filterTransactionPostings thisacctmatcher t
 | 
				
			||||||
 | 
					      numthisacctsposted = length $ nub $ map paccount thisacctps
 | 
				
			||||||
 | 
					      displaymatcher | numthisacctsposted > 1 = matcher
 | 
				
			||||||
 | 
					                     | otherwise              = MatchAnd [negateMatcher thisacctmatcher, matcher]
 | 
				
			||||||
 | 
					      t'@Transaction{tpostings=ps'} = filterTransactionPostings displaymatcher t
 | 
				
			||||||
 | 
					      (i,bal'') = case ps' of
 | 
				
			||||||
           []  -> (Nothing,bal) -- maybe a virtual transaction, or transfer to self
 | 
					           []  -> (Nothing,bal) -- maybe a virtual transaction, or transfer to self
 | 
				
			||||||
           [p] -> (Just (t, acct, amt, bal'), bal')
 | 
					           [p] -> (Just (t, t', False, acct, amt, bal'), bal')
 | 
				
			||||||
               where
 | 
					               where
 | 
				
			||||||
                 acct = paccount p
 | 
					                 acct = paccount p
 | 
				
			||||||
                 amt = pamount p
 | 
					                 amt = pamount p
 | 
				
			||||||
                 bal' = bal `sumfn` amt
 | 
					                 bal' = bal `sumfn` amt
 | 
				
			||||||
           ps' -> (Just (t,acct,amt,bal'), bal')
 | 
					           ps' -> (Just (t, t', True, acct, amt, bal'), bal')
 | 
				
			||||||
               where
 | 
					               where
 | 
				
			||||||
                 acct = "SPLIT ("++intercalate ", " (map (accountLeafName . paccount) ps')++")"
 | 
					                 -- describe split as from ..., to ... (not always right)
 | 
				
			||||||
 | 
					                 acct = case (simplify tos, simplify froms) of
 | 
				
			||||||
 | 
					                         ([],ts) -> "to "++commafy ts
 | 
				
			||||||
 | 
					                         (fs,[]) -> "from "++commafy fs
 | 
				
			||||||
 | 
					                         (fs,ts) -> "to "++commafy ts++" from "++commafy fs
 | 
				
			||||||
 | 
					                        where (tos,froms) = partition (fromMaybe False . isNegativeMixedAmount . pamount) ps'
 | 
				
			||||||
 | 
					                              simplify = nub . map (accountLeafName . paccount)
 | 
				
			||||||
 | 
					                              commafy = intercalate ", "
 | 
				
			||||||
                 amt = sum $ map pamount ps'
 | 
					                 amt = sum $ map pamount ps'
 | 
				
			||||||
                 bal' = bal `sumfn` amt
 | 
					                 bal' = bal `sumfn` amt
 | 
				
			||||||
      is = (accountRegisterReportItems ts displaymatcher t bal'' sumfn)
 | 
					      is = accountRegisterReportItems matcher thisacctmatcher bal'' sumfn ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filterTransactionPostings :: Matcher -> Transaction -> Transaction
 | 
				
			||||||
 | 
					filterTransactionPostings m t@Transaction{tpostings=ps} = t{tpostings=filter (m `matchesPosting`) ps}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- XXX confusing, refactor
 | 
					-- XXX confusing, refactor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user