mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-07 21:15:19 +03:00
lib: refactor
This commit is contained in:
parent
0c835acd18
commit
91e3ddd4fb
@ -485,21 +485,21 @@ filterJournalTransactionsByAccount apats j@Journal{jtxns=ts} = j{jtxns=filter tm
|
||||
-}
|
||||
|
||||
-- | Do post-parse processing on a parsed journal to make it ready for
|
||||
-- use. Reverse parsed data to normal order, canonicalise amount
|
||||
-- use. Reverse parsed data to normal order, standardise amount
|
||||
-- formats, check/ensure that transactions are balanced, and maybe
|
||||
-- check balance assertions.
|
||||
journalFinalise :: ClockTime -> FilePath -> Text -> Bool -> ParsedJournal -> Either String Journal
|
||||
journalFinalise t path txt assrt j@Journal{jfiles=fs} = do
|
||||
(journalTieTransactions <$>
|
||||
(journalBalanceTransactions assrt $
|
||||
journalApplyCommodityStyles $
|
||||
j{ jfiles = (path,txt) : reverse fs
|
||||
, jlastreadtime = t
|
||||
, jtxns = reverse $ jtxns j -- NOTE: see addTransaction
|
||||
, jmodifiertxns = reverse $ jmodifiertxns j -- NOTE: see addModifierTransaction
|
||||
, jperiodictxns = reverse $ jperiodictxns j -- NOTE: see addPeriodicTransaction
|
||||
, jmarketprices = reverse $ jmarketprices j -- NOTE: see addMarketPrice
|
||||
}))
|
||||
journalFinalise t path txt assrt j@Journal{jfiles=fs} =
|
||||
journalTieTransactions <$>
|
||||
(journalBalanceTransactions assrt $
|
||||
journalApplyCommodityStyles $
|
||||
j {jfiles = (path,txt) : reverse fs
|
||||
,jlastreadtime = t
|
||||
,jtxns = reverse $ jtxns j -- NOTE: see addTransaction
|
||||
,jmodifiertxns = reverse $ jmodifiertxns j -- NOTE: see addModifierTransaction
|
||||
,jperiodictxns = reverse $ jperiodictxns j -- NOTE: see addPeriodicTransaction
|
||||
,jmarketprices = reverse $ jmarketprices j -- NOTE: see addMarketPrice
|
||||
})
|
||||
|
||||
journalNumberAndTieTransactions = journalTieTransactions . journalNumberTransactions
|
||||
|
||||
@ -521,9 +521,12 @@ journalUntieTransactions t@Transaction{tpostings=ps} = t{tpostings=map (\p -> p{
|
||||
-- message if any of them fail.
|
||||
journalCheckBalanceAssertions :: Journal -> Either String Journal
|
||||
journalCheckBalanceAssertions j =
|
||||
runST $ journalBalanceTransactionsST True j
|
||||
(return ()) (\_ _ -> return ()) (const $ return j) -- noops
|
||||
|
||||
runST $ journalBalanceTransactionsST
|
||||
True
|
||||
j
|
||||
(return ())
|
||||
(\_ _ -> return ())
|
||||
(const $ return j)
|
||||
|
||||
-- | Check a posting's balance assertion and return an error if it
|
||||
-- fails.
|
||||
@ -561,59 +564,58 @@ checkBalanceAssertion p@Posting{ pbalanceassertion = Just (ass,_)} amt
|
||||
(diffplus ++ showAmount diff)
|
||||
checkBalanceAssertion _ _ = Right ()
|
||||
|
||||
-- | Environment for 'CurrentBalancesModifier'
|
||||
data Env s = Env { eBalances :: HT.HashTable s AccountName MixedAmount
|
||||
, eStoreTx :: Transaction -> ST s ()
|
||||
, eAssrt :: Bool
|
||||
, eStyles :: Maybe (M.Map CommoditySymbol AmountStyle) }
|
||||
|
||||
-- | Monad transformer stack with a reference to a mutable hashtable
|
||||
-- of current account balances and a mutable array of finished
|
||||
-- transactions in original parsing order.
|
||||
type CurrentBalancesModifier s = R.ReaderT (Env s) (ExceptT String (ST s))
|
||||
|
||||
-- | Fill in any missing amounts and check that all journal transactions
|
||||
-- balance, or return an error message. This is done after parsing all
|
||||
-- amounts and applying canonical commodity styles, since balancing
|
||||
-- depends on display precision. Reports only the first error encountered.
|
||||
journalBalanceTransactions :: Bool -> Journal -> Either String Journal
|
||||
journalBalanceTransactions assrt j =
|
||||
runST $
|
||||
journalBalanceTransactionsST
|
||||
assrt -- check balance assertions also ?
|
||||
(journalNumberTransactions j) -- journal to process
|
||||
(newArray_ (1, genericLength $ jtxns j) :: forall s. ST s (STArray s Integer Transaction)) -- initialise state
|
||||
(\arr tx -> writeArray arr (tindex tx) tx) -- update state
|
||||
(fmap (\txns -> j{ jtxns = txns}) . getElems) -- summarise state
|
||||
runST $ journalBalanceTransactionsST
|
||||
assrt -- check balance assertions also ?
|
||||
(journalNumberTransactions j) -- journal to process
|
||||
(newArray_ (1, genericLength $ jtxns j) :: forall s. ST s (STArray s Integer Transaction)) -- initialise state
|
||||
(\arr tx -> writeArray arr (tindex tx) tx) -- update state
|
||||
(fmap (\txns -> j{ jtxns = txns}) . getElems) -- summarise state
|
||||
|
||||
|
||||
-- | Generalization used in the definition of
|
||||
-- 'journalBalanceTransactionsST and 'journalCheckBalanceAssertions'
|
||||
-- | Helper used by 'journalBalanceTransactions' and 'journalCheckBalanceAssertions'.
|
||||
journalBalanceTransactionsST ::
|
||||
Bool
|
||||
-> Journal
|
||||
-> ST s txns
|
||||
-- ^ creates transaction store
|
||||
-> (txns -> Transaction -> ST s ())
|
||||
-- ^ "store" operation
|
||||
-> (txns -> ST s a)
|
||||
-- ^ calculate result from transactions
|
||||
-> ST s txns -- ^ initialise state
|
||||
-> (txns -> Transaction -> ST s ()) -- ^ update state
|
||||
-> (txns -> ST s a) -- ^ summarise state
|
||||
-> ST s (Either String a)
|
||||
journalBalanceTransactionsST assrt j createStore storeIn extract =
|
||||
runExceptT $ do
|
||||
bals <- lift $ HT.newSized size
|
||||
txStore <- lift $ createStore
|
||||
flip R.runReaderT (Env bals (storeIn txStore) assrt $
|
||||
Just $ jinferredcommodities j) $ do
|
||||
let env = Env bals
|
||||
(storeIn txStore)
|
||||
assrt
|
||||
(Just $ jinferredcommodities j)
|
||||
flip R.runReaderT env $ do
|
||||
dated <- fmap snd . sortBy (comparing fst) . concat
|
||||
<$> mapM' discriminateByDate (jtxns j)
|
||||
<$> mapM' discriminateByDate (jtxns j)
|
||||
mapM' checkInferAndRegisterAmounts dated
|
||||
lift $ extract txStore
|
||||
where size = genericLength $ journalPostings j
|
||||
where
|
||||
size = genericLength $ journalPostings j
|
||||
|
||||
-- | This converts a transaction into a list of objects whose dates
|
||||
-- have to be considered when checking balance assertions and handled
|
||||
-- by 'checkInferAndRegisterAmounts'.
|
||||
-- | Monad transformer stack with a reference to a mutable hashtable
|
||||
-- of current account balances and a mutable array of finished
|
||||
-- transactions in original parsing order.
|
||||
type CurrentBalancesModifier s = R.ReaderT (Env s) (ExceptT String (ST s))
|
||||
|
||||
-- | Environment for 'CurrentBalancesModifier'
|
||||
data Env s = Env { eBalances :: HT.HashTable s AccountName MixedAmount
|
||||
, eStoreTx :: Transaction -> ST s ()
|
||||
, eAssrt :: Bool
|
||||
, eStyles :: Maybe (M.Map CommoditySymbol AmountStyle)
|
||||
}
|
||||
|
||||
-- | This converts a transaction into a list of transactions or
|
||||
-- postings whose dates have to be considered when checking
|
||||
-- balance assertions and handled by 'checkInferAndRegisterAmounts'.
|
||||
--
|
||||
-- Transaction without balance assignments can be balanced and stored
|
||||
-- immediately and their (possibly) dated postings are returned.
|
||||
@ -626,25 +628,24 @@ discriminateByDate :: Transaction
|
||||
discriminateByDate tx
|
||||
| null (assignmentPostings tx) = do
|
||||
styles <- R.reader $ eStyles
|
||||
balanced <- lift $ ExceptT $ return
|
||||
$ balanceTransaction styles tx
|
||||
balanced <- lift $ ExceptT $ return $ balanceTransaction styles tx
|
||||
storeTransaction balanced
|
||||
return $ fmap (postingDate &&& (Left . removePrices))
|
||||
$ tpostings $ balanced
|
||||
return $
|
||||
fmap (postingDate &&& (Left . removePrices)) $ tpostings $ balanced
|
||||
| True = do
|
||||
when (any (isJust . pdate) $ tpostings tx) $
|
||||
throwError $ unlines $
|
||||
["Not supported: Transactions with balance assignments "
|
||||
,"AND dated postings without amount:\n"
|
||||
, showTransaction tx]
|
||||
return [(tdate tx, Right
|
||||
$ tx { tpostings = removePrices <$> tpostings tx })]
|
||||
return
|
||||
[(tdate tx, Right $ tx { tpostings = removePrices <$> tpostings tx })]
|
||||
|
||||
-- | This function takes different objects describing changes to
|
||||
-- account balances on a single day. It can handle either a single
|
||||
-- posting (from an already balanced transaction without assigments)
|
||||
-- or a whole transaction with assignments (which is required to no
|
||||
-- posting with pdate set.).
|
||||
-- | This function takes an object describing changes to
|
||||
-- account balances on a single day - either a single posting
|
||||
-- (from an already balanced transaction without assignments)
|
||||
-- or a whole transaction with assignments (which is required to
|
||||
-- have no posting with pdate set).
|
||||
--
|
||||
-- For a single posting, there is not much to do. Only add its amount
|
||||
-- to its account and check the assertion, if there is one. This
|
||||
@ -654,9 +655,9 @@ discriminateByDate tx
|
||||
-- 'addAmountAndCheckBalance', if there is an amount. If there is no
|
||||
-- amount, the amount is inferred by the assertion or left empty if
|
||||
-- there is no assertion. Then, the transaction is balanced, the
|
||||
-- inferred amount added to the balance (all in
|
||||
-- 'balanceTransactionUpdate') and the resulting transaction with no
|
||||
-- missing amounts is stored in the array, for later retrieval.
|
||||
-- inferred amount added to the balance (all in 'balanceTransactionUpdate')
|
||||
-- and the resulting transaction with no missing amounts is stored
|
||||
-- in the array, for later retrieval.
|
||||
--
|
||||
-- Again in short:
|
||||
--
|
||||
@ -682,45 +683,42 @@ checkInferAndRegisterAmounts (Right oldTx) = do
|
||||
(fmap (\a -> p { pamount = a, porigin = Just $ originalPosting p }) . setBalance (paccount p) . fst)
|
||||
$ pbalanceassertion p
|
||||
|
||||
-- | Adds a posting's amonut to the posting's account balance and
|
||||
-- checks a possible balance assertion. If there is no amount, it runs
|
||||
-- the supplied fallback action.
|
||||
addAmountAndCheckBalance :: (Posting -> CurrentBalancesModifier s Posting)
|
||||
-- ^ action to execute, if posting has no amount
|
||||
-> Posting
|
||||
-> CurrentBalancesModifier s Posting
|
||||
-- | Adds a posting's amount to the posting's account balance and
|
||||
-- checks a possible balance assertion. Or if there is no amount,
|
||||
-- runs the supplied fallback action.
|
||||
addAmountAndCheckBalance ::
|
||||
(Posting -> CurrentBalancesModifier s Posting) -- ^ action if posting has no amount
|
||||
-> Posting
|
||||
-> CurrentBalancesModifier s Posting
|
||||
addAmountAndCheckBalance _ p | hasAmount p = do
|
||||
newAmt <- addToBalance (paccount p) $ pamount p
|
||||
assrt <- R.reader eAssrt
|
||||
lift $ when assrt $ ExceptT $ return
|
||||
$ checkBalanceAssertion p newAmt
|
||||
lift $ when assrt $ ExceptT $ return $ checkBalanceAssertion p newAmt
|
||||
return p
|
||||
addAmountAndCheckBalance fallback p = fallback p
|
||||
|
||||
-- | Sets an account's balance to a given amount and returns the
|
||||
-- difference of new and old amount
|
||||
-- difference of new and old amount.
|
||||
setBalance :: AccountName -> Amount -> CurrentBalancesModifier s MixedAmount
|
||||
setBalance acc amt = liftModifier $ \Env{ eBalances = bals } -> do
|
||||
old <- HT.lookup bals acc
|
||||
let new = Mixed $ (amt :) $ maybe []
|
||||
(filter ((/= acommodity amt) . acommodity) . amounts) old
|
||||
(filter ((/= acommodity amt) . acommodity) . amounts) old
|
||||
HT.insert bals acc new
|
||||
return $ maybe new (new -) old
|
||||
|
||||
-- | Adds an amount to an account's balance and returns the resulting
|
||||
-- balance
|
||||
-- | Adds an amount to an account's balance and returns the resulting balance.
|
||||
addToBalance :: AccountName -> MixedAmount -> CurrentBalancesModifier s MixedAmount
|
||||
addToBalance acc amt = liftModifier $ \Env{ eBalances = bals } -> do
|
||||
new <- maybe amt (+ amt) <$> HT.lookup bals acc
|
||||
HT.insert bals acc new
|
||||
return new
|
||||
|
||||
-- | Stores a transaction in the transaction array in original parsing
|
||||
-- order.
|
||||
-- | Stores a transaction in the transaction array in original parsing order.
|
||||
storeTransaction :: Transaction -> CurrentBalancesModifier s ()
|
||||
storeTransaction tx = liftModifier $ ($tx) . eStoreTx
|
||||
|
||||
-- | Helper function
|
||||
-- | Helper function.
|
||||
liftModifier :: (Env s -> ST s a) -> CurrentBalancesModifier s a
|
||||
liftModifier f = R.ask >>= lift . lift . f
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user