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 nullacct = Account
{ aname = "" { aname = ""
, acode = Nothing
, aparent = Nothing , aparent = Nothing
, asubs = [] , asubs = []
, anumpostings = 0 , anumpostings = 0
@ -90,6 +91,10 @@ tieAccountParents = tie Nothing
where where
a' = a{aparent=parent, asubs=map (tie (Just a')) asubs} 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. -- | Get this account's parent accounts, from the nearest up to the root.
parentAccounts :: Account -> [Account] parentAccounts :: Account -> [Account]
parentAccounts Account{aparent=Nothing} = [] parentAccounts Account{aparent=Nothing} = []
@ -188,7 +193,9 @@ filterAccounts p a
-- | Sort each level of an account tree by inclusive amount, -- | Sort each level of an account tree by inclusive amount,
-- so that the accounts with largest normal balances are listed first. -- so that the accounts with largest normal balances are listed first.
-- The provided normal balance sign determines whether normal balances -- 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 -> Account -> Account
sortAccountTreeByAmount normalsign a sortAccountTreeByAmount normalsign a
| null $ asubs a = a | null $ asubs a = a
@ -199,6 +206,19 @@ sortAccountTreeByAmount normalsign a
maybeflip | normalsign==NormallyNegative = id maybeflip | normalsign==NormallyNegative = id
| otherwise = flip | 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. -- | Search an account list by name.
lookupAccount :: AccountName -> [Account] -> Maybe Account lookupAccount :: AccountName -> [Account] -> Maybe Account
lookupAccount a = find ((==a).aname) 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) (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 j' = filterJournalAmounts (filterQuery queryIsSym q) $ -- remove amount parts which the query's sym: terms would exclude
filterJournalPostings q' j filterJournalPostings q' j
as = accountsFromPostings $ journalPostings j' as = map (accountSetCodeFrom j) $ accountsFromPostings $ journalPostings j'
j'' = filterJournalPostings depthq j' j'' = filterJournalPostings depthq j'
-- | List a ledger's account names. -- | 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. -- which let you walk up or down the account tree.
data Account = Account { data Account = Account {
aname :: AccountName, -- ^ this account's full name 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 aebalance :: MixedAmount, -- ^ this account's balance, excluding subaccounts
asubs :: [Account], -- ^ sub-accounts asubs :: [Account], -- ^ sub-accounts
anumpostings :: Int, -- ^ number of postings to this account anumpostings :: Int, -- ^ number of postings to this account

View File

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

View File

@ -172,12 +172,13 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, sorteditems, tot
sorteditems :: [MultiBalanceReportRow] = sorteditems :: [MultiBalanceReportRow] =
dbg1 "sorteditems" $ dbg1 "sorteditems" $
maybesort items sortitems items
where where
maybesort sortitems
| not $ sort_amount_ opts = id | sort_amount_ opts && accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAmount
| accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAmount | sort_amount_ opts = sortFlatMultiBalanceReportRowsByAmount
| otherwise = sortFlatMultiBalanceReportRowsByAmount | not (sort_amount_ opts) && accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAccountCodeAndName
| otherwise = sortFlatMultiBalanceReportRowsByAccountCodeAndName
where where
-- Sort the report rows, representing a flat account list, by row total. -- Sort the report rows, representing a flat account list, by row total.
sortFlatMultiBalanceReportRowsByAmount = sortBy (maybeflip $ comparing fifth6) 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} setibalance a = a{aibalance=fromMaybe (error "sortTreeMultiBalanceReportRowsByAmount 1") $ lookup (aname a) atotals}
sortedaccounttree = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts) accounttreewithbals sortedaccounttree = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts) accounttreewithbals
sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree
sortedrows = [ -- dropped the root account, also ignore any parent accounts not in rows
-- this error should not happen, but it's ugly TODO sortedrows = concatMap (\a -> maybe [] (:[]) $ lookup (aname a) anamesandrows) sortedaccounts
fromMaybe (error "sortTreeMultiBalanceReportRowsByAmount 2") $ lookup (aname a) anamesandrows
| a <- 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] = totals :: [MixedAmount] =
-- dbg1 "totals" $ -- dbg1 "totals" $