From 61e3adde505805eb879b66cbae2239ceb6efa353 Mon Sep 17 00:00:00 2001 From: tofarr Date: Tue, 9 Apr 2024 12:26:23 -0600 Subject: [PATCH 01/20] Center content on desktop #186632607 (#4444) * Center content on desktop * Made content a bit wider * Centered buttons on desktop --- app/assets/stylesheets/_state-file.scss | 75 +++++++++++++++++-- .../layouts/state_file/question.html.erb | 2 +- .../questions/landing_page/edit.html.erb | 1 + .../state_file/questions/w2/edit.html.erb | 6 +- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/_state-file.scss b/app/assets/stylesheets/_state-file.scss index 5252310466..1041d894a3 100644 --- a/app/assets/stylesheets/_state-file.scss +++ b/app/assets/stylesheets/_state-file.scss @@ -426,11 +426,6 @@ } } - .landing-page-content { - padding-left: 1rem; - padding-right: 1rem; - } - .partner-logo-wrapper { display: flex; align-items: center; @@ -627,6 +622,76 @@ text-decoration: none; } + @media screen and (min-width: $tablet-up) { + .question-layout .question-wrapper { + margin: 0 auto; + max-width: 658px; + + .checkbox { + width: 100%; + } + + .button { + display: block; + min-width: 15rem; + max-width: 25rem; + margin: 0 auto; + } + } + + .options-wrapper .yes-no-buttons { + display: flex; + justify-content: center; + + .button { + margin: 0 2rem; + display: block; + min-width: 25rem; + max-width: 25rem; + } + } + + .question-layout.landing-page-outer .question-wrapper { + max-width: none; + + main { + display: flex; + justify-content: space-between; + .landing-page-content { + max-width: 570px; + } + .fyst-home-image { + width: 273px; + height: 250px; + } + } + } + + .initiate-data-transfer-outer .question-wrapper a.button { + width: 100%; + min-width: 100%; + max-width: 100%; + } + + .esign-declaration-outer, .return-status-outer, .submission-confirmation-outer { + + .question-wrapper .button--primary { + min-width: 40rem; + max-width: 40rem; + width: 40rem; + } + } + + .esign-declaration-outer .text--error { + margin: 0 auto; + text-align: center; + } + } + @media screen and (max-width: $tablet-up) { + .landing-page-container .fyst-home-image { + display: none + } + } } $state-colors: ( diff --git a/app/views/layouts/state_file/question.html.erb b/app/views/layouts/state_file/question.html.erb index f881145851..06ca5bcd4b 100644 --- a/app/views/layouts/state_file/question.html.erb +++ b/app/views/layouts/state_file/question.html.erb @@ -5,7 +5,7 @@ <%= yield :form_question %> <% end %> -
+
-outer">
<%= yield :notices %> diff --git a/app/views/state_file/questions/landing_page/edit.html.erb b/app/views/state_file/questions/landing_page/edit.html.erb index f395165246..7430d9df7c 100644 --- a/app/views/state_file/questions/landing_page/edit.html.erb +++ b/app/views/state_file/questions/landing_page/edit.html.erb @@ -46,4 +46,5 @@ <% end %>
+ <%= image_tag 'questions/welcome.svg', class: 'fyst-home-image' %> <% end %> diff --git a/app/views/state_file/questions/w2/edit.html.erb b/app/views/state_file/questions/w2/edit.html.erb index f2770acc1e..85006dee8a 100644 --- a/app/views/state_file/questions/w2/edit.html.erb +++ b/app/views/state_file/questions/w2/edit.html.erb @@ -28,9 +28,9 @@
<%= f.cfa_input_field(:locality_nm, t(".box20_locality_name"), classes: ["form-width--long"]) %>
-
+ <% end %> <% end %> From 452aacb9830d189d6bcd6bdba7c029ea80cbd9d8 Mon Sep 17 00:00:00 2001 From: Tahsina Islam <79296603+tahsinaislam@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:34:35 -0500 Subject: [PATCH 02/20] pre-populate w2 info (#4457) Co-authored-by: Tim O'Farrell --- .../state_file/questions/w2_controller.rb | 15 ++++++++++++++- app/models/state_file_w2.rb | 8 ++++---- config/locales/en.yml | 5 +++-- config/locales/es.yml | 5 +++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/controllers/state_file/questions/w2_controller.rb b/app/controllers/state_file/questions/w2_controller.rb index 891efba00a..d7d4dcaadc 100644 --- a/app/controllers/state_file/questions/w2_controller.rb +++ b/app/controllers/state_file/questions/w2_controller.rb @@ -86,7 +86,20 @@ def self.w2s_for_intake(intake) (intake.direct_file_data.w2s.each_with_index.map do |df_w2, index| if intake.invalid_df_w2?(df_w2) existing_record = intake.state_file_w2s.find { |intake_w2| intake_w2.w2_index == index } - existing_record.present? ? existing_record : StateFileW2.new(state_file_intake: intake, w2_index: index) + if existing_record.present? + existing_record + else + StateFileW2.new( + state_file_intake: intake, + w2_index: index, + employer_state_id_num: df_w2.EmployerStateIdNum, + state_wages_amt: df_w2.StateWagesAmt, + state_income_tax_amt: df_w2.StateIncomeTaxAmt, + local_wages_and_tips_amt: df_w2.LocalWagesAndTipsAmt, + local_income_tax_amt: df_w2.LocalIncomeTaxAmt, + locality_nm: df_w2.LocalityNm + ) + end end end).compact end diff --git a/app/models/state_file_w2.rb b/app/models/state_file_w2.rb index 617f4360bf..e9384bf6c3 100644 --- a/app/models/state_file_w2.rb +++ b/app/models/state_file_w2.rb @@ -39,13 +39,13 @@ class StateFileW2 < ApplicationRecord validates :w2_index, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :employer_state_id_num, format: { with: /\A(\d{0,17})\z/, message: ->(_object, _data) { I18n.t('state_file.questions.w2.edit.employer_state_id_error') } } - validates_numericality_of :state_wages_amt, only_integer: true, message: :whole_number, if: -> { state_wages_amt.present? } + validates_numericality_of :state_wages_amt, only_integer: true, message: :round_to_whole_number, if: -> { state_wages_amt.present? } validates :state_wages_amt, numericality: { greater_than_or_equal_to: 0 }, if: -> { state_wages_amt.present? } - validates_numericality_of :state_income_tax_amt, only_integer: true, message: :whole_number, if: -> { state_income_tax_amt.present? } + validates_numericality_of :state_income_tax_amt, only_integer: true, message: :round_to_whole_number, if: -> { state_income_tax_amt.present? } validates :state_income_tax_amt, numericality: { greater_than_or_equal_to: 0 }, if: -> { state_income_tax_amt.present? } - validates_numericality_of :local_wages_and_tips_amt, only_integer: true, message: :whole_number, if: -> { local_wages_and_tips_amt.present? } + validates_numericality_of :local_wages_and_tips_amt, only_integer: true, message: :round_to_whole_number, if: -> { local_wages_and_tips_amt.present? } validates :local_wages_and_tips_amt, numericality: { greater_than_or_equal_to: 0 }, if: -> { local_wages_and_tips_amt.present? } - validates_numericality_of :local_income_tax_amt, only_integer: true, message: :whole_number, if: -> { local_income_tax_amt.present? } + validates_numericality_of :local_income_tax_amt, only_integer: true, message: :round_to_whole_number, if: -> { local_income_tax_amt.present? } validates :local_income_tax_amt, numericality: { greater_than_or_equal_to: 0 }, if: -> { local_income_tax_amt.present? } validates :locality_nm, presence: { message: ->(_object, _data) { I18n.t('state_file.questions.w2.edit.locality_nm_missing_error') } }, if: -> { local_wages_and_tips_amt.present? && local_wages_and_tips_amt.positive? } validates :employer_state_id_num, presence: true, if: -> { state_wages_amt.present? && state_wages_amt.positive? } diff --git a/config/locales/en.yml b/config/locales/en.yml index b4eaa76504..02f8587ac9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -224,6 +224,7 @@ en: blank: Can't be blank. invalid: is invalid only_letters: Only letters are accepted + round_to_whole_number: round to the nearest whole number whole_number: must be a whole number forms: errors: @@ -2703,7 +2704,7 @@ en: box19_html: "Box 19: Local income tax" box20_locality_name: "Box 20: Locality name" employer_state_id_error: EIN must be a number. Do not include a dash. - instructions_1: We need a little more information about the state and local tax information on your W-2 from %{employer_name} for %{total_wages_amt}. + instructions_1: Please review the information below and make sure it matches the state and local taxes on your W-2 from %{employer_name} for %{total_wages_amt}. instructions_2: If any of these boxes are blank on your W-2, leave them blank here. local_income_tax_amt_error: Cannot be greater than local wages and tips. local_wages_and_tips_amt_error: Please enter local wages and tips @@ -2713,7 +2714,7 @@ en: w2s_error: Update W-2s before continuing. wages_amt_error: Total income tax cannot be greater than $%{wages_amount} index: - title: Please provide state tax information for the following W-2s. Do not worry if you have multiple W-2s and not all of them are listed below. + title: We noticed some discrepancies with the W-2 information you entered on the Direct File tool for the following W-2s. Do not worry if you have multiple W-2s and not all of them are listed below. updated: You've updated this W-2 w2_for: 'W-2 for: %{employer_name}' wages_amount: 'Total wages amount: $%{wages_amount}' diff --git a/config/locales/es.yml b/config/locales/es.yml index 06caef386e..9746d5418d 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -225,6 +225,7 @@ es: blank: No puede estar en blanco. invalid: es invalido only_letters: Sólo se aceptan cartas + round_to_whole_number: redondea al número entero más próximo whole_number: Debe ser un número entero forms: errors: @@ -2685,7 +2686,7 @@ es: box19_html: "Box 19: Impuesto sobre la renta local" box20_locality_name: "Box 20: Nombre de la localidad" employer_state_id_error: El EIN debe ser un número. No incluya un guión. - instructions_1: Necesitamos un poco más de información sobre la información de impuestos estatales y locales en tu W-2 de %{employer_name}por %{total_wages_amt}. + instructions_1: Revise la información a continuación y asegúrese de que coincida con los impuestos estatales y locales en su W-2 de %{employer_name} a %{total_wages_amt}. instructions_2: Si alguna de estas casillas está en blanco en tu W-2, déjalas en blanco aquí. local_income_tax_amt_error: No puede ser mayor que los salarios y propinas locales. local_wages_and_tips_amt_error: Ingresa los salarios y propinas locales @@ -2695,7 +2696,7 @@ es: w2s_error: Actualiza los formularios W-2 antes de continuar. wages_amt_error: El impuesto sobre la renta total no puede ser mas de $%{wages_amount} index: - title: Proporciona la información de impuestos estatales para los siguientes W-2. No te preocupes si tienes varios W-2 y no todos están listados abajo. + title: Notamos algunas discrepancias con la información del W-2 que ingresó en la herramienta Direct File para los siguientes W-2. No se preocupe si tiene varios W-2 y no todos se enumeran a continuación. updated: Has actualizado este W-2 w2_for: 'W-2 para: %{employer_name}' wages_amount: 'Cantidad total de salarios: $%{wages_amount}' From b874ebabf1976bb394bb6fa36d19df5e7bbc0da4 Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:18:16 -0300 Subject: [PATCH 03/20] Re-enable before_action for re_optin (#4464) * Re-enable before_action for re_optin Co-authored-by: Ricardo Kreyhsig * Revert the revert Co-authored-by: Ricardo Kreyhsig --- app/controllers/mailgun_webhooks_controller.rb | 3 +-- spec/controllers/mailgun_webhooks_controller_spec.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/controllers/mailgun_webhooks_controller.rb b/app/controllers/mailgun_webhooks_controller.rb index 6f43e00479..408b56a15a 100644 --- a/app/controllers/mailgun_webhooks_controller.rb +++ b/app/controllers/mailgun_webhooks_controller.rb @@ -1,8 +1,7 @@ class MailgunWebhooksController < ActionController::Base skip_before_action :verify_authenticity_token before_action :authenticate_mailgun_request - # This work is ongoing for https://www.pivotaltracker.com/story/show/187187633 - # before_action :re_optin_when_client_replies, only: :create_incoming_email + before_action :re_optin_when_client_replies, only: :create_incoming_email REGEX_FROM_ENVELOPE = /.*\<(?
(.*))>/.freeze diff --git a/spec/controllers/mailgun_webhooks_controller_spec.rb b/spec/controllers/mailgun_webhooks_controller_spec.rb index de37ad176b..fb6125c884 100644 --- a/spec/controllers/mailgun_webhooks_controller_spec.rb +++ b/spec/controllers/mailgun_webhooks_controller_spec.rb @@ -107,13 +107,20 @@ let!(:state_intake) { create :state_file_az_intake, email_address: email, unsubscribed_from_email: true } let!(:another_intake) { create :state_file_az_intake, email_address: 'another-email@test.gov', unsubscribed_from_email: true } - xit 'reopt-in statefile-intake based on email' do + it 'reopt-in statefile-intake based on email' do params['sender'] = email post :create_incoming_email, params: params state_intake.reload expect(state_intake.unsubscribed_from_email).to be_falsey end + it 'does NOT reopt-in statefile-intake based on a slighlty different email version' do + params['sender'] = 'email+01@test.com' + post :create_incoming_email, params: params + state_intake.reload + expect(state_intake.unsubscribed_from_email).to be_truthy + end + it 'does NOT reopt-in for state-file intakes without an associated incoming email' do params['sender'] = email post :create_incoming_email, params: params From 94b330304e86d600ffa082b1578c8e5478abc275 Mon Sep 17 00:00:00 2001 From: em barnard-shao <49880002+embarnard@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:27:34 -0400 Subject: [PATCH 04/20] Refine AZ excise credit #186927846 (#4418) Co-authored-by: Jenny Heath --- app/controllers/flows_controller.rb | 2 +- .../questions/az_excise_credit_controller.rb | 11 +- app/forms/state_file/az_excise_credit_form.rb | 18 +- app/lib/efile/az/az140.rb | 22 +- app/models/state_file_az_intake.rb | 43 ++- .../questions/az_excise_credit/edit.html.erb | 27 +- .../questions/az_review/edit.html.erb | 2 +- config/locales/en.yml | 9 +- config/locales/es.yml | 10 +- ...cerated_columns_to_state_file_az_intake.rb | 7 + .../az_excise_credit_controller_spec.rb | 42 +++ spec/factories/state_file_az_intakes.rb | 4 +- .../state_file/az_excise_credit_form_spec.rb | 144 ++++++++ spec/lib/efile/az/az140_spec.rb | 332 +++++++----------- spec/models/state_file_az_intake_spec.rb | 109 +++++- 15 files changed, 521 insertions(+), 261 deletions(-) create mode 100644 db/migrate/20240321215820_add_incarcerated_columns_to_state_file_az_intake.rb create mode 100644 spec/forms/state_file/az_excise_credit_form_spec.rb diff --git a/app/controllers/flows_controller.rb b/app/controllers/flows_controller.rb index cd00ec9ee1..40c1d4e3ca 100644 --- a/app/controllers/flows_controller.rb +++ b/app/controllers/flows_controller.rb @@ -654,9 +654,9 @@ def self.az_attributes(first_name: 'Testuser', last_name: 'Testuser') prior_last_names: "Jordan, Pippen, Rodman", tribal_member: "yes", tribal_wages: 100, - was_incarcerated: "no", primary_was_incarcerated: "no", spouse_was_incarcerated: "no", + was_incarcerated: "no", # TODO: remove when column is ignored household_excise_credit_claimed: "no", ssn_no_employment: "no", message_tracker: {}, diff --git a/app/controllers/state_file/questions/az_excise_credit_controller.rb b/app/controllers/state_file/questions/az_excise_credit_controller.rb index 12e4998401..f0153fff31 100644 --- a/app/controllers/state_file/questions/az_excise_credit_controller.rb +++ b/app/controllers/state_file/questions/az_excise_credit_controller.rb @@ -4,7 +4,16 @@ class AzExciseCreditController < AuthenticatedQuestionsController include ReturnToReviewConcern def self.show?(intake) - intake.ask_whether_incarcerated? + !intake.disqualified_from_excise_credit_df? + end + + def update + if params["state_file_az_excise_credit_form"]["was_incarcerated"].present? + flash[:notice] = I18n.t("state_file.questions.az_excise_credit.update.page_changed_notice") + render :edit + else + super + end end end end diff --git a/app/forms/state_file/az_excise_credit_form.rb b/app/forms/state_file/az_excise_credit_form.rb index db09d93440..1d50e1eeb6 100644 --- a/app/forms/state_file/az_excise_credit_form.rb +++ b/app/forms/state_file/az_excise_credit_form.rb @@ -1,13 +1,25 @@ module StateFile class AzExciseCreditForm < QuestionsForm - set_attributes_for :intake, :was_incarcerated, :ssn_no_employment, :household_excise_credit_claimed + set_attributes_for :intake, + :primary_was_incarcerated, + :spouse_was_incarcerated, + :ssn_no_employment, + :household_excise_credit_claimed, + :household_excise_credit_claimed_amt - validates :was_incarcerated, inclusion: { in: %w[yes no], message: :blank } + validates :primary_was_incarcerated, inclusion: { in: %w[yes no], message: :blank } + validates :spouse_was_incarcerated, inclusion: { in: %w[yes no], message: :blank }, if: -> { intake.filing_status_mfj? } validates :ssn_no_employment, inclusion: { in: %w[yes no], message: :blank } validates :household_excise_credit_claimed, inclusion: { in: %w[yes no], message: :blank } + validates_presence_of :household_excise_credit_claimed_amt, if: -> { household_excise_credit_claimed == "yes" } + validates :household_excise_credit_claimed_amt, numericality: { only_integer: true, greater_than: 0, message: ->(_object, _data) { I18n.t('validators.must_enter_amount') } }, allow_blank: true def save - @intake.update(attributes_for(:intake)) + attributes = attributes_for(:intake) + if household_excise_credit_claimed == "no" + attributes = attributes.merge(household_excise_credit_claimed_amt: nil) + end + @intake.update(attributes) end end end \ No newline at end of file diff --git a/app/lib/efile/az/az140.rb b/app/lib/efile/az/az140.rb index e182878fa8..1948029e49 100644 --- a/app/lib/efile/az/az140.rb +++ b/app/lib/efile/az/az140.rb @@ -234,19 +234,19 @@ def calculate_line_53 def calculate_line_56 - if !@direct_file_data.claimed_as_dependent? && @intake.qualified_for_excise_credit? && @intake.ask_whether_incarcerated? - # todo question: if they are filing with us does that automatically mean no AZ-140PTC? - if filing_status_mfj? || filing_status_hoh? - return 0 unless line_or_zero(:AZ140_LINE_12) <= 25000 - elsif filing_status_single? || filing_status_mfs? - return 0 unless line_or_zero(:AZ140_LINE_12) <= 12500 - end - wrksht_line_2 = filing_status_mfj? ? 2 : 1 + if @intake.disqualified_from_excise_credit_df? || @intake.disqualified_from_excise_credit_fyst? + 0 + else + # TODO question: if they are filing with us does that automatically mean no AZ-140PTC? + number_of_filers = filing_status_mfj? ? 2 : 1 + wrksht_line_2 = number_of_filers - @intake.incarcerated_filer_count wrksht_line_4 = (@dependent_count + wrksht_line_2) * 25 - return [wrksht_line_4, 100].min - end - 0 + max_credit = 100 + max_credit -= @intake.household_excise_credit_claimed_amt if @intake.household_excise_credit_claimed_yes? && @intake.household_excise_credit_claimed_amt.is_a?(Integer) + + [wrksht_line_4, max_credit].min + end end def calculate_line_59 diff --git a/app/models/state_file_az_intake.rb b/app/models/state_file_az_intake.rb index d9938960a5..15c71c42f4 100644 --- a/app/models/state_file_az_intake.rb +++ b/app/models/state_file_az_intake.rb @@ -47,7 +47,7 @@ # primary_last_name :string # primary_middle_initial :string # primary_suffix :string -# primary_was_incarcerated :integer default(0), not null +# primary_was_incarcerated :integer default("unfilled"), not null # prior_last_names :string # raw_direct_file_data :text # referrer :string @@ -61,7 +61,7 @@ # spouse_last_name :string # spouse_middle_initial :string # spouse_suffix :string -# spouse_was_incarcerated :integer default(0), not null +# spouse_was_incarcerated :integer default("unfilled"), not null # ssn_no_employment :integer default("unfilled"), not null # tribal_member :integer default("unfilled"), not null # tribal_wages :integer @@ -93,6 +93,8 @@ class StateFileAzIntake < StateFileBaseIntake enum has_prior_last_names: { unfilled: 0, yes: 1, no: 2 }, _prefix: :has_prior_last_names enum was_incarcerated: { unfilled: 0, yes: 1, no: 2 }, _prefix: :was_incarcerated + enum primary_was_incarcerated: { unfilled: 0, yes: 1, no: 2 }, _prefix: :primary_was_incarcerated + enum spouse_was_incarcerated: { unfilled: 0, yes: 1, no: 2 }, _prefix: :spouse_was_incarcerated enum ssn_no_employment: { unfilled: 0, yes: 1, no: 2 }, _prefix: :ssn_no_employment enum household_excise_credit_claimed: { unfilled: 0, yes: 1, no: 2 }, _prefix: :household_excise_credit_claimed enum tribal_member: { unfilled: 0, yes: 1, no: 2 }, _prefix: :tribal_member @@ -164,14 +166,39 @@ def disqualifying_eligibility_rules } end - def ask_whether_incarcerated? - has_valid_ssn = primary.ssn.present? && !primary.has_itin? - has_valid_agi = direct_file_data.fed_agi <= (filing_status_mfj? || filing_status_hoh? ? 25_000 : 12_500) - has_valid_ssn && has_valid_agi + def disqualified_from_excise_credit_df? + agi_limit = if filing_status_mfj? || filing_status_hoh? + 25000 + elsif filing_status_single? || filing_status_mfs? + 12500 + end + agi_over_limit = direct_file_data.fed_agi > agi_limit + lacks_valid_ssn = primary.ssn.blank? || primary.has_itin? + + agi_over_limit || lacks_valid_ssn + end + + def incarcerated_filer_count + count = 0 + if use_old_incarcerated_column? + count += 2 if was_incarcerated_yes? + else + count += 1 if primary_was_incarcerated_yes? + count += 1 if spouse_was_incarcerated_yes? + end + + count + end + + def use_old_incarcerated_column? + # TODO: remove once column ignored + !was_incarcerated_unfilled? && primary_was_incarcerated_unfilled? end - def qualified_for_excise_credit? - was_incarcerated_no? && ssn_no_employment_no? && household_excise_credit_claimed_no? + def disqualified_from_excise_credit_fyst? + all_filers_incarcerated = was_incarcerated_yes? || (primary_was_incarcerated_yes? && spouse_was_incarcerated_yes?) + whole_credit_already_claimed = use_old_incarcerated_column? && household_excise_credit_claimed_yes? + all_filers_incarcerated || whole_credit_already_claimed || ssn_no_employment_yes? || direct_file_data.claimed_as_dependent? end def filing_status diff --git a/app/views/state_file/questions/az_excise_credit/edit.html.erb b/app/views/state_file/questions/az_excise_credit/edit.html.erb index 6894791d00..e5656f6ecc 100644 --- a/app/views/state_file/questions/az_excise_credit/edit.html.erb +++ b/app/views/state_file/questions/az_excise_credit/edit.html.erb @@ -6,12 +6,21 @@ <%= form_with model: @form, url: { action: :update }, local: true, method: "put", builder: VitaMinFormBuilder do |f| %>
- <%= f.cfa_radio_set(:was_incarcerated, label_text: t(".was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year, count: current_intake.filer_count), collection: [ + <%= f.cfa_radio_set(:primary_was_incarcerated, label_text: t(".primary_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year), collection: [ { value: "yes", label: t("general.affirmative") }, { value: "no", label: t("general.negative") }, ]) %>
+ <% if current_intake.filing_status_mfj? %> +
+ <%= f.cfa_radio_set(:spouse_was_incarcerated, label_text: t(".spouse_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year), collection: [ + { value: "yes", label: t("general.affirmative") }, + { value: "no", label: t("general.negative") }, + ]) %> +
+ <% end %> +
<%= f.cfa_radio_set(:ssn_no_employment, label_text: t(".ssn_no_employment_html.label"), collection: [ { value: "yes", label: t("general.affirmative") }, @@ -20,10 +29,18 @@
- <%= f.cfa_radio_set(:household_excise_credit_claimed, label_text: t(".household_excise_credit_html"), collection: [ - { value: "yes", label: t("general.affirmative") }, - { value: "no", label: t("general.negative") }, - ]) %> +
+
+ <%= f.cfa_radio_set(:household_excise_credit_claimed, label_text: t(".household_excise_credit_html"), collection: [ + { value: "yes", label: t("general.affirmative"), input_html: { "data-follow-up": "#credit-amt" } }, + { value: "no", label: t("general.negative") }, + ]) %> +
+ + +
<% if params[:return_to_review].present? %> diff --git a/app/views/state_file/questions/az_review/edit.html.erb b/app/views/state_file/questions/az_review/edit.html.erb index 8666346081..608056e0cc 100644 --- a/app/views/state_file/questions/az_review/edit.html.erb +++ b/app/views/state_file/questions/az_review/edit.html.erb @@ -52,7 +52,7 @@ - <% if current_intake.ask_whether_incarcerated? %> + <% unless current_intake.disqualified_from_excise_credit_df? %>

<%=t(".was_incarcerated") %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index 02f8587ac9..45ccd9787e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2085,13 +2085,15 @@ en: other: Did you or your spouse make charitable contributions (cash and/or non-cash) to a qualifying organization in %{tax_year}? az_excise_credit: edit: + household_excise_credit_claimed_amt: How much, in total, did all other members of your household claim in Increased Excise Tax Credit? (You can look this up by looking at Line 56 of their Arizona return.) household_excise_credit_html: Did anyone in your household file a tax return separately from you and claim the Arizona Increased Excise Tax Credit?

(Your household is everyone that you lived with, even if they aren’t listed on your return.) + primary_was_incarcerated: Were you incarcerated in a county, state, or federal prison for at least 60 days in %{tax_year}? + spouse_was_incarcerated: Was your spouse incarcerated in a county, state, or federal prison for at least 60 days in %{tax_year}? ssn_no_employment_html: label: Do you have 'not valid for employment' written on your Social Security card? (This is rare.) title: You’re almost done filing! We have a few questions to see if you’re eligible for the Increased Excise Tax Credit. - was_incarcerated: - one: Were you incarcerated in a county, state, or federal prison for at least 60 days in %{tax_year}? - other: Were you or your spouse incarcerated in a county, state, or federal prison for at least 60 days in %{tax_year}? + update: + page_changed_notice: Sorry, this page has changed! Please answer the questions again. az_prior_last_names: edit: prior_last_names_label: @@ -2856,6 +2858,7 @@ en: itin: Please enter a valid individual taxpayer identification number. legal_name: Please enter a name that only contains letters, apostrophes, hyphens, periods, and accents. must_be_associated_with_tax_return: "%{document_type} must be associated with a tax year." + must_enter_amount: If someone claimed this credit you must enter a whole number amount. must_not_be_associated_with_tax_return: "%{document_type} cannot be associated with a tax year." pdf_file_corrupted: File is corrupt. Please generate a new PDF and try uploading again. pdf_file_type: "%{document_type} must be a PDF file" diff --git a/config/locales/es.yml b/config/locales/es.yml index 9746d5418d..67fe7adc36 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2045,14 +2045,15 @@ es: other: "¿Tú o tu cónyuge hicieron contribuciones caritativas (en efectivo y/o en especie) a una organización calificada en %{tax_year}?" az_excise_credit: edit: + household_excise_credit_claimed_amt: "¿Cuánto, en total, reclamaron todos los demás miembros de su hogar en Crédito por Impuesto Especial Incrementado? (Puede consultar esto en la línea 56 de su declaración de impuestos de Arizona)." household_excise_credit_html: "¿Alguien en tu hogar presentó una declaración de impuestos por separado de la tuya y reclamó el Crédito por Aumento de Impuestos Especiales de Arizona?

(Tu hogar incluye a todas las personas con las que viviste, incluso si no están incluidas en tu declaración.)" + primary_was_incarcerated: "¿Estuviste encarcelado en una cárcel del condado, estatal o federal durante al menos 60 días en %{tax_year}?" + spouse_was_incarcerated: "¿Tu cónyuge fue encarcelado en una cárcel del condado, estatal o federal durante al menos 60 días en %{tax_year}?" ssn_no_employment_html: label: "¿Tienes la frase 'not valid for employment' escrita en tu tarjeta de Seguro Social? (Esto es poco común.)" title: "¡Ya casi has terminado de presentar tu declaración! Tenemos algunas preguntas para ver si eres elegible para el Crédito por Aumento de Impuestos Especiales." - was_incarcerated: - many: "¿Tú o tu cónyuge estuvieron encarcelados en una cárcel del condado, estatal o federal durante al menos 60 días en %{tax_year}?" - one: "¿Estuviste encarcelado en una cárcel del condado, estatal o federal durante al menos 60 días en %{tax_year}?" - other: "¿Tú o tu cónyuge estuvieron encarcelados en una cárcel del condado, estatal o federal durante al menos 60 días en %{tax_year}?" + update: + page_changed_notice: "¡Lo sentimos, esta página ha cambiado! Por favor responda las preguntas otra vez." az_prior_last_names: edit: prior_last_names_label: @@ -2838,6 +2839,7 @@ es: itin: Por favor ingrese un Número de Identificación Personal del Contribuyente válido. legal_name: Ingrese un nombre que solo contenga cartas, apóstrofos, guiones, puntos y acentos. must_be_associated_with_tax_return: "%{document_type} debe estar asociado con un año fiscal" + must_enter_amount: Si alguien reclamó este crédito, debe ingresar el monto en un número entero. must_not_be_associated_with_tax_return: "%{document_type} no puede asociarse a un año fiscal en particular. " pdf_file_corrupted: Archivo está dañado. Genera un nuevo PDF e intenta subirlo de nuevo. pdf_file_type: "%{document_type} debe ser un archivo PDF" diff --git a/db/migrate/20240321215820_add_incarcerated_columns_to_state_file_az_intake.rb b/db/migrate/20240321215820_add_incarcerated_columns_to_state_file_az_intake.rb new file mode 100644 index 0000000000..6c7ce318e1 --- /dev/null +++ b/db/migrate/20240321215820_add_incarcerated_columns_to_state_file_az_intake.rb @@ -0,0 +1,7 @@ +class AddIncarceratedColumnsToStateFileAzIntake < ActiveRecord::Migration[7.1] + def change + add_column :state_file_az_intakes, :primary_was_incarcerated, :integer, default: 0, null: false + add_column :state_file_az_intakes, :spouse_was_incarcerated, :integer, default: 0, null: false + add_column :state_file_az_intakes, :household_excise_credit_claimed_amt, :integer + end +end diff --git a/spec/controllers/state_file/questions/az_excise_credit_controller_spec.rb b/spec/controllers/state_file/questions/az_excise_credit_controller_spec.rb index 8262e144f0..26eb5372a6 100644 --- a/spec/controllers/state_file/questions/az_excise_credit_controller_spec.rb +++ b/spec/controllers/state_file/questions/az_excise_credit_controller_spec.rb @@ -129,6 +129,33 @@ end end + describe "#edit" do + render_views + + context "single filer" do + it "shows fields for primary filer only" do + get :edit, params: { us_state: "az" } + + expect(response.body).to have_text I18n.t("state_file.questions.az_excise_credit.edit.primary_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year) + expect(response.body).not_to have_text I18n.t("state_file.questions.az_excise_credit.edit.spouse_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year) + end + end + + context "mfj filers" do + let(:intake) { create(:state_file_az_intake, filing_status: :married_filing_jointly) } + before do + sign_in intake + end + + it "shows fields for primary and spouse" do + get :edit, params: { us_state: "az" } + + expect(response.body).to have_text I18n.t("state_file.questions.az_excise_credit.edit.primary_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year) + expect(response.body).to have_text I18n.t("state_file.questions.az_excise_credit.edit.spouse_was_incarcerated", tax_year: MultiTenantService.statefile.current_tax_year) + end + end + end + describe "#update" do # use the return_to_review_concern shared example if the page # should skip to the review page when the return_to_review param is present @@ -136,6 +163,19 @@ it_behaves_like :return_to_review_concern do let(:form_params) do { + us_state: "az", + state_file_az_excise_credit_form: { + primary_was_incarcerated: "yes", + ssn_no_employment: "yes", + household_excise_credit_claimed: "no", + } + } + end + end + + context "handling people who had the old page open when we deployed" do + it "rerenders the page when the params are the old ones" do + post :update, params: { us_state: "az", state_file_az_excise_credit_form: { was_incarcerated: "yes", @@ -143,6 +183,8 @@ household_excise_credit_claimed: "yes", } } + + expect(response).to render_template :edit end end end diff --git a/spec/factories/state_file_az_intakes.rb b/spec/factories/state_file_az_intakes.rb index f886db6cbf..8ca5b6513a 100644 --- a/spec/factories/state_file_az_intakes.rb +++ b/spec/factories/state_file_az_intakes.rb @@ -47,7 +47,7 @@ # primary_last_name :string # primary_middle_initial :string # primary_suffix :string -# primary_was_incarcerated :integer default(0), not null +# primary_was_incarcerated :integer default("unfilled"), not null # prior_last_names :string # raw_direct_file_data :text # referrer :string @@ -61,7 +61,7 @@ # spouse_last_name :string # spouse_middle_initial :string # spouse_suffix :string -# spouse_was_incarcerated :integer default(0), not null +# spouse_was_incarcerated :integer default("unfilled"), not null # ssn_no_employment :integer default("unfilled"), not null # tribal_member :integer default("unfilled"), not null # tribal_wages :integer diff --git a/spec/forms/state_file/az_excise_credit_form_spec.rb b/spec/forms/state_file/az_excise_credit_form_spec.rb new file mode 100644 index 0000000000..5bc54a871e --- /dev/null +++ b/spec/forms/state_file/az_excise_credit_form_spec.rb @@ -0,0 +1,144 @@ +require "rails_helper" + +RSpec.describe StateFile::AzExciseCreditForm do + let!(:intake) { create :state_file_az_intake } + + describe "#valid?" do + context "incarceration field(s)" do + let(:single_filer_params) { + { + primary_was_incarcerated: "yes", + ssn_no_employment: "yes", + household_excise_credit_claimed: "no" + } + } + let(:mfj_params) { + { + primary_was_incarcerated: "no", + spouse_was_incarcerated: "yes", + ssn_no_employment: "yes", + household_excise_credit_claimed: "no" + } + } + + context "single filer" do + it "only needs field for primary" do + form = described_class.new(intake, { + primary_was_incarcerated: nil, + ssn_no_employment: "yes", + household_excise_credit_claimed: "no" + }) + expect(form).not_to be_valid + expect(form.errors).to include :primary_was_incarcerated + + form = described_class.new(intake, single_filer_params) + expect(form).to be_valid + expect(form.errors).to be_empty + end + end + + context "mfj filers" do + before do + intake.direct_file_data.filing_status = 2 + end + + it "does not accept fields for only primary" do + form = described_class.new(intake, single_filer_params) + expect(form).not_to be_valid + expect(form.errors).to include :spouse_was_incarcerated + end + + it "accepts fields for primary and spouse" do + form = described_class.new(intake, mfj_params) + expect(form).to be_valid + expect(form.errors).to be_empty + end + end + end + + context "excise credit claimed" do + it "requires credit amount when credit claimed" do + invalid_params = { + primary_was_incarcerated: "no", + ssn_no_employment: "yes", + household_excise_credit_claimed: "yes" + } + form = described_class.new(intake, invalid_params) + expect(form).not_to be_valid + expect(form.errors).to include :household_excise_credit_claimed_amt + + valid_params = { + primary_was_incarcerated: "no", + ssn_no_employment: "yes", + household_excise_credit_claimed: "yes", + household_excise_credit_claimed_amt: 1000 + } + form = described_class.new(intake, valid_params) + expect(form).to be_valid + expect(form.errors).to be_empty + end + + it "requires credit amount to be a positive integer" do + invalid_params_zero = { + primary_was_incarcerated: "no", + ssn_no_employment: "yes", + household_excise_credit_claimed: "yes", + household_excise_credit_claimed_amt: 0 + } + form = described_class.new(intake, invalid_params_zero) + expect(form).not_to be_valid + expect(form.errors).to include :household_excise_credit_claimed_amt + + invalid_params_float = { + primary_was_incarcerated: "no", + ssn_no_employment: "yes", + household_excise_credit_claimed: "yes", + household_excise_credit_claimed_amt: 500.23 + } + form = described_class.new(intake, invalid_params_float) + expect(form).not_to be_valid + expect(form.errors).to include :household_excise_credit_claimed_amt + end + end + end + + describe "#save" do + context "when params are valid" do + it "saves values" do + form = described_class.new(intake, { + primary_was_incarcerated: "yes", + ssn_no_employment: "yes", + household_excise_credit_claimed: "yes", + household_excise_credit_claimed_amt: 1000 + }) + expect(form).to be_valid + form.save + + intake.reload + expect(intake.primary_was_incarcerated_yes?).to eq true + expect(intake.ssn_no_employment_yes?).to eq true + expect(intake.household_excise_credit_claimed_yes?).to eq true + expect(intake.household_excise_credit_claimed_amt).to eq 1000 + end + + it "zeroes out credit amount if credit claimed = no (does not save the amount param if claimed = no)" do + intake.update(household_excise_credit_claimed_amt: 2000) + + form = described_class.new(intake, { + primary_was_incarcerated: "yes", + ssn_no_employment: "yes", + household_excise_credit_claimed: "no", + household_excise_credit_claimed_amt: 1000 + }) + expect(form).to be_valid + form.save + + intake.reload + expect(intake.primary_was_incarcerated_yes?).to eq true + expect(intake.ssn_no_employment_yes?).to eq true + expect(intake.household_excise_credit_claimed_no?).to eq true + expect(intake.household_excise_credit_claimed_amt).to be_nil + end + end + end +end \ No newline at end of file diff --git a/spec/lib/efile/az/az140_spec.rb b/spec/lib/efile/az/az140_spec.rb index 80b31d5d2e..77298ac059 100644 --- a/spec/lib/efile/az/az140_spec.rb +++ b/spec/lib/efile/az/az140_spec.rb @@ -1,8 +1,7 @@ require 'rails_helper' describe Efile::Az::Az140 do - let(:dependents) { [create(:state_file_dependent, dob: 7.years.ago)] } - let(:intake) { create(:state_file_az_intake, eligibility_lived_in_state: 1, dependents: dependents) } + let(:intake) { create(:state_file_az_intake, eligibility_lived_in_state: 1) } let(:instance) do described_class.new( year: MultiTenantService.statefile.current_tax_year, @@ -10,232 +9,153 @@ ) end - describe 'Line 56 Increased Excise Tax Credit' do - context 'when the client does not have a valid SSN because it is not present' do - before do - intake.direct_file_data.primary_ssn = nil - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - end + describe "Line 56: Increased Excise Tax Credit" do + before do + allow(intake).to receive(:disqualified_from_excise_credit_fyst?).and_return false + end - it 'sets the amount to 0 because the client does not qualify' do + context "when the client is disqualified because of the answers they gave during intake" do + it "sets the amount to 0" do + allow(intake).to receive(:disqualified_from_excise_credit_fyst?).and_return true instance.calculate expect(instance.lines[:AZ140_LINE_56].value).to eq(0) end end - end - context 'when the client does not have a valid SSN' do - before do - intake.direct_file_data.primary_ssn = '999999999' # invalid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + context "when the client is disqualified for having too much income" do + context "fed agi above 12,501" do + before do + intake.direct_file_data.fed_agi = 12_501 + end - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end - - context 'when the client does have a valid SSN that starts with 9' do - before do - intake.direct_file_data.primary_ssn = '999669999' # invalid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end - - it 'sets the credit to the correct amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(50) - end - end - - context 'when the client has been claimed as a dependent' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.direct_file_data.primary_claim_as_dependent = 'X' - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end - - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end - - context 'when the client was incarcerated for more than 60 days' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 1 # yes - end + it "when single sets the amount to 0" do + intake.direct_file_data.filing_status = 1 # single + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + end - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end - - context 'when the client has too much income and is filing single' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_501 # disqualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end - - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end - - context 'when the client has too much income and is filing mfs' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 3 # mfs - intake.direct_file_data.fed_agi = 12_501 # disqualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end - - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end - - context 'when the client has too much income and is filing mfj' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 2 # mfj - intake.direct_file_data.fed_agi = 25_001 # disqualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + it "when mfs sets the amount to 0" do + intake.direct_file_data.filing_status = 3 # mfs + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + end + end - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + context "fed agi above 25,001" do + before do + intake.direct_file_data.fed_agi = 25_001 + end + + it "when mfj sets the amount to 0" do + intake.direct_file_data.filing_status = 2 # mfj + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + end + + it "when hoh sets the amount to 0" do + intake.direct_file_data.filing_status = 4 # hoh + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + end + end end - end - context 'when the client has too much income and is filing hoh' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 4 # hoh - intake.direct_file_data.fed_agi = 25_001 # disqualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + context "single filer with one dependent" do + it "sets the credit to the correct amount" do + intake.dependents.create(dob: 7.years.ago) + intake.direct_file_data.filing_status = 1 # single + intake.direct_file_data.fed_agi = 12_500 # qualifying agi - it 'sets the amount to 0 because the client does not qualify' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + end end - end - context 'when the client qualifies for the credit and is filing single' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + context "mfs filer with one dependent" do + it "sets the credit to the correct amount" do + intake.dependents.create(dob: 7.years.ago) + intake.direct_file_data.filing_status = 3 # mfs + intake.direct_file_data.fed_agi = 12_500 # qualifying agi - it 'sets the credit to the correct amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + end end - end - context 'when the client qualifies for the credit and is filing mfs' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 3 # mfs - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + context "mfj filer with one dependent" do + it "sets the credit to the correct amount" do + intake.dependents.create(dob: 7.years.ago) + intake.direct_file_data.filing_status = 2 # mfj + intake.direct_file_data.fed_agi = 25_000 # qualifying agi - it 'sets the credit to the correct amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(75) # (2 filers + 1 dependent) * 25 + end end - end - context 'when the client qualifies for the credit and is filing mfj' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.spouse_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 2 # mfj - intake.direct_file_data.fed_agi = 25_000 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no + context "hoh filer with one dependent" do + it "sets the credit to the correct amount" do + intake.dependents.create(dob: 7.years.ago) + intake.direct_file_data.filing_status = 4 # hoh + intake.direct_file_data.fed_agi = 25_000 # # qualifying agi + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + end end - it 'sets the credit to the correct amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(75) # (2 filers + 1 dependent) * 25 + context "when the client qualifies for the maximum credit" do + it "sets the credit to the maximum amount" do + intake.direct_file_data.filing_status = 1 # single + intake.direct_file_data.fed_agi = 12_500 # qualifying agi + intake.dependents.create(dob: 7.years.ago) + intake.dependents.create(dob: 5.years.ago) + intake.dependents.create(dob: 3.years.ago) + intake.dependents.create(dob: 1.years.ago) + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(100) # (1 filer + 4 dependents) * 25 = 125 but max is 100 + end end - end - context 'when the client qualifies for the credit and is filing hoh' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 4 # hoh - intake.direct_file_data.fed_agi = 25_000 # # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - end + context "mfj filer, one incarcerated, no dependents" do + it "calculates the credit without incarcerated filer" do + intake.direct_file_data.filing_status = 2 # mfj + intake.direct_file_data.fed_agi = 12_500 # qualifying agi + intake.update(primary_was_incarcerated: "no", spouse_was_incarcerated: "yes") + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(25) # (1 filer) * 25 = 25 + end - it 'sets the credit to the correct amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (1 filer + 1 dependent) * 25 + it "handles the old column for now" do + intake.direct_file_data.filing_status = 2 # mfj + intake.direct_file_data.fed_agi = 12_500 # qualifying agi + intake.update(was_incarcerated: "no") + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(50) # (2 filers) * 25 = 25 + end end - end - context 'when the client qualifies for the maximum credit' do - before do - intake.direct_file_data.primary_ssn = '555002222' # valid - intake.direct_file_data.filing_status = 1 # single - intake.direct_file_data.fed_agi = 12_500 # qualifying agi - intake.was_incarcerated = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - intake.ssn_no_employment = 2 # no - intake.household_excise_credit_claimed = 2 # no - intake.dependents.create(dob: 5.years.ago) - intake.dependents.create(dob: 3.years.ago) - intake.dependents.create(dob: 1.years.ago) + context "single filer with four dependents with some credit already claimed" do + it "adjusts the max credit" do + intake.direct_file_data.filing_status = 1 # single + intake.direct_file_data.fed_agi = 12_500 # qualifying agi + intake.dependents.create(dob: 7.years.ago) + intake.dependents.create(dob: 5.years.ago) + intake.dependents.create(dob: 3.years.ago) + intake.dependents.create(dob: 1.years.ago) + intake.update(household_excise_credit_claimed: "yes", household_excise_credit_claimed_amt: 40) + instance.calculate + expect(instance.lines[:AZ140_LINE_56].value).to eq(60) # (1 filer + 4 dependents) * 25 = 125 but max is 60 + end end - it 'sets the credit to the maximum amount' do - instance.calculate - expect(instance.lines[:AZ140_LINE_56].value).to eq(100) # (1 filer + 4 dependents) * 25 = 125 but max is 100 + # TODO: [JH] i don't....understand this test? was copied from commit a674f6f + context "filing status is qualifying widow" do + it "sets the family income tax credit and excise credit to 0" do + intake.direct_file_data.filing_status = 5 # qualifying_widow + instance.calculate + expect(instance.lines[:AZ140_LINE_50].value).to eq(0) + expect(instance.lines[:AZ140_LINE_56].value).to eq(0) + end end end @@ -329,18 +249,4 @@ expect(instance.lines[:AZ140_LINE_43S].value).to eq('Standard') end end - - # Family income tax credit and excise credit Lines 50, 56 - describe 'Family income tax credit and excise credit' do - let(:intake) { create(:state_file_az_johnny_intake) } - before do - intake.direct_file_data.filing_status = 5 # qualifying_widow - end - - it 'sets the standard deduction correctly for QSS' do - instance.calculate - expect(instance.lines[:AZ140_LINE_50].value).to eq(0) - expect(instance.lines[:AZ140_LINE_56].value).to eq(0) - end - end end \ No newline at end of file diff --git a/spec/models/state_file_az_intake_spec.rb b/spec/models/state_file_az_intake_spec.rb index f3b225f087..6ff4b1222f 100644 --- a/spec/models/state_file_az_intake_spec.rb +++ b/spec/models/state_file_az_intake_spec.rb @@ -47,7 +47,7 @@ # primary_last_name :string # primary_middle_initial :string # primary_suffix :string -# primary_was_incarcerated :integer default(0), not null +# primary_was_incarcerated :integer default("unfilled"), not null # prior_last_names :string # raw_direct_file_data :text # referrer :string @@ -61,7 +61,7 @@ # spouse_last_name :string # spouse_middle_initial :string # spouse_suffix :string -# spouse_was_incarcerated :integer default(0), not null +# spouse_was_incarcerated :integer default("unfilled"), not null # ssn_no_employment :integer default("unfilled"), not null # tribal_member :integer default("unfilled"), not null # tribal_wages :integer @@ -207,6 +207,63 @@ end end + describe "#disqualified_from_excise_credit_fyst?" do + let(:intake) { build(:state_file_az_intake, ssn_no_employment: "no") } + let(:fake_df_data) { instance_double(DirectFileData) } + before do + allow(intake).to receive(:direct_file_data).and_return fake_df_data + allow(fake_df_data).to receive(:claimed_as_dependent?).and_return false + end + + # TODO: remove when old column is ignored + context "has old column" do + it "returns false if not incarcerated" do + intake.update(was_incarcerated: "no") + expect(intake.disqualified_from_excise_credit_fyst?).to eq false + end + + it "returns true if incarcerated" do + intake.update(was_incarcerated: "yes") + expect(intake.disqualified_from_excise_credit_fyst?).to eq true + end + + it "returns true if old incarcerated column filled out and credit claimed is yes" do + intake.update(was_incarcerated: "no", household_excise_credit_claimed: "yes") + expect(intake.disqualified_from_excise_credit_fyst?).to eq true + end + end + + context "has new columns" do + before do + intake.update(primary_was_incarcerated: "no", spouse_was_incarcerated: "no", household_excise_credit_claimed: "yes", household_excise_credit_claimed_amt: 50) + end + + it "returns false if neither filer was incarcerated" do + expect(intake.disqualified_from_excise_credit_fyst?).to eq false + end + + it "returns false if only one filer incarcerated" do + intake.update(primary_was_incarcerated: "no", spouse_was_incarcerated: "yes") + expect(intake.disqualified_from_excise_credit_fyst?).to eq false + end + + it "returns true if both filers were incarcerated" do + intake.update(primary_was_incarcerated: "yes", spouse_was_incarcerated: "yes") + expect(intake.disqualified_from_excise_credit_fyst?).to eq true + end + + it "returns true if claimed as dependent" do + allow(fake_df_data).to receive(:claimed_as_dependent?).and_return true + expect(intake.disqualified_from_excise_credit_fyst?).to eq true + end + + it "returns true if ssn_no_employment is yes" do + intake.update(ssn_no_employment: "yes") + expect(intake.disqualified_from_excise_credit_fyst?).to eq true + end + end + end + describe 'federal_dependent_counts' do let(:intake) { create :state_file_az_intake } @@ -301,22 +358,56 @@ end - describe 'ask_whether_incarcerated' do + describe "disqualified_from_excise_credit_df?" do let(:intake) { create :state_file_az_intake } + before do + intake.direct_file_data.fed_agi = 10000 + intake.direct_file_data.primary_ssn = '123456789' + end - context "when should ask" do - it 'asks whether incarcerated' do + context "when fed agi is under limit for excise credit" do + it "they are not disqualified" do intake.direct_file_data.fed_agi = 10000 - expect(intake.ask_whether_incarcerated?).to eq true + expect(intake.disqualified_from_excise_credit_df?).to eq false end end - context "when should not ask" do - it 'does not ask whether incarcerated' do + context "when fed agi is over limit for excise credit" do + it "they are disqualified" do intake.direct_file_data.fed_agi = 20000 - expect(intake.ask_whether_incarcerated?).to eq false + expect(intake.disqualified_from_excise_credit_df?).to eq true + end + end + + context "when client does not have a valid SSN" do + it "they are disqualified" do + intake.direct_file_data.primary_ssn = '912555678' + expect(intake.disqualified_from_excise_credit_df?).to eq true end end + + context "when client has a valid SSN" do + it "they are not disqualified" do + intake.direct_file_data.primary_ssn = '123456789' + expect(intake.disqualified_from_excise_credit_df?).to eq false + end + end + end + + describe "incarcerated_filer_count" do + context "TEMPORARY, accepts old was_incarcerated_column" do + it "returns 2 when yes and 0 when no" do + expect(create(:state_file_az_intake, was_incarcerated: "yes").incarcerated_filer_count).to eq 2 + expect(create(:state_file_az_intake, was_incarcerated: "no").incarcerated_filer_count).to eq 0 + end + end + + it "returns the number of filers who were incarcerated" do + expect(create(:state_file_az_intake, primary_was_incarcerated: "no", spouse_was_incarcerated: "no").incarcerated_filer_count).to eq 0 + expect(create(:state_file_az_intake, primary_was_incarcerated: "yes", spouse_was_incarcerated: "no").incarcerated_filer_count).to eq 1 + expect(create(:state_file_az_intake, primary_was_incarcerated: "no", spouse_was_incarcerated: "yes").incarcerated_filer_count).to eq 1 + expect(create(:state_file_az_intake, primary_was_incarcerated: "yes", spouse_was_incarcerated: "yes").incarcerated_filer_count).to eq 2 + end end describe "invalid_df_w2?" do From 5aec9049338799185dd38c9db2715a6dea45e585 Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:40:39 -0300 Subject: [PATCH 05/20] Follow up to re-opting in opt-outs (#4471) --- app/controllers/mailgun_webhooks_controller.rb | 3 ++- spec/controllers/mailgun_webhooks_controller_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/mailgun_webhooks_controller.rb b/app/controllers/mailgun_webhooks_controller.rb index 408b56a15a..b5a9e46c37 100644 --- a/app/controllers/mailgun_webhooks_controller.rb +++ b/app/controllers/mailgun_webhooks_controller.rb @@ -1,7 +1,8 @@ class MailgunWebhooksController < ActionController::Base skip_before_action :verify_authenticity_token before_action :authenticate_mailgun_request - before_action :re_optin_when_client_replies, only: :create_incoming_email + # Disabling this until https://www.pivotaltracker.com/n/projects/2409240/stories/187407168 is completed + # before_action :re_optin_when_client_replies, only: :create_incoming_email REGEX_FROM_ENVELOPE = /.*\<(?
(.*))>/.freeze diff --git a/spec/controllers/mailgun_webhooks_controller_spec.rb b/spec/controllers/mailgun_webhooks_controller_spec.rb index fb6125c884..12083a7472 100644 --- a/spec/controllers/mailgun_webhooks_controller_spec.rb +++ b/spec/controllers/mailgun_webhooks_controller_spec.rb @@ -107,21 +107,21 @@ let!(:state_intake) { create :state_file_az_intake, email_address: email, unsubscribed_from_email: true } let!(:another_intake) { create :state_file_az_intake, email_address: 'another-email@test.gov', unsubscribed_from_email: true } - it 'reopt-in statefile-intake based on email' do + xit 'reopt-in statefile-intake based on email' do params['sender'] = email post :create_incoming_email, params: params state_intake.reload expect(state_intake.unsubscribed_from_email).to be_falsey end - it 'does NOT reopt-in statefile-intake based on a slighlty different email version' do + xit 'does NOT reopt-in statefile-intake based on a slighlty different email version' do params['sender'] = 'email+01@test.com' post :create_incoming_email, params: params state_intake.reload expect(state_intake.unsubscribed_from_email).to be_truthy end - it 'does NOT reopt-in for state-file intakes without an associated incoming email' do + xit 'does NOT reopt-in for state-file intakes without an associated incoming email' do params['sender'] = email post :create_incoming_email, params: params another_intake.reload From d1fc32262a8e4654aa51d734241a645ac410d793 Mon Sep 17 00:00:00 2001 From: tofarr Date: Wed, 10 Apr 2024 13:56:09 -0600 Subject: [PATCH 06/20] Fix submission and skip flaky tests #187278919 (#4468) * Fix for submission page * Skip flaky tests --- .../hub/state_file/efile_submissions_controller.rb | 4 +--- spec/models/document_spec.rb | 2 +- spec/models/intake/gyr_intake_spec.rb | 2 +- spec/services/mixpanel_service_flaky_tcp_spec.rb | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/controllers/hub/state_file/efile_submissions_controller.rb b/app/controllers/hub/state_file/efile_submissions_controller.rb index b9d4be9dc8..755892a338 100644 --- a/app/controllers/hub/state_file/efile_submissions_controller.rb +++ b/app/controllers/hub/state_file/efile_submissions_controller.rb @@ -5,13 +5,11 @@ class EfileSubmissionsController < Hub::StateFile::BaseController before_action :load_efile_submissions, only: [:index] def index - join_sql = StateFileBaseIntake::STATE_CODES.map do |state_code| "SELECT state_file_#{state_code}_intakes.id as intake_id, 'StateFile#{state_code.to_s.titleize}Intake' as ds_type, '#{state_code}' as data_source_state_code, state_file_#{state_code}_intakes.email_address FROM state_file_#{state_code}_intakes" end - join_sql = "INNER JOIN (#{join_sql.join(" UNION ")}) data_source ON efile_submissions.id = data_source.intake_id and efile_submissions.data_source_type = data_source.ds_type" + join_sql = "INNER JOIN (#{join_sql.join(" UNION ")}) data_source ON efile_submissions.data_source_id = data_source.intake_id and efile_submissions.data_source_type = data_source.ds_type" @efile_submissions = EfileSubmission.joins(join_sql).select("efile_submissions.*, data_source.*") - search = params[:search] if search.present? query = "email_address LIKE ? OR irs_submission_id LIKE ?" diff --git a/spec/models/document_spec.rb b/spec/models/document_spec.rb index faa3c899ad..21dfbf80e1 100644 --- a/spec/models/document_spec.rb +++ b/spec/models/document_spec.rb @@ -262,7 +262,7 @@ let(:document) { build :document } let(:object) { document } - context "when client is the uploader" do + xcontext "when client is the uploader" do it_behaves_like "an incoming interaction" do let(:client) { create :client } let(:subject) { build :document, client: client, uploaded_by: client } diff --git a/spec/models/intake/gyr_intake_spec.rb b/spec/models/intake/gyr_intake_spec.rb index 4fc0c4f0a3..8943497aac 100644 --- a/spec/models/intake/gyr_intake_spec.rb +++ b/spec/models/intake/gyr_intake_spec.rb @@ -400,7 +400,7 @@ end end - describe "after_save when the intake is completed" do + xdescribe "after_save when the intake is completed" do let(:intake) { create :intake } it_behaves_like "an incoming interaction" do diff --git a/spec/services/mixpanel_service_flaky_tcp_spec.rb b/spec/services/mixpanel_service_flaky_tcp_spec.rb index 07cdc3abba..2238d7a185 100644 --- a/spec/services/mixpanel_service_flaky_tcp_spec.rb +++ b/spec/services/mixpanel_service_flaky_tcp_spec.rb @@ -5,7 +5,7 @@ allow(Rails.application.credentials).to receive(:dig).and_return("mock-mixpanel-token") end - context "when the TCP connection is fine" do + xcontext "when the TCP connection is fine" do it 'tries 1 time' do expect(MixpanelService.instance.instance_variable_get(:@consumer)).to receive(:send!).exactly(1).times allow(Concurrent::ScheduledTask).to receive(:new) { |delay, &block| MockScheduledTask.new(delay, &block) } @@ -13,7 +13,7 @@ end end - context "when the TCP connection fails" do + xcontext "when the TCP connection fails" do it 'tries 3 times' do expect(MixpanelService.instance.instance_variable_get(:@consumer)).to receive(:send!).exactly(3).times.and_raise(StandardError) allow(Concurrent::ScheduledTask).to receive(:new) { |delay, &block| MockScheduledTask.new(delay, &block) } From 580b0afa38fa6a40cf982d0d79d6bc6c29a73b08 Mon Sep 17 00:00:00 2001 From: jenny-heath <43800769+jenny-heath@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:55:05 -0700 Subject: [PATCH 07/20] Add back the drivers license annotations and schema lines (#4474) Co-authored-by: Martha Pidcock --- app/models/intake.rb | 2 ++ app/models/intake/ctc_intake.rb | 2 ++ app/models/intake/gyr_intake.rb | 2 ++ db/schema.rb | 2 ++ spec/models/intake/gyr_intake_spec.rb | 2 ++ spec/models/intake_spec.rb | 2 ++ 6 files changed, 12 insertions(+) diff --git a/app/models/intake.rb b/app/models/intake.rb index 9263fea383..a43da723da 100644 --- a/app/models/intake.rb +++ b/app/models/intake.rb @@ -306,6 +306,8 @@ # # fk_rails_... (client_id => clients.id) # fk_rails_... (matching_previous_year_intake_id => intakes.id) +# fk_rails_... (primary_drivers_license_id => drivers_licenses.id) +# fk_rails_... (spouse_drivers_license_id => drivers_licenses.id) # fk_rails_... (vita_partner_id => vita_partners.id) # diff --git a/app/models/intake/ctc_intake.rb b/app/models/intake/ctc_intake.rb index dd8d7f07d8..6508bc0ea8 100644 --- a/app/models/intake/ctc_intake.rb +++ b/app/models/intake/ctc_intake.rb @@ -306,6 +306,8 @@ # # fk_rails_... (client_id => clients.id) # fk_rails_... (matching_previous_year_intake_id => intakes.id) +# fk_rails_... (primary_drivers_license_id => drivers_licenses.id) +# fk_rails_... (spouse_drivers_license_id => drivers_licenses.id) # fk_rails_... (vita_partner_id => vita_partners.id) # class Intake::CtcIntake < Intake diff --git a/app/models/intake/gyr_intake.rb b/app/models/intake/gyr_intake.rb index 79fa2b4cb3..76a5a08f96 100644 --- a/app/models/intake/gyr_intake.rb +++ b/app/models/intake/gyr_intake.rb @@ -306,6 +306,8 @@ # # fk_rails_... (client_id => clients.id) # fk_rails_... (matching_previous_year_intake_id => intakes.id) +# fk_rails_... (primary_drivers_license_id => drivers_licenses.id) +# fk_rails_... (spouse_drivers_license_id => drivers_licenses.id) # fk_rails_... (vita_partner_id => vita_partners.id) # class Intake::GyrIntake < Intake diff --git a/db/schema.rb b/db/schema.rb index 498b516d95..e6d131813a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2272,6 +2272,8 @@ add_foreign_key "incoming_text_messages", "clients" add_foreign_key "intake_archives", "intakes", column: "id" add_foreign_key "intakes", "clients" + add_foreign_key "intakes", "drivers_licenses", column: "primary_drivers_license_id" + add_foreign_key "intakes", "drivers_licenses", column: "spouse_drivers_license_id" add_foreign_key "intakes", "intakes", column: "matching_previous_year_intake_id" add_foreign_key "intakes", "vita_partners" add_foreign_key "notes", "clients" diff --git a/spec/models/intake/gyr_intake_spec.rb b/spec/models/intake/gyr_intake_spec.rb index 8943497aac..0f5dcb3b42 100644 --- a/spec/models/intake/gyr_intake_spec.rb +++ b/spec/models/intake/gyr_intake_spec.rb @@ -306,6 +306,8 @@ # # fk_rails_... (client_id => clients.id) # fk_rails_... (matching_previous_year_intake_id => intakes.id) +# fk_rails_... (primary_drivers_license_id => drivers_licenses.id) +# fk_rails_... (spouse_drivers_license_id => drivers_licenses.id) # fk_rails_... (vita_partner_id => vita_partners.id) # require "rails_helper" diff --git a/spec/models/intake_spec.rb b/spec/models/intake_spec.rb index 7402905340..8cf0af8a39 100644 --- a/spec/models/intake_spec.rb +++ b/spec/models/intake_spec.rb @@ -306,6 +306,8 @@ # # fk_rails_... (client_id => clients.id) # fk_rails_... (matching_previous_year_intake_id => intakes.id) +# fk_rails_... (primary_drivers_license_id => drivers_licenses.id) +# fk_rails_... (spouse_drivers_license_id => drivers_licenses.id) # fk_rails_... (vita_partner_id => vita_partners.id) # From 8834462a2231e834480441b06ef3ad50ed8cd362 Mon Sep 17 00:00:00 2001 From: tofarr Date: Fri, 12 Apr 2024 08:20:49 -0600 Subject: [PATCH 08/20] Close fyst #187355990 (#4472) * Close fyst updates --- app/assets/stylesheets/_state-file.scss | 11 +-- app/controllers/application_controller.rb | 2 +- app/controllers/session_toggles_controller.rb | 3 +- .../questions/landing_page_controller.rb | 2 + .../questions/questions_controller.rb | 11 +++ .../questions/return_status_controller.rb | 3 + .../questions/submission_pdfs_controller.rb | 2 + .../state_file/state_file_pages_controller.rb | 4 +- app/models/state_file_az_intake.rb | 1 - app/models/state_file_ny_intake.rb | 1 - app/views/layouts/application.html.erb | 4 +- .../enter_verification_code.html.erb | 8 +- .../questions/landing_page/edit.html.erb | 78 +++++++++++------- .../return_status/_accepted.html.erb | 79 +++++++++++-------- .../questions/return_status/_pending.html.erb | 2 +- .../return_status/_rejected.html.erb | 34 ++++---- .../questions/return_status/edit.html.erb | 2 +- .../submission_confirmation/edit.html.erb | 2 +- .../state_file_pages/about_page.html.erb | 25 ++++-- .../state_file_pages/login_options.html.erb | 6 +- config/application.rb | 3 +- config/environments/test.rb | 2 + config/locales/en.yml | 28 ++++++- config/locales/es.yml | 28 ++++++- db/schema.rb | 2 - .../application_controller_spec.rb | 2 +- .../esign_declaration_controller_spec.rb | 25 ++++++ .../questions/landing_page_controller_spec.rb | 11 +++ .../submission_pdfs_controller_spec.rb | 12 +++ spec/factories/state_file_az_intakes.rb | 1 - spec/factories/state_file_ny_intakes.rb | 1 - spec/lib/pdf_filler/f13614c_pdf_spec.rb | 2 +- spec/models/state_file_az_intake_spec.rb | 1 - spec/models/state_file_ny_intake_spec.rb | 1 - 34 files changed, 280 insertions(+), 119 deletions(-) diff --git a/app/assets/stylesheets/_state-file.scss b/app/assets/stylesheets/_state-file.scss index 1041d894a3..5511813b2c 100644 --- a/app/assets/stylesheets/_state-file.scss +++ b/app/assets/stylesheets/_state-file.scss @@ -673,13 +673,10 @@ max-width: 100%; } - .esign-declaration-outer, .return-status-outer, .submission-confirmation-outer { - - .question-wrapper .button--primary { - min-width: 40rem; - max-width: 40rem; - width: 40rem; - } + .question-layout .question-wrapper .button--primary.button--wide { + min-width: 40rem; + max-width: 40rem; + width: 40rem; } .esign-declaration-outer .text--error { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 83aa075d73..ded1b93b13 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -401,7 +401,7 @@ def open_for_ctc_read_write? helper_method :open_for_ctc_read_write? def open_for_state_file_intake? - app_time.between?(Rails.configuration.state_file_start_of_open_intake, Rails.configuration.state_file_end_of_intake) + app_time.between?(Rails.configuration.state_file_start_of_open_intake, Rails.configuration.state_file_end_of_in_progress_intakes) end helper_method :open_for_state_file_intake? diff --git a/app/controllers/session_toggles_controller.rb b/app/controllers/session_toggles_controller.rb index 3ca1288f0c..751330c612 100644 --- a/app/controllers/session_toggles_controller.rb +++ b/app/controllers/session_toggles_controller.rb @@ -23,7 +23,8 @@ def index service_url: url_for(host: MultiTenantService.new(:statefile).host, controller: :session_toggles), times: [ SessionToggleTime.new(name: 'Start of open intake', property: :state_file_start_of_open_intake), - SessionToggleTime.new(name: 'End of intake', property: :state_file_end_of_intake), + SessionToggleTime.new(name: 'End of New intake', property: :state_file_end_of_new_intakes), + SessionToggleTime.new(name: 'End of In Progress Intakes', property: :state_file_end_of_in_progress_intakes), ] }, { diff --git a/app/controllers/state_file/questions/landing_page_controller.rb b/app/controllers/state_file/questions/landing_page_controller.rb index 1d555038ab..e6e8508a84 100644 --- a/app/controllers/state_file/questions/landing_page_controller.rb +++ b/app/controllers/state_file/questions/landing_page_controller.rb @@ -3,9 +3,11 @@ module Questions class LandingPageController < QuestionsController skip_before_action :redirect_if_no_intake skip_before_action :set_current_step + skip_before_action :redirect_if_in_progress_intakes_ended def edit @state_name = StateFileBaseIntake::STATE_CODE_AND_NAMES[params[:us_state]] + @closed = app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) if current_intake.present? if current_intake.primary_first_name.present? @user_name = current_intake.primary_first_name diff --git a/app/controllers/state_file/questions/questions_controller.rb b/app/controllers/state_file/questions/questions_controller.rb index 5cce0ed62d..a35487a48a 100644 --- a/app/controllers/state_file/questions/questions_controller.rb +++ b/app/controllers/state_file/questions/questions_controller.rb @@ -3,6 +3,7 @@ module Questions class QuestionsController < ::Questions::QuestionsController include StateFile::StateFileControllerConcern before_action :redirect_if_no_intake + before_action :redirect_if_in_progress_intakes_ended helper_method :card_postscript # default layout for all state file questions @@ -51,6 +52,16 @@ def redirect_if_no_intake end end + def redirect_if_in_progress_intakes_ended + if app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) + if current_intake.efile_submissions.empty? + redirect_to root_path + else + redirect_to StateFile::Questions::ReturnStatusController.to_path_helper(action: :edit, us_state: state_code) + end + end + end + def next_step form_navigation.next end diff --git a/app/controllers/state_file/questions/return_status_controller.rb b/app/controllers/state_file/questions/return_status_controller.rb index 9b593660fc..57937a42d1 100644 --- a/app/controllers/state_file/questions/return_status_controller.rb +++ b/app/controllers/state_file/questions/return_status_controller.rb @@ -1,8 +1,10 @@ module StateFile module Questions class ReturnStatusController < AuthenticatedQuestionsController + include StateFile::SurveyLinksConcern before_action :redirect_if_from_efile before_action :redirect_if_no_submission + skip_before_action :redirect_if_in_progress_intakes_ended def edit @error = submission_error @@ -12,6 +14,7 @@ def edit @download_form_name = download_form_name @mail_voucher_address = mail_voucher_address @voucher_path = voucher_path + @survey_link = survey_link(current_intake) end def prev_path diff --git a/app/controllers/state_file/questions/submission_pdfs_controller.rb b/app/controllers/state_file/questions/submission_pdfs_controller.rb index 531153e1be..2fb501da0d 100644 --- a/app/controllers/state_file/questions/submission_pdfs_controller.rb +++ b/app/controllers/state_file/questions/submission_pdfs_controller.rb @@ -1,6 +1,8 @@ module StateFile module Questions class SubmissionPdfsController < QuestionsController + skip_before_action :redirect_if_in_progress_intakes_ended + def show @submission = current_intake.efile_submissions.find_by(id: params[:id]) error_redirect and return unless @submission.present? diff --git a/app/controllers/state_file/state_file_pages_controller.rb b/app/controllers/state_file/state_file_pages_controller.rb index ddfb248d09..43fc67a1d3 100644 --- a/app/controllers/state_file/state_file_pages_controller.rb +++ b/app/controllers/state_file/state_file_pages_controller.rb @@ -32,7 +32,9 @@ def clear_session redirect_to action: :about_page end - def login_options; end + def login_options + @sign_in_closed = app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) + end private diff --git a/app/models/state_file_az_intake.rb b/app/models/state_file_az_intake.rb index 15c71c42f4..fcf51da368 100644 --- a/app/models/state_file_az_intake.rb +++ b/app/models/state_file_az_intake.rb @@ -31,7 +31,6 @@ # hashed_ssn :string # household_excise_credit_claimed :integer default("unfilled"), not null # household_excise_credit_claimed_amt :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") diff --git a/app/models/state_file_ny_intake.rb b/app/models/state_file_ny_intake.rb index 34d0607489..64e2696379 100644 --- a/app/models/state_file_ny_intake.rb +++ b/app/models/state_file_ny_intake.rb @@ -35,7 +35,6 @@ # household_rent_amount :integer # household_rent_own :integer default("unfilled"), not null # household_ssi :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 098a44bc0a..19f3e236c0 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -110,7 +110,9 @@ <% if hide_intercom? %>
<% elsif state_file? %> - <%= render("shared/state_file_intercom")%> + <% unless app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) %> + <%= render("shared/state_file_intercom")%> + <% end %> <% else %> <%= render("shared/intercom", isHub: hub?) unless (hub? || Routes::CtcDomain.new.matches?(request)) %> <% end %> diff --git a/app/views/state_file/intake_logins/enter_verification_code.html.erb b/app/views/state_file/intake_logins/enter_verification_code.html.erb index 0b9497ebbb..6ec64c52e3 100644 --- a/app/views/state_file/intake_logins/enter_verification_code.html.erb +++ b/app/views/state_file/intake_logins/enter_verification_code.html.erb @@ -5,7 +5,13 @@

<%= content_for :page_title %>

-

<%= t(".code_sent_to_html", contact_info: @verification_code_form.formatted_contact_info) %>

+

+ <% if app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) %> + <%= t(".code_sent_to_closed_html", contact_info: @verification_code_form.formatted_contact_info) %> + <% else %> + <%= t(".code_sent_to_html", contact_info: @verification_code_form.formatted_contact_info) %> + <% end %> +

<%= f.hidden_field(:contact_info) %>
<%= f.cfa_input_field(:verification_code, t(".enter_6_digit_code"), classes: ["form-width--long"]) %> diff --git a/app/views/state_file/questions/landing_page/edit.html.erb b/app/views/state_file/questions/landing_page/edit.html.erb index 7430d9df7c..1a18598939 100644 --- a/app/views/state_file/questions/landing_page/edit.html.erb +++ b/app/views/state_file/questions/landing_page/edit.html.erb @@ -1,5 +1,5 @@ <% state_image_path = params[:us_state] == "ny" ? "partner-logos/nygov-logo.svg" : "partner-logos/azgov-logo.svg" %> -<% title = t(".#{params[:us_state]}.title") %> +<% title = @closed ? t(".title_closed", state_name: @state_name) : t(".#{params[:us_state]}.title") %> <% content_for :page_title, title %> @@ -7,43 +7,63 @@

<%= title %>

- <% unless @user_name.present? %> + <% if @closed %>

- <%= t(".#{params[:us_state]}.built_with_html") %> + <%= t(".#{params[:us_state]}.closed_html") %>

- <% end %> - -
-
- <%= image_tag state_image_path, alt: "#{state_name} state logo", class: "" %> -
-
- <%=t(".#{params[:us_state]}.supported_by") %> +

+ <%= t(".already_filed_html") %> +

+
+
+ <%= image_tag state_image_path, alt: "#{state_name} state logo", class: "" %> +
+
+ <%=t(".#{params[:us_state]}.supported_by") %> +
-
- - <% if @user_name.present? %> -

<%= t(".welcome_back", user_name: @user_name) %>

-

<%= t(".continue", state_name: @state_name) %>

- <%= link_to StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: current_intake.state_code), class: "button button--primary button--wide", id: "firstCta" do %> - <%= t("general.sign_in") %> + <%= link_to StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: params[:us_state]), class: "button button--primary button--wide", id: "firstCta" do %> + <%= t(".download_your_record") %> <% end %> - <%= form_with model: @form, url: { action: :update }, local: true, method: :put, builder: VitaMinFormBuilder, id: "start-again-form" do |f| %> + <% else %> + <% unless @user_name.present? %>

