diff --git a/hledger-lib/Hledger/Write/Beancount.hs b/hledger-lib/Hledger/Write/Beancount.hs index e0e1d3302..5c5666086 100644 --- a/hledger-lib/Hledger/Write/Beancount.hs +++ b/hledger-lib/Hledger/Write/Beancount.hs @@ -10,6 +10,7 @@ module Hledger.Write.Beancount ( -- postingAsLinesBeancount, -- showAccountNameBeancount, accountNameToBeancount, + commodityToBeancount, -- beancountTopLevelAccounts, -- * Tests diff --git a/hledger/Hledger/Cli/Commands/Print.hs b/hledger/Hledger/Cli/Commands/Print.hs index a2e53fe91..d36fbac42 100644 --- a/hledger/Hledger/Cli/Commands/Print.hs +++ b/hledger/Hledger/Cli/Commands/Print.hs @@ -44,7 +44,8 @@ import Hledger.Cli.Utils import Hledger.Cli.Anchor (setAccountAnchor) import qualified Lucid import qualified System.IO as IO -import Data.Maybe (isJust) +import Data.Maybe (isJust, catMaybes) +import Hledger.Write.Beancount (commodityToBeancount) printmode = hledgerCommandMode $(embedFileRelative "Hledger/Cli/Commands/Print.txt") @@ -183,11 +184,17 @@ entriesReportAsTextHelper :: (Transaction -> T.Text) -> EntriesReport -> TL.Text entriesReportAsTextHelper showtxn = TB.toLazyText . foldMap (TB.fromText . showtxn) -- This transforms transactions in various ways (see Beancount.hs) to make them Beancount-compatible. --- It also generates an account open directive for each account used (on their earliest transaction dates). +-- It also generates an account open directive for each account used (on their earliest transaction dates), +-- and operating_currency directives based on currencies used in costs. entriesReportAsBeancount :: EntriesReport -> TL.Text entriesReportAsBeancount ts = -- PERF: gathers and converts all account names, then repeats that work when showing each transaction - opendirectives <> "\n" <> entriesReportAsTextHelper showTransactionBeancount ts3 + TL.concat [ + TL.fromStrict operatingcurrencydirectives + ,TL.fromStrict openaccountdirectives + ,"\n" + ,entriesReportAsTextHelper showTransactionBeancount ts3 + ] where -- Remove any virtual postings. ts2 = [t{tpostings=filter isReal $ tpostings t} | t <- ts] @@ -198,7 +205,7 @@ entriesReportAsBeancount ts = ts3 = [ t{tpostings=filter (not . isredundantconvp) $ tpostings t} | t <- ts2 - -- XXX But how to do it ? conversion-posting tag is on non-redundant postings too. + -- XXX But conversion-posting tag is on non-redundant postings too, so how to do it ? -- Assume the simple case of no more than one cost + conversion posting group in each transaction. -- Actually that seems to be required by hledger right now. , let isredundantconvp p = @@ -206,9 +213,48 @@ entriesReportAsBeancount ts = && any (any (isJust.acost) . amounts . pamount) (tpostings t) ] - opendirectives + -- https://fava.pythonanywhere.com/example-beancount-file/help/beancount_syntax + -- https://fava.pythonanywhere.com/example-beancount-file/help/options + -- "conversion-currencies + -- When set, the currency conversion select dropdown in all charts will show the list of currencies specified in this option. + -- By default, Fava lists all operating currencies and those currencies that match ISO 4217 currency codes." + + -- http://furius.ca/beancount/doc/syntax + -- http://furius.ca/beancount/doc/options + -- "This option may be supplied multiple times ... + -- A list of currencies that we single out during reporting and create dedicated columns for ... + -- we use this to display these values in table cells without their associated unit strings ... + -- This is used to indicate the main currencies that you work with in real life" + -- We use: all currencies used in costs. + operatingcurrencydirectives + | null basecurrencies = "" + | otherwise = T.unlines (map (todirective . commodityToBeancount) basecurrencies) <> "\n" + where + todirective c = "option \"operating_currency\" \"" <> c <> "\"" + basecurrencies = allcostcurrencies + where + allcostcurrencies = nubSort $ map acommodity costamounts + where + costamounts = + map (\c -> case c of + UnitCost a -> a + TotalCost a -> a + ) $ + catMaybes $ + map acost $ + concatMap (amounts . pamount) $ + concatMap tpostings + ts3 + + -- http://furius.ca/beancount/doc/syntax + -- "there exists an “Open” directive that is used to provide the start date of each account. + -- That can be located anywhere in the file, it does not have to appear in the file somewhere before you use an account name. + -- You can just start using account names in transactions right away, + -- though all account names that receive postings to them will eventually have to have + -- a corresponding Open directive with a date that precedes all transactions posted to the account in the input file." + openaccountdirectives | null ts = "" - | otherwise = TL.fromStrict $ T.unlines [ + | otherwise = T.unlines [ firstdate <> " open " <> accountNameToBeancount a | a <- nubSort $ concatMap (map paccount.tpostings) ts3 ] diff --git a/hledger/hledger.m4.md b/hledger/hledger.m4.md index f4f7d860b..2facc8066 100644 --- a/hledger/hledger.m4.md +++ b/hledger/hledger.m4.md @@ -875,8 +875,9 @@ Currently we support at most one cost + conversion postings group per transactio #### Beancount operating currency -Declaring an operating currency improves Beancount and Fava reports. -You can do this manually by adding a line like this to the beancount journal: +Declaring an operating currency (or several) improves Beancount and Fava reports. +Currently hledger will declare each currency used in cost amounts as an operating currency. +If needed, replace these with your own declaration, like ```beancount option "operating_currency" "USD" ``` diff --git a/hledger/test/print/beancount.test b/hledger/test/print/beancount.test index a77f88c93..4521a6423 100644 --- a/hledger/test/print/beancount.test +++ b/hledger/test/print/beancount.test @@ -85,6 +85,7 @@ $ hledger -f- print -O beancount >= # ** 6. Conversion postings that are redundant with costs are dropped. +# And when there are costs, operating_currency directives are added for the cost currencies. < 2000-01-01 Assets $1 @@ €1 @@ -93,6 +94,8 @@ $ hledger -f- print -O beancount Assets €-1 $ hledger -f- print -O beancount +option "operating_currency" "EUR" + 2000-01-01 open Assets:A 2000-01-01 *