reorg, split types into separate modules
This commit is contained in:
		
							parent
							
								
									7b32caa0aa
								
							
						
					
					
						commit
						ba40fbf733
					
				| @ -1,5 +1,5 @@ | |||||||
| 
 | 
 | ||||||
| module Account --  | module Account | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
| import Debug.Trace | import Debug.Trace | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								BasicTypes.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								BasicTypes.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | 
 | ||||||
|  | module BasicTypes | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Debug.Trace | ||||||
|  | import Text.Printf | ||||||
|  | import Text.Regex | ||||||
|  | import Data.List | ||||||
|  | 
 | ||||||
|  | import Utils | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | type Date = String | ||||||
|  | type Status = Bool | ||||||
|  | 
 | ||||||
|  | -- amounts | ||||||
|  | -- amount arithmetic currently ignores currency conversion | ||||||
|  | 
 | ||||||
|  | data Amount = Amount { | ||||||
|  |                       currency :: String, | ||||||
|  |                       quantity :: Double | ||||||
|  |                      } deriving (Eq,Ord) | ||||||
|  | 
 | ||||||
|  | instance Num Amount where | ||||||
|  |     abs (Amount c q) = Amount c (abs q) | ||||||
|  |     signum (Amount c q) = Amount c (signum q) | ||||||
|  |     fromInteger i = Amount "$" (fromInteger i) | ||||||
|  |     (+) = amountAdd | ||||||
|  |     (-) = amountSub | ||||||
|  |     (*) = amountMult | ||||||
|  | Amount ca qa `amountAdd` Amount cb qb = Amount ca (qa + qb) | ||||||
|  | Amount ca qa `amountSub` Amount cb qb = Amount ca (qa - qb) | ||||||
|  | Amount ca qa `amountMult` Amount cb qb = Amount ca (qa * qb) | ||||||
|  | 
 | ||||||
|  | instance Show Amount where show = amountRoundedOrZero | ||||||
|  | 
 | ||||||
|  | amountRoundedOrZero :: Amount -> String | ||||||
|  | amountRoundedOrZero (Amount cur qty) = | ||||||
|  |     let rounded = printf "%.2f" qty in | ||||||
|  |     case rounded of | ||||||
|  |       "0.00"    -> "0" | ||||||
|  |       "-0.00"   -> "0" | ||||||
|  |       otherwise -> cur ++ rounded | ||||||
|  | 
 | ||||||
							
								
								
									
										65
									
								
								Entry.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Entry.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | 
 | ||||||
|  | module Entry | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Debug.Trace | ||||||
|  | import Text.Printf | ||||||
|  | import Text.Regex | ||||||
|  | import Data.List | ||||||
|  | 
 | ||||||
|  | import Utils | ||||||
|  | import BasicTypes | ||||||
|  | import Transaction | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | -- a register entry is displayed as two or more lines like this: | ||||||
|  | -- date       description          account                 amount       balance | ||||||
|  | -- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA | ||||||
|  | --                                 aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA | ||||||
|  | --                                 ...                     ...         ... | ||||||
|  | -- dateWidth = 10 | ||||||
|  | -- descWidth = 20 | ||||||
|  | -- acctWidth = 22 | ||||||
|  | -- amtWidth  = 11 | ||||||
|  | -- balWidth  = 12 | ||||||
|  | 
 | ||||||
|  | data Entry = Entry { | ||||||
|  |                     edate :: Date, | ||||||
|  |                     estatus :: Status, | ||||||
|  |                     ecode :: String, | ||||||
|  |                     edescription :: String, | ||||||
|  |                     etransactions :: [Transaction] | ||||||
|  |                    } deriving (Eq,Ord) | ||||||
|  | 
 | ||||||
|  | instance Show Entry where show = showEntry | ||||||
|  | 
 | ||||||
|  | showEntry e = (showDate $ edate e) ++ " " ++ (showDescription $ edescription e) ++ " " | ||||||
|  | showDate d = printf "%-10s" d | ||||||
|  | showDescription s = printf "%-20s" (elideRight 20 s) | ||||||
|  | 
 | ||||||
|  | isEntryBalanced :: Entry -> Bool | ||||||
|  | isEntryBalanced e = (sumTransactions . etransactions) e == 0 | ||||||
|  | 
 | ||||||
|  | autofillEntry :: Entry -> Entry | ||||||
|  | autofillEntry e =  | ||||||
|  |     Entry (edate e) (estatus e) (ecode e) (edescription e) | ||||||
|  |               (autofillTransactions (etransactions e)) | ||||||
|  | 
 | ||||||
|  | -- modifier & periodic entries | ||||||
|  | 
 | ||||||
|  | data ModifierEntry = ModifierEntry { -- aka "automated entry" | ||||||
|  |                     valueexpr :: String, | ||||||
|  |                     m_transactions :: [Transaction] | ||||||
|  |                    } deriving (Eq) | ||||||
|  | 
 | ||||||
|  | instance Show ModifierEntry where  | ||||||
|  |     show e = "= " ++ (valueexpr e) ++ "\n" ++ unlines (map show (m_transactions e)) | ||||||
|  | 
 | ||||||
|  | data PeriodicEntry = PeriodicEntry { | ||||||
|  |                     periodexpr :: String, | ||||||
|  |                     p_transactions :: [Transaction] | ||||||
|  |                    } deriving (Eq) | ||||||
|  | 
 | ||||||
|  | instance Show PeriodicEntry where  | ||||||
|  |     show e = "~ " ++ (periodexpr e) ++ "\n" ++ unlines (map show (p_transactions e)) | ||||||
|  | 
 | ||||||
							
								
								
									
										82
									
								
								EntryTransaction.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								EntryTransaction.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | 
 | ||||||
|  | module EntryTransaction | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Debug.Trace | ||||||
|  | import Text.Printf | ||||||
|  | import Text.Regex | ||||||
|  | import Data.List | ||||||
|  | 
 | ||||||
|  | import Utils | ||||||
|  | import BasicTypes | ||||||
|  | import Account | ||||||
|  | import Entry | ||||||
|  | import Transaction | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | -- We parse Entries containing Transactions and flatten them into | ||||||
|  | -- (entry,transaction) pairs (entrytransactions, hereafter referred to as | ||||||
|  | -- "transactions") for easier processing. (So far, these types have | ||||||
|  | -- morphed through E->T; (T,E); ET; E<->T; (E,T)). | ||||||
|  | 
 | ||||||
|  | type EntryTransaction = (Entry,Transaction) | ||||||
|  | 
 | ||||||
