From 2d9259ab3a5a8ca5d09b6b46a1ea37643d49b172 Mon Sep 17 00:00:00 2001 From: Mykola Orliuk Date: Mon, 16 Jan 2017 02:02:32 +0200 Subject: [PATCH] budget: new addon --- bin/.gitignore | 1 + bin/hledger-budget.hs | 57 +++++++++++++++++++++ tests/bin/budget.test | 115 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100755 bin/hledger-budget.hs create mode 100644 tests/bin/budget.test diff --git a/bin/.gitignore b/bin/.gitignore index ef37f380d..9d769c127 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1,3 +1,4 @@ +hledger-budget hledger-chart hledger-check-dates hledger-dupes diff --git a/bin/hledger-budget.hs b/bin/hledger-budget.hs new file mode 100755 index 000000000..eb6a348b9 --- /dev/null +++ b/bin/hledger-budget.hs @@ -0,0 +1,57 @@ +#!/usr/bin/env stack +{- stack runghc --verbosity info + --package hledger-lib + --package hledger + --package text +-} +{-# LANGUAGE OverloadedStrings #-} +import Data.List +import System.Console.CmdArgs +import Hledger.Cli +import Hledger.Cli.Main (mainmode) +import Hledger.Data.AutoTransaction + +actions :: [(Mode RawOpts, CliOpts -> IO ())] +actions = + [ (manmode, man) + , (infomode, info') + , (balancemode, flip withJournalDo' balance) + , (balancesheetmode, flip withJournalDo' balancesheet) + , (cashflowmode, flip withJournalDo' cashflow) + , (incomestatementmode, flip withJournalDo' incomestatement) + , (registermode, flip withJournalDo' register) + , (printmode, flip withJournalDo' print') + ] + +cmdmode :: Mode RawOpts +cmdmode = (mainmode []) + { modeNames = ["hledger-budget"] + , modeGroupModes = Group + { groupUnnamed = map fst actions + , groupNamed = [] + , groupHidden = [] + } + } + +journalBalanceTransactions' :: CliOpts -> Journal -> IO Journal +journalBalanceTransactions' opts j = do + let assrt = not $ ignore_assertions_ opts + either error' return $ journalBalanceTransactions assrt j + +withJournalDo' :: CliOpts -> (CliOpts -> Journal -> IO ()) -> IO () +withJournalDo' opts = withJournalDo opts . wrapper where + wrapper f opts' j = f opts' =<< journalBalanceTransactions' opts' j{ jtxns = ts' } where + -- 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 + +main :: IO () +main = do + rawopts <- fmap decodeRawOpts . processArgs $ cmdmode + opts <- rawOptsToCliOpts rawopts + let cmd = command_ opts + case find (\e -> cmd `elem` modeNames (fst e)) actions of + Just (amode, _) | "h" `elem` map fst (rawopts_ opts) -> print amode + Just (_, action) -> action opts + Nothing -> print cmdmode diff --git a/tests/bin/budget.test b/tests/bin/budget.test new file mode 100644 index 000000000..06d8e50b9 --- /dev/null +++ b/tests/bin/budget.test @@ -0,0 +1,115 @@ +# Test budget addon + +# Rewrite rules within journal always applied +runghc ../../bin/hledger-budget.hs bal -f - --no-total -DH budget +<<< += ^assets:bank$ date:2017/1 amt:<0 + assets:bank *0.008 + expenses:fee *-0.008 ; cash withdraw fee += ^expenses:housing + (budget:housing) *-1 += ^expenses:grocery ^expenses:food + (budget:food) *-1 + +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/2/1 + assets:cash $100.00 + assets:bank + +; order with normal entries doesn't matter +; but relative order matters to refer-rewritten transactions += ^expenses not:housing not:grocery not:food + (budget:misc) *-1 +>>> +Ending balances (historical) in 2016/12/31-2017/01/02: + + || 2016/12/31 2017/01/01 2017/01/02 +================++===================================== + budget:food || 0 $-50.00 $-50.00 + budget:housing || $-600.00 $-600.00 $-600.00 + budget:misc || 0 $-15.00 $-16.60 + +>>>2 +>>>=0 + +# Rewrite rules can chain one another according to order of definition +runghc ../../bin/hledger-budget.hs reg -f - +<<< + +; unfortunately date override in posting comment doesn't work + += liabilities:credit amt:<0 date:2016/12 + liabilities:credit *-1 ; [2017/1/1] + assets:bank *1 ; [2017/1/1] + += liabilities:credit amt:<0 date:2017/1 + liabilities:credit *-1 ; [2017/2/1] + assets:bank *1 ; [2017/2/1] + += liabilities:credit amt:<0 date:2017/2 + liabilities:credit *-1 ; [2017/3/1] + assets:bank *1 ; [2017/3/1] + += assets:bank date:2017/2 amt:<0 + assets:bank *0.008 + expenses:fee *-0.008 ; :salary-card: cash withdraw fee + += expenses:fee tag:salary-card date:2017-2017/12/26 + income:compensate *-0.5 + assets:bank *0.5 ; compensation from employer + +2016/12/31 + expenses:housing $600 + liabilities:credit + +2017/1/1 + expenses:food $20 + expenses:leisure $15 + expenses:grocery $30 + liabilities:credit + +2017/1/2 + assets:cash $200.00 + liabilities:credit + +2017/2/1 + assets:cash $100.00 + liabilities:credit +>>> +2016/12/31 expenses:housing $600.00 $600.00 + liabilities:credit $-600.00 0 + liabilities:credit $600.00 $600.00 + assets:bank $-600.00 0 +2017/01/01 expenses:food $20.00 $20.00 + expenses:leisure $15.00 $35.00 + expenses:grocery $30.00 $65.00 + liabilities:credit $-65.00 0 + liabilities:credit $65.00 $65.00 + assets:bank $-65.00 0 +2017/01/02 assets:cash $200.00 $200.00 + liabilities:credit $-200.00 0 + liabilities:credit $200.00 $200.00 + assets:bank $-200.00 0 +2017/02/01 assets:cash $100.00 $100.00 + liabilities:credit $-100.00 0 + liabilities:credit $100.00 $100.00 + assets:bank $-100.00 0 + assets:bank $-0.80 $-0.80 + expenses:fee $0.80 0 + income:compensate $-0.40 $-0.40 + assets:bank $0.40 0 +>>>2 +>>>=0