-- 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.8 -- | This module provides helper definitions for Data.Aeson. module Haspara.Internal.Aeson -- | Common Aeson encoding/decoding options. commonAesonOptions :: String -> Options -- | Aeson encoding/decoding options for uppercase constructor tag -- modifiers aesonOptionsForSingleTag :: String -> Options -- | 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: -- --
-- >>> :set -XOverloadedStrings -- -- >>> 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. -- --
-- >>> :set -XOverloadedStrings -- -- >>> 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. -- --
-- >>> :set -XOverloadedStrings -- -- >>> 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. -- --
-- >>> :set -XOverloadedStrings
--
-- >>> 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:
--
-- -- >>> :set -XOverloadedStrings -- -- >>> 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.
--
-- -- >>> :set -XOverloadedStrings -- -- >>> toCurrencyTuple (CurrencyPair "EUR" "USD") -- (EUR,USD) --toCurrencyTuple :: CurrencyPair -> (Currency, Currency) -- | Converts a 2-tuple of Currency values to a CurrencyPair. -- --
-- >>> :set -XOverloadedStrings
--
-- >>> 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 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 Data.Aeson.Types.FromJSON.FromJSON Haspara.Currency.CurrencyPair
instance Data.Aeson.Types.ToJSON.ToJSON 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 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:
--
-- -- >>> :set -XTypeApplications -- -- >>> 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. -- --
-- >>> :set -XTypeApplications
--
-- >>> 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 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 GHC.Enum.Bounded Haspara.Accounting.Account.AccountKind
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.Aeson.Types.FromJSON.FromJSON o => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Account.Account o)
instance Data.Aeson.Types.ToJSON.ToJSON o => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Account.Account o)
instance Data.Hashable.Class.Hashable Haspara.Accounting.Account.AccountKind
instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Accounting.Account.AccountKind
instance Data.Aeson.Types.ToJSON.ToJSON 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) -- | Divides two quantities with same scales with possible loss. -- --
-- >>> divide (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.33 -- -- >>> divide (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0 :: Quantity 2) -- Nothing -- -- >>> divide (mkQuantity 0.42 :: Quantity 2) (mkQuantity 1 :: Quantity 2) -- Just 0.42 -- -- >>> divide (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.42 :: Quantity 2) -- Just 1.00 -- -- >>> divide (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.21 :: Quantity 2) -- Just 2.00 -- -- >>> divide (mkQuantity 0.42 :: Quantity 2) (mkQuantity (-0.21) :: Quantity 2) -- Just -2.00 --divide :: KnownNat s => Quantity s -> Quantity s -> Maybe (Quantity s) -- | Divides two quantities with different scales with possible loss -- preserving dividend's precision. -- --
-- >>> divideL (mkQuantity 10 :: Quantity 1) (mkQuantity 3 :: Quantity 2) -- Just 3.3 -- -- >>> divideL (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.33 -- -- >>> divideL (mkQuantity 10 :: Quantity 3) (mkQuantity 3 :: Quantity 2) -- Just 3.333 --divideL :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Maybe (Quantity s) -- | Divides two quantities with different scales with possible loss -- preserving divisor's precision. -- --
-- >>> divideR (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 1) -- Just 3.3 -- -- >>> divideR (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.33 -- -- >>> divideR (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 3) -- Just 3.333 --divideR :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Maybe (Quantity k) -- | Divides two quantities with different scales with possible loss with a -- target precision of result. -- --
-- >>> :set -XTypeApplications -- -- >>> divideD @0 (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3 -- -- >>> divideD @1 (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.3 -- -- >>> divideD @2 (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.33 -- -- >>> divideD @3 (mkQuantity 10 :: Quantity 2) (mkQuantity 3 :: Quantity 2) -- Just 3.333 -- -- >>> divideD @8 (mkQuantity 1111 :: Quantity 2) (mkQuantity 3333 :: Quantity 12) -- Just 0.33333333 --divideD :: (KnownNat r, KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Maybe (Quantity r) -- | Returns the total of a list of unsigned quantities. -- --
-- >>> sumUnsignedQuantity [] :: UnsignedQuantity 2 -- Refined 0.00 --sumUnsignedQuantity :: KnownNat s => [UnsignedQuantity s] -> UnsignedQuantity s -- | Returns the absolute value of the Quantity as -- UnsignedQuantity. -- --
-- >>> abs (mkQuantity 0.42 :: Quantity 2) -- 0.42 -- -- >>> abs (mkQuantity 0 :: Quantity 2) -- 0.00 -- -- >>> abs (mkQuantity (-0.42) :: Quantity 2) -- 0.42 --absQuantity :: KnownNat s => Quantity s -> UnsignedQuantity s -- | 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.Num.Integer.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: -- --
-- >>> :set -XTypeApplications
--
-- >>> mkFxQuoteError @(Either _) @2 "EUR" "USD" (read "2021-12-31") 1.16
-- Right (MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16})
--
-- >>> mkFxQuoteError @(Either _) @2 "EUR" "USD" (read "2021-12-31") (-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 => Currency -> Currency -> Day -> 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.
--
--
-- >>> :set -XTypeApplications
--
-- >>> mkFxQuoteFail @Maybe @2 "EUR" "USD" (read "2021-12-31") 1.16
-- Just (MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16})
--
-- >>> mkFxQuoteFail @Maybe @2 "EUR" "USD" (read "2021-12-31") (-1.16)
-- Nothing
--
mkFxQuoteFail :: MonadFail m => KnownNat s => Currency -> Currency -> Day -> Scientific -> m (FxQuote s)
-- | Unsafe FxQuote constructor that errors if it fails.
--
--
-- >>> :set -XTypeApplications
--
-- >>> mkFxQuoteUnsafe @2 "EUR" "USD" (read "2021-12-31") 1.16
-- MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-12-31, fxQuoteRate = Refined 1.16}
--
-- >>> mkFxQuoteUnsafe @2 "EUR" "USD" (read "2021-12-31") (-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 => Currency -> Currency -> Day -> 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)
-- | Returns empty FX rate quotation database.
--
-- -- >>> :set -XTypeApplications -- -- >>> emptyFxQuoteDatabase @8 -- fromList [] --emptyFxQuoteDatabase :: KnownNat n => FxQuoteDatabase n -- | Adds a list of FX rate quotations to the given database. -- --
-- >>> :set -XTypeApplications
--
-- >>> let database = emptyFxQuoteDatabase @8
--
-- >>> addFxQuotes [] database
-- fromList []
--
-- >>> addFxQuotes [mkFxQuoteUnsafe @8 "EUR" "USD" (read "2021-01-31") 1.13] database
-- fromList [(EUR/USD,FxQuotePairDatabase {fxQuotePairDatabasePair = EUR/USD, fxQuotePairDatabaseTable = fromList [(2021-01-31,MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-01-31, fxQuoteRate = Refined 1.13000000})], fxQuotePairDatabaseSince = 2021-01-31, fxQuotePairDatabaseUntil = 2021-01-31})]
--
-- >>> addFxQuotes [mkFxQuoteUnsafe @8 "EUR" "USD" (read "2021-01-31") 1.13, mkFxQuoteUnsafe @8 "USD" "EUR" (read "2021-01-31") 0.884956] database
-- fromList [(EUR/USD,FxQuotePairDatabase {fxQuotePairDatabasePair = EUR/USD, fxQuotePairDatabaseTable = fromList [(2021-01-31,MkFxQuote {fxQuotePair = EUR/USD, fxQuoteDate = 2021-01-31, fxQuoteRate = Refined 1.13000000})], fxQuotePairDatabaseSince = 2021-01-31, fxQuotePairDatabaseUntil = 2021-01-31}),(USD/EUR,FxQuotePairDatabase {fxQuotePairDatabasePair = USD/EUR, fxQuotePairDatabaseTable = fromList [(2021-01-31,MkFxQuote {fxQuotePair = USD/EUR, fxQuoteDate = 2021-01-31, fxQuoteRate = Refined 0.88495600})], fxQuotePairDatabaseSince = 2021-01-31, fxQuotePairDatabaseUntil = 2021-01-31})]
--
addFxQuotes :: KnownNat n => [FxQuote n] -> FxQuoteDatabase n -> FxQuoteDatabase n
-- | Adds an FX rate quotation to the given database.
addFxQuote :: KnownNat n => FxQuote n -> FxQuoteDatabase n -> FxQuoteDatabase n
-- | Initializes FX quote pair database with the given FX quote.
initFxQuotePairDatabase :: KnownNat n => FxQuote n -> FxQuotePairDatabase n
-- | Updates an existing FX quote pair database with the given FX quote.
updateFxQuotePairDatabase :: KnownNat n => FxQuote n -> FxQuotePairDatabase n -> FxQuotePairDatabase n
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)
instance GHC.TypeNats.KnownNat n => GHC.Show.Show (Haspara.FxQuote.FxQuotePairDatabase n)
instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.FxQuote.FxQuote s)
instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (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:
--
-- -- 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: -- --
-- >>> otherSide SideDebit -- SideCredit -- -- >>> otherSide SideCredit -- SideDebit --otherSide :: Side -> Side -- | Computes the Side by the given AccountKind and the sign -- of the given Quantity. -- -- The sign of the Quantity is indeed a proxy for whether the -- event of the Quantity is an increment (+1) or -- decrement (-1) event. -- -- 0 quantities are considered to originate from an increment -- event. So far, this seems to be a safe assumption that gives us -- totality in the context of this function. -- -- Note the following mapping as a guide: -- -- TODO: table -- --
-- >>> :set -XDataKinds -- -- >>> import Haspara.Quantity -- -- >>> let decrement = mkQuantity (-0.42) :: Quantity 2 -- -- >>> let nocrement = mkQuantity 0 :: Quantity 2 -- -- >>> let increment = mkQuantity 0.42 :: Quantity 2 -- -- >>> fmap (sideByAccountKind AccountKindAsset) [decrement, nocrement, increment] -- [SideCredit,SideDebit,SideDebit] -- -- >>> fmap (sideByAccountKind AccountKindLiability) [decrement, nocrement, increment] -- [SideDebit,SideCredit,SideCredit] -- -- >>> fmap (sideByAccountKind AccountKindEquity) [decrement, nocrement, increment] -- [SideDebit,SideCredit,SideCredit] -- -- >>> fmap (sideByAccountKind AccountKindRevenue) [decrement, nocrement, increment] -- [SideDebit,SideCredit,SideCredit] -- -- >>> fmap (sideByAccountKind AccountKindExpense) [decrement, nocrement, increment] -- [SideCredit,SideDebit,SideDebit] --sideByAccountKind :: KnownNat precision => AccountKind -> Quantity precision -> Side -- | Returns the "normal" side for a given AccountKind. -- -- Note the following mapping as a guide: -- -- TODO: table normalSideByAccountKind :: AccountKind -> Side instance GHC.Show.Show Haspara.Accounting.Side.Side instance GHC.Classes.Ord Haspara.Accounting.Side.Side instance GHC.Classes.Eq Haspara.Accounting.Side.Side instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Accounting.Side.Side instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Accounting.Side.Side -- | This module provides FIFO machinery for inventory accounting. module Haspara.Accounting.Inventory -- | Data definition that keeps track of inventory for an economic -- resource. -- -- This data definition is polymorphic over the precision for, -- respectively: -- --
-- >>> :set -XDataKinds
--
-- >>> import Haspara.Quantity
--
-- >>> amountFromQuantity AccountKindLiability (mkQuantity 1000 :: Quantity 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 1000.00}
--
--
-- However, if the call-site is referring to values as in the net effect
-- of the event to the net-worth of the entity, then:
--
--
-- >>> amountFromValue AccountKindLiability (mkQuantity (-1000) :: Quantity 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 1000.00}
--
--
-- For reference, given:
--
-- -- >>> let valPos = mkQuantity 42 :: Quantity 2 -- -- >>> let valNeg = mkQuantity (-42) :: Quantity 2 ---- -- ..., let's consider following events: -- -- We have an inflow and outflow of some assets, respectively: -- --
-- >>> amountFromValue AccountKindAsset valPos
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
-- >>> amountFromValue AccountKindAsset valNeg
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
--
-- We have some decrease and increase in our liabilities, respectively:
--
--
-- >>> amountFromValue AccountKindLiability valPos
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
-- >>> amountFromValue AccountKindLiability valNeg
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
--
-- We have some increase and decrease in our equity, respectively:
--
--
-- >>> amountFromValue AccountKindEquity valPos
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
-- >>> amountFromValue AccountKindEquity valNeg
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
--
-- We have some profit and loss in our PnL, respectively:
--
--
-- >>> amountFromValue AccountKindRevenue valPos
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
-- >>> amountFromValue AccountKindRevenue valNeg
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
--
-- We have some decrease and increase in our expenses, respectively:
--
--
-- >>> amountFromValue AccountKindExpense valPos
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
-- >>> amountFromValue AccountKindExpense valNeg
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
amountFromValue :: KnownNat precision => AccountKind -> Quantity precision -> Amount precision
-- | Returns the value for the given Amount for the given
-- AccountKind.
--
-- This is dual to amountFromValue.
--
-- For values of positive and negative net-effect on the net-worth of the
-- entity, respectively:
--
-- -- >>> :set -XDataKinds -- -- >>> import Haspara.Quantity -- -- >>> let valPos = mkQuantity 42 :: Quantity 2 -- -- >>> let valNeg = mkQuantity (-42) :: Quantity 2 ---- -- ..., for a check function that checks if the roundtrip to a -- value is successful for a given AccountKind: -- --
-- >>> let check = \k v -> v == valueFromAmount k (amountFromValue k v) ---- -- ..., and for the list of AccountKinds. -- --
-- >>> let kinds = [minBound .. maxBound] :: [AccountKind] -- -- >>> kinds -- [AccountKindAsset,AccountKindLiability,AccountKindEquity,AccountKindRevenue,AccountKindExpense] ---- -- All checks should pass: -- --
-- >>> all (\k -> check k valPos && check k valNeg) kinds -- True --valueFromAmount :: KnownNat precision => AccountKind -> Amount precision -> Quantity precision -- | Builds the Amount value for the given account kind and -- quantity. -- -- The concept of quantity here refers to the conventional concept -- of what it means for an Account of a given AccountKind. -- -- For example, a loan of USD 1,000 has an increase in our liabilities. -- Therefore, the quantity is expected to be positive: -- --
-- >>> :set -XDataKinds
--
-- >>> import Haspara.Quantity
--
-- >>> amountFromQuantity AccountKindLiability (mkQuantity 1000 :: Quantity 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 1000.00}
--
--
-- Note amountFromValue function if you are rather working with
-- values that are conceptually different than the quantity here
-- whereby a value refers to the value of a particular economic
-- event as in the contribution of that event to the net-worth of the
-- entity. Therefore, above example would be reflected as follows to get
-- the same Amount value:
--
--
-- >>> amountFromValue AccountKindLiability (mkQuantity (-1000) :: Quantity 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 1000.00}
--
--
-- Check amountFromValue documentation for further information.
amountFromQuantity :: KnownNat precision => AccountKind -> Quantity precision -> Amount precision
-- | Returns the quantity for the given amount.
--
-- This is dual to amountFromQuantity.
quantityFromAmount :: KnownNat precision => AccountKind -> Amount precision -> Quantity precision
instance GHC.TypeNats.KnownNat precision => GHC.Show.Show (Haspara.Accounting.Amount.Amount precision)
instance GHC.Classes.Ord (Haspara.Accounting.Amount.Amount precision)
instance GHC.Generics.Generic (Haspara.Accounting.Amount.Amount precision)
instance GHC.Classes.Eq (Haspara.Accounting.Amount.Amount precision)
instance GHC.TypeNats.KnownNat precision => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Amount.Amount precision)
instance GHC.TypeNats.KnownNat precision => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Amount.Amount precision)
-- | This module provides data definitions and functions to work with
-- journal entries.
module Haspara.Accounting.Journal
-- | Data definition for the journal entries of interest (like a general
-- ledger.)
--
-- A Journal is a list of JournalEntry records which are
-- polymorphic over the precision of the monetary quantities, the account
-- and event objects.
newtype Journal (precision :: Nat) account event
Journal :: [JournalEntry precision account event] -> Journal (precision :: Nat) account event
[journalEntries] :: Journal (precision :: Nat) account event -> [JournalEntry precision account event]
-- | Data definition for a journal entry.
--
-- A journal entry has a (unique) identifier, date and description, and a
-- list of JournalEntryItems. Journal entry definition is
-- polymorphic over the precision of the monetary quantities, the account
-- and event objects.
data JournalEntry (precision :: Nat) account event
JournalEntry :: !Text -> !Day -> ![JournalEntryItem precision account event] -> !Text -> JournalEntry (precision :: Nat) account event
[journalEntryId] :: JournalEntry (precision :: Nat) account event -> !Text
[journalEntryDate] :: JournalEntry (precision :: Nat) account event -> !Day
[journalEntryItems] :: JournalEntry (precision :: Nat) account event -> ![JournalEntryItem precision account event]
[journalEntryDescription] :: JournalEntry (precision :: Nat) account event -> !Text
-- | Returns the total debit amount of a journal entry.
journalEntryTotalDebit :: KnownNat precision => JournalEntry precision account event -> UnsignedQuantity precision
-- | Returns the total credit amount of a journal entry.
journalEntryTotalCredit :: KnownNat precision => JournalEntry precision account event -> UnsignedQuantity precision
-- | Predicate to check if a journal entry is balanced or not.
--
-- The logical check is indeed whether the total debit amount is equal to
-- the total credit amount or not.
isJournalEntryBalanced :: KnownNat precision => JournalEntry precision account event -> Bool
-- | Data definition for a journal entry item.
--
-- A journal entry item has a Side, an unsigned quantity as
-- amount, an account that it belongs to and the event the item is
-- originating from. Journal entry item definition is polymorphic over
-- the precision of the monetary quantities, the account and event
-- objects.
data JournalEntryItem (precision :: Nat) account event
JournalEntryItem :: !Amount precision -> !Account account -> !event -> !Maybe (JournalEntryItemInventoryEvent account event) -> JournalEntryItem (precision :: Nat) account event
[journalEntryItemAmount] :: JournalEntryItem (precision :: Nat) account event -> !Amount precision
[journalEntryItemAccount] :: JournalEntryItem (precision :: Nat) account event -> !Account account
[journalEntryItemEvent] :: JournalEntryItem (precision :: Nat) account event -> !event
[journalEntryItemInventoryEvent] :: JournalEntryItem (precision :: Nat) account event -> !Maybe (JournalEntryItemInventoryEvent account event)
-- | Data definition for inventory event.
data JournalEntryItemInventoryEvent account event
JournalEntryItemInventoryEvent :: !Account account -> !event -> !Quantity 12 -> JournalEntryItemInventoryEvent account event
[journalEntryItemInventoryEventPnlAccount] :: JournalEntryItemInventoryEvent account event -> !Account account
[journalEntryItemInventoryEventEvent] :: JournalEntryItemInventoryEvent account event -> !event
[journalEntryItemInventoryEventQuantity] :: JournalEntryItemInventoryEvent account event -> !Quantity 12
-- | Creates a JournalEntryItem from the given signed value,
-- the account it belongs to and the event it is originating from.
--
-- The value is defined as in amountFromValue function.
mkJournalEntryItemFromValue :: KnownNat precision => Quantity precision -> Account account -> event -> JournalEntryItem precision account event
-- | Creates a JournalEntryItem with inventory event informationfrom
-- the given signed value, the account it belongs to and the event
-- it is originating from.
--
-- The value is defined as in amountFromValue function.
mkInventoryJournalEntryItemFromValue :: KnownNat precision => Quantity precision -> Account account -> event -> Account account -> event -> Quantity 12 -> JournalEntryItem precision account event
instance (GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Journal.JournalEntryItemInventoryEvent account event)
instance GHC.Generics.Generic (Haspara.Accounting.Journal.JournalEntryItemInventoryEvent account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.Journal.JournalEntryItemInventoryEvent account event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Journal.JournalEntryItem precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.Journal.JournalEntryItem precision account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.Journal.JournalEntryItem precision account event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Journal.JournalEntry precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.Journal.JournalEntry precision account event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Journal.Journal precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.Journal.Journal precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Journal.Journal precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Journal.Journal precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Journal.JournalEntry precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Journal.JournalEntry precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Journal.JournalEntryItem precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Journal.JournalEntryItem precision account event)
instance (Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Journal.JournalEntryItemInventoryEvent account event)
instance (Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Journal.JournalEntryItemInventoryEvent account event)
-- | This module provides definitions for balances used as in accounting.
module Haspara.Accounting.Balance
-- | Data definition for balances.
--
-- This definition is similar to Amount, however, the value is
-- allowed to be negative to reflect "Negative Balance" phenomenon.
--
-- See
-- https://www.accountingtools.com/articles/what-is-a-negative-balance.html
data Balance (precision :: Nat)
Balance :: !Side -> !Quantity precision -> !Inventory 8 12 precision -> Balance (precision :: Nat)
[balanceSide] :: Balance (precision :: Nat) -> !Side
[balanceValue] :: Balance (precision :: Nat) -> !Quantity precision
[balanceInventory] :: Balance (precision :: Nat) -> !Inventory 8 12 precision
-- | Returns the debit quantity, if any.
balanceDebit :: KnownNat precision => Balance precision -> Maybe (Quantity precision)
-- | Returns the credit quantity, if any.
balanceCredit :: KnownNat precision => Balance precision -> Maybe (Quantity precision)
-- | Updates the balance with the given amount.
--
--
-- >>> :set -XDataKinds
--
-- >>> import Data.Default (def)
--
-- >>> import Haspara.Accounting.Amount
--
-- >>> import Haspara.Accounting.Side
--
-- >>> import Refined.Unsafe
--
-- >>> let balance = Balance SideDebit 42 def :: Balance 2
--
-- >>> balance
-- Balance {balanceSide = SideDebit, balanceValue = 42.00, balanceInventory = MkInventory {inventoryTotal = 0.000000000000, inventoryCurrent = fromList [], inventoryHistory = fromList []}}
--
-- >>> let amountDebit = Amount SideDebit (unsafeRefine 10) :: Amount 2
--
-- >>> amountDebit
-- Amount {amountSide = SideDebit, amountValue = Refined 10.00}
--
-- >>> let amountCredit = Amount SideCredit (unsafeRefine 10) :: Amount 2
--
-- >>> amountCredit
-- Amount {amountSide = SideCredit, amountValue = Refined 10.00}
--
-- >>> updateBalance balance amountDebit
-- Balance {balanceSide = SideDebit, balanceValue = 52.00, balanceInventory = MkInventory {inventoryTotal = 0.000000000000, inventoryCurrent = fromList [], inventoryHistory = fromList []}}
--
-- >>> updateBalance balance amountCredit
-- Balance {balanceSide = SideDebit, balanceValue = 32.00, balanceInventory = MkInventory {inventoryTotal = 0.000000000000, inventoryCurrent = fromList [], inventoryHistory = fromList []}}
--
updateBalance :: KnownNat precision => Balance precision -> Amount precision -> Balance precision
-- | Updates the balance with additional inventory event.
updateBalanceWithInventory :: KnownNat precision => Day -> Balance precision -> Amount precision -> Quantity 12 -> ([InventoryHistoryItem 8 12 precision], Balance precision)
-- | Converts the balance to amount.
--
--
-- >>> :set -XDataKinds
--
-- >>> import Data.Default (def)
--
-- >>> import Haspara.Accounting.Side
--
-- >>> amountFromBalance (Balance SideDebit 42 def :: Balance 2)
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
-- >>> amountFromBalance (Balance SideDebit (-42) def :: Balance 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
-- >>> amountFromBalance (Balance SideCredit 42 def :: Balance 2)
-- Amount {amountSide = SideCredit, amountValue = Refined 42.00}
--
-- >>> amountFromBalance (Balance SideCredit (-42) def :: Balance 2)
-- Amount {amountSide = SideDebit, amountValue = Refined 42.00}
--
amountFromBalance :: KnownNat precision => Balance precision -> Amount precision
-- | Returns the quantity of the balance given the account kind.
--
-- See quantityFromAmount for the meaning of quantity.
quantityFromBalance :: KnownNat precision => AccountKind -> Balance precision -> Quantity precision
-- | Returns the value of the balance given the account kind.
--
-- See valueFromAmount for the meaning of quantity.
valueFromBalance :: KnownNat precision => AccountKind -> Balance precision -> Quantity precision
instance GHC.TypeNats.KnownNat precision => GHC.Show.Show (Haspara.Accounting.Balance.Balance precision)
instance GHC.Generics.Generic (Haspara.Accounting.Balance.Balance precision)
instance GHC.Classes.Eq (Haspara.Accounting.Balance.Balance precision)
instance GHC.TypeNats.KnownNat precision => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Balance.Balance precision)
instance GHC.TypeNats.KnownNat precision => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Balance.Balance precision)
-- | This module provides data definitions and functions for ledgers and
-- postings.
module Haspara.Accounting.Ledger
-- | Data definition for a general ledger.
newtype GeneralLedger (precision :: Nat) account event
GeneralLedger :: [Ledger precision account event] -> GeneralLedger (precision :: Nat) account event
[generalLedgerLedgers] :: GeneralLedger (precision :: Nat) account event -> [Ledger precision account event]
-- | Data definition for a ledger.
data Ledger (precision :: Nat) account event
Ledger :: !Account account -> !Balance precision -> ![LedgerEntry precision event] -> Ledger (precision :: Nat) account event
[ledgerAccount] :: Ledger (precision :: Nat) account event -> !Account account
[ledgerOpening] :: Ledger (precision :: Nat) account event -> !Balance precision
[ledgerRunning] :: Ledger (precision :: Nat) account event -> ![LedgerEntry precision event]
-- | Returns the closing balance of a ledger.
ledgerClosing :: KnownNat precision => Ledger precision account event -> Balance precision
-- | Type encoding of a ledger item.
data LedgerEntry (precision :: Nat) event
LedgerEntry :: !Day -> !Amount precision -> !Text -> !event -> !Text -> !Balance precision -> LedgerEntry (precision :: Nat) event
[ledgerEntryDate] :: LedgerEntry (precision :: Nat) event -> !Day
[ledgerEntryAmount] :: LedgerEntry (precision :: Nat) event -> !Amount precision
[ledgerEntryDescription] :: LedgerEntry (precision :: Nat) event -> !Text
[ledgerEntryEvent] :: LedgerEntry (precision :: Nat) event -> !event
[ledgerEntryPostingId] :: LedgerEntry (precision :: Nat) event -> !Text
[ledgerEntryBalance] :: LedgerEntry (precision :: Nat) event -> !Balance precision
-- | Initializes an empty ledger for a given account.
initLedger :: KnownNat precision => Account account -> Ledger precision account event
-- | Initializes a ledger with the given opening balance.
initLedgerWithOpeningBalance :: KnownNat precision => Account account -> Balance precision -> Ledger precision account event
-- | Initializes a ledger with the given opening value.
--
-- See amountFromValue for the meaning of the concept of value.
initLedgerWithOpeningValue :: KnownNat precision => Account account -> (Maybe (Quantity 12), Quantity precision) -> Ledger precision account event
-- | Initializes a ledger with the given opening quantity.
--
-- See amountFromQuantity for the meaning of the concept of
-- quantity.
initLedgerWithOpeningQuantity :: KnownNat precision => Account account -> (Maybe (Quantity 12), Quantity precision) -> Ledger precision account event
-- | Posts a given list of journal entries to the given general ledger and
-- returns the new general ledger.
postEntries :: KnownNat precision => Eq account => Ord account => GeneralLedger precision account event -> [JournalEntry precision account event] -> GeneralLedger precision account event
-- | Posts a given journal entry to the given general ledger and returns
-- the new general ledger.
postEntry :: KnownNat precision => Eq account => Ord account => GeneralLedger precision account event -> JournalEntry precision account event -> GeneralLedger precision account event
-- | Posts a given journal entry item of a given journal entry to the given
-- general ledger and returns the new general ledger.
postEntryItem :: KnownNat precision => Eq account => Ord account => GeneralLedger precision account event -> JournalEntry precision account event -> JournalEntryItem precision account event -> GeneralLedger precision account event
-- | Performs a posting to the given ledger.
postItem :: KnownNat precision => Ledger precision account event -> Day -> Amount precision -> Text -> event -> Text -> Maybe (JournalEntryItemInventoryEvent account event) -> (Maybe (JournalEntry precision account event), Ledger precision account event)
-- | Creates 2 journal entry items for the captured non-zero PnL.
histItemToJournalEntryItem :: KnownNat precision => event -> Account account -> Account account -> InventoryHistoryItem 8 12 precision -> [JournalEntryItem precision account event]
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Ledger.LedgerEntry precision event)
instance GHC.Generics.Generic (Haspara.Accounting.Ledger.LedgerEntry precision event)
instance GHC.Classes.Eq event => GHC.Classes.Eq (Haspara.Accounting.Ledger.LedgerEntry precision event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Ledger.Ledger precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.Ledger.Ledger precision account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.Ledger.Ledger precision account event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.Ledger.GeneralLedger precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.Ledger.GeneralLedger precision account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.Ledger.GeneralLedger precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.GeneralLedger precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.GeneralLedger precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.Ledger precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.Ledger precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.Ledger.LedgerEntry precision event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.Ledger.LedgerEntry precision event)
-- | This module provides data definitions and functions for trial
-- balances.
module Haspara.Accounting.TrialBalance
-- | Data definition for a trial balance.
newtype TrialBalance (precision :: Nat) account event
TrialBalance :: [TrialBalanceItem precision account event] -> TrialBalance (precision :: Nat) account event
[trialBalanceItems] :: TrialBalance (precision :: Nat) account event -> [TrialBalanceItem precision account event]
-- | Data definition for a trial balance item.
data TrialBalanceItem (precision :: Nat) account event
TrialBalanceItem :: !Ledger precision account event -> !Balance precision -> TrialBalanceItem (precision :: Nat) account event
[trialBalanceItemLedger] :: TrialBalanceItem (precision :: Nat) account event -> !Ledger precision account event
[trialBalanceItemBalance] :: TrialBalanceItem (precision :: Nat) account event -> !Balance precision
-- | Returns the amount of the trial balance item. This is a simple
-- conversion from Balance to Amount.
trialBalanceItemAmount :: KnownNat precision => TrialBalanceItem precision account event -> Amount precision
-- | Given a general ledger, prepares the trial balance.
prepareTrialBalance :: KnownNat precision => GeneralLedger precision account event -> TrialBalance precision account event
-- | Converts a Ledger to a TrialBalanceItem.
mkTrialBalanceItem :: KnownNat precision => Ledger precision account event -> TrialBalanceItem precision account event
-- | Computes the trial balance totals as a 2-tuple of total debits and
-- total credits.
trialBalanceTotals :: KnownNat precision => TrialBalance precision account event -> (Balance precision, Balance precision)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.TrialBalance.TrialBalanceItem precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.TrialBalance.TrialBalanceItem precision account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.TrialBalance.TrialBalanceItem precision account event)
instance (GHC.TypeNats.KnownNat precision, GHC.Show.Show account, GHC.Show.Show event) => GHC.Show.Show (Haspara.Accounting.TrialBalance.TrialBalance precision account event)
instance GHC.Generics.Generic (Haspara.Accounting.TrialBalance.TrialBalance precision account event)
instance (GHC.Classes.Eq account, GHC.Classes.Eq event) => GHC.Classes.Eq (Haspara.Accounting.TrialBalance.TrialBalance precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.TrialBalance.TrialBalance precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.TrialBalance.TrialBalance precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.FromJSON.FromJSON account, Data.Aeson.Types.FromJSON.FromJSON event) => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Accounting.TrialBalance.TrialBalanceItem precision account event)
instance (GHC.TypeNats.KnownNat precision, Data.Aeson.Types.ToJSON.ToJSON account, Data.Aeson.Types.ToJSON.ToJSON event) => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Accounting.TrialBalance.TrialBalanceItem precision account event)
-- | This module provides a collection of definitions for a rudimentary
-- accounting functionality.
module Haspara.Accounting
-- | 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.
--
-- -- >>> :set -XDataKinds -- -- >>> :set -XOverloadedStrings -- -- >>> :set -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 -> Code Q (Quantity s) -- | Constructs a Currency value at compile-time using -- -XTemplateHaskell. -- --
-- >>> :set -XOverloadedStrings -- -- >>> :set -XTemplateHaskell -- -- >>> $$(currencyTH "USD") -- USD -- -- >>> $$(currencyTH "usd") -- ... -- ...Currency code error! Expecting at least 3 uppercase ASCII letters, but received: usd -- ... --currencyTH :: Text -> Code Q Currency -- | Constructs a CurrencyPair value at compile-time using -- -XTemplateHaskell. -- --
-- >>> :set -XOverloadedStrings -- -- >>> :set -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 -> Code Q CurrencyPair