Skip to content

Commit

Permalink
Run eligibility check feature (#2463)
Browse files Browse the repository at this point in the history
* Eligibility check backend changes (#2460)

* adds run eligibility check controller action and routes

* adds specs

* adds run eligibility route

* Eligibility check authorization (#2465)

* adds pundit policy for admin access only

* fixes typo

* fixes spec failures

* Run eligibility check front (#2464)

* Implement eligibility checks in front-end UI

* removed placeholder button

* Fixed failing specs

* fixed mistyped javascript

* Rails renders button_tag as a form with an input field, why selector was not being found.  Working specs

* Added Specs for Eligibility Check Partial

* Moved Eligibility Spec to its correct directory

* Added Translations for Run Eligibility button

* resolved rubocop errors for files that aren't slim templates

* fixed more rubocop issues, more are failing

* removed comment

* fixed route mispell

* Added translations for the response text

* fixed UI breaking from translations update

---------

Co-authored-by: Utkarsh Shukla <utkarsh7989@gmail.com>

* adds condition to display the button during OE period

* PR revisions

* fixes the row alignment issue

---------

Co-authored-by: TonyHasIdeas <118292503+TonyHasIdeas@users.noreply.github.com>
Co-authored-by: Antonio Irizarry <antonio.irizarry@ideacrew.com>
Co-authored-by: Sri Harsha <sriharsha.poosa@gmail.com>
  • Loading branch information
4 people authored Nov 8, 2023
1 parent 69c038b commit f777c09
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 3 deletions.
19 changes: 19 additions & 0 deletions app/assets/stylesheets/custom.scss.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4976,3 +4976,22 @@ input:required:valid {
color: gray;
background-color: #80808026;
}

.eligibility-status-text{
font-size: 18px;
}
.is-eligible-checkmark{color: green;}
.eligibility-response-close-icon{
font-size: 24px;
font-weight: bold;
display: block;
float: right;
}
.run-eligibility-check-response{
padding-top: 20px;
display: block;
width: 100%;
}
.eligibility-response-close-icon{
cursor: pointer;
}
113 changes: 113 additions & 0 deletions app/views/ui-components/v1/cards/_eligibility_check.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
.panel.panel-default#employee-enrollments
.panel-body
.run-eligibility-check-btn-container
= button_to l10n("employers.plan_years.eligibility_button_text"), '/', class: "btn btn-default", id: "eligibilityCheckButton", data: { url: profiles_employers_employer_profile_run_eligibility_check_path(@employer_profile) }
.col-xs-12.loading.run-eligibility-processing(style="display: none;")
i.fa.fa-spinner.fa-spin.fa-2x
.run-eligibility-check-response-container(style="display: none;")
span.eligibility-response-close-icon
| X
.run-eligibility-check-response
p.eligibility-status-text.minimum-participation
=> l10n("employers.plan_years.minimum_participation_text")
| &nbsp;&nbsp;
span.not-eligible
| &#10060;
span.is-eligible-checkmark style="display:none;"
| &#10004;
p.eligibility-status-text.non-business-owner-eligibility-count
=> l10n("employers.plan_years.non-business_owner_eligibility_count_text")
| &nbsp;&nbsp;
span.not-eligible
| &#10060;
span.is-eligible-checkmark style="display:none;"
| &#10004;
p.eligibility-status-text.minimum-eligible-member-count
=> l10n("employers.plan_years.minimum_eligible_member_count_text")
| &nbsp;&nbsp;
span.not-eligible
| &#10060;
span.is-eligible-checkmark style="display:none;"
| &#10004;


javascript:
document.getElementById('eligibilityCheckButton').addEventListener('click', function() {
this.disabled = true;
// Show Spinner
$('#eligibilityCheckButton').hide();
$('.run-eligibility-processing').show();
// Fetch the URL from the data attribute
var url = this.getAttribute('data-url');
$.ajax({
type: 'GET',
data: {},
url: url,
}).done(function(response) {
$('.run-eligibility-processing').hide();
$('.run-eligibility-check-response-container').show();
interpretResponse = interpretValidation(response);
updateEligibilityUI(interpretResponse);
});
function interpretValidation(response) {
// List of all attributes we are checking
const attributes = ['minimum_participation_rule', 'non_business_owner_enrollment_count', 'minimum_eligible_member_count'];
let result = {};
for (let attribute of attributes) {
// If the attribute exists in the response and its value is 'validated successfully', then it's valid.
if (response[attribute] && response[attribute] === 'validated successfully') {
result[attribute] = true;
} else if (response[attribute]) { // If the attribute exists in the response and its value isn't 'validated successfully', then it's invalid.
result[attribute] = false;
} else { // If the attribute doesn't exist in the response, then it's valid.
result[attribute] = true;
}
}
return result;
}
});
function updateEligibilityUI(interpretedResponse) {
// Getting all the p tags within the parent div
const eligibilityStatusElements = document.querySelectorAll(".run-eligibility-check-response .eligibility-status-text");
eligibilityStatusElements.forEach(element => {
// Based on the class of the p tag, deciding which attribute we are currently processing
let attribute;
if (element.classList.contains("minimum-participation")) {
attribute = "minimum_participation_rule";
} else if (element.classList.contains("non-business-owner-eligibility-count")) {
attribute = "non_business_owner_enrollment_count";
} else if (element.classList.contains("minimum-eligible-member-count")) {
attribute = "minimum_eligible_member_count";
}
if (interpretedResponse[attribute]) {
// If true, show the checkmark and hide the cross
element.querySelector('.is-eligible-checkmark').style.display = 'inline';
element.querySelector('.not-eligible').style.display = 'none';
} else {
// If false, show the cross and hide the checkmark
element.querySelector('.is-eligible-checkmark').style.display = 'none';
element.querySelector('.not-eligible').style.display = 'inline';
}
});
}
document.querySelector('.eligibility-response-close-icon').addEventListener('click', function() {
// Hide the container
$('.run-eligibility-check-response-container').hide();
// Hide the 'is-eligible-checkmark' and show the 'not-eligible' icon (original state)
$('.is-eligible-checkmark').hide();
$('.not-eligible').show();
// Show the button and activate
$('#eligibilityCheckButton').show().prop('disabled', false);
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Profiles
module Employers
class EmployerProfilesController < ::BenefitSponsors::ApplicationController

before_action :find_employer, only: [:show, :inbox, :bulk_employee_upload, :export_census_employees, :coverage_reports, :download_invoice, :show_invoice, :estimate_cost]
before_action :find_employer, only: [:show, :inbox, :bulk_employee_upload, :export_census_employees, :coverage_reports, :download_invoice, :show_invoice, :estimate_cost, :run_eligibility_check]
before_action :load_group_enrollments, only: [:coverage_reports], if: :is_format_csv?
before_action :check_and_download_invoice, only: [:download_invoice, :show_invoice]
before_action :wells_fargo_sso, only: [:show]
Expand Down Expand Up @@ -61,6 +61,7 @@ def show # TODO - Each when clause should be a seperate action.
if @benefit_sponsorship.present?
@broker_agency_accounts = @benefit_sponsorship.broker_agency_accounts
@current_plan_year = @benefit_sponsorship.submitted_benefit_application(include_term_pending: false)
@business_policy = business_policy_for(@current_plan_year)
end

collect_and_sort_invoices(params[:sort_order])
Expand All @@ -87,6 +88,19 @@ def coverage_reports
end
end

def run_eligibility_check
authorize @employer_profile
benefit_sponsorship = @employer_profile.latest_benefit_sponsorship
benefit_application = benefit_sponsorship.submitted_benefit_application(include_term_pending: false)
business_policy = business_policy_for(benefit_application)
eligibility_hash = if business_policy.is_satisfied?(benefit_application)
business_policy.success_results
else
business_policy.fail_results
end
render :json => eligibility_hash
end

def export_census_employees
authorize @employer_profile
respond_to do |format|
Expand Down Expand Up @@ -316,6 +330,11 @@ def user_not_authorized(exception)
session[:custom_url] = main_app.new_user_registration_path unless current_user
super
end

def business_policy_for(benefit_application)
enrollment_eligibility_policy = BenefitSponsors::BenefitApplications::AcaShopEnrollmentEligibilityPolicy.new
enrollment_eligibility_policy.business_policies_for(benefit_application, :end_open_enrollment)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,11 @@ def can_list_enrollments?
def can_modify_employer?
user.person.hbx_staff_role.permission.modify_employer
end

def run_eligibility_check?
return false unless user.present?

user.has_hbx_staff_role?
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- if @current_plan_year.present?
= render partial: 'ui-components/v1/cards/employee_enrollments'
= render partial: 'ui-components/v1/cards/plan_year'
- if current_user.has_hbx_staff_role? && @current_plan_year.open_enrollment_contains?(TimeKeeper.date_of_record)
= render partial: 'ui-components/v1/cards/eligibility_check'
- @current_plan_year.benefit_groups.each do |bg|
= render partial: 'ui-components/v1/cards/benefit_groups', locals: { bg: bg }
- else
Expand Down
1 change: 1 addition & 0 deletions components/benefit_sponsors/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
post :bulk_employee_upload
get :coverage_reports
get :estimate_cost
get :run_eligibility_check
collection do
get :generate_sic_tree
get :show_pending
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,28 @@ module BenefitSponsors
expect(response).to have_http_status(:success)
end
end

describe "GET run_eligibility_check" do
let(:admin_user) { FactoryGirl.create(:user, :with_hbx_staff_role, :person => person)}
let!(:employees) { FactoryGirl.create_list(:census_employee, 2, employer_profile: employer_profile, benefit_sponsorship: benefit_sponsorship)}
let(:business_policy) { instance_double("some_policy", success_results: { business_rule: "validated successfully" })}

before do
sign_in admin_user
allow(subject).to receive(:business_policy_for).and_return(business_policy)
allow(business_policy).to receive(:is_satisfied?).and_return(true)
get :run_eligibility_check, employer_profile_id: benefit_sponsor.profiles.first.id
allow(employer_profile).to receive(:active_benefit_sponsorship).and_return benefit_sponsorship
end

it "should return http success" do
expect(response).to have_http_status(:success)
end

it 'responds with a json content type' do
expect(response.content_type).to include('application/json')
expect(JSON.parse(response.body, symoblize_names: true)).to include("business_rule" => "validated successfully")
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,18 @@ module BenefitSponsors
it_behaves_like 'should not permit for person with active employer staff role', :coverage_reports?
it_behaves_like 'should not permit for person with active employer staff role', :updateable?
end

context 'for a user with admin role' do
let(:user) { FactoryGirl.create(:user, :with_hbx_staff_role, person: person) }

shared_examples_for 'should permit for a user with hbx staff role' do |policy_type|
it 'should permit for admin role' do
expect(policy.send(policy_type)).to be true
end
end

it_behaves_like 'should permit for a user with hbx staff role', :show?
it_behaves_like 'should permit for a user with hbx staff role', :run_eligibility_check?
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
require "rails_helper"

RSpec.describe "components/benefit_sponsors/app/views/benefit_sponsors/profiles/employers/employer_profiles/my_account/home_tab.html.slim" do

context "employer profile dashboard with current plan year" do

let(:start_on){TimeKeeper.date_of_record.beginning_of_year}
let(:end_on){TimeKeeper.date_of_record.end_of_year}
let(:end_on_negative){ TimeKeeper.date_of_record.beginning_of_year - 2.years }
let(:active_employees) { double("CensusEmployee", count: 10) }
let(:mock_user) do
instance_double(
"User",
has_hbx_staff_role?: false
)
end


def new_organization
Expand Down Expand Up @@ -189,7 +196,8 @@ def plan_year
aasm_state: 'draft',
predecessor_id: nil,
employee_participation_ratio_minimum: 0.75,
employer_profile: double(census_employees: double(active: active_employees))
employer_profile: double(census_employees: double(active: active_employees)),
open_enrollment_contains?: true
)
end

Expand Down Expand Up @@ -270,11 +278,13 @@ def reference_product
end

before :each do
view.extend BenefitSponsors::Engine.routes.url_helpers
allow(::BenefitSponsors::Services::SponsoredBenefitCostEstimationService).to receive(:new).and_return(cost_estimator)
allow(cost_estimator).to receive(:calculate_estimates_for_home_display).and_return(estimator)
allow(view).to receive(:pundit_class).and_return(double("EmployerProfilePolicy", updateable?: true))
allow(view).to receive(:policy_helper).and_return(double("EmployerProfilePolicy", updateable?: true))


assign :employer_profile, employer_profile
assign :hbx_enrollments, [hbx_enrollment]
assign :current_plan_year, employer_profile.published_plan_year
Expand All @@ -285,6 +295,7 @@ def reference_product

context "when employer setting is enabled" do
it "should display the employer external links advertisement" do
allow(view).to receive(:current_user).and_return(mock_user)
EnrollRegistry[:add_external_links].feature.stub(:is_enabled).and_return(true)
EnrollRegistry[:add_external_links].setting(:employer_display).stub(:item).and_return(true)

Expand All @@ -297,6 +308,7 @@ def reference_product

context "when employer setting is disabled" do
it "should not display the employer external links advertisement" do
allow(view).to receive(:current_user).and_return(mock_user)
EnrollRegistry[:add_external_links].feature.stub(:is_enabled).and_return(true)
EnrollRegistry[:add_external_links].setting(:employer_display).stub(:item).and_return(false)

Expand All @@ -306,5 +318,41 @@ def reference_product
expect(rendered).not_to include("href=\"https://www.mahealthconnector.org/business/employers/connectwell-for-employers")
end
end

context "when user is an admin" do
before do
allow(mock_user).to receive(:has_hbx_staff_role?).and_return(true)
allow(view).to receive(:current_user).and_return(mock_user)
render "benefit_sponsors/profiles/employers/employer_profiles/my_account/home_tab"
end

it "renders the 'Run Eligibility Check' button" do
expect(rendered).to have_selector('input#eligibilityCheckButton')
end
end

context "when user is not an admin" do
before do
allow(mock_user).to receive(:has_hbx_staff_role?).and_return(false)
allow(view).to receive(:current_user).and_return(mock_user)
render "benefit_sponsors/profiles/employers/employer_profiles/my_account/home_tab"
end

it "does not render the 'Run Eligibility Check' button" do
expect(rendered).not_to have_selector('input#eligibilityCheckButton')
end
end

context "when current plan year is in open enrollment period" do
before do
allow(mock_user).to receive(:has_hbx_staff_role?).and_return(true)
allow(view).to receive(:current_user).and_return(mock_user)
render "benefit_sponsors/profiles/employers/employer_profiles/my_account/home_tab"
end

it "renders the 'Run Eligibility Check' button" do
expect(rendered).to have_selector('input#eligibilityCheckButton')
end
end
end
end
6 changes: 6 additions & 0 deletions db/seedfiles/translations/en/cca/employer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# MA Census Employee Translations
EMPLOYER_TRANSLATIONS = {
:'en.employers.plan_years.reference_plan_details' => "Reference Plan Details",
Expand Down Expand Up @@ -43,6 +45,10 @@
:'en.employers.plan_years.employee_cost_details_description_part1' => "Details of the employee costs for your members are shown on the",
:'en.employers.plan_years.employee_cost_details_description_part2' => "Values are based on benefit type, contribution amounts, and reference plan chosen.",
:'en.employers.plan_years.estimated_employee_costs' => "Estimated Employee Costs",
:'en.employers.plan_years.eligibility_button_text' => "Run Eligibility Check",
:'en.employers.plan_years.minimum_participation_text' => 'Minimum Participation',
:'en.employers.plan_years.non-business_owner_eligibility_count_text' => 'Non-Business Owner Eligibility Count',
:'en.employers.plan_years.minimum_eligible_member_count_text' => 'Minimum Eligible Member Count',
:'en.employers.registration.kind' => 'Kind *',
:'en.employers.registration.address' => 'Address *',
:'en.employers.registration.city' => 'City *',
Expand Down
Loading

0 comments on commit f777c09

Please sign in to comment.