gt: 		if (DISCONTINUE != order.getAction()) {
context: /**
   * This Source Code Form is subject to the terms of the Mozilla Public License,
   * v. 2.0. If a copy of the MPL was not distributed with this file, You can
   * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
   * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
   *
   * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
   * graphic logo is a trademark of OpenMRS Inc.
   */
  package org.openmrs.api.impl;
  
  import static org.openmrs.Order.Action.DISCONTINUE;
  import static org.openmrs.Order.Action.REVISE;
  
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Calendar;
  import java.util.Collections;
  import java.util.Date;
  import java.util.List;
  import java.util.Locale;
  import java.util.Vector;
  
  import org.apache.commons.lang.time.DateUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.hibernate.proxy.HibernateProxy;
  import org.openmrs.CareSetting;
  import org.openmrs.Concept;
  import org.openmrs.ConceptClass;
  import org.openmrs.Drug;
  import org.openmrs.DrugOrder;
  import org.openmrs.Encounter;
  import org.openmrs.GlobalProperty;
  import org.openmrs.Order;
  import org.openmrs.OrderFrequency;
  import org.openmrs.OrderType;
  import org.openmrs.Patient;
  import org.openmrs.Provider;
  import org.openmrs.TestOrder;
  import org.openmrs.User;
  import org.openmrs.api.APIException;
  import org.openmrs.api.AmbiguousOrderException;
  import org.openmrs.api.GlobalPropertyListener;
  import org.openmrs.api.OrderContext;
  import org.openmrs.api.OrderNumberGenerator;
  import org.openmrs.api.OrderService;
  import org.openmrs.api.context.Context;
  import org.openmrs.api.db.OrderDAO;
  import org.openmrs.order.OrderUtil;
  import org.openmrs.util.OpenmrsConstants;
  import org.openmrs.util.OpenmrsUtil;
  import org.springframework.transaction.annotation.Propagation;
  import org.springframework.transaction.annotation.Transactional;
  import org.springframework.util.StringUtils;
  
  /**
   * Default implementation of the Order-related services class. This method should not be invoked by
   * itself. Spring injection is used to inject this implementation into the ServiceContext. Which
   * implementation is injected is determined by the spring application context file:
   * /metadata/api/spring/applicationContext.xml
   * 
   * @see org.openmrs.api.OrderService
   */
  @Transactional
  public class OrderServiceImpl extends BaseOpenmrsService implements OrderService, OrderNumberGenerator, GlobalPropertyListener {
  	
  	protected final Log log = LogFactory.getLog(getClass());
  	
  	private static final String ORDER_NUMBER_PREFIX = "ORD-";
  	
  	protected OrderDAO dao;
  	
  	private static OrderNumberGenerator orderNumberGenerator = null;
  	
  	public OrderServiceImpl() {
  	}
  	
  	/**
  	 * @see org.openmrs.api.OrderService#setOrderDAO(org.openmrs.api.db.OrderDAO)
  	 */
  	public void setOrderDAO(OrderDAO dao) {
  		this.dao = dao;
  	}
  	
  	/**
  	 * @see org.openmrs.api.OrderService#saveOrder(org.openmrs.Order, org.openmrs.api.OrderContext)
  	 */
  	public synchronized Order saveOrder(Order order, OrderContext orderContext) throws APIException {
  		if (order.getOrderId() != null) {
  			throw new APIException("Cannot edit an existing order, you need to revise it instead");
  		}
  		if (order.getDateActivated() == null) {
  			order.setDateActivated(new Date());
  		}
  		boolean isDrugOrder = DrugOrder.class.isAssignableFrom(getActualType(order));
  		Concept concept = order.getConcept();
  		if (concept == null && isDrugOrder) {
  			DrugOrder drugOrder = (DrugOrder) order;
  			if (drugOrder.getDrug() != null) {
  				concept = drugOrder.getDrug().getConcept();
  				drugOrder.setConcept(concept);
  			}
  		}
  		if (isDrugOrder) {
  			((DrugOrder) order).setAutoExpireDateBasedOnDuration();
  		}
  		
  		if (concept == null) {
  			throw new APIException("concept is required for an order");
  		}
  		
  		Order previousOrder = order.getPreviousOrder();
  		if (order.getOrderType() == null) {
  			OrderType orderType = null;
  			if (orderContext != null) {
  				orderType = orderContext.getOrderType();
  			}
  			if (orderType == null) {
  				orderType = getOrderTypeByConcept(concept);
  			}
  			//Check if it is instance of DrugOrder
  			if (orderType == null && order instanceof DrugOrder) {
  				orderType = Context.getOrderService().getOrderTypeByUuid(OrderType.DRUG_ORDER_TYPE_UUID);
  			}
  			//Check if it is an instance of TestOrder
  			if (orderType == null && order instanceof TestOrder) {
  				orderType = Context.getOrderService().getOrderTypeByUuid(OrderType.TEST_ORDER_TYPE_UUID);
  			}
  			
  			//this order's order type should match that of the previous
  			if (orderType == null || (previousOrder != null && !orderType.equals(previousOrder.getOrderType()))) {
  				throw new APIException(
  				        "Cannot determine the order type of the order, make sure the concept's class is mapped to an order type");
  			}
  			
  			order.setOrderType(orderType);
  		}
  		if (order.getCareSetting() == null) {
  			CareSetting careSetting = null;
  			if (orderContext != null) {
  				careSetting = orderContext.getCareSetting();
  			}
  			if (careSetting == null || (previousOrder != null && !careSetting.equals(previousOrder.getCareSetting()))) {
  				throw new APIException("Cannot determine the care setting of the order");
  			}
  			order.setCareSetting(careSetting);
  		}
  		
  		if (!order.getOrderType().getJavaClass().isAssignableFrom(order.getClass())) {
  			throw new APIException("Order type class " + order.getOrderType().getJavaClass()
  			        + " does not match the order class " + order.getClass().getName());
  		}
  		
  		if (REVISE == order.getAction()) {
  			if (previousOrder == null) {
  				throw new APIException("Previous Order is required for a revised order");
  			}
  			stopOrder(previousOrder, aMomentBefore(order.getDateActivated()));
  		} else if (DISCONTINUE == order.getAction()) {
  			discontinueExistingOrdersIfNecessary(order);
  		}
  		
  		if (previousOrder != null) {
  			//Check that patient, careSetting, concept and drug if is drug order have not changed
  			//we need to use a SQL query to by pass the hibernate cache
  			boolean isPreviousDrugOrder = DrugOrder.class.isAssignableFrom(previousOrder.getClass());
  			List<List<Object>> rows = dao.getOrderFromDatabase(previousOrder, isPreviousDrugOrder);
  			List<Object> rowData = rows.get(0);
  			if (!rowData.get(0).equals(previousOrder.getPatient().getPatientId())) {
  				throw new APIException("Cannot change the patient of an order");
  			} else if (!rowData.get(1).equals(previousOrder.getCareSetting().getCareSettingId())) {
  				throw new APIException("Cannot change the careSetting of an order");
  			} else if (!rowData.get(2).equals(previousOrder.getConcept().getConceptId())) {
  				throw new APIException("Cannot change the concept of an order");
  			} else if (isPreviousDrugOrder) {
  				Drug previousDrug = ((DrugOrder) previousOrder).getDrug();
  				if (previousDrug == null && rowData.get(3) != null) {
  					throw new APIException("Cannot change the drug of a drug order");
  				} else if (previousDrug != null && !OpenmrsUtil.nullSafeEquals(rowData.get(3), previousDrug.getDrugId())) {
  					throw new APIException("Cannot change the drug of a drug order");
  				}
  			}
  			
  			//concept should be the same as on previous order, same applies to drug for drug orders
  			boolean isDrugOrderAndHasADrug = isDrugOrder && ((DrugOrder) order).getDrug() != null;
  			if (!OpenmrsUtil.nullSafeEquals(order.getConcept(), previousOrder.getConcept())) {
  				throw new APIException("The concept of the previous order and the new one order don't match");
  			} else if (isDrugOrderAndHasADrug) {
  				DrugOrder drugOrder1 = (DrugOrder) order;
  				DrugOrder drugOrder2 = (DrugOrder) previousOrder;
  				if (!OpenmrsUtil.nullSafeEquals(drugOrder1.getDrug(), drugOrder2.getDrug())) {
  					throw new APIException("The drug of the previous order and the new one order don't match");
  				}
  			} else if (!order.getOrderType().equals(previousOrder.getOrderType())) {
  				throw new APIException("The order type does not match that of the previous order");
  			} else if (!order.getCareSetting().equals(previousOrder.getCareSetting())) {
  				throw new APIException("The care setting does not match that of the previous order");
  			} else if (!getActualType(order).equals(getActualType(previousOrder))) {
  				throw new APIException("The class does not match that of the previous order");
  			}
  		}
  		
  		if (DISCONTINUE != order.getAction()) {
  			List<Order> activeOrders = getActiveOrders(order.getPatient(), null, order.getCareSetting(), null);
  			List<String> parallelOrders = Collections.emptyList();
  			if (orderContext != null && orderContext.getAttribute(PARALLEL_ORDERS) != null) {
  				parallelOrders = Arrays.asList((String[]) orderContext.getAttribute(PARALLEL_ORDERS));
  			}
  			for (Order activeOrder : activeOrders) {
  				//Reject if there is an active drug order for the same orderable with overlapping schedule
  				if (!parallelOrders.contains(activeOrder.getUuid())
  				        && areDrugOrdersOfSameOrderableAndOverlappingSchedule(order, activeOrder)) {
  					throw new AmbiguousOrderException(
  					        "Cannot have more than one active order for the same orderable and care setting at same time");
  				}
  			}
  		}
  		
  		return saveOrderInternal(order, orderContext);
  	}
  	
  	private boolean areDrugOrdersOfSameOrderableAndOverlappingSchedule(Order firstOrder, Order secondOrder) {
  		return firstOrder.hasSameOrderableAs(secondOrder)
  		        && !OpenmrsUtil.nullSafeEquals(firstOrder.getPreviousOrder(), secondOrder)
  		        && OrderUtil.checkScheduleOverlap(firstOrder, secondOrder)
  		        && firstOrder.getOrderType().equals(
  		            Context.getOrderService().getOrderTypeByUuid(OrderType.DRUG_ORDER_TYPE_UUID));
  	}
  	
  	/**
  	 * To support MySQL datetime values (which are only precise to the second) we subtract one
  	 * second. Eventually we may move this method and enhance it to subtract the smallest moment the
  	 * underlying database will represent.
  	 * 
  	 * @param date
  	 * @return one moment before date
  	 */
  	private Date aMomentBefore(Date date) {
  		return DateUtils.addSeconds(date, -1);
  	}
  	
  	private Order saveOrderInternal(Order order, OrderContext orderContext) {
  		if (order.getOrderId() == null) {
  			setProperty(order, "orderNumber", getOrderNumberGenerator().getNewOrderNumber(orderContext));
  			
  			//DC orders should auto expire upon creating them
  			if (DISCONTINUE == order.getAction()) {
  				order.setAutoExpireDate(order.getDateActivated());
  			} else if (order.getAutoExpireDate() != null) {
  				Calendar cal = Calendar.getInstance();
  				cal.setTime(order.getAutoExpireDate());
  				int hours = cal.get(Calendar.HOUR_OF_DAY);
  				int minutes = cal.get(Calendar.MINUTE);
  				int seconds = cal.get(Calendar.SECOND);
  				int milliseconds = cal.get(Calendar.MILLISECOND);
  				//roll autoExpireDate to end of day (23:59:59:999) if no time portion is specified
  				if (hours == 0 && minutes == 0 && seconds == 0 && milliseconds == 0) {
  					cal.set(Calendar.HOUR_OF_DAY, 23);
  					cal.set(Calendar.MINUTE, 59);
  					cal.set(Calendar.SECOND, 59);
  					cal.set(Calendar.MILLISECOND, 999);
  					order.setAutoExpireDate(cal.getTime());
  				}
  			}
  		}
  		
  		return dao.saveOrder(order);
  	}
  	
  	private void setProperty(Order order, String propertyName, Object value) {
  		Boolean isAccessible = null;
  		Field field = null;
  		try {
  			field = Order.class.getDeclaredField(propertyName);
  			field.setAccessible(true);
  			field.set(order, value);
  		}
  		catch (Exception e) {
  			throw new APIException("Failed to set " + propertyName + " for order:" + order, e);
  		}
  		finally {
  			if (field != null && isAccessible != null) {
  				field.setAccessible(isAccessible);
  			}
  		}
  	}
  	
  	/**
  	 * Gets the configured order number generator, if none is specified, it defaults to an instance
  	 * if this class
  	 * 
  	 * @return
  	 */
  	private OrderNumberGenerator getOrderNumberGenerator() {
  		if (orderNumberGenerator == null) {
  			String generatorBeanId = Context.getAdministrationService().getGlobalProperty(
  			    OpenmrsConstants.GP_ORDER_NUMBER_GENERATOR_BEAN_ID);
  			if (StringUtils.hasText(generatorBeanId)) {
  				orderNumberGenerator = Context.getRegisteredComponent(generatorBeanId, OrderNumberGenerator.class);
  				log.info("Successfully set the configured order number generator");
  			} else {
  				orderNumberGenerator = this;
  				log.info("Setting default order number generator");
  			}
  		}
  		
  		return orderNumberGenerator;
  	}
  	
  	/**
  	 * If this is a discontinue order, ensure that the previous order is discontinued. If a
  	 * previousOrder is present, then ensure this is discontinued. If no previousOrder is present,
  	 * then try to find a previousOrder and discontinue it. If cannot find a previousOrder, throw
  	 * exception
  	 * 
  	 * @param order
  	 */
  	//Ignore and return if this is not an order to discontinue
  	private void discontinueExistingOrdersIfNecessary(Order order) {
  