|  | entry       (e,t) = e | ||||||
|  | transaction (e,t) = t | ||||||
|  | date        (e,t) = edate e | ||||||
|  | status      (e,t) = estatus e | ||||||
|  | code        (e,t) = ecode e | ||||||
|  | description (e,t) = edescription e | ||||||
|  | account     (e,t) = taccount t | ||||||
|  | amount      (e,t) = tamount t | ||||||
|  |                                           | ||||||
|  | flattenEntry :: Entry -> [EntryTransaction] | ||||||
|  | flattenEntry e = [(e,t) | t <- etransactions e] | ||||||
|  | 
 | ||||||
|  | entryTransactionsFrom :: [Entry] -> [EntryTransaction] | ||||||
|  | entryTransactionsFrom es = concat $ map flattenEntry es | ||||||
|  | 
 | ||||||
|  | sumEntryTransactions :: [EntryTransaction] -> Amount | ||||||
|  | sumEntryTransactions ets =  | ||||||
|  |     sumTransactions $ map transaction ets | ||||||
|  | 
 | ||||||
|  | accountNamesFromTransactions :: [EntryTransaction] -> [AccountName] | ||||||
|  | accountNamesFromTransactions ts = nub $ map account ts | ||||||
|  | 
 | ||||||
|  | matchTransactionAccount :: String -> EntryTransaction -> Bool | ||||||
|  | matchTransactionAccount s t = | ||||||
|  |     case matchRegex (mkRegex s) (account t) of | ||||||
|  |       Nothing -> False | ||||||
|  |       otherwise -> True | ||||||
|  | 
 | ||||||
|  | matchTransactionDescription :: String -> EntryTransaction -> Bool | ||||||
|  | matchTransactionDescription s t = | ||||||
|  |     case matchRegex (mkRegex s) (description t) of | ||||||
|  |       Nothing -> False | ||||||
|  |       otherwise -> True | ||||||
|  | 
 | ||||||
