Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[FYST-1704] Income/ donation forms: Inaccurate date selection #5570

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
3 changes: 1 addition & 2 deletions app/models/az321_contribution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ class Az321Contribution < ApplicationRecord
validates :date_of_contribution,
inclusion: {
in: TAX_YEAR.beginning_of_year..TAX_YEAR.end_of_year
},
presence: true
}
Copy link
Contributor

Choose a reason for hiding this comment

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

why is the presence check removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for this the inclusion validation serves as a presence check as well, as nil values fall outside of the range. It made it so I didn't have to duplicate the error message

end
5 changes: 2 additions & 3 deletions app/models/az322_contribution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ class Az322Contribution < ApplicationRecord
accepts_nested_attributes_for :state_file_az_intake, update_only: true

validates :school_name, presence: true
validates :ctds_code, presence: true, format: { with: /\A\d{9}\z/, message: -> (_object, _data) { I18n.t("validators.ctds_code") }}
validates :ctds_code, presence: true, format: { with: /\A\d{9}\z/, message: ->(_object, _data) { I18n.t("validators.ctds_code") }}
validates :district_name, presence: true
validates :amount, presence: true, numericality: { greater_than: 0 }
validates :date_of_contribution,
inclusion: {
in: TAX_YEAR.beginning_of_year..TAX_YEAR.end_of_year
},
presence: true
}
end
60 changes: 23 additions & 37 deletions app/models/concerns/date_accessible.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module DateAccessible
TAX_YEAR = Date.new(Rails.configuration.statefile_current_tax_year)

included do
private
Copy link
Contributor

Choose a reason for hiding this comment

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

what's the significance of making these not private methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The methods from the module are class methods, so they can't be private, and I believe this line has no effect


# Calls `date_reader` and `date_writer` on specified date properties to set
# getters and setters on the specified date properties. For use with
Expand All @@ -29,15 +28,13 @@ def self.date_reader(*properties)

properties.each do |property|
self.define_method("#{property}_month") do
send(property)&.month
self.instance_variable_get("@#{property}_month") || send(property)&.month
end

self.define_method("#{property}_year") do
send(property)&.year
self.instance_variable_get("@#{property}_year") || send(property)&.year
end

self.define_method("#{property}_day") do
send(property)&.day
self.instance_variable_get("@#{property}_day") || send(property)&.day
Copy link
Contributor

Choose a reason for hiding this comment

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

This will sidestep any other setters, as opposed to calling super. Intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that's the intent - for this setter to default to the instance variable, with a backup of parsing the persisted value

end
end
end
Expand All @@ -52,42 +49,31 @@ def self.date_writer(*properties)
properties = [properties] unless properties.is_a?(Enumerable)

properties.each do |property|
self.define_method("#{property}_month=") do |month|
change_date_property(property, month: month) unless month.blank?
end
attr_writer :"#{property}_month", :"#{property}_year", :"#{property}_day"

self.define_method("#{property}_year=") do |year|
change_date_property(property, year: year) unless year.blank?
end
before_validation do
month_to_set = self.instance_variable_get("@#{property}_month")
day_to_set = self.instance_variable_get("@#{property}_day")
year_to_set = self.instance_variable_get("@#{property}_year")

self.define_method("#{property}_day=") do |day|
change_date_property(property, day: day) unless day.blank?
if year_to_set.present? && month_to_set.present? && day_to_set.present?
send("#{property}=", Date.new(year_to_set.to_i, month_to_set.to_i, day_to_set.to_i))
end
Copy link
Contributor

Choose a reason for hiding this comment

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

what happens if day/month/year attribute is missing? (fails the .present? check?) Is there validation further down the line that will give an error that the date is invalid b/c something is missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if one of the values is missing, then the date won't get set / updated, and it'll fail the inclusion check in it's model

rescue Date::Error
send("#{property}=", nil)
end
end
end

# Takes in valid arguments to Date#change. Will create a new date if
# `date_of_contribution` is nil, otherwise will merely modify the correct
# date part. Values can be strings as long as #to_i renders an appropriate
# integer. Note that Date#change only accepts :year, :month, and :day as
# keys, all other keys will be treated as nothing was passed at all.
#
# Note that until all three fragments are passed; month, day, and year
# For year, a range must be indicated on the validator on the property itself
#
# @see Date#change
#
# @param date_property [Symbol] The property to manipulate
# @param args [Hash<Symbol, String | Integer>] Arguments conforming to Date#change
def change_date_property(date_property, args)
existing_date = send(date_property) || Date.new
self.class_eval do
validate :"#{property}_date_valid"

