2009-04-03 14:58:05 +04:00
|
|
|
{-|
|
|
|
|
|
2012-05-14 22:52:22 +04:00
|
|
|
A 'Transaction' represents a movement of some commodity(ies) between two
|
|
|
|
or more accounts. It consists of multiple account 'Posting's which balance
|
2012-05-28 02:59:06 +04:00
|
|
|
to zero, a date, and optional extras like description, cleared status, and
|
|
|
|
tags.
|
2009-04-03 14:58:05 +04:00
|
|
|
|
|
|
|
-}
|
|
|
|
|
2020-12-27 10:59:30 +03:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2019-02-15 21:34:40 +03:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2020-12-27 10:59:30 +03:00
|
|
|
{-# LANGUAGE Rank2Types #-}
|
|
|
|
{-# LANGUAGE RecordWildCards #-}
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
|
2021-03-03 17:44:25 +03:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2012-05-07 18:36:40 +04:00
|
|
|
module Hledger.Data.Transaction (
|
|
|
|
-- * Transaction
|
|
|
|
nulltransaction,
|
2019-02-22 03:20:04 +03:00
|
|
|
transaction,
|
2012-05-07 18:36:40 +04:00
|
|
|
txnTieKnot,
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
txnUntieKnot,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * operations
|
|
|
|
showAccountName,
|
|
|
|
hasRealPostings,
|
|
|
|
realPostings,
|
2016-12-10 18:04:48 +03:00
|
|
|
assignmentPostings,
|
2012-05-07 18:36:40 +04:00
|
|
|
virtualPostings,
|
|
|
|
balancedVirtualPostings,
|
|
|
|
transactionsPostings,
|
2021-06-04 15:40:10 +03:00
|
|
|
BalancingOpts(..),
|
|
|
|
balancingOpts,
|
2012-05-07 18:36:40 +04:00
|
|
|
isTransactionBalanced,
|
2019-02-15 21:34:40 +03:00
|
|
|
balanceTransaction,
|
2019-02-18 23:11:07 +03:00
|
|
|
balanceTransactionHelper,
|
2019-10-20 05:41:21 +03:00
|
|
|
transactionTransformPostings,
|
|
|
|
transactionApplyValuation,
|
|
|
|
transactionToCost,
|
2020-11-24 20:17:01 +03:00
|
|
|
transactionApplyAliases,
|
2021-02-04 18:57:26 +03:00
|
|
|
transactionMapPostings,
|
2021-02-04 18:58:09 +03:00
|
|
|
transactionMapPostingAmounts,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- nonzerobalanceerror,
|
|
|
|
-- * date operations
|
2012-12-06 08:43:41 +04:00
|
|
|
transactionDate2,
|
2019-07-10 13:04:05 +03:00
|
|
|
-- * transaction description parts
|
|
|
|
transactionPayee,
|
|
|
|
transactionNote,
|
|
|
|
-- payeeAndNoteFromDescription,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * rendering
|
|
|
|
showTransaction,
|
2019-11-16 14:23:38 +03:00
|
|
|
showTransactionOneLineAmounts,
|
2019-01-08 19:51:11 +03:00
|
|
|
-- showPostingLine,
|
2017-01-20 18:33:24 +03:00
|
|
|
showPostingLines,
|
|
|
|
-- * GenericSourcePos
|
|
|
|
sourceFilePath,
|
|
|
|
sourceFirstLine,
|
|
|
|
showGenericSourcePos,
|
2019-02-18 23:11:07 +03:00
|
|
|
annotateErrorWithTransaction,
|
2021-03-03 17:44:25 +03:00
|
|
|
transactionFile,
|
2018-09-04 21:31:31 +03:00
|
|
|
-- * tests
|
2018-09-06 23:08:26 +03:00
|
|
|
tests_Transaction
|
2012-05-07 18:36:40 +04:00
|
|
|
)
|
2009-04-03 14:58:05 +04:00
|
|
|
where
|
2020-12-22 15:35:20 +03:00
|
|
|
|
2021-06-04 15:40:10 +03:00
|
|
|
import Data.Default (Default(..))
|
2021-04-13 10:41:58 +03:00
|
|
|
import Data.Foldable (asum)
|
2020-12-22 15:35:20 +03:00
|
|
|
import Data.List (intercalate, partition)
|
2020-05-29 23:09:17 +03:00
|
|
|
import Data.List.Extra (nubSort)
|
2021-04-13 10:41:58 +03:00
|
|
|
import Data.Maybe (fromMaybe, isNothing, mapMaybe)
|
lib: textification: comments and tags
No change.
hledger -f data/100x100x10.journal stats
<<ghc: 42859576 bytes, 84 GCs, 193781/269984 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.016 MUT (0.020 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
<<ghc: 42859576 bytes, 84 GCs, 193781/269984 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.018 elapsed), 0.009 GC (0.013 elapsed) :ghc>>
hledger -f data/1000x1000x10.journal stats
<<ghc: 349576344 bytes, 681 GCs, 1407388/4091680 avg/max bytes residency (7 samples), 11M in use, 0.000 INIT (0.000 elapsed), 0.124 MUT (0.130 elapsed), 0.047 GC (0.055 elapsed) :ghc>>
<<ghc: 349576280 bytes, 681 GCs, 1407388/4091680 avg/max bytes residency (7 samples), 11M in use, 0.000 INIT (0.000 elapsed), 0.126 MUT (0.132 elapsed), 0.049 GC (0.058 elapsed) :ghc>>
hledger -f data/10000x1000x10.journal stats
<<ghc: 3424030664 bytes, 6658 GCs, 11403359/41071624 avg/max bytes residency (11 samples), 111M in use, 0.000 INIT (0.000 elapsed), 1.207 MUT (1.228 elapsed), 0.473 GC (0.528 elapsed) :ghc>>
<<ghc: 3424030760 bytes, 6658 GCs, 11403874/41077288 avg/max bytes residency (11 samples), 111M in use, 0.000 INIT (0.002 elapsed), 1.234 MUT (1.256 elapsed), 0.470 GC (0.520 elapsed) :ghc>>
hledger -f data/100000x1000x10.journal stats
<<ghc: 34306547448 bytes, 66727 GCs, 76805504/414629288 avg/max bytes residency (14 samples), 1009M in use, 0.000 INIT (0.003 elapsed), 12.615 MUT (12.813 elapsed), 4.656 GC (5.291 elapsed) :ghc>>
<<ghc: 34306547320 bytes, 66727 GCs, 76805504/414629288 avg/max bytes residency (14 samples), 1009M in use, 0.000 INIT (0.009 elapsed), 12.802 MUT (13.065 elapsed), 4.774 GC (5.441 elapsed) :ghc>>
2016-05-25 03:09:20 +03:00
|
|
|
import Data.Text (Text)
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
import qualified Data.Text as T
|
2020-12-25 08:38:26 +03:00
|
|
|
import qualified Data.Text.Lazy as TL
|
|
|
|
import qualified Data.Text.Lazy.Builder as TB
|
2020-12-22 15:35:20 +03:00
|
|
|
import Data.Time.Calendar (Day, fromGregorian)
|
2019-10-20 05:17:35 +03:00
|
|
|
import qualified Data.Map as M
|
2021-03-18 08:11:19 +03:00
|
|
|
import Safe (maximumDef)
|
2010-11-15 01:44:37 +03:00
|
|
|
|
2019-07-15 13:28:52 +03:00
|
|
|
import Hledger.Utils
|
2010-05-20 03:08:53 +04:00
|
|
|
import Hledger.Data.Types
|
|
|
|
import Hledger.Data.Dates
|
|
|
|
import Hledger.Data.Posting
|
|
|
|
import Hledger.Data.Amount
|
2019-10-20 05:41:21 +03:00
|
|
|
import Hledger.Data.Valuation
|
2020-12-25 08:38:26 +03:00
|
|
|
import Text.Tabular.AsciiWide
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2017-01-20 18:33:24 +03:00
|
|
|
sourceFilePath :: GenericSourcePos -> FilePath
|
|
|
|
sourceFilePath = \case
|
|
|
|
GenericSourcePos fp _ _ -> fp
|
|
|
|
JournalSourcePos fp _ -> fp
|
|
|
|
|
|
|
|
sourceFirstLine :: GenericSourcePos -> Int
|
|
|
|
sourceFirstLine = \case
|
|
|
|
GenericSourcePos _ line _ -> line
|
|
|
|
JournalSourcePos _ (line, _) -> line
|
|
|
|
|
2018-10-25 10:05:29 +03:00
|
|
|
-- | Render source position in human-readable form.
|
|
|
|
-- Keep in sync with Hledger.UI.ErrorScreen.hledgerparseerrorpositionp (temporary). XXX
|
2017-01-20 18:33:24 +03:00
|
|
|
showGenericSourcePos :: GenericSourcePos -> String
|
|
|
|
showGenericSourcePos = \case
|
|
|
|
GenericSourcePos fp line column -> show fp ++ " (line " ++ show line ++ ", column " ++ show column ++ ")"
|
|
|
|
JournalSourcePos fp (line, line') -> show fp ++ " (lines " ++ show line ++ "-" ++ show line' ++ ")"
|
|
|
|
|
2009-12-19 08:57:54 +03:00
|
|
|
nulltransaction :: Transaction
|
|
|
|
nulltransaction = Transaction {
|
2015-10-30 06:12:46 +03:00
|
|
|
tindex=0,
|
2014-08-01 04:32:42 +04:00
|
|
|
tsourcepos=nullsourcepos,
|
2009-12-19 08:57:54 +03:00
|
|
|
tdate=nulldate,
|
2012-12-06 08:43:41 +04:00
|
|
|
tdate2=Nothing,
|
2017-06-16 02:25:37 +03:00
|
|
|
tstatus=Unmarked,
|
2014-09-11 00:07:53 +04:00
|
|
|
tcode="",
|
|
|
|
tdescription="",
|
2009-12-19 08:57:54 +03:00
|
|
|
tcomment="",
|
2012-05-28 02:59:06 +04:00
|
|
|
ttags=[],
|
2009-12-19 08:57:54 +03:00
|
|
|
tpostings=[],
|
2019-01-04 23:01:45 +03:00
|
|
|
tprecedingcomment=""
|
2009-12-19 08:57:54 +03:00
|
|
|
}
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2019-02-22 03:20:04 +03:00
|
|
|
-- | Make a simple transaction with the given date and postings.
|
2020-08-26 11:11:20 +03:00
|
|
|
transaction :: Day -> [Posting] -> Transaction
|
|
|
|
transaction day ps = txnTieKnot $ nulltransaction{tdate=day, tpostings=ps}
|
2019-02-22 03:20:04 +03:00
|
|
|
|
2019-07-10 13:04:05 +03:00
|
|
|
transactionPayee :: Transaction -> Text
|
|
|
|
transactionPayee = fst . payeeAndNoteFromDescription . tdescription
|
|
|
|
|
|
|
|
transactionNote :: Transaction -> Text
|
|
|
|
transactionNote = snd . payeeAndNoteFromDescription . tdescription
|
|
|
|
|
|
|
|
-- | Parse a transaction's description into payee and note (aka narration) fields,
|
|
|
|
-- assuming a convention of separating these with | (like Beancount).
|
|
|
|
-- Ie, everything up to the first | is the payee, everything after it is the note.
|
|
|
|
-- When there's no |, payee == note == description.
|
|
|
|
payeeAndNoteFromDescription :: Text -> (Text,Text)
|
|
|
|
payeeAndNoteFromDescription t
|
|
|
|
| T.null n = (t, t)
|
2020-07-16 13:51:48 +03:00
|
|
|
| otherwise = (T.strip p, T.strip $ T.drop 1 n)
|
2019-07-10 13:04:05 +03:00
|
|
|
where
|
|
|
|
(p, n) = T.span (/= '|') t
|
|
|
|
|
2009-04-03 14:58:05 +04:00
|
|
|
{-|
|
2020-02-08 16:06:52 +03:00
|
|
|
Render a journal transaction as text similar to the style of Ledger's print command.
|
2018-10-22 16:46:31 +03:00
|
|
|
|
2020-02-08 16:06:52 +03:00
|
|
|
Adapted from Ledger 2.x and 3.x standard format:
|
2009-04-03 14:58:05 +04:00
|
|
|
|
|
|
|
@
|
2020-02-08 16:06:52 +03:00
|
|
|
yyyy-mm-dd[ *][ CODE] description......... [ ; comment...............]
|
2009-04-03 14:58:05 +04:00
|
|
|
account name 1..................... ...$amount1[ ; comment...............]
|
|
|
|
account name 2..................... ..$-amount1[ ; comment...............]
|
|
|
|
|
|
|
|
pcodewidth = no limit -- 10 -- mimicking ledger layout.
|
|
|
|
pdescwidth = no limit -- 20 -- I don't remember what these mean,
|
|
|
|
pacctwidth = 35 minimum, no maximum -- they were important at the time.
|
|
|
|
pamtwidth = 11
|
|
|
|
pcommentwidth = no limit -- 22
|
|
|
|
@
|
2018-10-22 16:46:31 +03:00
|
|
|
|
|
|
|
The output will be parseable journal syntax.
|
2019-07-15 13:28:52 +03:00
|
|
|
To facilitate this, postings with explicit multi-commodity amounts
|
2018-10-22 16:46:31 +03:00
|
|
|
are displayed as multiple similar postings, one per commodity.
|
|
|
|
(Normally does not happen with this function).
|
2009-04-03 14:58:05 +04:00
|
|
|
-}
|
2020-10-28 04:53:37 +03:00
|
|
|
showTransaction :: Transaction -> Text
|
2020-12-25 08:38:26 +03:00
|
|
|
showTransaction = TL.toStrict . TB.toLazyText . showTransactionHelper False
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2019-11-16 14:23:38 +03:00
|
|
|
-- | Like showTransaction, but explicit multi-commodity amounts
|
2019-07-15 13:28:52 +03:00
|
|
|
-- are shown on one line, comma-separated. In this case the output will
|
2018-10-22 16:46:31 +03:00
|
|
|
-- not be parseable journal syntax.
|
2020-10-28 04:53:37 +03:00
|
|
|
showTransactionOneLineAmounts :: Transaction -> Text
|
2020-12-25 08:38:26 +03:00
|
|
|
showTransactionOneLineAmounts = TL.toStrict . TB.toLazyText . showTransactionHelper True
|
2015-10-30 04:05:02 +03:00
|
|
|
|
2019-07-15 13:28:52 +03:00
|
|
|
-- | Helper for showTransaction*.
|
2020-12-25 08:38:26 +03:00
|
|
|
showTransactionHelper :: Bool -> Transaction -> TB.Builder
|
2019-11-16 14:23:38 +03:00
|
|
|
showTransactionHelper onelineamounts t =
|
2020-12-25 08:38:26 +03:00
|
|
|
TB.fromText descriptionline <> newline
|
|
|
|
<> foldMap ((<> newline) . TB.fromText) newlinecomments
|
|
|
|
<> foldMap ((<> newline) . TB.fromText) (postingsAsLines onelineamounts $ tpostings t)
|
|
|
|
<> newline
|
2020-10-28 04:53:37 +03:00
|
|
|
where
|
|
|
|
descriptionline = T.stripEnd $ T.concat [date, status, code, desc, samelinecomment]
|
2020-11-05 04:58:04 +03:00
|
|
|
date = showDate (tdate t) <> maybe "" (("="<>) . showDate) (tdate2 t)
|
2020-10-28 04:53:37 +03:00
|
|
|
status | tstatus t == Cleared = " *"
|
|
|
|
| tstatus t == Pending = " !"
|
|
|
|
| otherwise = ""
|
|
|
|
code = if T.null (tcode t) then "" else wrap " (" ")" $ tcode t
|
|
|
|
desc = if T.null d then "" else " " <> d where d = tdescription t
|
|
|
|
(samelinecomment, newlinecomments) =
|
|
|
|
case renderCommentLines (tcomment t) of [] -> ("",[])
|
|
|
|
c:cs -> (c,cs)
|
2020-12-25 08:38:26 +03:00
|
|
|
newline = TB.singleton '\n'
|
2013-09-10 21:32:49 +04:00
|
|
|
|
2018-10-22 16:46:31 +03:00
|
|
|
-- | Render a transaction or posting's comment as indented, semicolon-prefixed comment lines.
|
2019-06-12 11:38:05 +03:00
|
|
|
-- The first line (unless empty) will have leading space, subsequent lines will have a larger indent.
|
2020-10-28 04:53:37 +03:00
|
|
|
renderCommentLines :: Text -> [Text]
|
2019-06-12 11:38:05 +03:00
|
|
|
renderCommentLines t =
|
2020-10-28 04:53:37 +03:00
|
|
|
case T.lines t of
|
2019-06-12 11:38:05 +03:00
|
|
|
[] -> []
|
2020-10-28 04:53:37 +03:00
|
|
|
[l] -> [commentSpace $ comment l] -- single-line comment
|
2019-06-12 11:38:05 +03:00
|
|
|
("":ls) -> "" : map (lineIndent . comment) ls -- multi-line comment with empty first line
|
2020-10-28 04:53:37 +03:00
|
|
|
(l:ls) -> commentSpace (comment l) : map (lineIndent . comment) ls
|
2019-06-12 11:38:05 +03:00
|
|
|
where
|
2020-10-28 04:53:37 +03:00
|
|
|
comment = ("; "<>)
|
2013-09-10 21:32:49 +04:00
|
|
|
|
2018-10-22 16:46:31 +03:00
|
|
|
-- | Given a transaction and its postings, render the postings, suitable
|
2018-11-30 00:32:59 +03:00
|
|
|
-- for `print` output. Normally this output will be valid journal syntax which
|
|
|
|
-- hledger can reparse (though it may include no-longer-valid balance assertions).
|
2018-10-22 16:46:31 +03:00
|
|
|
--
|
2019-07-15 13:28:52 +03:00
|
|
|
-- Explicit amounts are shown, any implicit amounts are not.
|
2018-10-22 16:46:31 +03:00
|
|
|
--
|
2018-11-30 00:32:59 +03:00
|
|
|
-- Postings with multicommodity explicit amounts are handled as follows:
|
|
|
|
-- if onelineamounts is true, these amounts are shown on one line,
|
|
|
|
-- comma-separated, and the output will not be valid journal syntax.
|
|
|
|
-- Otherwise, they are shown as several similar postings, one per commodity.
|
2019-07-15 13:28:52 +03:00
|
|
|
--
|
2018-11-30 00:44:38 +03:00
|
|
|
-- The output will appear to be a balanced transaction.
|
|
|
|
-- Amounts' display precisions, which may have been limited by commodity
|
|
|
|
-- directives, will be increased if necessary to ensure this.
|
|
|
|
--
|
2018-11-30 00:32:59 +03:00
|
|
|
-- Posting amounts will be aligned with each other, starting about 4 columns
|
|
|
|
-- beyond the widest account name (see postingAsLines for details).
|
2020-10-28 04:53:37 +03:00
|
|
|
postingsAsLines :: Bool -> [Posting] -> [Text]
|
2021-03-18 08:11:19 +03:00
|
|
|
postingsAsLines onelineamounts ps = concatMap first3 linesWithWidths
|
|
|
|
where
|
|
|
|
linesWithWidths = map (postingAsLines False onelineamounts maxacctwidth maxamtwidth) ps
|
|
|
|
maxacctwidth = maximumDef 0 $ map second3 linesWithWidths
|
|
|
|
maxamtwidth = maximumDef 0 $ map third3 linesWithWidths
|
2012-05-15 05:49:05 +04:00
|
|
|
|
2019-07-15 13:28:52 +03:00
|
|
|
-- | Render one posting, on one or more lines, suitable for `print` output.
|
2018-10-22 16:46:31 +03:00
|
|
|
-- There will be an indented account name, plus one or more of status flag,
|
|
|
|
-- posting amount, balance assertion, same-line comment, next-line comments.
|
2019-07-15 13:28:52 +03:00
|
|
|
--
|
2018-10-22 16:46:31 +03:00
|
|
|
-- If the posting's amount is implicit or if elideamount is true, no amount is shown.
|
|
|
|
--
|
2019-07-15 13:28:52 +03:00
|
|
|
-- If the posting's amount is explicit and multi-commodity, multiple similar
|
2018-10-22 16:46:31 +03:00
|
|
|
-- postings are shown, one for each commodity, to help produce parseable journal syntax.
|
|
|
|
-- Or if onelineamounts is true, such amounts are shown on one line, comma-separated
|
|
|
|
-- (and the output will not be valid journal syntax).
|
|
|
|
--
|
2019-07-15 13:28:52 +03:00
|
|
|
-- By default, 4 spaces (2 if there's a status flag) are shown between
|
2018-10-22 16:46:31 +03:00
|
|
|
-- account name and start of amount area, which is typically 12 chars wide
|
2019-07-15 13:28:52 +03:00
|
|
|
-- and contains a right-aligned amount (so 10-12 visible spaces between
|
2018-10-22 16:46:31 +03:00
|
|
|
-- account name and amount is typical).
|
2019-07-15 13:28:52 +03:00
|
|
|
-- When given a list of postings to be aligned with, the whitespace will be
|
2018-10-22 16:46:31 +03:00
|
|
|
-- increased if needed to match the posting with the longest account name.
|
|
|
|
-- This is used to align the amounts of a transaction's postings.
|
|
|
|
--
|
2021-03-18 08:11:19 +03:00
|
|
|
-- Also returns the account width and amount width used.
|
|
|
|
postingAsLines :: Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
|
|
|
|
postingAsLines elideamount onelineamounts acctwidth amtwidth p =
|
|
|
|
(concatMap (++ newlinecomments) postingblocks, thisacctwidth, thisamtwidth)
|
2012-05-15 05:49:05 +04:00
|
|
|
where
|
2020-12-25 08:38:26 +03:00
|
|
|
-- This needs to be converted to strict Text in order to strip trailing
|
|
|
|
-- spaces. This adds a small amount of inefficiency, and the only difference
|
|
|
|
-- is whether there are trailing spaces in print (and related) reports. This
|
|
|
|
-- could be removed and we could just keep everything as a Text Builder, but
|
|
|
|
-- would require adding trailing spaces to 42 failing tests.
|
|
|
|
postingblocks = [map T.stripEnd . T.lines . TL.toStrict $
|
2021-01-13 08:31:13 +03:00
|
|
|
render [ textCell BottomLeft statusandaccount
|
|
|
|
, textCell BottomLeft " "
|
2021-03-18 08:11:19 +03:00
|
|
|
, Cell BottomLeft [pad amt]
|
2020-12-25 08:38:26 +03:00
|
|
|
, Cell BottomLeft [assertion]
|
2021-01-13 08:31:13 +03:00
|
|
|
, textCell BottomLeft samelinecomment
|
2020-12-25 08:38:26 +03:00
|
|
|
]
|
2020-10-28 04:53:37 +03:00
|
|
|
| amt <- shownAmounts]
|
2020-12-25 08:38:26 +03:00
|
|
|
render = renderRow def{tableBorders=False, borderSpaces=False} . Group NoLine . map Header
|
2021-03-18 08:11:19 +03:00
|
|
|
pad amt = WideBuilder (TB.fromText $ T.replicate w " ") w <> amt
|
|
|
|
where w = max 12 amtwidth - wbWidth amt -- min. 12 for backwards compatibility
|
|
|
|
|
2020-12-25 08:38:26 +03:00
|
|
|
assertion = maybe mempty ((WideBuilder (TB.singleton ' ') 1 <>).showBalanceAssertion) $ pbalanceassertion p
|
2021-03-18 08:11:19 +03:00
|
|
|
-- pad to the maximum account name width, plus 2 to leave room for status flags, to keep amounts aligned
|
|
|
|
statusandaccount = lineIndent . fitText (Just $ 2 + acctwidth) Nothing False True $ pstatusandacct p
|
|
|
|
thisacctwidth = textWidth $ pacctstr p
|
|
|
|
|
|
|
|
pacctstr p' = showAccountName Nothing (ptype p') (paccount p')
|
|
|
|
pstatusandacct p' = pstatusprefix p' <> pacctstr p'
|
|
|
|
pstatusprefix p' = case pstatus p' of
|
|
|
|
Unmarked -> ""
|
|
|
|
s -> T.pack (show s) <> " "
|
2015-10-11 01:09:42 +03:00
|
|
|
|
|
|
|
-- currently prices are considered part of the amount string when right-aligning amounts
|
2021-03-18 08:11:19 +03:00
|
|
|
-- Since we will usually be calling this function with the knot tied between
|
|
|
|
-- amtwidth and thisamtwidth, make sure thisamtwidth does not depend on
|
|
|
|
-- amtwidth at all.
|
2017-01-11 07:44:10 +03:00
|
|
|
shownAmounts
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
| elideamount = [mempty]
|
2021-04-12 15:50:56 +03:00
|
|
|
| otherwise = showMixedAmountLinesB noColour{displayOneLine=onelineamounts} $ pamount p
|
2021-03-18 08:11:19 +03:00
|
|
|
thisamtwidth = maximumDef 0 $ map wbWidth shownAmounts
|
2015-10-11 01:09:42 +03:00
|
|
|
|
|
|
|
(samelinecomment, newlinecomments) =
|
|
|
|
case renderCommentLines (pcomment p) of [] -> ("",[])
|
|
|
|
c:cs -> (c,cs)
|
2012-05-15 05:49:05 +04:00
|
|
|
|
2019-07-15 13:28:52 +03:00
|
|
|
-- | Render a balance assertion, as the =[=][*] symbol and expected amount.
|
2020-12-25 08:38:26 +03:00
|
|
|
showBalanceAssertion :: BalanceAssertion -> WideBuilder
|
2019-07-15 13:28:52 +03:00
|
|
|
showBalanceAssertion BalanceAssertion{..} =
|
2020-12-25 08:38:26 +03:00
|
|
|
singleton '=' <> eq <> ast <> singleton ' ' <> showAmountB def{displayZeroCommodity=True} baamount
|
|
|
|
where
|
|
|
|
eq = if batotal then singleton '=' else mempty
|
|
|
|
ast = if bainclusive then singleton '*' else mempty
|
|
|
|
singleton c = WideBuilder (TB.singleton c) 1
|
2019-02-18 06:50:22 +03:00
|
|
|
|
2019-01-08 19:51:11 +03:00
|
|
|
-- | Render a posting, simply. Used in balance assertion errors.
|
|
|
|
-- showPostingLine p =
|
2019-06-12 11:38:05 +03:00
|
|
|
-- lineIndent $
|
2019-01-08 19:51:11 +03:00
|
|
|
-- if pstatus p == Cleared then "* " else "" ++ -- XXX show !
|
|
|
|
-- showAccountName Nothing (ptype p) (paccount p) ++
|
|
|
|
-- " " ++
|
|
|
|
-- showMixedAmountOneLine (pamount p) ++
|
|
|
|
-- assertion
|
|
|
|
-- where
|
|
|
|
-- -- XXX extract, handle ==
|
|
|
|
-- assertion = maybe "" ((" = " ++) . showAmountWithZeroCommodity . baamount) $ pbalanceassertion p
|
2016-02-10 18:39:03 +03:00
|
|
|
|
2018-10-22 16:46:31 +03:00
|
|
|
-- | Render a posting, at the appropriate width for aligning with
|
2019-07-15 13:28:52 +03:00
|
|
|
-- its siblings if any. Used by the rewrite command.
|
2020-10-28 04:53:37 +03:00
|
|
|
showPostingLines :: Posting -> [Text]
|
2021-03-18 08:11:19 +03:00
|
|
|
showPostingLines p = first3 $ postingAsLines False False maxacctwidth maxamtwidth p
|
|
|
|
where
|
|
|
|
linesWithWidths = map (postingAsLines False False maxacctwidth maxamtwidth) . maybe [p] tpostings $ ptransaction p
|
|
|
|
maxacctwidth = maximumDef 0 $ map second3 linesWithWidths
|
|
|
|
maxamtwidth = maximumDef 0 $ map third3 linesWithWidths
|
2017-01-20 18:33:24 +03:00
|
|
|
|
2019-06-12 11:38:05 +03:00
|
|
|
-- | Prepend a suitable indent for a posting (or transaction/posting comment) line.
|
2020-10-28 04:53:37 +03:00
|
|
|
lineIndent :: Text -> Text
|
|
|
|
lineIndent = (" "<>)
|
2019-06-12 11:38:05 +03:00
|
|
|
|
|
|
|
-- | Prepend the space required before a same-line comment.
|
2020-10-28 04:53:37 +03:00
|
|
|
commentSpace :: Text -> Text
|
|
|
|
commentSpace = (" "<>)
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2009-05-24 10:22:44 +04:00
|
|
|
-- | Show an account name, clipped to the given width if any, and
|
|
|
|
-- appropriately bracketed/parenthesised for the given posting type.
|
2020-10-28 04:53:37 +03:00
|
|
|
showAccountName :: Maybe Int -> PostingType -> AccountName -> Text
|
2009-05-24 10:22:44 +04:00
|
|
|
showAccountName w = fmt
|
2020-07-16 13:28:48 +03:00
|
|
|
where
|
2020-10-28 04:53:37 +03:00
|
|
|
fmt RegularPosting = maybe id T.take w
|
|
|
|
fmt VirtualPosting = wrap "(" ")" . maybe id (T.takeEnd . subtract 2) w
|
|
|
|
fmt BalancedVirtualPosting = wrap "[" "]" . maybe id (T.takeEnd . subtract 2) w
|
2009-05-24 10:22:44 +04:00
|
|
|
|
2011-06-29 04:31:37 +04:00
|
|
|
hasRealPostings :: Transaction -> Bool
|
|
|
|
hasRealPostings = not . null . realPostings
|
|
|
|
|
2010-02-27 21:06:29 +03:00
|
|
|
realPostings :: Transaction -> [Posting]
|
|
|
|
realPostings = filter isReal . tpostings
|
|
|
|
|
2016-12-10 18:04:48 +03:00
|
|
|
assignmentPostings :: Transaction -> [Posting]
|
2019-02-18 23:11:07 +03:00
|
|
|
assignmentPostings = filter hasBalanceAssignment . tpostings
|
2016-12-10 18:04:48 +03:00
|
|
|
|
2010-02-27 21:06:29 +03:00
|
|
|
virtualPostings :: Transaction -> [Posting]
|
|
|
|
virtualPostings = filter isVirtual . tpostings
|
|
|
|
|
|
|
|
balancedVirtualPostings :: Transaction -> [Posting]
|
|
|
|
balancedVirtualPostings = filter isBalancedVirtual . tpostings
|
|
|
|
|
2011-06-11 20:11:38 +04:00
|
|
|
transactionsPostings :: [Transaction] -> [Posting]
|
2019-02-14 16:14:52 +03:00
|
|
|
transactionsPostings = concatMap tpostings
|
2011-06-11 20:11:38 +04:00
|
|
|
|
2021-06-04 15:40:10 +03:00
|
|
|
data BalancingOpts = BalancingOpts
|
|
|
|
{ ignore_assertions_ :: Bool -- ^ Ignore balance assertions
|
|
|
|
, infer_prices_ :: Bool -- ^ Infer prices in unbalanced multicommodity amounts
|
|
|
|
, commodity_styles_ :: Maybe (M.Map CommoditySymbol AmountStyle) -- ^ commodity display styles
|
|
|
|
} deriving (Show)
|
|
|
|
|
|
|
|
instance Default BalancingOpts where def = balancingOpts
|
|
|
|
|
|
|
|
balancingOpts :: BalancingOpts
|
|
|
|
balancingOpts = BalancingOpts
|
|
|
|
{ ignore_assertions_ = False
|
|
|
|
, infer_prices_ = True
|
|
|
|
, commodity_styles_ = Nothing
|
|
|
|
}
|
|
|
|
|
2020-05-30 02:31:15 +03:00
|
|
|
-- | Check that this transaction would appear balanced to a human when displayed.
|
|
|
|
-- On success, returns the empty list, otherwise one or more error messages.
|
2020-05-29 23:09:17 +03:00
|
|
|
--
|
|
|
|
-- In more detail:
|
|
|
|
-- For the real postings, and separately for the balanced virtual postings:
|
|
|
|
--
|
|
|
|
-- 1. Convert amounts to cost where possible
|
|
|
|
--
|
2020-05-30 03:57:10 +03:00
|
|
|
-- 2. When there are two or more non-zero amounts
|
|
|
|
-- (appearing non-zero when displayed, using the given display styles if provided),
|
2020-05-29 23:09:17 +03:00
|
|
|
-- are they a mix of positives and negatives ?
|
|
|
|
-- This is checked separately to give a clearer error message.
|
2020-05-30 03:57:10 +03:00
|
|
|
-- (Best effort; could be confused by postings with multicommodity amounts.)
|
2020-05-29 23:09:17 +03:00
|
|
|
--
|
|
|
|
-- 3. Does the amounts' sum appear non-zero when displayed ?
|
|
|
|
-- (using the given display styles if provided)
|
|
|
|
--
|
2021-06-04 15:40:10 +03:00
|
|
|
transactionCheckBalanced :: BalancingOpts -> Transaction -> [String]
|
|
|
|
transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs
|
2020-05-29 21:11:47 +03:00
|
|
|
where
|
2020-05-29 23:09:17 +03:00
|
|
|
(rps, bvps) = (realPostings t, balancedVirtualPostings t)
|
|
|
|
|
2020-05-30 03:57:10 +03:00
|
|
|
-- check for mixed signs, detecting nonzeros at display precision
|
2021-06-04 15:40:10 +03:00
|
|
|
canonicalise = maybe id canonicaliseMixedAmount commodity_styles_
|
2021-01-16 13:43:24 +03:00
|
|
|
signsOk ps =
|
2021-02-20 02:06:51 +03:00
|
|
|
case filter (not.mixedAmountLooksZero) $ map (canonicalise.mixedAmountCost.pamount) ps of
|
2020-05-30 03:57:10 +03:00
|
|
|
nonzeros | length nonzeros >= 2
|
|
|
|
-> length (nubSort $ mapMaybe isNegativeMixedAmount nonzeros) > 1
|
|
|
|
_ -> True
|
2020-05-29 23:09:17 +03:00
|
|
|
(rsignsok, bvsignsok) = (signsOk rps, signsOk bvps)
|
|
|
|
|
2020-05-30 03:57:10 +03:00
|
|
|
-- check for zero sum, at display precision
|
2020-05-29 23:09:17 +03:00
|
|
|
(rsum, bvsum) = (sumPostings rps, sumPostings bvps)
|
2020-06-01 01:48:08 +03:00
|
|
|
(rsumcost, bvsumcost) = (mixedAmountCost rsum, mixedAmountCost bvsum)
|
2020-05-29 21:11:47 +03:00
|
|
|
(rsumdisplay, bvsumdisplay) = (canonicalise rsumcost, canonicalise bvsumcost)
|
2021-02-20 02:06:51 +03:00
|
|
|
(rsumok, bvsumok) = (mixedAmountLooksZero rsumdisplay, mixedAmountLooksZero bvsumdisplay)
|
2020-05-30 03:57:10 +03:00
|
|
|
|
|
|
|
-- generate error messages, showing amounts with their original precision
|
2020-05-30 02:31:15 +03:00
|
|
|
errs = filter (not.null) [rmsg, bvmsg]
|
2020-05-30 03:57:10 +03:00
|
|
|
where
|
|
|
|
rmsg
|
|
|
|
| not rsignsok = "real postings all have the same sign"
|
|
|
|
| not rsumok = "real postings' sum should be 0 but is: " ++ showMixedAmount rsumcost
|
|
|
|
| otherwise = ""
|
|
|
|
bvmsg
|
|
|
|
| not bvsignsok = "balanced virtual postings all have the same sign"
|
|
|
|
| not bvsumok = "balanced virtual postings' sum should be 0 but is: " ++ showMixedAmount bvsumcost
|
|
|
|
| otherwise = ""
|
2020-05-29 21:11:47 +03:00
|
|
|
|
|
|
|
-- | Legacy form of transactionCheckBalanced.
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced :: BalancingOpts -> Transaction -> Bool
|
|
|
|
isTransactionBalanced bopts = null . transactionCheckBalanced bopts
|
2010-11-15 01:44:37 +03:00
|
|
|
|
2019-07-15 13:28:52 +03:00
|
|
|
-- | Balance this transaction, ensuring that its postings
|
2019-02-18 23:11:07 +03:00
|
|
|
-- (and its balanced virtual postings) sum to 0,
|
2019-07-15 13:28:52 +03:00
|
|
|
-- by inferring a missing amount or conversion price(s) if needed.
|
2019-02-18 23:11:07 +03:00
|
|
|
-- Or if balancing is not possible, because the amounts don't sum to 0 or
|
|
|
|
-- because there's more than one missing amount, return an error message.
|
|
|
|
--
|
|
|
|
-- Transactions with balance assignments can have more than one
|
2019-07-15 13:28:52 +03:00
|
|
|
-- missing amount; to balance those you should use the more powerful
|
2019-02-18 23:11:07 +03:00
|
|
|
-- journalBalanceTransactions.
|
|
|
|
--
|
|
|
|
-- The "sum to 0" test is done using commodity display precisions,
|
|
|
|
-- if provided, so that the result agrees with the numbers users can see.
|
|
|
|
--
|
2019-02-15 21:34:40 +03:00
|
|
|
balanceTransaction ::
|
2021-06-04 15:40:10 +03:00
|
|
|
BalancingOpts
|
2019-02-15 21:34:40 +03:00
|
|
|
-> Transaction
|
|
|
|
-> Either String Transaction
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction bopts = fmap fst . balanceTransactionHelper bopts
|
2019-02-15 21:34:40 +03:00
|
|
|
|
2019-02-18 23:11:07 +03:00
|
|
|
-- | Helper used by balanceTransaction and balanceTransactionWithBalanceAssignmentAndCheckAssertionsB;
|
2019-07-15 13:28:52 +03:00
|
|
|
-- use one of those instead. It also returns a list of accounts
|
2019-02-18 23:11:07 +03:00
|
|
|
-- and amounts that were inferred.
|
2019-02-15 21:34:40 +03:00
|
|
|
balanceTransactionHelper ::
|
2021-06-04 15:40:10 +03:00
|
|
|
BalancingOpts
|
2019-02-15 21:34:40 +03:00
|
|
|
-> Transaction
|
|
|
|
-> Either String (Transaction, [(AccountName, MixedAmount)])
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransactionHelper bopts t = do
|
|
|
|
(t', inferredamtsandaccts) <- inferBalancingAmount (fromMaybe M.empty $ commodity_styles_ bopts) $
|
|
|
|
if infer_prices_ bopts then inferBalancingPrices t else t
|
|
|
|
case transactionCheckBalanced bopts t' of
|
2020-05-30 02:31:15 +03:00
|
|
|
[] -> Right (txnTieKnot t', inferredamtsandaccts)
|
|
|
|
errs -> Left $ transactionBalanceError t' errs
|
|
|
|
|
|
|
|
-- | Generate a transaction balancing error message, given the transaction
|
|
|
|
-- and one or more suberror messages.
|
|
|
|
transactionBalanceError :: Transaction -> [String] -> String
|
|
|
|
transactionBalanceError t errs =
|
|
|
|
annotateErrorWithTransaction t $
|
|
|
|
intercalate "\n" $ "could not balance this transaction:" : errs
|
2015-06-29 00:13:11 +03:00
|
|
|
|
2019-02-18 23:11:07 +03:00
|
|
|
annotateErrorWithTransaction :: Transaction -> String -> String
|
2020-05-30 02:31:15 +03:00
|
|
|
annotateErrorWithTransaction t s =
|
2020-12-25 08:38:26 +03:00
|
|
|
unlines [ showGenericSourcePos $ tsourcepos t, s
|
|
|
|
, T.unpack . T.stripEnd $ showTransaction t
|
|
|
|
]
|
2018-10-24 01:23:26 +03:00
|
|
|
|
2015-06-29 00:13:11 +03:00
|
|
|
-- | Infer up to one missing amount for this transactions's real postings, and
|
|
|
|
-- likewise for its balanced virtual postings, if needed; or return an error
|
2019-02-15 21:34:40 +03:00
|
|
|
-- message if we can't. Returns the updated transaction and any inferred posting amounts,
|
|
|
|
-- with the corresponding accounts, in order).
|
2015-06-29 00:13:11 +03:00
|
|
|
--
|
|
|
|
-- We can infer a missing amount when there are multiple postings and exactly
|
|
|
|
-- one of them is amountless. If the amounts had price(s) the inferred amount
|
|
|
|
-- have the same price(s), and will be converted to the price commodity.
|
2019-07-15 13:28:52 +03:00
|
|
|
inferBalancingAmount ::
|
2019-10-20 05:17:35 +03:00
|
|
|
M.Map CommoditySymbol AmountStyle -- ^ commodity display styles
|
2019-02-15 21:34:40 +03:00
|
|
|
-> Transaction
|
|
|
|
-> Either String (Transaction, [(AccountName, MixedAmount)])
|
|
|
|
inferBalancingAmount styles t@Transaction{tpostings=ps}
|
2015-06-28 22:02:33 +03:00
|
|
|
| length amountlessrealps > 1
|
2020-05-30 02:31:15 +03:00
|
|
|
= Left $ transactionBalanceError t
|
|
|
|
["can't have more than one real posting with no amount"
|
|
|
|
,"(remember to put two or more spaces between account and amount)"]
|
2015-06-28 22:02:33 +03:00
|
|
|
| length amountlessbvps > 1
|
2020-05-30 02:31:15 +03:00
|
|
|
= Left $ transactionBalanceError t
|
|
|
|
["can't have more than one balanced virtual posting with no amount"
|
|
|
|
,"(remember to put two or more spaces between account and amount)"]
|
2015-06-28 22:02:33 +03:00
|
|
|
| otherwise
|
2019-02-15 21:34:40 +03:00
|
|
|
= let psandinferredamts = map inferamount ps
|
|
|
|
inferredacctsandamts = [(paccount p, amt) | (p, Just amt) <- psandinferredamts]
|
|
|
|
in Right (t{tpostings=map fst psandinferredamts}, inferredacctsandamts)
|
2015-06-28 22:02:33 +03:00
|
|
|
where
|
2017-01-13 03:24:53 +03:00
|
|
|
(amountfulrealps, amountlessrealps) = partition hasAmount (realPostings t)
|
2021-01-29 15:34:18 +03:00
|
|
|
realsum = sumPostings amountfulrealps
|
2017-01-13 03:24:53 +03:00
|
|
|
(amountfulbvps, amountlessbvps) = partition hasAmount (balancedVirtualPostings t)
|
2021-01-29 15:34:18 +03:00
|
|
|
bvsum = sumPostings amountfulbvps
|
2019-02-15 21:34:40 +03:00
|
|
|
|
|
|
|
inferamount :: Posting -> (Posting, Maybe MixedAmount)
|
|
|
|
inferamount p =
|
|
|
|
let
|
|
|
|
minferredamt = case ptype p of
|
2019-07-15 13:28:52 +03:00
|
|
|
RegularPosting | not (hasAmount p) -> Just realsum
|
|
|
|
BalancedVirtualPosting | not (hasAmount p) -> Just bvsum
|
|
|
|
_ -> Nothing
|
2019-02-15 21:34:40 +03:00
|
|
|
in
|
|
|
|
case minferredamt of
|
|
|
|
Nothing -> (p, Nothing)
|
2019-07-15 13:28:52 +03:00
|
|
|
Just a -> (p{pamount=a', poriginal=Just $ originalPosting p}, Just a')
|
2019-02-15 21:34:40 +03:00
|
|
|
where
|
|
|
|
-- Inferred amounts are converted to cost.
|
2019-07-15 13:28:52 +03:00
|
|
|
-- Also ensure the new amount has the standard style for its commodity
|
2019-02-15 21:34:40 +03:00
|
|
|
-- (since the main amount styling pass happened before this balancing pass);
|
2021-02-13 14:07:52 +03:00
|
|
|
a' = styleMixedAmount styles . mixedAmountCost $ maNegate a
|
2009-04-10 12:05:56 +04:00
|
|
|
|
2015-06-29 00:13:11 +03:00
|
|
|
-- | Infer prices for this transaction's posting amounts, if needed to make
|
2015-07-03 03:36:09 +03:00
|
|
|
-- the postings balance, and if possible. This is done once for the real
|
|
|
|
-- postings and again (separately) for the balanced virtual postings. When
|
|
|
|
-- it's not possible, the transaction is left unchanged.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- The simplest example is a transaction with two postings, each in a
|
|
|
|
-- different commodity, with no prices specified. In this case we'll add a
|
|
|
|
-- price to the first posting such that it can be converted to the commodity
|
|
|
|
-- of the second posting (with -B), and such that the postings balance.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- In general, we can infer a conversion price when the sum of posting amounts
|
2015-07-03 04:06:03 +03:00
|
|
|
-- contains exactly two different commodities and no explicit prices. Also
|
|
|
|
-- all postings are expected to contain an explicit amount (no missing
|
|
|
|
-- amounts) in a single commodity. Otherwise no price inferring is attempted.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-07-03 04:06:03 +03:00
|
|
|
-- The transaction itself could contain more than two commodities, and/or
|
|
|
|
-- prices, if they cancel out; what matters is that the sum of posting amounts
|
|
|
|
-- contains exactly two commodities and zero prices.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- There can also be more than two postings in either of the commodities.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- We want to avoid excessive display of digits when the calculated price is
|
2015-07-03 03:36:09 +03:00
|
|
|
-- an irrational number, while hopefully also ensuring the displayed numbers
|
|
|
|
-- make sense if the user does a manual calculation. This is (mostly) achieved
|
|
|
|
-- in two ways:
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- - when there is only one posting in the "from" commodity, a total price
|
|
|
|
-- (@@) is used, and all available decimal digits are shown
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
-- - otherwise, a suitable averaged unit price (@) is applied to the relevant
|
2015-07-03 03:36:09 +03:00
|
|
|
-- postings, with display precision equal to the summed display precisions
|
|
|
|
-- of the two commodities being converted between, or 2, whichever is larger.
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-07-03 03:36:09 +03:00
|
|
|
-- (We don't always calculate a good-looking display precision for unit prices
|
|
|
|
-- when the commodity display precisions are low, eg when a journal doesn't
|
|
|
|
-- use any decimal places. The minimum of 2 helps make the prices shown by the
|
|
|
|
-- print command a bit less surprising in this case. Could do better.)
|
2016-12-10 18:04:48 +03:00
|
|
|
--
|
2015-06-29 00:13:11 +03:00
|
|
|
inferBalancingPrices :: Transaction -> Transaction
|
|
|
|
inferBalancingPrices t@Transaction{tpostings=ps} = t{tpostings=ps'}
|
|
|
|
where
|
2019-02-14 16:14:52 +03:00
|
|
|
ps' = map (priceInferrerFor t BalancedVirtualPosting . priceInferrerFor t RegularPosting) ps
|
2015-06-29 00:13:11 +03:00
|
|
|
|
|
|
|
-- | Generate a posting update function which assigns a suitable balancing
|
|
|
|
-- price to the posting, if and as appropriate for the given transaction and
|
2021-04-13 10:41:58 +03:00
|
|
|
-- posting type (real or balanced virtual). If we cannot or should not infer
|
|
|
|
-- prices, just act as the identity on postings.
|
2015-06-29 00:13:11 +03:00
|
|
|
priceInferrerFor :: Transaction -> PostingType -> (Posting -> Posting)
|
2021-04-15 04:58:30 +03:00
|
|
|
priceInferrerFor t pt = maybe id inferprice inferFromAndTo
|
2015-06-29 00:13:11 +03:00
|
|
|
where
|
2021-04-13 10:41:58 +03:00
|
|
|
postings = filter ((==pt).ptype) $ tpostings t
|
|
|
|
pcommodities = map acommodity $ concatMap (amounts . pamount) postings
|
|
|
|
sumamounts = amounts $ sumPostings postings -- amounts normalises to one amount per commodity & price
|
2021-04-15 04:58:30 +03:00
|
|
|
|
|
|
|
-- We can infer prices if there are no prices given, exactly two commodities in the normalised
|
|
|
|
-- sum of postings in this transaction, and these two have opposite signs. The amount we are
|
|
|
|
-- converting from is the first commodity to appear in the ordered list of postings, and the
|
|
|
|
-- commodity we are converting to is the other. If we cannot infer prices, return Nothing.
|
|
|
|
inferFromAndTo = case sumamounts of
|
|
|
|
[a,b] | noprices, oppositesigns -> asum $ map orderIfMatches pcommodities
|
|
|
|
where
|
|
|
|
noprices = all (isNothing . aprice) sumamounts
|
|
|
|
oppositesigns = signum (aquantity a) /= signum (aquantity b)
|
|
|
|
orderIfMatches x | x == acommodity a = Just (a,b)
|
|
|
|
| x == acommodity b = Just (b,a)
|
|
|
|
| otherwise = Nothing
|
|
|
|
_ -> Nothing
|
2021-04-13 10:41:58 +03:00
|
|
|
|
|
|
|
-- For each posting, if the posting type matches, there is only a single amount in the posting,
|
|
|
|
-- and the commodity of the amount matches the amount we're converting from,
|
|
|
|
-- then set its price based on the ratio between fromamount and toamount.
|
|
|
|
inferprice (fromamount, toamount) posting
|
|
|
|
| [a] <- amounts (pamount posting), ptype posting == pt, acommodity a == acommodity fromamount
|
2021-04-15 04:58:30 +03:00
|
|
|
= posting{ pamount = mixedAmount a{aprice=Just conversionprice}
|
2021-04-13 10:41:58 +03:00
|
|
|
, poriginal = Just $ originalPosting posting }
|
|
|
|
| otherwise = posting
|
|
|
|
where
|
2021-04-15 04:58:30 +03:00
|
|
|
-- If only one Amount in the posting list matches fromamount we can use TotalPrice.
|
|
|
|
-- Otherwise divide the conversion equally among the Amounts by using a unit price.
|
|
|
|
conversionprice = case filter (== acommodity fromamount) pcommodities of
|
2021-04-16 03:53:52 +03:00
|
|
|
[_] -> TotalPrice $ negate toamount
|
2021-04-15 04:58:30 +03:00
|
|
|
_ -> UnitPrice $ negate unitprice `withPrecision` unitprecision
|
|
|
|
|
|
|
|
unitprice = aquantity fromamount `divideAmount` toamount
|
2021-04-13 10:41:58 +03:00
|
|
|
unitprecision = case (asprecision $ astyle fromamount, asprecision $ astyle toamount) of
|
|
|
|
(Precision a, Precision b) -> Precision . max 2 $ saturatedAdd a b
|
|
|
|
_ -> NaturalPrecision
|
|
|
|
saturatedAdd a b = if maxBound - a < b then maxBound else a + b
|
2009-07-09 23:22:27 +04:00
|
|
|
|
2012-12-06 08:43:41 +04:00
|
|
|
-- Get a transaction's secondary date, defaulting to the primary date.
|
|
|
|
transactionDate2 :: Transaction -> Day
|
|
|
|
transactionDate2 t = fromMaybe (tdate t) $ tdate2 t
|
2011-09-23 18:50:20 +04:00
|
|
|
|
2015-06-28 22:02:33 +03:00
|
|
|
-- | Ensure a transaction's postings refer back to it, so that eg
|
|
|
|
-- relatedPostings works right.
|
2009-12-19 06:44:52 +03:00
|
|
|
txnTieKnot :: Transaction -> Transaction
|
2017-01-16 18:42:41 +03:00
|
|
|
txnTieKnot t@Transaction{tpostings=ps} = t' where
|
|
|
|
t' = t{tpostings=map (postingSetTransaction t') ps}
|
2009-12-19 06:44:52 +03:00
|
|
|
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
-- | Ensure a transaction's postings do not refer back to it, so that eg
|
|
|
|
-- recursiveSize and GHCI's :sprint work right.
|
|
|
|
txnUntieKnot :: Transaction -> Transaction
|
|
|
|
txnUntieKnot t@Transaction{tpostings=ps} = t{tpostings=map (\p -> p{ptransaction=Nothing}) ps}
|
|
|
|
|
2009-12-19 06:44:52 +03:00
|
|
|
-- | Set a posting's parent transaction.
|
2016-08-14 22:44:19 +03:00
|
|
|
postingSetTransaction :: Transaction -> Posting -> Posting
|
|
|
|
postingSetTransaction t p = p{ptransaction=Just t}
|
2009-12-21 08:23:07 +03:00
|
|
|
|
2019-10-20 05:41:21 +03:00
|
|
|
-- | Apply a transform function to this transaction's amounts.
|
|
|
|
transactionTransformPostings :: (Posting -> Posting) -> Transaction -> Transaction
|
|
|
|
transactionTransformPostings f t@Transaction{tpostings=ps} = t{tpostings=map f ps}
|
|
|
|
|
|
|
|
-- | Apply a specified valuation to this transaction's amounts, using
|
2021-01-26 01:26:25 +03:00
|
|
|
-- the provided price oracle, commodity styles, and reference dates.
|
|
|
|
-- See amountApplyValuation.
|
2020-12-30 08:04:08 +03:00
|
|
|
transactionApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Day -> ValuationType -> Transaction -> Transaction
|
|
|
|
transactionApplyValuation priceoracle styles periodlast today v =
|
|
|
|
transactionTransformPostings (postingApplyValuation priceoracle styles periodlast today v)
|
2019-10-20 05:41:21 +03:00
|
|
|
|
|
|
|
-- | Convert this transaction's amounts to cost, and apply the appropriate amount styles.
|
|
|
|
transactionToCost :: M.Map CommoditySymbol AmountStyle -> Transaction -> Transaction
|
2021-05-07 14:56:48 +03:00
|
|
|
transactionToCost styles = transactionTransformPostings (postingToCost styles)
|
2019-10-20 05:41:21 +03:00
|
|
|
|
2020-11-24 20:17:01 +03:00
|
|
|
-- | Apply some account aliases to all posting account names in the transaction, as described by accountNameApplyAliases.
|
2020-11-26 07:59:07 +03:00
|
|
|
-- This can fail due to a bad replacement pattern in a regular expression alias.
|
|
|
|
transactionApplyAliases :: [AccountAlias] -> Transaction -> Either RegexError Transaction
|
2020-11-24 20:17:01 +03:00
|
|
|
transactionApplyAliases aliases t =
|
2020-11-26 07:59:07 +03:00
|
|
|
case mapM (postingApplyAliases aliases) $ tpostings t of
|
|
|
|
Right ps -> Right $ txnTieKnot $ t{tpostings=ps}
|
|
|
|
Left err -> Left err
|
2020-11-24 20:17:01 +03:00
|
|
|
|
2021-02-04 18:57:26 +03:00
|
|
|
-- | Apply a transformation to a transaction's postings.
|
|
|
|
transactionMapPostings :: (Posting -> Posting) -> Transaction -> Transaction
|
|
|
|
transactionMapPostings f t@Transaction{tpostings=ps} = t{tpostings=map f ps}
|
|
|
|
|
|
|
|
-- | Apply a transformation to a transaction's posting amounts.
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
transactionMapPostingAmounts :: (MixedAmount -> MixedAmount) -> Transaction -> Transaction
|
|
|
|
transactionMapPostingAmounts f = transactionMapPostings (postingTransformAmount f)
|
2021-02-04 18:58:09 +03:00
|
|
|
|
2021-03-03 17:44:25 +03:00
|
|
|
-- | The file path from which this transaction was parsed.
|
|
|
|
transactionFile :: Transaction -> FilePath
|
|
|
|
transactionFile Transaction{tsourcepos} =
|
|
|
|
case tsourcepos of
|
|
|
|
GenericSourcePos f _ _ -> f
|
|
|
|
JournalSourcePos f _ -> f
|
|
|
|
|
2018-09-04 21:31:31 +03:00
|
|
|
-- tests
|
|
|
|
|
2019-12-07 20:06:52 +03:00
|
|
|
tests_Transaction :: TestTree
|
2019-02-14 16:14:52 +03:00
|
|
|
tests_Transaction =
|
2019-11-27 23:46:29 +03:00
|
|
|
tests "Transaction" [
|
|
|
|
|
2021-03-18 08:11:19 +03:00
|
|
|
tests "showPostingLines" [
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
test "null posting" $ showPostingLines nullposting @?= [" 0"]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "non-null posting" $
|
2019-11-27 23:46:29 +03:00
|
|
|
let p =
|
2019-02-14 16:14:52 +03:00
|
|
|
posting
|
|
|
|
{ pstatus = Cleared
|
|
|
|
, paccount = "a"
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
, pamount = mixed [usd 1, hrs 2]
|
2019-02-14 16:14:52 +03:00
|
|
|
, pcomment = "pcomment1\npcomment2\n tag3: val3 \n"
|
|
|
|
, ptype = RegularPosting
|
|
|
|
, ptags = [("ptag1", "val1"), ("ptag2", "val2")]
|
|
|
|
}
|
2021-03-18 08:11:19 +03:00
|
|
|
in showPostingLines p @?=
|
2019-06-12 16:01:17 +03:00
|
|
|
[ " * a $1.00 ; pcomment1"
|
2019-02-14 16:14:52 +03:00
|
|
|
, " ; pcomment2"
|
|
|
|
, " ; tag3: val3 "
|
2019-06-12 16:01:17 +03:00
|
|
|
, " * a 2.00h ; pcomment1"
|
2019-02-14 16:14:52 +03:00
|
|
|
, " ; pcomment2"
|
|
|
|
, " ; tag3: val3 "
|
|
|
|
]
|
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
|
|
|
|
, let
|
|
|
|
-- one implicit amount
|
|
|
|
timp = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt]}
|
|
|
|
-- explicit amounts, balanced
|
|
|
|
texp = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` usd (-1)]}
|
|
|
|
-- explicit amount, only one posting
|
|
|
|
texp1 = nulltransaction {tpostings = ["(a)" `post` usd 1]}
|
|
|
|
-- explicit amounts, two commodities, explicit balancing price
|
|
|
|
texp2 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` (hrs (-1) `at` usd 1)]}
|
|
|
|
-- explicit amounts, two commodities, implicit balancing price
|
|
|
|
texp2b = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` hrs (-1)]}
|
|
|
|
-- one missing amount, not the last one
|
|
|
|
t3 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt, "c" `post` usd (-1)]}
|
|
|
|
-- unbalanced amounts when precision is limited (#931)
|
|
|
|
-- t4 = nulltransaction {tpostings = ["a" `post` usd (-0.01), "b" `post` usd (0.005), "c" `post` usd (0.005)]}
|
|
|
|
in tests "postingsAsLines" [
|
2019-11-29 02:29:03 +03:00
|
|
|
test "null-transaction" $ postingsAsLines False (tpostings nulltransaction) @?= []
|
|
|
|
, test "implicit-amount" $ postingsAsLines False (tpostings timp) @?=
|
2019-02-14 16:14:52 +03:00
|
|
|
[ " a $1.00"
|
|
|
|
, " b" -- implicit amount remains implicit
|
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "explicit-amounts" $ postingsAsLines False (tpostings texp) @?=
|
2019-02-14 16:14:52 +03:00
|
|
|
[ " a $1.00"
|
2019-11-16 14:23:38 +03:00
|
|
|
, " b $-1.00"
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "one-explicit-amount" $ postingsAsLines False (tpostings texp1) @?=
|
2019-11-16 14:23:38 +03:00
|
|
|
[ " (a) $1.00"
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "explicit-amounts-two-commodities" $ postingsAsLines False (tpostings texp2) @?=
|
2019-02-14 16:14:52 +03:00
|
|
|
[ " a $1.00"
|
2019-11-16 14:23:38 +03:00
|
|
|
, " b -1.00h @ $1.00"
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "explicit-amounts-not-explicitly-balanced" $ postingsAsLines False (tpostings texp2b) @?=
|
2019-02-14 16:14:52 +03:00
|
|
|
[ " a $1.00"
|
2019-11-16 14:23:38 +03:00
|
|
|
, " b -1.00h"
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "implicit-amount-not-last" $ postingsAsLines False (tpostings t3) @?=
|
2019-02-14 16:14:52 +03:00
|
|
|
[" a $1.00", " b", " c $-1.00"]
|
2019-11-29 02:29:03 +03:00
|
|
|
-- , test "ensure-visibly-balanced" $
|
2019-11-27 23:46:29 +03:00
|
|
|
-- in postingsAsLines False (tpostings t4) @?=
|
2019-11-27 00:56:14 +03:00
|
|
|
-- [" a $-0.01", " b $0.005", " c $0.005"]
|
|
|
|
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "inferBalancingAmount" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
(fst <$> inferBalancingAmount M.empty nulltransaction) @?= Right nulltransaction
|
|
|
|
(fst <$> inferBalancingAmount M.empty nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` missingamt]}) @?=
|
2019-02-15 21:34:40 +03:00
|
|
|
Right nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` usd 5]}
|
2019-11-27 23:46:29 +03:00
|
|
|
(fst <$> inferBalancingAmount M.empty nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` (eur 3 @@ usd 4), "c" `post` missingamt]}) @?=
|
2019-02-15 21:34:40 +03:00
|
|
|
Right nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` (eur 3 @@ usd 4), "c" `post` usd 1]}
|
2020-10-28 04:53:37 +03:00
|
|
|
|
2019-11-27 23:46:29 +03:00
|
|
|
, tests "showTransaction" [
|
2019-12-30 01:36:32 +03:00
|
|
|
test "null transaction" $ showTransaction nulltransaction @?= "0000-01-01\n\n"
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "non-null transaction" $ showTransaction
|
2019-11-16 14:23:38 +03:00
|
|
|
nulltransaction
|
2020-08-26 11:11:20 +03:00
|
|
|
{ tdate = fromGregorian 2012 05 14
|
|
|
|
, tdate2 = Just $ fromGregorian 2012 05 15
|
2019-11-16 14:23:38 +03:00
|
|
|
, tstatus = Unmarked
|
|
|
|
, tcode = "code"
|
|
|
|
, tdescription = "desc"
|
|
|
|
, tcomment = "tcomment1\ntcomment2\n"
|
|
|
|
, ttags = [("ttag1", "val1")]
|
|
|
|
, tpostings =
|
|
|
|
[ nullposting
|
|
|
|
{ pstatus = Cleared
|
|
|
|
, paccount = "a"
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
, pamount = mixed [usd 1, hrs 2]
|
2019-11-16 14:23:38 +03:00
|
|
|
, pcomment = "\npcomment2\n"
|
|
|
|
, ptype = RegularPosting
|
|
|
|
, ptags = [("ptag1", "val1"), ("ptag2", "val2")]
|
|
|
|
}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
} @?=
|
2020-10-28 04:53:37 +03:00
|
|
|
T.unlines
|
2019-12-30 01:36:32 +03:00
|
|
|
[ "2012-05-14=2012-05-15 (code) desc ; tcomment1"
|
2019-11-16 14:23:38 +03:00
|
|
|
, " ; tcomment2"
|
|
|
|
, " * a $1.00"
|
|
|
|
, " ; pcomment2"
|
|
|
|
, " * a 2.00h"
|
|
|
|
, " ; pcomment2"
|
|
|
|
, ""
|
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "show a balanced transaction" $
|
2019-02-14 16:14:52 +03:00
|
|
|
(let t =
|
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"coopportunity"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "expenses:food:groceries", pamount = mixedAmount (usd 47.18), ptransaction = Just t}
|
|
|
|
, posting {paccount = "assets:checking", pamount = mixedAmount (usd (-47.18)), ptransaction = Just t}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
in showTransaction t) @?=
|
2020-10-28 04:53:37 +03:00
|
|
|
(T.unlines
|
2019-12-30 01:36:32 +03:00
|
|
|
[ "2007-01-28 coopportunity"
|
2019-02-14 16:14:52 +03:00
|
|
|
, " expenses:food:groceries $47.18"
|
|
|
|
, " assets:checking $-47.18"
|
|
|
|
, ""
|
|
|
|
])
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "show an unbalanced transaction, should not elide" $
|
2019-02-14 16:14:52 +03:00
|
|
|
(showTransaction
|
|
|
|
(txnTieKnot $
|
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"coopportunity"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "expenses:food:groceries", pamount = mixedAmount (usd 47.18)}
|
|
|
|
, posting {paccount = "assets:checking", pamount = mixedAmount (usd (-47.19))}
|
2019-11-27 23:46:29 +03:00
|
|
|
])) @?=
|
2020-10-28 04:53:37 +03:00
|
|
|
(T.unlines
|
2019-12-30 01:36:32 +03:00
|
|
|
[ "2007-01-28 coopportunity"
|
2019-02-14 16:14:52 +03:00
|
|
|
, " expenses:food:groceries $47.18"
|
|
|
|
, " assets:checking $-47.19"
|
|
|
|
, ""
|
|
|
|
])
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "show a transaction with one posting and a missing amount" $
|
2019-02-14 16:14:52 +03:00
|
|
|
(showTransaction
|
|
|
|
(txnTieKnot $
|
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"coopportunity"
|
|
|
|
""
|
|
|
|
[]
|
2019-11-27 23:46:29 +03:00
|
|
|
[posting {paccount = "expenses:food:groceries", pamount = missingmixedamt}])) @?=
|
2020-10-28 04:53:37 +03:00
|
|
|
(T.unlines ["2007-01-28 coopportunity", " expenses:food:groceries", ""])
|
2019-11-29 02:29:03 +03:00
|
|
|
, test "show a transaction with a priced commodityless amount" $
|
2020-12-25 08:38:26 +03:00
|
|
|
(showTransaction
|
2019-02-14 16:14:52 +03:00
|
|
|
(txnTieKnot $
|
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2010 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"x"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "a", pamount = mixedAmount $ num 1 `at` (usd 2 `withPrecision` Precision 0)}
|
2019-02-14 16:14:52 +03:00
|
|
|
, posting {paccount = "b", pamount = missingmixedamt}
|
2019-11-27 23:46:29 +03:00
|
|
|
])) @?=
|
2020-12-25 08:38:26 +03:00
|
|
|
(T.unlines ["2010-01-01 x", " a 1 @ $2", " b", ""])
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
, tests "balanceTransaction" [
|
2019-11-29 02:29:03 +03:00
|
|
|
test "detect unbalanced entry, sign error" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertLeft
|
2021-06-04 15:40:10 +03:00
|
|
|
(balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"test"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[posting {paccount = "a", pamount = mixedAmount (usd 1)}, posting {paccount = "b", pamount = mixedAmount (usd 1)}]))
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "detect unbalanced entry, multiple missing amounts" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertLeft $
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"test"
|
|
|
|
""
|
|
|
|
[]
|
|
|
|
[ posting {paccount = "a", pamount = missingmixedamt}
|
|
|
|
, posting {paccount = "b", pamount = missingmixedamt}
|
|
|
|
])
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "one missing amount is inferred" $
|
2019-02-14 16:14:52 +03:00
|
|
|
(pamount . last . tpostings <$>
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
""
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[posting {paccount = "a", pamount = mixedAmount (usd 1)}, posting {paccount = "b", pamount = missingmixedamt}])) @?=
|
|
|
|
Right (mixedAmount $ usd (-1))
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "conversion price is inferred" $
|
2019-02-14 16:14:52 +03:00
|
|
|
(pamount . head . tpostings <$>
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2007 01 28)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
""
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "a", pamount = mixedAmount (usd 1.35)}
|
|
|
|
, posting {paccount = "b", pamount = mixedAmount (eur (-1))}
|
2019-11-27 23:46:29 +03:00
|
|
|
])) @?=
|
2021-04-16 03:53:52 +03:00
|
|
|
Right (mixedAmount $ usd 1.35 @@ eur 1)
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "balanceTransaction balances based on cost if there are unit prices" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertRight $
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2011 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
""
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "a", pamount = mixedAmount $ usd 1 `at` eur 2}
|
|
|
|
, posting {paccount = "a", pamount = mixedAmount $ usd (-2) `at` eur 1}
|
2019-02-14 16:14:52 +03:00
|
|
|
])
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "balanceTransaction balances based on cost if there are total prices" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertRight $
|
2021-06-04 15:40:10 +03:00
|
|
|
balanceTransaction def
|
2019-02-14 16:14:52 +03:00
|
|
|
(Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2011 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
""
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "a", pamount = mixedAmount $ usd 1 @@ eur 1}
|
|
|
|
, posting {paccount = "a", pamount = mixedAmount $ usd (-2) @@ eur (-1)}
|
2019-02-14 16:14:52 +03:00
|
|
|
])
|
|
|
|
]
|
2019-11-27 23:46:29 +03:00
|
|
|
, tests "isTransactionBalanced" [
|
2019-11-29 02:29:03 +03:00
|
|
|
test "detect balanced" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "b", pamount = mixedAmount (usd 1.00)}
|
|
|
|
, posting {paccount = "c", pamount = mixedAmount (usd (-1.00))}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "detect unbalanced" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2019-02-14 16:14:52 +03:00
|
|
|
not $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "b", pamount = mixedAmount (usd 1.00)}
|
|
|
|
, posting {paccount = "c", pamount = mixedAmount (usd (-1.01))}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "detect unbalanced, one posting" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2019-02-14 16:14:52 +03:00
|
|
|
not $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[posting {paccount = "b", pamount = mixedAmount (usd 1.00)}]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "one zero posting is considered balanced for now" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[posting {paccount = "b", pamount = mixedAmount (usd 0)}]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "virtual postings don't need to balance" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "b", pamount = mixedAmount (usd 1.00)}
|
|
|
|
, posting {paccount = "c", pamount = mixedAmount (usd (-1.00))}
|
|
|
|
, posting {paccount = "d", pamount = mixedAmount (usd 100), ptype = VirtualPosting}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "balanced virtual postings need to balance among themselves" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2019-02-14 16:14:52 +03:00
|
|
|
not $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "b", pamount = mixedAmount (usd 1.00)}
|
|
|
|
, posting {paccount = "c", pamount = mixedAmount (usd (-1.00))}
|
|
|
|
, posting {paccount = "d", pamount = mixedAmount (usd 100), ptype = BalancedVirtualPosting}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
2019-11-29 02:29:03 +03:00
|
|
|
,test "balanced virtual postings need to balance among themselves (2)" $
|
2019-11-27 23:46:29 +03:00
|
|
|
assertBool "" $
|
2021-06-04 15:40:10 +03:00
|
|
|
isTransactionBalanced def $
|
2019-02-14 16:14:52 +03:00
|
|
|
Transaction
|
|
|
|
0
|
|
|
|
""
|
|
|
|
nullsourcepos
|
2020-08-26 11:11:20 +03:00
|
|
|
(fromGregorian 2009 01 01)
|
2019-02-14 16:14:52 +03:00
|
|
|
Nothing
|
|
|
|
Unmarked
|
|
|
|
""
|
|
|
|
"a"
|
|
|
|
""
|
|
|
|
[]
|
lib: Change internal representation of MixedAmount to use a strict Map
instead of a list of Amounts. No longer export Mixed constructor, to
keep API clean (if you really need it, you can import it directly from
Hledger.Data.Types). We also ensure the JSON representation of
MixedAmount doesn't change: it is stored as a normalised list of
Amounts.
This commit improves performance. Here are some indicative results.
hledger reg -f examples/10000x1000x10.journal
- Maximum residency decreases from 65MB to 60MB (8% decrease)
- Total memory in use decreases from 178MiB to 157MiB (12% decrease)
hledger reg -f examples/10000x10000x10.journal
- Maximum residency decreases from 69MB to 60MB (13% decrease)
- Total memory in use decreases from 198MiB to 153MiB (23% decrease)
hledger bal -f examples/10000x1000x10.journal
- Total heap usage decreases from 6.4GB to 6.0GB (6% decrease)
- Total memory in use decreases from 178MiB to 153MiB (14% decrease)
hledger bal -f examples/10000x10000x10.journal
- Total heap usage decreases from 7.3GB to 6.9GB (5% decrease)
- Total memory in use decreases from 196MiB to 185MiB (5% decrease)
hledger bal -M -f examples/10000x1000x10.journal
- Total heap usage decreases from 16.8GB to 10.6GB (47% decrease)
- Total time decreases from 14.3s to 12.0s (16% decrease)
hledger bal -M -f examples/10000x10000x10.journal
- Total heap usage decreases from 108GB to 48GB (56% decrease)
- Total time decreases from 62s to 41s (33% decrease)
If you never directly use the constructor Mixed or pattern match against
it then you don't need to make any changes. If you do, then do the
following:
- If you really care about the individual Amounts and never normalise
your MixedAmount (for example, just storing `Mixed amts` and then
extracting `amts` as a pattern match, then use should switch to using
[Amount]. This should just involve removing the `Mixed` constructor.
- If you ever call `mixed`, `normaliseMixedAmount`, or do any sort of
amount arithmetic (+), (-), then you should replace the constructor
`Mixed` with the function `mixed`. To extract the list of Amounts, use
the function `amounts`.
- If you ever call `normaliseMixedAmountSquashPricesForDisplay`, you can
replace that with `mixedAmountStripPrices`. (N.B. this does something
slightly different from `normaliseMixedAmountSquashPricesForDisplay`,
but I don't think there's any use case for squashing prices and then
keeping the first of the squashed prices around. If you disagree let
me know.)
- Any remaining calls to `normaliseMixedAmount` can be removed, as that
is now the identity function.
2021-01-29 08:07:11 +03:00
|
|
|
[ posting {paccount = "b", pamount = mixedAmount (usd 1.00)}
|
|
|
|
, posting {paccount = "c", pamount = mixedAmount (usd (-1.00))}
|
|
|
|
, posting {paccount = "d", pamount = mixedAmount (usd 100), ptype = BalancedVirtualPosting}
|
|
|
|
, posting {paccount = "3", pamount = mixedAmount (usd (-100)), ptype = BalancedVirtualPosting}
|
2019-02-14 16:14:52 +03:00
|
|
|
]
|
|
|
|
]
|
2018-09-04 21:31:31 +03:00
|
|
|
]
|