1 {-|
    2 
    3 A 'Ledger' is derived from a 'Journal' by applying a filter specification
    4 to select 'Transaction's and 'Posting's of interest. It contains the
    5 filtered journal and knows the resulting chart of accounts, account
    6 balances, and postings in each account.
    7 
    8 -}
    9 
   10 module Hledger.Data.Ledger
   11 where
   12 import Data.Map (Map, findWithDefault, fromList)
   13 import Data.Tree
   14 import Test.HUnit
   15 import Text.Printf
   16 
   17 import Hledger.Utils
   18 import Hledger.Data.Types
   19 import Hledger.Data.Account (nullacct)
   20 import Hledger.Data.AccountName
   21 import Hledger.Data.Journal
   22 import Hledger.Data.Posting
   23 import Hledger.Data.Matching
   24 
   25 
   26 instance Show Ledger where
   27     show l = printf "Ledger with %d transactions, %d accounts\n%s"
   28              (length (jtxns $ journal l) +
   29               length (jmodifiertxns $ journal l) +
   30               length (jperiodictxns $ journal l))
   31              (length $ accountnames l)
   32              (showtree $ accountnametree l)
   33 
   34 nullledger :: Ledger
   35 nullledger = Ledger{
   36       journal = nulljournal,
   37       accountnametree = nullaccountnametree,
   38       accountmap = fromList []
   39     }
   40 
   41 -- | Filter a journal's transactions as specified, and then process them
   42 -- to derive a ledger containing all balances, the chart of accounts,
   43 -- canonicalised commodities etc.
   44 journalToLedger :: FilterSpec -> Journal -> Ledger
   45 journalToLedger fs j = nullledger{journal=j',accountnametree=t,accountmap=m}
   46     where j' = filterJournalPostings fs{depth=Nothing} j
   47           (t, m) = journalAccountInfo j'
   48 
   49 -- | Filter a journal's transactions as specified, and then process them
   50 -- to derive a ledger containing all balances, the chart of accounts,
   51 -- canonicalised commodities etc.
   52 -- Like journalToLedger but uses the new matchers.
   53 journalToLedger2 :: Matcher -> Journal -> Ledger
   54 journalToLedger2 m j = nullledger{journal=j',accountnametree=t,accountmap=amap}
   55     where j' = filterJournalPostings2 m j
   56           (t, amap) = journalAccountInfo j'
   57 
   58 -- | List a ledger's account names.
   59 ledgerAccountNames :: Ledger -> [AccountName]
   60 ledgerAccountNames = drop 1 . flatten . accountnametree
   61 
   62 -- | Get the named account from a ledger.
   63 ledgerAccount :: Ledger -> AccountName -> Account
   64 ledgerAccount l a = findWithDefault nullacct a $ accountmap l
   65 
   66 -- | List a ledger's accounts, in tree order
   67 ledgerAccounts :: Ledger -> [Account]
   68 ledgerAccounts = drop 1 . flatten . ledgerAccountTree 9999
   69 
   70 -- | List a ledger's top-level accounts, in tree order
   71 ledgerTopAccounts :: Ledger -> [Account]
   72 ledgerTopAccounts = map root . branches . ledgerAccountTree 9999
   73 
   74 -- | List a ledger's bottom-level (subaccount-less) accounts, in tree order
   75 ledgerLeafAccounts :: Ledger -> [Account]
   76 ledgerLeafAccounts = leaves . ledgerAccountTree 9999
   77 
   78 -- | Accounts in ledger whose name matches the pattern, in tree order.
   79 ledgerAccountsMatching :: [String] -> Ledger -> [Account]
   80 ledgerAccountsMatching pats = filter (matchpats pats . aname) . accounts
   81 
   82 -- | List a ledger account's immediate subaccounts
   83 ledgerSubAccounts :: Ledger -> Account -> [Account]
   84 ledgerSubAccounts l Account{aname=a} = 
   85     map (ledgerAccount l) $ filter (`isSubAccountNameOf` a) $ accountnames l
   86 
   87 -- | List a ledger's postings, in the order parsed.
   88 ledgerPostings :: Ledger -> [Posting]
   89 ledgerPostings = journalPostings . journal
   90 
   91 -- | Get a ledger's tree of accounts to the specified depth.
   92 ledgerAccountTree :: Int -> Ledger -> Tree Account
   93 ledgerAccountTree depth l = treemap (ledgerAccount l) $ treeprune depth $ accountnametree l
   94 
   95 -- | Get a ledger's tree of accounts rooted at the specified account.
   96 ledgerAccountTreeAt :: Ledger -> Account -> Maybe (Tree Account)
   97 ledgerAccountTreeAt l acct = subtreeat acct $ ledgerAccountTree 9999 l
   98 
   99 -- | The (fully specified) date span containing all the ledger's (filtered) transactions,
  100 -- or DateSpan Nothing Nothing if there are none.
  101 ledgerDateSpan :: Ledger -> DateSpan
  102 ledgerDateSpan = postingsDateSpan . ledgerPostings
  103 
  104 -- | Convenience aliases.
  105 accountnames :: Ledger -> [AccountName]
  106 accountnames = ledgerAccountNames
  107 
  108 account :: Ledger -> AccountName -> Account
  109 account = ledgerAccount
  110 
  111 accounts :: Ledger -> [Account]
  112 accounts = ledgerAccounts
  113 
  114 topaccounts :: Ledger -> [Account]
  115 topaccounts = ledgerTopAccounts
  116 
  117 accountsmatching :: [String] -> Ledger -> [Account]
  118 accountsmatching = ledgerAccountsMatching
  119 
  120 subaccounts :: Ledger -> Account -> [Account]
  121 subaccounts = ledgerSubAccounts
  122 
  123 postings :: Ledger -> [Posting]
  124 postings = ledgerPostings
  125 
  126 commodities :: Ledger -> Map String Commodity
  127 commodities = journalCanonicalCommodities . journal
  128 
  129 accounttree :: Int -> Ledger -> Tree Account
  130 accounttree = ledgerAccountTree
  131 
  132 accounttreeat :: Ledger -> Account -> Maybe (Tree Account)
  133 accounttreeat = ledgerAccountTreeAt
  134 
  135 -- datespan :: Ledger -> DateSpan
  136 -- datespan = ledgerDateSpan
  137 
  138 rawdatespan :: Ledger -> DateSpan
  139 rawdatespan = journalDateSpan . journal
  140 
  141 ledgeramounts :: Ledger -> [MixedAmount]
  142 ledgeramounts = journalAmounts . journal
  143 
  144 tests_Hledger_Data_Ledger = TestList
  145  [
  146  ]
  147