self.send(
"#{date_property}=",
existing_date.change(**args.transform_values(&:to_i))
)
rescue Date::Error
nil
define_method("#{property}_date_valid") do
date = send(property)
if date.present? && !Date.valid_date?(date.year, date.month, date.day)
errors.add(:date_of_contribution, :invalid_date, message: I18n.t("activerecord.errors.models.az321_contribution.attributes.date_of_contribution.inclusion"))
end
end
end
end
end
end
end
5 changes: 3 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ en:
charity_code:
invalid: should be a five digit number beginning with 2
date_of_contribution:
inclusion: Date must be in the current tax year
inclusion: Date must be valid and in the current tax year
state_file_1099_r:
errors:
must_be_less_than_gross_distribution: Must be less than gross distribution amount
Expand Down Expand Up @@ -2993,7 +2993,7 @@ en:
health_insurance_premium_question:
one: Did you pay health insurance premiums for yourself in %{year}?
other: Did you pay health insurance premiums for yourself and your household in %{year}?
medicaid_content: Yes, if you are on Medicaid, your health insurance premium counts.  Check your monthly letter from the Idaho Department of Health and Welfare for your premium amount. It could be as low as $0, depending on your income and other factors.
medicaid_content: Yes, if you are on Medicaid, your health insurance premium counts. Check your monthly letter from the Idaho Department of Health and Welfare for your premium amount. It could be as low as $0, depending on your income and other factors.
medicaid_title: Do Medicaid and Medicare payments count?
qualifications_content_html: |
You can deduct health insurance premiums paid for yourself, your spouse, and your dependents, including medical, dental, and vision, as long as they haven’t been deducted from your income pre-tax.
Expand Down Expand Up @@ -3664,6 +3664,7 @@ en:
16a_interest_income: Interest income
20a_retirement_income: Retirement income
27_total_income: Total income
28c_retirement_excluded_from_taxation: Retirement income excluded from Taxation
29_nj_gross_income: New Jersey Gross Income
30_exemptions: Exemptions (based on the size of your family, disability status, veteran status)
31_medical: Medical expenses deducted
Expand Down
27 changes: 14 additions & 13 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ es:
charity_code:
invalid: Debe ser un número de cinco dígitos que comience con 2
date_of_contribution:
inclusion: La fecha debe estar en el año fiscal actual
inclusion: La fecha debe ser válida y corresponder al año fiscal actual.
state_file_1099_r:
errors:
must_be_less_than_gross_distribution: Debe ser menor que el monto bruto de distribución
Expand Down Expand Up @@ -2151,7 +2151,7 @@ es:
<li>Medicaid (NJ FamilyCare)</li>
</ul>
<p><a href="https://nj.gov/treasury/njhealthinsurancemandate/getinfo.shtml" target="_blank" rel="noopener nofollow">Aquí está la lista completa de opciones de seguro que tienen cobertura mínima esencial (en inglés)</a></p>
<p>Debes responder no si tú o alguien en tu hogar solo tuvo plan de visión o dental independientes, cobertura de compensación laboral o un plan limitado a una enfermedad o dolencia específica. En New Jersey, a partir de 2018, necesitas cobertura mínima esencial, o podrías tener que pagar impuestos adicionales.</p>
<p>Debes responder no si tú o alguien en tu hogar solo tuvo plan de visión o dental independientes, cobertura de compensación laboral o un plan limitado a una enfermedad o dolencia específica. En New Jersey, a partir de 2018, necesitas cobertura mínima esencial, o podrías tener que pagar impuestos adicionales.</p>
register_to_vote: Regístrate para votar
see_detailed_return: Ver información detallada de la declaración
standard_deduction: Deducción estándar
Expand Down Expand Up @@ -2207,7 +2207,7 @@ es:
<li>El formulario 1099-G (si recibiste beneficios de desempleo en %{filing_year})</li>
<li>Los números de ruta y cuenta bancaria (si deseas recibir tu reembolso o hacer un pago de impuestos electrónicamente)</li>
</ul>
help_text_title: "¿Qué necesito para completar mi declaración de impuestos estatales?"
help_text_title: "¿Qué necesito para completar mi declaración de impuestos estatales?"
id:
closed_html: |
Ya cerramos por esta temporada de impuestos. <br /><br />
Expand Down Expand Up @@ -2455,7 +2455,7 @@ es:
one: "¿Cuánto recibiste en salario por servicio activo?"
other: "¿Cuánto recibiste tú o tu cónyuge en salario por servicio activo?"
learn_more_content_html: |
Si eres miembro de las <strong>Fuerzas Armadas de Estados Unidos</strong>, ingresa el salario que recibiste por servicio activo que reportaste en declaración de impuestos federales.<br><br>
Si eres miembro de las <strong>Fuerzas Armadas de Estados Unidos</strong>, ingresa el salario que recibiste por servicio activo que reportaste en declaración de impuestos federales.<br><br>
Si eres miembro de <strong>la Reserva o la Guardia Nacional</strong>, ingresa el salario que recibiste por entrenamientos durante fines de semana o de dos semanas que hayas reportado en tu declaración de impuestos federal.<br><br>
<strong>No</strong> puedes incluir ningún ingreso que hayas recibido por empleo a tiempo completo en el servicio civil como "técnico militar (doble estatus)".
learn_more_title: Obtener más información
Expand Down Expand Up @@ -3038,7 +3038,7 @@ es:
review_and_edit_state_info: Revisar y editar la información estatal
ssa_title: Beneficios del Seguro Social (SSA-1099)
state_info_to_be_collected: Información estatal que se recogerá
title: Estos son los formularios de ingresos que transferimos de tu declaración de impuestos federales.
title: Estos son los formularios de ingresos que transferimos de tu declaración de impuestos federales.
unemployment_title: Beneficios de desempleo (1099-G)
w2s_title: Empleos (W-2)
warning: "⚠️ Necesitamos verificar de nuevo cierta información"
Expand Down Expand Up @@ -3636,6 +3636,7 @@ es:
16a_interest_income: Ingreso total
20a_retirement_income: Retirement income
27_total_income: Ingreso total
28c_retirement_excluded_from_taxation: Ingresos de jubilación excluidos del impuesto
29_nj_gross_income: Ingreso Bruto de New Jersey
30_exemptions: Exenciones (según el tamaño de tu familia, estado de discapacidad, estado de veterano/a de guerra)
31_medical: Gastos médicos deducidos
Expand Down Expand Up @@ -3873,8 +3874,8 @@ es:
title: 'Selecciona tu estado de residencia en la ciudad de Nueva York durante el %{year}:'
pending_federal_return:
edit:
body_html: Lamentamos hacerte esperar.<br><br>Recibirás un correo electrónico de IRS Direct File cuando tu declaración de impuestos federales sea aceptada, con un enlace para traerte de regreso aquí. Revisa tu carpeta de spam.
title: Tu declaración de impuestos federales debe ser aceptada antes de que puedas presentar tu declaración de impuestos estatales
body_html: Lamentamos hacerte esperar.<br><br>Recibirás un correo electrónico de IRS Direct File cuando tu declaración de impuestos federales sea aceptada, con un enlace para traerte de regreso aquí. Revisa tu carpeta de spam.
title: Tu declaración de impuestos federales debe ser aceptada antes de que puedas presentar tu declaración de impuestos estatales
phone_number:
edit:
action: Enviar el código
Expand Down Expand Up @@ -6297,7 +6298,7 @@ es:
title: Pago de estímulo
tax_questions:
cannot_answer:
different_services: Preguntas sobre los impuestos declarados mediante otro servicio (p. ej., H&R Block o TurboTax)
different_services: Preguntas sobre los impuestos declarados mediante otro servicio (p. ej., H&R Block o TurboTax)
header: 'No podemos responder a lo siguiente:'
refund_amounts: El monto del reembolso que recibirá
header: "¡Tratemos de responder a sus preguntas sobre los impuestos!"
Expand Down Expand Up @@ -6928,14 +6929,14 @@ es:
enter_zip: Ingrese su código postal para encontrar proveedores cercanos.
header: Obtenga ayuda gratuita en declarar sus impuestos
info: Los programas de Asistencia Voluntaria al Contribuyente (VITA, por sus siglas en inglés) del IRS ofrecen ayuda con los impuestos gratis, a los contribuyentes que califiquen.
no_results: "¡Disculpe! No hemos encontrado ningún resultado dentro de 50 millas de su dirección."
results: Hemos encontrado %{total_entries} resultados dentro de 50 millas de %{zip}
no_results: "¡Disculpe! No hemos encontrado ningún resultado dentro de 50 millas de su dirección."
results: Hemos encontrado %{total_entries} resultados dentro de 50 millas de %{zip}
search:
header: Ingrese su código postal para encontrar proveedores cercanos.
no_results: No hemos encontrado ningún resultado dentro de 50 millas de %{zip} (%{zip_name}).
no_results: No hemos encontrado ningún resultado dentro de 50 millas de %{zip} (%{zip_name}).
placeholder: Ingrese su código postal
prepare_your_own_html: También puede preparar su propia declaración de impuestos utilizando el %{free_file_link} para encontrar una manera gratuita de hacerlo en línea.
results: Hemos encontrado %{total_entries} resultados dentro de 50 millas de %{zip} (%{zip_name}).
results: Hemos encontrado %{total_entries} resultados dentro de 50 millas de %{zip} (%{zip_name}).
try_another_or_apply_html: Intente otro código postal.
title: Ubicar
show:
Expand All @@ -6945,4 +6946,4 @@ es:
no_hours_listed: Disculpe, no hay horario de apertura.
no_phone_number_listed: Disculpe, no hay número de teléfono.
phone_number_title: Llame a %{provider_name}
search_radius: Dentro de %{distance} millas de %{zip} (%{zip_name})
search_radius: Dentro de %{distance} millas de %{zip} (%{zip_name})
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@
id: contribution.id,
az321_contribution: {
date_of_contribution_month: 6,
date_of_contribution_day: 16
date_of_contribution_day: 16,
date_of_contribution_year: Rails.configuration.statefile_current_tax_year
}
}