|  | showTransactionsWithBalances :: [EntryTransaction] -> Amount -> String | ||||||
|  | showTransactionsWithBalances [] _ = [] | ||||||
|  | showTransactionsWithBalances ts b = | ||||||
|  |     unlines $ showTransactionsWithBalances' ts dummyt b | ||||||
|  |         where | ||||||
|  |           dummyt = (Entry "" False "" "" [], Transaction "" (Amount "" 0)) | ||||||
|  |           showTransactionsWithBalances' [] _ _ = [] | ||||||
|  |           showTransactionsWithBalances' (t:ts) tprev b = | ||||||
|  |               (if (entry t /= (entry tprev)) | ||||||
|  |                then [showTransactionDescriptionAndBalance t b'] | ||||||
|  |                else [showTransactionAndBalance t b']) | ||||||
|  |               ++ (showTransactionsWithBalances' ts t b') | ||||||
|  |                   where b' = b + (amount t) | ||||||
|  | 
 | ||||||
|  | showTransactionDescriptionAndBalance :: EntryTransaction -> Amount -> String | ||||||
|  | showTransactionDescriptionAndBalance t b = | ||||||
|  |     (showEntry $ entry t) ++ (showTransaction $ transaction t) ++ (showBalance b) | ||||||
|  | 
 | ||||||
|  | showTransactionAndBalance :: EntryTransaction -> Amount -> String | ||||||
|  | showTransactionAndBalance t b = | ||||||
|  |     (replicate 32 ' ') ++ (showTransaction $ transaction t) ++ (showBalance b) | ||||||
|  | 
 | ||||||
|  | showBalance :: Amount -> String | ||||||
|  | showBalance b = printf " %12s" (amountRoundedOrZero b) | ||||||
|  | 
 | ||||||
							
								
								
									
										69
									
								
								Ledger.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								Ledger.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | module Ledger | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Debug.Trace | ||||||
|  | import Text.Printf | ||||||
|  | import Text.Regex | ||||||
|  | import Data.List | ||||||
|  | 
 | ||||||
|  | import Utils | ||||||
|  | import Account | ||||||
|  | import Entry | ||||||
|  | import EntryTransaction | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | data Ledger = Ledger { | ||||||
|  |                       modifier_entries :: [ModifierEntry], | ||||||
|  |                       periodic_entries :: [PeriodicEntry], | ||||||
|  |                       entries :: [Entry] | ||||||
|  |                      } deriving (Eq) | ||||||
|  | 
 | ||||||
|  | instance Show Ledger where | ||||||
|  |     show l = printf "Ledger with %d normal, %d modifier, %d periodic entries" | ||||||
|  |              (show $ length $ modifier_entries l) | ||||||
|  |              (show $ length $ periodic_entries l) | ||||||
|  |              (show $ length $ entries l) | ||||||
|  | 
 | ||||||
|  | ledgerTransactions :: Ledger -> [EntryTransaction] | ||||||
|  | ledgerTransactions l = entryTransactionsFrom $ entries l | ||||||
|  | 
 | ||||||
|  | ledgerTransactionsMatching :: ([String],[String]) -> Ledger -> [EntryTransaction] | ||||||
|  | ledgerTransactionsMatching ([],[]) l = ledgerTransactionsMatching ([".*"],[".*"]) l | ||||||
|  | ledgerTransactionsMatching (rs,[]) l = ledgerTransactionsMatching (rs,[".*"]) l | ||||||
|  | ledgerTransactionsMatching ([],rs) l = ledgerTransactionsMatching ([".*"],rs) l | ||||||
|  | ledgerTransactionsMatching (acctregexps,descregexps) l = | ||||||
|  |     intersect  | ||||||
|  |     (concat [filter (matchTransactionAccount r) ts | r <- acctregexps]) | ||||||
|  |     (concat [filter (matchTransactionDescription r) ts | r <- descregexps]) | ||||||
|  |     where ts = ledgerTransactions l | ||||||
|  | 
 | ||||||
|  | ledgerAccountNamesUsed :: Ledger -> [AccountName] | ||||||
|  | ledgerAccountNamesUsed l = accountNamesFromTransactions $ entryTransactionsFrom $ entries l | ||||||
|  | 
 | ||||||
|  | ledgerAccountNames :: Ledger -> [AccountName] | ||||||
|  | ledgerAccountNames = sort . expandAccountNames . ledgerAccountNamesUsed | ||||||
|  | 
 | ||||||
|  | ledgerTopAccountNames :: Ledger -> [AccountName] | ||||||
|  | ledgerTopAccountNames l = filter (notElem ':') (ledgerAccountNames l) | ||||||
|  | 
 | ||||||
|  | ledgerAccountNamesMatching :: [String] -> Ledger -> [AccountName] | ||||||
|  | ledgerAccountNamesMatching [] l = ledgerAccountNamesMatching [".*"] l | ||||||
|  | ledgerAccountNamesMatching acctregexps l = | ||||||
|  |     concat [filter (matchAccountName r) accountNames | r <- acctregexps] | ||||||
|  |         where accountNames = ledgerTopAccountNames l | ||||||
|  | 
 | ||||||
|  | ledgerAccounts :: Ledger -> Tree AccountName | ||||||
|  | ledgerAccounts l = accountFrom $ ledgerAccountNames l | ||||||
|  | 
 | ||||||
|  | showLedgerAccounts :: Ledger -> [String] -> Int -> String | ||||||
|  | showLedgerAccounts l acctpats depth =  | ||||||
|  |     showAccountsWithBalances l accounts depth | ||||||
|  |         where | ||||||
|  |           accounts = ledgerAccountsMatching l acctpats | ||||||
|  | 
 | ||||||
|  | showAccountsWithBalances :: Ledger -> [Account] -> Int -> String | ||||||
|  | showAccountsWithBalances l accts depth = | ||||||
|  |     "" | ||||||
|  | 
 | ||||||
|  | ledgerAccountsMatching :: Ledger -> [String] -> [Account] | ||||||
|  | ledgerAccountsMatching l acctpats = [] | ||||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | |||||||
| build: Tags | build: Tags | ||||||
| 	ghc --make -O2 hledger.hs | 	ghc --make hledger.hs | ||||||
| 
 | 
 | ||||||
| Tags: | Tags: | ||||||
| 	hasktags *hs | 	hasktags *hs | ||||||
|  | |||||||
							
								
								
									
										315
									
								
								Models.hs
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								Models.hs
									
									
									
									
									
								
							| @ -1,5 +1,13 @@ | |||||||
| 
 | -- data types & behaviours | ||||||
| module Models -- data types & behaviours | module Models ( | ||||||
|  |                module Models, | ||||||
|  |                module Ledger, | ||||||
|  |                module EntryTransaction, | ||||||
|  |                module Transaction, | ||||||
|  |                module Entry, | ||||||
|  |                module Account, | ||||||
|  |                module BasicTypes, | ||||||
|  |               ) | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
| import Debug.Trace | import Debug.Trace | ||||||
| @ -8,234 +16,24 @@ import Text.Regex | |||||||
| import Data.List | import Data.List | ||||||
| 
 | 
 | ||||||
| import Utils | import Utils | ||||||
|  | import BasicTypes | ||||||
| import Account | import Account | ||||||
|  | import Entry | ||||||
|  | import Transaction | ||||||
|  | import EntryTransaction | ||||||
|  | import Ledger | ||||||
| 
 | 
 | ||||||
| -- basic types |  | ||||||
| 
 | 
 | ||||||
| type Date = String | -- any top-level stuff that mixed up the other types | ||||||
| type Status = Bool |  | ||||||
| 
 | 
 | ||||||
| -- amounts |  | ||||||
| -- amount arithmetic currently ignores currency conversion |  | ||||||
| 
 | 
 | ||||||
| data Amount = Amount { | -- showAccountNamesWithBalances :: [(AccountName,String)] -> Ledger -> String | ||||||
|                       currency :: String, | -- showAccountNamesWithBalances as l = | ||||||
|                       quantity :: Double | --     unlines $ map (showAccountNameAndBalance l) as | ||||||
|                      } deriving (Eq,Ord) |  | ||||||
| 
 | 
 | ||||||
| instance Num Amount where | -- showAccountNameAndBalance :: Ledger -> (AccountName, String) -> String | ||||||
|     abs (Amount c q) = Amount c (abs q) | -- showAccountNameAndBalance l (a, adisplay) = | ||||||
|     signum (Amount c q) = Amount c (signum q) | --     printf "%20s  %s" (showBalance $ accountBalance l a) adisplay | ||||||
|     fromInteger i = Amount "$" (fromInteger i) |  | ||||||
|     (+) = amountAdd |  | ||||||
|     (-) = amountSub |  | ||||||
|     (*) = amountMult |  | ||||||
| Amount ca qa `amountAdd` Amount cb qb = Amount ca (qa + qb) |  | ||||||
| Amount ca qa `amountSub` Amount cb qb = Amount ca (qa - qb) |  | ||||||
| Amount ca qa `amountMult` Amount cb qb = Amount ca (qa * qb) |  | ||||||
| 
 |  | ||||||
| instance Show Amount where show = amountRoundedOrZero |  | ||||||
| 
 |  | ||||||
| amountRoundedOrZero :: Amount -> String |  | ||||||
| amountRoundedOrZero (Amount cur qty) = |  | ||||||
|     let rounded = printf "%.2f" qty in |  | ||||||
|     case rounded of |  | ||||||
|       "0.00"    -> "0" |  | ||||||
|       "-0.00"   -> "0" |  | ||||||
|       otherwise -> cur ++ rounded |  | ||||||
| 
 |  | ||||||
| -- modifier & periodic entries |  | ||||||
| 
 |  | ||||||
| data ModifierEntry = ModifierEntry { -- aka "automated entry" |  | ||||||
|                     valueexpr :: String, |  | ||||||
|                     m_transactions :: [Transaction] |  | ||||||
|                    } deriving (Eq) |  | ||||||
| 
 |  | ||||||
| instance Show ModifierEntry where  |  | ||||||
|     show e = "= " ++ (valueexpr e) ++ "\n" ++ unlines (map show (m_transactions e)) |  | ||||||
| 
 |  | ||||||
| data PeriodicEntry = PeriodicEntry { |  | ||||||
|                     periodexpr :: String, |  | ||||||
|                     p_transactions :: [Transaction] |  | ||||||
|                    } deriving (Eq) |  | ||||||
| 
 |  | ||||||
| instance Show PeriodicEntry where  |  | ||||||
|     show e = "~ " ++ (periodexpr e) ++ "\n" ++ unlines (map show (p_transactions e)) |  | ||||||
| 
 |  | ||||||
| -- entries |  | ||||||
| -- a register entry is displayed as two or more lines like this: |  | ||||||
| -- date       description          account                 amount       balance |  | ||||||
| -- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA |  | ||||||
| --                                 aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA |  | ||||||
| --                                 ...                     ...         ... |  | ||||||
| -- dateWidth = 10 |  | ||||||
| -- descWidth = 20 |  | ||||||
| -- acctWidth = 22 |  | ||||||
| -- amtWidth  = 11 |  | ||||||
| -- balWidth  = 12 |  | ||||||
| 
 |  | ||||||
| data Entry = Entry { |  | ||||||
|                     edate :: Date, |  | ||||||
|                     estatus :: Status, |  | ||||||
|                     ecode :: String, |  | ||||||
|                     edescription :: String, |  | ||||||
|                     etransactions :: [Transaction] |  | ||||||
|                    } deriving (Eq,Ord) |  | ||||||
| 
 |  | ||||||
| instance Show Entry where show = showEntry |  | ||||||
| 
 |  | ||||||
| showEntry e = (showDate $ edate e) ++ " " ++ (showDescription $ edescription e) ++ " " |  | ||||||
| showDate d = printf "%-10s" d |  | ||||||
| showDescription s = printf "%-20s" (elideRight 20 s) |  | ||||||
| 
 |  | ||||||
| isEntryBalanced :: Entry -> Bool |  | ||||||
| isEntryBalanced e = (sumTransactions . etransactions) e == 0 |  | ||||||
| 
 |  | ||||||
| autofillEntry :: Entry -> Entry |  | ||||||
| autofillEntry e =  |  | ||||||
|     Entry (edate e) (estatus e) (ecode e) (edescription e) |  | ||||||
|               (autofillTransactions (etransactions e)) |  | ||||||
| 
 |  | ||||||
| -- transactions |  | ||||||
| 
 |  | ||||||
| data Transaction = Transaction { |  | ||||||
|                                 taccount :: AccountName, |  | ||||||
|                                 tamount :: Amount |  | ||||||
|                                } deriving (Eq,Ord) |  | ||||||
| 
 |  | ||||||
| instance Show Transaction where show = showTransaction |  | ||||||
| 
 |  | ||||||
| showTransaction t = (showAccountName $ taccount t) ++ "  " ++ (showAmount $ tamount t)  |  | ||||||
| showAmount amt = printf "%11s" (show amt) |  | ||||||
| showAccountName s = printf "%-22s" (elideRight 22 s) |  | ||||||
| 
 |  | ||||||
| elideRight width s = |  | ||||||
|     case length s > width of |  | ||||||
|       True -> take (width - 2) s ++ ".." |  | ||||||
|       False -> s |  | ||||||
| 
 |  | ||||||
| -- elideAccountRight width abbrevlen a =  |  | ||||||
| --     case length a > width of |  | ||||||
| --       False -> a |  | ||||||
| --       True -> abbreviateAccountComponent abbrevlen a  |  | ||||||
|          |  | ||||||
| -- abbreviateAccountComponent abbrevlen a = |  | ||||||
| --     let components = splitAtElement ':' a in |  | ||||||
| --     case  |  | ||||||
|      |  | ||||||
| autofillTransactions :: [Transaction] -> [Transaction] |  | ||||||
| autofillTransactions ts = |  | ||||||
|     let (ns, as) = partition isNormal ts |  | ||||||
|             where isNormal t = (currency $ tamount t) /= "AUTO" in |  | ||||||
|     case (length as) of |  | ||||||
|       0 -> ns |  | ||||||
|       1 -> ns ++ [balanceTransaction $ head as] |  | ||||||
|           where balanceTransaction t = t{tamount = -(sumTransactions ns)} |  | ||||||
|       otherwise -> error "too many blank transactions in this entry" |  | ||||||
| 
 |  | ||||||
| sumTransactions :: [Transaction] -> Amount |  | ||||||
| sumTransactions ts = sum [tamount t | t <- ts] |  | ||||||
| 
 |  | ||||||
| -- entrytransactions |  | ||||||
| -- We parse Entries containing Transactions and flatten them into |  | ||||||
| -- (entry,transaction) pairs (entrytransactions, hereafter referred to as |  | ||||||
| -- "transactions") for easier processing. (So far, these types have |  | ||||||
| -- morphed through E->T; (T,E); ET; E<->T; (E,T)). |  | ||||||
| 
 |  | ||||||
| type EntryTransaction = (Entry,Transaction) |  | ||||||
| 
 |  | ||||||
| entry       (e,t) = e |  | ||||||
| transaction (e,t) = t |  | ||||||
| date        (e,t) = edate e |  | ||||||
| status      (e,t) = estatus e |  | ||||||
| code        (e,t) = ecode e |  | ||||||
| description (e,t) = edescription e |  | ||||||
| account     (e,t) = taccount t |  | ||||||
| amount      (e,t) = tamount t |  | ||||||
|                                           |  | ||||||
| flattenEntry :: Entry -> [EntryTransaction] |  | ||||||
| flattenEntry e = [(e,t) | t <- etransactions e] |  | ||||||
| 
 |  | ||||||
| entryTransactionsFrom :: [Entry] -> [EntryTransaction] |  | ||||||
| entryTransactionsFrom es = concat $ map flattenEntry es |  | ||||||
| 
 |  | ||||||
| sumEntryTransactions :: [EntryTransaction] -> Amount |  | ||||||
| sumEntryTransactions ets =  |  | ||||||
|     sumTransactions $ map transaction ets |  | ||||||
| 
 |  | ||||||
| matchTransactionAccount :: String -> EntryTransaction -> Bool |  | ||||||
| matchTransactionAccount s t = |  | ||||||
|     case matchRegex (mkRegex s) (account t) of |  | ||||||
|       Nothing -> False |  | ||||||
|       otherwise -> True |  | ||||||
| 
 |  | ||||||
| matchTransactionDescription :: String -> EntryTransaction -> Bool |  | ||||||
| matchTransactionDescription s t = |  | ||||||
|     case matchRegex (mkRegex s) (description t) of |  | ||||||
|       Nothing -> False |  | ||||||
|       otherwise -> True |  | ||||||
| 
 |  | ||||||
| showTransactionsWithBalances :: [EntryTransaction] -> Amount -> String |  | ||||||
| showTransactionsWithBalances [] _ = [] |  | ||||||
| showTransactionsWithBalances ts b = |  | ||||||
|     unlines $ showTransactionsWithBalances' ts dummyt b |  | ||||||
|         where |  | ||||||
|           dummyt = (Entry "" False "" "" [], Transaction "" (Amount "" 0)) |  | ||||||
|           showTransactionsWithBalances' [] _ _ = [] |  | ||||||
|           showTransactionsWithBalances' (t:ts) tprev b = |  | ||||||
|               (if (entry t /= (entry tprev)) |  | ||||||
|                then [showTransactionDescriptionAndBalance t b'] |  | ||||||
|                else [showTransactionAndBalance t b']) |  | ||||||
|               ++ (showTransactionsWithBalances' ts t b') |  | ||||||
|                   where b' = b + (amount t) |  | ||||||
| 
 |  | ||||||
| showTransactionDescriptionAndBalance :: EntryTransaction -> Amount -> String |  | ||||||
| showTransactionDescriptionAndBalance t b = |  | ||||||
|     (showEntry $ entry t) ++ (showTransaction $ transaction t) ++ (showBalance b) |  | ||||||
| 
 |  | ||||||
| showTransactionAndBalance :: EntryTransaction -> Amount -> String |  | ||||||
| showTransactionAndBalance t b = |  | ||||||
|     (replicate 32 ' ') ++ (showTransaction $ transaction t) ++ (showBalance b) |  | ||||||
| 
 |  | ||||||
| showBalance :: Amount -> String |  | ||||||
| showBalance b = printf " %12s" (amountRoundedOrZero b) |  | ||||||
| 
 |  | ||||||
| -- more account functions |  | ||||||
| 
 |  | ||||||
| accountNamesFromTransactions :: [EntryTransaction] -> [AccountName] |  | ||||||
| accountNamesFromTransactions ts = nub $ map account ts |  | ||||||
| 
 |  | ||||||
| -- like expandAccountNames, but goes from the top down and elides accountNames |  | ||||||
| -- with only one child and no transactions. Returns accountNames paired with |  | ||||||
| -- the appropriate indented name. Eg |  | ||||||
| -- [("assets","assets"),("assets:cash:gifts","  cash:gifts"),("assets:checking","  checking")] |  | ||||||
| expandAccountNamesMostly :: Ledger -> [AccountName] -> [(AccountName, String)] |  | ||||||
| expandAccountNamesMostly l as = concat $ map (expandAccountNameMostly l) as |  | ||||||
|     where  |  | ||||||
|       expandAccountNameMostly :: Ledger -> AccountName -> [(AccountName, String)] |  | ||||||
|       expandAccountNameMostly l a = |  | ||||||
|           [(acct, acctname)] ++ (concat $ map (expandAccountNameMostly l) subs) |  | ||||||
|               where  |  | ||||||
|                 subs = subAccountNames l a |  | ||||||
|                 txns = accountTransactionsNoSubs l a |  | ||||||
|                 (acct, acctname) =  |  | ||||||
|                     case (length subs == 1) && (length txns == 0) of |  | ||||||
|                       False -> (a, indentAccountName a) |  | ||||||
|                       True -> (a, indentAccountName a ++ ":" ++ subname) |  | ||||||
|                         where  |  | ||||||
|                           sub = head subs |  | ||||||
|                           subname = (reverse . takeWhile (/= ':') . reverse) sub |  | ||||||
| 
 |  | ||||||
| subAccountNames :: Ledger -> AccountName -> [AccountName] |  | ||||||
| subAccountNames l a = [a' | a' <- ledgerAccountNames l, a `isSubAccountNameOf` a'] |  | ||||||
| 
 |  | ||||||
| showAccountNamesWithBalances :: [(AccountName,String)] -> Ledger -> String |  | ||||||
| showAccountNamesWithBalances as l = |  | ||||||
|     unlines $ map (showAccountNameAndBalance l) as |  | ||||||
| 
 |  | ||||||
| showAccountNameAndBalance :: Ledger -> (AccountName, String) -> String |  | ||||||
| showAccountNameAndBalance l (a, adisplay) = |  | ||||||
|     printf "%20s  %s" (showBalance $ accountBalance l a) adisplay |  | ||||||
| 
 | 
 | ||||||
| accountBalance :: Ledger -> AccountName -> Amount | accountBalance :: Ledger -> AccountName -> Amount | ||||||
| accountBalance l a = | accountBalance l a = | ||||||
| @ -255,10 +53,10 @@ addDataToAccounts :: Ledger -> (Tree AccountName) -> (Tree AccountData) | |||||||
| addDataToAccounts l acct =  | addDataToAccounts l acct =  | ||||||
|     Tree (acctdata, map (addDataToAccounts l) (atsubs acct)) |     Tree (acctdata, map (addDataToAccounts l) (atsubs acct)) | ||||||
|         where  |         where  | ||||||
|  |           acctdata = (aname, atxns, abal) | ||||||
|           aname = atacct acct |           aname = atacct acct | ||||||
|           atxns = accountTransactionsNoSubs l aname |           atxns = accountTransactionsNoSubs l aname | ||||||
|           abal = accountBalance l aname |           abal = accountBalance l aname | ||||||
|           acctdata = (aname, atxns, abal) |  | ||||||
| 
 | 
 | ||||||
| -- an AccountData tree adds some other things we want to cache for | -- an AccountData tree adds some other things we want to cache for | ||||||
| -- convenience, like the account's balance and transactions. | -- convenience, like the account's balance and transactions. | ||||||
| @ -295,7 +93,7 @@ adamt (_,_,amt) = amt | |||||||
| -- $5    b | -- $5    b | ||||||
| -- $5      c | -- $5      c | ||||||
| -- $0  d | -- $0  d | ||||||
| showAccountWithBalances :: Ledger -> (Tree AccountData) -> String | showAccountWithBalances :: Ledger -> Tree AccountData -> String | ||||||
| showAccountWithBalances l adt = (showAccountsWithBalance l) (adtsubs adt) | showAccountWithBalances l adt = (showAccountsWithBalance l) (adtsubs adt) | ||||||
| 
 | 
 | ||||||
| showAccountsWithBalance :: Ledger -> [Tree AccountData] -> String | showAccountsWithBalance :: Ledger -> [Tree AccountData] -> String | ||||||
| @ -304,9 +102,10 @@ showAccountsWithBalance l adts = | |||||||
|         where |         where | ||||||
|           showAccountDataBranch :: Tree AccountData -> String |           showAccountDataBranch :: Tree AccountData -> String | ||||||
|           showAccountDataBranch adt =  |           showAccountDataBranch adt =  | ||||||
|               case boring of |               topacct ++ "\n" ++ subs | ||||||
|                 True  ->  | --               case boring of | ||||||
|                 False -> topacct ++ "\n" ++ subs | --                 True  ->  | ||||||
|  | --                 False ->  | ||||||
|               where |               where | ||||||
|                 topacct = (showAmount abal) ++ "  " ++ (indentAccountName aname) |                 topacct = (showAmount abal) ++ "  " ++ (indentAccountName aname) | ||||||
|                 showAmount amt = printf "%11s" (show amt) |                 showAmount amt = printf "%11s" (show amt) | ||||||
| @ -316,61 +115,9 @@ showAccountsWithBalance l adts = | |||||||
|                 subs = (showAccountsWithBalance l) $ adtsubs adt |                 subs = (showAccountsWithBalance l) $ adtsubs adt | ||||||
|                 boring = (length atxns == 0) && ((length subs) == 1) |                 boring = (length atxns == 0) && ((length subs) == 1) | ||||||
| 
 | 
 | ||||||
|      |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| -- ledger |  | ||||||
| 
 |  | ||||||
| data Ledger = Ledger { |  | ||||||
|                       modifier_entries :: [ModifierEntry], |  | ||||||
|                       periodic_entries :: [PeriodicEntry], |  | ||||||
|                       entries :: [Entry] |  | ||||||
|                      } deriving (Eq) |  | ||||||
| 
 |  | ||||||
| instance Show Ledger where |  | ||||||
|     show l = "Ledger with " ++ m ++ " modifier, " ++ p ++ " periodic, " ++ e ++ " normal entries:\n" |  | ||||||
|                      ++ (concat $ map show (modifier_entries l)) |  | ||||||
|                      ++ (concat $ map show (periodic_entries l)) |  | ||||||
|                      ++ (concat $ map show (entries l)) |  | ||||||
|                      where  |  | ||||||
|                        m = show $ length $ modifier_entries l |  | ||||||
|                        p = show $ length $ periodic_entries l |  | ||||||
|                        e = show $ length $ entries l |  | ||||||
| 
 |  | ||||||
| ledgerTransactions :: Ledger -> [EntryTransaction] |  | ||||||
| ledgerTransactions l = entryTransactionsFrom $ entries l |  | ||||||
| 
 |  | ||||||
| ledgerTransactionsMatching :: ([String],[String]) -> Ledger -> [EntryTransaction] |  | ||||||
| ledgerTransactionsMatching ([],[]) l = ledgerTransactionsMatching ([".*"],[".*"]) l |  | ||||||
| ledgerTransactionsMatching (rs,[]) l = ledgerTransactionsMatching (rs,[".*"]) l |  | ||||||
| ledgerTransactionsMatching ([],rs) l = ledgerTransactionsMatching ([".*"],rs) l |  | ||||||
| ledgerTransactionsMatching (acctregexps,descregexps) l = |  | ||||||
|     intersect  |  | ||||||
|     (concat [filter (matchTransactionAccount r) ts | r <- acctregexps]) |  | ||||||
|     (concat [filter (matchTransactionDescription r) ts | r <- descregexps]) |  | ||||||
|     where ts = ledgerTransactions l |  | ||||||
| 
 |  | ||||||
| ledgerAccountNamesUsed :: Ledger -> [AccountName] |  | ||||||
| ledgerAccountNamesUsed l = accountNamesFromTransactions $ entryTransactionsFrom $ entries l |  | ||||||
| 
 |  | ||||||
| ledgerAccountNames :: Ledger -> [AccountName] |  | ||||||
| ledgerAccountNames = sort . expandAccountNames . ledgerAccountNamesUsed |  | ||||||
| 
 |  | ||||||
| ledgerTopAccountNames :: Ledger -> [AccountName] |  | ||||||
| ledgerTopAccountNames l = filter (notElem ':') (ledgerAccountNames l) |  | ||||||
| 
 |  | ||||||
| ledgerAccountNamesMatching :: [String] -> Ledger -> [AccountName] |  | ||||||
| ledgerAccountNamesMatching [] l = ledgerAccountNamesMatching [".*"] l |  | ||||||
| ledgerAccountNamesMatching acctregexps l = |  | ||||||
|     concat [filter (matchAccountName r) accountNames | r <- acctregexps] |  | ||||||
|         where accountNames = ledgerTopAccountNames l |  | ||||||
| 
 |  | ||||||
| ledgerAccounts :: Ledger -> Tree AccountName |  | ||||||
| ledgerAccounts l = accountFrom $ ledgerAccountNames l |  | ||||||
| 
 |  | ||||||
| ledgerAccountsData :: Ledger -> Tree AccountData | ledgerAccountsData :: Ledger -> Tree AccountData | ||||||
| ledgerAccountsData l = addDataToAccounts l (ledgerAccounts l) | ledgerAccountsData l = addDataToAccounts l (ledgerAccounts l) | ||||||
| 
 | 
 | ||||||
| showLedgerAccountsWithBalances :: Ledger -> String | showLedgerAccountsWithBalances :: Ledger -> Tree AccountData -> String | ||||||
| showLedgerAccountsWithBalances l = | showLedgerAccountsWithBalances l adt = | ||||||
|     showAccountWithBalances l (ledgerAccountsData l) |     showAccountWithBalances l adt | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								Options.hs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Options.hs
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | |||||||
| 
 | 
 | ||||||
| module Options | module Options (module Options, usageInfo) | ||||||
| where | where | ||||||
|      |      | ||||||
| import System.Console.GetOpt | import System.Console.GetOpt | ||||||
| @ -15,7 +15,7 @@ options :: [OptDescr Flag] | |||||||
| options = [ | options = [ | ||||||
|             Option ['v'] ["version"] (NoArg Version)     "show version number" |             Option ['v'] ["version"] (NoArg Version)     "show version number" | ||||||
|           , Option ['f'] ["file"]    (OptArg inp "FILE") "ledger file, or - to read stdin" |           , Option ['f'] ["file"]    (OptArg inp "FILE") "ledger file, or - to read stdin" | ||||||
|           , Option ['s'] ["subtotal"] (NoArg ShowSubs)     "balance: show sub-accounts; other: show subtotals" | --          , Option ['s'] ["subtotal"] (NoArg ShowSubs)     "balance: show sub-accounts; register: show subtotals" | ||||||
|           ] |           ] | ||||||
| 
 | 
 | ||||||
| inp :: Maybe String -> Flag | inp :: Maybe String -> Flag | ||||||
| @ -25,8 +25,9 @@ getOptions :: [String] -> IO ([Flag], [String]) | |||||||
| getOptions argv = | getOptions argv = | ||||||
|     case getOpt RequireOrder options argv of |     case getOpt RequireOrder options argv of | ||||||
|       (o,n,[]  ) -> return (o,n) |       (o,n,[]  ) -> return (o,n) | ||||||
|       (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options)) |       (_,_,errs) -> ioError (userError (concat errs ++ usageInfo usageHeader options)) | ||||||
|         where header = "Usage: hledger [OPTIONS]" | 
 | ||||||
|  | usageHeader = "Usage: hledger [OPTIONS] register|balance [MATCHARGS]" | ||||||
| 
 | 
 | ||||||
| get_content :: Flag -> Maybe String | get_content :: Flag -> Maybe String | ||||||
| get_content (File s) = Just s | get_content (File s) = Just s | ||||||
| @ -45,3 +46,6 @@ ledgerPatternArgs args = | |||||||
|     case "--" `elem` args of |     case "--" `elem` args of | ||||||
|       True -> ((takeWhile (/= "--") args), tail $ (dropWhile (/= "--") args)) |       True -> ((takeWhile (/= "--") args), tail $ (dropWhile (/= "--") args)) | ||||||
|       False -> (args,[]) |       False -> (args,[]) | ||||||
|  | 
 | ||||||
|  | depthOption :: [Flag] -> Int | ||||||
|  | depthOption opts = 1 | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								TODO
									
									
									
									
									
								
							| @ -1,3 +1,17 @@ | |||||||
|  | cleanup/reorganize | ||||||
|  | hledger | ||||||
|  |  Options | ||||||
|  |  Tests | ||||||
|  |   Parse | ||||||
|  |    Models | ||||||
|  |     Ledger | ||||||
|  |      EntryTransaction | ||||||
|  |       Entry | ||||||
|  |        Transaction | ||||||
|  |         Account | ||||||
|  |         BasicTypes | ||||||
|  |          Utils | ||||||
|  | 
 | ||||||
| basic features | basic features | ||||||
|  balance |  balance | ||||||
|   show balances with new tree structures |   show balances with new tree structures | ||||||
| @ -21,6 +35,8 @@ more features | |||||||
| new features | new features | ||||||
|  graph automation |  graph automation | ||||||
|  smart data entry |  smart data entry | ||||||
|  |  incorporate timeclock features | ||||||
|  |  timelog simple amount entries | ||||||
| 
 | 
 | ||||||
| tests | tests | ||||||
|  better use of quickcheck/smallcheck |  better use of quickcheck/smallcheck | ||||||
|  | |||||||
							
								
								
									
										45
									
								
								Tests.hs
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								Tests.hs
									
									
									
									
									
								
							| @ -11,7 +11,6 @@ import Test.HUnit | |||||||
| 
 | 
 | ||||||
| import Options | import Options | ||||||
| import Models | import Models | ||||||
| import Account |  | ||||||
| import Parse | import Parse | ||||||
| 
 | 
 | ||||||
| -- sample data | -- sample data | ||||||
| @ -155,6 +154,7 @@ ledger7_str = "\ | |||||||
| \    assets:checking                                 \n\ | \    assets:checking                                 \n\ | ||||||
| \\n" --" | \\n" --" | ||||||
| 
 | 
 | ||||||
|  | l = ledger7 | ||||||
| ledger7 = Ledger | ledger7 = Ledger | ||||||
|           []  |           []  | ||||||
|           []  |           []  | ||||||
| @ -167,7 +167,8 @@ ledger7 = Ledger | |||||||
|                                 Transaction {taccount="equity:opening balances",  |                                 Transaction {taccount="equity:opening balances",  | ||||||
|                                              tamount=Amount {currency="$", quantity=(-4.82)}} |                                              tamount=Amount {currency="$", quantity=(-4.82)}} | ||||||
|                                ] |                                ] | ||||||
|                  }, |                  } | ||||||
|  |           , | ||||||
|            Entry { |            Entry { | ||||||
|                   edate="2007/02/01", estatus=False, ecode="*", edescription="ayres suites", |                   edate="2007/02/01", estatus=False, ecode="*", edescription="ayres suites", | ||||||
|                   etransactions=[ |                   etransactions=[ | ||||||
| @ -177,6 +178,46 @@ ledger7 = Ledger | |||||||
|                                              tamount=Amount {currency="$", quantity=(-179.92)}} |                                              tamount=Amount {currency="$", quantity=(-179.92)}} | ||||||
|                                ] |                                ] | ||||||
|                  } |                  } | ||||||
|  |           , | ||||||
|  |            Entry { | ||||||
|  |                   edate="2007/01/02", estatus=False, ecode="*", edescription="auto transfer to savings", | ||||||
|  |                   etransactions=[ | ||||||
|  |                                 Transaction {taccount="assets:saving",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=200}}, | ||||||
|  |                                 Transaction {taccount="assets:checking",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=(-200)}} | ||||||
|  |                                ] | ||||||
|  |                  } | ||||||
|  |           , | ||||||
|  |            Entry { | ||||||
|  |                   edate="2007/01/03", estatus=False, ecode="*", edescription="poquito mas", | ||||||
|  |                   etransactions=[ | ||||||
|  |                                 Transaction {taccount="expenses:food:dining",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=4.82}}, | ||||||
|  |                                 Transaction {taccount="assets:cash",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=(-4.82)}} | ||||||
|  |                                ] | ||||||
|  |                  } | ||||||
|  |           , | ||||||
|  |            Entry { | ||||||
|  |                   edate="2007/01/03", estatus=False, ecode="*", edescription="verizon", | ||||||
|  |                   etransactions=[ | ||||||
|  |                                 Transaction {taccount="expenses:phone",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=95.11}}, | ||||||
|  |                                 Transaction {taccount="assets:checking",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=(-95.11)}} | ||||||
|  |                                ] | ||||||
|  |                  } | ||||||
|  |           , | ||||||
|  |            Entry { | ||||||
|  |                   edate="2007/01/03", estatus=False, ecode="*", edescription="discover", | ||||||
|  |                   etransactions=[ | ||||||
|  |                                 Transaction {taccount="liabilities:credit cards:discover",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=80}}, | ||||||
|  |                                 Transaction {taccount="assets:checking",  | ||||||
|  |                                              tamount=Amount {currency="$", quantity=(-80)}} | ||||||
|  |                                ] | ||||||
|  |                  } | ||||||
|           ] |           ] | ||||||
| 
 | 
 | ||||||
