• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 17466465426

04 Sep 2025 02:02PM UTC coverage: 64.954% (+0.02%) from 64.938%
17466465426

push

github

web-flow
TRUNK-6414: Obs.setValueAsString() should allow "blank" strings (#5289)

9 of 28 new or added lines in 1 file covered. (32.14%)

4 existing lines in 3 files now uncovered.

23397 of 36021 relevant lines covered (64.95%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.39
/api/src/main/java/org/openmrs/Obs.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs;
11

12
import java.text.DateFormat;
13
import java.text.DecimalFormat;
14
import java.text.NumberFormat;
15
import java.text.ParseException;
16
import java.text.SimpleDateFormat;
17
import java.util.Date;
18
import java.util.HashSet;
19
import java.util.LinkedHashSet;
20
import java.util.Locale;
21
import java.util.Set;
22

23
import org.apache.commons.lang3.StringUtils;
24
import org.hibernate.envers.Audited;
25
import org.openmrs.annotation.AllowDirectAccess;
26
import org.openmrs.api.APIException;
27
import org.openmrs.api.context.Context;
28
import org.openmrs.api.db.hibernate.HibernateUtil;
29
import org.openmrs.obs.ComplexData;
30
import org.openmrs.obs.ComplexObsHandler;
31
import org.openmrs.util.Format;
32
import org.openmrs.util.Format.FORMAT_TYPE;
33
import org.openmrs.util.OpenmrsUtil;
34
import org.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
36

37
/**
38
 * An observation is a single unit of clinical information. <br>
39
 * <br>
40
 * Observations are collected and grouped together into one Encounter (one visit). Obs can be
41
 * grouped in a hierarchical fashion. <br>
42
 * <br>
43
 * <p>
44
 * The {@link #getObsGroup()} method returns an optional parent. That parent object is also an Obs.
45
 * The parent Obs object knows about its child objects through the {@link #getGroupMembers()}
46
 * method.
47
 * </p>
48
 * <p>
49
 * (Multi-level hierarchies are achieved by an Obs parent object being a member of another Obs
50
 * (grand)parent object) Read up on the obs table: http://openmrs.org/wiki/Obs_Table_Primer In an
51
 * OpenMRS installation, there may be an occasion need to change an Obs.
52
 * </p>
53
 * <p>
54
 * For example, a site may decide to replace a concept in the dictionary with a more specific set of
55
 * concepts. An observation is part of the official record of an encounter. There may be legal,
56
 * ethical, and auditing consequences from altering a record. It is recommended that you create a
57
 * new Obs and void the old one:
58
 * </p>
59
 * Obs newObs = Obs.newInstance(oldObs); //copies values from oldObs
60
 * newObs.setPreviousVersion(oldObs);
61
 * Context.getObsService().saveObs(newObs,"Your reason for the change here");
62
 * Context.getObsService().voidObs(oldObs, "Your reason for the change here");
63
 * 
64
 * @see Encounter
65
 */
66
@Audited
67
public class Obs extends BaseFormRecordableOpenmrsData {
68
        
69
        /**
70
         * @since 2.1.0
71
         */
72
        public enum Interpretation {
1✔
73
                NORMAL, ABNORMAL, CRITICALLY_ABNORMAL, NEGATIVE, POSITIVE, CRITICALLY_LOW, LOW, HIGH, CRITICALLY_HIGH, VERY_SUSCEPTIBLE, SUSCEPTIBLE, INTERMEDIATE, RESISTANT, SIGNIFICANT_CHANGE_DOWN, SIGNIFICANT_CHANGE_UP, OFF_SCALE_LOW, OFF_SCALE_HIGH
1✔
74
        }
75
        
76
        /**
77
         * @since 2.1.0
78
         */
79
        public enum Status {
1✔
80
                PRELIMINARY, FINAL, AMENDED
1✔
81
        }
82
        
83
        private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm";
84
        
85
        private static final String TIME_PATTERN = "HH:mm";
86
        
87
        private static final String DATE_PATTERN = "yyyy-MM-dd";
88
        
89
        public static final long serialVersionUID = 112342333L;
90
        
91
        private static final Logger log = LoggerFactory.getLogger(Obs.class);
1✔
92
        
93
        protected Integer obsId;
94
        
95
        protected Concept concept;
96
        
97
        protected Date obsDatetime;
98
        
99
        protected String accessionNumber;
100
        
101
        /**
102
         * The "parent" of this obs. It is the grouping that brings other obs together. note:
103
         * obsGroup.getConcept().isSet() should be true This will be non-null if this obs is a member of
104
         * another groupedObs
105
         * 
106
         * @see #isObsGrouping() (??)
107
         */
108
        protected Obs obsGroup;
109
        
110
        /**
111
         * The list of obs grouped under this obs.
112
         */
113
        @AllowDirectAccess
114
        protected Set<Obs> groupMembers;
115
        
116
        protected Concept valueCoded;
117
        
118
        protected ConceptName valueCodedName;
119
        
120
        protected Drug valueDrug;
121
        
122
        protected Integer valueGroupId;
123
        
124
        protected Date valueDatetime;
125
        
126
        protected Double valueNumeric;
127
        
128
        protected String valueModifier;
129
        
130
        protected String valueText;
131
        
132
        protected String valueComplex;
133
        
134
        // ComplexData is not persisted in the database.
135
        protected transient ComplexData complexData;
136
        
137
        protected String comment;
138
        
139
        protected transient Integer personId;
140
        
141
        protected Person person;
142
        
143
        protected Order order;
144
        
145
        protected Location location;
146
        
147
        protected Encounter encounter;
148
        
149
        private Obs previousVersion;
150
        
151
        private Boolean dirty = Boolean.FALSE;
1✔
152
        
153
        private Interpretation interpretation;
154
        
155
        private Status status = Status.FINAL;
1✔
156

157
        private ObsReferenceRange referenceRange;
158

159
        /** default constructor */
160
        public Obs() {
1✔
161
        }
1✔
162
        
163
        /**
164
         * Required parameters constructor A value is also required, but that can be one of: valueCoded,
165
         * valueDrug, valueNumeric, or valueText
166
         * 
167
         * @param person The Person this obs is acting on
168
         * @param question The question concept this obs is related to
169
         * @param obsDatetime The time this obs took place
170
         * @param location The location this obs took place
171
         */
172
        public Obs(Person person, Concept question, Date obsDatetime, Location location) {
1✔
173
                this.person = person;
1✔
174
                if (person != null) {
1✔
175
                        this.personId = person.getPersonId();
1✔
176
                }
177
                this.concept = question;
1✔
178
                this.obsDatetime = obsDatetime;
1✔
179
                this.location = location;
1✔
180
        }
1✔
181
        
182
        /** constructor with id */
183
        public Obs(Integer obsId) {
1✔
184
                this.obsId = obsId;
1✔
185
        }
1✔
186
        
187
        /**
188
         * This is an equivalent to a copy constructor. Creates a new copy of the given
189
         * <code>obsToCopy</code> with a null obs id
190
         * 
191
         * @param obsToCopy The Obs that is going to be copied
192
         * @return a new Obs object with all the same attributes as the given obs
193
         */
194
        public static Obs newInstance(Obs obsToCopy) {
195
                Obs newObs = new Obs(obsToCopy.getPerson(), obsToCopy.getConcept(), obsToCopy.getObsDatetime(),
1✔
196
                        obsToCopy.getLocation());
1✔
197
                
198
                newObs.setObsGroup(obsToCopy.getObsGroup());
1✔
199
                newObs.setAccessionNumber(obsToCopy.getAccessionNumber());
1✔
200
                newObs.setValueCoded(obsToCopy.getValueCoded());
1✔
201
                newObs.setValueDrug(obsToCopy.getValueDrug());
1✔
202
                newObs.setValueGroupId(obsToCopy.getValueGroupId());
1✔
203
                newObs.setValueDatetime(obsToCopy.getValueDatetime());
1✔
204
                newObs.setValueNumeric(obsToCopy.getValueNumeric());
1✔
205
                newObs.setValueModifier(obsToCopy.getValueModifier());
1✔
206
                newObs.setValueText(obsToCopy.getValueText());
1✔
207
                newObs.setComment(obsToCopy.getComment());
1✔
208
                newObs.setEncounter(obsToCopy.getEncounter());
1✔
209
                newObs.setCreator(obsToCopy.getCreator());
1✔
210
                newObs.setDateCreated(obsToCopy.getDateCreated());
1✔
211
                newObs.setVoided(obsToCopy.getVoided());
1✔
212
                newObs.setVoidedBy(obsToCopy.getVoidedBy());
1✔
213
                newObs.setDateVoided(obsToCopy.getDateVoided());
1✔
214
                newObs.setVoidReason(obsToCopy.getVoidReason());
1✔
215
                newObs.setStatus(obsToCopy.getStatus());
1✔
216
                newObs.setInterpretation(obsToCopy.getInterpretation());
1✔
217
                newObs.setOrder(obsToCopy.getOrder());
1✔
218
                
219
                newObs.setValueComplex(obsToCopy.getValueComplex());
1✔
220
                newObs.setComplexData(obsToCopy.getComplexData());
1✔
221
                newObs.setFormField(obsToCopy.getFormFieldNamespace(), obsToCopy.getFormFieldPath());
1✔
222
                
223
                // Copy list of all members, including voided, and put them in respective groups
224
                if (obsToCopy.hasGroupMembers(true)) {
1✔
225
                        for (Obs member : obsToCopy.getGroupMembers(true)) {
1✔
226
                                // if the obs hasn't been saved yet, no need to duplicate it
227
                                if (member.getObsId() == null) {
1✔
228
                                        newObs.addGroupMember(member);
1✔
229
                                } else {
230
                                        Obs newMember = Obs.newInstance(member);
1✔
231
                                        newMember.setPreviousVersion(member);
1✔
232
                                        newObs.addGroupMember(newMember);
1✔
233
                                }
234
                        }
1✔
235
                }
236
                
237
                return newObs;
1✔
238
        }
239
        
240
        // Property accessors
241
        
242
        /**
243
         * @return Returns the comment.
244
         */
245
        public String getComment() {
246
                return comment;
1✔
247
        }
248
        
249
        /**
250
         * @param comment The comment to set.
251
         */
252
        public void setComment(String comment) {
253
                markAsDirty(this.comment, comment);
1✔
254
                this.comment = comment;
1✔
255
        }
1✔
256
        
257
        /**
258
         * @return Returns the concept.
259
         */
260
        public Concept getConcept() {
261
                return concept;
1✔
262
        }
263
        
264
        /**
265
         * @param concept The concept to set.
266
         */
267
        public void setConcept(Concept concept) {
268
                markAsDirty(this.concept, concept);
1✔
269
                this.concept = concept;
1✔
270
        }
1✔
271
        
272
        /**
273
         * Get the concept description that is tied to the concept name that was used when making this
274
         * observation
275
         * 
276
         * @return ConceptDescription the description used
277
         */
278
        public ConceptDescription getConceptDescription() {
279
                // if we don't have a question for this concept,
280
                // then don't bother looking for a description
281
                if (getConcept() == null) {
×
282
                        return null;
×
283
                }
284
                
285
                // ABKTOD: description in which locale?
286
                return concept.getDescription();
×
287
        }
288
        
289
        /**
290
         * @return Returns the encounter.
291
         */
292
        public Encounter getEncounter() {
293
                return encounter;
1✔
294
        }
295
        
296
        /**
297
         * @param encounter The encounter to set.
298
         */
299
        public void setEncounter(Encounter encounter) {
300
                markAsDirty(this.encounter, encounter);
1✔
301
                this.encounter = encounter;
1✔
302
        }
1✔
303
        
304
        /**
305
         * @return Returns the location.
306
         */
307
        public Location getLocation() {
308
                return location;
1✔
309
        }
310
        
311
        /**
312
         * @param location The location to set.
313
         */
314
        public void setLocation(Location location) {
315
                markAsDirty(this.location, location);
1✔
316
                this.location = location;
1✔
317
        }
1✔
318
        
319
        /**
320
         * @return Returns the obsDatetime.
321
         */
322
        public Date getObsDatetime() {
323
                return obsDatetime;
1✔
324
        }
325
        
326
        /**
327
         * @param obsDatetime The obsDatetime to set.
328
         */
329
        public void setObsDatetime(Date obsDatetime) {
330
                markAsDirty(this.obsDatetime, obsDatetime);
1✔
331
                this.obsDatetime = obsDatetime;
1✔
332
        }
1✔
333
        
334
        /**
335
         * An obs grouping occurs when the question (#getConcept()) is a set. (@link
336
         * org.openmrs.Concept#isSet()) If this is non-null, it means the current Obs is in the list
337
         * returned by <code>obsGroup</code>.{@link #getGroupMembers()}
338
         * 
339
         * @return the Obs that is the grouping factor
340
         */
341
        public Obs getObsGroup() {
342
                return obsGroup;
1✔
343
        }
344
        
345
        /**
346
         * This method does NOT add this current obs to the list of obs in obsGroup.getGroupMembers().
347
         * That must be done (and should be done) manually. (I am not doing it here for fear of screwing
348
         * up the normal loading and creation of this object via hibernate/spring)
349
         * 
350
         * @param obsGroup the obsGroup to set
351
         */
352
        public void setObsGroup(Obs obsGroup) {
353
                markAsDirty(this.obsGroup, obsGroup);
1✔
354
                this.obsGroup = obsGroup;
1✔
355
        }
1✔
356
        
357
        /**
358
         * Convenience method that checks for if this obs has 1 or more group members (either voided or
359
         * non-voided) Note this method differs from hasGroupMembers(), as that method excludes voided
360
         * obs; logic is that while a obs that has only voided group members should be seen as
361
         * "having no group members" it still should be considered an "obs grouping"
362
         * <p>
363
         * NOTE: This method could also be called "isObsGroup" for a little less confusion on names.
364
         * However, jstl in a web layer (or any psuedo-getter) access isn't good with both an
365
         * "isObsGroup" method and a "getObsGroup" method. Which one should be returned with a
366
         * simplified jstl call like ${obs.obsGroup} ? With this setup, ${obs.obsGrouping} returns a
367
         * boolean of whether this obs is a parent and has members. ${obs.obsGroup} returns the parent
368
         * object to this obs if this obs is a group member of some other group.
369
         * 
370
         * @return true if this is the parent group of other obs
371
         */
372
        public boolean isObsGrouping() {
373
                return hasGroupMembers(true);
1✔
374
        }
375
        
376
        /**
377
         * A convenience method to check for nullity and length to determine if this obs has group
378
         * members. By default, this ignores voided-objects. To include voided, use
379
         * {@link #hasGroupMembers(boolean)} with value true.
380
         * 
381
         * @return true if this is the parent group of other obs
382
         * <strong>Should</strong> not include voided obs
383
         */
384
        public boolean hasGroupMembers() {
385
                return hasGroupMembers(false);
1✔
386
        }
387
        
388
        /**
389
         * Convenience method that checks for nullity and length to determine if this obs has group
390
         * members. The parameter specifies if this method whether or not voided obs should be
391
         * considered.
392
         * 
393
         * @param includeVoided determines if Voided members should be considered as group members.
394
         * @return true if this is the parent group of other Obs
395
         * <strong>Should</strong> return true if this obs has group members based on parameter
396
         */
397
        public boolean hasGroupMembers(boolean includeVoided) {
398
                // ! symbol used because if it's not empty, we want true
399
                return !org.springframework.util.CollectionUtils.isEmpty(getGroupMembers(includeVoided));
1✔
400
        }
401
        
402
        /**
403
         * Get the non-voided members of the obs group, if this obs is a group. By default this method
404
         * only returns non-voided group members. To get all group members, use
405
         * {@link #getGroupMembers(boolean)} with value true.
406
         * <p>
407
         * If it's not a group (i.e. {@link #getConcept()}.{@link org.openmrs.Concept#getSet()} is not
408
         * true, then this returns null.
409
         * 
410
         * @return a Set&lt;Obs&gt; of the members of this group.
411
         * @see #addGroupMember(Obs)
412
         * @see #hasGroupMembers()
413
         */
414
        public Set<Obs> getGroupMembers() {
415
                //same as just returning groupMembers
416
                return getGroupMembers(false);
1✔
417
        }
418
        
419
        /**
420
         * Get the group members of this obs group, if this obs is a group. This method will either
421
         * return all group members, or only non-voided group members, depending on if the argument is
422
         * set to be true or false respectively.
423
         * 
424
         * @param includeVoided
425
         * @return the set of group members in this obs group
426
         * <strong>Should</strong> Get all group members if passed true, and non-voided if passed false
427
         */
428
        public Set<Obs> getGroupMembers(boolean includeVoided) {
429
                if (includeVoided) {
1✔
430
                        //just return all group members
431
                        return groupMembers;
1✔
432
                }
433
                if (groupMembers == null) {
1✔
434
                        //Empty set so return null
435
                        return null;
1✔
436
                }
437
                Set<Obs> nonVoided = new LinkedHashSet<>(groupMembers);
1✔
438
                nonVoided.removeIf(BaseOpenmrsData::getVoided);
1✔
439
                return nonVoided;
1✔
440
        }
441
        
442
        /**
443
         * Set the members of the obs group, if this obs is a group.
444
         * <p>
445
         * If it's not a group (i.e. {@link #getConcept()}.{@link org.openmrs.Concept#getSet()} is not
446
         * true, then this returns null.
447
         * 
448
         * @param groupMembers the groupedObs to set
449
         * @see #addGroupMember(Obs)
450
         * @see #hasGroupMembers()
451
         * <strong>Should</strong> mark the obs as dirty when the set is changed from null to a non empty one
452
         * <strong>Should</strong> not mark the obs as dirty when the set is changed from null to an empty one
453
         * <strong>Should</strong> mark the obs as dirty when the set is replaced with another with different members
454
         * <strong>Should</strong> not mark the obs as dirty when the set is replaced with another with same members
455
         */
456
        public void setGroupMembers(Set<Obs> groupMembers) {
457
                //Copy over the entire list
458
                this.groupMembers = groupMembers;
1✔
459
                
460
        }
1✔
461
        
462
        /**
463
         * Convenience method to add the given <code>obs</code> to this grouping. Will implicitly make
464
         * this obs an ObsGroup.
465
         * 
466
         * @param member Obs to add to this group
467
         * @see #setGroupMembers(Set)
468
         * @see #getGroupMembers()
469
         * <strong>Should</strong> return true when a new obs is added as a member
470
         * <strong>Should</strong> return false when a duplicate obs is added as a member
471
         */
472
        public void addGroupMember(Obs member) {
473
                if (member == null) {
1✔
474
                        return;
×
475
                }
476
                
477
                if (getGroupMembers() == null) {
1✔
478
                        groupMembers = new HashSet<>();
1✔
479
                }
480
                
481
                // a quick sanity check to make sure someone isn't adding
482
                // itself to the group
483
                if (member.equals(this)) {
1✔
484
                        throw new APIException("Obs.error.groupCannotHaveItselfAsAMentor", new Object[] { this, member });
1✔
485
                }
486
                
487
                member.setObsGroup(this);
1✔
488
                groupMembers.add(member);
1✔
489
        }
1✔
490
        
491
        /**
492
         * Convenience method to remove an Obs from this grouping This also removes the link in the
493
         * given <code>obs</code>object to this obs grouper
494
         * 
495
         * @param member Obs to remove from this group
496
         * @see #setGroupMembers(Set)
497
         * @see #getGroupMembers()
498
         * <strong>Should</strong> return true when an obs is removed
499
         * <strong>Should</strong> return false when a non existent obs is removed
500
         */
501
        public void removeGroupMember(Obs member) {
502
                if (member == null || getGroupMembers() == null) {
1✔
503
                        return;
1✔
504
                }
505
                
506
                if (groupMembers.remove(member)) {
1✔
507
                        member.setObsGroup(null);
1✔
508
                }
509
        }
1✔
510
        
511
        /**
512
         * Convenience method that returns related Obs If the Obs argument is not an ObsGroup: a
513
         * Set&lt;Obs&gt; will be returned containing all of the children of this Obs' parent that are
514
         * not ObsGroups themselves. This will include this Obs by default, unless getObsGroup() returns
515
         * null, in which case an empty set is returned. If the Obs argument is an ObsGroup: a
516
         * Set&lt;Obs&gt; will be returned containing 1. all of this Obs' group members, and 2. all
517
         * ancestor Obs that are not themselves obsGroups.
518
         * 
519
         * @return Set&lt;Obs&gt;
520
         */
521
        public Set<Obs> getRelatedObservations() {
522
                Set<Obs> ret = new HashSet<>();
1✔
523
                if (this.isObsGrouping()) {
1✔
524
                        ret.addAll(this.getGroupMembers());
1✔
525
                        Obs parentObs = this;
1✔
526
                        while (parentObs.getObsGroup() != null) {
1✔
527
                                for (Obs obsSibling : parentObs.getObsGroup().getGroupMembers()) {
1✔
528
                                        if (!obsSibling.isObsGrouping()) {
1✔
529
                                                ret.add(obsSibling);
1✔
530
                                        }
531
                                }
1✔
532
                                parentObs = parentObs.getObsGroup();
1✔
533
                        }
534
                } else if (this.getObsGroup() != null) {
1✔
535
                        for (Obs obsSibling : this.getObsGroup().getGroupMembers()) {
1✔
536
                                if (!obsSibling.isObsGrouping()) {
1✔
537
                                        ret.add(obsSibling);
1✔
538
                                }
539
                        }
1✔
540
                }
541
                return ret;
1✔
542
        }
543
        
544
        /**
545
         * @return Returns the obsId.
546
         */
547
        public Integer getObsId() {
548
                return obsId;
1✔
549
        }
550
        
551
        /**
552
         * @param obsId The obsId to set.
553
         */
554
        public void setObsId(Integer obsId) {
555
                this.obsId = obsId;
1✔
556
        }
1✔
557
        
558
        /**
559
         * @return Returns the order.
560
         */
561
        public Order getOrder() {
562
                return order;
1✔
563
        }
564
        
565
        /**
566
         * @param order The order to set.
567
         */
568
        public void setOrder(Order order) {
569
                markAsDirty(this.order, order);
1✔
570
                this.order = order;
1✔
571
        }
1✔
572
        
573
        /**
574
         * The person id of the person on this object. This should be the same as
575
         * <code>{@link #getPerson()}.getPersonId()</code>. It is duplicated here for speed and
576
         * simplicity reasons
577
         * 
578
         * @return the integer person id of the person this obs is acting on
579
         */
580
        public Integer getPersonId() {
581
                return personId;
1✔
582
        }
583
        
584
        /**
585
         * Set the person id on this obs object. This method is here for convenience, but really the
586
         * {@link #setPerson(Person)} method should be used like
587
         * <code>setPerson(new Person(personId))</code>
588
         * 
589
         * @see #setPerson(Person)
590
         * @param personId
591
         */
592
        protected void setPersonId(Integer personId) {
593
                markAsDirty(this.personId, personId);
1✔
594
                this.personId = personId;
1✔
595
        }
1✔
596
        
597
        /**
598
         * Get the person object that this obs is acting on.
599
         * 
600
         * @see #getPersonId()
601
         * @return the person object
602
         */
603
        public Person getPerson() {
604
                return person;
1✔
605
        }
606
        
607
        /**
608
         * Set the person object to this obs object. This will also set the personId on this obs object
609
         * 
610
         * @see #setPersonId(Integer)
611
         * @param person the Patient/Person object that this obs is acting on
612
         */
613
        public void setPerson(Person person) {
614
                markAsDirty(this.person, person);
1✔
615
                this.person = person;
1✔
616
                if (person != null) {
1✔
617
                        setPersonId(person.getPersonId());
1✔
618
                }
619
        }
1✔
620
        
621
        /**
622
         * Sets the value of this obs to the specified valueBoolean if this obs has a boolean concept.
623
         * 
624
         * @param valueBoolean the boolean value matching the boolean coded concept to set to
625
         */
626
        public void setValueBoolean(Boolean valueBoolean) {
627
                if (getConcept() != null && getConcept().getDatatype() != null && getConcept().getDatatype().isBoolean()) {
1✔
628
                        if (valueBoolean != null) {
1✔
629
                                setValueCoded(valueBoolean ? Context.getConceptService().getTrueConcept() : Context.getConceptService()
1✔
630
                                        .getFalseConcept());
1✔
631
                        } else {
632
                                setValueCoded(null);
×
633
                        }
634
                }
635
        }
1✔
636
        
637
        /**
638
         * Coerces a value to a Boolean representation
639
         * 
640
         * @return Boolean representation of the obs value
641
         * <strong>Should</strong> return true for value_numeric concepts if value is 1
642
         * <strong>Should</strong> return false for value_numeric concepts if value is 0
643
         * <strong>Should</strong> return null for value_numeric concepts if value is neither 1 nor 0
644
         */
645
        public Boolean getValueAsBoolean() {
646
                
647
                if (getValueCoded() != null) {
1✔
648
                        if (getValueCoded().equals(Context.getConceptService().getTrueConcept())) {
×
649
                                return Boolean.TRUE;
×
650
                        } else if (getValueCoded().equals(Context.getConceptService().getFalseConcept())) {
×
651
                                return Boolean.FALSE;
×
652
                        }
653
                } else if (getValueNumeric() != null) {
1✔
654
                        if (getValueNumeric() == 1) {
1✔
655
                                return Boolean.TRUE;
1✔
656
                        } else if (getValueNumeric() == 0) {
1✔
657
                                return Boolean.FALSE;
1✔
658
                        }
659
                }
660
                //returning null is preferred to defaulting to false to support validation of user input is from a form
661
                return null;
1✔
662
        }
663
        
664
        /**
665
         * Returns the boolean value if the concept of this obs is of boolean datatype
666
         * 
667
         * @return true or false if value is set otherwise null
668
         * <strong>Should</strong> return true if value coded answer concept is true concept
669
         * <strong>Should</strong> return false if value coded answer concept is false concept
670
         */
671
        public Boolean getValueBoolean() {
672
                if (getConcept() != null && valueCoded != null && getConcept().getDatatype().isBoolean()) {
1✔
673
                        Concept trueConcept = Context.getConceptService().getTrueConcept();
1✔
674
                        return trueConcept != null && valueCoded.getId().equals(trueConcept.getId());
1✔
675
                }
676
                
677
                return null;
1✔
678
        }
679
        
680
        /**
681
         * @return Returns the valueCoded.
682
         */
683
        
684
        public Concept getValueCoded() {
685
                return valueCoded;
1✔
686
        }
687
        
688
        /**
689
         * @param valueCoded The valueCoded to set.
690
         */
691
        public void setValueCoded(Concept valueCoded) {
692
                markAsDirty(this.valueCoded, valueCoded);
1✔
693
                this.valueCoded = valueCoded;
1✔
694
        }
1✔
695
        
696
        /**
697
         * Gets the specific name used for the coded value.
698
         * 
699
         * @return the name of the coded value
700
         */
701
        public ConceptName getValueCodedName() {
702
                return valueCodedName;
1✔
703
        }
704
        
705
        /**
706
         * Sets the specific name used for the coded value.
707
         * 
708
         * @param valueCodedName the name of the coded value
709
         */
710
        public void setValueCodedName(ConceptName valueCodedName) {
711
                markAsDirty(this.valueCodedName, valueCodedName);
1✔
712
                this.valueCodedName = valueCodedName;
1✔
713
        }
1✔
714
        
715
        /**
716
         * @return Returns the valueDrug
717
         */
718
        public Drug getValueDrug() {
719
                return valueDrug;
1✔
720
        }
721
        
722
        /**
723
         * @param valueDrug The valueDrug to set.
724
         */
725
        public void setValueDrug(Drug valueDrug) {
726
                markAsDirty(this.valueDrug, valueDrug);
1✔
727
                this.valueDrug = valueDrug;
1✔
728
        }
1✔
729
        
730
        /**
731
         * @return Returns the valueDatetime.
732
         */
733
        public Date getValueDatetime() {
734
                return valueDatetime;
1✔
735
        }
736
        
737
        /**
738
         * @param valueDatetime The valueDatetime to set.
739
         */
740
        public void setValueDatetime(Date valueDatetime) {
741
                markAsDirty(this.valueDatetime, valueDatetime);
1✔
742
                this.valueDatetime = valueDatetime;
1✔
743
        }
1✔
744
        
745
        /**
746
         * @return the value of this obs as a Date. Note that this uses a java.util.Date, so it includes
747
         *         a time component, that should be ignored.
748
         * @since 1.9
749
         */
750
        public Date getValueDate() {
751
                return valueDatetime;
1✔
752
        }
753
        
754
        /**
755
         * @param valueDate The date value to set.
756
         * @since 1.9
757
         */
758
        public void setValueDate(Date valueDate) {
759
                markAsDirty(this.valueDatetime, valueDate);
1✔
760
                this.valueDatetime = valueDate;
1✔
761
        }
1✔
762
        
763
        /**
764
         * @return the time value of this obs. Note that this uses a java.util.Date, so it includes a
765
         *         date component, that should be ignored.
766
         * @since 1.9
767
         */
768
        public Date getValueTime() {
769
                return valueDatetime;
1✔
770
        }
771
        
772
        /**
773
         * @param valueTime the time value to set
774
         * @since 1.9
775
         */
776
        public void setValueTime(Date valueTime) {
777
                markAsDirty(this.valueDatetime, valueTime);
1✔
778
                this.valueDatetime = valueTime;
1✔
779
        }
1✔
780
        
781
        /**
782
         * @return Returns the valueGroupId.
783
         */
784
        public Integer getValueGroupId() {
785
                return valueGroupId;
1✔
786
        }
787
        
788
        /**
789
         * @param valueGroupId The valueGroupId to set.
790
         */
791
        public void setValueGroupId(Integer valueGroupId) {
792
                markAsDirty(this.valueGroupId, valueGroupId);
1✔
793
                this.valueGroupId = valueGroupId;
1✔
794
        }
1✔
795
        
796
        /**
797
         * @return Returns the valueModifier.
798
         */
799
        public String getValueModifier() {
800
                return valueModifier;
1✔
801
        }
802
        
803
        /**
804
         * @param valueModifier The valueModifier to set.
805
         */
806
        public void setValueModifier(String valueModifier) {
807
                markAsDirty(this.valueModifier, valueModifier);
1✔
808
                this.valueModifier = valueModifier;
1✔
809
        }
1✔
810
        
811
        /**
812
         * @return Returns the valueNumeric.
813
         */
814
        public Double getValueNumeric() {
815
                return valueNumeric;
1✔
816
        }
817
        
818
        /**
819
         * @param valueNumeric The valueNumeric to set.
820
         */
821
        public void setValueNumeric(Double valueNumeric) {
822
                markAsDirty(this.valueNumeric, valueNumeric);
1✔
823
                this.valueNumeric = valueNumeric;
1✔
824
        }
1✔
825
        
826
        /**
827
         * @return Returns the valueText.
828
         */
829
        public String getValueText() {
830
                return valueText;
1✔
831
        }
832
        
833
        /**
834
         * @param valueText The valueText to set.
835
         */
836
        public void setValueText(String valueText) {
837
                markAsDirty(this.valueText, valueText);
1✔
838
                this.valueText = valueText;
1✔
839
        }
1✔
840
        
841
        /**
842
         * @return Returns true if this Obs is complex.
843
         * @since 1.5
844
         * <strong>Should</strong> return true if the concept is complex
845
         */
846
        public boolean isComplex() {
847
                if (getConcept() != null) {
1✔
848
                        return getConcept().isComplex();
1✔
849
                }
850
                
851
                return false;
1✔
852
        }
853
        
854
        /**
855
         * Get the value for the ComplexData. This method is used by the ComplexObsHandler. The
856
         * valueComplex has two parts separated by a bar '|' character: part A) the title; and part B)
857
         * the URI. The title is the readable description of the valueComplex that is returned by
858
         * {@link Obs#getValueAsString(java.util.Locale)}. The URI is the location where the ComplexData is stored.
859
         * 
860
         * @return readable title and URI for the location of the ComplexData binary object.
861
         * @since 1.5
862
         */
863
        public String getValueComplex() {
864
                return this.valueComplex;
1✔
865
        }
866
        
867
        /**
868
         * Set the value for the ComplexData. This method is used by the ComplexObsHandler. The
869
         * valueComplex has two parts separated by a bar '|' character: part A) the title; and part B)
870
         * the URI. The title is the readable description of the valueComplex that is returned by
871
         * {@link Obs#getValueAsString(java.util.Locale)}. The URI is the location where the ComplexData is stored.
872
         * 
873
         * @param valueComplex readable title and URI for the location of the ComplexData binary object.
874
         * @since 1.5
875
         */
876
        public void setValueComplex(String valueComplex) {
877
                markAsDirty(this.valueComplex, valueComplex);
1✔
878
                this.valueComplex = valueComplex;
1✔
879
        }
1✔
880
        
881
        /**
882
         * Set the ComplexData for this Obs. The ComplexData is stored in the file system or elsewhere,
883
         * but is not persisted to the database. <br>
884
         * <br>
885
         * {@link ComplexObsHandler}s that are registered to {@link ConceptComplex}s will persist the
886
         * {@link ComplexData#getData()} object to the correct place for the given concept.
887
         * 
888
         * @param complexData
889
         * @since 1.5
890
         */
891
        public void setComplexData(ComplexData complexData) {
892
                markAsDirty(this.complexData, complexData);
1✔
893
                this.complexData = complexData;
1✔
894
        }
1✔
895
        
896
        /**
897
         * Get the ComplexData. This is retrieved by the {@link ComplexObsHandler} from the file system
898
         * or another location, not from the database. <br>
899
         * <br>
900
         * This will be null unless you call:
901
         * 
902
         * <pre>
903
         * Obs obsWithComplexData =
904
         * Context.getObsService().getComplexObs(obsId, OpenmrsConstants.RAW_VIEW);
905
         * 
906
         * <pre/>
907
         *
908
         * @return the complex data for this obs (if its a complex obs)
909
         * @since 1.5
910
         */
911
        public ComplexData getComplexData() {
912
                return this.complexData;
1✔
913
        }
914
        
915
        /**
916
         * @return Returns the accessionNumber.
917
         */
918
        public String getAccessionNumber() {
919
                return accessionNumber;
1✔
920
        }
921
        
922
        /**
923
         * @param accessionNumber The accessionNumber to set.
924
         */
925
        public void setAccessionNumber(String accessionNumber) {
926
                markAsDirty(this.accessionNumber, accessionNumber);
1✔
927
                this.accessionNumber = accessionNumber;
1✔
928
        }
1✔
929
        
930
        /***************************************************************************
931
         * Convenience methods
932
         **************************************************************************/
933
        
934
        /**
935
         * Convenience method for obtaining the observation's value as a string If the Obs is complex,
936
         * returns the title of the complexData denoted by the section of getValueComplex() before the
937
         * first bar '|' character; or returns the entire getValueComplex() if the bar '|' character is
938
         * missing.
939
         *
940
         * @param locale locale for locale-specific depictions of value
941
         * <strong>Should</strong> return first part of valueComplex for complex obs
942
         * <strong>Should</strong> return first part of valueComplex for non null valueComplexes
943
         * <strong>Should</strong> return non precise values for NumericConcepts
944
         * <strong>Should</strong> return date in correct format
945
         * <strong>Should</strong> not return long decimal numbers as scientific notation
946
         * <strong>Should</strong> use commas or decimal places depending on locale
947
         * <strong>Should</strong> not use thousand separator
948
         * <strong>Should</strong> return regular number for size of zero to or greater than ten digits
949
         * <strong>Should</strong> return regular number if decimal places are as high as six
950
         */
951
        public String getValueAsString(Locale locale) {
952
                // formatting for the return of numbers of type double
953
                NumberFormat nf = NumberFormat.getNumberInstance(locale);
1✔
954
                DecimalFormat df = (DecimalFormat) nf;
1✔
955
                // formatting style up to 6 digits
956
                df.applyPattern("#0.0#####");
1✔
957
                //branch on hl7 abbreviations
958
                if (getConcept() != null) {
1✔
959
                        String abbrev = getConcept().getDatatype().getHl7Abbreviation();
1✔
960
                        if ("BIT".equals(abbrev)) {
1✔
961
                                return getValueAsBoolean() == null ? "" : getValueAsBoolean().toString();
×
962
                        } else if ("CWE".equals(abbrev)) {
1✔
963
                                if (getValueCoded() == null) {
1✔
964
                                        return "";
×
965
                                }
966
                                if (getValueDrug() != null) {
1✔
967
                                        return getValueDrug().getFullName(locale);
×
968
                                } else {
969
                                        ConceptName codedName = getValueCodedName();
1✔
970
                                        if (codedName != null) {
1✔
971
                                                return getValueCoded().getName(locale, false).getName();
1✔
972
                                        } else {
973
                                                ConceptName fallbackName = getValueCoded().getName();
×
974
                                                if (fallbackName != null) {
×
975
                                                        return fallbackName.getName();
×
976
                                                } else {
977
                                                        return "";
×
978
                                                }
979
                                                
980
                                        }
981
                                }
982
                        } else if ("NM".equals(abbrev) || "SN".equals(abbrev)) {
1✔
983
                                if (getValueNumeric() == null) {
1✔
984
                                        return "";
×
985
                                } else {
986
                                        Concept deproxiedConcept = HibernateUtil.getRealObjectFromProxy(getConcept());
1✔
987
                                        if (deproxiedConcept instanceof ConceptNumeric) {
1✔
988
                                                ConceptNumeric cn = (ConceptNumeric) deproxiedConcept;
1✔
989
                                                if (!cn.getAllowDecimal()) {
1✔
990
                                                        double d = getValueNumeric();
1✔
991
                                                        int i = (int) d;
1✔
992
                                                        return Integer.toString(i);
1✔
993
                                                } else {
994
                                                        df.format(getValueNumeric());
×
995
                                                }
996
                                        }
997
                                }
×
998
                        } else if ("DT".equals(abbrev)) {
1✔
999
                                DateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
1✔
1000
                                return (getValueDatetime() == null ? "" : dateFormat.format(getValueDatetime()));
1✔
1001
                        } else if ("TM".equals(abbrev)) {
1✔
1002
                                return (getValueDatetime() == null ? "" : Format.format(getValueDatetime(), locale, FORMAT_TYPE.TIME));
×
1003
                        } else if ("TS".equals(abbrev)) {
1✔
1004
                                return (getValueDatetime() == null ? "" : Format.format(getValueDatetime(), locale, FORMAT_TYPE.TIMESTAMP));
×
1005
                        } else if ("ST".equals(abbrev)) {
1✔
1006
                                return getValueText();
×
1007
                        } else if ("ED".equals(abbrev) && getValueComplex() != null) {
1✔
1008
                                String[] valuesComplex = getValueComplex().split("\\|");
×
1009
                                for (String value : valuesComplex) {
×
1010
                                        if (StringUtils.isNotEmpty(value)) {
×
1011
                                                return value.trim();
×
1012
                                        }
1013
                                }
1014
                        }
1015
                }
1016
                
1017
                // if the datatype is 'unknown', default to just returning what is not null
1018
                if (getValueNumeric() != null) {
1✔
1019
                        return df.format(getValueNumeric());
1✔
1020
                } else if (getValueCoded() != null) {
×
1021
                        if (getValueDrug() != null) {
×
1022
                                return getValueDrug().getFullName(locale);
×
1023
                        } else {
1024
                                ConceptName valudeCodedName = getValueCodedName();
×
1025
                                if (valudeCodedName != null) {
×
1026
                                        return valudeCodedName.getName();
×
1027
                                } else {
1028
                                        return "";
×
1029
                                }
1030
                        }
1031
                } else if (getValueDatetime() != null) {
×
1032
                        return Format.format(getValueDatetime(), locale, FORMAT_TYPE.DATE);
×
1033
                } else if (getValueText() != null) {
×
1034
                        return getValueText();
×
1035
                } else if (hasGroupMembers()) {
×
1036
                        // all of the values are null and we're an obs group...so loop
1037
                        // over the members and just do a getValueAsString on those
1038
                        // this could potentially cause an infinite loop if an obs group
1039
                        // is a member of its own group at some point in the hierarchy
1040
                        StringBuilder sb = new StringBuilder();
×
1041
                        for (Obs groupMember : getGroupMembers()) {
×
1042
                                if (sb.length() > 0) {
×
1043
                                        sb.append(", ");
×
1044
                                }
1045
                                sb.append(groupMember.getValueAsString(locale));
×
1046
                        }
×
1047
                        return sb.toString();
×
1048
                }
1049
                
1050
                // returns the title portion of the valueComplex
1051
                // which is everything before the first bar '|' character.
1052
                if (getValueComplex() != null) {
×
1053
                        String[] valuesComplex = getValueComplex().split("\\|");
×
1054
                        for (String value : valuesComplex) {
×
1055
                                if (StringUtils.isNotEmpty(value)) {
×
1056
                                        return value.trim();
×
1057
                                }
1058
                        }
1059
                }
1060
                
1061
                return "";
×
1062
        }
1063
        
1064
        /**
1065
         * Sets the value for the obs from a string depending on the datatype of the question concept
1066
         *
1067
         * @param s the string to coerce to a boolean
1068
         * <strong>Should</strong> set value as boolean if the datatype of the question concept is boolean
1069
         * <strong>Should</strong> fail if the value of the string is null
1070
         * <strong>Should</strong> fail if the value of the string is empty
1071
         */
1072
        public void setValueAsString(String s) throws ParseException {
1073
                log.debug("getConcept() == {}", getConcept());
1✔
1074

1075
                if (getConcept() != null && s != null) {
1✔
1076
                        String abbrev = getConcept().getDatatype().getHl7Abbreviation();
1✔
1077
                        if ("ST".equals(abbrev)) {
1✔
1078
                                if (!StringUtils.isEmpty(s)) {
1✔
1079
                                        setValueText(s);
1✔
1080
                                }
1081
                                else {
1082
                                        throw new RuntimeException("Cannot set value to a empty string for concept: " + getConcept().getDisplayString());
1✔
1083
                                }
1084
                        } else if (!StringUtils.isBlank(s)) {
1✔
NEW
1085
                                s = s.trim();
×
NEW
1086
                                if ("BIT".equals(abbrev)) {
×
NEW
1087
                                        setValueBoolean(Boolean.valueOf(s));
×
NEW
1088
                                } else if ("CWE".equals(abbrev)) {
×
NEW
1089
                                        throw new RuntimeException("Not Yet Implemented");
×
NEW
1090
                                } else if ("NM".equals(abbrev) || "SN".equals(abbrev)) {
×
NEW
1091
                                        setValueNumeric(Double.valueOf(s));
×
NEW
1092
                                } else if ("DT".equals(abbrev)) {
×
NEW
1093
                                        DateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
×
NEW
1094
                                        setValueDatetime(dateFormat.parse(s));
×
NEW
1095
                                } else if ("TM".equals(abbrev)) {
×
NEW
1096
                                        DateFormat timeFormat = new SimpleDateFormat(TIME_PATTERN);
×
NEW
1097
                                        setValueDatetime(timeFormat.parse(s));
×
NEW
1098
                                } else if ("TS".equals(abbrev)) {
×
NEW
1099
                                        DateFormat datetimeFormat = new SimpleDateFormat(DATE_TIME_PATTERN);
×
NEW
1100
                                        setValueDatetime(datetimeFormat.parse(s));
×
NEW
1101
                                }  else {
×
NEW
1102
                                        throw new RuntimeException("Don't know how to handle " + abbrev + " for concept: " + getConcept().getDisplayString());
×
1103
                                }
1104
                        } else {
1105
                                throw new RuntimeException("Cannot set value to a blank string for concept: " + getConcept().getDisplayString());
1✔
1106
                        }
1107
                } else {
1✔
1108
                        if (s == null) {
1✔
1109
                                throw new RuntimeException("cannot set value to null via setValueAsString()");
1✔
1110
                        } else {
NEW
1111
                                throw new RuntimeException("concept is null for " + this);
×
1112
                        }
1113
                }
1114
        }
1✔
1115
        
1116
        /**
1117
         * @see java.lang.Object#toString()
1118
         */
1119
        @Override
1120
        public String toString() {
1121
                if (obsId == null) {
1✔
1122
                        return "obs id is null";
1✔
1123
                }
1124
                
1125
                return "Obs #" + obsId.toString();
1✔
1126
        }
1127
        
1128
        /**
1129
         * @since 1.5
1130
         * @see org.openmrs.OpenmrsObject#getId()
1131
         */
1132
        @Override
1133
        public Integer getId() {
1134
                return getObsId();
1✔
1135
                
1136
        }
1137
        
1138
        /**
1139
         * @since 1.5
1140
         * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
1141
         */
1142
        @Override
1143
        public void setId(Integer id) {
1144
                setObsId(id);
1✔
1145
                
1146
        }
1✔
1147
        
1148
        /**
1149
         * When ObsService updates an obs, it voids the old version, creates a new Obs with the updates,
1150
         * and adds a reference to the previousVersion in the new Obs. getPreviousVersion returns the
1151
         * last version of this Obs.
1152
         */
1153
        public Obs getPreviousVersion() {
1154
                return previousVersion;
1✔
1155
        }
1156
        
1157
        /**
1158
         * A previousVersion indicates that this Obs replaces an earlier one.
1159
         *
1160
         * @param previousVersion the Obs that this Obs superceeds
1161
         */
1162
        public void setPreviousVersion(Obs previousVersion) {
1163
                markAsDirty(this.previousVersion, previousVersion);
1✔
1164
                this.previousVersion = previousVersion;
1✔
1165
        }
1✔
1166
        
1167
        public Boolean hasPreviousVersion() {
1168
                return getPreviousVersion() != null;
1✔
1169
        }
1170
        
1171
        /**
1172
         * @param creator
1173
         * @see Auditable#setCreator(User)
1174
         */
1175
        @Override
1176
        public void setCreator(User creator) {
1177
                markAsDirty(getCreator(), creator);
1✔
1178
                super.setCreator(creator);
1✔
1179
        }
1✔
1180
        
1181
        /**
1182
         * @param dateCreated
1183
         * @see Auditable#setDateCreated(Date)
1184
         */
1185
        @Override
1186
        public void setDateCreated(Date dateCreated) {
1187
                markAsDirty(getDateCreated(), dateCreated);
1✔
1188
                super.setDateCreated(dateCreated);
1✔
1189
        }
1✔
1190
        
1191
        /**
1192
         * @see org.openmrs.FormRecordable#setFormField(String,String)
1193
         */
1194
        @Override
1195
        public void setFormField(String namespace, String formFieldPath) {
1196
                String oldValue = formNamespaceAndPath;
1✔
1197
                super.setFormField(namespace, formFieldPath);
1✔
1198
                markAsDirty(oldValue, formNamespaceAndPath);
1✔
1199
        }
1✔
1200
        
1201
        /**
1202
         * Returns true if any change has been made to an Obs instance. In general, the only time
1203
         * isDirty() is going to return false is when a new Obs has just been instantiated or loaded
1204
         * from the database and no method that modifies it internally has been invoked.
1205
         *
1206
         * @return true if not changed otherwise false
1207
         * @since 2.0
1208
         * <strong>Should</strong> return false when no change has been made
1209
         * <strong>Should</strong> return true when any immutable field has been changed
1210
         * <strong>Should</strong> return false when only mutable fields are changed
1211
         * <strong>Should</strong> return true when an immutable field is changed from a null to a non null value
1212
         * <strong>Should</strong> return true when an immutable field is changed from a non null to a null value
1213
         */
1214
        public boolean isDirty() {
1215
                return dirty;
1✔
1216
        }
1217
        
1218
        protected void markAsDirty(Object oldValue, Object newValue) {
1219
                //Should we ignore the case for Strings?
1220
                if (!isDirty() && obsId != null && !OpenmrsUtil.nullSafeEquals(oldValue, newValue)) {
1✔
1221
                        dirty = true;
1✔
1222
                }
1223
        }
1✔
1224
        
1225
        /**
1226
         * Similar to FHIR's Observation.interpretation. Supports a subset of FHIR's Observation
1227
         * Interpretation Codes. See https://www.hl7.org/fhir/valueset-observation-interpretation.html
1228
         * 
1229
         * @since 2.1.0
1230
         */
1231
        public Interpretation getInterpretation() {
1232
                return interpretation;
1✔
1233
        }
1234
        
1235
        /**
1236
         * @since 2.1.0
1237
         */
1238
        public void setInterpretation(Interpretation interpretation) {
1239
                markAsDirty(this.interpretation, interpretation);
1✔
1240
                this.interpretation = interpretation;
1✔
1241
        }
1✔
1242
        
1243
        /**
1244
         * Similar to FHIR's Observation.status. Supports a subset of FHIR's ObservationStatus values.
1245
         * At present OpenMRS does not support FHIR's REGISTERED and CANCELLED statuses, because we
1246
         * don't support obs with null values. See:
1247
         * https://www.hl7.org/fhir/valueset-observation-status.html
1248
         * 
1249
         * @since 2.1.0
1250
         */
1251
        public Status getStatus() {
1252
                return status;
1✔
1253
        }
1254
        
1255
        /**
1256
         * @since 2.1.0
1257
         */
1258
        public void setStatus(Status status) {
1259
                markAsDirty(this.status, status);
1✔
1260
                this.status = status;
1✔
1261
        }
1✔
1262

1263
        /**
1264
         * Returns the ObsReferenceRange
1265
         * @return obsReferenceRange.
1266
         * 
1267
         * @since 2.7.0
1268
         */
1269
        public ObsReferenceRange getReferenceRange() {
1270
                return referenceRange;
1✔
1271
        }
1272
        
1273
        /**
1274
         * Sets ObsReferenceRange
1275
         * 
1276
         * @param referenceRange ObsReferenceRange to set.
1277
         *               
1278
         * @since 2.7.0
1279
         */
1280
        public void setReferenceRange(ObsReferenceRange referenceRange) {
1281
                this.referenceRange = referenceRange;
1✔
1282
        }
1✔
1283
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc