103 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
{-|
 | 
						|
 | 
						|
An 'Entry' represents a regular entry in the ledger file. It normally
 | 
						|
contains two or more balanced 'RawTransaction's.
 | 
						|
 | 
						|
-}
 | 
						|
 | 
						|
module Ledger.Entry
 | 
						|
where
 | 
						|
import Ledger.Utils
 | 
						|
import Ledger.Types
 | 
						|
import Ledger.Dates
 | 
						|
import Ledger.RawTransaction
 | 
						|
import Ledger.Amount
 | 
						|
 | 
						|
 | 
						|
instance Show Entry where show = showEntry
 | 
						|
 | 
						|
instance Show ModifierEntry where 
 | 
						|
    show e = "= " ++ (valueexpr e) ++ "\n" ++ unlines (map show (m_transactions e))
 | 
						|
 | 
						|
instance Show PeriodicEntry where 
 | 
						|
    show e = "~ " ++ (periodicexpr e) ++ "\n" ++ unlines (map show (p_transactions e))
 | 
						|
 | 
						|
nullentry = Entry {
 | 
						|
              edate=parsedate "1900/1/1", 
 | 
						|
              estatus=False, 
 | 
						|
              ecode="", 
 | 
						|
              edescription="", 
 | 
						|
              ecomment="",
 | 
						|
              etransactions=[],
 | 
						|
              epreceding_comment_lines=""
 | 
						|
            }
 | 
						|
 | 
						|
{-|
 | 
						|
Show a ledger entry, formatted for the print command. ledger 2.x's
 | 
						|
standard format looks like this:
 | 
						|
 | 
						|
@
 | 
						|
yyyy/mm/dd[ *][ CODE] description.........          [  ; comment...............]
 | 
						|
    account name 1.....................  ...$amount1[  ; comment...............]
 | 
						|
    account name 2.....................  ..$-amount1[  ; comment...............]
 | 
						|
 | 
						|
pcodewidth    = no limit -- 10          -- mimicking ledger layout.
 | 
						|
pdescwidth    = no limit -- 20          -- I don't remember what these mean,
 | 
						|
pacctwidth    = 35 minimum, no maximum  -- they were important at the time.
 | 
						|
pamtwidth     = 11
 | 
						|
pcommentwidth = no limit -- 22
 | 
						|
@
 | 
						|
-}
 | 
						|
showEntry :: Entry -> String
 | 
						|
showEntry = showEntry' True
 | 
						|
 | 
						|
showEntryUnelided :: Entry -> String
 | 
						|
showEntryUnelided = showEntry' False
 | 
						|
 | 
						|
showEntry' :: Bool -> Entry -> String
 | 
						|
showEntry' elide e = 
 | 
						|
    unlines $ [{-precedingcomment ++ -}description] ++ (showtxns $ etransactions e) ++ [""]
 | 
						|
    where
 | 
						|
      precedingcomment = epreceding_comment_lines e
 | 
						|
      description = concat [date, status, code, desc] -- , comment]
 | 
						|
      date = showdate $ edate e
 | 
						|
      status = if estatus e then " *" else ""
 | 
						|
      code = if (length $ ecode e) > 0 then (printf " (%s)" $ ecode e) else ""
 | 
						|
      desc = " " ++ edescription e
 | 
						|
      comment = if (length $ ecomment e) > 0 then "  ; "++(ecomment e) else ""
 | 
						|
      showtxns ts
 | 
						|
          | elide && length ts == 2 = [showtxn (ts !! 0), showtxnnoamt (ts !! 1)]
 | 
						|
          | otherwise = map showtxn ts
 | 
						|
      showtxn t = showacct t ++ "  " ++ (showamount $ tamount t) ++ (showcomment $ tcomment t)
 | 
						|
      showtxnnoamt t = showacct t ++ "              " ++ (showcomment $ tcomment t)
 | 
						|
      showacct t = "    " ++ (showaccountname $ taccount t)
 | 
						|
      showamount = printf "%12s" . showMixedAmount
 | 
						|
      showaccountname s = printf "%-34s" s
 | 
						|
      showcomment s = if (length s) > 0 then "  ; "++s else ""
 | 
						|
      showdate d = printf "%-10s" (showDate d)
 | 
						|
 | 
						|
isEntryBalanced :: Entry -> Bool
 | 
						|
isEntryBalanced (Entry {etransactions=ts}) = 
 | 
						|
    isZeroMixedAmount $ costOfMixedAmount $ sum $ map tamount $ filter isReal ts
 | 
						|
 | 
						|
-- | Ensure that this entry is balanced, possibly auto-filling a missing
 | 
						|
-- amount first. We can auto-fill if there is just one non-virtual
 | 
						|
-- transaction without an amount. The auto-filled balance will be
 | 
						|
-- converted to cost basis if possible. If the entry can not be balanced,
 | 
						|
-- return an error message instead.
 | 
						|
balanceEntry :: Entry -> Either String Entry
 | 
						|
balanceEntry e@Entry{etransactions=ts}
 | 
						|
    | length missingamounts > 1 = Left $ showerr "could not balance this entry, too many missing amounts"
 | 
						|
    | not $ isEntryBalanced e' = Left $ showerr "could not balance this entry, amounts do not balance"
 | 
						|
    | otherwise = Right e'
 | 
						|
    where
 | 
						|
      (withamounts, missingamounts) = partition hasAmount $ filter isReal ts
 | 
						|
      e' = e{etransactions=ts'}
 | 
						|
      ts' | length missingamounts == 1 = map balance ts
 | 
						|
          | otherwise = ts
 | 
						|
          where 
 | 
						|
            balance t | isReal t && not (hasAmount t) = t{tamount = costOfMixedAmount (-otherstotal)}
 | 
						|
                      | otherwise = t
 | 
						|
                      where otherstotal = sum $ map tamount withamounts
 | 
						|
      showerr s = printf "%s:\n%s" s (showEntryUnelided e)
 |