-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A library providing definitions to work with monetary values. -- -- Please see the README on GitHub at -- https://github.com/telostat/haspara#readme @package haspara @version 0.0.0.2 -- | This module provides definitions for modeling and working with -- currencies. module Haspara.Currency -- | Type encoding for currency symbol values with a syntax of -- [A-Z]{3}[A-Z]*. -- -- Currency values can be constructed via mkCurrencyError -- that works in MonadError Text context: -- --
--   >>> mkCurrencyError "EUR" :: Either T.Text Currency
--   Right EUR
--   
-- -- ... or via mkCurrencyFail that works in MonadFail -- context: -- --
--   >>> mkCurrencyFail "EUR" :: Maybe Currency
--   Just EUR
--   
-- -- An IsString instance is provided as well which is unsafe but -- convenient: -- --
--   >>> "EUR" :: Currency
--   EUR
--   
newtype Currency MkCurrency :: Text -> Currency [currencyCode] :: Currency -> Text -- | Smart constructor for Currency values within MonadError -- context. -- --
--   >>> mkCurrencyError "" :: Either T.Text Currency
--   Left "Currency code error! Expecting at least 3 uppercase ASCII letters, but received: "
--   
--   >>> mkCurrencyError " " :: Either T.Text Currency
--   Left "Currency code error! Expecting at least 3 uppercase ASCII letters, but received:  "
--   
--   >>> mkCurrencyError "AB" :: Either T.Text Currency
--   Left "Currency code error! Expecting at least 3 uppercase ASCII letters, but received: AB"
--   
--   >>> mkCurrencyError " ABC " :: Either T.Text Currency
--   Left "Currency code error! Expecting at least 3 uppercase ASCII letters, but received:  ABC "
--   
--   >>> mkCurrencyError "ABC" :: Either T.Text Currency
--   Right ABC
--   
mkCurrencyError :: MonadError Text m => Text -> m Currency -- | Smart constructor for Currency values within MonadFail -- context. -- --
--   >>> mkCurrencyFail "" :: Maybe Currency
--   Nothing
--   
--   >>> mkCurrencyFail "US" :: Maybe Currency
--   Nothing
--   
--   >>> mkCurrencyFail "usd" :: Maybe Currency
--   Nothing
--   
--   >>> mkCurrencyFail "USD" :: Maybe Currency
--   Just USD
--   
mkCurrencyFail :: MonadFail m => Text -> m Currency -- | Parser that parses currency codes. -- --
--   >>> MP.runParser currencyCodeParser "Example" ""
--   Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
--   >>> MP.runParser currencyCodeParser "Example" " "
--   Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just (Tokens (' ' :| ""))) (fromList []) :| [], bundlePosState = PosState {pstateInput = " ", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
--   >>> MP.runParser currencyCodeParser "Example" "a"
--   Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just (Tokens ('a' :| ""))) (fromList []) :| [], bundlePosState = PosState {pstateInput = "a", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
--   >>> MP.runParser currencyCodeParser "Example" "A"
--   Left (ParseErrorBundle {bundleErrors = TrivialError 1 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "A", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
--   >>> MP.runParser currencyCodeParser "Example" "AB"
--   Left (ParseErrorBundle {bundleErrors = TrivialError 2 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "AB", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
--   >>> MP.runParser currencyCodeParser "Example" "ABC"
--   Right "ABC"
--   
--   >>> MP.runParser currencyCodeParser "Example" "ABCD"
--   Right "ABCD"
--   
--   >>> MP.runParser currencyCodeParser "Example" " ABCD "
--   Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just (Tokens (' ' :| ""))) (fromList []) :| [], bundlePosState = PosState {pstateInput = " ABCD ", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--   
currencyCodeParser :: Parsec Void Text Text -- | Type encoding of a currency pair. -- -- CurrencyPair values are constructed via the data constructor: -- --
--   >>> CurrencyPair "EUR" "USD"
--   EUR/USD
--   
-- -- FromJSON and ToJSON instances are provided as well: -- --
--   >>> Aeson.decode "{\"base\": \"EUR\", \"quote\": \"EUR\"}" :: Maybe CurrencyPair
--   Just EUR/EUR
--   
--   >>> Aeson.encode (CurrencyPair "EUR" "USD")
--   "{\"base\":\"EUR\",\"quote\":\"USD\"}"
--   
data CurrencyPair CurrencyPair :: !Currency -> !Currency -> CurrencyPair -- | Base currency of the currency pair. Also referred to as -- counter currency. [currencyPairBase] :: CurrencyPair -> !Currency -- | Quote currency of the currency pair. Also referred to as -- transaction currency. [currencyPairQuote] :: CurrencyPair -> !Currency -- | Converts a CurrencyPair to a 2-tuple of Currency values. -- --
--   >>> toCurrencyTuple (CurrencyPair "EUR" "USD")
--   (EUR,USD)
--   
toCurrencyTuple :: CurrencyPair -> (Currency, Currency) -- | Converts a 2-tuple of Currency values to a CurrencyPair. -- --
--   >>> fromCurrencyTuple ("EUR", "USD")
--   EUR/USD
--   
fromCurrencyTuple :: (Currency, Currency) -> CurrencyPair instance Language.Haskell.TH.Syntax.Lift Haspara.Currency.Currency instance GHC.Classes.Ord Haspara.Currency.Currency instance Data.Hashable.Class.Hashable Haspara.Currency.Currency instance GHC.Classes.Eq Haspara.Currency.Currency instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Currency.CurrencyPair instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Currency.CurrencyPair instance Language.Haskell.TH.Syntax.Lift Haspara.Currency.CurrencyPair instance GHC.Classes.Ord Haspara.Currency.CurrencyPair instance GHC.Generics.Generic Haspara.Currency.CurrencyPair instance GHC.Classes.Eq Haspara.Currency.CurrencyPair instance GHC.Show.Show Haspara.Currency.CurrencyPair instance Data.String.IsString Haspara.Currency.Currency instance GHC.Show.Show Haspara.Currency.Currency instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Currency.Currency instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Currency.Currency -- | This module provides helper definitions for Data.Aeson. module Haspara.Internal.Aeson -- | Type definition for string modifiers that uppercase a given symbol. data UpperCase instance Deriving.Aeson.StringModifier Haspara.Internal.Aeson.UpperCase -- | This module provides definitions for acccounts and types of accounts -- as they are used in accounting reporting. module Haspara.Accounting.Account -- | Type encoding for ledger account type. -- -- This type covers both balance sheet and income statement account -- types: -- --
    --
  1. For balance sheet accounts:
  2. --
  3. Asset (AccountKindAsset)
  4. --
  5. Liability (AccountKindLiability)
  6. --
  7. Equity (AccountKindEquity)
  8. --
  9. For income statement accounts:
  10. --
  11. Revenue (AccountKindRevenue)
  12. --
  13. Expense (AccountKindExpense)
  14. --
-- -- FromJSON and ToJSON instances, too: -- --
--   >>> Data.Aeson.decode @AccountKind "\"ASSET\""
--   Just AccountKindAsset
--   
--   >>> Data.Aeson.decode @AccountKind "\"LIABILITY\""
--   Just AccountKindLiability
--   
--   >>> Data.Aeson.decode @AccountKind "\"EQUITY\""
--   Just AccountKindEquity
--   
--   >>> Data.Aeson.decode @AccountKind "\"REVENUE\""
--   Just AccountKindRevenue
--   
--   >>> Data.Aeson.decode @AccountKind "\"EXPENSE\""
--   Just AccountKindExpense
--   
--   >>> Data.Aeson.encode AccountKindAsset
--   "\"ASSET\""
--   
--   >>> Data.Aeson.encode AccountKindLiability
--   "\"LIABILITY\""
--   
--   >>> Data.Aeson.encode AccountKindEquity
--   "\"EQUITY\""
--   
--   >>> Data.Aeson.encode AccountKindRevenue
--   "\"REVENUE\""
--   
--   >>> Data.Aeson.encode AccountKindExpense
--   "\"EXPENSE\""
--   
data AccountKind AccountKindAsset :: AccountKind AccountKindLiability :: AccountKind AccountKindEquity :: AccountKind AccountKindRevenue :: AccountKind AccountKindExpense :: AccountKind -- | Provides textual representation of a given AccountKind. -- --
--   >>> accountKindText AccountKindAsset
--   "Asset"
--   
--   >>> accountKindText AccountKindLiability
--   "Liability"
--   
--   >>> accountKindText AccountKindEquity
--   "Equity"
--   
--   >>> accountKindText AccountKindRevenue
--   "Revenue"
--   
--   >>> accountKindText AccountKindExpense
--   "Expense"
--   
accountKindText :: AccountKind -> Text -- | Type encoding for account values. -- -- This definition provides both the AccountKind and an arbitrary -- object identifying the account. This arbitrary nature provides -- flexibility to use-site to use its own account identity and -- accompanying information when required. -- --
--   >>> let acc = Account AccountKindAsset (1 ::Int)
--   
--   >>> Data.Aeson.encode acc
--   "{\"kind\":\"ASSET\",\"object\":1}"
--   
--   >>> Data.Aeson.decode @(Account Int) (Data.Aeson.encode acc)
--   Just (Account {accountKind = AccountKindAsset, accountObject = 1})
--   
--   >>> Data.Aeson.decode (Data.Aeson.encode acc) == Just acc
--   True
--   
data Account o Account :: !AccountKind -> !o -> Account o [accountKind] :: Account o -> !AccountKind [accountObject] :: Account o -> !o instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Accounting.Account.AccountKind instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Accounting.Account.AccountKind instance GHC.Show.Show Haspara.Accounting.Account.AccountKind instance GHC.Classes.Ord Haspara.Accounting.Account.AccountKind instance GHC.Generics.Generic Haspara.Accounting.Account.AccountKind instance GHC.Classes.Eq Haspara.Accounting.Account.AccountKind instance GHC.Enum.Enum Haspara.Accounting.Account.AccountKind instance Data.Aeson.Types.ToJSON.ToJSON o => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Account.Account o) instance Data.Aeson.Types.FromJSON.FromJSON o => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Account.Account o) instance GHC.Show.Show o => GHC.Show.Show (Haspara.Accounting.Account.Account o) instance GHC.Classes.Ord o => GHC.Classes.Ord (Haspara.Accounting.Account.Account o) instance GHC.Generics.Generic (Haspara.Accounting.Account.Account o) instance GHC.Classes.Eq o => GHC.Classes.Eq (Haspara.Accounting.Account.Account o) instance Data.Hashable.Class.Hashable o => Data.Hashable.Class.Hashable (Haspara.Accounting.Account.Account o) instance Data.Hashable.Class.Hashable Haspara.Accounting.Account.AccountKind -- | This module provides definitions for modeling and working with -- quantities with fixed decimal points. module Haspara.Quantity -- | Type encoding for quantity values with a given scaling (digits after -- the decimal point). -- --
--   >>> 42 :: Quantity 0
--   42
--   
--   >>> 42 :: Quantity 1
--   42.0
--   
--   >>> 42 :: Quantity 2
--   42.00
--   
--   >>> 41 + 1 :: Quantity 2
--   42.00
--   
--   >>> 43 - 1 :: Quantity 2
--   42.00
--   
--   >>> 2 * 3 * 7 :: Quantity 2
--   42.00
--   
--   >>> negate (-42) :: Quantity 2
--   42.00
--   
--   >>> abs (-42) :: Quantity 2
--   42.00
--   
--   >>> signum (-42) :: Quantity 2
--   -1.00
--   
--   >>> fromInteger 42 :: Quantity 2
--   42.00
--   
--   >>> mkQuantity 0.415 :: Quantity 2
--   0.42
--   
--   >>> mkQuantity 0.425 :: Quantity 2
--   0.42
--   
--   >>> mkQuantityLossless 0.42 :: Either String (Quantity 2)
--   Right 0.42
--   
--   >>> mkQuantityLossless 0.415 :: Either String (Quantity 2)
--   Left "Underflow while trying to create quantity: 0.415"
--   
newtype Quantity (s :: Nat) MkQuantity :: Decimal RoundHalfEven s Integer -> Quantity (s :: Nat) [unQuantity] :: Quantity (s :: Nat) -> Decimal RoundHalfEven s Integer -- | Type definition for unsigned Quantity values. type UnsignedQuantity s = Refined NonNegative (Quantity s) -- | Constructs Quantity values from Scientific values in a -- lossy way. -- -- This function uses mkQuantityAux in case that the lossless -- attempt fails. We could have used mkQuantityAux directly. -- However, mkQuantityAux is doing too much (see -- roundScientific). Therefore, we are first attempting a lossless -- construction (see mkQuantityLossless) and we fallback to -- mkQuantityAux in case the lossless construction fails. -- --
--   >>> mkQuantity 0 :: Quantity 0
--   0
--   
--   >>> mkQuantity 0 :: Quantity 1
--   0.0
--   
--   >>> mkQuantity 0 :: Quantity 2
--   0.00
--   
--   >>> mkQuantity 0.04 :: Quantity 1
--   0.0
--   
--   >>> mkQuantity 0.05 :: Quantity 1
--   0.0
--   
--   >>> mkQuantity 0.06 :: Quantity 1
--   0.1
--   
--   >>> mkQuantity 0.14 :: Quantity 1
--   0.1
--   
--   >>> mkQuantity 0.15 :: Quantity 1
--   0.2
--   
--   >>> mkQuantity 0.16 :: Quantity 1
--   0.2
--   
--   >>> mkQuantity 0.04 :: Quantity 2
--   0.04
--   
--   >>> mkQuantity 0.05 :: Quantity 2
--   0.05
--   
--   >>> mkQuantity 0.06 :: Quantity 2
--   0.06
--   
--   >>> mkQuantity 0.14 :: Quantity 2
--   0.14
--   
--   >>> mkQuantity 0.15 :: Quantity 2
--   0.15
--   
--   >>> mkQuantity 0.16 :: Quantity 2
--   0.16
--   
--   >>> mkQuantity 0.04 :: Quantity 3
--   0.040
--   
--   >>> mkQuantity 0.05 :: Quantity 3
--   0.050
--   
--   >>> mkQuantity 0.06 :: Quantity 3
--   0.060
--   
--   >>> mkQuantity 0.14 :: Quantity 3
--   0.140
--   
--   >>> mkQuantity 0.15 :: Quantity 3
--   0.150
--   
--   >>> mkQuantity 0.16 :: Quantity 3
--   0.160
--   
mkQuantity :: KnownNat s => Scientific -> Quantity s -- | Constructs Quantity values from Scientific values in a -- lossy way. -- --
--   >>> mkQuantityLossless 0 :: Either String (Quantity 0)
--   Right 0
--   
--   >>> mkQuantityLossless 0 :: Either String (Quantity 1)
--   Right 0.0
--   
--   >>> mkQuantityLossless 0 :: Either String (Quantity 2)
--   Right 0.00
--   
--   >>> mkQuantityLossless 0.04 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 4.0e-2"
--   
--   >>> mkQuantityLossless 0.05 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 5.0e-2"
--   
--   >>> mkQuantityLossless 0.06 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 6.0e-2"
--   
--   >>> mkQuantityLossless 0.14 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 0.14"
--   
--   >>> mkQuantityLossless 0.15 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 0.15"
--   
--   >>> mkQuantityLossless 0.16 :: Either String (Quantity 1)
--   Left "Underflow while trying to create quantity: 0.16"
--   
--   >>> mkQuantityLossless 0.04 :: Either String (Quantity 2)
--   Right 0.04
--   
--   >>> mkQuantityLossless 0.05 :: Either String (Quantity 2)
--   Right 0.05
--   
--   >>> mkQuantityLossless 0.06 :: Either String (Quantity 2)
--   Right 0.06
--   
--   >>> mkQuantityLossless 0.14 :: Either String (Quantity 2)
--   Right 0.14
--   
--   >>> mkQuantityLossless 0.15 :: Either String (Quantity 2)
--   Right 0.15
--   
--   >>> mkQuantityLossless 0.16 :: Either String (Quantity 2)
--   Right 0.16
--   
--   >>> mkQuantityLossless 0.04 :: Either String (Quantity 3)
--   Right 0.040
--   
--   >>> mkQuantityLossless 0.05 :: Either String (Quantity 3)
--   Right 0.050
--   
--   >>> mkQuantityLossless 0.06 :: Either String (Quantity 3)
--   Right 0.060
--   
--   >>> mkQuantityLossless 0.14 :: Either String (Quantity 3)
--   Right 0.140
--   
--   >>> mkQuantityLossless 0.15 :: Either String (Quantity 3)
--   Right 0.150
--   
--   >>> mkQuantityLossless 0.16 :: Either String (Quantity 3)
--   Right 0.160
--   
mkQuantityLossless :: (KnownNat s, MonadError String m) => Scientific -> m (Quantity s) -- | Rounds given quantity by k digits. -- --
--   >>> roundQuantity (mkQuantity 0.415 :: Quantity 3) :: Quantity 2
--   0.42
--   
--   >>> roundQuantity (mkQuantity 0.425 :: Quantity 3) :: Quantity 2
--   0.42
--   
roundQuantity :: KnownNat k => Quantity (n + k) -> Quantity n -- | Multiplies two quantities with different scales and rounds back to the -- scale of the frst operand. -- --
--   >>> times (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.42 :: Quantity 2)
--   0.18
--   
times :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity s -- | Multiplies two quantities with different scales. -- --
--   >>> timesLossless (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.42 :: Quantity 2)
--   0.1764
--   
timesLossless :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity (s + k) -- | Auxiliary function for constructing Quantity values. -- -- See mkQuantity why we need this function and why we haven't -- used it as the direct implementation of mkQuantity. -- -- Call-sites should avoid using this function directly due to its -- performance characteristics. mkQuantityAux :: forall s. KnownNat s => Scientific -> Quantity s -- | Rounds a given scientific into a new scientific with given max digits -- after decimal point. -- -- This uses half-even rounding method. -- --
--   >>> roundScientific 0 0.4
--   0.0
--   
--   >>> roundScientific 0 0.5
--   0.0
--   
--   >>> roundScientific 0 0.6
--   1.0
--   
--   >>> roundScientific 0 1.4
--   1.0
--   
--   >>> roundScientific 0 1.5
--   2.0
--   
--   >>> roundScientific 0 1.6
--   2.0
--   
--   >>> roundScientific 1 0.04
--   0.0
--   
--   >>> roundScientific 1 0.05
--   0.0
--   
--   >>> roundScientific 1 0.06
--   0.1
--   
--   >>> roundScientific 1 0.14
--   0.1
--   
--   >>> roundScientific 1 0.15
--   0.2
--   
--   >>> roundScientific 1 0.16
--   0.2
--   
--   >>> roundScientific 1 3.650
--   3.6
--   
--   >>> roundScientific 1 3.740
--   3.7
--   
--   >>> roundScientific 1 3.749
--   3.7
--   
--   >>> roundScientific 1 3.750
--   3.8
--   
--   >>> roundScientific 1 3.751
--   3.8
--   
--   >>> roundScientific 1  3.760
--   3.8
--   
--   >>> roundScientific 1 (-3.650)
--   -3.6
--   
--   >>> roundScientific 1 (-3.740)
--   -3.7
--   
--   >>> roundScientific 1 (-3.749)
--   -3.7
--   
--   >>> roundScientific 1 (-3.750)
--   -3.8
--   
--   >>> roundScientific 1 (-3.751)
--   -3.8
--   
--   >>> roundScientific 1 (-3.760)
--   -3.8
--   
-- -- TODO: Refactor to improve the performance of this function. roundScientific :: Int -> Scientific -> Scientific instance GHC.TypeNats.KnownNat s => GHC.Num.Num (Haspara.Quantity.Quantity s) instance GHC.Generics.Generic (Haspara.Quantity.Quantity s) instance GHC.Classes.Ord (Haspara.Quantity.Quantity s) instance GHC.Classes.Eq (Haspara.Quantity.Quantity s) instance Language.Haskell.TH.Syntax.Lift (Numeric.Decimal.Internal.Decimal Numeric.Decimal.RoundHalfEven s GHC.Integer.Type.Integer) instance Language.Haskell.TH.Syntax.Lift (Haspara.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => GHC.Num.Num (Numeric.Decimal.BoundedArithmetic.Arith (Haspara.Quantity.Quantity s)) instance GHC.TypeNats.KnownNat s => GHC.Real.Fractional (Numeric.Decimal.BoundedArithmetic.Arith (Haspara.Quantity.Quantity s)) instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.Quantity.Quantity s) -- | This module provides definitions for modeling and working with foreign -- exchange (FX) rate quotations. module Haspara.FxQuote -- | Type encoding for FX rate quotations with fixed precision. -- -- An FX rate quotation is a 3-tuple of: -- --
    --
  1. a currency pair the rate is quoted for, and
  2. --
  3. a date that the quotation is effective as of,
  4. --
  5. a (positive) rate as the value of the quotation.
  6. --
-- --
--   >>> 
--   
data FxQuote (s :: Nat) MkFxQuote :: !CurrencyPair -> !Day -> !Refined Positive (Quantity s) -> FxQuote (s :: Nat) -- | Currency pair of the FX rate. [fxQuotePair] :: FxQuote (s :: Nat) -> !CurrencyPair -- | Actual date of the FX rate. [fxQuoteDate] :: FxQuote (s :: Nat) -> !Day -- | (Positive) rate value of the FX rate. [fxQuoteRate] :: FxQuote (s :: Nat) -> !Refined Positive (Quantity s) -- | Smart constructor for FxQuote values within -- MonadError Text context. -- -- The rate is expected to be a positive value. If it is not, the -- function will throw an error. -- --
--   >>> mkFxQuoteError @(Either _) @2 (read "2021-12-31") "EUR" "USD" 1.16
--   Right (MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16})
--   
--   >>> mkFxQuoteError @(Either _) @2 (read "2021-12-31") "EUR" "USD" (-1.16)
--   Left "Can not create FX Rate. Error was:   The predicate (GreaterThan 0) failed with the message: Value is not greater than 0\n"
--   
mkFxQuoteError :: MonadError Text m => KnownNat s => Day -> Currency -> Currency -> Scientific -> m (FxQuote s) -- | Smart constructor for FxQuote values within MonadFail -- context. -- -- The rate is expected to be a positive value. If it is not, the -- function will fail. >>> mkFxQuoteFail Maybe 2 (read -- "2021-12-31") EUR USD 1.16 Just (MkFxQuote {fxQuotePair -- = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16}) -- >>> mkFxQuoteFail Maybe 2 (read "2021-12-31") -- EUR USD (-1.16) Nothing mkFxQuoteFail :: MonadFail m => KnownNat s => Day -> Currency -> Currency -> Scientific -> m (FxQuote s) -- | Unsafe FxQuote constructor that errors if it fails. -- --
--   >>> mkFxQuoteUnsafe @2 (read "2021-12-31") "EUR" "USD" 1.16
--   MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16}
--   
--   >>> mkFxQuoteUnsafe @2 (read "2021-12-31") "EUR" "USD" (-1.16)
--   ...
--   ...Can not create FX Rate. Error was:   The predicate (GreaterThan 0) failed with the message: Value is not greater than 0
--   ...
--   
mkFxQuoteUnsafe :: KnownNat s => Day -> Currency -> Currency -> Scientific -> FxQuote s -- | Type encoding for a dictionary-based FX rate quotation database for -- various CurrencyPair values. type FxQuoteDatabase (n :: Nat) = Map CurrencyPair (FxQuotePairDatabase n) -- | Type encoding for FX rate quotation database for a -- CurrencyPair. data FxQuotePairDatabase (n :: Nat) FxQuotePairDatabase :: !CurrencyPair -> !Map Day (FxQuote n) -> !Day -> !Day -> FxQuotePairDatabase (n :: Nat) [fxQuotePairDatabasePair] :: FxQuotePairDatabase (n :: Nat) -> !CurrencyPair [fxQuotePairDatabaseTable] :: FxQuotePairDatabase (n :: Nat) -> !Map Day (FxQuote n) [fxQuotePairDatabaseSince] :: FxQuotePairDatabase (n :: Nat) -> !Day [fxQuotePairDatabaseUntil] :: FxQuotePairDatabase (n :: Nat) -> !Day -- | Attempts to find and return the FX quotation for a given -- CurrencyPair as of a give Day in a given -- FxQuoteDatabase. findFxQuote :: KnownNat n => FxQuoteDatabase n -> CurrencyPair -> Day -> Maybe (FxQuote n) -- | Attempts to find and return the FX quotation as of a give Day -- in a given FxQuotePairDatabase. findFxQuoteAux :: KnownNat n => Day -> FxQuotePairDatabase n -> Maybe (FxQuote n) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.FxQuote.FxQuote s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.FxQuote.FxQuote s) instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.FxQuote.FxQuote s) instance GHC.Classes.Ord (Haspara.FxQuote.FxQuote s) instance GHC.Generics.Generic (Haspara.FxQuote.FxQuote s) instance GHC.Classes.Eq (Haspara.FxQuote.FxQuote s) -- | This module provides definitions for modeling and working with -- monetary values. module Haspara.Monetary -- | Type encoding for dated monetary values. -- -- A dated monetary value is a 3-tuple of: -- --
    --
  1. a date when the monetary value is effective as of,
  2. --
  3. the currency of the monetary value, and
  4. --
  5. the quantity of the monetary value.
  6. --
