tax-ato 2023.1 → 2023.2
raw patch · 3 files changed
+254/−20 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Tax.ATO: costOfManagingTaxAffairs :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: data Deductions a
+ Data.Tax.ATO: deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: deductionForProjectPool :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: dividendDeductions :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: foreignIncomeDeductions :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: forestryManagedInvestmentSchemeDeduction :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: giftsOrDonations :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: instance GHC.Num.Num a => GHC.Base.Monoid (Data.Tax.ATO.Deductions a)
+ Data.Tax.ATO: instance GHC.Num.Num a => GHC.Base.Semigroup (Data.Tax.ATO.Deductions a)
+ Data.Tax.ATO: interestDeductions :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: lowValuePoolDeduction :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: otherDeductions :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: otherWorkRelatedExpenses :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: paygInstalments :: Lens' (Offsets a) (Money a)
+ Data.Tax.ATO: personalSuperannuationContributions :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: totalDeductions :: (Num a, Ord a) => Deductions a -> Money a
+ Data.Tax.ATO: workRelatedCarExpenses :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: workRelatedClothingLaundryAndDryCleaningExpenses :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: workRelatedSelfEducationExpenses :: Lens' (Deductions a) (Money a)
+ Data.Tax.ATO: workRelatedTravelExpenses :: Lens' (Deductions a) (Money a)
- Data.Tax.ATO: deductions :: Lens' (TaxReturnInfo y a) (Money a)
+ Data.Tax.ATO: deductions :: Lens' (TaxReturnInfo y a) (Deductions a)
Files
- CHANGELOG.md +16/−0
- src/Data/Tax/ATO.hs +236/−19
- tax-ato.cabal +2/−1
+ CHANGELOG.md view
@@ -0,0 +1,16 @@+## Version 2023.2++- Add support for PAYG Instalments, which are specified in aggregate+ as a refundable tax offset via the `paygInstalments` field.++- Add the `Deductions` type, which expresses the various deduction+ types, as well as the (aggregate) amount of deductions related to+ foreign income.++- Implement the Foreign Income Tax Offset Limit. The limit will be+ calculated and the `foreignTaxOffset` field in the `TaxReturnInfo`+ will be clamped to it.++## Older versions++See Git commit history
src/Data/Tax/ATO.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PolyKinds #-}+ -- This file is part of hs-tax-ato -- Copyright (C) 2018-2021 Fraser Tweedale --@@ -96,13 +98,32 @@ , dependentChildren -- ** Deductions+ , Deductions+ , deductions+ , totalDeductions+ , workRelatedCarExpenses+ , workRelatedTravelExpenses+ , workRelatedClothingLaundryAndDryCleaningExpenses+ , workRelatedSelfEducationExpenses+ , otherWorkRelatedExpenses+ , lowValuePoolDeduction+ , interestDeductions+ , dividendDeductions+ , giftsOrDonations+ , costOfManagingTaxAffairs+ , deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity+ , personalSuperannuationContributions+ , deductionForProjectPool+ , forestryManagedInvestmentSchemeDeduction+ , otherDeductions+ , foreignIncomeDeductions -- ** Tax offsets- , deductions , Offsets , offsets , spouseContributionOffset , foreignTaxOffset+ , paygInstalments -- ** Assessing tax , TaxAssessment@@ -128,7 +149,7 @@ , module Data.Tax.ATO.Rounding ) where -import Control.Lens (Getter, Lens', foldOf, lens, to, view)+import Control.Lens (Getter, Lens', (&), foldOf, lens, set, to, view, views) import Data.Tax import Data.Tax.ATO.CGT@@ -234,7 +255,7 @@ , _ess :: ESSStatement a , _foreignIncome :: Money a , _cgtEvents :: [CGTEvent a]- , _deductions :: Money a+ , _deductions :: Deductions a , _offsets :: Offsets a , _triCapitalLossCarryForward :: Money a , _phi :: [PrivateHealthInsurancePolicyDetail a]@@ -310,7 +331,7 @@ cgtEvents :: Lens' (TaxReturnInfo y a) [CGTEvent a] cgtEvents = lens _cgtEvents (\s b -> s { _cgtEvents = b }) -deductions :: Lens' (TaxReturnInfo y a) (Money a)+deductions :: Lens' (TaxReturnInfo y a) (Deductions a) deductions = lens _deductions (\s b -> s { _deductions = b }) offsets :: Lens' (TaxReturnInfo y a) (Offsets a)@@ -377,18 +398,24 @@ -- | Consolidated individual tax rate incorporating -- HELP and SFSS repayments--- (if applicable) and automatic offsets (e.g. LITO).+-- (if applicable) and automatic offsets (e.g. LITO, LMITO). individualTax :: (Fractional a, Ord a) => TaxTables y a+ -> Tax (Money a) (Money a)+individualTax table =+ greaterOf mempty (ttIndividualIncomeTax table <> ttAdditional table)++-- | Tax to calculate compulsory study and training loan repayments+-- (e.g. HELP, SFSS)+studyAndTrainingLoanRepayment+ :: (Fractional a, Ord a)+ => TaxTables y a -> TaxReturnInfo y a -> Tax (Money a) (Money a)-individualTax table info =- greaterOf mempty $- ttIndividualIncomeTax table- <> limit (view helpBalance info) (ttHelp table)- <> limit (view sfssBalance info) (ttSfss table)- <> ttAdditional table+studyAndTrainingLoanRepayment table info =+ limit (view helpBalance info) (ttHelp table)+ <> limit (view sfssBalance info) (ttSfss table) -- | Medicare levy + surcharge medicareLevyTax@@ -422,7 +449,7 @@ , view foreignIncome info ] in- wholeDollars (gross $-$ view deductions info)+ wholeDollars (gross $-$ views deductions totalDeductions info) instance (Num a) => HasTaxWithheld (TaxReturnInfo y) a a where taxWithheld = to $ \info ->@@ -438,7 +465,9 @@ cg = assessCGTEvents (view capitalLossCarryForward info) (view cgtEvents info) taxable = view income info- due = getTax (individualTax tables info) taxable+ due = getTax (individualTax tables) taxable+ studyRepayment = getTax (studyAndTrainingLoanRepayment tables info) taxable+ mlAndMLS = getTax (medicareLevyTax tables info) taxable incomeForSurchargePurposes = taxable@@ -456,17 +485,34 @@ (ttPHIRebateRates tables) (view privateHealthInsurancePolicyDetails info) + foreignIncomeTaxOffsetLimit =+ let+ step1 = due <> mlAndMLS+ step2 =+ let+ info' = info & set foreignIncome mempty+ taxable' = view income info' <> view (deductions . foreignIncomeDeductions) info'+ due' = getTax (individualTax tables) taxable'+ mlAndMLS' = getTax (medicareLevyTax tables info') taxable'+ in+ due' <> mlAndMLS'+ step3 = step1 $-$ step2+ in+ max (Money 1000) step3+ frankingCredit = wholeDollars $ foldMap dividendFrankingCredit (view dividends info) off = view (offsets . spouseContributionOffset) info- <> view (offsets . foreignTaxOffset) info+ <> min (view (offsets . foreignTaxOffset) info) foreignIncomeTaxOffsetLimit+ <> view (offsets . paygInstalments) info+ in TaxAssessment taxable- due- (getTax (medicareLevyTax tables info) taxable)+ (due <> studyRepayment)+ mlAndMLS (view taxWithheld info) (frankingCredit <> off) cg@@ -475,7 +521,7 @@ -- | Australian Business Number type ABN = String --- | PAYG payment summary+-- | PAYG payment summary - individual non-business data PaymentSummary a = PaymentSummary { summaryABN :: ABN , summaryGross :: Money a@@ -531,16 +577,28 @@ <> view taxWithheld d -- | Tax offsets that individuals can claim+--+-- The following lenses are available:+--+-- +---------------------------------------+----------------------------------++-- | 'spouseContributionOffset' | Spouse super contribution |+-- +---------------------------------------+----------------------------------++-- | 'foreignTaxOffset' | Foreign income tax offset |+-- +---------------------------------------+----------------------------------++-- | 'paygInstalments' | PAYG Instalments |+-- +---------------------------------------+----------------------------------++-- data Offsets a = Offsets { _spouseOffset :: Money a , _foreignTaxOffset :: Money a+ , _paygInstalments :: Money a } instance Num a => Semigroup (Offsets a) where- Offsets a b <> Offsets a' b' = Offsets (a <> a') (b <> b')+ Offsets a b c <> Offsets a' b' c' = Offsets (a <> a') (b <> b') (c <> c') instance Num a => Monoid (Offsets a) where- mempty = Offsets mempty mempty+ mempty = Offsets mempty mempty mempty mappend = (<>) -- | Spouse contribution offset. Maximum of /$540/ (not enforced).@@ -550,6 +608,165 @@ -- | Offset for tax paid on foreign income. foreignTaxOffset :: Lens' (Offsets a) (Money a) foreignTaxOffset = lens _foreignTaxOffset (\s b -> s { _foreignTaxOffset = b })++paygInstalments :: Lens' (Offsets a) (Money a)+paygInstalments = lens _paygInstalments (\s b -> s { _paygInstalments = b })+++-- | Deductions that individuals can claim.+--+-- The only "special case" field is 'foreignIncomeDeductions', which is+-- the aggregate amount of /other/ deductions that pertains to foreign+-- income. It is used only for calculating the Foreign Income Tax Offset+-- Limit.+--+data Deductions a = Deductions+ { _workRelatedCarExpenses :: Money a+ , _workRelatedTravelExpenses :: Money a+ , _workRelatedClothingLaundryAndDryCleaningExpenses :: Money a+ , _workRelatedSelfEducationExpenses :: Money a+ , _otherWorkRelatedExpenses :: Money a+ , _lowValuePoolDeduction :: Money a+ , _interestDeductions :: Money a+ , _dividendDeductions :: Money a+ , _giftsOrDonations :: Money a+ , _costOfManagingTaxAffairs :: Money a+ , _deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity :: Money a+ , _personalSuperannuationContributions :: Money a+ , _deductionForProjectPool :: Money a+ , _forestryManagedInvestmentSchemeDeduction :: Money a+ , _otherDeductions :: Money a+ , _foreignIncomeDeductions :: Money a+ }++instance Num a => Semigroup (Deductions a) where+ Deductions a b c d e f g h i j k l m n o p+ <> Deductions a' b' c' d' e' f' g' h' i' j' k' l' m' n' o' p'+ = Deductions (a <> a') (b <> b') (c <> c') (d <> d') (e <> e') (f <> f') (g <> g') (h <> h')+ (i <> i') (j <> j') (k <> k') (l <> l') (m <> m') (n <> n') (o <> o') (p <> p')++instance Num a => Monoid (Deductions a) where+ mempty = Deductions mempty mempty mempty mempty mempty mempty mempty mempty+ mempty mempty mempty mempty mempty mempty mempty mempty++-- | Sum the deductions. Negative components are ignored.+totalDeductions :: (Num a, Ord a) => Deductions a -> Money a+totalDeductions a =+ foldMap (max mempty)+ [ view workRelatedCarExpenses a+ , view workRelatedTravelExpenses a+ , view workRelatedClothingLaundryAndDryCleaningExpenses a+ , view workRelatedSelfEducationExpenses a+ , view otherWorkRelatedExpenses a+ , view lowValuePoolDeduction a+ , view interestDeductions a+ , view dividendDeductions a+ , view giftsOrDonations a+ , view costOfManagingTaxAffairs a+ , view deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity a+ , view personalSuperannuationContributions a+ , view deductionForProjectPool a+ , view forestryManagedInvestmentSchemeDeduction a+ , view otherDeductions a+ ]++-- | __D1__ Work-related car expenses+workRelatedCarExpenses :: Lens' (Deductions a) (Money a)+workRelatedCarExpenses =+ lens _workRelatedCarExpenses (\s b -> s { _workRelatedCarExpenses = b })++-- | __D2__ Work-related travel expenses+workRelatedTravelExpenses :: Lens' (Deductions a) (Money a)+workRelatedTravelExpenses =+ lens _workRelatedTravelExpenses (\s b -> s { _workRelatedTravelExpenses = b })++-- | __D3__ Work-related clothing, laundry and dry-cleaning expenses+workRelatedClothingLaundryAndDryCleaningExpenses :: Lens' (Deductions a) (Money a)+workRelatedClothingLaundryAndDryCleaningExpenses =+ lens+ _workRelatedClothingLaundryAndDryCleaningExpenses+ (\s b -> s { _workRelatedClothingLaundryAndDryCleaningExpenses = b })++-- | __D4__ Work-related self-education expenses+workRelatedSelfEducationExpenses :: Lens' (Deductions a) (Money a)+workRelatedSelfEducationExpenses =+ lens _workRelatedSelfEducationExpenses (\s b -> s { _workRelatedSelfEducationExpenses = b })++-- | __D5__ Other work-related expenses+otherWorkRelatedExpenses :: Lens' (Deductions a) (Money a)+otherWorkRelatedExpenses =+ lens _otherWorkRelatedExpenses (\s b -> s { _otherWorkRelatedExpenses = b })++-- | __D6__ Low-value pool deduction+lowValuePoolDeduction :: Lens' (Deductions a) (Money a)+lowValuePoolDeduction =+ lens _lowValuePoolDeduction (\s b -> s { _lowValuePoolDeduction = b })++-- | __D7__ Interest deductions+interestDeductions :: Lens' (Deductions a) (Money a)+interestDeductions =+ lens _interestDeductions (\s b -> s { _interestDeductions = b })++-- | __D8__ Dividend deductions+dividendDeductions :: Lens' (Deductions a) (Money a)+dividendDeductions =+ lens _dividendDeductions (\s b -> s { _dividendDeductions = b })++-- | __D9__ Gifts or donations+giftsOrDonations :: Lens' (Deductions a) (Money a)+giftsOrDonations =+ lens _giftsOrDonations (\s b -> s { _giftsOrDonations = b })++-- | __D10__ Cost of managing tax affairs+costOfManagingTaxAffairs :: Lens' (Deductions a) (Money a)+costOfManagingTaxAffairs =+ lens _costOfManagingTaxAffairs (\s b -> s { _costOfManagingTaxAffairs = b })++-- | __D11__ Deductible amount of undeducted purchase price of a foreign pension or annuity+deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity :: Lens' (Deductions a) (Money a)+deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity =+ lens+ _deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity + (\s b -> s { _deductibleAmountOfUndeductedPurchasePriceOfAForeignPensionOrAnnuity = b })++-- | __D12__ Personal superannuation contributions+personalSuperannuationContributions :: Lens' (Deductions a) (Money a)+personalSuperannuationContributions =+ lens _personalSuperannuationContributions (\s b -> s { _personalSuperannuationContributions = b })++-- | __D13__ Deduction for project pool+deductionForProjectPool :: Lens' (Deductions a) (Money a)+deductionForProjectPool =+ lens _deductionForProjectPool (\s b -> s { _deductionForProjectPool = b })++-- | __D14__ Forestry managed investment scheme deduction+forestryManagedInvestmentSchemeDeduction :: Lens' (Deductions a) (Money a)+forestryManagedInvestmentSchemeDeduction =+ lens+ _forestryManagedInvestmentSchemeDeduction+ (\s b -> s { _forestryManagedInvestmentSchemeDeduction = b })++-- | __D15__ Other deductions — not claimable at __D1__ to __D14__ or elsewhere+-- in your tax return+otherDeductions :: Lens' (Deductions a) (Money a)+otherDeductions =+ lens _otherDeductions (\s b -> s { _otherDeductions = b })++-- | Aggregate of deductions related to foreign income, including:+--+-- * Deductions that are reasonably related to amounts on which foreign+-- income tax has been paid+-- * Debt deductions attributable to your overseas permanent establishment+-- * Amount of the foreign loss component of one or more tax losses deducted+-- in the income year.+--+-- The components making up this amount __must be included in other fields__.+-- This field is only used in calculating the Foreign Income Tax Offset Limit.+--+foreignIncomeDeductions :: Lens' (Deductions a) (Money a)+foreignIncomeDeductions =+ lens _foreignIncomeDeductions (\s b -> s { _foreignIncomeDeductions = b })+ -- | A gross income (first argument) and amount of tax withheld (second argument) data GrossAndWithheld a = GrossAndWithheld (Money a) (Money a)
tax-ato.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: tax-ato-version: 2023.1+version: 2023.2 synopsis: Tax types and computations for Australia description: This library provides types and tax computations for tax@@ -16,6 +16,7 @@ build-type: Simple tested-with: GHC ==8.10.7 || ==9.0.2 || ==9.2.7 || ==9.4.4 || ==9.6.1 extra-source-files:+ CHANGELOG.md .hlint.yaml homepage: https://github.com/frasertweedale/hs-tax-ato