Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FYST 754 implement form 502 xml pdf two income subtraction #5012

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e975d02
WIP: preliminary implementation with no income
mpidcock Nov 13, 2024
4933874
WIP: add tests with hardcoded values for spouse and primary
mpidcock Nov 13, 2024
86ed380
WIP: implement first pass at data sources for worksheet calculations
mpidcock Nov 13, 2024
3a1986f
Add TwoIncome calculation to xml & pdf
mpidcock Nov 13, 2024
4d07b8f
WIP add labels and minor fixes
mpidcock Nov 13, 2024
522da0d
WIP #calculate_fed_income base case tests passing
mpidcock Nov 18, 2024
8cfc8f8
WIP #calculate_fed_income base case tests passing
mpidcock Nov 19, 2024
14c32c8
WIP test #calculate_fed_subtractions
mpidcock Nov 19, 2024
7299cf1
WIP test #calculate_line_2
mpidcock Nov 19, 2024
187af36
WIP test #calculate_line_4
mpidcock Nov 19, 2024
e355e92
WIP test #calculate_line_2
mpidcock Nov 19, 2024
b4809e1
WIP test math calculations
mpidcock Nov 20, 2024
aee0d99
WIP handle nil and non-MFJ
mpidcock Nov 20, 2024
4e85dfc
WIP add some missing federal info editor fields
mpidcock Nov 20, 2024
ecef327
WIP fix tests
mpidcock Nov 20, 2024
b3b5768
WIP update MFJ/MFS ssns to match new trait
mpidcock Nov 20, 2024
18d1df9
WIP nil safety and cosmetics
mpidcock Nov 20, 2024
f182b0e
more tests
mpidcock Nov 20, 2024
cd9fe69
add line 14 to line 15 calculations
mpidcock Nov 20, 2024
ac5b37c
update subtractions to use caluclation methods
mpidcock Nov 20, 2024
a36b52a
code review feedback
mpidcock Nov 20, 2024
8253798
add labels
mpidcock Nov 20, 2024
272f350
don't use setter for interest_reports
mpidcock Nov 20, 2024
aa027e7
cleanup
mpidcock Nov 21, 2024
3ef8915
add line 14 test
mpidcock Nov 21, 2024
2b92f3d
code review feedback
mpidcock Nov 22, 2024
43a352c
use json for 1099G info instead
mpidcock Nov 22, 2024
c7dcf70
Merge branch 'refs/heads/main' into FYST-754-implement-form-502-xml-p…
mpidcock Nov 25, 2024
6600cc4
Merge branch 'refs/heads/main' into FYST-754-implement-form-502-xml-p…
mpidcock Nov 27, 2024
0fa7f68
add acceptance testing scenarios
mpidcock Nov 27, 2024
1be6233
fix scenario 9 w2
mpidcock Nov 27, 2024
1f854ae
Remove acceptance testing scenarios
mpidcock Nov 27, 2024
28ff738
Merge branch 'refs/heads/main' into FYST-754-implement-form-502-xml-p…
mpidcock Nov 27, 2024
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
38 changes: 38 additions & 0 deletions app/lib/efile/line_data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -610,10 +610,48 @@ MD502_LINE_D_COUNT_TOTAL:
label: 'D Total Exemption Count: add lines A, B and C counts'
MD502_LINE_D_AMOUNT_TOTAL:
label: 'D Total Exemption Dollar Amount: add lines A, B and C amounts'
MD502_LINE_9:
label: 'Child and dependent care expenses'
MD502_LINE_10A:
label: 'TBD'
MD502_LINE_11:
label: 'Taxable Social Security and RR benefits (Tier I, II and supplemental) included in line 1'
MD502_LINE_13:
label: 'Subtractions from attached form 502SU'
MD_TWO_INCOME_WK_LINE_1_A:
label: 'ENTER the portion of federal adjusted gross income from line 1 of Form 502
attributable to each spouse. (a) you'
MD_TWO_INCOME_WK_LINE_2_A:
label: 'ENTER the portion of additions to income from line 6 of Form 502
attributable to each spouse. (a) you'
MD_TWO_INCOME_WK_LINE_3_A:
label: 'ADD lines 1 and 2. (a) you'
MD_TWO_INCOME_WK_LINE_4_A:
label: 'ENTER the portion of subtractions from income from lines 8 - 13 of Form 502
attributable to each spouse. (a) you'
MD_TWO_INCOME_WK_LINE_5_A:
label: 'SUBTRACT line 4 from line 3. (a) you'
MD_TWO_INCOME_WK_LINE_1_B:
label: 'ENTER the portion of federal adjusted gross income from line 1 of Form 502
attributable to each spouse. (b) your spouse'
MD_TWO_INCOME_WK_LINE_2_B:
label: 'ENTER the portion of additions to income from line 6 of Form 502
attributable to each spouse. (b) your spouse'
MD_TWO_INCOME_WK_LINE_3_B:
label: 'ADD lines 1 and 2. (b) your spouse'
MD_TWO_INCOME_WK_LINE_4_B:
label: 'ENTER the portion of subtractions from income from lines 8 - 13 of Form 502
attributable to each spouse. (b) your spouse'
MD_TWO_INCOME_WK_LINE_5_B:
label: 'SUBTRACT line 4 from line 3. (b) your spouse'
MD_TWO_INCOME_WK_LINE_6:
label: 'COMPARE the amounts on lines 5 (a) and (b) and enter the smaller amount here
but not less than zero'
MD_TWO_INCOME_WK_LINE_7:
label: 'ENTER $1,200 or the amount on line 6, whichever is less.
ENTER this amount on line 14 of Form 502.'
MD502_LINE_14:
label: 'Two-income subtraction from worksheet in Instruction 13.'
MD502_LINE_15:
label: 'Total subtractions (Add lines 8 through 14. See instructions.)'
MD502_LINE_16:
Expand Down
29 changes: 23 additions & 6 deletions app/lib/efile/md/md502_calculator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ def initialize(year:, intake:, include_source: false)
intake: @intake
)

