diff --git a/Hledger/Cli/Commands/WebYesod.hs b/Hledger/Cli/Commands/WebYesod.hs index 92e2d3a42..6da0ffc4a 100644 --- a/Hledger/Cli/Commands/WebYesod.hs +++ b/Hledger/Cli/Commands/WebYesod.hs @@ -5,23 +5,24 @@ A web-based UI. module Hledger.Cli.Commands.WebYesod where - import Control.Concurrent -- (forkIO) +import qualified Data.ByteString.Char8 as B +import Data.Either import qualified Network.Wai (Request(pathInfo)) import System.FilePath (()) import System.IO.Storage (withStore, putValue, getValue) import Text.Hamlet -import qualified Data.ByteString.Char8 as B +import Text.ParserCombinators.Parsec (parse) import Yesod --- import Hledger.Cli.Commands.Add (journalAddTransaction) +import Hledger.Cli.Commands.Add (journalAddTransaction) import Hledger.Cli.Commands.Balance import Hledger.Cli.Commands.Print import Hledger.Cli.Commands.Register - import Hledger.Cli.Options hiding (value) import Hledger.Cli.Utils import Hledger.Data +import Hledger.Read.Journal (someamount) #ifdef MAKE import Paths_hledger_make (getDataFileName) #else @@ -90,9 +91,6 @@ getIndexPage = redirect RedirectTemporary TransactionsPage getTransactionsPage :: Handler HledgerWebApp RepHtml getTransactionsPage = withLatestJournalRender (const showTransactions) -postTransactionsPage :: Handler HledgerWebApp RepHtml -postTransactionsPage = withLatestJournalRender (const showTransactions) - getRegisterPage :: Handler HledgerWebApp RepHtml getRegisterPage = withLatestJournalRender showRegisterReport @@ -104,17 +102,23 @@ withLatestJournalRender f = do app <- getYesod req <- getRequest params <- getParams + msg <- getMessage t <- liftIO $ getCurrentLocalTime + -- today <- liftIO $ liftM showDate $ getCurrentDay let as = params "a" ps = params "p" opts = appOpts app ++ [Period $ unwords ps] args = appArgs app ++ as fs = optsToFilterSpec opts args t + -- date = fromMaybe (decodeString today) $ getParam "date" + -- desc = fromMaybe "" $ getParam "desc" + -- acct = fromMaybe "" $ getParam "acctvar" + -- amt = fromMaybe "" $ getParam "amtvar" j <- liftIO $ fromJust `fmap` getValue "hledger" "journal" (changed, j') <- liftIO $ journalReloadIfChanged opts j when changed $ liftIO $ putValue "hledger" "journal" j' let content = f opts fs j' - return $ RepHtml $ toContent $ renderHamlet id $ template req as ps "" content + return $ RepHtml $ toContent $ renderHamlet id $ template req msg as ps "hledger" content -- hamletToRepHtml $ template "" s getStyleCss :: Handler HledgerWebApp RepPlain @@ -125,8 +129,8 @@ getStyleCss = do header "Content-Type" "text/css" return $ RepPlain $ toContent s -template :: Request -> [String] -> [String] -> String -> String -> Hamlet String -template req as ps t s = [$hamlet| +template :: Request -> Maybe (Html ()) -> [String] -> [String] -> String -> String -> Hamlet String +template req msg as ps t s = [$hamlet| !!! %html %head @@ -135,12 +139,14 @@ template req as ps t s = [$hamlet| %title $string.t$ %body ^navbar'^ - #messages $string.msgs$ + #messages $m$ + ^addform'^ #content %pre $string.s$ |] - where msgs = intercalate ", " [] + where m = fromMaybe (string "") msg navbar' = navbar req as ps + addform' = addform req as ps stylesheet = "/style.css" metacontent = "text/html; charset=utf-8" @@ -196,119 +202,100 @@ searchform req as ps = [$hamlet| helplink topic = [$hamlet|%a!href=@u@ ?|] where u = manualurl ++ if null topic then "" else '#':topic -{- - -addform :: Hack.Env -> HSP XML -addform env = do - today <- io $ liftM showDate $ getCurrentDay - let inputs = Hack.Contrib.Request.inputs env - date = decodeString $ fromMaybe today $ lookup "date" inputs - desc = decodeString $ fromMaybe "" $ lookup "desc" inputs -
-
-
- - - - - <% transactionfields 1 env %> - <% transactionfields 2 env %> - -
- Date: <% help "dates" %><% nbsp %> - Description: <% nbsp %> -
<% help "file-format" %>
-
-
-
-
+addform :: Request -> [String] -> [String] -> Hamlet String +addform _ _ _ = [$hamlet| + %form#addform!action=$string.action$!method=POST + %table!border=0 + %tr + %td + Date: + %input!size=15!name=date!value=$string.date$ + ^datehelp^ $ + Description: + %input!size=35!name=desc!value=$string.desc$ $ + ^transactionfields1^ + ^transactionfields2^ + %tr#addbuttonrow + %td + %input!type=submit!value=$string.addlabel$ + ^addhelp^ +
+|] + where + datehelp = helplink "dates" + addlabel = "add transaction" + addhelp = helplink "file-format" + action="" + date = "" + desc = "" + transactionfields1 = transactionfields 1 + transactionfields2 = transactionfields 2 -transactionfields :: Int -> Hack.Env -> HSP XML -transactionfields n env = do - let inputs = Hack.Contrib.Request.inputs env - acct = decodeString $ fromMaybe "" $ lookup acctvar inputs - amt = decodeString $ fromMaybe "" $ lookup amtvar inputs - - - <% nbsp %><% nbsp %> - Account: <% nbsp %> - Amount: <% nbsp %> - - - where - numbered = (++ show n) - acctvar = numbered "acct" - amtvar = numbered "amt" +transactionfields :: Int -> Hamlet String +transactionfields n = [$hamlet| + %tr + %td +    + Account: + %input!size=35!name=$string.acctvar$!value=$string.acct$ +   + Amount: + %input!size=15!name=$string.amtvar$!value=$string.amt$ $ +|] + where + acct = "" + amt = "" + numbered = (++ show n) + acctvar = numbered "acct" + amtvar = numbered "amt" -handleAddform :: Journal -> AppUnit -handleAddform j = do - env <- getenv - d <- io getCurrentDay - t <- io getCurrentLocalTime - handle t $ validate env d - where - validate :: Hack.Env -> Day -> Failing Transaction - validate env today = - let inputs = Hack.Contrib.Request.inputs env - date = decodeString $ fromMaybe "today" $ lookup "date" inputs - desc = decodeString $ fromMaybe "" $ lookup "desc" inputs - acct1 = decodeString $ fromMaybe "" $ lookup "acct1" inputs - amt1 = decodeString $ fromMaybe "" $ lookup "amt1" inputs - acct2 = decodeString $ fromMaybe "" $ lookup "acct2" inputs - amt2 = decodeString $ fromMaybe "" $ lookup "amt2" inputs - validateDate "" = ["missing date"] - validateDate _ = [] - validateDesc "" = ["missing description"] - validateDesc _ = [] - validateAcct1 "" = ["missing account 1"] - validateAcct1 _ = [] - validateAmt1 "" = ["missing amount 1"] - validateAmt1 _ = [] - validateAcct2 "" = ["missing account 2"] - validateAcct2 _ = [] - validateAmt2 _ = [] - amt1' = either (const missingamt) id $ parse someamount "" amt1 - amt2' = either (const missingamt) id $ parse someamount "" amt2 - (date', dateparseerr) = case fixSmartDateStrEither today date of - Right d -> (d, []) - Left e -> ("1900/01/01", [showDateParseError e]) - t = Transaction { - tdate = parsedate date' -- date' must be parseable - ,teffectivedate=Nothing - ,tstatus=False - ,tcode="" - ,tdescription=desc - ,tcomment="" - ,tpostings=[ - Posting False acct1 amt1' "" RegularPosting (Just t') - ,Posting False acct2 amt2' "" RegularPosting (Just t') - ] - ,tpreceding_comment_lines="" - } - (t', balanceerr) = case balanceTransaction t of - Right t'' -> (t'', []) - Left e -> (t, [head $ lines e]) -- show just the error not the transaction - errs = concat [ - validateDate date - ,dateparseerr - ,validateDesc desc - ,validateAcct1 acct1 - ,validateAmt1 amt1 - ,validateAcct2 acct2 - ,validateAmt2 amt2 - ,balanceerr - ] - in - case null errs of - False -> Failure errs - True -> Success t' +postTransactionsPage :: Handler HledgerWebApp RepPlain +postTransactionsPage = do + today <- liftIO getCurrentDay + -- get form input values, or basic validation errors. E suffix means an Either value. + dateE <- runFormPost $ catchFormError $ notEmpty $ required $ input "date" + descE <- runFormPost $ catchFormError $ required $ input "desc" + acct1E <- runFormPost $ catchFormError $ notEmpty $ required $ input "acct1" + amt1E <- runFormPost $ catchFormError $ required $ input "amt1" + acct2E <- runFormPost $ catchFormError $ notEmpty $ required $ input "acct2" + amt2E <- runFormPost $ catchFormError $ required $ input "amt2" + -- supply defaults and parse date and amounts, or get errors. + let dateE' = either Left (either (\e -> Left [("date", showDateParseError e)]) Right . fixSmartDateStrEither today) dateE + amt1E' = either Left (either (const (Right missingamt)) Right . parse someamount "") amt1E -- XXX missingamt only when missing/empty + amt2E' = either Left (either (const (Right missingamt)) Right . parse someamount "") amt2E + strEs = [dateE', descE, acct1E, acct2E] + amtEs = [amt1E', amt2E'] + errs = lefts strEs ++ lefts amtEs + [date,desc,acct1,acct2] = rights strEs + [amt1,amt2] = rights amtEs + -- if no errors so far, generate a transaction and balance it or get the error. + tE | not $ null errs = Left errs + | otherwise = either (\e -> Left [[("unbalanced postings", head $ lines e)]]) Right + (balanceTransaction $ nulltransaction { + tdate=parsedate date + ,teffectivedate=Nothing + ,tstatus=False + ,tcode="" + ,tdescription=desc + ,tcomment="" + ,tpostings=[ + Posting False acct1 amt1 "" RegularPosting Nothing + ,Posting False acct2 amt2 "" RegularPosting Nothing + ] + ,tpreceding_comment_lines="" + }) + -- display errors or add transaction + case tE of + Left errs -> do + -- save current form values in session + setMessage $ string $ intercalate ", " $ map (intercalate ", " . map (\(a,b) -> a++": "++b)) errs + redirect RedirectTemporary TransactionsPage - handle :: LocalTime -> Failing Transaction -> AppUnit - handle _ (Failure errs) = hsp errs addform - handle ti (Success t) = do - io $ journalAddTransaction j t >> reload j - ledgerpage [msg] j (showTransactions (optsToFilterSpec [] [] ti)) - where msg = printf "Added transaction:\n%s" (show t) + Right t -> do + let t' = txnTieKnot t -- XXX move into balanceTransaction + j <- liftIO $ fromJust `fmap` getValue "hledger" "journal" + j' <- liftIO $ journalAddTransaction j t' >>= journalReload + liftIO $ putValue "hledger" "journal" j' + setMessage $ string $ printf "Added transaction:\n%s" (show t') + redirect RedirectTemporary TransactionsPage --}