Expand Down
1 change: 1 addition & 0 deletions spec/factories/az321_contributions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
state_file_az_intake
end
end

32 changes: 1 addition & 31 deletions spec/models/az321_contribution_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

describe 'simple validation' do
it { should validate_presence_of :charity_name }
it { should validate_presence_of :date_of_contribution }
it { should validate_inclusion_of(:date_of_contribution).in_range(described_class::TAX_YEAR.beginning_of_year..described_class::TAX_YEAR.end_of_year) }
end

describe '#made_az321_contributions' do
Expand All @@ -44,36 +44,6 @@
end

describe '#date_of_contribution' do
it 'should be valid in the current tax year' do
az = Az321Contribution.new(state_file_az_intake: intake)

az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year

az.valid?

expect(az.errors[:date_of_contribution]).to be_empty
end

it 'should be invalid in the previous year' do
az = Az321Contribution.new(state_file_az_intake: intake)

az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year - 1

az.valid?

expect(az.errors[:date_of_contribution]).not_to be_empty
end

it 'should be invalid in the next year' do
az = Az321Contribution.new(state_file_az_intake: intake)

az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year + 1

az.valid?

expect(az.errors[:date_of_contribution]).not_to be_empty
end

it 'should be valid when a correct date is provided' do
az = Az321Contribution.new(state_file_az_intake: intake)

Expand Down
6 changes: 3 additions & 3 deletions spec/models/az322_contribution_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,23 @@

describe '#date_of_contribution' do
it 'should be valid in the current tax year' do
az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year
az.date_of_contribution = Date.new(Rails.configuration.statefile_current_tax_year, 1, 1)

az.valid?

expect(az.errors[:date_of_contribution]).to be_empty
end

it 'should be invalid in the previous year' do
az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year - 1
az.date_of_contribution = Date.new(Rails.configuration.statefile_current_tax_year - 1, 1, 1)

az.valid?

expect(az.errors[:date_of_contribution]).not_to be_empty
end

it 'should be invalid in the next year' do
az.date_of_contribution_year = Rails.configuration.statefile_current_tax_year + 1
az.date_of_contribution = Date.new(Rails.configuration.statefile_current_tax_year + 1, 1, 1)

az.valid?

Expand Down
Loading
Loading