{-# 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
(Amount precision -> Amount precision -> Bool)
-> (Amount precision -> Amount precision -> Bool)
-> Eq (Amount precision)
forall (precision :: Nat).
Amount precision -> Amount precision -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$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
Eq, (forall x. Amount precision -> Rep (Amount precision) x)
-> (forall x. Rep (Amount precision) x -> Amount precision)
-> Generic (Amount precision)
forall (precision :: Nat) x.
Rep (Amount precision) x -> Amount precision
forall (precision :: Nat) x.
Amount precision -> Rep (Amount precision) x
forall x. Rep (Amount precision) x -> Amount precision
forall x. Amount precision -> Rep (Amount precision) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall (precision :: Nat) x.
Amount precision -> Rep (Amount precision) x
from :: forall x. Amount precision -> Rep (Amount precision) x
$cto :: forall (precision :: Nat) x.
Rep (Amount precision) x -> Amount precision
to :: forall x. Rep (Amount precision) x -> Amount precision
Generic, Eq (Amount precision)
Eq (Amount precision) =>
(Amount precision -> Amount precision -> Ordering)
-> (Amount precision -> Amount precision -> Bool)
-> (Amount precision -> Amount precision -> Bool)
-> (Amount precision -> Amount precision -> Bool)
-> (Amount precision -> Amount precision -> Bool)
-> (Amount precision -> Amount precision -> Amount precision)
-> (Amount precision -> Amount precision -> Amount precision)
-> Ord (Amount precision)
Amount precision -> Amount precision -> Bool
Amount precision -> Amount precision -> Ordering
Amount precision -> Amount precision -> Amount precision
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
$ccompare :: forall (precision :: Nat).
Amount precision -> Amount precision -> Ordering
compare :: Amount precision -> Amount precision -> Ordering
$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
>= :: Amount precision -> Amount precision -> Bool
$cmax :: forall (precision :: Nat).
Amount precision -> Amount precision -> Amount precision
max :: Amount precision -> Amount precision -> Amount precision
$cmin :: forall (precision :: Nat).
Amount precision -> Amount precision -> Amount precision
min :: Amount precision -> Amount precision -> Amount precision
Ord, Int -> Amount precision -> ShowS
[Amount precision] -> ShowS
Amount precision -> String
(Int -> Amount precision -> ShowS)
-> (Amount precision -> String)
-> ([Amount precision] -> ShowS)
-> Show (Amount precision)
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
$cshowsPrec :: forall (precision :: Nat).
KnownNat precision =>
Int -> Amount precision -> ShowS
showsPrec :: Int -> Amount precision -> ShowS
$cshow :: forall (precision :: Nat).
KnownNat precision =>
Amount precision -> String
show :: Amount precision -> String
$cshowList :: forall (precision :: Nat).
KnownNat precision =>
[Amount precision] -> ShowS
showList :: [Amount precision] -> ShowS
Show)
instance KnownNat precision => Aeson.FromJSON (Amount precision) where
parseJSON :: Value -> Parser (Amount precision)
parseJSON = Options -> Value -> Parser (Amount precision)
forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
Aeson.genericParseJSON (Options -> Value -> Parser (Amount precision))
-> Options -> Value -> Parser (Amount precision)
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 = Options -> Amount precision -> Value
forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
Aeson.genericToJSON (Options -> Amount precision -> Value)
-> Options -> Amount precision -> Value
forall a b. (a -> b) -> a -> b
$ String -> Options
commonAesonOptions String
"amount"
toEncoding :: Amount precision -> Encoding
toEncoding = Options -> Amount precision -> Encoding
forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
Aeson.genericToEncoding (Options -> Amount precision -> Encoding)
-> Options -> Amount precision -> Encoding
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) = UnsignedQuantity precision -> Maybe (UnsignedQuantity precision)
forall a. a -> Maybe a
Just UnsignedQuantity precision
value
amountDebit Amount precision
_ = Maybe (UnsignedQuantity 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) = UnsignedQuantity precision -> Maybe (UnsignedQuantity precision)
forall a. a -> Maybe a
Just UnsignedQuantity precision
value
amountCredit Amount precision
_ = Maybe (UnsignedQuantity 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 Quantity precision -> Quantity precision -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideDebit else Side
SideCredit, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindLiability -> Amount {amountSide :: Side
amountSide = if Quantity precision
q Quantity precision -> Quantity precision -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideDebit else Side
SideCredit, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindEquity -> Amount {amountSide :: Side
amountSide = if Quantity precision
q Quantity precision -> Quantity precision -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindRevenue -> Amount {amountSide :: Side
amountSide = if Quantity precision
q Quantity precision -> Quantity precision -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
q}
AccountKind
AccountKindExpense -> Amount {amountSide :: Side
amountSide = if Quantity precision
q Quantity precision -> Quantity precision -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity precision
0 then Side
SideCredit else Side
SideDebit, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
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, UnsignedQuantity precision -> Quantity precision
forall {k} (p :: k) 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 = AccountKind -> Quantity precision -> Side
forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Quantity precision -> Side
sideByAccountKind AccountKind
k Quantity precision
q
, amountValue :: UnsignedQuantity precision
amountValue = Quantity precision -> UnsignedQuantity precision
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 = UnsignedQuantity precision -> Quantity precision
forall {k} (p :: k) 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