- <%= t(".not_you", user_name: @user_name) %> - <%=f.submit t(".start_new", state_name: @state_name), class: "button--link" %> + <%= t(".#{params[:us_state]}.built_with_html") %>

<% end %> - <% else %> -
- <%= t(".help_text_html") %> + +
+
+ <%= image_tag state_image_path, alt: "#{state_name} state logo", class: "" %> +
+
+ <%=t(".#{params[:us_state]}.supported_by") %> +
- <%= form_with model: @form, url: { action: :update }, local: true, method: :put, builder: VitaMinFormBuilder do |f| %> - <%= f.submit t("general.get_started"), class: "button button--primary button--wide", id: "firstCta" %> + + <% if @user_name.present? %> +

<%= t(".welcome_back", user_name: @user_name) %>

+

<%= t(".continue", state_name: @state_name) %>

+ <%= link_to StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: current_intake.state_code), class: "button button--primary button--wide", id: "firstCta" do %> + <%= t("general.sign_in") %> + <% end %> + <%= form_with model: @form, url: { action: :update }, local: true, method: :put, builder: VitaMinFormBuilder, id: "start-again-form" do |f| %> +

+ <%= t(".not_you", user_name: @user_name) %> + <%=f.submit t(".start_new", state_name: @state_name), class: "button--link" %> +

