From 16bc0955d34a66d48d643fd3c274093a61927d93 Mon Sep 17 00:00:00 2001 From: Mark Gould Date: Thu, 5 Jun 2025 12:55:56 -0500 Subject: [PATCH] Adds 2025 tax limitation calculation support Extends the tax limitation calculator to support the 2025 calculation year, including handling for SB4 and SB23 exemptions. Introduces toggles to enable/disable SB4 and SB23 calculations, reflecting the preliminary nature of the 2025 support. Updates the UI and calculation logic to accommodate new exemptions and their corresponding reductions in the running total. --- .../TaxLimitation/CalculationParameters.cs | 3 +- .../TaxLimitation/CalculationResult.cs | 10 +- src/TaxTools.Core/TaxLimitation/Calculator.cs | 88 ++++++++--- .../TaxLimitationCalculator/DetailModel.cs | 13 +- .../Pages/TaxLimitationCalculator/Index.razor | 8 +- .../LimitationCalculator.razor | 145 ++++++++++-------- .../TaxRateConditionallyRequired.cs | 2 +- 7 files changed, 172 insertions(+), 97 deletions(-) diff --git a/src/TaxTools.Core/TaxLimitation/CalculationParameters.cs b/src/TaxTools.Core/TaxLimitation/CalculationParameters.cs index e89e43a..85aee57 100644 --- a/src/TaxTools.Core/TaxLimitation/CalculationParameters.cs +++ b/src/TaxTools.Core/TaxLimitation/CalculationParameters.cs @@ -4,7 +4,8 @@ public class CalculationParameters { public int ExemptionQualifyYear { get; set; } public int CalculationYear { get; set; } - public bool EnableSB2Calculation { get; set; } + public bool EnableSB4Calculation { get; set; } + public bool EnableSB23Calculation { get; set; } public Dictionary YearDetails { get; } public CalculationParameters() diff --git a/src/TaxTools.Core/TaxLimitation/CalculationResult.cs b/src/TaxTools.Core/TaxLimitation/CalculationResult.cs index 81f2b93..9075641 100644 --- a/src/TaxTools.Core/TaxLimitation/CalculationResult.cs +++ b/src/TaxTools.Core/TaxLimitation/CalculationResult.cs @@ -13,10 +13,18 @@ public class CalculationResultDetail public decimal AdditionalImprovement { get; set; } public int TaxableValue { get; set; } public decimal SB12Reduction { get; set; } + //2023 only public decimal? SB2Reduction { get; set; } - public decimal RunningTotal => Math.Max(StartingAmount + AdditionalImprovement - SB12Reduction - (SB2Reduction ?? 0), 0); + //2025 only + public decimal? SB4Reduction { get; set; } + //2025 only + public decimal? SB23Reduction { get; set; } + public decimal RunningTotal => Math.Max(StartingAmount + AdditionalImprovement - SB12Reduction + - (SB2Reduction ?? 0) - (SB4Reduction ?? 0) - (SB23Reduction ?? 0), 0); public string SB12CalculationText { get; set; } public string SB2CalculationText { get; set; } + public string SB4CalculationText { get; set; } + public string SB23CalculationText { get; set; } } } diff --git a/src/TaxTools.Core/TaxLimitation/Calculator.cs b/src/TaxTools.Core/TaxLimitation/Calculator.cs index aaae8e2..bd7a818 100644 --- a/src/TaxTools.Core/TaxLimitation/Calculator.cs +++ b/src/TaxTools.Core/TaxLimitation/Calculator.cs @@ -65,31 +65,79 @@ private static CalculationResultDetail CalculateYear(CalculationParameters param } else { - if (!parameters.EnableSB2Calculation || parameters.CalculationYear != 2023) - return result; - - var sb = new StringBuilder(); - if (parameters.ExemptionQualifyYear < 2022) - { - var prevYear = parameters.YearDetails[year - 1]; - var exemptAmount = 15000 * (prevYear.OwnershipPercent / 100); - result.SB2Reduction = Math.Round(exemptAmount * prevYear.TaxRate / 100, 2); - sb.Append($"({exemptAmount:N0} x {prevYear.TaxRate} / 100) + "); - } - if (parameters.ExemptionQualifyYear < 2023) + switch (parameters.CalculationYear) { - var exemptAmount = 60000 * (curYear.OwnershipPercent / 100); - var sb2Amount = Math.Round(exemptAmount * curYear.TaxRate / 100, 2); - if (result.SB2Reduction == null) - result.SB2Reduction = sb2Amount; - else - result.SB2Reduction += sb2Amount; - sb.Append($"({exemptAmount:N0} x {curYear.TaxRate} / 100)"); + case 2023: + Handle2023Calculation(parameters, year, result, curYear); + break; + case 2025: + Handle2025Calculation(parameters, result, curYear); + break; } - result.SB2CalculationText = sb.ToString(); } return result; } + + private static void Handle2023Calculation(CalculationParameters parameters, int year, CalculationResultDetail result, + CalculationParameterYearDetail curYear) + { + var sb = new StringBuilder(); + if (parameters.ExemptionQualifyYear < 2022) + { + var prevYear = parameters.YearDetails[year - 1]; + var exemptAmount = 15000 * (prevYear.OwnershipPercent / 100); + result.SB2Reduction = Math.Round(exemptAmount * prevYear.TaxRate / 100, 2); + sb.Append($"({exemptAmount:N0} x {prevYear.TaxRate} / 100) + "); + } + + if (parameters.ExemptionQualifyYear < 2023) + { + var exemptAmount = 60000 * (curYear.OwnershipPercent / 100); + var sb2Amount = Math.Round(exemptAmount * curYear.TaxRate / 100, 2); + if (result.SB2Reduction == null) + result.SB2Reduction = sb2Amount; + else + result.SB2Reduction += sb2Amount; + sb.Append($"({exemptAmount:N0} x {curYear.TaxRate} / 100)"); + } + + result.SB2CalculationText = sb.ToString(); + } + + private static void Handle2025Calculation(CalculationParameters parameters, CalculationResultDetail result, + CalculationParameterYearDetail curYear) + { + if (parameters.ExemptionQualifyYear > 2024) + return; + + if (parameters.EnableSB4Calculation) + { + var sb = new StringBuilder(); + var exemptAmount = 40000 * (curYear.OwnershipPercent / 100); + var amount = Math.Round(exemptAmount * curYear.TaxRate / 100, 2); + if (result.SB4Reduction == null) + result.SB4Reduction = amount; + else + result.SB4Reduction += amount; + sb.Append($"({exemptAmount:N0} x {curYear.TaxRate} / 100)"); + + result.SB4CalculationText = sb.ToString(); + } + + if (parameters.EnableSB23Calculation) + { + var sb = new StringBuilder(); + var exemptAmount = 50000 * (curYear.OwnershipPercent / 100); + var amount = Math.Round(exemptAmount * curYear.TaxRate / 100, 2); + if (result.SB23Reduction == null) + result.SB23Reduction = amount; + else + result.SB23Reduction += amount; + sb.Append($"({exemptAmount:N0} x {curYear.TaxRate} / 100)"); + + result.SB23CalculationText = sb.ToString(); + } + } } } diff --git a/src/TaxTools/Pages/TaxLimitationCalculator/DetailModel.cs b/src/TaxTools/Pages/TaxLimitationCalculator/DetailModel.cs index 09bbfa1..ee440d5 100644 --- a/src/TaxTools/Pages/TaxLimitationCalculator/DetailModel.cs +++ b/src/TaxTools/Pages/TaxLimitationCalculator/DetailModel.cs @@ -23,8 +23,9 @@ public class DetailModel public bool MCRPopulated { get; set; } public bool TaxRatePopulated { get; set; } - public bool RequireSB2Data => _parent.EnableSB2Calculation && - (( _parent.ExemptionQualifyYear <= 2022 && Year == 2023) || (_parent.ExemptionQualifyYear <= 2021 && Year == 2022)); + public bool RequireAdditionalData => (_parent.ExemptionQualifyYear <= 2022 && Year == 2023) + || (_parent.ExemptionQualifyYear <= 2021 && Year == 2022) + || (_parent.ExemptionQualifyYear <= 2024 && Year == 2025 && (_parent.EnableSB4Calculation || _parent.EnableSB23Calculation)); public DetailModel(CalculatorModel parent, int year) { @@ -45,7 +46,8 @@ public class CalculatorModel [Range(1900, 2050, ErrorMessage = "Exemption Qualify Year must be greater than 1900")] public int ExemptionQualifyYear { get; set; } - public bool EnableSB2Calculation { get; set; } + public bool EnableSB4Calculation { get; set; } + public bool EnableSB23Calculation { get; set; } [ValidateComplexType] public List Details { get; set; } @@ -53,8 +55,9 @@ public class CalculatorModel public CalculatorModel() { Details = new List(); - EnableSB2Calculation = true; - TaxYear = 2023; + EnableSB4Calculation = true; + EnableSB23Calculation = true; + TaxYear = 2024; } } } diff --git a/src/TaxTools/Pages/TaxLimitationCalculator/Index.razor b/src/TaxTools/Pages/TaxLimitationCalculator/Index.razor index 7d74b34..5ab3641 100644 --- a/src/TaxTools/Pages/TaxLimitationCalculator/Index.razor +++ b/src/TaxTools/Pages/TaxLimitationCalculator/Index.razor @@ -14,7 +14,6 @@ } else { - @@ -23,10 +22,13 @@ else
-

This tool has been provided by Spindlemedia to aid in understanding the tax limitation calculation.

+

This tool has been provided by Spindlemedia to aid in understanding the tax limitation calculation. For any feedback please email us at contact@spindlemedia.com.

+
+
+

Preliminary 2025 support for SB4 and SB23 has been added.

-

New for 2024: For multi-owner accounts, you must enter just the homesite taxable value for the ceiling owner(s)

+

For multi-owner accounts, you must enter just the homesite taxable value for the ceiling owner(s)

Steps: diff --git a/src/TaxTools/Pages/TaxLimitationCalculator/LimitationCalculator.razor b/src/TaxTools/Pages/TaxLimitationCalculator/LimitationCalculator.razor index 9a250b0..d9bb56e 100644 --- a/src/TaxTools/Pages/TaxLimitationCalculator/LimitationCalculator.razor +++ b/src/TaxTools/Pages/TaxLimitationCalculator/LimitationCalculator.razor @@ -5,6 +5,23 @@ + @if (_model.TaxYear == 2025) + { +

+
+ + +
+
+ +
+
+ + +
+
+ } +
@if (_model.DistrictId is null) @@ -59,36 +76,30 @@ }
- @if (_model.EnableSB2Calculation) - { -
- @if (detail.RequireSB2Data) - { -
- - -
- } -
- } +
+ @if (detail.RequireAdditionalData) + { +
+ + +
+ } +
- @if (_model.EnableSB2Calculation) - { -
- @if (detail.RequireSB2Data) - { -
- - -
- } -
- } +
+ @if (detail.RequireAdditionalData) + { +
+ + +
+ } +
} @@ -106,10 +117,15 @@ Ceiling Adjustments Taxable Value SB 12 Reduction - @if (_model.EnableSB2Calculation) + @if (_model.TaxYear == 2023) { SB 2 Reduction } + else if (_model.TaxYear == 2025) + { + SB 4 Reduction + SB 23 Reduction + } Running Total @@ -117,50 +133,46 @@ @for (var i = 0; i < _calculationResult.Details.Count; i++) { var detail = _calculationResult.Details[i]; - if (i < _calculationResult.Details.Count - 1) - { - - @detail.Year - @detail.StartingAmount.ToString("C2") - @(i == 0 ? "N/A" : detail.AdditionalImprovement.ToString("C2")) - @(detail.TaxableValue.ToString("N0")) - @detail.SB12Reduction.ToString("C2") - @if (_model.EnableSB2Calculation) + var firstOrFinal = i == 0 || i == _calculationResult.Details.Count - 1; + + + @detail.Year + @detail.StartingAmount.ToString("C2") + @(firstOrFinal ? "N/A" : detail.AdditionalImprovement.ToString("C2")) + @(firstOrFinal ? "N/A" : detail.TaxableValue.ToString("N0")) + @detail.SB12Reduction.ToString("C2") + @if (_model.TaxYear == 2023) + { + if (detail.SB2Reduction.HasValue) { - if (detail.SB2Reduction.HasValue) - { - @detail.SB2Reduction.Value.ToString("C2") - } - else - { - N/A - } + @detail.SB2Reduction.Value.ToString("C2") } - @detail.RunningTotal.ToString("C2") - - } - else - { - - @detail.Year - @detail.StartingAmount.ToString("C2") - @detail.AdditionalImprovement.ToString("C2") - N/A - N/A - @if (_model.EnableSB2Calculation) + else { - if (detail.SB2Reduction.HasValue) - { - @detail.SB2Reduction.Value.ToString("C2") - } - else - { - N/A - } + N/A } - @detail.RunningTotal.ToString("C2") - - } + } + else if (_model.TaxYear == 2025) + { + if (detail.SB4Reduction.HasValue) + { + @detail.SB4Reduction.Value.ToString("C2") + } + else + { + N/A + } + if (detail.SB23Reduction.HasValue) + { + @detail.SB23Reduction.Value.ToString("C2") + } + else + { + N/A + } + } + @detail.RunningTotal.ToString("C2") + } @@ -252,7 +264,8 @@ { ExemptionQualifyYear = _model.ExemptionQualifyYear, CalculationYear = _model.TaxYear, - EnableSB2Calculation = _model.EnableSB2Calculation + EnableSB4Calculation = _model.EnableSB4Calculation, + EnableSB23Calculation = _model.EnableSB23Calculation }; foreach (var detail in _model.Details) diff --git a/src/TaxTools/Pages/TaxLimitationCalculator/TaxRateConditionallyRequired.cs b/src/TaxTools/Pages/TaxLimitationCalculator/TaxRateConditionallyRequired.cs index 341ae8a..fafd01f 100644 --- a/src/TaxTools/Pages/TaxLimitationCalculator/TaxRateConditionallyRequired.cs +++ b/src/TaxTools/Pages/TaxLimitationCalculator/TaxRateConditionallyRequired.cs @@ -7,7 +7,7 @@ public class TaxRateConditionallyRequired : ValidationAttribute protected override ValidationResult IsValid(object value, ValidationContext context) { var model = (DetailModel)context.ObjectInstance; - if (!model.RequireSB2Data) + if (!model.RequireAdditionalData) return ValidationResult.Success; var taxRate = value as decimal?;