tax-ato 2025.1 → 2026.1
raw patch · 10 files changed
+1460/−197 lines, 10 filesdep +containersdep ~timePVP ok
version bump matches the API change (PVP)
Dependencies added: containers
Dependency ranges changed: time
API changes (from Hackage documentation)
- Data.Tax.ATO.CGT: CGTEvent :: String -> a -> Day -> Money a -> Money a -> Day -> Money a -> Money a -> Money a -> Money a -> CGTEvent a
- Data.Tax.ATO.CGT: [acquisitionCosts] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [acquisitionDate] :: CGTEvent a -> Day
- Data.Tax.ATO.CGT: [acquisitionPrice] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [assetDesc] :: CGTEvent a -> String
- Data.Tax.ATO.CGT: [capitalCosts] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [disposalCosts] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [disposalDate] :: CGTEvent a -> Day
- Data.Tax.ATO.CGT: [disposalPrice] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [ownershipCosts] :: CGTEvent a -> Money a
- Data.Tax.ATO.CGT: [units] :: CGTEvent a -> a
- Data.Tax.ATO.CGT: class HasCapitalGain (a :: k -> Type) (b :: k) c
- Data.Tax.ATO.CGT: instance (Data.Foldable.Foldable t, Data.Tax.ATO.CGT.HasCapitalGain x a a, GHC.Num.Num a) => Data.Tax.ATO.CGT.HasCapitalGain t (x a) a
- Data.Tax.ATO.CGT: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Data.Tax.ATO.CGT.HasCapitalGain Data.Tax.ATO.CGT.CGTEvent a a
- Data.Tax.ATO.Common: instance (2016 Data.Type.Ord.<= y, y Data.Type.Ord.<= 2025, Data.Tax.ATO.FY.FinancialYear y) => Data.Tax.ATO.Common.HasCentsPerKilometreMethod y
- Data.Tax.ATO.Common: instance Data.Tax.ATO.Common.HasFixedRateMethod 2023
- Data.Tax.ATO.Common: instance Data.Tax.ATO.Common.HasFixedRateMethod 2024
- Data.Tax.ATO.Common: instance Data.Tax.ATO.Common.HasFixedRateMethod 2025
+ Data.Tax.ATO: businessAndProfessionalItems :: forall (y :: Nat) a f. Functor f => (BusinessAndProfessionalItemsSchedule a -> f (BusinessAndProfessionalItemsSchedule a)) -> TaxReturnInfo y a -> f (TaxReturnInfo y a)
+ Data.Tax.ATO: capitalLossCarryForwardCollectables :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalLossCarryForward a -> f (CapitalLossCarryForward a)
+ Data.Tax.ATO: capitalLossCarryForwardOther :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalLossCarryForward a -> f (CapitalLossCarryForward a)
+ Data.Tax.ATO: data OtherIncome a
+ Data.Tax.ATO: instance GHC.Num.Num a => Data.Tax.ATO.Common.HasTaxWithheld Data.Tax.ATO.OtherIncome a a
+ Data.Tax.ATO: instance GHC.Num.Num a => Data.Tax.ATO.Common.HasTaxableIncome Data.Tax.ATO.OtherIncome a a
+ Data.Tax.ATO: otherIncome :: forall (y :: Nat) a f. Functor f => (OtherIncome a -> f (OtherIncome a)) -> TaxReturnInfo y a -> f (TaxReturnInfo y a)
+ Data.Tax.ATO: otherIncomeCategory1 :: forall a f. Functor f => ([(String, Money a)] -> f [(String, Money a)]) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: otherIncomeCategory2 :: forall a f. Functor f => ([(String, Money a)] -> f [(String, Money a)]) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: otherIncomeCategory3 :: forall a f. Functor f => (Money a -> f (Money a)) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: otherIncomeCategory4 :: forall a f. Functor f => ([(String, Money a)] -> f [(String, Money a)]) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: taxWithheldAssessableFHSSReleasedAmount :: forall a f. Functor f => (Money a -> f (Money a)) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: taxWithheldLumpSumPaymentsInArrears :: forall a f. Functor f => (Money a -> f (Money a)) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO: taxableProfessionalIncome :: forall a f. Functor f => (Money a -> f (Money a)) -> OtherIncome a -> f (OtherIncome a)
+ Data.Tax.ATO.BPI: data BusinessAndProfessionalItemsSchedule a
+ Data.Tax.ATO.BPI: data PersonalServicesIncome a
+ Data.Tax.ATO.BPI: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Data.Tax.ATO.Common.HasTaxableIncome Data.Tax.ATO.BPI.BusinessAndProfessionalItemsSchedule a a
+ Data.Tax.ATO.BPI: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Data.Tax.ATO.Common.HasTaxableIncome Data.Tax.ATO.BPI.PersonalServicesIncome a a
+ Data.Tax.ATO.BPI: netPSI :: Num a => PersonalServicesIncome a -> Money a
+ Data.Tax.ATO.BPI: newBusinessAndProfessionalItemsSchedule :: Num a => BusinessAndProfessionalItemsSchedule a
+ Data.Tax.ATO.BPI: newPersonalServicesIncome :: Num a => PersonalServicesIncome a
+ Data.Tax.ATO.BPI: personalServicesIncome :: forall a f. Functor f => (PersonalServicesIncome a -> f (PersonalServicesIncome a)) -> BusinessAndProfessionalItemsSchedule a -> f (BusinessAndProfessionalItemsSchedule a)
+ Data.Tax.ATO.BPI: psiDeductionsForPaymentsToAssociatesForPrincipalWork :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.BPI: psiLabourHireOrOtherSpecifiedPayments :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.BPI: psiOther :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.BPI: psiTotalAmountOfOtherDeductions :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.BPI: psiVoluntaryAgreement :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.BPI: psiWhereABNNotQuoted :: forall a f. Functor f => (Money a -> f (Money a)) -> PersonalServicesIncome a -> f (PersonalServicesIncome a)
+ Data.Tax.ATO.CGT: AffordableHousing :: CGTAssetFlag
+ Data.Tax.ATO.CGT: AffordableHousingDiscount :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: AssetTypeCollectable :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeOther :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypePersonalUse :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeRealEstateAU :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeRealEstateOther :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeSharesAUListed :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeSharesOther :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeUnitsAUListed :: CGTAssetType
+ Data.Tax.ATO.CGT: AssetTypeUnitsOther :: CGTAssetType
+ Data.Tax.ATO.CGT: CGTScheduleCategoryCollectable :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryDeferred :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryOther :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryRealEstateAU :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryRealEstateOther :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategorySharesAUListed :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategorySharesOther :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryTrust :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryUnitsAUListed :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CGTScheduleCategoryUnitsOther :: CGTScheduleCategory
+ Data.Tax.ATO.CGT: CapitalGainsDisregardedAsAResultOfTheSaleOfAPreCGTAsset :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: DemergerRollover :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: DisposalOrCreationOfAssetsInAWhollyOwnedCompany :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: EarlyStageInnovationCompany :: CGTAssetFlag
+ Data.Tax.ATO.CGT: EarlyStageInvestor :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ExchangeOfRightsOrOptions :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ExchangeOfSharesInOneCompanyForSharesInAnotherCompany :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ExchangeOfSharesOrUnits :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ExchangeOfUnitsInAUnitTrustForSharesInACompany :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ForeignResidentCGTExemption :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: MainResidence :: CGTAssetFlag
+ Data.Tax.ATO.CGT: MainResidenceExemption :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: OtherExemptionsAndRollovers :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ReplacementAssetRollovers :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SameAssetRollovers :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: ScripForScripRollover :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SmallBusiness15YearExemption :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SmallBusinessActiveAsset :: CGTAssetFlag
+ Data.Tax.ATO.CGT: SmallBusinessActiveAssetReduction :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SmallBusinessRestructureRollover :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SmallBusinessRetirementExemption :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: SmallBusinessRollover :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: VentureCapitalInvestment :: CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: assetAcquisitionCosts :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetAcquisitionDate :: forall a f. Functor f => (Day -> f Day) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetAcquisitionPrice :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetCapitalCosts :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetDescription :: forall a f. Functor f => (String -> f String) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetFlag :: forall a f. Functor f => (Maybe CGTAssetFlag -> f (Maybe CGTAssetFlag)) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetOwnershipCosts :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetShare :: forall a f. Functor f => (Rational -> f Rational) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetType :: forall a f. Functor f => (CGTAssetType -> f CGTAssetType) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: assetUnits :: forall a f. Functor f => (Rational -> f Rational) -> CGTAsset a -> f (CGTAsset a)
+ Data.Tax.ATO.CGT: capitalLossCarryForwardCollectables :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalLossCarryForward a -> f (CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: capitalLossCarryForwardOther :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalLossCarryForward a -> f (CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: cgtEventA1 :: CGTAsset a -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode -> Day -> Money a -> Money a -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtEventB1 :: CGTAsset a -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode -> Day -> Money a -> Money a -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtEventC1 :: CGTAsset a -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode -> Day -> Money a -> Money a -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtEventC2 :: CGTAsset a -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode -> Day -> Money a -> Money a -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtEventC3 :: Num a => CGTAsset a -> Day -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtEventE10 :: Num a => CGTAsset a -> Day -> Money a -> CGTEvent a
+ Data.Tax.ATO.CGT: cgtGainsAndLossesByCategory :: forall a f. Functor f => (Map CGTScheduleCategory (Money a, Money a) -> f (Map CGTScheduleCategory (Money a, Money a))) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: cgtTotalCGTDiscountApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: cgtTotalCurrentYearLosses :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: cgtTotalCurrentYearLossesApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: cgtTotalPriorYearLossesApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: data CGTAsset a
+ Data.Tax.ATO.CGT: data CGTAssetFlag
+ Data.Tax.ATO.CGT: data CGTAssetType
+ Data.Tax.ATO.CGT: data CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: data CGTScheduleCategory
+ Data.Tax.ATO.CGT: data CapitalGainsFromTrust a
+ Data.Tax.ATO.CGT: data CapitalLossCarryForward a
+ Data.Tax.ATO.CGT: eventAsset :: forall a f. Applicative f => (CGTAsset a -> f (CGTAsset a)) -> CGTEvent a -> f (CGTEvent a)
+ Data.Tax.ATO.CGT: eventCode :: forall a f. Functor f => (Maybe CGTExemptionRolloverOrAdditionalDiscountCode -> f (Maybe CGTExemptionRolloverOrAdditionalDiscountCode)) -> CGTEvent a -> f (CGTEvent a)
+ Data.Tax.ATO.CGT: eventCosts :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTEvent a -> f (CGTEvent a)
+ Data.Tax.ATO.CGT: eventDate :: forall a f. Functor f => (Day -> f Day) -> CGTEvent a -> f (CGTEvent a)
+ Data.Tax.ATO.CGT: eventPrice :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTEvent a -> f (CGTEvent a)
+ Data.Tax.ATO.CGT: eventProceeds :: CGTEvent Rational -> Money Rational
+ Data.Tax.ATO.CGT: instance GHC.Base.Functor Data.Tax.ATO.CGT.CGTAsset
+ Data.Tax.ATO.CGT: instance GHC.Base.Functor Data.Tax.ATO.CGT.CGTEventType
+ Data.Tax.ATO.CGT: instance GHC.Base.Functor Data.Tax.ATO.CGT.CapitalLossCarryForward
+ Data.Tax.ATO.CGT: instance GHC.Classes.Eq Data.Tax.ATO.CGT.CGTAssetFlag
+ Data.Tax.ATO.CGT: instance GHC.Classes.Eq Data.Tax.ATO.CGT.CGTAssetType
+ Data.Tax.ATO.CGT: instance GHC.Classes.Eq Data.Tax.ATO.CGT.CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: instance GHC.Classes.Eq Data.Tax.ATO.CGT.CGTScheduleCategory
+ Data.Tax.ATO.CGT: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Tax.ATO.CGT.CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: instance GHC.Classes.Ord Data.Tax.ATO.CGT.CGTAssetFlag
+ Data.Tax.ATO.CGT: instance GHC.Classes.Ord Data.Tax.ATO.CGT.CGTAssetType
+ Data.Tax.ATO.CGT: instance GHC.Classes.Ord Data.Tax.ATO.CGT.CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: instance GHC.Classes.Ord Data.Tax.ATO.CGT.CGTScheduleCategory
+ Data.Tax.ATO.CGT: instance GHC.Num.Num a => GHC.Base.Monoid (Data.Tax.ATO.CGT.CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: instance GHC.Num.Num a => GHC.Base.Semigroup (Data.Tax.ATO.CGT.CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: instance GHC.Show.Show Data.Tax.ATO.CGT.CGTAssetFlag
+ Data.Tax.ATO.CGT: instance GHC.Show.Show Data.Tax.ATO.CGT.CGTAssetType
+ Data.Tax.ATO.CGT: instance GHC.Show.Show Data.Tax.ATO.CGT.CGTExemptionRolloverOrAdditionalDiscountCode
+ Data.Tax.ATO.CGT: instance GHC.Show.Show Data.Tax.ATO.CGT.CGTScheduleCategory
+ Data.Tax.ATO.CGT: instance GHC.Show.Show Data.Tax.ATO.CGT.IsDiscountable
+ Data.Tax.ATO.CGT: instance GHC.Show.Show a => GHC.Show.Show (Data.Tax.ATO.CGT.CGTAssessment a)
+ Data.Tax.ATO.CGT: instance GHC.Show.Show a => GHC.Show.Show (Data.Tax.ATO.CGT.CGTAsset a)
+ Data.Tax.ATO.CGT: instance GHC.Show.Show a => GHC.Show.Show (Data.Tax.ATO.CGT.CGTEventType a)
+ Data.Tax.ATO.CGT: instance GHC.Show.Show a => GHC.Show.Show (Data.Tax.ATO.CGT.CapitalGainsFromTrust a)
+ Data.Tax.ATO.CGT: instance GHC.Show.Show a => GHC.Show.Show (Data.Tax.ATO.CGT.CapitalLossCarryForward a)
+ Data.Tax.ATO.CGT: newCGTAsset :: Num a => String -> Rational -> CGTAssetType -> Rational -> Day -> Money a -> Money a -> CGTAsset a
+ Data.Tax.ATO.CGT: newCapitalGainsFromTrust :: Money a -> Money a -> Money a -> Money a -> CapitalGainsFromTrust a
+ Data.Tax.ATO.CGT: newCapitalLossCarryForward :: Num a => CapitalLossCarryForward a
+ Data.Tax.ATO.CGT: prepTrustCGT :: (Fractional a, Eq a) => CapitalGainsFromTrust a -> [CGTEvent a]
+ Data.Tax.ATO.CGT: trustCGTDiscountApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalGainsFromTrust a -> f (CapitalGainsFromTrust a)
+ Data.Tax.ATO.CGT: trustCGTSmallBusinessConcessionApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalGainsFromTrust a -> f (CapitalGainsFromTrust a)
+ Data.Tax.ATO.CGT: trustCapitalLossesApplied :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalGainsFromTrust a -> f (CapitalGainsFromTrust a)
+ Data.Tax.ATO.CGT: trustGrossCapitalGain :: forall a f. Functor f => (Money a -> f (Money a)) -> CapitalGainsFromTrust a -> f (CapitalGainsFromTrust a)
+ Data.Tax.ATO.Common: Company :: EntityType
+ Data.Tax.ATO.Common: Individual :: EntityType
+ Data.Tax.ATO.Common: SuperFund :: EntityType
+ Data.Tax.ATO.Common: Trust :: EntityType
+ Data.Tax.ATO.Common: data EntityType
+ Data.Tax.ATO.Common: instance (2016 Data.Type.Ord.<= y, y Data.Type.Ord.<= 2027, Data.Tax.ATO.FY.FinancialYear y) => Data.Tax.ATO.Common.HasCentsPerKilometreMethod y
+ Data.Tax.ATO.Common: instance (2023 Data.Type.Ord.<= y, y Data.Type.Ord.<= 2027, Data.Tax.ATO.FY.FinancialYear y) => Data.Tax.ATO.Common.HasFixedRateMethod y
+ Data.Tax.ATO.Common: instance GHC.Classes.Eq Data.Tax.ATO.Common.EntityType
+ Data.Tax.ATO.Common: instance GHC.Classes.Ord Data.Tax.ATO.Common.EntityType
+ Data.Tax.ATO.Common: instance GHC.Show.Show Data.Tax.ATO.Common.EntityType
+ Data.Tax.ATO.FY.FY2026: fyProxy :: Proxy FY
+ Data.Tax.ATO.FY.FY2026: tables :: (Ord a, Fractional a) => TaxTables FY a
+ Data.Tax.ATO.FY.FY2026: type FY = 2026
+ Data.Tax.ATO.FY.FY2027: fyProxy :: Proxy FY
+ Data.Tax.ATO.FY.FY2027: tables :: (Ord a, Fractional a) => TaxTables FY a
+ Data.Tax.ATO.FY.FY2027: type FY = 2027
+ Data.Tax.ATO.Pretty: summariseCGTSchedule :: CGTAssessment Rational -> Doc
- Data.Tax.ATO: capitalLossCarryForward :: HasCapitalLossCarryForward a b => Lens' (a b) (Money b)
+ Data.Tax.ATO: capitalLossCarryForward :: HasCapitalLossCarryForward a b => Lens' (a b) (CapitalLossCarryForward b)
- Data.Tax.ATO: paygInstalmentIncome :: forall a (y :: Nat). RealFrac a => TaxReturnInfo y a -> Money a
+ Data.Tax.ATO: paygInstalmentIncome :: forall (y :: Nat) a. RealFrac a => TaxReturnInfo y a -> Money a
- Data.Tax.ATO.CGT: assessCGTEvents :: (Fractional a, Ord a, Foldable t) => Money a -> t (CGTEvent a) -> CGTAssessment a
+ Data.Tax.ATO.CGT: assessCGTEvents :: (Fractional a, Ord a, Foldable t) => EntityType -> CapitalLossCarryForward a -> t (CGTEvent a) -> CGTAssessment a
- Data.Tax.ATO.CGT: capitalGain :: HasCapitalGain a b c => Getter (a b) (Money c)
+ Data.Tax.ATO.CGT: capitalGain :: (Fractional a, Ord a) => CGTEvent a -> Money a
- Data.Tax.ATO.CGT: capitalLoss :: (Num a, Ord a) => CGTEvent a -> Money a
+ Data.Tax.ATO.CGT: capitalLoss :: (Fractional a, Ord a) => CGTEvent a -> Money a
- Data.Tax.ATO.CGT: capitalLossCarryForward :: HasCapitalLossCarryForward a b => Lens' (a b) (Money b)
+ Data.Tax.ATO.CGT: capitalLossCarryForward :: HasCapitalLossCarryForward a b => Lens' (a b) (CapitalLossCarryForward b)
- Data.Tax.ATO.CGT: cgtNetLossesCarriedForward :: forall a f. Functor f => (Money a -> f (Money a)) -> CGTAssessment a -> f (CGTAssessment a)
+ Data.Tax.ATO.CGT: cgtNetLossesCarriedForward :: Num a => Getter (CGTAssessment a) (Money a)
- Data.Tax.ATO.CGT: isCapitalGain :: (Num a, Ord a) => CGTEvent a -> Bool
+ Data.Tax.ATO.CGT: isCapitalGain :: (Fractional a, Ord a) => CGTEvent a -> Bool
- Data.Tax.ATO.CGT: isCapitalLoss :: (Num a, Ord a) => CGTEvent a -> Bool
+ Data.Tax.ATO.CGT: isCapitalLoss :: (Fractional a, Ord a) => CGTEvent a -> Bool
Files
- CHANGELOG.md +94/−0
- src/Data/Tax/ATO.hs +176/−53
- src/Data/Tax/ATO/BPI.hs +131/−0
- src/Data/Tax/ATO/CGT.hs +675/−101
- src/Data/Tax/ATO/Common.hs +25/−9
- src/Data/Tax/ATO/FY/FY2025.hs +2/−6
- src/Data/Tax/ATO/FY/FY2026.hs +84/−0
- src/Data/Tax/ATO/FY/FY2027.hs +95/−0
- src/Data/Tax/ATO/Pretty.hs +170/−24
- tax-ato.cabal +8/−4
CHANGELOG.md view
@@ -1,3 +1,97 @@+## Version 2026.1++### CGT overhaul++Capital gains tax (CGT) received a significant overhaul.+Enhancements include:++- Added the a `CGTAsset` data type, which includes `CGTAssetType`+ for tracking different categories of CGT assets.++- Implemented a separate loss carry-forward for collectables. The+ new `CapitalLossCarryForward` data type has separate fields for+ capital losses on collectables and other capital losses.++- Capital losses on personal use assets (`AssetTypePersonalUse`) are+ disregarded.++- Added the `EntityType` data type. This ensures the correct CGT+ discount proportion is used depending on whether the taxed entity+ is an `Individual`, `Company`, `Trust` or `SuperFund`.++- The `assessCGTEvents` function now has an `EntityType` argument.++- Capital gains from trusts can now be included in CGT calculations.+ The `CapitalGainsFromTrust` data type represents capital gains+ from trust distributions. The 'prepTrustCGT' function converts+ `CapitalGainsFromTrust` values into CGT events that represent the+ grossed-up gain, including eligibility for the CGT discount or+ small business concession.++- Added convenience constructors for various kinds of CGT events.++- Added some data types that will support the implementation (in a+ future release) of various CGT exemptions and small business CGT+ concessions.++- The `Data.Tax.ATO.Pretty.summariseCGTSchedule` prints the *CGT+ schedule* for a given `CGTAssessment`.++### Personal services income (PSI)++Added module `Data.Tax.ATO.BPI` (*Business and professional items*),+which provides for declaring personal services income and associated+deductions.++Usage:++```haskell+info =+ newTaxReturnInfoForTables tables+ & set+ ( businessAndProfessionalItems+ . personalServicesIncome+ . psiOther )+ ( Money 18000 )+ & set+ ( businessAndProfessionalItems+ . personalServicesIncome+ . psiTotalAmountOfOtherDeductions+ )+ ( Money 2000 )+```++### "Other income"++Added the `otherIncome` field and the `OtherIncome` data type, which+contains a number of fields for declaring various kinds of income+(including FHSS released amounts).++Example usage:++```haskell+info =+ newTaxReturnInfoForTables tables+ & set (otherIncome . otherIncomeCategory4) [("Airdrop", Money 5000)]+```++### Other features and enhancements++- Add `FY2026` module. Apart from the usual Medicare levy and+ private health insurance rebate adjustments, study loan repayments+ changed to a marginal tax (previously a threshold tax).++- Add preliminary `FY2027` module. Values are not final. The+ new *$1,000 instant deduction* is not yet implemented.++### Bug fixes++- FY2020+ now calculate study and training loan repayment correctly+ where individual has both HELP and SFSS balances.++- Pretty-printers updated to omit various line items when zero.++ ## Version 2025.1 ### Features and enhancements
src/Data/Tax/ATO.hs view
@@ -60,6 +60,8 @@ -- *** Capital gains tax (CGT) , HasCapitalLossCarryForward(..)+ , capitalLossCarryForwardOther+ , capitalLossCarryForwardCollectables , cgtEvents -- *** Employee share schemes@@ -74,9 +76,24 @@ , essTFNAmounts , essForeignSourceDiscounts + -- ***Business and professional items***+ , businessAndProfessionalItems+ , module Data.Tax.ATO.BPI+ -- *** Foreign income , foreignIncome + -- *** Other income+ , OtherIncome+ , otherIncome+ , otherIncomeCategory1+ , otherIncomeCategory2+ , otherIncomeCategory3+ , otherIncomeCategory4+ , taxWithheldLumpSumPaymentsInArrears+ , taxWithheldAssessableFHSSReleasedAmount+ , taxableProfessionalIncome+ -- ** Medicare Levy Surcharge and Private Health Insurance , mlsExemption , privateHealthInsurancePolicyDetails@@ -177,12 +194,18 @@ , module Data.Tax.ATO.Rounding ) where -import Control.Lens (Getter, Lens', (&), foldOf, lens, preview, set, to, view, views)+import Data.Proxy++import Control.Lens+ ( Getter, Lens'+ , (&), _2, foldOf, lens, preview, set, to, view, views+ ) import Data.Time (Day) import Data.Tax-import Data.Tax.ATO.CGT import Data.Tax.ATO.ABN (ABN)+import Data.Tax.ATO.BPI+import Data.Tax.ATO.CGT import Data.Tax.ATO.Common import Data.Tax.ATO.Depreciation import Data.Tax.ATO.FY@@ -316,48 +339,56 @@ -- -- The following lenses are available: ----- +------------------------------------------------------+----------------------------------+--- | 'mlsExemption' | Medicare levy exemption |--- +------------------------------------------------------+----------------------------------+--- | 'helpBalance' | HELP, VSL, SSL, ABSTUDY SSL, |--- | | and AASL/TSL account balance |--- +------------------------------------------------------+----------------------------------+--- | 'sfssBalance' | SFSS account balance |--- +------------------------------------------------------+----------------------------------+--- | 'paymentSummariesIndividualNonBusiness' | PAYG payment summaries - |--- | | individual non-business |--- +------------------------------------------------------+----------------------------------+--- | 'paymentSummariesForeignEmployment' | PAYG payment summaries - |--- | | foreign employment |--- +------------------------------------------------------+----------------------------------+--- | 'paymentSummariesBusinessAndPersonalServicesIncome' | PAYG payment summaries - |--- | | business and personal services |--- | | income |--- +------------------------------------------------------+----------------------------------+--- | 'paymentSummariesWithholdingWhereABNNotQuoted' | PAYG payment summaries - |--- | | withholding where ABN not quoted |--- +------------------------------------------------------+----------------------------------+--- | 'interest' | Interest income and tax withheld |--- +------------------------------------------------------+----------------------------------+--- | 'dividends' | Dividend data |--- +------------------------------------------------------+----------------------------------+--- | 'ess' | Employee Share Scheme statement |--- +------------------------------------------------------+----------------------------------+--- | 'foreignIncome' | Foreign income |--- +------------------------------------------------------+----------------------------------+--- | 'cgtEvents' | Capital gains and losses |--- +------------------------------------------------------+----------------------------------+--- | 'deductions' | Deductions |--- +------------------------------------------------------+----------------------------------+--- | 'offsets' | Tax offsets |--- +------------------------------------------------------+----------------------------------+--- | 'privateHealthInsurancePolicyDetails' | Private health insurance |--- | | policy details |--- +------------------------------------------------------+----------------------------------+--- | 'spouseDetails' | Spouse Details (or @Nothing@) |--- +------------------------------------------------------+----------------------------------+--- | 'incomeTests' | Income Tests |--- +------------------------------------------------------+----------------------------------++-- +------------------------------------------------------+----------------------------------------++-- | 'mlsExemption' | Medicare levy exemption |+-- +------------------------------------------------------+----------------------------------------++-- | 'helpBalance' | HELP, VSL, SSL, ABSTUDY SSL, |+-- | | and AASL/TSL account balance |+-- +------------------------------------------------------+----------------------------------------++-- | 'sfssBalance' | SFSS account balance |+-- +------------------------------------------------------+----------------------------------------++-- | 'paymentSummariesIndividualNonBusiness' | PAYG payment summaries - |+-- | | individual non-business |+-- +------------------------------------------------------+----------------------------------------++-- | 'paymentSummariesForeignEmployment' | PAYG payment summaries - |+-- | | foreign employment |+-- +------------------------------------------------------+----------------------------------------++-- | 'paymentSummariesBusinessAndPersonalServicesIncome' | PAYG payment summaries - |+-- | | business and personal services income |+-- +------------------------------------------------------+----------------------------------------++-- | 'paymentSummariesWithholdingWhereABNNotQuoted' | PAYG payment summaries - |+-- | | withholding where ABN not quoted |+-- +------------------------------------------------------+----------------------------------------++-- | 'interest' | Interest income and tax withheld |+-- +------------------------------------------------------+----------------------------------------++-- | 'dividends' | Dividend data |+-- +------------------------------------------------------+----------------------------------------++-- | 'ess' | Employee Share Scheme statement |+-- +------------------------------------------------------+----------------------------------------++-- | 'otherIncome' | /Other income/ fields |+-- +------------------------------------------------------+----------------------------------------++-- | 'businessAndProfessionalItems' | See |+-- | | 'BusinessAndProfessionalItemsSchedule' |+-- +------------------------------------------------------+----------------------------------------++-- | 'foreignIncome' | Foreign income |+-- +------------------------------------------------------+----------------------------------------++-- | 'cgtEvents' :: ['CGTEvent'] | Capital gains tax events for |+-- | | this income year |+-- +------------------------------------------------------+----------------------------------------++-- | 'capitalLossCarryForward' | Capital losses carried forward |+-- | :: 'CapitalLossCarryForward' | from previous income years |+-- +------------------------------------------------------+----------------------------------------++-- | 'deductions' | Deductions |+-- +------------------------------------------------------+----------------------------------------++-- | 'offsets' | Tax offsets |+-- +------------------------------------------------------+----------------------------------------++-- | 'privateHealthInsurancePolicyDetails' | Private health insurance |+-- | | policy details |+-- +------------------------------------------------------+----------------------------------------++-- | 'spouseDetails' | Spouse Details (or @Nothing@) |+-- +------------------------------------------------------+----------------------------------------++-- | 'incomeTests' | Income Tests |+-- +------------------------------------------------------+----------------------------------------+ -- data TaxReturnInfo y a = TaxReturnInfo { _mlsExemption :: Days y@@ -370,11 +401,13 @@ , _interest :: GrossAndWithheld a , _dividends :: [Dividend a] , _ess :: [ESSStatement a]+ , _otherIncome :: OtherIncome a+ , _businessAndProfessionalItems :: BusinessAndProfessionalItemsSchedule a , _foreignIncome :: Money a , _cgtEvents :: [CGTEvent a] , _deductions :: Deductions a , _offsets :: Offsets a- , _triCapitalLossCarryForward :: Money a+ , _triCapitalLossCarryForward :: CapitalLossCarryForward a , _phi :: [PrivateHealthInsurancePolicyDetail a] , _spouseDetails :: Maybe (SpouseDetails a) , _incomeTests :: IncomeTests a@@ -402,11 +435,13 @@ mempty -- interest mempty -- dividends mempty -- ESS+ newOtherIncome+ newBusinessAndProfessionalItemsSchedule mempty -- foreign income mempty -- CGT events mempty -- deductions mempty -- offsets- mempty -- cap loss carry forward+ newCapitalLossCarryForward mempty -- private health insurance policy details Nothing -- spouse details newIncomeTests@@ -467,9 +502,17 @@ dividends :: Lens' (TaxReturnInfo y a) [Dividend a] dividends = lens _dividends (\s b -> s { _dividends = b }) +businessAndProfessionalItems+ :: Lens' (TaxReturnInfo y a) (BusinessAndProfessionalItemsSchedule a)+businessAndProfessionalItems =+ lens _businessAndProfessionalItems (\s b -> s { _businessAndProfessionalItems = b })+ ess :: Lens' (TaxReturnInfo y a) [ESSStatement a] ess = lens _ess (\s b -> s { _ess = b }) +otherIncome :: Lens' (TaxReturnInfo y a) (OtherIncome a)+otherIncome = lens _otherIncome (\s b -> s { _otherIncome = b })+ foreignIncome :: Lens' (TaxReturnInfo y a) (Money a) foreignIncome = lens _foreignIncome (\s b -> s { _foreignIncome = b }) @@ -592,13 +635,16 @@ -- | Tax to calculate compulsory study and training loan repayments -- (e.g. HELP, SFSS) studyAndTrainingLoanRepaymentTax- :: (Fractional a, Ord a)+ :: forall y a. (FinancialYear y, Fractional a, Ord a) => TaxTables y a -> TaxReturnInfo y a -> Tax (Money a) (Money a)-studyAndTrainingLoanRepaymentTax table info =- limit (view helpBalance info) (ttHelp table)- <> limit (view sfssBalance info) (ttSfss table)+studyAndTrainingLoanRepaymentTax table info = case fromProxy (Proxy @y) of+ y | y < 2020 ->+ limit (view helpBalance info) (ttHelp table)+ <> limit (view sfssBalance info) (ttSfss table)+ | otherwise ->+ limit (view helpBalance info <> view sfssBalance info) (ttHelp table) -- | Taxable income instance (RealFrac a) => HasTaxableIncome (TaxReturnInfo y) a a where@@ -613,7 +659,9 @@ , view (interest . taxableIncome) info , view (dividends . taxableIncome) info , view (ess . taxableIncome) info- , view (cgtEvents . to (assessCGTEvents cf) . cgtNetGain) info+ , view (otherIncome . taxableIncome) info+ , view (businessAndProfessionalItems . taxableIncome) info+ , view (cgtEvents . to (assessCGTEvents Individual cf) . cgtNetGain) info , view foreignIncome info ] in@@ -632,6 +680,7 @@ <> view (paymentSummariesWithholdingWhereABNNotQuoted . taxWithheld) info <> view (interest . taxWithheld) info <> view (ess . taxWithheld) info+ <> view (otherIncome . taxWithheld) info fringeBenefits :: (Num a) => TaxReturnInfo y a -> Money a fringeBenefits info =@@ -665,7 +714,17 @@ -- TODO super lump sum taxed elements with zero tax rate -- TODO assessable FHSS released amount -paygInstalmentIncome :: (RealFrac a) => TaxReturnInfo y a -> Money a+-- | PAYG instalment income includes:+--+-- * Dividends and interest payments+-- * Foreign income+-- * Payments where amounts withheld due to non-quotation of TFN or ABN+--+-- It also includes rent, business income and partnership/trust income, but+-- this library does not yet implement these features so these amounts are+-- are not reflected in this calculation.+--+paygInstalmentIncome :: forall y a. (RealFrac a) => TaxReturnInfo y a -> Money a paygInstalmentIncome info = -- TODO gross rent view (dividends . taxableIncome) info@@ -686,7 +745,7 @@ => TaxTables y a -> TaxReturnInfo y a -> TaxAssessment a assessTax tables info = let- cg = assessCGTEvents+ cg = assessCGTEvents Individual (view capitalLossCarryForward info) (view cgtEvents info) taxable = view taxableIncome info due = getTax (individualTax tables) taxable@@ -1210,3 +1269,67 @@ instance HasTaxWithheld ESSStatement a a where taxWithheld = essTFNAmounts+++-- | __24__ Other income+data OtherIncome a = OtherIncome+ { _category1 :: [(String, Money a)]+ , _category2 :: [(String, Money a)]+ , _category3 :: Money a+ , _category4 :: [(String, Money a)]+ , _taxWithheldLSPIA :: Money a+ , _taxWithheldFHSS :: Money a+ , _taxableProfessionalIncome :: Money a+ }++newOtherIncome :: (Num a) => OtherIncome a+newOtherIncome = OtherIncome+ { _category1 = []+ , _category2 = []+ , _category3 = mempty+ , _category4 = []+ , _taxWithheldLSPIA = mempty+ , _taxWithheldFHSS = mempty+ , _taxableProfessionalIncome = mempty+ }++otherIncomeCategory1 :: Lens' (OtherIncome a) [(String, Money a)]+otherIncomeCategory1 = lens _category1 (\s b -> s { _category1 = b })++otherIncomeCategory2 :: Lens' (OtherIncome a) [(String, Money a)]+otherIncomeCategory2 = lens _category2 (\s b -> s { _category2 = b })++otherIncomeCategory3 :: Lens' (OtherIncome a) (Money a)+otherIncomeCategory3 = lens _category3 (\s b -> s { _category3 = b })++otherIncomeCategory4 :: Lens' (OtherIncome a) [(String, Money a)]+otherIncomeCategory4 = lens _category4 (\s b -> s { _category4 = b })++taxWithheldLumpSumPaymentsInArrears :: Lens' (OtherIncome a) (Money a)+taxWithheldLumpSumPaymentsInArrears =+ lens _taxWithheldLSPIA (\s b -> s { _taxWithheldLSPIA = b })++taxWithheldAssessableFHSSReleasedAmount :: Lens' (OtherIncome a) (Money a)+taxWithheldAssessableFHSSReleasedAmount =+ lens _taxWithheldFHSS (\s b -> s { _taxWithheldFHSS = b })++-- | __Z__ Taxable professional income+--+-- This field is used for __income averaging__ for /special professionals/+-- (which this library does not yet implement).+--+taxableProfessionalIncome :: Lens' (OtherIncome a) (Money a)+taxableProfessionalIncome =+ lens _taxableProfessionalIncome (\s b -> s { _taxableProfessionalIncome = b })++instance (Num a) => HasTaxableIncome OtherIncome a a where+ taxableIncome = to $ \s ->+ view (otherIncomeCategory1 . traverse . _2) s+ <> view (otherIncomeCategory2 . traverse . _2) s+ <> view otherIncomeCategory3 s+ <> view (otherIncomeCategory4 . traverse . _2) s++instance (Num a) => HasTaxWithheld OtherIncome a a where+ taxWithheld = to $ \s ->+ view taxWithheldLumpSumPaymentsInArrears s+ <> view taxWithheldAssessableFHSSReleasedAmount s
+ src/Data/Tax/ATO/BPI.hs view
@@ -0,0 +1,131 @@+-- 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
src/Data/Tax/ATO/CGT.hs view
@@ -15,6 +15,8 @@ -- along with this program. If not, see <http://www.gnu.org/licenses/>. +{-# LANGUAGE LambdaCase #-}+ {-| Types and calculations for /capital gains tax/ (CGT).@@ -25,24 +27,83 @@ The main function you need is 'assessCGTEvents'. --}+Things that are __not yet implemented__: +* Small business CGT concessions+* Exemptions, rollovers, and earnout arrangements++-} module Data.Tax.ATO.CGT (+ -- * CGT assets+ CGTAssetType(..)+ , CGTAssetFlag(..)+ , CGTAsset+ , assetDescription+ , assetShare+ , assetType+ , assetFlag+ , assetUnits+ , assetAcquisitionDate+ , assetAcquisitionPrice+ , assetAcquisitionCosts+ , assetCapitalCosts+ , assetOwnershipCosts+ -- ** Constructors+ , newCGTAsset+ -- * CGT events- CGTEvent(..)+ , CGTEvent+ , eventAsset+ , eventCode+ , eventDate+ , eventPrice+ , eventCosts+ , CGTExemptionRolloverOrAdditionalDiscountCode(..)+ -- ** Constructors+ , cgtEventA1+ , cgtEventB1+ , cgtEventC1+ , cgtEventC2+ , cgtEventC3+ , cgtEventE10+ -- ** Helper functions+ , eventProceeds + -- * Capital gains from trusts+ , CapitalGainsFromTrust+ , newCapitalGainsFromTrust+ , trustGrossCapitalGain+ , trustCapitalLossesApplied+ , trustCGTDiscountApplied+ , trustCGTSmallBusinessConcessionApplied+ , prepTrustCGT+ -- * CGT assessments for tax returns , assessCGTEvents , CGTAssessment , nullCGTAssessment- , HasCapitalLossCarryForward(..) , cgtNetGain , cgtTotalCurrentYearGains+ , cgtTotalCurrentYearLosses+ , cgtTotalCurrentYearLossesApplied+ , cgtTotalPriorYearLossesApplied+ , cgtTotalCGTDiscountApplied , cgtNetLossesCarriedForward+ , cgtGainsAndLossesByCategory + -- ** Carry-forward+ , HasCapitalLossCarryForward(..)+ , CapitalLossCarryForward+ , newCapitalLossCarryForward+ , capitalLossCarryForwardCollectables+ , capitalLossCarryForwardOther++ -- ** Helper types for CGT Schedule+ , CGTScheduleCategory(..)+ -- * CGT computations- , HasCapitalGain(..)+ , capitalGain , capitalLoss , isCapitalGain , isCapitalLoss@@ -52,99 +113,389 @@ import Data.Foldable (toList) import Data.List (partition) -import Control.Lens (Getter, Lens', both, lens, over, to, view)+import Control.Lens+ ( Getter, Lens', Traversal'+ , (&), _3, anyOf, both, lens, over, preview, set, to, view, views+ )+import qualified Data.Map as M import Data.Time.Calendar (Day, diffDays) import Data.Tax --- | A CGT Event (usually an asset disposal)+import Data.Tax.ATO.Common (EntityType(..))+++-- | The CGT schedule requires reporting of gains and losses for+-- different asset types. Also, collectables and personal use+-- assets are subject to different rules from other assets.+-- Specifically, capital losses from personal use assets are+-- disregarded, and capital losses from collectables can only offset+-- capital gains from collectables. ---data CGTEvent a = CGTEvent- { assetDesc :: String- , units :: a- , acquisitionDate :: Day- , acquisitionPrice :: Money a- , acquisitionCosts :: Money a- , disposalDate :: Day- , disposalPrice :: Money a- , disposalCosts :: Money a- , capitalCosts :: Money a- , ownershipCosts :: Money a+data CGTAssetType+ = AssetTypeSharesAUListed+ | AssetTypeSharesOther+ | AssetTypeUnitsAUListed+ | AssetTypeUnitsOther+ | AssetTypeRealEstateAU+ | AssetTypeRealEstateOther+ | AssetTypeCollectable+ | AssetTypePersonalUse+ | AssetTypeOther+ deriving (Eq, Ord, Show)++-- | Flags for CGT assets that can affect CGT calculations.+--+data CGTAssetFlag+ = MainResidence+ | AffordableHousing+ | SmallBusinessActiveAsset+ | EarlyStageInnovationCompany+ deriving (Eq, Ord, Show)++data CGTAsset a = CGTAsset+ { _assetDescription :: String+ , _assetShare :: Rational -- owner's share of asset+ , _assetType :: CGTAssetType+ , _assetFlag :: Maybe CGTAssetFlag+ , _assetUnits :: Rational -- number of units+ , _assetAcquisitionDate :: Day+ , _assetAcquisitionPrice :: Money a -- acquisition price (per unit)+ , _assetAcquisitionCosts :: Money a -- acquisition costs (of parcel)+ , _assetCapitalCosts :: Money a+ , _assetOwnershipCosts :: Money a }+ deriving (Functor, Show)++-- | Construct a new CGT asset. Capital and ownership costs are set to zero.+-- Use 'assetFlag' to set a flag, if relevant.+newCGTAsset+ :: (Num a)+ => String -- ^ asset description+ -> Rational -- ^ owner's share (a fraction between 0 and (typically) 1)+ -> CGTAssetType+ -> Rational -- ^ quantity of units/shares+ -> Day -- ^ acquisition date+ -> Money a -- ^ price per unit/share+ -> Money a -- ^ acquisition costs (e.g. brokerage, settlement costs, etc)+ -> CGTAsset a+newCGTAsset desc share typ units date price cost =+ CGTAsset desc share typ Nothing units date price cost mempty mempty++assetDescription :: Lens' (CGTAsset a) String+assetDescription =+ lens _assetDescription (\s b -> s { _assetDescription = b })++assetShare :: Lens' (CGTAsset a) Rational+assetShare =+ lens _assetShare (\s b -> s { _assetShare = b })++assetType :: Lens' (CGTAsset a) CGTAssetType+assetType =+ lens _assetType (\s b -> s { _assetType = b })++assetFlag :: Lens' (CGTAsset a) (Maybe CGTAssetFlag)+assetFlag =+ lens _assetFlag (\s b -> s { _assetFlag = b })++assetUnits :: Lens' (CGTAsset a) Rational+assetUnits =+ lens _assetUnits (\s b -> s { _assetUnits = b })++assetAcquisitionDate :: Lens' (CGTAsset a) Day+assetAcquisitionDate =+ lens _assetAcquisitionDate (\s b -> s { _assetAcquisitionDate = b })++assetAcquisitionPrice :: Lens' (CGTAsset a) (Money a)+assetAcquisitionPrice =+ lens _assetAcquisitionPrice (\s b -> s { _assetAcquisitionPrice = b })++assetAcquisitionCosts :: Lens' (CGTAsset a) (Money a)+assetAcquisitionCosts =+ lens _assetAcquisitionCosts (\s b -> s { _assetAcquisitionCosts = b })++assetCapitalCosts :: Lens' (CGTAsset a) (Money a)+assetCapitalCosts =+ lens _assetCapitalCosts (\s b -> s { _assetCapitalCosts = b })++assetOwnershipCosts :: Lens' (CGTAsset a) (Money a)+assetOwnershipCosts =+ lens _assetOwnershipCosts (\s b -> s { _assetOwnershipCosts = b })+++data CGTExemptionRolloverOrAdditionalDiscountCode+ = {- A -} SmallBusinessActiveAssetReduction+ | {- B -} SmallBusinessRetirementExemption+ | {- C -} SmallBusinessRollover+ | {- D -} SmallBusiness15YearExemption+ | {- E -} ForeignResidentCGTExemption+ | {- F -} ScripForScripRollover+ | {- I -} MainResidenceExemption+ | {- J -} CapitalGainsDisregardedAsAResultOfTheSaleOfAPreCGTAsset+ | {- K -} DisposalOrCreationOfAssetsInAWhollyOwnedCompany+ | {- L -} ReplacementAssetRollovers+ | {- M -} ExchangeOfSharesOrUnits+ | {- N -} ExchangeOfRightsOrOptions+ | {- O -} ExchangeOfSharesInOneCompanyForSharesInAnotherCompany+ | {- P -} ExchangeOfUnitsInAUnitTrustForSharesInACompany+ | {- R -} DemergerRollover+ | {- S -} SameAssetRollovers+ | {- T -} SmallBusinessRestructureRollover+ | {- U -} EarlyStageInvestor+ | {- V -} VentureCapitalInvestment+ | {- W -} AffordableHousingDiscount+ | {- X -} OtherExemptionsAndRollovers+ deriving (Eq, Ord, Show)++data CGTEventType a+ = CGTEventEnd (CGTAsset a)+ -- ^ Ownership of a CGT asset ends (or contracted)+ | CGTEventReAsset (CGTAsset a)+ -- ^ A CGT event relating to the asset, where cost base ignored.+ -- Acquisition date determines discounts, etc.+ | CGTEventNoAsset+ -- ^ A CGT event not relating to an owned asset+ | CGTEventTrustDistribution IsDiscountable+ -- ^ Special purpose constructor for representing distributions+ -- of capital gains from trusts.+ deriving (Functor, Show)++data IsDiscountable = Discountable | NotDiscountable deriving (Show) -instance Functor CGTEvent where- fmap f (CGTEvent k n t1 p1 b1 t2 p2 b2 cap own) =- CGTEvent k (f n)- t1 (f <$> p1) (f <$> b1)- t2 (f <$> p2) (f <$> b2)- (f <$> cap) (f <$> own)+-- | A CGT event (usually an asset disposal)+data CGTEvent a = CGTEvent+ { _eventType :: CGTEventType a+ , _eventCode :: Maybe CGTExemptionRolloverOrAdditionalDiscountCode+ , _eventDate :: Day+ , _eventPrice :: Money a+ , _eventCosts :: Money a+ }+ deriving (Functor, Show) -reducedCostBase :: Num a => CGTEvent a -> Money a-reducedCostBase event =- (units event *$ acquisitionPrice event)- $+$ acquisitionCosts event- $+$ disposalCosts event- $+$ capitalCosts event+eventType :: Lens' (CGTEvent a) (CGTEventType a)+eventType =+ lens _eventType (\s b -> s { _eventType = b }) -costBase :: Num a => CGTEvent a -> Money a-costBase event = reducedCostBase event $+$ ownershipCosts event+-- | A CGT event could have zero or one related assets.+eventAsset :: Traversal' (CGTEvent a) (CGTAsset a)+eventAsset = eventType . l+ where+ l f = \case+ CGTEventEnd a -> CGTEventEnd <$> f a+ CGTEventReAsset a -> CGTEventReAsset <$> f a+ s -> pure s -capitalGain' :: (Num a, Ord a) => CGTEvent a -> Money a-capitalGain' event =- max mempty (units event *$ disposalPrice event $-$ costBase event)+eventCode :: Lens' (CGTEvent a) (Maybe CGTExemptionRolloverOrAdditionalDiscountCode)+eventCode =+ lens _eventCode (\s b -> s { _eventCode = b }) --- | The capital loss as a /non-negative/ amount.--- /$0/ if the event is not a loss.+eventDate :: Lens' (CGTEvent a) Day+eventDate =+ lens _eventDate (\s b -> s { _eventDate = b })++eventPrice :: Lens' (CGTEvent a) (Money a)+eventPrice =+ lens _eventPrice (\s b -> s { _eventPrice = b })++eventCosts :: Lens' (CGTEvent a) (Money a)+eventCosts =+ lens _eventCosts (\s b -> s { _eventCosts = b })++-- | A1 - Disposal of a CGT asset+cgtEventA1+ :: CGTAsset a+ -- ^ The asset the event is associated with. If disposing of a+ -- share or fewer shares/units than held, the asset must be split+ -- beforehand.+ -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode+ -- ^ Exemption, rollover or discount code applying to this event, if any.+ -> Day -- ^ When the disposal contract is entered into or, if none,+ -- when the entity stops being the asset's owner+ -> Money a -- ^ Capital proceeds (per unit)+ -> Money a -- ^ Costs of disposal (consolidated; for whole asset)+ -> CGTEvent a+cgtEventA1 asset code date price cost =+ CGTEvent (CGTEventEnd asset) code date price cost++-- | B1 - Use and enjoyment before title passes+cgtEventB1+ :: CGTAsset a+ -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode+ -- ^ Exemption, rollover or discount code applying to this event, if any.+ -> Day -- ^ When use of the CGT asset passes+ -> Money a -- ^ Capital proceeds (per unit)+ -> Money a -- ^ Costs of arrangement+ -> CGTEvent a+cgtEventB1 = cgtEventA1++-- | C1 - Loss or destruction of a CGT asset+cgtEventC1+ :: CGTAsset a+ -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode+ -- ^ Exemption, rollover or discount code applying to this event, if any.+ -> Day -- ^ When compensation is first received or, if none,+ -- when the loss is discovered or destruction occurred+ -> Money a -- ^ Capital proceeds (per unit)+ -> Money a -- ^ Incidental costs relating to the event+ -> CGTEvent a+cgtEventC1 = cgtEventA1++-- | C2 - Cancellation, surrender and similar endings+cgtEventC2+ :: CGTAsset a+ -> Maybe CGTExemptionRolloverOrAdditionalDiscountCode+ -- ^ Exemption, rollover or discount code applying to this event, if any.+ -> Day -- ^ When the contract ending an asset is entered into or,+ -- if none, when an asset ends+ -> Money a -- ^ Capital proceeds (per unit)+ -> Money a -- ^ Costs relating to the event+ -> CGTEvent a+cgtEventC2 = cgtEventA1++-- | C3 - End of an option to acquire shares etc+cgtEventC3+ :: (Num a)+ => CGTAsset a+ -- ^ The option. Its acqusition price could be /negative/, i.e.+ -- capital proceeds from granting the option. Acquisition costs+ -- record the expenditure in granting the option.+ -- granting the option is recorded in the a+ -> Day -- ^ When the option ends+ -> CGTEvent a+cgtEventC3 asset date = CGTEvent (CGTEventEnd asset) Nothing date (Money 0) (Money 0)++-- | E10 - Annual cost base reduction exceeds cost base of interest+-- in attribution managed investment trust.+cgtEventE10+ :: (Num a)+ => CGTAsset a+ -- ^ The asset the event is associated with.+ -> Day+ -> Money a+ -- ^ Amount cost base reduction exceeds cost base (consolidated; for whole asset)+ -> CGTEvent a+cgtEventE10 asset date amount =+ CGTEvent (CGTEventReAsset asset) Nothing date amount (Money 0)++-- | Capital proceeds arising from CGT event+eventProceeds :: CGTEvent Rational -> Money Rational+eventProceeds ev = case preview (eventAsset . assetUnits) ev of+ Just n -> n *$ view eventPrice ev -- asset with units (possibly 1)+ Nothing -> view eventPrice ev -- bare capital proceeds+++-- | @sub x y@ = subtract @y@ from @x@, clamping to 0 and+-- returning @(result, leftovers)@ ---capitalLoss :: (Num a, Ord a) => CGTEvent a -> Money a-capitalLoss event = over money abs $- min mempty (units event *$ disposalPrice event $-$ reducedCostBase event)+sub :: (Num a, Ord a) => Money a -> Money a -> (Money a, Money a)+sub x y =+ let r = x $-$ y+ in (max mempty r, over money abs (min mempty r)) ++-- | Reduced cost base of the (whole) asset subject to this CGT event+assetReducedCostBase :: (Fractional a) => CGTAsset a -> Money a+assetReducedCostBase asset =+ ( views assetUnits fromRational asset+ *$ view assetAcquisitionPrice asset )+ $+$ view assetAcquisitionCosts asset+ $+$ view assetCapitalCosts asset++-- | Cost base of the (whole) asset subject to this CGT event+assetCostBase :: (Fractional a) => CGTAsset a -> Money a+assetCostBase asset =+ assetReducedCostBase asset+ $+$ view assetOwnershipCosts asset++-- | Take of the amount in the same proportion as the share of the given asset.+shareOf :: (Fractional a) => CGTAsset a -> Money a -> Money a+shareOf asset = ($* views assetShare fromRational asset)++-- | Capital gain, scaled to the owner's share (per 'assetShare').+-- __Zero__ if there is no capital gain. Does not apply discounts+-- or exemptions.+capitalGain :: (Fractional a, Ord a) => CGTEvent a -> Money a+capitalGain event =+ max mempty+ $ case view eventType event of+ CGTEventEnd asset ->+ shareOf asset+ $ views assetUnits fromRational asset *$ view eventPrice event+ $-$ assetCostBase asset+ $-$ view eventCosts event+ CGTEventReAsset asset ->+ shareOf asset+ $ view eventPrice event+ $-$ view eventCosts event+ CGTEventNoAsset ->+ view eventPrice event+ $-$ view eventCosts event+ CGTEventTrustDistribution _ ->+ view eventPrice event++-- | The capital loss as a __non-negative__ amount, scaled to the+-- owner's share (per 'assetShare'). __Zero__ if there is no+-- capital loss.+--+-- Capital losses on personal use assets are disregarded;+-- if type is 'AssetTypePersonalUse' this will always be zero.+--+capitalLoss :: (Fractional a, Ord a) => CGTEvent a -> Money a+capitalLoss event =+ fmap abs . min mempty+ $ case view eventType event of+ CGTEventEnd asset ->+ shareOf asset+ $ ( case view assetType asset of AssetTypePersonalUse -> const (Money 0) ; _ -> id )+ $ views assetUnits fromRational asset *$ view eventPrice event+ $-$ assetReducedCostBase asset+ $-$ view eventCosts event+ CGTEventReAsset asset ->+ shareOf asset+ $ view eventPrice event+ $-$ view eventCosts event+ CGTEventNoAsset ->+ view eventPrice event+ $-$ view eventCosts event+ CGTEventTrustDistribution _ ->+ mempty -- trust cannot distribute a capital loss++ -- | Whether the CGT event is a capital gain. /Not the opposite -- of 'isCapitalLoss'!/ A CGT event may be neither a loss nor a -- gain. ---isCapitalGain :: (Num a, Ord a) => CGTEvent a -> Bool-isCapitalGain = (> mempty) . capitalGain'+isCapitalGain :: (Fractional a, Ord a) => CGTEvent a -> Bool+isCapitalGain = (> mempty) . capitalGain -- | Whether the CGT event is a capital loss. /Not the opposite -- of 'isCapitalGain'!/ A CGT event may be neither a loss nor a -- gain. ---isCapitalLoss :: (Num a, Ord a) => CGTEvent a -> Bool+isCapitalLoss :: (Fractional a, Ord a) => CGTEvent a -> Bool isCapitalLoss = (> mempty) . capitalLoss --- | Whether the 50% CGT discount is applicable to this event (only--- with regard to duration of holding; acquisition date ignored).+-- | Whether the CGT discount is (potentially) applicable to this event.+-- Whether it is actually applicable and the discount proportion depends+-- on the type of the entity being assessed. -- discountApplicable :: CGTEvent a -> Bool discountApplicable ev =- diffDays (disposalDate ev) (acquisitionDate ev) > 365+ case view eventType ev of+ CGTEventTrustDistribution Discountable -> True+ CGTEventTrustDistribution NotDiscountable -> False+ _ -> case preview eventAsset ev of+ Nothing -> False+ Just a -> diffDays (view eventDate ev) (view assetAcquisitionDate a) > 365 --- | Types that may have a capital gain. Non-discounted, losses ignored.-class HasCapitalGain a b c where- capitalGain :: Getter (a b) (Money c) --- | Capital gain as a positive amount. /$0/ if the event not a gain.-instance (Num a, Ord a) => HasCapitalGain CGTEvent a a where- capitalGain = to capitalGain'---- | Sum of capital gains, ignoring losses.--- Input __H__ at /item 18/ on tax return.+-- | Assess the total capital gains and net capital gain or loss. ---instance (Foldable t, HasCapitalGain x a a, Num a) => HasCapitalGain t (x a) a where- capitalGain = to (foldMap (view capitalGain))----- | @sub x y@ = subtract @y@ from @x@, clamping to 0 and--- returning @(result, leftovers)@+-- __Assumes all events relate to a particular financial year.__+-- This function does not check or filter the input data. ---sub :: (Num a, Ord a) => Money a -> Money a -> (Money a, Money a)-sub x y =- let r = x $-$ y- in (max mempty r, over money abs (min mempty r))---- | Assess the total capital gains and net capital gain or loss.+-- Use 'prepTrustCGT' to process 'CapitalGainsFromTrust' values into+-- CGT events. -- -- Losses are used to offset non-discountable capital gains -- first, then discountable gains, before the discount is applied@@ -154,59 +505,230 @@ -- assessCGTEvents :: (Fractional a, Ord a, Foldable t)- => Money a -- ^ capital loss carried forward+ => EntityType+ -> CapitalLossCarryForward a -> t (CGTEvent a) -> CGTAssessment a-assessCGTEvents carry events =+assessCGTEvents etype carry events = let l = toList events+ (lC, lO) = partition (anyOf (eventAsset . assetType) (== AssetTypeCollectable)) l+ carryC = view capitalLossCarryForwardCollectables carry+ carryO = view capitalLossCarryForwardOther carry+ r@(rC, rO) =+ ( applyLossesAndDiscounts etype carryC lC+ , applyLossesAndDiscounts etype carryO lO+ )+ appliedLossC = view vectorTotalLoss rC $+$ carryC $-$ view vectorUnappliedLoss rC+ appliedLossO = view vectorTotalLoss rO $+$ carryO $-$ view vectorUnappliedLoss rO+ curFYLossesAppliedC = min appliedLossC (view vectorTotalLoss rC)+ curFYLossesAppliedO = min appliedLossO (view vectorTotalLoss rO)+ priorLossesAppliedC = appliedLossC $-$ curFYLossesAppliedC+ priorLossesAppliedO = appliedLossO $-$ curFYLossesAppliedO+ byCategory =+ M.fromListWith (<>) $ flip fmap l $ \ev ->+ ( scheduleCategory ev, (capitalGain ev, capitalLoss ev) )+ in+ CGTAssessment+ (view (both . vectorTotalGain) r)+ (view (both . vectorTotalLoss) r)++ -- current year losses applied+ ( curFYLossesAppliedC <> curFYLossesAppliedO )++ -- prior year losses applied+ ( priorLossesAppliedC <> priorLossesAppliedO )++ ( newCapitalLossCarryForward+ & set capitalLossCarryForwardCollectables (view vectorUnappliedLoss rC)+ & set capitalLossCarryForwardOther (view vectorUnappliedLoss rO) )+ (view (both . vectorDiscount) r)+ (view (both . vectorNetGain) r)++ byCategory++-- | Internal data type for asset type-partitioned CGT calculations+data CGTVector a = CGTVector+ { _vectorTotalGain :: Money a+ , _vectorTotalLoss :: Money a+ , _vectorNetGain :: Money a+ , _vectorUnappliedLoss :: Money a+ , _vectorDiscount :: Money a+ }+vectorTotalGain, vectorTotalLoss, vectorNetGain, vectorUnappliedLoss, vectorDiscount+ :: Lens' (CGTVector a) (Money a)+vectorTotalGain = lens _vectorTotalGain (\s b -> s { _vectorTotalGain = b })+vectorTotalLoss = lens _vectorTotalLoss (\s b -> s { _vectorTotalLoss = b })+vectorNetGain = lens _vectorNetGain (\s b -> s { _vectorNetGain = b })+vectorUnappliedLoss = lens _vectorUnappliedLoss (\s b -> s { _vectorUnappliedLoss = b })+vectorDiscount = lens _vectorDiscount (\s b -> s { _vectorDiscount = b })++-- | Amount of capital gains from a trust or trusts (including managed funds).+--+-- Use 'newCapitalGainsFromTrust' to construct. Apply 'prepTrustCGT' to+-- produce CGT events representing the grossed-up capital gains.+--+-- The following optics give access to the fields (common use cases should+-- not need them):+--+-- * 'trustGrossCapitalGain'+-- * 'trustCapitalLossesApplied'+-- * 'trustCGTDiscountApplied'+-- * 'trustCGTSmallBusinessConcessionApplied'+--+data CapitalGainsFromTrust a = CapitalGainsFromTrust+ { _trustGrossGain :: Money a+ , _trustLossesAppl :: Money a+ , _trustCGTDiscAppl :: Money a+ , _trustSBConcAppl :: Money a+ }+ deriving (Show)++-- Construct a 'CapitalGainsFromTrust'.+--+-- Values are accepted without checking. It is possible to+-- construct a value that doesn't make sense (e.g. a CGT+-- discount that is more than half of the gross gain).+--+newCapitalGainsFromTrust+ :: Money a -- ^ Gross capital gain+ -> Money a -- ^ Capital losses applied+ -> Money a -- ^ CGT discount applied+ -> Money a -- ^ CGT small business concessions applied+ -> CapitalGainsFromTrust a+newCapitalGainsFromTrust = CapitalGainsFromTrust++trustGrossCapitalGain :: Lens' (CapitalGainsFromTrust a) (Money a)+trustGrossCapitalGain+ = lens _trustGrossGain (\s b -> s { _trustGrossGain = b })++trustCapitalLossesApplied :: Lens' (CapitalGainsFromTrust a) (Money a)+trustCapitalLossesApplied+ = lens _trustLossesAppl (\s b -> s { _trustLossesAppl = b })++trustCGTDiscountApplied :: Lens' (CapitalGainsFromTrust a) (Money a)+trustCGTDiscountApplied+ = lens _trustCGTDiscAppl (\s b -> s { _trustCGTDiscAppl = b })++trustCGTSmallBusinessConcessionApplied :: Lens' (CapitalGainsFromTrust a) (Money a)+trustCGTSmallBusinessConcessionApplied+ = lens _trustSBConcAppl (\s b -> s { _trustSBConcAppl = b })+++-- | Produce CGT events representing grossed-up capital gains from+-- the trust distribution. Could produce multiple events, in cases where+-- the CGT discount or small business concessions have been applied to+-- only a proportion of the distributed gain.+--+prepTrustCGT :: (Fractional a, Eq a) => CapitalGainsFromTrust a -> [CGTEvent a]+prepTrustCGT trust =+ let+ gross = view trustGrossCapitalGain trust+ loss = view trustCapitalLossesApplied trust++ discount = view trustCGTDiscountApplied trust+ discountable = discount $* 2+ nonDiscountable = gross $-$ loss $-$ discountable++ sbc = view trustCGTSmallBusinessConcessionApplied trust+ sbcEligible = sbc $* 2+ sbcProportion = sbcEligible $/$ (gross $-$ loss $-$ discount)+ sbcCode = Just SmallBusinessActiveAssetReduction++ someDay = toEnum 0 :: Day -- ghastly hack, = 1858-11-17+ mk (t,c,x) = CGTEvent (CGTEventTrustDistribution t) c someDay x mempty+ in+ fmap mk . filter ((/=) mempty . view _3) $+ [ ( NotDiscountable, sbcCode, nonDiscountable $* sbcProportion )+ , ( NotDiscountable, Nothing, nonDiscountable $* (1 - sbcProportion) )+ , ( Discountable, sbcCode, discountable $* sbcProportion )+ , ( Discountable, Nothing, discountable $* (1 - sbcProportion) )+ ]+++applyLossesAndDiscounts+ :: (Fractional a, Ord a)+ => EntityType+ -> Money a -- ^ unapplied losses from previous income years+ -> [CGTEvent a]+ -> CGTVector a+applyLossesAndDiscounts etype carry l =+ let (discountableGain, nonDiscountableGain) =- over both (view capitalGain) (partition discountApplicable l)+ over both (foldMap capitalGain) (partition discountApplicable l) totalGain = discountableGain <> nonDiscountableGain totalLoss = foldMap capitalLoss l (nonDiscountableGainLossesApplied, unappliedLosses) = sub nonDiscountableGain (totalLoss <> carry) (discountableGainLossesApplied, finalUnappliedLosses) = sub discountableGain unappliedLosses- discount = discountableGainLossesApplied $* 0.5+ discount = discountableGainLossesApplied $* discountProportion etype discountedGain = nonDiscountableGainLossesApplied <> (discountableGainLossesApplied $-$ discount)-- lossesApplied = (totalLoss <> carry) $-$ finalUnappliedLosses- priorYearLossesApplied = min carry lossesApplied- currentYearLossesApplied = lossesApplied $-$ priorYearLossesApplied in- CGTAssessment- totalGain- totalLoss- currentYearLossesApplied- priorYearLossesApplied- finalUnappliedLosses- discount- discountedGain+ CGTVector totalGain totalLoss discountedGain finalUnappliedLosses discount --- | Total undiscounted gains and net gain/loss for tax assessment+discountProportion :: (Fractional a) => EntityType -> a+discountProportion etype = fromRational $ case etype of+ Individual -> 1 / 2+ Trust -> 1 / 2+ SuperFund -> 1 / 3+ Company -> 0+++--+-- CGTAssessment data type+--++data CGTScheduleCategory+ = CGTScheduleCategorySharesAUListed+ | CGTScheduleCategorySharesOther+ | CGTScheduleCategoryUnitsAUListed+ | CGTScheduleCategoryUnitsOther+ | CGTScheduleCategoryRealEstateAU+ | CGTScheduleCategoryRealEstateOther+ | CGTScheduleCategoryTrust+ | CGTScheduleCategoryCollectable+ | CGTScheduleCategoryOther+ | CGTScheduleCategoryDeferred+ deriving (Eq, Ord, Show)++scheduleCategory :: CGTEvent a -> CGTScheduleCategory+scheduleCategory ev =+ case view eventType ev of+ CGTEventTrustDistribution _+ -> CGTScheduleCategoryTrust+ _+ -> case preview (eventAsset . assetType) ev of+ Just AssetTypeSharesAUListed -> CGTScheduleCategorySharesAUListed+ Just AssetTypeSharesOther -> CGTScheduleCategorySharesOther+ Just AssetTypeUnitsAUListed -> CGTScheduleCategoryUnitsAUListed+ Just AssetTypeUnitsOther -> CGTScheduleCategoryUnitsOther+ Just AssetTypeRealEstateAU -> CGTScheduleCategoryRealEstateAU+ Just AssetTypeRealEstateOther -> CGTScheduleCategoryRealEstateOther+ Just AssetTypeCollectable -> CGTScheduleCategoryCollectable+ Just AssetTypePersonalUse -> CGTScheduleCategoryOther+ Just AssetTypeOther -> CGTScheduleCategoryOther+ Nothing -> CGTScheduleCategoryOther+ -- TODO transitional relief deferrals?+++-- | This data type stores the completed CGT computations for some+-- financial year. It includes all data required for the /CGT schedule/+-- and the tax return CGT section.+-- data CGTAssessment a = CGTAssessment { _totalCurrentYearCapitalGains :: Money a , _totalCurrentYearCapitalLosses :: Money a , _totalCurrentYearCapitalLossesApplied :: Money a , _totalPriorYearCapitalLossesApplied :: Money a- , _netCapitalLossesCarriedForward :: Money a+ , _capitalLossCarryForward :: CapitalLossCarryForward a , _totalCGTDiscountApplied :: Money a , _netCapitalGain :: Money a+ , _byCategory :: M.Map CGTScheduleCategory (Money a, Money a) }- deriving (Eq)+ deriving (Eq, Functor, Show) -- | A 'CGTAssessment' whose values are all zero nullCGTAssessment :: (Num a) => CGTAssessment a-nullCGTAssessment = CGTAssessment mempty mempty mempty mempty mempty mempty mempty--instance Functor CGTAssessment where- fmap f (CGTAssessment a b c d e g h) = CGTAssessment- (fmap f a)- (fmap f b)- (fmap f c)- (fmap f d)- (fmap f e)- (fmap f g)- (fmap f h)+nullCGTAssessment = CGTAssessment mempty mempty mempty mempty mempty mempty mempty mempty -- | __18A__ The net capital gain, or zero if a loss. cgtNetGain :: Lens' (CGTAssessment a) (Money a)@@ -217,16 +739,68 @@ cgtTotalCurrentYearGains = lens _totalCurrentYearCapitalGains (\s b -> s { _totalCurrentYearCapitalGains = b }) +cgtTotalCurrentYearLosses :: Lens' (CGTAssessment a) (Money a)+cgtTotalCurrentYearLosses =+ lens _totalCurrentYearCapitalLosses (\s b -> s { _totalCurrentYearCapitalLosses = b })++cgtTotalCurrentYearLossesApplied :: Lens' (CGTAssessment a) (Money a)+cgtTotalCurrentYearLossesApplied =+ lens _totalCurrentYearCapitalLossesApplied (\s b -> s { _totalCurrentYearCapitalLossesApplied = b })++cgtTotalCGTDiscountApplied :: Lens' (CGTAssessment a) (Money a)+cgtTotalCGTDiscountApplied =+ lens _totalCGTDiscountApplied (\s b -> s { _totalCGTDiscountApplied = b })++cgtTotalPriorYearLossesApplied :: Lens' (CGTAssessment a) (Money a)+cgtTotalPriorYearLossesApplied =+ lens _totalPriorYearCapitalLossesApplied (\s b -> s { _totalPriorYearCapitalLossesApplied = b })+ -- | __18V__ Net capital losses carried forward to later income years-cgtNetLossesCarriedForward :: Lens' (CGTAssessment a) (Money a)-cgtNetLossesCarriedForward =- lens _netCapitalLossesCarriedForward (\s b -> s { _netCapitalLossesCarriedForward = b })+cgtNetLossesCarriedForward :: (Num a) => Getter (CGTAssessment a) (Money a)+cgtNetLossesCarriedForward = to $ \o ->+ view (capitalLossCarryForward . capitalLossCarryForwardCollectables) o+ <> view (capitalLossCarryForward . capitalLossCarryForwardOther) o +cgtGainsAndLossesByCategory :: Lens' (CGTAssessment a) (M.Map CGTScheduleCategory (Money a, Money a))+cgtGainsAndLossesByCategory =+ lens _byCategory (\s b -> s { _byCategory = b })+++data CapitalLossCarryForward a = CapitalLossCarryForward+ { _carryC :: Money a -- losses on collectables+ , _carryO :: Money a -- all other losses+ }+ deriving (Eq, Functor, Show)++instance (Num a) => Semigroup (CapitalLossCarryForward a) where+ CapitalLossCarryForward lc lo <> CapitalLossCarryForward rc ro =+ CapitalLossCarryForward (lc <> rc) (lo <> ro)++instance (Num a) => Monoid (CapitalLossCarryForward a) where+ mempty = CapitalLossCarryForward mempty mempty+++-- | Create a new capital loss carry-forward object with+-- all components (collectables and other) set to zero.+--+-- @+-- newCapitalLossCarryForward = mempty+-- @+--+newCapitalLossCarryForward :: (Num a) => CapitalLossCarryForward a+newCapitalLossCarryForward = mempty++capitalLossCarryForwardCollectables :: Lens' (CapitalLossCarryForward a) (Money a)+capitalLossCarryForwardCollectables = lens _carryC (\s b -> s { _carryC = b })+capitalLossCarryForwardOther :: Lens' (CapitalLossCarryForward a) (Money a)+capitalLossCarryForwardOther = lens _carryO (\s b -> s { _carryO = b })++ -- | Types that have a carry-forward capital loss (either as an -- input or an output). class HasCapitalLossCarryForward a b where- capitalLossCarryForward :: Lens' (a b) (Money b)+ capitalLossCarryForward :: Lens' (a b) (CapitalLossCarryForward b) instance HasCapitalLossCarryForward CGTAssessment a where capitalLossCarryForward =- lens _netCapitalLossesCarriedForward (\s b -> s { _netCapitalLossesCarriedForward = b })+ lens _capitalLossCarryForward (\s b -> s { _capitalLossCarryForward = b })
src/Data/Tax/ATO/Common.hs view
@@ -26,8 +26,11 @@ module Data.Tax.ATO.Common (+ -- * Taxable entity types+ EntityType(..)+ -- * Tax tables- TaxTables(..)+ , TaxTables(..) -- * Classes , HasTaxableIncome(..)@@ -69,6 +72,13 @@ import Data.Tax.ATO.PrivateHealthInsuranceRebate import Data.Tax.ATO.FY ++-- | Some tax calculations depend on the type of entity being assessed.+data EntityType = Individual | Company | Trust | SuperFund+ deriving (Eq, Ord, Show)+++ -- | A set of tax tables for a particular financial year data TaxTables y a = TaxTables { ttIndividualIncomeTax :: Tax (Money a) (Money a)@@ -76,6 +86,8 @@ , ttMedicareLevySurcharge :: Tax (Money a) (Money a) , ttHelp :: Tax (Money a) (Money a) , ttSfss :: Tax (Money a) (Money a)+ -- ^ Ignored since FY2020 (uses consolidated rate from @ttHelp@).+ -- Just set it to @ttHelp@ or @undefined@. , ttAdditional :: Tax (Money a) (Money a) -- ^ Additional taxes and offsets that apply at EOY , ttPHIRebateRates :: PrivateHealthInsuranceRebateRates a@@ -183,9 +195,11 @@ class HasCentsPerKilometreMethod y where centsPerKilometre :: Num a => a -instance (2016 <= y, y <= 2025, FinancialYear y) => HasCentsPerKilometreMethod y where+instance (2016 <= y, y <= 2027, FinancialYear y) => HasCentsPerKilometreMethod y where centsPerKilometre = case fromProxy (Proxy @y) of- y | y == 2025 -> 88+ y | y == 2027 -> 88 -- TODO confirm+ | y == 2026 -> 88+ | y == 2025 -> 88 | y == 2024 -> 85 | y == 2023 -> 78 | y > 2020, y <= 2022 -> 72@@ -214,12 +228,14 @@ class HasFixedRateMethod y where fixedRateMethodCentsPerHour :: Num a => a -instance HasFixedRateMethod 2023 where- fixedRateMethodCentsPerHour = 67-instance HasFixedRateMethod 2024 where- fixedRateMethodCentsPerHour = 67-instance HasFixedRateMethod 2025 where- fixedRateMethodCentsPerHour = 70+instance (2023 <= y, y <= 2027, FinancialYear y) => HasFixedRateMethod y where+ fixedRateMethodCentsPerHour = case fromProxy (Proxy @y) of+ y | y == 2023 -> 67+ | y == 2024 -> 67+ | y == 2025 -> 70+ | y == 2026 -> 70+ | y == 2027 -> 70 -- TODO confirm+ | otherwise -> 0 -- can't happen -- | The updated fixed rate method available from the 2022–23 income -- year. It covers:
src/Data/Tax/ATO/FY/FY2025.hs view
@@ -17,10 +17,6 @@ {-# LANGUAGE DataKinds #-} -- | Tax tables for 2024–25 financial year.------ These data are NOT YET FINAL. We are waiting on federal budget--- (25 March 2025) to confirm Medicare Levy thresholds, and the PHI--- rebate adjustment factor (~1 April 2025). module Data.Tax.ATO.FY.FY2025 (FY, fyProxy, tables) where import Data.Proxy@@ -79,8 +75,8 @@ , (135000, 0.37 - 0.30) , (190000, 0.45 - 0.37) ] --- Rebate adjustment factor = TBC (currently re-using FY2024 rates)--- However, the /thresholds/ did change this year.+-- Rebate adjustment factor = 0.987+-- https://www.health.gov.au/news/phi-circulars/phi-2125-private-health-insurance-rebate-adjustment-factor-effective-1-april-2025 privateHealthInsuranceRebateRates :: (Fractional a) => PrivateHealthInsuranceRebateRates a privateHealthInsuranceRebateRates =
+ src/Data/Tax/ATO/FY/FY2026.hs view
@@ -0,0 +1,84 @@+-- This file is part of hs-tax-ato+-- Copyright (C) 2025 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/>.++{-# LANGUAGE DataKinds #-}++-- | Tax tables for 2025–26 financial year.+--+-- Apart from the usual Medicare levy and private health insurance+-- rebate adjustments, study loan repayments changed to a marginal+-- tax (previously a threshold tax).+--+module Data.Tax.ATO.FY.FY2026 (FY, fyProxy, tables) where++import Data.Proxy+import Data.Tax+import Data.Tax.ATO.Common+import Data.Tax.ATO.PrivateHealthInsuranceRebate+import qualified Data.Tax.ATO.FY.FY2025 as FY2025++type FY = 2026+fyProxy :: Proxy FY+fyProxy = Proxy++help :: (Fractional a, Ord a) => Tax (Money a) (Money a)+help =+ lesserOf+ ( flat 0.10 )+ ( marginal'+ [ ( 67_000, 0.15 )+ , (125_000, 0.17 - 0.15)+ ]+ )++medicare :: (Fractional a) => MedicareLevyRatesAndThresholds a+medicare = MedicareLevyRatesAndThresholds+ { medicareLevyRate = 0.02+ , medicareLevyThresholdIndividual = Money 28011+ , medicareLevyThresholdIndividualSeniorAndPensioner = Money 44268+ , medicareLevyThresholdFamily = Money 47238+ , medicareLevyThresholdFamilySeniorAndPensioner = Money 61623+ , medicareLevyThresholdDependentChildIncrease = Money 4338+ }++-- | Medicare levy surcharge thresholds for 2025–26+medicareLevySurcharge :: (Fractional a, Ord a) => Tax (Money a) (Money a)+medicareLevySurcharge =+ threshold (Money 101000) 0.01+ <> threshold (Money 118000) 0.0025+ <> threshold (Money 158000) 0.0025++-- Rebate adjustment factor = 0.993.+-- https://www.health.gov.au/news/phi-circulars/phi-1226-private-health-insurance-rebate-adjustment-factor-effective-1-april-2026+privateHealthInsuranceRebateRates+ :: (Fractional a) => PrivateHealthInsuranceRebateRates a+privateHealthInsuranceRebateRates =+ [ (101000, (0.24288, 0.24118), (0.28337, 0.28139), (0.32385, 0.32158) )+ , (118000, (0.16192, 0.16079), (0.20240, 0.20098), (0.24288, 0.24118) )+ , (158000, (0.08095, 0.08095), (0.12143, 0.12058), (0.16192, 0.16079) )+ ]++tables :: (Ord a, Fractional a) => TaxTables FY a+tables = TaxTables+ (ttIndividualIncomeTax FY2025.tables)+ medicare+ medicareLevySurcharge+ help+ help++ lowIncomeTaxOffset2021++ privateHealthInsuranceRebateRates
+ src/Data/Tax/ATO/FY/FY2027.hs view
@@ -0,0 +1,95 @@+-- 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/>.++{-# LANGUAGE DataKinds #-}++-- | Tax tables for 2026–27 financial year.+--+-- The 16% personal income tax bracket drops to 15%.+--+-- These data are NOT YET FINAL. We await the federal+-- budget to confirm Medicare Levy thresholds, and the PHI+-- rebate adjustment factor (~1 April 2027), and there could+-- be other changes.+--+module Data.Tax.ATO.FY.FY2027 (FY, fyProxy, tables) where++import Data.Proxy+import Data.Tax+import Data.Tax.ATO.Common+import Data.Tax.ATO.PrivateHealthInsuranceRebate++type FY = 2027+fyProxy :: Proxy FY+fyProxy = Proxy++-- | In 2026–27 the 16% rate dropped to 15%+individualIncomeTax :: (Fractional a, Ord a) => Tax (Money a) (Money a)+individualIncomeTax = marginal'+ [ (18200, 0.15)+ , (45000, 0.30 - 0.15)+ , (135000, 0.37 - 0.30)+ , (190000, 0.45 - 0.37) ]++help :: (Fractional a, Ord a) => Tax (Money a) (Money a)+help =+ lesserOf+ ( flat 0.10 )+ ( marginal'+ [ ( 69_528, 0.15 ) -- TODO confirm these numbers+ , (129_717, 0.17 - 0.15)+ ]+ )++-- TODO update (budget night)+medicare :: (Fractional a) => MedicareLevyRatesAndThresholds a+medicare = MedicareLevyRatesAndThresholds+ { medicareLevyRate = 0.02+ , medicareLevyThresholdIndividual = Money 28011+ , medicareLevyThresholdIndividualSeniorAndPensioner = Money 44268+ , medicareLevyThresholdFamily = Money 47238+ , medicareLevyThresholdFamilySeniorAndPensioner = Money 61623+ , medicareLevyThresholdDependentChildIncrease = Money 4338+ }++-- | Medicare levy surcharge thresholds for 2025–26+-- TODO update+medicareLevySurcharge :: (Fractional a, Ord a) => Tax (Money a) (Money a)+medicareLevySurcharge =+ threshold (Money 101000) 0.01+ <> threshold (Money 118000) 0.0025+ <> threshold (Money 158000) 0.0025++-- TODO update (~April 1 PHI circular)+privateHealthInsuranceRebateRates+ :: (Fractional a) => PrivateHealthInsuranceRebateRates a+privateHealthInsuranceRebateRates =+ [ (101000, (0.24288, 0.24118), (0.28337, 0.28139), (0.32385, 0.32158) )+ , (118000, (0.16192, 0.16079), (0.20240, 0.20098), (0.24288, 0.24118) )+ , (158000, (0.08095, 0.08095), (0.12143, 0.12058), (0.16192, 0.16079) )+ ]++tables :: (Ord a, Fractional a) => TaxTables FY a+tables = TaxTables+ individualIncomeTax+ medicare+ medicareLevySurcharge+ help+ help++ lowIncomeTaxOffset2021++ privateHealthInsuranceRebateRates
src/Data/Tax/ATO/Pretty.hs view
@@ -26,16 +26,21 @@ module Data.Tax.ATO.Pretty ( summariseTaxReturnInfo+ , summariseCGTSchedule , summariseAssessment , formatMoney ) where import Data.List.NonEmpty as NE (NonEmpty, groupAllWith, head) -import Control.Lens (ALens', cloneLens, foldOf, view, views)+import Control.Lens+ ( ALens'+ , _2, allOf, at, both, cloneLens, foldOf, nullOf, view, views+ ) import qualified Text.PrettyPrint as P import Data.Tax.ATO+import Data.Tax.ATO.Common (EntityType(..)) import Data.Tax.ATO.CGT @@ -58,20 +63,48 @@ putCommas (a:b:c:d:rest) | d /= '-' = a:b:c:',':putCommas (d:rest) putCommas rest = rest +omitIfZero :: ((a, Money Rational) -> P.Doc) -> (a, Money Rational) -> P.Doc+omitIfZero = omitIf ((== mempty) . snd)++omitIf :: (a -> Bool) -> (a -> P.Doc) -> a -> P.Doc+omitIf test f a+ | test a = P.empty+ | otherwise = f a++data Formatter a = Formatter Int (a -> P.Doc)++prepend :: String -> Formatter a -> Formatter a+prepend s (Formatter w f) = Formatter (w + length s) ((P.text s <>) . f)++append :: String -> Formatter a -> Formatter a+append s (Formatter w f) = Formatter (w + length s) ((<> P.text s) . f)++moneyFormatter :: Formatter (Money Rational)+moneyFormatter = Formatter colWidthMoney formatMoney++-- | Make a formatter that has a same width as the given formatter,+-- but outputs blank space.+blank :: Formatter a -> Formatter a+blank (Formatter w _) = Formatter w (\_ -> P.text $ replicate w ' ')++ twoCol :: (P.Doc, Money Rational) -> P.Doc-twoCol (label, value) = label P.$$ P.nest (80 - colWidthMoney) (formatMoney value)+twoCol = twoCol' id moneyFormatter -omitIfZero :: ((a, Money Rational) -> P.Doc) -> (a, Money Rational) -> P.Doc-omitIfZero f rec@(_,x)- | x == mempty = P.empty- | otherwise = f rec+twoCol' :: (a -> P.Doc) -> Formatter b -> (a, b) -> P.Doc+twoCol' fa (Formatter wb fb) (a, b) =+ fa a+ P.$$ P.nest (80 - wb) (fb b) threeCol :: (P.Doc, Money Rational, Money Rational) -> P.Doc-threeCol (label, v1, v2) =- label- P.$$ P.nest (80 - 2 * colWidthMoney) (formatMoney v1)- P.<> formatMoney v2+threeCol = threeCol' id moneyFormatter moneyFormatter +threeCol' :: (a -> P.Doc) -> Formatter b -> Formatter c -> (a, b, c) -> P.Doc+threeCol' fa (Formatter wb fb) (Formatter wc fc) (a, b, c) =+ fa a+ P.$$ P.nest (80 - wb - wc) (fb b)+ P.<> fc c+ -- | 3-column layout with rightmost column blank threeColLeft :: (P.Doc, Money Rational) -> P.Doc threeColLeft (label, v1) =@@ -84,27 +117,31 @@ summariseTaxReturnInfo :: TaxReturnInfo y Rational -> P.Doc summariseTaxReturnInfo info = "Income"- P.$+$ vcatWith threeCol+ P.$+$ vcatWith (omitIf (allOf both (== mempty)) threeCol) [ ( " 1 Salary or wages" , view (paymentSummariesIndividualNonBusiness . taxWithheld) info , view (paymentSummariesIndividualNonBusiness . taxableIncome) info ) , (" 10 Interest" , view (interest . taxWithheld) info, view (interest . taxableIncome) info) ]- P.$+$ " 11 Dividends"+ P.$+$ ( if nullOf dividends info then P.empty else " 11 Dividends" ) P.$+$ views dividends summariseDividends info P.$+$ views ess summariseESS info P.$+$ summariseCGT info- P.$+$ vcatWith twoCol+ P.$+$ vcatWith (omitIfZero twoCol) [ (" 20M Other net foreign source income" , view foreignIncome info) ]+ P.$+$ views otherIncome summariseOtherIncome info+ P.$+$ views businessAndProfessionalItems summariseBPI info P.$+$ "Deductions" P.$+$ P.vcat (uncurry (summariseDeduction (view deductions info)) <$> deductionsTable)- P.$+$ "Tax offsets"- P.$+$ vcatWith threeColLeft- [ (" 20O Foreign income tax offset" , view (offsets . foreignTaxOffset) info)- ] + P.$+$ ( \doc -> if P.isEmpty doc then P.empty else "Tax offsets" P.$+$ doc )+ ( vcatWith (omitIfZero threeColLeft)+ [ (" 20O Foreign income tax offset" , view (offsets . foreignTaxOffset) info)+ ]+ )+ summariseDividends :: [Dividend Rational] -> P.Doc summariseDividends = vcatWith (threeCol . prep)@@ -122,23 +159,132 @@ summariseESS l = " 12 Employee share schemes" P.$+$ P.vcat- [ twoCol+ [ omitIfZero twoCol ( " D Discount from upfront schemes - eligible for reduction" , foldOf (traverse . essTaxedUpfrontReduction) l )- , twoCol+ , omitIfZero twoCol ( " E Discount from upfront schemes - ineligible for reduction" , foldOf (traverse . essTaxedUpfrontNoReduction) l )- , twoCol+ , omitIfZero twoCol ( " F Discount from deferral schemes" , foldOf (traverse . essDeferral) l )- , threeColLeft+ , omitIfZero threeColLeft ( " C TFN amounts withheld from discounts" , foldOf (traverse . essTFNAmounts) l )- , twoCol+ , omitIfZero twoCol ( " A Foreign source discounts" , foldOf (traverse . essForeignSourceDiscounts) l ) ] +summariseOtherIncome :: OtherIncome Rational -> P.Doc+summariseOtherIncome oi | view taxableIncome oi == Money (0 :: Rational)+ = P.empty+summariseOtherIncome oi =+ P.vcat+ [ " 24 Other income"+ , case view otherIncomeCategory1 oi of+ [] -> P.empty+ [(s, x)] -> twoCol (" Y - Category 1 - " <> P.text s, x)+ l -> twoCol (" Y - Category 1 - <multiple>", foldOf (traverse . _2) l)+ , case view otherIncomeCategory2 oi of+ [] -> P.empty+ [(s, x)] -> twoCol (" X - Category 2 (ATO interest) - " <> P.text s, x)+ l -> twoCol (" X - Category 2 (ATO interest) - <multiple>", foldOf (traverse . _2) l)+ , omitIfZero twoCol (" R - Category 3 (FHSS)", view otherIncomeCategory3 oi)+ , case view otherIncomeCategory4 oi of+ [] -> P.empty+ [(s, x)] -> twoCol (" V - Category 4 - " <> P.text s, x)+ l -> twoCol (" V - Category 4 - <multiple>", foldOf (traverse . _2) l)+ , omitIfZero threeColLeft+ (" E Tax withheld - LSPIA", view taxWithheldLumpSumPaymentsInArrears oi)+ , omitIfZero threeColLeft+ (" Z Taxable professional income", view taxableProfessionalIncome oi)+ , omitIfZero threeColLeft+ (" S Tax withheld - FHSS released amount", view taxWithheldAssessableFHSSReleasedAmount oi)+ ]++summariseBPI :: BusinessAndProfessionalItemsSchedule Rational -> P.Doc+summariseBPI bpi =+ views personalServicesIncome summarisePSI bpi++summarisePSI :: PersonalServicesIncome Rational -> P.Doc+summarisePSI psi | netPSI psi == mempty+ = P.empty+summarisePSI psi =+ " P1 Personal services income"+ P.$+$ vcatWith (omitIfZero twoCol)+ [ (" M PSI - voluntary agreement", view psiVoluntaryAgreement psi)+ , (" N PSI - where Australian business number not quoted", view psiWhereABNNotQuoted psi)+ , (" O PSI - labour hire or other specified payments", view psiLabourHireOrOtherSpecifiedPayments psi)+ , (" J PSI - other", view psiOther psi)+ , (" K Deductions for payments to associates for principal work", view psiDeductionsForPaymentsToAssociatesForPrincipalWork psi)+ , (" L Total amount of other deductions against PSI", view psiTotalAmountOfOtherDeductions psi)+ ]+ P.$+$ twoCol (" Net PSI", netPSI psi)++summariseCGTSchedule :: CGTAssessment Rational -> P.Doc+summariseCGTSchedule o = P.vcat+ [ "1 Current year capital gains and capital losses"+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategorySharesAUListed . traverse) o+ in print3 "A" "K" (" Australian listed shares", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategorySharesOther . traverse) o+ in print3 "B" "L" (" Other shares", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryUnitsAUListed . traverse) o+ in print3 "C" "M" (" Australian listed units", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryUnitsOther . traverse) o+ in print3 "D" "N" (" Other units", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryRealEstateAU . traverse) o+ in print3 "E" "O" (" Australian real estate", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryRealEstateOther . traverse) o+ in print3 "F" "P" (" Other real estate", g, l)+ , let (g,_) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryTrust . traverse) o+ in print2_ "G" (" Capital gains from trusts", g)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryCollectable . traverse) o+ in print3 "H" "Q" (" Collectables", g, l)+ , let (g,l) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryOther . traverse) o+ in print3 "I" "R" (" Other CGT assets and CGT events", g, l)+ , let (g,_) = view (cgtGainsAndLossesByCategory . at CGTScheduleCategoryOther . traverse) o+ in print2_ "S" (" Previously deferred CGT relief", g)+ , print2_ "J" (" Total current year capital gains", view cgtTotalCurrentYearGains o)++ , P.empty+ , "2 Capital losses"+ , print2 "A" (" Total current year capital losses", view cgtTotalCurrentYearLosses o)+ , print2 "B" (" Total current year capital losses applied", view cgtTotalCurrentYearLossesApplied o)+ , print2 "C" (" Total prior year net capital losses applied", view cgtTotalPriorYearLossesApplied o)+ , let total = view cgtTotalCurrentYearLossesApplied o <> view cgtTotalPriorYearLossesApplied o+ in print2 "E" (" Total capital losses applied", total)++ , P.empty+ , "3 Unapplied net capital losses carried forward"+ , print2 "A"+ (" Net capital losses from collectables carried forward"+ , view (capitalLossCarryForward . capitalLossCarryForwardCollectables) o)+ , print2 "B"+ (" Other capital losses carried forward"+ , view (capitalLossCarryForward . capitalLossCarryForwardOther) o)++ , P.empty+ , "4 CGT discount"+ , print2 "A" (" Total CGT discount applied", view cgtTotalCGTDiscountApplied o)++ , P.empty+ , "6 Net capital gain"+ , print2 "A" (" Net capital gain", view cgtNetGain o)+ ]+ where+ prepLabel s = " " <> s <> " $"+ print3 labelL labelR =+ threeCol' id+ (prepend (prepLabel labelL) moneyFormatter)+ (prepend (prepLabel labelR) moneyFormatter)+ print2_ labelL (k, v) =+ threeCol' id+ (prepend (prepLabel labelL) moneyFormatter)+ (blank $ prepend (prepLabel labelL) moneyFormatter)+ (k, v, mempty)+ print2 label = twoCol' id (prepend (prepLabel label) moneyFormatter)+ summariseCGT :: TaxReturnInfo y Rational -> P.Doc summariseCGT info | o == nullCGTAssessment = P.empty@@ -150,7 +296,7 @@ , threeColLeft (" V Net capital losses carried forward", view cgtNetLossesCarriedForward o) ] where- o = assessCGTEvents (view capitalLossCarryForward info) (view cgtEvents info)+ o = assessCGTEvents Individual (view capitalLossCarryForward info) (view cgtEvents info) deductionsTable :: [(ALens' (Deductions Rational) (Money Rational), String)] deductionsTable =@@ -207,4 +353,4 @@ ] P.$+$ P.text (replicate 80 '-') P.$+$ "Result of this notice" P.$$ P.nest colWidthLabel (views taxBalance formatMoney assessment)- P.$+$ "Net capital loss to carry forward" P.$$ P.nest colWidthLabel (views (taxCGTAssessment . capitalLossCarryForward) formatMoney assessment)+ P.$+$ "Net capital loss to carry forward" P.$$ P.nest colWidthLabel (views (taxCGTAssessment . cgtNetLossesCarriedForward) formatMoney assessment)
tax-ato.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: tax-ato-version: 2025.1+version: 2026.1 synopsis: Tax types and computations for Australia description: This library provides types and tax computations for tax@@ -11,10 +11,10 @@ license-file: LICENSE author: Fraser Tweedale maintainer: frase@frase.id.au-copyright: Copyright (C) 2018-2023 Fraser Tweedale+copyright: Copyright (C) 2018-2026 Fraser Tweedale category: Finance build-type: Simple-tested-with: GHC ==9.6.7 || ==9.8.4 || ==9.10.2 || ==9.12.2+tested-with: GHC ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.4 || ==9.14.1 extra-source-files: .hlint.yaml extra-doc-files:@@ -56,6 +56,7 @@ exposed-modules: Data.Tax.ATO Data.Tax.ATO.ABN+ Data.Tax.ATO.BPI Data.Tax.ATO.TFN Data.Tax.ATO.CGT Data.Tax.ATO.Common@@ -74,8 +75,11 @@ Data.Tax.ATO.FY.FY2023 Data.Tax.ATO.FY.FY2024 Data.Tax.ATO.FY.FY2025+ Data.Tax.ATO.FY.FY2026+ Data.Tax.ATO.FY.FY2027 build-depends:+ , containers >= 0.6 && < 0.9 , lens >= 4.18 && < 6- , time >= 1.11 && < 1.15+ , time >= 1.11 && < 1.16 , tax >= 0.2 && < 0.3 , pretty >= 1.1.3.6 && < 2