1 {-# LANGUAGE DeriveDataTypeable #-}
 
   2 {-# LANGUAGE FlexibleInstances #-}
 
   3 {-# LANGUAGE OverloadedStrings #-}
 
   4 {-# LANGUAGE NamedFieldPuns #-}
 
   5 {-# LANGUAGE TypeFamilies #-}
 
   6 module Hcompta.Format.Ledger.Posting where
 
   8 import           Control.DeepSeq (NFData(..))
 
  10 import           Data.Data (Data(..))
 
  11 import           Data.Eq (Eq(..))
 
  12 import           Data.Function (($), (.), flip)
 
  13 import qualified Data.List as List
 
  14 import           Data.List.NonEmpty (NonEmpty(..))
 
  15 import           Data.Map.Strict (Map)
 
  16 import qualified Data.Map.Strict as Map
 
  17 import           Data.Maybe (Maybe(..))
 
  18 import           Data.Monoid (Monoid(..))
 
  19 import           Data.Text (Text)
 
  20 import           Data.Tuple (uncurry)
 
  21 import           Data.Typeable (Typeable)
 
  22 import           Prelude (seq, undefined)
 
  23 import           Text.Parsec.Pos (SourcePos, initialPos)
 
  24 import           Text.Show (Show)
 
  26 import qualified Hcompta.Balance as Balance
 
  27 import qualified Hcompta.Chart as Chart
 
  28 import           Hcompta.Date (Date)
 
  29 import qualified Hcompta.Filter as Filter
 
  30 import qualified Hcompta.GL as GL
 
  31 import           Hcompta.Lib.Parsec ()
 
  32 import qualified Hcompta.Polarize as Polarize
 
  33 import           Hcompta.Posting (Posting_Tags(..))
 
  34 import qualified Hcompta.Posting as Posting
 
  35 import qualified Hcompta.Stats as Stats
 
  36 import           Hcompta.Tag (Tags(..))
 
  38 import           Hcompta.Format.Ledger.Account
 
  39 import           Hcompta.Format.Ledger.Amount
 
  40 import           Hcompta.Format.Ledger.Chart
 
  42 -- * Type 'Posting_Type'
 
  45  =   Posting_Type_Regular
 
  46  |   Posting_Type_Virtual
 
  47  |   Posting_Type_Virtual_Balanced
 
  48  deriving (Data, Eq, Show, Typeable)
 
  50 data Posting_Typed posting
 
  51  =   Posting_Typed Posting_Type posting
 
  52  deriving (Data, Eq, Show, Typeable)
 
  54 posting_type :: Posting -> Posting_Type
 
  55 posting_type Posting{posting_tags=Posting_Tags (Tags attrs)} =
 
  56         case Map.lookup ("Virtual":|[]) attrs of
 
  57          Nothing -> Posting_Type_Regular
 
  58          Just l | "Balanced" `List.elem` l -> Posting_Type_Virtual_Balanced
 
  59          Just _  -> Posting_Type_Virtual
 
  69  { posting_account   :: Account
 
  70  , posting_amounts   :: Map Unit Quantity
 
  71  , posting_comments  :: [Comment]
 
  72  , posting_dates     :: [Date]
 
  73  , posting_sourcepos :: SourcePos
 
  74  , posting_status    :: Bool
 
  75  , posting_tags      :: Posting_Tags
 
  76  } deriving (Data, Eq, Show, Typeable)
 
  77 instance NFData Posting where
 
  84          -- , posting_sourcepos
 
  88                 rnf posting_account `seq`
 
  89                 rnf posting_amounts `seq`
 
  90                 rnf posting_comments `seq`
 
  91                 rnf posting_dates `seq`
 
  92                 -- rnf posting_sourcepos `seq`
 
  93                 rnf posting_status `seq`
 
  96 posting :: Account -> Posting
 
  99          { posting_account   = acct
 
 100          , posting_amounts   = mempty
 
 101          , posting_comments  = mempty
 
 102          , posting_dates     = mempty
 
 103          , posting_status    = False
 
 104          , posting_sourcepos = initialPos ""
 
 105          , posting_tags      = mempty
 
 108 postings_by_account :: [Posting] -> Map Account [Posting]
 
 109 postings_by_account =
 
 110         Map.fromListWith (flip mappend) .
 
 111         List.map (\p -> (posting_account p, [p]))
 
 113 instance Posting.Posting Posting where
 
 114         type Posting_Account   Posting = Account
 
 115         type Posting_Amount    Posting = Amount
 
 116         type Posting_Amounts   Posting = []
 
 117         posting_account = posting_account
 
 118         posting_amounts = List.map (uncurry Amount) . Map.toList . posting_amounts
 
 120 instance Posting.Posting (Charted Posting) where
 
 121         type Posting_Account   (Charted Posting) = Charted Account
 
 122         type Posting_Amount    (Charted Posting) = Posting.Posting_Amount  Posting
 
 123         type Posting_Amounts   (Charted Posting) = Posting.Posting_Amounts Posting
 
 124         posting_account (Chart.Charted c p) = Chart.Charted c $ Posting.posting_account p
 
 125         posting_amounts = Posting.posting_amounts . Chart.charted
 
 127 instance Balance.Posting Posting where
 
 128         type Posting_Account   Posting = Account
 
 129         type Posting_Quantity  Posting = Polarize.Polarized Quantity
 
 130         type Posting_Unit      Posting = Unit
 
 131         posting_account = posting_account
 
 132         posting_amounts = Map.map Polarize.polarize . posting_amounts
 
 133         posting_set_amounts amounts p =
 
 134                 p { posting_amounts=Map.map Polarize.depolarize amounts }
 
 136 instance Balance.Posting (Charted Posting) where
 
 137         type Posting_Account   (Charted Posting) = Account
 
 138         type Posting_Quantity  (Charted Posting) = Balance.Posting_Quantity Posting
 
 139         type Posting_Unit      (Charted Posting) = Balance.Posting_Unit     Posting
 
 140         posting_account = posting_account . Chart.charted
 
 141         posting_amounts = Map.map Polarize.polarize . posting_amounts . Chart.charted
 
 142         posting_set_amounts amounts (Chart.Charted c p) =
 
 143                 Chart.Charted c p{ posting_amounts=Map.map Polarize.depolarize amounts }
 
 145 instance Filter.Posting (Charted Posting) where
 
 146         posting_type = undefined
 
 147          -- NOTE: the posting_type will be given to Filter.test
 
 148          --       through instance Posting p => Posting (Posting_Typed p)
 
 149          --       by Filter.transaction_postings
 
 150          --       and Filter.transaction_postings_virtual
 
 152 instance GL.Posting     Posting where
 
 153         type Posting_Account  Posting = Account
 
 154         type Posting_Quantity Posting = Map Unit (Polarize.Polarized Quantity)
 
 155         posting_account  = posting_account
 
 156         posting_quantity = Map.map Polarize.polarize . posting_amounts
 
 158 instance GL.Posting     (Charted Posting) where
 
 159         type Posting_Account  (Charted Posting) = Account
 
 160         type Posting_Quantity (Charted Posting) = GL.Posting_Quantity Posting
 
 161         posting_account  = GL.posting_account  . Chart.charted
 
 162         posting_quantity = GL.posting_quantity . Chart.charted
 
 164 instance Stats.Posting  Posting where
 
 165         type Posting_Account  Posting = Account
 
 166         type Posting_Quantity Posting = Quantity
 
 167         type Posting_Unit     Posting = Unit
 
 168         posting_account = posting_account
 
 169         posting_amounts = posting_amounts
 
 172 -- ** 'Posting' mappings
 
 174 type Posting_by_Account
 
 175  = Map Account [Posting]
 
 177 type Posting_by_Amount_and_Account
 
 178  = Map (Map Unit Amount) Posting_by_Account
 
 180 type Posting_by_Signs_and_Account
 
 181  = Map Signs Posting_by_Account
 
 183 -- | Return a Data.'Map.Map' associating the given 'Posting's with their respective 'Account'.
 
 184 posting_by_Account :: [Posting] -> Posting_by_Account
 
 186         Map.fromListWith (flip mappend) .
 
 188          (\p -> (posting_account p, [p]))
 
 190 posting_by_Amount_and_Account :: Posting_by_Account -> Posting_by_Amount_and_Account
 
 191 posting_by_Amount_and_Account =
 
 197                          (Map.unionWith mappend)
 
 199                          (Map.singleton acct [p])))))
 
 202 posting_by_Signs_and_Account :: Posting_by_Account -> Posting_by_Signs_and_Account
 
 203 posting_by_Signs_and_Account =
 
 209                          (Map.unionWith mappend)
 
 210                          (signs $ posting_amounts p)
 
 211                          (Map.singleton acct [p])))))