{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE KindSignatures #-}
module Haspara.Accounting.Balance where
import qualified Data.Aeson as Aeson
import qualified Data.Foldable as Foldable
import Data.Time (Day)
import GHC.Generics (Generic)
import GHC.TypeLits (KnownNat, Nat)
import Haspara.Accounting.Account (AccountKind (AccountKindAsset))
import Haspara.Accounting.Amount (Amount (Amount), quantityFromAmount, valueFromAmount)
import Haspara.Accounting.Inventory (Inventory, InventoryHistoryItem, updateInventoryVV)
import Haspara.Accounting.Side (Side (..), otherSide)
import Haspara.Internal.Aeson (commonAesonOptions)
import Haspara.Quantity (Quantity, absQuantity)
import Refined (unrefine)
data Balance (precision :: Nat) = Balance
{ forall (precision :: Nat). Balance precision -> Side
balanceSide :: !Side
, forall (precision :: Nat). Balance precision -> Quantity precision
balanceValue :: !(Quantity precision)
, forall (precision :: Nat).
Balance precision -> Inventory 8 12 precision
balanceInventory :: !(Inventory 8 12 precision)
}
deriving (Balance precision -> Balance precision -> Bool
forall (precision :: Nat).
Balance precision -> Balance precision -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Balance precision -> Balance precision -> Bool
$c/= :: forall (precision :: Nat).
Balance precision -> Balance precision -> Bool
== :: Balance precision -> Balance precision -> Bool
$c== :: forall (precision :: Nat).
Balance precision -> Balance precision -> Bool
Eq, forall (precision :: Nat) x.
Rep (Balance precision) x -> Balance precision
forall (precision :: Nat) x.
Balance precision -> Rep (Balance precision) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall (precision :: Nat) x.
Rep (Balance precision) x -> Balance precision
$cfrom :: forall (precision :: Nat) x.
Balance precision -> Rep (Balance precision) x
Generic, Int -> Balance precision -> ShowS
forall (precision :: Nat).
KnownNat precision =>
Int -> Balance precision -> ShowS
forall (precision :: Nat).
KnownNat precision =>
[Balance precision] -> ShowS
forall (precision :: Nat).
KnownNat precision =>
Balance precision -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Balance precision] -> ShowS
$cshowList :: forall (precision :: Nat).
KnownNat precision =>
[Balance precision] -> ShowS
show :: Balance precision -> String
$cshow :: forall (precision :: Nat).
KnownNat precision =>
Balance precision -> String
showsPrec :: Int -> Balance precision -> ShowS
$cshowsPrec :: forall (precision :: Nat).
KnownNat precision =>
Int -> Balance precision -> ShowS
Show)
instance KnownNat precision => Aeson.FromJSON (Balance precision) where
parseJSON :: Value -> Parser (Balance 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
"balance"
instance KnownNat precision => Aeson.ToJSON (Balance precision) where
toJSON :: Balance 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
"balance"
toEncoding :: Balance 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
"balance"
balanceDebit
:: KnownNat precision
=> Balance precision
-> Maybe (Quantity precision)
balanceDebit :: forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Maybe (Quantity precision)
balanceDebit (Balance Side
SideDebit Quantity precision
v Inventory 8 12 precision
_) = forall a. a -> Maybe a
Just Quantity precision
v
balanceDebit Balance precision
_ = forall a. Maybe a
Nothing
balanceCredit
:: KnownNat precision
=> Balance precision
-> Maybe (Quantity precision)
balanceCredit :: forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Maybe (Quantity precision)
balanceCredit (Balance Side
SideCredit Quantity precision
v Inventory 8 12 precision
_) = forall a. a -> Maybe a
Just Quantity precision
v
balanceCredit Balance precision
_ = forall a. Maybe a
Nothing
updateBalance
:: KnownNat precision
=> Balance precision
-> Amount precision
-> Balance precision
updateBalance :: forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Amount precision -> Balance precision
updateBalance (Balance Side
bSide Quantity precision
bVal Inventory 8 12 precision
inventory) (Amount Side
aSide UnsignedQuantity precision
aVal) =
forall (precision :: Nat).
Side
-> Quantity precision
-> Inventory 8 12 precision
-> Balance precision
Balance Side
bSide (Quantity precision
bVal forall a. Num a => a -> a -> a
+ (forall p x. Refined p x -> x
unrefine UnsignedQuantity precision
aVal forall a. Num a => a -> a -> a
* (if Side
bSide forall a. Eq a => a -> a -> Bool
== Side
aSide then Quantity precision
1 else (-Quantity precision
1)))) Inventory 8 12 precision
inventory
updateBalanceWithInventory
:: KnownNat precision
=> Day
-> Balance precision
-> Amount precision
-> Quantity 12
-> ([InventoryHistoryItem 8 12 precision], Balance precision)
updateBalanceWithInventory :: forall (precision :: Nat).
KnownNat precision =>
Day
-> Balance precision
-> Amount precision
-> Quantity 12
-> ([InventoryHistoryItem 8 12 precision], Balance precision)
updateBalanceWithInventory Day
date Balance precision
balance Amount precision
amount Quantity 12
quantity =
let nb :: Balance precision
nb = forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Amount precision -> Balance precision
updateBalance Balance precision
balance Amount precision
amount
vv :: Quantity precision
vv = forall a. Num a => a -> a
abs (forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Amount precision -> Quantity precision
valueFromAmount AccountKind
AccountKindAsset Amount precision
amount)
(Seq (InventoryHistoryItem 8 12 precision)
is, Inventory 8 12 precision
ni) = forall (pprec :: Nat) (sprec :: Nat) (vprec :: Nat).
(KnownNat pprec, KnownNat sprec, KnownNat vprec) =>
Day
-> Quantity vprec
-> Quantity sprec
-> Inventory pprec sprec vprec
-> (Seq (InventoryHistoryItem pprec sprec vprec),
Inventory pprec sprec vprec)
updateInventoryVV Day
date Quantity precision
vv Quantity 12
quantity (forall (precision :: Nat).
Balance precision -> Inventory 8 12 precision
balanceInventory Balance precision
nb)
in (forall (t :: * -> *) a. Foldable t => t a -> [a]
Foldable.toList Seq (InventoryHistoryItem 8 12 precision)
is, Balance precision
nb {balanceInventory :: Inventory 8 12 precision
balanceInventory = Inventory 8 12 precision
ni})
amountFromBalance
:: KnownNat precision
=> Balance precision
-> Amount precision
amountFromBalance :: forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Amount precision
amountFromBalance (Balance Side
side Quantity precision
value Inventory 8 12 precision
_) =
forall (precision :: Nat).
Side -> UnsignedQuantity precision -> Amount precision
Amount (if Quantity precision
value forall a. Ord a => a -> a -> Bool
< Quantity precision
0 then Side -> Side
otherSide Side
side else Side
side) (forall (s :: Nat). KnownNat s => Quantity s -> UnsignedQuantity s
absQuantity Quantity precision
value)
quantityFromBalance
:: KnownNat precision
=> AccountKind
-> Balance precision
-> Quantity precision
quantityFromBalance :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Balance precision -> Quantity precision
quantityFromBalance AccountKind
k = forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Amount precision -> Quantity precision
quantityFromAmount AccountKind
k forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Amount precision
amountFromBalance
valueFromBalance
:: KnownNat precision
=> AccountKind
-> Balance precision
-> Quantity precision
valueFromBalance :: forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Balance precision -> Quantity precision
valueFromBalance AccountKind
k = forall (precision :: Nat).
KnownNat precision =>
AccountKind -> Amount precision -> Quantity precision
valueFromAmount AccountKind
k forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (precision :: Nat).
KnownNat precision =>
Balance precision -> Amount precision
amountFromBalance