mirror of
https://github.com/simonmichael/hledger.git
synced 2024-09-19 18:29:36 +03:00
refactor: lib: hlint cleanups
This commit is contained in:
parent
03877057fb
commit
bc7a1476ed
@ -47,10 +47,11 @@
|
|||||||
# - ignore: {name: Use let}
|
# - ignore: {name: Use let}
|
||||||
# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
|
# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
|
||||||
|
|
||||||
- ignore: {name: Use camelCase}
|
- ignore: {name: Reduce duplication}
|
||||||
- ignore: {name: Redundant $}
|
- ignore: {name: Redundant $}
|
||||||
- ignore: {name: Redundant bracket}
|
- ignore: {name: Redundant bracket}
|
||||||
- ignore: {name: Redundant do}
|
- ignore: {name: Redundant do}
|
||||||
|
- ignore: {name: Use camelCase}
|
||||||
|
|
||||||
|
|
||||||
# Define some custom infix operators
|
# Define some custom infix operators
|
||||||
|
@ -242,7 +242,7 @@ sortAccountTreeByDeclaration :: Account -> Account
|
|||||||
sortAccountTreeByDeclaration a
|
sortAccountTreeByDeclaration a
|
||||||
| null $ asubs a = a
|
| null $ asubs a = a
|
||||||
| otherwise = a{asubs=
|
| otherwise = a{asubs=
|
||||||
sortBy (comparing accountDeclarationOrderAndName) $
|
sortOn accountDeclarationOrderAndName $
|
||||||
map sortAccountTreeByDeclaration $ asubs a
|
map sortAccountTreeByDeclaration $ asubs a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,6 @@ import Data.List
|
|||||||
import Data.Map (findWithDefault)
|
import Data.Map (findWithDefault)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Time.Calendar (Day)
|
import Data.Time.Calendar (Day)
|
||||||
import Data.Ord (comparing)
|
|
||||||
-- import Data.Text (Text)
|
-- import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Safe (maximumDef)
|
import Safe (maximumDef)
|
||||||
@ -469,7 +468,7 @@ commodityValue j valuationdate c
|
|||||||
where
|
where
|
||||||
dbg = dbg8 ("using market price for "++T.unpack c)
|
dbg = dbg8 ("using market price for "++T.unpack c)
|
||||||
applicableprices =
|
applicableprices =
|
||||||
[p | p <- sortBy (comparing mpdate) $ jmarketprices j
|
[p | p <- sortOn mpdate $ jmarketprices j
|
||||||
, mpcommodity p == c
|
, mpcommodity p == c
|
||||||
, mpdate p <= valuationdate
|
, mpdate p <= valuationdate
|
||||||
]
|
]
|
||||||
|
@ -26,7 +26,7 @@ import Hledger.Utils
|
|||||||
|
|
||||||
|
|
||||||
-- characters that may not be used in a non-quoted commodity symbol
|
-- characters that may not be used in a non-quoted commodity symbol
|
||||||
nonsimplecommoditychars = "0123456789-+.@*;\n \"{}=" :: [Char]
|
nonsimplecommoditychars = "0123456789-+.@*;\n \"{}=" :: String
|
||||||
|
|
||||||
isNonsimpleCommodityChar :: Char -> Bool
|
isNonsimpleCommodityChar :: Char -> Bool
|
||||||
isNonsimpleCommodityChar c = isDigit c || c `textElem` otherChars
|
isNonsimpleCommodityChar c = isDigit c || c `textElem` otherChars
|
||||||
|
@ -126,21 +126,15 @@ showDateSpanMonthAbbrev = showPeriodMonthAbbrev . dateSpanAsPeriod
|
|||||||
|
|
||||||
-- | Get the current local date.
|
-- | Get the current local date.
|
||||||
getCurrentDay :: IO Day
|
getCurrentDay :: IO Day
|
||||||
getCurrentDay = do
|
getCurrentDay = localDay . zonedTimeToLocalTime <$> getZonedTime
|
||||||
t <- getZonedTime
|
|
||||||
return $ localDay (zonedTimeToLocalTime t)
|
|
||||||
|
|
||||||
-- | Get the current local month number.
|
-- | Get the current local month number.
|
||||||
getCurrentMonth :: IO Int
|
getCurrentMonth :: IO Int
|
||||||
getCurrentMonth = do
|
getCurrentMonth = second3 . toGregorian <$> getCurrentDay
|
||||||
(_,m,_) <- toGregorian `fmap` getCurrentDay
|
|
||||||
return m
|
|
||||||
|
|
||||||
-- | Get the current local year.
|
-- | Get the current local year.
|
||||||
getCurrentYear :: IO Integer
|
getCurrentYear :: IO Integer
|
||||||
getCurrentYear = do
|
getCurrentYear = first3 . toGregorian <$> getCurrentDay
|
||||||
(y,_,_) <- toGregorian `fmap` getCurrentDay
|
|
||||||
return y
|
|
||||||
|
|
||||||
elapsedSeconds :: Fractional a => UTCTime -> UTCTime -> a
|
elapsedSeconds :: Fractional a => UTCTime -> UTCTime -> a
|
||||||
elapsedSeconds t1 = realToFrac . diffUTCTime t1
|
elapsedSeconds t1 = realToFrac . diffUTCTime t1
|
||||||
@ -380,14 +374,13 @@ spanFromSmartDate refdate sdate = DateSpan (Just b) (Just e)
|
|||||||
-- | Convert a smart date string to an explicit yyyy\/mm\/dd string using
|
-- | Convert a smart date string to an explicit yyyy\/mm\/dd string using
|
||||||
-- the provided reference date, or raise an error.
|
-- the provided reference date, or raise an error.
|
||||||
fixSmartDateStr :: Day -> Text -> String
|
fixSmartDateStr :: Day -> Text -> String
|
||||||
fixSmartDateStr d s = either
|
fixSmartDateStr d s =
|
||||||
(\e->error' $ printf "could not parse date %s %s" (show s) (show e))
|
either (error' . printf "could not parse date %s %s" (show s) . show) id $
|
||||||
id
|
(fixSmartDateStrEither d s :: Either (ParseErrorBundle Text CustomErr) String)
|
||||||
$ (fixSmartDateStrEither d s :: Either (ParseErrorBundle Text CustomErr) String)
|
|
||||||
|
|
||||||
-- | A safe version of fixSmartDateStr.
|
-- | A safe version of fixSmartDateStr.
|
||||||
fixSmartDateStrEither :: Day -> Text -> Either (ParseErrorBundle Text CustomErr) String
|
fixSmartDateStrEither :: Day -> Text -> Either (ParseErrorBundle Text CustomErr) String
|
||||||
fixSmartDateStrEither d = either Left (Right . showDate) . fixSmartDateStrEither' d
|
fixSmartDateStrEither d = fmap showDate . fixSmartDateStrEither' d
|
||||||
|
|
||||||
fixSmartDateStrEither'
|
fixSmartDateStrEither'
|
||||||
:: Day -> Text -> Either (ParseErrorBundle Text CustomErr) Day
|
:: Day -> Text -> Either (ParseErrorBundle Text CustomErr) Day
|
||||||
@ -469,34 +462,34 @@ fixSmartDateStrEither' d s = case parsewith smartdateonly (T.toLower s) of
|
|||||||
-- "2009/01/01"
|
-- "2009/01/01"
|
||||||
--
|
--
|
||||||
fixSmartDate :: Day -> SmartDate -> Day
|
fixSmartDate :: Day -> SmartDate -> Day
|
||||||
fixSmartDate refdate sdate = fix sdate
|
fixSmartDate refdate = fix
|
||||||
where
|
where
|
||||||
fix :: SmartDate -> Day
|
fix :: SmartDate -> Day
|
||||||
fix ("","","today") = fromGregorian ry rm rd
|
fix ("", "", "today") = fromGregorian ry rm rd
|
||||||
fix ("","this","day") = fromGregorian ry rm rd
|
fix ("", "this", "day") = fromGregorian ry rm rd
|
||||||
fix ("","","yesterday") = prevday refdate
|
fix ("", "", "yesterday") = prevday refdate
|
||||||
fix ("","last","day") = prevday refdate
|
fix ("", "last", "day") = prevday refdate
|
||||||
fix ("","","tomorrow") = nextday refdate
|
fix ("", "", "tomorrow") = nextday refdate
|
||||||
fix ("","next","day") = nextday refdate
|
fix ("", "next", "day") = nextday refdate
|
||||||
fix ("","last","week") = prevweek refdate
|
fix ("", "last", "week") = prevweek refdate
|
||||||
fix ("","this","week") = thisweek refdate
|
fix ("", "this", "week") = thisweek refdate
|
||||||
fix ("","next","week") = nextweek refdate
|
fix ("", "next", "week") = nextweek refdate
|
||||||
fix ("","last","month") = prevmonth refdate
|
fix ("", "last", "month") = prevmonth refdate
|
||||||
fix ("","this","month") = thismonth refdate
|
fix ("", "this", "month") = thismonth refdate
|
||||||
fix ("","next","month") = nextmonth refdate
|
fix ("", "next", "month") = nextmonth refdate
|
||||||
fix ("","last","quarter") = prevquarter refdate
|
fix ("", "last", "quarter") = prevquarter refdate
|
||||||
fix ("","this","quarter") = thisquarter refdate
|
fix ("", "this", "quarter") = thisquarter refdate
|
||||||
fix ("","next","quarter") = nextquarter refdate
|
fix ("", "next", "quarter") = nextquarter refdate
|
||||||
fix ("","last","year") = prevyear refdate
|
fix ("", "last", "year") = prevyear refdate
|
||||||
fix ("","this","year") = thisyear refdate
|
fix ("", "this", "year") = thisyear refdate
|
||||||
fix ("","next","year") = nextyear refdate
|
fix ("", "next", "year") = nextyear refdate
|
||||||
fix ("","",d) = fromGregorian ry rm (read d)
|
fix ("", "", d) = fromGregorian ry rm (read d)
|
||||||
fix ("",m,"") = fromGregorian ry (read m) 1
|
fix ("", m, "") = fromGregorian ry (read m) 1
|
||||||
fix ("",m,d) = fromGregorian ry (read m) (read d)
|
fix ("", m, d) = fromGregorian ry (read m) (read d)
|
||||||
fix (y,"","") = fromGregorian (read y) 1 1
|
fix (y, "", "") = fromGregorian (read y) 1 1
|
||||||
fix (y,m,"") = fromGregorian (read y) (read m) 1
|
fix (y, m, "") = fromGregorian (read y) (read m) 1
|
||||||
fix (y,m,d) = fromGregorian (read y) (read m) (read d)
|
fix (y, m, d) = fromGregorian (read y) (read m) (read d)
|
||||||
(ry,rm,rd) = toGregorian refdate
|
(ry, rm, rd) = toGregorian refdate
|
||||||
|
|
||||||
prevday :: Day -> Day
|
prevday :: Day -> Day
|
||||||
prevday = addDays (-1)
|
prevday = addDays (-1)
|
||||||
@ -764,7 +757,7 @@ smartdateonly = do
|
|||||||
eof
|
eof
|
||||||
return d
|
return d
|
||||||
|
|
||||||
datesepchars :: [Char]
|
datesepchars :: String
|
||||||
datesepchars = "/-."
|
datesepchars = "/-."
|
||||||
|
|
||||||
datesepchar :: TextParser m Char
|
datesepchar :: TextParser m Char
|
||||||
@ -980,8 +973,7 @@ reportingintervalp = choice' [
|
|||||||
return $ DayOfWeek n,
|
return $ DayOfWeek n,
|
||||||
do string' "every"
|
do string' "every"
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
n <- weekday
|
DayOfWeek <$> weekday,
|
||||||
return $ DayOfWeek n,
|
|
||||||
do string' "every"
|
do string' "every"
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
n <- nth
|
n <- nth
|
||||||
@ -1034,7 +1026,7 @@ reportingintervalp = choice' [
|
|||||||
return $ intcons 1,
|
return $ intcons 1,
|
||||||
do string' "every"
|
do string' "every"
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
n <- fmap read $ some digitChar
|
n <- read <$> some digitChar
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
string' plural'
|
string' plural'
|
||||||
return $ intcons n
|
return $ intcons n
|
||||||
@ -1061,8 +1053,7 @@ doubledatespanp rdate = do
|
|||||||
b <- smartdate
|
b <- smartdate
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
optional (choice [string' "to", string' "-"] >> skipMany spacenonewline)
|
optional (choice [string' "to", string' "-"] >> skipMany spacenonewline)
|
||||||
e <- smartdate
|
DateSpan (Just $ fixSmartDate rdate b) . Just . fixSmartDate rdate <$> smartdate
|
||||||
return $ DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e)
|
|
||||||
|
|
||||||
fromdatespanp :: Day -> TextParser m DateSpan
|
fromdatespanp :: Day -> TextParser m DateSpan
|
||||||
fromdatespanp rdate = do
|
fromdatespanp rdate = do
|
||||||
@ -1081,14 +1072,12 @@ fromdatespanp rdate = do
|
|||||||
todatespanp :: Day -> TextParser m DateSpan
|
todatespanp :: Day -> TextParser m DateSpan
|
||||||
todatespanp rdate = do
|
todatespanp rdate = do
|
||||||
choice [string' "to", string' "-"] >> skipMany spacenonewline
|
choice [string' "to", string' "-"] >> skipMany spacenonewline
|
||||||
e <- smartdate
|
DateSpan Nothing . Just . fixSmartDate rdate <$> smartdate
|
||||||
return $ DateSpan Nothing (Just $ fixSmartDate rdate e)
|
|
||||||
|
|
||||||
justdatespanp :: Day -> TextParser m DateSpan
|
justdatespanp :: Day -> TextParser m DateSpan
|
||||||
justdatespanp rdate = do
|
justdatespanp rdate = do
|
||||||
optional (string' "in" >> skipMany spacenonewline)
|
optional (string' "in" >> skipMany spacenonewline)
|
||||||
d <- smartdate
|
spanFromSmartDate rdate <$> smartdate
|
||||||
return $ spanFromSmartDate rdate d
|
|
||||||
|
|
||||||
-- | Make a datespan from two valid date strings parseable by parsedate
|
-- | Make a datespan from two valid date strings parseable by parsedate
|
||||||
-- (or raise an error). Eg: mkdatespan \"2011/1/1\" \"2011/12/31\".
|
-- (or raise an error). Eg: mkdatespan \"2011/1/1\" \"2011/12/31\".
|
||||||
|
@ -90,7 +90,6 @@ import Data.Maybe
|
|||||||
#if !(MIN_VERSION_base(4,11,0))
|
#if !(MIN_VERSION_base(4,11,0))
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
#endif
|
#endif
|
||||||
import Data.Ord
|
|
||||||
import qualified Data.Semigroup as Sem
|
import qualified Data.Semigroup as Sem
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
@ -672,7 +671,7 @@ journalBalanceTransactionsST assrt j createStore storeIn extract =
|
|||||||
(Just $ journalCommodityStyles j)
|
(Just $ journalCommodityStyles j)
|
||||||
(getModifierAccountNames j)
|
(getModifierAccountNames j)
|
||||||
flip R.runReaderT env $ do
|
flip R.runReaderT env $ do
|
||||||
dated <- fmap snd . sortBy (comparing fst) . concat
|
dated <- fmap snd . sortOn fst . concat
|
||||||
<$> mapM' discriminateByDate (jtxns j)
|
<$> mapM' discriminateByDate (jtxns j)
|
||||||
mapM' checkInferAndRegisterAmounts dated
|
mapM' checkInferAndRegisterAmounts dated
|
||||||
lift $ extract txStore
|
lift $ extract txStore
|
||||||
@ -714,33 +713,33 @@ discriminateByDate :: Transaction
|
|||||||
-> CurrentBalancesModifier s [(Day, Either Posting Transaction)]
|
-> CurrentBalancesModifier s [(Day, Either Posting Transaction)]
|
||||||
discriminateByDate tx
|
discriminateByDate tx
|
||||||
| null (assignmentPostings tx) = do
|
| null (assignmentPostings tx) = do
|
||||||
styles <- R.reader $ eStyles
|
styles <- R.reader $ eStyles
|
||||||
balanced <- lift $ ExceptT $ return $ balanceTransaction styles tx
|
balanced <- lift $ ExceptT $ return $ balanceTransaction styles tx
|
||||||
storeTransaction balanced
|
storeTransaction balanced
|
||||||
return $
|
return $ fmap (postingDate &&& (Left . removePrices)) $ tpostings $ balanced
|
||||||
fmap (postingDate &&& (Left . removePrices)) $ tpostings $ balanced
|
| otherwise = do
|
||||||
| True = do
|
when (any (isJust . pdate) $ tpostings tx) $
|
||||||
when (any (isJust . pdate) $ tpostings tx) $
|
throwError $
|
||||||
throwError $ unlines $
|
unlines $
|
||||||
["postings may not have both a custom date and a balance assignment."
|
[ "postings may not have both a custom date and a balance assignment."
|
||||||
,"Write the posting amount explicitly, or remove the posting date:\n"
|
, "Write the posting amount explicitly, or remove the posting date:\n"
|
||||||
, showTransaction tx]
|
, showTransaction tx
|
||||||
return
|
]
|
||||||
[(tdate tx, Right $ tx { tpostings = removePrices <$> tpostings tx })]
|
return [(tdate tx, Right $ tx {tpostings = removePrices <$> tpostings tx})]
|
||||||
|
|
||||||
-- | Throw an error if a posting is in the unassignable set.
|
-- | Throw an error if a posting is in the unassignable set.
|
||||||
checkUnassignablePosting :: Posting -> CurrentBalancesModifier s ()
|
checkUnassignablePosting :: Posting -> CurrentBalancesModifier s ()
|
||||||
checkUnassignablePosting p = do
|
checkUnassignablePosting p = do
|
||||||
unassignable <- R.asks eUnassignable
|
unassignable <- R.asks eUnassignable
|
||||||
if (isAssignment p && paccount p `S.member` unassignable)
|
when (isAssignment p && paccount p `S.member` unassignable) $
|
||||||
then throwError $ unlines $
|
throwError $
|
||||||
[ "cannot assign amount to account "
|
unlines $
|
||||||
, ""
|
[ "cannot assign amount to account "
|
||||||
, " " ++ (T.unpack $ paccount p)
|
, ""
|
||||||
, ""
|
, " " ++ T.unpack (paccount p)
|
||||||
, "because it is also included in transaction modifiers."
|
, ""
|
||||||
]
|
, "because it is also included in transaction modifiers."
|
||||||
else return ()
|
]
|
||||||
|
|
||||||
|
|
||||||
-- | This function takes an object describing changes to
|
-- | This function takes an object describing changes to
|
||||||
@ -789,7 +788,7 @@ checkInferAndRegisterAmounts (Right oldTx) = do
|
|||||||
Just ba | baexact ba -> do
|
Just ba | baexact ba -> do
|
||||||
diff <- setMixedBalance acc $ Mixed [baamount ba]
|
diff <- setMixedBalance acc $ Mixed [baamount ba]
|
||||||
fullPosting diff p
|
fullPosting diff p
|
||||||
Just ba | otherwise -> do
|
Just ba -> do
|
||||||
old <- liftModifier $ \Env{ eBalances = bals } -> HT.lookup bals acc
|
old <- liftModifier $ \Env{ eBalances = bals } -> HT.lookup bals acc
|
||||||
let amt = baamount ba
|
let amt = baamount ba
|
||||||
assertedcomm = acommodity amt
|
assertedcomm = acommodity amt
|
||||||
@ -884,13 +883,12 @@ commodityStylesFromAmounts amts = M.fromList commstyles
|
|||||||
-- That is: the style of the first, and the maximum precision of all.
|
-- That is: the style of the first, and the maximum precision of all.
|
||||||
canonicalStyleFrom :: [AmountStyle] -> AmountStyle
|
canonicalStyleFrom :: [AmountStyle] -> AmountStyle
|
||||||
canonicalStyleFrom [] = amountstyle
|
canonicalStyleFrom [] = amountstyle
|
||||||
canonicalStyleFrom ss@(first:_) =
|
canonicalStyleFrom ss@(first:_) = first {asprecision = prec, asdecimalpoint = mdec, asdigitgroups = mgrps}
|
||||||
first{asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
|
||||||
where
|
where
|
||||||
mgrps = maybe Nothing Just $ headMay $ catMaybes $ map asdigitgroups ss
|
mgrps = headMay $ mapMaybe asdigitgroups ss
|
||||||
-- precision is maximum of all precisions
|
-- precision is maximum of all precisions
|
||||||
prec = maximumStrict $ map asprecision ss
|
prec = maximumStrict $ map asprecision ss
|
||||||
mdec = Just $ headDef '.' $ catMaybes $ map asdecimalpoint ss
|
mdec = Just $ headDef '.' $ mapMaybe asdecimalpoint ss
|
||||||
-- precision is that of first amount with a decimal point
|
-- precision is that of first amount with a decimal point
|
||||||
-- (mdec, prec) =
|
-- (mdec, prec) =
|
||||||
-- case filter (isJust . asdecimalpoint) ss of
|
-- case filter (isJust . asdecimalpoint) ss of
|
||||||
@ -993,7 +991,7 @@ journalDateSpan secondary j
|
|||||||
latest = maximumStrict dates
|
latest = maximumStrict dates
|
||||||
dates = pdates ++ tdates
|
dates = pdates ++ tdates
|
||||||
tdates = map (if secondary then transactionDate2 else tdate) ts
|
tdates = map (if secondary then transactionDate2 else tdate) ts
|
||||||
pdates = concatMap (catMaybes . map (if secondary then (Just . postingDate2) else pdate) . tpostings) ts
|
pdates = concatMap (mapMaybe (if secondary then (Just . postingDate2) else pdate) . tpostings) ts
|
||||||
ts = jtxns j
|
ts = jtxns j
|
||||||
|
|
||||||
-- | Apply the pivot transformation to all postings in a journal,
|
-- | Apply the pivot transformation to all postings in a journal,
|
||||||
|
@ -107,12 +107,13 @@ ledgerCommodities = M.keys . jinferredcommodities . ljournal
|
|||||||
|
|
||||||
-- tests
|
-- tests
|
||||||
|
|
||||||
tests_Ledger = tests "Ledger" [
|
tests_Ledger =
|
||||||
|
tests
|
||||||
tests "ledgerFromJournal" [
|
"Ledger"
|
||||||
(length $ ledgerPostings $ ledgerFromJournal Any nulljournal) `is` 0
|
[ tests
|
||||||
,(length $ ledgerPostings $ ledgerFromJournal Any samplejournal) `is` 13
|
"ledgerFromJournal"
|
||||||
,(length $ ledgerPostings $ ledgerFromJournal (Depth 2) samplejournal) `is` 7
|
[ length (ledgerPostings $ ledgerFromJournal Any nulljournal) `is` 0
|
||||||
]
|
, length (ledgerPostings $ ledgerFromJournal Any samplejournal) `is` 13
|
||||||
|
, length (ledgerPostings $ ledgerFromJournal (Depth 2) samplejournal) `is` 7
|
||||||
]
|
]
|
||||||
|
]
|
||||||
|
@ -8,8 +8,6 @@ value of things at a given date.
|
|||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE OverloadedStrings, LambdaCase #-}
|
|
||||||
|
|
||||||
module Hledger.Data.MarketPrice
|
module Hledger.Data.MarketPrice
|
||||||
where
|
where
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE StandaloneDeriving #-}
|
|
||||||
{-|
|
{-|
|
||||||
|
|
||||||
A 'PeriodicTransaction' is a rule describing recurring transactions.
|
A 'PeriodicTransaction' is a rule describing recurring transactions.
|
||||||
|
@ -66,7 +66,6 @@ import Data.MemoUgly (memo)
|
|||||||
#if !(MIN_VERSION_base(4,11,0))
|
#if !(MIN_VERSION_base(4,11,0))
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
#endif
|
#endif
|
||||||
import Data.Ord
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time.Calendar
|
import Data.Time.Calendar
|
||||||
@ -176,7 +175,7 @@ postingDate2 p = headDef nulldate $ catMaybes dates
|
|||||||
where dates = [pdate2 p
|
where dates = [pdate2 p
|
||||||
,maybe Nothing tdate2 $ ptransaction p
|
,maybe Nothing tdate2 $ ptransaction p
|
||||||
,pdate p
|
,pdate p
|
||||||
,maybe Nothing (Just . tdate) $ ptransaction p
|
,fmap tdate (ptransaction p)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Get a posting's status. This is cleared or pending if those are
|
-- | Get a posting's status. This is cleared or pending if those are
|
||||||
@ -237,14 +236,14 @@ isEmptyPosting = isZeroMixedAmount . pamount
|
|||||||
postingsDateSpan :: [Posting] -> DateSpan
|
postingsDateSpan :: [Posting] -> DateSpan
|
||||||
postingsDateSpan [] = DateSpan Nothing Nothing
|
postingsDateSpan [] = DateSpan Nothing Nothing
|
||||||
postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps')
|
postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps')
|
||||||
where ps' = sortBy (comparing postingDate) ps
|
where ps' = sortOn postingDate ps
|
||||||
|
|
||||||
-- --date2-sensitive version, as above.
|
-- --date2-sensitive version, as above.
|
||||||
postingsDateSpan' :: WhichDate -> [Posting] -> DateSpan
|
postingsDateSpan' :: WhichDate -> [Posting] -> DateSpan
|
||||||
postingsDateSpan' _ [] = DateSpan Nothing Nothing
|
postingsDateSpan' _ [] = DateSpan Nothing Nothing
|
||||||
postingsDateSpan' wd ps = DateSpan (Just $ postingdate $ head ps') (Just $ addDays 1 $ postingdate $ last ps')
|
postingsDateSpan' wd ps = DateSpan (Just $ postingdate $ head ps') (Just $ addDays 1 $ postingdate $ last ps')
|
||||||
where
|
where
|
||||||
ps' = sortBy (comparing postingdate) ps
|
ps' = sortOn postingdate ps
|
||||||
postingdate = if wd == PrimaryDate then postingDate else postingDate2
|
postingdate = if wd == PrimaryDate then postingDate else postingDate2
|
||||||
|
|
||||||
-- AccountName stuff that depends on PostingType
|
-- AccountName stuff that depends on PostingType
|
||||||
|
@ -46,7 +46,7 @@ boolopt :: String -> RawOpts -> Bool
|
|||||||
boolopt = inRawOpts
|
boolopt = inRawOpts
|
||||||
|
|
||||||
maybestringopt :: String -> RawOpts -> Maybe String
|
maybestringopt :: String -> RawOpts -> Maybe String
|
||||||
maybestringopt name = maybe Nothing (Just . T.unpack . stripquotes . T.pack) . lookup name . reverse
|
maybestringopt name = fmap (T.unpack . stripquotes . T.pack) . lookup name . reverse
|
||||||
|
|
||||||
stringopt :: String -> RawOpts -> String
|
stringopt :: String -> RawOpts -> String
|
||||||
stringopt name = fromMaybe "" . maybestringopt name
|
stringopt name = fromMaybe "" . maybestringopt name
|
||||||
|
@ -107,7 +107,7 @@ formatliteralp = do
|
|||||||
s <- some c
|
s <- some c
|
||||||
return $ FormatLiteral s
|
return $ FormatLiteral s
|
||||||
where
|
where
|
||||||
isPrintableButNotPercentage x = isPrint x && (not $ x == '%')
|
isPrintableButNotPercentage x = isPrint x && x /= '%'
|
||||||
c = (satisfy isPrintableButNotPercentage <?> "printable character")
|
c = (satisfy isPrintableButNotPercentage <?> "printable character")
|
||||||
<|> try (string "%%" >> return '%')
|
<|> try (string "%%" >> return '%')
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ fieldp = do
|
|||||||
<|> try (string "date" >> return DescriptionField)
|
<|> try (string "date" >> return DescriptionField)
|
||||||
<|> try (string "description" >> return DescriptionField)
|
<|> try (string "description" >> return DescriptionField)
|
||||||
<|> try (string "total" >> return TotalField)
|
<|> try (string "total" >> return TotalField)
|
||||||
<|> try (some digitChar >>= (\s -> return $ FieldNo $ read s))
|
<|> try ((FieldNo . read) <$> some digitChar)
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ timeclockEntriesToTransactions now (i:o:rest)
|
|||||||
(idate,odate) = (localDay itime,localDay otime)
|
(idate,odate) = (localDay itime,localDay otime)
|
||||||
o' = o{tldatetime=itime{localDay=idate, localTimeOfDay=TimeOfDay 23 59 59}}
|
o' = o{tldatetime=itime{localDay=idate, localTimeOfDay=TimeOfDay 23 59 59}}
|
||||||
i' = i{tldatetime=itime{localDay=addDays 1 idate, localTimeOfDay=midnight}}
|
i' = i{tldatetime=itime{localDay=addDays 1 idate, localTimeOfDay=midnight}}
|
||||||
|
{- HLINT ignore timeclockEntriesToTransactions -}
|
||||||
|
|
||||||
-- | Convert a timeclock clockin and clockout entry to an equivalent journal
|
-- | Convert a timeclock clockin and clockout entry to an equivalent journal
|
||||||
-- transaction, representing the time expenditure. Note this entry is not balanced,
|
-- transaction, representing the time expenditure. Note this entry is not balanced,
|
||||||
|
@ -192,9 +192,9 @@ renderCommentLines t = case lines $ T.unpack t of ("":ls) -> "":map commentpref
|
|||||||
--
|
--
|
||||||
postingsAsLines :: Bool -> Bool -> Transaction -> [Posting] -> [String]
|
postingsAsLines :: Bool -> Bool -> Transaction -> [Posting] -> [String]
|
||||||
postingsAsLines elide onelineamounts t ps
|
postingsAsLines elide onelineamounts t ps
|
||||||
| elide && length ps > 1 && all hasAmount ps && isTransactionBalanced Nothing t -- imprecise balanced check
|
| elide && length ps > 1 && all hasAmount ps && isTransactionBalanced Nothing t -- imprecise balanced check
|
||||||
= (concatMap (postingAsLines False onelineamounts ps) $ init ps) ++ postingAsLines True onelineamounts ps (last ps)
|
= concatMap (postingAsLines False onelineamounts ps) (init ps) ++ postingAsLines True onelineamounts ps (last ps)
|
||||||
| otherwise = concatMap (postingAsLines False onelineamounts ps) ps
|
| otherwise = concatMap (postingAsLines False onelineamounts ps) ps
|
||||||
|
|
||||||
-- | Render one posting, on one or more lines, suitable for `print` output.
|
-- | 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,
|
-- There will be an indented account name, plus one or more of status flag,
|
||||||
@ -300,7 +300,7 @@ balancedVirtualPostings :: Transaction -> [Posting]
|
|||||||
balancedVirtualPostings = filter isBalancedVirtual . tpostings
|
balancedVirtualPostings = filter isBalancedVirtual . tpostings
|
||||||
|
|
||||||
transactionsPostings :: [Transaction] -> [Posting]
|
transactionsPostings :: [Transaction] -> [Posting]
|
||||||
transactionsPostings = concat . map tpostings
|
transactionsPostings = concatMap tpostings
|
||||||
|
|
||||||
-- | Get the sums of a transaction's real, virtual, and balanced virtual postings.
|
-- | Get the sums of a transaction's real, virtual, and balanced virtual postings.
|
||||||
transactionPostingBalances :: Transaction -> (MixedAmount,MixedAmount,MixedAmount)
|
transactionPostingBalances :: Transaction -> (MixedAmount,MixedAmount,MixedAmount)
|
||||||
@ -445,9 +445,7 @@ inferBalancingAmount update styles t@Transaction{tpostings=ps}
|
|||||||
inferBalancingPrices :: Transaction -> Transaction
|
inferBalancingPrices :: Transaction -> Transaction
|
||||||
inferBalancingPrices t@Transaction{tpostings=ps} = t{tpostings=ps'}
|
inferBalancingPrices t@Transaction{tpostings=ps} = t{tpostings=ps'}
|
||||||
where
|
where
|
||||||
ps' = map (priceInferrerFor t BalancedVirtualPosting) $
|
ps' = map (priceInferrerFor t BalancedVirtualPosting . priceInferrerFor t RegularPosting) ps
|
||||||
map (priceInferrerFor t RegularPosting) $
|
|
||||||
ps
|
|
||||||
|
|
||||||
-- | Generate a posting update function which assigns a suitable balancing
|
-- | Generate a posting update function which assigns a suitable balancing
|
||||||
-- price to the posting, if and as appropriate for the given transaction and
|
-- price to the posting, if and as appropriate for the given transaction and
|
||||||
@ -478,7 +476,7 @@ priceInferrerFor t pt = inferprice
|
|||||||
tocommodity = head $ filter (/=fromcommodity) sumcommodities
|
tocommodity = head $ filter (/=fromcommodity) sumcommodities
|
||||||
toamount = head $ filter ((==tocommodity).acommodity) sumamounts
|
toamount = head $ filter ((==tocommodity).acommodity) sumamounts
|
||||||
unitprice = (aquantity fromamount) `divideAmount` toamount
|
unitprice = (aquantity fromamount) `divideAmount` toamount
|
||||||
unitprecision = max 2 ((asprecision $ astyle $ toamount) + (asprecision $ astyle $ fromamount))
|
unitprecision = max 2 (asprecision (astyle toamount) + asprecision (astyle fromamount))
|
||||||
inferprice p = p
|
inferprice p = p
|
||||||
|
|
||||||
-- Get a transaction's secondary date, defaulting to the primary date.
|
-- Get a transaction's secondary date, defaulting to the primary date.
|
||||||
@ -502,371 +500,495 @@ postingSetTransaction t p = p{ptransaction=Just t}
|
|||||||
|
|
||||||
-- tests
|
-- tests
|
||||||
|
|
||||||
tests_Transaction = tests "Transaction" [
|
tests_Transaction =
|
||||||
|
tests
|
||||||
tests "showTransactionUnelided" [
|
"Transaction"
|
||||||
showTransactionUnelided nulltransaction `is` "0000/01/01\n\n"
|
[ tests
|
||||||
,showTransactionUnelided nulltransaction{
|
"showTransactionUnelided"
|
||||||
tdate=parsedate "2012/05/14",
|
[ showTransactionUnelided nulltransaction `is` "0000/01/01\n\n"
|
||||||
tdate2=Just $ parsedate "2012/05/15",
|
, showTransactionUnelided
|
||||||
tstatus=Unmarked,
|
nulltransaction
|
||||||
tcode="code",
|
{ tdate = parsedate "2012/05/14"
|
||||||
tdescription="desc",
|
, tdate2 = Just $ parsedate "2012/05/15"
|
||||||
tcomment="tcomment1\ntcomment2\n",
|
, tstatus = Unmarked
|
||||||
ttags=[("ttag1","val1")],
|
, tcode = "code"
|
||||||
tpostings=[
|
, tdescription = "desc"
|
||||||
nullposting{
|
, tcomment = "tcomment1\ntcomment2\n"
|
||||||
pstatus=Cleared,
|
, ttags = [("ttag1", "val1")]
|
||||||
paccount="a",
|
, tpostings =
|
||||||
pamount=Mixed [usd 1, hrs 2],
|
[ nullposting
|
||||||
pcomment="\npcomment2\n",
|
{ pstatus = Cleared
|
||||||
ptype=RegularPosting,
|
, paccount = "a"
|
||||||
ptags=[("ptag1","val1"),("ptag2","val2")]
|
, pamount = Mixed [usd 1, hrs 2]
|
||||||
}
|
, pcomment = "\npcomment2\n"
|
||||||
]
|
, ptype = RegularPosting
|
||||||
}
|
, ptags = [("ptag1", "val1"), ("ptag2", "val2")]
|
||||||
`is` unlines [
|
}
|
||||||
"2012/05/14=2012/05/15 (code) desc ; tcomment1",
|
]
|
||||||
" ; tcomment2",
|
} `is`
|
||||||
" * a $1.00",
|
unlines
|
||||||
" ; pcomment2",
|
[ "2012/05/14=2012/05/15 (code) desc ; tcomment1"
|
||||||
" * a 2.00h",
|
, " ; tcomment2"
|
||||||
" ; pcomment2",
|
, " * a $1.00"
|
||||||
""
|
, " ; pcomment2"
|
||||||
]
|
, " * a 2.00h"
|
||||||
]
|
, " ; pcomment2"
|
||||||
|
, ""
|
||||||
,tests "postingAsLines" [
|
]
|
||||||
postingAsLines False False [posting] posting `is` [""]
|
]
|
||||||
,let p = posting{
|
, tests
|
||||||
pstatus=Cleared,
|
"postingAsLines"
|
||||||
paccount="a",
|
[ postingAsLines False False [posting] posting `is` [""]
|
||||||
pamount=Mixed [usd 1, hrs 2],
|
, let p =
|
||||||
pcomment="pcomment1\npcomment2\n tag3: val3 \n",
|
posting
|
||||||
ptype=RegularPosting,
|
{ pstatus = Cleared
|
||||||
ptags=[("ptag1","val1"),("ptag2","val2")]
|
, paccount = "a"
|
||||||
}
|
, pamount = Mixed [usd 1, hrs 2]
|
||||||
in postingAsLines False False [p] p `is`
|
, pcomment = "pcomment1\npcomment2\n tag3: val3 \n"
|
||||||
[
|
, ptype = RegularPosting
|
||||||
" * a $1.00 ; pcomment1",
|
, ptags = [("ptag1", "val1"), ("ptag2", "val2")]
|
||||||
" ; pcomment2",
|
}
|
||||||
" ; tag3: val3 ",
|
in postingAsLines False False [p] p `is`
|
||||||
" * a 2.00h ; pcomment1",
|
[ " * a $1.00 ; pcomment1"
|
||||||
" ; pcomment2",
|
, " ; pcomment2"
|
||||||
" ; tag3: val3 "
|
, " ; tag3: val3 "
|
||||||
]
|
, " * a 2.00h ; pcomment1"
|
||||||
]
|
, " ; pcomment2"
|
||||||
|
, " ; tag3: val3 "
|
||||||
|
]
|
||||||
|
]
|
||||||
-- postingsAsLines
|
-- postingsAsLines
|
||||||
,let
|
|
||||||
-- one implicit amount
|
-- one implicit amount
|
||||||
timp = nulltransaction{tpostings=[
|
, let timp = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt]}
|
||||||
"a" `post` usd 1,
|
|
||||||
"b" `post` missingamt
|
|
||||||
]}
|
|
||||||
-- explicit amounts, balanced
|
-- explicit amounts, balanced
|
||||||
texp = nulltransaction{tpostings=[
|
texp = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` usd (-1)]}
|
||||||
"a" `post` usd 1,
|
|
||||||
"b" `post` usd (-1)
|
|
||||||
]}
|
|
||||||
-- explicit amount, only one posting
|
-- explicit amount, only one posting
|
||||||
texp1 = nulltransaction{tpostings=[
|
texp1 = nulltransaction {tpostings = ["(a)" `post` usd 1]}
|
||||||
"(a)" `post` usd 1
|
|
||||||
]}
|
|
||||||
-- explicit amounts, two commodities, explicit balancing price
|
-- explicit amounts, two commodities, explicit balancing price
|
||||||
texp2 = nulltransaction{tpostings=[
|
texp2 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` (hrs (-1) `at` usd 1)]}
|
||||||
"a" `post` usd 1,
|
|
||||||
"b" `post` (hrs (-1) `at` usd 1)
|
|
||||||
]}
|
|
||||||
-- explicit amounts, two commodities, implicit balancing price
|
-- explicit amounts, two commodities, implicit balancing price
|
||||||
texp2b = nulltransaction{tpostings=[
|
texp2b = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` hrs (-1)]}
|
||||||
"a" `post` usd 1,
|
|
||||||
"b" `post` hrs (-1)
|
|
||||||
]}
|
|
||||||
-- one missing amount, not the last one
|
-- one missing amount, not the last one
|
||||||
t3 = nulltransaction{tpostings=[
|
t3 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt, "c" `post` usd (-1)]}
|
||||||
"a" `post` usd 1
|
|
||||||
,"b" `post` missingamt
|
|
||||||
,"c" `post` usd (-1)
|
|
||||||
]}
|
|
||||||
-- unbalanced amounts when precision is limited (#931)
|
-- unbalanced amounts when precision is limited (#931)
|
||||||
t4 = nulltransaction{tpostings=[
|
t4 = nulltransaction {tpostings = ["a" `post` usd (-0.01), "b" `post` usd (0.005), "c" `post` usd (0.005)]}
|
||||||
"a" `post` usd (-0.01)
|
in tests
|
||||||
,"b" `post` usd (0.005)
|
"postingsAsLines"
|
||||||
,"c" `post` usd (0.005)
|
[ test "null-transaction" $
|
||||||
]}
|
let t = nulltransaction
|
||||||
in
|
in postingsAsLines True False t (tpostings t) `is` []
|
||||||
tests "postingsAsLines" [
|
, test "implicit-amount-elide-false" $
|
||||||
|
let t = timp
|
||||||
test "null-transaction" $
|
in postingsAsLines False False t (tpostings t) `is`
|
||||||
let t = nulltransaction
|
[ " a $1.00"
|
||||||
in postingsAsLines True False t (tpostings t) `is` []
|
, " b" -- implicit amount remains implicit
|
||||||
|
]
|
||||||
,test "implicit-amount-elide-false" $
|
, test "implicit-amount-elide-true" $
|
||||||
let t = timp in postingsAsLines False False t (tpostings t) `is` [
|
let t = timp
|
||||||
" a $1.00"
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
," b" -- implicit amount remains implicit
|
[ " a $1.00"
|
||||||
]
|
, " b" -- implicit amount remains implicit
|
||||||
|
]
|
||||||
,test "implicit-amount-elide-true" $
|
, test "explicit-amounts-elide-false" $
|
||||||
let t = timp in postingsAsLines True False t (tpostings t) `is` [
|
let t = texp
|
||||||
" a $1.00"
|
in postingsAsLines False False t (tpostings t) `is`
|
||||||
," b" -- implicit amount remains implicit
|
[ " a $1.00"
|
||||||
]
|
, " b $-1.00" -- both amounts remain explicit
|
||||||
|
]
|
||||||
,test "explicit-amounts-elide-false" $
|
, test "explicit-amounts-elide-true" $
|
||||||
let t = texp in postingsAsLines False False t (tpostings t) `is` [
|
let t = texp
|
||||||
" a $1.00"
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
," b $-1.00" -- both amounts remain explicit
|
[ " a $1.00"
|
||||||
]
|
, " b" -- explicit amount is made implicit
|
||||||
|
]
|
||||||
,test "explicit-amounts-elide-true" $
|
, test "one-explicit-amount-elide-true" $
|
||||||
let t = texp in postingsAsLines True False t (tpostings t) `is` [
|
let t = texp1
|
||||||
" a $1.00"
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
," b" -- explicit amount is made implicit
|
[ " (a) $1.00" -- explicit amount remains explicit since only one posting
|
||||||
]
|
]
|
||||||
|
, test "explicit-amounts-two-commodities-elide-true" $
|
||||||
,test "one-explicit-amount-elide-true" $
|
let t = texp2
|
||||||
let t = texp1 in postingsAsLines True False t (tpostings t) `is` [
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
" (a) $1.00" -- explicit amount remains explicit since only one posting
|
[ " a $1.00"
|
||||||
]
|
, " b" -- explicit amount is made implicit since txn is explicitly balanced
|
||||||
|
]
|
||||||
,test "explicit-amounts-two-commodities-elide-true" $
|
, test "explicit-amounts-not-explicitly-balanced-elide-true" $
|
||||||
let t = texp2 in postingsAsLines True False t (tpostings t) `is` [
|
let t = texp2b
|
||||||
" a $1.00"
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
," b" -- explicit amount is made implicit since txn is explicitly balanced
|
[ " a $1.00"
|
||||||
]
|
, " b -1.00h" -- explicit amount remains explicit since a conversion price would have be inferred to balance
|
||||||
|
]
|
||||||
,test "explicit-amounts-not-explicitly-balanced-elide-true" $
|
, test "implicit-amount-not-last" $
|
||||||
let t = texp2b in postingsAsLines True False t (tpostings t) `is` [
|
let t = t3
|
||||||
" a $1.00"
|
in postingsAsLines True False t (tpostings t) `is`
|
||||||
," b -1.00h" -- explicit amount remains explicit since a conversion price would have be inferred to balance
|
[" a $1.00", " b", " c $-1.00"]
|
||||||
]
|
, _test "ensure-visibly-balanced" $
|
||||||
|
let t = t4
|
||||||
,test "implicit-amount-not-last" $
|
in postingsAsLines False False t (tpostings t) `is`
|
||||||
let t = t3 in postingsAsLines True False t (tpostings t) `is` [
|
[" a $-0.01", " b $0.005", " c $0.005"]
|
||||||
" a $1.00"
|
]
|
||||||
," b"
|
, do let inferTransaction :: Transaction -> Either String Transaction
|
||||||
," c $-1.00"
|
inferTransaction = runIdentity . runExceptT . inferBalancingAmount (\_ _ -> return ()) Map.empty
|
||||||
]
|
tests
|
||||||
|
"inferBalancingAmount"
|
||||||
,_test "ensure-visibly-balanced" $
|
[ inferTransaction nulltransaction `is` Right nulltransaction
|
||||||
let t = t4 in postingsAsLines False False t (tpostings t) `is` [
|
, inferTransaction nulltransaction {tpostings = ["a" `post` usd (-5), "b" `post` missingamt]} `is`
|
||||||
" a $-0.01"
|
Right nulltransaction {tpostings = ["a" `post` usd (-5), "b" `post` usd 5]}
|
||||||
," b $0.005"
|
, inferTransaction
|
||||||
," c $0.005"
|
nulltransaction {tpostings = ["a" `post` usd (-5), "b" `post` (eur 3 @@ usd 4), "c" `post` missingamt]} `is`
|
||||||
]
|
Right nulltransaction {tpostings = ["a" `post` usd (-5), "b" `post` (eur 3 @@ usd 4), "c" `post` usd 1]}
|
||||||
|
]
|
||||||
]
|
, tests
|
||||||
|
"showTransaction"
|
||||||
,do
|
[ test "show a balanced transaction, eliding last amount" $
|
||||||
let inferTransaction :: Transaction -> Either String Transaction
|
let t =
|
||||||
inferTransaction = runIdentity . runExceptT . inferBalancingAmount (\_ _ -> return ()) Map.empty
|
Transaction
|
||||||
tests "inferBalancingAmount" [
|
0
|
||||||
inferTransaction nulltransaction `is` Right nulltransaction
|
""
|
||||||
,inferTransaction nulltransaction{
|
nullsourcepos
|
||||||
tpostings=[
|
(parsedate "2007/01/28")
|
||||||
"a" `post` usd (-5),
|
Nothing
|
||||||
"b" `post` missingamt
|
Unmarked
|
||||||
]}
|
""
|
||||||
`is` Right
|
"coopportunity"
|
||||||
nulltransaction{
|
""
|
||||||
tpostings=[
|
[]
|
||||||
"a" `post` usd (-5),
|
[ posting {paccount = "expenses:food:groceries", pamount = Mixed [usd 47.18], ptransaction = Just t}
|
||||||
"b" `post` usd 5
|
, posting {paccount = "assets:checking", pamount = Mixed [usd (-47.18)], ptransaction = Just t}
|
||||||
]}
|
]
|
||||||
,inferTransaction nulltransaction{
|
in showTransaction t `is`
|
||||||
tpostings=[
|
unlines
|
||||||
"a" `post` usd (-5),
|
["2007/01/28 coopportunity", " expenses:food:groceries $47.18", " assets:checking", ""]
|
||||||
"b" `post` (eur 3 @@ usd 4),
|
, test "show a balanced transaction, no eliding" $
|
||||||
"c" `post` missingamt
|
(let t =
|
||||||
]}
|
Transaction
|
||||||
`is` Right
|
0
|
||||||
nulltransaction{
|
""
|
||||||
tpostings=[
|
nullsourcepos
|
||||||
"a" `post` usd (-5),
|
(parsedate "2007/01/28")
|
||||||
"b" `post` (eur 3 @@ usd 4),
|
Nothing
|
||||||
"c" `post` usd 1
|
Unmarked
|
||||||
]}
|
""
|
||||||
]
|
"coopportunity"
|
||||||
|
""
|
||||||
,tests "showTransaction" [
|
[]
|
||||||
test "show a balanced transaction, eliding last amount" $
|
[ posting {paccount = "expenses:food:groceries", pamount = Mixed [usd 47.18], ptransaction = Just t}
|
||||||
let t = Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "coopportunity" "" []
|
, posting {paccount = "assets:checking", pamount = Mixed [usd (-47.18)], ptransaction = Just t}
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
]
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
in showTransactionUnelided t) `is`
|
||||||
]
|
(unlines
|
||||||
in
|
[ "2007/01/28 coopportunity"
|
||||||
showTransaction t
|
, " expenses:food:groceries $47.18"
|
||||||
`is`
|
, " assets:checking $-47.18"
|
||||||
unlines
|
, ""
|
||||||
["2007/01/28 coopportunity"
|
])
|
||||||
," expenses:food:groceries $47.18"
|
|
||||||
," assets:checking"
|
|
||||||
,""
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "show a balanced transaction, no eliding" $
|
|
||||||
(let t = Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "coopportunity" "" []
|
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}
|
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}
|
|
||||||
]
|
|
||||||
in showTransactionUnelided t)
|
|
||||||
`is`
|
|
||||||
(unlines
|
|
||||||
["2007/01/28 coopportunity"
|
|
||||||
," expenses:food:groceries $47.18"
|
|
||||||
," assets:checking $-47.18"
|
|
||||||
,""
|
|
||||||
])
|
|
||||||
|
|
||||||
-- document some cases that arise in debug/testing:
|
-- document some cases that arise in debug/testing:
|
||||||
,test "show an unbalanced transaction, should not elide" $
|
, test "show an unbalanced transaction, should not elide" $
|
||||||
(showTransaction
|
(showTransaction
|
||||||
(txnTieKnot $ Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "coopportunity" "" []
|
(txnTieKnot $
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
Transaction
|
||||||
,posting{paccount="assets:checking", pamount=Mixed [usd (-47.19)]}
|
0
|
||||||
]))
|
""
|
||||||
`is`
|
nullsourcepos
|
||||||
(unlines
|
(parsedate "2007/01/28")
|
||||||
["2007/01/28 coopportunity"
|
Nothing
|
||||||
," expenses:food:groceries $47.18"
|
Unmarked
|
||||||
," assets:checking $-47.19"
|
""
|
||||||
,""
|
"coopportunity"
|
||||||
])
|
""
|
||||||
|
[]
|
||||||
,test "show an unbalanced transaction with one posting, should not elide" $
|
[ posting {paccount = "expenses:food:groceries", pamount = Mixed [usd 47.18]}
|
||||||
(showTransaction
|
, posting {paccount = "assets:checking", pamount = Mixed [usd (-47.19)]}
|
||||||
(txnTieKnot $ Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "coopportunity" "" []
|
])) `is`
|
||||||
[posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}
|
(unlines
|
||||||
]))
|
[ "2007/01/28 coopportunity"
|
||||||
`is`
|
, " expenses:food:groceries $47.18"
|
||||||
(unlines
|
, " assets:checking $-47.19"
|
||||||
["2007/01/28 coopportunity"
|
, ""
|
||||||
," expenses:food:groceries $47.18"
|
])
|
||||||
,""
|
, test "show an unbalanced transaction with one posting, should not elide" $
|
||||||
])
|
(showTransaction
|
||||||
|
(txnTieKnot $
|
||||||
,test "show a transaction with one posting and a missing amount" $
|
Transaction
|
||||||
(showTransaction
|
0
|
||||||
(txnTieKnot $ Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "coopportunity" "" []
|
""
|
||||||
[posting{paccount="expenses:food:groceries", pamount=missingmixedamt}
|
nullsourcepos
|
||||||
]))
|
(parsedate "2007/01/28")
|
||||||
`is`
|
Nothing
|
||||||
(unlines
|
Unmarked
|
||||||
["2007/01/28 coopportunity"
|
""
|
||||||
," expenses:food:groceries"
|
"coopportunity"
|
||||||
,""
|
""
|
||||||
])
|
[]
|
||||||
|
[posting {paccount = "expenses:food:groceries", pamount = Mixed [usd 47.18]}])) `is`
|
||||||
,test "show a transaction with a priced commodityless amount" $
|
(unlines ["2007/01/28 coopportunity", " expenses:food:groceries $47.18", ""])
|
||||||
(showTransaction
|
, test "show a transaction with one posting and a missing amount" $
|
||||||
(txnTieKnot $ Transaction 0 "" nullsourcepos (parsedate "2010/01/01") Nothing Unmarked "" "x" "" []
|
(showTransaction
|
||||||
[posting{paccount="a", pamount=Mixed [num 1 `at` (usd 2 `withPrecision` 0)]}
|
(txnTieKnot $
|
||||||
,posting{paccount="b", pamount= missingmixedamt}
|
Transaction
|
||||||
]))
|
0
|
||||||
`is`
|
""
|
||||||
(unlines
|
nullsourcepos
|
||||||
["2010/01/01 x"
|
(parsedate "2007/01/28")
|
||||||
," a 1 @ $2"
|
Nothing
|
||||||
," b"
|
Unmarked
|
||||||
,""
|
""
|
||||||
])
|
"coopportunity"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[posting {paccount = "expenses:food:groceries", pamount = missingmixedamt}])) `is`
|
||||||
|
(unlines ["2007/01/28 coopportunity", " expenses:food:groceries", ""])
|
||||||
|
, test "show a transaction with a priced commodityless amount" $
|
||||||
|
(showTransaction
|
||||||
|
(txnTieKnot $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2010/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"x"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "a", pamount = Mixed [num 1 `at` (usd 2 `withPrecision` 0)]}
|
||||||
|
, posting {paccount = "b", pamount = missingmixedamt}
|
||||||
|
])) `is`
|
||||||
|
(unlines ["2010/01/01 x", " a 1 @ $2", " b", ""])
|
||||||
|
]
|
||||||
|
, tests
|
||||||
|
"balanceTransaction"
|
||||||
|
[ test "detect unbalanced entry, sign error" $
|
||||||
|
expectLeft
|
||||||
|
(balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2007/01/28")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"test"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[posting {paccount = "a", pamount = Mixed [usd 1]}, posting {paccount = "b", pamount = Mixed [usd 1]}]))
|
||||||
|
, test "detect unbalanced entry, multiple missing amounts" $
|
||||||
|
expectLeft $
|
||||||
|
balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2007/01/28")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"test"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "a", pamount = missingmixedamt}
|
||||||
|
, posting {paccount = "b", pamount = missingmixedamt}
|
||||||
|
])
|
||||||
|
, test "one missing amount is inferred" $
|
||||||
|
(pamount . last . tpostings <$>
|
||||||
|
balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2007/01/28")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[posting {paccount = "a", pamount = Mixed [usd 1]}, posting {paccount = "b", pamount = missingmixedamt}])) `is`
|
||||||
|
Right (Mixed [usd (-1)])
|
||||||
|
, test "conversion price is inferred" $
|
||||||
|
(pamount . head . tpostings <$>
|
||||||
|
balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2007/01/28")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "a", pamount = Mixed [usd 1.35]}
|
||||||
|
, posting {paccount = "b", pamount = Mixed [eur (-1)]}
|
||||||
|
])) `is`
|
||||||
|
Right (Mixed [usd 1.35 @@ (eur 1 `withPrecision` maxprecision)])
|
||||||
|
, test "balanceTransaction balances based on cost if there are unit prices" $
|
||||||
|
expectRight $
|
||||||
|
balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2011/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "a", pamount = Mixed [usd 1 `at` eur 2]}
|
||||||
|
, posting {paccount = "a", pamount = Mixed [usd (-2) `at` eur 1]}
|
||||||
|
])
|
||||||
|
, test "balanceTransaction balances based on cost if there are total prices" $
|
||||||
|
expectRight $
|
||||||
|
balanceTransaction
|
||||||
|
Nothing
|
||||||
|
(Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2011/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "a", pamount = Mixed [usd 1 @@ eur 1]}
|
||||||
|
, posting {paccount = "a", pamount = Mixed [usd (-2) @@ eur 1]}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
, tests
|
||||||
|
"isTransactionBalanced"
|
||||||
|
[ test "detect balanced" $
|
||||||
|
expect $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "b", pamount = Mixed [usd 1.00]}
|
||||||
|
, posting {paccount = "c", pamount = Mixed [usd (-1.00)]}
|
||||||
|
]
|
||||||
|
, test "detect unbalanced" $
|
||||||
|
expect $
|
||||||
|
not $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "b", pamount = Mixed [usd 1.00]}
|
||||||
|
, posting {paccount = "c", pamount = Mixed [usd (-1.01)]}
|
||||||
|
]
|
||||||
|
, test "detect unbalanced, one posting" $
|
||||||
|
expect $
|
||||||
|
not $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[posting {paccount = "b", pamount = Mixed [usd 1.00]}]
|
||||||
|
, test "one zero posting is considered balanced for now" $
|
||||||
|
expect $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[posting {paccount = "b", pamount = Mixed [usd 0]}]
|
||||||
|
, test "virtual postings don't need to balance" $
|
||||||
|
expect $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "b", pamount = Mixed [usd 1.00]}
|
||||||
|
, posting {paccount = "c", pamount = Mixed [usd (-1.00)]}
|
||||||
|
, posting {paccount = "d", pamount = Mixed [usd 100], ptype = VirtualPosting}
|
||||||
|
]
|
||||||
|
, test "balanced virtual postings need to balance among themselves" $
|
||||||
|
expect $
|
||||||
|
not $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "b", pamount = Mixed [usd 1.00]}
|
||||||
|
, posting {paccount = "c", pamount = Mixed [usd (-1.00)]}
|
||||||
|
, posting {paccount = "d", pamount = Mixed [usd 100], ptype = BalancedVirtualPosting}
|
||||||
|
]
|
||||||
|
, test "balanced virtual postings need to balance among themselves (2)" $
|
||||||
|
expect $
|
||||||
|
isTransactionBalanced Nothing $
|
||||||
|
Transaction
|
||||||
|
0
|
||||||
|
""
|
||||||
|
nullsourcepos
|
||||||
|
(parsedate "2009/01/01")
|
||||||
|
Nothing
|
||||||
|
Unmarked
|
||||||
|
""
|
||||||
|
"a"
|
||||||
|
""
|
||||||
|
[]
|
||||||
|
[ posting {paccount = "b", pamount = Mixed [usd 1.00]}
|
||||||
|
, posting {paccount = "c", pamount = Mixed [usd (-1.00)]}
|
||||||
|
, posting {paccount = "d", pamount = Mixed [usd 100], ptype = BalancedVirtualPosting}
|
||||||
|
, posting {paccount = "3", pamount = Mixed [usd (-100)], ptype = BalancedVirtualPosting}
|
||||||
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
,tests "balanceTransaction" [
|
|
||||||
test "detect unbalanced entry, sign error" $
|
|
||||||
(expectLeft $ balanceTransaction Nothing
|
|
||||||
(Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "test" "" []
|
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1]}
|
|
||||||
,posting{paccount="b", pamount=Mixed [usd 1]}
|
|
||||||
]))
|
|
||||||
|
|
||||||
,test "detect unbalanced entry, multiple missing amounts" $
|
|
||||||
(expectLeft $ balanceTransaction Nothing
|
|
||||||
(Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "test" "" []
|
|
||||||
[posting{paccount="a", pamount=missingmixedamt}
|
|
||||||
,posting{paccount="b", pamount=missingmixedamt}
|
|
||||||
]))
|
|
||||||
|
|
||||||
,test "one missing amount is inferred" $
|
|
||||||
(pamount . last . tpostings <$> balanceTransaction
|
|
||||||
Nothing
|
|
||||||
(Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "" "" []
|
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1]}
|
|
||||||
,posting{paccount="b", pamount=missingmixedamt}
|
|
||||||
]))
|
|
||||||
`is` Right (Mixed [usd (-1)])
|
|
||||||
|
|
||||||
,test "conversion price is inferred" $
|
|
||||||
(pamount . head . tpostings <$> balanceTransaction
|
|
||||||
Nothing
|
|
||||||
(Transaction 0 "" nullsourcepos (parsedate "2007/01/28") Nothing Unmarked "" "" "" []
|
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1.35]}
|
|
||||||
,posting{paccount="b", pamount=Mixed [eur (-1)]}
|
|
||||||
]))
|
|
||||||
`is` Right (Mixed [usd 1.35 @@ (eur 1 `withPrecision` maxprecision)])
|
|
||||||
|
|
||||||
,test "balanceTransaction balances based on cost if there are unit prices" $
|
|
||||||
expectRight $
|
|
||||||
balanceTransaction Nothing (Transaction 0 "" nullsourcepos (parsedate "2011/01/01") Nothing Unmarked "" "" "" []
|
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1 `at` eur 2]}
|
|
||||||
,posting{paccount="a", pamount=Mixed [usd (-2) `at` eur 1]}
|
|
||||||
])
|
|
||||||
|
|
||||||
,test "balanceTransaction balances based on cost if there are total prices" $
|
|
||||||
expectRight $
|
|
||||||
balanceTransaction Nothing (Transaction 0 "" nullsourcepos (parsedate "2011/01/01") Nothing Unmarked "" "" "" []
|
|
||||||
[posting{paccount="a", pamount=Mixed [usd 1 @@ eur 1]}
|
|
||||||
,posting{paccount="a", pamount=Mixed [usd (-2) @@ eur 1]}
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
,tests "isTransactionBalanced" [
|
|
||||||
test "detect balanced" $ expect $
|
|
||||||
isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)]}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "detect unbalanced" $ expect $
|
|
||||||
not $ isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.01)]}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "detect unbalanced, one posting" $ expect $
|
|
||||||
not $ isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "one zero posting is considered balanced for now" $ expect $
|
|
||||||
isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 0]}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "virtual postings don't need to balance" $ expect $
|
|
||||||
isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)]}
|
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=VirtualPosting}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "balanced virtual postings need to balance among themselves" $ expect $
|
|
||||||
not $ isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)]}
|
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting}
|
|
||||||
]
|
|
||||||
|
|
||||||
,test "balanced virtual postings need to balance among themselves (2)" $ expect $
|
|
||||||
isTransactionBalanced Nothing $ Transaction 0 "" nullsourcepos (parsedate "2009/01/01") Nothing Unmarked "" "a" "" []
|
|
||||||
[posting{paccount="b", pamount=Mixed [usd 1.00]}
|
|
||||||
,posting{paccount="c", pamount=Mixed [usd (-1.00)]}
|
|
||||||
,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting}
|
|
||||||
,posting{paccount="3", pamount=Mixed [usd (-100)], ptype=BalancedVirtualPosting}
|
|
||||||
]
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
]
|
|
||||||
|
Loading…
Reference in New Issue
Block a user