data Money (s :: Nat) Money :: !Day -> !Currency -> !Quantity s -> Money (s :: Nat) [moneyDate] :: Money (s :: Nat) -> !Day [moneyCurrency] :: Money (s :: Nat) -> !Currency [moneyQuantity] :: Money (s :: Nat) -> !Quantity s -- | Type encoding of a monetary context. class MonadThrow m => Monetary m -- | Converts the given monetary value in one currency to another currency. -- -- Note that the conversion is performed with an FX rate quotation as of -- the date of the given monetary value. convertM :: (Monetary m, HasCallStack) => KnownNat s => Currency -> Money s -> m (Money s) -- | Converts the given monetary value in one currency to another currency -- as of the given date. -- -- The rule is: -- --
--   convertAsofM DATE2 CCY2 (Money DATE1 CCY1 QTY1) === convertM CCY2 (Money DATE2 CCY1 QTY1)
--   
convertAsofM :: (Monetary m, HasCallStack) => KnownNat s => Day -> Currency -> Money s -> m (Money s) -- | Attempts to convert the given Money to another using the given -- FxQuote value. -- -- This function runs some guards before attempting to do the conversion: -- --
    --
  1. Base currency of the FX rate quotation should be the same as the -- currency of the monetary value, throws -- IncompatibleCurrenciesException otherwise.
  2. --
  3. Date of the FX rate quotation should be equal to or greater than -- the date of the monetary value, throws -- IncompatibleDatesException otherwise.
  4. --
  5. Rate of the FX rate quotation should be 1 if the base and -- quote quotation are same, throws InconsistentFxQuoteException -- otherwise.
  6. --
