Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/TaxTools.Core/TaxLimitation/CalculationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, CalculationParameterYearDetail> YearDetails { get; }

public CalculationParameters()
Expand Down
10 changes: 9 additions & 1 deletion src/TaxTools.Core/TaxLimitation/CalculationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
public class CalculationResult
{
public List<CalculationResultDetail> Details { get; set; }

Check warning on line 5 in src/TaxTools.Core/TaxLimitation/CalculationResult.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Details' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public decimal CalculatedCeiling { get; set; }
}

Expand All @@ -13,10 +13,18 @@
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; }

Check warning on line 25 in src/TaxTools.Core/TaxLimitation/CalculationResult.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'SB12CalculationText' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string SB2CalculationText { get; set; }

Check warning on line 26 in src/TaxTools.Core/TaxLimitation/CalculationResult.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'SB2CalculationText' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string SB4CalculationText { get; set; }

Check warning on line 27 in src/TaxTools.Core/TaxLimitation/CalculationResult.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'SB4CalculationText' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string SB23CalculationText { get; set; }

Check warning on line 28 in src/TaxTools.Core/TaxLimitation/CalculationResult.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'SB23CalculationText' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}
}
88 changes: 68 additions & 20 deletions src/TaxTools.Core/TaxLimitation/Calculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
}
13 changes: 8 additions & 5 deletions src/TaxTools/Pages/TaxLimitationCalculator/DetailModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -45,16 +46,18 @@ 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<DetailModel> Details { get; set; }

public CalculatorModel()
{
Details = new List<DetailModel>();
EnableSB2Calculation = true;
TaxYear = 2023;
EnableSB4Calculation = true;
EnableSB23Calculation = true;
TaxYear = 2024;
}
}
}
8 changes: 5 additions & 3 deletions src/TaxTools/Pages/TaxLimitationCalculator/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
}
else
{

<RadzenPanel AllowCollapse="true" Class="rz-my-3">
<HeaderTemplate>
<RadzenText TextStyle="TextStyle.H6" Class="rz-display-flex rz-align-items-center rz-m-0">
Expand All @@ -23,10 +22,13 @@ else
</HeaderTemplate>
<ChildContent>
<h5 class="mb-3 mt-3">
<p>This tool has been provided by Spindlemedia to aid in understanding the tax limitation calculation.</p>
<p>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.</p>
</h5>
<h5 class="mb-3 mt-3" style="color: red">
<p>Preliminary 2025 support for SB4 and SB23 has been added.</p>
</h5>
<h5 class="mb-3 mt-3" style="color: red">
<p>New for 2024: For multi-owner accounts, you must enter just the homesite taxable value for the ceiling owner(s)</p>
<p>For multi-owner accounts, you must enter just the homesite taxable value for the ceiling owner(s)</p>
</h5>
<p>
Steps:
Expand Down
145 changes: 79 additions & 66 deletions src/TaxTools/Pages/TaxLimitationCalculator/LimitationCalculator.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@
<ObjectGraphDataAnnotationsValidator />
<ValidationSummary />

@if (_model.TaxYear == 2025)
{
<div class="form-floating mb-2">
<div class="form-check form-switch">
<InputCheckbox id="EnableSB4Calculation" @bind-Value="_model.EnableSB4Calculation" class="form-check-input" />
<label for="EnableSB4Calculation" class="form-check-label">Enable SB4 (140k HS) calculation</label>
</div>
</div>

<div class="form-floating mb-2">
<div class="form-check form-switch">
<InputCheckbox id="EnableSB23Calculation" @bind-Value="_model.EnableSB23Calculation" class="form-check-input" />
<label for="EnableSB23Calculation" class="form-check-label">Enable SB23 (60k O65/DP) calculation</label>
</div>
</div>
}

<div class="form-floating mb-2">
<InputSelect id="DistrictId" class="form-select form-control" @bind-Value="_model.DistrictId">
@if (_model.DistrictId is null)
Expand Down Expand Up @@ -59,36 +76,30 @@
}
</div>
</div>
@if (_model.EnableSB2Calculation)
{
<div class="col-lg">
@if (detail.RequireSB2Data)
{
<div class="form-floating">
<InputNumber id="@($"Ownership_{detail.Year}")" @bind-Value="detail.OwnershipPercent" class="form-control"></InputNumber>
<label for="@($"Ownership__{detail.Year}")" class="form-label">@detail.Year Ownership %</label>
</div>
}
</div>
}
<div class="col-lg">
@if (detail.RequireAdditionalData)
{
<div class="form-floating">
<InputNumber id="@($"Ownership_{detail.Year}")" @bind-Value="detail.OwnershipPercent" class="form-control"></InputNumber>
<label for="@($"Ownership__{detail.Year}")" class="form-label">@detail.Year Ownership %</label>
</div>
}
</div>
<div class="col-lg">
<div class="form-floating">
<InputNumber id="@($"MCR_{detail.Year}")" @bind-Value="detail.MCR" class="form-control" disabled="@detail.MCRPopulated"></InputNumber>
<label for="@($"MCR_{detail.Year}")" class="form-label">@detail.Year MCR</label>
</div>
</div>
@if (_model.EnableSB2Calculation)
{
<div class="col-lg">
@if (detail.RequireSB2Data)
{
<div class="form-floating">
<InputNumber id="@($"TaxRate_{detail.Year}")" @bind-Value="detail.TaxRate" class="form-control" disabled="@detail.TaxRatePopulated"></InputNumber>
<label for="@($"TaxRate_{detail.Year}")" class="form-label">@detail.Year Total Tax Rate</label>
</div>
}
</div>
}
<div class="col-lg">
@if (detail.RequireAdditionalData)
{
<div class="form-floating">
<InputNumber id="@($"TaxRate_{detail.Year}")" @bind-Value="detail.TaxRate" class="form-control" disabled="@detail.TaxRatePopulated"></InputNumber>
<label for="@($"TaxRate_{detail.Year}")" class="form-label">@detail.Year Total Tax Rate</label>
</div>
}
</div>
</div>
}