+ <% end %> + <% else %> +
+ <%= t(".help_text_html") %> +
+ <%= form_with model: @form, url: { action: :update }, local: true, method: :put, builder: VitaMinFormBuilder do |f| %> + <%= f.submit t("general.get_started"), class: "button button--primary button--wide", id: "firstCta" %> + <% end %> +

+ <%= t(".already_started_html", sign_in_url: StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: params[:us_state])) %> +

<% end %> -

- <%= t(".already_started_html", sign_in_url: StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: params[:us_state])) %> -

<% end %>
<%= image_tag 'questions/welcome.svg', class: 'fyst-home-image' %> diff --git a/app/views/state_file/questions/return_status/_accepted.html.erb b/app/views/state_file/questions/return_status/_accepted.html.erb index 3ade65eb7b..f722462287 100644 --- a/app/views/state_file/questions/return_status/_accepted.html.erb +++ b/app/views/state_file/questions/return_status/_accepted.html.erb @@ -8,51 +8,62 @@ <%= t("state_file.questions.return_status.accepted.title", state_name: States.name_for_key(params[:us_state].upcase)) %> -<% refund_or_owed_amount = current_intake.calculated_refund_or_owed_amount %> -<% if refund_or_owed_amount.positive? %> -

<%= t('.check_your_refund_html', refund_url: @refund_url) %>

