ADD priority flag to keep tx's sorted always

This commit is contained in:
Nathan Dwarshuis 2023-07-07 00:20:18 -04:00
parent 24bc9a239b
commit 2946a8f9e2
8 changed files with 89 additions and 76 deletions

View File

@ -679,7 +679,7 @@ let Amount =
-} -}
\(w : Type) -> \(w : Type) ->
\(v : Type) -> \(v : Type) ->
{ amtWhen : w, amtValue : v, amtDesc : Text } { amtWhen : w, amtValue : v, amtDesc : Text, amtPriority : Integer }
let TransferType = let TransferType =
{- {-
@ -967,11 +967,13 @@ let Income =
(if any) after all allocations have been applied. (if any) after all allocations have been applied.
-} -}
TaggedAcnt.Type TaggedAcnt.Type
, incPriority : Integer
} }
, default = , default =
{ incPretax = [] : List (SingleAllocation PretaxValue) { incPretax = [] : List (SingleAllocation PretaxValue)
, incTaxes = [] : List (SingleAllocation TaxValue) , incTaxes = [] : List (SingleAllocation TaxValue)
, incPosttaxx = [] : List (SingleAllocation PosttaxValue) , incPosttaxx = [] : List (SingleAllocation PosttaxValue)
, incPriority = +0
} }
} }

View File