| -- utils | -- utils | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								Transaction.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Transaction.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | 
 | ||||||
|  | module Transaction | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Debug.Trace | ||||||
|  | import Text.Printf | ||||||
|  | import Text.Regex | ||||||
|  | import Data.List | ||||||
|  | 
 | ||||||
|  | import Utils | ||||||
|  | import BasicTypes | ||||||
|  | import Account | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | data Transaction = Transaction { | ||||||
|  |                                 taccount :: AccountName, | ||||||
|  |                                 tamount :: Amount | ||||||
|  |                                } deriving (Eq,Ord) | ||||||
|  | 
 | ||||||
|  | instance Show Transaction where show = showTransaction | ||||||
|  | 
 | ||||||
|  | showTransaction t = (showAccountName $ taccount t) ++ "  " ++ (showAmount $ tamount t)  | ||||||
|  | showAmount amt = printf "%11s" (show amt) | ||||||
|  | showAccountName s = printf "%-22s" (elideRight 22 s) | ||||||
|  | 
 | ||||||
|  | elideRight width s = | ||||||
|  |     case length s > width of | ||||||
|  |       True -> take (width - 2) s ++ ".." | ||||||
|  |       False -> s | ||||||
|  | 
 | ||||||
|  | -- elideAccountRight width abbrevlen a =  | ||||||
|  | --     case length a > width of | ||||||
|  | --       False -> a | ||||||
|  | --       True -> abbreviateAccountComponent abbrevlen a  | ||||||
|  |          | ||||||
|  | -- abbreviateAccountComponent abbrevlen a = | ||||||
|  | --     let components = splitAtElement ':' a in | ||||||
|  | --     case  | ||||||
|  |      | ||||||
|  | autofillTransactions :: [Transaction] -> [Transaction] | ||||||
|  | autofillTransactions ts = | ||||||
|  |     let (ns, as) = partition isNormal ts | ||||||
|  |             where isNormal t = (currency $ tamount t) /= "AUTO" in | ||||||
|  |     case (length as) of | ||||||
|  |       0 -> ns | ||||||
|  |       1 -> ns ++ [balanceTransaction $ head as] | ||||||
|  |           where balanceTransaction t = t{tamount = -(sumTransactions ns)} | ||||||
|  |       otherwise -> error "too many blank transactions in this entry" | ||||||
|  | 
 | ||||||
