{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hledger.Reports.BudgetReport (
BudgetGoal,
BudgetTotal,
BudgetAverage,
BudgetCell,
BudgetReportRow,
BudgetReport,
budgetReport,
budgetReportAsTable,
budgetReportAsText,
budgetReportAsCsv,
combineBudgetAndActual,
tests_BudgetReport
)
where
import Control.Applicative ((<|>))
import Control.Arrow ((***))
import Data.Decimal (roundTo)
import Data.Function (on)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HM
import Data.List (find, partition, transpose, foldl', maximumBy, intercalate)
import Data.List.Extra (nubSort)
import Data.Maybe (fromMaybe, catMaybes, isJust)
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import Safe (minimumDef)
import qualified Text.Tabular.AsciiWide as Tab
import Hledger.Data
import Hledger.Utils
import Hledger.Reports.ReportOptions
import Hledger.Reports.ReportTypes
import Hledger.Reports.MultiBalanceReport
import Data.Ord (comparing)
import Control.Monad ((>=>))
type BudgetGoal = Change
type BudgetTotal = Total
type BudgetAverage = Average
type BudgetCell = (Maybe Change, Maybe BudgetGoal)
type BudgetReportRow = PeriodicReportRow DisplayName BudgetCell
type BudgetReport = PeriodicReport DisplayName BudgetCell
type BudgetDisplayCell = (WideBuilder, Maybe (WideBuilder, Maybe WideBuilder))
type BudgetDisplayRow = [BudgetDisplayCell]
type BudgetShowAmountsFn = MixedAmount -> [WideBuilder]
type BudgetCalcPercentagesFn = Change -> BudgetGoal -> [Maybe Percentage]
_brrShowDebug :: BudgetReportRow -> String
_brrShowDebug :: BudgetReportRow -> String
_brrShowDebug (PeriodicReportRow DisplayName
dname [(Maybe Change, Maybe Change)]
budgetpairs (Maybe Change, Maybe Change)
_tot (Maybe Change, Maybe Change)
_avg) =
[String] -> String
unwords [
Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ DisplayName -> Text
displayFull DisplayName
dname,
String
"",
String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
" | "
[ String -> (Change -> String) -> Maybe Change -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"-" Change -> String
showMixedAmount Maybe Change
mactual String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" [" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String -> (Change -> String) -> Maybe Change -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"-" Change -> String
showMixedAmount Maybe Change
mgoal String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"]"
| (Maybe Change
mactual,Maybe Change
mgoal) <- [(Maybe Change, Maybe Change)]
budgetpairs ]
]
budgetReport :: ReportSpec -> BalancingOpts -> DateSpan -> Journal -> BudgetReport
budgetReport :: ReportSpec -> BalancingOpts -> DateSpan -> Journal -> BudgetReport
budgetReport ReportSpec
rspec BalancingOpts
bopts DateSpan
reportspan Journal
j = String -> BudgetReport -> BudgetReport
forall a. Show a => String -> a -> a
dbg4 String
"sortedbudgetreport" BudgetReport
budgetreport
where
ropts :: ReportOpts
ropts = (ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec){ accountlistmode_ = ALTree }
showunbudgeted :: Bool
showunbudgeted = ReportOpts -> Bool
empty_ ReportOpts
ropts
budgetedaccts :: Set Text
budgetedaccts =
String -> Set Text -> Set Text
forall a. Show a => String -> a -> a
dbg3 String
"budgetedacctsinperiod" (Set Text -> Set Text) -> Set Text -> Set Text
forall a b. (a -> b) -> a -> b
$
[Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList ([Text] -> Set Text) -> [Text] -> Set Text
forall a b. (a -> b) -> a -> b
$
[Text] -> [Text]
expandAccountNames ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
[Posting] -> [Text]
accountNamesFromPostings ([Posting] -> [Text]) -> [Posting] -> [Text]
forall a b. (a -> b) -> a -> b
$
(Transaction -> [Posting]) -> [Transaction] -> [Posting]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Transaction -> [Posting]
tpostings ([Transaction] -> [Posting]) -> [Transaction] -> [Posting]
forall a b. (a -> b) -> a -> b
$
(PeriodicTransaction -> [Transaction])
-> [PeriodicTransaction] -> [Transaction]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\PeriodicTransaction
pt -> Bool -> PeriodicTransaction -> DateSpan -> [Transaction]
runPeriodicTransaction Bool
False PeriodicTransaction
pt DateSpan
reportspan) ([PeriodicTransaction] -> [Transaction])
-> [PeriodicTransaction] -> [Transaction]
forall a b. (a -> b) -> a -> b
$
Journal -> [PeriodicTransaction]
jperiodictxns Journal
j
actualj :: Journal
actualj = Set Text -> Bool -> Journal -> Journal
journalWithBudgetAccountNames Set Text
budgetedaccts Bool
showunbudgeted Journal
j
budgetj :: Journal
budgetj = BalancingOpts -> ReportOpts -> DateSpan -> Journal -> Journal
journalAddBudgetGoalTransactions BalancingOpts
bopts ReportOpts
ropts DateSpan
reportspan Journal
j
priceoracle :: PriceOracle
priceoracle = Bool -> Journal -> PriceOracle
journalPriceOracle (ReportOpts -> Bool
infer_prices_ ReportOpts
ropts) Journal
j
budgetgoalreport :: MultiBalanceReport
budgetgoalreport@(PeriodicReport [DateSpan]
_ [PeriodicReportRow DisplayName Change]
budgetgoalitems PeriodicReportRow () Change
budgetgoaltotals) =
String -> MultiBalanceReport -> MultiBalanceReport
forall a. Show a => String -> a -> a
dbg5 String
"budgetgoalreport" (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal -> PriceOracle -> Set Text -> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec{_rsReportOpts=ropts{empty_=True}} Journal
budgetj PriceOracle
priceoracle Set Text
forall a. Monoid a => a
mempty
budgetedacctsseen :: Set Text
budgetedacctsseen = [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList ([Text] -> Set Text) -> [Text] -> Set Text
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName Change -> Text)
-> [PeriodicReportRow DisplayName Change] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName Change -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName [PeriodicReportRow DisplayName Change]
budgetgoalitems
actualreport :: MultiBalanceReport
actualreport@(PeriodicReport [DateSpan]
actualspans [PeriodicReportRow DisplayName Change]
_ PeriodicReportRow () Change
_) =
String -> MultiBalanceReport -> MultiBalanceReport
forall a. Show a => String -> a -> a
dbg5 String
"actualreport" (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal -> PriceOracle -> Set Text -> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec{_rsReportOpts=ropts{empty_=True}} Journal
actualj PriceOracle
priceoracle Set Text
budgetedacctsseen
budgetgoalreport' :: MultiBalanceReport
budgetgoalreport'
| ReportOpts -> Interval
interval_ ReportOpts
ropts Interval -> Interval -> Bool
forall a. Eq a => a -> a -> Bool
== Interval
NoInterval = [DateSpan]
-> [PeriodicReportRow DisplayName Change]
-> PeriodicReportRow () Change
-> MultiBalanceReport
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
actualspans [PeriodicReportRow DisplayName Change]
budgetgoalitems PeriodicReportRow () Change
budgetgoaltotals
| Bool
otherwise = MultiBalanceReport
budgetgoalreport
budgetreport :: BudgetReport
budgetreport = ReportOpts
-> Journal
-> MultiBalanceReport
-> MultiBalanceReport
-> BudgetReport
combineBudgetAndActual ReportOpts
ropts Journal
j MultiBalanceReport
budgetgoalreport' MultiBalanceReport
actualreport
journalAddBudgetGoalTransactions :: BalancingOpts -> ReportOpts -> DateSpan -> Journal -> Journal
journalAddBudgetGoalTransactions :: BalancingOpts -> ReportOpts -> DateSpan -> Journal -> Journal
journalAddBudgetGoalTransactions BalancingOpts
bopts ReportOpts
ropts DateSpan
reportspan Journal
j =
(String -> Journal)
-> (Journal -> Journal) -> Either String Journal -> Journal
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> Journal
forall a. String -> a
error' Journal -> Journal
forall a. a -> a
id (Either String Journal -> Journal)
-> Either String Journal -> Journal
forall a b. (a -> b) -> a -> b
$
(Journal -> Either String Journal
journalStyleAmounts (Journal -> Either String Journal)
-> (Journal -> Either String Journal)
-> Journal
-> Either String Journal
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> BalancingOpts -> Journal -> Either String Journal
journalBalanceTransactions BalancingOpts
bopts) Journal
j{ jtxns = budgetts }
where
budgetspan :: DateSpan
budgetspan = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg3 String
"budget span" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Maybe EFDay -> Maybe EFDay -> DateSpan
DateSpan (Day -> EFDay
Exact (Day -> EFDay) -> Maybe Day -> Maybe EFDay
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Day
mbudgetgoalsstartdate) (Day -> EFDay
Exact (Day -> EFDay) -> Maybe Day -> Maybe EFDay
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DateSpan -> Maybe Day
spanEnd DateSpan
reportspan)
where
mbudgetgoalsstartdate :: Maybe Day
mbudgetgoalsstartdate =
case Maybe Day -> [Maybe Day] -> Maybe Day
forall a. Ord a => a -> [a] -> a
minimumDef Maybe Day
forall a. Maybe a
Nothing ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Maybe Day -> Bool) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> Bool) -> [a] -> [a]
filter Maybe Day -> Bool
forall a. Maybe a -> Bool
isJust [Bool -> Journal -> Maybe Day
journalStartDate Bool
False Journal
j, DateSpan -> Maybe Day
spanStart DateSpan
reportspan] of
Maybe Day
Nothing -> Maybe Day
forall a. Maybe a
Nothing
Just Day
d -> Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d'
where
(Interval
intervl, DateSpan
spn) =
case [PeriodicTransaction]
budgetpts of
[] -> (Int -> Interval
Days Int
1, DateSpan
nulldatespan)
[PeriodicTransaction]
pts -> (PeriodicTransaction -> Interval
ptinterval PeriodicTransaction
pt, PeriodicTransaction -> DateSpan
ptspan PeriodicTransaction
pt)
where pt :: PeriodicTransaction
pt = (PeriodicTransaction -> PeriodicTransaction -> Ordering)
-> [PeriodicTransaction] -> PeriodicTransaction
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
maximumBy ((PeriodicTransaction -> Interval)
-> PeriodicTransaction -> PeriodicTransaction -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing PeriodicTransaction -> Interval
ptinterval) [PeriodicTransaction]
pts
intervalstart :: Day
intervalstart = Interval -> Day -> Day
intervalBoundaryBefore Interval
intervl Day
d
d' :: Day
d' = Day -> Day -> Day
forall a. Ord a => a -> a -> a
min Day
d (Day -> Day) -> Day -> Day
forall a b. (a -> b) -> a -> b
$ Day -> (Day -> Day) -> Maybe Day -> Day
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Day
intervalstart (Day -> Day -> Day
forall a. Ord a => a -> a -> a
max Day
intervalstart) (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$ DateSpan -> Maybe Day
spanStart DateSpan
spn
pat :: Text
pat = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" (Maybe Text -> Text) -> Maybe Text -> Text
forall a b. (a -> b) -> a -> b
$ String -> Maybe Text -> Maybe Text
forall a. Show a => String -> a -> a
dbg3 String
"budget pattern" (Maybe Text -> Maybe Text) -> Maybe Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.toLower (Text -> Text) -> Maybe Text -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReportOpts -> Maybe Text
budgetpat_ ReportOpts
ropts
budgetpts :: [PeriodicTransaction]
budgetpts = [PeriodicTransaction
pt | PeriodicTransaction
pt <- Journal -> [PeriodicTransaction]
jperiodictxns Journal
j, Text
pat Text -> Text -> Bool
`T.isInfixOf` Text -> Text
T.toLower (PeriodicTransaction -> Text
ptdescription PeriodicTransaction
pt)]
budgetts :: [Transaction]
budgetts =
String -> [Transaction] -> [Transaction]
forall a. Show a => String -> a -> a
dbg5 String
"budget goal txns" ([Transaction] -> [Transaction]) -> [Transaction] -> [Transaction]
forall a b. (a -> b) -> a -> b
$
[Transaction -> Transaction
makeBudgetTxn Transaction
t
| PeriodicTransaction
pt <- [PeriodicTransaction]
budgetpts
, Transaction
t <- Bool -> PeriodicTransaction -> DateSpan -> [Transaction]
runPeriodicTransaction Bool
False PeriodicTransaction
pt DateSpan
budgetspan
]
makeBudgetTxn :: Transaction -> Transaction
makeBudgetTxn Transaction
t = Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$ Transaction
t { tdescription = T.pack "Budget transaction" }
journalWithBudgetAccountNames :: S.Set AccountName -> Bool -> Journal -> Journal
journalWithBudgetAccountNames :: Set Text -> Bool -> Journal -> Journal
journalWithBudgetAccountNames Set Text
budgetedaccts Bool
showunbudgeted Journal
j =
(Journal -> String) -> Journal -> Journal
forall a. Show a => (a -> String) -> a -> a
dbg5With ((String
"budget account names: "String -> String -> String
forall a. [a] -> [a] -> [a]
++)(String -> String) -> (Journal -> String) -> Journal -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.[Text] -> String
forall a. Show a => a -> String
pshow([Text] -> String) -> (Journal -> [Text]) -> Journal -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Journal -> [Text]
journalAccountNamesUsed) (Journal -> Journal) -> Journal -> Journal
forall a b. (a -> b) -> a -> b
$
Journal
j { jtxns = remapTxn <$> jtxns j }
where
remapTxn :: Transaction -> Transaction
remapTxn = Transaction -> Transaction
txnTieKnot (Transaction -> Transaction)
-> (Transaction -> Transaction) -> Transaction -> Transaction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Posting -> Posting) -> Transaction -> Transaction
transactionTransformPostings Posting -> Posting
remapPosting
remapPosting :: Posting -> Posting
remapPosting Posting
p = Posting
p { paccount = remapAccount $ paccount p, poriginal = poriginal p <|> Just p }
remapAccount :: Text -> Text
remapAccount Text
a
| Text
a Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set Text
budgetedaccts = Text
a
| Just Text
p <- Maybe Text
budgetedparent = if Bool
showunbudgeted then Text
a else Text
p
| Bool
otherwise = if Bool
showunbudgeted then Text
u Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
acctsep Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
a else Text
u
where
budgetedparent :: Maybe Text
budgetedparent = (Text -> Bool) -> [Text] -> Maybe Text
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set Text
budgetedaccts) ([Text] -> Maybe Text) -> [Text] -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
parentAccountNames Text
a
u :: Text
u = Text
unbudgetedAccountName
combineBudgetAndActual :: ReportOpts -> Journal -> MultiBalanceReport -> MultiBalanceReport -> BudgetReport
combineBudgetAndActual :: ReportOpts
-> Journal
-> MultiBalanceReport
-> MultiBalanceReport
-> BudgetReport
combineBudgetAndActual ReportOpts
ropts Journal
j
(PeriodicReport [DateSpan]
budgetperiods [PeriodicReportRow DisplayName Change]
budgetrows (PeriodicReportRow ()
_ [Change]
budgettots Change
budgetgrandtot Change
budgetgrandavg))
(PeriodicReport [DateSpan]
actualperiods [PeriodicReportRow DisplayName Change]
actualrows (PeriodicReportRow ()
_ [Change]
actualtots Change
actualgrandtot Change
actualgrandavg)) =
[DateSpan]
-> [BudgetReportRow]
-> PeriodicReportRow () (Maybe Change, Maybe Change)
-> BudgetReport
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
periods [BudgetReportRow]
combinedrows PeriodicReportRow () (Maybe Change, Maybe Change)
totalrow
where
periods :: [DateSpan]
periods = [DateSpan] -> [DateSpan]
forall a. Ord a => [a] -> [a]
nubSort ([DateSpan] -> [DateSpan])
-> ([DateSpan] -> [DateSpan]) -> [DateSpan] -> [DateSpan]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (DateSpan -> Bool) -> [DateSpan] -> [DateSpan]
forall a. (a -> Bool) -> [a] -> [a]
filter (DateSpan -> DateSpan -> Bool
forall a. Eq a => a -> a -> Bool
/= DateSpan
nulldatespan) ([DateSpan] -> [DateSpan]) -> [DateSpan] -> [DateSpan]
forall a b. (a -> b) -> a -> b
$ [DateSpan]
budgetperiods [DateSpan] -> [DateSpan] -> [DateSpan]
forall a. [a] -> [a] -> [a]
++ [DateSpan]
actualperiods
actualsplusgoals :: [BudgetReportRow]
actualsplusgoals = [
DisplayName
-> [(Maybe Change, Maybe Change)]
-> (Maybe Change, Maybe Change)
-> (Maybe Change, Maybe Change)
-> BudgetReportRow
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
acct [(Maybe Change, Maybe Change)]
amtandgoals (Maybe Change, Maybe Change)
totamtandgoal (Maybe Change, Maybe Change)
avgamtandgoal
| PeriodicReportRow DisplayName
acct [Change]
actualamts Change
actualtot Change
actualavg <- [PeriodicReportRow DisplayName Change]
actualrows
, let mbudgetgoals :: Maybe ([Change], Change, Change)
mbudgetgoals = Text
-> HashMap Text ([Change], Change, Change)
-> Maybe ([Change], Change, Change)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup (DisplayName -> Text
displayFull DisplayName
acct) HashMap Text ([Change], Change, Change)
budgetGoalsByAcct :: Maybe ([BudgetGoal], BudgetTotal, BudgetAverage)
, let budgetmamts :: [Maybe Change]
budgetmamts = [Maybe Change]
-> (([Change], Change, Change) -> [Maybe Change])
-> Maybe ([Change], Change, Change)
-> [Maybe Change]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe Change
forall a. Maybe a
Nothing Maybe Change -> [DateSpan] -> [Maybe Change]
forall a b. a -> [b] -> [a]
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ [DateSpan]
periods) ((Change -> Maybe Change) -> [Change] -> [Maybe Change]
forall a b. (a -> b) -> [a] -> [b]
map Change -> Maybe Change
forall a. a -> Maybe a
Just ([Change] -> [Maybe Change])
-> (([Change], Change, Change) -> [Change])
-> ([Change], Change, Change)
-> [Maybe Change]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Change], Change, Change) -> [Change]
forall {a} {b} {c}. (a, b, c) -> a
first3) Maybe ([Change], Change, Change)
mbudgetgoals :: [Maybe BudgetGoal]
, let mbudgettot :: Maybe Change
mbudgettot = ([Change], Change, Change) -> Change
forall {a} {b} {c}. (a, b, c) -> b
second3 (([Change], Change, Change) -> Change)
-> Maybe ([Change], Change, Change) -> Maybe Change
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe ([Change], Change, Change)
mbudgetgoals :: Maybe BudgetTotal
, let mbudgetavg :: Maybe Change
mbudgetavg = ([Change], Change, Change) -> Change
forall {a} {b} {c}. (a, b, c) -> c
third3 (([Change], Change, Change) -> Change)
-> Maybe ([Change], Change, Change) -> Maybe Change
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe ([Change], Change, Change)
mbudgetgoals :: Maybe BudgetAverage
, let acctGoalByPeriod :: Map DateSpan Change
acctGoalByPeriod = [(DateSpan, Change)] -> Map DateSpan Change
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [ (DateSpan
p,Change
budgetamt) | (DateSpan
p, Just Change
budgetamt) <- [DateSpan] -> [Maybe Change] -> [(DateSpan, Maybe Change)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [Maybe Change]
budgetmamts ] :: Map DateSpan BudgetGoal
, let acctActualByPeriod :: Map DateSpan Change
acctActualByPeriod = [(DateSpan, Change)] -> Map DateSpan Change
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [ (DateSpan
p,Change
actualamt) | (DateSpan
p, Just Change
actualamt) <- [DateSpan] -> [Maybe Change] -> [(DateSpan, Maybe Change)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
actualperiods ((Change -> Maybe Change) -> [Change] -> [Maybe Change]
forall a b. (a -> b) -> [a] -> [b]
map Change -> Maybe Change
forall a. a -> Maybe a
Just [Change]
actualamts) ] :: Map DateSpan Change
, let amtandgoals :: [(Maybe Change, Maybe Change)]
amtandgoals = [ (DateSpan -> Map DateSpan Change -> Maybe Change
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan Change
acctActualByPeriod, DateSpan -> Map DateSpan Change -> Maybe Change
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan Change
acctGoalByPeriod) | DateSpan
p <- [DateSpan]
periods ] :: [BudgetCell]
, let totamtandgoal :: (Maybe Change, Maybe Change)
totamtandgoal = (Change -> Maybe Change
forall a. a -> Maybe a
Just Change
actualtot, Maybe Change
mbudgettot)
, let avgamtandgoal :: (Maybe Change, Maybe Change)
avgamtandgoal = (Change -> Maybe Change
forall a. a -> Maybe a
Just Change
actualavg, Maybe Change
mbudgetavg)
]
where
HashMap Text ([Change], Change, Change)
budgetGoalsByAcct :: HashMap AccountName ([BudgetGoal], BudgetTotal, BudgetAverage) =
[(Text, ([Change], Change, Change))]
-> HashMap Text ([Change], Change, Change)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList [ (DisplayName -> Text
displayFull DisplayName
acct, ([Change]
amts, Change
tot, Change
avg))
| PeriodicReportRow DisplayName
acct [Change]
amts Change
tot Change
avg <-
[PeriodicReportRow DisplayName Change]
budgetrows
]
othergoals :: [BudgetReportRow]
othergoals = [
DisplayName
-> [(Maybe Change, Maybe Change)]
-> (Maybe Change, Maybe Change)
-> (Maybe Change, Maybe Change)
-> BudgetReportRow
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
acct [(Maybe Change, Maybe Change)]
amtandgoals (Maybe Change, Maybe Change)
totamtandgoal (Maybe Change, Maybe Change)
avgamtandgoal
| PeriodicReportRow DisplayName
acct [Change]
budgetgoals Change
budgettot Change
budgetavg <- [PeriodicReportRow DisplayName Change]
budgetrows
, DisplayName -> Text
displayFull DisplayName
acct Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` (BudgetReportRow -> Text) -> [BudgetReportRow] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map BudgetReportRow -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName [BudgetReportRow]
actualsplusgoals
, let acctGoalByPeriod :: Map DateSpan Change
acctGoalByPeriod = [(DateSpan, Change)] -> Map DateSpan Change
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, Change)] -> Map DateSpan Change)
-> [(DateSpan, Change)] -> Map DateSpan Change
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [Change] -> [(DateSpan, Change)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [Change]
budgetgoals :: Map DateSpan BudgetGoal
, let amtandgoals :: [(Maybe Change, Maybe Change)]
amtandgoals = [ (Change -> Maybe Change
forall a. a -> Maybe a
Just Change
0, DateSpan -> Map DateSpan Change -> Maybe Change
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan Change
acctGoalByPeriod) | DateSpan
p <- [DateSpan]
periods ] :: [BudgetCell]
, let totamtandgoal :: (Maybe Change, Maybe Change)
totamtandgoal = (Change -> Maybe Change
forall a. a -> Maybe a
Just Change
0, Change -> Maybe Change
forall a. a -> Maybe a
Just Change
budgettot)
, let avgamtandgoal :: (Maybe Change, Maybe Change)
avgamtandgoal = (Change -> Maybe Change
forall a. a -> Maybe a
Just Change
0, Change -> Maybe Change
forall a. a -> Maybe a
Just Change
budgetavg)
]
[BudgetReportRow]
combinedrows :: [BudgetReportRow] =
[Text] -> [BudgetReportRow] -> [BudgetReportRow]
forall b.
[Text]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike ([BudgetReportRow] -> [Text]
forall {b}.
[PeriodicReportRow DisplayName (Maybe Change, b)] -> [Text]
mbrsorted [BudgetReportRow]
unbudgetedrows [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [BudgetReportRow] -> [Text]
forall {b}.
[PeriodicReportRow DisplayName (Maybe Change, b)] -> [Text]
mbrsorted [BudgetReportRow]
rows') [BudgetReportRow]
rows
where
([BudgetReportRow]
unbudgetedrows, [BudgetReportRow]
rows') = (BudgetReportRow -> Bool)
-> [BudgetReportRow] -> ([BudgetReportRow], [BudgetReportRow])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition ((Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==Text
unbudgetedAccountName) (Text -> Bool)
-> (BudgetReportRow -> Text) -> BudgetReportRow -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BudgetReportRow -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName) [BudgetReportRow]
rows
mbrsorted :: [PeriodicReportRow DisplayName (Maybe Change, b)] -> [Text]
mbrsorted = (PeriodicReportRow DisplayName Change -> Text)
-> [PeriodicReportRow DisplayName Change] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName Change -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName ([PeriodicReportRow DisplayName Change] -> [Text])
-> ([PeriodicReportRow DisplayName (Maybe Change, b)]
-> [PeriodicReportRow DisplayName Change])
-> [PeriodicReportRow DisplayName (Maybe Change, b)]
-> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportOpts
-> Journal
-> [PeriodicReportRow DisplayName Change]
-> [PeriodicReportRow DisplayName Change]
sortRows ReportOpts
ropts Journal
j ([PeriodicReportRow DisplayName Change]
-> [PeriodicReportRow DisplayName Change])
-> ([PeriodicReportRow DisplayName (Maybe Change, b)]
-> [PeriodicReportRow DisplayName Change])
-> [PeriodicReportRow DisplayName (Maybe Change, b)]
-> [PeriodicReportRow DisplayName Change]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PeriodicReportRow DisplayName (Maybe Change, b)
-> PeriodicReportRow DisplayName Change)
-> [PeriodicReportRow DisplayName (Maybe Change, b)]
-> [PeriodicReportRow DisplayName Change]
forall a b. (a -> b) -> [a] -> [b]
map (((Maybe Change, b) -> Change)
-> PeriodicReportRow DisplayName (Maybe Change, b)
-> PeriodicReportRow DisplayName Change
forall a b.
(a -> b)
-> PeriodicReportRow DisplayName a
-> PeriodicReportRow DisplayName b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Maybe Change, b) -> Change)
-> PeriodicReportRow DisplayName (Maybe Change, b)
-> PeriodicReportRow DisplayName Change)
-> ((Maybe Change, b) -> Change)
-> PeriodicReportRow DisplayName (Maybe Change, b)
-> PeriodicReportRow DisplayName Change
forall a b. (a -> b) -> a -> b
$ Change -> Maybe Change -> Change
forall a. a -> Maybe a -> a
fromMaybe Change
nullmixedamt (Maybe Change -> Change)
-> ((Maybe Change, b) -> Maybe Change)
-> (Maybe Change, b)
-> Change
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Change, b) -> Maybe Change
forall a b. (a, b) -> a
fst)
rows :: [BudgetReportRow]
rows = [BudgetReportRow]
actualsplusgoals [BudgetReportRow] -> [BudgetReportRow] -> [BudgetReportRow]
forall a. [a] -> [a] -> [a]
++ [BudgetReportRow]
othergoals
totalrow :: PeriodicReportRow () (Maybe Change, Maybe Change)
totalrow = ()
-> [(Maybe Change, Maybe Change)]
-> (Maybe Change, Maybe Change)
-> (Maybe Change, Maybe Change)
-> PeriodicReportRow () (Maybe Change, Maybe Change)
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow ()
[ (DateSpan -> Map DateSpan Change -> Maybe Change
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan Change
totActualByPeriod, DateSpan -> Map DateSpan Change -> Maybe Change
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan Change
totGoalByPeriod) | DateSpan
p <- [DateSpan]
periods ]
( Change -> Maybe Change
forall a. a -> Maybe a
Just Change
actualgrandtot, Change -> Maybe Change
budget Change
budgetgrandtot )
( Change -> Maybe Change
forall a. a -> Maybe a
Just Change
actualgrandavg, Change -> Maybe Change
budget Change
budgetgrandavg )
where
totGoalByPeriod :: Map DateSpan Change
totGoalByPeriod = [(DateSpan, Change)] -> Map DateSpan Change
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, Change)] -> Map DateSpan Change)
-> [(DateSpan, Change)] -> Map DateSpan Change
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [Change] -> [(DateSpan, Change)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [Change]
budgettots :: Map DateSpan BudgetTotal
totActualByPeriod :: Map DateSpan Change
totActualByPeriod = [(DateSpan, Change)] -> Map DateSpan Change
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, Change)] -> Map DateSpan Change)
-> [(DateSpan, Change)] -> Map DateSpan Change
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [Change] -> [(DateSpan, Change)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
actualperiods [Change]
actualtots :: Map DateSpan Change
budget :: Change -> Maybe Change
budget Change
b = if Change -> Bool
mixedAmountLooksZero Change
b then Maybe Change
forall a. Maybe a
Nothing else Change -> Maybe Change
forall a. a -> Maybe a
Just Change
b
budgetReportAsText :: ReportOpts -> BudgetReport -> TL.Text
budgetReportAsText :: ReportOpts -> BudgetReport -> Text
budgetReportAsText ropts :: ReportOpts
ropts@ReportOpts{Bool
Int
[Text]
[Status]
Maybe Int
Maybe Text
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
accountlistmode_ :: ReportOpts -> AccountListMode
empty_ :: ReportOpts -> Bool
infer_prices_ :: ReportOpts -> Bool
interval_ :: ReportOpts -> Interval
budgetpat_ :: ReportOpts -> Maybe Text
period_ :: Period
interval_ :: Interval
statuses_ :: [Status]
conversionop_ :: Maybe ConversionOp
value_ :: Maybe ValuationType
infer_prices_ :: Bool
depth_ :: Maybe Int
date2_ :: Bool
empty_ :: Bool
no_elide_ :: Bool
real_ :: Bool
format_ :: StringFormat
pretty_ :: Bool
querystring_ :: [Text]
average_ :: Bool
related_ :: Bool
txn_dates_ :: Bool
balancecalc_ :: BalanceCalculation
balanceaccum_ :: BalanceAccumulation
budgetpat_ :: Maybe Text
accountlistmode_ :: AccountListMode
drop_ :: Int
declared_ :: Bool
row_total_ :: Bool
no_total_ :: Bool
summary_only_ :: Bool
show_costs_ :: Bool
sort_amount_ :: Bool
percent_ :: Bool
invert_ :: Bool
normalbalance_ :: Maybe NormalSign
color_ :: Bool
transpose_ :: Bool
layout_ :: Layout
period_ :: ReportOpts -> Period
statuses_ :: ReportOpts -> [Status]
conversionop_ :: ReportOpts -> Maybe ConversionOp
value_ :: ReportOpts -> Maybe ValuationType
depth_ :: ReportOpts -> Maybe Int
date2_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
real_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
pretty_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [Text]
average_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
txn_dates_ :: ReportOpts -> Bool
balancecalc_ :: ReportOpts -> BalanceCalculation
balanceaccum_ :: ReportOpts -> BalanceAccumulation
drop_ :: ReportOpts -> Int
declared_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
summary_only_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
invert_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
color_ :: ReportOpts -> Bool
transpose_ :: ReportOpts -> Bool
layout_ :: ReportOpts -> Layout
..} BudgetReport
budgetr = Builder -> Text
TB.toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$
Text -> Builder
TB.fromText Text
title Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
TB.fromText Text
"\n\n" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
ReportOpts -> Table Text Text WideBuilder -> Builder
balanceReportTableAsText ReportOpts
ropts (ReportOpts -> BudgetReport -> Table Text Text WideBuilder
budgetReportAsTable ReportOpts
ropts BudgetReport
budgetr)
where
title :: Text
title = Text
"Budget performance in " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> DateSpan -> Text
showDateSpan (BudgetReport -> DateSpan
forall a b. PeriodicReport a b -> DateSpan
periodicReportSpan BudgetReport
budgetr)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (case Maybe ConversionOp
conversionop_ of
Just ConversionOp
ToCost -> Text
", converted to cost"
Maybe ConversionOp
_ -> Text
"")
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (case Maybe ValuationType
value_ of
Just (AtThen Maybe Text
_mc) -> Text
", valued at posting date"
Just (AtEnd Maybe Text
_mc) -> Text
", valued at period ends"
Just (AtNow Maybe Text
_mc) -> Text
", current value"
Just (AtDate Day
d Maybe Text
_mc) -> Text
", valued at " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Day -> Text
showDate Day
d
Maybe ValuationType
Nothing -> Text
"")
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
":"
budgetReportAsTable :: ReportOpts -> BudgetReport -> Tab.Table Text Text WideBuilder
budgetReportAsTable :: ReportOpts -> BudgetReport -> Table Text Text WideBuilder
budgetReportAsTable ReportOpts{Bool
Int
[Text]
[Status]
Maybe Int
Maybe Text
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
accountlistmode_ :: ReportOpts -> AccountListMode
empty_ :: ReportOpts -> Bool
infer_prices_ :: ReportOpts -> Bool
interval_ :: ReportOpts -> Interval
budgetpat_ :: ReportOpts -> Maybe Text
period_ :: ReportOpts -> Period
statuses_ :: ReportOpts -> [Status]
conversionop_ :: ReportOpts -> Maybe ConversionOp
value_ :: ReportOpts -> Maybe ValuationType
depth_ :: ReportOpts -> Maybe Int
date2_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
real_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
pretty_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [Text]
average_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
txn_dates_ :: ReportOpts -> Bool
balancecalc_ :: ReportOpts -> BalanceCalculation
balanceaccum_ :: ReportOpts -> BalanceAccumulation
drop_ :: ReportOpts -> Int
declared_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
summary_only_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
invert_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
color_ :: ReportOpts -> Bool
transpose_ :: ReportOpts -> Bool
layout_ :: ReportOpts -> Layout
period_ :: Period
interval_ :: Interval
statuses_ :: [Status]
conversionop_ :: Maybe ConversionOp
value_ :: Maybe ValuationType
infer_prices_ :: Bool
depth_ :: Maybe Int
date2_ :: Bool
empty_ :: Bool
no_elide_ :: Bool
real_ :: Bool
format_ :: StringFormat
pretty_ :: Bool
querystring_ :: [Text]
average_ :: Bool
related_ :: Bool
txn_dates_ :: Bool
balancecalc_ :: BalanceCalculation
balanceaccum_ :: BalanceAccumulation
budgetpat_ :: Maybe Text
accountlistmode_ :: AccountListMode
drop_ :: Int
declared_ :: Bool
row_total_ :: Bool
no_total_ :: Bool
summary_only_ :: Bool
show_costs_ :: Bool
sort_amount_ :: Bool
percent_ :: Bool
invert_ :: Bool
normalbalance_ :: Maybe NormalSign
color_ :: Bool
transpose_ :: Bool
layout_ :: Layout
..} (PeriodicReport [DateSpan]
spans [BudgetReportRow]
items PeriodicReportRow () (Maybe Change, Maybe Change)
totrow) =
Table Text Text WideBuilder -> Table Text Text WideBuilder
forall {rh} {a}. Table rh rh a -> Table rh rh a
maybetransposetable (Table Text Text WideBuilder -> Table Text Text WideBuilder)
-> Table Text Text WideBuilder -> Table Text Text WideBuilder
forall a b. (a -> b) -> a -> b
$
Table Text Text WideBuilder -> Table Text Text WideBuilder
forall {ch}. Table Text ch WideBuilder -> Table Text ch WideBuilder
addtotalrow (Table Text Text WideBuilder -> Table Text Text WideBuilder)
-> Table Text Text WideBuilder -> Table Text Text WideBuilder
forall a b. (a -> b) -> a -> b
$
Header Text
-> Header Text -> [[WideBuilder]] -> Table Text Text WideBuilder
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Tab.Table
(Properties -> [Header Text] -> Header Text
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header Text] -> Header Text) -> [Header Text] -> Header Text
forall a b. (a -> b) -> a -> b
$ (Text -> Header Text) -> [Text] -> [Header Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Header Text
forall h. h -> Header h
Tab.Header [Text]
accts)
(Properties -> [Header Text] -> Header Text
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header Text] -> Header Text) -> [Header Text] -> Header Text
forall a b. (a -> b) -> a -> b
$ (Text -> Header Text) -> [Text] -> [Header Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Header Text
forall h. h -> Header h
Tab.Header [Text]
colheadings)
[[WideBuilder]]
rows
where
maybetransposetable :: Table rh rh a -> Table rh rh a
maybetransposetable
| Bool
transpose_ = \(Tab.Table Header rh
rh Header rh
ch [[a]]
vals) -> Header rh -> Header rh -> [[a]] -> Table rh rh a
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Tab.Table Header rh
ch Header rh
rh ([[a]] -> [[a]]
forall a. [[a]] -> [[a]]
transpose [[a]]
vals)
| Bool
otherwise = Table rh rh a -> Table rh rh a
forall a. a -> a
id
addtotalrow :: Table Text ch WideBuilder -> Table Text ch WideBuilder
addtotalrow
| Bool
no_total_ = Table Text ch WideBuilder -> Table Text ch WideBuilder
forall a. a -> a
id
| Bool
otherwise = let rh :: Header Text
rh = Properties -> [Header Text] -> Header Text
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header Text] -> Header Text)
-> (Header Text -> [Header Text]) -> Header Text -> Header Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Header Text -> [Header Text]
forall a. Int -> a -> [a]
replicate ([[WideBuilder]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[WideBuilder]]
totalrows) (Header Text -> Header Text) -> Header Text -> Header Text
forall a b. (a -> b) -> a -> b
$ Text -> Header Text
forall h. h -> Header h
Tab.Header Text
""
ch :: Header [a]
ch = [a] -> Header [a]
forall h. h -> Header h
Tab.Header []
in ((Table Text ch WideBuilder
-> Table Text [Any] WideBuilder -> Table Text ch WideBuilder)
-> Table Text [Any] WideBuilder
-> Table Text ch WideBuilder
-> Table Text ch WideBuilder
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Properties
-> Table Text ch WideBuilder
-> Table Text [Any] WideBuilder
-> Table Text ch WideBuilder
forall rh ch a ch2.
Properties -> Table rh ch a -> Table rh ch2 a -> Table rh ch a
Tab.concatTables Properties
Tab.SingleLine) (Table Text [Any] WideBuilder
-> Table Text ch WideBuilder -> Table Text ch WideBuilder)
-> Table Text [Any] WideBuilder
-> Table Text ch WideBuilder
-> Table Text ch WideBuilder
forall a b. (a -> b) -> a -> b
$ Header Text
-> Header [Any] -> [[WideBuilder]] -> Table Text [Any] WideBuilder
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Tab.Table Header Text
rh Header [Any]
forall {a}. Header [a]
ch [[WideBuilder]]
totalrows)
colheadings :: [Text]
colheadings = [Text
"Commodity" | Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare]
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ (DateSpan -> Text) -> [DateSpan] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
reportPeriodName BalanceAccumulation
balanceaccum_ [DateSpan]
spans) [DateSpan]
spans
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [Text
" Total" | Bool
row_total_]
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [Text
"Average" | Bool
average_]
([Text]
accts, [[WideBuilder]]
rows, [[WideBuilder]]
totalrows) =
([Text]
accts'
,[WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
maybecommcol [WideBuilder]
itemscs ([[WideBuilder]] -> [[WideBuilder]])
-> [[WideBuilder]] -> [[WideBuilder]]
forall a b. (a -> b) -> a -> b
$ [[BudgetDisplayCell]] -> [[WideBuilder]]
showcells [[BudgetDisplayCell]]
texts
,[WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
maybecommcol [WideBuilder]
totrowcs ([[WideBuilder]] -> [[WideBuilder]])
-> [[WideBuilder]] -> [[WideBuilder]]
forall a b. (a -> b) -> a -> b
$ [[BudgetDisplayCell]] -> [[WideBuilder]]
showtotrow [[BudgetDisplayCell]]
totrowtexts)
where
maybecommcol :: [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
maybecommcol :: [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
maybecommcol [WideBuilder]
cs
| Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare = (WideBuilder -> [WideBuilder] -> [WideBuilder])
-> [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (:) [WideBuilder]
cs
| Bool
otherwise = [[WideBuilder]] -> [[WideBuilder]]
forall a. a -> a
id
showcells, showtotrow :: [[BudgetDisplayCell]] -> [[WideBuilder]]
([[BudgetDisplayCell]] -> [[WideBuilder]]
showcells, [[BudgetDisplayCell]] -> [[WideBuilder]]
showtotrow) =
([[WideBuilder]] -> [[WideBuilder]]
forall a. [[a]] -> [[a]]
maybetranspose ([[WideBuilder]] -> [[WideBuilder]])
-> ([[BudgetDisplayCell]] -> [[WideBuilder]])
-> [[BudgetDisplayCell]]
-> [[WideBuilder]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([BudgetDisplayCell] -> [WideBuilder])
-> [[BudgetDisplayCell]] -> [[WideBuilder]]
forall a b. (a -> b) -> [a] -> [b]
map (((Int, Int, Int) -> BudgetDisplayCell -> WideBuilder)
-> [(Int, Int, Int)] -> [BudgetDisplayCell] -> [WideBuilder]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
showBudgetDisplayCell [(Int, Int, Int)]
widths) ([[BudgetDisplayCell]] -> [[WideBuilder]])
-> ([[BudgetDisplayCell]] -> [[BudgetDisplayCell]])
-> [[BudgetDisplayCell]]
-> [[WideBuilder]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a. [[a]] -> [[a]]
maybetranspose
,[[WideBuilder]] -> [[WideBuilder]]
forall a. [[a]] -> [[a]]
maybetranspose ([[WideBuilder]] -> [[WideBuilder]])
-> ([[BudgetDisplayCell]] -> [[WideBuilder]])
-> [[BudgetDisplayCell]]
-> [[WideBuilder]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([BudgetDisplayCell] -> [WideBuilder])
-> [[BudgetDisplayCell]] -> [[WideBuilder]]
forall a b. (a -> b) -> [a] -> [b]
map (((Int, Int, Int) -> BudgetDisplayCell -> WideBuilder)
-> [(Int, Int, Int)] -> [BudgetDisplayCell] -> [WideBuilder]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
showBudgetDisplayCell [(Int, Int, Int)]
totrowwidths) ([[BudgetDisplayCell]] -> [[WideBuilder]])
-> ([[BudgetDisplayCell]] -> [[BudgetDisplayCell]])
-> [[BudgetDisplayCell]]
-> [[WideBuilder]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a. [[a]] -> [[a]]
maybetranspose)
where
showBudgetDisplayCell :: (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
showBudgetDisplayCell :: (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
showBudgetDisplayCell (Int
actualwidth, Int
budgetwidth, Int
percentwidth) (WideBuilder
actual, Maybe (WideBuilder, Maybe WideBuilder)
mbudget) =
(Builder -> Int -> WideBuilder) -> Int -> Builder -> WideBuilder
forall a b c. (a -> b -> c) -> b -> a -> c
flip Builder -> Int -> WideBuilder
WideBuilder (Int
actualwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
totalbudgetwidth) (Builder -> WideBuilder) -> Builder -> WideBuilder
forall a b. (a -> b) -> a -> b
$
WideBuilder -> Builder
toPadded WideBuilder
actual Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
-> ((WideBuilder, Maybe WideBuilder) -> Builder)
-> Maybe (WideBuilder, Maybe WideBuilder)
-> Builder
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Builder
emptycell (WideBuilder, Maybe WideBuilder) -> Builder
showBudgetGoalAndPercentage Maybe (WideBuilder, Maybe WideBuilder)
mbudget
where
toPadded :: WideBuilder -> Builder
toPadded (WideBuilder Builder
b Int
w) = (Text -> Builder
TB.fromText (Text -> Builder) -> (Int -> Text) -> Int -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Text -> Text) -> Text -> Int -> Text
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> Text -> Text
T.replicate Text
" " (Int -> Builder) -> Int -> Builder
forall a b. (a -> b) -> a -> b
$ Int
actualwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
w) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
b
(Int
totalpercentwidth, Int
totalbudgetwidth) =
let totalpercentwidth' :: Int
totalpercentwidth' = if Int
percentwidth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Int
0 else Int
percentwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
5
in ( Int
totalpercentwidth'
, if Int
budgetwidth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Int
0 else Int
budgetwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
totalpercentwidth' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3
)
emptycell :: TB.Builder
emptycell :: Builder
emptycell = Text -> Builder
TB.fromText (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
T.replicate Int
totalbudgetwidth Text
" "
showBudgetGoalAndPercentage :: (WideBuilder, Maybe WideBuilder) -> TB.Builder
showBudgetGoalAndPercentage :: (WideBuilder, Maybe WideBuilder) -> Builder
showBudgetGoalAndPercentage (WideBuilder
goal, Maybe WideBuilder
perc) =
let perct :: Text
perct = case Maybe WideBuilder
perc of
Maybe WideBuilder
Nothing -> Int -> Text -> Text
T.replicate Int
totalpercentwidth Text
" "
Just WideBuilder
pct -> Int -> Text -> Text
T.replicate (Int
percentwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
pct) Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> WideBuilder -> Text
wbToText WideBuilder
pct Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"% of "
in Text -> Builder
TB.fromText (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ Text
" [" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
perct Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text -> Text
T.replicate (Int
budgetwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
goal) Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> WideBuilder -> Text
wbToText WideBuilder
goal Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"]"
widths :: [(Int, Int, Int)]
widths :: [(Int, Int, Int)]
widths = [Int] -> [Int] -> [Int] -> [(Int, Int, Int)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [Int]
actualwidths [Int]
budgetwidths [Int]
percentwidths
where
actualwidths :: [Int]
actualwidths = ([(Int, Int, Int)] -> Int) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map ([Int] -> Int
forall a. Integral a => [a] -> a
maximum' ([Int] -> Int)
-> ([(Int, Int, Int)] -> [Int]) -> [(Int, Int, Int)] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int, Int) -> Int) -> [(Int, Int, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Int, Int) -> Int
forall {a} {b} {c}. (a, b, c) -> a
first3 ) ([[(Int, Int, Int)]] -> [Int]) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> a -> b
$ [[(Int, Int, Int)]]
cols
budgetwidths :: [Int]
budgetwidths = ([(Int, Int, Int)] -> Int) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map ([Int] -> Int
forall a. Integral a => [a] -> a
maximum' ([Int] -> Int)
-> ([(Int, Int, Int)] -> [Int]) -> [(Int, Int, Int)] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int, Int) -> Int) -> [(Int, Int, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Int, Int) -> Int
forall {a} {b} {c}. (a, b, c) -> b
second3) ([[(Int, Int, Int)]] -> [Int]) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> a -> b
$ [[(Int, Int, Int)]]
cols
percentwidths :: [Int]
percentwidths = ([(Int, Int, Int)] -> Int) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map ([Int] -> Int
forall a. Integral a => [a] -> a
maximum' ([Int] -> Int)
-> ([(Int, Int, Int)] -> [Int]) -> [(Int, Int, Int)] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int, Int) -> Int) -> [(Int, Int, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Int, Int) -> Int
forall {a} {b} {c}. (a, b, c) -> c
third3 ) ([[(Int, Int, Int)]] -> [Int]) -> [[(Int, Int, Int)]] -> [Int]
forall a b. (a -> b) -> a -> b
$ [[(Int, Int, Int)]]
cols
catcolumnwidths :: [[[a]]] -> [[a]]
catcolumnwidths = ([[a]] -> [[a]] -> [[a]]) -> [[a]] -> [[[a]]] -> [[a]]
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (([a] -> [a] -> [a]) -> [[a]] -> [[a]] -> [[a]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
(++)) ([[a]] -> [[[a]]] -> [[a]]) -> [[a]] -> [[[a]]] -> [[a]]
forall a b. (a -> b) -> a -> b
$ [a] -> [[a]]
forall a. a -> [a]
repeat []
cols :: [[(Int, Int, Int)]]
cols = [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]
forall a. [[a]] -> [[a]]
maybetranspose ([[(Int, Int, Int)]] -> [[(Int, Int, Int)]])
-> [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]
forall a b. (a -> b) -> a -> b
$ [[[(Int, Int, Int)]]] -> [[(Int, Int, Int)]]
forall {a}. [[[a]]] -> [[a]]
catcolumnwidths ([[[(Int, Int, Int)]]] -> [[(Int, Int, Int)]])
-> [[[(Int, Int, Int)]]] -> [[(Int, Int, Int)]]
forall a b. (a -> b) -> a -> b
$ (BudgetReportRow -> [[(Int, Int, Int)]])
-> [BudgetReportRow] -> [[[(Int, Int, Int)]]]
forall a b. (a -> b) -> [a] -> [b]
map ([(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]]
cellswidth ([(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]])
-> (BudgetReportRow -> [(Maybe Change, Maybe Change)])
-> BudgetReportRow
-> [[(Int, Int, Int)]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BudgetReportRow -> [(Maybe Change, Maybe Change)]
forall a.
PeriodicReportRow a (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
rowToBudgetCells) [BudgetReportRow]
items [[[(Int, Int, Int)]]]
-> [[[(Int, Int, Int)]]] -> [[[(Int, Int, Int)]]]
forall a. [a] -> [a] -> [a]
++ [[(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]]
cellswidth ([(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]])
-> [(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
forall a.
PeriodicReportRow a (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
rowToBudgetCells PeriodicReportRow () (Maybe Change, Maybe Change)
totrow]
cellswidth :: [BudgetCell] -> [[(Int, Int, Int)]]
cellswidth :: [(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]]
cellswidth [(Maybe Change, Maybe Change)]
row =
let cs :: [Text]
cs = [(Maybe Change, Maybe Change)] -> [Text]
budgetCellsCommodities [(Maybe Change, Maybe Change)]
row
(BudgetShowAmountsFn
showmixed, BudgetCalcPercentagesFn
percbudget) = [Text] -> (BudgetShowAmountsFn, BudgetCalcPercentagesFn)
mkBudgetDisplayFns [Text]
cs
disp :: (Maybe Change, Maybe Change) -> [BudgetDisplayCell]
disp = BudgetShowAmountsFn
-> BudgetCalcPercentagesFn
-> (Maybe Change, Maybe Change)
-> [BudgetDisplayCell]
showcell BudgetShowAmountsFn
showmixed BudgetCalcPercentagesFn
percbudget
budgetpercwidth :: (WideBuilder, Maybe WideBuilder) -> (Int, Int)
budgetpercwidth = WideBuilder -> Int
wbWidth (WideBuilder -> Int)
-> (Maybe WideBuilder -> Int)
-> (WideBuilder, Maybe WideBuilder)
-> (Int, Int)
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Int -> (WideBuilder -> Int) -> Maybe WideBuilder -> Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
0 WideBuilder -> Int
wbWidth
cellwidth :: BudgetDisplayCell -> (Int, Int, Int)
cellwidth (WideBuilder
am, Maybe (WideBuilder, Maybe WideBuilder)
bm) = let (Int
bw, Int
pw) = (Int, Int)
-> ((WideBuilder, Maybe WideBuilder) -> (Int, Int))
-> Maybe (WideBuilder, Maybe WideBuilder)
-> (Int, Int)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Int
0, Int
0) (WideBuilder, Maybe WideBuilder) -> (Int, Int)
budgetpercwidth Maybe (WideBuilder, Maybe WideBuilder)
bm in (WideBuilder -> Int
wbWidth WideBuilder
am, Int
bw, Int
pw)
in ((Maybe Change, Maybe Change) -> [(Int, Int, Int)])
-> [(Maybe Change, Maybe Change)] -> [[(Int, Int, Int)]]
forall a b. (a -> b) -> [a] -> [b]
map ((BudgetDisplayCell -> (Int, Int, Int))
-> [BudgetDisplayCell] -> [(Int, Int, Int)]
forall a b. (a -> b) -> [a] -> [b]
map BudgetDisplayCell -> (Int, Int, Int)
cellwidth ([BudgetDisplayCell] -> [(Int, Int, Int)])
-> ((Maybe Change, Maybe Change) -> [BudgetDisplayCell])
-> (Maybe Change, Maybe Change)
-> [(Int, Int, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Change, Maybe Change) -> [BudgetDisplayCell]
disp) [(Maybe Change, Maybe Change)]
row
totrowwidths :: [(Int, Int, Int)]
totrowwidths :: [(Int, Int, Int)]
totrowwidths
| Bool
transpose_ = Int -> [(Int, Int, Int)] -> [(Int, Int, Int)]
forall a. Int -> [a] -> [a]
drop ([[BudgetDisplayCell]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[BudgetDisplayCell]]
texts) [(Int, Int, Int)]
widths
| Bool
otherwise = [(Int, Int, Int)]
widths
maybetranspose :: [[a]] -> [[a]]
maybetranspose
| Bool
transpose_ = [[a]] -> [[a]]
forall a. [[a]] -> [[a]]
transpose
| Bool
otherwise = [[a]] -> [[a]]
forall a. a -> a
id
([Text]
accts', [WideBuilder]
itemscs, [[BudgetDisplayCell]]
texts) = [(Text, WideBuilder, [BudgetDisplayCell])]
-> ([Text], [WideBuilder], [[BudgetDisplayCell]])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(Text, WideBuilder, [BudgetDisplayCell])]
-> ([Text], [WideBuilder], [[BudgetDisplayCell]]))
-> [(Text, WideBuilder, [BudgetDisplayCell])]
-> ([Text], [WideBuilder], [[BudgetDisplayCell]])
forall a b. (a -> b) -> a -> b
$ [[(Text, WideBuilder, [BudgetDisplayCell])]]
-> [(Text, WideBuilder, [BudgetDisplayCell])]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[(Text, WideBuilder, [BudgetDisplayCell])]]
shownitems
where
shownitems :: [[(AccountName, WideBuilder, BudgetDisplayRow)]]
shownitems :: [[(Text, WideBuilder, [BudgetDisplayCell])]]
shownitems =
(BudgetReportRow -> [(Text, WideBuilder, [BudgetDisplayCell])])
-> [BudgetReportRow]
-> [[(Text, WideBuilder, [BudgetDisplayCell])]]
forall a b. (a -> b) -> [a] -> [b]
map (\BudgetReportRow
i ->
let
addacctcolumn :: [(b, c)] -> [(Text, b, c)]
addacctcolumn = ((b, c) -> (Text, b, c)) -> [(b, c)] -> [(Text, b, c)]
forall a b. (a -> b) -> [a] -> [b]
map (\(b
cs, c
cvals) -> (BudgetReportRow -> Text
forall a. PeriodicReportRow DisplayName a -> Text
renderacct BudgetReportRow
i, b
cs, c
cvals))
isunbudgetedrow :: Bool
isunbudgetedrow = DisplayName -> Text
displayFull (BudgetReportRow -> DisplayName
forall a b. PeriodicReportRow a b -> a
prrName BudgetReportRow
i) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
unbudgetedAccountName
in [(WideBuilder, [BudgetDisplayCell])]
-> [(Text, WideBuilder, [BudgetDisplayCell])]
forall {b} {c}. [(b, c)] -> [(Text, b, c)]
addacctcolumn ([(WideBuilder, [BudgetDisplayCell])]
-> [(Text, WideBuilder, [BudgetDisplayCell])])
-> [(WideBuilder, [BudgetDisplayCell])]
-> [(Text, WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ Bool
-> [(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])]
showrow Bool
isunbudgetedrow ([(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])])
-> [(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ BudgetReportRow -> [(Maybe Change, Maybe Change)]
forall a.
PeriodicReportRow a (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
rowToBudgetCells BudgetReportRow
i)
[BudgetReportRow]
items
where
renderacct :: PeriodicReportRow DisplayName a -> Text
renderacct PeriodicReportRow DisplayName a
row = case AccountListMode
accountlistmode_ of
AccountListMode
ALTree -> Int -> Text -> Text
T.replicate ((PeriodicReportRow DisplayName a -> Int
forall a. PeriodicReportRow DisplayName a -> Int
prrDepth PeriodicReportRow DisplayName a
row Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
2) Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> PeriodicReportRow DisplayName a -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrDisplayName PeriodicReportRow DisplayName a
row
AccountListMode
ALFlat -> Int -> Text -> Text
accountNameDrop (Int
drop_) (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow DisplayName a -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName PeriodicReportRow DisplayName a
row
([WideBuilder]
totrowcs, [[BudgetDisplayCell]]
totrowtexts) = [(WideBuilder, [BudgetDisplayCell])]
-> ([WideBuilder], [[BudgetDisplayCell]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(WideBuilder, [BudgetDisplayCell])]
-> ([WideBuilder], [[BudgetDisplayCell]]))
-> [(WideBuilder, [BudgetDisplayCell])]
-> ([WideBuilder], [[BudgetDisplayCell]])
forall a b. (a -> b) -> a -> b
$ [[(WideBuilder, [BudgetDisplayCell])]]
-> [(WideBuilder, [BudgetDisplayCell])]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[(WideBuilder, [BudgetDisplayCell])]]
showntotrow
where
showntotrow :: [[(WideBuilder, BudgetDisplayRow)]]
showntotrow :: [[(WideBuilder, [BudgetDisplayCell])]]
showntotrow = [Bool
-> [(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])]
showrow Bool
False ([(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])])
-> [(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
forall a.
PeriodicReportRow a (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
rowToBudgetCells PeriodicReportRow () (Maybe Change, Maybe Change)
totrow]
rowToBudgetCells :: PeriodicReportRow a BudgetCell -> [BudgetCell]
rowToBudgetCells :: forall a.
PeriodicReportRow a (Maybe Change, Maybe Change)
-> [(Maybe Change, Maybe Change)]
rowToBudgetCells (PeriodicReportRow a
_ [(Maybe Change, Maybe Change)]
as (Maybe Change, Maybe Change)
rowtot (Maybe Change, Maybe Change)
rowavg) = [(Maybe Change, Maybe Change)]
as
[(Maybe Change, Maybe Change)]
-> [(Maybe Change, Maybe Change)] -> [(Maybe Change, Maybe Change)]
forall a. [a] -> [a] -> [a]
++ [(Maybe Change, Maybe Change)
rowtot | Bool
row_total_ Bool -> Bool -> Bool
&& Bool -> Bool
not ([(Maybe Change, Maybe Change)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Maybe Change, Maybe Change)]
as)]
[(Maybe Change, Maybe Change)]
-> [(Maybe Change, Maybe Change)] -> [(Maybe Change, Maybe Change)]
forall a. [a] -> [a] -> [a]
++ [(Maybe Change, Maybe Change)
rowavg | Bool
average_ Bool -> Bool -> Bool
&& Bool -> Bool
not ([(Maybe Change, Maybe Change)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Maybe Change, Maybe Change)]
as)]
showrow :: Bool -> [BudgetCell] -> [(WideBuilder, BudgetDisplayRow)]
showrow :: Bool
-> [(Maybe Change, Maybe Change)]
-> [(WideBuilder, [BudgetDisplayCell])]
showrow Bool
isunbudgetedrow [(Maybe Change, Maybe Change)]
cells =
let
cs :: [Text]
cs = [(Maybe Change, Maybe Change)] -> [Text]
budgetCellsCommodities [(Maybe Change, Maybe Change)]
cells
cs1 :: [Text]
cs1 = if [Text] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
cs Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
isunbudgetedrow then [Text
""] else [Text]
cs
(BudgetShowAmountsFn
showmixed, BudgetCalcPercentagesFn
percbudget) = [Text] -> (BudgetShowAmountsFn, BudgetCalcPercentagesFn)
mkBudgetDisplayFns [Text]
cs1
in
[WideBuilder]
-> [[BudgetDisplayCell]] -> [(WideBuilder, [BudgetDisplayCell])]
forall a b. [a] -> [b] -> [(a, b)]
zip ((Text -> WideBuilder) -> [Text] -> [WideBuilder]
forall a b. (a -> b) -> [a] -> [b]
map Text -> WideBuilder
wbFromText [Text]
cs1) ([[BudgetDisplayCell]] -> [(WideBuilder, [BudgetDisplayCell])])
-> [[BudgetDisplayCell]] -> [(WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$
[[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a. [[a]] -> [[a]]
transpose ([[BudgetDisplayCell]] -> [[BudgetDisplayCell]])
-> [[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a b. (a -> b) -> a -> b
$
((Maybe Change, Maybe Change) -> [BudgetDisplayCell])
-> [(Maybe Change, Maybe Change)] -> [[BudgetDisplayCell]]
forall a b. (a -> b) -> [a] -> [b]
map (BudgetShowAmountsFn
-> BudgetCalcPercentagesFn
-> (Maybe Change, Maybe Change)
-> [BudgetDisplayCell]
showcell BudgetShowAmountsFn
showmixed BudgetCalcPercentagesFn
percbudget)
[(Maybe Change, Maybe Change)]
cells
budgetCellsCommodities :: [BudgetCell] -> [CommoditySymbol]
budgetCellsCommodities :: [(Maybe Change, Maybe Change)] -> [Text]
budgetCellsCommodities = Set Text -> [Text]
forall a. Set a -> [a]
S.toList (Set Text -> [Text])
-> ([(Maybe Change, Maybe Change)] -> Set Text)
-> [(Maybe Change, Maybe Change)]
-> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Set Text -> Set Text -> Set Text)
-> Set Text -> [Set Text] -> Set Text
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Set Text -> Set Text -> Set Text
forall a. Ord a => Set a -> Set a -> Set a
S.union Set Text
forall a. Monoid a => a
mempty ([Set Text] -> Set Text)
-> ([(Maybe Change, Maybe Change)] -> [Set Text])
-> [(Maybe Change, Maybe Change)]
-> Set Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Maybe Change, Maybe Change) -> Set Text)
-> [(Maybe Change, Maybe Change)] -> [Set Text]
forall a b. (a -> b) -> [a] -> [b]
map (Maybe Change, Maybe Change) -> Set Text
budgetCellCommodities
where
budgetCellCommodities :: BudgetCell -> S.Set CommoditySymbol
budgetCellCommodities :: (Maybe Change, Maybe Change) -> Set Text
budgetCellCommodities (Maybe Change
am, Maybe Change
bm) = Maybe Change -> Set Text
f Maybe Change
am Set Text -> Set Text -> Set Text
forall a. Ord a => Set a -> Set a -> Set a
`S.union` Maybe Change -> Set Text
f Maybe Change
bm
where f :: Maybe Change -> Set Text
f = Set Text -> (Change -> Set Text) -> Maybe Change -> Set Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Set Text
forall a. Monoid a => a
mempty Change -> Set Text
maCommodities
showcell :: BudgetShowAmountsFn -> BudgetCalcPercentagesFn -> BudgetCell -> BudgetDisplayRow
showcell :: BudgetShowAmountsFn
-> BudgetCalcPercentagesFn
-> (Maybe Change, Maybe Change)
-> [BudgetDisplayCell]
showcell BudgetShowAmountsFn
showCommodityAmounts BudgetCalcPercentagesFn
calcCommodityPercentages (Maybe Change
mactual, Maybe Change
mbudget) =
[WideBuilder]
-> [Maybe (WideBuilder, Maybe WideBuilder)] -> [BudgetDisplayCell]
forall a b. [a] -> [b] -> [(a, b)]
zip [WideBuilder]
actualamts [Maybe (WideBuilder, Maybe WideBuilder)]
budgetinfos
where
actual :: Change
actual = Change -> Maybe Change -> Change
forall a. a -> Maybe a -> a
fromMaybe Change
nullmixedamt Maybe Change
mactual
actualamts :: [WideBuilder]
actualamts = BudgetShowAmountsFn
showCommodityAmounts Change
actual
budgetinfos :: [Maybe (WideBuilder, Maybe WideBuilder)]
budgetinfos =
case Maybe Change
mbudget of
Maybe Change
Nothing -> Maybe (WideBuilder, Maybe WideBuilder)
-> [Maybe (WideBuilder, Maybe WideBuilder)]
forall a. a -> [a]
repeat Maybe (WideBuilder, Maybe WideBuilder)
forall a. Maybe a
Nothing
Just Change
goal -> ((WideBuilder, Maybe WideBuilder)
-> Maybe (WideBuilder, Maybe WideBuilder))
-> [(WideBuilder, Maybe WideBuilder)]
-> [Maybe (WideBuilder, Maybe WideBuilder)]
forall a b. (a -> b) -> [a] -> [b]
map (WideBuilder, Maybe WideBuilder)
-> Maybe (WideBuilder, Maybe WideBuilder)
forall a. a -> Maybe a
Just ([(WideBuilder, Maybe WideBuilder)]
-> [Maybe (WideBuilder, Maybe WideBuilder)])
-> [(WideBuilder, Maybe WideBuilder)]
-> [Maybe (WideBuilder, Maybe WideBuilder)]
forall a b. (a -> b) -> a -> b
$ Change -> [(WideBuilder, Maybe WideBuilder)]
showGoalAmountsAndPercentages Change
goal
where
showGoalAmountsAndPercentages :: MixedAmount -> [(WideBuilder, Maybe WideBuilder)]
showGoalAmountsAndPercentages :: Change -> [(WideBuilder, Maybe WideBuilder)]
showGoalAmountsAndPercentages Change
goal = [WideBuilder]
-> [Maybe WideBuilder] -> [(WideBuilder, Maybe WideBuilder)]
forall a b. [a] -> [b] -> [(a, b)]
zip [WideBuilder]
amts [Maybe WideBuilder]
mpcts
where
amts :: [WideBuilder]
amts = BudgetShowAmountsFn
showCommodityAmounts Change
goal
mpcts :: [Maybe WideBuilder]
mpcts = (Maybe Percentage -> Maybe WideBuilder)
-> [Maybe Percentage] -> [Maybe WideBuilder]
forall a b. (a -> b) -> [a] -> [b]
map (Percentage -> WideBuilder
showrounded (Percentage -> WideBuilder)
-> Maybe Percentage -> Maybe WideBuilder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>) ([Maybe Percentage] -> [Maybe WideBuilder])
-> [Maybe Percentage] -> [Maybe WideBuilder]
forall a b. (a -> b) -> a -> b
$ BudgetCalcPercentagesFn
calcCommodityPercentages Change
actual Change
goal
where showrounded :: Percentage -> WideBuilder
showrounded = Text -> WideBuilder
wbFromText (Text -> WideBuilder)
-> (Percentage -> Text) -> Percentage -> WideBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (Percentage -> String) -> Percentage -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Percentage -> String
forall a. Show a => a -> String
show (Percentage -> String)
-> (Percentage -> Percentage) -> Percentage -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Percentage -> Percentage
forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo Word8
0
mkBudgetDisplayFns :: [CommoditySymbol] -> (BudgetShowAmountsFn, BudgetCalcPercentagesFn)
mkBudgetDisplayFns :: [Text] -> (BudgetShowAmountsFn, BudgetCalcPercentagesFn)
mkBudgetDisplayFns [Text]
cs = case Layout
layout_ of
LayoutWide Maybe Int
width ->
( WideBuilder -> [WideBuilder]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (WideBuilder -> [WideBuilder])
-> (Change -> WideBuilder) -> BudgetShowAmountsFn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountFormat -> Change -> WideBuilder
showMixedAmountB AmountFormat
oneLineNoCostFmt{displayMaxWidth=width, displayColour=color_}
, \Change
a -> Maybe Percentage -> [Maybe Percentage]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Percentage -> [Maybe Percentage])
-> (Change -> Maybe Percentage) -> Change -> [Maybe Percentage]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Change -> Change -> Maybe Percentage
percentage Change
a)
Layout
_ -> ( AmountFormat -> BudgetShowAmountsFn
showMixedAmountLinesB AmountFormat
noCostFmt{displayCommodity=layout_/=LayoutBare, displayCommodityOrder=Just cs, displayMinWidth=Nothing, displayColour=color_}
, \Change
a Change
b -> (Text -> Maybe Percentage) -> [Text] -> [Maybe Percentage]
forall a b. (a -> b) -> [a] -> [b]
map (Change -> Change -> Text -> Maybe Percentage
percentage' Change
a Change
b) [Text]
cs)
where
percentage :: Change -> BudgetGoal -> Maybe Percentage
percentage :: Change -> Change -> Maybe Percentage
percentage Change
actual Change
budget =
case (Change -> [Amount]
costedAmounts Change
actual, Change -> [Amount]
costedAmounts Change
budget) of
([Amount
a], [Amount
b]) | (Amount -> Text
acommodity Amount
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Amount -> Text
acommodity Amount
b Bool -> Bool -> Bool
|| Amount -> Bool
amountLooksZero Amount
a) Bool -> Bool -> Bool
&& Bool -> Bool
not (Amount -> Bool
amountLooksZero Amount
b)
-> Percentage -> Maybe Percentage
forall a. a -> Maybe a
Just (Percentage -> Maybe Percentage) -> Percentage -> Maybe Percentage
forall a b. (a -> b) -> a -> b
$ Percentage
100 Percentage -> Percentage -> Percentage
forall a. Num a => a -> a -> a
* Amount -> Percentage
aquantity Amount
a Percentage -> Percentage -> Percentage
forall a. Fractional a => a -> a -> a
/ Amount -> Percentage
aquantity Amount
b
([Amount], [Amount])
_ -> Maybe Percentage
forall a. Maybe a
Nothing
where
costedAmounts :: Change -> [Amount]
costedAmounts = case Maybe ConversionOp
conversionop_ of
Just ConversionOp
ToCost -> Change -> [Amount]
amounts (Change -> [Amount]) -> (Change -> Change) -> Change -> [Amount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Change -> Change
mixedAmountCost
Maybe ConversionOp
_ -> Change -> [Amount]
amounts
percentage' :: Change -> BudgetGoal -> CommoditySymbol -> Maybe Percentage
percentage' :: Change -> Change -> Text -> Maybe Percentage
percentage' Change
am Change
bm Text
c = case ((,) (Maybe Amount -> Maybe Amount -> (Maybe Amount, Maybe Amount))
-> (Change -> Maybe Amount)
-> Change
-> Change
-> (Maybe Amount, Maybe Amount)
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Amount -> Bool) -> [Amount] -> Maybe Amount
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) Text
c (Text -> Bool) -> (Amount -> Text) -> Amount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Text
acommodity) ([Amount] -> Maybe Amount)
-> (Change -> [Amount]) -> Change -> Maybe Amount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Change -> [Amount]
amounts) Change
am Change
bm of
(Just Amount
a, Just Amount
b) -> Change -> Change -> Maybe Percentage
percentage (Amount -> Change
mixedAmount Amount
a) (Amount -> Change
mixedAmount Amount
b)
(Maybe Amount, Maybe Amount)
_ -> Maybe Percentage
forall a. Maybe a
Nothing
budgetReportAsCsv :: ReportOpts -> BudgetReport -> [[Text]]
budgetReportAsCsv :: ReportOpts -> BudgetReport -> [[Text]]
budgetReportAsCsv
ReportOpts{Bool
Int
[Text]
[Status]
Maybe Int
Maybe Text
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
accountlistmode_ :: ReportOpts -> AccountListMode
empty_ :: ReportOpts -> Bool
infer_prices_ :: ReportOpts -> Bool
interval_ :: ReportOpts -> Interval
budgetpat_ :: ReportOpts -> Maybe Text
period_ :: ReportOpts -> Period
statuses_ :: ReportOpts -> [Status]
conversionop_ :: ReportOpts -> Maybe ConversionOp
value_ :: ReportOpts -> Maybe ValuationType
depth_ :: ReportOpts -> Maybe Int
date2_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
real_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
pretty_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [Text]
average_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
txn_dates_ :: ReportOpts -> Bool
balancecalc_ :: ReportOpts -> BalanceCalculation
balanceaccum_ :: ReportOpts -> BalanceAccumulation
drop_ :: ReportOpts -> Int
declared_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
summary_only_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
invert_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
color_ :: ReportOpts -> Bool
transpose_ :: ReportOpts -> Bool
layout_ :: ReportOpts -> Layout
period_ :: Period
interval_ :: Interval
statuses_ :: [Status]
conversionop_ :: Maybe ConversionOp
value_ :: Maybe ValuationType
infer_prices_ :: Bool
depth_ :: Maybe Int
date2_ :: Bool
empty_ :: Bool
no_elide_ :: Bool
real_ :: Bool
format_ :: StringFormat
pretty_ :: Bool
querystring_ :: [Text]
average_ :: Bool
related_ :: Bool
txn_dates_ :: Bool
balancecalc_ :: BalanceCalculation
balanceaccum_ :: BalanceAccumulation
budgetpat_ :: Maybe Text
accountlistmode_ :: AccountListMode
drop_ :: Int
declared_ :: Bool
row_total_ :: Bool
no_total_ :: Bool
summary_only_ :: Bool
show_costs_ :: Bool
sort_amount_ :: Bool
percent_ :: Bool
invert_ :: Bool
normalbalance_ :: Maybe NormalSign
color_ :: Bool
transpose_ :: Bool
layout_ :: Layout
..}
(PeriodicReport [DateSpan]
colspans [BudgetReportRow]
items PeriodicReportRow () (Maybe Change, Maybe Change)
totrow)
= (if Bool
transpose_ then [[Text]] -> [[Text]]
forall a. [[a]] -> [[a]]
transpose else [[Text]] -> [[Text]]
forall a. a -> a
id) ([[Text]] -> [[Text]]) -> [[Text]] -> [[Text]]
forall a b. (a -> b) -> a -> b
$
(Text
"Account" Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:
[Text
"Commodity" | Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare ]
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ (DateSpan -> [Text]) -> [DateSpan] -> [Text]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\DateSpan
spn -> [DateSpan -> Text
showDateSpan DateSpan
spn, Text
"budget"]) [DateSpan]
colspans
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [[Text]] -> [Text]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Text
"Total" ,Text
"budget"] | Bool
row_total_]
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [[Text]] -> [Text]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Text
"Average",Text
"budget"] | Bool
average_]
) [Text] -> [[Text]] -> [[Text]]
forall a. a -> [a] -> [a]
:
(BudgetReportRow -> [[Text]]) -> [BudgetReportRow] -> [[Text]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((BudgetReportRow -> Text) -> BudgetReportRow -> [[Text]]
forall a.
(PeriodicReportRow a (Maybe Change, Maybe Change) -> Text)
-> PeriodicReportRow a (Maybe Change, Maybe Change) -> [[Text]]
rowAsTexts BudgetReportRow -> Text
forall a. PeriodicReportRow DisplayName a -> Text
prrFullName) [BudgetReportRow]
items
[[Text]] -> [[Text]] -> [[Text]]
forall a. [a] -> [a] -> [a]
++ [[[Text]]] -> [[Text]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ (PeriodicReportRow () (Maybe Change, Maybe Change) -> Text)
-> PeriodicReportRow () (Maybe Change, Maybe Change) -> [[Text]]
forall a.
(PeriodicReportRow a (Maybe Change, Maybe Change) -> Text)
-> PeriodicReportRow a (Maybe Change, Maybe Change) -> [[Text]]
rowAsTexts (Text -> PeriodicReportRow () (Maybe Change, Maybe Change) -> Text
forall a b. a -> b -> a
const Text
"Total:") PeriodicReportRow () (Maybe Change, Maybe Change)
totrow | Bool -> Bool
not Bool
no_total_ ]
where
flattentuples :: [(a, a)] -> [a]
flattentuples [(a, a)]
tups = [[a]] -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[a
a,a
b] | (a
a,a
b) <- [(a, a)]
tups]
showNorm :: Maybe Change -> Text
showNorm = Text -> (Change -> Text) -> Maybe Change -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" (WideBuilder -> Text
wbToText (WideBuilder -> Text) -> (Change -> WideBuilder) -> Change -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountFormat -> Change -> WideBuilder
showMixedAmountB AmountFormat
oneLineNoCostFmt)
rowAsTexts :: (PeriodicReportRow a BudgetCell -> Text)
-> PeriodicReportRow a BudgetCell
-> [[Text]]
rowAsTexts :: forall a.
(PeriodicReportRow a (Maybe Change, Maybe Change) -> Text)
-> PeriodicReportRow a (Maybe Change, Maybe Change) -> [[Text]]
rowAsTexts PeriodicReportRow a (Maybe Change, Maybe Change) -> Text
render row :: PeriodicReportRow a (Maybe Change, Maybe Change)
row@(PeriodicReportRow a
_ [(Maybe Change, Maybe Change)]
as (Maybe Change
rowtot,Maybe Change
budgettot) (Maybe Change
rowavg, Maybe Change
budgetavg))
| Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
/= Layout
LayoutBare = [PeriodicReportRow a (Maybe Change, Maybe Change) -> Text
render PeriodicReportRow a (Maybe Change, Maybe Change)
row Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: (Maybe Change -> Text) -> [Maybe Change] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Maybe Change -> Text
showNorm [Maybe Change]
vals]
| Bool
otherwise =
[[Text]] -> [[Text]]
joinNames ([[Text]] -> [[Text]])
-> ([Maybe Change] -> [[Text]]) -> [Maybe Change] -> [[Text]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> [Text] -> [Text]) -> [Text] -> [[Text]] -> [[Text]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (:) [Text]
cs
([[Text]] -> [[Text]])
-> ([Maybe Change] -> [[Text]]) -> [Maybe Change] -> [[Text]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[Text]] -> [[Text]]
forall a. [[a]] -> [[a]]
transpose
([[Text]] -> [[Text]])
-> ([Maybe Change] -> [[Text]]) -> [Maybe Change] -> [[Text]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Change -> [Text]) -> [Maybe Change] -> [[Text]]
forall a b. (a -> b) -> [a] -> [b]
map ((WideBuilder -> Text) -> [WideBuilder] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map WideBuilder -> Text
wbToText ([WideBuilder] -> [Text])
-> (Maybe Change -> [WideBuilder]) -> Maybe Change -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountFormat -> BudgetShowAmountsFn
showMixedAmountLinesB AmountFormat
dopts BudgetShowAmountsFn
-> (Maybe Change -> Change) -> Maybe Change -> [WideBuilder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Change -> Maybe Change -> Change
forall a. a -> Maybe a -> a
fromMaybe Change
nullmixedamt)
([Maybe Change] -> [[Text]]) -> [Maybe Change] -> [[Text]]
forall a b. (a -> b) -> a -> b
$ [Maybe Change]
vals
where
cs :: [Text]
cs = Set Text -> [Text]
forall a. Set a -> [a]
S.toList (Set Text -> [Text])
-> ([Change] -> Set Text) -> [Change] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Set Text -> Set Text -> Set Text)
-> Set Text -> [Set Text] -> Set Text
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Set Text -> Set Text -> Set Text
forall a. Ord a => Set a -> Set a -> Set a
S.union Set Text
forall a. Monoid a => a
mempty ([Set Text] -> Set Text)
-> ([Change] -> [Set Text]) -> [Change] -> Set Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Change -> Set Text) -> [Change] -> [Set Text]
forall a b. (a -> b) -> [a] -> [b]
map Change -> Set Text
maCommodities ([Change] -> [Text]) -> [Change] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Maybe Change] -> [Change]
forall a. [Maybe a] -> [a]
catMaybes [Maybe Change]
vals
dopts :: AmountFormat
dopts = AmountFormat
oneLineNoCostFmt{displayCommodity=layout_ /= LayoutBare, displayCommodityOrder=Just cs, displayMinWidth=Nothing}
vals :: [Maybe Change]
vals = [(Maybe Change, Maybe Change)] -> [Maybe Change]
forall {a}. [(a, a)] -> [a]
flattentuples [(Maybe Change, Maybe Change)]
as
[Maybe Change] -> [Maybe Change] -> [Maybe Change]
forall a. [a] -> [a] -> [a]
++ [[Maybe Change]] -> [Maybe Change]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Maybe Change
rowtot, Maybe Change
budgettot] | Bool
row_total_]
[Maybe Change] -> [Maybe Change] -> [Maybe Change]
forall a. [a] -> [a] -> [a]
++ [[Maybe Change]] -> [Maybe Change]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Maybe Change
rowavg, Maybe Change
budgetavg] | Bool
average_]
joinNames :: [[Text]] -> [[Text]]
joinNames = ([Text] -> [Text]) -> [[Text]] -> [[Text]]
forall a b. (a -> b) -> [a] -> [b]
map (PeriodicReportRow a (Maybe Change, Maybe Change) -> Text
render PeriodicReportRow a (Maybe Change, Maybe Change)
row Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:)
tests_BudgetReport :: TestTree
tests_BudgetReport = String -> [TestTree] -> TestTree
testGroup String
"BudgetReport" [
]