@ -92,29 +92,30 @@ readIncome
Income Income
{ incWhen { incWhen
, incCurrency , incCurrency
, incFrom , incFrom = TaggedAcnt {taAcnt = srcAcnt, taTags = srcTags}
, incPretax , incPretax
, incPosttax , incPosttax
, incTaxes , incTaxes
, incToBal , incToBal = TaggedAcnt {taAcnt = destAcnt, taTags = destTags}
, incGross , incGross
, incPayPeriod , incPayPeriod
, incPriority
} = } =
combineErrorM combineErrorM
(combineError incRes nonIncRes (,)) (combineError incRes nonIncRes (,))
(combineError precRes dayRes (,)) (combineError cpRes dayRes (,))
$ \_ (precision, days) -> do $ \_ (cp, days) -> do
let gross = roundPrecision precision incGross let gross = roundPrecisionCur cp incGross
concat <$> foldDays (allocate precision gross) start days foldDays (allocate cp gross) start days
where where
incRes = isIncomeAcnt $ taAcnt incFrom incRes = isIncomeAcnt srcAcnt
nonIncRes = nonIncRes =
mapErrors isNotIncomeAcnt $ mapErrors isNotIncomeAcnt $
taAcnt incToBal destAcnt
: (alloAcnt <$> incPretax) : (alloAcnt <$> incPretax)
++ (alloAcnt <$> incTaxes) ++ (alloAcnt <$> incTaxes)
++ (alloAcnt <$> incPosttax) ++ (alloAcnt <$> incPosttax)
precRes = lookupCurrencyPrec incCurrency cpRes = lookupCurrency incCurrency
dayRes = liftExcept $ expandDatePat ds incWhen dayRes = liftExcept $ expandDatePat ds incWhen
start = fromGregorian' $ pStart incPayPeriod start = fromGregorian' $ pStart incPayPeriod
pType' = pType incPayPeriod pType' = pType incPayPeriod
@ -123,8 +124,9 @@ readIncome
flatPost = concatMap flattenAllo incPosttax flatPost = concatMap flattenAllo incPosttax
sumAllos = sum . fmap faValue sumAllos = sum . fmap faValue
-- TODO ensure these are all the "correct" accounts -- TODO ensure these are all the "correct" accounts
allocate precision gross prevDay day = do allocate cp gross prevDay day = do
scaler <- liftExcept $ periodScaler pType' prevDay day scaler <- liftExcept $ periodScaler pType' prevDay day
let precision = cpPrec cp
let (preDeductions, pre) = let (preDeductions, pre) =
allocatePre precision gross $ allocatePre precision gross $
flatPre ++ concatMap (selectAllos day) intPre flatPre ++ concatMap (selectAllos day) intPre
@ -135,31 +137,39 @@ readIncome
let post = let post =
allocatePost precision aftertaxGross $ allocatePost precision aftertaxGross $
flatPost ++ concatMap (selectAllos day) intPost flatPost ++ concatMap (selectAllos day) intPost
let balance = aftertaxGross - sumAllos post
-- TODO double or rational here? -- TODO double or rational here?
primary <- let src =
entryPair Entry
incFrom { eAcnt = srcAcnt
incToBal , eValue = ()
incCurrency , eComment = ""
"balance after deductions" , eTags = srcTags
(fromRational balance) }
() let dest =
-- TODO make this into one large tx? Entry
allos <- mapErrors (allo2Trans key name day incFrom) (pre ++ tax ++ post) { eAcnt = destAcnt
let bal = , eValue = ()
, eComment = "balance after deductions"
, eTags = destTags
}
let allos = allo2Trans <$> (pre ++ tax ++ post)
let primary =
EntrySet
{ esTotalValue = gross
, esCurrency = cp
, esFrom = HalfEntrySet {hesPrimary = src, hesOther = []}
, esTo = HalfEntrySet {hesPrimary = dest, hesOther = allos}
}
return $
Tx Tx
{ txCommit = key { txCommit = key
, txDate = day , txDate = day
, txPrimary = Left primary , txPrimary = Left primary
, txOther = [] , txOther = []
, txDescr = "balance after deductions" , txDescr = ""
, txBudget = name , txBudget = name
, txPriority = incPriority
} }
-- TODO use real name here
if balance < 0
then throwError $ InsertException [IncomeError day name balance]
else return (bal : allos)
periodScaler periodScaler
:: PeriodType :: PeriodType
@ -236,48 +246,34 @@ checkAcntTypes ts i = void $ go =<< lookupAccountType i
| otherwise = throwError $ InsertException [AccountError i ts] | otherwise = throwError $ InsertException [AccountError i ts]
flattenAllo :: SingleAllocation v -> [FlatAllocation v] flattenAllo :: SingleAllocation v -> [FlatAllocation v]
flattenAllo Allocation {alloAmts, alloCur, alloTo} = fmap go alloAmts flattenAllo Allocation {alloAmts, alloTo} = fmap go alloAmts
where where
go Amount {amtValue, amtDesc} = go Amount {amtValue, amtDesc} =
FlatAllocation FlatAllocation
{ faCur = alloCur { faTo = alloTo
, faTo = alloTo
, faValue = amtValue , faValue = amtValue
, faDesc = amtDesc , faDesc = amtDesc
} }
-- ASSUME allocations are sorted -- ASSUME allocations are sorted
selectAllos :: Day -> DaySpanAllocation v -> [FlatAllocation v] selectAllos :: Day -> DaySpanAllocation v -> [FlatAllocation v]
selectAllos day Allocation {alloAmts, alloCur, alloTo} = selectAllos day Allocation {alloAmts, alloTo} =
go <$> filter ((`inDaySpan` day) . amtWhen) alloAmts go <$> filter ((`inDaySpan` day) . amtWhen) alloAmts
where where
go Amount {amtValue, amtDesc} = go Amount {amtValue, amtDesc} =
FlatAllocation FlatAllocation
{ faCur = alloCur { faTo = alloTo
, faTo = alloTo
, faValue = amtValue , faValue = amtValue
, faDesc = amtDesc , faDesc = amtDesc
} }
allo2Trans allo2Trans :: FlatAllocation Rational -> Entry AcntID (LinkDeferred Rational) TagID
:: (MonadInsertError m, MonadFinance m) allo2Trans FlatAllocation {faValue, faTo = TaggedAcnt {taAcnt, taTags}, faDesc} =
=> CommitR Entry
-> T.Text { eValue = LinkDeferred (EntryValue TFixed faValue)
-> Day , eComment = faDesc
-> TaggedAcnt , eAcnt = taAcnt
-> FlatAllocation Rational , eTags = taTags
-> m (Tx CommitR)
allo2Trans meta name day from FlatAllocation {faValue, faTo, faDesc, faCur} = do
-- TODO double here?
p <- entryPair from faTo faCur faDesc (fromRational faValue) ()
return
Tx
{ txCommit = meta
, txDate = day
, txPrimary = Left p
, txOther = []
, txDescr = faDesc
, txBudget = name
} }
allocatePre allocatePre
@ -414,6 +410,5 @@ data FlatAllocation v = FlatAllocation
{ faValue :: !v { faValue :: !v
, faDesc :: !T.Text , faDesc :: !T.Text
, faTo :: !TaggedAcnt , faTo :: !TaggedAcnt
, faCur :: !CurID
} }
deriving (Functor, Show) deriving (Functor, Show)

