budget: periodic transactions support
This commit is contained in:
parent
2d9259ab3a
commit
3a632acea0
@ -40,11 +40,21 @@ journalBalanceTransactions' opts j = do
|
||||
|
||||
withJournalDo' :: CliOpts -> (CliOpts -> Journal -> IO ()) -> IO ()
|
||||
withJournalDo' opts = withJournalDo opts . wrapper where
|
||||
wrapper f opts' j = f opts' =<< journalBalanceTransactions' opts' j{ jtxns = ts' } where
|
||||
wrapper f opts' j = do
|
||||
-- use original transactions as input for journalBalanceTransactions to re-infer balances/prices
|
||||
modifier = originalTransaction . foldr (flip (.) . fmap txnTieKnot . runModifierTransaction Any) id mtxns
|
||||
mtxns = jmodifiertxns j
|
||||
ts' = map modifier $ jtxns j
|
||||
let modifier = originalTransaction . foldr (flip (.) . runModifierTransaction') id mtxns
|
||||
runModifierTransaction' = fmap txnTieKnot . runModifierTransaction Any
|
||||
mtxns = jmodifiertxns j
|
||||
dates = jdatespan j
|
||||
ts' = map modifier $ jtxns j
|
||||
ts'' = [makeBudget t | pt <- jperiodictxns j, t <- runPeriodicTransaction pt dates] ++ ts'
|
||||
makeBudget t = txnTieKnot $ t
|
||||
{ tdescription = "Budget transaction"
|
||||
, tpostings = map makeBudgetPosting $ tpostings t
|
||||
}
|
||||
makeBudgetPosting p = p { pamount = negate $ pamount p }
|
||||
j' <- journalBalanceTransactions' opts' j{ jtxns = ts'' }
|
||||
f opts' j'
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
|
||||
@ -9,9 +9,11 @@ module Hledger.Data.AutoTransaction
|
||||
(
|
||||
-- * Transaction processors
|
||||
runModifierTransaction
|
||||
, runPeriodicTransaction
|
||||
|
||||
-- * Accessors
|
||||
, mtvaluequery
|
||||
, jdatespan
|
||||
)
|
||||
where
|
||||
|
||||
@ -22,12 +24,15 @@ import qualified Data.Text as T
|
||||
import Hledger.Data.Types
|
||||
import Hledger.Data.Dates
|
||||
import Hledger.Data.Amount
|
||||
import Hledger.Data.Transaction
|
||||
import Hledger.Utils.Parse
|
||||
import Hledger.Utils.UTF8IOCompat (error')
|
||||
import Hledger.Query
|
||||
|
||||
-- $setup
|
||||
-- >>> :set -XOverloadedStrings
|
||||
-- >>> import Hledger.Data.Posting
|
||||
-- >>> import Hledger.Data.Transaction
|
||||
-- >>> import Hledger.Data.Journal
|
||||
|
||||
-- | Builds a 'Transaction' transformer based on 'ModifierTransaction'.
|
||||
--
|
||||
@ -76,6 +81,29 @@ runModifierTransaction q mt = modifier where
|
||||
mtvaluequery :: ModifierTransaction -> (Day -> Query)
|
||||
mtvaluequery mt = fst . flip parseQuery (mtvalueexpr mt)
|
||||
|
||||
-- | 'DateSpan' of all dates mentioned in 'Journal'
|
||||
--
|
||||
-- >>> jdatespan nulljournal
|
||||
-- DateSpan -
|
||||
-- >>> jdatespan nulljournal{jtxns=[nulltransaction{tdate=read "2016-01-01"}] }
|
||||
-- DateSpan 2016/01/01
|
||||
-- >>> jdatespan nulljournal{jtxns=[nulltransaction{tdate=read "2016-01-01", tpostings=[nullposting{pdate=Just $ read "2016-02-01"}]}] }
|
||||
-- DateSpan 2016/01/01-2016/02/01
|
||||
jdatespan :: Journal -> DateSpan
|
||||
jdatespan j
|
||||
| null dates = nulldatespan
|
||||
| otherwise = DateSpan (Just $ minimum dates) (Just $ 1 `addDays` maximum dates)
|
||||
where
|
||||
dates = concatMap tdates $ jtxns j
|
||||
|
||||
-- | 'DateSpan' of all dates mentioned in 'Transaction'
|
||||
--
|
||||
-- >>> tdates nulltransaction
|
||||
-- [0000-01-01]
|
||||
tdates :: Transaction -> [Day]
|
||||
tdates t = tdate t : concatMap pdates (tpostings t) ++ maybeToList (tdate2 t) where
|
||||
pdates p = catMaybes [pdate p, pdate2 p]
|
||||
|
||||
postingScale :: Posting -> Maybe Quantity
|
||||
postingScale p =
|
||||
case amounts $ pamount p of
|
||||
@ -101,3 +129,28 @@ renderPostingCommentDates p = p { pcomment = comment' }
|
||||
comment'
|
||||
| T.null datesComment = pcomment p
|
||||
| otherwise = T.intercalate "\n" $ filter (not . T.null) [T.strip $ pcomment p, "[" <> datesComment <> "]"]
|
||||
|
||||
-- | Generate transactions from 'PeriodicTransaction' within a 'DateSpan'
|
||||
--
|
||||
-- Note that new transactions require 'txnTieKnot' post-processing.
|
||||
--
|
||||
-- >>> mapM_ (putStr . show) $ runPeriodicTransaction (PeriodicTransaction "monthly from 2017/1 to 2017/4" ["hi" `post` usd 1]) nulldatespan
|
||||
-- 2017/01/01
|
||||
-- hi $1.00
|
||||
-- <BLANKLINE>
|
||||
-- 2017/02/01
|
||||
-- hi $1.00
|
||||
-- <BLANKLINE>
|
||||
-- 2017/03/01
|
||||
-- hi $1.00
|
||||
-- <BLANKLINE>
|
||||
runPeriodicTransaction :: PeriodicTransaction -> (DateSpan -> [Transaction])
|
||||
runPeriodicTransaction pt = generate where
|
||||
base = nulltransaction { tpostings = ptpostings pt }
|
||||
periodExpr = ptperiodicexpr pt
|
||||
errCurrent = error' $ "Current date cannot be referenced in " ++ show (T.unpack periodExpr)
|
||||
(interval, effectspan) =
|
||||
case parsePeriodExpr errCurrent periodExpr of
|
||||
Left e -> error' $ "Failed to parse " ++ show (T.unpack periodExpr) ++ ": " ++ showDateParseError e
|
||||
Right x -> x
|
||||
generate jspan = [base {tdate=date} | span <- interval `splitSpan` spanIntersect effectspan jspan, let Just date = spanStart span]
|
||||
|
||||
@ -113,3 +113,52 @@ runghc ../../bin/hledger-budget.hs reg -f -
|
||||
assets:bank $0.40 0
|
||||
>>>2
|
||||
>>>=0
|
||||
|
||||
# Periodical transactions within journal being applied with inverted sign in amounts
|
||||
runghc ../../bin/hledger-budget.hs bal -f - --no-total -DH expenses
|
||||
<<<
|
||||
~ daily from 2016/12/31
|
||||
expenses:food $8
|
||||
assets
|
||||
|
||||
~ weekly
|
||||
expenses:leisure $20
|
||||
expenses:grocery $50
|
||||
expenses:housing $250
|
||||
expenses:fee $10
|
||||
assets
|
||||
|
||||
= ^assets:bank$ date:2017/1 amt:<0
|
||||
assets:bank *0.008
|
||||
expenses:fee *-0.008 ; cash withdraw fee
|
||||
|
||||
2016/12/31
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
2017/1/1
|
||||
expenses:food $20
|
||||
expenses:leisure $15
|
||||
expenses:grocery $30
|
||||
assets:cash
|
||||
|
||||
2017/1/2
|
||||
assets:cash $200.00
|
||||
assets:bank
|
||||
|
||||
2017/1/4
|
||||
assets:cash $100.00
|
||||
assets:bank
|
||||
>>>
|
||||
Ending balances (historical) in 2016/12/26-2017/01/04:
|
||||
|
||||
|| 2016/12/26 2016/12/27 2016/12/28 2016/12/29 2016/12/30 2016/12/31 2017/01/01 2017/01/02 2017/01/03 2017/01/04
|
||||
==================++=========================================================================================================================
|
||||
expenses:fee || $-10 $-10 $-10 $-10 $-10 $-10 $-10 $-18.40 $-18.40 $-17.60
|
||||
expenses:food || 0 0 0 0 0 $-8 $4.00 $-4.00 $-12.00 $-20.00
|
||||
expenses:grocery || $-50 $-50 $-50 $-50 $-50 $-50 $-20.00 $-70.00 $-70.00 $-70.00
|
||||
expenses:housing || $-250 $-250 $-250 $-250 $-250 $350.00 $350.00 $100.00 $100.00 $100.00
|
||||
expenses:leisure || $-20 $-20 $-20 $-20 $-20 $-20 $-5.00 $-25.00 $-25.00 $-25.00
|
||||
|
||||
>>>2
|
||||
>>>=0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user