lib: Generalise PeriodicReport to be polymorphic in the account labels.
This commit is contained in:
parent
88dc619257
commit
2e20d0717f
@ -47,8 +47,8 @@ type BudgetAverage = Average
|
|||||||
|
|
||||||
-- | A budget report tracks expected and actual changes per account and subperiod.
|
-- | A budget report tracks expected and actual changes per account and subperiod.
|
||||||
type BudgetCell = (Maybe Change, Maybe BudgetGoal)
|
type BudgetCell = (Maybe Change, Maybe BudgetGoal)
|
||||||
type BudgetReport = PeriodicReport BudgetCell
|
type BudgetReport = PeriodicReport AccountLeaf BudgetCell
|
||||||
type BudgetReportRow = PeriodicReportRow BudgetCell
|
type BudgetReportRow = PeriodicReportRow AccountLeaf BudgetCell
|
||||||
|
|
||||||
-- | Calculate budget goals from all periodic transactions,
|
-- | Calculate budget goals from all periodic transactions,
|
||||||
-- actual balance changes from the regular transactions,
|
-- actual balance changes from the regular transactions,
|
||||||
@ -100,9 +100,9 @@ sortBudgetReport ropts j (PeriodicReport ps rows trow) = PeriodicReport ps sorte
|
|||||||
sortTreeBURByActualAmount :: [BudgetReportRow] -> [BudgetReportRow]
|
sortTreeBURByActualAmount :: [BudgetReportRow] -> [BudgetReportRow]
|
||||||
sortTreeBURByActualAmount rows = sortedrows
|
sortTreeBURByActualAmount rows = sortedrows
|
||||||
where
|
where
|
||||||
anamesandrows = [(prrName r, r) | r <- rows]
|
anamesandrows = [(prrFullName r, r) | r <- rows]
|
||||||
anames = map fst anamesandrows
|
anames = map fst anamesandrows
|
||||||
atotals = [(a, tot) | PeriodicReportRow a _ _ _ (tot,_) _ <- rows]
|
atotals = [(acctFull a, tot) | PeriodicReportRow a _ _ (tot,_) _ <- rows]
|
||||||
accounttree = accountTree "root" anames
|
accounttree = accountTree "root" anames
|
||||||
accounttreewithbals = mapAccounts setibalance accounttree
|
accounttreewithbals = mapAccounts setibalance accounttree
|
||||||
where
|
where
|
||||||
@ -125,8 +125,8 @@ sortBudgetReport ropts j (PeriodicReport ps rows trow) = PeriodicReport ps sorte
|
|||||||
-- <unbudgeted> remains at the top.
|
-- <unbudgeted> remains at the top.
|
||||||
sortByAccountDeclaration rows = sortedrows
|
sortByAccountDeclaration rows = sortedrows
|
||||||
where
|
where
|
||||||
(unbudgetedrow,rows') = partition ((=="<unbudgeted>") . prrName) rows
|
(unbudgetedrow,rows') = partition ((=="<unbudgeted>") . prrFullName) rows
|
||||||
anamesandrows = [(prrName r, r) | r <- rows']
|
anamesandrows = [(prrFullName r, r) | r <- rows']
|
||||||
anames = map fst anamesandrows
|
anames = map fst anamesandrows
|
||||||
sortedanames = sortAccountNamesByDeclaration j (tree_ ropts) anames
|
sortedanames = sortAccountNamesByDeclaration j (tree_ ropts) anames
|
||||||
sortedrows = unbudgetedrow ++ sortAccountItemsLike sortedanames anamesandrows
|
sortedrows = unbudgetedrow ++ sortAccountItemsLike sortedanames anamesandrows
|
||||||
@ -190,17 +190,17 @@ budgetRollUp budgetedaccts showunbudgeted j = j { jtxns = remapTxn <$> jtxns j }
|
|||||||
--
|
--
|
||||||
combineBudgetAndActual :: MultiBalanceReport -> MultiBalanceReport -> BudgetReport
|
combineBudgetAndActual :: MultiBalanceReport -> MultiBalanceReport -> BudgetReport
|
||||||
combineBudgetAndActual
|
combineBudgetAndActual
|
||||||
(PeriodicReport budgetperiods budgetrows (PeriodicReportRow _ _ _ budgettots budgetgrandtot budgetgrandavg))
|
(PeriodicReport budgetperiods budgetrows (PeriodicReportRow _ _ budgettots budgetgrandtot budgetgrandavg))
|
||||||
(PeriodicReport actualperiods actualrows (PeriodicReportRow _ _ _ actualtots actualgrandtot actualgrandavg)) =
|
(PeriodicReport actualperiods actualrows (PeriodicReportRow _ _ actualtots actualgrandtot actualgrandavg)) =
|
||||||
PeriodicReport periods rows totalrow
|
PeriodicReport periods rows totalrow
|
||||||
where
|
where
|
||||||
periods = nubSort . filter (/= nulldatespan) $ budgetperiods ++ actualperiods
|
periods = nubSort . filter (/= nulldatespan) $ budgetperiods ++ actualperiods
|
||||||
|
|
||||||
-- first, combine any corresponding budget goals with actual changes
|
-- first, combine any corresponding budget goals with actual changes
|
||||||
rows1 =
|
rows1 =
|
||||||
[ PeriodicReportRow acct treeacct treeindent amtandgoals totamtandgoal avgamtandgoal
|
[ PeriodicReportRow acct treeindent amtandgoals totamtandgoal avgamtandgoal
|
||||||
| PeriodicReportRow acct treeacct treeindent actualamts actualtot actualavg <- actualrows
|
| PeriodicReportRow acct treeindent actualamts actualtot actualavg <- actualrows
|
||||||
, let mbudgetgoals = Map.lookup acct budgetGoalsByAcct :: Maybe ([BudgetGoal], BudgetTotal, BudgetAverage)
|
, let mbudgetgoals = Map.lookup (acctFull acct) budgetGoalsByAcct :: Maybe ([BudgetGoal], BudgetTotal, BudgetAverage)
|
||||||
, let budgetmamts = maybe (replicate (length periods) Nothing) (map Just . first3) mbudgetgoals :: [Maybe BudgetGoal]
|
, let budgetmamts = maybe (replicate (length periods) Nothing) (map Just . first3) mbudgetgoals :: [Maybe BudgetGoal]
|
||||||
, let mbudgettot = second3 <$> mbudgetgoals :: Maybe BudgetTotal
|
, let mbudgettot = second3 <$> mbudgetgoals :: Maybe BudgetTotal
|
||||||
, let mbudgetavg = third3 <$> mbudgetgoals :: Maybe BudgetAverage
|
, let mbudgetavg = third3 <$> mbudgetgoals :: Maybe BudgetAverage
|
||||||
@ -212,31 +212,31 @@ combineBudgetAndActual
|
|||||||
]
|
]
|
||||||
where
|
where
|
||||||
budgetGoalsByAcct :: Map AccountName ([BudgetGoal], BudgetTotal, BudgetAverage) =
|
budgetGoalsByAcct :: Map AccountName ([BudgetGoal], BudgetTotal, BudgetAverage) =
|
||||||
Map.fromList [ (acct, (amts, tot, avg))
|
Map.fromList [ (acctFull acct, (amts, tot, avg))
|
||||||
| PeriodicReportRow acct _ _ amts tot avg <- budgetrows ]
|
| PeriodicReportRow acct _ amts tot avg <- budgetrows ]
|
||||||
|
|
||||||
-- next, make rows for budget goals with no actual changes
|
-- next, make rows for budget goals with no actual changes
|
||||||
rows2 =
|
rows2 =
|
||||||
[ PeriodicReportRow acct treeacct treeindent amtandgoals totamtandgoal avgamtandgoal
|
[ PeriodicReportRow acct treeindent amtandgoals totamtandgoal avgamtandgoal
|
||||||
| PeriodicReportRow acct treeacct treeindent budgetgoals budgettot budgetavg <- budgetrows
|
| PeriodicReportRow acct treeindent budgetgoals budgettot budgetavg <- budgetrows
|
||||||
, acct `notElem` acctsdone
|
, acctFull acct `notElem` acctsdone
|
||||||
, let acctBudgetByPeriod = Map.fromList $ zip budgetperiods budgetgoals :: Map DateSpan BudgetGoal
|
, let acctBudgetByPeriod = Map.fromList $ zip budgetperiods budgetgoals :: Map DateSpan BudgetGoal
|
||||||
, let amtandgoals = [ (Nothing, Map.lookup p acctBudgetByPeriod) | p <- periods ] :: [BudgetCell]
|
, let amtandgoals = [ (Nothing, Map.lookup p acctBudgetByPeriod) | p <- periods ] :: [BudgetCell]
|
||||||
, let totamtandgoal = (Nothing, Just budgettot)
|
, let totamtandgoal = (Nothing, Just budgettot)
|
||||||
, let avgamtandgoal = (Nothing, Just budgetavg)
|
, let avgamtandgoal = (Nothing, Just budgetavg)
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
acctsdone = map prrName rows1
|
acctsdone = map prrFullName rows1
|
||||||
|
|
||||||
-- combine and re-sort rows
|
-- combine and re-sort rows
|
||||||
-- TODO: use MBR code
|
-- TODO: use MBR code
|
||||||
-- TODO: respect --sort-amount
|
-- TODO: respect --sort-amount
|
||||||
-- TODO: add --sort-budget to sort by budget goal amount
|
-- TODO: add --sort-budget to sort by budget goal amount
|
||||||
rows :: [BudgetReportRow] =
|
rows :: [BudgetReportRow] =
|
||||||
sortOn prrName $ rows1 ++ rows2
|
sortOn prrFullName $ rows1 ++ rows2
|
||||||
|
|
||||||
-- TODO: grand total & average shows 0% when there are no actual amounts, inconsistent with other cells
|
-- TODO: grand total & average shows 0% when there are no actual amounts, inconsistent with other cells
|
||||||
totalrow = PeriodicReportRow "" "" 0
|
totalrow = PeriodicReportRow () 0
|
||||||
[ (Map.lookup p totActualByPeriod, Map.lookup p totBudgetByPeriod) | p <- periods ]
|
[ (Map.lookup p totActualByPeriod, Map.lookup p totBudgetByPeriod) | p <- periods ]
|
||||||
( Just actualgrandtot, Just budgetgrandtot )
|
( Just actualgrandtot, Just budgetgrandtot )
|
||||||
( Just actualgrandavg, Just budgetgrandavg )
|
( Just actualgrandavg, Just budgetgrandavg )
|
||||||
@ -313,7 +313,7 @@ budgetReportAsText ropts@ReportOpts{..} budgetr =
|
|||||||
budgetReportAsTable :: ReportOpts -> BudgetReport -> Table String String (Maybe MixedAmount, Maybe MixedAmount)
|
budgetReportAsTable :: ReportOpts -> BudgetReport -> Table String String (Maybe MixedAmount, Maybe MixedAmount)
|
||||||
budgetReportAsTable
|
budgetReportAsTable
|
||||||
ropts
|
ropts
|
||||||
(PeriodicReport periods rows (PeriodicReportRow _ _ _ coltots grandtot grandavg)) =
|
(PeriodicReport periods rows (PeriodicReportRow _ _ coltots grandtot grandavg)) =
|
||||||
addtotalrow $
|
addtotalrow $
|
||||||
Table
|
Table
|
||||||
(T.Group NoLine $ map Header accts)
|
(T.Group NoLine $ map Header accts)
|
||||||
@ -324,10 +324,10 @@ budgetReportAsTable
|
|||||||
++ [" Total" | row_total_ ropts]
|
++ [" Total" | row_total_ ropts]
|
||||||
++ ["Average" | average_ ropts]
|
++ ["Average" | average_ ropts]
|
||||||
accts = map renderacct rows
|
accts = map renderacct rows
|
||||||
renderacct (PeriodicReportRow a a' i _ _ _)
|
renderacct (PeriodicReportRow (AccountLeaf a a') i _ _ _)
|
||||||
| tree_ ropts = replicate ((i-1)*2) ' ' ++ T.unpack a'
|
| tree_ ropts = replicate ((i-1)*2) ' ' ++ T.unpack a'
|
||||||
| otherwise = T.unpack $ maybeAccountNameDrop ropts a
|
| otherwise = T.unpack $ maybeAccountNameDrop ropts a
|
||||||
rowvals (PeriodicReportRow _ _ _ as rowtot rowavg) =
|
rowvals (PeriodicReportRow _ _ as rowtot rowavg) =
|
||||||
as ++ [rowtot | row_total_ ropts] ++ [rowavg | average_ ropts]
|
as ++ [rowtot | row_total_ ropts] ++ [rowavg | average_ ropts]
|
||||||
addtotalrow
|
addtotalrow
|
||||||
| no_total_ ropts = id
|
| no_total_ ropts = id
|
||||||
|
|||||||
@ -60,8 +60,8 @@ import Hledger.Reports.BalanceReport
|
|||||||
-- 3. the column totals, and the overall grand total (or zero for
|
-- 3. the column totals, and the overall grand total (or zero for
|
||||||
-- cumulative/historical reports) and grand average.
|
-- cumulative/historical reports) and grand average.
|
||||||
|
|
||||||
type MultiBalanceReport = PeriodicReport MixedAmount
|
type MultiBalanceReport = PeriodicReport AccountLeaf MixedAmount
|
||||||
type MultiBalanceReportRow = PeriodicReportRow MixedAmount
|
type MultiBalanceReportRow = PeriodicReportRow AccountLeaf MixedAmount
|
||||||
|
|
||||||
-- type alias just to remind us which AccountNames might be depth-clipped, below.
|
-- type alias just to remind us which AccountNames might be depth-clipped, below.
|
||||||
type ClippedAccountName = AccountName
|
type ClippedAccountName = AccountName
|
||||||
@ -233,7 +233,7 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
-- One row per account, with account name info, row amounts, row total and row average.
|
-- One row per account, with account name info, row amounts, row total and row average.
|
||||||
rows :: [MultiBalanceReportRow] =
|
rows :: [MultiBalanceReportRow] =
|
||||||
dbg1 "rows" $
|
dbg1 "rows" $
|
||||||
[ PeriodicReportRow a (accountLeafName a)
|
[ PeriodicReportRow (AccountLeaf a $ accountLeafName a)
|
||||||
(accountNameLevel a) valuedrowbals rowtot rowavg
|
(accountNameLevel a) valuedrowbals rowtot rowavg
|
||||||
| (a,changes) <- dbg1 "acctchanges" acctchanges
|
| (a,changes) <- dbg1 "acctchanges" acctchanges
|
||||||
-- The row amounts to be displayed: per-period changes,
|
-- The row amounts to be displayed: per-period changes,
|
||||||
@ -287,9 +287,9 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
sortTreeMBRByAmount :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
|
sortTreeMBRByAmount :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
|
||||||
sortTreeMBRByAmount rows = sortedrows
|
sortTreeMBRByAmount rows = sortedrows
|
||||||
where
|
where
|
||||||
anamesandrows = [(prrName r, r) | r <- rows]
|
anamesandrows = [(prrFullName r, r) | r <- rows]
|
||||||
anames = map fst anamesandrows
|
anames = map fst anamesandrows
|
||||||
atotals = [(a,tot) | PeriodicReportRow a _ _ _ tot _ <- rows]
|
atotals = [(prrFullName r, prrTotal r) | r <- rows]
|
||||||
accounttree = accountTree "root" anames
|
accounttree = accountTree "root" anames
|
||||||
accounttreewithbals = mapAccounts setibalance accounttree
|
accounttreewithbals = mapAccounts setibalance accounttree
|
||||||
where
|
where
|
||||||
@ -307,7 +307,7 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
-- Sort the report rows by account declaration order then account name.
|
-- Sort the report rows by account declaration order then account name.
|
||||||
sortMBRByAccountDeclaration rows = sortedrows
|
sortMBRByAccountDeclaration rows = sortedrows
|
||||||
where
|
where
|
||||||
anamesandrows = [(prrName r, r) | r <- rows]
|
anamesandrows = [(prrFullName r, r) | r <- rows]
|
||||||
anames = map fst anamesandrows
|
anames = map fst anamesandrows
|
||||||
sortedanames = sortAccountNamesByDeclaration j (tree_ ropts) anames
|
sortedanames = sortAccountNamesByDeclaration j (tree_ ropts) anames
|
||||||
sortedrows = sortAccountItemsLike sortedanames anamesandrows
|
sortedrows = sortAccountItemsLike sortedanames anamesandrows
|
||||||
@ -318,7 +318,7 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
-- Calculate the column totals. These are always the sum of column amounts.
|
-- Calculate the column totals. These are always the sum of column amounts.
|
||||||
highestlevelaccts = [a | a <- displayaccts, not $ any (`elem` displayaccts) $ init $ expandAccountName a]
|
highestlevelaccts = [a | a <- displayaccts, not $ any (`elem` displayaccts) $ init $ expandAccountName a]
|
||||||
colamts = transpose . map prrAmounts $ filter isHighest rows
|
colamts = transpose . map prrAmounts $ filter isHighest rows
|
||||||
where isHighest row = not (tree_ ropts) || prrName row `elem` highestlevelaccts
|
where isHighest row = not (tree_ ropts) || prrFullName row `elem` highestlevelaccts
|
||||||
coltotals :: [MixedAmount] =
|
coltotals :: [MixedAmount] =
|
||||||
dbg1 "coltotals" $ map sum colamts
|
dbg1 "coltotals" $ map sum colamts
|
||||||
-- Calculate the grand total and average. These are always the sum/average
|
-- Calculate the grand total and average. These are always the sum/average
|
||||||
@ -330,8 +330,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
]
|
]
|
||||||
in amts
|
in amts
|
||||||
-- Totals row.
|
-- Totals row.
|
||||||
totalsrow :: PeriodicReportRow MixedAmount =
|
totalsrow :: PeriodicReportRow () MixedAmount =
|
||||||
dbg1 "totalsrow" $ PeriodicReportRow "" "" 0 coltotals grandtotal grandaverage
|
dbg1 "totalsrow" $ PeriodicReportRow () 0 coltotals grandtotal grandaverage
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- 9. Map the report rows to percentages if needed
|
-- 9. Map the report rows to percentages if needed
|
||||||
@ -341,14 +341,14 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
mappedsortedrows :: [MultiBalanceReportRow] =
|
mappedsortedrows :: [MultiBalanceReportRow] =
|
||||||
if not percent_ then sortedrows
|
if not percent_ then sortedrows
|
||||||
else dbg1 "mappedsortedrows"
|
else dbg1 "mappedsortedrows"
|
||||||
[ PeriodicReportRow aname alname alevel
|
[ PeriodicReportRow aname alevel
|
||||||
(zipWith perdivide rowvals coltotals)
|
(zipWith perdivide rowvals coltotals)
|
||||||
(rowtotal `perdivide` grandtotal)
|
(rowtotal `perdivide` grandtotal)
|
||||||
(rowavg `perdivide` grandaverage)
|
(rowavg `perdivide` grandaverage)
|
||||||
| PeriodicReportRow aname alname alevel rowvals rowtotal rowavg <- sortedrows
|
| PeriodicReportRow aname alevel rowvals rowtotal rowavg <- sortedrows
|
||||||
]
|
]
|
||||||
mappedtotalsrow :: PeriodicReportRow MixedAmount
|
mappedtotalsrow :: PeriodicReportRow () MixedAmount
|
||||||
| percent_ = dbg1 "mappedtotalsrow" $ PeriodicReportRow "" "" 0
|
| percent_ = dbg1 "mappedtotalsrow" $ PeriodicReportRow () 0
|
||||||
(map (\t -> perdivide t t) coltotals)
|
(map (\t -> perdivide t t) coltotals)
|
||||||
(perdivide grandtotal grandtotal)
|
(perdivide grandtotal grandtotal)
|
||||||
(perdivide grandaverage grandaverage)
|
(perdivide grandaverage grandaverage)
|
||||||
@ -361,12 +361,12 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
balanceReportFromMultiBalanceReport :: ReportOpts -> Query -> Journal -> BalanceReport
|
balanceReportFromMultiBalanceReport :: ReportOpts -> Query -> Journal -> BalanceReport
|
||||||
balanceReportFromMultiBalanceReport opts q j = (rows', total)
|
balanceReportFromMultiBalanceReport opts q j = (rows', total)
|
||||||
where
|
where
|
||||||
PeriodicReport _ rows (PeriodicReportRow _ _ _ totals _ _) = multiBalanceReport opts q j
|
PeriodicReport _ rows (PeriodicReportRow _ _ totals _ _) = multiBalanceReport opts q j
|
||||||
rows' = [( a
|
rows' = [( a
|
||||||
, if flat_ opts then a else a' -- BalanceReport expects full account name here with --flat
|
, if flat_ opts then a else a' -- BalanceReport expects full account name here with --flat
|
||||||
, if tree_ opts then d-1 else 0 -- BalanceReport uses 0-based account depths
|
, if tree_ opts then d-1 else 0 -- BalanceReport uses 0-based account depths
|
||||||
, headDef nullmixedamt amts -- 0 columns is illegal, should not happen, return zeroes if it does
|
, headDef nullmixedamt amts -- 0 columns is illegal, should not happen, return zeroes if it does
|
||||||
) | PeriodicReportRow a a' d amts _ _ <- rows]
|
) | PeriodicReportRow (AccountLeaf a a') d amts _ _ <- rows]
|
||||||
total = headDef nullmixedamt totals
|
total = headDef nullmixedamt totals
|
||||||
|
|
||||||
|
|
||||||
@ -395,7 +395,7 @@ tests_MultiBalanceReport = tests "MultiBalanceReport" [
|
|||||||
(opts,journal) `gives` r = do
|
(opts,journal) `gives` r = do
|
||||||
let (eitems, etotal) = r
|
let (eitems, etotal) = r
|
||||||
(PeriodicReport _ aitems atotal) = multiBalanceReport opts (queryFromOpts nulldate opts) journal
|
(PeriodicReport _ aitems atotal) = multiBalanceReport opts (queryFromOpts nulldate opts) journal
|
||||||
showw (PeriodicReportRow acct acct' indent lAmt amt amt')
|
showw (PeriodicReportRow (AccountLeaf acct acct') indent lAmt amt amt')
|
||||||
= (acct, acct', indent, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt')
|
= (acct, acct', indent, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt')
|
||||||
(map showw aitems) @?= (map showw eitems)
|
(map showw aitems) @?= (map showw eitems)
|
||||||
showMixedAmountDebug (prrTotal atotal) @?= showMixedAmountDebug etotal -- we only check the sum of the totals
|
showMixedAmountDebug (prrTotal atotal) @?= showMixedAmountDebug etotal -- we only check the sum of the totals
|
||||||
@ -407,8 +407,8 @@ tests_MultiBalanceReport = tests "MultiBalanceReport" [
|
|||||||
,test "with -H on a populated period" $
|
,test "with -H on a populated period" $
|
||||||
(defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
(defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
||||||
(
|
(
|
||||||
[ PeriodicReportRow "assets:bank:checking" "checking" 3 [mamountp' "$1.00"] (Mixed [nullamt]) (Mixed [amt0 {aquantity=1}])
|
[ PeriodicReportRow (AccountLeaf "assets:bank:checking" "checking") 3 [mamountp' "$1.00"] (Mixed [nullamt]) (Mixed [amt0 {aquantity=1}])
|
||||||
, PeriodicReportRow "income:salary" "salary" 2 [mamountp' "$-1.00"] (Mixed [nullamt]) (Mixed [amt0 {aquantity=(-1)}])
|
, PeriodicReportRow (AccountLeaf "income:salary" "salary") 2 [mamountp' "$-1.00"] (Mixed [nullamt]) (Mixed [amt0 {aquantity=(-1)}])
|
||||||
],
|
],
|
||||||
Mixed [nullamt])
|
Mixed [nullamt])
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ New common report types, used by the BudgetReport for now, perhaps all reports l
|
|||||||
module Hledger.Reports.ReportTypes
|
module Hledger.Reports.ReportTypes
|
||||||
( PeriodicReport(..)
|
( PeriodicReport(..)
|
||||||
, PeriodicReportRow(..)
|
, PeriodicReportRow(..)
|
||||||
|
, AccountLeaf(..)
|
||||||
|
|
||||||
, Percentage
|
, Percentage
|
||||||
, Change
|
, Change
|
||||||
@ -15,6 +16,9 @@ module Hledger.Reports.ReportTypes
|
|||||||
, periodicReportSpan
|
, periodicReportSpan
|
||||||
, prNegate
|
, prNegate
|
||||||
, prNormaliseSign
|
, prNormaliseSign
|
||||||
|
|
||||||
|
, prrFullName
|
||||||
|
, prrLeaf
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Decimal
|
import Data.Decimal
|
||||||
@ -28,17 +32,16 @@ type Total = MixedAmount -- ^ The sum of 'Change's in a report or a report ro
|
|||||||
type Average = MixedAmount -- ^ The average of 'Change's or 'Balance's in a report or report row.
|
type Average = MixedAmount -- ^ The average of 'Change's or 'Balance's in a report or report row.
|
||||||
|
|
||||||
-- | A periodic report is a generic tabular report, where each row corresponds
|
-- | A periodic report is a generic tabular report, where each row corresponds
|
||||||
-- to an account and each column to a date period. The column periods are
|
-- to some label (usually an account name) and each column to a date period.
|
||||||
-- usually consecutive subperiods formed by splitting the overall report period
|
-- The column periods are usually consecutive subperiods formed by splitting
|
||||||
-- by some report interval (daily, weekly, etc.). It has:
|
-- the overall report period by some report interval (daily, weekly, etc.).
|
||||||
|
-- It has:
|
||||||
--
|
--
|
||||||
-- 1. a list of each column's period (date span)
|
-- 1. a list of each column's period (date span)
|
||||||
--
|
--
|
||||||
-- 2. a list of rows, each containing:
|
-- 2. a list of rows, each containing:
|
||||||
--
|
--
|
||||||
-- * the full account name
|
-- * an account label
|
||||||
--
|
|
||||||
-- * the leaf account name
|
|
||||||
--
|
--
|
||||||
-- * the account's depth
|
-- * the account's depth
|
||||||
--
|
--
|
||||||
@ -56,43 +59,58 @@ type Average = MixedAmount -- ^ The average of 'Change's or 'Balance's in a rep
|
|||||||
-- 3. the column totals, and the overall grand total (or zero for
|
-- 3. the column totals, and the overall grand total (or zero for
|
||||||
-- cumulative/historical reports) and grand average.
|
-- cumulative/historical reports) and grand average.
|
||||||
|
|
||||||
data PeriodicReport a =
|
data PeriodicReport a b =
|
||||||
PeriodicReport
|
PeriodicReport
|
||||||
{ prDates :: [DateSpan] -- The subperiods formed by splitting the overall
|
{ prDates :: [DateSpan] -- The subperiods formed by splitting the overall
|
||||||
-- report period by the report interval. For
|
-- report period by the report interval. For
|
||||||
-- ending-balance reports, only the end date is
|
-- ending-balance reports, only the end date is
|
||||||
-- significant. Usually displayed as report columns.
|
-- significant. Usually displayed as report columns.
|
||||||
, prRows :: [PeriodicReportRow a] -- One row per account in the report.
|
, prRows :: [PeriodicReportRow a b] -- One row per account in the report.
|
||||||
, prTotals :: PeriodicReportRow a -- The grand totals row. The account name in this row is always empty.
|
, prTotals :: PeriodicReportRow () b -- The grand totals row.
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
data PeriodicReportRow a =
|
data PeriodicReportRow a b =
|
||||||
PeriodicReportRow
|
PeriodicReportRow
|
||||||
{ prrName :: AccountName -- A full account name.
|
{ prrName :: a -- An account name.
|
||||||
, prrLeaf :: AccountName -- Shortened form of the account name to display
|
, prrDepth :: Int -- Indent level for displaying this account name in tree mode. 0, 1, 2...
|
||||||
-- in tree mode. Usually the leaf name, possibly
|
, prrAmounts :: [b] -- The data value for each subperiod.
|
||||||
-- with parent accounts prefixed.
|
, prrTotal :: b -- The total of this row's values.
|
||||||
, prrDepth :: Int -- Indent level for displaying this account name in tree mode. 0, 1, 2...
|
, prrAverage :: b -- The average of this row's values.
|
||||||
, prrAmounts :: [a] -- The data value for each subperiod.
|
|
||||||
, prrTotal :: a -- The total of this row's values.
|
|
||||||
, prrAverage :: a -- The average of this row's values.
|
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
|
-- | A combination of a full account name and a shortened form, used for display
|
||||||
|
-- purposes.
|
||||||
|
data AccountLeaf = AccountLeaf
|
||||||
|
{ acctFull :: AccountName -- A full account name.
|
||||||
|
, acctLeaf :: AccountName -- Shortened form of the account name to display
|
||||||
|
-- in tree mode. Usually the leaf name, possibly
|
||||||
|
-- with parent accounts prefixed.
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
-- | Figure out the overall date span of a PeridicReport
|
-- | Figure out the overall date span of a PeridicReport
|
||||||
periodicReportSpan :: PeriodicReport a -> DateSpan
|
periodicReportSpan :: PeriodicReport a b -> DateSpan
|
||||||
periodicReportSpan (PeriodicReport [] _ _) = DateSpan Nothing Nothing
|
periodicReportSpan (PeriodicReport [] _ _) = DateSpan Nothing Nothing
|
||||||
periodicReportSpan (PeriodicReport colspans _ _) = DateSpan (spanStart $ head colspans) (spanEnd $ last colspans)
|
periodicReportSpan (PeriodicReport colspans _ _) = DateSpan (spanStart $ head colspans) (spanEnd $ last colspans)
|
||||||
|
|
||||||
-- | Given a PeriodicReport and its normal balance sign,
|
-- | Given a PeriodicReport and its normal balance sign,
|
||||||
-- if it is known to be normally negative, convert it to normally positive.
|
-- if it is known to be normally negative, convert it to normally positive.
|
||||||
prNormaliseSign :: Num a => NormalSign -> PeriodicReport a -> PeriodicReport a
|
prNormaliseSign :: Num b => NormalSign -> PeriodicReport a b -> PeriodicReport a b
|
||||||
prNormaliseSign NormallyNegative = prNegate
|
prNormaliseSign NormallyNegative = prNegate
|
||||||
prNormaliseSign _ = id
|
prNormaliseSign _ = id
|
||||||
|
|
||||||
-- | Flip the sign of all amounts in a PeriodicReport.
|
-- | Flip the sign of all amounts in a PeriodicReport.
|
||||||
prNegate :: Num a => PeriodicReport a -> PeriodicReport a
|
prNegate :: Num b => PeriodicReport a b -> PeriodicReport a b
|
||||||
prNegate (PeriodicReport colspans rows totalsrow) =
|
prNegate (PeriodicReport colspans rows totalsrow) =
|
||||||
PeriodicReport colspans (map rowNegate rows) (rowNegate totalsrow)
|
PeriodicReport colspans (map rowNegate rows) (rowNegate totalsrow)
|
||||||
where
|
where
|
||||||
rowNegate (PeriodicReportRow name leaf indent amts tot avg) =
|
rowNegate (PeriodicReportRow name indent amts tot avg) =
|
||||||
PeriodicReportRow name leaf indent (map negate amts) (-tot) (-avg)
|
PeriodicReportRow name indent (map negate amts) (-tot) (-avg)
|
||||||
|
|
||||||
|
-- | Get the full account name for a `PeriodicReport AccountLeaf`>
|
||||||
|
prrFullName :: PeriodicReportRow AccountLeaf b -> AccountName
|
||||||
|
prrFullName = acctFull . prrName
|
||||||
|
|
||||||
|
-- | Get the shortened form of the account name for a `PeriodicReport AccountLeaf`>
|
||||||
|
prrLeaf :: PeriodicReportRow AccountLeaf b -> AccountName
|
||||||
|
prrLeaf = acctLeaf . prrName
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user