|  | sumTransactions :: [Transaction] -> Amount | ||||||
|  | sumTransactions ts = sum [tamount t | t <- ts] | ||||||
|  | 
 | ||||||
							
								
								
									
										29
									
								
								Utils.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Utils.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | 
 | ||||||
|  | module Utils | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Data.List | ||||||
|  | import System.Directory | ||||||
|  | 
 | ||||||
|  | rhead = head . reverse  | ||||||
|  | rtail = reverse . tail . reverse  | ||||||
|  | 
 | ||||||
|  | splitAtElement :: Eq a => a -> [a] -> [[a]] | ||||||
|  | splitAtElement e l =  | ||||||
|  |     case dropWhile (e==) l of | ||||||
|  |       [] -> [] | ||||||
|  |       l' -> first : splitAtElement e rest | ||||||
|  |         where | ||||||
|  |           (first,rest) = break (e==) l' | ||||||
|  | 
 | ||||||
|  | -- courtesy of allberry_b | ||||||
|  | tildeExpand              :: FilePath -> IO FilePath | ||||||
|  | tildeExpand ('~':[])     =  getHomeDirectory | ||||||
|  | tildeExpand ('~':'/':xs) =  getHomeDirectory >>= return . (++ ('/':xs)) | ||||||
|  | -- ~name, requires -fvia-C or ghc 6.8 | ||||||
|  | --import System.Posix.User | ||||||
|  | -- tildeExpand ('~':xs)     =  do let (user, path) = span (/= '/') xs | ||||||
|  | --                                pw <- getUserEntryForName user | ||||||
|  | --                                return (homeDirectory pw ++ path) | ||||||
|  | tildeExpand xs           =  return xs | ||||||
|  | 
 | ||||||
							
								
								
									
										17
									
								
								hledger.hs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								hledger.hs
									
									
									
									
									
								
							| @ -6,6 +6,7 @@ | |||||||