@two_income_subtraction_worksheet = Efile::Md::TwoIncomeSubtractionWorksheet.new(
value_access_tracker: @value_access_tracker,
lines: @lines,
intake: @intake
)

@md502cr = Efile::Md::Md502crCalculator.new(
value_access_tracker: @value_access_tracker,
lines: @lines,
Expand Down Expand Up @@ -55,9 +61,15 @@ def calculate
set_line(:MD502_LINE_7, :calculate_line_7)

# Subtractions
set_line(:MD502_LINE_9, @direct_file_data, :total_qualifying_dependent_care_expenses)
set_line(:MD502_LINE_10A, :calculate_line_10a) # STUBBED: PLEASE REPLACE, don't forget line_data.yml
set_line(:MD502_LINE_11, @direct_file_data, :fed_taxable_ssb)
@md502_su.calculate
set_line(:MD502_LINE_13, :calculate_line_13)
if filing_status_mfj?
@two_income_subtraction_worksheet.calculate
end
set_line(:MD502_LINE_14, :calculate_line_14)
# lines 15 and 16 depend on lines 8-14
set_line(:MD502_LINE_15, :calculate_line_15)
set_line(:MD502_LINE_16, :calculate_line_16)
Expand Down Expand Up @@ -226,12 +238,21 @@ def calculate_line_7

def calculate_line_10a; end

def calculate_line_13
@lines[:MD502_SU_LINE_1].value
end

def calculate_line_14
line_or_zero(:MD_TWO_INCOME_WK_LINE_7)
end

def calculate_line_15
[
@direct_file_data.total_qualifying_dependent_care_expenses, # line 9
@direct_file_data.fed_taxable_ssb, # line 11
line_or_zero(:MD502_LINE_9),
line_or_zero(:MD502_LINE_10A),
line_or_zero(:MD502_LINE_11),
line_or_zero(:MD502_LINE_13),
line_or_zero(:MD502_LINE_14),
].sum
end

Expand Down Expand Up @@ -330,10 +351,6 @@ def calculate_line_20
end
end

def calculate_line_13
@lines[:MD502_SU_LINE_1].value
end

def calculate_line_21
# Maryland state income tax
taxable_net_income = line_or_zero(:MD502_LINE_20)
Expand Down
127 changes: 127 additions & 0 deletions app/lib/efile/md/two_income_subtraction_worksheet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
module Efile
module Md
class TwoIncomeSubtractionWorksheet < ::Efile::TaxCalculator
# https://www.marylandtaxes.gov/forms/worksheets/Two-income-worksheet.pdf

attr_accessor :lines, :value_access_tracker

def initialize(value_access_tracker:, lines:, intake:)
@value_access_tracker = value_access_tracker
@lines = lines
@intake = intake
@direct_file_data = intake.direct_file_data
@direct_file_json_data = intake.direct_file_json_data
end

def calculate
set_line(:MD_TWO_INCOME_WK_LINE_1_A, -> { calculate_line_1 :primary })
set_line(:MD_TWO_INCOME_WK_LINE_2_A, -> { calculate_line_2 :primary })
set_line(:MD_TWO_INCOME_WK_LINE_3_A, -> { calculate_line_3 :primary })
set_line(:MD_TWO_INCOME_WK_LINE_4_A, -> { calculate_line_4 :primary })
set_line(:MD_TWO_INCOME_WK_LINE_5_A, -> { calculate_line_5 :primary })
set_line(:MD_TWO_INCOME_WK_LINE_1_B, -> { calculate_line_1 :spouse })
set_line(:MD_TWO_INCOME_WK_LINE_2_B, -> { calculate_line_2 :spouse })
set_line(:MD_TWO_INCOME_WK_LINE_3_B, -> { calculate_line_3 :spouse })
set_line(:MD_TWO_INCOME_WK_LINE_4_B, -> { calculate_line_4 :spouse })
set_line(:MD_TWO_INCOME_WK_LINE_5_B, -> { calculate_line_5 :spouse })
set_line(:MD_TWO_INCOME_WK_LINE_6, :calculate_line_6)
set_line(:MD_TWO_INCOME_WK_LINE_7, :calculate_line_7)
end

def calculate_fed_income(primary_or_spouse)
filer_ssn = @intake.send(primary_or_spouse).ssn

wage_income = @direct_file_data.w2s
.select { |w2| w2.EmployeeSSN == filer_ssn }
.sum { |w2| w2.WagesAmt&.round }

interest_income = @direct_file_json_data.interest_reports
.select { |interest_report| interest_report.recipient_tin.delete("-") == filer_ssn }
.sum { |interest_report|
(interest_report.amount_1099&.round || 0) + (interest_report.amount_no_1099&.round || 0)
}

retirement_income = @intake.state_file1099_rs
.select { |form1099r| form1099r.recipient_ssn == filer_ssn }
.sum { |form1099r| form1099r.taxable_amount&.round }

unemployment_income = find_filer_json_for(primary_or_spouse)&.form_1099_gs_total&.round || 0

wage_income +
interest_income +
retirement_income +
unemployment_income
end

def calculate_fed_subtractions(primary_or_spouse)
filer_json = find_filer_json_for(primary_or_spouse)

student_loan_interest = {
primary: @intake.primary_student_loan_interest_ded_amount&.round,
spouse: @intake.spouse_student_loan_interest_ded_amount&.round,
}[primary_or_spouse]

educator_expenses = filer_json&.educator_expenses&.round || 0

hsa_total_deductible_amount = filer_json&.hsa_total_deductible_amount&.round || 0

student_loan_interest +
educator_expenses +
hsa_total_deductible_amount
end

private

def calculate_line_1(primary_or_spouse)
calculate_fed_income(primary_or_spouse) - calculate_fed_subtractions(primary_or_spouse)
end

def calculate_line_2(primary_or_spouse)
@intake.state_file_w2s
.select { |w2| w2.employee_ssn == @intake.send(primary_or_spouse).ssn }
.sum { |w2| w2.box14_stpickup&.round || 0 }
end

def calculate_line_3(primary_or_spouse)
filer = primary_or_spouse == :primary ? "A" : "B"
line_or_zero("MD_TWO_INCOME_WK_LINE_1_#{filer}") +
line_or_zero("MD_TWO_INCOME_WK_LINE_2_#{filer}")
end

def calculate_line_4(primary_or_spouse)
cdc_expenses = (@direct_file_data.total_qualifying_dependent_care_expenses || 0) / 2

# NOTE: Stub alert - this data relies on 1099R followup questions, which have been deprioritized
pension_exclusion = 0
military_retirement_exclusion = 0

cdc_expenses +
pension_exclusion +
military_retirement_exclusion
end

def calculate_line_5(primary_or_spouse)
filer = primary_or_spouse == :primary ? "A" : "B"
line_or_zero("MD_TWO_INCOME_WK_LINE_3_#{filer}") -
line_or_zero("MD_TWO_INCOME_WK_LINE_4_#{filer}")
end

def calculate_line_6
lower_income = [@lines[:MD_TWO_INCOME_WK_LINE_5_A].value,
@lines[:MD_TWO_INCOME_WK_LINE_5_B].value].min
[lower_income, 0].max
end

def calculate_line_7
@lines[:MD_TWO_INCOME_WK_LINE_6].value.clamp(0, 1_200)
end

def find_filer_json_for(primary_or_spouse)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this extraction!

@direct_file_json_data.filers
.find { |df_filer_data|
df_filer_data.tin.delete("-") == @intake.send(primary_or_spouse).ssn
}
end
end
end
end
1 change: 1 addition & 0 deletions app/lib/pdf_filler/md502_pdf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def hash_for_pdf
'Text Field 11': generate_codes_for_502_su.at(2),
'Text Field 12': generate_codes_for_502_su.at(3),
'Enter 13': @xml_document.at('Form502 Subtractions Other')&.text,
'Enter 14': @xml_document.at('Form502 Subtractions TwoIncome')&.text,
'Text Box 68': @xml_document.at('Form502 TaxWithheld')&.text,
'Text Box 34': @xml_document.at('Form502 StateTaxComputation EarnedIncomeCredit')&.text,
'Check Box 37': checkbox_value(@xml_document.at('Form502 StateTaxComputation MDEICWithQualChildInd')&.text),
Expand Down
4 changes: 2 additions & 2 deletions app/lib/submission_builder/ty2024/states/id/id_return_xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ def supported_documents
supported_docs = [
{
xml: SubmissionBuilder::Ty2024::States::Id::Documents::Id40,
pdf: PdfFiller::Id40Pdf,
pdf: PdfFiller::Id40Pdf,
include: true
},
{
xml: SubmissionBuilder::Ty2024::States::Id::Documents::Id39R,
pdf: PdfFiller::Id39rPdf,
pdf: PdfFiller::Id39rPdf,
include: true
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ def document
xml.FedAGIAndStateAdditions calculated_fields.fetch(:MD502_LINE_7)
end
xml.Subtractions do
xml.ChildAndDependentCareExpenses @direct_file_data.total_qualifying_dependent_care_expenses
xml.SocialSecurityRailRoadBenefits @direct_file_data.fed_taxable_ssb
xml.Other calculated_fields.fetch(:MD502_LINE_13)
add_element_if_present(xml, "ChildAndDependentCareExpenses", :MD502_LINE_9)
add_element_if_present(xml, "SocialSecurityRailRoadBenefits", :MD502_LINE_11)
add_element_if_present(xml, "Other", :MD502_LINE_13)
add_element_if_present(xml, "TwoIncome", :MD502_LINE_14)
add_element_if_present(xml, "Total", :MD502_LINE_15)
add_element_if_present(xml, "StateAdjustedGrossIncome", :MD502_LINE_16)
end
Expand Down
13 changes: 7 additions & 6 deletions app/models/direct_file_json_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class DfJsonPerson < DfJsonWrapper

class DfJsonFiler < DfJsonPerson
json_accessor is_primary_filer: { type: :boolean, key: "isPrimaryFiler" }
json_accessor form_1099_gs_total: { type: :money_amount, key: "form1099GsTotal" }
json_accessor educator_expenses: { type: :money_amount, key: "educatorExpenses" }
json_accessor hsa_total_deductible_amount: { type: :money_amount, key: "hsaTotalDeductibleAmount" }
end

class DfJsonDependent < DfJsonPerson
Expand Down Expand Up @@ -63,13 +66,11 @@ def interest_reports
data["interestReports"]&.map { |interest_report| DfJsonInterestReport.new(interest_report) } || []
end

def dependents
data["familyAndHousehold"]&.map { |dependent| DfJsonDependent.new(dependent) } || []
end

private

def filers
data["filers"]&.map { |filer| DfJsonFiler.new(filer) } || []
end

def dependents
data["familyAndHousehold"]&.map { |dependent| DfJsonDependent.new(dependent) } || []
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<%= render 'node_xml_reveal', xml: f.object.to_pretty_s %>

<table>
<%= f.state_file_nested_xml_field :EmployeeSSN %>
<%= f.state_file_nested_xml_field :WagesAmt %>
<%= f.state_file_nested_xml_field :WithholdingAmt %>
<%= f.state_file_nested_xml_field :StateWagesAmt %>
Expand Down
1 change: 1 addition & 0 deletions app/views/state_file/questions/federal_info/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<%= f.state_file_qa_input_field :fed_agi, "Adjusted Gross Income" %>
<%= f.state_file_qa_input_field :fed_wages, "Wages, salaries, tips" %>
<%= f.state_file_qa_input_field :fed_taxable_income, "Taxable interest" %>
<%= f.state_file_qa_input_field :fed_student_loan_interest, "Subtractable student loan interest" %>
<%= f.state_file_qa_input_field :fed_unemployment, "Unemployment compensation" %>
<%= f.state_file_qa_input_field :fed_taxable_ssb, "Taxable SS Income" %>
<%= f.state_file_qa_input_field :total_exempt_primary_spouse, "Total exempt primary and spouse count" %>
Expand Down
8 changes: 4 additions & 4 deletions spec/factories/state_file1099_rs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
payer_address_line1 { "123 Sesame ST" }
payer_address_line2 { "Apt 202" }
payer_city_name { "Long Island" }
payer_zip { "12345"}
payer_identification_number { "22345"}
recipient_ssn { "123456789"}
recipient_name { "Dorothy Jane Red"}
payer_zip { "12345" }
payer_identification_number { "22345" }
recipient_ssn { "123456789" }
recipient_name { "Dorothy Jane Red" }
gross_distribution_amount { 100.25 }
taxable_amount { 50.5 }
taxable_amount_not_determined { true }
Expand Down
7 changes: 5 additions & 2 deletions spec/factories/state_file_md_intakes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@
end

trait :with_spouse do
raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml("md_nate_mfj") }
raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml("md_minimal_with_spouse") }
raw_direct_file_intake_data { StateFile::DirectFileApiResponseSampleService.new.read_json('md_minimal_with_spouse') }
filing_status { 'married_filing_jointly' }

spouse_first_name { "Marty" }
Expand All @@ -158,7 +159,8 @@
end

trait :with_senior_spouse do
raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml("md_nate_mfj") }
raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml("md_minimal_with_spouse") }
raw_direct_file_intake_data { StateFile::DirectFileApiResponseSampleService.new.read_json('md_minimal_with_spouse') }
filing_status { 'married_filing_jointly' }

spouse_first_name { "Marty" }
Expand All @@ -173,6 +175,7 @@

trait :df_data_many_w2s do
raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml('md_zeus_many_w2s') }
raw_direct_file_intake_data { StateFile::DirectFileApiResponseSampleService.new.read_json('md_zeus_many_w2s') }
end

trait :head_of_household do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"familyAndHousehold": [],
"filers": [
{
"firstName": "Beaches",
"middleInitial": "T",
"lastName": "Yarn Mat",
"dateOfBirth": "1950-01-01",
"isPrimaryFiler": true,
"tin": "123-45-6789"
},
{
"firstName": "Pebble",
"middleInitial": "Y",
"lastName": "Yarn Mat",
"dateOfBirth": "1950-01-01",
"isPrimaryFiler": false,
"tin": "987-65-4321"
}
]
}
Loading