示例#1
0
        public void FinancialResultCategoryEquality()
        {
            var items1 = new[] {
                new FinancialResultLineItem("Title1", 10),
                new FinancialResultLineItem("Title2", 10)
            };

            var items2 = new[] {
                new FinancialResultLineItem("Title1", 10),
                new FinancialResultLineItem("Title2", 10)
            };

            FinancialResultCategory category1 = new FinancialResultCategory("catTitle", items1);
            FinancialResultCategory category2 = new FinancialResultCategory("catTitle", items2);

            Assert.Equal(category1, category2);
        }
示例#2
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;
            }
        }
示例#3
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 }));
            }
        }