Module: Lab::OrdersService
- Defined in:
- app/services/lab/orders_service.rb
Overview
Manage lab orders.
Lab orders are just ordinary openmrs orders with extra metadata that separates them from other orders. Lab orders have an order type of ‘Lab’ with the order’s test type as the order’s concept. The order’s start date is the day the order is made. Additional information pertaining to the order is stored as observations that point to the order. The specimen types, requesting clinician, target lab, and reason for test are saved as observations to the order. Refer to method #order_test for more information.
Class Method Summary collapse
- .attach_test_method(order, order_params) ⇒ Object
- .check_tracking_number(tracking_number) ⇒ Object
- .lab_orders(start_date, end_date, concept_id = nil, include_data: false) ⇒ Object
-
.order_test(order_params) ⇒ Object
Create a lab order.
- .update_order(order_id, params) ⇒ Object
- .update_order_result(order_params) ⇒ Object
- .update_order_status(order_params) ⇒ Object
- .void_order(order_id, reason) ⇒ Object
Class Method Details
.attach_test_method(order, order_params) ⇒ Object
100 101 102 103 104 105 106 107 |
# File 'app/services/lab/orders_service.rb', line 100 def attach_test_method(order, order_params) create_order_observation( order, Lab::Metadata::TEST_METHOD_CONCEPT_NAME, order_params[:date], value_coded: order_params[:test_method] ) end |
.check_tracking_number(tracking_number) ⇒ Object
152 153 154 |
# File 'app/services/lab/orders_service.rb', line 152 def check_tracking_number(tracking_number) accession_number_exists?(tracking_number) || nlims_accession_number_exists?(tracking_number) end |
.lab_orders(start_date, end_date, concept_id = nil, include_data: false) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'app/services/lab/orders_service.rb', line 233 def lab_orders(start_date, end_date, concept_id = nil, include_data: false) tests = Lab::LabTest.where('date_created >= ? AND date_created <= ?', start_date, end_date) tests = tests.where(value_coded: concept_id) if concept_id orders = Lab::LabOrder.where(order_id: tests.pluck(:order_id)) data = { count: orders.count, last_order_date: Lab::LabOrder.last&.start_date&.to_date, lab_orders: [] } if include_data data[:lab_orders] = orders.map do |order| Lab::LabOrderSerializer.serialize_order( order, requesting_clinician: order.requesting_clinician, reason_for_test: order.reason_for_test, target_lab: order.target_lab ) end end data end |
.order_test(order_params) ⇒ Object
Create a lab order.
Parameters schema:
{
encounter_id: {
type: :integer,
required: :false,
description: 'Attach order to this if program_id and patient_id are not provided'
},
program_id: { type: :integer, required: false },
patient_id: { type: :integer, required: false }
specimen: { type: :object, properties: { concept_id: :integer }, required: %i[concept_id] },
test_type_ids: {
type: :array,
items: {
type: :object,
properties: { concept_id: :integer },
required: %i[concept_id]
}
},
start_date: { type: :datetime }
accession_number: { type: :string }
target_lab: { type: :string },
reason_for_test_id: { type: :integer },
requesting_clinician: { type: :string }
}
encounter_id: is an ID of the encounter the lab order is to be created under test_type_id: is a concept_id of the name of test being ordered specimen_type_id: is a list of IDs for the specimens to be tested (can be ommited) target_lab: is the name of the lab where test will be carried out reason_for_test_id: is a concept_id for a (standard) reason of why the test is being carried out requesting_clinician: Name of the clinician requesting the test (defaults to current user)
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'app/services/lab/orders_service.rb', line 52 def order_test(order_params) serialized_order = nil order_id = nil patient_id = nil Order.transaction do encounter = find_encounter(order_params) if order_params[:accession_number].present? && check_tracking_number(order_params[:accession_number]) raise 'Accession number already exists' end order = create_order(encounter, order_params) attach_test_method(order, order_params) if order_params[:test_method] # Create initial order status trail create_initial_order_status_trail(order) Lab::TestsService.create_tests(order, order_params[:date], order_params[:tests]) # Reload order to include status trails and tests order = Lab::LabOrder.prefetch_relationships.find(order.order_id) serialized_order = Lab::LabOrderSerializer.serialize_order( order, requesting_clinician: add_requesting_clinician(order, order_params), reason_for_test: add_reason_for_test(order, order_params), target_lab: add_target_lab(order, order_params), comment_to_fulfiller: add_comment_to_fulfiller(order, order_params) ) # Store IDs for notification after transaction commits order_id = order.order_id patient_id = order.patient_id end # Publish notification AFTER transaction commits # This ensures the order is visible in the database before rebuilding ActiveSupport::Notifications.instrument( 'lab.order_created', patient_id: patient_id, order_id: order_id, accession_number: serialized_order[:accession_number], timestamp: Time.current ) serialized_order end |
.update_order(order_id, params) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'app/services/lab/orders_service.rb', line 109 def update_order(order_id, params) specimen_id = params.dig(:specimen, :concept_id) raise ::InvalidParameterError, 'Specimen concept_id is required' unless specimen_id order = Lab::LabOrder.find(order_id) if order.concept_id != unknown_concept_id && !params[:force_update]&.casecmp?('true') raise ::UnprocessableEntityError, "Can't change order specimen once set" end if specimen_id.to_i != order.concept_id Rails.logger.debug("Updating order ##{order.order_id}") order.update!(concept_id: specimen_id, discontinued: true, discontinued_by: User.current.user_id, discontinued_date: params[:date]&.to_date || Time.now, discontinued_reason_non_coded: 'Sample drawn/updated') end reason_for_test = params[:reason_for_test] || params[:reason_for_test_id] if reason_for_test Rails.logger.debug("Updating reason for test on order ##{order.order_id}") update_reason_for_test(order, Concept.find(reason_for_test)&.id, force_update: params.fetch('force_update', false)) end Lab::LabOrderSerializer.serialize_order(order) end |
.update_order_result(order_params) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'app/services/lab/orders_service.rb', line 188 def update_order_result(order_params) # Extract tracking number from nested structure if present tracking_number = order_params['tracking_number'] || order_params.dig('order', 'tracking_number') order = find_order(tracking_number) order_dto = Lab::Lims::OrderSerializer.serialize_order(order) # Handle results if present in the old format patch_order_dto_with_lims_results!(order_dto, order_params['results']) if order_params['results'] # Handle test results in NLIMS format if order_params['tests'] # Extract test results from NLIMS tests payload test_results = {} order_params['tests'].each do |test_data| test_name = test_data.dig('test_type', 'name') next unless test_name && test_data['test_results'] test_results[test_name] = { 'results' => test_data['test_results'].each_with_object({}) do |result, formatted| measure_name = result.dig('measure', 'name') result_value = result.dig('result', 'value') next unless measure_name && result_value formatted[measure_name] = { 'result_value' => result_value } end, 'result_date' => test_data['test_results'].first&.dig('result', 'result_date'), 'result_entered_by' => {} } end patch_order_dto_with_lims_results!(order_dto, test_results) unless test_results.empty? end # Save order status trail if available from NLIMS if order_params['order'] && order_params['order']['status_trail'] save_order_status_trails_from_nlims(order, order_params['order']['status_trail']) end # Save test status trails if available from NLIMS save_test_status_trails_from_nlims(order, order_params['tests']) if order_params['tests'] Lab::Lims::PullWorker.new(nil).process_order(order_dto) end |
.update_order_status(order_params) ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'app/services/lab/orders_service.rb', line 156 def update_order_status(order_params) # find the order order = find_order(order_params['tracking_number']) concept = ConceptName.find_by_name Lab::Metadata::LAB_ORDER_STATUS_CONCEPT_NAME ActiveRecord::Base.transaction do void_order_status(order, concept) Observation.create!( person_id: order.patient_id, encounter_id: order.encounter_id, concept_id: concept.concept_id, order_id: order.id, obs_datetime: order_params['status_time'] || Time.now, value_text: order_params['status'], creator: User.current.id ) # Save order status trail if available save_order_status_trail(order, order_params) if order_params['status'] end create_rejection_notification(order_params) if order_params['status'] == 'test-rejected' # Publish notification that order status has changed ActiveSupport::Notifications.instrument( 'lab.order_status_changed', patient_id: order.patient_id, order_id: order.order_id, new_status: order_params['status'], tracking_number: order_params['tracking_number'], timestamp: Time.current ) end |
.void_order(order_id, reason) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'app/services/lab/orders_service.rb', line 138 def void_order(order_id, reason) order = Lab::LabOrder.includes(%i[requesting_clinician reason_for_test target_lab comment_to_fulfiller], tests: [:result]) .find(order_id) order.requesting_clinician&.void(reason) order.reason_for_test&.void(reason) order.comment_to_fulfiller&.void(reason) order.target_lab&.void(reason) order.tests.each { |test| test.void(reason) } order.void(reason) end |