예제 #1
0
        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));
        }
예제 #2
0
        private decimal GetTotalHomeDutyAmount(PropertySaleInformation propertySaleInformation, decimal fullPurchasePrice, Fraction buyerFractionWithHomeDiscount)
        {
            decimal homeDutiableAmount = Math.Min(fullPurchasePrice, 350000);
            decimal homeDuty           = homeConsessionService.CalculateTieredRate(homeDutiableAmount);

            homeDuty *= buyerFractionWithHomeDiscount;
            return(homeDuty);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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;
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        /// <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 }));
            }
        }