diff --git a/app/models/az321_contribution.rb b/app/models/az321_contribution.rb index cd058abad0..efbcf011f4 100644 --- a/app/models/az321_contribution.rb +++ b/app/models/az321_contribution.rb @@ -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 + } end diff --git a/app/models/az322_contribution.rb b/app/models/az322_contribution.rb index 98412c313d..0338a07933 100644 --- a/app/models/az322_contribution.rb +++ b/app/models/az322_contribution.rb @@ -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 diff --git a/app/models/concerns/date_accessible.rb b/app/models/concerns/date_accessible.rb index 4b88ea638f..82f709ccfa 100644 --- a/app/models/concerns/date_accessible.rb +++ b/app/models/concerns/date_accessible.rb @@ -4,7 +4,6 @@ module DateAccessible TAX_YEAR = Date.new(Rails.configuration.statefile_current_tax_year) included do - private # Calls `date_reader` and `date_writer` on specified date properties to set # getters and setters on the specified date properties. For use with @@ -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 end end end @@ -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 + 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] 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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 2a45deba7a..876b5b622c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 @@ -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. @@ -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 diff --git a/config/locales/es.yml b/config/locales/es.yml index e3e8474577..a762b32979 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -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 @@ -2151,7 +2151,7 @@ es:
  • Medicaid (NJ FamilyCare)
  • Aquí está la lista completa de opciones de seguro que tienen cobertura mínima esencial (en inglés)

    -

    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.

    +

    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.

    register_to_vote: Regístrate para votar see_detailed_return: Ver información detallada de la declaración standard_deduction: Deducción estándar @@ -2207,7 +2207,7 @@ es:
  • El formulario 1099-G (si recibiste beneficios de desempleo en %{filing_year})
  • Los números de ruta y cuenta bancaria (si deseas recibir tu reembolso o hacer un pago de impuestos electrónicamente)
  • - 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.

    @@ -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 Fuerzas Armadas de Estados Unidos, ingresa el salario que recibiste por servicio activo que reportaste en declaración de impuestos federales.

    + Si eres miembro de las Fuerzas Armadas de Estados Unidos, ingresa el salario que recibiste por servicio activo que reportaste en declaración de impuestos federales.

    Si eres miembro de la Reserva o la Guardia Nacional, 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.

    No 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 @@ -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" @@ -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 @@ -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.

    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.

    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 @@ -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!" @@ -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: @@ -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}) diff --git a/spec/controllers/state_file/questions/az_qualifying_organization_contributions_controller_spec.rb b/spec/controllers/state_file/questions/az_qualifying_organization_contributions_controller_spec.rb index 3e1db9d466..cdac8288d3 100644 --- a/spec/controllers/state_file/questions/az_qualifying_organization_contributions_controller_spec.rb +++ b/spec/controllers/state_file/questions/az_qualifying_organization_contributions_controller_spec.rb @@ -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 } } diff --git a/spec/factories/az321_contributions.rb b/spec/factories/az321_contributions.rb index 7f91f5e3e4..52d422fa15 100644 --- a/spec/factories/az321_contributions.rb +++ b/spec/factories/az321_contributions.rb @@ -24,3 +24,4 @@ state_file_az_intake end end + diff --git a/spec/models/az321_contribution_spec.rb b/spec/models/az321_contribution_spec.rb index 683a957b49..9cafcfd4c0 100644 --- a/spec/models/az321_contribution_spec.rb +++ b/spec/models/az321_contribution_spec.rb @@ -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 @@ -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) diff --git a/spec/models/az322_contribution_spec.rb b/spec/models/az322_contribution_spec.rb index eda648aa41..89a7e13341 100644 --- a/spec/models/az322_contribution_spec.rb +++ b/spec/models/az322_contribution_spec.rb @@ -86,7 +86,7 @@ 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? @@ -94,7 +94,7 @@ 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? @@ -102,7 +102,7 @@ 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? diff --git a/spec/models/concerns/date_accessible_spec.rb b/spec/models/concerns/date_accessible_spec.rb index 8ce9b46cb3..3fc5b556bb 100644 --- a/spec/models/concerns/date_accessible_spec.rb +++ b/spec/models/concerns/date_accessible_spec.rb @@ -1,162 +1,99 @@ require 'rails_helper' -class ExampleDateAccessor - include DateAccessible - attr_accessor :read_date, :write_date, :readwrite_date, - :readwrite_multi_date, :readwrite_multi_second_date - - date_reader :read_date - date_writer :write_date - - date_accessor :readwrite_date - date_accessor :readwrite_multi_date, :readwrite_multi_second_date -end - RSpec.describe DateAccessible do - subject { ExampleDateAccessor.new } + let(:year) { Rails.configuration.statefile_current_tax_year } + let(:month) { 2 } + let(:day) { 1 } + let(:expiration_date) { Date.new(year, month, day) } + subject { create(:state_id, expiration_date: expiration_date) } describe '#date_accessor' do it 'should create readers & writers for a single property' do - expect(subject).to respond_to(:readwrite_date_day) - expect(subject).to respond_to(:readwrite_date_month) - expect(subject).to respond_to(:readwrite_date_year) + expect(subject).to respond_to(:expiration_date_day) + expect(subject).to respond_to(:expiration_date_month) + expect(subject).to respond_to(:expiration_date_year) - expect(subject).to respond_to(:readwrite_date_day=) - expect(subject).to respond_to(:readwrite_date_month=) - expect(subject).to respond_to(:readwrite_date_year=) + expect(subject).to respond_to(:expiration_date_day=) + expect(subject).to respond_to(:expiration_date_month=) + expect(subject).to respond_to(:expiration_date_year=) end it 'should create readers & writers for multiple property' do - expect(subject).to respond_to(:readwrite_multi_date_day) - expect(subject).to respond_to(:readwrite_multi_date_month) - expect(subject).to respond_to(:readwrite_multi_date_year) + expect(subject).to respond_to(:expiration_date_day) + expect(subject).to respond_to(:expiration_date_month) + expect(subject).to respond_to(:expiration_date_year) - expect(subject).to respond_to(:readwrite_multi_date_day=) - expect(subject).to respond_to(:readwrite_multi_date_month=) - expect(subject).to respond_to(:readwrite_multi_date_year=) + expect(subject).to respond_to(:expiration_date_day=) + expect(subject).to respond_to(:expiration_date_month=) + expect(subject).to respond_to(:expiration_date_year=) - expect(subject).to respond_to(:readwrite_multi_second_date_day) - expect(subject).to respond_to(:readwrite_multi_second_date_month) - expect(subject).to respond_to(:readwrite_multi_second_date_year) + expect(subject).to respond_to(:issue_date_day) + expect(subject).to respond_to(:issue_date_month) + expect(subject).to respond_to(:issue_date_year) - expect(subject).to respond_to(:readwrite_multi_second_date_day=) - expect(subject).to respond_to(:readwrite_multi_second_date_month=) - expect(subject).to respond_to(:readwrite_multi_second_date_year=) + expect(subject).to respond_to(:issue_date_day=) + expect(subject).to respond_to(:issue_date_month=) + expect(subject).to respond_to(:issue_date_year=) end end describe "#date_writer" do - it 'should allow the use of the a _day writer' do - expect(subject).to respond_to(:write_date_day=) - - expect(subject.write_date).to be_nil - - subject.write_date_day = 12 - expect(subject.write_date).to eq(Date.new.change(day: 12)) - end - - it 'should allow the use of the a _month writer' do - expect(subject).to respond_to(:write_date_month=) - - expect(subject.write_date).to be_nil - - subject.write_date_month = 5 - expect(subject.write_date).to eq(Date.new.change(month: 5)) - end - - it 'should allow the use of the a _year writer' do - expect(subject).to respond_to(:write_date_year=) - - expect(subject.write_date).to be_nil - - subject.write_date_year = 1990 - expect(subject.write_date).to eq(Date.new.change(year: 1990)) - end - - it 'should have garbage-resistant writers' do - subject.write_date_day = '' - - expect(subject.write_date).to be_nil - - subject.write_date_day = "foo" - - expect(subject.write_date).to be_nil - - subject.write_date_day = 0 - - expect(subject.write_date).to be_nil + it 'should allow the use of the a writer for all values' do + subject.expiration_date = nil + subject.expiration_date_day = 1 + subject.expiration_date_month = 2 + subject.expiration_date_year = year + subject.valid? + expect(subject.expiration_date).to eq(Date.new(year, 2, 1)) end - it 'should not change the value when writing garbage' do - subject.write_date = Date.new(1990, 5, 12) - - subject.write_date_day = "foo" - - expect(subject.write_date).to eq(Date.new(1990, 5, 12)) + context "with a date set" do + context "and an invalid submitted value to update" do + it "is not set" do + subject.expiration_date_day = 21 + subject.expiration_date_year = nil + subject.valid? + expect(subject.expiration_date).to eq(expiration_date) + end + end end end describe "#date_reader" do - it 'should allow the use of the a _day reader' do - expect(subject).to respond_to(:read_date_day) - - expect(subject.read_date_day).to be_nil - - subject.read_date = Date.new(1990, 5, 12) - - expect(subject.read_date_day).to eq(12) - end - - it 'should allow the use of the a _month reader' do - expect(subject).to respond_to(:read_date_month) - - expect(subject.read_date_month).to be_nil - - subject.read_date = Date.new(1990, 5, 12) - - expect(subject.read_date_month).to eq(5) - end - - it 'should allow the use of the a _year reader' do - expect(subject).to respond_to(:read_date_year) - - expect(subject.read_date_year).to be_nil - - subject.read_date = Date.new(1990, 5, 12) - - expect(subject.read_date_year).to eq(1990) + context "when the date is not set" do + let(:expiration_date) { nil } + + it 'should allow the use of the a _day reader' do + expect(subject).to respond_to(:expiration_date_day) + expect(subject.expiration_date_day).to be_nil + end + + it 'should allow the use of the a _month reader' do + expect(subject).to respond_to(:expiration_date_month) + expect(subject.expiration_date_month).to be_nil + end + + it 'should allow the use of the a _year reader' do + expect(subject).to respond_to(:expiration_date_year) + expect(subject.expiration_date_year).to be_nil + end end - end - - describe "#change_date_property" do - it 'should create a date if one does not exist'do - expect(subject.readwrite_date).to be_nil - - subject.send(:change_date_property, :readwrite_date, day: 12) - - expect(subject.readwrite_date.day).to eq(12) - end - - it 'should only change the date fragements specified' do - subject.readwrite_date = Date.new(1990, 5, 28) - expect(subject.readwrite_date).to eq(Date.new(1990, 5, 28)) - - subject.send(:change_date_property, :readwrite_date, day: 12) - - expect(subject.readwrite_date.day).to eq(12) - expect(subject.readwrite_date.month).to eq(5) - expect(subject.readwrite_date.year).to eq(1990) - end - - it 'should not consider fragments other than month, day, or year' do - subject.readwrite_date = Date.new(1990, 5, 12) - expect(subject.readwrite_date).to eq(Date.new(1990, 5, 12)) - - subject.send(:change_date_property, :readwrite_date, foo: "bar") - expect(subject.readwrite_date.day).to eq(12) - expect(subject.readwrite_date.month).to eq(5) - expect(subject.readwrite_date.year).to eq(1990) + context "when the date is set" do + it 'should allow the use of the a _day reader' do + expect(subject).to respond_to(:expiration_date_day) + expect(subject.expiration_date_day).to eq(day) + end + + it 'should allow the use of the a _month reader' do + expect(subject).to respond_to(:expiration_date_month) + expect(subject.expiration_date_month).to eq(month) + end + + it 'should allow the use of the a _year reader' do + expect(subject).to respond_to(:expiration_date_year) + expect(subject.expiration_date_year).to eq(year) + end end end end