aboutsummaryrefslogtreecommitdiff
path: root/server/src/Payer.hs
blob: ab8312eba1128f9ab7726c97006aea50c02ab752 (plain)
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
76
77
78
79
80
81
82
83
84
85
86
87
module Payer
  ( getExceedingPayers
  ) where

import           Data.Map     (Map)
import qualified Data.Map     as M

import           Common.Model (ExceedingPayer (..), User (..), UserId)

data Payer = Payer
  { _payer_userId             :: UserId
  , _payer_preIncomePayments  :: Int
  , _payer_postIncomePayments :: Int
  , _payer_income             :: Int
  }

data PostPaymentPayer = PostPaymentPayer
  { _postPaymentPayer_userId            :: UserId
  , _postPaymentPayer_preIncomePayments :: Int
  , _postPaymentPayer_cumulativeIncome  :: Int
  , _postPaymentPayer_ratio             :: Float
  }

getExceedingPayers :: [User] -> Map UserId Int -> Map UserId Int -> Map UserId Int -> [ExceedingPayer]
getExceedingPayers users cumulativeIncome preIncomeRepartition postIncomeRepartition =
  let userIds = map _user_id users
      payers = getPayers userIds cumulativeIncome preIncomeRepartition postIncomeRepartition
      postPaymentPayers = map getPostPaymentPayer payers
      mbMaxRatio = safeMaximum . map _postPaymentPayer_ratio $ postPaymentPayers
  in  case mbMaxRatio of
        Just maxRatio ->
          exceedingPayersFromAmounts
            . map (\p -> (_postPaymentPayer_userId p, getFinalDiff maxRatio p))
            $ postPaymentPayers
        Nothing ->
          exceedingPayersFromAmounts
            . map (\p -> (_payer_userId p, _payer_preIncomePayments p))
            $ payers

getPayers :: [UserId] -> Map UserId Int -> Map UserId Int -> Map UserId Int -> [Payer]
getPayers userIds cumulativeIncome preIncomeRepartition postIncomeRepartition =
  flip map userIds (\userId -> Payer
    { _payer_userId = userId
    , _payer_preIncomePayments = M.findWithDefault 0 userId preIncomeRepartition
    , _payer_postIncomePayments = M.findWithDefault 0 userId postIncomeRepartition
    , _payer_income = M.findWithDefault 0 userId cumulativeIncome
    }
  )

exceedingPayersFromAmounts :: [(UserId, Int)] -> [ExceedingPayer]
exceedingPayersFromAmounts userAmounts =
  case mbMinAmount of
    Nothing ->
      []
    Just minAmount ->
      filter (\payer -> _exceedingPayer_amount payer > 0)
        . map (\userAmount ->
           ExceedingPayer
             { _exceedingPayer_userId = fst userAmount
             , _exceedingPayer_amount = snd userAmount - minAmount
             }
        )
        $ userAmounts
  where mbMinAmount = safeMinimum . map snd $ userAmounts

getPostPaymentPayer :: Payer -> PostPaymentPayer
getPostPaymentPayer payer =
  PostPaymentPayer
    { _postPaymentPayer_userId = _payer_userId payer
    , _postPaymentPayer_preIncomePayments = _payer_preIncomePayments payer
    , _postPaymentPayer_cumulativeIncome = _payer_income payer
    , _postPaymentPayer_ratio = (fromIntegral . _payer_postIncomePayments $ payer) / (fromIntegral $ _payer_income payer)
    }

getFinalDiff :: Float -> PostPaymentPayer -> Int
getFinalDiff maxRatio payer =
  let postIncomeDiff =
        truncate $ -1.0 * (maxRatio - _postPaymentPayer_ratio payer) * (fromIntegral . _postPaymentPayer_cumulativeIncome $ payer)
  in  postIncomeDiff + _postPaymentPayer_preIncomePayments payer

safeMinimum :: (Ord a) => [a] -> Maybe a
safeMinimum [] = Nothing
safeMinimum xs = Just . minimum $ xs

safeMaximum :: (Ord a) => [a] -> Maybe a
safeMaximum [] = Nothing
safeMaximum xs = Just . maximum $ xs