packages feed

tax-ato-2026.1: src/Data/Tax/ATO/BPI.hs

-- This file is part of hs-tax-ato
-- Copyright (C) 2026  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/>.

{-|

Business and professional items (BPI) schedule

Only Personal services income (PSI) and deductions are implemented at the
current time.

-}

module Data.Tax.ATO.BPI
  ( BusinessAndProfessionalItemsSchedule
  , newBusinessAndProfessionalItemsSchedule
  , personalServicesIncome

  , PersonalServicesIncome
  , newPersonalServicesIncome
  , psiVoluntaryAgreement
  , psiWhereABNNotQuoted
  , psiLabourHireOrOtherSpecifiedPayments
  , psiOther
  , psiDeductionsForPaymentsToAssociatesForPrincipalWork
  , psiTotalAmountOfOtherDeductions
  , netPSI
  ) where

import Control.Lens
import Data.Tax

import Data.Tax.ATO.Common

-- | Business and professional items (BPI) schedule
data BusinessAndProfessionalItemsSchedule a = BusinessAndProfessionalItemsSchedule
  { _personalServicesIncome :: PersonalServicesIncome a
  }

newBusinessAndProfessionalItemsSchedule :: (Num a) => BusinessAndProfessionalItemsSchedule a
newBusinessAndProfessionalItemsSchedule = BusinessAndProfessionalItemsSchedule
  { _personalServicesIncome = newPersonalServicesIncome
  }

instance (Num a, Ord a)
    => HasTaxableIncome BusinessAndProfessionalItemsSchedule a a where
  taxableIncome = personalServicesIncome . taxableIncome

-- | __P1__ Personal services income (PSI)
personalServicesIncome
  :: Lens' (BusinessAndProfessionalItemsSchedule a) (PersonalServicesIncome a)
personalServicesIncome =
  lens _personalServicesIncome (\s b -> s { _personalServicesIncome = b })


data PersonalServicesIncome a = PersonalServicesIncome
  { _psiVoluntaryAgreement :: Money a
  , _psiWhereABNNotQuoted :: Money a
  , _psiLabourHireOrOtherSpecifiedPayments :: Money a
  , _psiOther :: Money a
  , _psiDeductionsForPaymentsToAssociatesForPrincipalWork :: Money a
  , _psiTotalAmountOfOtherDeductions :: Money a
  }

newPersonalServicesIncome :: (Num a) => PersonalServicesIncome a
newPersonalServicesIncome = PersonalServicesIncome
  { _psiVoluntaryAgreement = mempty
  , _psiWhereABNNotQuoted = mempty
  , _psiLabourHireOrOtherSpecifiedPayments = mempty
  , _psiOther = mempty
  , _psiDeductionsForPaymentsToAssociatesForPrincipalWork = mempty
  , _psiTotalAmountOfOtherDeductions = mempty
  }

-- | __M__ PSI — voluntary agreement
psiVoluntaryAgreement :: Lens' (PersonalServicesIncome a) (Money a)
psiVoluntaryAgreement =
  lens _psiVoluntaryAgreement (\s b -> s { _psiVoluntaryAgreement = b })

-- | __N__ PSI — where Australian business number not quoted
psiWhereABNNotQuoted :: Lens' (PersonalServicesIncome a) (Money a)
psiWhereABNNotQuoted =
  lens _psiWhereABNNotQuoted (\s b -> s { _psiWhereABNNotQuoted = b })

-- | __O__ PSI — labour hire or other specified payments
psiLabourHireOrOtherSpecifiedPayments :: Lens' (PersonalServicesIncome a) (Money a)
psiLabourHireOrOtherSpecifiedPayments =
  lens _psiLabourHireOrOtherSpecifiedPayments (\s b -> s { _psiLabourHireOrOtherSpecifiedPayments = b })

-- | __J__ PSI — other
psiOther :: Lens' (PersonalServicesIncome a) (Money a)
psiOther =
  lens _psiOther (\s b -> s { _psiOther = b })

-- | __K__ Deductions for payments to associates for principal work
psiDeductionsForPaymentsToAssociatesForPrincipalWork
  :: Lens' (PersonalServicesIncome a) (Money a)
psiDeductionsForPaymentsToAssociatesForPrincipalWork =
  lens _psiDeductionsForPaymentsToAssociatesForPrincipalWork
    (\s b -> s { _psiDeductionsForPaymentsToAssociatesForPrincipalWork = b })

-- | __L__ Total amount of other deductions against PSI
psiTotalAmountOfOtherDeductions :: Lens' (PersonalServicesIncome a) (Money a)
psiTotalAmountOfOtherDeductions =
  lens _psiTotalAmountOfOtherDeductions (\s b -> s { _psiTotalAmountOfOtherDeductions = b })


instance (Num a, Ord a) => HasTaxableIncome PersonalServicesIncome a a where
  taxableIncome = to (max mempty . netPSI)

-- | Net PSI
netPSI :: (Num a) => PersonalServicesIncome a -> Money a
netPSI a =
  view psiVoluntaryAgreement a
  $+$ view psiWhereABNNotQuoted a
  $+$ view psiLabourHireOrOtherSpecifiedPayments a
  $+$ view psiOther a
  $-$ view psiDeductionsForPaymentsToAssociatesForPrincipalWork a
  $-$ view psiTotalAmountOfOtherDeductions a