diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,20 @@
 ## [Unreleased]
 
 
+<a name="0.0.0.1"></a>
+## [0.0.0.1] - 2022-03-01
+### Chore
+- bump development version to 0.0.0.1
+- **deps:** upgrade to lts-18.27
+
+### Feat
+- add rudimentary accounting functionality
+
+### Pull Requests
+- Merge pull request [#4](https://github.com/telostat/haspara/issues/4) from telostat/upgrade-stack-lts
+- Merge pull request [#3](https://github.com/telostat/haspara/issues/3) from telostat/rudimentary-accounting
+
+
 <a name="0.0.0.0"></a>
 ## 0.0.0.0 - 2022-03-01
 ### Chore
@@ -13,6 +27,7 @@
 - init codebase
 - **deps:** upgrade to lts-18.17
 - **docs:** update README
+- **release:** 0.0.0.0
 
 ### Feat
 - add Aeson instances to Money data type
@@ -22,4 +37,5 @@
 - Merge pull request [#1](https://github.com/telostat/haspara/issues/1) from telostat/init
 
 
-[Unreleased]: https://github.com/telostat/haspara/compare/0.0.0.0...HEAD
+[Unreleased]: https://github.com/telostat/haspara/compare/0.0.0.1...HEAD
+[0.0.0.1]: https://github.com/telostat/haspara/compare/0.0.0.0...0.0.0.1
diff --git a/haspara.cabal b/haspara.cabal
--- a/haspara.cabal
+++ b/haspara.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               haspara
-version:            0.0.0.0
+version:            0.0.0.1
 license:            MIT
 license-file:       LICENSE
 copyright:          Copyright (c) 2021-2022 Telostat Pte Ltd
@@ -27,6 +27,14 @@
 library
     exposed-modules:
         Haspara
+        Haspara.Accounting
+        Haspara.Accounting.Account
+        Haspara.Accounting.AccountKind
+        Haspara.Accounting.Entry
+        Haspara.Accounting.Event
+        Haspara.Accounting.Ledger
+        Haspara.Accounting.Posting
+        Haspara.Accounting.Types
         Haspara.Currency
         Haspara.Date
         Haspara.FXQuote
@@ -50,16 +58,17 @@
     build-depends:
         aeson >=1.5.6.0 && <1.6,
         base >=4.11 && <5,
+        deriving-aeson >=0.2.8 && <0.3,
         hashable >=1.3.0.0 && <1.4,
         megaparsec >=9.0.1 && <9.1,
         mtl >=2.2.2 && <2.3,
-        refined >=0.6.2 && <0.7,
+        refined >=0.6.3 && <0.7,
         safe-decimal >=0.2.1.0 && <0.3,
         scientific >=0.3.7.0 && <0.4,
         template-haskell >=2.16.0.0 && <2.17,
         text >=1.2.4.1 && <1.3,
         time >=1.9.3 && <1.10,
-        unordered-containers >=0.2.15.0 && <0.3
+        unordered-containers >=0.2.16.0 && <0.3
 
 test-suite haspara-doctest
     type:             exitcode-stdio-1.0
@@ -71,15 +80,16 @@
     build-depends:
         aeson >=1.5.6.0 && <1.6,
         base >=4.11 && <5,
+        deriving-aeson >=0.2.8 && <0.3,
         doctest ==0.17.*,
         hashable >=1.3.0.0 && <1.4,
         haspara -any,
         megaparsec >=9.0.1 && <9.1,
         mtl >=2.2.2 && <2.3,
-        refined >=0.6.2 && <0.7,
+        refined >=0.6.3 && <0.7,
         safe-decimal >=0.2.1.0 && <0.3,
         scientific >=0.3.7.0 && <0.4,
         template-haskell >=2.16.0.0 && <2.17,
         text >=1.2.4.1 && <1.3,
         time >=1.9.3 && <1.10,
-        unordered-containers >=0.2.15.0 && <0.3
+        unordered-containers >=0.2.16.0 && <0.3
diff --git a/src/Haspara/Accounting.hs b/src/Haspara/Accounting.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting.hs
@@ -0,0 +1,42 @@
+module Haspara.Accounting
+  ( Account(..)
+  , AccountKind(..)
+  , accountKindText
+  , Entry(..)
+  , buildEntry
+  , Event(..)
+  , eventDate
+  , eventObject
+  , negateEvent
+  , mkEvent
+  , Posting(..)
+  , postingEvents
+  , post
+  , UnsignedQuantity
+  , Ledger(..)
+  , LedgerItem(..)
+  , mkLedger
+  , addEntry
+  , entryDate
+  , entryObject
+  , entryQuantity
+  , entryDebit
+  , entryCredit
+  ) where
+
+
+import Haspara.Accounting.Account     (Account(..))
+import Haspara.Accounting.AccountKind (AccountKind(..), accountKindText)
+import Haspara.Accounting.Entry
+       ( Entry(..)
+       , buildEntry
+       , entryCredit
+       , entryDate
+       , entryDebit
+       , entryObject
+       , entryQuantity
+       )
+import Haspara.Accounting.Event       (Event(..), eventDate, eventObject, mkEvent, negateEvent)
+import Haspara.Accounting.Ledger      (Ledger(..), LedgerItem(..), addEntry, mkLedger)
+import Haspara.Accounting.Posting     (Posting(..), post, postingEvents)
+import Haspara.Accounting.Types       (UnsignedQuantity)
diff --git a/src/Haspara/Accounting/Account.hs b/src/Haspara/Accounting/Account.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Account.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE DataKinds     #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingVia   #-}
+
+module Haspara.Accounting.Account where
+
+import Data.Hashable                  (Hashable)
+import Deriving.Aeson                 (CustomJSON(CustomJSON), FromJSON, Generic, ToJSON)
+import Deriving.Aeson.Stock           (PrefixedSnake)
+import Haspara.Accounting.AccountKind (AccountKind)
+
+
+-- | Account model.
+--
+-- >>> import Haspara.Accounting.AccountKind (AccountKind(..))
+-- >>> import qualified Data.Aeson as Aeson
+-- >>> let acc = Account AccountKindAsset (1 ::Int)
+-- >>> Aeson.encode acc
+-- "{\"kind\":\"ASSET\",\"object\":1}"
+-- >>> Aeson.decode (Aeson.encode acc) :: Maybe (Account Int)
+-- Just (Account {accountKind = AccountKindAsset, accountObject = 1})
+-- >>> Aeson.decode (Aeson.encode acc) == Just acc
+-- True
+data Account o = Account
+  { accountKind   :: !AccountKind
+  , accountObject :: !o
+  } deriving (Eq, Generic, Ord, Show)
+  deriving (FromJSON, ToJSON)
+  via PrefixedSnake "account" (Account o)
+
+
+instance Hashable o => Hashable (Account o)
diff --git a/src/Haspara/Accounting/AccountKind.hs b/src/Haspara/Accounting/AccountKind.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/AccountKind.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Haspara.Accounting.AccountKind where
+
+import qualified Data.Aeson    as Aeson
+import qualified Data.Char     as C
+import           Data.Hashable (Hashable)
+import qualified Data.Text     as T
+import           GHC.Generics  (Generic)
+
+
+data AccountKind =
+    AccountKindAsset
+  | AccountKindLiability
+  | AccountKindEquity
+  | AccountKindRevenue
+  | AccountKindExpense
+  deriving (Enum, Eq, Generic, Ord, Show)
+
+
+instance Hashable AccountKind
+
+
+-- | 'Aeson.FromJSON' instance for 'AccountKind'.
+--
+-- >>> Aeson.decode "\"Asset\"" :: Maybe AccountKind
+-- Just AccountKindAsset
+-- >>> Aeson.decode "\"aSSET\"" :: Maybe AccountKind
+-- Just AccountKindAsset
+-- >>> Aeson.decode "\"ASSET\"" :: Maybe AccountKind
+-- Just AccountKindAsset
+-- >>> Aeson.decode "\"LIABILITY\"" :: Maybe AccountKind
+-- Just AccountKindLiability
+-- >>> Aeson.decode "\"EQUITY\"" :: Maybe AccountKind
+-- Just AccountKindEquity
+-- >>> Aeson.decode "\"REVENUE\"" :: Maybe AccountKind
+-- Just AccountKindRevenue
+-- >>> Aeson.decode "\"EXPENSE\"" :: Maybe AccountKind
+-- Just AccountKindExpense
+instance Aeson.FromJSON AccountKind where
+  parseJSON = Aeson.withText "AccountKind" $ \t -> case T.map C.toUpper t of
+    "ASSET"     -> pure AccountKindAsset
+    "LIABILITY" -> pure AccountKindLiability
+    "EQUITY"    -> pure AccountKindEquity
+    "REVENUE"   -> pure AccountKindRevenue
+    "EXPENSE"   -> pure AccountKindExpense
+    _           -> fail $ "Unknown account kind: " <> show t
+
+
+-- | 'Aeson.ToJSON' instance for 'AccountKind'.
+--
+-- >>> Aeson.encode AccountKindAsset
+-- "\"ASSET\""
+-- >>> Aeson.encode AccountKindLiability
+-- "\"LIABILITY\""
+-- >>> Aeson.encode AccountKindEquity
+-- "\"EQUITY\""
+-- >>> Aeson.encode AccountKindRevenue
+-- "\"REVENUE\""
+-- >>> Aeson.encode AccountKindExpense
+-- "\"EXPENSE\""
+instance Aeson.ToJSON AccountKind where
+  toJSON AccountKindAsset     = Aeson.String "ASSET"
+  toJSON AccountKindLiability = Aeson.String "LIABILITY"
+  toJSON AccountKindEquity    = Aeson.String "EQUITY"
+  toJSON AccountKindRevenue   = Aeson.String "REVENUE"
+  toJSON AccountKindExpense   = Aeson.String "EXPENSE"
+
+
+accountKindText :: AccountKind -> T.Text
+accountKindText AccountKindAsset     = "Asset"
+accountKindText AccountKindLiability = "Liability"
+accountKindText AccountKindEquity    = "Equity"
+accountKindText AccountKindRevenue   = "Revenue"
+accountKindText AccountKindExpense   = "Expense"
diff --git a/src/Haspara/Accounting/Entry.hs b/src/Haspara/Accounting/Entry.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Entry.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE DataKinds      #-}
+{-# LANGUAGE KindSignatures #-}
+
+module Haspara.Accounting.Entry where
+
+import           Data.Aeson                     ((.:), (.=))
+import qualified Data.Aeson                     as Aeson
+import qualified Data.Char                      as C
+import qualified Data.Text                      as T
+import           GHC.TypeLits                   (KnownNat, Nat)
+import qualified Haspara                        as H
+import           Haspara.Accounting.AccountKind (AccountKind(..))
+import           Haspara.Accounting.Event       (Event(..))
+import           Haspara.Accounting.Types       (UnsignedQuantity)
+import           Refined                        (unrefine)
+
+
+-- | Encoding of a posting entry.
+--
+-- >>> :set -XDataKinds
+-- >>> import Refined
+-- >>> let date = read "2021-01-01"
+-- >>> let oid = 1 :: Int
+-- >>> let qty = $$(refineTH 42) :: UnsignedQuantity 2
+-- >>> let entry = EntryDebit date oid qty
+-- >>> let json = Aeson.encode entry
+-- >>> json
+-- "{\"qty\":42.0,\"obj\":1,\"date\":\"2021-01-01\",\"type\":\"DEBIT\"}"
+-- >>> Aeson.decode json :: Maybe (Entry Int 2)
+-- Just (EntryDebit 2021-01-01 1 (Refined 42.00))
+-- >>> Aeson.decode json == Just entry
+-- True
+data Entry o (s :: Nat) =
+    EntryDebit H.Date o (UnsignedQuantity s)
+  | EntryCredit H.Date o (UnsignedQuantity s)
+  deriving (Eq, Ord, Show)
+
+
+instance (Aeson.FromJSON o, KnownNat s) => Aeson.FromJSON (Entry o s) where
+  parseJSON = Aeson.withObject "Entry" $ \o -> do
+    dorc <- o .: "type"
+    cons <- case T.map C.toUpper dorc of
+      "DEBIT"  -> pure EntryDebit
+      "CREDIT" -> pure EntryCredit
+      x        -> fail ("Unknown entry type: " <> T.unpack x)
+    date <- o .: "date"
+    obj <- o .: "obj"
+    qty <- o .: "qty"
+    pure (cons date obj qty)
+
+
+instance (Aeson.ToJSON o, KnownNat s) => Aeson.ToJSON (Entry o s) where
+  toJSON x = case x of
+    EntryDebit d o q  -> Aeson.object ["type" .= ("DEBIT" :: T.Text), "date" .= d, "obj" .= o, "qty" .= q]
+    EntryCredit d o q -> Aeson.object ["type" .= ("CREDIT" :: T.Text), "date" .= d, "obj" .= o, "qty" .= q]
+
+
+entryDate :: KnownNat s => Entry o s -> H.Date
+entryDate (EntryDebit d _ _)  = d
+entryDate (EntryCredit d _ _) = d
+
+
+entryQuantity :: KnownNat s => Entry o s -> H.Quantity s
+entryQuantity (EntryDebit _ _ q)  = unrefine q
+entryQuantity (EntryCredit _ _ q) = -(unrefine q)
+
+
+entryObject :: KnownNat s => Entry o s -> o
+entryObject (EntryDebit _ o _)  = o
+entryObject (EntryCredit _ o _) = o
+
+
+entryDebit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s)
+entryDebit (EntryDebit _ _ x) = Just x
+entryDebit EntryCredit {}     = Nothing
+
+
+entryCredit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s)
+entryCredit EntryDebit {}       = Nothing
+entryCredit (EntryCredit _ _ x) = Just x
+
+
+-- |
+--
+-- +-----------------------+----------+----------+
+-- | Kind of account       | Debit    | Credit   |
+-- +-----------------------+----------+----------+
+-- | Asset                 | Increase | Decrease |
+-- +-----------------------+----------+----------+
+-- | Liability             | Decrease | Increase |
+-- +-----------------------+----------+----------+
+-- | Equity/Capital        | Decrease | Increase |
+-- +-----------------------+----------+----------+
+-- | Income/Revenue        | Decrease | Increase |
+-- +-----------------------+----------+----------+
+-- | Expense/Cost/Dividend | Increase | Decrease |
+-- +-----------------------+----------+----------+
+--
+buildEntry :: (KnownNat s) => Event o s -> AccountKind -> Entry o s
+buildEntry (EventDecrement d o x) AccountKindAsset     = EntryCredit d o x
+buildEntry (EventIncrement d o x) AccountKindAsset     = EntryDebit  d o x
+buildEntry (EventDecrement d o x) AccountKindLiability = EntryDebit  d o x
+buildEntry (EventIncrement d o x) AccountKindLiability = EntryCredit d o x
+buildEntry (EventDecrement d o x) AccountKindEquity    = EntryDebit  d o x
+buildEntry (EventIncrement d o x) AccountKindEquity    = EntryCredit d o x
+buildEntry (EventDecrement d o x) AccountKindRevenue   = EntryDebit  d o x
+buildEntry (EventIncrement d o x) AccountKindRevenue   = EntryCredit d o x
+buildEntry (EventDecrement d o x) AccountKindExpense   = EntryCredit d o x
+buildEntry (EventIncrement d o x) AccountKindExpense   = EntryDebit  d o x
diff --git a/src/Haspara/Accounting/Event.hs b/src/Haspara/Accounting/Event.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Event.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE KindSignatures   #-}
+
+module Haspara.Accounting.Event where
+
+import           Control.Monad.Except     (MonadError(throwError))
+import           Data.Aeson               ((.:), (.=))
+import qualified Data.Aeson               as Aeson
+import qualified Data.Char                as C
+import qualified Data.Text                as T
+import           GHC.TypeLits             (KnownNat, Nat)
+import qualified Haspara                  as H
+import           Haspara.Accounting.Types (UnsignedQuantity)
+import           Refined                  (refine)
+
+
+-- | Encoding of an increment/decrement event.
+--
+-- >>> :set -XDataKinds
+-- >>> import Refined
+-- >>> let date = read "2021-01-01"
+-- >>> let oid = 1 :: Int
+-- >>> let qty = $$(refineTH 42) :: UnsignedQuantity 2
+-- >>> let event = EventDecrement date oid qty
+-- >>> let json = Aeson.encode event
+-- >>> json
+-- "{\"qty\":42.0,\"obj\":1,\"date\":\"2021-01-01\",\"type\":\"DECREMENT\"}"
+-- >>> Aeson.decode json :: Maybe (Event Int 2)
+-- Just (EventDecrement 2021-01-01 1 (Refined 42.00))
+-- >>> Aeson.decode json == Just event
+-- True
+data Event o (s :: Nat) =
+    EventDecrement H.Date o (UnsignedQuantity s)
+  | EventIncrement H.Date o (UnsignedQuantity s)
+  deriving (Eq, Ord, Show)
+
+
+instance (Aeson.FromJSON o, KnownNat s) => Aeson.FromJSON (Event o s) where
+  parseJSON = Aeson.withObject "Event" $ \o -> do
+    dorc <- o .: "type"
+    cons <- case T.map C.toUpper dorc of
+      "DECREMENT" -> pure EventDecrement
+      "INCREMENT" -> pure EventIncrement
+      x           -> fail ("Unknown event type: " <> T.unpack x)
+    date <- o .: "date"
+    obj <- o .: "obj"
+    qty <- o .: "qty"
+    pure (cons date obj qty)
+
+
+instance (Aeson.ToJSON o, KnownNat s) => Aeson.ToJSON (Event o s) where
+  toJSON x = case x of
+    EventDecrement d o q -> Aeson.object ["type" .= ("DECREMENT" :: T.Text), "date" .= d, "obj" .= o, "qty" .= q]
+    EventIncrement d o q -> Aeson.object ["type" .= ("INCREMENT" :: T.Text), "date" .= d, "obj" .= o, "qty" .= q]
+
+
+eventDate :: (KnownNat s) => Event o s -> H.Date
+eventDate (EventDecrement d _ _) = d
+eventDate (EventIncrement d _ _) = d
+
+
+eventObject :: (KnownNat s) => Event o s -> o
+eventObject (EventDecrement _ o _) = o
+eventObject (EventIncrement _ o _) = o
+
+
+negateEvent :: (KnownNat s) => Event o s -> Event o s
+negateEvent (EventDecrement d o x) = EventIncrement d o x
+negateEvent (EventIncrement d o x) = EventDecrement d o x
+
+
+mkEvent :: (MonadError String m, KnownNat s) => H.Date -> o -> H.Quantity s -> m (Event o s)
+mkEvent d o x
+  | x < 0     = either (throwError . show) pure $ EventDecrement d o <$> refine (abs x)
+  | otherwise = either (throwError . show) pure $ EventIncrement d o <$> refine (abs x)
diff --git a/src/Haspara/Accounting/Ledger.hs b/src/Haspara/Accounting/Ledger.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Ledger.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE DeriveGeneric    #-}
+{-# LANGUAGE DerivingVia      #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE KindSignatures   #-}
+
+module Haspara.Accounting.Ledger where
+
+import Deriving.Aeson             (CustomJSON(CustomJSON), FromJSON, Generic, ToJSON)
+import Deriving.Aeson.Stock       (PrefixedSnake)
+import GHC.TypeLits               (KnownNat, Nat)
+import Haspara                    (Quantity)
+import Haspara.Accounting.Account (Account)
+import Haspara.Accounting.Entry   (Entry(..), entryQuantity)
+
+
+data Ledger a o (s :: Nat) = Ledger
+  { ledgerAccount :: !(Account a)
+  , ledgerOpening :: !(Quantity s)
+  , ledgerClosing :: !(Quantity s)
+  , ledgerRunning :: ![LedgerItem o s]
+  } deriving (Eq, Generic, Ord, Show)
+  deriving (FromJSON, ToJSON)
+  via PrefixedSnake "ledger" (Ledger a o s)
+
+
+data LedgerItem o (s :: Nat) = LedgerItem
+  { ledgerItemEntry   :: !(Entry o s)
+  , ledgerItemBalance :: !(Quantity s)
+  } deriving (Eq, Generic, Ord, Show)
+  deriving (FromJSON, ToJSON)
+  via PrefixedSnake "ledgerItem" (LedgerItem o s)
+
+
+mkLedger :: KnownNat s => Account a -> Quantity s -> [Entry o s] -> Ledger a o s
+mkLedger a o = foldl addEntry (Ledger a o o [])
+
+
+addEntry :: KnownNat s => Ledger a o s -> Entry o s -> Ledger a o s
+addEntry l@(Ledger _ _ c r) e = l { ledgerClosing = balance, ledgerRunning = r <> [item]}
+  where
+    balance = c + entryQuantity e
+    item = LedgerItem e balance
diff --git a/src/Haspara/Accounting/Posting.hs b/src/Haspara/Accounting/Posting.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Posting.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE DataKinds       #-}
+{-# LANGUAGE DeriveGeneric   #-}
+{-# LANGUAGE DerivingVia     #-}
+{-# LANGUAGE KindSignatures  #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Haspara.Accounting.Posting where
+
+import qualified Data.List.NonEmpty         as NE
+import           Deriving.Aeson             (CustomJSON(CustomJSON), FromJSON, Generic, ToJSON)
+import           Deriving.Aeson.Stock       (Vanilla)
+import           GHC.TypeLits               (KnownNat, Nat)
+import           Haspara.Accounting.Account (Account(accountKind))
+import           Haspara.Accounting.Entry   (Entry, buildEntry)
+import           Haspara.Accounting.Event   (Event, eventObject)
+
+
+-- | Type encoding for a posting.
+--
+-- >>> :set -XDataKinds
+-- >>> import Haspara.Accounting
+-- >>> import Refined
+-- >>> import qualified Data.Aeson as Aeson
+-- >>> import qualified Data.List.NonEmpty as NE
+-- >>> let date = read "2021-01-01"
+-- >>> let oid = 1 :: Int
+-- >>> let qty = $$(refineTH 42) :: UnsignedQuantity 2
+-- >>> let event = EventDecrement date oid qty
+-- >>> let account = Account AccountKindAsset ("Cash" :: String, 1 ::Int)
+-- >>> let posting =  Posting . NE.fromList $ [(event, account)]
+-- >>> let json = Aeson.encode posting
+-- >>> json
+-- "[[{\"qty\":42.0,\"obj\":1,\"date\":\"2021-01-01\",\"type\":\"DECREMENT\"},{\"kind\":\"ASSET\",\"object\":[\"Cash\",1]}]]"
+-- >>> Aeson.decode json :: Maybe (Posting (String, Int) Int 2)
+-- Just (Posting ((EventDecrement 2021-01-01 1 (Refined 42.00),Account {accountKind = AccountKindAsset, accountObject = ("Cash",1)}) :| []))
+-- >>> Aeson.decode json == Just posting
+-- True
+newtype Posting a o (s :: Nat) = Posting (NE.NonEmpty (Event o s, Account a))
+  deriving (Eq, Generic, Ord, Show)
+  deriving (FromJSON, ToJSON)
+  via Vanilla (Posting a o s)
+
+
+postingEvents :: (KnownNat s) => Posting a o s -> [o]
+postingEvents (Posting es)  = eventObject . fst <$> NE.toList es
+
+
+post :: (KnownNat s) => Posting a o s -> [(Account a, Entry o s)]
+post (Posting xs)       = go (NE.toList xs)
+  where
+    go []              = []
+    go ((ev, ac) : ys) = (ac, buildEntry ev (accountKind ac)) : go ys
diff --git a/src/Haspara/Accounting/Types.hs b/src/Haspara/Accounting/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Haspara/Accounting/Types.hs
@@ -0,0 +1,7 @@
+module Haspara.Accounting.Types where
+
+import Haspara.Quantity (Quantity)
+import Refined          (NonNegative, Refined)
+
+
+type UnsignedQuantity s = Refined NonNegative (Quantity s)
