hledger-web 1.17.1 → 1.18
raw patch · 17 files changed
+638/−173 lines, 17 filesdep +unordered-containersdep ~aesondep ~hledgerdep ~hledger-libPVP ok
version bump matches the API change (PVP)
Dependencies added: unordered-containers
Dependency ranges changed: aeson, hledger, hledger-lib
API changes (from Hackage documentation)
+ Hledger.Web.Import: setDescription :: MonadWidget m => Text -> m ()
+ Hledger.Web.Import: setDescriptionI :: (MonadWidget m, RenderMessage (HandlerSite m) msg) => msg -> m ()
+ Hledger.Web.Import: setOGImage :: MonadWidget m => Text -> m ()
+ Hledger.Web.Import: setOGType :: MonadWidget m => Text -> m ()
+ Hledger.Web.Widget.Common: removeInacct :: Text -> [Text]
+ Hledger.Web.Widget.Common: replaceInacct :: Text -> Text -> Text
+ Hledger.Web.Widget.Common: transactionFragment :: Journal -> Transaction -> String
- Hledger.Web.Widget.Common: balanceReportAsHtml :: Eq r => (r, r) -> r -> Bool -> Journal -> [QueryOpt] -> BalanceReport -> HtmlUrl r
+ Hledger.Web.Widget.Common: balanceReportAsHtml :: Eq r => (r, r) -> r -> Bool -> Journal -> Text -> [QueryOpt] -> BalanceReport -> HtmlUrl r
Files
- CHANGES.md +18/−6
- Hledger/Web/Foundation.hs +1/−1
- Hledger/Web/Handler/JournalR.hs +6/−3
- Hledger/Web/Handler/RegisterR.hs +6/−3
- Hledger/Web/Widget/Common.hs +25/−2
- README +0/−1
- README.md +10/−0
- app/main.hs +0/−2
- hledger-web.1 +183/−28
- hledger-web.cabal +21/−19
- hledger-web.info +178/−41
- hledger-web.txt +161/−24
- static/hledger.js +21/−31
- templates/add-form.hamlet +0/−4
- templates/balance-report.hamlet +4/−4
- templates/journal.hamlet +2/−2
- templates/register.hamlet +2/−2
CHANGES.md view
@@ -1,15 +1,27 @@ User-visible changes in hledger-web. See also the hledger changelog. +# 1.18 2020-06-07 +- Hyperlinks are now more robust when there are multiple journal+ files, eg links from register to journal now work properly. (#1041)++## add form++- Fixed a 2016 regression causing too many rows to be added by+ keypresses in the last amount field or CTRL-plus (#422, #1059).++- Always start with four rows when opened.++- Drop unneeded C-minus/C-plus keys & related help text.++ # 1.17.1 2020-03-19 -- require newer Decimal, math-functions, fixing inconsistent rounding- Decimal 0.5.1+ changed to banker's rounding (round to nearest even- number), and math-functions 0.3.3.0 (used by roi) fixed various- precision-related issues. Now we require the latest versions of these.- This was causing some functional test failures when building with old- GHCs/snapshots.+- require newer Decimal, math-functions libs to ensure consistent+ rounding behaviour, even when built with old GHCs/snapshots. + hledger uses banker's rounding (rounds to nearest even number, eg+ 0.5 displayed with zero decimal places is "0"). # 1.17 2020-03-01
Hledger/Web/Foundation.hs view
@@ -122,7 +122,7 @@ -- flip the default for items with zero amounts, show them by default ropts' = ropts { empty_ = not (empty_ ropts) } accounts =- balanceReportAsHtml (JournalR, RegisterR) here hideEmptyAccts j qopts $+ balanceReportAsHtml (JournalR, RegisterR) here hideEmptyAccts j q qopts $ balanceReport ropts' m j topShowmd = if showSidebar then "col-md-4" else "col-any-0" :: Text
Hledger/Web/Handler/JournalR.hs view
@@ -12,20 +12,23 @@ import Hledger.Web.Import import Hledger.Web.WebOptions import Hledger.Web.Widget.AddForm (addModal)-import Hledger.Web.Widget.Common (accountQuery, mixedAmountAsHtml)+import Hledger.Web.Widget.Common+ (accountQuery, mixedAmountAsHtml,+ transactionFragment, replaceInacct) -- | The formatted journal view, with sidebar. getJournalR :: Handler Html getJournalR = do checkServerSideUiEnabled- VD{caps, j, m, opts, qopts, today} <- getViewData+ VD{caps, j, m, opts, q, qopts, today} <- getViewData when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") let title = case inAccount qopts of Nothing -> "General Journal" Just (a, inclsubs) -> "Transactions in " <> a <> if inclsubs then "" else " (excluding subaccounts)" title' = title <> if m /= Any then ", filtered" else ""- acctlink a = (RegisterR, [("q", accountQuery a)])+ acctlink a = (RegisterR, [("q", replaceInacct q $ accountQuery a)]) (_, items) = transactionsReport (reportopts_ $ cliopts_ opts) j m+ transactionFrag = transactionFragment j defaultLayout $ do setTitle "journal - hledger-web"
Hledger/Web/Handler/RegisterR.hs view
@@ -17,13 +17,15 @@ import Hledger.Web.Import import Hledger.Web.WebOptions import Hledger.Web.Widget.AddForm (addModal)-import Hledger.Web.Widget.Common (accountQuery, mixedAmountAsHtml)+import Hledger.Web.Widget.Common+ (accountQuery, mixedAmountAsHtml,+ transactionFragment, removeInacct, replaceInacct) -- | The main journal/account register view, with accounts sidebar. getRegisterR :: Handler Html getRegisterR = do checkServerSideUiEnabled- VD{caps, j, m, opts, qopts, today} <- getViewData+ VD{caps, j, m, opts, q, qopts, today} <- getViewData when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") let (a,inclsubs) = fromMaybe ("all accounts",True) $ inAccount qopts@@ -33,7 +35,7 @@ let ropts = reportopts_ (cliopts_ opts) acctQuery = fromMaybe Any (inAccountQuery qopts)- acctlink acc = (RegisterR, [("q", accountQuery acc)])+ acctlink acc = (RegisterR, [("q", replaceInacct q $ accountQuery acc)]) otherTransAccounts = map (\(acct,(name,comma)) -> (acct, (T.pack name, T.pack comma))) . undecorateLinks . elideRightDecorated 40 . decorateLinks .@@ -44,6 +46,7 @@ tail $ (", "<$xs) ++ [""] r@(balancelabel,items) = accountTransactionsReport ropts j m acctQuery balancelabel' = if isJust (inAccount qopts) then balancelabel else "Total"+ transactionFrag = transactionFragment j defaultLayout $ do setTitle "register - hledger-web" $(widgetFile "register")
Hledger/Web/Widget/Common.hs view
@@ -14,6 +14,9 @@ , fromFormSuccess , writeJournalTextIfValidAndChanged , journalFile404+ , transactionFragment+ , removeInacct+ , replaceInacct ) where import Data.Default (def)@@ -23,17 +26,20 @@ #endif import Data.Text (Text) import qualified Data.Text as T+import qualified Data.HashMap.Strict as HashMap import System.FilePath (takeFileName) import Text.Blaze ((!), textValue) import qualified Text.Blaze.Html5 as H import qualified Text.Blaze.Html5.Attributes as A import Text.Blaze.Internal (preEscapedString) import Text.Hamlet (hamletFile)+import Text.Printf (printf) import Yesod import Hledger import Hledger.Cli.Utils (writeFileWithBackupIfChanged) import Hledger.Web.Settings (manualurl)+import qualified Hledger.Query as Query #if MIN_VERSION_yesod(1,6,0) journalFile404 :: FilePath -> Journal -> HandlerFor m (FilePath, Text)@@ -79,8 +85,8 @@ where u = textValue $ manualurl <> if T.null topic then "" else T.cons '#' topic -- | Render a "BalanceReport" as html.-balanceReportAsHtml :: Eq r => (r, r) -> r -> Bool -> Journal -> [QueryOpt] -> BalanceReport -> HtmlUrl r-balanceReportAsHtml (journalR, registerR) here hideEmpty j qopts (items, total) =+balanceReportAsHtml :: Eq r => (r, r) -> r -> Bool -> Journal -> Text -> [QueryOpt] -> BalanceReport -> HtmlUrl r+balanceReportAsHtml (journalR, registerR) here hideEmpty j q qopts (items, total) = $(hamletFile "templates/balance-report.hamlet") where l = ledgerFromJournal Any j@@ -103,3 +109,20 @@ c = case isNegativeMixedAmount b of Just True -> "negative amount" _ -> "positive amount"++transactionFragment :: Journal -> Transaction -> String+transactionFragment j =+ let hm = HashMap.fromList $ zip (map fst $ jfiles j) [(1::Integer) ..]+ in \t ->+ printf "transaction-%d-%d"+ (hm HashMap.! sourceFilePath (tsourcepos t)) (tindex t)++removeInacct :: Text -> [Text]+removeInacct =+ map quoteIfSpaced .+ filter (\term ->+ not $ T.isPrefixOf "inacct:" term || T.isPrefixOf "inacctonly:" term) .+ Query.words'' Query.prefixes++replaceInacct :: Text -> Text -> Text+replaceInacct q acct = T.unwords $ acct : removeInacct q
− README
@@ -1,1 +0,0 @@-A basic web UI for hledger data. Intended to be robust and somewhat useful.
+ README.md view
@@ -0,0 +1,10 @@+# hledger-web++A simple web-based user interface for the hledger accounting system,+providing a more modern UI than the command-line or terminal interfaces.+It can be used as a local single-user UI, or as a multi-user UI for+viewing/adding/editing on the web.++See also:+the [project README](https://hledger.org/README.html)+and [home page](https://hledger.org).
app/main.hs view
@@ -1,5 +1,3 @@-import Prelude (IO)- import Hledger.Web.Main main :: IO ()
hledger-web.1 view
@@ -1,5 +1,5 @@ -.TH "hledger-web" "1" "March 2020" "hledger-web 1.17.1" "hledger User Manuals"+.TH "hledger-web" "1" "June 2020" "hledger-web 1.18" "hledger User Manuals" @@ -316,60 +316,215 @@ that both machine clocks are roughly in step.) .SH JSON API .PP-In addition to the web UI, hledger-web provides some API routes that-serve JSON in response to GET requests.-(And when started with \f[C]--serve-api\f[R], it provides only these-routes.):+In addition to the web UI, hledger-web also serves a JSON API that can+be used to get data or add new transactions.+If you want the JSON API only, you can use the \f[C]--serve-api\f[R]+flag.+Eg: .IP .nf \f[C]+$ hledger-web -f examples/sample.journal --serve-api+\&...+\f[R]+.fi+.PP+You can get JSON data from these routes:+.IP+.nf+\f[C] /accountnames /transactions /prices /commodities /accounts-/accounttransactions/#AccountName+/accounttransactions/ACCOUNTNAME \f[R] .fi .PP-Also, you can append a new transaction to the journal by sending a PUT-request to \f[C]/add\f[R] (hledger-web only).-As with the web UI\[aq]s add form, hledger-web must be started with the-\f[C]add\f[R] capability for this (enabled by default).+Eg, all account names in the journal (similar to the accounts command).+(hledger-web\[aq]s JSON does not include newlines, here we use python to+prettify it):+.IP+.nf+\f[C]+$ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool+[+ \[dq]assets\[dq],+ \[dq]assets:bank\[dq],+ \[dq]assets:bank:checking\[dq],+ \[dq]assets:bank:saving\[dq],+ \[dq]assets:cash\[dq],+ \[dq]expenses\[dq],+ \[dq]expenses:food\[dq],+ \[dq]expenses:supplies\[dq],+ \[dq]income\[dq],+ \[dq]income:gifts\[dq],+ \[dq]income:salary\[dq],+ \[dq]liabilities\[dq],+ \[dq]liabilities:debts\[dq]+]+\f[R]+.fi .PP-The payload should be a valid hledger transaction as JSON, similar to-what you get from \f[C]/transactions\f[R] or-\f[C]/accounttransactions\f[R].+Or all transactions:+.IP+.nf+\f[C]+$ curl -s http://127.0.0.1:5000/transactions | python -m json.tool+[+ {+ \[dq]tcode\[dq]: \[dq]\[dq],+ \[dq]tcomment\[dq]: \[dq]\[dq],+ \[dq]tdate\[dq]: \[dq]2008-01-01\[dq],+ \[dq]tdate2\[dq]: null,+ \[dq]tdescription\[dq]: \[dq]income\[dq],+ \[dq]tindex\[dq]: 1,+ \[dq]tpostings\[dq]: [+ {+ \[dq]paccount\[dq]: \[dq]assets:bank:checking\[dq],+ \[dq]pamount\[dq]: [+ {+ \[dq]acommodity\[dq]: \[dq]$\[dq],+ \[dq]aismultiplier\[dq]: false,+ \[dq]aprice\[dq]: null,+\&...+\f[R]+.fi .PP-Another way to generate test data is with the-\f[C]readJsonFile\f[R]/\f[C]writeJsonFile\f[R] helpers in-Hledger.Web.Json, which can write or read most of hledger\[aq]s data-types to or from a file.-Eg here we write the first transaction of a sample journal:+Most of the JSON corresponds to hledger\[aq]s data types; for details of+what the fields mean, see the Hledger.Data.Json haddock docs and click+on the various data types, eg Transaction.+And for a higher level understanding, see the journal manual.+.PP+In some cases there is outer JSON corresponding to a \[dq]Report\[dq]+type.+To understand that, go to the Hledger.Web.Handler.MiscR haddock and look+at the source for the appropriate handler to see what it returns.+Eg for \f[C]/accounttransactions\f[R] it\[aq]s getAccounttransactionsR,+returning a \[dq]\f[C]accountTransactionsReport ...\f[R]\[dq].+Looking up the haddock for that we can see that /accounttransactions+returns an AccountTransactionsReport, which consists of a report title+and a list of AccountTransactionsReportItem (etc).+.PP+You can add a new transaction to the journal with a PUT request to+\f[C]/add\f[R], if hledger-web was started with the \f[C]add\f[R]+capability (enabled by default).+The payload must be the full, exact JSON representation of a hledger+transaction (partial data won\[aq]t do).+You can get sample JSON from hledger-web\[aq]s \f[C]/transactions\f[R]+or \f[C]/accounttransactions\f[R], or you can export it with+hledger-lib, eg like so: .IP .nf \f[C]-$ make ghci-web->>> :m +*Hledger.Web.Json+\&.../hledger$ stack ghci hledger-lib >>> writeJsonFile \[dq]txn.json\[dq] (head $ jtxns samplejournal) >>> :q-$ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable \f[R] .fi .PP-(sample output & discussion)-.PP-And here\[aq]s how to test adding that with curl:+Here\[aq]s how it looks as of hledger-1.17 (remember, this JSON+corresponds to hledger\[aq]s Transaction and related data types): .IP .nf \f[C]-$ curl -s http://127.0.0.1:5000/add -X PUT -H \[aq]Content-Type: application/json\[aq] --data-binary \[at]txn.pretty.json; echo+{+ \[dq]tcomment\[dq]: \[dq]\[dq],+ \[dq]tpostings\[dq]: [+ {+ \[dq]pbalanceassertion\[dq]: null,+ \[dq]pstatus\[dq]: \[dq]Unmarked\[dq],+ \[dq]pamount\[dq]: [+ {+ \[dq]aprice\[dq]: null,+ \[dq]acommodity\[dq]: \[dq]$\[dq],+ \[dq]aquantity\[dq]: {+ \[dq]floatingPoint\[dq]: 1,+ \[dq]decimalPlaces\[dq]: 10,+ \[dq]decimalMantissa\[dq]: 10000000000+ },+ \[dq]aismultiplier\[dq]: false,+ \[dq]astyle\[dq]: {+ \[dq]ascommodityside\[dq]: \[dq]L\[dq],+ \[dq]asdigitgroups\[dq]: null,+ \[dq]ascommodityspaced\[dq]: false,+ \[dq]asprecision\[dq]: 2,+ \[dq]asdecimalpoint\[dq]: \[dq].\[dq]+ }+ }+ ],+ \[dq]ptransaction_\[dq]: \[dq]1\[dq],+ \[dq]paccount\[dq]: \[dq]assets:bank:checking\[dq],+ \[dq]pdate\[dq]: null,+ \[dq]ptype\[dq]: \[dq]RegularPosting\[dq],+ \[dq]pcomment\[dq]: \[dq]\[dq],+ \[dq]pdate2\[dq]: null,+ \[dq]ptags\[dq]: [],+ \[dq]poriginal\[dq]: null+ },+ {+ \[dq]pbalanceassertion\[dq]: null,+ \[dq]pstatus\[dq]: \[dq]Unmarked\[dq],+ \[dq]pamount\[dq]: [+ {+ \[dq]aprice\[dq]: null,+ \[dq]acommodity\[dq]: \[dq]$\[dq],+ \[dq]aquantity\[dq]: {+ \[dq]floatingPoint\[dq]: -1,+ \[dq]decimalPlaces\[dq]: 10,+ \[dq]decimalMantissa\[dq]: -10000000000+ },+ \[dq]aismultiplier\[dq]: false,+ \[dq]astyle\[dq]: {+ \[dq]ascommodityside\[dq]: \[dq]L\[dq],+ \[dq]asdigitgroups\[dq]: null,+ \[dq]ascommodityspaced\[dq]: false,+ \[dq]asprecision\[dq]: 2,+ \[dq]asdecimalpoint\[dq]: \[dq].\[dq]+ }+ }+ ],+ \[dq]ptransaction_\[dq]: \[dq]1\[dq],+ \[dq]paccount\[dq]: \[dq]income:salary\[dq],+ \[dq]pdate\[dq]: null,+ \[dq]ptype\[dq]: \[dq]RegularPosting\[dq],+ \[dq]pcomment\[dq]: \[dq]\[dq],+ \[dq]pdate2\[dq]: null,+ \[dq]ptags\[dq]: [],+ \[dq]poriginal\[dq]: null+ }+ ],+ \[dq]ttags\[dq]: [],+ \[dq]tsourcepos\[dq]: {+ \[dq]tag\[dq]: \[dq]JournalSourcePos\[dq],+ \[dq]contents\[dq]: [+ \[dq]\[dq],+ [+ 1,+ 1+ ]+ ]+ },+ \[dq]tdate\[dq]: \[dq]2008-01-01\[dq],+ \[dq]tcode\[dq]: \[dq]\[dq],+ \[dq]tindex\[dq]: 1,+ \[dq]tprecedingcomment\[dq]: \[dq]\[dq],+ \[dq]tdate2\[dq]: null,+ \[dq]tdescription\[dq]: \[dq]income\[dq],+ \[dq]tstatus\[dq]: \[dq]Unmarked\[dq]+} \f[R] .fi .PP-By default, both the server-side HTML UI and the JSON API are served.-Running with \f[C]--serve-api\f[R] disables the former, useful if you-only want to serve the API.+And here\[aq]s how to test adding it with curl.+This should add a new entry to your journal:+.IP+.nf+\f[C]+$ curl http://127.0.0.1:5000/add -X PUT -H \[aq]Content-Type: application/json\[aq] --data-binary \[at]txn.json+\f[R]+.fi .SH ENVIRONMENT .PP \f[B]LEDGER_FILE\f[R] The journal file path when not specified with
hledger-web.cabal view
@@ -1,24 +1,25 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: 9a8cb3ef7223d8bdfbc8e2c5a8caa8d9c80b645289d749fdec200b98094ecd60+-- hash: daab2dd5e0ca3ed383252d6577500975576694432c856b386d9cbc2c5f7b75c0 name: hledger-web-version: 1.17.1-synopsis: Web interface for the hledger accounting tool-description: This is hledger's web interface.- It provides a more user-friendly and collaborative UI than the- command-line or terminal interfaces.+version: 1.18+synopsis: Web-based user interface for the hledger accounting system+description: A simple web-based user interface for the hledger accounting system,+ providing a more modern UI than the command-line or terminal interfaces.+ It can be used as a local single-user UI, or as a multi-user UI for+ viewing/adding/editing on the web. .- hledger is a cross-platform program for tracking money, time, or- any other commodity, using double-entry accounting and a simple,- editable file format. It is inspired by and largely compatible- with ledger(1). hledger provides command-line, terminal and web- interfaces, and aims to be a reliable, practical tool for daily- use.+ hledger is a robust, cross-platform set of tools for tracking money,+ time, or any other commodity, using double-entry accounting and a+ simple, editable file format, with command-line, terminal and web+ interfaces. It is a Haskell rewrite of Ledger, and one of the leading+ implementations of Plain Text Accounting. Read more at:+ <https://hledger.org> category: Finance stability: stable homepage: http://hledger.org@@ -27,11 +28,11 @@ maintainer: Simon Michael <simon@joyful.com> license: GPL-3 license-file: LICENSE-tested-with: GHC==8.2.2, GHC==8.4.3, GHC==8.6.5, GHC==8.8.2, GHC==8.10+tested-with: GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC==8.10.0.20200123 build-type: Simple extra-source-files: CHANGES.md- README+ README.md config/favicon.ico config/keter.yaml config/robots.txt@@ -150,7 +151,7 @@ hs-source-dirs: ./. ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints- cpp-options: -DVERSION="1.17.1"+ cpp-options: -DVERSION="1.18" build-depends: Decimal >=0.5.1 , aeson@@ -169,8 +170,8 @@ , extra >=1.6.3 , filepath , hjsmin- , hledger >=1.17.1 && <1.18- , hledger-lib >=1.17.1 && <1.18+ , hledger >=1.18 && <1.19+ , hledger-lib >=1.18 && <1.19 , http-client , http-conduit , http-types@@ -184,6 +185,7 @@ , time >=1.5 , transformers , unix-compat+ , unordered-containers , utf8-string , wai , wai-cors@@ -208,7 +210,7 @@ hs-source-dirs: app ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints- cpp-options: -DVERSION="1.17.1"+ cpp-options: -DVERSION="1.18" build-depends: base , hledger-web
hledger-web.info view
@@ -3,8 +3,8 @@ File: hledger-web.info, Node: Top, Next: OPTIONS, Up: (dir) -hledger-web(1) hledger-web 1.17.1-*********************************+hledger-web(1) hledger-web 1.18+******************************* hledger-web - web interface for the hledger accounting tool @@ -330,45 +330,182 @@ 5 JSON API ********** -In addition to the web UI, hledger-web provides some API routes that-serve JSON in response to GET requests. (And when started with-'--serve-api', it provides only these routes.):+In addition to the web UI, hledger-web also serves a JSON API that can+be used to get data or add new transactions. If you want the JSON API+only, you can use the '--serve-api' flag. Eg: +$ hledger-web -f examples/sample.journal --serve-api+...++ You can get JSON data from these routes:+ /accountnames /transactions /prices /commodities /accounts-/accounttransactions/#AccountName+/accounttransactions/ACCOUNTNAME - Also, you can append a new transaction to the journal by sending a-PUT request to '/add' (hledger-web only). As with the web UI's add-form, hledger-web must be started with the 'add' capability for this-(enabled by default).+ Eg, all account names in the journal (similar to the accounts+command). (hledger-web's JSON does not include newlines, here we use+python to prettify it): - The payload should be a valid hledger transaction as JSON, similar to-what you get from '/transactions' or '/accounttransactions'.+$ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool+[+ "assets",+ "assets:bank",+ "assets:bank:checking",+ "assets:bank:saving",+ "assets:cash",+ "expenses",+ "expenses:food",+ "expenses:supplies",+ "income",+ "income:gifts",+ "income:salary",+ "liabilities",+ "liabilities:debts"+] - Another way to generate test data is with the-'readJsonFile'/'writeJsonFile' helpers in Hledger.Web.Json, which can-write or read most of hledger's data types to or from a file. Eg here-we write the first transaction of a sample journal:+ Or all transactions: -$ make ghci-web->>> :m +*Hledger.Web.Json+$ curl -s http://127.0.0.1:5000/transactions | python -m json.tool+[+ {+ "tcode": "",+ "tcomment": "",+ "tdate": "2008-01-01",+ "tdate2": null,+ "tdescription": "income",+ "tindex": 1,+ "tpostings": [+ {+ "paccount": "assets:bank:checking",+ "pamount": [+ {+ "acommodity": "$",+ "aismultiplier": false,+ "aprice": null,+...++ Most of the JSON corresponds to hledger's data types; for details of+what the fields mean, see the Hledger.Data.Json haddock docs and click+on the various data types, eg Transaction. And for a higher level+understanding, see the journal manual.++ In some cases there is outer JSON corresponding to a "Report" type.+To understand that, go to the Hledger.Web.Handler.MiscR haddock and look+at the source for the appropriate handler to see what it returns. Eg+for '/accounttransactions' it's getAccounttransactionsR, returning a+"'accountTransactionsReport ...'". Looking up the haddock for that we+can see that /accounttransactions returns an AccountTransactionsReport,+which consists of a report title and a list of+AccountTransactionsReportItem (etc).++ You can add a new transaction to the journal with a PUT request to+'/add', if hledger-web was started with the 'add' capability (enabled by+default). The payload must be the full, exact JSON representation of a+hledger transaction (partial data won't do). You can get sample JSON+from hledger-web's '/transactions' or '/accounttransactions', or you can+export it with hledger-lib, eg like so:++.../hledger$ stack ghci hledger-lib >>> writeJsonFile "txn.json" (head $ jtxns samplejournal) >>> :q-$ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable - (sample output & discussion)+ Here's how it looks as of hledger-1.17 (remember, this JSON+corresponds to hledger's Transaction and related data types): - And here's how to test adding that with curl:+{+ "tcomment": "",+ "tpostings": [+ {+ "pbalanceassertion": null,+ "pstatus": "Unmarked",+ "pamount": [+ {+ "aprice": null,+ "acommodity": "$",+ "aquantity": {+ "floatingPoint": 1,+ "decimalPlaces": 10,+ "decimalMantissa": 10000000000+ },+ "aismultiplier": false,+ "astyle": {+ "ascommodityside": "L",+ "asdigitgroups": null,+ "ascommodityspaced": false,+ "asprecision": 2,+ "asdecimalpoint": "."+ }+ }+ ],+ "ptransaction_": "1",+ "paccount": "assets:bank:checking",+ "pdate": null,+ "ptype": "RegularPosting",+ "pcomment": "",+ "pdate2": null,+ "ptags": [],+ "poriginal": null+ },+ {+ "pbalanceassertion": null,+ "pstatus": "Unmarked",+ "pamount": [+ {+ "aprice": null,+ "acommodity": "$",+ "aquantity": {+ "floatingPoint": -1,+ "decimalPlaces": 10,+ "decimalMantissa": -10000000000+ },+ "aismultiplier": false,+ "astyle": {+ "ascommodityside": "L",+ "asdigitgroups": null,+ "ascommodityspaced": false,+ "asprecision": 2,+ "asdecimalpoint": "."+ }+ }+ ],+ "ptransaction_": "1",+ "paccount": "income:salary",+ "pdate": null,+ "ptype": "RegularPosting",+ "pcomment": "",+ "pdate2": null,+ "ptags": [],+ "poriginal": null+ }+ ],+ "ttags": [],+ "tsourcepos": {+ "tag": "JournalSourcePos",+ "contents": [+ "",+ [+ 1,+ 1+ ]+ ]+ },+ "tdate": "2008-01-01",+ "tcode": "",+ "tindex": 1,+ "tprecedingcomment": "",+ "tdate2": null,+ "tdescription": "income",+ "tstatus": "Unmarked"+} -$ curl -s http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.pretty.json; echo+ And here's how to test adding it with curl. This should add a new+entry to your journal: - By default, both the server-side HTML UI and the JSON API are served.-Running with '--serve-api' disables the former, useful if you only want-to serve the API.+$ curl http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.json File: hledger-web.info, Node: ENVIRONMENT, Next: FILES, Prev: JSON API, Up: Top@@ -427,22 +564,22 @@ Tag Table: Node: Top72-Node: OPTIONS1750-Ref: #options1855-Node: PERMISSIONS8199-Ref: #permissions8338-Node: EDITING UPLOADING DOWNLOADING9550-Ref: #editing-uploading-downloading9731-Node: RELOADING10565-Ref: #reloading10699-Node: JSON API11132-Ref: #json-api11246-Node: ENVIRONMENT12684-Ref: #environment12800-Node: FILES13533-Ref: #files13633-Node: BUGS13846-Ref: #bugs13924+Node: OPTIONS1746+Ref: #options1851+Node: PERMISSIONS8195+Ref: #permissions8334+Node: EDITING UPLOADING DOWNLOADING9546+Ref: #editing-uploading-downloading9727+Node: RELOADING10561+Ref: #reloading10695+Node: JSON API11128+Ref: #json-api11242+Node: ENVIRONMENT16723+Ref: #environment16839+Node: FILES17572+Ref: #files17672+Node: BUGS17885+Ref: #bugs17963 End Tag Table
hledger-web.txt view
@@ -289,45 +289,182 @@ that both machine clocks are roughly in step.) JSON API- In addition to the web UI, hledger-web provides some API routes that- serve JSON in response to GET requests. (And when started with- --serve-api, it provides only these routes.):+ In addition to the web UI, hledger-web also serves a JSON API that can+ be used to get data or add new transactions. If you want the JSON API+ only, you can use the --serve-api flag. Eg: + $ hledger-web -f examples/sample.journal --serve-api+ ...++ You can get JSON data from these routes:+ /accountnames /transactions /prices /commodities /accounts- /accounttransactions/#AccountName+ /accounttransactions/ACCOUNTNAME - Also, you can append a new transaction to the journal by sending a PUT- request to /add (hledger-web only). As with the web UI's add form,- hledger-web must be started with the add capability for this (enabled- by default).+ Eg, all account names in the journal (similar to the accounts command).+ (hledger-web's JSON does not include newlines, here we use python to+ prettify it): - The payload should be a valid hledger transaction as JSON, similar to- what you get from /transactions or /accounttransactions.+ $ curl -s http://127.0.0.1:5000/accountnames | python -m json.tool+ [+ "assets",+ "assets:bank",+ "assets:bank:checking",+ "assets:bank:saving",+ "assets:cash",+ "expenses",+ "expenses:food",+ "expenses:supplies",+ "income",+ "income:gifts",+ "income:salary",+ "liabilities",+ "liabilities:debts"+ ] - Another way to generate test data is with the readJsonFile/writeJson-- File helpers in Hledger.Web.Json, which can write or read most of- hledger's data types to or from a file. Eg here we write the first- transaction of a sample journal:+ Or all transactions: - $ make ghci-web- >>> :m +*Hledger.Web.Json+ $ curl -s http://127.0.0.1:5000/transactions | python -m json.tool+ [+ {+ "tcode": "",+ "tcomment": "",+ "tdate": "2008-01-01",+ "tdate2": null,+ "tdescription": "income",+ "tindex": 1,+ "tpostings": [+ {+ "paccount": "assets:bank:checking",+ "pamount": [+ {+ "acommodity": "$",+ "aismultiplier": false,+ "aprice": null,+ ...++ Most of the JSON corresponds to hledger's data types; for details of+ what the fields mean, see the Hledger.Data.Json haddock docs and click+ on the various data types, eg Transaction. And for a higher level un-+ derstanding, see the journal manual.++ In some cases there is outer JSON corresponding to a "Report" type. To+ understand that, go to the Hledger.Web.Handler.MiscR haddock and look+ at the source for the appropriate handler to see what it returns. Eg+ for /accounttransactions it's getAccounttransactionsR, returning a "ac-+ countTransactionsReport ...". Looking up the haddock for that we can+ see that /accounttransactions returns an AccountTransactionsReport,+ which consists of a report title and a list of AccountTransactionsRe-+ portItem (etc).++ You can add a new transaction to the journal with a PUT request to+ /add, if hledger-web was started with the add capability (enabled by+ default). The payload must be the full, exact JSON representation of a+ hledger transaction (partial data won't do). You can get sample JSON+ from hledger-web's /transactions or /accounttransactions, or you can+ export it with hledger-lib, eg like so:++ .../hledger$ stack ghci hledger-lib >>> writeJsonFile "txn.json" (head $ jtxns samplejournal) >>> :q- $ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable - (sample output & discussion)+ Here's how it looks as of hledger-1.17 (remember, this JSON corresponds+ to hledger's Transaction and related data types): - And here's how to test adding that with curl:+ {+ "tcomment": "",+ "tpostings": [+ {+ "pbalanceassertion": null,+ "pstatus": "Unmarked",+ "pamount": [+ {+ "aprice": null,+ "acommodity": "$",+ "aquantity": {+ "floatingPoint": 1,+ "decimalPlaces": 10,+ "decimalMantissa": 10000000000+ },+ "aismultiplier": false,+ "astyle": {+ "ascommodityside": "L",+ "asdigitgroups": null,+ "ascommodityspaced": false,+ "asprecision": 2,+ "asdecimalpoint": "."+ }+ }+ ],+ "ptransaction_": "1",+ "paccount": "assets:bank:checking",+ "pdate": null,+ "ptype": "RegularPosting",+ "pcomment": "",+ "pdate2": null,+ "ptags": [],+ "poriginal": null+ },+ {+ "pbalanceassertion": null,+ "pstatus": "Unmarked",+ "pamount": [+ {+ "aprice": null,+ "acommodity": "$",+ "aquantity": {+ "floatingPoint": -1,+ "decimalPlaces": 10,+ "decimalMantissa": -10000000000+ },+ "aismultiplier": false,+ "astyle": {+ "ascommodityside": "L",+ "asdigitgroups": null,+ "ascommodityspaced": false,+ "asprecision": 2,+ "asdecimalpoint": "."+ }+ }+ ],+ "ptransaction_": "1",+ "paccount": "income:salary",+ "pdate": null,+ "ptype": "RegularPosting",+ "pcomment": "",+ "pdate2": null,+ "ptags": [],+ "poriginal": null+ }+ ],+ "ttags": [],+ "tsourcepos": {+ "tag": "JournalSourcePos",+ "contents": [+ "",+ [+ 1,+ 1+ ]+ ]+ },+ "tdate": "2008-01-01",+ "tcode": "",+ "tindex": 1,+ "tprecedingcomment": "",+ "tdate2": null,+ "tdescription": "income",+ "tstatus": "Unmarked"+ } - $ curl -s http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.pretty.json; echo+ And here's how to test adding it with curl. This should add a new en-+ try to your journal: - By default, both the server-side HTML UI and the JSON API are served.- Running with --serve-api disables the former, useful if you only want- to serve the API.+ $ curl http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.json ENVIRONMENT LEDGER_FILE The journal file path when not specified with -f. Default:@@ -392,4 +529,4 @@ -hledger-web 1.17.1 March 2020 hledger-web(1)+hledger-web 1.18 June 2020 hledger-web(1)
static/hledger.js view
@@ -33,11 +33,6 @@ $('body').bind('keydown', 'a', function(){ addformShow(); return false; }); $('body').bind('keydown', 'n', function(){ addformShow(); return false; }); $('body').bind('keydown', 'f', function(){ $('#searchform input').focus(); return false; });- $('body, #addform input, #addform select').bind('keydown', 'ctrl++', addformAddPosting);- $('body, #addform input, #addform select').bind('keydown', 'ctrl+shift+=', addformAddPosting);- $('body, #addform input, #addform select').bind('keydown', 'ctrl+=', addformAddPosting);- $('body, #addform input, #addform select').bind('keydown', 'ctrl+-', addformDeletePosting);- $('.amount-input:last').keypress(addformAddPosting); // highlight the entry from the url hash if (window.location.hash && $(window.location.hash)[0]) {@@ -138,18 +133,27 @@ $('#addmodal').modal('show'); } -// Make sure the add form is empty and clean for display.+// Make sure the add form is empty and clean and has the default number of rows. function addformReset(showmsg) { showmsg = typeof showmsg !== 'undefined' ? showmsg : false; if ($('form#addform').length > 0) { if (!showmsg) $('div#message').html('');- $('form#addform')[0].reset();+ $('#addform .account-group.added-row').remove();+ addformLastAmountBindKey();+ $('#addform')[0].reset(); // reset typehead state (though not fetched completions) $('.typeahead').typeahead('val', ''); $('.tt-dropdown-menu').hide(); } } +// Set the add-new-row-on-keypress handler on the add form's current last amount field, only.+// (NB: removes all other keypress handlers from all amount fields).+function addformLastAmountBindKey() {+ $('.amount-input').off('keypress');+ $('.amount-input:last').keypress(addformAddPosting);+}+ // Focus the first add form field. function addformFocus() { focus($('#addform input#date'));@@ -167,28 +171,14 @@ if (!$('#addform').is(':visible')) { return; }-- var prevLastRow = $('#addform .account-group:last');- prevLastRow.off('keypress');-- // Clone the currently last row- $('#addform .account-postings').append(prevLastRow.clone());- var num = $('#addform .account-group').length;-- // clear and renumber the field, add keybindings- // XXX Enable typehead on dynamically created inputs- $('.amount-input:last')- .val('')- .prop('placeholder','Amount ' + num)- .keypress(addformAddPosting);-- $('.account-input:last')- .val('')- .prop('placeholder', 'Account ' + num)- .bind('keydown', 'ctrl++', addformAddPosting)- .bind('keydown', 'ctrl+shift+=', addformAddPosting)- .bind('keydown', 'ctrl+=', addformAddPosting)- .bind('keydown', 'ctrl+-', addformDeletePosting);+ // Clone the old last row to make a new last row+ $('#addform .account-postings').append( $('#addform .account-group:last').clone().addClass('added-row') );+ // renumber and clear the new last account and amount fields+ var n = $('#addform .account-group').length;+ $('.account-input:last').prop('placeholder', 'Account '+n).val('');+ $('.amount-input:last').prop('placeholder','Amount '+n).val(''); // XXX Enable typehead on dynamically created inputs+ // and move the keypress handler to the new last amount field+ addformLastAmountBindKey(); } // Remove the add form's last posting row, if empty, keeping at least two.@@ -205,8 +195,8 @@ if (focuslost) { focus($('.account-input:last')); }- // Rebind keypress- $('.amount-input:last').keypress(addformAddPosting);+ // move the keypress handler to the new last amount field+ addformLastAmountBindKey(); } //----------------------------------------------------------------------
templates/add-form.hamlet view
@@ -65,7 +65,3 @@ $forall p <- journals <option value=#{p}>#{p} <span .small style="padding-left:2em;">- Enter a value in the last field for #- <a href="#" onclick="addformAddPosting(); return false;">- more- \ (or ctrl +, ctrl -)
templates/balance-report.hamlet view
@@ -7,15 +7,15 @@ $forall (acct, adisplay, aindent, abal) <- items <tr :matchesAcctSelector acct:.inacct- :isZeroMixedAmount abal && hideEmpty:.hide>- <td .acct :isZeroMixedAmount abal:.empty>+ :mixedAmountLooksZero abal && hideEmpty:.hide>+ <td .acct :mixedAmountLooksZero abal:.empty> <div .ff-wrapper> \#{indent aindent}- <a.acct-name href="@?{(registerR, [("q", accountQuery acct)])}"+ <a.acct-name href="@?{(registerR, [("q", replaceInacct q $ accountQuery acct)])}" title="Show transactions affecting this account and subaccounts"> #{adisplay} $if hasSubAccounts acct- <a href="@?{(registerR, [("q", accountOnlyQuery acct)])}" .only.hidden-sm.hidden-xs+ <a href="@?{(registerR, [("q", replaceInacct q $ accountOnlyQuery acct)])}" .only.hidden-sm.hidden-xs title="Show transactions affecting this account but not subaccounts">only <td> ^{mixedAmountAsHtml abal}
templates/journal.hamlet view
@@ -15,13 +15,13 @@ <th .amount style="text-align:right;">Amount $forall (torig, _, split, _, amt, _) <- items- <tr .title #transaction-#{tindex torig} title="#{showTransaction torig}">+ <tr .title ##{transactionFrag torig} title="#{showTransaction torig}"> <td .date nowrap> #{show (tdate torig)} <td colspan=2> #{textElideRight 60 (tdescription torig)} <td .amount style="text-align:right;">- $if not split && not (isZeroMixedAmount amt)+ $if not split && not (mixedAmountLooksZero amt) ^{mixedAmountAsHtml amt} $forall Posting { paccount = acc, pamount = amt } <- tpostings torig
templates/register.hamlet view
@@ -21,7 +21,7 @@ $forall (torig, tacct, split, _acct, amt, bal) <- items <tr ##{tindex torig} title="#{showTransaction torig}" style="vertical-align:top;"> <td .date>- <a href="@{JournalR}#transaction-#{tindex torig}">+ <a href="@?{(JournalR, [("q", T.unwords $ removeInacct q)])}##{transactionFrag torig}"> #{show (tdate tacct)} <td> #{textElideRight 30 (tdescription tacct)}@@ -30,7 +30,7 @@ <a href="@?{acctlink acc}##{tindex torig}" title="#{acc}"> #{summName}</a>#{comma} <td .amount style="text-align:right; white-space:nowrap;">- $if not split || not (isZeroMixedAmount amt)+ $if not split || not (mixedAmountLooksZero amt) ^{mixedAmountAsHtml amt} <td style="text-align:right;"> ^{mixedAmountAsHtml bal}