{-# 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')
import Data.List.Extra (nubSort)
import Data.Maybe (fromMaybe, catMaybes)
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 qualified Text.Tabular.AsciiWide as Tab
import Hledger.Data
import Hledger.Utils
import Hledger.Reports.ReportOptions
import Hledger.Reports.ReportTypes
import Hledger.Reports.MultiBalanceReport
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 BudgetShowMixed = MixedAmount -> [WideBuilder]
type BudgetPercBudget = Change -> BudgetGoal -> [Maybe Percentage]
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_ :: AccountListMode
accountlistmode_ = AccountListMode
ALTree }
showunbudgeted :: Bool
showunbudgeted = ReportOpts -> Bool
empty_ ReportOpts
ropts
budgetedaccts :: Set AccountName
budgetedaccts =
String -> Set AccountName -> Set AccountName
forall a. Show a => String -> a -> a
dbg3 String
"budgetedacctsinperiod" (Set AccountName -> Set AccountName)
-> Set AccountName -> Set AccountName
forall a b. (a -> b) -> a -> b
$
[AccountName] -> Set AccountName
forall a. Ord a => [a] -> Set a
S.fromList ([AccountName] -> Set AccountName)
-> [AccountName] -> Set AccountName
forall a b. (a -> b) -> a -> b
$
[AccountName] -> [AccountName]
expandAccountNames ([AccountName] -> [AccountName]) -> [AccountName] -> [AccountName]
forall a b. (a -> b) -> a -> b
$
[Posting] -> [AccountName]
accountNamesFromPostings ([Posting] -> [AccountName]) -> [Posting] -> [AccountName]
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 -> DateSpan -> [Transaction]
`runPeriodicTransaction` DateSpan
reportspan) ([PeriodicTransaction] -> [Transaction])
-> [PeriodicTransaction] -> [Transaction]
forall a b. (a -> b) -> a -> b
$
Journal -> [PeriodicTransaction]
jperiodictxns Journal
j
actualj :: Journal
actualj = Set AccountName -> Bool -> Journal -> Journal
journalWithBudgetAccountNames Set AccountName
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 :: PeriodicReport DisplayName MixedAmount
budgetgoalreport@(PeriodicReport [DateSpan]
_ [PeriodicReportRow DisplayName MixedAmount]
budgetgoalitems PeriodicReportRow () MixedAmount
budgetgoaltotals) =
String
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
forall a. Show a => String -> a -> a
dbg5 String
"budgetgoalreport" (PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount)
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal
-> PriceOracle
-> Set AccountName
-> PeriodicReport DisplayName MixedAmount
multiBalanceReportWith ReportSpec
rspec{_rsReportOpts :: ReportOpts
_rsReportOpts=ReportOpts
ropts{empty_ :: Bool
empty_=Bool
True}} Journal
budgetj PriceOracle
priceoracle Set AccountName
forall a. Monoid a => a
mempty
budgetedacctsseen :: Set AccountName
budgetedacctsseen = [AccountName] -> Set AccountName
forall a. Ord a => [a] -> Set a
S.fromList ([AccountName] -> Set AccountName)
-> [AccountName] -> Set AccountName
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount -> AccountName)
-> [PeriodicReportRow DisplayName MixedAmount] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName [PeriodicReportRow DisplayName MixedAmount]
budgetgoalitems
actualreport :: PeriodicReport DisplayName MixedAmount
actualreport@(PeriodicReport [DateSpan]
actualspans [PeriodicReportRow DisplayName MixedAmount]
_ PeriodicReportRow () MixedAmount
_) =
String
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
forall a. Show a => String -> a -> a
dbg5 String
"actualreport" (PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount)
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal
-> PriceOracle
-> Set AccountName
-> PeriodicReport DisplayName MixedAmount
multiBalanceReportWith ReportSpec
rspec{_rsReportOpts :: ReportOpts
_rsReportOpts=ReportOpts
ropts{empty_ :: Bool
empty_=Bool
True}} Journal
actualj PriceOracle
priceoracle Set AccountName
budgetedacctsseen
budgetgoalreport' :: PeriodicReport DisplayName MixedAmount
budgetgoalreport'
| ReportOpts -> Interval
interval_ ReportOpts
ropts Interval -> Interval -> Bool
forall a. Eq a => a -> a -> Bool
== Interval
NoInterval = [DateSpan]
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
-> PeriodicReport DisplayName MixedAmount
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
actualspans [PeriodicReportRow DisplayName MixedAmount]
budgetgoalitems PeriodicReportRow () MixedAmount
budgetgoaltotals
| Bool
otherwise = PeriodicReport DisplayName MixedAmount
budgetgoalreport
budgetreport :: BudgetReport
budgetreport = ReportOpts
-> Journal
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
-> BudgetReport
combineBudgetAndActual ReportOpts
ropts Journal
j PeriodicReport DisplayName MixedAmount
budgetgoalreport' PeriodicReport DisplayName MixedAmount
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
$ BalancingOpts -> Journal -> Either String Journal
journalBalanceTransactions BalancingOpts
bopts Journal
j{ jtxns :: [Transaction]
jtxns = [Transaction]
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
$ DateSpan
reportspan
pat :: AccountName
pat = AccountName -> Maybe AccountName -> AccountName
forall a. a -> Maybe a -> a
fromMaybe AccountName
"" (Maybe AccountName -> AccountName)
-> Maybe AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ String -> Maybe AccountName -> Maybe AccountName
forall a. Show a => String -> a -> a
dbg3 String
"budget pattern" (Maybe AccountName -> Maybe AccountName)
-> Maybe AccountName -> Maybe AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> AccountName
T.toLower (AccountName -> AccountName)
-> Maybe AccountName -> Maybe AccountName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReportOpts -> Maybe AccountName
budgetpat_ ReportOpts
ropts
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 <- Journal -> [PeriodicTransaction]
jperiodictxns Journal
j
, AccountName
pat AccountName -> AccountName -> Bool
`T.isInfixOf` AccountName -> AccountName
T.toLower (PeriodicTransaction -> AccountName
ptdescription PeriodicTransaction
pt)
, Transaction
t <- PeriodicTransaction -> DateSpan -> [Transaction]
runPeriodicTransaction 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 :: AccountName
tdescription = String -> AccountName
T.pack String
"Budget transaction" }
journalWithBudgetAccountNames :: S.Set AccountName -> Bool -> Journal -> Journal
journalWithBudgetAccountNames :: Set AccountName -> Bool -> Journal -> Journal
journalWithBudgetAccountNames Set AccountName
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
.[AccountName] -> String
forall a. Show a => a -> String
pshow([AccountName] -> String)
-> (Journal -> [AccountName]) -> Journal -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Journal -> [AccountName]
journalAccountNamesUsed) (Journal -> Journal) -> Journal -> Journal
forall a b. (a -> b) -> a -> b
$
Journal
j { jtxns :: [Transaction]
jtxns = Transaction -> Transaction
remapTxn (Transaction -> Transaction) -> [Transaction] -> [Transaction]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Journal -> [Transaction]
jtxns Journal
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 :: AccountName
paccount = AccountName -> AccountName
remapAccount (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ Posting -> AccountName
paccount Posting
p, poriginal :: Maybe Posting
poriginal = Posting -> Maybe Posting
poriginal Posting
p Maybe Posting -> Maybe Posting -> Maybe Posting
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Posting -> Maybe Posting
forall a. a -> Maybe a
Just Posting
p }
remapAccount :: AccountName -> AccountName
remapAccount AccountName
a
| AccountName
a AccountName -> Set AccountName -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set AccountName
budgetedaccts = AccountName
a
| Just AccountName
p <- Maybe AccountName
budgetedparent = if Bool
showunbudgeted then AccountName
a else AccountName
p
| Bool
otherwise = if Bool
showunbudgeted then AccountName
u AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
acctsep AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
a else AccountName
u
where
budgetedparent :: Maybe AccountName
budgetedparent = (AccountName -> Bool) -> [AccountName] -> Maybe AccountName
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (AccountName -> Set AccountName -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set AccountName
budgetedaccts) ([AccountName] -> Maybe AccountName)
-> [AccountName] -> Maybe AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> [AccountName]
parentAccountNames AccountName
a
u :: AccountName
u = AccountName
unbudgetedAccountName
combineBudgetAndActual :: ReportOpts -> Journal -> MultiBalanceReport -> MultiBalanceReport -> BudgetReport
combineBudgetAndActual :: ReportOpts
-> Journal
-> PeriodicReport DisplayName MixedAmount
-> PeriodicReport DisplayName MixedAmount
-> BudgetReport
combineBudgetAndActual ReportOpts
ropts Journal
j
(PeriodicReport [DateSpan]
budgetperiods [PeriodicReportRow DisplayName MixedAmount]
budgetrows (PeriodicReportRow ()
_ [MixedAmount]
budgettots MixedAmount
budgetgrandtot MixedAmount
budgetgrandavg))
(PeriodicReport [DateSpan]
actualperiods [PeriodicReportRow DisplayName MixedAmount]
actualrows (PeriodicReportRow ()
_ [MixedAmount]
actualtots MixedAmount
actualgrandtot MixedAmount
actualgrandavg)) =
[DateSpan]
-> [PeriodicReportRow DisplayName BudgetCell]
-> PeriodicReportRow () BudgetCell
-> BudgetReport
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
periods [PeriodicReportRow DisplayName BudgetCell]
sortedrows PeriodicReportRow () BudgetCell
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
rows1 :: [PeriodicReportRow DisplayName BudgetCell]
rows1 =
[ DisplayName
-> [BudgetCell]
-> BudgetCell
-> BudgetCell
-> PeriodicReportRow DisplayName BudgetCell
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
acct [BudgetCell]
amtandgoals BudgetCell
totamtandgoal BudgetCell
avgamtandgoal
| PeriodicReportRow DisplayName
acct [MixedAmount]
actualamts MixedAmount
actualtot MixedAmount
actualavg <- [PeriodicReportRow DisplayName MixedAmount]
actualrows
, let mbudgetgoals :: Maybe ([MixedAmount], MixedAmount, MixedAmount)
mbudgetgoals = AccountName
-> HashMap AccountName ([MixedAmount], MixedAmount, MixedAmount)
-> Maybe ([MixedAmount], MixedAmount, MixedAmount)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup (DisplayName -> AccountName
displayFull DisplayName
acct) HashMap AccountName ([MixedAmount], MixedAmount, MixedAmount)
budgetGoalsByAcct :: Maybe ([BudgetGoal], BudgetTotal, BudgetAverage)
, let budgetmamts :: [Maybe MixedAmount]
budgetmamts = [Maybe MixedAmount]
-> (([MixedAmount], MixedAmount, MixedAmount)
-> [Maybe MixedAmount])
-> Maybe ([MixedAmount], MixedAmount, MixedAmount)
-> [Maybe MixedAmount]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe MixedAmount
forall a. Maybe a
Nothing Maybe MixedAmount -> [DateSpan] -> [Maybe MixedAmount]
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ [DateSpan]
periods) ((MixedAmount -> Maybe MixedAmount)
-> [MixedAmount] -> [Maybe MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just ([MixedAmount] -> [Maybe MixedAmount])
-> (([MixedAmount], MixedAmount, MixedAmount) -> [MixedAmount])
-> ([MixedAmount], MixedAmount, MixedAmount)
-> [Maybe MixedAmount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([MixedAmount], MixedAmount, MixedAmount) -> [MixedAmount]
forall a b c. (a, b, c) -> a
first3) Maybe ([MixedAmount], MixedAmount, MixedAmount)
mbudgetgoals :: [Maybe BudgetGoal]
, let mbudgettot :: Maybe MixedAmount
mbudgettot = ([MixedAmount], MixedAmount, MixedAmount) -> MixedAmount
forall a b c. (a, b, c) -> b
second3 (([MixedAmount], MixedAmount, MixedAmount) -> MixedAmount)
-> Maybe ([MixedAmount], MixedAmount, MixedAmount)
-> Maybe MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe ([MixedAmount], MixedAmount, MixedAmount)
mbudgetgoals :: Maybe BudgetTotal
, let mbudgetavg :: Maybe MixedAmount
mbudgetavg = ([MixedAmount], MixedAmount, MixedAmount) -> MixedAmount
forall a b c. (a, b, c) -> c
third3 (([MixedAmount], MixedAmount, MixedAmount) -> MixedAmount)
-> Maybe ([MixedAmount], MixedAmount, MixedAmount)
-> Maybe MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe ([MixedAmount], MixedAmount, MixedAmount)
mbudgetgoals :: Maybe BudgetAverage
, let acctBudgetByPeriod :: Map DateSpan MixedAmount
acctBudgetByPeriod = [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [ (DateSpan
p,MixedAmount
budgetamt) | (DateSpan
p, Just MixedAmount
budgetamt) <- [DateSpan]
-> [Maybe MixedAmount] -> [(DateSpan, Maybe MixedAmount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [Maybe MixedAmount]
budgetmamts ] :: Map DateSpan BudgetGoal
, let acctActualByPeriod :: Map DateSpan MixedAmount
acctActualByPeriod = [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [ (DateSpan
p,MixedAmount
actualamt) | (DateSpan
p, Just MixedAmount
actualamt) <- [DateSpan]
-> [Maybe MixedAmount] -> [(DateSpan, Maybe MixedAmount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
actualperiods ((MixedAmount -> Maybe MixedAmount)
-> [MixedAmount] -> [Maybe MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just [MixedAmount]
actualamts) ] :: Map DateSpan Change
, let amtandgoals :: [BudgetCell]
amtandgoals = [ (DateSpan -> Map DateSpan MixedAmount -> Maybe MixedAmount
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan MixedAmount
acctActualByPeriod, DateSpan -> Map DateSpan MixedAmount -> Maybe MixedAmount
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan MixedAmount
acctBudgetByPeriod) | DateSpan
p <- [DateSpan]
periods ] :: [BudgetCell]
, let totamtandgoal :: BudgetCell
totamtandgoal = (MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
actualtot, Maybe MixedAmount
mbudgettot)
, let avgamtandgoal :: BudgetCell
avgamtandgoal = (MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
actualavg, Maybe MixedAmount
mbudgetavg)
]
where
HashMap AccountName ([MixedAmount], MixedAmount, MixedAmount)
budgetGoalsByAcct :: HashMap AccountName ([BudgetGoal], BudgetTotal, BudgetAverage) =
[(AccountName, ([MixedAmount], MixedAmount, MixedAmount))]
-> HashMap AccountName ([MixedAmount], MixedAmount, MixedAmount)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList [ (DisplayName -> AccountName
displayFull DisplayName
acct, ([MixedAmount]
amts, MixedAmount
tot, MixedAmount
avg))
| PeriodicReportRow DisplayName
acct [MixedAmount]
amts MixedAmount
tot MixedAmount
avg <- [PeriodicReportRow DisplayName MixedAmount]
budgetrows ]
rows2 :: [PeriodicReportRow DisplayName BudgetCell]
rows2 =
[ DisplayName
-> [BudgetCell]
-> BudgetCell
-> BudgetCell
-> PeriodicReportRow DisplayName BudgetCell
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
acct [BudgetCell]
amtandgoals BudgetCell
forall a. (Maybe a, Maybe MixedAmount)
totamtandgoal BudgetCell
forall a. (Maybe a, Maybe MixedAmount)
avgamtandgoal
| PeriodicReportRow DisplayName
acct [MixedAmount]
budgetgoals MixedAmount
budgettot MixedAmount
budgetavg <- [PeriodicReportRow DisplayName MixedAmount]
budgetrows
, DisplayName -> AccountName
displayFull DisplayName
acct AccountName -> [AccountName] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` (PeriodicReportRow DisplayName BudgetCell -> AccountName)
-> [PeriodicReportRow DisplayName BudgetCell] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName BudgetCell -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName [PeriodicReportRow DisplayName BudgetCell]
rows1
, let acctBudgetByPeriod :: Map DateSpan MixedAmount
acctBudgetByPeriod = [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount)
-> [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [MixedAmount] -> [(DateSpan, MixedAmount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [MixedAmount]
budgetgoals :: Map DateSpan BudgetGoal
, let amtandgoals :: [BudgetCell]
amtandgoals = [ (Maybe MixedAmount
forall a. Maybe a
Nothing, DateSpan -> Map DateSpan MixedAmount -> Maybe MixedAmount
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan MixedAmount
acctBudgetByPeriod) | DateSpan
p <- [DateSpan]
periods ] :: [BudgetCell]
, let totamtandgoal :: (Maybe a, Maybe MixedAmount)
totamtandgoal = (Maybe a
forall a. Maybe a
Nothing, MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
budgettot)
, let avgamtandgoal :: (Maybe a, Maybe MixedAmount)
avgamtandgoal = (Maybe a
forall a. Maybe a
Nothing, MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
budgetavg)
]
[PeriodicReportRow DisplayName BudgetCell]
sortedrows :: [BudgetReportRow] = [AccountName]
-> [PeriodicReportRow DisplayName BudgetCell]
-> [PeriodicReportRow DisplayName BudgetCell]
forall b.
[AccountName]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike ([PeriodicReportRow DisplayName BudgetCell] -> [AccountName]
forall b.
[PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [AccountName]
mbrsorted [PeriodicReportRow DisplayName BudgetCell]
unbudgetedrows [AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ [PeriodicReportRow DisplayName BudgetCell] -> [AccountName]
forall b.
[PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [AccountName]
mbrsorted [PeriodicReportRow DisplayName BudgetCell]
rows') [PeriodicReportRow DisplayName BudgetCell]
rows
where
([PeriodicReportRow DisplayName BudgetCell]
unbudgetedrows, [PeriodicReportRow DisplayName BudgetCell]
rows') = (PeriodicReportRow DisplayName BudgetCell -> Bool)
-> [PeriodicReportRow DisplayName BudgetCell]
-> ([PeriodicReportRow DisplayName BudgetCell],
[PeriodicReportRow DisplayName BudgetCell])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition ((AccountName -> AccountName -> Bool
forall a. Eq a => a -> a -> Bool
==AccountName
unbudgetedAccountName) (AccountName -> Bool)
-> (PeriodicReportRow DisplayName BudgetCell -> AccountName)
-> PeriodicReportRow DisplayName BudgetCell
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodicReportRow DisplayName BudgetCell -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName) [PeriodicReportRow DisplayName BudgetCell]
rows
mbrsorted :: [PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [AccountName]
mbrsorted = (PeriodicReportRow DisplayName MixedAmount -> AccountName)
-> [PeriodicReportRow DisplayName MixedAmount] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName ([PeriodicReportRow DisplayName MixedAmount] -> [AccountName])
-> ([PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [PeriodicReportRow DisplayName MixedAmount])
-> [PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportOpts
-> Journal
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortRows ReportOpts
ropts Journal
j ([PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount])
-> ([PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [PeriodicReportRow DisplayName MixedAmount])
-> [PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [PeriodicReportRow DisplayName MixedAmount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PeriodicReportRow DisplayName (Maybe MixedAmount, b)
-> PeriodicReportRow DisplayName MixedAmount)
-> [PeriodicReportRow DisplayName (Maybe MixedAmount, b)]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map (((Maybe MixedAmount, b) -> MixedAmount)
-> PeriodicReportRow DisplayName (Maybe MixedAmount, b)
-> PeriodicReportRow DisplayName MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Maybe MixedAmount, b) -> MixedAmount)
-> PeriodicReportRow DisplayName (Maybe MixedAmount, b)
-> PeriodicReportRow DisplayName MixedAmount)
-> ((Maybe MixedAmount, b) -> MixedAmount)
-> PeriodicReportRow DisplayName (Maybe MixedAmount, b)
-> PeriodicReportRow DisplayName MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe MixedAmount
nullmixedamt (Maybe MixedAmount -> MixedAmount)
-> ((Maybe MixedAmount, b) -> Maybe MixedAmount)
-> (Maybe MixedAmount, b)
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe MixedAmount, b) -> Maybe MixedAmount
forall a b. (a, b) -> a
fst)
rows :: [PeriodicReportRow DisplayName BudgetCell]
rows = [PeriodicReportRow DisplayName BudgetCell]
rows1 [PeriodicReportRow DisplayName BudgetCell]
-> [PeriodicReportRow DisplayName BudgetCell]
-> [PeriodicReportRow DisplayName BudgetCell]
forall a. [a] -> [a] -> [a]
++ [PeriodicReportRow DisplayName BudgetCell]
rows2
totalrow :: PeriodicReportRow () BudgetCell
totalrow = ()
-> [BudgetCell]
-> BudgetCell
-> BudgetCell
-> PeriodicReportRow () BudgetCell
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow ()
[ (DateSpan -> Map DateSpan MixedAmount -> Maybe MixedAmount
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan MixedAmount
totActualByPeriod, DateSpan -> Map DateSpan MixedAmount -> Maybe MixedAmount
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup DateSpan
p Map DateSpan MixedAmount
totBudgetByPeriod) | DateSpan
p <- [DateSpan]
periods ]
( MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
actualgrandtot, MixedAmount -> Maybe MixedAmount
budget MixedAmount
budgetgrandtot )
( MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
actualgrandavg, MixedAmount -> Maybe MixedAmount
budget MixedAmount
budgetgrandavg )
where
totBudgetByPeriod :: Map DateSpan MixedAmount
totBudgetByPeriod = [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount)
-> [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [MixedAmount] -> [(DateSpan, MixedAmount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
budgetperiods [MixedAmount]
budgettots :: Map DateSpan BudgetTotal
totActualByPeriod :: Map DateSpan MixedAmount
totActualByPeriod = [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount)
-> [(DateSpan, MixedAmount)] -> Map DateSpan MixedAmount
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> [MixedAmount] -> [(DateSpan, MixedAmount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
actualperiods [MixedAmount]
actualtots :: Map DateSpan Change
budget :: MixedAmount -> Maybe MixedAmount
budget MixedAmount
b = if MixedAmount -> Bool
mixedAmountLooksZero MixedAmount
b then Maybe MixedAmount
forall a. Maybe a
Nothing else MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
b
budgetReportAsText :: ReportOpts -> BudgetReport -> TL.Text
budgetReportAsText :: ReportOpts -> BudgetReport -> Text
budgetReportAsText ropts :: ReportOpts
ropts@ReportOpts{Bool
Int
[AccountName]
[Status]
Maybe Int
Maybe AccountName
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
layout_ :: ReportOpts -> Layout
transpose_ :: ReportOpts -> Bool
color_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
invert_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
declared_ :: ReportOpts -> Bool
drop_ :: ReportOpts -> Int
balanceaccum_ :: ReportOpts -> BalanceAccumulation
balancecalc_ :: ReportOpts -> BalanceCalculation
txn_dates_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
average_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [AccountName]
pretty_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
real_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
date2_ :: ReportOpts -> Bool
depth_ :: ReportOpts -> Maybe Int
value_ :: ReportOpts -> Maybe ValuationType
conversionop_ :: ReportOpts -> Maybe ConversionOp
statuses_ :: ReportOpts -> [Status]
period_ :: ReportOpts -> Period
layout_ :: Layout
transpose_ :: Bool
color_ :: Bool
normalbalance_ :: Maybe NormalSign
invert_ :: Bool
percent_ :: Bool
sort_amount_ :: Bool
show_costs_ :: Bool
no_total_ :: Bool
row_total_ :: Bool
declared_ :: Bool
drop_ :: Int
accountlistmode_ :: AccountListMode
budgetpat_ :: Maybe AccountName
balanceaccum_ :: BalanceAccumulation
balancecalc_ :: BalanceCalculation
txn_dates_ :: Bool
related_ :: Bool
average_ :: Bool
querystring_ :: [AccountName]
pretty_ :: Bool
format_ :: StringFormat
real_ :: Bool
no_elide_ :: Bool
empty_ :: Bool
date2_ :: Bool
depth_ :: Maybe Int
infer_prices_ :: Bool
value_ :: Maybe ValuationType
conversionop_ :: Maybe ConversionOp
statuses_ :: [Status]
interval_ :: Interval
period_ :: Period
budgetpat_ :: ReportOpts -> Maybe AccountName
interval_ :: ReportOpts -> Interval
infer_prices_ :: ReportOpts -> Bool
empty_ :: ReportOpts -> Bool
accountlistmode_ :: ReportOpts -> AccountListMode
..} BudgetReport
budgetr = Builder -> Text
TB.toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$
AccountName -> Builder
TB.fromText AccountName
title Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> AccountName -> Builder
TB.fromText AccountName
"\n\n" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
ReportOpts -> Table AccountName AccountName WideBuilder -> Builder
balanceReportTableAsText ReportOpts
ropts (ReportOpts
-> BudgetReport -> Table AccountName AccountName WideBuilder
budgetReportAsTable ReportOpts
ropts BudgetReport
budgetr)
where
title :: AccountName
title = AccountName
"Budget performance in " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> DateSpan -> AccountName
showDateSpan (BudgetReport -> DateSpan
forall a b. PeriodicReport a b -> DateSpan
periodicReportSpan BudgetReport
budgetr)
AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> (case Maybe ConversionOp
conversionop_ of
Just ConversionOp
ToCost -> AccountName
", converted to cost"
Maybe ConversionOp
_ -> AccountName
"")
AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> (case Maybe ValuationType
value_ of
Just (AtThen Maybe AccountName
_mc) -> AccountName
", valued at posting date"
Just (AtEnd Maybe AccountName
_mc) -> AccountName
", valued at period ends"
Just (AtNow Maybe AccountName
_mc) -> AccountName
", current value"
Just (AtDate Day
d Maybe AccountName
_mc) -> AccountName
", valued at " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> Day -> AccountName
showDate Day
d
Maybe ValuationType
Nothing -> AccountName
"")
AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
":"
budgetReportAsTable :: ReportOpts -> BudgetReport -> Tab.Table Text Text WideBuilder
budgetReportAsTable :: ReportOpts
-> BudgetReport -> Table AccountName AccountName WideBuilder
budgetReportAsTable
ReportOpts{Bool
Int
[AccountName]
[Status]
Maybe Int
Maybe AccountName
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
layout_ :: Layout
transpose_ :: Bool
color_ :: Bool
normalbalance_ :: Maybe NormalSign
invert_ :: Bool
percent_ :: Bool
sort_amount_ :: Bool
show_costs_ :: Bool
no_total_ :: Bool
row_total_ :: Bool
declared_ :: Bool
drop_ :: Int
accountlistmode_ :: AccountListMode
budgetpat_ :: Maybe AccountName
balanceaccum_ :: BalanceAccumulation
balancecalc_ :: BalanceCalculation
txn_dates_ :: Bool
related_ :: Bool
average_ :: Bool
querystring_ :: [AccountName]
pretty_ :: Bool
format_ :: StringFormat
real_ :: Bool
no_elide_ :: Bool
empty_ :: Bool
date2_ :: Bool
depth_ :: Maybe Int
infer_prices_ :: Bool
value_ :: Maybe ValuationType
conversionop_ :: Maybe ConversionOp
statuses_ :: [Status]
interval_ :: Interval
period_ :: Period
layout_ :: ReportOpts -> Layout
transpose_ :: ReportOpts -> Bool
color_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
invert_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
declared_ :: ReportOpts -> Bool
drop_ :: ReportOpts -> Int
balanceaccum_ :: ReportOpts -> BalanceAccumulation
balancecalc_ :: ReportOpts -> BalanceCalculation
txn_dates_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
average_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [AccountName]
pretty_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
real_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
date2_ :: ReportOpts -> Bool
depth_ :: ReportOpts -> Maybe Int
value_ :: ReportOpts -> Maybe ValuationType
conversionop_ :: ReportOpts -> Maybe ConversionOp
statuses_ :: ReportOpts -> [Status]
period_ :: ReportOpts -> Period
budgetpat_ :: ReportOpts -> Maybe AccountName
interval_ :: ReportOpts -> Interval
infer_prices_ :: ReportOpts -> Bool
empty_ :: ReportOpts -> Bool
accountlistmode_ :: ReportOpts -> AccountListMode
..}
(PeriodicReport [DateSpan]
spans [PeriodicReportRow DisplayName BudgetCell]
items PeriodicReportRow () BudgetCell
tr) =
Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder
forall rh a. Table rh rh a -> Table rh rh a
maybetransposetable (Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder)
-> Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder
forall a b. (a -> b) -> a -> b
$
Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder
forall ch.
Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder
addtotalrow (Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder)
-> Table AccountName AccountName WideBuilder
-> Table AccountName AccountName WideBuilder
forall a b. (a -> b) -> a -> b
$
Header AccountName
-> Header AccountName
-> [[WideBuilder]]
-> Table AccountName AccountName WideBuilder
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Tab.Table
(Properties -> [Header AccountName] -> Header AccountName
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header AccountName] -> Header AccountName)
-> [Header AccountName] -> Header AccountName
forall a b. (a -> b) -> a -> b
$ (AccountName -> Header AccountName)
-> [AccountName] -> [Header AccountName]
forall a b. (a -> b) -> [a] -> [b]
map AccountName -> Header AccountName
forall h. h -> Header h
Tab.Header [AccountName]
accts)
(Properties -> [Header AccountName] -> Header AccountName
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header AccountName] -> Header AccountName)
-> [Header AccountName] -> Header AccountName
forall a b. (a -> b) -> a -> b
$ (AccountName -> Header AccountName)
-> [AccountName] -> [Header AccountName]
forall a b. (a -> b) -> [a] -> [b]
map AccountName -> Header AccountName
forall h. h -> Header h
Tab.Header [AccountName]
colheadings)
[[WideBuilder]]
rows
where
colheadings :: [AccountName]
colheadings = [AccountName
"Commodity" | Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare]
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ (DateSpan -> AccountName) -> [DateSpan] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map (BalanceAccumulation -> [DateSpan] -> DateSpan -> AccountName
reportPeriodName BalanceAccumulation
balanceaccum_ [DateSpan]
spans) [DateSpan]
spans
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ [AccountName
" Total" | Bool
row_total_]
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ [AccountName
"Average" | Bool
average_]
renderacct :: PeriodicReportRow DisplayName a -> AccountName
renderacct PeriodicReportRow DisplayName a
row = case AccountListMode
accountlistmode_ of
AccountListMode
ALTree -> Int -> AccountName -> AccountName
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) AccountName
" " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> PeriodicReportRow DisplayName a -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrDisplayName PeriodicReportRow DisplayName a
row
AccountListMode
ALFlat -> Int -> AccountName -> AccountName
accountNameDrop (Int
drop_) (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow DisplayName a -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName a
row
addtotalrow :: Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder
addtotalrow
| Bool
no_total_ = Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder
forall a. a -> a
id
| Bool
otherwise = let rh :: Header AccountName
rh = Properties -> [Header AccountName] -> Header AccountName
forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.NoLine ([Header AccountName] -> Header AccountName)
-> (Header AccountName -> [Header AccountName])
-> Header AccountName
-> Header AccountName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Header AccountName -> [Header AccountName]
forall a. Int -> a -> [a]
replicate ([[WideBuilder]] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[WideBuilder]]
totalrows) (Header AccountName -> Header AccountName)
-> Header AccountName -> Header AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> Header AccountName
forall h. h -> Header h
Tab.Header AccountName
""
ch :: Header [a]
ch = [a] -> Header [a]
forall h. h -> Header h
Tab.Header []
in ((Table AccountName ch WideBuilder
-> Table AccountName [Any] WideBuilder
-> Table AccountName ch WideBuilder)
-> Table AccountName [Any] WideBuilder
-> Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Properties
-> Table AccountName ch WideBuilder
-> Table AccountName [Any] WideBuilder
-> Table AccountName 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 AccountName [Any] WideBuilder
-> Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder)
-> Table AccountName [Any] WideBuilder
-> Table AccountName ch WideBuilder
-> Table AccountName ch WideBuilder
forall a b. (a -> b) -> a -> b
$ Header AccountName
-> Header [Any]
-> [[WideBuilder]]
-> Table AccountName [Any] WideBuilder
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Tab.Table Header AccountName
rh Header [Any]
forall a. Header [a]
ch [[WideBuilder]]
totalrows)
maybetranspose :: [[a]] -> [[a]]
maybetranspose
| Bool
transpose_ = [[a]] -> [[a]]
forall a. [[a]] -> [[a]]
transpose
| Bool
otherwise = [[a]] -> [[a]]
forall a. a -> a
id
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
([AccountName]
accts, [[WideBuilder]]
rows, [[WideBuilder]]
totalrows) = ([AccountName]
accts, [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
forall a. [a] -> [[a]] -> [[a]]
prependcs [WideBuilder]
itemscs ([[BudgetDisplayCell]] -> [[WideBuilder]]
padcells [[BudgetDisplayCell]]
texts), [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
forall a. [a] -> [[a]] -> [[a]]
prependcs [WideBuilder]
trcs ([[BudgetDisplayCell]] -> [[WideBuilder]]
padtr [[BudgetDisplayCell]]
trtexts))
where
shownitems :: [[(AccountName, WideBuilder, BudgetDisplayRow)]]
shownitems :: [[(AccountName, WideBuilder, [BudgetDisplayCell])]]
shownitems = ((PeriodicReportRow DisplayName BudgetCell
-> [(AccountName, WideBuilder, [BudgetDisplayCell])])
-> [PeriodicReportRow DisplayName BudgetCell]
-> [[(AccountName, WideBuilder, [BudgetDisplayCell])]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\PeriodicReportRow DisplayName BudgetCell
i -> ((WideBuilder, [BudgetDisplayCell])
-> (AccountName, WideBuilder, [BudgetDisplayCell]))
-> [(WideBuilder, [BudgetDisplayCell])]
-> [(AccountName, WideBuilder, [BudgetDisplayCell])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(WideBuilder
cs, [BudgetDisplayCell]
cvals) -> (PeriodicReportRow DisplayName BudgetCell -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
renderacct PeriodicReportRow DisplayName BudgetCell
i, WideBuilder
cs, [BudgetDisplayCell]
cvals)) ([(WideBuilder, [BudgetDisplayCell])]
-> [(AccountName, WideBuilder, [BudgetDisplayCell])])
-> ([BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])])
-> [BudgetCell]
-> [(AccountName, WideBuilder, [BudgetDisplayCell])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])]
showrow ([BudgetCell] -> [(AccountName, WideBuilder, [BudgetDisplayCell])])
-> [BudgetCell]
-> [(AccountName, WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow DisplayName BudgetCell -> [BudgetCell]
forall a a. PeriodicReportRow a a -> [a]
rowToBudgetCells PeriodicReportRow DisplayName BudgetCell
i) [PeriodicReportRow DisplayName BudgetCell]
items)
([AccountName]
accts, [WideBuilder]
itemscs, [[BudgetDisplayCell]]
texts) = [(AccountName, WideBuilder, [BudgetDisplayCell])]
-> ([AccountName], [WideBuilder], [[BudgetDisplayCell]])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(AccountName, WideBuilder, [BudgetDisplayCell])]
-> ([AccountName], [WideBuilder], [[BudgetDisplayCell]]))
-> [(AccountName, WideBuilder, [BudgetDisplayCell])]
-> ([AccountName], [WideBuilder], [[BudgetDisplayCell]])
forall a b. (a -> b) -> a -> b
$ [[(AccountName, WideBuilder, [BudgetDisplayCell])]]
-> [(AccountName, WideBuilder, [BudgetDisplayCell])]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[(AccountName, WideBuilder, [BudgetDisplayCell])]]
shownitems
showntr :: [[(WideBuilder, BudgetDisplayRow)]]
showntr :: [[(WideBuilder, [BudgetDisplayCell])]]
showntr = [[BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])]
showrow ([BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])])
-> [BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () BudgetCell -> [BudgetCell]
forall a a. PeriodicReportRow a a -> [a]
rowToBudgetCells PeriodicReportRow () BudgetCell
tr]
([WideBuilder]
trcs, [[BudgetDisplayCell]]
trtexts) = [(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])]]
showntr
trwidths :: [(Int, Int, Int)]
trwidths
| Bool
transpose_ = Int -> [(Int, Int, Int)] -> [(Int, Int, Int)]
forall a. Int -> [a] -> [a]
drop ([[BudgetDisplayCell]] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[BudgetDisplayCell]]
texts) [(Int, Int, Int)]
widths
| Bool
otherwise = [(Int, Int, Int)]
widths
padcells :: [[BudgetDisplayCell]] -> [[WideBuilder]]
padcells = [[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 (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((((Int, Int, Int), BudgetDisplayCell) -> WideBuilder)
-> [((Int, Int, Int), BudgetDisplayCell)] -> [WideBuilder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Int, Int, Int) -> BudgetDisplayCell -> WideBuilder)
-> ((Int, Int, Int), BudgetDisplayCell) -> WideBuilder
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
paddisplaycell) ([((Int, Int, Int), BudgetDisplayCell)] -> [WideBuilder])
-> ([BudgetDisplayCell] -> [((Int, Int, Int), BudgetDisplayCell)])
-> [BudgetDisplayCell]
-> [WideBuilder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Int, Int, Int)]
-> [BudgetDisplayCell] -> [((Int, Int, Int), BudgetDisplayCell)]
forall a b. [a] -> [b] -> [(a, b)]
zip [(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
padtr :: [[BudgetDisplayCell]] -> [[WideBuilder]]
padtr = [[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 (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((((Int, Int, Int), BudgetDisplayCell) -> WideBuilder)
-> [((Int, Int, Int), BudgetDisplayCell)] -> [WideBuilder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Int, Int, Int) -> BudgetDisplayCell -> WideBuilder)
-> ((Int, Int, Int), BudgetDisplayCell) -> WideBuilder
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
paddisplaycell) ([((Int, Int, Int), BudgetDisplayCell)] -> [WideBuilder])
-> ([BudgetDisplayCell] -> [((Int, Int, Int), BudgetDisplayCell)])
-> [BudgetDisplayCell]
-> [WideBuilder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Int, Int, Int)]
-> [BudgetDisplayCell] -> [((Int, Int, Int), BudgetDisplayCell)]
forall a b. [a] -> [b] -> [(a, b)]
zip [(Int, Int, Int)]
trwidths) ([[BudgetDisplayCell]] -> [[WideBuilder]])
-> ([[BudgetDisplayCell]] -> [[BudgetDisplayCell]])
-> [[BudgetDisplayCell]]
-> [[WideBuilder]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a. [[a]] -> [[a]]
maybetranspose
prependcs :: [a] -> [[a]] -> [[a]]
prependcs [a]
cs
| Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
/= Layout
LayoutBare = [[a]] -> [[a]]
forall a. a -> a
id
| Bool
otherwise = (a -> [a] -> [a]) -> [a] -> [[a]] -> [[a]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (:) [a]
cs
rowToBudgetCells :: PeriodicReportRow a a -> [a]
rowToBudgetCells (PeriodicReportRow a
_ [a]
as a
rowtot a
rowavg) = [a]
as
[a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [a
rowtot | Bool
row_total_ Bool -> Bool -> Bool
&& Bool -> Bool
not ([a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
as)]
[a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [a
rowavg | Bool
average_ Bool -> Bool -> Bool
&& Bool -> Bool
not ([a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
as)]
rowfuncs :: [CommoditySymbol] -> (BudgetShowMixed, BudgetPercBudget)
rowfuncs :: [AccountName] -> (BudgetShowMixed, BudgetPercBudget)
rowfuncs [AccountName]
cs = case Layout
layout_ of
LayoutWide Maybe Int
width ->
( WideBuilder -> [WideBuilder]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (WideBuilder -> [WideBuilder])
-> (MixedAmount -> WideBuilder) -> BudgetShowMixed
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountDisplayOpts -> MixedAmount -> WideBuilder
showMixedAmountB AmountDisplayOpts
oneLine{displayColour :: Bool
displayColour=Bool
color_, displayMaxWidth :: Maybe Int
displayMaxWidth=Maybe Int
width}
, \MixedAmount
a -> Maybe Percentage -> [Maybe Percentage]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Percentage -> [Maybe Percentage])
-> (MixedAmount -> Maybe Percentage)
-> MixedAmount
-> [Maybe Percentage]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> MixedAmount -> Maybe Percentage
percentage MixedAmount
a)
Layout
_ -> ( AmountDisplayOpts -> BudgetShowMixed
showMixedAmountLinesB AmountDisplayOpts
noPrice{displayOrder :: Maybe [AccountName]
displayOrder=[AccountName] -> Maybe [AccountName]
forall a. a -> Maybe a
Just [AccountName]
cs, displayMinWidth :: Maybe Int
displayMinWidth=Maybe Int
forall a. Maybe a
Nothing, displayColour :: Bool
displayColour=Bool
color_}
, \MixedAmount
a MixedAmount
b -> (AccountName -> Maybe Percentage)
-> [AccountName] -> [Maybe Percentage]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (MixedAmount -> MixedAmount -> AccountName -> Maybe Percentage
percentage' MixedAmount
a MixedAmount
b) [AccountName]
cs)
showrow :: [BudgetCell] -> [(WideBuilder, BudgetDisplayRow)]
showrow :: [BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])]
showrow [BudgetCell]
row =
let cs :: [AccountName]
cs = [BudgetCell] -> [AccountName]
budgetCellsCommodities [BudgetCell]
row
(BudgetShowMixed
showmixed, BudgetPercBudget
percbudget) = [AccountName] -> (BudgetShowMixed, BudgetPercBudget)
rowfuncs [AccountName]
cs
in [WideBuilder]
-> [[BudgetDisplayCell]] -> [(WideBuilder, [BudgetDisplayCell])]
forall a b. [a] -> [b] -> [(a, b)]
zip ((AccountName -> WideBuilder) -> [AccountName] -> [WideBuilder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap AccountName -> WideBuilder
wbFromText [AccountName]
cs)
([[BudgetDisplayCell]] -> [(WideBuilder, [BudgetDisplayCell])])
-> ([BudgetCell] -> [[BudgetDisplayCell]])
-> [BudgetCell]
-> [(WideBuilder, [BudgetDisplayCell])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[BudgetDisplayCell]] -> [[BudgetDisplayCell]]
forall a. [[a]] -> [[a]]
transpose
([[BudgetDisplayCell]] -> [[BudgetDisplayCell]])
-> ([BudgetCell] -> [[BudgetDisplayCell]])
-> [BudgetCell]
-> [[BudgetDisplayCell]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BudgetCell -> [BudgetDisplayCell])
-> [BudgetCell] -> [[BudgetDisplayCell]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (BudgetShowMixed
-> BudgetPercBudget -> BudgetCell -> [BudgetDisplayCell]
showcell BudgetShowMixed
showmixed BudgetPercBudget
percbudget)
([BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])])
-> [BudgetCell] -> [(WideBuilder, [BudgetDisplayCell])]
forall a b. (a -> b) -> a -> b
$ [BudgetCell]
row
budgetCellsCommodities :: [BudgetCell] -> [AccountName]
budgetCellsCommodities = Set AccountName -> [AccountName]
forall a. Set a -> [a]
S.toList (Set AccountName -> [AccountName])
-> ([BudgetCell] -> Set AccountName)
-> [BudgetCell]
-> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Set AccountName -> Set AccountName -> Set AccountName)
-> Set AccountName -> [Set AccountName] -> Set AccountName
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Set AccountName -> Set AccountName -> Set AccountName
forall a. Ord a => Set a -> Set a -> Set a
S.union Set AccountName
forall a. Monoid a => a
mempty ([Set AccountName] -> Set AccountName)
-> ([BudgetCell] -> [Set AccountName])
-> [BudgetCell]
-> Set AccountName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BudgetCell -> Set AccountName)
-> [BudgetCell] -> [Set AccountName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap BudgetCell -> Set AccountName
budgetCellCommodities
budgetCellCommodities :: BudgetCell -> S.Set CommoditySymbol
budgetCellCommodities :: BudgetCell -> Set AccountName
budgetCellCommodities (Maybe MixedAmount
am, Maybe MixedAmount
bm) = Maybe MixedAmount -> Set AccountName
f Maybe MixedAmount
am Set AccountName -> Set AccountName -> Set AccountName
forall a. Ord a => Set a -> Set a -> Set a
`S.union` Maybe MixedAmount -> Set AccountName
f Maybe MixedAmount
bm
where f :: Maybe MixedAmount -> Set AccountName
f = Set AccountName
-> (MixedAmount -> Set AccountName)
-> Maybe MixedAmount
-> Set AccountName
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Set AccountName
forall a. Monoid a => a
mempty MixedAmount -> Set AccountName
maCommodities
cellswidth :: [BudgetCell] -> [[(Int, Int, Int)]]
cellswidth :: [BudgetCell] -> [[(Int, Int, Int)]]
cellswidth [BudgetCell]
row =
let cs :: [AccountName]
cs = [BudgetCell] -> [AccountName]
budgetCellsCommodities [BudgetCell]
row
(BudgetShowMixed
showmixed, BudgetPercBudget
percbudget) = [AccountName] -> (BudgetShowMixed, BudgetPercBudget)
rowfuncs [AccountName]
cs
disp :: BudgetCell -> [BudgetDisplayCell]
disp = BudgetShowMixed
-> BudgetPercBudget -> BudgetCell -> [BudgetDisplayCell]
showcell BudgetShowMixed
showmixed BudgetPercBudget
percbudget
budgetpercwidth :: (WideBuilder, Maybe WideBuilder) -> (Int, Int)
budgetpercwidth = WideBuilder -> Int
wbWidth (WideBuilder -> Int)
-> (Maybe WideBuilder -> Int)
-> (WideBuilder, Maybe WideBuilder)
-> (Int, Int)
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 (BudgetCell -> [(Int, Int, Int)])
-> [BudgetCell] -> [[(Int, Int, Int)]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((BudgetDisplayCell -> (Int, Int, Int))
-> [BudgetDisplayCell] -> [(Int, Int, Int)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap BudgetDisplayCell -> (Int, Int, Int)
cellwidth ([BudgetDisplayCell] -> [(Int, Int, Int)])
-> (BudgetCell -> [BudgetDisplayCell])
-> BudgetCell
-> [(Int, Int, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BudgetCell -> [BudgetDisplayCell]
disp) [BudgetCell]
row
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 (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
$ (PeriodicReportRow DisplayName BudgetCell -> [[(Int, Int, Int)]])
-> [PeriodicReportRow DisplayName BudgetCell]
-> [[[(Int, Int, Int)]]]
forall a b. (a -> b) -> [a] -> [b]
map ([BudgetCell] -> [[(Int, Int, Int)]]
cellswidth ([BudgetCell] -> [[(Int, Int, Int)]])
-> (PeriodicReportRow DisplayName BudgetCell -> [BudgetCell])
-> PeriodicReportRow DisplayName BudgetCell
-> [[(Int, Int, Int)]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodicReportRow DisplayName BudgetCell -> [BudgetCell]
forall a a. PeriodicReportRow a a -> [a]
rowToBudgetCells) [PeriodicReportRow DisplayName BudgetCell]
items [[[(Int, Int, Int)]]]
-> [[[(Int, Int, Int)]]] -> [[[(Int, Int, Int)]]]
forall a. [a] -> [a] -> [a]
++ [[BudgetCell] -> [[(Int, Int, Int)]]
cellswidth ([BudgetCell] -> [[(Int, Int, Int)]])
-> [BudgetCell] -> [[(Int, Int, Int)]]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () BudgetCell -> [BudgetCell]
forall a a. PeriodicReportRow a a -> [a]
rowToBudgetCells PeriodicReportRow () BudgetCell
tr]
showcell :: BudgetShowMixed -> BudgetPercBudget -> BudgetCell -> BudgetDisplayRow
showcell :: BudgetShowMixed
-> BudgetPercBudget -> BudgetCell -> [BudgetDisplayCell]
showcell BudgetShowMixed
showmixed BudgetPercBudget
percbudget (Maybe MixedAmount
actual, Maybe MixedAmount
mbudget) = [WideBuilder]
-> [Maybe (WideBuilder, Maybe WideBuilder)] -> [BudgetDisplayCell]
forall a b. [a] -> [b] -> [(a, b)]
zip (BudgetShowMixed
showmixed MixedAmount
actual') [Maybe (WideBuilder, Maybe WideBuilder)]
full
where
actual' :: MixedAmount
actual' = MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe MixedAmount
nullmixedamt Maybe MixedAmount
actual
budgetAndPerc :: MixedAmount -> [(WideBuilder, Maybe WideBuilder)]
budgetAndPerc MixedAmount
b = ([WideBuilder]
-> [Maybe WideBuilder] -> [(WideBuilder, Maybe WideBuilder)])
-> ([WideBuilder], [Maybe WideBuilder])
-> [(WideBuilder, Maybe WideBuilder)]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry [WideBuilder]
-> [Maybe WideBuilder] -> [(WideBuilder, Maybe WideBuilder)]
forall a b. [a] -> [b] -> [(a, b)]
zip
( BudgetShowMixed
showmixed MixedAmount
b
, (Percentage -> WideBuilder)
-> Maybe Percentage -> Maybe WideBuilder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (AccountName -> WideBuilder
wbFromText (AccountName -> WideBuilder)
-> (Percentage -> AccountName) -> Percentage -> WideBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> AccountName
T.pack (String -> AccountName)
-> (Percentage -> String) -> Percentage -> AccountName
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) (Maybe Percentage -> Maybe WideBuilder)
-> [Maybe Percentage] -> [Maybe WideBuilder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BudgetPercBudget
percbudget MixedAmount
actual' MixedAmount
b
)
full :: [Maybe (WideBuilder, Maybe WideBuilder)]
full
| Just MixedAmount
b <- Maybe MixedAmount
mbudget = (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 (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MixedAmount -> [(WideBuilder, Maybe WideBuilder)]
budgetAndPerc MixedAmount
b
| Bool
otherwise = Maybe (WideBuilder, Maybe WideBuilder)
-> [Maybe (WideBuilder, Maybe WideBuilder)]
forall a. a -> [a]
repeat Maybe (WideBuilder, Maybe WideBuilder)
forall a. Maybe a
Nothing
paddisplaycell :: (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
paddisplaycell :: (Int, Int, Int) -> BudgetDisplayCell -> WideBuilder
paddisplaycell (Int
actualwidth, Int
budgetwidth, Int
percentwidth) (WideBuilder
actual, Maybe (WideBuilder, Maybe WideBuilder)
mbudget) = WideBuilder
full
where
toPadded :: WideBuilder -> Builder
toPadded (WideBuilder Builder
b Int
w) =
(AccountName -> Builder
TB.fromText (AccountName -> Builder) -> (Int -> AccountName) -> Int -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> AccountName -> AccountName)
-> AccountName -> Int -> AccountName
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> AccountName -> AccountName
T.replicate AccountName
" " (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
)
budgetb :: (WideBuilder, Maybe WideBuilder) -> Builder
budgetb (WideBuilder
budget, Maybe WideBuilder
perc) =
let perct :: AccountName
perct = case Maybe WideBuilder
perc of
Maybe WideBuilder
Nothing -> Int -> AccountName -> AccountName
T.replicate Int
totalpercentwidth AccountName
" "
Just pct -> Int -> AccountName -> AccountName
T.replicate (Int
percentwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
pct) AccountName
" " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> WideBuilder -> AccountName
wbToText WideBuilder
pct AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
"% of "
in AccountName -> Builder
TB.fromText (AccountName -> Builder) -> AccountName -> Builder
forall a b. (a -> b) -> a -> b
$ AccountName
" [" AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
perct AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> Int -> AccountName -> AccountName
T.replicate (Int
budgetwidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
budget) AccountName
" " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> WideBuilder -> AccountName
wbToText WideBuilder
budget AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
"]"
emptyBudget :: Builder
emptyBudget = AccountName -> Builder
TB.fromText (AccountName -> Builder) -> AccountName -> Builder
forall a b. (a -> b) -> a -> b
$ Int -> AccountName -> AccountName
T.replicate Int
totalbudgetwidth AccountName
" "
full :: WideBuilder
full = (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
emptyBudget (WideBuilder, Maybe WideBuilder) -> Builder
budgetb Maybe (WideBuilder, Maybe WideBuilder)
mbudget
percentage :: Change -> BudgetGoal -> Maybe Percentage
percentage :: MixedAmount -> MixedAmount -> Maybe Percentage
percentage MixedAmount
actual MixedAmount
budget =
case (MixedAmount -> [Amount]
costedAmounts MixedAmount
actual, MixedAmount -> [Amount]
costedAmounts MixedAmount
budget) of
([Amount
a], [Amount
b]) | (Amount -> AccountName
acommodity Amount
a AccountName -> AccountName -> Bool
forall a. Eq a => a -> a -> Bool
== Amount -> AccountName
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 :: MixedAmount -> [Amount]
costedAmounts = case Maybe ConversionOp
conversionop_ of
Just ConversionOp
ToCost -> MixedAmount -> [Amount]
amounts (MixedAmount -> [Amount])
-> (MixedAmount -> MixedAmount) -> MixedAmount -> [Amount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> MixedAmount
mixedAmountCost
Maybe ConversionOp
_ -> MixedAmount -> [Amount]
amounts
percentage' :: Change -> BudgetGoal -> CommoditySymbol -> Maybe Percentage
percentage' :: MixedAmount -> MixedAmount -> AccountName -> Maybe Percentage
percentage' MixedAmount
am MixedAmount
bm AccountName
c = case ((,) (Maybe Amount -> Maybe Amount -> (Maybe Amount, Maybe Amount))
-> (MixedAmount -> Maybe Amount)
-> MixedAmount
-> MixedAmount
-> (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 (AccountName -> AccountName -> Bool
forall a. Eq a => a -> a -> Bool
(==) AccountName
c (AccountName -> Bool) -> (Amount -> AccountName) -> Amount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> AccountName
acommodity) ([Amount] -> Maybe Amount)
-> (MixedAmount -> [Amount]) -> MixedAmount -> Maybe Amount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> [Amount]
amounts) MixedAmount
am MixedAmount
bm of
(Just Amount
a, Just Amount
b) -> MixedAmount -> MixedAmount -> Maybe Percentage
percentage (Amount -> MixedAmount
mixedAmount Amount
a) (Amount -> MixedAmount
mixedAmount Amount
b)
(Maybe Amount, Maybe Amount)
_ -> Maybe Percentage
forall a. Maybe a
Nothing
budgetReportAsCsv :: ReportOpts -> BudgetReport -> [[Text]]
budgetReportAsCsv :: ReportOpts -> BudgetReport -> [[AccountName]]
budgetReportAsCsv
ReportOpts{Bool
Int
[AccountName]
[Status]
Maybe Int
Maybe AccountName
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
layout_ :: Layout
transpose_ :: Bool
color_ :: Bool
normalbalance_ :: Maybe NormalSign
invert_ :: Bool
percent_ :: Bool
sort_amount_ :: Bool
show_costs_ :: Bool
no_total_ :: Bool
row_total_ :: Bool
declared_ :: Bool
drop_ :: Int
accountlistmode_ :: AccountListMode
budgetpat_ :: Maybe AccountName
balanceaccum_ :: BalanceAccumulation
balancecalc_ :: BalanceCalculation
txn_dates_ :: Bool
related_ :: Bool
average_ :: Bool
querystring_ :: [AccountName]
pretty_ :: Bool
format_ :: StringFormat
real_ :: Bool
no_elide_ :: Bool
empty_ :: Bool
date2_ :: Bool
depth_ :: Maybe Int
infer_prices_ :: Bool
value_ :: Maybe ValuationType
conversionop_ :: Maybe ConversionOp
statuses_ :: [Status]
interval_ :: Interval
period_ :: Period
layout_ :: ReportOpts -> Layout
transpose_ :: ReportOpts -> Bool
color_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
invert_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
declared_ :: ReportOpts -> Bool
drop_ :: ReportOpts -> Int
balanceaccum_ :: ReportOpts -> BalanceAccumulation
balancecalc_ :: ReportOpts -> BalanceCalculation
txn_dates_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
average_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [AccountName]
pretty_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
real_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
date2_ :: ReportOpts -> Bool
depth_ :: ReportOpts -> Maybe Int
value_ :: ReportOpts -> Maybe ValuationType
conversionop_ :: ReportOpts -> Maybe ConversionOp
statuses_ :: ReportOpts -> [Status]
period_ :: ReportOpts -> Period
budgetpat_ :: ReportOpts -> Maybe AccountName
interval_ :: ReportOpts -> Interval
infer_prices_ :: ReportOpts -> Bool
empty_ :: ReportOpts -> Bool
accountlistmode_ :: ReportOpts -> AccountListMode
..}
(PeriodicReport [DateSpan]
colspans [PeriodicReportRow DisplayName BudgetCell]
items PeriodicReportRow () BudgetCell
tr)
= (if Bool
transpose_ then [[AccountName]] -> [[AccountName]]
forall a. [[a]] -> [[a]]
transpose else [[AccountName]] -> [[AccountName]]
forall a. a -> a
id) ([[AccountName]] -> [[AccountName]])
-> [[AccountName]] -> [[AccountName]]
forall a b. (a -> b) -> a -> b
$
(AccountName
"Account" AccountName -> [AccountName] -> [AccountName]
forall a. a -> [a] -> [a]
:
[AccountName
"Commodity" | Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare ]
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ (DateSpan -> [AccountName]) -> [DateSpan] -> [AccountName]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\DateSpan
span -> [DateSpan -> AccountName
showDateSpan DateSpan
span, AccountName
"budget"]) [DateSpan]
colspans
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ [[AccountName]] -> [AccountName]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[AccountName
"Total" ,AccountName
"budget"] | Bool
row_total_]
[AccountName] -> [AccountName] -> [AccountName]
forall a. [a] -> [a] -> [a]
++ [[AccountName]] -> [AccountName]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[AccountName
"Average",AccountName
"budget"] | Bool
average_]
) [AccountName] -> [[AccountName]] -> [[AccountName]]
forall a. a -> [a] -> [a]
:
(PeriodicReportRow DisplayName BudgetCell -> [[AccountName]])
-> [PeriodicReportRow DisplayName BudgetCell] -> [[AccountName]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((PeriodicReportRow DisplayName BudgetCell -> AccountName)
-> PeriodicReportRow DisplayName BudgetCell -> [[AccountName]]
forall a.
(PeriodicReportRow a BudgetCell -> AccountName)
-> PeriodicReportRow a BudgetCell -> [[AccountName]]
rowAsTexts PeriodicReportRow DisplayName BudgetCell -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName) [PeriodicReportRow DisplayName BudgetCell]
items
[[AccountName]] -> [[AccountName]] -> [[AccountName]]
forall a. [a] -> [a] -> [a]
++ [[[AccountName]]] -> [[AccountName]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ (PeriodicReportRow () BudgetCell -> AccountName)
-> PeriodicReportRow () BudgetCell -> [[AccountName]]
forall a.
(PeriodicReportRow a BudgetCell -> AccountName)
-> PeriodicReportRow a BudgetCell -> [[AccountName]]
rowAsTexts (AccountName -> PeriodicReportRow () BudgetCell -> AccountName
forall a b. a -> b -> a
const AccountName
"Total:") PeriodicReportRow () BudgetCell
tr | Bool -> Bool
not Bool
no_total_ ]
where
flattentuples :: [(a, a)] -> [a]
flattentuples [(a, a)]
abs = [[a]] -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[a
a,a
b] | (a
a,a
b) <- [(a, a)]
abs]
showNorm :: Maybe MixedAmount -> AccountName
showNorm = AccountName
-> (MixedAmount -> AccountName) -> Maybe MixedAmount -> AccountName
forall b a. b -> (a -> b) -> Maybe a -> b
maybe AccountName
"" (WideBuilder -> AccountName
wbToText (WideBuilder -> AccountName)
-> (MixedAmount -> WideBuilder) -> MixedAmount -> AccountName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountDisplayOpts -> MixedAmount -> WideBuilder
showMixedAmountB AmountDisplayOpts
oneLine)
rowAsTexts :: (PeriodicReportRow a BudgetCell -> Text)
-> PeriodicReportRow a BudgetCell
-> [[Text]]
rowAsTexts :: (PeriodicReportRow a BudgetCell -> AccountName)
-> PeriodicReportRow a BudgetCell -> [[AccountName]]
rowAsTexts PeriodicReportRow a BudgetCell -> AccountName
render row :: PeriodicReportRow a BudgetCell
row@(PeriodicReportRow a
_ [BudgetCell]
as (Maybe MixedAmount
rowtot,Maybe MixedAmount
budgettot) (Maybe MixedAmount
rowavg, Maybe MixedAmount
budgetavg))
| Layout
layout_ Layout -> Layout -> Bool
forall a. Eq a => a -> a -> Bool
/= Layout
LayoutBare = [PeriodicReportRow a BudgetCell -> AccountName
render PeriodicReportRow a BudgetCell
row AccountName -> [AccountName] -> [AccountName]
forall a. a -> [a] -> [a]
: (Maybe MixedAmount -> AccountName)
-> [Maybe MixedAmount] -> [AccountName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Maybe MixedAmount -> AccountName
showNorm [Maybe MixedAmount]
all]
| Bool
otherwise =
[[AccountName]] -> [[AccountName]]
joinNames ([[AccountName]] -> [[AccountName]])
-> ([Maybe MixedAmount] -> [[AccountName]])
-> [Maybe MixedAmount]
-> [[AccountName]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (AccountName -> [AccountName] -> [AccountName])
-> [AccountName] -> [[AccountName]] -> [[AccountName]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (:) [AccountName]
cs
([[AccountName]] -> [[AccountName]])
-> ([Maybe MixedAmount] -> [[AccountName]])
-> [Maybe MixedAmount]
-> [[AccountName]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[AccountName]] -> [[AccountName]]
forall a. [[a]] -> [[a]]
transpose
([[AccountName]] -> [[AccountName]])
-> ([Maybe MixedAmount] -> [[AccountName]])
-> [Maybe MixedAmount]
-> [[AccountName]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe MixedAmount -> [AccountName])
-> [Maybe MixedAmount] -> [[AccountName]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((WideBuilder -> AccountName) -> [WideBuilder] -> [AccountName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap WideBuilder -> AccountName
wbToText ([WideBuilder] -> [AccountName])
-> (Maybe MixedAmount -> [WideBuilder])
-> Maybe MixedAmount
-> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AmountDisplayOpts -> BudgetShowMixed
showMixedAmountLinesB AmountDisplayOpts
oneLine{displayOrder :: Maybe [AccountName]
displayOrder=[AccountName] -> Maybe [AccountName]
forall a. a -> Maybe a
Just [AccountName]
cs, displayMinWidth :: Maybe Int
displayMinWidth=Maybe Int
forall a. Maybe a
Nothing}
BudgetShowMixed
-> (Maybe MixedAmount -> MixedAmount)
-> Maybe MixedAmount
-> [WideBuilder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe MixedAmount
nullmixedamt)
([Maybe MixedAmount] -> [[AccountName]])
-> [Maybe MixedAmount] -> [[AccountName]]
forall a b. (a -> b) -> a -> b
$ [Maybe MixedAmount]
all
where
cs :: [AccountName]
cs = Set AccountName -> [AccountName]
forall a. Set a -> [a]
S.toList (Set AccountName -> [AccountName])
-> ([MixedAmount] -> Set AccountName)
-> [MixedAmount]
-> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Set AccountName -> Set AccountName -> Set AccountName)
-> Set AccountName -> [Set AccountName] -> Set AccountName
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Set AccountName -> Set AccountName -> Set AccountName
forall a. Ord a => Set a -> Set a -> Set a
S.union Set AccountName
forall a. Monoid a => a
mempty ([Set AccountName] -> Set AccountName)
-> ([MixedAmount] -> [Set AccountName])
-> [MixedAmount]
-> Set AccountName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (MixedAmount -> Set AccountName)
-> [MixedAmount] -> [Set AccountName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MixedAmount -> Set AccountName
maCommodities ([MixedAmount] -> [AccountName]) -> [MixedAmount] -> [AccountName]
forall a b. (a -> b) -> a -> b
$ [Maybe MixedAmount] -> [MixedAmount]
forall a. [Maybe a] -> [a]
catMaybes [Maybe MixedAmount]
all
all :: [Maybe MixedAmount]
all = [BudgetCell] -> [Maybe MixedAmount]
forall a. [(a, a)] -> [a]
flattentuples [BudgetCell]
as
[Maybe MixedAmount] -> [Maybe MixedAmount] -> [Maybe MixedAmount]
forall a. [a] -> [a] -> [a]
++ [[Maybe MixedAmount]] -> [Maybe MixedAmount]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Maybe MixedAmount
rowtot, Maybe MixedAmount
budgettot] | Bool
row_total_]
[Maybe MixedAmount] -> [Maybe MixedAmount] -> [Maybe MixedAmount]
forall a. [a] -> [a] -> [a]
++ [[Maybe MixedAmount]] -> [Maybe MixedAmount]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Maybe MixedAmount
rowavg, Maybe MixedAmount
budgetavg] | Bool
average_]
joinNames :: [[AccountName]] -> [[AccountName]]
joinNames = ([AccountName] -> [AccountName])
-> [[AccountName]] -> [[AccountName]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (PeriodicReportRow a BudgetCell -> AccountName
render PeriodicReportRow a BudgetCell
row AccountName -> [AccountName] -> [AccountName]
forall a. a -> [a] -> [a]
:)
tests_BudgetReport :: TestTree
tests_BudgetReport = String -> [TestTree] -> TestTree
testGroup String
"BudgetReport" [
]