diff --git a/app/lib/efile/line_data.yml b/app/lib/efile/line_data.yml index 82fb42cd35..6a98b8cc2e 100644 --- a/app/lib/efile/line_data.yml +++ b/app/lib/efile/line_data.yml @@ -578,6 +578,8 @@ NJ1040_LINE_15: label: '15 Wages, salaries, tips, and other employee compensation (State wages from Box 16 of enclosed W-2(s))' NJ1040_LINE_16A: label: 'Taxable interest income (Enclose federal Schedule B if over $1,500) (See instructions)' +NJ1040_LINE_16B: + label: 'Tax-exempt interest income (Enclose schedule) (See instructions) Do not include on line 16a' NJ1040_LINE_27: label: '27 Total Income (Add lines 15, 16a, and 20a)' NJ1040_LINE_29: diff --git a/app/lib/efile/nj/nj1040_calculator.rb b/app/lib/efile/nj/nj1040_calculator.rb index 9f6fe9238a..dfaf226ae1 100644 --- a/app/lib/efile/nj/nj1040_calculator.rb +++ b/app/lib/efile/nj/nj1040_calculator.rb @@ -20,6 +20,7 @@ def calculate set_line(:NJ1040_LINE_13, :calculate_line_13) set_line(:NJ1040_LINE_15, :calculate_line_15) set_line(:NJ1040_LINE_16A, :calculate_line_16a) + set_line(:NJ1040_LINE_16B, :calculate_line_16b) set_line(:NJ1040_LINE_27, :calculate_line_27) set_line(:NJ1040_LINE_29, :calculate_line_29) set_line(:NJ1040_LINE_31, :calculate_line_31) @@ -139,6 +140,10 @@ def calculate_use_tax(nj_gross_income) end end + def calculate_tax_exempt_interest_income + @intake.direct_file_data.fed_tax_exempt_interest + interest_on_gov_bonds + end + private def line_6_spouse_checkbox @@ -190,11 +195,12 @@ def calculate_line_15 end def calculate_line_16a - interest_reports = @intake.direct_file_json_data.interest_reports - interest_on_gov_bonds = interest_reports&.map(&:interest_on_government_bonds) - interest_sum = interest_on_gov_bonds.sum - return nil unless interest_sum.positive? - (@intake.direct_file_data.fed_taxable_income - interest_sum).round + return nil unless interest_on_gov_bonds.positive? + @intake.direct_file_data.fed_taxable_income - interest_on_gov_bonds + end + + def calculate_line_16b + calculate_tax_exempt_interest_income if calculate_tax_exempt_interest_income.positive? end def calculate_line_27 @@ -330,6 +336,12 @@ def number_of_true_checkboxes(checkbox_array_for_line) checkbox_array_for_line.sum { |a| a == true ? 1 : 0 } end + def interest_on_gov_bonds + interest_reports = @intake.direct_file_json_data.interest_reports + interests_on_gov_bonds = interest_reports&.map(&:interest_on_government_bonds) + interests_on_gov_bonds&.sum&.round + end + def is_mfs_same_home is_mfs = @intake.filing_status_mfs? is_same_home = @intake.tenant_same_home_spouse_yes? || @intake.homeowner_same_home_spouse_yes? diff --git a/app/lib/pdf_filler/nj1040_pdf.rb b/app/lib/pdf_filler/nj1040_pdf.rb index 5212bc58bb..eb396b444d 100644 --- a/app/lib/pdf_filler/nj1040_pdf.rb +++ b/app/lib/pdf_filler/nj1040_pdf.rb @@ -97,22 +97,7 @@ def hash_for_pdf answers.merge!(dependent_hash) end - if @xml_document.at("TaxableInterestIncome") - taxable_interest_income = @xml_document.at("TaxableInterestIncome").text.to_i - answers.merge!(insert_digits_into_fields(taxable_interest_income, [ - "112", - "111", - "110", - "109", - "108", - "Text107", - "undefined_41", - "undefined_40", - "undefined_39", - "undefined_43" - ])) - end - + # lines 13 and 30 if @xml_document.at("Exemptions TotalExemptionAmountA") total_exemptions = @xml_document.at("Exemptions TotalExemptionAmountA").text.to_i answers.merge!(insert_digits_into_fields(total_exemptions, [ @@ -126,6 +111,7 @@ def hash_for_pdf ])) end + # line 13 if @xml_document.at("Body TotalExemptionAmountB") total_exemptions = @xml_document.at("Body TotalExemptionAmountB").text.to_i answers.merge!(insert_digits_into_fields(total_exemptions, [ @@ -169,6 +155,7 @@ def hash_for_pdf ])) end + # line 15 if @xml_document.at("WagesSalariesTips").present? wages = @xml_document.at("WagesSalariesTips").text.to_i answers.merge!(insert_digits_into_fields(wages, [ @@ -185,6 +172,39 @@ def hash_for_pdf ])) end + # line 16a + if @xml_document.at("TaxableInterestIncome") + taxable_interest_income = @xml_document.at("TaxableInterestIncome").text.to_i + answers.merge!(insert_digits_into_fields(taxable_interest_income, [ + "112", + "111", + "110", + "109", + "108", + "Text107", + "undefined_41", + "undefined_40", + "undefined_39", + "undefined_43" + ])) + end + + # line 16b + if @xml_document.at("TaxexemptInterestIncome") + tax_exempt_interest_income = @xml_document.at("TaxexemptInterestIncome").text.to_i + answers.merge!(insert_digits_into_fields(tax_exempt_interest_income, [ + "117", + "116", + "115", + "114", + "113", + "undefined_44", + "16a", + "undefined_42", + "16b" + ])) + end + if @xml_document.at("TotalIncome").present? total_income = @xml_document.at("TotalIncome").text.to_i answers.merge!(insert_digits_into_fields(total_income, [ @@ -219,6 +239,7 @@ def hash_for_pdf ])) end + # line 39 if @xml_document.at("TaxableIncome").present? taxable_income = @xml_document.at("TaxableIncome").text.to_i answers.merge!(insert_digits_into_fields(taxable_income, [ diff --git a/app/lib/submission_builder/ty2024/states/nj/documents/nj1040.rb b/app/lib/submission_builder/ty2024/states/nj/documents/nj1040.rb index 5ba49106e3..b56d7e14b3 100644 --- a/app/lib/submission_builder/ty2024/states/nj/documents/nj1040.rb +++ b/app/lib/submission_builder/ty2024/states/nj/documents/nj1040.rb @@ -106,6 +106,10 @@ def document xml.TaxableInterestIncome calculated_fields.fetch(:NJ1040_LINE_16A) end + if calculated_fields.fetch(:NJ1040_LINE_16B)&.positive? + xml.TaxexemptInterestIncome calculated_fields.fetch(:NJ1040_LINE_16B) + end + if calculated_fields.fetch(:NJ1040_LINE_27).positive? xml.TotalIncome calculated_fields.fetch(:NJ1040_LINE_27) end diff --git a/app/models/state_file_nj_intake.rb b/app/models/state_file_nj_intake.rb index 23bd42221d..364611905f 100644 --- a/app/models/state_file_nj_intake.rb +++ b/app/models/state_file_nj_intake.rb @@ -139,6 +139,9 @@ def disqualifying_df_data_reason return :has_out_of_state_w2 if w2_states.any? do |state| !(state.text || '').casecmp(state_code).zero? end + + tax_exempt_interest_income = calculator.calculate_tax_exempt_interest_income + return :exempt_interest_exceeds_10k if tax_exempt_interest_income > 10_000 end def disqualifying_eligibility_rules diff --git a/config/locales/en.yml b/config/locales/en.yml index 1e9c5455f5..3ac6c543c8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2341,6 +2341,7 @@ en: data_transfer_offboarding: edit: ineligible_reason: + exempt_interest_exceeds_10k: your tax exempt interest income exceeds $10,000. has_irc_125_code: W2 lists IRC 125 benefit plan amounts. has_out_of_state_w2: you earned income in another state in 2023. has_yonkers_income: lived, earned income, or maintained a home in Yonkers in 2023. diff --git a/config/locales/es.yml b/config/locales/es.yml index 2bb9e5d11f..9b9317de33 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2315,6 +2315,7 @@ es: data_transfer_offboarding: edit: ineligible_reason: + exempt_interest_exceeds_10k: your tax exempt interest income exceeds $10,000. has_irc_125_code: tu formulario W-2 incluye cantidades del plan de beneficios del Código de Rentas Internas (IRC 125). has_out_of_state_w2: recibiste ingresos en otro estado en el 2023 has_yonkers_income: viviste, recibiste ingresos o mantuviste un hogar en Yonkers en 2023. diff --git a/spec/factories/state_file_nj_intakes.rb b/spec/factories/state_file_nj_intakes.rb index 5c407d748f..ca97757ef3 100644 --- a/spec/factories/state_file_nj_intakes.rb +++ b/spec/factories/state_file_nj_intakes.rb @@ -185,6 +185,11 @@ raw_direct_file_intake_data { StateFile::DirectFileApiResponseSampleService.new.read_json('nj_married_filing_separately') } end + trait :df_data_exempt_interest do + raw_direct_file_data { StateFile::DirectFileApiResponseSampleService.new.read_xml('nj_exempt_interest_over_10k') } + raw_direct_file_intake_data { StateFile::DirectFileApiResponseSampleService.new.read_json('nj_exempt_interest_over_10k') } + end + trait :married_filing_jointly do filing_status { "married_filing_jointly" } spouse_birth_date { Date.new(1990, 1, 1) } diff --git a/spec/fixtures/state_file/fed_return_jsons/2023/nj/exempt_interest_over_10k.json b/spec/fixtures/state_file/fed_return_jsons/2023/nj/exempt_interest_over_10k.json new file mode 100644 index 0000000000..5f20584636 --- /dev/null +++ b/spec/fixtures/state_file/fed_return_jsons/2023/nj/exempt_interest_over_10k.json @@ -0,0 +1,46 @@ +{ + "familyAndHousehold": [], + "filers": [ + { + "firstName": "Ernie", + "middleInitial": null, + "lastName": "Muppet", + "dateOfBirth": "1980-01-01", + "isPrimaryFiler": true, + "tin": "400-00-0039" + }, + { + "firstName": "Bert", + "middleInitial": "K", + "lastName": "Muppet", + "dateOfBirth": "1990-01-01", + "isPrimaryFiler": false, + "tin": "600-00-0039" + } + ], + "interestReports": [ + { + "1099Amount": "0.00", + "has1099": false, + "interestOnGovernmentBonds": "5000.00", + "no1099Amount": "800.00", + "recipientTin": "400-00-0039", + "taxExemptInterest": "0.00", + "payer": "The payer name", + "payerTin": "101-23-4567", + "taxWithheld": "0.00", + "taxExemptAndTaxCreditBondCusipNo": "123456789" + }, + { + "1099Amount": "0.00", + "has1099": false, + "interestOnGovernmentBonds": "5000.00", + "no1099Amount": "800.00", + "recipientTin": "400-00-0039", + "taxExemptInterest": "0.00", + "payer": "The payer name", + "payerTin": "101-23-4567", + "taxWithheld": "0.00", + "taxExemptAndTaxCreditBondCusipNo": "123456789" + }] +} \ No newline at end of file diff --git a/spec/fixtures/state_file/fed_return_xmls/2023/nj/exempt_interest_over_10k.xml b/spec/fixtures/state_file/fed_return_xmls/2023/nj/exempt_interest_over_10k.xml new file mode 100644 index 0000000000..14063d6a9d --- /dev/null +++ b/spec/fixtures/state_file/fed_return_xmls/2023/nj/exempt_interest_over_10k.xml @@ -0,0 +1,134 @@ + + + + 2023 + 2023-01-01 + 2023-12-31 + 2023.0.1 + 1040 + + 400000039 + 600000039 + BERT & ERNIE<MUPPET + MUPP + BERT + + 123 Sesame St Apt 1 + Hammonton + NJ + 85034 + + 4805559876 + + + + bert@sesame.com + + + + + + 2 + Ernie Muppet + false + X + 1 + 2 + 0 + 0 + 2 + 9000 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 9000 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 9000 + 0 + 9000 + 29200 + 29200 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 900 + 0 + 0 + 900 + 0 + 0 + 900 + 900 + 900 + 0 + 0 + 0 + false + Eyebrow technician + Rubber ducky manufacturer + NO FINANCIAL PRODUCT + + + 400000039 + 678912345 + SESA + + Sesame Street Eye Lip and Hair Removal + + + 300 Josephine St + Hammonton + NJ + 80212 + + Bert Muppet + + 123 Sesame St Apt 1 + Hammonton + NJ + 85034 + + 9000 + 900 + 9000 + 558 + 9000 + 131 + 0 + 0 + 0 + + + NJ + 12345 + 9000 + 15 + + 0 + 0 + + + + S + + + diff --git a/spec/fixtures/state_file/fed_return_xmls/2023/nj/zeus_two_deps.xml b/spec/fixtures/state_file/fed_return_xmls/2023/nj/zeus_two_deps.xml index 7b13604cc9..785f54fe49 100644 --- a/spec/fixtures/state_file/fed_return_xmls/2023/nj/zeus_two_deps.xml +++ b/spec/fixtures/state_file/fed_return_xmls/2023/nj/zeus_two_deps.xml @@ -118,6 +118,7 @@ 4 50000 50000 + 1 500 8000 6800 diff --git a/spec/lib/efile/nj/nj1040_calculator_spec.rb b/spec/lib/efile/nj/nj1040_calculator_spec.rb index 63e86f6a90..adf58b9e8b 100644 --- a/spec/lib/efile/nj/nj1040_calculator_spec.rb +++ b/spec/lib/efile/nj/nj1040_calculator_spec.rb @@ -414,6 +414,29 @@ def over_65_birth_year end end + describe 'line 16b tax exempt interest income' do + context 'with federal tax exempt interest income and interest on government bonds' do + let(:intake) { create(:state_file_nj_intake, :df_data_exempt_interest) } + it 'calculates the sum' do + expect(instance.calculate_tax_exempt_interest_income).to eq(10_001) + end + end + + context 'with no tax exempt interest income' do + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } + it 'does not set line 16b' do + expect(instance.lines[:NJ1040_LINE_16B].value).to eq(nil) + end + end + + context 'with tax exempt interest income and interest on government bonds less than 10k' do + let(:intake) { create(:state_file_nj_intake, :df_data_two_deps) } + it 'sets line 1b to the sum' do + expect(instance.lines[:NJ1040_LINE_16B].value).to eq(201) + end + end + end + describe 'line 27 - total income' do let(:intake) { create(:state_file_nj_intake) } diff --git a/spec/lib/pdf_filler/nj1040_pdf_spec.rb b/spec/lib/pdf_filler/nj1040_pdf_spec.rb index bca9ec623e..9a35f88395 100644 --- a/spec/lib/pdf_filler/nj1040_pdf_spec.rb +++ b/spec/lib/pdf_filler/nj1040_pdf_spec.rb @@ -3,11 +3,12 @@ RSpec.describe PdfFiller::Nj1040Pdf do include PdfSpecHelper - let(:submission) { create :efile_submission, tax_return: nil, data_source: create(:state_file_nj_intake) } + let(:submission) { create :efile_submission, tax_return: nil, data_source: intake } let(:pdf) { described_class.new(submission) } describe '#hash_for_pdf' do let(:pdf_fields) { filled_in_values(submission.generate_filing_pdf.path) } + let(:intake) { create(:state_file_nj_intake)} it 'uses field names that exist in the pdf' do missing_fields = pdf.hash_for_pdf.keys.map(&:to_s) - pdf_fields.keys @@ -443,12 +444,6 @@ end describe "address fields" do - let(:submission) { - create :efile_submission, tax_return: nil, data_source: create( - :state_file_nj_intake, - ) - } - it 'enters values into PDF' do # address values from zeus_one_dep.xml expect(pdf_fields["SpousesCU Partners SSN if filing jointly"]).to eq "391 US-206 B" @@ -467,9 +462,6 @@ :df_data_one_dep ) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } before do intake.dependents[0].update(dob: Date.new(2023, 1, 1)) @@ -518,9 +510,6 @@ :df_data_many_deps ) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } before do intake.dependents.each_with_index do |dependent, i| @@ -613,12 +602,7 @@ describe "line 15 wages" do context "when no w2 wages" do - let(:submission) { - create :efile_submission, tax_return: nil, data_source: create( - :state_file_nj_intake, - :df_data_minimal - ) - } + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } it "does not fill in any box on line 15" do expect(pdf_fields["15"]).to eq "" @@ -692,9 +676,6 @@ describe "line 16a taxable interest income" do context 'with interest reports, but no interest on government bonds' do let(:intake) { create(:state_file_nj_intake, :df_data_one_dep) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } it 'does not set line 16a' do ["112", @@ -712,9 +693,6 @@ context 'with interest on government bonds' do let(:intake) { create(:state_file_nj_intake, :df_data_two_deps) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } it 'sets line 16a to 300 (fed taxable income minus sum of bond interest)' do expect(pdf_fields["undefined_43"]).to eq "" @@ -731,12 +709,39 @@ end end + describe "line 16b tax exempt interest income" do + context "with no tax exempt interest income" do + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } + it 'does not set line 16b' do + ["117", + "116", + "115", + "114", + "113", + "undefined_44", + "16a", + "undefined_42", + "16b"].each { |pdf_field| expect(pdf_fields[pdf_field]).to eq "" } + end + end + + context "with tax exempt interest income and interest on government bonds less than 10k" do + let(:intake) { create(:state_file_nj_intake, :df_data_two_deps) } + it 'sets line 16b to the sum' do + expect(pdf_fields["16b"]).to eq "" + expect(pdf_fields["undefined_42"]).to eq "" + expect(pdf_fields["16a"]).to eq "" + expect(pdf_fields["undefined_44"]).to eq "" + expect(pdf_fields["113"]).to eq "2" + expect(pdf_fields["114"]).to eq "0" + expect(pdf_fields["115"]).to eq "1" + expect(pdf_fields["116"]).to eq "0" + expect(pdf_fields["117"]).to eq "0" + end + end + end + describe "line 13/30 total exemptions" do - let(:submission) { - create :efile_submission, tax_return: nil, data_source: create( - :state_file_nj_intake - ) - } it "totals line 6-8 and writes it to line 13" do # thousands expect(pdf_fields["undefined_15"]).to eq "" @@ -866,12 +871,8 @@ end context "when taxpayer provides total income of 0" do - let(:submission) { - create :efile_submission, tax_return: nil, data_source: create( - :state_file_nj_intake, - :df_data_minimal - ) - } + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } + it "does not fill in any of the boxes on line 27" do # millions expect(pdf_fields["263"]).to eq "" @@ -921,12 +922,8 @@ end context "when taxpayer provides total income of 0" do - let(:submission) { - create :efile_submission, tax_return: nil, data_source: create( - :state_file_nj_intake, - :df_data_minimal - ) - } + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } + it "does not fill in any of the boxes on line 29" do # millions expect(pdf_fields["270"]).to eq "" @@ -1340,9 +1337,6 @@ let(:intake) { create(:state_file_nj_intake, :df_data_one_dep, :with_w2s_synced, :fed_credit_for_child_and_dependent_care) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } it "adds 40% of federal credit for an income of 60k or less" do digits_in_pdf = "" digit_fields = [ @@ -1370,9 +1364,6 @@ :with_w2s_synced ) } - let(:submission) { - create :efile_submission, tax_return: nil, data_source: intake - } before do intake.dependents.first.update(dob: Date.new(2023, 1, 1)) end diff --git a/spec/lib/submission_builder/ty2024/states/nj/documents/nj1040_spec.rb b/spec/lib/submission_builder/ty2024/states/nj/documents/nj1040_spec.rb index 7cee921e64..3660a5d7c9 100644 --- a/spec/lib/submission_builder/ty2024/states/nj/documents/nj1040_spec.rb +++ b/spec/lib/submission_builder/ty2024/states/nj/documents/nj1040_spec.rb @@ -318,6 +318,22 @@ end end + describe 'line 16b tax exempt interest income' do + context 'with no tax exempt interest income' do + let(:intake) { create(:state_file_nj_intake, :df_data_minimal) } + it 'does not set line 16b' do + expect(xml.at("Body TaxexemptInterestIncome")).to eq(nil) + end + end + + context 'with tax exempt interest income and interest on government bonds less than 10k' do + let(:intake) { create(:state_file_nj_intake, :df_data_two_deps) } + it 'sets line 16b to the sum' do + expect(xml.at("Body TaxexemptInterestIncome").text).to eq('201') + end + end + end + describe "total income - line 27" do context "when filer submits w2 wages" do it "fills TotalIncome with the value from Line 15" do diff --git a/spec/models/state_file_nj_intake_spec.rb b/spec/models/state_file_nj_intake_spec.rb index 9485e0b2ad..a663c7e642 100644 --- a/spec/models/state_file_nj_intake_spec.rb +++ b/spec/models/state_file_nj_intake_spec.rb @@ -103,5 +103,30 @@ require 'rails_helper' RSpec.describe StateFileNjIntake, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end + describe "#disqualifying_df_data_reason" do + context "when federal non taxable interest income exceeds 10k" do + let(:intake) { create :state_file_nj_intake, :df_data_minimal } + before do + intake.direct_file_data.fed_tax_exempt_interest = 10_001 + end + + it "returns exempt_interest_exceeds_10k" do + expect(intake.disqualifying_df_data_reason).to eq :exempt_interest_exceeds_10k + end + end + + context "when gov bonds are 10k and federal tax exempt interest amt is 1" do + let(:intake) { create :state_file_nj_intake, :df_data_exempt_interest } + it "returns exempt_interest_exceeds_10k" do + expect(intake.disqualifying_df_data_reason).to eq :exempt_interest_exceeds_10k + end + end + + context "when there are no disqualifying reasons" do + let(:intake) { create :state_file_nj_intake, :df_data_two_deps } + it "returns nil" do + expect(intake.disqualifying_df_data_reason).to eq nil + end + end + end +end \ No newline at end of file