View File

@ -447,6 +447,7 @@ readUpdates hashes = do
( entrysets ^. EntrySetRId ( entrysets ^. EntrySetRId
, txs ^. TransactionRDate , txs ^. TransactionRDate
, txs ^. TransactionRBudgetName , txs ^. TransactionRBudgetName
, txs ^. TransactionRPriority
, ,
( entrysets ^. EntrySetRCurrency ( entrysets ^. EntrySetRCurrency
, currencies ^. CurrencyRPrecision , currencies ^. CurrencyRPrecision
@ -456,10 +457,10 @@ readUpdates hashes = do
) )
) )
let (toUpdate, toRead) = L.partition (E.unValue . fst) xs let (toUpdate, toRead) = L.partition (E.unValue . fst) xs
toUpdate' <- liftExcept $ mapErrors makeUES $ groupKey (\(i, _, _, _) -> i) (snd <$> toUpdate) toUpdate' <- liftExcept $ mapErrors makeUES $ groupKey (\(i, _, _, _, _) -> i) (snd <$> toUpdate)
return (makeRE . snd <$> toRead, toUpdate') return (makeRE . snd <$> toRead, toUpdate')
where where
makeUES ((_, day, name, (curID, prec)), es) = do makeUES ((_, day, name, pri, (curID, prec)), es) = do
let res = let res =
bimap NE.nonEmpty NE.nonEmpty $ bimap NE.nonEmpty NE.nonEmpty $
NE.partition ((< 0) . entryRIndex . snd) $ NE.partition ((< 0) . entryRIndex . snd) $
@ -485,6 +486,7 @@ readUpdates hashes = do
, utToUnk = toUnk , utToUnk = toUnk
, utTotalValue = tot , utTotalValue = tot
, utBudget = E.unValue name , utBudget = E.unValue name
, utPriority = E.unValue pri
} }
Right x -> Right x ->
Right $ Right $
@ -499,9 +501,10 @@ readUpdates hashes = do
, utToUnk = toUnk , utToUnk = toUnk
, utTotalValue = () , utTotalValue = ()
, utBudget = E.unValue name , utBudget = E.unValue name
, utPriority = E.unValue pri
} }
_ -> throwError undefined _ -> throwError undefined
makeRE ((_, day, name, (curID, _)), entry) = makeRE ((_, day, name, pri, (curID, _)), entry) =
let e = entityVal entry let e = entityVal entry
in ReadEntry in ReadEntry
{ reDate = E.unValue day { reDate = E.unValue day
@ -509,6 +512,7 @@ readUpdates hashes = do
, reAcnt = entryRAccount e , reAcnt = entryRAccount e
, reValue = entryRValue e , reValue = entryRValue e
, reBudget = E.unValue name , reBudget = E.unValue name
, rePriority = E.unValue pri
} }
splitFrom splitFrom
@ -660,8 +664,8 @@ insertAll ebs = do
-- getCommit (BudgetCommit c _) = c -- getCommit (BudgetCommit c _) = c
insertTx :: MonadSqlQuery m => CommitRId -> InsertTx -> m () insertTx :: MonadSqlQuery m => CommitRId -> InsertTx -> m ()
insertTx c InsertTx {itxDate, itxDescr, itxEntrySets, itxBudget} = do insertTx c InsertTx {itxDate, itxDescr, itxEntrySets, itxBudget, itxPriority} = do
k <- insert $ TransactionR c itxDate itxDescr itxBudget k <- insert $ TransactionR c itxDate itxDescr itxBudget itxPriority
mapM_ (uncurry (insertEntrySet k)) $ zip [0 ..] (NE.toList itxEntrySets) mapM_ (uncurry (insertEntrySet k)) $ zip [0 ..] (NE.toList itxEntrySets)
where where
insertEntrySet tk i InsertEntrySet {iesCurrency, iesFromEntries, iesToEntries} = do insertEntrySet tk i InsertEntrySet {iesCurrency, iesFromEntries, iesToEntries} = do

