Skip to content

Commit f75a08a

Browse files
authored
Fix OH renewability/refillability to require completed dispense (#27123)
* fix: require completed dispense for OH renewability gate 3 * fix: require completed dispense for OH refillability Gate 5 Apply the same entered-in-error fix to the refill helper that was applied to the renewability helper. Gate 5 now checks for at least one completed MedicationDispense, preventing prescriptions with only entered-in-error dispenses from being marked as refillable. Bug: An Oracle Health prescription whose only MedicationDispense had status 'entered-in-error' was incorrectly marked is_refillable: true, causing it to appear on the Ready to Refill page even though no initial fill was ever processed. Root cause: Gate 5 checked medication_dispenses(resource).empty? which passes for any dispense regardless of status. entered-in-error dispenses represent voided records, not actual fills. Fix: Replace the empty? check with completed_dispense_exists? which requires at least one dispense with status == 'completed'. * Move completed_dispense_exists? to FhirHelpers to deduplicate completed_dispense_exists? was defined identically in both OracleHealthRefillHelper and OracleHealthRenewabilityHelper. Since it operates on FHIR MedicationDispense data and both helpers already depend on FhirHelpers, move it there as the single source of truth. most_recent_dispense_in_progress? stays in OracleHealthRefillHelper since it's not duplicated (only used by refill helper and adapter). * Address Copilot review: fix dependency docs and renewability test - Fix refill helper dependency docs: replace stale extract_expiration_date with parse_expiration_date_utc and prescription_expired? (actual deps) - Add completed_dispense_exists? and non_va_med? to renewability helper dependency docs - Fix entered-in-error renewability test: change from refills:1/non-expired (which failed on Gate 6, not Gate 3) to refills:0/expired so the failure is attributable to Gate 3 (completed dispense required)
1 parent 9cacdf6 commit f75a08a

File tree

5 files changed

+92
-9
lines changed

5 files changed

+92
-9
lines changed

lib/unified_health_data/adapters/fhir_helpers.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ def find_most_recent_medication_dispense(medication_request)
8181
end
8282
end
8383

84+
# Checks for at least one successfully completed dispense.
85+
# Dispenses with status 'entered-in-error' represent voided records and
86+
# do not count as evidence that the initial fill was processed.
87+
#
88+
# @param resource [Hash] FHIR MedicationRequest resource
89+
# @return [Boolean] true when a completed MedicationDispense exists
90+
def completed_dispense_exists?(resource)
91+
medication_dispenses(resource).any? { |dispense| dispense['status'] == 'completed' }
92+
end
93+
8494
# Builds instruction text from FHIR dosageInstruction components
8595
#
8696
# @param instruction [Hash] FHIR dosageInstruction object

lib/unified_health_data/adapters/oracle_health_refill_helper.rb

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ module Adapters
88
# This module is designed to be included in OracleHealthPrescriptionAdapter
99
# and has dependencies on methods from the including class and other mixed-in modules:
1010
#
11-
# Required methods from including class (OracleHealthPrescriptionAdapter):
12-
# - extract_expiration_date(resource) - Extracts validityPeriod.end from resource
13-
#
1411
# Required methods from other modules (via include):
1512
# - categorize_medication(resource) - From OracleHealthCategorizer
1613
# - non_va_med?(resource) - From OracleHealthCategorizer
1714
# - medication_dispenses(resource) - From FhirHelpers
15+
# - completed_dispense_exists?(resource) - From FhirHelpers
1816
# - find_most_recent_medication_dispense(medication_request) - From FhirHelpers
19-
# - log_invalid_expiration_date(resource, date) - From FhirHelpers
17+
# - parse_expiration_date_utc(resource) - From FhirHelpers
18+
# - prescription_expired?(resource) - From FhirHelpers
2019
module OracleHealthRefillHelper
2120
# Determines if a medication is refillable based on gate checks
2221
# A medication is refillable only if ALL gate conditions pass
@@ -25,7 +24,7 @@ module OracleHealthRefillHelper
2524
# Gate 2: MedicationRequest.status == 'active'
2625
# Gate 3: Prescription not expired
2726
# Gate 4: Refills remaining > 0
28-
# Gate 5: At least one dispense exists
27+
# Gate 5: At least one completed dispense exists
2928
# Gate 6: Most recent dispense is not in-progress
3029
# Gate 7: No pending refill request (refill_status != 'submitted')
3130
#
@@ -37,7 +36,7 @@ def refillable?(resource, refill_status)
3736
return false unless resource['status'] == 'active'
3837
return false unless prescription_not_expired?(resource)
3938
return false unless extract_refill_remaining(resource).positive?
40-
return false if medication_dispenses(resource).empty?
39+
return false unless completed_dispense_exists?(resource)
4140
return false if most_recent_dispense_in_progress?(resource)
4241
return false if refill_status == 'submitted'
4342

@@ -75,7 +74,7 @@ def extract_refill_remaining(resource)
7574
# In-progress statuses: preparation, in-progress, on-hold
7675
#
7776
# @param resource [Hash] FHIR MedicationRequest resource
78-
# @return [Boolean] True if most recent dispense is in-progress
77+
# @return [Boolean] true if most recent dispense is in-progress
7978
def most_recent_dispense_in_progress?(resource)
8079
most_recent = find_most_recent_medication_dispense(resource)
8180
return false if most_recent.nil?

lib/unified_health_data/adapters/oracle_health_renewability_helper.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ module Adapters
1212
#
1313
# Requires these methods from other modules (via include):
1414
# - categorize_medication(resource) - From OracleHealthCategorizer
15+
# - non_va_med?(resource) - From OracleHealthCategorizer
1516
# - medication_dispenses(resource) - From FhirHelpers
17+
# - completed_dispense_exists?(resource) - From FhirHelpers
1618
# - parse_expiration_date_utc(resource) - From FhirHelpers
1719
# - prescription_expired?(resource) - From FhirHelpers
1820
module OracleHealthRenewabilityHelper
@@ -23,7 +25,7 @@ module OracleHealthRenewabilityHelper
2325
#
2426
# Gate 1: MedicationRequest.status == 'active'
2527
# Gate 2: Category must be VA Prescription
26-
# Gate 3: At least one dispense exists
28+
# Gate 3: At least one completed dispense exists
2729
# Gate 4: Validity period end date exists
2830
# Gate 5: Within 120 days of validity period end
2931
# Gate 6: Refills exhausted OR prescription expired
@@ -35,7 +37,7 @@ def renewable?(resource)
3537
return false if resource.nil? || !resource.is_a?(Hash)
3638
return false unless resource['status'] == 'active'
3739
return false if non_va_med?(resource)
38-
return false if medication_dispenses(resource).empty?
40+
return false unless completed_dispense_exists?(resource)
3941
return false unless validity_period_end_exists?(resource)
4042
return false unless within_renewal_window?(resource)
4143
return false unless refills_exhausted_or_expired?(resource)

spec/lib/unified_health_data/adapters/oracle_health_refill_helper_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,42 @@
127127
end
128128
end
129129

130+
context 'when only an entered-in-error dispense exists' do
131+
let(:entered_in_error_dispense) do
132+
{
133+
'resourceType' => 'MedicationDispense',
134+
'status' => 'entered-in-error',
135+
'whenHandedOver' => '2025-01-01T10:00:00Z'
136+
}
137+
end
138+
139+
let(:entered_in_error_resource) do
140+
refillable_resource.merge('contained' => [entered_in_error_dispense])
141+
end
142+
143+
it 'returns false' do
144+
expect(subject.refillable?(entered_in_error_resource, nil)).to be false
145+
end
146+
end
147+
148+
context 'when entered-in-error and completed dispenses both exist' do
149+
let(:entered_in_error_dispense) do
150+
{
151+
'resourceType' => 'MedicationDispense',
152+
'status' => 'entered-in-error',
153+
'whenHandedOver' => '2025-01-01T10:00:00Z'
154+
}
155+
end
156+
157+
let(:mixed_dispense_resource) do
158+
refillable_resource.merge('contained' => [entered_in_error_dispense, completed_dispense])
159+
end
160+
161+
it 'returns true' do
162+
expect(subject.refillable?(mixed_dispense_resource, nil)).to be true
163+
end
164+
end
165+
130166
context 'when most recent dispense is in progress' do
131167
let(:in_progress_dispense) do
132168
{

spec/lib/unified_health_data/adapters/oracle_health_renewability_helper_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,42 @@
180180
expect(subject.send(:renewable?, resource)).to be false
181181
end
182182

183+
it 'returns false when only entered-in-error dispense exists for active expired prescription' do
184+
resource = fhir_resource(
185+
status: 'active',
186+
refills: 0,
187+
expiration: 30.days.ago,
188+
source: 'VA'
189+
).merge(
190+
'contained' => [
191+
{
192+
'resourceType' => 'MedicationDispense',
193+
'id' => 'dispense-1',
194+
'status' => 'entered-in-error'
195+
}
196+
]
197+
)
198+
expect(subject.send(:renewable?, resource)).to be false
199+
end
200+
201+
it 'returns false with entered-in-error dispense for active non-expired prescription with no repeats' do
202+
resource = fhir_resource(
203+
status: 'active',
204+
refills: 0,
205+
expiration: 30.days.from_now,
206+
source: 'VA'
207+
).merge(
208+
'contained' => [
209+
{
210+
'resourceType' => 'MedicationDispense',
211+
'id' => 'dispense-1',
212+
'status' => 'entered-in-error'
213+
}
214+
]
215+
)
216+
expect(subject.send(:renewable?, resource)).to be false
217+
end
218+
183219
it 'returns true with one completed dispense' do
184220
resource = fhir_resource(
185221
status: 'active',

0 commit comments

Comments
 (0)