@@ -23,8 +23,9 @@ module Hledger.Data.Journal (
23
23
addTransaction ,
24
24
journalBalanceTransactions ,
25
25
journalInferMarketPricesFromTransactions ,
26
+ journalInferCommodityStyles ,
26
27
journalApplyCommodityStyles ,
27
- commodityStylesFromAmounts ,
28
+ journalInferAndApplyCommodityStyles ,
28
29
journalCommodityStyles ,
29
30
journalToCost ,
30
31
journalReverse ,
@@ -78,7 +79,6 @@ module Hledger.Data.Journal (
78
79
journalEquityAccountQuery ,
79
80
journalCashAccountQuery ,
80
81
-- * Misc
81
- canonicalStyleFrom ,
82
82
nulljournal ,
83
83
journalCheckBalanceAssertions ,
84
84
journalNumberAndTieTransactions ,
@@ -87,7 +87,7 @@ module Hledger.Data.Journal (
87
87
journalApplyAliases ,
88
88
-- * Tests
89
89
samplejournal ,
90
- tests_Journal ,
90
+ tests_Journal
91
91
)
92
92
where
93
93
@@ -101,7 +101,7 @@ import Data.Function ((&))
101
101
import qualified Data.HashTable.Class as H (toList )
102
102
import qualified Data.HashTable.ST.Cuckoo as H
103
103
import Data.List (find , sortOn )
104
- import Data.List.Extra (groupSort , nubSort )
104
+ import Data.List.Extra (nubSort )
105
105
import qualified Data.Map as M
106
106
import Data.Maybe (catMaybes , fromJust , fromMaybe , isJust , mapMaybe )
107
107
#if !(MIN_VERSION_base(4,11,0))
@@ -662,8 +662,7 @@ type Balancing s = ReaderT (BalancingState s) (ExceptT String (ST s))
662
662
-- | The state used while balancing a sequence of transactions.
663
663
data BalancingState s = BalancingState {
664
664
-- read only
665
- bsStyles :: Maybe (M. Map CommoditySymbol AmountStyle ) -- ^ commodity display styles
666
- ,bsUnassignable :: S. Set AccountName -- ^ accounts in which balance assignments may not be used
665
+ bsUnassignable :: S. Set AccountName -- ^ accounts in which balance assignments may not be used
667
666
,bsAssrt :: Bool -- ^ whether to check balance assertions
668
667
-- mutable
669
668
,bsBalances :: H. HashTable s AccountName MixedAmount -- ^ running account balances, initially empty
@@ -722,18 +721,18 @@ updateTransactionB t = withRunningBalance $ \BalancingState{bsTransactions} ->
722
721
-- and (optional) all balance assertions pass. Or return an error message
723
722
-- (just the first error encountered).
724
723
--
725
- -- Assumes journalInferCommodityStyles has been called, since those
726
- -- affect transaction balancing.
724
+ -- Assumes the journal amounts' display styles still have the original number
725
+ -- of decimal places that was parsed (ie, display styles have not yet been normalised),
726
+ -- since this affects transaction balancing.
727
727
--
728
728
-- This does multiple things at once because amount inferring, balance
729
729
-- assignments, balance assertions and posting dates are interdependent.
730
+ --
730
731
journalBalanceTransactions :: Bool -> Journal -> Either String Journal
731
732
journalBalanceTransactions assrt j' =
732
733
let
733
734
-- ensure transactions are numbered, so we can store them by number
734
735
j@ Journal {jtxns= ts} = journalNumberTransactions j'
735
- -- display precisions used in balanced checking
736
- styles = Just $ journalCommodityStyles j
737
736
-- balance assignments will not be allowed on these
738
737
txnmodifieraccts = S. fromList $ map paccount $ concatMap tmpostingrules $ jtxnmodifiers j
739
738
in
@@ -750,7 +749,7 @@ journalBalanceTransactions assrt j' =
750
749
-- and leaving the others for later. The balanced ones are split into their postings.
751
750
-- The postings and not-yet-balanced transactions remain in the same relative order.
752
751
psandts :: [Either Posting Transaction ] <- fmap concat $ forM ts $ \ case
753
- t | null $ assignmentPostings t -> case balanceTransaction styles t of
752
+ t | null $ assignmentPostings t -> case balanceTransaction t of
754
753
Left e -> throwError e
755
754
Right t' -> do
756
755
lift $ writeArray balancedtxns (tindex t') t'
@@ -760,7 +759,7 @@ journalBalanceTransactions assrt j' =
760
759
-- 2. Sort these items by date, preserving the order of same-day items,
761
760
-- and step through them while keeping running account balances,
762
761
runningbals <- lift $ H. newSized (length $ journalAccountNamesUsed j)
763
- flip runReaderT (BalancingState styles txnmodifieraccts assrt runningbals balancedtxns) $ do
762
+ flip runReaderT (BalancingState txnmodifieraccts assrt runningbals balancedtxns) $ do
764
763
-- performing balance assignments in, and balancing, the remaining transactions,
765
764
-- and checking balance assertions as each posting is processed.
766
765
void $ mapM' balanceTransactionAndCheckAssertionsB $ sortOn (either postingDate tdate) psandts
@@ -788,8 +787,7 @@ balanceTransactionAndCheckAssertionsB (Right t@Transaction{tpostings=ps}) = do
788
787
-- update the account's running balance and check the balance assertion if any
789
788
ps' <- forM ps $ \ p -> pure (removePrices p) >>= addOrAssignAmountAndCheckAssertionB
790
789
-- infer any remaining missing amounts, and make sure the transaction is now fully balanced
791
- styles <- R. reader bsStyles
792
- case balanceTransactionHelper styles t{tpostings= ps'} of
790
+ case balanceTransactionHelper t{tpostings= ps'} of
793
791
Left err -> throwError err
794
792
Right (t', inferredacctsandamts) -> do
795
793
-- for each amount just inferred, update the running balance
@@ -965,25 +963,66 @@ checkBalanceAssignmentUnassignableAccountB p = do
965
963
966
964
--
967
965
966
+ -- | Get an ordered list of amounts in this journal which can
967
+ -- influence canonical amount display styles. Those are, in
968
+ -- the following order:
969
+ --
970
+ -- * amounts in market price (P) directives (in parse order)
971
+ -- * posting amounts in transactions (in parse order)
972
+ -- * the amount in the final default commodity (D) directive
973
+ --
974
+ -- Transaction price amounts (posting amounts' aprice field) are not included.
975
+ --
976
+ journalStyleInfluencingAmounts :: Journal -> [Amount ]
977
+ journalStyleInfluencingAmounts j =
978
+ dbg7 " journalStyleInfluencingAmounts" $
979
+ catMaybes $ concat [
980
+ [mdefaultcommodityamt]
981
+ ,map (Just . pdamount) $ jpricedirectives j
982
+ ,map Just $ concatMap amounts $ map pamount $ journalPostings j
983
+ ]
984
+ where
985
+ -- D's amount style isn't actually stored as an amount, make it into one
986
+ mdefaultcommodityamt =
987
+ case jparsedefaultcommodity j of
988
+ Just (symbol,style) -> Just nullamt{acommodity= symbol,astyle= style}
989
+ Nothing -> Nothing
990
+
991
+ -- | Infer commodity display styles for each commodity (see commodityStylesFromAmounts)
992
+ -- based on the amounts in this journal (see journalStyleInfluencingAmounts),
993
+ -- and save those inferred styles in the journal.
994
+ -- Can return an error message eg if inconsistent number formats are found.
995
+ journalInferCommodityStyles :: Journal -> Either String Journal
996
+ journalInferCommodityStyles j =
997
+ case commodityStylesFromAmounts $ journalStyleInfluencingAmounts j of
998
+ Left e -> Left e
999
+ Right cs -> Right j{jinferredcommodities = dbg7 " journalInferCommodityStyles" cs}
1000
+
1001
+ -- | Apply the given commodity display styles to the posting amounts in this journal.
1002
+ journalApplyCommodityStyles :: M. Map CommoditySymbol AmountStyle -> Journal -> Journal
1003
+ journalApplyCommodityStyles styles j@ Journal {jtxns= ts, jpricedirectives= pds} =
1004
+ j {jtxns= map fixtransaction ts
1005
+ ,jpricedirectives= map fixpricedirective pds
1006
+ }
1007
+ where
1008
+ fixtransaction t@ Transaction {tpostings= ps} = t{tpostings= map fixposting ps}
1009
+ fixposting p = p{pamount= styleMixedAmount styles $ pamount p
1010
+ ,pbalanceassertion= fixbalanceassertion <$> pbalanceassertion p}
1011
+ -- balance assertion/assignment amounts, and price amounts, are always displayed
1012
+ -- (eg by print) at full precision
1013
+ fixbalanceassertion ba = ba{baamount= styleAmountExceptPrecision styles $ baamount ba}
1014
+ fixpricedirective pd@ PriceDirective {pdamount= a} = pd{pdamount= styleAmountExceptPrecision styles a}
1015
+
968
1016
-- | Choose and apply a consistent display style to the posting
969
1017
-- amounts in each commodity (see journalCommodityStyles).
970
1018
-- Can return an error message eg if inconsistent number formats are found.
971
- journalApplyCommodityStyles :: Journal -> Either String Journal
972
- journalApplyCommodityStyles j @ Journal {jtxns = ts, jpricedirectives = pds} =
1019
+ journalInferAndApplyCommodityStyles :: Journal -> Either String Journal
1020
+ journalInferAndApplyCommodityStyles j =
973
1021
case journalInferCommodityStyles j of
974
1022
Left e -> Left e
975
- Right j' -> Right j' '
1023
+ Right j' -> Right $ journalApplyCommodityStyles allstyles j '
976
1024
where
977
- styles = journalCommodityStyles j'
978
- j'' = j'{jtxns= map fixtransaction ts
979
- ,jpricedirectives= map fixpricedirective pds
980
- }
981
- fixtransaction t@ Transaction {tpostings= ps} = t{tpostings= map fixposting ps}
982
- fixposting p = p{pamount= styleMixedAmount styles $ pamount p
983
- ,pbalanceassertion= fixbalanceassertion <$> pbalanceassertion p}
984
- -- balance assertion amounts are always displayed (by print) at full precision, per docs
985
- fixbalanceassertion ba = ba{baamount= styleAmountExceptPrecision styles $ baamount ba}
986
- fixpricedirective pd@ PriceDirective {pdamount= a} = pd{pdamount= styleAmountExceptPrecision styles a}
1025
+ allstyles = journalCommodityStyles j'
987
1026
988
1027
-- | Get the canonical amount styles for this journal, whether (in order of precedence):
989
1028
-- set globally in InputOpts,
@@ -1002,18 +1041,6 @@ journalCommodityStyles j =
1002
1041
defaultcommoditystyle = M. fromList $ catMaybes [jparsedefaultcommodity j]
1003
1042
inferredstyles = jinferredcommodities j
1004
1043
1005
- -- | Collect and save inferred amount styles for each commodity based on
1006
- -- the posting amounts in that commodity (excluding price amounts), ie:
1007
- -- "the format of the first amount, adjusted to the highest precision of all amounts".
1008
- -- Can return an error message eg if inconsistent number formats are found.
1009
- journalInferCommodityStyles :: Journal -> Either String Journal
1010
- journalInferCommodityStyles j =
1011
- case
1012
- commodityStylesFromAmounts $ journalStyleInfluencingAmounts j
1013
- of
1014
- Left e -> Left e
1015
- Right cs -> Right j{jinferredcommodities = dbg7 " journalInferCommodityStyles" cs}
1016
-
1017
1044
-- -- | Apply this journal's historical price records to unpriced amounts where possible.
1018
1045
-- journalApplyPriceDirectives :: Journal -> Journal
1019
1046
-- journalApplyPriceDirectives j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts}
0 commit comments