penny- Extensible double-entry accounting system

Safe HaskellNone




Containers for entries.

This module is the key guardian of the core principle of double-entry accounting, which is that debits and credits must always balance. An Ent is a container for an Entry. An Entry holds a DrCr and an Amount which, in turn, holds a Commodity and a Qty. For a given Commodity in a particular transaction, the sum of the debits must always be equal to the sum of the credits.

In addition to the Entry, the Ent holds information about whether the particular Entry it holds is inferred or not. An Ent is inferred if the user did not supply the entry, but Penny was able to deduce its Entry because proper entries were supplied for all the other postings in the transaction. The Ent also holds arbitrary metadata--which will typically be other information about the particular posting, such as the payee, account, etc.

A collection of Ent is an Ents. This module will only create an Ent as part of an Ents (though you can later separate the Ent from its other Ents if you like.) In any given Ents, all of the Ent collectively have a zero balance.

This module also contains type synonyms used to represent a Posting, which is an Ent bundled with its sibling Ents, and a Transaction.



data Ent m Source

Information about an entry, along with whether it is inferred and its metadata.


Functor Ent 
Eq m => Eq (Ent m) 
Ord m => Ord (Ent m) 
Show m => Show (Ent m) 
Equivalent m => Equivalent (Ent m)

Two Ents are equivalent if the entries are equivalent and the metadata is equivalent (whether the Ent is inferred or not is ignored.)

entry :: Ent m -> Either (Entry QtyRep) (Entry Qty)Source

The entry from an Ent. If the Ent is inferred--that is, if the user did not supply an entry for it and Penny was able to infer the entry--this will be a Right with the inferred Entry.

meta :: Ent m -> mSource

The metadata accompanying an Ent

inferred :: Ent m -> BoolSource

True if the entry was inferred.


data Ents m Source


Functor Ents 
Foldable Ents 
Traversable Ents 
Eq m => Eq (Ents m) 
Ord m => Ord (Ents m) 
Show m => Show (Ents m) 
Equivalent m => Equivalent (Ents m)

Ents are equivalent if the content Ents of each are equivalent. The order of the ents is insignificant.

unEnts :: Ents m -> [Ent m]Source

tupleEnts :: Ents m -> (Ent m, Ent m, [Ent m])Source

Every Ents alwas contains at least two ents, and possibly additional ones.

mapEnts :: (Ent a -> b) -> Ents a -> Ents bSource

Alter the metadata Ents, while examining the Ents themselves. If you only want to change the metadata and you don't need to examine the other contents of the Ent, use the Functor instance. You cannot change non-metadata aspects of the Ent.

traverseEnts :: Applicative f => (Ent a -> f b) -> Ents a -> f (Ents b)Source

Alter the metadata of Ents while examing their contents. If you do not need to examine their contents, use the Traversable instance.

ents :: [(Maybe (Either (Entry QtyRep) (Entry Qty)), m)] -> Maybe (Ents m)Source

Creates an Ents. At most, one of the Maybe Entry can be Nothing and this function will infer the remaining Entry. This function fails if it cannot create a balanced Ents.



:: Commodity

Commodity for all postings

-> DrCr

DrCr for all non-inferred postings

-> (Either QtyRep Qty, m)

Non-inferred posting 1

-> [(Either QtyRep Qty, m)]

Remaining non-inferred postings

-> m

Metadata for inferred posting

-> Ents m 

Creates Ents. Unlike ents this function never fails because you are restricted in the inputs that you can give it. It will always infer the last Entry. All Entries except one will have the same DrCr; the last, inferred one will have the opposite DrCr.

headEnt :: Ents m -> Ent mSource

Get information from the head posting in the View, which is the one you are most likely interested in. This never fails, as every Ents has at least two postings.

tailEnts :: Ents m -> (Ent m, [Ent m])Source

Get information on sibling Ents.

Postings and transactions

newtype Posting Source

In a Posting, the Ent yielded by headEnt will be the posting of interest. The other sibling postings are also available for inspection.




newtype Transaction Source

A Transaction and a Posting are identical on the inside, but they have different semantic meanings so they are wrapped in newtypes.

transactionToPostings :: Transaction -> [Posting]Source

Splits a Transaction into Postings.

views :: Ents m -> [Ents m]Source

In a Posting, the Ent at the front of the list of Ents is the main posting. There are additional postings. This function rearranges the Ents multiple times so that each posting is at the head of the list exactly once.

unrollSnd :: (a, [b]) -> [(a, b)]Source

 unrollSnd (undefined, []) == []
 unrollSnd (1, [1,2,3]) = [(1,1), (1,2), (1,3)]