{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE KindSignatures #-}
module Haspara.Accounting.Amount where
import qualified Data.Aeson as Aeson
import GHC.Generics (Generic)
import GHC.TypeLits (KnownNat, Nat)
import Haspara.Accounting.Account (AccountKind (..))
import Haspara.Accounting.Side (Side (..), sideByAccountKind)
import Haspara.Internal.Aeson (commonAesonOptions)
import Haspara.Quantity (Quantity, UnsignedQuantity, absQuantity)
import Refined (unrefine)
data Amount (precision :: Nat) = Amount
{ forall (precision :: Nat). Amount precision -> Side
amountSide :: !Side
, forall (precision :: Nat).
Amount precision -> UnsignedQuantity precision
amountValue :: !(UnsignedQuantity precision)
}
deriving (Amount precision -> Amount precision -> Bool
forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Amount precision -> Amount precision -> Bool
$c/= :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
== :: Amount precision -> Amount precision -> Bool
$c== :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
Eq, forall (precision :: Nat) x.
Rep (Amount precision) x -> Amount precision
forall (precision :: Nat) x.
Amount precision -> Rep (Amount precision) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall (precision :: Nat) x.
Rep (Amount precision) x -> Amount precision
$cfrom :: forall (precision :: Nat) x.
Amount precision -> Rep (Amount precision) x
Generic, Amount precision -> Amount precision -> Bool
Amount precision -> Amount precision -> Ordering
forall (precision :: Nat). Eq (Amount precision)
forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
forall (precision :: Nat).
Amount precision -> Amount precision -> Ordering
forall (precision :: Nat).
Amount precision -> Amount precision -> Amount precision
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Amount precision -> Amount precision -> Amount precision
$cmin :: forall (precision :: Nat).
Amount precision -> Amount precision -> Amount precision
max :: Amount precision -> Amount precision -> Amount precision
$cmax :: forall (precision :: Nat).
Amount precision -> Amount precision -> Amount precision
>= :: Amount precision -> Amount precision -> Bool
$c>= :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
> :: Amount precision -> Amount precision -> Bool
$c> :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
<= :: Amount precision -> Amount precision -> Bool
$c<= :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
< :: Amount precision -> Amount precision -> Bool
$c< :: forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
compare :: Amount precision -> Amount precision -> Ordering
$ccompare :: forall (precision :: Nat).
Amount precision -> Amount precision -> Ordering
Ord, Int -> Amount precision -> ShowS
forall (precision :: Nat).
KnownNat precision =>
Int -> Amount precision -> ShowS
forall (precision :: Nat).
KnownNat precision =>
[Amount precision] -> ShowS
forall (precision :: Nat).
KnownNat precision =>
Amount precision -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Amount precision] -> ShowS
$cshowList :: forall (precision :: Nat).
KnownNat precision =>
[Amount precision] -> ShowS
show :: Amount precision -> String
$cshow :: forall (precision :: Nat).
KnownNat precision =>
Amount precision -> String
showsPrec :: Int -> Amount precision -> ShowS
$cshowsPrec :: forall (precision :: Nat).
KnownNat precision =>
Int -> Amount precision -> ShowS
Show)
instance KnownNat precision => Aeson.FromJSON (Amount precision) where
parseJSON :: Value -> Parser (Amount precision)
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
Aeson.genericParseJSON forall a b. (a -> b) -> a -> b
$ String -> Options
commonAesonOptions String
"amount"
instance KnownNat precision => Aeson.ToJSON (Amount precision) where
toJSON :: Amount precision -> Value
toJSON = forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
Aeson.genericToJSON forall a b. (a -> b) -> a -> b
$ String -> Options
commonAesonOptions String
"amount"
toEncoding :: Amount precision -> Encoding
toEncoding = forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
Aeson.genericToEncoding forall a b. (a -> b) -> a -> b
$ String -> Options
commonAesonOptions String
"amount"
amountDebit :: KnownNat precision => Amount precision -> Maybe (UnsignedQuantity precision)
amountDebit :: forall (precision :: Nat).
KnownNat precision =>
Amount precision -> Maybe (UnsignedQuantity precision)
amountDebit (Amount Side
SideDebit UnsignedQuantity precision
value) = forall a. a -> Maybe a
Just UnsignedQuantity precision
value
amountDebit Amount precision
_ = forall a. Maybe a
Nothing
amountCredit :: KnownNat precision => Amount precision -> Maybe (UnsignedQuantity precision)
amountCredit :: forall (precision :: Nat).
KnownNat precision =>
Amount precision -> Maybe (UnsignedQuantity precision)
amountCredit (Amount Side
SideCredit UnsignedQuantity precision
value) = forall a. a -> Maybe a
Just UnsignedQuantity precision
value
amountCredit Amount precision
_ = forall a. Maybe a
Nothing
amountFromValue
:: KnownNat precision
=> AccountKind
-> Quantity precision
-> Amount precision
amountFromValue :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Quantity precision -> Amount precision
amountFromValue AccountKind
k Quantity precision
q = case AccountKind
k of
AccountKind
AccountKindAsset -> Amount {amountSide :: Side
amountSide = if Quantity precision
q forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideDebit else Side
SideCredit, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindLiability -> Amount {amountSide :: Side
amountSide = if Quantity precision
q forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideDebit else Side
SideCredit, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindEquity -> Amount {amountSide :: Side
amountSide = if Quantity precision
q forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindRevenue -> Amount {amountSide :: Side
amountSide = if Quantity precision
q forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindExpense -> Amount {amountSide :: Side
amountSide = if Quantity precision
q forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
valueFromAmount
:: KnownNat precision
=> AccountKind
-> Amount precision
-> Quantity precision
valueFromAmount :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Amount precision -> Quantity precision
valueFromAmount AccountKind
k (Amount Side
s UnsignedQuantity precision
v) = case (AccountKind
k, Side
s, forall p x. Refined p x -> x
unrefine UnsignedQuantity precision
v) of
(AccountKind
AccountKindAsset, Side
SideDebit, Quantity precision
q) -> Quantity precision
q
(AccountKind
AccountKindAsset, Side
SideCredit, Quantity precision
q) -> -Quantity precision
q
(AccountKind
AccountKindLiability, Side
SideDebit, Quantity precision
q) -> Quantity precision
q
(AccountKind
AccountKindLiability, Side
SideCredit, Quantity precision
q) -> -Quantity precision
q
(AccountKind
AccountKindEquity, Side
SideDebit, Quantity precision
q) -> -Quantity precision
q
(AccountKind
AccountKindEquity, Side
SideCredit, Quantity precision
q) -> Quantity precision
q
(AccountKind
AccountKindRevenue, Side
SideDebit, Quantity precision
q) -> -Quantity precision
q
(AccountKind
AccountKindRevenue, Side
SideCredit, Quantity precision
q) -> Quantity precision
q
(AccountKind
AccountKindExpense, Side
SideDebit, Quantity precision
q) -> -Quantity precision
q
(AccountKind
AccountKindExpense, Side
SideCredit, Quantity precision
q) -> Quantity precision
q
amountFromQuantity
:: KnownNat precision
=> AccountKind
-> Quantity precision
-> Amount precision
amountFromQuantity :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Quantity precision -> Amount precision
amountFromQuantity AccountKind
k Quantity precision
q =
Amount
{ amountSide :: Side
amountSide = forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Quantity precision -> Side
sideByAccountKind AccountKind
k Quantity precision
q
, amountValue :: UnsignedQuantity precision
amountValue = forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q
}
quantityFromAmount
:: KnownNat precision
=> AccountKind
-> Amount precision
-> Quantity precision
quantityFromAmount :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Amount precision -> Quantity precision
quantityFromAmount AccountKind
k (Amount Side
side UnsignedQuantity precision
absValue) =
let value :: Quantity precision
value = forall p x. Refined p x -> x
unrefine UnsignedQuantity precision
absValue
in case (AccountKind
k, Side
side) of
(AccountKind
AccountKindAsset, Side
SideDebit) -> Quantity precision
value
(AccountKind
AccountKindAsset, Side
SideCredit) -> -Quantity precision
value
(AccountKind
AccountKindLiability, Side
SideDebit) -> -Quantity precision
value
(AccountKind
AccountKindLiability, Side
SideCredit) -> Quantity precision
value
(AccountKind
AccountKindEquity, Side
SideDebit) -> -Quantity precision
value
(AccountKind
AccountKindEquity, Side
SideCredit) -> Quantity precision
value
(AccountKind
AccountKindRevenue, Side
SideDebit) -> -Quantity precision
value
(AccountKind
AccountKindRevenue, Side
SideCredit) -> Quantity precision
value
(AccountKind
AccountKindExpense, Side
SideDebit) -> Quantity precision
value
(AccountKind
AccountKindExpense, Side
SideCredit) -> -Quantity precision
value