| module Main -- application logic & most IO | module Main -- application logic & most IO | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
|  | import System.Environment (withArgs) -- for testing in old hugs | ||||||
| import System | import System | ||||||
| import Data.List | import Data.List | ||||||
| import Test.HUnit (runTestTT) | import Test.HUnit (runTestTT) | ||||||
| @ -14,7 +15,6 @@ import Text.ParserCombinators.Parsec (parseFromFile, ParseError) | |||||||
| 
 | 
 | ||||||
| import Options | import Options | ||||||
| import Models | import Models | ||||||
| import Account |  | ||||||
| import Parse | import Parse | ||||||
| import Tests | import Tests | ||||||
| 
 | 
 | ||||||
| @ -28,7 +28,7 @@ main = do | |||||||
|       if "reg" `isPrefixOf` command then (register opts args') |       if "reg" `isPrefixOf` command then (register opts args') | ||||||
|       else if "bal" `isPrefixOf` command then balance opts args' |       else if "bal" `isPrefixOf` command then balance opts args' | ||||||
|            else if "test" `isPrefixOf` command then test |            else if "test" `isPrefixOf` command then test | ||||||
|                 else error "could not recognise your command" |                 else putStr $ usageInfo usageHeader options | ||||||
| 
 | 
 | ||||||