-<% elsif refund_or_owed_amount.negative? %> +<% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> + <% refund_or_owed_amount = current_intake.calculated_refund_or_owed_amount %> + <% if refund_or_owed_amount.positive? %> +

<%= t('.check_your_refund_html', refund_url: @refund_url) %>

+ <% elsif refund_or_owed_amount.negative? %> +

+ <%= t('.direct_debit_html', tax_payment_url: @tax_payment_url) %> + <%= t('.track_your_payment_html') if params[:us_state] == 'ny' %> +

+ <% end %> +

- <%= t('.direct_debit_html', tax_payment_url: @tax_payment_url) %> - <%= t('.track_your_payment_html') if params[:us_state] == 'ny' %> + <%= t('.register_to_vote') %>

-<% end %> -

- <%= t('.register_to_vote') %> -

+

<%= t('general.spread_the_word_html') %>

-

<%= t('general.spread_the_word_html') %>

+
+

+ <%= t('.pay_by_mail_or_moneyorder') %> +

-
-

- <%= t('.pay_by_mail_or_moneyorder') %> -

+

+ <%= t('.include_payment') %> + (<%= @download_form_name %>) +

-

- <%= t('.include_payment') %> - (<%= @download_form_name %>) -

+
+ <%= link_to t('.download_voucher'), ActionController::Base.helpers.asset_path(@voucher_path) %> +
-
- <%= link_to t('.download_voucher'), ActionController::Base.helpers.asset_path(@voucher_path) %> -
+
+ + <% if params[:us_state] == 'az' %> +

+ <%= t('.check_payable') %> +

-
+
+ <% end %> - <% if params[:us_state] == 'az' %>

- <%= t('.check_payable') %> + <%= t('.mail_voucher_and_payment') %>

-
- <% end %> - + + <%= @mail_voucher_address %> + +
+<% else %> +

+ <%= t("state_file.questions.return_status.accepted.download_title", state_name: States.name_for_key(params[:us_state].upcase)) %> +

- <%= t('.mail_voucher_and_payment') %> + <%= t("state_file.questions.return_status.thank_you_html", state_name: States.name_for_key(params[:us_state].upcase)) %>

- - - <%= @mail_voucher_address %> - -
\ No newline at end of file +

<%= t("state_file.questions.return_status.accepted.feedback") %>

+

<%= t('.register_to_vote') %>

+<% end %> diff --git a/app/views/state_file/questions/return_status/_pending.html.erb b/app/views/state_file/questions/return_status/_pending.html.erb index cf477fd9dc..d6df1be3b0 100644 --- a/app/views/state_file/questions/return_status/_pending.html.erb +++ b/app/views/state_file/questions/return_status/_pending.html.erb @@ -17,5 +17,5 @@

- <%= t('.more_than_two_days') %> + <%= app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) ? t('.more_than_two_days') : t('.more_than_two_days_closed_html') %>

\ No newline at end of file diff --git a/app/views/state_file/questions/return_status/_rejected.html.erb b/app/views/state_file/questions/return_status/_rejected.html.erb index 5c2796da50..c51b5a9d0c 100644 --- a/app/views/state_file/questions/return_status/_rejected.html.erb +++ b/app/views/state_file/questions/return_status/_rejected.html.erb @@ -39,20 +39,26 @@ <% end %> <% if @error&.auto_wait %> -
-

<%= t('.next_steps.can_edit.title') %>

- <%= @error.resolution(I18n.locale).present? ? @error.resolution(I18n.locale) : t('.next_steps.can_edit.body') %> -
+ <% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> +
+

<%= t('.next_steps.can_edit.title') %>

+ <%= @error.resolution(I18n.locale).present? ? @error.resolution(I18n.locale) : t('.next_steps.can_edit.body') %> +
+ <% end %> -
-

<%= t('.have_questions') %>

- <%= t('.contact_us') %> -
+ <% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> +
+

<%= t('.have_questions') %>

+ <%= t('.contact_us') %> +
+ <% end %> - <% edit_return_controller = @error.correction_path.present? ? EfileError.path_to_controller(@error.correction_path) : EfileError.default_controller %> - <%= link_to t('.edit_return'), edit_return_controller.to_path_helper( - action: edit_return_controller.navigation_actions.first, - return_to_review: :y, - us_state: current_intake.state_code - ), class: "button button--primary spacing-above-60" %> + <% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> + <% edit_return_controller = @error.correction_path.present? ? EfileError.path_to_controller(@error.correction_path) : EfileError.default_controller %> + <%= link_to t('.edit_return'), edit_return_controller.to_path_helper( + action: edit_return_controller.navigation_actions.first, + return_to_review: :y, + us_state: current_intake.state_code + ), class: "button button--primary button--wide spacing-above-60" %> + <% end %> <% end %> diff --git a/app/views/state_file/questions/return_status/edit.html.erb b/app/views/state_file/questions/return_status/edit.html.erb index 0a0c40498d..9611c9d029 100644 --- a/app/views/state_file/questions/return_status/edit.html.erb +++ b/app/views/state_file/questions/return_status/edit.html.erb @@ -3,7 +3,7 @@ <%= render @return_status %> <% unless @error&.auto_wait %> - <%= link_to t('.download_state_return_pdf'), StateFile::Questions::SubmissionPdfsController.to_path_helper(us_state: params[:us_state], action: :show, id: EfileSubmission.where(data_source: current_intake).last), class: "button button--primary spacing-above-60" %> + <%= link_to t('.download_state_return_pdf'), StateFile::Questions::SubmissionPdfsController.to_path_helper(us_state: params[:us_state], action: :show, id: EfileSubmission.where(data_source: current_intake).last), class: "button button--primary button--wide spacing-above-60" %> <% end %> <% if show_xml? %> diff --git a/app/views/state_file/questions/submission_confirmation/edit.html.erb b/app/views/state_file/questions/submission_confirmation/edit.html.erb index e9789ead0e..ab6cfbc674 100644 --- a/app/views/state_file/questions/submission_confirmation/edit.html.erb +++ b/app/views/state_file/questions/submission_confirmation/edit.html.erb @@ -32,7 +32,7 @@

<%= t('general.spread_the_word_html') %>

- <%= link_to t(".download_state_return_pdf"), StateFile::Questions::SubmissionPdfsController.to_path_helper(us_state: params[:us_state], action: :show, id: EfileSubmission.where(data_source: current_intake).first), class: "button button--primary" %> + <%= link_to t(".download_state_return_pdf"), StateFile::Questions::SubmissionPdfsController.to_path_helper(us_state: params[:us_state], action: :show, id: EfileSubmission.where(data_source: current_intake).first), class: "button button--primary button--wide" %> <% if show_xml? %>

diff --git a/app/views/state_file/state_file_pages/about_page.html.erb b/app/views/state_file/state_file_pages/about_page.html.erb index 942a387b3a..551185d25f 100644 --- a/app/views/state_file/state_file_pages/about_page.html.erb +++ b/app/views/state_file/state_file_pages/about_page.html.erb @@ -4,13 +4,26 @@

<%= title %>

-
- <%= t(".subheader_html", login_path: login_options_path(us_state: 'us')) %> -

+ <% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> +
+

<%= t(".subheader_1_html", login_path: login_options_path(us_state: 'us')) %>

+ <% if app_time.before?(Rails.configuration.state_file_end_of_new_intakes) %> +

<%= t(".subheader_2_html", login_path: login_options_path(us_state: 'us')) %>

+ <% end %> +

<%= t(".subheader_3_html", login_path: login_options_path(us_state: 'us')) %>

+
-
- <%= t(".section1_html", faq_link: state_faq_path(us_state: 'us')) %> -
+
+ <%= t(".section1_html", faq_link: state_faq_path(us_state: 'us')) %> +
+ <% else %> +
+ <%= t(".closed_subheader_html") %> + <%= link_to StateFile::StateFilePagesController.to_path_helper(action: :login_options, us_state: "us"), class: "button button--primary button--wide", id: "firstCta" do %> + <%= t("general.sign_in") %> + <% end %> +
+ <% end %>
<%= image_tag 'questions/welcome.svg', class: 'fyst-home-image' %>
diff --git a/app/views/state_file/state_file_pages/login_options.html.erb b/app/views/state_file/state_file_pages/login_options.html.erb index 3939f9ae87..35c30f746d 100644 --- a/app/views/state_file/state_file_pages/login_options.html.erb +++ b/app/views/state_file/state_file_pages/login_options.html.erb @@ -1,7 +1,9 @@ -<% content_for :page_title, t(".title") %> +<% content_for :page_title, @sign_in_closed ? t(".title_sign_in_closed") : t(".title") %>

<%= content_for(:page_title) %>

-

<%= t(".help_text_html", link: root_path) %>

+<% unless @sign_in_closed %> +

<%= t(".help_text_html", link: root_path) %>

+<% end %> <% if Flipper.enabled?(:sms_notifications) %>

diff --git a/config/application.rb b/config/application.rb index ee6a80f358..e5f37d5036 100644 --- a/config/application.rb +++ b/config/application.rb @@ -107,7 +107,8 @@ class Application < Rails::Application # StateFile config.state_file_start_of_open_intake = Time.find_zone('America/New_York').parse('2024-02-08 09:00:00') - config.state_file_end_of_intake = Time.find_zone('America/New_York').parse('2024-04-15 23:59:59') + config.state_file_end_of_new_intakes = Time.find_zone('America/New_York').parse('2024-04-15 23:59:59') + config.state_file_end_of_in_progress_intakes = Time.find_zone('America/New_York').parse('2024-04-25 23:59:59') config.allow_magic_verification_code = (Rails.env.demo? || Rails.env.development? || Rails.env.heroku?) config.allow_magic_ssn = (Rails.env.demo? || Rails.env.development? || Rails.env.heroku? || Rails.env.staging?) diff --git a/config/environments/test.rb b/config/environments/test.rb index 508e2d037f..e908d47f02 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -106,6 +106,8 @@ # StateFile config.state_file_start_of_open_intake = Time.find_zone('America/New_York').parse('2024-01-01 7:59:59') + config.state_file_end_of_new_intakes = Time.find_zone('America/New_York').parse('2038-04-15 23:59:59') + config.state_file_end_of_in_progress_intakes = Time.find_zone('America/New_York').parse('2038-04-25 23:59:59') config.active_record.encryption.primary_key = 'test' config.active_record.encryption.deterministic_key = 'test' diff --git a/config/locales/en.yml b/config/locales/en.yml index 45ccd9787e..2b3137c286 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2030,6 +2030,7 @@ en: ssn_label: Enter your Social Security number or ITIN. For example, 123-45-6789. title: Code verified! Authentication needed to continue. enter_verification_code: + code_sent_to_closed_html: We’ve sent your code to %{contact_info}. If you didn’t receive a code, and need help resubscribing, email us at help@fileyourstatetaxes.org. code_sent_to_html: We’ve sent your code to %{contact_info}. If you didn’t receive a code, and need help resubscribing, chat with us. enter_6_digit_code: Enter the 6-digit code title: Enter the code to continue @@ -2361,12 +2362,17 @@ en: title: Now, let’s complete your state tax return landing_page: edit: + already_filed_html: Already filed your state taxes with us? You can download a copy of your state return until December 31, 2024. already_started_html: Already started filing your state return? Sign in. az: built_with_html: "FileYourStateTaxes is built in partnership with the state of Arizona to integrate with the IRS Direct File tool and takes about 10 to 15 minutes to complete once your federal return is accepted. Get started by setting up your account." + closed_html: | + FileYourStateTaxes is built in partnership with the state of Arizona to integrate with the IRS Direct File tool.

