1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
-- copyright and license info here
module PTO where
import Import
import Data.Maybe (fromJust)
import Data.Time.Calendar (Day, fromGregorian, toGregorian, gregorianMonthLength, diffDays)
import Database.Persist.Sql (SqlBackend) -- this can't be good
accrual :: (Functor m, PersistQuery m, PersistMonadBackend m ~ SqlBackend) =>
UserId -> (Integer, Int) -> m Rational
accrual u' (y',m') = do
[e'] <- fmap (map entityVal) (selectList [EmploymentUser ==. u'] [])
return $ accrual' e' (y',m')
where
accrual' e@Employment{..} (y,m)
| employmentStartDate > endOfMonth (y,m) = 0
| fmap (\x -> x < startOfMonth (y,m)) employmentEndDate == Just True = 0
| monthOf employmentStartDate == (y,m) = percent employmentStartDate (endOfMonth (y,m)) * baseLevel * toRational employmentCommitment
| Just (y,m) == endMonth e && ((y,m) > monthOf (twoYearsFrom employmentStartDate)) = (percent (startOfMonth (y,m)) (fromJust employmentEndDate)) * afterTwo * toRational employmentCommitment -- FIXME or leave bug-compatible?
| Just (y,m) == endMonth e = (percent (startOfMonth (y,m)) (fromJust employmentEndDate)) * baseLevel * toRational employmentCommitment -- FIXME or leave bug-compatible?
| monthOf (twoYearsFrom employmentStartDate) < (y,m) = afterTwo * toRational employmentCommitment
| (y,m) == monthOf (twoYearsFrom employmentStartDate) = toRational employmentCommitment * (((percent (twoYearsFrom employmentStartDate) (endOfMonth (y,m))) * (afterTwo - baseLevel)) + baseLevel)
| otherwise = baseLevel * toRational employmentCommitment
baseLevel = 104/12
afterTwo = 12
percent s e
| monthOf s == monthOf e = toRational (daysInPeriod s e) / toRational (daysInMonth s)
| otherwise = error "this should never happen"
daysInPeriod s e = diffDays e s + 1 -- is this right or off-by-one?
daysInMonth = uncurry gregorianMonthLength . monthOf
-- deductions :: PersistQuery backend m => (Integer, Int) -> Key backend (UserGeneric backend) -> backend m [Entity (TimeEntryGeneric backend)]
deductions (year,month) user = do
let monthstart = startOfMonth (year, month)
monthend = endOfMonth (year, month)
cats <- deductibles
entries <- selectList [TimeEntryUser ==. user, TimeEntryDay >=. monthstart, TimeEntryDay <=. monthend, TimeEntryCategory <-. cats] []
return entries
-- deductibles :: PersistQuery backend m => backend m [Key backend (TimeCategoryGeneric backend)]
deductibles = do
ds <- selectList ([TimeCategoryName ==. "/admin/paid-time-off"]
||. [TimeCategoryName ==. "/admin/personal"]
||. [TimeCategoryName ==. "/admin/pto"]
||. [TimeCategoryName ==. "/admin/vacation"]) []
return $ map entityKey ds
yearlySacrifice hs = 160 - hs
-- monthlyTransactions :: UserId -> (Integer, Int) -> m (Rational, Double)
monthlyTransactions u (y,m) = do
accrue <- accrual u (y,m)
deducts <- fmap (sum . map (timeEntryHours . entityVal)) (deductions (y,m) u)
return (accrue, deducts)
startOfMonth :: (Integer, Int) -> Day
startOfMonth (year,month) = fromGregorian year month 1
endOfMonth :: (Integer, Int) -> Day
endOfMonth (year,month) = fromGregorian year month 32
startMonth :: Employment -> (Integer, Int)
startMonth = monthOf . employmentStartDate
endMonth :: Employment -> Maybe (Integer, Int)
endMonth = fmap monthOf . employmentEndDate
twoYearsFrom :: Day -> Day
twoYearsFrom = (\(y,m,d) -> fromGregorian (y+2) m d) . toGregorian
monthOf :: Day -> (Integer, Int)
monthOf = (\(x,y,_) -> (x,y)) . toGregorian
|