Safe Haskell | None |
---|
Lincoln - the Penny core
Penny's core types and classes are here. This module re-exports the most useful things. For more details you will want to look at the sub-modules. Also, not all types and functions are re-exported due to naming conflicts. In particular, neither Penny.Lincoln.Predicates nor Penny.Lincoln.Queries is exported from here due to the blizzard of name conflicts that would result.
- data Balance
- unBalance :: Balance -> Map Commodity BottomLine
- data Balanced
- = Balanced
- | Inferable Entry
- | NotInferable
- isBalanced :: Balance -> Balanced
- entryToBalance :: Entry -> Balance
- addBalances :: Balance -> Balance -> Balance
- removeZeroCommodities :: Balance -> Balance
- data BottomLine
- data Column = Column DrCr Qty
- newtype SubAccount = SubAccount {
- unSubAccount :: Text
- newtype Account = Account {
- unAccount :: [SubAccount]
- data Amount = Amount {
- qty :: Qty
- commodity :: Commodity
- side :: Maybe Side
- spaceBetween :: Maybe SpaceBetween
- newtype Commodity = Commodity {
- unCommodity :: Text
- data TimeZoneOffset
- minsToOffset :: Int -> Maybe TimeZoneOffset
- noOffset :: TimeZoneOffset
- data Hours
- intToHours :: Int -> Maybe Hours
- data Minutes
- intToMinutes :: Int -> Maybe Minutes
- data Seconds
- intToSeconds :: Int -> Maybe Seconds
- zeroSeconds :: Seconds
- midnight :: (Hours, Minutes, Seconds)
- data DateTime = DateTime {}
- dateTimeMidnightUTC :: Day -> DateTime
- toUTC :: DateTime -> UTCTime
- toZonedTime :: DateTime -> ZonedTime
- fromZonedTime :: ZonedTime -> Maybe DateTime
- sameInstant :: DateTime -> DateTime -> Bool
- showDateTime :: DateTime -> String
- data DrCr
- opposite :: DrCr -> DrCr
- data Entry = Entry {}
- newtype Flag = Flag {}
- newtype Memo = Memo {}
- newtype Number = Number {}
- newtype Payee = Payee {}
- newtype From = From {}
- newtype To = To {}
- newtype CountPerUnit = CountPerUnit {}
- data Price
- newPrice :: From -> To -> CountPerUnit -> Maybe Price
- data PricePoint = PricePoint {}
- data Qty
- data NumberStr
- toQty :: NumberStr -> Maybe Qty
- mantissa :: Qty -> Integer
- places :: Qty -> Integer
- add :: Qty -> Qty -> Qty
- mult :: Qty -> Qty -> Qty
- difference :: Qty -> Qty -> Difference
- equivalent :: Qty -> Qty -> Bool
- newQty :: Mantissa -> Places -> Maybe Qty
- type Mantissa = Integer
- type Places = Integer
- data Difference
- = LeftBiggerBy Qty
- | RightBiggerBy Qty
- | Equal
- allocate :: Qty -> NonEmpty Qty -> NonEmpty Qty
- newtype Tag = Tag {}
- newtype Tags = Tags {}
- account :: Text -> Account
- data Family p c = Family p c c [c]
- data Child p c = Child c c [c] p
- data Siblings a = Siblings a a [a]
- children :: Family p c -> Siblings (Child p c)
- orphans :: Family p c -> Siblings c
- adopt :: p -> Siblings c -> Family p c
- marryWith :: (p1 -> p2 -> p3) -> (c1 -> c2 -> c3) -> Family p1 c1 -> Family p2 c2 -> Family p3 c3
- marry :: Family p1 c1 -> Family p2 c2 -> Family (p1, p2) (c1, c2)
- divorceWith :: (p1 -> (p2, p3)) -> (c1 -> (c2, c3)) -> Family p1 c1 -> (Family p2 c2, Family p3 c3)
- divorce :: Family (p1, p2) (c1, c2) -> (Family p1 c1, Family p2 c2)
- filterChildren :: (a -> Bool) -> Family p a -> Maybe (Family p a)
- find :: (p -> c -> Bool) -> Family p c -> Maybe c
- mapChildren :: (a -> b) -> Family p a -> Family p b
- mapChildrenA :: Applicative m => (a -> m b) -> Family p a -> m (Family p b)
- mapParent :: (a -> b) -> Family a c -> Family b c
- mapParentA :: Applicative m => (a -> m b) -> Family a c -> m (Family b c)
- class HasText a where
- class HasTextList a where
- data Posting
- data Transaction
- data PostFam
- transaction :: Family TopLine Posting -> Exceptional Error Transaction
- data RTransaction = RTransaction {}
- rTransaction :: RTransaction -> Transaction
- data Error
- toUnverified :: Transaction -> Family TopLine Posting
- data Inferred
- = Inferred
- | NotInferred
- pPayee :: Posting -> Maybe Payee
- pNumber :: Posting -> Maybe Number
- pFlag :: Posting -> Maybe Flag
- pAccount :: Posting -> Account
- pTags :: Posting -> Tags
- pEntry :: Posting -> Entry
- pMemo :: Posting -> Maybe Memo
- pInferred :: Posting -> Inferred
- pPostingLine :: Posting -> Maybe PostingLine
- pGlobalPosting :: Posting -> Maybe GlobalPosting
- pFilePosting :: Posting -> Maybe FilePosting
- data TopLine
- tDateTime :: TopLine -> DateTime
- tFlag :: TopLine -> Maybe Flag
- tNumber :: TopLine -> Maybe Number
- tPayee :: TopLine -> Maybe Payee
- tMemo :: TopLine -> Maybe Memo
- tTopLineLine :: TopLine -> Maybe TopLineLine
- tTopMemoLine :: TopLine -> Maybe TopMemoLine
- tFilename :: TopLine -> Maybe Filename
- tGlobalTransaction :: TopLine -> Maybe GlobalTransaction
- tFileTransaction :: TopLine -> Maybe FileTransaction
- postFam :: Transaction -> [PostFam]
- unTransaction :: Transaction -> Family TopLine Posting
- unPostFam :: PostFam -> Child TopLine Posting
- data Box m = Box {
- boxMeta :: m
- boxPostFam :: PostFam
- data TopLineChangeData = TopLineChangeData {
- tcDateTime :: Maybe DateTime
- tcFlag :: Maybe (Maybe Flag)
- tcNumber :: Maybe (Maybe Number)
- tcPayee :: Maybe (Maybe Payee)
- tcMemo :: Maybe (Maybe Memo)
- tcTopLineLine :: Maybe (Maybe TopLineLine)
- tcTopMemoLine :: Maybe (Maybe TopMemoLine)
- tcFilename :: Maybe (Maybe Filename)
- tcGlobalTransaction :: Maybe (Maybe GlobalTransaction)
- tcFileTransaction :: Maybe (Maybe FileTransaction)
- emptyTopLineChangeData :: TopLineChangeData
- data PostingChangeData = PostingChangeData {
- pcPayee :: Maybe (Maybe Payee)
- pcNumber :: Maybe (Maybe Number)
- pcFlag :: Maybe (Maybe Flag)
- pcAccount :: Maybe Account
- pcTags :: Maybe Tags
- pcMemo :: Maybe (Maybe Memo)
- pcSide :: Maybe (Maybe Side)
- pcSpaceBetween :: Maybe (Maybe SpaceBetween)
- pcPostingLine :: Maybe (Maybe PostingLine)
- pcGlobalPosting :: Maybe (Maybe GlobalPosting)
- pcFilePosting :: Maybe (Maybe FilePosting)
- emptyPostingChangeData :: PostingChangeData
- changeTransaction :: Family TopLineChangeData PostingChangeData -> Transaction -> Transaction
- newtype TopLineLine = TopLineLine {
- unTopLineLine :: Int
- newtype TopMemoLine = TopMemoLine {
- unTopMemoLine :: Int
- data Side
- data SpaceBetween
- newtype Filename = Filename {
- unFilename :: Text
- newtype PriceLine = PriceLine {
- unPriceLine :: Int
- newtype PostingLine = PostingLine {
- unPostingLine :: Int
- newtype GlobalPosting = GlobalPosting {}
- newtype FilePosting = FilePosting {}
- newtype GlobalTransaction = GlobalTransaction {}
- newtype FileTransaction = FileTransaction {}
- data PriceDb
- emptyDb :: PriceDb
- addPrice :: PriceDb -> PricePoint -> PriceDb
- getPrice :: PriceDb -> From -> To -> DateTime -> Exceptional PriceDbError CountPerUnit
- data PriceDbError
- convert :: PriceDb -> DateTime -> To -> Amount -> Exceptional PriceDbError Qty
- data Serial
- forward :: Serial -> Int
- backward :: Serial -> Int
- data GenSerial a
- incrementBack :: GenSerial ()
- getSerial :: GenSerial Serial
- makeSerials :: GenSerial a -> a
- serialItems :: (Serial -> a -> b) -> [a] -> [b]
- nSerials :: Int -> [Serial]
- type Factory = CaseSensitive -> Text -> Exceptional Text Matcher
- display :: PostFam -> Text
Balances
A balance summarizes several entries. You do not create a Balance
directly. Instead, use entryToBalance
.
unBalance :: Balance -> Map Commodity BottomLineSource
Returns a map where the keys are the commodities in the balance and the values are the balance for each commodity. If there is no balance at all, this map can be empty.
Returned by isBalanced
.
isBalanced :: Balance -> BalancedSource
Is this balance balanced?
entryToBalance :: Entry -> BalanceSource
Converts an Entry to a Balance.
addBalances :: Balance -> Balance -> BalanceSource
Add two Balances together. Commodities are never removed from the balance, even if their balance is zero. Instead, they are left in the balance. Sometimes you want to know that a commodity was in the account but its balance is now zero.
removeZeroCommodities :: Balance -> BalanceSource
Removes zero balances from a Balance.
Bits
Accounts
newtype SubAccount Source
Account | |
|
Amounts
Commodities
DateTime
data TimeZoneOffset Source
The number of minutes that this timezone is offset from UTC. Can be positive, negative, or zero.
minsToOffset :: Int -> Maybe TimeZoneOffsetSource
Convert minutes to a time zone offset. I'm having a hard time deciding whether to be liberal or strict in what to accept here. Currently it is somewhat strict in that it will fail if absolute value is greater than 840 minutes; currently the article at http:en.wikipedia.orgwikiList_of_time_zones_by_UTC_offset says there is no offset greater than 14 hours, or 840 minutes.
intToHours :: Int -> Maybe HoursSource
succeeds if 0 <= x < 24
intToMinutes :: Int -> Maybe MinutesSource
succeeds if 0 <= x < 60
intToSeconds :: Int -> Maybe SecondsSource
succeeds if 0 <= x < 61 (to allow for leap seconds)
A DateTime is a a local date and time, along with a time zone
offset. The Eq and Ord instances are derived; therefore, two
DateTime instances will not be equivalent if the time zone offsets
are different, even if they are the same instant. To compare one
DateTime to another, you probably want to use toUTC
and compare
those. To see if two DateTime are the same instant, use
sameInstant
.
sameInstant :: DateTime -> DateTime -> BoolSource
Are these DateTimes the same instant in time, after adjusting for local timezones?
showDateTime :: DateTime -> StringSource
Shows a DateTime in a pretty way.
Debits and credits
Entries
Flag
Memos
There is one item in the list for each line of the memo. Do not include newlines in the texts themselves. However there is nothing to enforce this convention.
Number
Payee
Prices and price points
newtype CountPerUnit Source
newPrice :: From -> To -> CountPerUnit -> Maybe PriceSource
Succeeds only if From and To are different commodities.
Quantities
A quantity is always greater than zero. Various odd questions happen if quantities can be zero. For instance, what if you have a debit whose quantity is zero? Does it require a balancing credit that is also zero? And how can you have a debit of zero anyway?
I can imagine situations where a quantity of zero might be useful; for instance maybe you want to specifically indicate that a particular posting in a transaction did not happen (for instance, that a paycheck deduction did not take place). I think the better way to handle that though would be through an addition to DebitCredit - maybe DebitCredit/Zero. Barring the addition of that, though, the best way to indicate a situation such as this would be through transaction memos.
The Eq instance is derived. Therefore q1 == q2 only if q1 and q2
have both the same mantissa and the same exponent. You may instead
want equivalent
.
Whole String | A whole number only. No radix point. |
WholeRad String | A whole number and a radix point, but nothing after the radix point. |
WholeRadFrac String String | A whole number and something after the radix point. |
RadFrac String | A radix point and a fractional value after it, but nothing before the radix point. |
toQty :: NumberStr -> Maybe QtySource
Converts strings to Qty. Fails if any of the strings have non-digits, or if any are negative, or if the result is not greater than zero, or if the strings are empty.
difference :: Qty -> Qty -> DifferenceSource
Subtract the second Qty from the first, after equalizing their exponents.
equivalent :: Qty -> Qty -> BoolSource
Compares Qty after equalizing their exponents.
:: Qty | The result will add up to this Qty. |
-> NonEmpty Qty | Allocate using this list of Qty. |
-> NonEmpty Qty | The length of this list will be equal to the length of the list of allocations. Each item will correspond to the original allocation. |
Allocate a Qty proportionally so that the sum of the results adds up to a given Qty. Fails if the allocation cannot be made (e.g. if it is impossible to allocate without overflowing Decimal.) The result will always add up to the given sum.
Tags
Builders
account :: Text -> AccountSource
Create an Account. You supply a single Text, with colons to separate the different sub-accounts.
Families
Family types
A Family has one parent (ah, the anomie, sorry) and at least two children.
Family p c c [c] |
A Child has at least one sibling and a parent.
Child c c [c] p |
Describes the siblings of a family, but tells you nothing about the parent. There are always at least two Siblings.
Siblings a a [a] |
Manipulating families
children :: Family p c -> Siblings (Child p c)Source
Gets a family's children. The Child type contains information on the parent, and each Child contains information on the other Siblings.
adopt :: p -> Siblings c -> Family p cSource
Unites a parent and some siblings into one family; the dual of orphans.
marryWith :: (p1 -> p2 -> p3) -> (c1 -> c2 -> c3) -> Family p1 c1 -> Family p2 c2 -> Family p3 c3Source
Marries two families into one. This function is rather cruel: if one family has more children than the other family, then the extra children are discarded. That is, all children must pair one-by-one.
marry :: Family p1 c1 -> Family p2 c2 -> Family (p1, p2) (c1, c2)Source
marryWith a tupling function.
divorceWith :: (p1 -> (p2, p3)) -> (c1 -> (c2, c3)) -> Family p1 c1 -> (Family p2 c2, Family p3 c3)Source
Splits up a family.
divorce :: Family (p1, p2) (c1, c2) -> (Family p1 c1, Family p2 c2)Source
divorceWith an untupling function.
filterChildren :: (a -> Bool) -> Family p a -> Maybe (Family p a)Source
Filters the children. Fails if there are not at least two children after filtering. Retains the original order of the children (after removing the children you don't want.)
mapChildren :: (a -> b) -> Family p a -> Family p bSource
Maps over all children.
mapChildrenA :: Applicative m => (a -> m b) -> Family p a -> m (Family p b)Source
Maps over all children, in order starting with child 1, then child 2, then the children in the list from left to right.
mapParentA :: Applicative m => (a -> m b) -> Family a c -> m (Family b c)Source
Maps over the parent in an Applicative.
HasText
class HasTextList a whereSource
Transactions
Postings and transactions
data Transaction Source
All the Postings in a Transaction must produce a Total whose debits and credits are equal. That is, the Transaction must be balanced. No Transactions are created that are not balanced.
Making and deconstructing transactions
transaction :: Family TopLine Posting -> Exceptional Error TransactionSource
Makes transactions.
data RTransaction Source
RTransaction | |
|
rTransaction :: RTransaction -> TransactionSource
Creates a restricted transaction
; that is, one in which all the
entries will have the same commodity, and in which all but one of
the postings will all be debits or credits. The last posting will
have no quantity specified at all and will be inferred. Creating
these transactions never fails, in contrast to the transactions
created by transaction
, which can fail at runtime.
Errors that can arise when making a Transaction.
toUnverified :: Transaction -> Family TopLine PostingSource
Deconstruct a Transaction to a family of unverified data.
Querying postings
Indicates whether the entry for this posting was inferred. That is, if the user did not supply an entry for this posting, then it was inferred.
Querying transactions
The TopLine holds information that applies to all the postings in a transaction (so named because in a ledger file, this information appears on the top line.)
postFam :: Transaction -> [PostFam]Source
Get the Postings from a Transaction, with information on the sibling Postings.
Unwrapping Transactions
Transaction boxes
A box stores a family of transaction data along with metadata. The transaction is stored in child form, indicating a particular posting of interest. The metadata is in addition to the metadata associated with the TopLine and with each posting.
Box | |
|
Changing transactions
data TopLineChangeData Source
Each field in the record is a Maybe. If Nothing, make no change to this part of the TopLine.
TopLineChangeData | |
|
data PostingChangeData Source
PostingChangeData | |
|
changeTransaction :: Family TopLineChangeData PostingChangeData -> Transaction -> TransactionSource
Allows you to change the parts of a transaction that can be
chanaged without unbalancing the transaction. You cannot change the
DrCr, Qty, or Commodity, as changing these might unbalance the
transaction. If there are elements you do not want to change at
all, use an emptyTopLineChangeData
or an emptyPostingChangeData
in the appropriate part of the Family that you pass in. If the
Family of change data has more children than the transaction, these
extra children are ignored. If the Family in the Transaction has
more children than the Family of change data, the extra postings
are unchanged. That is, changeTransaction
will never delete
postings.
Metadata
newtype TopLineLine Source
The line number that the TopLine starts on (excluding the memo accompanying the TopLine).
newtype TopMemoLine Source
The line number that the memo accompanying the TopLine starts on.
The commodity and and the quantity may appear with the commodity on the left (e.g. USD 2.14) or with the commodity on the right (e.g. 2.14 USD).
data SpaceBetween Source
There may or may not be a space in between the commodity and the quantity.
The name of the file in which a transaction appears.
The line number on which a price appears.
newtype PostingLine Source
The line number on which a posting appears.
newtype GlobalPosting Source
All postings are numbered in order, beginning with the first posting in the first file and ending with the last posting in the last file.
newtype FilePosting Source
The postings in each file are numbered in order.
newtype GlobalTransaction Source
All transactions are numbered in order, beginning with the first transaction in the first file and ending with the last transaction in the last file.
newtype FileTransaction Source
The transactions in each file are numbered in order.
PriceDb
The PriceDb holds information about prices. Create an empty one
using emptyDb
then fill it with values using foldl or similar.
addPrice :: PriceDb -> PricePoint -> PriceDbSource
Add a single price to the PriceDb.
getPrice :: PriceDb -> From -> To -> DateTime -> Exceptional PriceDbError CountPerUnitSource
Looks up values from the PriceDb. Throws Error if something fails.
The DateTime is the time at which to find a price. If a price exists for that exact DateTime, that price is returned. If no price exists for that exact DateTime, but there is a price for an earlier DateTime, the latest possible price is returned. If there are no earlier prices, CpuNotFound is thrown.
data PriceDbError Source
Getting prices can fail; if it fails, an Error is returned.
convert :: PriceDb -> DateTime -> To -> Amount -> Exceptional PriceDbError QtySource
Given an Amount and a Commodity to convert the amount to,
converts the Amount to the given commodity. If the Amount given is
already in the To commodity, simply returns what was passed in. Can
fail and throw PriceDbError. Internally uses getPrice
, so read its
documentation for details on how price lookup works.
Serials
incrementBack :: GenSerial ()Source
makeSerials :: GenSerial a -> aSource
serialItems :: (Serial -> a -> b) -> [a] -> [b]Source
Matchers
= CaseSensitive | Will this matcher be case sensitive? |
-> Text | The pattern to use when testing for a match. For example, this might be a regular expression, or simply the text to be matched. |
-> Exceptional Text Matcher | Sometimes producing a matcher might fail; for example, the user might have supplied a bad pattern. If so, an exception is returned. On success, a Matcher is returned. |
A function that makes Matchers.