packages feed

exchangealgebra-0.6.0.0: src/ExchangeAlgebra/Accounting/Account.hs

{-# LANGUAGE StrictData #-}
{-# LANGUAGE Strict #-}

-- | Account title metadata and direction helpers for exchange bases (Definition 7).
module ExchangeAlgebra.Accounting.Account
    ( AccountTitles(..)
    , AccountDivision(..), Side(..), ClosingRule(..), FixedCurrent(..)
    , AccountRole(..), PostingCapability(..), DivisionSemantics(..)
    , HomeSideSemantics(..), ReportingEligibility(..)
    , AccountSpec(..), AccountSemantics(..), accountAliases, accountSpec
    , accountSemantics, accountSpecMap, concreteAccountTitles
    , classifyAccountContra, accountDescriptions, jcciAliases
    , ProcessingContext(..), postingAllowedIn, postingCapabilityFor
    , PIMO(..), switchSide, defaultSide, classifyAccountDivision
    , pimoFromDivision, pimoFlip
    ) where

import ExchangeAlgebra.Algebra.Base.Representation (customError)
import ExchangeAlgebra.Accounting.Account.Title (AccountTitles(..))
import ExchangeAlgebra.Accounting.Account.Classification
import ExchangeAlgebra.Accounting.Account.Registry
import ExchangeAlgebra.Accounting.Account.Aliases (jcciAliases)
import ExchangeAlgebra.Accounting.Account.PostingPolicy
import GHC.Stack (HasCallStack)
import Control.DeepSeq (NFData(..))

-- | Reverse the credit/debit side. Swaps Credit and Debit.
-- The wildcard Side is returned unchanged.
--
-- Complexity: O(1)
{-# INLINE switchSide #-}
switchSide :: Side -> Side
switchSide Credit = Debit
switchSide Debit  = Credit
switchSide Side   = Side

-- | Default (home) side of an account division before any contra reversal:
-- Assets\/Cost are debit-normal, Liability\/Equity\/Revenue are credit-normal.
-- The actual home side of a base is this, reversed when
-- 'ExchangeAlgebra.Accounting.Exchange.isContra' holds
-- (contract: @isContra b == (homeSide of b \/= defaultSide (whatDiv b))@).
--
-- Complexity: O(1)
{-# INLINE defaultSide #-}
defaultSide :: AccountDivision -> Side
defaultSide Assets    = Debit
defaultSide Cost      = Debit
defaultSide Liability = Credit
defaultSide Equity    = Credit
defaultSide Revenue   = Credit

-- | Classify an account title into an account division (Assets/Equity/Liability/Cost/Revenue).
--
-- Complexity: O(1)
{-# INLINE classifyAccountDivision #-}
classifyAccountDivision :: HasCallStack => AccountTitles -> AccountDivision
classifyAccountDivision AccountTitle = customError "this is wildcard AccountTitle"
classifyAccountDivision title =
    case accountSpec title of
        Just spec -> asDivision spec
        Nothing   -> customError "this is wildcard AccountTitle"

-- | PIMO direction. In Proposition 5.3.8 (Deguchi 2004, pp.89-91) PS, IN,
-- MS and OUT mean __plus stock, input, minus stock and output__ —
-- directions of exchange, not statement labels. The allowed exchange pairs
-- are exactly PS ⇔ IN, PS ⇔ MS, OUT ⇔ IN, OUT ⇔ MS (the
-- t'ExchangeAlgebra.Accounting.Exchange.AccountBase'
-- instance below). The earlier Haddock glossed these as "Product Stock \/
-- Income \/ Money Stock \/ Outflow"; that was naming drift from the
-- original and is kept only as a mnemonic.
data PIMO   = PS  -- ^ plus stock (stock increase; non-contra Assets)
            | IN  -- ^ input (flow in; Revenue)
            | MS  -- ^ minus stock (stock decrease; Liability\/Equity and contra assets)
            | OUT -- ^ output (flow out; Cost)
            deriving (Ord, Show, Eq)

instance NFData PIMO where
    rnf value = value `seq` ()

-- | The division-to-PIMO map of the standard interpretation (the @g@ of
-- Proposition 5.3.8 restricted to non-contra accounts): Assets are plus
-- stock, Liability\/Equity are minus stock, Cost is output, Revenue is
-- input. Contra accounts flip this via 'pimoFlip' (see
-- 'ExchangeAlgebra.Accounting.Exchange.whatPIMO').
--
-- Complexity: O(1)
{-# INLINE pimoFromDivision #-}
pimoFromDivision :: AccountDivision -> PIMO
pimoFromDivision Assets    = PS
pimoFromDivision Equity    = MS
pimoFromDivision Liability = MS
pimoFromDivision Cost      = OUT
pimoFromDivision Revenue   = IN

-- | Direction flip used for contra accounts: PS ↔ MS, IN ↔ OUT.
-- Self-inverse, and it preserves the exchange relation:
-- @x \<=\> y@ implies @pimoFlip x \<=\> pimoFlip y@.
--
-- Complexity: O(1)
{-# INLINE pimoFlip #-}
pimoFlip :: PIMO -> PIMO
pimoFlip PS  = MS
pimoFlip MS  = PS
pimoFlip IN  = OUT
pimoFlip OUT = IN