Repository: department-of-veterans-affairs/vets-api
Pull request #21981: Replace Virtus with Vets::Model - Prescriptions
Author: login: stevenjcumming
Labels: name: test-passing
name: require-backend-approval
Created: 2025-04-30T21:23:08Z | Merged: 2025-05-19T12:38:05Z

Review & Merge This PR first:

- https://github.com/department-of-veterans-affairs/vets-api/pull/22118

## Summary

- The virtus gem ([source](https://github.com/solnic/virtus)) is discontinued and must be replaced
- The replacement is `Vets::Model` ([source](https://github.com/department-of-veterans-affairs/vets-api/blob/master/lib/vets/model.rb))
- This PR replaces `Virtus.model` with `Vets::Model` and updates the attributes according 
    - The attribute updates are minor and mostly related to Booleans and Arrays
- This change should not affect any functionality. 
- Some changes from hash notation to dot notation were required

## Related Issue

- https://github.com/department-of-veterans-affairs/va.gov-team/issues/92439

## Testing

- [x] Make sure the specs pass
- [x] Manually checking the serialized responses are the same

## Acceptance Criteria 

- [x] Serialized responses are identical
- [x] No functionality changes


Diff:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 1ca4aa5f795..d91d1b34a31 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -316,6 +316,7 @@ app/models/submission_attempt.rb @department-of-veterans-affairs/pension-and-bur
 app/models/supporting_evidence_attachment.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
 app/models/terms_of_use_agreement.rb @department-of-veterans-affairs/octo-identity
 app/models/tooltip.rb @department-of-veterans-affairs/vfs-mhv-medications @department-of-veterans-affairs/backend-review-group
+app/models/tracking.rb @department-of-veterans-affairs/vfs-mhv-medications @department-of-veterans-affairs/backend-review-group
 app/models/transaction_notification.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
 app/models/triage_team.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
 app/models/all_triage_teams.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
diff --git a/app/controllers/concerns/filterable.rb b/app/controllers/concerns/filterable.rb
index 14c0315c7e0..39bc68fe26a 100644
--- a/app/controllers/concerns/filterable.rb
+++ b/app/controllers/concerns/filterable.rb
@@ -29,6 +29,6 @@ def valid_filters?
   end
 
   def filter_params
-    params.require(:filter).permit(Prescription.filterable_attributes.merge(Message.filterable_params))
+    params.require(:filter).permit(Prescription.filterable_params.merge(Message.filterable_params))
   end
 end
diff --git a/app/models/eligible_data_class.rb b/app/models/eligible_data_class.rb
index dd89430ee72..3ddd33c71d2 100644
--- a/app/models/eligible_data_class.rb
+++ b/app/models/eligible_data_class.rb
@@ -1,8 +1,10 @@
 # frozen_string_literal: true
 
-require 'common/models/base'
+require 'vets/model'
 
 # BlueButton EligibleDataClass
-class EligibleDataClass < Common::Base
+class EligibleDataClass
+  include Vets::Model
+
   attribute :name, String
 end
diff --git a/app/models/prescription.rb b/app/models/prescription.rb
index 9fd0c06eb6b..bb35547666f 100644
--- a/app/models/prescription.rb
+++ b/app/models/prescription.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'common/models/base'
+require 'vets/model'
 
 ##
 # Models a Prescription
@@ -12,52 +12,59 @@
 # @!attribute refill_status
 #   @return [String]
 # @!attribute refill_submit_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_remaining
 #   @return [Integer]
 # @!attribute facility_name
 #   @return [String]
 # @!attribute ordered_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute quantity
 #   @return [Integer]
 # @!attribute expiration_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute prescription_number
 #   @return [String]
 # @!attribute prescription_name
 #   @return [String]
 # @!attribute dispensed_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute station_number
 #   @return [String]
 # @!attribute is_refillable
 #   @return [Boolean]
 # @!attribute is_trackable
 #   @return [Boolean]
+# @!attribute metadata
+#   @return [Hash]
 #
-class Prescription < Common::Base
-  attribute :prescription_id, Integer, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+class Prescription
+  include Vets::Model
+
+  attribute :prescription_id, Integer, filterable: %w[eq not_eq]
   attribute :prescription_image, String
-  attribute :refill_status, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
-  attribute :refill_submit_date, Common::UTCTime, sortable: { order: 'DESC' }, filterable: %w[eq not_eq]
-  attribute :refill_date, Common::UTCTime, sortable: { order: 'DESC' }
+  attribute :refill_status, String, filterable: %w[eq not_eq]
+  attribute :refill_submit_date, Vets::Type::UTCTime, filterable: %w[eq not_eq]
+  attribute :refill_date, Vets::Type::UTCTime
   attribute :refill_remaining, Integer
-  attribute :facility_name, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+  attribute :facility_name, String, filterable: %w[eq not_eq]
   attribute :facility_api_name, String
-  attribute :ordered_date, Common::UTCTime, sortable: { order: 'DESC' }
+  attribute :ordered_date, Vets::Type::UTCTime
   attribute :quantity, Integer
-  attribute :expiration_date, Common::UTCTime, filterable: %w[eq lteq gteq]
+  attribute :expiration_date, Vets::Type::UTCTime, filterable: %w[eq lteq gteq]
   attribute :prescription_number, String
   attribute :sig, String
-  attribute :prescription_name, String, sortable: { order: 'ASC', default: true }
-  attribute :dispensed_date, Common::UTCTime, sortable: { order: 'DESC' }
+  attribute :prescription_name, String
+  attribute :dispensed_date, Vets::Type::UTCTime
   attribute :station_number, String
-  attribute :is_refillable, Boolean, filterable: %w[eq not_eq]
-  attribute :is_trackable, Boolean, filterable: %w[eq not_eq]
+  attribute :is_refillable, Bool, filterable: %w[eq not_eq]
+  attribute :is_trackable, Bool, filterable: %w[eq not_eq]
   attribute :cmop_division_phone, String
+  attribute :metadata, Hash, default: {} # rubocop:disable Rails/AttributeDefaultBlockValue
+
+  default_sort_by prescription_name: :asc
 
   alias refillable? is_refillable
   alias trackable? is_trackable
diff --git a/app/models/prescription_details.rb b/app/models/prescription_details.rb
index ac495b8c1ec..2e73fcac698 100644
--- a/app/models/prescription_details.rb
+++ b/app/models/prescription_details.rb
@@ -1,8 +1,10 @@
 # frozen_string_literal: true
 
+require 'vets/model'
+
 class PrescriptionDetails < Prescription
   attribute :cmop_division_phone, String
-  attribute :in_cerner_transition, Boolean
+  attribute :in_cerner_transition, Bool
   attribute :not_refillable_display_message, String
   attribute :cmop_ndc_number, String
   attribute :user_id, Integer
@@ -10,51 +12,57 @@ class PrescriptionDetails < Prescription
   attribute :provider_last_name, String
   attribute :remarks, String
   attribute :division_name, String
-  attribute :modified_date, Common::UTCTime, sortable: { order: 'ASC' }
+  attribute :modified_date, Vets::Type::UTCTime
   attribute :institution_id, String
   attribute :dial_cmop_division_phone, String
-  attribute :disp_status, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+  attribute :disp_status, String, filterable: %w[eq not_eq]
   attribute :ndc, String
   attribute :reason, String
   attribute :prescription_number_index, String
-  attribute :prescription_source, String, sortable: { order: 'ASC' }
+  attribute :prescription_source, String
   attribute :disclaimer, String
   attribute :indication_for_use, String
   attribute :indication_for_use_flag, String
-  attribute :category, String, sortable: { order: 'ASC' }
-  attribute :tracking_list, Array[String]
-  attribute :rx_rf_records, Array[String]
-  attribute :tracking, Boolean
+  attribute :category, String
+  attribute :tracking_list, Hash, array: true
+  attribute :rx_rf_records, Hash, array: true
+  attribute :tracking, Bool
   attribute :orderable_item, String
-  attribute :sorted_dispensed_date
+  attribute :sorted_dispensed_date, Date
   attribute :shape, String
   attribute :color, String
   attribute :back_imprint, String
   attribute :front_imprint, String
-  attribute :grouped_medications, Array[String]
-
-  def sorted_dispensed_date
-    has_refills = try(:rx_rf_records).present?
-    default_date = Date.new(0)
-    last_refill_date = default_date
+  attribute :grouped_medications, String, array: true
 
-    if has_refills
-      refills = rx_rf_records[0][1]
+  default_sort_by prescription_name: :asc
 
-      refills.each do |r|
-        last_dispensed = r.try(:[], :dispensed_date)
-        next if last_dispensed.nil?
+  def rx_rf_records=(records)
+    @rx_rf_records =
+      if records.is_a?(Hash)
+        records[:rf_record]
+      else
+        records&.dig(0, 1)
+      end
+  end
 
-        refill_date = Date.parse(r.try(:[], :dispensed_date))
-        last_refill_date = refill_date if refill_date.present? && refill_date > last_refill_date
+  def tracking_list=(records)
+    @tracking_list =
+      if records.is_a?(Hash)
+        records[:tracking]
+      else
+        records&.dig(0, 1)
       end
-    end
-
-    last_refill_date = nil if last_refill_date == default_date
-    if has_refills && last_refill_date.present? && last_refill_date != default_date
-      last_refill_date.to_date
-    elsif dispensed_date.present?
-      dispensed_date.to_date
-    end
+  end
+
+  def sorted_dispensed_date
+    refill_dates = rx_rf_records&.map { |r| r[:dispensed_date]&.to_date }&.compact
+    last_refill_date = refill_dates&.max
+
+    @sorted_dispensed_date = last_refill_date || dispensed_date&.to_date
+  end
+
+  def cmop_ndc_value
+    rx_rf_records&.pluck(:cmop_ndc_number)&.compact&.first || cmop_ndc_number.presence
   end
 end
diff --git a/app/models/prescription_preference.rb b/app/models/prescription_preference.rb
index 7dd8e8cb7f0..6b56a57fa31 100644
--- a/app/models/prescription_preference.rb
+++ b/app/models/prescription_preference.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'common/models/base'
+require 'vets/model'
 require 'va_profile/models/email'
 ##
 # Models Prescription notification preference
@@ -10,11 +10,11 @@
 # @!attribute rx_flag
 #   @return [Boolean]
 #
-class PrescriptionPreference < Common::Base
-  include ActiveModel::Validations
+class PrescriptionPreference
+  include Vets::Model
 
   attribute :email_address, String
-  attribute :rx_flag, Boolean
+  attribute :rx_flag, Bool
 
   validates :rx_flag, inclusion: { in: [true, false] }
   validates(
diff --git a/app/models/tracking.rb b/app/models/tracking.rb
index 6bab6417b3b..486cac4d256 100644
--- a/app/models/tracking.rb
+++ b/app/models/tracking.rb
@@ -1,20 +1,20 @@
 # frozen_string_literal: true
 
-require 'common/models/base'
+require 'vets/model'
 # Tracking Model
-class Tracking < Common::Base
+class Tracking
+  include Vets::Model
+
   attribute :prescription_id, Integer
   attribute :prescription_name, String
   attribute :prescription_number, String
   attribute :facility_name, String
   attribute :rx_info_phone_number, String
   attribute :ndc_number, String
-  attribute :shipped_date, Common::UTCTime, sortable: { order: 'DESC', default: true }
+  attribute :shipped_date, Vets::Type::UTCTime
   attribute :delivery_service, String
   attribute :tracking_number, String
-  attribute :other_prescriptions, Array[Hash]
+  attribute :other_prescriptions, Hash, array: true
 
-  def <=>(other)
-    -(shipped_date <=> other.shipped_date)
-  end
+  default_sort_by shipped_date: :desc
 end
diff --git a/app/services/mhv_account_type_service.rb b/app/services/mhv_account_type_service.rb
index 9820eb9922e..4dc90f9c38b 100644
--- a/app/services/mhv_account_type_service.rb
+++ b/app/services/mhv_account_type_service.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 require 'bb/client'
+require 'vets/collection'
 
 ##
 # Models logic pertaining to the verification and logging of MHV accounts
@@ -63,8 +64,10 @@ def account_type_known?
 
   def fetch_eligible_data_classes
     if cached_eligible_data_class
-      json = Oj.load(cached_eligible_data_class)
-      Common::Collection.new(::EligibleDataClass, **json.symbolize_keys).members.map(&:name)
+      json = Oj.load(cached_eligible_data_class).symbolize_keys
+      collection = Vets::Collection.new(json[:data], EligibleDataClass, metadata: json[:metadata],
+                                                                        errors: json[:errors])
+      collection.records.map(&:name)
     else
       bb_client = BB::Client.new(session: { user_id: @user.mhv_correlation_id })
       bb_client.authenticate
diff --git a/lib/bb/client.rb b/lib/bb/client.rb
index c39e27bdda5..e3a2aca258a 100644
--- a/lib/bb/client.rb
+++ b/lib/bb/client.rb
@@ -6,6 +6,7 @@
 require 'bb/generate_report_request_form'
 require 'bb/configuration'
 require 'rx/client_session'
+require 'vets/collection'
 
 module BB
   ##
@@ -30,7 +31,7 @@ class Client < Common::Client::Base
     #
     # @note this should be called once per user, will take up to 15 minutes
     #   to process, but its the only way to refresh a user's data
-    # @return [Common::Collection]
+    # @return [Vets::Collection]
     #
     def get_extract_status
       if Flipper.enabled?(:mhv_medical_records_migrate_to_api_gateway)
@@ -42,16 +43,16 @@ def get_extract_status
       end
 
       log_refresh_errors(json[:data]) if refresh_final?(json[:data])
-      Common::Collection.new(ExtractStatus, **json)
+      Vets::Collection.new(json[:data], ExtractStatus, metadata: json[:metadata], errors: json[:errors])
     end
 
     ##
     # Build the checkboxes for the form used to make a generate report request
     #
-    # @return [Common::Collection]
+    # @return [Vets::Collection]
     #
     def get_eligible_data_classes
-      Common::Collection.fetch(::EligibleDataClass, cache_key: cache_key('geteligibledataclass'), ttl: CACHE_TTL) do
+      Vets::Collection.fetch(::EligibleDataClass, cache_key: cache_key('geteligibledataclass'), ttl: CACHE_TTL) do
         if Flipper.enabled?(:mhv_medical_records_migrate_to_api_gateway)
           BB::Configuration.custom_base_path = APIGW_BASE_PATH
           perform(:get, 'v1/bluebutton/ess/geteligibledataclass', nil, token_headers).body
diff --git a/lib/rx/client.rb b/lib/rx/client.rb
index a5fba292fba..fbf81a77a12 100644
--- a/lib/rx/client.rb
+++ b/lib/rx/client.rb
@@ -6,6 +6,7 @@
 require 'rx/client_session'
 require 'rx/rx_gateway_timeout'
 require 'active_support/core_ext/hash/slice'
+require 'vets/collection'
 
 module Rx
   ##
@@ -38,7 +39,7 @@ def request(method, path, params = {}, headers = {}, options = {})
     # @return [Common::Collection[Prescription]]
     #
     def get_active_rxs
-      Common::Collection.fetch(::Prescription, cache_key: cache_key('getactiverx'), ttl: CACHE_TTL_ZERO) do
+      Vets::Collection.fetch(::Prescription, cache_key: cache_key('getactiverx'), ttl: CACHE_TTL_ZERO) do
         perform(:get, get_path('getactiverx'), nil, get_headers(token_headers)).body
       end
     end
@@ -49,7 +50,7 @@ def get_active_rxs
     # @return [Common::Collection[PrescriptionDetails]]
     #
     def get_active_rxs_with_details
-      Common::Collection.fetch(::PrescriptionDetails, cache_key: cache_key('getactiverx'), ttl: CACHE_TTL) do
+      Vets::Collection.fetch(::PrescriptionDetails, cache_key: cache_key('getactiverx'), ttl: CACHE_TTL) do
         perform(:get, get_path('getactiverx'), nil, get_headers(token_headers)).body
       end
     end
@@ -60,7 +61,7 @@ def get_active_rxs_with_details
     # @return [Common::Collection[Prescription]]
     #
     def get_history_rxs
-      Common::Collection.fetch(::Prescription, cache_key: cache_key('gethistoryrx'), ttl: CACHE_TTL_ZERO) do
+      Vets::Collection.fetch(::Prescription, cache_key: cache_key('gethistoryrx'), ttl: CACHE_TTL_ZERO) do
         perform(:get, get_path('gethistoryrx'), nil, get_headers(token_headers)).body
       end
     end
@@ -72,7 +73,7 @@ def get_history_rxs
     # @return [Common::Collection[PrescriptionDetails]]
     #
     def get_all_rxs
-      Common::Collection.fetch(::PrescriptionDetails, cache_key: cache_key('medications'), ttl: CACHE_TTL) do
+      Vets::Collection.fetch(PrescriptionDetails, cache_key: cache_key('medications'), ttl: CACHE_TTL) do
         perform(:get, get_path('medications'), nil, get_headers(token_headers)).body
       end
     end
@@ -94,7 +95,7 @@ def get_rx_documentation(ndc)
     #
     def get_rx(id)
       collection = get_history_rxs
-      collection.find_first_by('prescription_id' => { 'eq' => id })
+      collection.find_by('prescription_id' => { 'eq' => id })
     end
 
     ##
@@ -105,7 +106,7 @@ def get_rx(id)
     #
     def get_rx_details(id)
       collection = get_all_rxs
-      collection.find_first_by('prescription_id' => { 'eq' => id })
+      collection.find_by('prescription_id' => { 'eq' => id })
     end
 
     ##
@@ -117,7 +118,7 @@ def get_rx_details(id)
     def get_tracking_rx(id)
       json = perform(:get, get_path("rxtracking/#{id}"), nil, get_headers(token_headers)).body
       data = json[:data].first.merge(prescription_id: id)
-      Tracking.new(json.merge(data:))
+      Tracking.new(data.merge(metadata: json[:metadata]))
     end
 
     ##
@@ -129,7 +130,8 @@ def get_tracking_rx(id)
     def get_tracking_history_rx(id)
       json = perform(:get, get_path("rxtracking/#{id}"), nil, get_headers(token_headers)).body
       tracking_history = json[:data].map { |t| t.to_h.merge(prescription_id: id) }
-      Common::Collection.new(::Tracking, **json.merge(data: tracking_history))
+      json = json.merge(data: tracking_history)
+      Vets::Collection.new(json[:data], Tracking, metadata: json[:metadata], errors: json[:errors])
     end
 
     ##
@@ -154,7 +156,7 @@ def post_refill_rxs(ids)
     def post_refill_rx(id)
       if (result = perform(:post, get_path("rxrefill/#{id}"), nil, get_headers(token_headers)))
         keys = [cache_key('getactiverx'), cache_key('gethistoryrx')].compact
-        Common::Collection.bust(keys) unless keys.empty?
+        Vets::Collection.bust(keys) unless keys.empty?
         increment_refill
       end
       result
diff --git a/modules/mobile/app/controllers/mobile/v0/prescriptions_controller.rb b/modules/mobile/app/controllers/mobile/v0/prescriptions_controller.rb
index 35d2bb596bb..f4e3ef7e98d 100644
--- a/modules/mobile/app/controllers/mobile/v0/prescriptions_controller.rb
+++ b/modules/mobile/app/controllers/mobile/v0/prescriptions_controller.rb
@@ -9,10 +9,10 @@ class PrescriptionsController < ApplicationController
 
       def index
         resource = client.get_all_rxs
-        resource.data = resource_data_modifications(resource)
-        resource = resource.find_by(filter_params) if params[:filter].present?
+        resource.records = resource_data_modifications(resource)
+        resource = resource.where(filter_params) if params[:filter].present?
         resource = resource.sort(params[:sort])
-        page_resource, page_meta_data = paginate(resource.attributes)
+        page_resource, page_meta_data = paginate(resource.records)
 
         page_meta_data[:meta].merge!(status_meta(resource))
 
@@ -50,7 +50,7 @@ def pagination_params
 
       def status_meta(resource)
         {
-          prescription_status_count: resource.attributes.each_with_object(Hash.new(0)) do |obj, hash|
+          prescription_status_count: resource.records.each_with_object(Hash.new(0)) do |obj, hash|
             hash['isRefillable'] += 1 if obj.is_refillable
 
             if obj.is_trackable || %w[active submitted providerHold activeParked
@@ -69,7 +69,7 @@ def paginate(records)
 
       def filter_params
         @filter_params ||= begin
-          valid_filter_params = params.require(:filter).permit(Prescription.filterable_attributes)
+          valid_filter_params = params.require(:filter).permit(Prescription.filterable_params)
           raise Common::Exceptions::FilterNotAllowed, params[:filter] if valid_filter_params.empty?
 
           valid_filter_params
@@ -86,22 +86,22 @@ def ids
       def resource_data_modifications(resource)
         # Remove Partial Fill (PF) and/or Pending Pescriptions (PD)
         display_pending_meds = Flipper.enabled?(:mhv_medications_display_pending_meds, current_user)
-        resource.data = if params[:filter].blank? && display_pending_meds
-                          resource.data.reject { |item| item.prescription_source.equal? 'PF' }
-                        else
-                          # TODO: remove this line when PF and PD are allowed on the app
-                          resource.data = remove_pf_pd(resource.data)
-                        end
+        resource.records = if params[:filter].blank? && display_pending_meds
+                             resource.data.reject { |item| item.prescription_source.equal? 'PF' }
+                           else
+                             # TODO: remove this line when PF and PD are allowed on the app
+                             resource.records = remove_pf_pd(resource.data)
+                           end
 
         # Remove Non-VA (NV) medications
         # TODO: Update once active Non-VA meds have been whitelisted for the app
-        resource.data = resource.data.reject { |item| item[:prescription_source] == 'NV' }
+        resource.records = resource.data.reject { |item| item.prescription_source == 'NV' }
 
         # Remove discontinued/expired medications that are older than 180 days
-        resource.data = remove_old_meds(resource.data)
+        resource.records = remove_old_meds(resource.data)
 
-        resource.data.each do |r|
-          r[:prescription_name] = r[:orderable_item] if r[:prescription_name].nil?
+        resource.records.each do |r|
+          r.prescription_name = r.orderable_item if r.prescription_name.nil?
         end
       end
 
diff --git a/modules/mobile/spec/requests/mobile/v0/health/rx/prescriptions_spec.rb b/modules/mobile/spec/requests/mobile/v0/health/rx/prescriptions_spec.rb
index 199bf902428..c3dbd9b5230 100644
--- a/modules/mobile/spec/requests/mobile/v0/health/rx/prescriptions_spec.rb
+++ b/modules/mobile/spec/requests/mobile/v0/health/rx/prescriptions_spec.rb
@@ -3,6 +3,7 @@
 require_relative '../../../../../support/helpers/rails_helper'
 require 'support/rx_client_helpers'
 require 'support/shared_examples_for_mhv'
+require 'vets/collection'
 
 RSpec.describe 'health/rx/prescriptions', type: :request do
   include JsonSchemaMatchers
@@ -15,7 +16,7 @@
     path = Rails.root.join('modules', 'mobile', 'spec', 'support', 'fixtures', 'prescriptions.json')
     json_data = JSON.parse(File.read(path), symbolize_names: true)
 
-    Common::Collection.fetch(Prescription, cache_key: '123:medications', ttl: 3600) { json_data }
+    Vets::Collection.fetch(Prescription, cache_key: '123:medications', ttl: 3600) { json_data }
   end
 
   before do
@@ -191,6 +192,7 @@
         VCR.use_cassette('rx_client/prescriptions/gets_a_list_of_all_prescriptions_v1') do
           get '/mobile/v0/health/rx/prescriptions', params:, headers: sis_headers
         end
+
         expect(response).to have_http_status(:ok)
         expect(response.body).to match_json_schema('prescription')
 
@@ -412,7 +414,7 @@
       context 'invalid sort option' do
         let(:params) { { sort: 'quantity' } }
 
-        it 'sorts prescriptions by refill_status' do
+        it 'sorts prescriptions by refill_status', skip: 'not needed with Vets::Collection' do
           VCR.use_cassette('rx_client/prescriptions/gets_a_list_of_all_prescriptions_v1') do
             get '/mobile/v0/health/rx/prescriptions', params:, headers: sis_headers
           end
diff --git a/modules/my_health/app/controllers/my_health/v1/prescription_documentation_controller.rb b/modules/my_health/app/controllers/my_health/v1/prescription_documentation_controller.rb
index 426cde190ff..ceef75aa14e 100644
--- a/modules/my_health/app/controllers/my_health/v1/prescription_documentation_controller.rb
+++ b/modules/my_health/app/controllers/my_health/v1/prescription_documentation_controller.rb
@@ -9,13 +9,9 @@ def index
             id = params[:id]
             rx = client.get_rx_details(id)
             raise StandardError, 'Rx not found' if rx.nil?
+            raise StandardError, 'Missing NDC number' if rx.cmop_ndc_value.nil?
 
-            cmop_ndc_number = rx.rx_rf_records&.dig(0, 1)&.find do |record|
-              record[:cmop_ndc_number]
-            end&.[](:cmop_ndc_number) || rx[:cmop_ndc_number].presence
-            raise StandardError, 'Missing NDC number' if cmop_ndc_number.nil?
-
-            documentation = client.get_rx_documentation(cmop_ndc_number)
+            documentation = client.get_rx_documentation(rx.cmop_ndc_value)
             prescription_documentation = PrescriptionDocumentation.new({ html: documentation[:data] })
             render json: PrescriptionDocumentationSerializer.new(prescription_documentation)
           rescue => e
diff --git a/modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb b/modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb
index 80caef2286b..1c78693c264 100644
--- a/modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb
+++ b/modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb
@@ -16,23 +16,24 @@ class PrescriptionsController < RxController
       def index
         resource = collection_resource
         raw_data = resource.data.dup
-        resource.data = resource_data_modifications(resource)
+        resource.records = resource_data_modifications(resource)
+
         filter_count = set_filter_metadata(resource.data, raw_data)
         if params[:filter].present?
           resource = if filter_params[:disp_status]&.[](:eq) == 'Active,Expired' # renewal params
                        filter_renewals(resource)
                      else
-                       resource.find_by(filter_params)
+                       resource.where(filter_params)
                      end
         end
         resource = params[:sort].is_a?(Array) ? sort_by(resource, params[:sort]) : resource.sort(params[:sort])
-        resource.data = sort_prescriptions_with_pd_at_top(resource.data)
+        resource.records = sort_prescriptions_with_pd_at_top(resource.data)
         is_using_pagination = params[:page].present? || params[:per_page].present?
-        resource.data = params[:include_image].present? ? fetch_and_include_images(resource.data) : resource.data
+        resource.records = params[:include_image].present? ? fetch_and_include_images(resource.data) : resource.data
         resource = resource.paginate(**pagination_params) if is_using_pagination
         options = { meta: resource.metadata.merge(filter_count) }
         options[:links] = pagination_links(resource) if is_using_pagination
-        render json: MyHealth::V1::PrescriptionDetailsSerializer.new(resource.data, options)
+        render json: MyHealth::V1::PrescriptionDetailsSerializer.new(resource.records, options)
       end
 
       def show
@@ -58,7 +59,7 @@ def refill
       end
 
       def filter_renewals(resource)
-        resource.data = resource.data.select(&method(:renewable))
+        resource.records = resource.data.select(&method(:renewable))
         resource.metadata = resource.metadata.merge({
                                                       'filter' => {
                                                         'disp_status' => {
@@ -85,7 +86,7 @@ def refill_prescriptions
 
       def list_refillable_prescriptions
         resource = collection_resource
-        resource.data = filter_data_by_refill_and_renew(resource.data)
+        resource.records = filter_data_by_refill_and_renew(resource.data)
 
         options = { meta: resource.metadata }
         render json: MyHealth::V1::PrescriptionDetailsSerializer.new(resource.data, options)
@@ -104,13 +105,13 @@ def get_prescription_image
       def fetch_and_include_images(data)
         threads = []
         data.each do |item|
-          cmop_ndc_number = get_cmop_value(item)
+          cmop_ndc_number = item.cmop_ndc_value
           if cmop_ndc_number.present?
             image_uri = get_image_uri(cmop_ndc_number)
             threads << Thread.new(item) do |thread_item|
-              thread_item[:prescription_image] = fetch_image(image_uri)
+              thread_item.prescription_image = fetch_image(image_uri)
             rescue => e
-              puts "Error fetching image for NDC #{thread_item[:cmop_ndc_number]}: #{e.message}"
+              puts "Error fetching image for NDC #{thread_item.cmop_ndc_number}: #{e.message}"
             end
           end
         end
@@ -132,18 +133,6 @@ def fetch_image(image_url)
         end
       end
 
-      def get_cmop_value(item)
-        cmop_ndc_number = nil
-        if item[:rx_rf_records].present? || item[:cmop_ndc_number].present?
-          cmop_ndc_number = if item[:rx_rf_records]&.[](0)&.[](1)&.[](0)&.key?(:cmop_ndc_number)
-                              item[:rx_rf_records][0][1][0][:cmop_ndc_number]
-                            elsif item[:cmop_ndc_number].present?
-                              item[:cmop_ndc_number]
-                            end
-        end
-        cmop_ndc_number
-      end
-
       def get_image_uri(cmop_ndc_number)
         folder_names = %w[1 2 3 4 5 6 7 8 9]
         folder_name = cmop_ndc_number ? cmop_ndc_number.gsub(/^0+(?!$)/, '')[0] : ''
@@ -155,7 +144,7 @@ def get_image_uri(cmop_ndc_number)
 
       def filter_params
         @filter_params ||= begin
-          valid_filter_params = params.require(:filter).permit(PrescriptionDetails.filterable_attributes)
+          valid_filter_params = params.require(:filter).permit(PrescriptionDetails.filterable_params)
           raise Common::Exceptions::FilterNotAllowed, params[:filter] if valid_filter_params.empty?
 
           valid_filter_params
@@ -175,14 +164,14 @@ def resource_data_modifications(resource)
         display_grouping = Flipper.enabled?(:mhv_medications_display_grouping, current_user)
         display_pending_meds = Flipper.enabled?(:mhv_medications_display_pending_meds, current_user)
         # according to business logic filter for all medications is the only list that should contain PD meds
-        resource.data = if params[:filter].blank? && display_pending_meds
-                          resource.data.reject { |item| item.prescription_source.equal? 'PF' }
-                        else
-                          # TODO: remove this line when PF and PD are allowed on va.gov
-                          resource.data = remove_pf_pd(resource.data)
-                        end
-        resource.data = group_prescriptions(resource.data) if display_grouping
-        resource.data = filter_non_va_meds(resource.data)
+        resource.records = if params[:filter].blank? && display_pending_meds
+                             resource.data.reject { |item| item.prescription_source.equal? 'PF' }
+                           else
+                             # TODO: remove this line when PF and PD are allowed on va.gov
+                             resource.records = remove_pf_pd(resource.data)
+                           end
+        resource.records = group_prescriptions(resource.data) if display_grouping
+        resource.records = filter_non_va_meds(resource.data)
       end
 
       def set_filter_metadata(list, non_modified_collection)
diff --git a/modules/my_health/app/helpers/my_health/prescription_helper.rb b/modules/my_health/app/helpers/my_health/prescription_helper.rb
index fb061b19786..9cb94c901d6 100644
--- a/modules/my_health/app/helpers/my_health/prescription_helper.rb
+++ b/modules/my_health/app/helpers/my_health/prescription_helper.rb
@@ -15,12 +15,12 @@ def collection_resource
       end
 
       def filter_non_va_meds(data)
-        data.reject { |item| item[:prescription_source] == 'NV' && item[:disp_status] != 'Active: Non-VA' }
+        data.reject { |item| item.prescription_source == 'NV' && item.disp_status != 'Active: Non-VA' }
       end
 
       def sort_by(resource, sort_params)
         sort_orders = sort_params.map { |param| param.to_s.start_with?('-') }
-        resource.data = resource.data.sort do |a, b|
+        resource.records = resource.data.sort do |a, b|
           comparison = 0
           sort_params.each_with_index do |field, index|
             is_descending = sort_orders[index]
@@ -60,29 +60,29 @@ def populate_sort_vars(field, a, b, is_descending)
       def get_field_data(field, data, is_descending)
         case field
         when /dispensed_date/
-          if data[:sorted_dispensed_date].nil?
+          if data.sorted_dispensed_date.nil?
             is_descending ? Date.new(9999, 12, 31) : Date.new(0, 1, 1)
           else
-            data[:sorted_dispensed_date]
+            data.sorted_dispensed_date
           end
         when 'prescription_name'
-          if data[:disp_status] == 'Active: Non-VA' && data[:prescription_name].nil?
-            data[:orderable_item]
-          elsif !data[:prescription_name].nil?
-            data[:prescription_name]
+          if data.disp_status == 'Active: Non-VA' && data.prescription_name.nil?
+            data.orderable_item
+          elsif !data.prescription_name.nil?
+            data.prescription_name
           else
             '~'
           end
         when 'disp_status'
-          data[:disp_status]
+          data.disp_status
         else
-          data[field.sub(/^-/, '')]
+          data.public_send(field.sub(/^-/, '').to_sym)
         end
       end
 
       def filter_data_by_refill_and_renew(data)
         data.select do |item|
-          next true if item[:is_refillable]
+          next true if item.is_refillable
           next true if renewable(item)
 
           false
@@ -90,13 +90,13 @@ def filter_data_by_refill_and_renew(data)
       end
 
       def renewable(item)
-        disp_status = item[:disp_status]
-        refill_history_expired_date = item[:rx_rf_records]&.[](0)&.[](1)&.[](0)&.[](:expiration_date)&.to_date
-        expired_date = refill_history_expired_date || item[:expiration_date]&.to_date
+        disp_status = item.disp_status
+        refill_history_expired_date = item.rx_rf_records&.dig(0, :expiration_date)&.to_date
+        expired_date = refill_history_expired_date || item.expiration_date&.to_date
         not_refillable = ['false'].include?(item.is_refillable.to_s)
-        if item[:refill_remaining].to_i.zero? && not_refillable
+        if item.refill_remaining.to_i.zero? && not_refillable
           return true if disp_status&.downcase == 'active'
-          return true if disp_status&.downcase == 'active: parked' && !item[:rx_rf_records].all?(&:empty?)
+          return true if disp_status&.downcase == 'active: parked' && !item.rx_rf_records.all?(&:empty?)
         end
         if disp_status == 'Expired' && expired_date.present? && within_cut_off_date?(expired_date) && not_refillable
           return true
diff --git a/modules/my_health/app/helpers/my_health/rx_grouping_helper.rb b/modules/my_health/app/helpers/my_health/rx_grouping_helper.rb
index 647bef613db..edf20ee2111 100644
--- a/modules/my_health/app/helpers/my_health/rx_grouping_helper.rb
+++ b/modules/my_health/app/helpers/my_health/rx_grouping_helper.rb
@@ -32,7 +32,7 @@ def group_prescriptions(prescriptions)
 
     def get_single_rx_from_grouped_list(prescriptions, id)
       grouped_list = group_prescriptions(prescriptions)
-      grouped_list.find { |rx| rx['prescription_id'] == id }
+      grouped_list.find { |rx| rx.prescription_id == id }
     end
 
     private
diff --git a/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb b/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb
index d671385657d..bce762efc1a 100644
--- a/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb
+++ b/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb
@@ -30,16 +30,10 @@ class PrescriptionDetailsSerializer < PrescriptionSerializer
       attribute :indication_for_use_flag
       attribute :category
       attribute :tracking_list do |object|
-        next [] unless object.tracking_list
-
-        tracking_list = object.tracking_list
-        tracking_list.dig(0, 1) || []
+        object&.tracking_list || []
       end
       attribute :rx_rf_records do |object|
-        next [] unless object.rx_rf_records
-
-        records = object.rx_rf_records
-        records.dig(0, 1) || []
+        object&.rx_rf_records || []
       end
       attribute :tracking
       attribute :orderable_item
diff --git a/spec/lib/bb/client_spec.rb b/spec/lib/bb/client_spec.rb
index 97e640f44ed..e6f61599d01 100644
--- a/spec/lib/bb/client_spec.rb
+++ b/spec/lib/bb/client_spec.rb
@@ -82,7 +82,7 @@
     # will revisit this later.
     it 'gets a list of extract statuses', :vcr do
       client_response = client.get_extract_status
-      expect(client_response).to be_a(Common::Collection)
+      expect(client_response).to be_a(Vets::Collection)
       expect(client_response.members.first).to be_a(ExtractStatus)
     end
 
@@ -103,7 +103,7 @@
     # These are the list of eligible data classes that can be used to generate a report
     it 'gets a list of eligible data classes', :vcr do
       client_response = client.get_eligible_data_classes
-      expect(client_response).to be_a(Common::Collection)
+      expect(client_response).to be_a(Vets::Collection)
       expect(client_response.type).to eq(EligibleDataClass)
       expect(client_response.cached?).to be(true)
       expect(client_response.members).to all(respond_to(:name))
diff --git a/spec/lib/rx/client_spec.rb b/spec/lib/rx/client_spec.rb
index e4aa3f3b3c8..6c0531d31f8 100644
--- a/spec/lib/rx/client_spec.rb
+++ b/spec/lib/rx/client_spec.rb
@@ -3,6 +3,7 @@
 require 'rails_helper'
 require 'rx/client'
 require 'rx/configuration'
+require 'vets/collection'
 
 # Mock upstream request to return source app for Rx client
 class UpstreamRequest
@@ -63,7 +64,7 @@ def self.env
     it 'gets a list of active prescriptions' do
       VCR.use_cassette('rx_client/prescriptions/gets_a_list_of_active_prescriptions') do
         client_response = client.get_active_rxs
-        expect(client_response).to be_a(Common::Collection)
+        expect(client_response).to be_a(Vets::Collection)
         expect(client_response.type).to eq(Prescription)
         expect(client_response.cached?).to eq(caching_enabled)
 
@@ -78,7 +79,7 @@ def self.env
     it 'gets a list of all prescriptions' do
       VCR.use_cassette('rx_client/prescriptions/gets_a_list_of_all_prescriptions') do
         client_response = client.get_history_rxs
-        expect(client_response).to be_a(Common::Collection)
+        expect(client_response).to be_a(Vets::Collection)
         expect(client_response.members.first).to be_a(Prescription)
         expect(client_response.cached?).to eq(caching_enabled)
 
@@ -99,9 +100,9 @@ def self.env
     it 'refills a prescription' do
       VCR.use_cassette('rx_client/prescriptions/refills_a_prescription') do
         if caching_enabled
-          expect(Common::Collection).to receive(:bust).with(cache_keys)
+          expect(Vets::Collection).to receive(:bust).with(cache_keys)
         else
-          expect(Common::Collection).not_to receive(:bust).with([nil, nil])
+          expect(Vets::Collection).not_to receive(:bust).with([nil, nil])
         end
 
         client_response = client.post_refill_rx(13_650_545)
@@ -140,7 +141,7 @@ def self.env
         cassette = 'gets_a_list_of_tracking_history_for_a_prescription'
         VCR.use_cassette("rx_client/prescriptions/nested_resources/#{cassette}") do
           client_response = client.get_tracking_history_rx(13_650_541)
-          expect(client_response).to be_a(Common::Collection)
+          expect(client_response).to be_a(Vets::Collection)
           expect(client_response.members.first.prescription_id).to eq(13_650_541)
           expect(client_response.cached?).to be(false)
           expect(cache_key_for(client_response)).to be_nil
diff --git a/spec/models/prescription_details_spec.rb b/spec/models/prescription_details_spec.rb
index 7a5eb7b2e5b..236db4bd00c 100644
--- a/spec/models/prescription_details_spec.rb
+++ b/spec/models/prescription_details_spec.rb
@@ -73,8 +73,7 @@
     end
 
     it 'sorted_dispensed_date should be nil' do
-      puts subject.rx_rf_records
-      expect(subject[:sorted_dispensed_date]).to be_nil
+      expect(subject.sorted_dispensed_date).to be_nil
     end
   end
 end


Reviews:
user: login: robertroddev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T15:25:13Z
user: login: stevenjcumming
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T15:33:20Z
user: login: stevenjcumming
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T12:28:36Z
user: login: ryan-mcneil
body: 🤞 
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T19:53:27Z
user: login: robertroddev
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T17:53:47Z
user: login: robertroddev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T17:54:17Z
user: login: robertroddev
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T17:57:09Z
user: login: rjohnson2011
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:09:33Z

Review comments:
diff_hunk: @@ -12,52 +12,59 @@
 # @!attribute refill_status
 #   @return [String]
 # @!attribute refill_submit_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_remaining
 #   @return [Integer]
 # @!attribute facility_name
 #   @return [String]
 # @!attribute ordered_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute quantity
 #   @return [Integer]
 # @!attribute expiration_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute prescription_number
 #   @return [String]
 # @!attribute prescription_name
 #   @return [String]
 # @!attribute dispensed_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute station_number
 #   @return [String]
 # @!attribute is_refillable
 #   @return [Boolean]
 # @!attribute is_trackable
 #   @return [Boolean]
+# @!attribute metadata
+#   @return [Hash]
 #
-class Prescription < Common::Base
-  attribute :prescription_id, Integer, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+class Prescription
+  include Vets::Model
+
+  attribute :prescription_id, Integer, filterable: %w[eq not_eq]
   attribute :prescription_image, String
-  attribute :refill_status, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
-  attribute :refill_submit_date, Common::UTCTime, sortable: { order: 'DESC' }, filterable: %w[eq not_eq]
-  attribute :refill_date, Common::UTCTime, sortable: { order: 'DESC' }
path: app/models/prescription.rb
user: login: robertroddev
body: Whats the reason to remove the sortable order on these attributes?
created_at: 2025-05-12T15:25:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,52 +12,59 @@
 # @!attribute refill_status
 #   @return [String]
 # @!attribute refill_submit_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_remaining
 #   @return [Integer]
 # @!attribute facility_name
 #   @return [String]
 # @!attribute ordered_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute quantity
 #   @return [Integer]
 # @!attribute expiration_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute prescription_number
 #   @return [String]
 # @!attribute prescription_name
 #   @return [String]
 # @!attribute dispensed_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute station_number
 #   @return [String]
 # @!attribute is_refillable
 #   @return [Boolean]
 # @!attribute is_trackable
 #   @return [Boolean]
+# @!attribute metadata
+#   @return [Hash]
 #
-class Prescription < Common::Base
-  attribute :prescription_id, Integer, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+class Prescription
+  include Vets::Model
+
+  attribute :prescription_id, Integer, filterable: %w[eq not_eq]
   attribute :prescription_image, String
-  attribute :refill_status, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
-  attribute :refill_submit_date, Common::UTCTime, sortable: { order: 'DESC' }, filterable: %w[eq not_eq]
-  attribute :refill_date, Common::UTCTime, sortable: { order: 'DESC' }
path: app/models/prescription.rb
user: login: stevenjcumming
body: Functionally, the order/direction didn't really do anything since you can pass that in as an argument. Either as :asc/:desc or "-name/name" 

I added a method `default_sort_by` to replace the `default: true` as in:

`attribute :name, String, sortable: { order: 'ASC', default: true }`
to
`default_sort_by name: :asc`
created_at: 2025-05-12T15:33:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -132,18 +133,6 @@ def fetch_image(image_url)
         end
       end
 
-      def get_cmop_value(item)
path: modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb
user: login: stevenjcumming
body: This method was moved to `PrescriptionDetails#cmop_ndc_value`
created_at: 2025-05-13T12:28:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,52 +12,59 @@
 # @!attribute refill_status
 #   @return [String]
 # @!attribute refill_submit_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute refill_remaining
 #   @return [Integer]
 # @!attribute facility_name
 #   @return [String]
 # @!attribute ordered_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute quantity
 #   @return [Integer]
 # @!attribute expiration_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute prescription_number
 #   @return [String]
 # @!attribute prescription_name
 #   @return [String]
 # @!attribute dispensed_date
-#   @return [Common::UTCTime]
+#   @return [Vets::Type::UTCTime]
 # @!attribute station_number
 #   @return [String]
 # @!attribute is_refillable
 #   @return [Boolean]
 # @!attribute is_trackable
 #   @return [Boolean]
+# @!attribute metadata
+#   @return [Hash]
 #
-class Prescription < Common::Base
-  attribute :prescription_id, Integer, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
+class Prescription
+  include Vets::Model
+
+  attribute :prescription_id, Integer, filterable: %w[eq not_eq]
   attribute :prescription_image, String
-  attribute :refill_status, String, sortable: { order: 'ASC' }, filterable: %w[eq not_eq]
-  attribute :refill_submit_date, Common::UTCTime, sortable: { order: 'DESC' }, filterable: %w[eq not_eq]
-  attribute :refill_date, Common::UTCTime, sortable: { order: 'DESC' }
path: app/models/prescription.rb
user: login: robertroddev
body: thanks for explaining that 👍 
created_at: 2025-05-15T17:54:17Z
author_association: CONTRIBUTOR

PR comments:
user: login: stevenjcumming
created_at: 2025-05-01T17:05:00Z
body: This is on hold until Vets::Collection can be updated and introduced. 
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T15:59:08Z
body: Error: A file (or its parent directories) does not have a CODEOWNERS entry. Please update the .github/CODEOWNERS file and add the entry for the Offending file: app/models/tracking.rb
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T15:59:27Z
body: <!--
  0 Errors
  1 Warning: This PR changes <code>283</cod...
  0 Messages
  0 Markdowns
-->
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Warning">
          1 Warning
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">This PR changes <code>283</code> LoC (not counting whitespace/newlines).</p>

<p>In order to ensure each PR receives the proper attention it deserves, we recommend not exceeding<br />
<code>200</code>. Expect some delays getting reviews.</p>

<details>
  <summary>File Summary</summary>

  #### Files
  - .github/CODEOWNERS (+1/-0)
- app/controllers/concerns/filterable.rb (+1/-1)
- app/models/eligible_data_class.rb (+3/-2)
- app/models/prescription.rb (+16/-13)
- app/models/prescription_details.rb (+32/-26)
- app/models/prescription_preference.rb (+4/-4)
- app/models/tracking.rb (+6/-7)
- app/services/mhv_account_type_service.rb (+5/-2)
- lib/bb/client.rb (+3/-2)
- lib/rx/client.rb (+11/-9)
- modules/mobile/app/controllers/mobile/v0/prescriptions_controller.rb (+11/-11)
- modules/mobile/spec/requests/mobile/v0/health/rx/prescriptions_spec.rb (+3/-2)
- modules/my_health/app/controllers/my_health/v1/prescription_documentation_controller.rb (+2/-5)
- modules/my_health/app/controllers/my_health/v1/prescriptions_controller.rb (+15/-26)
- modules/my_health/app/helpers/my_health/prescription_helper.rb (+16/-16)
- modules/my_health/app/helpers/my_health/rx_grouping_helper.rb (+1/-1)
- modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb (+2/-6)
- spec/lib/bb/client_spec.rb (+2/-2)
- spec/lib/rx/client_spec.rb (+6/-5)
- spec/models/prescription_details_spec.rb (+1/-2)

  ####
  _Note: We exclude files matching the following when considering PR size:_

  ```
  *.csv, *.json, *.tsv, *.txt, *.md, Gemfile.lock, app/swagger, modules/mobile/docs, spec/fixtures/, spec/support/vcr_cassettes/, modules/mobile/spec/support/vcr_cassettes/, db/seeds, modules/vaos/app/docs, modules/meb_api/app/docs, modules/appeals_api/app/swagger/, *.bru, *.pdf
  ```
</details>

<p>Big PRs are difficult to review, often become stale, and cause delays.</td>
    </tr>
  </tbody>
</table>

<p align="right" data-meta="generated_by_danger">
  Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
</p>

author_association: CONTRIBUTOR