convert :: HasCallStack => MonadThrow m => KnownNat s => KnownNat k => Money s -> FxQuote k -> m (Money s) -- | Type encoding of exceptions thrown by the Monetary module. data MonetaryException -- | Indicates that we received a currency other than the expected -- currency. [IncompatibleCurrenciesException] :: HasCallStack => Currency -> Currency -> MonetaryException -- | Indicates that we received a currency other than the expected -- currency. [IncompatibleDatesException] :: HasCallStack => Day -> Day -> MonetaryException -- | Indicates that we received a currency other than the expected -- currency. [InconsistentFxQuoteException] :: forall (s :: Nat). (HasCallStack, KnownNat s) => FxQuote s -> MonetaryException instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Monetary.Money s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Monetary.Money s) instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.Monetary.Money s) instance GHC.Classes.Ord (Haspara.Monetary.Money s) instance GHC.Generics.Generic (Haspara.Monetary.Money s) instance GHC.Classes.Eq (Haspara.Monetary.Money s) instance GHC.Show.Show Haspara.Monetary.MonetaryException instance GHC.Exception.Type.Exception Haspara.Monetary.MonetaryException -- | This module provides definitions for economic events. -- -- /Note: The concept is not YET REA-compatible although we want to -- achieve it at some point/. module Haspara.Accounting.Event -- | Type encoding of an economic increment/decrement event. -- -- The event explicitly carries the date and quantity information along -- with a parameterized, arbitrary object providing the source of the -- event. -- --
--   >>> :set -XDataKinds
--   
--   >>> let date = read "2021-01-01"
--   
--   >>> let oid = 1 :: Int
--   
--   >>> let qty = $$(Refined.refineTH 42) :: UnsignedQuantity 2
--   
--   >>> let event = EventDecrement date oid qty
--   
--   >>> let json = Data.Aeson.encode event
--   
--   >>> json
--   "{\"qty\":42.0,\"type\":\"DECREMENT\",\"obj\":1,\"date\":\"2021-01-01\"}"
--   
--   >>> Data.Aeson.decode @(Event Int 2) json
--   Just (EventDecrement 2021-01-01 1 (Refined 42.00))
--   
--   >>> Data.Aeson.decode json == Just event
--   True
--   
data Event o (s :: Nat) EventDecrement :: Day -> o -> UnsignedQuantity s -> Event o (s :: Nat) EventIncrement :: Day -> o -> UnsignedQuantity s -> Event o (s :: Nat) -- | Returns the date of the event. eventDate :: KnownNat s => Event o s -> Day -- | Returns the source object of the event. eventObject :: KnownNat s => Event o s -> o -- | Negates the event. negateEvent :: KnownNat s => Event o s -> Event o s -- | Smart constuctor for Event values. mkEvent :: MonadError String m => KnownNat s => Day -> o -> Quantity s -> m (Event o s) instance (GHC.Show.Show o, GHC.TypeNats.KnownNat s) => GHC.Show.Show (Haspara.Accounting.Event.Event o s) instance GHC.Classes.Ord o => GHC.Classes.Ord (Haspara.Accounting.Event.Event o s) instance GHC.Classes.Eq o => GHC.Classes.Eq (Haspara.Accounting.Event.Event o s) instance (Data.Aeson.Types.FromJSON.FromJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Event.Event o s) instance (Data.Aeson.Types.ToJSON.ToJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Event.Event o s) -- | This module provides definitions for postings, ledgers and ledger -- entries. module Haspara.Accounting.Ledger -- | Type encoding of a ledger. data Ledger a o (s :: Nat) Ledger :: !Account a -> !Quantity s -> !Quantity s -> ![LedgerItem o s] -> Ledger a o (s :: Nat) [ledgerAccount] :: Ledger a o (s :: Nat) -> !Account a [ledgerOpening] :: Ledger a o (s :: Nat) -> !Quantity s [ledgerClosing] :: Ledger a o (s :: Nat) -> !Quantity s [ledgerRunning] :: Ledger a o (s :: Nat) -> ![LedgerItem o s] -- | Type encoding of a ledger item. data LedgerItem o (s :: Nat) LedgerItem :: !Entry o s -> !Quantity s -> LedgerItem o (s :: Nat) [ledgerItemEntry] :: LedgerItem o (s :: Nat) -> !Entry o s [ledgerItemBalance] :: LedgerItem o (s :: Nat) -> !Quantity s -- | Creates a ledger from a given list of Entry values. mkLedger :: KnownNat s => Account a -> Quantity s -> [Entry o s] -> Ledger a o s -- | Adds a new entry to a ledger. addEntry :: KnownNat s => Ledger a o s -> Entry o s -> Ledger a o s -- | 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,\"type\":\"DECREMENT\",\"obj\":1,\"date\":\"2021-01-01\"},{\"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 :: NonEmpty (Event o s, Account a) -> Posting a o (s :: Nat) -- | Returns the list of posting event sources. postingEvents :: KnownNat s => Posting a o s -> [o] -- | Posts an event. post :: KnownNat s => Posting a o s -> [(Account a, Entry o s)] -- | 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,\"type\":\"DEBIT\",\"obj\":1,\"date\":\"2021-01-01\"}"
--   
--   >>> 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 :: Day -> o -> UnsignedQuantity s -> Entry o (s :: Nat) EntryCredit :: Day -> o -> UnsignedQuantity s -> Entry o (s :: Nat) -- | Returns the date of the posting entry. entryDate :: KnownNat s => Entry o s -> Day -- | Returns the quantity of the posting entry. entryQuantity :: KnownNat s => Entry o s -> Quantity s -- | Returns the source object of the posting entry. entryObject :: KnownNat s => Entry o s -> o -- | Returns the debit quantity of the posting entry. entryDebit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s) -- | Returns the credit quantity of the posting entry. entryCredit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s) -- | Consumes an event and a type of account, and produces a posting entry. -- -- Note the following map as a guide: -- -- TODO: table buildEntry :: KnownNat s => Event o s -> AccountKind -> Entry o s instance (Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.Posting a o s) instance (GHC.TypeNats.KnownNat s, Data.Aeson.Types.FromJSON.FromJSON o, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.Posting a o s) instance (GHC.TypeNats.KnownNat s, GHC.Show.Show o, GHC.Show.Show a) => GHC.Show.Show (Haspara.Accounting.Ledger.Posting a o s) instance (GHC.Classes.Ord o, GHC.Classes.Ord a) => GHC.Classes.Ord (Haspara.Accounting.Ledger.Posting a o s) instance GHC.Generics.Generic (Haspara.Accounting.Ledger.Posting a o s) instance (GHC.Classes.Eq o, GHC.Classes.Eq a) => GHC.Classes.Eq (Haspara.Accounting.Ledger.Posting a o s) instance (GHC.Show.Show o, GHC.TypeNats.KnownNat s) => GHC.Show.Show (Haspara.Accounting.Ledger.Entry o s) instance GHC.Classes.Ord o => GHC.Classes.Ord (Haspara.Accounting.Ledger.Entry o s) instance GHC.Classes.Eq o => GHC.Classes.Eq (Haspara.Accounting.Ledger.Entry o s) instance (GHC.TypeNats.KnownNat s, Data.Aeson.Types.ToJSON.ToJSON o) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.LedgerItem o s) instance (Data.Aeson.Types.FromJSON.FromJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.LedgerItem o s) instance (GHC.Show.Show o, GHC.TypeNats.KnownNat s) => GHC.Show.Show (Haspara.Accounting.Ledger.LedgerItem o s) instance GHC.Classes.Ord o => GHC.Classes.Ord (Haspara.Accounting.Ledger.LedgerItem o s) instance GHC.Generics.Generic (Haspara.Accounting.Ledger.LedgerItem o s) instance GHC.Classes.Eq o => GHC.Classes.Eq (Haspara.Accounting.Ledger.LedgerItem o s) instance (Data.Aeson.Types.ToJSON.ToJSON o, Data.Aeson.Types.ToJSON.ToJSON a, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.Ledger a o s) instance (GHC.TypeNats.KnownNat s, Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSON o) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.Ledger a o s) instance (GHC.TypeNats.KnownNat s, GHC.Show.Show a, GHC.Show.Show o) => GHC.Show.Show (Haspara.Accounting.Ledger.Ledger a o s) instance (GHC.Classes.Ord a, GHC.Classes.Ord o) => GHC.Classes.Ord (Haspara.Accounting.Ledger.Ledger a o s) instance GHC.Generics.Generic (Haspara.Accounting.Ledger.Ledger a o s) instance (GHC.Classes.Eq a, GHC.Classes.Eq o) => GHC.Classes.Eq (Haspara.Accounting.Ledger.Ledger a o s) instance (Data.Aeson.Types.FromJSON.FromJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.Entry o s) instance (Data.Aeson.Types.ToJSON.ToJSON o, GHC.TypeNats.KnownNat s) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.Entry o s) -- | This module provides a collection of definitions for a rudimentary -- accounting functionality. module Haspara.Accounting -- | Type encoding for account values. -- -- This definition provides both the AccountKind and an arbitrary -- object identifying the account. This arbitrary nature provides -- flexibility to use-site to use its own account identity and -- accompanying information when required. -- --
--   >>> let acc = Account AccountKindAsset (1 ::Int)
--   
--   >>> Data.Aeson.encode acc
--   "{\"kind\":\"ASSET\",\"object\":1}"
--   
--   >>> Data.Aeson.decode @(Account Int) (Data.Aeson.encode acc)
--   Just (Account {accountKind = AccountKindAsset, accountObject = 1})
--   
--   >>> Data.Aeson.decode (Data.Aeson.encode acc) == Just acc
--   True
--   
data Account o Account :: !AccountKind -> !o -> Account o [accountKind] :: Account o -> !AccountKind [accountObject] :: Account o -> !o -- | Type encoding for ledger account type. -- -- This type covers both balance sheet and income statement account -- types: -- --
    --
  1. For balance sheet accounts:
  2. --
  3. Asset (AccountKindAsset)
  4. --
  5. Liability (AccountKindLiability)
  6. --
  7. Equity (AccountKindEquity)
  8. --
  9. For income statement accounts:
  10. --
  11. Revenue (AccountKindRevenue)
  12. --
  13. Expense (AccountKindExpense)
  14. --