+ We're closed for the tax season. Unfortunately you can no longer file your state return with us this year. supported_by: Supported by the state of Arizona title: File your Arizona state taxes for free continue: To continue filing your %{state_name} State return, please sign in below + download_your_record: Download Your Record help_text_html: | What you’ll need:
@@ -2380,9 +2386,13 @@ en: not_you: Not %{user_name}? ny: built_with_html: "FileYourStateTaxes is built in partnership with New York State to integrate with the IRS Direct File tool and takes about 10 to 15 minutes to complete once your federal return is accepted. Get started by setting up your account." + closed_html: | + FileYourStateTaxes is built in partnership with New York State to integrate with the IRS Direct File tool.

+ We're closed for the tax season. Unfortunately you can no longer file your state return with us this year. supported_by: Supported by New York State title: File your New York State taxes for free start_new: Click here to start a new %{state_name} State return. + title_closed: Our free %{state_name} tax filing service is closed for the season welcome_back: Welcome back %{user_name}! name_dob: edit: @@ -2533,7 +2543,9 @@ en: check_payable: Make your check or money order payable to Arizona Department of Revenue. Write your full SSN, “2023 Tax” and 140 on your payment. check_your_refund_html: Check on the status of your refund. direct_debit_html: If you have a remaining tax balance, remember to pay your taxes online at %{tax_payment_url} or by mail before the April 15 filing deadline. + download_title: Download a copy of your 2023 %{state_name} state tax return until December 31, 2024 download_voucher: Download payment voucher form + feedback: We value your feedback—let us know what you think of this tool. include_payment: You'll need to include the payment voucher form. mail_voucher_and_payment: 'Mail the voucher form and payment to:' pay_by_mail_or_moneyorder: 'If you are paying by mail via check or money order:' @@ -2546,6 +2558,7 @@ en: show_xml: Show XML pending: more_than_two_days: If it's been more than two days since you submitted your state taxes, contact our team of support specialists by clicking chat with us. + more_than_two_days_closed_html: If it's been more than two days since you submitted your state taxes, contact our team of support specialists by emailing us at help@fileyourstatetaxes.org receive_email: You'll receive an email update when your state tax return is accepted. receive_email_sms: You'll receive an email or text update when your state tax return is accepted. title: You have submitted your 2023 %{state_name} tax return! It is currently waiting to be accepted for processing. @@ -2563,6 +2576,9 @@ en: reject_code: 'Reject Code:' reject_desc: 'Reject Description:' title: Unfortunately, your 2023 %{state_name} state tax return was rejected + thank_you_html: | + Thank you for filing your return with FileYourStateTaxes, built in partnership with the state of %{state_name} to integrate with the IRS Direct File tool.

+ Filed your state taxes with us? You can download a copy of your state return until December 31, 2024. review: w2: W-2 state and local tax information shared: @@ -2725,6 +2741,10 @@ en: title_html: Just a moment, we’re transferring your federal tax return to complete parts of your state return.

This usually takes a few minutes. Don’t close this page. state_file_pages: about_page: + closed_subheader_html: | + FileYourStateTaxes integrates with IRS Direct File to help you complete your state tax return for free.

+ We're closed for the tax season. Unfortunately you can no longer file your state return with us this year.

+ Already filed your state taxes with us? You can download a copy of your state return until December 31, 2024

