diff --git a/.github/dependencies.txt b/.github/dependencies.txt new file mode 100644 index 000000000..bdb2dea21 --- /dev/null +++ b/.github/dependencies.txt @@ -0,0 +1,48 @@ +aws-cli +bash +build-base +ca-certificates +chromium +curl +curl-dev +file +file-dev +freetds-dev +freetype +freetype-dev +geos +geos-dev +git +glib +harfbuzz +icu +icu-dev +imagemagick +lftp +libcurl +libgcc +libintl +libmagic +libstdc++ +libx11 +libxext +libxml2-dev +libxrender +libxslt-dev +nodejs +npm +nss +postgis +postgresql +postgresql-dev +shared-mime-info +tmux +ttf-dejavu +ttf-droid +ttf-freefont +ttf-freefont +ttf-freefont +ttf-liberation +tzdata +xz +yarn diff --git a/.github/workflows/asset_compilation.yml b/.github/workflows/asset_compilation.yml index 8774353c4..0fbac5415 100644 --- a/.github/workflows/asset_compilation.yml +++ b/.github/workflows/asset_compilation.yml @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install GNU tar (required by cache step and checksumming) run: | @@ -58,34 +58,16 @@ jobs: DATABASE_DB_TEST: cas_test REPORTING_DATABASE_DB_TEST: reporting_test run: | - apk add --no-cache \ - nodejs yarn npm \ - aws-cli \ - tzdata \ - git \ - bash \ - freetds-dev \ - icu icu-dev \ - curl libcurl curl-dev \ - imagemagick \ - libmagic file-dev file \ - build-base libxml2-dev libxslt-dev postgresql-dev \ - libgcc libstdc++ libx11 glib libxrender libxext libintl ttf-dejavu ttf-droid ttf-freefont ttf-liberation ttf-freefont \ - chromium nss freetype freetype-dev harfbuzz ca-certificates ttf-freefont \ - lftp postgresql tmux postgis geos geos-dev xz \ - shared-mime-info \ - xz + apk add --no-cache $(cat .github/dependencies.txt) echo "postgres:5432:*:postgres:postgres" > ~/.pgpass chmod 600 ~/.pgpass - gem install bundler --version=2.5.17 - # According to https://www.jessesquires.com/blog/2021/08/23/caching-bundler-on-github-actions/ # this is fragile and failure prone, but the step they recommend using instead (ruby/ruby-setup) # isn't supported on Alpine. - name: cache gems - uses: actions/cache@v3 + uses: actions/cache@v4 id: gemcache with: path: | @@ -102,6 +84,7 @@ jobs: - name: Install gems if: always() run: | + gem install bundler --version=2.5.17 bundle config set --local without 'production staging development' bundle install --jobs 10 --retry 3 diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 000000000..1f80560fa --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,37 @@ +name: Bundle Audit and Brakeman + +on: + push: + branches: + - '*' + - '**/*' +concurrency: + group: ${{ github.ref }}-audit + cancel-in-progress: true + +jobs: + audit: + runs-on: ubuntu-20.04 + # Docker Hub image that the job executes in + container: ruby:3.1.6-alpine3.20 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up dependencies + run: | + apk add --no-cache $(cat .github/dependencies.txt) + + - name: 'Install gems' + run: | + gem install bundler --version=2.5.17 + bundle config set --local without 'production staging development' + bundle install --jobs 4 --retry 3 + + - name: Run bundle-audit + run: | + bundle exec bundle-audit check --update + - name: Run brakeman + run: | + bundle exec brakeman -q --no-pager --except PermitAttributes,Render diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 40fec87fc..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Bundle Audit and Brakeman - -on: - push: - branches: - - '*' - - '**/*' - # NOTE: you don't need to build all PRs if you are also building all branches - # pull_request: - # branches: - # - '*' - # - '**/*' -jobs: - build: - runs-on: ubuntu-20.04 - # Docker Hub image that the job executes in - container: ruby:3.1.6-alpine3.20 - - # Service containers to run with job - services: - postgres: - image: postgres:13-alpine - env: - POSTGRES_PASSWORD: postgres - POSTGRES_USER: postgres - POSTGRES_PASS: postgres - POSTGRES_MULTIPLE_EXTENSIONS: hstore - # Set health checks to wait until postgres has started - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - - redis: - image: redis:alpine - ports: - - 6379:6379 - - env: - DATABASE_WAREHOUSE_DB_TEST: test_hmis_warehouse - DATABASE_DB_TEST: boston_cas_test - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up dependencies - - run: | - apk add --no-cache \ - nodejs yarn npm \ - tzdata \ - git \ - bash \ - freetds-dev \ - icu icu-dev \ - curl libcurl curl-dev \ - imagemagick \ - libmagic file-dev file \ - build-base libxml2-dev libxslt-dev postgresql-dev \ - libgcc libstdc++ libx11 glib libxrender libxext libintl ttf-dejavu ttf-droid ttf-freefont ttf-liberation ttf-freefont \ - freetype freetype-dev harfbuzz ca-certificates ttf-freefont \ - postgresql tmux postgis geos geos-dev xz \ - shared-mime-info - - echo "postgres:5432:*:postgres:postgres" > ~/.pgpass - chmod 600 ~/.pgpass - - yarn install --frozen-lockfile - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true - gem install bundler -v '< 2' - - - name: 'Install gems' - run: | - bundle install --jobs 4 --retry 3 - - - name: Run bundle-audit - run: | - bundle exec bundle-audit check --update - - name: Run brakeman - run: | - bundle exec brakeman -q --no-pager --except PermitAttributes,Render diff --git a/.github/workflows/rails_tests.yml b/.github/workflows/rails_tests.yml index ff6bef0d4..8284ff8d5 100644 --- a/.github/workflows/rails_tests.yml +++ b/.github/workflows/rails_tests.yml @@ -5,11 +5,9 @@ on: branches: - '*' - '**/*' - # NOTE: you don't need to build all PRs if you are also building all branches - # pull_request: - # branches: - # - '*' - # - '**/*' +concurrency: + group: ${{ github.ref }}-tests + cancel-in-progress: true jobs: build: runs-on: ubuntu-20.04 @@ -43,36 +41,34 @@ jobs: DATABASE_WAREHOUSE_DB_TEST: test_hmis_warehouse DATABASE_DB_TEST: boston_cas_test steps: + - name: Install git + run: | + apk add --no-cache git - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up dependencies run: | - apk add --no-cache \ - nodejs yarn npm \ - tzdata \ - git \ - bash \ - freetds-dev \ - icu icu-dev \ - curl libcurl curl-dev \ - imagemagick \ - libmagic file-dev file \ - build-base libxml2-dev libxslt-dev postgresql-dev \ - libgcc libstdc++ libx11 glib libxrender libxext libintl ttf-dejavu ttf-droid ttf-freefont ttf-liberation ttf-freefont \ - freetype freetype-dev harfbuzz ca-certificates ttf-freefont \ - postgresql tmux postgis geos geos-dev xz \ - shared-mime-info + apk add --no-cache $(cat .github/dependencies.txt) echo "postgres:5432:*:postgres:postgres" > ~/.pgpass chmod 600 ~/.pgpass yarn install --frozen-lockfile - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true - gem install bundler -v '< 2' - - name: 'Install gems' + - name: cache gems + uses: actions/cache@v4 + id: gemcache + with: + path: | + vendor/bundle + /usr/local/bundle/ + key: ${{ runner.os }}-gemcache-${{ hashFiles('**/Gemfile.lock') }}-${{ hashFiles('**/.ruby-version') }} + + - name: Install gems run: | + gem install bundler --version=2.5.17 + bundle config set --local without 'production staging development' bundle install --jobs 4 --retry 3 - name: 'App setup' diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index e1f1fb067..379844bd9 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -17,37 +17,36 @@ jobs: # Docker Hub image that the job executes in container: ruby:3.1.6-alpine3.20 steps: - - id: dependencies - name: Set up dependencies + - name: Install git run: | - apk add --no-cache \ - nodejs yarn npm \ - tzdata \ - git \ - bash \ - freetds-dev \ - icu icu-dev \ - curl libcurl curl-dev \ - imagemagick \ - libmagic file-dev file \ - build-base libxml2-dev libxslt-dev postgresql-dev \ - libgcc libstdc++ libx11 glib libxrender libxext libintl ttf-dejavu ttf-droid ttf-freefont ttf-liberation ttf-freefont \ - chromium nss freetype freetype-dev harfbuzz ca-certificates ttf-freefont \ - lftp postgresql postgis geos geos-dev \ - shared-mime-info - gem install bundler --version=2.5.7 + apk add --no-cache git - uses: actions/checkout@v4 with: fetch-depth: 0 + - id: dependencies + name: Set up dependencies + run: | + apk add --no-cache $(cat .github/dependencies.txt) + # ensure changed-files can read repo files from git - name: Configure Git Safe Directory run: git config --global --add safe.directory /__w/boston-cas/boston-cas + - name: cache gems + uses: actions/cache@v4 + id: gemcache + with: + path: | + vendor/bundle + /usr/local/bundle/ + key: ${{ runner.os }}-gemcache-${{ hashFiles('**/Gemfile.lock') }}-${{ hashFiles('**/.ruby-version') }} + - id: gems name: Install gems run: | + gem install bundler --version=2.5.17 bundle config set --local without 'production staging' bundle install --jobs 4 --retry 3 @@ -60,7 +59,9 @@ jobs: env: ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | - files=`echo "${ALL_CHANGED_FILES}" | tr " " "\n" | grep "**/*.rb" | tr "\n" " ./"` + # Ignore files we don't control the format of + ignore_patterns="db/schema.rb\|bin/rails\|bin/rake\|bin/bundle" + files=`echo "${ALL_CHANGED_FILES}" | tr " " "\n" | grep -v $ignore_patterns | grep "**/*.rb" | tr "\n" " ./"` num=`echo $files | wc -w` echo $files if [ $num -gt 0 ]; then bundle exec rubocop --config ./.rubocop.yml $files; else echo "No changed ruby files"; fi diff --git a/app/controllers/reports/agency_interactions_controller.rb b/app/controllers/reports/agency_interactions_controller.rb index 13f8f7788..d191a96f8 100644 --- a/app/controllers/reports/agency_interactions_controller.rb +++ b/app/controllers/reports/agency_interactions_controller.rb @@ -26,6 +26,7 @@ def index def match_decision_reasons_collection MatchDecisionReasons::Base. + active. order(name: :asc). map { |reason| [reason_text(reason), reason.id] } end @@ -33,7 +34,7 @@ def match_decision_reasons_collection def reason_text(match_decision_reason) text = match_decision_reason.name - text << " (#{match_decision_reason.title})" if match_decision_reason.title.present? + text << " (#{match_decision_reason.title})" if match_decision_reason.title.present? && match_decision_reason.title != match_decision_reason.name text end helper_method :reason_text diff --git a/app/models/cas_seeds/match_decision_reasons.rb b/app/models/cas_seeds/match_decision_reasons.rb index 489c2045a..96ea83156 100644 --- a/app/models/cas_seeds/match_decision_reasons.rb +++ b/app/models/cas_seeds/match_decision_reasons.rb @@ -9,236 +9,70 @@ class MatchDecisionReasons CLIENT_REJECTED = 2 PROVIDER_REJECTED = 3 - DND_REASONS = [ - ["Client won't be eligible for services", nil], - ["Client won't be eligible for housing type", nil], - ["Client won't be eligible based on funding source", nil], - ['Client has another housing option', nil], - ].freeze - - HSA_REASONS = [ - ['CORI', PROVIDER_REJECTED], - ['SORI', PROVIDER_REJECTED], - ['Immigration status', nil], - ['Household did not respond after initial acceptance of match', CLIENT_REJECTED], - ['Ineligible for Housing Program', nil], - ['Client refused offer', CLIENT_REJECTED], - ['Self-resolved', nil], - ['Falsification of documents', nil], + # When adding new decline or cancel reasons to CAS, add them here, and indicate the rejection type for CE APR reporting. + # Then in the appropriate route or step, add to step_cancel_reasons or step_decline_reasons + ALL_REASONS = [ ['Additional screening criteria imposed by third parties', PROVIDER_REJECTED], - ['Health and Safety', nil], - ].freeze - - LIMITED_HSA_REASONS = [ + ['Barred from working with agency', PROVIDER_REJECTED], + ['Client deceased', CLIENT_REJECTED], + ['Client has another housing option', CLIENT_REJECTED], + ['Client has declined match', CLIENT_REJECTED], + ['Client has disappeared', CLIENT_REJECTED], + ['Client has disengaged', CLIENT_REJECTED], ['Client needs higher level of care', PROVIDER_REJECTED], - ['Unable to reach client after multiple attempts', nil], - ].freeze - - HSA_PROVIDER_ONLY_REASONS = [ - ['Household could not be located', nil], - ['Ineligible for Housing Program', nil], + ['Client no longer eligible for match', PROVIDER_REJECTED], + ['Client received another housing opportunity', CLIENT_REJECTED], + ['Client receiving navigation services', nil], ['Client refused offer', CLIENT_REJECTED], - ['Health and Safety', nil], - ].freeze - - SHELTER_AGENCY_REASONS = [ - ['Does not agree to services', CLIENT_REJECTED], - ['Unwilling to live in that neighborhood', CLIENT_REJECTED], - ['Unwilling to live in SRO', CLIENT_REJECTED], - ['Does not want housing at this time', CLIENT_REJECTED], - ['Unsafe environment for this person', CLIENT_REJECTED], - ['Client has another housing option', nil], ['Client refused unit (non-SRO)', CLIENT_REJECTED], ['Client refused voucher', CLIENT_REJECTED], - ].freeze - - MITIGATION_REASONS = [ - ['Mitigation failed', nil], - ].freeze - - SHELTER_AGENCY_NOT_WORKING_WITH_CLIENT_REASONS = [ - ['Barred from working with agency', PROVIDER_REJECTED], - ['Hospitalized', nil], + ['Client is already receiving navigation services', CLIENT_REJECTED], + ["Client won't be eligible based on funding source", PROVIDER_REJECTED], + ["Client won't be eligible for housing type", PROVIDER_REJECTED], + ["Client won't be eligible for services", PROVIDER_REJECTED], + ['CORI', PROVIDER_REJECTED], + ['Does not agree to services', CLIENT_REJECTED], + ['Does not want housing at this time', CLIENT_REJECTED], ['Don’t know / disappeared', nil], - ['Incarcerated', nil], - ].freeze - - ADMINISTRATIVE_CANCEL_REASONS = [ + ['Falsification of documents', PROVIDER_REJECTED], + ['Health and Safety', PROVIDER_REJECTED], + ['Hospitalized', nil], + ['Household became disengaged', nil], + ['Household could not be located', PROVIDER_REJECTED], + ['Household did not respond after initial acceptance of match', PROVIDER_REJECTED], + ['Housing Case Manager has disengaged', PROVIDER_REJECTED], + ['HSP CORI', PROVIDER_REJECTED], + ['Immigration status', PROVIDER_REJECTED], + ['In Treatment/Recovery Center', CLIENT_REJECTED], + ['Incarcerated', CLIENT_REJECTED], + ['Institutionalized', CLIENT_REJECTED], + ['Ineligible for Housing Program', PROVIDER_REJECTED], + ['Match expired – Agency interaction', nil], + ['Match expired – No agency interaction', PROVIDER_REJECTED], + ['Match expired – No Housing Case Manager interaction', PROVIDER_REJECTED], + ['Match expired – No Shelter Agency interaction', PROVIDER_REJECTED], ['Match expired', nil], - ['Client has declined match', CLIENT_REJECTED], - ['Client has disengaged', CLIENT_REJECTED], - ['Client has disappeared', CLIENT_REJECTED], + ['Match stalled - Agency has disengaged', PROVIDER_REJECTED], + ['Match stalled – Housing Case Manager has disengaged', PROVIDER_REJECTED], + ['Mitigation failed', nil], + ['Self-resolved', CLIENT_REJECTED], + ['Shelter Agency has disengaged', PROVIDER_REJECTED], + ['SORI', PROVIDER_REJECTED], ['SSP CORI', PROVIDER_REJECTED], - ['HSP CORI', PROVIDER_REJECTED], - ['Incarcerated', nil], + ['Unable to reach client after multiple attempts', PROVIDER_REJECTED], + ['Unsafe environment for this person', CLIENT_REJECTED], + ['Unwilling to live in SRO', CLIENT_REJECTED], + ['Unwilling to live in that neighborhood', CLIENT_REJECTED], + ['Vacancy filled by other client', PROVIDER_REJECTED], ['Vacancy should not have been entered', nil], - ['Client received another housing opportunity', nil], - ['Client no longer eligible for match', nil], - ['Client deceased', nil], - ['Vacancy filled by other client', nil], - ['Client receiving navigation services', nil], - ].freeze - - LIMITED_ADMINISTRATIVE_CANCEL_REASONS = [ - ['Match expired – No agency interaction', nil], - ['Match expired – Agency interaction', nil], - ['Match stalled - Agency has disengaged', nil], - ['Match expired – No Housing Case Manager interaction', nil], - ['Match expired – No Shelter Agency interaction', nil], - ['Shelter Agency has disengaged', nil], - ['Housing Case Manager has disengaged', nil], - ['Match stalled – Housing Case Manager has disengaged', nil], - ['Client needs higher level of care', nil], - ['Unable to reach client after multiple attempts', nil], - ].freeze - - NINE_RECORD_VOUCHER_DATE_DECLINE_REASONS = [ - [' Household became disengaged', nil], - ].freeze - - NINE_CASE_CONTACT_ASSIGNS_MANAGER_DECLINE_REASONS = [].freeze - - CASE_CONTACT_ASSIGNS_MANAGER_DECLINE_REASONS = [].freeze - - TEN_DECLINE_REASONS = [ - ['Immigration status', nil], - ['Household did not respond after initial acceptance of match', nil], - ['Ineligible for Housing Program', nil], - ['Client refused offer', nil], - ['Self-resolved', nil], - ['Client needs higher level of care', nil], - ['Unable to reach client after multiple attempts', nil], - ].freeze - - OTHER = [ ['Other', nil], ].freeze def run! - create_other_reason! - create_dnd_reasons! - create_hsa_reasons! - create_hsa_provider_only_reasons! - create_shelter_agency_reasons! - create_shelter_agency_not_working_with_client_reasons! - create_shelter_agency_not_working_with_client_other_reason! - create_admin_cancel_reasons! - create_mitigation_reasons! - create_record_voucher_decline_reasons! - create_case_contact_assigns_manager_decline_reasons! - create_nine_case_contact_assigns_manager_decline_reasons! - create_ten_decline_reasons! - create_base_reasons! - end - - private def create_base_reasons! - reasons = DND_REASONS + - HSA_REASONS + - LIMITED_HSA_REASONS + - HSA_PROVIDER_ONLY_REASONS + - SHELTER_AGENCY_REASONS + - MITIGATION_REASONS + - SHELTER_AGENCY_NOT_WORKING_WITH_CLIENT_REASONS + - ADMINISTRATIVE_CANCEL_REASONS + - LIMITED_ADMINISTRATIVE_CANCEL_REASONS + - OTHER - reasons.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::All.where(name: reason_name).first_or_create! - reason.update(referral_result: referral_result) - end - end - - private def create_other_reason! - reason = ::MatchDecisionReasons::Other.all.first_or_create! name: 'Other' - reason.update(referral_result: nil) - end - - private def create_dnd_reasons! - DND_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::DndStaffDecline.where(name: reason_name).first_or_create! + ALL_REASONS.each do |reason_name, referral_result| + reason = ::MatchDecisionReasons::Base.where(name: reason_name).first_or_create! reason.update(referral_result: referral_result) end end - - private def create_hsa_reasons! - HSA_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::HousingSubsidyAdminDecline.where(name: reason_name).first_or_create! - reason.update(referral_result: referral_result) - end - - LIMITED_HSA_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::HousingSubsidyAdminDecline.where(name: reason_name, limited: true).first_or_create! - reason.update(referral_result: referral_result) - end - end - - private def create_hsa_provider_only_reasons! - ::MatchDecisionReasons::HousingSubsidyAdminPriorityDecline.update_all(active: false) - HSA_PROVIDER_ONLY_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::HousingSubsidyAdminPriorityDecline.where(name: reason_name).first_or_create! - reason.update(active: true, referral_result: referral_result) - end - end - - private def create_shelter_agency_reasons! - SHELTER_AGENCY_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::ShelterAgencyDecline.where(name: reason_name).first_or_create! - reason.update(referral_result: referral_result) - end - end - - private def create_shelter_agency_not_working_with_client_reasons! - SHELTER_AGENCY_NOT_WORKING_WITH_CLIENT_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::ShelterAgencyNotWorkingWithClient.where(name: reason_name).first_or_create! - reason.update(referral_result: referral_result) - end - end - - private def create_shelter_agency_not_working_with_client_other_reason! - reason = ::MatchDecisionReasons::ShelterAgencyNotWorkingWithClientOther.all.first_or_create! name: 'Other' - reason.update(referral_result: nil) - end - - private def create_admin_cancel_reasons! - ADMINISTRATIVE_CANCEL_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::AdministrativeCancel.where(name: reason_name).first_or_create! - reason.update(referral_result: referral_result) - end - - LIMITED_ADMINISTRATIVE_CANCEL_REASONS.each do |reason_name, referral_result| - reason = ::MatchDecisionReasons::AdministrativeCancel.where(name: reason_name, limited: true).first_or_create! - reason.update(referral_result: referral_result) - end - end - - private def create_mitigation_reasons! - MITIGATION_REASONS.each do |reason_name, _| - ::MatchDecisionReasons::MitigationDecline.where(name: reason_name).first_or_create! - end - end - - private def create_record_voucher_decline_reasons! - NINE_RECORD_VOUCHER_DATE_DECLINE_REASONS.each do |reason_name, _| - ::MatchDecisionReasons::NineRecordVoucherDateDecline.where(name: reason_name).first_or_create! - end - end - - private def create_case_contact_assigns_manager_decline_reasons! - CASE_CONTACT_ASSIGNS_MANAGER_DECLINE_REASONS.each do |reason_name, _| - ::MatchDecisionReasons::CaseContactAssignsManagerDecline.where(name: reason_name).first_or_create! - end - end - - private def create_nine_case_contact_assigns_manager_decline_reasons! - NINE_CASE_CONTACT_ASSIGNS_MANAGER_DECLINE_REASONS.each do |reason_name, _| - ::MatchDecisionReasons::NineCaseContactAssignsManagerDecline.where(name: reason_name).first_or_create! - end - end - - private def create_ten_decline_reasons! - TEN_DECLINE_REASONS.each do |reason_name, _| - ::MatchDecisionReasons::RouteTenDeclineReasons.where(name: reason_name).first_or_create! - end - end end end diff --git a/app/models/client_opportunity_match.rb b/app/models/client_opportunity_match.rb index 98e4091fd..7ab036cfa 100644 --- a/app/models/client_opportunity_match.rb +++ b/app/models/client_opportunity_match.rb @@ -452,7 +452,7 @@ def current_decision return nil if closed? later_decisions_first = match_route.class.match_steps_for_reporting.keys.reverse - @current_decision ||= initialized_decisions.order_as_specified(type: later_decisions_first).find_by(status: [:pending, :acknowledged]) + @current_decision ||= initialized_decisions.order_as_specified(type: later_decisions_first).find_by(status: [:pending, :acknowledged, :expiration_update]) @current_decision ||= initialized_decisions.order_as_specified(type: later_decisions_first).limit(1).first end diff --git a/app/models/concerns/match_decisions/accepts_decline_reason.rb b/app/models/concerns/match_decisions/accepts_decline_reason.rb index 7a99e8138..effdc4226 100644 --- a/app/models/concerns/match_decisions/accepts_decline_reason.rb +++ b/app/models/concerns/match_decisions/accepts_decline_reason.rb @@ -12,30 +12,16 @@ module AcceptsDeclineReason validate :validate_decline_reason end - # FIXME: remove 'include_other' after migration - def decline_reasons(include_other: true, contact:) + def decline_reasons(contact:) @_decline_reasons ||= [].tap do |result| # rubocop:disable Naming/MemoizedInstanceVariableName - # FIXME: remove if/else after migration - if respond_to?(:step_decline_reasons) - MatchDecisionReasons::All.where(name: step_decline_reasons(contact)).find_each do |reason| - result << reason - end - else - decline_reason_scope(contact).each do |reason| - result << reason - end - result << MatchDecisionReasons::Other.first if include_other + MatchDecisionReasons::Base.active.where(name: step_decline_reasons(contact)).find_each do |reason| + result << reason end # Move other to the end of the list result.sort_by! { |m| m.name.downcase == 'other' ? 1 : 0 } end end - private def decline_reason_scope(_contact) - # just the base scope. other gets included automatically by #decline_reasons - raise 'abstract method not implemented' - end - def whitelist_params_for_update params result = super reason_id_array = Array.wrap params.require(:decision)[:decline_reason_id] diff --git a/app/models/concerns/match_decisions/route_nine_cancel_reasons.rb b/app/models/concerns/match_decisions/route_nine_cancel_reasons.rb new file mode 100644 index 000000000..3a9e68e84 --- /dev/null +++ b/app/models/concerns/match_decisions/route_nine_cancel_reasons.rb @@ -0,0 +1,29 @@ +### +# Copyright 2016 - 2024 Green River Data Analysis, LLC +# +# License detail: https://github.com/greenriver/boston-cas/blob/production/LICENSE.md +### + +module MatchDecisions + module RouteNineCancelReasons + extend ActiveSupport::Concern + + def step_cancel_reasons + [ + 'Match expired', + 'Vacancy should not have been entered', + 'Client received another housing opportunity', + 'Client no longer eligible for match', + 'Client deceased', + 'Incarcerated', + 'Vacancy filled by other client', + 'Client has declined match', + 'Client has disengaged', + 'Client has disappeared', + 'Client needs higher level of care', + 'Unable to reach client after multiple attempts', + 'Other', + ] + end + end +end diff --git a/app/models/concerns/match_decisions/route_nine_decline_reasons.rb b/app/models/concerns/match_decisions/route_nine_decline_reasons.rb new file mode 100644 index 000000000..cb095a825 --- /dev/null +++ b/app/models/concerns/match_decisions/route_nine_decline_reasons.rb @@ -0,0 +1,21 @@ +### +# Copyright 2016 - 2024 Green River Data Analysis, LLC +# +# License detail: https://github.com/greenriver/boston-cas/blob/production/LICENSE.md +### + +module MatchDecisions + module RouteNineDeclineReasons + extend ActiveSupport::Concern + + def step_decline_reasons + [ + "Client won't be eligible for services", + "Client won't be eligible for housing type", + "Client won't be eligible based on funding source", + 'Client has another housing option', + 'Other', + ] + end + end +end diff --git a/app/models/concerns/match_decisions/route_thirteen_cancel_reasons.rb b/app/models/concerns/match_decisions/route_thirteen_cancel_reasons.rb index 92d5bf4f0..c5ddc03c7 100644 --- a/app/models/concerns/match_decisions/route_thirteen_cancel_reasons.rb +++ b/app/models/concerns/match_decisions/route_thirteen_cancel_reasons.rb @@ -36,7 +36,6 @@ def step_cancel_reasons reasons << 'Client deceased' reasons << 'Vacancy filled by other client' unless pre_hsa_decision? || match_success_decision? reasons << 'Health and Safety' unless match_success_decision? - reasons << 'Do not allow other matches for this vacancy' unless match_success_decision? reasons << 'Other' end end diff --git a/app/models/concerns/pathways_version_four_calculations.rb b/app/models/concerns/pathways_version_four_calculations.rb index 936bd6c87..4d4fd265a 100644 --- a/app/models/concerns/pathways_version_four_calculations.rb +++ b/app/models/concerns/pathways_version_four_calculations.rb @@ -65,6 +65,10 @@ def tie_breaker_date end end + def family_member + pregnant_or_parent + end + def self.export_fields(assessment_name) shared = super.merge( { diff --git a/app/models/match_decision_reasons/administrative_cancel.rb b/app/models/match_decision_reasons/administrative_cancel.rb index 7932a124e..d73b57458 100644 --- a/app/models/match_decision_reasons/administrative_cancel.rb +++ b/app/models/match_decision_reasons/administrative_cancel.rb @@ -7,7 +7,7 @@ module MatchDecisionReasons class AdministrativeCancel < Base def self.available(include_other: true, route: nil) # rubocop:disable Lint/UnusedMethodArgument - other = MatchDecisionReasons::Other.first + other = MatchDecisionReasons::Base.other.first # none = OpenStruct.new(name: 'None', id: nil) av = active.to_a # av << none @@ -21,7 +21,7 @@ def self.available(include_other: true, route: nil) # rubocop:disable Lint/Unuse end def self.available_for_provider_only(include_other: true, route: nil) # rubocop:disable Lint/UnusedMethodArgument - other = MatchDecisionReasons::Other.first + other = MatchDecisionReasons::Base.other.first # none = OpenStruct.new(name: 'None', id: nil) av = active.where(name: provider_only_options).to_a av << other if include_other diff --git a/app/models/match_decision_reasons/all.rb b/app/models/match_decision_reasons/all.rb index 7ee08bfe8..8b15a97b8 100644 --- a/app/models/match_decision_reasons/all.rb +++ b/app/models/match_decision_reasons/all.rb @@ -4,6 +4,7 @@ # License detail: https://github.com/greenriver/boston-cas/blob/production/LICENSE.md ### +# TODO: remove all non Base MatchDecisionReasons after release-63 is deployed to production module MatchDecisionReasons class All < Base def title diff --git a/app/models/match_decision_reasons/base.rb b/app/models/match_decision_reasons/base.rb index 9930e1b97..88fc069a9 100644 --- a/app/models/match_decision_reasons/base.rb +++ b/app/models/match_decision_reasons/base.rb @@ -6,16 +6,8 @@ module MatchDecisionReasons class Base < ApplicationRecord - # Pick-list reasons associated with a particular match decision - # Reasons are currently split up by actor type - # + special other option and shelter agency - # no longer working with the client - # Per requirements in https://www.pivotaltracker.com/story/show/118185729 - # Feel free to change how available reasons are associated with decisions - # as the requirements evolve - # ~@rrosen, 5/24/2016 - self.table_name = 'match_decision_reasons' + acts_as_paranoid has_many :decisions, class_name: 'MatchDecisions::Base', foreign_key: :decline_reason_id @@ -29,15 +21,11 @@ class Base < ApplicationRecord validates :name, presence: true def title - type.demodulize.underscore.humanize + name end def other? name == 'Other' end - - def not_working_with_client? - false - end end end diff --git a/app/models/match_decision_reasons/housing_subsidy_admin_decline.rb b/app/models/match_decision_reasons/housing_subsidy_admin_decline.rb index 264068b9a..78df6ed11 100644 --- a/app/models/match_decision_reasons/housing_subsidy_admin_decline.rb +++ b/app/models/match_decision_reasons/housing_subsidy_admin_decline.rb @@ -7,7 +7,7 @@ module MatchDecisionReasons class HousingSubsidyAdminDecline < Base def self.available(include_other: false, route: nil) - other = MatchDecisionReasons::Other.first + other = MatchDecisionReasons::Base.other.first # none = OpenStruct.new(name: 'None', id: nil) av = active.to_a # av << none diff --git a/app/models/match_decision_reasons/other.rb b/app/models/match_decision_reasons/other.rb index 25cffce8f..035139cf2 100644 --- a/app/models/match_decision_reasons/other.rb +++ b/app/models/match_decision_reasons/other.rb @@ -6,10 +6,6 @@ module MatchDecisionReasons class Other < Base - def other? - true - end - def title '' end diff --git a/app/models/match_decision_reasons/shelter_agency_not_working_with_client.rb b/app/models/match_decision_reasons/shelter_agency_not_working_with_client.rb index 41528121e..8c85d5be8 100644 --- a/app/models/match_decision_reasons/shelter_agency_not_working_with_client.rb +++ b/app/models/match_decision_reasons/shelter_agency_not_working_with_client.rb @@ -6,10 +6,6 @@ module MatchDecisionReasons class ShelterAgencyNotWorkingWithClient < Base - def not_working_with_client? - true - end - def title "#{Translation.translate('Shelter Agency')} Not Working with Client" end diff --git a/app/models/match_decision_reasons/shelter_agency_not_working_with_client_other.rb b/app/models/match_decision_reasons/shelter_agency_not_working_with_client_other.rb index bfa37b299..7fefdc828 100644 --- a/app/models/match_decision_reasons/shelter_agency_not_working_with_client_other.rb +++ b/app/models/match_decision_reasons/shelter_agency_not_working_with_client_other.rb @@ -6,14 +6,6 @@ module MatchDecisionReasons class ShelterAgencyNotWorkingWithClientOther < Base - def other? - true - end - - def not_working_with_client? - true - end - def title "#{Translation.translate('Shelter Agency')} Not Working with Client Other" end diff --git a/app/models/match_decisions/eleven/eleven_hsa_acknowledges_receipt.rb b/app/models/match_decisions/eleven/eleven_hsa_acknowledges_receipt.rb index 72c39e6a5..219282505 100644 --- a/app/models/match_decisions/eleven/eleven_hsa_acknowledges_receipt.rb +++ b/app/models/match_decisions/eleven/eleven_hsa_acknowledges_receipt.rb @@ -70,7 +70,7 @@ def initialize_decision! send_notifications: true end def accessible_by? contact - contact.user_can_act_on_behalf_of_match_contacts? + contact.user_can_act_on_behalf_of_match_contacts? || contact.in?(match.send(contact_actor_type)) end diff --git a/app/models/match_decisions/nine/nine_assign_case_contact.rb b/app/models/match_decisions/nine/nine_assign_case_contact.rb index cd8341674..02aa8eb4a 100644 --- a/app/models/match_decisions/nine/nine_assign_case_contact.rb +++ b/app/models/match_decisions/nine/nine_assign_case_contact.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineAssignCaseContact < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons validate :ensure_required_contacts_present_on_accept diff --git a/app/models/match_decisions/nine/nine_assign_manager.rb b/app/models/match_decisions/nine/nine_assign_manager.rb index b78f97361..eb7dcb3de 100644 --- a/app/models/match_decisions/nine/nine_assign_manager.rb +++ b/app/models/match_decisions/nine/nine_assign_manager.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineAssignManager < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons validate :manager_present_if_status_complete diff --git a/app/models/match_decisions/nine/nine_confirm_assign_manager_decline.rb b/app/models/match_decisions/nine/nine_confirm_assign_manager_decline.rb index 8817c3b04..9d536e73f 100644 --- a/app/models/match_decisions/nine/nine_confirm_assign_manager_decline.rb +++ b/app/models/match_decisions/nine/nine_confirm_assign_manager_decline.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineConfirmAssignManagerDecline < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons def step_name "#{Translation.translate('DND')} confirms case manager assignment decline" diff --git a/app/models/match_decisions/nine/nine_confirm_lease_up_decline.rb b/app/models/match_decisions/nine/nine_confirm_lease_up_decline.rb index a5a745beb..4cc30e50f 100644 --- a/app/models/match_decisions/nine/nine_confirm_lease_up_decline.rb +++ b/app/models/match_decisions/nine/nine_confirm_lease_up_decline.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineConfirmLeaseUpDecline < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons def step_name "#{Translation.translate('DND')} confirms #{Translation.translate('Move In')} failure" diff --git a/app/models/match_decisions/nine/nine_confirm_voucher_decline.rb b/app/models/match_decisions/nine/nine_confirm_voucher_decline.rb index 6636c516e..52bfd8e9d 100644 --- a/app/models/match_decisions/nine/nine_confirm_voucher_decline.rb +++ b/app/models/match_decisions/nine/nine_confirm_voucher_decline.rb @@ -6,7 +6,7 @@ module MatchDecisions::Nine class NineConfirmVoucherDecline < ::MatchDecisions::Base - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineCancelReasons def statuses { diff --git a/app/models/match_decisions/nine/nine_lease_up.rb b/app/models/match_decisions/nine/nine_lease_up.rb index a44795ee0..eaaec7bc9 100644 --- a/app/models/match_decisions/nine/nine_lease_up.rb +++ b/app/models/match_decisions/nine/nine_lease_up.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineLeaseUp < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons validate :client_move_in_date_present_if_status_complete diff --git a/app/models/match_decisions/nine/nine_match_recommendation.rb b/app/models/match_decisions/nine/nine_match_recommendation.rb index 61061b09e..ee00a92d7 100644 --- a/app/models/match_decisions/nine/nine_match_recommendation.rb +++ b/app/models/match_decisions/nine/nine_match_recommendation.rb @@ -7,8 +7,8 @@ module MatchDecisions::Nine class NineMatchRecommendation < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason + include MatchDecisions::RouteNineDeclineReasons include MatchDecisions::DefaultDndStaffDeclineReasons - include MatchDecisions::RouteEightCancelReasons validate :cant_accept_if_match_closed validate :cant_accept_if_related_active_match diff --git a/app/models/match_decisions/nine/nine_record_voucher_date.rb b/app/models/match_decisions/nine/nine_record_voucher_date.rb index d99d202d0..884e1dec3 100644 --- a/app/models/match_decisions/nine/nine_record_voucher_date.rb +++ b/app/models/match_decisions/nine/nine_record_voucher_date.rb @@ -7,7 +7,8 @@ module MatchDecisions::Nine class NineRecordVoucherDate < ::MatchDecisions::Base include MatchDecisions::AcceptsDeclineReason - include MatchDecisions::RouteEightCancelReasons + include MatchDecisions::RouteNineDeclineReasons + include MatchDecisions::RouteNineCancelReasons attr_accessor :rfta_submitted @@ -106,17 +107,21 @@ def accessible_by? contact # :nine_record_voucher_date_housing_subsidy_admin # end - private def decline_reason_scope(_contact) - reasons = [ + def step_decline_reasons(_contact) + [ + 'Client has another housing option', + 'Client needs higher level of care', + 'Client refused offer', + "Client won't be eligible based on funding source", + "Client won't be eligible for housing type", + "Client won't be eligible for services", + 'Household became disengaged', 'Immigration status', 'Ineligible for Housing Program', 'Self-resolved', - 'Client refused offer', - 'Client needs higher level of care', 'Unable to reach client after multiple attempts', + 'Other', ] - MatchDecisionReasons::Base.where(type: 'MatchDecisionReasons::All', name: reasons). - or(MatchDecisionReasons::NineRecordVoucherDateDecline.all) end # Only record an event if we moved forward (which only happens if rfta_submitted is checked) diff --git a/app/models/match_decisions/thirteen/base.rb b/app/models/match_decisions/thirteen/base.rb new file mode 100644 index 000000000..0798e198c --- /dev/null +++ b/app/models/match_decisions/thirteen/base.rb @@ -0,0 +1,14 @@ +### +# Copyright 2016 - 2024 Green River Data Analysis, LLC +# +# License detail: https://github.com/greenriver/boston-cas/blob/production/LICENSE.md +### + +module MatchDecisions::Thirteen + class Base < ::MatchDecisions::Base + def accessible_by?(contact) + contact.user_can_act_on_behalf_of_match_contacts? || + contact.in?(match.send(contact_actor_type)) + end + end +end diff --git a/app/models/match_decisions/thirteen/thirteen_accept_referral.rb b/app/models/match_decisions/thirteen/thirteen_accept_referral.rb index 73254dbd1..35a01f66d 100644 --- a/app/models/match_decisions/thirteen/thirteen_accept_referral.rb +++ b/app/models/match_decisions/thirteen/thirteen_accept_referral.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenAcceptReferral < ::MatchDecisions::Base + class ThirteenAcceptReferral < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenCancelReasons include MatchDecisions::RouteThirteenDeclineReasons diff --git a/app/models/match_decisions/thirteen/thirteen_accept_referral_decline.rb b/app/models/match_decisions/thirteen/thirteen_accept_referral_decline.rb index 1aca3154e..25dc505bf 100644 --- a/app/models/match_decisions/thirteen/thirteen_accept_referral_decline.rb +++ b/app/models/match_decisions/thirteen/thirteen_accept_referral_decline.rb @@ -5,13 +5,13 @@ ### module MatchDecisions::Thirteen - class ThirteenAcceptReferralDecline < ::MatchDecisions::Base + class ThirteenAcceptReferralDecline < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenDeclineReasons include MatchDecisions::RouteThirteenCancelReasons def to_partial_path - 'match_decisions/thirteen/hsa_review_decline' + 'match_decisions/thirteen/accept_referral_decline' end def step_name diff --git a/app/models/match_decisions/thirteen/thirteen_client_match.rb b/app/models/match_decisions/thirteen/thirteen_client_match.rb index 405506f4b..4025c308c 100644 --- a/app/models/match_decisions/thirteen/thirteen_client_match.rb +++ b/app/models/match_decisions/thirteen/thirteen_client_match.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenClientMatch < ::MatchDecisions::Base + class ThirteenClientMatch < Base include MatchDecisions::RouteThirteenCancelReasons validate :ensure_required_contacts_present_on_accept diff --git a/app/models/match_decisions/thirteen/thirteen_client_review.rb b/app/models/match_decisions/thirteen/thirteen_client_review.rb index 932398d4d..116a2b627 100644 --- a/app/models/match_decisions/thirteen/thirteen_client_review.rb +++ b/app/models/match_decisions/thirteen/thirteen_client_review.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenClientReview < ::MatchDecisions::Base + class ThirteenClientReview < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenCancelReasons include MatchDecisions::RouteThirteenDeclineReasons diff --git a/app/models/match_decisions/thirteen/thirteen_client_review_decline.rb b/app/models/match_decisions/thirteen/thirteen_client_review_decline.rb index 13885077b..1f13b95ce 100644 --- a/app/models/match_decisions/thirteen/thirteen_client_review_decline.rb +++ b/app/models/match_decisions/thirteen/thirteen_client_review_decline.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenClientReviewDecline < ::MatchDecisions::Base + class ThirteenClientReviewDecline < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenDeclineReasons include MatchDecisions::RouteThirteenCancelReasons diff --git a/app/models/match_decisions/thirteen/thirteen_confirm_match_success.rb b/app/models/match_decisions/thirteen/thirteen_confirm_match_success.rb index 737c9375f..aa1ed4b5a 100644 --- a/app/models/match_decisions/thirteen/thirteen_confirm_match_success.rb +++ b/app/models/match_decisions/thirteen/thirteen_confirm_match_success.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenConfirmMatchSuccess < ::MatchDecisions::Base + class ThirteenConfirmMatchSuccess < Base include MatchDecisions::RouteEightCancelReasons def statuses diff --git a/app/models/match_decisions/thirteen/thirteen_hearing_outcome.rb b/app/models/match_decisions/thirteen/thirteen_hearing_outcome.rb index 40e799973..1b130535f 100644 --- a/app/models/match_decisions/thirteen/thirteen_hearing_outcome.rb +++ b/app/models/match_decisions/thirteen/thirteen_hearing_outcome.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHearingOutcome < ::MatchDecisions::Base + class ThirteenHearingOutcome < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenCancelReasons include MatchDecisions::RouteThirteenDeclineReasons diff --git a/app/models/match_decisions/thirteen/thirteen_hearing_outcome_decline.rb b/app/models/match_decisions/thirteen/thirteen_hearing_outcome_decline.rb index 570213e53..7aafae108 100644 --- a/app/models/match_decisions/thirteen/thirteen_hearing_outcome_decline.rb +++ b/app/models/match_decisions/thirteen/thirteen_hearing_outcome_decline.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHearingOutcomeDecline < ::MatchDecisions::Base + class ThirteenHearingOutcomeDecline < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenDeclineReasons include MatchDecisions::RouteThirteenCancelReasons diff --git a/app/models/match_decisions/thirteen/thirteen_hearing_scheduled.rb b/app/models/match_decisions/thirteen/thirteen_hearing_scheduled.rb index 79b2b1eb3..6dc547c3f 100644 --- a/app/models/match_decisions/thirteen/thirteen_hearing_scheduled.rb +++ b/app/models/match_decisions/thirteen/thirteen_hearing_scheduled.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHearingScheduled < ::MatchDecisions::Base + class ThirteenHearingScheduled < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenCancelReasons include MatchDecisions::RouteThirteenDeclineReasons diff --git a/app/models/match_decisions/thirteen/thirteen_hearing_scheduled_decline.rb b/app/models/match_decisions/thirteen/thirteen_hearing_scheduled_decline.rb index 9a9121828..9522e1abb 100644 --- a/app/models/match_decisions/thirteen/thirteen_hearing_scheduled_decline.rb +++ b/app/models/match_decisions/thirteen/thirteen_hearing_scheduled_decline.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHearingScheduledDecline < ::MatchDecisions::Base + class ThirteenHearingScheduledDecline < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenDeclineReasons include MatchDecisions::RouteThirteenCancelReasons diff --git a/app/models/match_decisions/thirteen/thirteen_hsa_review.rb b/app/models/match_decisions/thirteen/thirteen_hsa_review.rb index 767fe2ca9..c49a9d6b4 100644 --- a/app/models/match_decisions/thirteen/thirteen_hsa_review.rb +++ b/app/models/match_decisions/thirteen/thirteen_hsa_review.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHsaReview < ::MatchDecisions::Base + class ThirteenHsaReview < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenCancelReasons include MatchDecisions::RouteThirteenDeclineReasons diff --git a/app/models/match_decisions/thirteen/thirteen_hsa_review_decline.rb b/app/models/match_decisions/thirteen/thirteen_hsa_review_decline.rb index f26ef2f39..b6fa39354 100644 --- a/app/models/match_decisions/thirteen/thirteen_hsa_review_decline.rb +++ b/app/models/match_decisions/thirteen/thirteen_hsa_review_decline.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenHsaReviewDecline < ::MatchDecisions::Base + class ThirteenHsaReviewDecline < Base include MatchDecisions::AcceptsDeclineReason include MatchDecisions::RouteThirteenDeclineReasons include MatchDecisions::RouteThirteenCancelReasons diff --git a/app/models/match_decisions/thirteen/thirteen_match_acknowledgement.rb b/app/models/match_decisions/thirteen/thirteen_match_acknowledgement.rb index 500b5d786..459657889 100644 --- a/app/models/match_decisions/thirteen/thirteen_match_acknowledgement.rb +++ b/app/models/match_decisions/thirteen/thirteen_match_acknowledgement.rb @@ -5,7 +5,7 @@ ### module MatchDecisions::Thirteen - class ThirteenMatchAcknowledgement < ::MatchDecisions::Base + class ThirteenMatchAcknowledgement < Base include MatchDecisions::RouteThirteenCancelReasons validate :ensure_required_contacts_present_on_accept diff --git a/app/models/match_decisions/twelve/twelve_agency_acknowledges_receipt.rb b/app/models/match_decisions/twelve/twelve_agency_acknowledges_receipt.rb index ed83b014f..364d54244 100644 --- a/app/models/match_decisions/twelve/twelve_agency_acknowledges_receipt.rb +++ b/app/models/match_decisions/twelve/twelve_agency_acknowledges_receipt.rb @@ -71,7 +71,7 @@ def initialize_decision! send_notifications: true end def accessible_by? contact - contact.user_can_act_on_behalf_of_match_contacts? + contact.user_can_act_on_behalf_of_match_contacts? || contact.in?(match.send(contact_actor_type)) end diff --git a/app/models/match_routes/base.rb b/app/models/match_routes/base.rb index 9759e8127..ee5f24062 100644 --- a/app/models/match_routes/base.rb +++ b/app/models/match_routes/base.rb @@ -92,7 +92,7 @@ def auto_initialize_event? # The number of the step in match_steps of the first step where a client interaction is required private def first_client_step_number - self.class.match_steps[first_client_step] + self.class.match_steps_for_reporting[first_client_step] end def on_or_after_first_client_step?(current_decision) @@ -100,15 +100,15 @@ def on_or_after_first_client_step?(current_decision) end def on_first_client_step?(current_decision) - return false unless self.class.match_steps[current_decision.class.name].present? + return false unless self.class.match_steps_for_reporting[current_decision.class.name].present? - self.class.match_steps[current_decision.class.name] == first_client_step_number + self.class.match_steps_for_reporting[current_decision.class.name] == first_client_step_number end def after_first_client_step?(current_decision) - return false unless self.class.match_steps[current_decision.class.name].present? + return false unless self.class.match_steps_for_reporting[current_decision.class.name].present? - self.class.match_steps[current_decision.class.name] > first_client_step_number + self.class.match_steps_for_reporting[current_decision.class.name] > first_client_step_number end def success_decision diff --git a/app/models/match_routes/eleven.rb b/app/models/match_routes/eleven.rb index ccc21d327..db0454f4e 100644 --- a/app/models/match_routes/eleven.rb +++ b/app/models/match_routes/eleven.rb @@ -25,7 +25,7 @@ def self.match_steps_for_reporting { 'MatchDecisions::Eleven::ElevenHsaAcknowledgesReceipt' => 1, 'MatchDecisions::Eleven::ElevenHsaAcceptsClient' => 2, - 'MatchDecisions::Eleven::ElevenConfirmHsaAcceptsClientDeclineDndStaff' => 2, + 'MatchDecisions::Eleven::ElevenConfirmHsaAcceptsClientDeclineDndStaff' => 3, } end diff --git a/app/models/warehouse/build_report.rb b/app/models/warehouse/build_report.rb index 8eb1909df..23a24eaf8 100644 --- a/app/models/warehouse/build_report.rb +++ b/app/models/warehouse/build_report.rb @@ -122,7 +122,8 @@ def fill_cas_report_table! end end - match_rows.flatten! + # Oddly, calling `match_rows.flatten!` turns match_rows into nil when it is an empty array + match_rows = match_rows.flatten.compact # Replace reporting decisions data Reporting::Decisions.transaction do diff --git a/app/views/match_decisions/_decline_reason.haml b/app/views/match_decisions/_decline_reason.haml index 161427233..ef98f8b99 100644 --- a/app/views/match_decisions/_decline_reason.haml +++ b/app/views/match_decisions/_decline_reason.haml @@ -1,7 +1,7 @@ - client_action ||= 'decline' - include_other = true unless include_other === false -= form.association :decline_reason, collection: @decision.decline_reasons(include_other: include_other, contact: current_contact), label: "Please indicate a reason to #{client_action} this match:", as: :check_boxes, wrapper_html: {class: 'jDeclineReasons jButtonStateTriggers'} += form.association :decline_reason, collection: @decision.decline_reasons(contact: current_contact), label: "Please indicate a reason to #{client_action} this match:", as: :check_boxes, wrapper_html: {class: 'jDeclineReasons jButtonStateTriggers'} - if include_other = form.input :decline_reason_other_explanation, as: :text, label: "List any details related to the #{client_action} below", hint: 'Required for "Other"', placeholder: 'Other reason', input_html: {rows: 8}, wrapper_html: {class: 'jOtherDeclineReason'} diff --git a/app/views/match_decisions/eleven/_confirm_hsa_accepts_client_decline_dnd_staff.haml b/app/views/match_decisions/eleven/_confirm_hsa_accepts_client_decline_dnd_staff.haml index 052f1ef15..27f6ccbab 100644 --- a/app/views/match_decisions/eleven/_confirm_hsa_accepts_client_decline_dnd_staff.haml +++ b/app/views/match_decisions/eleven/_confirm_hsa_accepts_client_decline_dnd_staff.haml @@ -1,11 +1,6 @@ .match-decision.c-card.c-card--flush.card--block .c-card__content = simple_form_for @decision, url: access_context.match_decision_path(@match, @decision) do |form| - .o-pre-choose - .form-inputs - .row - .col-md-6 - = form.input :note, as: :text, input_html: {rows: 4, disabled: (!@decision.editable?)} .o-choose.o-choose--flush - if can_reject_matches? .o-choose__choice diff --git a/app/views/match_decisions/eleven/_hsa_accepts_client.haml b/app/views/match_decisions/eleven/_hsa_accepts_client.haml index 21f90f078..014aa53ce 100644 --- a/app/views/match_decisions/eleven/_hsa_accepts_client.haml +++ b/app/views/match_decisions/eleven/_hsa_accepts_client.haml @@ -4,8 +4,7 @@ = render 'match_decisions/shelter_agency_expiration', form: form - if ! @match.shelter_expiration? || @match.shelter_expiration >= Date.current || can_reject_matches? .o-pre-choose - %p After contacting the client and obtaining client consent to proceed and all necessary paperwork submit this form to indicate acceptance of the match for all parties. - = form.input :note, as: :text, input_html: {rows: 4, disabled: (!@decision.editable?)} + %p.mb-0 After contacting the client and obtaining client consent to proceed and all necessary paperwork submit this form to indicate acceptance of the match for all parties. .o-choose.o-choose--flush .o-choose__choice %header diff --git a/db/migrate/20240807193023_add_deleted_at_to_match_decision_reasons.rb b/db/migrate/20240807193023_add_deleted_at_to_match_decision_reasons.rb new file mode 100644 index 000000000..f560070d0 --- /dev/null +++ b/db/migrate/20240807193023_add_deleted_at_to_match_decision_reasons.rb @@ -0,0 +1,5 @@ +class AddDeletedAtToMatchDecisionReasons < ActiveRecord::Migration[7.0] + def change + add_column :match_decision_reasons, :deleted_at, :datetime + end +end diff --git a/db/migrate/20240807193306_cleanup_decision_reasons.rb b/db/migrate/20240807193306_cleanup_decision_reasons.rb new file mode 100644 index 000000000..d9a4b6dd8 --- /dev/null +++ b/db/migrate/20240807193306_cleanup_decision_reasons.rb @@ -0,0 +1,19 @@ +class CleanupDecisionReasons < ActiveRecord::Migration[7.0] + def up + # This generates a hash of reasons keyed by name with ascending ids as the value + reasons = MatchDecisionReasons::Base.all.pluck(:name, :id).group_by(&:shift).transform_values(&:flatten).transform_values(&:sort) + reasons.each do |name, ids| + # Nothing to do if we only have one of these + next if ids.size == 1 + + to_keep = ids.first + to_delete = ids.drop(1) + MatchDecisions::Base.where(decline_reason_id: to_delete).update_all(decline_reason_id: to_keep) + MatchDecisions::Base.where(administrative_cancel_reason_id: to_delete).update_all(administrative_cancel_reason_id: to_keep) + # Delete the duplicate records using an obvious and consistent delete date so we can find them later + MatchDecisionReasons::Base.where(id: to_delete).update_all(deleted_at: Date.current) + # Make sure all kept records are active + MatchDecisionReasons::Base.where(id: to_keep).update_all(active: true) + end + end +end diff --git a/db/migrate/20240807201535_remove_type_from_decision_reasons.rb b/db/migrate/20240807201535_remove_type_from_decision_reasons.rb new file mode 100644 index 000000000..29ed67045 --- /dev/null +++ b/db/migrate/20240807201535_remove_type_from_decision_reasons.rb @@ -0,0 +1,5 @@ +class RemoveTypeFromDecisionReasons < ActiveRecord::Migration[7.0] + def change + remove_column :match_decision_reasons, :type, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index eb851595f..8576041bc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_08_05_133313) do +ActiveRecord::Schema[7.0].define(version: 2024_08_07_201535) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -625,14 +625,13 @@ create_table "match_decision_reasons", id: :serial, force: :cascade do |t| t.string "name", null: false - t.string "type", null: false t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.boolean "active", default: true, null: false t.boolean "ineligible_in_warehouse", default: false, null: false t.integer "referral_result" t.boolean "limited", default: false - t.index ["type"], name: "index_match_decision_reasons_on_type" + t.datetime "deleted_at" end create_table "match_decisions", id: :serial, force: :cascade do |t| diff --git a/docs/developer_setup.md b/docs/developer_setup.md index 3615c9f97..99553df71 100644 --- a/docs/developer_setup.md +++ b/docs/developer_setup.md @@ -27,4 +27,12 @@ docker-compose run --rm web ## Accessing the Site -If everything worked as designed your site should now be available at [https://boston-cas.dev.test](https://boston-cas.dev.test). Any mail that the site sends will be delivered to [MailHog](https://github.com/mailhog/MailHog) which is availble at [https://mail.boston-cas.dev.test](https://mail.boston-cas.dev.test) \ No newline at end of file +If everything worked as designed your site should now be available at [https://boston-cas.dev.test](https://boston-cas.dev.test). Any mail that the site sends will be delivered to [MailHog](https://github.com/mailhog/MailHog) which is availble at [https://mail.boston-cas.dev.test](https://mail.boston-cas.dev.test) + +## Additional Notes + +Depending on how your development environment's root permission are set, you may run into some issues with the web app-user not having required permissions on some sub-folders. The following command may clean up any folder permissions that are needed for the web user to work with these folders. + +`docker compose run -u 0 --entrypoint='' web chown -R app-user:app-user /node_modules /bundle /app /log /tmp` + +You may need to replace or add to the `/node_modules /bundle /app /log /tmp` section to include the directory needing a permission reset. \ No newline at end of file diff --git a/spec/factories/match_decision_reasons/dnd_staff_decline.rb b/spec/factories/match_decision_reasons/dnd_staff_decline.rb index f03be747e..4172aac93 100644 --- a/spec/factories/match_decision_reasons/dnd_staff_decline.rb +++ b/spec/factories/match_decision_reasons/dnd_staff_decline.rb @@ -5,9 +5,8 @@ ### FactoryBot.define do - factory :dnd_staff_decline_reason, class: 'MatchDecisionReasons::DndStaffDecline' do + factory :dnd_staff_decline_reason, class: 'MatchDecisionReasons::Base' do name { 'Client won\'t be eligible for housing type' } - type { 'MatchDecisionReasons::HousingSubsidyAdminDecline' } active { true } ineligible_in_warehouse { false } end diff --git a/spec/factories/match_decision_reasons/housing_subsidy_admin_decline.rb b/spec/factories/match_decision_reasons/housing_subsidy_admin_decline.rb index a1941e2d2..2a7dc22e5 100644 --- a/spec/factories/match_decision_reasons/housing_subsidy_admin_decline.rb +++ b/spec/factories/match_decision_reasons/housing_subsidy_admin_decline.rb @@ -5,9 +5,8 @@ ### FactoryBot.define do - factory :hsa_decline_reason, class: 'MatchDecisionReasons::HousingSubsidyAdminDecline' do + factory :hsa_decline_reason, class: 'MatchDecisionReasons::Base' do name { 'Ineligible for Housing Program' } - type { 'MatchDecisionReasons::HousingSubsidyAdminDecline' } active { true } ineligible_in_warehouse { true } end