View File

@ -267,7 +267,7 @@ matchNonDates ms = go ([], [], initZipper ms)
matches :: MonadFinance m => MatchRe -> TxRecord -> InsertExceptT m (MatchRes (Tx ())) matches :: MonadFinance m => MatchRe -> TxRecord -> InsertExceptT m (MatchRes (Tx ()))
matches matches
StatementParser {spTx, spOther, spVal, spDate, spDesc} StatementParser {spTx, spOther, spVal, spDate, spDesc, spPriority}
r@TxRecord {trDate, trAmount, trDesc, trOther} = do r@TxRecord {trDate, trAmount, trDesc, trOther} = do
res <- liftInner $ res <- liftInner $
combineError3 val other desc $ combineError3 val other desc $
@ -280,10 +280,11 @@ matches
date = maybe True (`dateMatches` trDate) spDate date = maybe True (`dateMatches` trDate) spDate
other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True spOther other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True spOther
desc = maybe (return True) (matchMaybe trDesc . snd) spDesc desc = maybe (return True) (matchMaybe trDesc . snd) spDesc
convert tg = MatchPass <$> toTx tg r convert tg = MatchPass <$> toTx (fromIntegral spPriority) tg r
toTx :: MonadFinance m => TxGetter -> TxRecord -> InsertExceptT m (Tx ()) toTx :: MonadFinance m => Int -> TxGetter -> TxRecord -> InsertExceptT m (Tx ())
toTx toTx
priority
TxGetter TxGetter
{ tgFrom { tgFrom
, tgTo , tgTo
@ -305,8 +306,9 @@ toTx
, esFrom = f , esFrom = f
, esTo = t , esTo = t
} }
, txOther = fmap Left ss , txOther = Left <$> ss
, txBudget = historyName , txBudget = historyName
, txPriority = priority
} }
where where
curRes = do curRes = do

View File

@ -45,6 +45,7 @@ TransactionR sql=transactions
date Day date Day
description T.Text description T.Text
budgetName T.Text budgetName T.Text
priority Int
deriving Show Eq deriving Show Eq
EntrySetR sql=entry_sets EntrySetR sql=entry_sets
transaction TransactionRId OnDeleteCascade transaction TransactionRId OnDeleteCascade

View File

@ -243,6 +243,7 @@ data Income = Income
, incFrom :: TaggedAcnt , incFrom :: TaggedAcnt
, incToBal :: TaggedAcnt , incToBal :: TaggedAcnt
, incPayPeriod :: !Period , incPayPeriod :: !Period
, incPriority :: !Int
} }
deriving instance Hashable HourlyPeriod deriving instance Hashable HourlyPeriod

View File

