his_emr_api_lab 2.1.8 → 2.1.9.pre.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44e72036503453b83d8f3cfcdff281e1085e461c476a8f793e24a3d72f12f223
4
- data.tar.gz: 0100d95046a1a81db0fc8b7eace3e9fa02dc2f9be6f396f073954bbfa5578993
3
+ metadata.gz: c6c20f1ba07edd60ba5795eaaef1ca3f34395060b1f9ed0160a5513777d657d0
4
+ data.tar.gz: d16b695b3592233474fa9bbc2f90c85db7d04161a02f8107c56f92fd4e82a9fb
5
5
  SHA512:
6
- metadata.gz: 25cb61dabafa209680d649747e35a06e19ed3685c7730bc0c539a35357e9e9529794ed180714c626125ec6ab8104b107e90e6ad0ab0cbebfcaee2ae728ea7815
7
- data.tar.gz: 0da64fc5aa9647ffcc3252ae597b0492bacb3b77ec97064c4433f72bcd122431e04114323f7f13dc602faa0ccdc7c2f836b098a1090e9a56fa8cae774503101f
6
+ metadata.gz: 035cf4578ee6f60ba3406a66f88ea33cc54ea7d85ab38524b7e6ae54c9b4b80e03fe2938fc34d047e72df9423b71b8eba1f40004a8fd688555b56c3aebb81605
7
+ data.tar.gz: feda70dff8797923f46078a42429b274d7562eb5105a5d43c1d746f744436c6c516847aa2ef68d7b235f1d1a216981e10d4c707bc6f4fb45372d6b867c412a2a
data/README.md CHANGED
@@ -22,25 +22,25 @@ For details on how to perform these operations please see the
22
22
  Add this line to your application's Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'his_emr_api_lab', git: 'https://github.com/EGPAFMalawiHIS/HIS-EMR-API-Lab', branch: 'development'
25
+ gem 'lab', git: 'https://github.com/EGPAFMalawiHIS/HIS-EMR-API-Lab', branch: 'development'
26
26
  ```
27
27
 
28
28
  And then execute:
29
29
 
30
30
  ```bash
31
- $ bundle install
31
+ $ bundle install lab
32
32
  ```
33
33
 
34
34
  Or install it yourself as:
35
35
 
36
36
  ```bash
37
- $ gem install his_emr_api_lab
37
+ $ gem install lab
38
38
  ```
39
39
 
40
40
  Finally run:
41
41
 
42
42
  ```bash
