2009-04-03 14:58:05 +04:00
|
|
|
{-|
|
|
|
|
|
2012-05-14 22:52:22 +04:00
|
|
|
A 'Posting' represents a change (by some 'MixedAmount') of the balance in
|
|
|
|
some 'Account'. Each 'Transaction' contains two or more postings which
|
|
|
|
should add up to 0. Postings reference their parent transaction, so we can
|
|
|
|
look up the date or description there.
|
2009-04-03 14:58:05 +04:00
|
|
|
|
|
|
|
-}
|
|
|
|
|
2024-10-01 00:10:16 +03:00
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
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
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
2012-05-07 18:36:40 +04:00
|
|
|
module Hledger.Data.Posting (
|
|
|
|
-- * Posting
|
|
|
|
nullposting,
|
2012-12-06 04:03:07 +04:00
|
|
|
posting,
|
|
|
|
post,
|
2019-02-22 03:20:04 +03:00
|
|
|
vpost,
|
|
|
|
post',
|
|
|
|
vpost',
|
2018-10-12 06:37:20 +03:00
|
|
|
nullsourcepos,
|
|
|
|
nullassertion,
|
2019-02-22 03:20:04 +03:00
|
|
|
balassert,
|
|
|
|
balassertTot,
|
|
|
|
balassertParInc,
|
|
|
|
balassertTotInc,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * operations
|
2017-01-13 18:25:44 +03:00
|
|
|
originalPosting,
|
2015-05-16 21:51:35 +03:00
|
|
|
postingStatus,
|
2012-05-07 18:36:40 +04:00
|
|
|
isReal,
|
|
|
|
isVirtual,
|
|
|
|
isBalancedVirtual,
|
|
|
|
isEmptyPosting,
|
2019-02-18 23:11:07 +03:00
|
|
|
hasBalanceAssignment,
|
2012-05-07 18:36:40 +04:00
|
|
|
hasAmount,
|
2012-05-28 04:27:55 +04:00
|
|
|
postingAllTags,
|
|
|
|
transactionAllTags,
|
2012-12-22 04:24:38 +04:00
|
|
|
relatedPostings,
|
2024-01-23 20:58:26 +03:00
|
|
|
postingStripCosts,
|
2020-11-24 20:17:01 +03:00
|
|
|
postingApplyAliases,
|
2021-07-26 17:37:08 +03:00
|
|
|
postingApplyCommodityStyles,
|
2023-09-19 09:54:25 +03:00
|
|
|
postingStyleAmounts,
|
2022-01-29 08:56:49 +03:00
|
|
|
postingAddTags,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * date operations
|
|
|
|
postingDate,
|
2012-12-06 08:43:41 +04:00
|
|
|
postingDate2,
|
2021-10-10 22:27:09 +03:00
|
|
|
postingDateOrDate2,
|
2012-05-07 18:36:40 +04:00
|
|
|
isPostingInDateSpan,
|
2014-04-14 01:57:40 +04:00
|
|
|
isPostingInDateSpan',
|
2012-05-14 22:52:22 +04:00
|
|
|
-- * account name operations
|
2012-05-07 18:36:40 +04:00
|
|
|
accountNamesFromPostings,
|
2019-07-17 20:38:14 +03:00
|
|
|
-- * comment/tag operations
|
|
|
|
commentJoin,
|
|
|
|
commentAddTag,
|
2023-11-24 01:29:08 +03:00
|
|
|
commentAddTagUnspaced,
|
2019-07-17 20:38:14 +03:00
|
|
|
commentAddTagNextLine,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * arithmetic
|
|
|
|
sumPostings,
|
|
|
|
-- * rendering
|
|
|
|
showPosting,
|
2021-10-14 08:48:19 +03:00
|
|
|
showPostingLines,
|
|
|
|
postingAsLines,
|
|
|
|
postingsAsLines,
|
2023-11-23 10:11:59 +03:00
|
|
|
postingsAsLinesBeancount,
|
|
|
|
postingAsLinesBeancount,
|
2021-10-14 08:48:19 +03:00
|
|
|
showAccountName,
|
2023-11-23 10:11:59 +03:00
|
|
|
showAccountNameBeancount,
|
2021-10-14 08:48:19 +03:00
|
|
|
renderCommentLines,
|
2022-07-10 13:33:21 +03:00
|
|
|
showBalanceAssertion,
|
2012-05-07 18:36:40 +04:00
|
|
|
-- * misc.
|
2019-05-23 22:16:20 +03:00
|
|
|
postingTransformAmount,
|
2019-05-28 21:42:32 +03:00
|
|
|
postingApplyValuation,
|
2019-05-23 22:16:20 +03:00
|
|
|
postingToCost,
|
2022-01-15 16:25:16 +03:00
|
|
|
postingAddInferredEquityPostings,
|
2022-01-27 05:49:45 +03:00
|
|
|
postingPriceDirectivesFromCost,
|
2018-09-06 23:08:26 +03:00
|
|
|
tests_Posting
|
2012-05-07 18:36:40 +04:00
|
|
|
)
|
2009-04-03 14:58:05 +04:00
|
|
|
where
|
2019-11-12 04:14:21 +03:00
|
|
|
|
2021-10-14 08:48:19 +03:00
|
|
|
import Data.Default (def)
|
2019-11-12 04:14:21 +03:00
|
|
|
import Data.Foldable (asum)
|
2023-04-30 07:25:11 +03:00
|
|
|
import Data.Function ((&))
|
2019-05-23 22:16:20 +03:00
|
|
|
import qualified Data.Map as M
|
2022-01-27 05:49:45 +03:00
|
|
|
import Data.Maybe (fromMaybe, isJust, mapMaybe)
|
2024-10-01 00:10:16 +03:00
|
|
|
import Data.List (sort, union)
|
|
|
|
#if !MIN_VERSION_base(4,20,0)
|
|
|
|
import Data.List (foldl')
|
|
|
|
#endif
|
2021-09-18 02:56:30 +03:00
|
|
|
import qualified Data.Set as S
|
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
|
2021-10-14 08:48:19 +03:00
|
|
|
import qualified Data.Text.Lazy as TL
|
|
|
|
import qualified Data.Text.Lazy.Builder as TB
|
2021-01-29 15:34:18 +03:00
|
|
|
import Data.Time.Calendar (Day)
|
2022-02-11 22:18:35 +03:00
|
|
|
import Safe (maximumBound)
|
2021-11-12 04:49:26 +03:00
|
|
|
import Text.DocLayout (realLength)
|
2021-10-14 08:48:19 +03:00
|
|
|
|
2022-08-23 13:58:31 +03:00
|
|
|
import Text.Tabular.AsciiWide hiding (render)
|
2011-05-28 08:11:44 +04: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.Amount
|
|
|
|
import Hledger.Data.AccountName
|
2021-10-14 08:48:19 +03:00
|
|
|
import Hledger.Data.Dates (nulldate, spanContainsDate)
|
2019-06-15 02:17:06 +03:00
|
|
|
import Hledger.Data.Valuation
|
2009-04-03 14:58:05 +04:00
|
|
|
|
|
|
|
|
2023-08-31 06:11:14 +03:00
|
|
|
instance HasAmounts BalanceAssertion where
|
|
|
|
styleAmounts styles ba@BalanceAssertion{baamount} = ba{baamount=styleAmounts styles baamount}
|
|
|
|
|
|
|
|
instance HasAmounts Posting where
|
|
|
|
styleAmounts styles p@Posting{pamount, pbalanceassertion} =
|
|
|
|
p{ pamount=styleAmounts styles pamount
|
|
|
|
,pbalanceassertion=styleAmounts styles pbalanceassertion
|
|
|
|
}
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2023-09-19 09:54:25 +03:00
|
|
|
{-# DEPRECATED postingApplyCommodityStyles "please use styleAmounts instead" #-}
|
|
|
|
-- | Find and apply the appropriate display style to the posting amounts
|
|
|
|
-- in each commodity (see journalCommodityStyles).
|
|
|
|
-- Main amount precisions may be set or not according to the styles, but cost precisions are not set.
|
|
|
|
postingApplyCommodityStyles :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
|
|
|
|
postingApplyCommodityStyles = styleAmounts
|
|
|
|
|
|
|
|
{-# DEPRECATED postingStyleAmounts "please use styleAmounts instead" #-}
|
|
|
|
-- | Like postingApplyCommodityStyles, but neither
|
|
|
|
-- main amount precisions or cost precisions are set.
|
|
|
|
postingStyleAmounts :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
|
|
|
|
postingStyleAmounts = styleAmounts
|
|
|
|
|
2012-12-06 04:03:07 +04:00
|
|
|
nullposting, posting :: Posting
|
|
|
|
nullposting = Posting
|
2012-12-06 04:28:23 +04:00
|
|
|
{pdate=Nothing
|
2012-12-06 06:41:37 +04:00
|
|
|
,pdate2=Nothing
|
2017-06-16 02:25:37 +03:00
|
|
|
,pstatus=Unmarked
|
2012-12-06 04:03:07 +04:00
|
|
|
,paccount=""
|
|
|
|
,pamount=nullmixedamt
|
|
|
|
,pcomment=""
|
|
|
|
,ptype=RegularPosting
|
|
|
|
,ptags=[]
|
2013-05-29 03:18:15 +04:00
|
|
|
,pbalanceassertion=Nothing
|
2012-12-06 04:03:07 +04:00
|
|
|
,ptransaction=Nothing
|
2019-02-21 07:07:40 +03:00
|
|
|
,poriginal=Nothing
|
2012-12-06 04:03:07 +04:00
|
|
|
}
|
|
|
|
posting = nullposting
|
|
|
|
|
2019-02-22 03:20:04 +03:00
|
|
|
-- constructors
|
|
|
|
|
|
|
|
-- | Make a posting to an account.
|
2012-12-06 04:03:07 +04:00
|
|
|
post :: AccountName -> Amount -> Posting
|
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
|
|
|
post acc amt = posting {paccount=acc, pamount=mixedAmount amt}
|
2019-02-22 03:20:04 +03:00
|
|
|
|
|
|
|
-- | Make a virtual (unbalanced) posting to an account.
|
|
|
|
vpost :: AccountName -> Amount -> Posting
|
|
|
|
vpost acc amt = (post acc amt){ptype=VirtualPosting}
|
|
|
|
|
|
|
|
-- | Make a posting to an account, maybe with a balance assertion.
|
|
|
|
post' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
|
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
|
|
|
post' acc amt ass = posting {paccount=acc, pamount=mixedAmount amt, pbalanceassertion=ass}
|
2019-02-22 03:20:04 +03:00
|
|
|
|
|
|
|
-- | Make a virtual (unbalanced) posting to an account, maybe with a balance assertion.
|
|
|
|
vpost' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
|
|
|
|
vpost' acc amt ass = (post' acc amt ass){ptype=VirtualPosting, pbalanceassertion=ass}
|
2009-04-03 14:58:05 +04:00
|
|
|
|
2021-08-25 06:04:28 +03:00
|
|
|
nullsourcepos :: (SourcePos, SourcePos)
|
|
|
|
nullsourcepos = (SourcePos "" (mkPos 1) (mkPos 1), SourcePos "" (mkPos 2) (mkPos 1))
|
2018-10-12 06:37:20 +03:00
|
|
|
|
2019-12-07 20:05:41 +03:00
|
|
|
nullassertion :: BalanceAssertion
|
2018-10-12 06:37:20 +03:00
|
|
|
nullassertion = BalanceAssertion
|
|
|
|
{baamount=nullamt
|
2019-02-15 21:34:40 +03:00
|
|
|
,batotal=False
|
2019-02-18 06:50:22 +03:00
|
|
|
,bainclusive=False
|
2021-08-25 06:04:28 +03:00
|
|
|
,baposition=initialPos ""
|
2018-10-12 06:37:20 +03:00
|
|
|
}
|
|
|
|
|
2019-02-22 03:20:04 +03:00
|
|
|
-- | Make a partial, exclusive balance assertion.
|
|
|
|
balassert :: Amount -> Maybe BalanceAssertion
|
|
|
|
balassert amt = Just $ nullassertion{baamount=amt}
|
|
|
|
|
|
|
|
-- | Make a total, exclusive balance assertion.
|
|
|
|
balassertTot :: Amount -> Maybe BalanceAssertion
|
|
|
|
balassertTot amt = Just $ nullassertion{baamount=amt, batotal=True}
|
|
|
|
|
|
|
|
-- | Make a partial, inclusive balance assertion.
|
|
|
|
balassertParInc :: Amount -> Maybe BalanceAssertion
|
|
|
|
balassertParInc amt = Just $ nullassertion{baamount=amt, bainclusive=True}
|
|
|
|
|
|
|
|
-- | Make a total, inclusive balance assertion.
|
|
|
|
balassertTotInc :: Amount -> Maybe BalanceAssertion
|
|
|
|
balassertTotInc amt = Just $ nullassertion{baamount=amt, batotal=True, bainclusive=True}
|
|
|
|
|
2021-10-14 08:48:19 +03:00
|
|
|
-- | Render a balance assertion, as the =[=][*] symbol and expected amount.
|
|
|
|
showBalanceAssertion :: BalanceAssertion -> WideBuilder
|
|
|
|
showBalanceAssertion ba =
|
2024-02-26 05:47:58 +03:00
|
|
|
singleton '=' <> eq <> ast <> singleton ' ' <> showAmountB def{displayZeroCommodity=True, displayForceDecimalMark=True} (baamount ba)
|
2021-10-14 08:48:19 +03:00
|
|
|
where
|
|
|
|
eq = if batotal ba then singleton '=' else mempty
|
|
|
|
ast = if bainclusive ba then singleton '*' else mempty
|
|
|
|
singleton c = WideBuilder (TB.singleton c) 1
|
|
|
|
|
2017-05-30 23:39:53 +03:00
|
|
|
-- Get the original posting, if any.
|
2017-01-13 18:25:44 +03:00
|
|
|
originalPosting :: Posting -> Posting
|
2019-02-21 07:07:40 +03:00
|
|
|
originalPosting p = fromMaybe p $ poriginal p
|
2017-01-13 18:25:44 +03:00
|
|
|
|
2009-04-03 14:58:05 +04:00
|
|
|
showPosting :: Posting -> String
|
2021-10-14 08:48:19 +03:00
|
|
|
showPosting p = T.unpack . T.unlines $ postingsAsLines False [p]
|
|
|
|
|
|
|
|
-- | Render a posting, at the appropriate width for aligning with
|
|
|
|
-- its siblings if any. Used by the rewrite command.
|
|
|
|
showPostingLines :: Posting -> [Text]
|
|
|
|
showPostingLines p = first3 $ postingAsLines False False maxacctwidth maxamtwidth p
|
|
|
|
where
|
|
|
|
linesWithWidths = map (postingAsLines False False maxacctwidth maxamtwidth) . maybe [p] tpostings $ ptransaction p
|
2021-11-15 05:31:21 +03:00
|
|
|
maxacctwidth = maximumBound 0 $ map second3 linesWithWidths
|
|
|
|
maxamtwidth = maximumBound 0 $ map third3 linesWithWidths
|
2021-10-14 08:48:19 +03:00
|
|
|
|
|
|
|
-- | Given a transaction and its postings, render the postings, suitable
|
|
|
|
-- for `print` output. Normally this output will be valid journal syntax which
|
|
|
|
-- hledger can reparse (though it may include no-longer-valid balance assertions).
|
|
|
|
--
|
|
|
|
-- Explicit amounts are shown, any implicit amounts are not.
|
|
|
|
--
|
|
|
|
-- 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.
|
2022-12-26 23:20:18 +03:00
|
|
|
-- When the posting has a balance assertion, it is attached to the last of these postings.
|
2021-10-14 08:48:19 +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.
|
|
|
|
--
|
|
|
|
-- Posting amounts will be aligned with each other, starting about 4 columns
|
|
|
|
-- beyond the widest account name (see postingAsLines for details).
|
|
|
|
postingsAsLines :: Bool -> [Posting] -> [Text]
|
|
|
|
postingsAsLines onelineamounts ps = concatMap first3 linesWithWidths
|
|
|
|
where
|
|
|
|
linesWithWidths = map (postingAsLines False onelineamounts maxacctwidth maxamtwidth) ps
|
2021-11-15 05:31:21 +03:00
|
|
|
maxacctwidth = maximumBound 0 $ map second3 linesWithWidths
|
|
|
|
maxamtwidth = maximumBound 0 $ map third3 linesWithWidths
|
2021-10-14 08:48:19 +03:00
|
|
|
|
|
|
|
-- | Render one posting, on one or more lines, suitable for `print` output.
|
|
|
|
-- There will be an indented account name, plus one or more of status flag,
|
|
|
|
-- posting amount, balance assertion, same-line comment, next-line comments.
|
|
|
|
--
|
|
|
|
-- If the posting's amount is implicit or if elideamount is true, no amount is shown.
|
|
|
|
--
|
|
|
|
-- If the posting's amount is explicit and multi-commodity, multiple similar
|
|
|
|
-- 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).
|
|
|
|
--
|
2023-08-24 16:57:19 +03:00
|
|
|
-- If an amount is zero, any commodity symbol attached to it is shown
|
|
|
|
-- (and the corresponding commodity display style is used).
|
|
|
|
--
|
2021-10-14 08:48:19 +03:00
|
|
|
-- By default, 4 spaces (2 if there's a status flag) are shown between
|
|
|
|
-- account name and start of amount area, which is typically 12 chars wide
|
|
|
|
-- and contains a right-aligned amount (so 10-12 visible spaces between
|
|
|
|
-- account name and amount is typical).
|
|
|
|
-- When given a list of postings to be aligned with, the whitespace will be
|
|
|
|
-- increased if needed to match the posting with the longest account name.
|
|
|
|
-- This is used to align the amounts of a transaction's postings.
|
|
|
|
--
|
|
|
|
-- 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)
|
2021-04-22 08:20:56 +03:00
|
|
|
where
|
2021-10-14 08:48:19 +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 $
|
|
|
|
render [ textCell BottomLeft statusandaccount
|
|
|
|
, textCell BottomLeft " "
|
|
|
|
, Cell BottomLeft [pad amt]
|
|
|
|
, Cell BottomLeft [assertion]
|
|
|
|
, textCell BottomLeft samelinecomment
|
|
|
|
]
|
2022-12-26 23:20:18 +03:00
|
|
|
| (amt,assertion) <- shownAmountsAssertions]
|
2021-10-14 08:48:19 +03:00
|
|
|
render = renderRow def{tableBorders=False, borderSpaces=False} . Group NoLine . map Header
|
|
|
|
pad amt = WideBuilder (TB.fromText $ T.replicate w " ") w <> amt
|
|
|
|
where w = max 12 amtwidth - wbWidth amt -- min. 12 for backwards compatibility
|
|
|
|
|
|
|
|
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) <> " "
|
|
|
|
|
|
|
|
-- currently prices are considered part of the amount string when right-aligning amounts
|
|
|
|
-- 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.
|
|
|
|
shownAmounts
|
|
|
|
| elideamount = [mempty]
|
2023-11-23 03:22:51 +03:00
|
|
|
| otherwise = showMixedAmountLinesB displayopts $ pamount p
|
2024-01-24 00:28:19 +03:00
|
|
|
where displayopts = defaultFmt{
|
2023-12-08 03:25:40 +03:00
|
|
|
displayZeroCommodity=True, displayForceDecimalMark=True, displayOneLine=onelineamounts
|
2023-11-23 03:22:51 +03:00
|
|
|
}
|
2021-11-15 05:31:21 +03:00
|
|
|
thisamtwidth = maximumBound 0 $ map wbWidth shownAmounts
|
2021-10-14 08:48:19 +03:00
|
|
|
|
2022-12-26 23:20:18 +03:00
|
|
|
-- when there is a balance assertion, show it only on the last posting line
|
|
|
|
shownAmountsAssertions = zip shownAmounts shownAssertions
|
|
|
|
where
|
|
|
|
shownAssertions = replicate (length shownAmounts - 1) mempty ++ [assertion]
|
|
|
|
where
|
|
|
|
assertion = maybe mempty ((WideBuilder (TB.singleton ' ') 1 <>).showBalanceAssertion) $ pbalanceassertion p
|
|
|
|
|
|
|
|
-- 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 = realLength $ pacctstr p
|
|
|
|
|
2021-10-14 08:48:19 +03:00
|
|
|
(samelinecomment, newlinecomments) =
|
|
|
|
case renderCommentLines (pcomment p) of [] -> ("",[])
|
|
|
|
c:cs -> (c,cs)
|
|
|
|
|
|
|
|
-- | Show an account name, clipped to the given width if any, and
|
|
|
|
-- appropriately bracketed/parenthesised for the given posting type.
|
|
|
|
showAccountName :: Maybe Int -> PostingType -> AccountName -> Text
|
|
|
|
showAccountName w = fmt
|
|
|
|
where
|
|
|
|
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
|
|
|
|
|
2023-11-23 10:11:59 +03:00
|
|
|
-- | Like postingsAsLines but generates Beancount journal format.
|
|
|
|
postingsAsLinesBeancount :: [Posting] -> [Text]
|
|
|
|
postingsAsLinesBeancount ps = concatMap first3 linesWithWidths
|
|
|
|
where
|
|
|
|
linesWithWidths = map (postingAsLinesBeancount False maxacctwidth maxamtwidth) ps
|
|
|
|
maxacctwidth = maximumBound 0 $ map second3 linesWithWidths
|
|
|
|
maxamtwidth = maximumBound 0 $ map third3 linesWithWidths
|
|
|
|
|
|
|
|
-- | Like postingAsLines but generates Beancount journal format.
|
|
|
|
postingAsLinesBeancount :: Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
|
|
|
|
postingAsLinesBeancount elideamount acctwidth amtwidth p =
|
|
|
|
(concatMap (++ newlinecomments) postingblocks, thisacctwidth, thisamtwidth)
|
|
|
|
where
|
|
|
|
-- 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 $
|
|
|
|
render [ textCell BottomLeft statusandaccount
|
|
|
|
, textCell BottomLeft " "
|
|
|
|
, Cell BottomLeft [pad amt]
|
|
|
|
, textCell BottomLeft samelinecomment
|
|
|
|
]
|
2023-11-23 14:47:15 +03:00
|
|
|
| (amt,_assertion) <- shownAmountsAssertions]
|
2023-11-23 10:11:59 +03:00
|
|
|
render = renderRow def{tableBorders=False, borderSpaces=False} . Group NoLine . map Header
|
|
|
|
pad amt = WideBuilder (TB.fromText $ T.replicate w " ") w <> amt
|
|
|
|
where w = max 12 amtwidth - wbWidth amt -- min. 12 for backwards compatibility
|
|
|
|
|
|
|
|
pacct = showAccountNameBeancount Nothing $ paccount p
|
|
|
|
pstatusandacct p' = if pstatus p' == Pending then "! " else "" <> pacct
|
|
|
|
|
|
|
|
-- currently prices are considered part of the amount string when right-aligning amounts
|
|
|
|
-- 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.
|
|
|
|
shownAmounts
|
|
|
|
| elideamount = [mempty]
|
|
|
|
| otherwise = showMixedAmountLinesB displayopts a'
|
|
|
|
where
|
2024-01-24 00:28:19 +03:00
|
|
|
displayopts = defaultFmt{ displayZeroCommodity=True, displayForceDecimalMark=True }
|
2023-11-23 10:11:59 +03:00
|
|
|
a' = mapMixedAmount amountToBeancount $ pamount p
|
|
|
|
thisamtwidth = maximumBound 0 $ map wbWidth shownAmounts
|
|
|
|
|
|
|
|
-- when there is a balance assertion, show it only on the last posting line
|
|
|
|
shownAmountsAssertions = zip shownAmounts shownAssertions
|
|
|
|
where
|
|
|
|
shownAssertions = replicate (length shownAmounts - 1) mempty ++ [assertion]
|
|
|
|
where
|
|
|
|
assertion = maybe mempty ((WideBuilder (TB.singleton ' ') 1 <>).showBalanceAssertion) $ pbalanceassertion p
|
|
|
|
|
|
|
|
-- 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 = realLength pacct
|
|
|
|
|
|
|
|
(samelinecomment, newlinecomments) =
|
|
|
|
case renderCommentLines (pcomment p) of [] -> ("",[])
|
|
|
|
c:cs -> (c,cs)
|
|
|
|
|
|
|
|
type BeancountAmount = Amount
|
|
|
|
|
|
|
|
-- | Do some best effort adjustments to make an amount that renders
|
|
|
|
-- in a way that Beancount can read: forces the commodity symbol to the right,
|
2023-11-23 14:47:15 +03:00
|
|
|
-- converts a few currency symbols to names, capitalises all letters.
|
2023-11-23 10:11:59 +03:00
|
|
|
amountToBeancount :: Amount -> BeancountAmount
|
2024-01-23 20:50:11 +03:00
|
|
|
amountToBeancount a@Amount{acommodity=c,astyle=s,acost=mp} = a{acommodity=c', astyle=s', acost=mp'}
|
2023-11-23 10:11:59 +03:00
|
|
|
-- https://beancount.github.io/docs/beancount_language_syntax.html#commodities-currencies
|
|
|
|
where
|
2023-11-23 14:47:15 +03:00
|
|
|
c' = T.toUpper $
|
|
|
|
T.replace "$" "USD" $
|
|
|
|
T.replace "€" "EUR" $
|
|
|
|
T.replace "¥" "JPY" $
|
|
|
|
T.replace "£" "GBP" $
|
|
|
|
c
|
2023-11-23 12:54:10 +03:00
|
|
|
s' = s{ascommodityside=R, ascommodityspaced=True}
|
|
|
|
mp' = costToBeancount <$> mp
|
|
|
|
where
|
2024-01-23 20:38:59 +03:00
|
|
|
costToBeancount (TotalCost amt) = TotalCost $ amountToBeancount amt
|
|
|
|
costToBeancount (UnitCost amt) = UnitCost $ amountToBeancount amt
|
2023-11-23 10:11:59 +03:00
|
|
|
|
|
|
|
-- | Like showAccountName for Beancount journal format.
|
|
|
|
-- Calls accountNameToBeancount first.
|
|
|
|
showAccountNameBeancount :: Maybe Int -> AccountName -> Text
|
|
|
|
showAccountNameBeancount w = maybe id T.take w . accountNameToBeancount
|
|
|
|
|
2021-10-14 08:48:19 +03:00
|
|
|
-- | Render a transaction or posting's comment as indented, semicolon-prefixed comment lines.
|
|
|
|
-- The first line (unless empty) will have leading space, subsequent lines will have a larger indent.
|
|
|
|
renderCommentLines :: Text -> [Text]
|
|
|
|
renderCommentLines t =
|
|
|
|
case T.lines t of
|
|
|
|
[] -> []
|
|
|
|
[l] -> [commentSpace $ comment l] -- single-line comment
|
|
|
|
("":ls) -> "" : map (lineIndent . comment) ls -- multi-line comment with empty first line
|
|
|
|
(l:ls) -> commentSpace (comment l) : map (lineIndent . comment) ls
|
|
|
|
where
|
|
|
|
comment = ("; "<>)
|
|
|
|
|
|
|
|
-- | Prepend a suitable indent for a posting (or transaction/posting comment) line.
|
|
|
|
lineIndent :: Text -> Text
|
|
|
|
lineIndent = (" "<>)
|
|
|
|
|
|
|
|
-- | Prepend the space required before a same-line comment.
|
|
|
|
commentSpace :: Text -> Text
|
|
|
|
commentSpace = (" "<>)
|
|
|
|
|
2012-05-14 22:52:22 +04:00
|
|
|
|
2009-04-03 14:58:05 +04:00
|
|
|
isReal :: Posting -> Bool
|
|
|
|
isReal p = ptype p == RegularPosting
|
|
|
|
|
2009-05-17 03:12:42 +04:00
|
|
|
isVirtual :: Posting -> Bool
|
|
|
|
isVirtual p = ptype p == VirtualPosting
|
|
|
|
|
|
|
|
isBalancedVirtual :: Posting -> Bool
|
|
|
|
isBalancedVirtual p = ptype p == BalancedVirtualPosting
|
|
|
|
|
2009-04-03 14:58:05 +04:00
|
|
|
hasAmount :: Posting -> Bool
|
2021-07-13 10:11:50 +03:00
|
|
|
hasAmount = not . isMissingMixedAmount . pamount
|
2009-05-25 21:28:41 +04:00
|
|
|
|
2019-02-18 23:11:07 +03:00
|
|
|
hasBalanceAssignment :: Posting -> Bool
|
|
|
|
hasBalanceAssignment p = not (hasAmount p) && isJust (pbalanceassertion p)
|
2016-12-10 18:04:48 +03:00
|
|
|
|
2017-12-29 23:16:46 +03:00
|
|
|
-- | Sorted unique account names referenced by these postings.
|
2009-12-19 08:57:54 +03:00
|
|
|
accountNamesFromPostings :: [Posting] -> [AccountName]
|
2021-09-18 02:56:30 +03:00
|
|
|
accountNamesFromPostings = S.toList . S.fromList . map paccount
|
2009-12-19 08:57:54 +03:00
|
|
|
|
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
|
|
|
-- | Sum all amounts from a list of postings.
|
2009-12-19 08:57:54 +03:00
|
|
|
sumPostings :: [Posting] -> MixedAmount
|
2021-01-29 15:34:18 +03:00
|
|
|
sumPostings = foldl' (\amt p -> maPlus amt $ pamount p) nullmixedamt
|
2009-12-19 08:57:54 +03:00
|
|
|
|
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
|
|
|
-- | Strip all prices from a Posting.
|
2024-01-23 20:58:26 +03:00
|
|
|
postingStripCosts :: Posting -> Posting
|
|
|
|
postingStripCosts = postingTransformAmount mixedAmountStripCosts
|
2016-12-10 18:04:48 +03:00
|
|
|
|
2012-12-06 06:41:37 +04:00
|
|
|
-- | Get a posting's (primary) date - it's own primary date if specified,
|
|
|
|
-- otherwise the parent transaction's primary date, or the null date if
|
|
|
|
-- there is no parent transaction.
|
2009-12-19 08:57:54 +03:00
|
|
|
postingDate :: Posting -> Day
|
2019-11-12 04:14:21 +03:00
|
|
|
postingDate p = fromMaybe nulldate $ asum dates
|
|
|
|
where dates = [ pdate p, tdate <$> ptransaction p ]
|
2009-12-19 08:57:54 +03:00
|
|
|
|
2012-12-06 08:43:41 +04:00
|
|
|
-- | Get a posting's secondary (secondary) date, which is the first of:
|
2012-12-06 06:41:37 +04:00
|
|
|
-- posting's secondary date, transaction's secondary date, posting's
|
|
|
|
-- primary date, transaction's primary date, or the null date if there is
|
|
|
|
-- no parent transaction.
|
2012-12-06 08:43:41 +04:00
|
|
|
postingDate2 :: Posting -> Day
|
2019-11-12 04:14:21 +03:00
|
|
|
postingDate2 p = fromMaybe nulldate $ asum dates
|
|
|
|
where dates = [ pdate2 p
|
|
|
|
, tdate2 =<< ptransaction p
|
|
|
|
, pdate p
|
|
|
|
, tdate <$> ptransaction p
|
2012-12-06 06:41:37 +04:00
|
|
|
]
|
2012-12-06 05:10:15 +04:00
|
|
|
|
2021-10-10 22:27:09 +03:00
|
|
|
-- | Get a posting's primary or secondary date, as specified.
|
|
|
|
postingDateOrDate2 :: WhichDate -> Posting -> Day
|
|
|
|
postingDateOrDate2 PrimaryDate = postingDate
|
|
|
|
postingDateOrDate2 SecondaryDate = postingDate2
|
|
|
|
|
2017-06-16 02:25:37 +03:00
|
|
|
-- | Get a posting's status. This is cleared or pending if those are
|
|
|
|
-- explicitly set on the posting, otherwise the status of its parent
|
|
|
|
-- transaction, or unmarked if there is no parent transaction. (Note
|
2019-07-15 13:28:52 +03:00
|
|
|
-- the ambiguity, unmarked can mean "posting and transaction are both
|
2017-06-16 02:25:37 +03:00
|
|
|
-- unmarked" or "posting is unmarked and don't know about the transaction".
|
2017-06-16 02:54:34 +03:00
|
|
|
postingStatus :: Posting -> Status
|
2021-08-16 06:55:15 +03:00
|
|
|
postingStatus Posting{pstatus=s, ptransaction=mt} = case s of
|
|
|
|
Unmarked -> maybe Unmarked tstatus mt
|
|
|
|
_ -> s
|
2015-05-16 21:51:35 +03:00
|
|
|
|
2012-05-28 04:27:55 +04:00
|
|
|
-- | Tags for this posting including any inherited from its parent transaction.
|
|
|
|
postingAllTags :: Posting -> [Tag]
|
2014-03-06 02:43:58 +04:00
|
|
|
postingAllTags p = ptags p ++ maybe [] ttags (ptransaction p)
|
2012-05-28 04:27:55 +04:00
|
|
|
|
2014-03-02 05:42:13 +04:00
|
|
|
-- | Tags for this transaction including any from its postings.
|
2012-05-28 04:27:55 +04:00
|
|
|
transactionAllTags :: Transaction -> [Tag]
|
2014-03-02 05:42:13 +04:00
|
|
|
transactionAllTags t = ttags t ++ concatMap ptags (tpostings t)
|
2012-05-28 04:27:55 +04:00
|
|
|
|
2012-12-22 04:24:38 +04:00
|
|
|
-- Get the other postings from this posting's transaction.
|
|
|
|
relatedPostings :: Posting -> [Posting]
|
|
|
|
relatedPostings p@Posting{ptransaction=Just t} = filter (/= p) $ tpostings t
|
|
|
|
relatedPostings _ = []
|
|
|
|
|
2009-12-19 08:57:54 +03:00
|
|
|
-- | Does this posting fall within the given date span ?
|
|
|
|
isPostingInDateSpan :: DateSpan -> Posting -> Bool
|
2019-11-12 04:14:21 +03:00
|
|
|
isPostingInDateSpan = isPostingInDateSpan' PrimaryDate
|
2009-12-21 08:23:07 +03:00
|
|
|
|
2014-04-14 01:57:40 +04:00
|
|
|
-- --date2-sensitive version, separate for now to avoid disturbing multiBalanceReport.
|
|
|
|
isPostingInDateSpan' :: WhichDate -> DateSpan -> Posting -> Bool
|
|
|
|
isPostingInDateSpan' PrimaryDate s = spanContainsDate s . postingDate
|
|
|
|
isPostingInDateSpan' SecondaryDate s = spanContainsDate s . postingDate2
|
|
|
|
|
2009-12-21 08:23:07 +03:00
|
|
|
isEmptyPosting :: Posting -> Bool
|
2020-05-30 04:57:22 +03:00
|
|
|
isEmptyPosting = mixedAmountLooksZero . pamount
|
2009-12-21 08:23:07 +03:00
|
|
|
|
2020-11-24 20:17:01 +03:00
|
|
|
-- | Apply some account aliases to the posting's account name, 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.
|
|
|
|
postingApplyAliases :: [AccountAlias] -> Posting -> Either RegexError Posting
|
2020-11-24 20:17:01 +03:00
|
|
|
postingApplyAliases aliases p@Posting{paccount} =
|
|
|
|
case accountNameApplyAliases aliases paccount of
|
2020-11-26 07:59:07 +03:00
|
|
|
Right a -> Right p{paccount=a}
|
|
|
|
Left e -> Left err
|
2020-11-24 20:17:01 +03:00
|
|
|
where
|
2022-08-23 13:58:31 +03:00
|
|
|
err = "problem while applying account aliases:\n" ++ pshow aliases
|
2020-11-26 07:59:07 +03:00
|
|
|
++ "\n to account name: "++T.unpack paccount++"\n "++e
|
2020-11-24 20:17:01 +03:00
|
|
|
|
2022-01-29 08:56:49 +03:00
|
|
|
-- | Add tags to a posting, discarding any for which the posting already has a value.
|
|
|
|
postingAddTags :: Posting -> [Tag] -> Posting
|
|
|
|
postingAddTags p@Posting{ptags} tags = p{ptags=ptags `union` tags}
|
|
|
|
|
2019-09-05 23:41:36 +03:00
|
|
|
-- | Apply a specified valuation to this posting's amount, using the
|
2021-01-26 01:26:25 +03:00
|
|
|
-- provided price oracle, commodity styles, and reference dates.
|
|
|
|
-- See amountApplyValuation.
|
2020-12-30 08:04:08 +03:00
|
|
|
postingApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Day -> ValuationType -> Posting -> Posting
|
|
|
|
postingApplyValuation priceoracle styles periodlast today v p =
|
|
|
|
postingTransformAmount (mixedAmountApplyValuation priceoracle styles periodlast today (postingDate p) v) p
|
2019-05-28 21:42:32 +03:00
|
|
|
|
2023-08-28 08:04:37 +03:00
|
|
|
-- | Maybe convert this 'Posting's amount to cost.
|
|
|
|
postingToCost :: ConversionOp -> Posting -> Maybe Posting
|
|
|
|
postingToCost NoConversionOp p = Just p
|
|
|
|
postingToCost ToCost p
|
2023-12-08 03:39:52 +03:00
|
|
|
-- If this is a conversion posting with a matched transaction price posting, ignore it
|
|
|
|
| "_conversion-matched" `elem` map fst (ptags p) && nocosts = Nothing
|
|
|
|
| otherwise = Just $ postingTransformAmount mixedAmountCost p
|
2022-02-09 04:53:56 +03:00
|
|
|
where
|
2024-01-23 20:50:11 +03:00
|
|
|
nocosts = (not . any (isJust . acost) . amountsRaw) $ pamount p
|
2022-01-15 16:25:16 +03:00
|
|
|
|
2024-01-26 03:52:24 +03:00
|
|
|
-- | Generate inferred equity postings from a 'Posting''s costs.
|
|
|
|
-- Make sure not to duplicate them when matching ones exist already.
|
2023-04-30 07:25:11 +03:00
|
|
|
postingAddInferredEquityPostings :: Bool -> Text -> Posting -> [Posting]
|
|
|
|
postingAddInferredEquityPostings verbosetags equityAcct p
|
2022-02-09 04:53:56 +03:00
|
|
|
| "_price-matched" `elem` map fst (ptags p) = [p]
|
2024-01-26 03:52:24 +03:00
|
|
|
| otherwise = taggedPosting : concatMap conversionPostings costs
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
where
|
2024-01-26 03:52:24 +03:00
|
|
|
costs = filter (isJust . acost) . amountsRaw $ pamount p
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
taggedPosting
|
2024-01-26 03:52:24 +03:00
|
|
|
| null costs = p
|
|
|
|
| otherwise = p{ ptags = ("_price-matched","") : ptags p }
|
2024-01-23 20:50:11 +03:00
|
|
|
conversionPostings amt = case acost amt of
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
Nothing -> []
|
|
|
|
Just _ -> [ cp{ paccount = accountPrefix <> amtCommodity
|
2024-01-23 20:16:35 +03:00
|
|
|
, pamount = mixedAmount . negate $ amountStripCost amt
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
}
|
|
|
|
, cp{ paccount = accountPrefix <> costCommodity
|
2022-01-15 16:25:16 +03:00
|
|
|
, pamount = mixedAmount cost
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
}
|
|
|
|
]
|
|
|
|
where
|
|
|
|
cost = amountCost amt
|
|
|
|
amtCommodity = commodity amt
|
|
|
|
costCommodity = commodity cost
|
2023-04-30 07:25:11 +03:00
|
|
|
cp = p{ pcomment = pcomment p & (if verbosetags then (`commentAddTag` ("generated-posting","conversion")) else id)
|
|
|
|
, ptags =
|
|
|
|
("_conversion-matched","") : -- implementation-specific internal tag, not for users
|
|
|
|
("_generated-posting","conversion") :
|
|
|
|
(if verbosetags then [("generated-posting", "conversion")] else [])
|
imp: cost: Generate totally balanced conversion postings for amounts with costs.
Introduce --infer-equity option which will generate conversion postings.
--cost will override --infer-equity.
This means there will no longer be unbalanced transactions, but will be
offsetting conversion postings to balance things out. For example.
2000-01-01
a 1 AAA @@ 2 BBB
b -2 BBB
When converting to cost, this is treated the same as before.
When used with --infer-equity, this is now treated as:
2000-01-01
a 1 AAA
equity:conversion:AAA-BBB:AAA -1 AAA
equity:conversion:AAA-BBB:BBB 2 BBB
b -2 BBB
There is a new account type, Conversion/V, which is a subtype of Equity/E.
The first account declared with this type, if any, is used as the base account
for inferred equity postings in conversion transactions, overriding the default
"equity:conversion".
API changes:
Costing has been changed to ConversionOp with three options:
NoConversionOp, ToCost, and InferEquity.
The first correspond to the previous NoCost and Cost options, while the
third corresponds to the --infer-equity flag. This converts transactions with costs
(one or more transaction prices) to transactions with equity:conversion postings.
It is in ConversionOp because converting to cost with -B/--cost and inferring conversion
equity postings with --infer-equity are mutually exclusive.
Correspondingly, the cost_ record of ReportOpts has been changed to
conversionop_.
This also removes show_costs_ option in ReportOpts, as its functionality
has been replaced by the richer cost_ option.
2021-05-04 04:15:55 +03:00
|
|
|
, pbalanceassertion = Nothing
|
|
|
|
, poriginal = Nothing
|
|
|
|
}
|
|
|
|
accountPrefix = mconcat [ equityAcct, ":", T.intercalate "-" $ sort [amtCommodity, costCommodity], ":"]
|
|
|
|
-- Take the commodity of an amount and collapse consecutive spaces to a single space
|
|
|
|
commodity = T.unwords . filter (not . T.null) . T.words . acommodity
|
|
|
|
|
2022-01-27 05:49:45 +03:00
|
|
|
-- | Make a market price equivalent to this posting's amount's unit
|
|
|
|
-- price, if any.
|
|
|
|
postingPriceDirectivesFromCost :: Posting -> [PriceDirective]
|
|
|
|
postingPriceDirectivesFromCost p@Posting{pamount} =
|
|
|
|
mapMaybe (amountPriceDirectiveFromCost $ postingDate p) $ amountsRaw pamount
|
|
|
|
|
2019-05-28 21:42:32 +03:00
|
|
|
-- | Apply a transform function to this posting's amount.
|
|
|
|
postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting
|
|
|
|
postingTransformAmount f p@Posting{pamount=a} = p{pamount=f a}
|
2019-05-23 22:16:20 +03:00
|
|
|
|
2019-07-17 20:38:14 +03:00
|
|
|
-- | Join two parts of a comment, eg a tag and another tag, or a tag
|
|
|
|
-- and a non-tag, on a single line. Interpolates a comma and space
|
|
|
|
-- unless one of the parts is empty.
|
|
|
|
commentJoin :: Text -> Text -> Text
|
|
|
|
commentJoin c1 c2
|
|
|
|
| T.null c1 = c2
|
|
|
|
| T.null c2 = c1
|
|
|
|
| otherwise = c1 <> ", " <> c2
|
|
|
|
|
|
|
|
-- | Add a tag to a comment, comma-separated from any prior content.
|
2019-10-02 09:30:31 +03:00
|
|
|
-- A space is inserted following the colon, before the value.
|
2019-07-17 20:38:14 +03:00
|
|
|
commentAddTag :: Text -> Tag -> Text
|
|
|
|
commentAddTag c (t,v)
|
|
|
|
| T.null c' = tag
|
|
|
|
| otherwise = c' `commentJoin` tag
|
|
|
|
where
|
2020-07-16 13:51:48 +03:00
|
|
|
c' = T.stripEnd c
|
2019-10-02 09:30:31 +03:00
|
|
|
tag = t <> ": " <> v
|
2019-07-17 20:38:14 +03:00
|
|
|
|
2023-11-24 01:29:08 +03:00
|
|
|
-- | Like commentAddTag, but omits the space after the colon.
|
|
|
|
commentAddTagUnspaced :: Text -> Tag -> Text
|
|
|
|
commentAddTagUnspaced c (t,v)
|
|
|
|
| T.null c' = tag
|
|
|
|
| otherwise = c' `commentJoin` tag
|
|
|
|
where
|
|
|
|
c' = T.stripEnd c
|
|
|
|
tag = t <> ":" <> v
|
|
|
|
|
2019-07-17 20:38:14 +03:00
|
|
|
-- | Add a tag on its own line to a comment, preserving any prior content.
|
2019-10-02 09:30:31 +03:00
|
|
|
-- A space is inserted following the colon, before the value.
|
2019-07-17 20:38:14 +03:00
|
|
|
commentAddTagNextLine :: Text -> Tag -> Text
|
|
|
|
commentAddTagNextLine cmt (t,v) =
|
2022-08-23 13:58:31 +03:00
|
|
|
cmt <> (if "\n" `T.isSuffixOf` cmt then "" else "\n") <> t <> ": " <> v
|
2019-07-17 20:38:14 +03:00
|
|
|
|
2011-08-05 04:05:39 +04:00
|
|
|
|
2018-09-04 20:26:22 +03:00
|
|
|
-- tests
|
2011-08-03 03:29:13 +04:00
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
tests_Posting = testGroup "Posting" [
|
2018-09-04 20:26:22 +03:00
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
testCase "accountNamePostingType" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
accountNamePostingType "a" @?= RegularPosting
|
|
|
|
accountNamePostingType "(a)" @?= VirtualPosting
|
|
|
|
accountNamePostingType "[a]" @?= BalancedVirtualPosting
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "accountNameWithoutPostingType" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
accountNameWithoutPostingType "(a)" @?= "a"
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "accountNameWithPostingType" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
accountNameWithPostingType VirtualPosting "[a]" @?= "(a)"
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "joinAccountNames" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
"a" `joinAccountNames` "b:c" @?= "a:b:c"
|
|
|
|
"a" `joinAccountNames` "(b:c)" @?= "(a:b:c)"
|
|
|
|
"[a]" `joinAccountNames` "(b:c)" @?= "[a:b:c]"
|
|
|
|
"" `joinAccountNames` "a" @?= "a"
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "concatAccountNames" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
concatAccountNames [] @?= ""
|
|
|
|
concatAccountNames ["a","(b)","[c:d]"] @?= "(a:b:c:d)"
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "commentAddTag" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
commentAddTag "" ("a","") @?= "a: "
|
|
|
|
commentAddTag "[1/2]" ("a","") @?= "[1/2], a: "
|
|
|
|
|
2021-08-30 08:23:23 +03:00
|
|
|
,testCase "commentAddTagNextLine" $ do
|
2019-11-27 23:46:29 +03:00
|
|
|
commentAddTagNextLine "" ("a","") @?= "\na: "
|
|
|
|
commentAddTagNextLine "[1/2]" ("a","") @?= "[1/2]\na: "
|
|
|
|
|
2010-12-27 23:26:22 +03:00
|
|
|
]
|
2014-09-11 00:07:53 +04:00
|
|
|
|