public Visit mergeVisits(Visit preferred, Visit nonPreferred) {
    // extend date range of winning
    if (OpenmrsUtil.compareWithNullAsEarliest(
            nonPreferred.getStartDatetime(), preferred.getStartDatetime())
        < 0) {
      preferred.setStartDatetime(nonPreferred.getStartDatetime());
    }
    if (preferred.getStopDatetime() != null
        && OpenmrsUtil.compareWithNullAsLatest(
                preferred.getStopDatetime(), nonPreferred.getStopDatetime())
            < 0) {
      preferred.setStopDatetime(nonPreferred.getStopDatetime());
    }

    // move encounters from losing into winning
    if (nonPreferred.getEncounters() != null) {
      for (Encounter e : nonPreferred.getEncounters()) {
        e.setPatient(preferred.getPatient());
        preferred.addEncounter(e);
        encounterService.saveEncounter(e);
      }
    }
    nonPreferred.setEncounters(
        null); // we need to manually the encounters from the non-preferred visit before voiding or
               // all the encounters we just moved will also get voided!

    visitService.voidVisit(
        nonPreferred, "EMR - Merge Patients: merged into visit " + preferred.getVisitId());
    visitService.saveVisit(preferred);
    return preferred;
  }
 @Override
 public boolean visitsOverlap(Visit v1, Visit v2) {
   Location where1 = v1.getLocation();
   Location where2 = v2.getLocation();
   if ((where1 == null && where2 == null)
       || isSameOrAncestor(where1, where2)
       || isSameOrAncestor(where2, where1)) {
     // "same" location, so check if date ranges overlap (assuming startDatetime is never null)
     return (OpenmrsUtil.compareWithNullAsLatest(v1.getStartDatetime(), v2.getStopDatetime()) <= 0)
         && (OpenmrsUtil.compareWithNullAsLatest(v2.getStartDatetime(), v1.getStopDatetime())
             <= 0);
   }
   return false;
 }
  /**
   * Validates the given Encounter. Currently checks if the patient has been set and also ensures
   * that the patient for an encounter and the visit it is associated to if any, are the same.
   *
   * @param obj The encounter to validate.
   * @param errors Errors
   * @see org.springframework.validation.Validator#validate(java.lang.Object,
   *     org.springframework.validation.Errors)
   * @should fail if the patients for the visit and the encounter dont match
   * @should fail if patient is not set
   * @should fail if encounter dateTime is after current dateTime
   * @should fail if encounter dateTime is before visit startDateTime
   * @should fail if encounter dateTime is after visit stopDateTime
   */
  public void validate(Object obj, Errors errors) throws APIException {
    if (log.isDebugEnabled()) {
      log.debug(this.getClass().getName() + ".validate...");
    }

    if (obj == null || !(obj instanceof Encounter)) {
      throw new IllegalArgumentException(
          "The parameter obj should not be null and must be of type " + Encounter.class);
    }

    Encounter encounter = (Encounter) obj;

    ValidationUtils.rejectIfEmpty(
        errors, "patient", "Encounter.error.patient.required", "Patient is required");
    if (encounter.getVisit() != null
        && !ObjectUtils.equals(encounter.getVisit().getPatient(), encounter.getPatient())) {
      errors.rejectValue(
          "visit",
          "Encounter.visit.patients.dontMatch",
          "The patient for the encounter and visit should be the same");
    }

    Date encounterDateTime = encounter.getEncounterDatetime();

    if (encounterDateTime != null && encounterDateTime.after(new Date())) {
      errors.rejectValue(
          "encounterDatetime",
          "Encounter.datetimeShouldBeBeforeCurrent",
          "The encounter datetime should be before the current date.");
    }

    Visit visit = encounter.getVisit();
    if (visit != null && encounterDateTime != null) {
      if (visit.getStartDatetime() != null && encounterDateTime.before(visit.getStartDatetime())) {
        errors.rejectValue(
            "encounterDatetime",
            "Encounter.datetimeShouldBeInVisitDatesRange",
            "The encounter datetime should be between the visit start and stop dates.");
      }

      if (visit.getStopDatetime() != null && encounterDateTime.after(visit.getStopDatetime())) {
        errors.rejectValue(
            "encounterDatetime",
            "Encounter.datetimeShouldBeInVisitDatesRange",
            "The encounter datetime should be between the visit start and stop dates.");
      }
    }
  }
 /**
  * @param visit
  * @param location
  * @param when
  * @return true if when falls in the visits timespan AND location is within visit.location
  */
 @Override
 public boolean isSuitableVisit(Visit visit, Location location, Date when) {
   if (OpenmrsUtil.compare(when, visit.getStartDatetime()) < 0) {
     return false;
   }
   if (OpenmrsUtil.compareWithNullAsLatest(when, visit.getStopDatetime()) > 0) {
     return false;
   }
   return isSameOrAncestor(visit.getLocation(), location);
 }
  private boolean shouldBeClosed(Visit visit) {

    if (visit.getStopDatetime() != null) {
      return false; // already closed
    }

    VisitDomainWrapper visitDomainWrapper = domainWrapperFactory.newVisitDomainWrapper(visit);

    if (visitDomainWrapper.isAdmitted() || visitDomainWrapper.isAwaitingAdmission()) {
      return false; // don't close the visit if patient is admitted or waiting admission
    }

    Disposition mostRecentDisposition = visitDomainWrapper.getMostRecentDisposition();
    if (mostRecentDisposition != null
        && mostRecentDisposition.getKeepsVisitOpen() != null
        && mostRecentDisposition.getKeepsVisitOpen()) {
      return false; // don't close the visit if the most recent disposition is one that keeps visit
                    // opens
    }

    Date now = new Date();
    Date mustHaveSomethingAfter = DateUtils.addHours(now, -emrApiProperties.getVisitExpireHours());

    if (OpenmrsUtil.compare(visit.getStartDatetime(), mustHaveSomethingAfter) >= 0) {
      return false;
    }

    if (visit.getEncounters() != null) {
      for (Encounter candidate : visit.getEncounters()) {
        if (OpenmrsUtil.compare(candidate.getEncounterDatetime(), mustHaveSomethingAfter) >= 0) {
          return false;
        }
      }
    }

    return true;
  }