add: add default commodity to commodity-less amounts (#26), misc. defaults fixes

This commit is contained in:
Simon Michael 2010-11-13 15:11:45 +00:00
parent 18fd5fe482
commit 37378d6b97
4 changed files with 71 additions and 25 deletions

View File

@ -390,12 +390,26 @@ The following commands can alter your journal file.
##### add
The add command prompts interactively for new transactions, and adds them
to the journal. It is experimental.
to the journal, with assistance:
- During data entry, the usual console editing keys should work
- If there are earlier transactions approximately matching the description
you enter, the best match will provide defaults for the other fields.
- If you specify [account pattern(s)](#filter-patterns) on the command
line, only matching transactions will be considered for defaults.
- While entering account names, the tab key will auto-complete up to the
next : separator
- If a [default commodity](#default-commodity) is defined, it will be used
for any commodity-less amounts entered.
Examples:
$ hledger add
$ hledger add accounts:personal:bob
$ hledger -f home.journal add equity:bob
##### web

View File

@ -27,6 +27,8 @@ import System.Console.Haskeline (
import Control.Monad.Trans (liftIO)
import System.Console.Haskeline.Completion
import qualified Data.Set as Set
import Safe (headMay)
-- | Read transactions from the terminal, prompting for each field,
-- and append them to the journal file. If the journal came from stdin, this
@ -74,7 +76,7 @@ getTransaction j opts args defaultDate = do
else True
where (ant,_,_,_) = groupPostings $ journalPostings j
getpostingsandvalidate = do
ps <- getPostings accept bestmatchpostings []
ps <- getPostings (jContext j) accept bestmatchpostings []
let t = nulltransaction{tdate=date
,tstatus=False
,tdescription=description
@ -90,37 +92,51 @@ getTransaction j opts args defaultDate = do
hPutStr stderr $ concatMap (\(n,t) -> printf "[%3d%%] %s" (round $ n*100 :: Int) (show t)) $ take 3 historymatches)
getpostingsandvalidate
-- | Read postings from the command line until . is entered, using the
-- provided historical postings, if any, to guess defaults.
getPostings :: (AccountName -> Bool) -> Maybe [Posting] -> [Posting] -> InputT IO [Posting]
getPostings accept historicalps enteredps = do
account <- askFor (printf "account %d" n) defaultaccount (Just accept)
if account=="."
then return enteredps
else do
amountstr <- askFor (printf "amount %d" n) defaultamount validateamount
let amount = fromparse $ parse (someamount <|> return missingamt) "" amountstr
let p = nullposting{paccount=stripbrackets account,
pamount=amount,
ptype=postingtype account}
getPostings accept historicalps $ enteredps ++ [p]
where
n = length enteredps + 1
enteredrealps = filter isReal enteredps
bestmatch | isNothing historicalps = Nothing
-- | Read postings from the command line until . is entered, using any
-- provided historical postings and the journal context to guess defaults.
getPostings :: JournalContext -> (AccountName -> Bool) -> Maybe [Posting] -> [Posting] -> InputT IO [Posting]
getPostings ctx accept historicalps enteredps = do
let bestmatch | isNothing historicalps = Nothing
| n <= length ps = Just $ ps !! (n-1)
| otherwise = Nothing
where Just ps = historicalps
defaultaccount = maybe Nothing (Just . showacctname) bestmatch
account <- askFor (printf "account %d" n) defaultaccount (Just accept)
if account=="."
then return enteredps
else do
let defaultacctused = Just account == defaultaccount
historicalps' = if defaultacctused then historicalps else Nothing
bestmatch' | isNothing historicalps' = Nothing
| n <= length ps = Just $ ps !! (n-1)
| otherwise = Nothing
where Just ps = historicalps'
defaultamountstr | isJust bestmatch' = Just $ showMixedAmountWithPrecision maxprecision $ pamount $ fromJust bestmatch'
| n > 1 = Just balancingamountstr
| otherwise = Nothing
where balancingamountstr = showMixedAmountWithPrecision maxprecision $ negate $ sumMixedAmountsPreservingHighestPrecision $ map pamount enteredrealps
amountstr <- askFor (printf "amount %d" n) defaultamountstr validateamount
let amount = setMixedAmountPrecision maxprecision $ fromparse $ runParser (someamount <|> return missingamt) ctx "" amountstr
defaultamtused = Just (showMixedAmount amount) == defaultamountstr
historicalps'' = if defaultamtused then historicalps' else Nothing
commodityadded | showMixedAmountWithPrecision maxprecision amount == amountstr = Nothing
| otherwise = maybe Nothing (Just . commodity) $ headMay $ amounts amount
p = nullposting{paccount=stripbrackets account,
pamount=amount,
ptype=postingtype account}
when (isJust commodityadded) $
liftIO $ hPutStrLn stderr $ printf "using default commodity (%s)" (symbol $ fromJust commodityadded)
getPostings ctx accept historicalps'' $ enteredps ++ [p]
where
n = length enteredps + 1
enteredrealps = filter isReal enteredps
showacctname p = showAccountName Nothing (ptype p) $ paccount p
defaultamount = maybe balancingamount (Just . show . pamount) bestmatch
where balancingamount = Just $ show $ negate $ sumMixedAmountsPreservingHighestPrecision $ map pamount enteredrealps
postingtype ('[':_) = BalancedVirtualPosting
postingtype ('(':_) = VirtualPosting
postingtype _ = RegularPosting
stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse
validateamount = Just $ \s -> (null s && not (null enteredrealps))
|| isRight (parse (someamount>>many spacenonewline>>eof) "" s)
|| isRight (runParser (someamount>>many spacenonewline>>eof) ctx "" s)
-- | Prompt for and read a string value, optionally with a default value
-- and a validator. A validator causes the prompt to repeat until the

View File

@ -9,6 +9,7 @@ import Hledger.Cli.Version (versionstr)
import Hledger.Data.Types (Journal,AccountName,Transaction(..),Posting(..),PostingType(..))
import Hledger.Data.Utils (strip, spacenonewline, restofline, parseWithCtx, assertParse, assertParseEqual, error')
import Hledger.Read.Journal (someamount,ledgeraccountname)
import Hledger.Data.Journal (nullctx)
import Hledger.Data.Amount (nullmixedamt)
import Safe (atDef, maximumDef)
import System.IO (stderr)
@ -281,7 +282,7 @@ transactionFromCsvRecord rules fields =
strnegate s = '-':s
currency = maybe (fromMaybe "" $ baseCurrency rules) (atDef "" fields) (currencyField rules)
amountstr'' = currency ++ amountstr'
amountparse = parse someamount "" amountstr''
amountparse = runParser someamount nullctx "" amountstr''
amount = either (const nullmixedamt) id amountparse
unknownacct | (readDef 0 amountstr' :: Double) < 0 = "income:unknown"
| otherwise = "expenses:unknown"

View File

@ -0,0 +1,15 @@
# add should use the (final) default commodity if any
# disabled as add currently requires ctrl-c to terminate
#
# printf 'D £1000.00\n' >add-default-commodity-$$.j; hledger -fadd-default-commodity-$$.j add; rm -f add-default-commodity-$$.j
# <<<
# 2010/1/1
# a
# 1000
# b
# >>>
# 2010/01/01 y
# a £1000.00
# b £-1000.00