private decimal GetTotalVacantLandDeductionAmount(PropertySaleInformation propertySaleInformation, decimal fullPurchasePrice, decimal fullTransferDuty, Fraction buyerFractionWhereFirstHomeDutyApplies) { decimal firstHomeVacantLandTransferDutyPortion = fullTransferDuty * buyerFractionWhereFirstHomeDutyApplies; decimal firstHomeVacantLandRebate = firstHomeVacantLandDiscountService.CalculateDiscountRate(fullPurchasePrice * buyerFractionWhereFirstHomeDutyApplies); return(Math.Min(firstHomeVacantLandTransferDutyPortion, firstHomeVacantLandRebate)); }
private decimal GetTotalHomeDutyAmount(PropertySaleInformation propertySaleInformation, decimal fullPurchasePrice, Fraction buyerFractionWithHomeDiscount) { decimal homeDutiableAmount = Math.Min(fullPurchasePrice, 350000); decimal homeDuty = homeConsessionService.CalculateTieredRate(homeDutiableAmount); homeDuty *= buyerFractionWithHomeDiscount; return(homeDuty); }
private decimal GetTotalFirstHomeDutyAmount(PropertySaleInformation propertySaleInformation, decimal fullPurchasePrice, Fraction buyerFractionWhereHomeDutyApplies) { decimal firstHomeDuty = homeConsessionService.CalculateTieredRate(fullPurchasePrice); decimal firstHomeDiscount = firstHomeDiscountService.CalculateDiscountRate(propertySaleInformation.PurchasePrice); firstHomeDuty = Math.Max(firstHomeDuty - firstHomeDiscount, 0M); firstHomeDuty *= buyerFractionWhereHomeDutyApplies; return(firstHomeDuty); }
private decimal GetFirstHomeOwnerGrant(PropertySaleInformation propertySaleInformation, PropertyBuyer buyer) { // https://firsthomeowners.initiatives.qld.gov.au/eligibility.php if (propertySaleInformation.PurchasePrice < 750000 && propertySaleInformation.PropertyType == PropertyType.NewHome) { if (BuyerFirstHomeDutyApplies(buyer)) { return(15000M); } } return(0M); }
public void StampDutyCalculatorCorrectFees(PropertySaleInformation saleInfo, FinancialResults expectedResults, decimal expectedTotalFees, decimal transferDutyPayable, decimal expectedTotalConcessions, int scenarioId) { var calc = new StampDutyService(); FinancialResults results = calc.Calculate(saleInfo); _output.WriteLine($"Testing scenario {scenarioId}"); try { FinancialResultCategory stampDutySummaryCategory = results.Categories.Where(c => c.Title == "Stamp Duty Summary").SingleOrDefault(); if (stampDutySummaryCategory != null) { Assert.Equal(transferDutyPayable, stampDutySummaryCategory.Total); } Assert.Equal(expectedTotalFees, results.Categories.Where(c => c.Title == "Fees").Single().Total); Assert.Equal(expectedTotalConcessions, results.Categories.Where(c => c.Title == "Concessions").Single().Total); Assert.Equal(expectedResults, results); } catch { var settings = new Newtonsoft.Json.JsonSerializerSettings(); settings.Formatting = Newtonsoft.Json.Formatting.Indented; var serialiser = Newtonsoft.Json.JsonSerializer.Create(settings); using (TextWriter writer = new StringWriter()) { serialiser.Serialize(writer, expectedResults); _output.WriteLine("Expected financial results:"); _output.WriteLine(writer.ToString()); } using (TextWriter writer = new StringWriter()) { serialiser.Serialize(writer, results); _output.WriteLine("Actual financial result:"); _output.WriteLine(writer.ToString()); } throw; } }
private decimal GetTotalDeductionAmount(PropertySaleInformation propertySaleInformation, decimal fullPurchasePrice, Fraction buyerFractionHomeDutyDeductionApplies) { if (propertySaleInformation.Buyers.Count() > 0) { decimal deductionBase = (decimal)(buyerFractionHomeDutyDeductionApplies * Math.Min(350000M, fullPurchasePrice)); foreach (PropertyBuyer buyer in propertySaleInformation.Buyers) { if (buyer.FirstHomeBuyer) { deductionBase = (decimal)(buyerFractionHomeDutyDeductionApplies * fullPurchasePrice); break; } } decimal totalDeductionAmount = transferDutyService.CalculateTieredRate(deductionBase); return(totalDeductionAmount); } return(0M); }
/// <summary> /// Calculates the specified property sale information. /// Additional references for calculations: /// - https://www.business.qld.gov.au/industries/building-property-development/titles-property-surveying/titles-property/fee-calculator /// - https://www.dnrm.qld.gov.au/__data/assets/pdf_file/0007/369592/titles-registry-lodgement-fees-2016-2017.pdf /// </summary> /// <param name="propertySaleInformation">The property sale information.</param> /// <returns></returns> /// <exception cref="InvalidPropertySaleInformationException"> /// Transfer duty can currently only be calculated for Queensland. /// or /// </exception> public FinancialResults Calculate(PropertySaleInformation propertySaleInformation) { if (propertySaleInformation.State != State.QLD) { throw new InvalidPropertySaleInformationException("Transfer duty can currently only be calculated for Queensland."); } Fraction allTransfereesInterests = propertySaleInformation.Buyers.Sum(b => b.Shares); if (allTransfereesInterests > 1) { throw new InvalidPropertySaleInformationException($"The combined fraction of ownership for all buyers is {allTransfereesInterests} which is more than 1. All buyers fractions must be less than or equal to 1."); } var feeLineItems = new List <FinancialResultLineItem>(); var concessionLineItems = new List <FinancialResultLineItem>(); var stampDutySummaryLineItems = new List <FinancialResultLineItem>(); decimal propertyDutiableAmount = propertySaleInformation.PurchasePrice * allTransfereesInterests; decimal fullMorgtgageFee = 192M; decimal fullTransferFee = 192 + transferFeeService.CalculateTieredRate(propertySaleInformation.PurchasePrice); decimal fullTransferDuty = transferDutyService.CalculateTieredRate(propertyDutiableAmount); decimal dutyPayable = 0M; Fraction buyerFractionWhereHomeDutyApplies = propertySaleInformation.Buyers.Where(b => BuyerHomeDutyApplies(b)).Sum(b => b.Shares); Fraction buyerFractionWhereHomeDutyAppliesInverse = buyerFractionWhereHomeDutyApplies.InverseOrZero(); Fraction buyerFractionHomeDutyDeductionApplies = propertySaleInformation.Buyers.Where(b => BuyerHomeDutyDeductionApplies(b)).Sum(b => b.Shares); Fraction buyerFractionHomeDutyDeductionAppliesInverse = buyerFractionHomeDutyDeductionApplies.InverseOrZero(); Fraction buyerFractionWhereFirstHomeDutyApplies = propertySaleInformation.Buyers.Where(b => BuyerFirstHomeDutyApplies(b)).Sum(b => b.Shares); Fraction buyerFractionWhereFirstHomeDutyAppliesInverse = buyerFractionWhereFirstHomeDutyApplies.InverseOrZero(); decimal totalFirstHomeVacantLandDeduction = 0M; decimal totalDeductionAmount = 0M; decimal totalHomeDuty = 0M; decimal totalFirstHomeDuty = 0M; if (propertySaleInformation.PropertyType == PropertyType.VacantLand) { totalFirstHomeVacantLandDeduction = GetTotalVacantLandDeductionAmount(propertySaleInformation, propertySaleInformation.PurchasePrice, fullTransferDuty, buyerFractionWhereFirstHomeDutyApplies); dutyPayable = fullTransferDuty - totalFirstHomeVacantLandDeduction; } else { totalDeductionAmount = GetTotalDeductionAmount(propertySaleInformation, propertySaleInformation.PurchasePrice, buyerFractionHomeDutyDeductionApplies); totalHomeDuty = GetTotalHomeDutyAmount(propertySaleInformation, propertySaleInformation.PurchasePrice, buyerFractionWhereHomeDutyApplies); totalFirstHomeDuty = GetTotalFirstHomeDutyAmount(propertySaleInformation, propertySaleInformation.PurchasePrice, buyerFractionWhereFirstHomeDutyApplies); dutyPayable = fullTransferDuty - totalDeductionAmount + totalHomeDuty + totalFirstHomeDuty; } feeLineItems.Add(new FinancialResultLineItem($"Mortgage Fee", fullMorgtgageFee)); feeLineItems.Add(new FinancialResultLineItem($"Transfer Fee", fullTransferFee)); feeLineItems.Add(new FinancialResultLineItem($"Transfer Duty Payable", dutyPayable)); Fraction allTransfereesInterestsInverse = allTransfereesInterests.InverseOrZero(); int buyerCount = 0; foreach (PropertyBuyer buyer in propertySaleInformation.Buyers) { buyerCount++; decimal buyerTransferDuty = fullTransferDuty * buyer.Shares * allTransfereesInterestsInverse; if (propertySaleInformation.PropertyType == PropertyType.VacantLand) { if (BuyerFirstHomeDutyApplies(buyer)) { buyerTransferDuty = buyerTransferDuty - totalFirstHomeVacantLandDeduction * buyer.Shares * buyerFractionWhereFirstHomeDutyAppliesInverse; } } else { if (BuyerHomeDutyDeductionApplies(buyer)) { buyerTransferDuty = buyerTransferDuty - totalDeductionAmount * buyer.Shares * buyerFractionHomeDutyDeductionAppliesInverse; } if (BuyerHomeDutyApplies(buyer)) { buyerTransferDuty = buyerTransferDuty + totalHomeDuty * buyer.Shares * buyerFractionWhereHomeDutyAppliesInverse; } if (BuyerFirstHomeDutyApplies(buyer)) { buyerTransferDuty = buyerTransferDuty + totalFirstHomeDuty * buyer.Shares * buyerFractionWhereFirstHomeDutyAppliesInverse; } } stampDutySummaryLineItems.Add(new FinancialResultLineItem($"Buyer {buyer.BuyerNumber}", buyerTransferDuty)); decimal foreignBuyerDuty = GetForeignBuyerDuty(propertySaleInformation.PurchasePrice, buyer); if (foreignBuyerDuty > 0) { feeLineItems.Add(new FinancialResultLineItem($"Buyer {buyer.BuyerNumber} - Foreign Buyers Duty", foreignBuyerDuty)); } decimal firstHomeOwnerGrant = GetFirstHomeOwnerGrant(propertySaleInformation, buyer); if (firstHomeOwnerGrant > 0) { concessionLineItems.Add(new FinancialResultLineItem($"Buyer {buyer.BuyerNumber} - First Home Owners Grant", firstHomeOwnerGrant)); } } // Wrap up categories to be returned FinancialResultCategory feeCategory = new FinancialResultCategory("Fees", feeLineItems.ToArray()); FinancialResultCategory concessionCategory = new FinancialResultCategory("Concessions", concessionLineItems.ToArray()); FinancialResultCategory stampDutySummaryCategory = new FinancialResultCategory("Stamp Duty Summary", stampDutySummaryLineItems.ToArray()); if (buyerCount > 1 && stampDutySummaryCategory.Total > 0) { return(new FinancialResults(new[] { feeCategory, stampDutySummaryCategory, concessionCategory })); } else { return(new FinancialResults(new[] { feeCategory, concessionCategory })); } }