@ -76,6 +76,7 @@ data ReadEntry = ReadEntry
, reAcnt :: !AccountRId , reAcnt :: !AccountRId
, reValue :: !Rational , reValue :: !Rational
, reDate :: !Day , reDate :: !Day
, rePriority :: !Int
, reBudget :: !T.Text , reBudget :: !T.Text
} }
deriving (Show) deriving (Show)
@ -124,6 +125,7 @@ data UpdateEntrySet f t = UpdateEntrySet
, utDate :: !Day , utDate :: !Day
, utTotalValue :: !t , utTotalValue :: !t
, utBudget :: !T.Text , utBudget :: !T.Text
, utPriority :: !Int
} }
deriving (Show) deriving (Show)
@ -245,6 +247,7 @@ data TxCommit = HistoryCommit CommitR | BudgetCommit CommitR T.Text
data Tx k = Tx data Tx k = Tx
{ txDescr :: !T.Text { txDescr :: !T.Text
, txDate :: !Day , txDate :: !Day
, txPriority :: !Int
, txPrimary :: !(Either PrimaryEntrySet TransferEntrySet) , txPrimary :: !(Either PrimaryEntrySet TransferEntrySet)
, txOther :: ![Either SecondayEntrySet ShadowEntrySet] , txOther :: ![Either SecondayEntrySet ShadowEntrySet]
, txCommit :: !k , txCommit :: !k
@ -266,6 +269,7 @@ data InsertEntrySet = InsertEntrySet
data InsertTx = InsertTx data InsertTx = InsertTx
{ itxDescr :: !T.Text { itxDescr :: !T.Text
, itxDate :: !Day , itxDate :: !Day
, itxPriority :: !Int
, itxEntrySets :: !(NonEmpty InsertEntrySet) , itxEntrySets :: !(NonEmpty InsertEntrySet)
, itxCommit :: !CommitR , itxCommit :: !CommitR
, itxBudget :: !T.Text , itxBudget :: !T.Text

View File

@ -692,7 +692,7 @@ balanceTxs ebs =
go (ToRead ReadEntry {reCurrency, reAcnt, reValue, reBudget}) = do go (ToRead ReadEntry {reCurrency, reAcnt, reValue, reBudget}) = do
modify $ mapAdd_ (reAcnt, (reCurrency, reBudget)) reValue modify $ mapAdd_ (reAcnt, (reCurrency, reBudget)) reValue
return Nothing return Nothing
go (ToInsert Tx {txPrimary, txOther, txDescr, txCommit, txDate, txBudget}) = do go (ToInsert Tx {txPrimary, txOther, txDescr, txCommit, txDate, txBudget, txPriority}) = do
e <- either (balancePrimaryEntrySet txBudget) (balanceSecondaryEntrySet txBudget) txPrimary e <- either (balancePrimaryEntrySet txBudget) (balanceSecondaryEntrySet txBudget) txPrimary
let tot = sum $ fmap (eValue . ieEntry) $ NE.toList $ iesFromEntries e let tot = sum $ fmap (eValue . ieEntry) $ NE.toList $ iesFromEntries e
es <- mapErrors (either (balanceSecondaryEntrySet txBudget) (balancePrimaryEntrySet txBudget . fromShadow tot)) txOther es <- mapErrors (either (balanceSecondaryEntrySet txBudget) (balancePrimaryEntrySet txBudget . fromShadow tot)) txOther
@ -704,15 +704,17 @@ balanceTxs ebs =
, itxEntrySets = e :| es , itxEntrySets = e :| es
, itxCommit = txCommit , itxCommit = txCommit
, itxBudget = txBudget , itxBudget = txBudget
, itxPriority = txPriority
} }
return $ Just $ Right tx return $ Just $ Right tx
fromShadow tot e@EntrySet {esTotalValue} = e {esTotalValue = tot * toRational esTotalValue} fromShadow tot e@EntrySet {esTotalValue} = e {esTotalValue = tot * toRational esTotalValue}
binDate :: EntryBin -> Day binDate :: EntryBin -> (Day, Int)
binDate (ToUpdate (Right UpdateEntrySet {utDate})) = utDate binDate (ToRead ReadEntry {reDate, rePriority}) = (reDate, rePriority)
binDate (ToUpdate (Left UpdateEntrySet {utDate})) = utDate binDate (ToInsert Tx {txDate, txPriority}) = (txDate, txPriority)
binDate (ToRead ReadEntry {reDate}) = reDate binDate (ToUpdate u) = either go go u
binDate (ToInsert Tx {txDate}) = txDate where
go UpdateEntrySet {utDate, utPriority} = (utDate, utPriority)
type BCKey = (CurrencyRId, Text) type BCKey = (CurrencyRId, Text)
@ -1044,6 +1046,7 @@ expandTransfer tc name bounds Transfer {transAmounts, transTo, transCurrency, tr
{ amtWhen = pat { amtWhen = pat
, amtValue = TransferValue {tvVal = v, tvType = t} , amtValue = TransferValue {tvVal = v, tvType = t}
, amtDesc = desc , amtDesc = desc
, amtPriority = pri
} = } =
withDates bounds pat $ \day -> do withDates bounds pat $ \day -> do
p <- entryPair transFrom transTo transCurrency desc () (EntryValue t (toRational (-v))) p <- entryPair transFrom transTo transCurrency desc () (EntryValue t (toRational (-v)))
@ -1055,6 +1058,7 @@ expandTransfer tc name bounds Transfer {transAmounts, transTo, transCurrency, tr
, txOther = [] , txOther = []
, txDescr = desc , txDescr = desc
, txBudget = name , txBudget = name
, txPriority = fromIntegral pri
} }
entryPair entryPair