header: A free state filing tool for Arizona and New York taxpayers using IRS Direct File section1_html: | How does it work? @@ -2737,10 +2757,9 @@ en:

  • E-file your return for free!
  • To learn more, check out the FAQs. - subheader_html: | - FileYourStateTaxes integrates with IRS Direct File to help you complete your state tax return for free.

    - IRS Direct File is now broadly available to all eligible taxpayers through April 15, 2024. For more information on timeline and eligibility, or to start filing your federal return, go to directfile.irs.gov

    - Already filed your federal return with IRS Direct File and need to complete your state return? Sign in here.
    + subheader_1_html: "FileYourStateTaxes integrates with IRS Direct File to help you complete your state tax return for free." + subheader_2_html: IRS Direct File is now broadly available to all eligible taxpayers through April 15, 2024. For more information on timeline and eligibility, or to start filing your federal return, go to directfile.irs.gov + subheader_3_html: Already filed your federal return with IRS Direct File and need to complete your state return? Sign in here. card_postscript: responses_saved_html: Your responses are saved. If you need a break, you can come back and log in to your account at fileyourstatetaxes.org. coming_soon: @@ -2757,6 +2776,7 @@ en: help_text_html: New to FileYourStateTaxes? Get Started phone_number_signin: Sign in with phone number title: Sign in to FileYourStateTaxes + title_sign_in_closed: Sign in to download a copy of your state tax return. privacy_policy: access_request_1_html: FileYourStateTaxes.org respects your control over your information and, upon request, we will confirm whether we hold or are processing information that we have collected from you. You also have the right to amend or update inaccurate or incomplete personal information, request deletion of your personal information, or request that we no longer use it. Under certain circumstances we will not be able to fulfill your request, such as if it interferes with our regulatory obligations, affects legal matters, we cannot verify your identity, or it involves disproportionate cost or effort, but in any event we will respond to your request within a reasonable timeframe and provide you an explanation. In order to make such a request of us, please email us at help@fileyourstatetaxes.org. access_request_2: Please note that for personal information about you that we have obtained or received for processing on behalf of a separate, unaffiliated entity—which determined the means and purposes of processing—all such requests should be made to that entity directly. We will honor and support any instructions they provide us with respect to your personal information. diff --git a/config/locales/es.yml b/config/locales/es.yml index 67fe7adc36..cda1e50e2e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1989,6 +1989,7 @@ es: ssn_label: Ingresa tu número de Seguro Social o ITIN. Por ejemplo, 123-45-6789. title: Código verificado. Se necesita autenticación para continuar. enter_verification_code: + code_sent_to_closed_html: Hemos enviado su código a %{contact_info}. Si no recibió un código y necesita ayuda para volver a suscribirse, envíenos un correo electrónico a help@fileyourstatetaxes.org. code_sent_to_html: Te enviamos tu código a %{contact_info}. Si no recibiste un código y necesitas ayuda para volver a suscribirte, chatea con nosotros. enter_6_digit_code: Ingresa el código de 6 dígitos title: Ingresa el código para continuar @@ -2327,12 +2328,17 @@ es: title: Ahora completemos tu declaración de impuestos estatales landing_page: edit: + already_filed_html: "¿Ya presentó tus impuestos estatales con nosotros? Puede descargar una copia de su declaración estatal hasta el 31 de diciembre de 2024." already_started_html: ¿Ya comenzaste a presentar tu declaración estatal? Inicia sesión. az: built_with_html: "FileYourStateTaxes ha sido creado en colaboración con el estado de Arizona para integrarse con la herramienta Direct File del IRS y solo toma de 10 a 15 minutos completarlo una vez que tu declaración federal sea aceptada. Comienza configurando tu cuenta." + closed_html: | + FileYourStateTaxes se creó en asociación con el estado de Arizona para integrarse con la herramienta Direct File del IRS.

    + Estamos cerrados por la temporada de impuestos. Desafortunadamente no puede presentar su declaración estatal con nosotros este año. supported_by: Respaldada por el estado de Arizona title: Presenta tus impuestos estatales de Arizona sin costo continue: Para continuar presentando su declaración estatal de %{state_name}, inicie sesión a continuación + download_your_record: Descargue tu registro help_text_html: | Lo que necesitarás para comenzar
    @@ -2346,9 +2352,13 @@ es: not_you: "¿No es %{user_name}?" ny: built_with_html: "FileYourStateTaxes ha sido creado en colaboración con el estado de Nueva York para integrarse con la herramienta Direct File del IRS y tarda aproximadamente de 10 a 15 minutos completarlo una vez que tu declaración federal sea aceptada. Comienza configurando tu cuenta." + closed_html: | + FileYourStateTaxes se creó en asociación con el estado de Nueva York para integrarse con la herramienta Direct File del IRS.

    + Estamos cerrados por la temporada de impuestos. Lamentablemente no puede presentar su declaración estatal con nosotros este año. supported_by: Respaldada por el estado de Nueva York title: Presenta tus impuestos Estatales de Nueva York sin costo start_new: Haga clic aquí para iniciar una nueva declaración estatal de %{state_name}. + title_closed: Nuestro servicio gratuito de presentación de impuestos %{state_name} está cerrado por temporada welcome_back: "¡Bienvenido de nuevo %{user_name}!" name_dob: edit: @@ -2513,7 +2523,9 @@ es: check_payable: Haz tu cheque o giro postal a nombre del “Arizona Department of Revenue. Escribe tu SSN completo, "2023 Tax" y 140 en tu pago. check_your_refund_html: Verifica el estado de tu reembolso. direct_debit_html: Si no elegiste el débito directo, recuerda pagar tus impuestos en línea en %{tax_payment_url} o por correo antes de la fecha límite del 15 de abril. + download_title: Descargue una copia de tu declaración de impuestos estatales de %{state_name} de 2023 hasta el 31 de diciembre de 2024 download_voucher: Descargar formulario de comprobante de pago + feedback: Valoramos sus comentarios; háganos saber lo que piensa de esta herramienta. include_payment: Debes incluir el formulario de comprobante de pago. mail_voucher_and_payment: 'Envía el formulario de comprobante de pago y el pago a:' pay_by_mail_or_moneyorder: 'Si estás pagando por correo mediante cheque o giro postal:' @@ -2526,6 +2538,7 @@ es: show_xml: Mostrar XML pending: more_than_two_days: Si han pasado más de 2 días desde que presentaste tus impuestos estatales, ponte en contacto con nuestro equipo haciendo clic en "chatea con nosotros". + more_than_two_days_closed_html: Si han pasado más de dos días desde que presentó sus impuestos estatales, comuníquese con nuestro equipo de especialistas de soporte enviándonos un correo electrónico a help@fileyourstatetaxes.org receive_email: Recibirás una actualización por correo electrónico cuando tu declaración de impuestos estatales sea aceptada. receive_email_sms: Recibirás una actualización por correo electrónico o mensaje de texto cuando tu declaración de impuestos estatales sea aceptada. title: Has enviado tu declaración de impuestos estatales de %{state_name} para el año 2023, y está esperando ser aceptada. @@ -2543,6 +2556,9 @@ es: reject_code: 'Código de Rechazo:' reject_desc: 'Descripción del Rechazo:' title: Lamentablemente, tu declaración de impuestos del estado de %{state_name} para el año 2023 fue rechazada + thank_you_html: | + Gracias por presentar su declaración con FileYourStateTaxes, creado en asociación con el estado de %{state_name} para integrarse con la herramienta Direct File del IRS.

    + ¿Presentó sus impuestos estatales con nosotros? Puede descargar una copia de su declaración estatal hasta el 31 de diciembre de 2024. review: w2: Información fiscal estatal y local W-2 shared: @@ -2706,6 +2722,10 @@ es: title_html: Un momento, estamos transfiriendo tu declaración de impuestos federales para completar partes de tu declaración estatales.

    Esto suele tomar unos minutos. No cierres la página. state_file_pages: about_page: + closed_subheader_html: | + FileYourStateTaxes se integra con IRS Direct File para ayudarte a completar tu declaración de impuestos estatales sin costo.

    + Estamos cerrados por la temporada de impuestos. Desafortunadamente, no puede presentar tu declaración estatal con nosotros este año.

    + ¿Ya presentó tus impuestos estatales con nosotros? Puedes descargar una copia de tu declaración estatal hasta el 31 de diciembre de 2024

    header: Una herramienta sin costo para presentar impuestos estatales para los contribuyentes de Arizona y Nueva York utilizando IRS Direct File section1_html: | ¿Cómo funciona? @@ -2718,10 +2738,9 @@ es:
  • ¡Presenta tu declaración electrónicamente sin costo!
  • Para aprender más, consulta las preguntas frecuentes. - subheader_html: | - FileYourStateTaxes se integra con IRS Direct File para ayudarte a completar tu declaración de impuestos estatales sin costo.

    - El IRS Direct File ahora está ampliamente disponible para todos los contribuyentes elegibles hasta el 15 de abril de 2024. Para obtener más información sobre el cronograma y la elegibilidad, o para comenzar a presentar tu declaración federal, ve a directfile.irs.gov.

    - ¿Ya presentaste tu declaración federal con IRS Direct File y necesitas completar tu declaración estatal? Inicia sesión aquí.
    + subheader_1_html: "FileYourStateTaxes se integra con IRS Direct File para ayudarte a completar tu declaración de impuestos estatales sin costo." + subheader_2_html: El IRS Direct File ahora está ampliamente disponible para todos los contribuyentes elegibles hasta el 15 de abril de 2024. Para obtener más información sobre el cronograma y la elegibilidad, o para comenzar a presentar tu declaración federal, ve a directfile.irs.gov. + subheader_3_html: "¿Ya presentaste tu declaración federal con IRS Direct File y necesitas completar tu declaración estatal? Inicia sesión aquí." card_postscript: responses_saved_html: Tus respuestas han sido guardadas. Si necesitas hacer una pausa, puedes regresar e iniciar sesión en tu cuenta en fileyourstatetaxes.org. coming_soon: @@ -2738,6 +2757,7 @@ es: help_text_html: "¿Eres nuevo en FileYourStateTaxes? Comienza aquí" phone_number_signin: Iniciar sesión con el teléfono title: Iniciar sesión en FileYourStateTaxes + title_sign_in_closed: Inicie sesión para descargar una copia de tu declaración de impuestos estatales. privacy_policy: access_request_1_html: FileYourStateTaxes.org respeta tu control sobre tu información y, a pedido, confirmaremos si tenemos o estamos procesando información que hemos recopilado de ti. También tienes derecho a corregir o actualizar información personal inexacta o incompleta, solicitar la eliminación de tu información personal o solicitar que dejemos de usarla. En ciertas circunstancias, no podremos cumplir con tu solicitud, como si interfiere con nuestras obligaciones regulatorias, afecta asuntos legales, no podemos verificar tu identidad o implica un costo o esfuerzo desproporcionados, pero de todos modos responderemos a tu solicitud en un plazo razonable y te proporcionaremos una explicación. Para hacer tal solicitud, envíanos un correo electrónico a help@fileyourstatetaxes.org. access_request_2: Ten en cuenta que para la información personal sobre ti que hemos obtenido o recibido para procesar en nombre de una entidad separada y no afiliada, que determinó los medios y fines del procesamiento, todas esas solicitudes deben hacerse directamente a esa entidad. Honraremos y apoyaremos cualquier instrucción que nos proporcionen con respecto a tu información personal. diff --git a/db/schema.rb b/db/schema.rb index e6d131813a..1faae459f7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1650,7 +1650,6 @@ t.string "hashed_ssn" t.integer "household_excise_credit_claimed", default: 0, null: false t.integer "household_excise_credit_claimed_amt" - t.string "last_completed_step" t.datetime "last_sign_in_at" t.inet "last_sign_in_ip" t.string "locale", default: "en" @@ -1780,7 +1779,6 @@ t.integer "household_rent_amount" t.integer "household_rent_own", default: 0, null: false t.integer "household_ssi" - t.string "last_completed_step" t.datetime "last_sign_in_at" t.inet "last_sign_in_ip" t.string "locale", default: "en" diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 5da2ff5f6a..420190f0c6 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1397,7 +1397,7 @@ def index end context "after end of intake" do - let(:fake_time) { Rails.configuration.state_file_end_of_intake + 1.minute } + let(:fake_time) { Rails.configuration.state_file_end_of_new_intakes + 1.minute } it "returns false" do Timecop.freeze(fake_time) do diff --git a/spec/controllers/state_file/questions/esign_declaration_controller_spec.rb b/spec/controllers/state_file/questions/esign_declaration_controller_spec.rb index 1b50b8969e..52d26304ff 100644 --- a/spec/controllers/state_file/questions/esign_declaration_controller_spec.rb +++ b/spec/controllers/state_file/questions/esign_declaration_controller_spec.rb @@ -30,6 +30,31 @@ expect(efile_info.device_id).to eq nil expect(efile_info.intake).to eq intake end + + context "when it is after closing" do + around do |example| + Timecop.freeze(Rails.configuration.state_file_end_of_in_progress_intakes + 1.day) do + example.run + end + end + + context "when there is a submission" do + before do + create :efile_submission, data_source: intake + end + it "redirects them to the return status page" do + get :edit, params: { us_state: :ny } + expect(response).to redirect_to(ny_questions_return_status_path(us_state: "ny")) + end + end + + context "when there no submission" do + it "redirects them to the return about page" do + get :edit, params: { us_state: :ny } + expect(response).to redirect_to(root_path) + end + end + end end describe "#update" do diff --git a/spec/controllers/state_file/questions/landing_page_controller_spec.rb b/spec/controllers/state_file/questions/landing_page_controller_spec.rb index 5857f8b74d..a5afbd1cfe 100644 --- a/spec/controllers/state_file/questions/landing_page_controller_spec.rb +++ b/spec/controllers/state_file/questions/landing_page_controller_spec.rb @@ -35,6 +35,17 @@ expect(StateFileNyIntake.last.current_step).to be_nil end + context "when it is after closing" do + around do |example| + Timecop.freeze(Rails.configuration.state_file_end_of_in_progress_intakes + 1.day) do + example.run + end + end + it "does not redirect them to the about page" do + get :edit, params: { us_state: :ny } + expect(response).not_to have_http_status(:redirect) + end + end end end \ No newline at end of file diff --git a/spec/controllers/state_file/questions/submission_pdfs_controller_spec.rb b/spec/controllers/state_file/questions/submission_pdfs_controller_spec.rb index 68d7f84f96..2d768e70b5 100644 --- a/spec/controllers/state_file/questions/submission_pdfs_controller_spec.rb +++ b/spec/controllers/state_file/questions/submission_pdfs_controller_spec.rb @@ -36,6 +36,18 @@ tempfile.write(response.body) expect(filled_in_values(tempfile.path)).to match(a_hash_including("1a" => "Jerry L")) end + + context "when it is after closing" do + around do |example| + Timecop.freeze(Rails.configuration.state_file_end_of_in_progress_intakes + 1.day) do + example.run + end + end + it "does not redirect them to the about page" do + get :show, params: { us_state: "az", id: efile_submission.id } + expect(response).not_to have_http_status(:redirect) + end + end end end end \ No newline at end of file diff --git a/spec/factories/state_file_az_intakes.rb b/spec/factories/state_file_az_intakes.rb index 8ca5b6513a..572de9183b 100644 --- a/spec/factories/state_file_az_intakes.rb +++ b/spec/factories/state_file_az_intakes.rb @@ -31,7 +31,6 @@ # hashed_ssn :string # household_excise_credit_claimed :integer default("unfilled"), not null # household_excise_credit_claimed_amt :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") diff --git a/spec/factories/state_file_ny_intakes.rb b/spec/factories/state_file_ny_intakes.rb index 1cc0c0d377..533cad5237 100644 --- a/spec/factories/state_file_ny_intakes.rb +++ b/spec/factories/state_file_ny_intakes.rb @@ -35,7 +35,6 @@ # household_rent_amount :integer # household_rent_own :integer default("unfilled"), not null # household_ssi :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") diff --git a/spec/lib/pdf_filler/f13614c_pdf_spec.rb b/spec/lib/pdf_filler/f13614c_pdf_spec.rb index f40bbb5d17..ff858365c8 100644 --- a/spec/lib/pdf_filler/f13614c_pdf_spec.rb +++ b/spec/lib/pdf_filler/f13614c_pdf_spec.rb @@ -555,7 +555,7 @@ ) end - it "includes extra dependent information in the additional comments field" do + xit "includes extra dependent information in the additional comments field" do expect(intake_pdf.hash_for_pdf[additional_comments_key]).to eq(<<~COMMENT.strip) if there is another gnome living in my garden but only i have an income, does that make me head of household? Also here are some additional notes. Other income types: garden gnoming diff --git a/spec/models/state_file_az_intake_spec.rb b/spec/models/state_file_az_intake_spec.rb index 6ff4b1222f..475c6d9387 100644 --- a/spec/models/state_file_az_intake_spec.rb +++ b/spec/models/state_file_az_intake_spec.rb @@ -31,7 +31,6 @@ # hashed_ssn :string # household_excise_credit_claimed :integer default("unfilled"), not null # household_excise_credit_claimed_amt :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") diff --git a/spec/models/state_file_ny_intake_spec.rb b/spec/models/state_file_ny_intake_spec.rb index 5308520b84..c21fd17e85 100644 --- a/spec/models/state_file_ny_intake_spec.rb +++ b/spec/models/state_file_ny_intake_spec.rb @@ -35,7 +35,6 @@ # household_rent_amount :integer # household_rent_own :integer default("unfilled"), not null # household_ssi :integer -# last_completed_step :string # last_sign_in_at :datetime # last_sign_in_ip :inet # locale :string default("en") From d0295c511c7d1a2bb452bcb0522025453a921a52 Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:48:30 -0300 Subject: [PATCH 09/20] Change logic for Reminder notification (#4447) * Change logic for Reminder notification --- ...reminder_to_finish_state_return_service.rb | 27 ++++++++++--------- lib/tasks/efile.rake | 4 --- ...der_to_finish_state_return_service_spec.rb | 18 +++++-------- ..._finish_state_return_notifications_spec.rb | 2 +- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/app/services/state_file/reminder_to_finish_state_return_service.rb b/app/services/state_file/reminder_to_finish_state_return_service.rb index 27760a6e4e..e9c20bde99 100644 --- a/app/services/state_file/reminder_to_finish_state_return_service.rb +++ b/app/services/state_file/reminder_to_finish_state_return_service.rb @@ -1,21 +1,24 @@ module StateFile class ReminderToFinishStateReturnService + BATCH_SIZE = 10 + HOURS_AGO = 12 def self.run - cutoff_time_ago = 12.hours.ago - batch_size = 10 - intakes_with_no_submission = StateFileAzIntake.where("df_data_imported_at < ?", cutoff_time_ago) - .left_joins(:efile_submissions) - .where(efile_submissions: { id: nil }) - .where.not("state_file_az_intakes.message_tracker #> '{messages.state_file.finish_return}' IS NOT NULL") + cutoff_time_ago = HOURS_AGO.hours.ago + intakes_to_notify = [] - intakes_with_no_submission += StateFileNyIntake.where("df_data_imported_at < ?", cutoff_time_ago) - .left_joins(:efile_submissions) - .where(efile_submissions: { id: nil }) - .where.not("state_file_ny_intakes.message_tracker #> '{messages.state_file.finish_return}' IS NOT NULL") + ApplicationRecord::STATE_INTAKE_CLASS_NAMES.each do |base_class| + class_object = base_class.constantize + intakes_to_notify += class_object.where("#{base_class.underscore.pluralize}.created_at < ?", cutoff_time_ago) + .where.not(email_address: nil).where.not(email_address_verified_at: nil) + .where(unsubscribed_from_email: false) + .where("#{base_class.underscore.pluralize}.message_tracker #> '{messages.state_file.finish_return}' IS NULL") + end - intakes_with_no_submission.each_slice(batch_size) do |batch| + intakes_to_notify.each_slice(BATCH_SIZE) do |batch| batch.each do |intake| - StateFile::MessagingService.new(message: StateFile::AutomatedMessage::FinishReturn, intake: intake).send_message + StateFile::MessagingService.new( + message: StateFile::AutomatedMessage::FinishReturn, intake: intake + ).send_message end end end diff --git a/lib/tasks/efile.rake b/lib/tasks/efile.rake index 462b93378e..516583a8a8 100644 --- a/lib/tasks/efile.rake +++ b/lib/tasks/efile.rake @@ -23,8 +23,4 @@ namespace :efile do puts("End: Found #{EfileSubmission.in_state(:failed).count} failed submissions") exit end - - task reminder_to_finish_state_return: :environment do - StateFile::ReminderToFinishStateReturnService.run - end end diff --git a/spec/services/state_file/reminder_to_finish_state_return_service_spec.rb b/spec/services/state_file/reminder_to_finish_state_return_service_spec.rb index be48f6f88f..15c8291d58 100644 --- a/spec/services/state_file/reminder_to_finish_state_return_service_spec.rb +++ b/spec/services/state_file/reminder_to_finish_state_return_service_spec.rb @@ -11,25 +11,19 @@ allow(state_file_messaging_service).to receive(:send_message) end - context "when there is an incomplete intake with df transfer from exactly 12 hours ago" do - let!(:intake) do - create :state_file_az_intake, - df_data_imported_at: 12.hours.ago - end + context 'when there is a started intake from more than 12 hours ago' do + let!(:intake) { create :state_file_az_intake, created_at: (12.hours + 1.minute).ago, email_address: 'test@email.com', email_address_verified_at: Time.now } - it "sends a message to the email associated with the intake" do + it 'sends a message to the email associated with the intake' do StateFile::ReminderToFinishStateReturnService.run expect(StateFile::MessagingService).to have_received(:new).with(intake: intake, message: message) expect(state_file_messaging_service).to have_received(:send_message) end end - context "when there is an incomplete intake with df transfer from less than 12 hours ago" do - let(:intake) do - create :state_file_az_intake, - df_data_imported_at: (11.hours + 59.minutes).ago - end - it "does not send a message to the email associated with the intake" do + context 'when there is a started intake from less than 12 hours ago' do + let(:intake) { create :state_file_az_intake, created_at: (11.hours + 59.minutes).ago } + it 'does not send a message to the email associated with the intake' do StateFile::ReminderToFinishStateReturnService.run expect(StateFile::MessagingService).to_not have_received(:new) end diff --git a/spec/tasks/send_reminder_to_finish_state_return_notifications_spec.rb b/spec/tasks/send_reminder_to_finish_state_return_notifications_spec.rb index 880ad66cae..7d2d8f88ac 100644 --- a/spec/tasks/send_reminder_to_finish_state_return_notifications_spec.rb +++ b/spec/tasks/send_reminder_to_finish_state_return_notifications_spec.rb @@ -23,7 +23,7 @@ expect(StateFile::MessagingService).to have_received(:new).exactly(2).times end - it "doesn't send the notification to an intake that has NOT been submitted" do + it 'doesn\'t send the notification to an intake that has NOT been submitted' do expect(StateFileNyIntake.where.not(federal_submission_id: nil).count).to eq(1) end end From 00f443547daea0a8851894eed5791beeee62ef60 Mon Sep 17 00:00:00 2001 From: tofarr Date: Fri, 12 Apr 2024 10:41:25 -0600 Subject: [PATCH 10/20] Close FYST 2 #187355990 (#4480) More copy updates --- .../questions/return_status/_rejected.html.erb | 11 +++++++++-- config/locales/en.yml | 4 +--- config/locales/es.yml | 4 +--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/views/state_file/questions/return_status/_rejected.html.erb b/app/views/state_file/questions/return_status/_rejected.html.erb index c51b5a9d0c..5d7fb8eb95 100644 --- a/app/views/state_file/questions/return_status/_rejected.html.erb +++ b/app/views/state_file/questions/return_status/_rejected.html.erb @@ -51,9 +51,7 @@

    <%= t('.have_questions') %>

    <%= t('.contact_us') %>
    - <% end %> - <% if app_time.before?(Rails.configuration.state_file_end_of_in_progress_intakes) %> <% edit_return_controller = @error.correction_path.present? ? EfileError.path_to_controller(@error.correction_path) : EfileError.default_controller %> <%= link_to t('.edit_return'), edit_return_controller.to_path_helper( action: edit_return_controller.navigation_actions.first, @@ -62,3 +60,12 @@ ), class: "button button--primary button--wide spacing-above-60" %> <% end %> <% end %> + +<% if app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) %> +

    + <%= t("state_file.questions.return_status.accepted.download_title", state_name: States.name_for_key(params[:us_state].upcase)) %> +

    +

    <%= t("state_file.questions.return_status.thank_you_html", state_name: States.name_for_key(params[:us_state].upcase)) %>

    +

    <%= t("state_file.questions.return_status.accepted.feedback") %>

    +

    <%= t('state_file.questions.return_status.accepted.register_to_vote') %>

    +<% end %> \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 2b3137c286..4d1eba1255 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2576,9 +2576,7 @@ en: reject_code: 'Reject Code:' reject_desc: 'Reject Description:' title: Unfortunately, your 2023 %{state_name} state tax return was rejected - thank_you_html: | - Thank you for filing your return with FileYourStateTaxes, built in partnership with the state of %{state_name} to integrate with the IRS Direct File tool.

    - Filed your state taxes with us? You can download a copy of your state return until December 31, 2024. + thank_you_html: Thank you for filing your return with FileYourStateTaxes, built in partnership with the state of %{state_name} to integrate with the IRS Direct File tool. review: w2: W-2 state and local tax information shared: diff --git a/config/locales/es.yml b/config/locales/es.yml index cda1e50e2e..016118f6cf 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2556,9 +2556,7 @@ es: reject_code: 'Código de Rechazo:' reject_desc: 'Descripción del Rechazo:' title: Lamentablemente, tu declaración de impuestos del estado de %{state_name} para el año 2023 fue rechazada - thank_you_html: | - Gracias por presentar su declaración con FileYourStateTaxes, creado en asociación con el estado de %{state_name} para integrarse con la herramienta Direct File del IRS.

    - ¿Presentó sus impuestos estatales con nosotros? Puede descargar una copia de su declaración estatal hasta el 31 de diciembre de 2024. + thank_you_html: Gracias por presentar su declaración con FileYourStateTaxes, creado en asociación con el estado de %{state_name} para integrarse con la herramienta Direct File del IRS. review: w2: Información fiscal estatal y local W-2 shared: From 71c791c7b5a410ab518678c7674d4d7994127707 Mon Sep 17 00:00:00 2001 From: Tahsina Islam <79296603+tahsinaislam@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:29:00 -0500 Subject: [PATCH 11/20] truncate 3rd party designee name (#4462) --- app/lib/submission_builder/ty2022/states/ny/documents/it201.rb | 2 +- spec/fixtures/files/fed_return_albert_v2_ny.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/submission_builder/ty2022/states/ny/documents/it201.rb b/app/lib/submission_builder/ty2022/states/ny/documents/it201.rb index 57812e6846..23b07f1e3f 100644 --- a/app/lib/submission_builder/ty2022/states/ny/documents/it201.rb +++ b/app/lib/submission_builder/ty2022/states/ny/documents/it201.rb @@ -79,7 +79,7 @@ def document add_non_zero_claimed_value(xml, :RFND_B4_EDU_AMT, :IT201_LINE_78) add_non_zero_claimed_value(xml, :RFND_AMT, :IT201_LINE_78B) if @submission.data_source.confirmed_third_party_designee_yes? - xml.THRD_PRTY_NAME claimed: intake.direct_file_data.third_party_designee_name.strip.gsub(/\s+/, ' ') if @submission.data_source.direct_file_data.third_party_designee_name.present? + xml.THRD_PRTY_NAME claimed: truncate(intake.direct_file_data.third_party_designee_name, 29) if @submission.data_source.direct_file_data.third_party_designee_name.present? xml.THRD_PRTY_PH_NMBR claimed: intake.direct_file_data.third_party_designee_phone_number.strip.gsub(/\s+/, ' ') if @submission.data_source.direct_file_data.third_party_designee_phone_number.present? end xml.PR_SGN_IND claimed: 1 diff --git a/spec/fixtures/files/fed_return_albert_v2_ny.xml b/spec/fixtures/files/fed_return_albert_v2_ny.xml index 9eaf2b9765..e61b3bbe0b 100644 --- a/spec/fixtures/files/fed_return_albert_v2_ny.xml +++ b/spec/fixtures/files/fed_return_albert_v2_ny.xml @@ -51,7 +51,7 @@ 234523452345 0 true - Desig Nee + Third Party Desig Nee Very Very Long Name 7185554321 25252 Inventor From cccdde05d2fc2736ff8abdb22bfd3f9f14488c7a Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:34:45 -0300 Subject: [PATCH 12/20] Pre-Deadline notification (#4469) * Pre Deadline notification --- .../pre_deadline_reminder.rb | 28 ++++++++ .../send_pre_deadline_reminder_service.rb | 38 +++++++++++ config/locales/en.yml | 7 ++ config/locales/es.yml | 7 ++ crontab | 1 + lib/tasks/state_file.rake | 5 ++ ...send_pre_deadline_reminder_service_spec.rb | 66 +++++++++++++++++++ 7 files changed, 152 insertions(+) create mode 100644 app/models/state_file/automated_message/pre_deadline_reminder.rb create mode 100644 app/services/state_file/send_pre_deadline_reminder_service.rb create mode 100644 spec/tasks/send_pre_deadline_reminder_service_spec.rb diff --git a/app/models/state_file/automated_message/pre_deadline_reminder.rb b/app/models/state_file/automated_message/pre_deadline_reminder.rb new file mode 100644 index 0000000000..ee2f5225d8 --- /dev/null +++ b/app/models/state_file/automated_message/pre_deadline_reminder.rb @@ -0,0 +1,28 @@ +module StateFile::AutomatedMessage + class PreDeadlineReminder < BaseAutomatedMessage + + def self.name + 'messages.state_file.pre_deadline_reminder'.freeze + end + + def self.after_transition_notification? + false + end + + def self.send_only_once? + true + end + + def sms_body(**args) + I18n.t("messages.state_file.pre_deadline_reminder.sms", **args) + end + + def email_subject(**args) + I18n.t("messages.state_file.pre_deadline_reminder.email.subject", **args) + end + + def email_body(**args) + I18n.t("messages.state_file.pre_deadline_reminder.email.body", **args) + end + end +end diff --git a/app/services/state_file/send_pre_deadline_reminder_service.rb b/app/services/state_file/send_pre_deadline_reminder_service.rb new file mode 100644 index 0000000000..9cc746d25b --- /dev/null +++ b/app/services/state_file/send_pre_deadline_reminder_service.rb @@ -0,0 +1,38 @@ +module StateFile + class SendPreDeadlineReminderService + BATCH_SIZE = 10 + HOURS_AGO = 24 + + def self.run + cutoff_time_ago = HOURS_AGO.hours.ago + intakes_to_notify = [] + + ApplicationRecord::STATE_INTAKE_CLASS_NAMES.each do |base_class| + class_object = base_class.constantize + intakes_to_notify += class_object.left_joins(:efile_submissions) + .where(efile_submissions: { id: nil }) + .where.not(email_address: nil) + .where.not(email_address_verified_at: nil) + .where(unsubscribed_from_email: false) + .where("#{base_class.underscore.pluralize}.message_tracker #> '{messages.state_file.pre_deadline_reminder}' IS NULL") + .select do |intake| + if intake.message_tracker.present? && intake.message_tracker["messages.state_file.finish_return"] + finish_return_msg_sent_time = Date.parse(intake.message_tracker["messages.state_file.finish_return"]) + finish_return_msg_sent_time < cutoff_time_ago + else + true + end + end + end + + intakes_to_notify.each_slice(BATCH_SIZE) do |batch| + batch.each do |intake| + StateFile::MessagingService.new( + message: StateFile::AutomatedMessage::PreDeadlineReminder, + intake: intake + ).send_message + end + end + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 4d1eba1255..bd9fe0a224 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1610,6 +1610,13 @@ en: Hello - Thank you for using FileYourStateTaxes! Any issues you may have experienced while using our tool have now been resolved. To continue filing your state taxes, log in here: %{login_link} Questions? Reply to this text. + pre_deadline_reminder: + email: + body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" + subject: Don't forget to finish your state tax return by April 15! + sms: | + Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes by April 15 in order to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. + Need help? Reply to this text. rejected: email: body: | diff --git a/config/locales/es.yml b/config/locales/es.yml index 016118f6cf..5c0d2dd3fa 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1584,6 +1584,13 @@ es: Hola, ¡Gracias por usar FileYourStateTaxes! Todos los problemas que hayas experimentado al usar nuestra herramienta han sido resueltos. Para continuar presentando tus impuestos estatales, inicia sesión aquí: %{login_link} ¿Preguntas? Responde a este mensaje. + pre_deadline_reminder: + email: + body: "Hola %{primary_first_name}, \n\nAún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía de %{state_name}. \n\nVe a fileyourstatetaxes.org/en/us/login-options para terminarlos ahora.\n\n¿Necesitas ayuda? Responde a este mensaje de texto.\n\nAtentamente,\nEl equipo de FileYourStateTaxes\n" + subject: "¡No olvides terminar tu declaración de impuestos estatales antes del 15 de abril!" + sms: | + Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/en/us/login-options para terminarlos ahora. + ¿Necesitas ayuda? Responde a este mensaje de texto. rejected: email: body: "Hola %{primary_first_name},\n\nLamentablemente, %{state_name} rechazó tu declaración de impuestos estatales. ¡No te preocupes! Podemos ayudarte a corregirla y volver a enviarla en %{return_status_link}.\n\n¿Preguntas? Responde a este correo electrónico.\n\nSaludos, \nEl equipo de FileYourStateTaxes\n" diff --git a/crontab b/crontab index 28d96d9bb5..efcd66e1ac 100644 --- a/crontab +++ b/crontab @@ -8,3 +8,4 @@ */5 * * * * bundle exec rake worker_heartbeat:perform */10 * * * * bundle exec rake efile:poll_and_get_acknowledgments 0 17 * * * bundle exec rake state_file:reminder_to_finish_state_return +0 21 13 4 * bundle exec rake state_file:pre_deadline_reminder diff --git a/lib/tasks/state_file.rake b/lib/tasks/state_file.rake index 6d860b2331..1819e0060c 100644 --- a/lib/tasks/state_file.rake +++ b/lib/tasks/state_file.rake @@ -4,4 +4,9 @@ namespace :state_file do task reminder_to_finish_state_return: :environment do StateFile::ReminderToFinishStateReturnService.run end + + task pre_deadline_reminder: :environment do + return unless DateTime.now.year == 2024 + StateFile::SendPreDeadlineReminderService.run + end end diff --git a/spec/tasks/send_pre_deadline_reminder_service_spec.rb b/spec/tasks/send_pre_deadline_reminder_service_spec.rb new file mode 100644 index 0000000000..659e3c51ee --- /dev/null +++ b/spec/tasks/send_pre_deadline_reminder_service_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +require 'rails_helper' + +describe 'state_file:pre_deadline_reminder' do + before(:context) do + Rails.application.load_tasks + end + + context 'Sends the notification to all state-filing' do + let!(:az_intake) { create :state_file_az_intake, email_address: 'test@example.com', email_address_verified_at: 1.minute.ago, created_at: 25.hours.ago } + let!(:ny_intake) { create :state_file_ny_intake, email_address: 'test+01@example.com', email_address_verified_at: 1.minute.ago, created_at: 25.hours.ago } + let!(:submitted_intake) { create :state_file_ny_intake, email_address: 'test+01@example.com', email_address_verified_at: 1.minute.ago } + let!(:efile_submission) { create :efile_submission, :for_state, data_source: submitted_intake } + + it 'intakes without submissions & without reminders' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:pre_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(2).times + end + end + + context 'Sends the notification to intakes that have' do + let!(:intake_with_reminder) { + create :state_file_az_intake, email_address: "test@example.com", + email_address_verified_at: 1.hour.ago, created_at: 25.hours.ago + } + + before do + allow_any_instance_of(StateFileNyIntake).to receive(:message_tracker).and_return((Time.now - 25.hours).utc.to_s) + end + + it 'the reminder_notification sent more than 24 hours ago' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:pre_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(1).times + end + end + + context 'Does NOT send the notification to' do + let!(:intake_with_reminder) { + create :state_file_az_intake, email_address: "test@example.com", + email_address_verified_at: 1.hour.ago, created_at: 25.hours.ago + } + + before do + allow_any_instance_of(StateFileAzIntake).to receive(:message_tracker).and_return( + { "messages.state_file.finish_return" => (Time.now - 2.hours).utc.to_s } + ) + end + + it 'intakes that have the reminder_notification sent less than 24 hours ago' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:pre_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(0).times + end + end +end From d17e1504fc7dfc65d6132aa4e2a4981fb13d121c Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:59:15 -0300 Subject: [PATCH 13/20] Post-Deadline notification (#4475) * Post-Deadline notification --- .../post_deadline_reminder.rb | 28 ++++++++ .../send_post_deadline_reminder_service.rb | 38 +++++++++++ config/locales/en.yml | 7 ++ config/locales/es.yml | 14 ++++ crontab | 1 + lib/tasks/state_file.rake | 7 +- ...end_post_deadline_reminder_service_spec.rb | 66 +++++++++++++++++++ 7 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 app/models/state_file/automated_message/post_deadline_reminder.rb create mode 100644 app/services/state_file/send_post_deadline_reminder_service.rb create mode 100644 spec/tasks/send_post_deadline_reminder_service_spec.rb diff --git a/app/models/state_file/automated_message/post_deadline_reminder.rb b/app/models/state_file/automated_message/post_deadline_reminder.rb new file mode 100644 index 0000000000..6923292d46 --- /dev/null +++ b/app/models/state_file/automated_message/post_deadline_reminder.rb @@ -0,0 +1,28 @@ +module StateFile::AutomatedMessage + class PostDeadlineReminder < BaseAutomatedMessage + + def self.name + 'messages.state_file.post_deadline_reminder'.freeze + end + + def self.after_transition_notification? + false + end + + def self.send_only_once? + true + end + + def sms_body(**args) + I18n.t("messages.state_file.post_deadline_reminder.sms", **args) + end + + def email_subject(**args) + I18n.t("messages.state_file.post_deadline_reminder.email.subject", **args) + end + + def email_body(**args) + I18n.t("messages.state_file.post_deadline_reminder.email.body", **args) + end + end +end diff --git a/app/services/state_file/send_post_deadline_reminder_service.rb b/app/services/state_file/send_post_deadline_reminder_service.rb new file mode 100644 index 0000000000..902ea9e8f4 --- /dev/null +++ b/app/services/state_file/send_post_deadline_reminder_service.rb @@ -0,0 +1,38 @@ +module StateFile + class SendPostDeadlineReminderService + BATCH_SIZE = 10 + HOURS_AGO = 24 + + def self.run + cutoff_time_ago = HOURS_AGO.hours.ago + intakes_to_notify = [] + + ApplicationRecord::STATE_INTAKE_CLASS_NAMES.each do |base_class| + class_object = base_class.constantize + intakes_to_notify += class_object.left_joins(:efile_submissions) + .where(efile_submissions: { id: nil }) + .where.not(email_address: nil) + .where.not(email_address_verified_at: nil) + .where(unsubscribed_from_email: false) + .where("#{base_class.underscore.pluralize}.message_tracker #> '{messages.state_file.post_deadline_reminder}' IS NULL") + .select do |intake| + if intake.message_tracker.present? && intake.message_tracker["messages.state_file.finish_return"] + finish_return_msg_sent_time = Date.parse(intake.message_tracker["messages.state_file.finish_return"]) + finish_return_msg_sent_time < cutoff_time_ago + else + true + end + end + end + + intakes_to_notify.each_slice(BATCH_SIZE) do |batch| + batch.each do |intake| + StateFile::MessagingService.new( + message: StateFile::AutomatedMessage::PostDeadlineReminder, + intake: intake + ).send_message + end + end + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index bd9fe0a224..919cf76507 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1610,6 +1610,13 @@ en: Hello - Thank you for using FileYourStateTaxes! Any issues you may have experienced while using our tool have now been resolved. To continue filing your state taxes, log in here: %{login_link} Questions? Reply to this text. + post_deadline_reminder: + email: + body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" + subject: Make sure to finish filing your state taxes as soon as possible. + sms: | + Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes by April 15 in order to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. + Need help? Reply to this text. pre_deadline_reminder: email: body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" diff --git a/config/locales/es.yml b/config/locales/es.yml index 5c0d2dd3fa..abcc0dbd29 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1584,6 +1584,20 @@ es: Hola, ¡Gracias por usar FileYourStateTaxes! Todos los problemas que hayas experimentado al usar nuestra herramienta han sido resueltos. Para continuar presentando tus impuestos estatales, inicia sesión aquí: %{login_link} ¿Preguntas? Responde a este mensaje. + post_deadline_reminder: + email: + body: | + Hola %{primary_first_name}, + Aún no has presentado tu declaración de impuestos estatales. Como recordatorio, los impuestos estatales vencieron el 15 de abril. Asegúrate de presentar tu declaración de impuestos estatales antes del 15 de abril para evitar multas por presentación tardía en %{state_name}. + FileYourStateTaxes cerrará sus servicios para el año el 25 de abril. + Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. + ¿Necesitas ayuda? Responde a este correo electrónico. + Saludos, + El equipo de FileYourStateTaxes + subject: "¡No olvides terminar de presentar tu declaración de impuestos estatales lo antes posible!" + sms: | + Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. FileYourStateTaxes cerrará sus servicios para el año el 25 de abril. + ¿Necesitas ayuda? Responde a este mensaje de texto. pre_deadline_reminder: email: body: "Hola %{primary_first_name}, \n\nAún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía de %{state_name}. \n\nVe a fileyourstatetaxes.org/en/us/login-options para terminarlos ahora.\n\n¿Necesitas ayuda? Responde a este mensaje de texto.\n\nAtentamente,\nEl equipo de FileYourStateTaxes\n" diff --git a/crontab b/crontab index efcd66e1ac..4972aef8a4 100644 --- a/crontab +++ b/crontab @@ -9,3 +9,4 @@ */10 * * * * bundle exec rake efile:poll_and_get_acknowledgments 0 17 * * * bundle exec rake state_file:reminder_to_finish_state_return 0 21 13 4 * bundle exec rake state_file:pre_deadline_reminder +0 21 16 4 * bundle exec rake state_file:post_deadline_reminder diff --git a/lib/tasks/state_file.rake b/lib/tasks/state_file.rake index 1819e0060c..1fbaaed022 100644 --- a/lib/tasks/state_file.rake +++ b/lib/tasks/state_file.rake @@ -4,9 +4,14 @@ namespace :state_file do task reminder_to_finish_state_return: :environment do StateFile::ReminderToFinishStateReturnService.run end - + task pre_deadline_reminder: :environment do return unless DateTime.now.year == 2024 StateFile::SendPreDeadlineReminderService.run end + + task post_deadline_reminder: :environment do + return unless DateTime.now.year == 2024 + StateFile::SendPostDeadlineReminderService.run + end end diff --git a/spec/tasks/send_post_deadline_reminder_service_spec.rb b/spec/tasks/send_post_deadline_reminder_service_spec.rb new file mode 100644 index 0000000000..dec53c50d0 --- /dev/null +++ b/spec/tasks/send_post_deadline_reminder_service_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +require 'rails_helper' + +describe 'state_file:post_deadline_reminder' do + before(:context) do + Rails.application.load_tasks + end + + context 'Sends the notification to all state-filing' do + let!(:az_intake) { create :state_file_az_intake, email_address: 'test@example.com', email_address_verified_at: 1.minute.ago, created_at: 25.hours.ago } + let!(:ny_intake) { create :state_file_ny_intake, email_address: 'test+01@example.com', email_address_verified_at: 1.minute.ago, created_at: 25.hours.ago } + let!(:submitted_intake) { create :state_file_ny_intake, email_address: 'test+01@example.com', email_address_verified_at: 1.minute.ago } + let!(:efile_submission) { create :efile_submission, :for_state, data_source: submitted_intake } + + it 'intakes without submissions & without reminders' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:post_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(2).times + end + end + + context 'Sends the notification to intakes that have' do + let!(:intake_with_reminder) { + create :state_file_az_intake, email_address: "test@example.com", + email_address_verified_at: 1.hour.ago, created_at: 25.hours.ago + } + + before do + allow_any_instance_of(StateFileNyIntake).to receive(:message_tracker).and_return((Time.now - 25.hours).utc.to_s) + end + + it 'the reminder_notification sent more than 24 hours ago' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:post_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(1).times + end + end + + context 'Does NOT send the notification to' do + let!(:intake_with_reminder) { + create :state_file_az_intake, email_address: "test@example.com", + email_address_verified_at: 1.hour.ago, created_at: 25.hours.ago + } + + before do + allow_any_instance_of(StateFileAzIntake).to receive(:message_tracker).and_return( + { "messages.state_file.finish_return" => (Time.now - 2.hours).utc.to_s } + ) + end + + it 'intakes that have the reminder_notification sent less than 24 hours ago' do + messaging_service = spy('StateFile::MessagingService') + allow(StateFile::MessagingService).to receive(:new).and_return(messaging_service) + + Rake::Task['state_file:post_deadline_reminder'].execute + + expect(StateFile::MessagingService).to have_received(:new).exactly(0).times + end + end +end From 2f2a865af242d89c8ba6f2b7a91c0f81d094ee5b Mon Sep 17 00:00:00 2001 From: tofarr Date: Fri, 12 Apr 2024 12:20:08 -0600 Subject: [PATCH 14/20] Always showing download button after April 25 #187355990 (#4481) * Always showing download button after April 25 * Invert for simplicity --- app/views/state_file/questions/return_status/edit.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/state_file/questions/return_status/edit.html.erb b/app/views/state_file/questions/return_status/edit.html.erb index 9611c9d029..b0c2e8c92f 100644 --- a/app/views/state_file/questions/return_status/edit.html.erb +++ b/app/views/state_file/questions/return_status/edit.html.erb @@ -2,7 +2,7 @@ <% content_for :card do %> <%= render @return_status %> - <% unless @error&.auto_wait %> + <% if !@error&.auto_wait || app_time.after?(Rails.configuration.state_file_end_of_in_progress_intakes) %> <%= link_to t('.download_state_return_pdf'), StateFile::Questions::SubmissionPdfsController.to_path_helper(us_state: params[:us_state], action: :show, id: EfileSubmission.where(data_source: current_intake).last), class: "button button--primary button--wide spacing-above-60" %> <% end %> From fe6767482db922b72e9162d620a2ddc475a96fae Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:51:23 -0300 Subject: [PATCH 15/20] Fix pre&post deadline notification copy (#4482) --- config/locales/en.yml | 4 ++-- config/locales/es.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 919cf76507..6b5820c8d6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1612,10 +1612,10 @@ en: Questions? Reply to this text. post_deadline_reminder: email: - body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" + body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nFileYourStateTaxes will be closing its services for the year on April 25. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" subject: Make sure to finish filing your state taxes as soon as possible. sms: | - Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes by April 15 in order to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. + Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes as soon as possible to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. FileYourStateTaxes will be closing its services for the year on April 25. Need help? Reply to this text. pre_deadline_reminder: email: diff --git a/config/locales/es.yml b/config/locales/es.yml index abcc0dbd29..e9bc3978ff 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1596,7 +1596,7 @@ es: El equipo de FileYourStateTaxes subject: "¡No olvides terminar de presentar tu declaración de impuestos estatales lo antes posible!" sms: | - Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. FileYourStateTaxes cerrará sus servicios para el año el 25 de abril. + Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales lo antes posible para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. FileYourStateTaxes cerrará sus servicios para el año el 25 de abril. ¿Necesitas ayuda? Responde a este mensaje de texto. pre_deadline_reminder: email: From bcf784589d70f5f630351e5fdb56f474ffd528c4 Mon Sep 17 00:00:00 2001 From: em barnard-shao <49880002+embarnard@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:06:00 -0400 Subject: [PATCH 16/20] Update GYR banner for closing season (#4473) --- app/controllers/application_controller.rb | 5 +- .../questions/final_info_controller.rb | 4 +- .../successful_submission_online_intake.rb | 20 +++----- app/views/shared/_homepage_banner.html.erb | 10 +++- config/application.rb | 4 +- config/locales/en.yml | 5 +- config/locales/es.yml | 1 + .../application_controller_spec.rb | 3 +- .../questions/final_info_controller_spec.rb | 46 +++++++++++++++---- spec/features/visit_home_page_spec.rb | 25 +++++----- .../replacement_parameters_service_spec.rb | 9 ++-- 11 files changed, 84 insertions(+), 48 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ded1b93b13..5e54f5ed94 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -337,7 +337,10 @@ def homepage_banner if app_time <= Rails.configuration.tax_deadline && open_for_gyr_intake? # after open intake, before tax_deadline :open_intake - elsif app_time.between?(Rails.configuration.tax_deadline, Rails.configuration.end_of_in_progress_intake) + elsif app_time.between?(Rails.configuration.tax_deadline, Rails.configuration.end_of_intake) + # after tax_deadline, before end_of_intake + :open_intake_post_tax_deadline + elsif app_time.between?(Rails.configuration.end_of_intake, Rails.configuration.end_of_in_progress_intake) # after tax_deadline, before end_of_in_progress_intake :in_progress_intake_only elsif app_time.between?(Rails.configuration.end_of_in_progress_intake, Rails.configuration.end_of_login) diff --git a/app/controllers/questions/final_info_controller.rb b/app/controllers/questions/final_info_controller.rb index 30a58916a8..4b5db0c0fb 100644 --- a/app/controllers/questions/final_info_controller.rb +++ b/app/controllers/questions/final_info_controller.rb @@ -24,11 +24,13 @@ def tracking_data def send_confirmation_message @client = current_intake.client + doc_date = app_time.before?(Rails.configuration.tax_deadline) ? DateTime.parse('2024-04-01') : Rails.configuration.end_of_docs.to_date ClientMessagingService.send_system_message_to_all_opted_in_contact_methods( client: current_intake.client, message: AutomatedMessage::SuccessfulSubmissionOnlineIntake, - locale: I18n.locale + locale: I18n.locale, + body_args: { end_of_docs_date: I18n.l(doc_date, format: :medium, locale: I18n.locale, default: "%B %-d") } ) end end diff --git a/app/models/automated_message/successful_submission_online_intake.rb b/app/models/automated_message/successful_submission_online_intake.rb index 6eceb301ae..0ccbd91441 100644 --- a/app/models/automated_message/successful_submission_online_intake.rb +++ b/app/models/automated_message/successful_submission_online_intake.rb @@ -4,24 +4,16 @@ def self.name 'messages.successful_submission_online_intake'.freeze end - def sms_body(**args) - I18n.t("messages.successful_submission_online_intake.sms", **args.merge(docs_day_params)) + def sms_body(locale: nil, body_args: {}) + I18n.t("messages.successful_submission_online_intake.sms", locale: locale, **body_args) end - def email_subject(**args) - I18n.t("messages.successful_submission_online_intake.email.subject", **args) + def email_subject(locale: nil, body_args: {}) + I18n.t("messages.successful_submission_online_intake.email.subject", locale: locale, **body_args) end - def email_body(**args) - I18n.t("messages.successful_submission_online_intake.email.body", **args.merge(docs_day_params)) - end - - private - - def docs_day_params - { - end_of_docs_date: I18n.l(Rails.configuration.end_of_docs.to_date, format: :medium, locale: I18n.locale) - } + def email_body(locale: nil, body_args: {}) + I18n.t("messages.successful_submission_online_intake.email.body", locale: locale, **body_args) end end end diff --git a/app/views/shared/_homepage_banner.html.erb b/app/views/shared/_homepage_banner.html.erb index a84f749320..98798b8589 100644 --- a/app/views/shared/_homepage_banner.html.erb +++ b/app/views/shared/_homepage_banner.html.erb @@ -3,12 +3,18 @@
    + <% end_of_intake = Rails.configuration.end_of_intake %> + <% end_of_doc_submission = Rails.configuration.end_of_docs %> + <% case banner %> <% when :open_intake %> <%= t('views.shared.document_deadline_warning.body_html') %> + <% when :open_intake_post_tax_deadline %> + <%= t('views.public_pages.home.open_intake_post_tax_deadline_banner', + end_of_intake: I18n.l(end_of_intake.to_date, format: :medium, locale: locale), + end_of_docs: I18n.l(end_of_doc_submission.to_date, format: :medium, locale: locale) + ) %> <% when :in_progress_intake_only %> - <% end_of_intake = Rails.configuration.end_of_intake %> - <% end_of_doc_submission = Rails.configuration.end_of_docs %> <%= t("views.shared.environment_warning.off_season_filing", end_of_intake_date: I18n.l(end_of_intake.to_date, format: :medium, locale: locale), end_of_docs_date: I18n.l(end_of_doc_submission.to_date, format: :medium, locale: locale)) %> diff --git a/config/application.rb b/config/application.rb index e5f37d5036..6f5071ff32 100644 --- a/config/application.rb +++ b/config/application.rb @@ -90,9 +90,9 @@ class Application < Rails::Application # GetYourRefund config.start_of_unique_links_only_intake = Time.find_zone('America/Los_Angeles').parse('2024-01-24 12:00:00') config.start_of_open_intake = Time.find_zone('America/Los_Angeles').parse('2024-01-31 09:59:59') - config.tax_deadline = Time.find_zone('America/New_York').parse('2024-04-18 23:59:59') + config.tax_deadline = Time.find_zone('America/New_York').parse('2024-04-15 23:59:59') config.end_of_intake = Time.find_zone('America/New_York').parse('2024-10-01 23:59:59') - config.end_of_docs = Time.find_zone('America/New_York').parse('2024-4-01 23:59:59') + config.end_of_docs = Time.find_zone('America/New_York').parse('2024-10-08 23:59:59') config.end_of_in_progress_intake = Time.find_zone('America/New_York').parse('2024-10-16 23:59:59') config.end_of_login = Time.find_zone('America/New_York').parse('2024-10-23 23:59:00') diff --git a/config/locales/en.yml b/config/locales/en.yml index 6b5820c8d6..a5a6b9236f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1359,9 +1359,9 @@ en: messages: closing_soon: email: - body: We are closing GetYourRefund for the tax season on October 16th. We will need all required documentation by October 9th; otherwise, we will not be able to prepare your taxes. Please login and respond by October 9th. <> If we do not hear from you by then, we will close out your case and you can file with us next year. + body: We are closing GetYourRefund for the tax season on October 16th. We will need all required documentation by October 8th; otherwise, we will not be able to prepare your taxes. Please login and respond by October 8th. <> If we do not hear from you by then, we will close out your case and you can file with us next year. subject: GetYourRefund closing soon -- take action now if you need tax services - sms: We are closing GetYourRefund for the tax season on October 16th. We will need all required documentation by October 9th; otherwise, we will not be able to prepare your taxes. Please login and respond by October 9th. <> If we do not hear from you by then, we will close out your case and you can file with us next year. + sms: We are closing GetYourRefund for the tax season on October 16th. We will need all required documentation by October 8th; otherwise, we will not be able to prepare your taxes. Please login and respond by October 8th. <> If we do not hear from you by then, we will close out your case and you can file with us next year. contact_info_change: email: body: | @@ -4521,6 +4521,7 @@ en: faq_cta: Read our FAQ header: 'Common Tax Questions:' header: Free tax filing, made simple. + open_intake_post_tax_deadline_banner: Get started with GetYourRefund by %{end_of_intake} if you want to file with us in 2024. If your return is in progress, sign in and submit your documents by %{end_of_docs}. security: body: In accordance with national IRS guidelines, all Personally Identifiable Information (PII) provided by taxpayers to volunteers must be protected. We keep your information safe and secure. header: Committed to keeping your information secure. diff --git a/config/locales/es.yml b/config/locales/es.yml index e9bc3978ff..67b92f607e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -4520,6 +4520,7 @@ es: faq_cta: Lee nuestras preguntas frecuentes header: 'Preguntas frecuentes:' header: Declare sus impuestos sin costo. ¡Es sencillo! + open_intake_post_tax_deadline_banner: Comience con GetYourRefund antes del %{end_of_intake} si desea presentar su declaración con nosotros en 2024. Si su declaración está en progreso, inicie sesión y envíe sus documentos antes del %{end_of_docs}. security: body: De acuerdo con las pautas nacionales del IRS, toda la información de identificación personal (PII, por sus siglas en inglés) que los contribuyentes proporcionan a los voluntarios debe estar protegida. Mantenemos su información segura y protegida. header: Nos comprometemos a mantener la seguridad de su información. diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 420190f0c6..ccea5eb60a 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1216,7 +1216,7 @@ def index context "after tax deadline and before end-of-in-progress-intakes" do before do - allow(Rails.application.config).to receive(:tax_deadline).and_return(past) + allow(Rails.application.config).to receive(:end_of_intake).and_return(past) allow(Rails.application.config).to receive(:end_of_in_progress_intake).and_return(future) end @@ -1240,6 +1240,7 @@ def index context "after end of login" do before do allow(Rails.application.config).to receive(:end_of_intake).and_return(past) + allow(Rails.application.config).to receive(:end_of_in_progress_intake).and_return(past) allow(Rails.application.config).to receive(:end_of_login).and_return(past) end diff --git a/spec/controllers/questions/final_info_controller_spec.rb b/spec/controllers/questions/final_info_controller_spec.rb index a7eba1b8b6..cace87a655 100644 --- a/spec/controllers/questions/final_info_controller_spec.rb +++ b/spec/controllers/questions/final_info_controller_spec.rb @@ -36,24 +36,52 @@ end context "with english locale" do - it "sends a success email in the correct language" do - post :update, params: params - expect(ClientMessagingService).to have_received(:send_system_message_to_all_opted_in_contact_methods).with( - client: client, - message: AutomatedMessage::SuccessfulSubmissionOnlineIntake, - locale: :en - ) + let(:fake_time) { Rails.configuration.tax_deadline + 1.minute } + + context "after the tax deadline" do + it "sends a success email with the October 8th date" do + Timecop.freeze(fake_time) do + post :update, params: params + end + expect(ClientMessagingService).to have_received(:send_system_message_to_all_opted_in_contact_methods).with( + client: client, + message: AutomatedMessage::SuccessfulSubmissionOnlineIntake, + locale: :en, + body_args: { end_of_docs_date: "October 8th"} + ) + end + end + + context "before the tax deadline" do + let(:fake_time) { Rails.configuration.tax_deadline - 1.minute } + + it "sends a success email with the April 1st date" do + Timecop.freeze(fake_time) do + post :update, params: params + end + expect(ClientMessagingService).to have_received(:send_system_message_to_all_opted_in_contact_methods).with( + client: client, + message: AutomatedMessage::SuccessfulSubmissionOnlineIntake, + locale: :en, + body_args: { end_of_docs_date: "April 1"} + ) + end end end context "with spanish locale" do + let(:fake_time) { Rails.configuration.tax_deadline + 1.minute } + it "sends a success email in the correct language" do - post :update, params: params.merge(locale: "es") + Timecop.freeze(fake_time) do + post :update, params: params.merge(locale: "es") + end expect(ClientMessagingService).to have_received(:send_system_message_to_all_opted_in_contact_methods).with( client: client, message: AutomatedMessage::SuccessfulSubmissionOnlineIntake, - locale: :es + locale: :es, + body_args: { end_of_docs_date: "8 de octubre"} ) end end diff --git a/spec/features/visit_home_page_spec.rb b/spec/features/visit_home_page_spec.rb index 9e76cf2bb7..be0f1e5270 100644 --- a/spec/features/visit_home_page_spec.rb +++ b/spec/features/visit_home_page_spec.rb @@ -46,26 +46,27 @@ end before do - allow(Rails.configuration).to receive(:start_of_open_intake).and_return(DateTime.new(2023, 1, 31)) - allow(Rails.configuration).to receive(:tax_deadline).and_return(DateTime.new(2023, 4, 18)) - allow(Rails.configuration).to receive(:end_of_intake).and_return(DateTime.new(2023, 10, 1)) - allow(Rails.configuration).to receive(:end_of_in_progress_intake).and_return(DateTime.new(2023, 10, 16)) - allow(Rails.configuration).to receive(:end_of_login).and_return(DateTime.new(2023, 10, 23)) + allow(Rails.configuration).to receive(:start_of_open_intake).and_return(DateTime.new(2024, 1, 31)) + allow(Rails.configuration).to receive(:tax_deadline).and_return(DateTime.new(2024, 4, 15)) + allow(Rails.configuration).to receive(:end_of_intake).and_return(DateTime.new(2024, 10, 1)) + allow(Rails.configuration).to receive(:end_of_docs).and_return(DateTime.new(2024, 10, 8)) + allow(Rails.configuration).to receive(:end_of_in_progress_intake).and_return(DateTime.new(2024, 10, 16)) + allow(Rails.configuration).to receive(:end_of_login).and_return(DateTime.new(2024, 10, 23)) end context "when closed for new intakes" do - let(:current_time) { DateTime.new(2023, 10, 2) } + let(:current_time) { DateTime.new(2024, 10, 2) } scenario "shows the off season banner" do visit "/" - expect(page).to have_text "We are unable to accept new clients after October 1st. If your return is in progress, log in and submit your documents by April 1st in order to file by the deadline." + expect(page).to have_text "We are unable to accept new clients after October 1st. If your return is in progress, log in and submit your documents by October 8th in order to file by the deadline." expect(page.all(:css, '.slab--banner').length).to eq 1 end end context "when open for filing and before the tax deadline" do - let(:current_time) { DateTime.new(2023, 4, 1) } + let(:current_time) { DateTime.new(2024, 4, 1) } scenario "shows the document deadline banner" do visit "/" @@ -76,19 +77,19 @@ end context "when open for filing and after the deadline" do - let(:current_time) { DateTime.new(2023, 4, 20) } + let(:current_time) { DateTime.new(2024, 4, 20) } - scenario "shows the banner with closing date and document submission deadline" do + scenario "shows the open_intake_post_tax_deadline_banner banner" do visit "/" - expect(page).to have_text "We are unable to accept new clients after October 1st. If your return is in progress, log in and submit your documents by April 1st in order to file by the deadline." + expect(page).to have_text "Get started with GetYourRefund by October 1st if you want to file with us in 2024. If your return is in progress, sign in and submit your documents by October 8th" expect(page.all(:css, '.slab--banner').length).to eq 1 end scenario "shows the banner with closing date and document submission deadline with correctly formatted spanish dates" do visit "/es" - expect(page).to have_text "No podemos aceptar nuevos clientes después de 1 de octubre. Si su declaración está en progreso, inicie sesión y envíe sus documentos antes de 1 de abril para presentarla antes de la fecha límite." + expect(page).to have_text "Comience con GetYourRefund antes del 1 de octubre si desea presentar su declaración con nosotros en 2024. Si su declaración está en progreso, inicie sesión y envíe sus documentos antes del 8 de octubre." end end end diff --git a/spec/services/replacement_parameters_service_spec.rb b/spec/services/replacement_parameters_service_spec.rb index 964956678f..9225182a13 100644 --- a/spec/services/replacement_parameters_service_spec.rb +++ b/spec/services/replacement_parameters_service_spec.rb @@ -360,7 +360,7 @@ context "successfully submitted email" do context "in english" do - let(:body) {AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.email_body } + let(:body) { AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.email_body(locale: "en", body_args: {end_of_docs_date: "October 8th"}) } it "replaces the replacement strings in the template" do result = subject.process @@ -370,8 +370,9 @@ end context "in spanish" do - let(:body) { AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.email_body(locale: locale)} + let(:body) { AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.email_body(locale: locale, body_args: {end_of_docs_date: "8 de octubre"})} let(:locale) { "es" } + it "replaces the replacement strings in the template" do result = subject.process expect(result).to include "Hola #{client.preferred_name}" @@ -382,7 +383,7 @@ context "successfully submitted text message" do context "in english" do - let(:body) { AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.sms_body(locale: "en") } + let(:body) { AutomatedMessage::SuccessfulSubmissionOnlineIntake.new.sms_body(locale: "en", body_args: {end_of_docs_date: "October 8th"})} it "replaces the replacement strings in the template" do result = subject.process @@ -392,7 +393,7 @@ end context "in spanish" do - let(:body) { I18n.t("messages.successful_submission_drop_off.sms", locale: "es") } + let(:body) { I18n.t("messages.successful_submission_drop_off.sms", locale: locale, body_args: {end_of_docs_date: "8 de octubre"})} let(:locale) { "es" } it "replaces the replacement strings in the template" do result = subject.process From be6f62ee2abccd8f5d17c37ead18e28fc50fb0b4 Mon Sep 17 00:00:00 2001 From: em barnard-shao <49880002+embarnard@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:17:20 -0400 Subject: [PATCH 17/20] Send reject resolution message on 4/13 and 4/22 for clients that have been notified of rejection and not yet been accepted (#4479) --- .../automated_messages_controller.rb | 1 + ...ct_resolution_reminder_notification_job.rb | 34 ++++++++ .../reject_resolution_reminder.rb | 24 ++++++ config/locales/en.yml | 17 ++++ config/locales/es.yml | 17 ++++ crontab | 3 + ...ect_resolution_reminder_notifications.rake | 26 +++++++ ...solution_reminder_notification_job_spec.rb | 78 +++++++++++++++++++ ..._resolution_reminder_notifications_spec.rb | 36 +++++++++ 9 files changed, 236 insertions(+) create mode 100644 app/jobs/send_reject_resolution_reminder_notification_job.rb create mode 100644 app/models/state_file/automated_message/reject_resolution_reminder.rb create mode 100644 lib/tasks/send_reject_resolution_reminder_notifications.rake create mode 100644 spec/jobs/send_reject_resolution_reminder_notification_job_spec.rb create mode 100644 spec/tasks/send_reject_resolution_reminder_notifications_spec.rb diff --git a/app/controllers/hub/state_file/automated_messages_controller.rb b/app/controllers/hub/state_file/automated_messages_controller.rb index 917debde40..3207d67ed9 100644 --- a/app/controllers/hub/state_file/automated_messages_controller.rb +++ b/app/controllers/hub/state_file/automated_messages_controller.rb @@ -16,6 +16,7 @@ def messages_preview [StateFile::AutomatedMessage::IssueResolved, {}], [StateFile::AutomatedMessage::StillProcessing, {}], [StateFile::AutomatedMessage::SuccessfulSubmission, {}], + [StateFile::AutomatedMessage::RejectResolutionReminder, {}], ] automated_messages_and_mailers = automated_messages.map do |m| diff --git a/app/jobs/send_reject_resolution_reminder_notification_job.rb b/app/jobs/send_reject_resolution_reminder_notification_job.rb new file mode 100644 index 0000000000..422c0e1b77 --- /dev/null +++ b/app/jobs/send_reject_resolution_reminder_notification_job.rb @@ -0,0 +1,34 @@ +class SendRejectResolutionReminderNotificationJob < ApplicationJob + def perform(intake) + return unless notified_of_rejected_and_not_accepted(intake) + + StateFile::MessagingService.new( + intake: intake, + message: StateFile::AutomatedMessage::RejectResolutionReminder, + body_args: { return_status_link: return_status_link(intake) } + ).send_message + end + + def priority + PRIORITY_LOW + end + + private + + def return_status_link(intake) + locale = intake.locale || "en" + Rails.application.routes.url_helpers.url_for(host: MultiTenantService.new(:statefile).host, controller: "state_file/questions/return_status", action: "edit", us_state: intake.state_code, locale: locale) + end + + def notified_of_rejected_and_not_accepted(intake) + transition_states = intake.efile_submissions.flat_map do |submission| + submission.efile_submission_transitions.map(&:to_state) + end.uniq + + last_state = intake.efile_submissions.last.current_state + in_progress_states = ["preparing", "bundling", "queued", "transmitted", "ready_for_ack"] + return false if transition_states.include?("accepted") || in_progress_states.include?(last_state) + + transition_states.include?("notified_of_rejection") + end +end \ No newline at end of file diff --git a/app/models/state_file/automated_message/reject_resolution_reminder.rb b/app/models/state_file/automated_message/reject_resolution_reminder.rb new file mode 100644 index 0000000000..315a8d1908 --- /dev/null +++ b/app/models/state_file/automated_message/reject_resolution_reminder.rb @@ -0,0 +1,24 @@ +module StateFile::AutomatedMessage + class RejectResolutionReminder < BaseAutomatedMessage + + def self.name + 'messages.state_file.reject_resolution_reminder'.freeze + end + + def self.send_only_once? + true + end + + def sms_body(**args) + I18n.t("messages.state_file.reject_resolution_reminder.sms", **args) + end + + def email_subject(**args) + I18n.t("messages.state_file.reject_resolution_reminder.email.subject", **args) + end + + def email_body(**args) + I18n.t("messages.state_file.reject_resolution_reminder.email.body", **args) + end + end +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index a5a6b9236f..99a8b7aa13 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1624,6 +1624,23 @@ en: sms: | Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes by April 15 in order to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. Need help? Reply to this text. + reject_resolution_reminder: + email: + body: | + Hi %{primary_first_name}, + + As a reminder, %{state_name} rejected your state tax return. Don't worry! We can help you fix and resubmit it at %{return_status_link}. Make sure to take action on your rejected return as soon as possible in order to avoid late filing fees from %{state_name} state. + + FileYourStateTaxes will be closing its services for the year on April 25. + + Questions? Reply to this email. + + Best, + The FileYourStateTaxes team + + If you would like to unsubscribe from emails, click here. + subject: Take action on your rejected %{state_name} state return. + sms: Hi %{primary_first_name} - This is a reminder that %{state_name} rejected your state tax return. We can help you fix and resubmit it at %{return_status_link}. Make sure to take action as soon as possible in order to avoid late filing fees. FileYourStateTaxes will be closing its services for the year on April 25. Questions? Reply to this text. rejected: email: body: | diff --git a/config/locales/es.yml b/config/locales/es.yml index 67b92f607e..d5dc1316ca 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1605,6 +1605,23 @@ es: sms: | Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales antes del 15 de abril para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/en/us/login-options para terminarlos ahora. ¿Necesitas ayuda? Responde a este mensaje de texto. + reject_resolution_reminder: + email: + body: | + Hola %{primary_first_name}, + + Como recordatorio, tu declaración de impuestos estatales de %{state_name} fue rechazada. ¡No te preocupes! Podemos ayudarte a corregirla y volver a enviarla en %{return_status_link}. Asegúrate de tomar acción en tu declaración rechazada lo más pronto posible para evitar cargos por presentación tardía del estado de %{state_name}. + + Los servicios de FileYourStateTaxes se cerrarán para el año el 25 de abril. + + ¿Preguntas? Responde a este correo electrónico. + + Atentamente, + El equipo de FileYourStateTaxes + + Si deseas cancelar la suscripción a correos electrónicos, haz clic aquí. + subject: "¡Toma acción en tu declaración estatal de %{state_name} rechazada!" + sms: Hola %{primary_first_name} - Te queremos recordar que tu declaración de impuestos estatales de %{state_name} fue rechazada. Podemos ayudarte a corregirla y volver a enviarla en %{return_status_link}. Asegúrate de tomar acción lo más pronto posible para evitar cargos por presentación tardía. Los servicios de FileYourStateTaxes se cerrarán para el año el 25 de abril. ¿Preguntas? Responde a este mensaje. rejected: email: body: "Hola %{primary_first_name},\n\nLamentablemente, %{state_name} rechazó tu declaración de impuestos estatales. ¡No te preocupes! Podemos ayudarte a corregirla y volver a enviarla en %{return_status_link}.\n\n¿Preguntas? Responde a este correo electrónico.\n\nSaludos, \nEl equipo de FileYourStateTaxes\n" diff --git a/crontab b/crontab index 4972aef8a4..55f5cf02b0 100644 --- a/crontab +++ b/crontab @@ -10,3 +10,6 @@ 0 17 * * * bundle exec rake state_file:reminder_to_finish_state_return 0 21 13 4 * bundle exec rake state_file:pre_deadline_reminder 0 21 16 4 * bundle exec rake state_file:post_deadline_reminder +0 21 13 4 * bundle exec rake send_reject_resolution_reminder_notifications:send +0 21 22 4 * bundle exec rake send_reject_resolution_reminder_notifications:send +40 19 12 4 * bundle exec rake send_reject_resolution_reminder_notifications:send diff --git a/lib/tasks/send_reject_resolution_reminder_notifications.rake b/lib/tasks/send_reject_resolution_reminder_notifications.rake new file mode 100644 index 0000000000..88524bcb79 --- /dev/null +++ b/lib/tasks/send_reject_resolution_reminder_notifications.rake @@ -0,0 +1,26 @@ +namespace :send_reject_resolution_reminder_notifications do + desc 'Send reject resolution reminder notifications' + task 'send' => :environment do + intakes_to_send_message_to.each do |intake| + SendRejectResolutionReminderNotificationJob.perform_later(intake) + end + end + + def intakes_to_send_message_to + ny_intakes = StateFileNyIntake.joins(efile_submissions: :efile_submission_transitions) + .where(efile_submission_transitions: { to_state: 'notified_of_rejection' }) + .where.not(id: StateFileNyIntake.joins(efile_submissions: :efile_submission_transitions) + .where(efile_submission_transitions: { to_state: 'accepted' }) + .select(:id)) + .distinct + + az_intakes = StateFileAzIntake.joins(efile_submissions: :efile_submission_transitions) + .where(efile_submission_transitions: { to_state: 'notified_of_rejection' }) + .where.not(id: StateFileAzIntake.joins(efile_submissions: :efile_submission_transitions) + .where(efile_submission_transitions: { to_state: 'accepted' }) + .select(:id)) + .distinct + + ny_intakes.to_a + az_intakes.to_a + end +end diff --git a/spec/jobs/send_reject_resolution_reminder_notification_job_spec.rb b/spec/jobs/send_reject_resolution_reminder_notification_job_spec.rb new file mode 100644 index 0000000000..2f001e4a48 --- /dev/null +++ b/spec/jobs/send_reject_resolution_reminder_notification_job_spec.rb @@ -0,0 +1,78 @@ +require 'rails_helper' + +RSpec.describe SendRejectResolutionReminderNotificationJob, type: :job do + describe "#perform" do + let(:intake) { + create :state_file_az_intake, + efile_submissions: efile_subimissions, + primary_first_name: "Mona", + email_address: "monalisa@example.com", + email_address_verified_at: 1.minute.ago, + message_tracker: {} + } + let(:efile_subimissions) { [create(:efile_submission, :notified_of_rejection)] } + let(:message) { StateFile::AutomatedMessage::RejectResolutionReminder } + let(:body_args) { { return_status_link: "http://statefile.test.localhost/en/az/questions/return-status" } } + let(:sf_messaging_service) { + StateFile::MessagingService.new( + intake: intake, + message: message, + body_args: body_args) + } + + before do + allow(StateFile::MessagingService).to receive(:new).with(intake: intake, message: message, body_args: body_args).and_return(sf_messaging_service) + end + + context "with an intake that has been sent a notified-of-rejection message, does not have an accepted return and is not currently in an in-progress state" do + it "sends the message" do + expect { + described_class.perform_now(intake) + }.to change(StateFileNotificationEmail, :count).by(1) + + expect(intake.reload.message_tracker).to include("messages.state_file.reject_resolution_reminder") + + expect(StateFile::MessagingService).to have_received(:new).with( + intake: intake, + message: message, + body_args: body_args) + end + end + + context "with an intake that has not been sent the notified-of-rejection message" do + let(:efile_subimissions) { [create(:efile_submission, :rejected)] } + + it "does not send the message" do + expect { + described_class.perform_now(intake) + }.to change(StateFileNotificationEmail, :count).by(0) + + expect(intake.reload.message_tracker).not_to include("messages.state_file.reject_resolution_reminder") + end + end + + context "with an intake that has an accepted return" do + let(:efile_subimissions) { [create(:efile_submission, :notified_of_rejection), create(:efile_submission, :accepted)] } + + it "does not send the message" do + expect { + described_class.perform_now(intake) + }.to change(StateFileNotificationEmail, :count).by(0) + + expect(intake.reload.message_tracker).not_to include("messages.state_file.reject_resolution_reminder") + end + end + + context "currently in transmitted state" do + let(:efile_subimissions) { [create(:efile_submission, :notified_of_rejection), create(:efile_submission, :transmitted)] } + + it "does not send the message" do + expect { + described_class.perform_now(intake) + }.to change(StateFileNotificationEmail, :count).by(0) + + expect(intake.reload.message_tracker).not_to include("messages.state_file.reject_resolution_reminder") + end + end + end +end diff --git a/spec/tasks/send_reject_resolution_reminder_notifications_spec.rb b/spec/tasks/send_reject_resolution_reminder_notifications_spec.rb new file mode 100644 index 0000000000..351cf5d049 --- /dev/null +++ b/spec/tasks/send_reject_resolution_reminder_notifications_spec.rb @@ -0,0 +1,36 @@ +require "rails_helper" + +describe "send_reject_resolution_reminder_notifications:send" do + let!(:intake) { create :state_file_az_intake, efile_submissions: efile_subimissions } + before do + allow(SendRejectResolutionReminderNotificationJob).to receive(:perform_later).with(intake) + end + + include_context "rake" + context "clients that have been notified of rejection but have no accepted return" do + let(:efile_subimissions) { [create(:efile_submission, :notified_of_rejection)] } + + it "enqueues a job" do + task.invoke + expect(SendRejectResolutionReminderNotificationJob).to have_received(:perform_later).with(intake) + end + end + + context "clients that have not been notified of rejection" do + let(:efile_subimissions) { [create(:efile_submission, :transmitted)] } + + it "doesn't enqueues a job" do + task.invoke + expect(SendRejectResolutionReminderNotificationJob).not_to have_received(:perform_later).with(intake) + end + end + + context "clients that have been accepted" do + let(:efile_subimissions) { [create(:efile_submission, :accepted), create(:efile_submission, :notified_of_rejection)] } + + it "doesn't enqueues a job" do + task.invoke + expect(SendRejectResolutionReminderNotificationJob).not_to have_received(:perform_later).with(intake) + end + end +end From 8607a4dde4e1b55eaad74781af01fda825ee20a6 Mon Sep 17 00:00:00 2001 From: tofarr Date: Fri, 12 Apr 2024 13:43:09 -0600 Subject: [PATCH 18/20] Changing eastern to pacific (#4483) --- config/application.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/application.rb b/config/application.rb index 6f5071ff32..f287c8e228 100644 --- a/config/application.rb +++ b/config/application.rb @@ -107,8 +107,8 @@ class Application < Rails::Application # StateFile config.state_file_start_of_open_intake = Time.find_zone('America/New_York').parse('2024-02-08 09:00:00') - config.state_file_end_of_new_intakes = Time.find_zone('America/New_York').parse('2024-04-15 23:59:59') - config.state_file_end_of_in_progress_intakes = Time.find_zone('America/New_York').parse('2024-04-25 23:59:59') + config.state_file_end_of_new_intakes = Time.find_zone('America/Los_Angeles').parse('2024-04-15 23:59:59') + config.state_file_end_of_in_progress_intakes = Time.find_zone('America/Los_Angeles').parse('2024-04-25 23:59:59') config.allow_magic_verification_code = (Rails.env.demo? || Rails.env.development? || Rails.env.heroku?) config.allow_magic_ssn = (Rails.env.demo? || Rails.env.development? || Rails.env.heroku? || Rails.env.staging?) From 5dd1e4868ff1fa582fd31b6deeb8d3b585a9a162 Mon Sep 17 00:00:00 2001 From: em barnard-shao <49880002+embarnard@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:55:01 -0400 Subject: [PATCH 19/20] Remove extra copy from reject resolution message (#4485) --- config/locales/en.yml | 2 -- config/locales/es.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 99a8b7aa13..73fcafcaef 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1637,8 +1637,6 @@ en: Best, The FileYourStateTaxes team - - If you would like to unsubscribe from emails, click here. subject: Take action on your rejected %{state_name} state return. sms: Hi %{primary_first_name} - This is a reminder that %{state_name} rejected your state tax return. We can help you fix and resubmit it at %{return_status_link}. Make sure to take action as soon as possible in order to avoid late filing fees. FileYourStateTaxes will be closing its services for the year on April 25. Questions? Reply to this text. rejected: diff --git a/config/locales/es.yml b/config/locales/es.yml index d5dc1316ca..4c280aa218 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1618,8 +1618,6 @@ es: Atentamente, El equipo de FileYourStateTaxes - - Si deseas cancelar la suscripción a correos electrónicos, haz clic aquí. subject: "¡Toma acción en tu declaración estatal de %{state_name} rechazada!" sms: Hola %{primary_first_name} - Te queremos recordar que tu declaración de impuestos estatales de %{state_name} fue rechazada. Podemos ayudarte a corregirla y volver a enviarla en %{return_status_link}. Asegúrate de tomar acción lo más pronto posible para evitar cargos por presentación tardía. Los servicios de FileYourStateTaxes se cerrarán para el año el 25 de abril. ¿Preguntas? Responde a este mensaje. rejected: From 6dc7994e743a4e422d7e4e6286a28bdc7a2b7811 Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Fri, 12 Apr 2024 17:26:16 -0300 Subject: [PATCH 20/20] More post-deadline copy updates (#4484) * More post-deadline copy updates --- config/locales/en.yml | 2 +- config/locales/es.yml | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 73fcafcaef..678d0a765d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1612,7 +1612,7 @@ en: Questions? Reply to this text. post_deadline_reminder: email: - body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes are due by April 15. Make sure to submit your state tax return by April 15 in order to avoid late filing fees from %{state_name}. \n\nFileYourStateTaxes will be closing its services for the year on April 25. \n\nGo to fileyourstatetaxes.org/en/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" + body: "Hi %{primary_first_name},\n\nYou haven't submitted your state tax return yet. As a reminder, state taxes were due by April 15. Make sure to submit your state tax return as soon as possible in order to avoid late filing fees from %{state_name}. \n\nFileYourStateTaxes will be closing its services for the year on April 25. \n\nGo to https://www.fileyourstatetaxes.org/es/us/login-options to finish them now.\n\nNeed help? Reply to this email.\n\nBest,\nThe FileYourStateTaxes team\n" subject: Make sure to finish filing your state taxes as soon as possible. sms: | Hi %{primary_first_name} - You haven't submitted your state tax return yet. Make sure to submit your state taxes as soon as possible to avoid late filing fees. Go to fileyourstatetaxes.org/en/us/login-options to finish them now. FileYourStateTaxes will be closing its services for the year on April 25. diff --git a/config/locales/es.yml b/config/locales/es.yml index 4c280aa218..18ca225afd 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1586,14 +1586,7 @@ es: ¿Preguntas? Responde a este mensaje. post_deadline_reminder: email: - body: | - Hola %{primary_first_name}, - Aún no has presentado tu declaración de impuestos estatales. Como recordatorio, los impuestos estatales vencieron el 15 de abril. Asegúrate de presentar tu declaración de impuestos estatales antes del 15 de abril para evitar multas por presentación tardía en %{state_name}. - FileYourStateTaxes cerrará sus servicios para el año el 25 de abril. - Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. - ¿Necesitas ayuda? Responde a este correo electrónico. - Saludos, - El equipo de FileYourStateTaxes + body: "Hola %{primary_first_name}, \n\nAún no has presentado tu declaración de impuestos estatales. Como recordatorio, los impuestos estatales vencieron el 15 de abril. Asegúrate de presentar tus impuestos estatales lo antes posible para evitar multas por presentación tardía de %{state_name}.\n\nFileYourStateTaxes cerrará sus servicios para el año el 25 de abril.\n\nVe a https://www.fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. \n\n¿Necesitas ayuda? Responde a este correo electrónico. \n\nSaludos, \n\nEl equipo de FileYourStateTaxes" subject: "¡No olvides terminar de presentar tu declaración de impuestos estatales lo antes posible!" sms: | Hola %{primary_first_name} - Aún no has presentado tu declaración de impuestos estatales. Asegúrate de presentar tus impuestos estatales lo antes posible para evitar multas por presentación tardía. Ve a fileyourstatetaxes.org/es/us/login-options para terminarlos ahora. FileYourStateTaxes cerrará sus servicios para el año el 25 de abril.