packages feed

tax-ato-2022.1: src/Data/Tax/ATO/Common.hs

-- This file is part of hs-tax-ato
-- Copyright (C) 2018  Fraser Tweedale
--
-- hs-tax-ato is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

{-|

Common taxes and helpers.

-}

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}

module Data.Tax.ATO.Common
  (
  -- * Tax tables
    TaxTables(..)

  -- * Classes
  , HasIncome(..)

  -- * Common taxes and helpers
  , medicareLevy
  , medicareLevySurcharge
  , lowIncomeTaxOffset
  , lowIncomeTaxOffset2021
  , lamito
  , corporateTax

  -- * Convenience functions
  , thresholds'
  , marginal'
  ) where

import Control.Lens (Getter, review, to, view)
import Data.Bifunctor (first)

import Data.Tax
import Data.Tax.ATO.PrivateHealthInsuranceRebate

-- | A set of tax tables for a particular financial year
data TaxTables y a = TaxTables
  { ttIndividualIncomeTax :: Tax (Money a) (Money a)
  , ttMedicareLevy :: Tax (Money a) (Money a)
  , ttMedicareLevySurcharge :: Tax (Money a) (Money a)
  , ttHelp :: Tax (Money a) (Money a)
  , ttSfss :: Tax (Money a) (Money a)
  , ttAdditional :: Tax (Money a) (Money a)
  -- ^ Additional taxes and offsets that apply at EOY
  , ttPHIRebateRates :: PrivateHealthInsuranceRebateRates a
  }

-- | The Medicare levy, incorporating the Medicare levy reduction.
-- The rate is 10% of the income above the given shade-in threshold
-- or 2% of the total income, whichever is less.
--
medicareLevy :: (Fractional a, Ord a) => Money a -> Tax (Money a) (Money a)
medicareLevy l = lesserOf (above l 0.1) (flat 0.02)

-- | /Medicare levy surcharge (MLS)/.  Certain exemptions are available.
--
-- __Known issues__: the MLS is levied on taxable income + fringe
-- benefits, but this is not implemented properly yet.  The
-- thresholds are affected by family income and number of
-- dependents; this also is not implemented.
medicareLevySurcharge :: (Fractional a, Ord a) => Tax (Money a) (Money a)
medicareLevySurcharge =
  threshold (review money 90000) 0.01
  <> threshold (review money 105000) 0.0025
  <> threshold (review money 140000) 0.0025

-- | /Low income tax offset (LITO)/.  $445, reduced by 1.5c for
-- every dollar earned over $37,000. The lump amount may change in
-- the future.
lowIncomeTaxOffset :: (Fractional a, Ord a) => Tax (Money a) (Money a)
lowIncomeTaxOffset =
  limit mempty
  (lump (review money (-445)) <> above (review money 37000) 0.015)

-- | /Low income tax offset/, 2020–21 version.
lowIncomeTaxOffset2021 :: (Fractional a, Ord a) => Tax (Money a) (Money a)
lowIncomeTaxOffset2021 =
  limit mempty $
    lump (review money (-700))
    <> above (review money 37500) 0.05
    <> above (review money 45000) (-0.035)

-- | Low and middle income tax offset. FY2019, 2020, 2021.
--
lamito :: (Fractional a, Ord a) => Tax (Money a) (Money a)
lamito = limit mempty $
  greaterOf (lump (review money (-1080)))
    ( lump (review money (-255))
    <> above (review money 37000) (-0.075) )
  <> above (review money 90000) 0.03

-- | The corporate tax rate of 30%.  In the future, different rates may
-- be levied depending on business turnover/income.
corporateTax :: (Fractional a) => Tax (Money a) (Money a)
corporateTax = flat 0.3

thresholds', marginal'
  :: (Fractional a, Ord a) => [(a, a)] -> Tax (Money a) (Money a)
thresholds' = thresholds . fmap (first (review money))
-- ^ Convenience wrapper for 'thresholds'.  Turns the thresholds into 'Money'
marginal' = marginal . fmap (first (review money))
-- ^ Convenience wrapper for 'marginal'.  Turns the margins into 'Money'


-- | Types that have an income value.
class HasIncome a b c where
  income :: Getter (a b) (Money c)

instance (Foldable t, HasIncome x a a, Num a) => HasIncome t (x a) a where
  income = to (foldMap (view income))