Expand All @@ -106,61 +117,62 @@
<th scope="col">Ceiling Adjustments</th>
<th scope="col">Taxable Value</th>
<th scope="col">SB 12 Reduction</th>
@if (_model.EnableSB2Calculation)
@if (_model.TaxYear == 2023)
{
<th scope="col">SB 2 Reduction</th>
}
else if (_model.TaxYear == 2025)
{
<th scope="col">SB 4 Reduction</th>
<th scope="col">SB 23 Reduction</th>
}
<th scope="col">Running Total</th>
</tr>
</thead>
<tbody>
@for (var i = 0; i < _calculationResult.Details.Count; i++)
{
var detail = _calculationResult.Details[i];
if (i < _calculationResult.Details.Count - 1)
{
<tr>
<th scope="row">@detail.Year</th>
<td>@detail.StartingAmount.ToString("C2")</td>
<td>@(i == 0 ? "N/A" : detail.AdditionalImprovement.ToString("C2"))</td>
<td>@(detail.TaxableValue.ToString("N0"))</td>
<td><Tooltip Text="@detail.SB12CalculationText">@detail.SB12Reduction.ToString("C2")</Tooltip></td>
@if (_model.EnableSB2Calculation)
var firstOrFinal = i == 0 || i == _calculationResult.Details.Count - 1;

<tr>
<th scope="row">@detail.Year</th>
<td>@detail.StartingAmount.ToString("C2")</td>
<td>@(firstOrFinal ? "N/A" : detail.AdditionalImprovement.ToString("C2"))</td>
<td>@(firstOrFinal ? "N/A" : detail.TaxableValue.ToString("N0"))</td>
<td><Tooltip Text="@detail.SB12CalculationText">@detail.SB12Reduction.ToString("C2")</Tooltip></td>
@if (_model.TaxYear == 2023)
{
if (detail.SB2Reduction.HasValue)
{
if (detail.SB2Reduction.HasValue)
{
<td><Tooltip Text="@detail.SB2CalculationText">@detail.SB2Reduction.Value.ToString("C2")</Tooltip></td>
}
else
{
<td>N/A</td>
}
<td><Tooltip Text="@detail.SB2CalculationText">@detail.SB2Reduction.Value.ToString("C2")</Tooltip></td>
}
<td>@detail.RunningTotal.ToString("C2")</td>
</tr>
}
else
{
<tr>
<th scope="row">@detail.Year</th>
<td>@detail.StartingAmount.ToString("C2")</td>
<td>@detail.AdditionalImprovement.ToString("C2")</td>
<td>N/A</td>
<td>N/A</td>
@if (_model.EnableSB2Calculation)
else
{
if (detail.SB2Reduction.HasValue)
{
<td><Tooltip Text="@detail.SB2CalculationText">@detail.SB2Reduction.Value.ToString("C2")</Tooltip></td>
}
else
{
<td>N/A</td>
}
<td>N/A</td>
}
<td><b>@detail.RunningTotal.ToString("C2")</b></td>
</tr>
}
}
else if (_model.TaxYear == 2025)
{
if (detail.SB4Reduction.HasValue)
{
<td><Tooltip Text="@detail.SB4CalculationText">@detail.SB4Reduction.Value.ToString("C2")</Tooltip></td>
}
else
{
<td>N/A</td>
}
if (detail.SB23Reduction.HasValue)
{
<td><Tooltip Text="@detail.SB23CalculationText">@detail.SB23Reduction.Value.ToString("C2")</Tooltip></td>
}
else
{
<td>N/A</td>
}
}
<td>@detail.RunningTotal.ToString("C2")</td>
</tr>
}
</tbody>
</table>
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?;
Expand Down
Loading