| -- commands | -- commands | ||||||
| 
 | 
 | ||||||
| @ -67,10 +67,13 @@ printRegister opts args ledger = do | |||||||
| 
 | 
 | ||||||
| printBalance :: [Flag] -> [String] -> Ledger -> IO () | printBalance :: [Flag] -> [String] -> Ledger -> IO () | ||||||
| printBalance opts args ledger = do | printBalance opts args ledger = do | ||||||
|   putStr $ showLedgerAccountsWithBalances ledger | --   putStr $ showAccountWithBalances ledger (ledgerAccountsData l) | ||||||
|  |   putStr $ showLedgerAccounts ledger acctpats depth | ||||||
|       where  |       where  | ||||||
|         (acctpats,_) = ledgerPatternArgs args |         (acctpats,_) = ledgerPatternArgs args | ||||||
|         showsubs = (ShowSubs `elem` opts) |         depth = depthOption opts | ||||||
|         accounts = case showsubs of | 
 | ||||||
|                      True -> expandAccountNamesMostly ledger (ledgerTopAccountNames ledger) | --         showsubs = (ShowSubs `elem` opts) | ||||||
|                      False -> [(a,indentAccountName a) | a <- ledgerAccountNamesMatching acctpats ledger] | --         accounts = case showsubs of | ||||||
|  | --                      True -> expandAccountNamesMostly ledger (ledgerTopAccountNames ledger) | ||||||
|  | --                      False -> [(a,indentAccountName a) | a <- ledgerAccountNamesMatching acctpats ledger] | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user