{-| A 'Ledger' is derived from a 'Journal' by applying a filter specification to select 'Transaction's and 'Posting's of interest. It contains the filtered journal and knows the resulting chart of accounts, account balances, and postings in each account. -} module Hledger.Data.Ledger where import qualified Data.Map as M -- import Data.Text (Text) import qualified Data.Text as T import Safe (headDef) import Test.HUnit import Text.Printf import Hledger.Data.Types import Hledger.Data.Account import Hledger.Data.Dates import Hledger.Data.Journal import Hledger.Data.Posting import Hledger.Query import Hledger.Utils.Debug instance Show Ledger where show l = printf "Ledger with %d transactions, %d accounts\n" --"%s" (length (jtxns $ ljournal l) + length (jmodifiertxns $ ljournal l) + length (jperiodictxns $ ljournal l)) (length $ ledgerAccountNames l) -- (showtree $ ledgerAccountNameTree l) nullledger :: Ledger nullledger = Ledger { ljournal = nulljournal, laccounts = [] } -- | Filter a journal's transactions with the given query, then derive -- a ledger containing the chart of accounts and balances. If the -- query includes a depth limit, that will affect the ledger's -- journal but not the ledger's account tree. ledgerFromJournal :: Query -> Journal -> Ledger ledgerFromJournal q j = nullledger{ljournal=depthclippedperiodj, laccounts=as} where symq = filterQuery queryIsSym q depthq = filterQuery queryIsDepth q periodq = filterQuery (not . queryIsDepth) q -- get account totals before and during report period datelessq = filterQuery (not . queryIsDateOrDate2) periodq dateqcons = Date -- if date2_ opts then Date2 else Date -- import cycle, don't bother supporting date2 reportspan = queryDateSpan False {-(date2_ opts)-} q -- date span specified by -b/-e/-p options and query args mstartdate = dbg1 "mstartdate" $ spanStart reportspan menddate = dbg1 "menddate" $ spanEnd reportspan precedingq = dbg1 "precedingq" $ case mstartdate of Just _ -> And [datelessq, dateqcons $ DateSpan Nothing mstartdate] Nothing -> None -- remove amount parts which the query's sym: terms would exclude precedingj = filterJournalAmounts symq $ filterJournalPostings precedingq j periodj = filterJournalAmounts symq $ filterJournalPostings periodq j precedingas = accountsFromPostings $ journalPostings precedingj as = accountsFromPostings $ journalPostings periodj depthclippedperiodj = filterJournalPostings depthq periodj -- j' = journalSelectingAmountFromOpts opts j -- | List a ledger's account names. ledgerAccountNames :: Ledger -> [AccountName] ledgerAccountNames = drop 1 . map aname . laccounts -- | Get the named account from a ledger. ledgerAccount :: Ledger -> AccountName -> Maybe Account ledgerAccount l a = lookupAccount a $ laccounts l -- | Get this ledger's root account, which is a dummy "root" account -- above all others. This should always be first in the account list, -- if somehow not this returns a null account. ledgerRootAccount :: Ledger -> Account ledgerRootAccount = headDef nullacct . laccounts -- | List a ledger's top-level accounts (the ones below the root), in tree order. ledgerTopAccounts :: Ledger -> [Account] ledgerTopAccounts = asubs . head . laccounts -- | List a ledger's bottom-level (subaccount-less) accounts, in tree order. ledgerLeafAccounts :: Ledger -> [Account] ledgerLeafAccounts = filter (null.asubs) . laccounts -- | Accounts in ledger whose name matches the pattern, in tree order. ledgerAccountsMatching :: [String] -> Ledger -> [Account] ledgerAccountsMatching pats = filter (matchpats pats . T.unpack . aname) . laccounts -- XXX pack -- | List a ledger's postings, in the order parsed. ledgerPostings :: Ledger -> [Posting] ledgerPostings = journalPostings . ljournal -- | The (fully specified) date span containing all the ledger's (filtered) transactions, -- or DateSpan Nothing Nothing if there are none. ledgerDateSpan :: Ledger -> DateSpan ledgerDateSpan = postingsDateSpan . ledgerPostings -- | All commodities used in this ledger. ledgerCommodities :: Ledger -> [CommoditySymbol] ledgerCommodities = M.keys . jinferredcommodities . ljournal tests_ledgerFromJournal = [ "ledgerFromJournal" ~: do assertEqual "" (0) (length $ ledgerPostings $ ledgerFromJournal Any nulljournal) assertEqual "" (11) (length $ ledgerPostings $ ledgerFromJournal Any samplejournal) assertEqual "" (6) (length $ ledgerPostings $ ledgerFromJournal (Depth 2) samplejournal) ] tests_Hledger_Data_Ledger = TestList $ tests_ledgerFromJournal