-- -- FromJSON and ToJSON instances, too: -- --
--   >>> Data.Aeson.decode @AccountKind "\"ASSET\""
--   Just AccountKindAsset
--   
--   >>> Data.Aeson.decode @AccountKind "\"LIABILITY\""
--   Just AccountKindLiability
--   
--   >>> Data.Aeson.decode @AccountKind "\"EQUITY\""
--   Just AccountKindEquity
--   
--   >>> Data.Aeson.decode @AccountKind "\"REVENUE\""
--   Just AccountKindRevenue
--   
--   >>> Data.Aeson.decode @AccountKind "\"EXPENSE\""
--   Just AccountKindExpense
--   
--   >>> Data.Aeson.encode AccountKindAsset
--   "\"ASSET\""
--   
--   >>> Data.Aeson.encode AccountKindLiability
--   "\"LIABILITY\""
--   
--   >>> Data.Aeson.encode AccountKindEquity
--   "\"EQUITY\""
--   
--   >>> Data.Aeson.encode AccountKindRevenue
--   "\"REVENUE\""
--   
--   >>> Data.Aeson.encode AccountKindExpense
--   "\"EXPENSE\""
--   
data AccountKind AccountKindAsset :: AccountKind AccountKindLiability :: AccountKind AccountKindEquity :: AccountKind AccountKindRevenue :: AccountKind AccountKindExpense :: AccountKind -- | Provides textual representation of a given AccountKind. -- --
--   >>> accountKindText AccountKindAsset
--   "Asset"
--   
--   >>> accountKindText AccountKindLiability
--   "Liability"
--   
--   >>> accountKindText AccountKindEquity
--   "Equity"
--   
--   >>> accountKindText AccountKindRevenue
--   "Revenue"
--   
--   >>> accountKindText AccountKindExpense
--   "Expense"
--   
accountKindText :: AccountKind -> Text -- | 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,\"type\":\"DEBIT\",\"obj\":1,\"date\":\"2021-01-01\"}"
--   
--   >>> 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 :: Day -> o -> UnsignedQuantity s -> Entry o (s :: Nat) EntryCredit :: Day -> o -> UnsignedQuantity s -> Entry o (s :: Nat) -- | Consumes an event and a type of account, and produces a posting entry. -- -- Note the following map as a guide: -- -- TODO: table buildEntry :: KnownNat s => Event o s -> AccountKind -> Entry o s -- | Type encoding of an economic increment/decrement event. -- -- The event explicitly carries the date and quantity information along -- with a parameterized, arbitrary object providing the source of the -- event. -- --
--   >>> :set -XDataKinds
--   
--   >>> let date = read "2021-01-01"
--   
--   >>> let oid = 1 :: Int
--   
--   >>> let qty = $$(Refined.refineTH 42) :: UnsignedQuantity 2
--   
--   >>> let event = EventDecrement date oid qty
--   
--   >>> let json = Data.Aeson.encode event
--   
--   >>> json
--   "{\"qty\":42.0,\"type\":\"DECREMENT\",\"obj\":1,\"date\":\"2021-01-01\"}"
--   
--   >>> Data.Aeson.decode @(Event Int 2) json
--   Just (EventDecrement 2021-01-01 1 (Refined 42.00))
--   
--   >>> Data.Aeson.decode json == Just event
--   True
--   
data Event o (s :: Nat) EventDecrement :: Day -> o -> UnsignedQuantity s -> Event o (s :: Nat) EventIncrement :: Day -> o -> UnsignedQuantity s -> Event o (s :: Nat) -- | Returns the date of the event. eventDate :: KnownNat s => Event o s -> Day -- | Returns the source object of the event. eventObject :: KnownNat s => Event o s -> o -- | Negates the event. negateEvent :: KnownNat s => Event o s -> Event o s -- | Smart constuctor for Event values. mkEvent :: MonadError String m => KnownNat s => Day -> o -> Quantity s -> m (Event o s) -- | 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,\"type\":\"DECREMENT\",\"obj\":1,\"date\":\"2021-01-01\"},{\"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 :: NonEmpty (Event o s, Account a) -> Posting a o (s :: Nat) -- | Returns the list of posting event sources. postingEvents :: KnownNat s => Posting a o s -> [o] -- | Posts an event. post :: KnownNat s => Posting a o s -> [(Account a, Entry o s)] -- | Type encoding of a ledger. data Ledger a o (s :: Nat) Ledger :: !Account a -> !Quantity s -> !Quantity s -> ![LedgerItem o s] -> Ledger a o (s :: Nat) [ledgerAccount] :: Ledger a o (s :: Nat) -> !Account a [ledgerOpening] :: Ledger a o (s :: Nat) -> !Quantity s [ledgerClosing] :: Ledger a o (s :: Nat) -> !Quantity s [ledgerRunning] :: Ledger a o (s :: Nat) -> ![LedgerItem o s] -- | Type encoding of a ledger item. data LedgerItem o (s :: Nat) LedgerItem :: !Entry o s -> !Quantity s -> LedgerItem o (s :: Nat) [ledgerItemEntry] :: LedgerItem o (s :: Nat) -> !Entry o s [ledgerItemBalance] :: LedgerItem o (s :: Nat) -> !Quantity s -- | Creates a ledger from a given list of Entry values. mkLedger :: KnownNat s => Account a -> Quantity s -> [Entry o s] -> Ledger a o s -- | Adds a new entry to a ledger. addEntry :: KnownNat s => Ledger a o s -> Entry o s -> Ledger a o s -- | Returns the date of the posting entry. entryDate :: KnownNat s => Entry o s -> Day -- | Returns the source object of the posting entry. entryObject :: KnownNat s => Entry o s -> o -- | Returns the quantity of the posting entry. entryQuantity :: KnownNat s => Entry o s -> Quantity s -- | Returns the debit quantity of the posting entry. entryDebit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s) -- | Returns the credit quantity of the posting entry. entryCredit :: KnownNat s => Entry o s -> Maybe (UnsignedQuantity s) -- | This module provides high-level definitions of haspara -- library. -- -- haspara provides rudimentary (and experimental) accounting -- functionality, too. These definitions can be found under -- Haspara.Accounting module. module Haspara -- | This module provides template-haskell functions for various -- Haspara definitions. module Haspara.TH -- | Constructs a Quantity value at compile-time using -- -XTemplateHaskell. -- --
--   >>> $$(quantityTH 0.00) :: Quantity 2
--   0.00
--   
--   >>> $$(quantityTH 0.09) :: Quantity 2
--   0.09
--   
--   >>> $$(quantityTH 0.009) :: Quantity 2
--   ...
--   ..."Underflow while trying to create quantity: 9.0e-3"
--   ...
--   
--   >>> $$(quantityTH 0.009) :: Quantity 3
--   0.009
--   
quantityTH :: KnownNat s => Scientific -> Q (TExp (Quantity s)) -- | Constructs a Currency value at compile-time using -- -XTemplateHaskell. -- --
--   >>> $$(currencyTH "USD")
--   USD
--   
--   >>> $$(currencyTH "usd")
--   ...
--   ...Currency code error! Expecting at least 3 uppercase ASCII letters, but received: usd
--   ...
--   
currencyTH :: Text -> Q (TExp Currency) -- | Constructs a CurrencyPair value at compile-time using -- -XTemplateHaskell. -- --
--   >>> $$(currencyPairTH "EUR" "USD")
--   EUR/USD
--   
--   >>> $$(currencyPairTH "USD" "USD")
--   USD/USD
--   
--   >>> $$(currencyPairTH "USD" "eur")
--   ...
--   ...Currency code error! Expecting at least 3 uppercase ASCII letters, but received: eur
--   ...
--   
currencyPairTH :: Text -> Text -> Q (TExp CurrencyPair)