add: add default commodity to commodity-less amounts (#26), misc. defaults fixes
This commit is contained in:
parent
18fd5fe482
commit
37378d6b97
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
15
tests/add-default-commodity.test
Normal file
15
tests/add-default-commodity.test
Normal 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user