43
- $ bundle exec rails his_emr_api_lab:install
43
+ $ bundle exec rails lab:install
44
44
  ```
45
45
 
46
46
  ## Configuration
@@ -66,19 +66,6 @@ but too much a departure from it is frowned upon. For example, you will be forgi
66
66
  for writing a method with 15 to 20 lines if you clearly justify why you couldn't
67
67
  break that method into multiple smaller methods.
68
68
 
69
- ## Publishing
70
-
71
- To publish a new version of the gem, first update the version number in
72
- `lib/lab/version.rb` and then run the following command:
73
-
74
- ```bash
75
- $ gem build his_emr_api_lab.gemspec
76
- $ gem push his_emr_api_lab-<version>.gem
77
- ```
78
- Make sure to replace `<version>` with the version number you set in `lib/lab/version.rb`.
79
-
80
- NB: You need to have an account on [rubygems.org](https://rubygems.org/) and permission to publish gems.
81
-
82
69
  ## License
83
70
 
84
71
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -7,8 +7,11 @@ module Lab
7
7
  queue_as :default
8
8
  def perform(results_obs_id, serializer, result_enter_by)
9
9
  Rails.logger.info("Lab::ProcessLabResultJob: Processing result completion for #{serializer}")
10
- results_obs = Lab::LabResult.find(results_obs_id)
10
+ # set location context for the job based on the order's encounter to ensure proper context for any operations performed in the job
11
+ results_obs = Lab::LabResult.unscoped.find(results_obs_id)
12
+ encounter = Encounter.unscoped.find_by(encounter_id: results_obs.encounter_id)
13
+ Location.current = Location.find(encounter.location_id) if encounter&.location_id
11
14
  Lab::ResultsService.process_result_completion(results_obs, serializer, result_enter_by)
12
15
  end
13
16
  end
14
- end
17
+ end
@@ -10,7 +10,8 @@ module Lab
10
10
  Rails.logger.info('Initialising LIMS REST API...')
11
11
 
12
12
  User.current = Lab::Lims::Utils.lab_user
13
- Location.current = Location.find_by_name('ART clinic')
13
+ # Set location from patient's most recent encounter to ensure proper context
14
+ set_location_from_patient_encounter(patient_id)
14
15
 
15
16
  lockfile = Rails.root.join('tmp', "update-patient-orders-#{patient_id}.lock")
16
17
 
@@ -28,5 +29,53 @@ module Lab
28
29
 
29
30
  File.unlink(lockfile) if done
30
31
  end
32
+
33
+ private
34
+
35
+ def set_location_from_patient_encounter(patient_id)
36
+ Rails.logger.info("Setting location context for patient #{patient_id}")
37
+
38
+ # Strategy 1: Find location from patient's most recent order (ANY order type)
39
+ recent_order = Order.unscoped
40
+ .where(patient_id: patient_id)
41
+ .order(start_date: :desc)
42
+ .first
43
+
44
+ if recent_order
45
+ encounter = Encounter.unscoped.find_by(encounter_id: recent_order.encounter_id)
46
+ if encounter&.location_id
47
+ Location.current = Location.find(encounter.location_id)
48
+ Rails.logger.info("Location set from patient's recent order: #{Location.current.name} (ID: #{Location.current.location_id})")
49
+ return
50
+ end
51
+ end
52
+
53
+ # Strategy 2: Find location from patient's most recent encounter
54
+ recent_encounter = Encounter.unscoped
55
+ .where(patient_id: patient_id)
56
+ .order(encounter_datetime: :desc)
57
+ .first
58
+
59
+ if recent_encounter&.location_id
60
+ Location.current = Location.find(recent_encounter.location_id)
61
+ Rails.logger.info("Location set from patient's recent encounter: #{Location.current.name} (ID: #{Location.current.location_id})")
62
+ return
63
+ end
64
+
65
+ # Fallback chain: Try multiple options to ensure location is ALWAYS set
66
+ Location.current ||= begin
67
+ Location.current_health_center
68
+ rescue StandardError
69
+ nil
70
+ end
71
+ Location.current ||= Location.first
72
+
73
+ if Location.current
74
+ Rails.logger.info("Location set to fallback: #{Location.current.name} (ID: #{Location.current.location_id})")
75
+ else
76
+ Rails.logger.error('CRITICAL: Could not set Location.current - no locations found in database!')
77
+ raise 'No locations available in database'
78
+ end
79
+ end
31
80
  end
32
81
  end
@@ -8,10 +8,14 @@ module Lab
8
8
  Rails.logger.info("Voiding order ##{order_id} in LIMS")
9
9
 
10
10
  User.current = Lab::Lims::Utils.lab_user
11
- Location.current = Location.find_by_name('ART clinic')
11
+ # Set location from order's encounter to ensure proper context
12
+ order = Lab::LabOrder.unscoped.find(order_id)
13
+ encounter = Encounter.unscoped.find_by(encounter_id: order.encounter_id)
14
+ Location.current = Location.find(encounter.location_id) if encounter&.location_id
15
+ Location.current ||= Location.find_by_name('ART clinic')
12
16
 
13
17
  worker = Lab::Lims::PushWorker.new(Lab::Lims::ApiFactory.create_api)
14
- worker.push_order(Lab::LabOrder.unscoped.find(order_id))
18
+ worker.push_order(order)
15
19
  end
16
20
  end
17
21
  end
@@ -9,7 +9,8 @@ module Lab
9
9
  reason_for_test ||= order.reason_for_test
10
10
  target_lab = target_lab&.value_text || order.target_lab&.value_text || Location.current_health_center&.name
11
11
 
12
- encounter = Encounter.find_by_encounter_id(order.encounter_id)
12
+ # Use unscoped to find encounter across all locations
13
+ encounter = Encounter.unscoped.find_by_encounter_id(order.encounter_id)
13
14
  program = Program.find_by_program_id(encounter.program_id)
14
15
 
15
16
  ActiveSupport::HashWithIndifferentAccess.new(
@@ -34,10 +34,11 @@ module Lab
34
34
 
35
35
  unless specimen_type
36
36
  return ActiveRecord::Base.connection.select_all <<~SQL
37
- SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
37
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code, c.uuid
38
38
  FROM concept_attribute ca
39
39
  INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
40
40
  AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
41
+ INNER JOIN concept c ON c.concept_id = ca.concept_id
41
42
  WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
42
43
  AND ca.concept_id IN (#{test_types.select(:concept_id).to_sql})
43
44
  GROUP BY ca.concept_id
@@ -56,10 +57,11 @@ module Lab
56
57
  )
57
58
 
58
59
  return ActiveRecord::Base.connection.select_all <<~SQL
59
- SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
60
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code, c.uuid
60
61
  FROM concept_attribute ca
61
62
  INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
62
63
  AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
64
+ INNER JOIN concept c ON c.concept_id = ca.concept_id
63
65
  WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
64
66
  AND ca.concept_id IN (#{concept_set.select(:concept_set).to_sql})
65
67
  GROUP BY ca.concept_id
@@ -72,10 +74,11 @@ module Lab
72
74
 
73
75
  unless test_type
74
76
  return ActiveRecord::Base.connection.select_all <<~SQL
75
- SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
77
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code, c.uuid
76
78
  FROM concept_attribute ca
77
79
  INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
78
80
  AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
81
+ INNER JOIN concept c ON c.concept_id = ca.concept_id
79
82
  WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
80
83
  AND ca.concept_id IN (#{specimen_types.select(:concept_id).to_sql})
81
84
  GROUP BY ca.concept_id
@@ -94,10 +97,11 @@ module Lab
94
97
  )
95
98
 
96
99
  return ActiveRecord::Base.connection.select_all <<~SQL
97
- SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
100
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code, c.uuid
98
101
  FROM concept_attribute ca
99
102
  INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
100
103
  AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
104
+ INNER JOIN concept c ON c.concept_id = ca.concept_id
101
105
  WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
102
106
  AND ca.concept_id IN (#{concept_set.pluck(:concept_id).push(0).join(',')})
103
107
  GROUP BY ca.concept_id
@@ -114,15 +118,16 @@ module Lab
114
118
  measures = ConceptSet.find_members_by_name(Lab::Metadata::TEST_RESULT_INDICATOR_CONCEPT_NAME)
115
119
  .select(:concept_id)
116
120
 
117
- sets = ConceptSet.where(concept_set: measures, concept_id: test)
121
+ sets = ConceptSet.where(concept_set: test, concept_id: measures)
118
122
 
119
123
  return ActiveRecord::Base.connection.select_all <<~SQL
120
- SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
124
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code, c.uuid
121
125
  FROM concept_attribute ca
122
126
  INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
123
127
  AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
128
+ INNER JOIN concept c ON c.concept_id = ca.concept_id
124
129
  WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
125
- AND ca.concept_id IN (#{sets.pluck(:concept_set).push(0).join(',')})
130
+ AND ca.concept_id IN (#{sets.pluck(:concept_id).push(0).join(',')})
126
131
  GROUP BY ca.concept_id
127
132
  SQL
128
133
  end
@@ -227,7 +227,7 @@ module Lab
227
227
  {
228
228
  order: {
229
229
  tracking_number: order_dto.fetch(:tracking_number),
230
- district: current_district,
230
+ district: order_dto.fetch(:districy),
231
231
  health_facility_name: order_dto.fetch(:sending_facility),
232
232
  sending_facility: order_dto.fetch(:sending_facility),
233
233
  arv_number: order_dto.fetch(:patient).fetch(:arv_number),
@@ -281,24 +281,17 @@ module Lab
281
281
  status: 'specimen_collected',
282
282
  time_updated: date_updated,
283
283
  sample_type: order_dto.fetch(:sample_type_map),
284
- updated_by: status.fetch(:updated_by)
284
+ updated_by: status.fetch(:updated_by),
285
+ status_trail: [
286
+ updated_by: {
287
+ first_name: status.fetch(:updated_by).fetch(:first_name),
288
+ last_name: status.fetch(:updated_by).fetch(:last_name),
289
+ id_number: status.fetch(:updated_by).fetch(:id)
290
+ }
291
+ ]
285
292
  }
286
293
  end
287
294
 
288
- def current_district
289
- health_centre = Location.current_health_center
290
- raise 'Current health centre not set' unless health_centre
291
-
292
- district = health_centre.district || Lab::Lims::Config.application['district']
293
-
294
- unless district
295
- health_centre_name = "##{health_centre.id} - #{health_centre.name}"
296
- raise "Current health centre district not set: #{health_centre_name}"
297
- end
298
-
299
- district
300
- end
301
-
302
295
  ##
303
296
  # Extracts sample drawn status from an OrderDto
304
297
  def sample_drawn_status(order_dto)
@@ -444,23 +437,25 @@ module Lab
444
437
  order_dto['test_results'].each do |test_name, results|
445
438
  Rails.logger.info("Pushing result for order ##{order_dto['tracking_number']}")
446
439
  in_authenticated_session do |headers|
447
- params = make_update_test_params(order_dto['tracking_number'], test_name, results)
440
+ params = make_update_test_params(order_dto, test_name, results)
448
441
 
449
- RestClient.post(expand_uri("tests/#{order_dto['tracking_number']}", api_version: 'v2'), params, headers)
442
+ RestClient.put(expand_uri("tests/#{order_dto['tracking_number']}", api_version: 'v2'), params, headers)
450
443
  end
451
444
  end
452
445
  end
453
446
 
454
- def make_update_test_params(_tracking_number, test, results, test_status = 'Drawn')
447
+ def make_update_test_params(order_dto, test, results, test_status = 'Drawn')
448
+ # Find the concept from the test name (which is a string)
449
+ concept = ::ConceptName.find_by(name: test)&.concept
455
450
  {
456
451
  test_status:,
457
452
  time_updated: results['result_date'],
458
453
  test_type: {
459
- name: ::Concept.find(test.concept_id).test_catalogue_name,
460
- nlims_code: ::Concept.find(test.concept_id).nlims_code
454
+ name: concept&.test_catalogue_name,
455
+ nlims_code: concept&.nlims_code
461
456
  },
462
- test_results: results['results'].map do |measure, _value|
463
- measure_name, measure_value = measure
457
+ test_results: results['results'].map do |measure_name, value|
458
+ measure_value = value['result_value']
464
459
  {
465
460
  measure: {
466
461
  name: measure_name,
@@ -472,6 +467,18 @@ module Lab
472
467
  result_date: results['result_date']
473
468
  }
474
469
  }
470
+ end,
471
+ status_trail: order_dto['sample_statuses'].map do |trail_entry|
472
+ date, status = trail_entry.each_pair.first
473
+ {
474
+ status: status['status'],
475
+ timestamp: date,
476
+ updated_by: {
477
+ first_name: status.fetch('updated_by').fetch('first_name'),
478
+ last_name: status.fetch('updated_by').fetch('last_name'),
479
+ id_number: status.fetch('updated_by').fetch('id')
480
+ }
481
+ }
475
482
  end
476
483
  }
477
484
  end
@@ -678,7 +685,7 @@ module Lab
678
685
  def make_create_params(order_dto)
679
686
  {
680
687
  tracking_number: order_dto.fetch(:tracking_number),
681
- district: current_district,
688
+ district: order_dto.fetch(:districy),
682
689
  health_facility_name: order_dto.fetch(:sending_facility),
683
690
  first_name: order_dto.fetch(:patient).fetch(:first_name),
684
691
  last_name: order_dto.fetch(:patient).fetch(:last_name),
@@ -716,20 +723,6 @@ module Lab
716
723
  }
717
724
  end
718
725
 
719
- def current_district
720
- health_centre = Location.current_health_center
721
- raise 'Current health centre not set' unless health_centre
722
-
723
- district = health_centre.district || Lab::Lims::Config.application['district']
724
-
725
- unless district
726
- health_centre_name = "##{health_centre.id} - #{health_centre.name}"
727
- raise "Current health centre district not set: #{health_centre_name}"
728
- end
729
-
730
- district
731
- end
732
-
733
726
  ##
734
727
  # Extracts sample drawn status from an OrderDto
735
728
  def sample_drawn_status(order_dto)
@@ -14,15 +14,17 @@ module Lab
14
14
 
15
15
  def serialize_order(order)
16
16
  serialized_order = Lims::Utils.structify(Lab::LabOrderSerializer.serialize_order(order))
17
+ location = get_location(serialized_order.location_id, order)
18
+
17
19
  Lims::OrderDto.new(
18
20
  _id: Lab::LimsOrderMapping.find_by(order: order)&.lims_id || serialized_order.accession_number,
19
21
  tracking_number: serialized_order.accession_number,
20
- sending_facility: current_facility_name,
22
+ sending_facility: location.name,
21
23
  receiving_facility: serialized_order.target_lab,
22
24
  tests: serialized_order.tests.map { |test| format_test_name(test.name) },
23
25
  tests_map: serialized_order.tests,
24
26
  patient: format_patient(serialized_order.patient_id),
25
- order_location: format_order_location(serialized_order.encounter_id),
27
+ order_location: location.name,
26
28
  sample_type: format_sample_type(serialized_order.specimen.name),
27
29
  sample_type_map: {
28
30
  name: format_sample_type(serialized_order.specimen.name),
@@ -32,7 +34,7 @@ module Lab
32
34
  sample_statuses: format_sample_status_trail(order),
33
35
  test_statuses: format_test_status_trail(order),
34
36
  who_order_test: format_orderer(order),
35
- districy: current_district, # yes districy [sic]...
37
+ districy: get_district(location), # yes districy [sic]...
36
38
  priority: format_sample_priority(serialized_order.reason_for_test.name),
37
39
  date_created: serialized_order.order_date,
38
40
  test_results: format_test_results(serialized_order),
@@ -43,13 +45,26 @@ module Lab
43
45
 
44
46
  private
45
47
 
46
- def format_order_location(encounter_id)
47
- location_id = Encounter.select(:location_id).where(encounter_id:)
48
- location = Location.select(:name)
49
- .where(location_id:)
50
- .first
48
+ def get_location(location_id, order)
49
+ # Try to get location from the location_id first
50
+ location = Location.find_by(location_id: location_id) if location_id.present?
51
+
52
+ # Fallback to current health center
53
+ location ||= Location.current_health_center
54
+
55
+ # Last fallback: try to get from order's observation encounter
56
+ # Use unscoped to find observations/encounters across all locations
57
+ if location.nil? && order.present?
58
+ obs = Observation.unscoped.find_by(order_id: order.order_id)
59
+ if obs.respond_to?(:encounter) && obs.encounter.respond_to?(:location)
60
+ encounter = Encounter.unscoped.find_by(encounter_id: obs.encounter_id)
61
+ location = encounter&.location
62
+ end
63
+ end
64
+
65
+ raise 'Current health center not set' unless location
51
66
 
52
- location&.name
67
+ location
53
68
  end
54
69
 
55
70
  # Format patient into a structure that LIMS expects
@@ -123,11 +138,13 @@ module Lab
123
138
  def format_sample_status_trail(order)
124
139
  return [] if order.concept_id == ConceptName.find_by_name!('Unknown').concept_id
125
140
 
126
- user = User.find(order.creator)
127
- user = User.find(order.discontinued_by) if Order.columns_hash.key?('discontinued_by') && user.blank?
141
+ user = User.unscoped.find(order.creator)
142
+ user = User.unscoped.find(order.discontinued_by) if Order.columns_hash.key?('discontinued_by') && user.blank?
128
143
 
129
144
  drawn_by = PersonName.find_by_person_id(user.user_id)
130
- drawn_date = order.discontinued_date || order.start_date if ['discontinued_date', 'start_date'].all? { |column| order.respond_to?(column) }
145
+ drawn_date = order.discontinued_date || order.start_date if %w[discontinued_date start_date].all? do |column|
146
+ order.respond_to?(column)
147
+ end
131
148
  drawn_date ||= order.date_created
132
149
 
133
150
  [
@@ -179,13 +196,15 @@ module Lab
179
196
  order.tests&.each_with_object({}) do |test, results|
180
197
  next if test.result.nil? || test.result.empty?
181
198
 
182
- result_obs = Observation.find_by(obs_id: test.result.first.id)
199
+ # Use unscoped to find observations across locations
200
+ result_obs = Observation.unscoped.find_by(obs_id: test.result.first.id)
183
201
  unless result_obs
184
202
  Rails.logger.warn("Observation with obs_id=#{test.result.first.id} not found for test #{test.name} in order #{order.accession_number}")
185
203
  next
186
204
  end
187
205
 
188
- test_creator = User.find(result_obs.creator)
206
+ # Use unscoped to find user regardless of location
207
+ test_creator = User.unscoped.find(result_obs.creator)
189
208
  test_creator_name = PersonName.find_by_person_id(test_creator.person_id)
190
209
 
191
210
  results[format_test_name(test.name)] = {
@@ -208,7 +227,7 @@ module Lab
208
227
  end
209
228
 
210
229
  def format_test_name(test_name)
211
- return test_name
230
+ test_name
212
231
  end
213
232
 
214
233
  def format_sample_priority(priority)
@@ -217,16 +236,11 @@ module Lab
217
236
  priority&.titleize
218
237
  end
219
238
 
220
- def current_health_center
221
- health_center = Location.current_health_center
222
- raise 'Current health center not set' unless health_center
223
-
224
- health_center
225
- end
239
+ def get_district(location)
240
+ return nil unless location
226
241
 
227
- def current_district
228
- district = current_health_center.city_village \
229
- || current_health_center.parent&.name \
242
+ district = location.city_village \
243
+ || location.parent&.name \
230
244
  || GlobalProperty.find_by_property('current_health_center_district')&.property_value
231
245
 
232
246
  return district if district
@@ -238,12 +252,8 @@ module Lab
238
252
  Config.application['district']
239
253
  end
240
254
 
241
- def current_facility_name
242
- current_health_center.name
243
- end
244
-
245
255
  def find_user(user_id)
246
- user = User.find(user_id)
256
+ user = User.unscoped.find(user_id)
247
257
  person_name = PersonName.find_by(person_id: user.person_id)
248
258
  phone_number = PersonAttribute.find_by(type: PersonAttributeType.where(name: 'Cell phone number'),
249
259
  person_id: user.person_id)
@@ -43,7 +43,8 @@ module Lab
43
43
  end
44
44
 
45
45
  def self.lab_user
46
- user = User.find_by_username('lab_daemon')
46
+ # Use unscoped to find user regardless of location context
47
+ user = User.unscoped.find_by_username('lab_daemon')
47
48
  return user if user
48
49
 
49
50
  god_user = User.first
@@ -30,7 +30,8 @@ class Lab::NotificationService
30
30
  def create_notification(alert_type, alert_message)
31
31
  return if alert_type != 'LIMS'
32
32
 
33
- lab = User.find_by(username: 'lab_daemon')
33
+ # Use unscoped to find user regardless of location context
34
+ lab = User.unscoped.find_by(username: 'lab_daemon')
34
35
  ActiveRecord::Base.transaction do
35
36
  alert = NotificationAlert.create!(text: alert_message.to_json, date_to_expire: Time.now + not_period.days,
36
37
  creator: lab, changed_by: lab, date_created: Time.now)
@@ -247,7 +247,6 @@ module Lab
247
247
 
248
248
  concept = params.dig(:specimen, :concept)
249
249
  concept ||= params.dig(:specimen, :concept_id)
250
- concept ||= unknown_concept_id
251
250
 
252
251
  order_type = nil
253
252
  order_type = OrderType.find_by_order_type_id!(params[:order_type_id])&.id if params[:order_type_id].present?
@@ -260,7 +259,8 @@ module Lab
260
259
  order.date_created = params[:date]&.to_date || Date.today if order.respond_to?(:date_created)
261
260
  order.start_date = params[:date]&.to_date || Date.today if order.respond_to?(:start_date)
262
261
  order.auto_expire_date = params[:end_date]
263
- order.comment_to_fulfiller = params[:comment_to_fulfiller] if params[:comment_to_fulfiller]
262
+ # Note: comment_to_fulfiller is a has_one association, not a field
263
+ # It will be created via add_comment_to_fulfiller method
264
264
  order.accession_number = access_number
265
265
  order.orderer = User.current&.user_id
266
266
 
@@ -331,7 +331,8 @@ module Lab
331
331
  end
332
332
 
333
333
  def create_order_observation(order, concept_name, date, **values)
334
- creator = User.find_by(username: 'lab_daemon')
334
+ # Use unscoped to find user regardless of location context
335
+ creator = User.unscoped.find_by(username: 'lab_daemon')
335
336
  User.current ||= creator
336
337
  Observation.create!(
337
338
  order:,
@@ -348,7 +349,7 @@ module Lab
348
349
  end
349
350
 
350
351
  def unknown_concept_id
351
- ConceptName.find_by_name!('Unknown').concept_id
352
+ ConceptName.find_by_name!('Unknown').concept
352
353
  end
353
354
 
354
355
  def update_reason_for_test(order, concept_id, force_update: false)
@@ -65,7 +65,7 @@ module Lab
65
65
  order_date: Order.columns.include?('start_date') ? order.start_date : order.date_created,
66
66
  'ARV-Number': find_arv_number(result.person_id),
67
67
  PatientID: result.person_id,
68
- 'Ordered By': Order.columns.include?('provider_id') ? order&.provider&.person&.name : Person.find(order.creator)&.name,
68
+ 'Ordered By': Order.columns.include?('provider_id') ? order&.provider&.person&.name : Person.find(User.unscoped.find(order.creator).person_id)&.name,
69
69
  Result: values }.as_json
70
70
  NotificationService.new.create_notification(result_enter_by, data)
71
71
  end
@@ -15,7 +15,8 @@ module Lab
15
15
  end
16
16
 
17
17
  def authenticate_user(username:, password:, user_agent:, request_ip:)
18
- user = User.find_by_username username
18
+ # Use unscoped for authentication - should not be location-dependent
19
+ user = User.unscoped.find_by_username username
19
20
  encrypted_pass = Password.new(user.password)
20
21
  if encrypted_pass == password
21
22
  generate_token(user, user_agent, request_ip)
@@ -30,7 +31,7 @@ module Lab
30
31
  ##
31
32
  # Validate that the username doesn't already exists
32
33
  def validate(username:)
33
- raise UnprocessableEntityError, 'Username already exists' if User.find_by_username username
34
+ raise UnprocessableEntityError, 'Username already exists' if User.unscoped.find_by_username username
34
35
  end
35
36
 
36
37
  def create_lims_person
data/lib/lab/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lab
4
- VERSION = '2.1.8'
4
+ VERSION = '2.1.9-beta'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: his_emr_api_lab
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.8
4
+ version: 2.1.9.pre.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elizabeth Glaser Pediatric Foundation Malawi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-27 00:00:00.000000000 Z
11
+ date: 2026-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: couchrest
@@ -340,9 +340,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
340
340
  version: '0'
341
341
  required_rubygems_version: !ruby/object:Gem::Requirement
342
342
  requirements:
343
- - - ">="
343
+ - - ">"
344
344
  - !ruby/object:Gem::Version
345
- version: '0'
345
+ version: 1.3.1
346
346
  requirements: []
347
347
  rubygems_version: 3.4.1
348
348
  signing_key: