bal/bs/cf/is: sort accounts by account code, if any, then account name

If any accounts have numeric codes assigned via account directives,
those accounts will be listed first, lowest account codes first.
This commit is contained in:
Simon Michael 2018-01-20 19:42:05 -08:00
parent d420a8b534
commit 9b88fc2ed0
5 changed files with 60 additions and 18 deletions

View File

@ -47,6 +47,7 @@ instance Eq Account where
nullacct = Account
{ aname = ""
, acode = Nothing
, aparent = Nothing
, asubs = []
, anumpostings = 0
@ -90,6 +91,10 @@ tieAccountParents = tie Nothing
where
a' = a{aparent=parent, asubs=map (tie (Just a')) asubs}
-- | Look up an account's numeric code, if any, from the Journal and set it.
accountSetCodeFrom :: Journal -> Account -> Account
accountSetCodeFrom j a = a{acode=fromMaybe Nothing $ (lookup (aname a) $ jaccounts j)}
-- | Get this account's parent accounts, from the nearest up to the root.
parentAccounts :: Account -> [Account]
parentAccounts Account{aparent=Nothing} = []
@ -188,7 +193,9 @@ filterAccounts p a
-- | Sort each level of an account tree by inclusive amount,
-- so that the accounts with largest normal balances are listed first.
-- The provided normal balance sign determines whether normal balances
-- are negative or positive.
-- are negative or positive, affecting the sort order. Ie,
-- if balances are normally negative, then the most negative balances
-- sort first, and vice versa.
sortAccountTreeByAmount :: NormalSign -> Account -> Account
sortAccountTreeByAmount normalsign a
| null $ asubs a = a
@ -199,6 +206,19 @@ sortAccountTreeByAmount normalsign a
maybeflip | normalsign==NormallyNegative = id
| otherwise = flip
-- | Sort each level of an account tree first by the account code
-- if any, with the empty account code sorting last, and then by
-- the account name.
sortAccountTreeByAccountCodeAndName :: Account -> Account
sortAccountTreeByAccountCodeAndName a
| null $ asubs a = a
| otherwise = a{asubs=
sortBy (comparing accountCodeAndNameForSort) $ map sortAccountTreeByAccountCodeAndName $ asubs a}
accountCodeAndNameForSort a = (acode', aname a)
where
acode' = fromMaybe maxBound (acode a)
-- | Search an account list by name.
lookupAccount :: AccountName -> [Account] -> Maybe Account
lookupAccount a = find ((==a).aname)

View File

@ -47,7 +47,7 @@ ledgerFromJournal q j = nullledger{ljournal=j'', laccounts=as}
(q',depthq) = (filterQuery (not . queryIsDepth) q, filterQuery queryIsDepth q)
j' = filterJournalAmounts (filterQuery queryIsSym q) $ -- remove amount parts which the query's sym: terms would exclude
filterJournalPostings q' j
as = accountsFromPostings $ journalPostings j'
as = map (accountSetCodeFrom j) $ accountsFromPostings $ journalPostings j'
j'' = filterJournalPostings depthq j'
-- | List a ledger's account names.

View File

@ -354,6 +354,7 @@ instance Show Reader where show r = rFormat r ++ " reader"
-- which let you walk up or down the account tree.
data Account = Account {
aname :: AccountName, -- ^ this account's full name
acode :: Maybe AccountCode, -- ^ this account's numeric code, if any (not always set)
aebalance :: MixedAmount, -- ^ this account's balance, excluding subaccounts
asubs :: [Account], -- ^ sub-accounts
anumpostings :: Int, -- ^ number of postings to this account

View File

@ -88,7 +88,7 @@ balanceReport opts q j = (items, total)
dbg1 "accts" $
take 1 $ clipAccountsAndAggregate (queryDepth q) $ flattenAccounts accts
| flat_ opts = dbg1 "accts" $
maybesortflat $
sortflat $
filterzeros $
filterempty $
drop 1 $ clipAccountsAndAggregate (queryDepth q) $ flattenAccounts accts
@ -97,7 +97,7 @@ balanceReport opts q j = (items, total)
drop 1 $ flattenAccounts $
markboring $
prunezeros $
maybesorttree $
sorttree $
clipAccounts (queryDepth q) accts
where
balance = if flat_ opts then aebalance else aibalance
@ -105,12 +105,12 @@ balanceReport opts q j = (items, total)
filterempty = filter (\a -> anumpostings a > 0 || not (isZeroMixedAmount (balance a)))
prunezeros = if empty_ opts then id else fromMaybe nullacct . pruneAccounts (isZeroMixedAmount . balance)
markboring = if no_elide_ opts then id else markBoringParentAccounts
maybesortflat | sort_amount_ opts = sortBy (maybeflip $ comparing balance)
| otherwise = id
sortflat | sort_amount_ opts = sortBy (maybeflip $ comparing balance)
| otherwise = sortBy (comparing accountCodeAndNameForSort)
where
maybeflip = if normalbalance_ opts == Just NormallyNegative then id else flip
maybesorttree | sort_amount_ opts = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts)
| otherwise = id
sorttree | sort_amount_ opts = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts)
| otherwise = sortAccountTreeByAccountCodeAndName
items = dbg1 "items" $ map (balanceReportItem opts q) accts'
total | not (flat_ opts) = dbg1 "total" $ sum [amt | (_,_,indent,amt) <- items, indent == 0]
| otherwise = dbg1 "total" $

View File

@ -172,12 +172,13 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, sorteditems, tot
sorteditems :: [MultiBalanceReportRow] =
dbg1 "sorteditems" $
maybesort items
sortitems items
where
maybesort
| not $ sort_amount_ opts = id
| accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAmount
| otherwise = sortFlatMultiBalanceReportRowsByAmount
sortitems
| sort_amount_ opts && accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAmount
| sort_amount_ opts = sortFlatMultiBalanceReportRowsByAmount
| not (sort_amount_ opts) && accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAccountCodeAndName
| otherwise = sortFlatMultiBalanceReportRowsByAccountCodeAndName
where
-- Sort the report rows, representing a flat account list, by row total.
sortFlatMultiBalanceReportRowsByAmount = sortBy (maybeflip $ comparing fifth6)
@ -201,11 +202,31 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, sorteditems, tot
setibalance a = a{aibalance=fromMaybe (error "sortTreeMultiBalanceReportRowsByAmount 1") $ lookup (aname a) atotals}
sortedaccounttree = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts) accounttreewithbals
sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree
sortedrows = [
-- this error should not happen, but it's ugly TODO
fromMaybe (error "sortTreeMultiBalanceReportRowsByAmount 2") $ lookup (aname a) anamesandrows
| a <- sortedaccounts
]
-- dropped the root account, also ignore any parent accounts not in rows
sortedrows = concatMap (\a -> maybe [] (:[]) $ lookup (aname a) anamesandrows) sortedaccounts
-- Sort the report rows by account code if any, with the empty account code coming last, then account name.
sortFlatMultiBalanceReportRowsByAccountCodeAndName = sortBy (comparing acodeandname)
where
acodeandname r = (acode', aname)
where
aname = first6 r
macode = fromMaybe Nothing $ lookup aname $ jaccounts j
acode' = fromMaybe maxBound macode
-- Sort the report rows, representing a tree of accounts, by account code and then account name at each level.
-- Convert a tree of account names, look up the account codes, sort and flatten the tree, reorder the rows.
sortTreeMultiBalanceReportRowsByAccountCodeAndName rows = sortedrows
where
anamesandrows = [(first6 r, r) | r <- rows]
anames = map fst anamesandrows
nametree = treeFromPaths $ map expandAccountName anames
accounttree = nameTreeToAccount "root" nametree
accounttreewithcodes = mapAccounts (accountSetCodeFrom j) accounttree
sortedaccounttree = sortAccountTreeByAccountCodeAndName accounttreewithcodes
sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree
-- dropped the root account, also ignore any parent accounts not in rows
sortedrows = concatMap (\a -> maybe [] (:[]) $ lookup (aname a) anamesandrows) sortedaccounts
totals :: [MixedAmount] =
-- dbg1 "totals" $