diff --git a/app/jobs/state_file/create_nj_analytics_record_job.rb b/app/jobs/state_file/create_nj_analytics_record_job.rb new file mode 100644 index 0000000000..bd133efa60 --- /dev/null +++ b/app/jobs/state_file/create_nj_analytics_record_job.rb @@ -0,0 +1,13 @@ +module StateFile + class CreateNjAnalyticsRecordJob < ApplicationJob + def perform(submission_id) + submission = EfileSubmission.find(submission_id) + nj_analytics = submission.data_source.create_state_file_nj_analytics! + nj_analytics&.update(nj_analytics.calculated_fields) + end + + def priority + PRIORITY_LOW + end + end +end diff --git a/app/lib/efile/line_data.yml b/app/lib/efile/line_data.yml index 8af0902556..8c74cbc7b9 100644 --- a/app/lib/efile/line_data.yml +++ b/app/lib/efile/line_data.yml @@ -858,6 +858,10 @@ NJ1040_LINE_7_SELF: label: '7 Primary Senior 65+ Exemption' NJ1040_LINE_7_SPOUSE: label: '7 Spouse Senior 65+ Exemption' +NJ1040_LINE_8_SELF: + label: '8 Primary Blind/Disabled Exemption' +NJ1040_LINE_8_SPOUSE: + label: '8 Spouse Blind/Disabled Exemption' NJ1040_LINE_10_COUNT: label: 'Number of Qualified Dependent Children' NJ1040_LINE_10_EXEMPTION: diff --git a/app/lib/efile/nj/nj1040_calculator.rb b/app/lib/efile/nj/nj1040_calculator.rb index 490e99f536..d1a42d1181 100644 --- a/app/lib/efile/nj/nj1040_calculator.rb +++ b/app/lib/efile/nj/nj1040_calculator.rb @@ -27,6 +27,8 @@ def calculate set_line(:NJ1040_LINE_6_SPOUSE, :line_6_spouse_checkbox) set_line(:NJ1040_LINE_7_SELF, :line_7_self_checkbox) set_line(:NJ1040_LINE_7_SPOUSE, :line_7_spouse_checkbox) + set_line(:NJ1040_LINE_8_SELF, :line_8_self_checkbox) + set_line(:NJ1040_LINE_8_SPOUSE, :line_8_spouse_checkbox) set_line(:NJ1040_LINE_10_COUNT, :calculate_line_10_count) set_line(:NJ1040_LINE_10_EXEMPTION, :calculate_line_10_exemption) set_line(:NJ1040_LINE_11_COUNT, :calculate_line_11_count) @@ -211,8 +213,8 @@ def calculate_line_7 end def calculate_line_8 - number_of_line_8_exemptions = number_of_true_checkboxes([@direct_file_data.is_primary_blind? || @intake.primary_disabled_yes?, - @direct_file_data.is_spouse_blind? || @intake.spouse_disabled_yes?]) + number_of_line_8_exemptions = number_of_true_checkboxes([line_8_self_checkbox, + line_8_spouse_checkbox]) number_of_line_8_exemptions * 1_000 end @@ -283,6 +285,14 @@ def line_6_spouse_checkbox @intake.filing_status_mfj? end + def line_8_self_checkbox + @direct_file_data.is_primary_blind? || @intake.primary_disabled_yes? + end + + def line_8_spouse_checkbox + @direct_file_data.is_spouse_blind? || @intake.spouse_disabled_yes? + end + def calculate_line_13 calculate_line_6 + calculate_line_7 + diff --git a/app/models/state_file_nj_analytics.rb b/app/models/state_file_nj_analytics.rb new file mode 100644 index 0000000000..3308c208d8 --- /dev/null +++ b/app/models/state_file_nj_analytics.rb @@ -0,0 +1,74 @@ +# == Schema Information +# +# Table name: state_file_nj_analytics +# +# id :bigint not null, primary key +# NJ1040_LINE_12_COUNT :integer default(0), not null +# NJ1040_LINE_15 :integer default(0), not null +# NJ1040_LINE_16A :integer default(0), not null +# NJ1040_LINE_16B :integer default(0), not null +# NJ1040_LINE_29 :integer default(0), not null +# NJ1040_LINE_31 :integer default(0), not null +# NJ1040_LINE_41 :integer default(0), not null +# NJ1040_LINE_42 :integer default(0), not null +# NJ1040_LINE_43 :integer default(0), not null +# NJ1040_LINE_51 :integer default(0), not null +# NJ1040_LINE_56 :integer default(0), not null +# NJ1040_LINE_58 :integer default(0), not null +# NJ1040_LINE_58_IRS :boolean +# NJ1040_LINE_59 :integer default(0), not null +# NJ1040_LINE_61 :integer default(0), not null +# NJ1040_LINE_64 :integer default(0), not null +# NJ1040_LINE_65 :integer default(0), not null +# NJ1040_LINE_65_DEPENDENTS :integer default(0), not null +# NJ1040_LINE_7_SELF :boolean +# NJ1040_LINE_7_SPOUSE :boolean +# NJ1040_LINE_8_SELF :boolean +# NJ1040_LINE_8_SPOUSE :boolean +# claimed_as_dep :boolean +# created_at :datetime not null +# updated_at :datetime not null +# state_file_nj_intake_id :bigint not null +# +# Indexes +# +# index_state_file_nj_analytics_on_state_file_nj_intake_id (state_file_nj_intake_id) +# +class StateFileNjAnalytics < ApplicationRecord + belongs_to :state_file_nj_intake + + def calculated_fields + nj1040_fields = state_file_nj_intake.tax_calculator.calculate + required_fields = [ + :NJ1040_LINE_7_SELF, + :NJ1040_LINE_7_SPOUSE, + :NJ1040_LINE_8_SELF, + :NJ1040_LINE_8_SPOUSE, + :NJ1040_LINE_12_COUNT, + :NJ1040_LINE_15, + :NJ1040_LINE_16A, + :NJ1040_LINE_16B, + :NJ1040_LINE_29, + :NJ1040_LINE_31, + :NJ1040_LINE_41, + :NJ1040_LINE_42, + :NJ1040_LINE_43, + :NJ1040_LINE_51, + :NJ1040_LINE_56, + :NJ1040_LINE_58, + :NJ1040_LINE_58_IRS, + :NJ1040_LINE_59, + :NJ1040_LINE_61, + :NJ1040_LINE_64, + :NJ1040_LINE_65, + :NJ1040_LINE_65_DEPENDENTS + ] + metabase_metrics = { + claimed_as_dep: state_file_nj_intake.direct_file_data.claimed_as_dependent?, + } + required_fields.each do |metric| + metabase_metrics[metric] = nj1040_fields[metric] || 0 + end + metabase_metrics + end +end diff --git a/app/models/state_file_nj_intake.rb b/app/models/state_file_nj_intake.rb index f211eff1d9..dc25f4207f 100644 --- a/app/models/state_file_nj_intake.rb +++ b/app/models/state_file_nj_intake.rb @@ -114,6 +114,7 @@ class StateFileNjIntake < StateFileBaseIntake self.ignored_columns += ["primary_signature_pin", "spouse_signature_pin"] encrypts :account_number, :routing_number, :raw_direct_file_data, :raw_direct_file_intake_data + has_one :state_file_nj_analytics enum household_rent_own: { unfilled: 0, rent: 1, own: 2, neither: 3, both: 4 }, _prefix: :household_rent_own diff --git a/app/state_machines/efile_submission_state_machine.rb b/app/state_machines/efile_submission_state_machine.rb index d9aff872ff..b6c18aaab5 100644 --- a/app/state_machines/efile_submission_state_machine.rb +++ b/app/state_machines/efile_submission_state_machine.rb @@ -77,6 +77,9 @@ class EfileSubmissionStateMachine after_transition(to: :queued) do |submission| StateFile::SendSubmissionJob.perform_later(submission) StateFile::BuildSubmissionPdfJob.perform_later(submission.id) + if submission.data_source.state_code == 'nj' + StateFile::CreateNjAnalyticsRecordJob.perform_later(submission.id) + end end after_transition(to: :transmitted) do |submission| @@ -131,13 +134,13 @@ class EfileSubmissionStateMachine end after_transition do |submission, transition| - from_status = ( + from_status = EfileSubmissionTransition .where(efile_submission_id: transition.efile_submission_id) .where.not(id: transition.id) .last &.to_state - ) + Rails.logger.info({ event_type: "submission_transition", from_status: from_status, diff --git a/db/migrate/20250205153246_create_state_file_nj_analytics.rb b/db/migrate/20250205153246_create_state_file_nj_analytics.rb new file mode 100644 index 0000000000..f2b3fac3b0 --- /dev/null +++ b/db/migrate/20250205153246_create_state_file_nj_analytics.rb @@ -0,0 +1,31 @@ +class CreateStateFileNjAnalytics < ActiveRecord::Migration[7.1] + def change + create_table :state_file_nj_analytics do |t| + t.belongs_to :state_file_nj_intake, null: false + t.boolean :claimed_as_dep + t.boolean :NJ1040_LINE_7_SELF + t.boolean :NJ1040_LINE_7_SPOUSE + t.boolean :NJ1040_LINE_8_SELF + t.boolean :NJ1040_LINE_8_SPOUSE + t.integer :NJ1040_LINE_12_COUNT, default: 0, null: false + t.integer :NJ1040_LINE_15, default: 0, null: false + t.integer :NJ1040_LINE_16A, default: 0, null: false + t.integer :NJ1040_LINE_16B, default: 0, null: false + t.integer :NJ1040_LINE_29, default: 0, null: false + t.integer :NJ1040_LINE_31, default: 0, null: false + t.integer :NJ1040_LINE_41, default: 0, null: false + t.integer :NJ1040_LINE_42, default: 0, null: false + t.integer :NJ1040_LINE_43, default: 0, null: false + t.integer :NJ1040_LINE_51, default: 0, null: false + t.integer :NJ1040_LINE_56, default: 0, null: false + t.integer :NJ1040_LINE_58, default: 0, null: false + t.boolean :NJ1040_LINE_58_IRS + t.integer :NJ1040_LINE_59, default: 0, null: false + t.integer :NJ1040_LINE_61, default: 0, null: false + t.integer :NJ1040_LINE_64, default: 0, null: false + t.integer :NJ1040_LINE_65, default: 0, null: false + t.integer :NJ1040_LINE_65_DEPENDENTS, default: 0, null: false + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 71063b2e00..7e4c4b885d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2025_01_30_234706) do +ActiveRecord::Schema[7.1].define(version: 2025_02_05_153246) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "plpgsql" @@ -2245,6 +2245,36 @@ t.datetime "updated_at", null: false end + create_table "state_file_nj_analytics", force: :cascade do |t| + t.integer "NJ1040_LINE_12_COUNT", default: 0, null: false + t.integer "NJ1040_LINE_15", default: 0, null: false + t.integer "NJ1040_LINE_16A", default: 0, null: false + t.integer "NJ1040_LINE_16B", default: 0, null: false + t.integer "NJ1040_LINE_29", default: 0, null: false + t.integer "NJ1040_LINE_31", default: 0, null: false + t.integer "NJ1040_LINE_41", default: 0, null: false + t.integer "NJ1040_LINE_42", default: 0, null: false + t.integer "NJ1040_LINE_43", default: 0, null: false + t.integer "NJ1040_LINE_51", default: 0, null: false + t.integer "NJ1040_LINE_56", default: 0, null: false + t.integer "NJ1040_LINE_58", default: 0, null: false + t.boolean "NJ1040_LINE_58_IRS" + t.integer "NJ1040_LINE_59", default: 0, null: false + t.integer "NJ1040_LINE_61", default: 0, null: false + t.integer "NJ1040_LINE_64", default: 0, null: false + t.integer "NJ1040_LINE_65", default: 0, null: false + t.integer "NJ1040_LINE_65_DEPENDENTS", default: 0, null: false + t.boolean "NJ1040_LINE_7_SELF" + t.boolean "NJ1040_LINE_7_SPOUSE" + t.boolean "NJ1040_LINE_8_SELF" + t.boolean "NJ1040_LINE_8_SPOUSE" + t.boolean "claimed_as_dep" + t.datetime "created_at", null: false + t.bigint "state_file_nj_intake_id", null: false + t.datetime "updated_at", null: false + t.index ["state_file_nj_intake_id"], name: "index_state_file_nj_analytics_on_state_file_nj_intake_id" + end + create_table "state_file_nj_intakes", force: :cascade do |t| t.string "account_number" t.integer "account_type", default: 0, null: false diff --git a/spec/jobs/state_file/create_nj_analytics_record_job_spec.rb b/spec/jobs/state_file/create_nj_analytics_record_job_spec.rb new file mode 100644 index 0000000000..df6847de7e --- /dev/null +++ b/spec/jobs/state_file/create_nj_analytics_record_job_spec.rb @@ -0,0 +1,15 @@ +require "rails_helper" + +describe StateFile::CreateNjAnalyticsRecordJob do + describe "#perform" do + let!(:submitted_intake) { create :state_file_nj_intake, email_address: 'test+01@example.com', email_address_verified_at: 1.minute.ago } + let!(:submission) { create :efile_submission, :for_state, data_source: submitted_intake } + + it "populates the NJ analytics record attached to intake" do + expect(submitted_intake.state_file_nj_analytics).not_to be_present + described_class.perform_now(submission.id) + submitted_intake.reload + expect(submitted_intake.state_file_nj_analytics).to be_present + end + end +end \ No newline at end of file diff --git a/spec/models/state_file_nj_analytics_spec.rb b/spec/models/state_file_nj_analytics_spec.rb new file mode 100644 index 0000000000..7d7768a14e --- /dev/null +++ b/spec/models/state_file_nj_analytics_spec.rb @@ -0,0 +1,132 @@ +# == Schema Information +# +# Table name: state_file_nj_analytics +# +# id :bigint not null, primary key +# NJ1040_LINE_12_COUNT :integer default(0), not null +# NJ1040_LINE_15 :integer default(0), not null +# NJ1040_LINE_16A :integer default(0), not null +# NJ1040_LINE_16B :integer default(0), not null +# NJ1040_LINE_29 :integer default(0), not null +# NJ1040_LINE_31 :integer default(0), not null +# NJ1040_LINE_41 :integer default(0), not null +# NJ1040_LINE_42 :integer default(0), not null +# NJ1040_LINE_43 :integer default(0), not null +# NJ1040_LINE_51 :integer default(0), not null +# NJ1040_LINE_56 :integer default(0), not null +# NJ1040_LINE_58 :integer default(0), not null +# NJ1040_LINE_58_IRS :boolean +# NJ1040_LINE_59 :integer default(0), not null +# NJ1040_LINE_61 :integer default(0), not null +# NJ1040_LINE_64 :integer default(0), not null +# NJ1040_LINE_65 :integer default(0), not null +# NJ1040_LINE_65_DEPENDENTS :integer default(0), not null +# NJ1040_LINE_7_SELF :boolean +# NJ1040_LINE_7_SPOUSE :boolean +# NJ1040_LINE_8_SELF :boolean +# NJ1040_LINE_8_SPOUSE :boolean +# claimed_as_dep :boolean +# created_at :datetime not null +# updated_at :datetime not null +# state_file_nj_intake_id :bigint not null +# +# Indexes +# +# index_state_file_nj_analytics_on_state_file_nj_intake_id (state_file_nj_intake_id) +# +require 'rails_helper' + +describe StateFileNjAnalytics do + describe "#calculated_attrs" do + let(:intake) { create :state_file_nj_intake } + + it "sets intake columns for metabase analytics" do + analytics_record = StateFileNjAnalytics.create(state_file_nj_intake: intake) + + expected_claimed_as_dep = true + expected_line_7_self = true + expected_line_7_spouse = true + expected_line_8_self = true + expected_line_8_spouse = true + expected_line_12_count = 18 + expected_line_15 = 10_000 + expected_line_16a = 20_000 + expected_line_16b = 0 + expected_line_29 = 40_000 + expected_line_31 = 1_000 + expected_line_41 = 2_000 + expected_line_42 = 3_000 + expected_line_43 = 4_000 + expected_line_51 = 5_000 + expected_line_56 = 6_000 + expected_line_58 = 7_000 + expected_line_58_irs = true + expected_line_59 = 9_000 + expected_line_61 = 9 + expected_line_64 = 10 + expected_line_65 = 1_000 + expected_line_65_dependents = 3 + + allow(analytics_record.state_file_nj_intake.direct_file_data).to receive(:claimed_as_dependent?).and_return expected_claimed_as_dep + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:line_7_self_checkbox).and_return expected_line_7_self + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:line_7_spouse_checkbox).and_return expected_line_7_spouse + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:line_8_self_checkbox).and_return expected_line_8_self + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:line_8_spouse_checkbox).and_return expected_line_8_spouse + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:line_12_count).and_return expected_line_12_count + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_15).and_return expected_line_15 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_16a).and_return expected_line_16a + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_16b).and_return expected_line_16b + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_29).and_return expected_line_29 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_31).and_return expected_line_31 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_41).and_return expected_line_41 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_42).and_return expected_line_42 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_43).and_return expected_line_43 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_51).and_return expected_line_51 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_56).and_return expected_line_56 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_58).and_return expected_line_58 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_58_irs).and_return expected_line_58_irs + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_59).and_return expected_line_59 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_61).and_return expected_line_61 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_64).and_return expected_line_64 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_65).and_return expected_line_65 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:number_of_dependents_age_5_younger).and_return expected_line_65_dependents + + columns = analytics_record.calculated_fields + + expect(columns[:claimed_as_dep]).to eq(expected_claimed_as_dep) + expect(columns[:NJ1040_LINE_7_SELF]).to eq(expected_line_7_self) + expect(columns[:NJ1040_LINE_7_SPOUSE]).to eq(expected_line_7_spouse) + expect(columns[:NJ1040_LINE_8_SELF]).to eq(expected_line_8_self) + expect(columns[:NJ1040_LINE_8_SPOUSE]).to eq(expected_line_8_spouse) + expect(columns[:NJ1040_LINE_12_COUNT]).to eq(expected_line_12_count) + expect(columns[:NJ1040_LINE_15]).to eq(expected_line_15) + expect(columns[:NJ1040_LINE_16A]).to eq(expected_line_16a) + expect(columns[:NJ1040_LINE_16B]).to eq(expected_line_16b) + expect(columns[:NJ1040_LINE_29]).to eq(expected_line_29) + expect(columns[:NJ1040_LINE_31]).to eq(expected_line_31) + expect(columns[:NJ1040_LINE_41]).to eq(expected_line_41) + expect(columns[:NJ1040_LINE_42]).to eq(expected_line_42) + expect(columns[:NJ1040_LINE_43]).to eq(expected_line_43) + expect(columns[:NJ1040_LINE_51]).to eq(expected_line_51) + expect(columns[:NJ1040_LINE_56]).to eq(expected_line_56) + expect(columns[:NJ1040_LINE_58]).to eq(expected_line_58) + expect(columns[:NJ1040_LINE_58_IRS]).to eq(expected_line_58_irs) + expect(columns[:NJ1040_LINE_59]).to eq(expected_line_59) + expect(columns[:NJ1040_LINE_61]).to eq(expected_line_61) + expect(columns[:NJ1040_LINE_64]).to eq(expected_line_64) + expect(columns[:NJ1040_LINE_65]).to eq(expected_line_65) + expect(columns[:NJ1040_LINE_65_DEPENDENTS]).to eq(expected_line_65_dependents) + end + + it "sets nil values from the calculator to 0 in the record" do + analytics_record = StateFileNjAnalytics.create(state_file_nj_intake: intake) + + expected_line_16b = 0 + allow_any_instance_of(Efile::Nj::Nj1040Calculator).to receive(:calculate_line_16b).and_return nil + + columns = analytics_record.calculated_fields + + expect(columns[:NJ1040_LINE_16B]).to eq(expected_line_16b) + end + end +end diff --git a/spec/state_machines/efile_submission_state_machine_spec.rb b/spec/state_machines/efile_submission_state_machine_spec.rb index 46b582dc05..924d88422b 100644 --- a/spec/state_machines/efile_submission_state_machine_spec.rb +++ b/spec/state_machines/efile_submission_state_machine_spec.rb @@ -48,6 +48,31 @@ end end + context "to queued" do + let!(:submission) { create(:efile_submission, :bundling, :for_state, data_source: submitted_intake) } + before do + allow(StateFile::CreateNjAnalyticsRecordJob).to receive(:perform_later) + end + + context "for NJ" do + let!(:submitted_intake) { create :state_file_nj_intake } + + it "creates the NJ Analytics record" do + submission.transition_to!(:queued) + expect(StateFile::CreateNjAnalyticsRecordJob).to have_received(:perform_later).with(submission.id) + end + end + + context "for not NJ" do + let!(:submitted_intake) { create :state_file_az_intake } + + it "does not create the NJ Analytics record" do + submission.transition_to!(:queued) + expect(StateFile::CreateNjAnalyticsRecordJob).not_to have_received(:perform_later).with(submission.id) + end + end + end + context "to transmitted" do let(:submission) { create(:efile_submission, :queued, :for_state) } before do @@ -61,7 +86,7 @@ expect(submission.data_source.state_file_analytics.fed_eitc_amount).to eq 1776 expect(submission.data_source.state_file_analytics.filing_status).to eq 1 - expect(submission.data_source.state_file_analytics.refund_or_owed_amount).to eq -2000 + expect(submission.data_source.state_file_analytics.refund_or_owed_amount).to eq(-2000) end end