/** * 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.hibernate.proxy.HibernateProxy; import org.openmrs.CareSetting; import org.openmrs.Concept; import org.openmrs.ConceptClass; import org.openmrs.DrugOrder; import org.openmrs.Encounter; import org.openmrs.GlobalProperty; import org.openmrs.Order; import org.openmrs.OrderFrequency; import org.openmrs.OrderGroup; 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.CannotDeleteObjectInUseException; import org.openmrs.api.CannotUpdateObjectInUseException; import org.openmrs.api.GlobalPropertyListener; import org.openmrs.api.MissingRequiredPropertyException; import org.openmrs.api.OrderContext; import org.openmrs.api.OrderNumberGenerator; import org.openmrs.api.OrderService; import org.openmrs.api.UnchangeableObjectException; import org.openmrs.api.context.Context; import org.openmrs.api.db.OrderDAO; import org.openmrs.api.CannotStopDiscontinuationOrderException; import org.openmrs.api.CannotStopInactiveOrderException; import org.openmrs.api.CannotUnvoidOrderException; import org.openmrs.api.EditedOrderDoesNotMatchPreviousException; import org.openmrs.api.OrderEntryException; import org.openmrs.order.OrderUtil; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 Logger log = LoggerFactory.getLogger(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) */ @Override public void setOrderDAO(OrderDAO dao) { this.dao = dao; } /** * @see org.openmrs.api.OrderService#saveOrder(org.openmrs.Order, org.openmrs.api.OrderContext) */ @Override public synchronized Order saveOrder(Order order, OrderContext orderContext) throws APIException { return saveOrder(order, orderContext, false); } /** * @see org.openmrs.api.OrderService#saveOrderGroup(org.openmrs.OrderGroup) */ @Override public OrderGroup saveOrderGroup(OrderGroup orderGroup) throws APIException { if (orderGroup.getId() == null) { dao.saveOrderGroup(orderGroup); } List<Order> orders = orderGroup.getOrders(); for (Order order : orders) { if (order.getId() == null) { saveOrder(order, null); } } return orderGroup; } /** * @see org.openmrs.api.OrderService#saveOrder(org.openmrs.Order, org.openmrs.api.OrderContext) */ @Override public synchronized Order saveRetrospectiveOrder(Order order, OrderContext orderContext) { return saveOrder(order, orderContext, true); } private Order saveOrder(Order order, OrderContext orderContext, boolean isRetrospective) { failOnExistingOrder(order); ensureDateActivatedIsSet(order); ensureConceptIsSet(order); ensureDrugOrderAutoExpirationDateIsSet(order); ensureOrderTypeIsSet(order,orderContext); ensureCareSettingIsSet(order,orderContext); failOnOrderTypeMismatch(order); Order previousOrder = order.getPreviousOrder(); if (REVISE == order.getAction()) { if (previousOrder == null) { throw new MissingRequiredPropertyException("Order.previous.required", (Object[]) null); } stopOrder(previousOrder, aMomentBefore(order.getDateActivated()), isRetrospective); } else if (DISCONTINUE == order.getAction()) { discontinueExistingOrdersIfNecessary(order, isRetrospective); } if (previousOrder != null) { //concept should be the same as on previous order, same applies to drug for drug orders if (!order.hasSameOrderableAs(previousOrder)) { throw new EditedOrderDoesNotMatchPreviousException("Order.orderable.doesnot.match"); } else if (!order.getOrderType().equals(previousOrder.getOrderType())) { throw new EditedOrderDoesNotMatchPreviousException("Order.type.doesnot.match"); } else if (!order.getCareSetting().equals(previousOrder.getCareSetting())) { throw new EditedOrderDoesNotMatchPreviousException("Order.care.setting.doesnot.match"); } else if (!getActualType(order).equals(getActualType(previousOrder))) { throw new EditedOrderDoesNotMatchPreviousException("Order.class.doesnot.match"); } } if (DISCONTINUE != order.getAction()) { Date asOfDate = new Date(); if (isRetrospective) { asOfDate = order.getDateActivated(); } List<Order> activeOrders = getActiveOrders(order.getPatient(), null, order.getCareSetting(), asOfDate); 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("Order.cannot.have.more.than.one"); } } } return saveOrderInternal(order, orderContext); } private void failOnExistingOrder(Order order) { if (order.getOrderId() != null) { throw new UnchangeableObjectException("Order.cannot.edit.existing"); } } private void ensureDateActivatedIsSet(Order order) { if (order.getDateActivated() == null) { order.setDateActivated(new Date()); } } private void ensureConceptIsSet(Order order) { Concept concept = order.getConcept(); if (concept == null && isDrugOrder(order)) { DrugOrder drugOrder = (DrugOrder) order; if (drugOrder.getDrug() != null) { concept = drugOrder.getDrug().getConcept(); drugOrder.setConcept(concept); } } if (concept == null) { throw new MissingRequiredPropertyException("Order.concept.required"); } } private void ensureDrugOrderAutoExpirationDateIsSet(Order order) { if (isDrugOrder(order)) { ((DrugOrder) order).setAutoExpireDateBasedOnDuration(); } } private void ensureOrderTypeIsSet(Order order, OrderContext orderContext) { if (order.getOrderType() != null) { return; } OrderType orderType = null; if (orderContext != null) { orderType = orderContext.getOrderType(); } if (orderType == null) { orderType = getOrderTypeByConcept(order.getConcept()); } if (orderType == null && order instanceof DrugOrder) { orderType = Context.getOrderService().getOrderTypeByUuid(OrderType.DRUG_ORDER_TYPE_UUID); } if (orderType == null && order instanceof TestOrder) { orderType = Context.getOrderService().getOrderTypeByUuid(OrderType.TEST_ORDER_TYPE_UUID); } if (orderType == null) { throw new OrderEntryException("Order.type.cannot.determine"); } Order previousOrder = order.getPreviousOrder(); if (previousOrder != null && !orderType.equals(previousOrder.getOrderType())) { throw new OrderEntryException("Order.type.does.not.match"); } order.setOrderType(orderType); } private void ensureCareSettingIsSet(Order order, OrderContext orderContext) { if (order.getCareSetting() != null) { return; } CareSetting careSetting = null; if (orderContext != null) { careSetting = orderContext.getCareSetting(); } Order previousOrder = order.getPreviousOrder(); if (careSetting == null || (previousOrder != null && !careSetting.equals(previousOrder.getCareSetting()))) { throw new OrderEntryException("Order.care.cannot.determine"); } order.setCareSetting(careSetting); } private void failOnOrderTypeMismatch(Order order) { if (!order.getOrderType().getJavaClass().isAssignableFrom(order.getClass())) { throw new OrderEntryException("Order.type.class.does.not.match", new Object[] { order.getOrderType().getJavaClass(), order.getClass().getName() }); } } 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)); } private boolean isDrugOrder(Order order) { return DrugOrder.class.isAssignableFrom(getActualType(order)); } /** * 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); cal.get(Calendar.MILLISECOND); //roll autoExpireDate to end of day (23:59:59) if no time portion is specified if (hours == 0 && minutes == 0 && seconds == 0) { cal.set(Calendar.HOUR_OF_DAY, 23); cal.set(Calendar.MINUTE, 59); cal.set(Calendar.SECOND, 59); // the OpenMRS database is only precise to the second cal.set(Calendar.MILLISECOND, 0); 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("Order.failed.set.property", new Object[] { propertyName, 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 * @param isRetrospective */ //Ignore and return if this is not an order to discontinue private void discontinueExistingOrdersIfNecessary(Order order, Boolean isRetrospective) { if (DISCONTINUE != order.getAction()) { return; } //Mark previousOrder as discontinued if it is not already Order previousOrder = order.getPreviousOrder(); if (previousOrder != null) { stopOrder(previousOrder, aMomentBefore(order.getDateActivated()), isRetrospective); return; } //Mark first order found corresponding to this DC order as discontinued. Date asOfDate = null; if (isRetrospective) { asOfDate = order.getDateActivated(); } List<? extends Order> orders = getActiveOrders(order.getPatient(), order.getOrderType(), order.getCareSetting(), asOfDate); boolean isDrugOrderAndHasADrug = isDrugOrder(order) && (((DrugOrder) order).getDrug() != null || ((DrugOrder) order).isNonCodedDrug()); Order orderToBeDiscontinued = null; for (Order activeOrder : orders) { if (!getActualType(order).equals(getActualType(activeOrder))) { continue; } //For drug orders, the drug must match if the order has a drug if (isDrugOrderAndHasADrug) { Order existing = order.hasSameOrderableAs(activeOrder) ? activeOrder : null; if (existing != null) { if (orderToBeDiscontinued == null) { orderToBeDiscontinued = existing; } else { throw new AmbiguousOrderException("Order.discontinuing.ambiguous.orders"); } } } else if (activeOrder.getConcept().equals(order.getConcept())) { if (orderToBeDiscontinued == null) { orderToBeDiscontinued = activeOrder; } else { throw new AmbiguousOrderException("Order.discontinuing.ambiguous.orders"); } } } if (orderToBeDiscontinued != null) { order.setPreviousOrder(orderToBeDiscontinued); stopOrder(orderToBeDiscontinued, aMomentBefore(order.getDateActivated()), isRetrospective); } } /** * Returns the class object of the specified persistent object returning the actual persistent * class in case it is a hibernate proxy * * @param persistentObject * @return the Class object */ private Class<?> getActualType(Object persistentObject) { Class<?> type = persistentObject.getClass(); if (persistentObject instanceof HibernateProxy) { type = ((HibernateProxy) persistentObject).getHibernateLazyInitializer().getPersistentClass(); } return type; } /** * @see org.openmrs.api.OrderService#purgeOrder(org.openmrs.Order) */ @Override public void purgeOrder(Order order) throws APIException { purgeOrder(order, false); } /** * @see org.openmrs.api.OrderService#purgeOrder(Order) */ @Override public void purgeOrder(Order order, boolean cascade) throws APIException { if (cascade) { dao.deleteObsThatReference(order); } dao.deleteOrder(order); } /** * @see org.openmrs.api.OrderService#voidOrder(org.openmrs.Order, java.lang.String) */ @Override public Order voidOrder(Order order, String voidReason) throws APIException { if (!StringUtils.hasLength(voidReason)) { throw new IllegalArgumentException("voidReason cannot be empty or null"); } Order previousOrder = order.getPreviousOrder(); if (previousOrder != null && isDiscontinueOrReviseOrder(order)) { setProperty(previousOrder, "dateStopped", null); } return saveOrderInternal(order, null); } /** * @see org.openmrs.api.OrderService#unvoidOrder(org.openmrs.Order) */ @Override public Order unvoidOrder(Order order) throws APIException { Order previousOrder = order.getPreviousOrder(); if (previousOrder != null && isDiscontinueOrReviseOrder(order)) { if (!previousOrder.isActive()) { final String action = DISCONTINUE == order.getAction() ? "discontinuation" : "revision"; throw new CannotUnvoidOrderException(action); } stopOrder(previousOrder, aMomentBefore(order.getDateActivated()), false); } return saveOrderInternal(order, null); } /** * @see org.openmrs.api.OrderService#getOrder(java.lang.Integer) */ @Override @Transactional(readOnly = true) public Order getOrder(Integer orderId) throws APIException { return dao.getOrder(orderId); } /** * @see OrderService#getOrders(org.openmrs.Patient, org.openmrs.CareSetting, * org.openmrs.OrderType, boolean) */ @Override public List<Order> getOrders(Patient patient, CareSetting careSetting, OrderType orderType, boolean includeVoided) { if (patient == null) { throw new IllegalArgumentException("Patient is required"); } if (careSetting == null) { throw new IllegalArgumentException("CareSetting is required"); } List<OrderType> orderTypes = null; if (orderType != null) { orderTypes = new ArrayList<OrderType>(); orderTypes.add(orderType); orderTypes.addAll(getSubtypes(orderType, true)); } return dao.getOrders(patient, careSetting, orderTypes, includeVoided, false); } /** * @see OrderService#getAllOrdersByPatient(org.openmrs.Patient) */ @Override public List<Order> getAllOrdersByPatient(Patient patient) { if (patient == null) { throw new IllegalArgumentException("Patient is required"); } return dao.getOrders(patient, null, null, true, true); } /** * @see org.openmrs.api.OrderService#getOrderByUuid(java.lang.String) */ @Override @Transactional(readOnly = true) public Order getOrderByUuid(String uuid) throws APIException { return dao.getOrderByUuid(uuid); } /** * @see org.openmrs.api.OrderService#getDiscontinuationOrder(Order) */ @Transactional(readOnly = true) @Override public Order getDiscontinuationOrder(Order order) throws APIException { return dao.getDiscontinuationOrder(order); } /** * @see org.openmrs.api.OrderService#getRevisionOrder(Order) */ @Override public Order getRevisionOrder(Order order) throws APIException { return dao.getRevisionOrder(order); } /** * @see org.openmrs.api.OrderNumberGenerator#getNewOrderNumber(org.openmrs.api.OrderContext) * @param orderContext */ @Override public String getNewOrderNumber(OrderContext orderContext) throws APIException { return ORDER_NUMBER_PREFIX + Context.getOrderService().getNextOrderNumberSeedSequenceValue(); } /** * @see org.openmrs.api.OrderService#getOrderByOrderNumber(java.lang.String) */ @Override @Transactional(readOnly = true) public Order getOrderByOrderNumber(String orderNumber) { return dao.getOrderByOrderNumber(orderNumber); } /** * @see org.openmrs.api.OrderService#getOrderHistoryByConcept(org.openmrs.Patient, * org.openmrs.Concept) */ @Override @Transactional(readOnly = true) public List<Order> getOrderHistoryByConcept(Patient patient, Concept concept) { if (patient == null || concept == null) { throw new IllegalArgumentException("patient and concept are required"); } List<Concept> concepts = new Vector<Concept>(); concepts.add(concept); List<Patient> patients = new Vector<Patient>(); patients.add(patient); return dao.getOrders(null, patients, concepts, new Vector<User>(), new Vector<Encounter>()); } /** * @see org.openmrs.api.OrderService#getNextOrderNumberSeedSequenceValue() */ @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public synchronized Long getNextOrderNumberSeedSequenceValue() { return dao.getNextOrderNumberSeedSequenceValue(); } /** * @see org.openmrs.api.OrderService#getOrderHistoryByOrderNumber(java.lang.String) */ @Override @Transactional(readOnly = true) public List<Order> getOrderHistoryByOrderNumber(String orderNumber) { List<Order> orders = new ArrayList<Order>(); Order order = dao.getOrderByOrderNumber(orderNumber); while (order != null) { orders.add(order); order = order.getPreviousOrder(); } return orders; } /** * @see org.openmrs.api.OrderService#getActiveOrders(org.openmrs.Patient, org.openmrs.OrderType, * org.openmrs.CareSetting, java.util.Date) */ @Override @Transactional(readOnly = true) public List<Order> getActiveOrders(Patient patient, OrderType orderType, CareSetting careSetting, Date asOfDate) { if (patient == null) { throw new IllegalArgumentException("Patient is required when fetching active orders"); } if (asOfDate == null) { asOfDate = new Date(); } List<OrderType> orderTypes = null; if (orderType != null) { orderTypes = new ArrayList<OrderType>(); orderTypes.add(orderType); orderTypes.addAll(getSubtypes(orderType, true)); } return dao.getActiveOrders(patient, orderTypes, careSetting, asOfDate); } /** * @see org.openmrs.api.OrderService#getCareSetting(Integer) */ @Override public CareSetting getCareSetting(Integer careSettingId) { return dao.getCareSetting(careSettingId); } /** * @see org.openmrs.api.OrderService#getCareSettingByUuid(String) */ @Override public CareSetting getCareSettingByUuid(String uuid) { return dao.getCareSettingByUuid(uuid); } /** * @see org.openmrs.api.OrderService#getCareSettingByName(String) */ @Override public CareSetting getCareSettingByName(String name) { return dao.getCareSettingByName(name); } /** * @see org.openmrs.api.OrderService#getCareSettings(boolean) */ @Override public List<CareSetting> getCareSettings(boolean includeRetired) { return dao.getCareSettings(includeRetired); } /** * @see OrderService#getOrderTypeByName(String) */ @Override public OrderType getOrderTypeByName(String orderTypeName) { return dao.getOrderTypeByName(orderTypeName); } /** * @see OrderService#getOrderFrequency(Integer) */ @Override public OrderFrequency getOrderFrequency(Integer orderFrequencyId) { return dao.getOrderFrequency(orderFrequencyId); } /** * @see OrderService#getOrderFrequencyByUuid(String) */ @Override public OrderFrequency getOrderFrequencyByUuid(String uuid) { return dao.getOrderFrequencyByUuid(uuid); } /** * @see OrderService#getOrderFrequencies(boolean) */ @Override public List<OrderFrequency> getOrderFrequencies(boolean includeRetired) { return dao.getOrderFrequencies(includeRetired); } /** * @see OrderService#getOrderFrequencies(String, java.util.Locale, boolean, boolean) */ @Override public List<OrderFrequency> getOrderFrequencies(String searchPhrase, Locale locale, boolean exactLocale, boolean includeRetired) { if (searchPhrase == null) { throw new IllegalArgumentException("searchPhrase is required"); } return dao.getOrderFrequencies(searchPhrase, locale, exactLocale, includeRetired); } /** * @see org.openmrs.api.OrderService#discontinueOrder(org.openmrs.Order, org.openmrs.Concept, * java.util.Date, org.openmrs.Provider, org.openmrs.Encounter) */ @Override public Order discontinueOrder(Order orderToDiscontinue, Concept reasonCoded, Date discontinueDate, Provider orderer, Encounter encounter) { if (discontinueDate == null) { discontinueDate = aMomentBefore(new Date()); } stopOrder(orderToDiscontinue, discontinueDate, false); Order newOrder = orderToDiscontinue.cloneForDiscontinuing(); newOrder.setOrderReason(reasonCoded); newOrder.setOrderer(orderer); newOrder.setEncounter(encounter); newOrder.setDateActivated(discontinueDate); return saveOrderInternal(newOrder, null); } /** * @see org.openmrs.api.OrderService#discontinueOrder(org.openmrs.Order, String, java.util.Date, * org.openmrs.Provider, org.openmrs.Encounter) */ @Override public Order discontinueOrder(Order orderToDiscontinue, String reasonNonCoded, Date discontinueDate, Provider orderer, Encounter encounter) { if (discontinueDate == null) { discontinueDate = aMomentBefore(new Date()); } stopOrder(orderToDiscontinue, discontinueDate, false); Order newOrder = orderToDiscontinue.cloneForDiscontinuing(); newOrder.setOrderReasonNonCoded(reasonNonCoded); newOrder.setOrderer(orderer); newOrder.setEncounter(encounter); newOrder.setDateActivated(discontinueDate); return saveOrderInternal(newOrder, null); } private boolean isDiscontinueOrReviseOrder(Order order) { return DISCONTINUE == order.getAction() || REVISE == order.getAction(); } /** * Make necessary checks, set necessary fields for discontinuing <code>orderToDiscontinue</code> * and save. * * @param orderToStop * @param discontinueDate */ private void stopOrder(Order orderToStop, Date discontinueDate, boolean isRetrospective) { if (discontinueDate == null) { discontinueDate = new Date(); } if (discontinueDate.after(new Date())) { throw new IllegalArgumentException("Discontinue date cannot be in the future"); } if (DISCONTINUE == orderToStop.getAction()) { throw new CannotStopDiscontinuationOrderException(); } if (isRetrospective && orderToStop.getDateStopped() != null) { throw new CannotStopInactiveOrderException(); } if (!isRetrospective && !orderToStop.isActive()) { throw new CannotStopInactiveOrderException(); } else if (isRetrospective && !orderToStop.isActive(discontinueDate)) { throw new CannotStopInactiveOrderException(); } setProperty(orderToStop, "dateStopped", discontinueDate); saveOrderInternal(orderToStop, null); } /** * @see org.openmrs.api.OrderService#saveOrderFrequency(org.openmrs.OrderFrequency) */ @Override public OrderFrequency saveOrderFrequency(OrderFrequency orderFrequency) throws APIException { if (orderFrequency.getOrderFrequencyId() != null && dao.isOrderFrequencyInUse(orderFrequency)) { throw new CannotUpdateObjectInUseException("Order.frequency.cannot.edit"); } return dao.saveOrderFrequency(orderFrequency); } /** * @see org.openmrs.api.OrderService#retireOrderFrequency(org.openmrs.OrderFrequency, * java.lang.String) */ @Override public OrderFrequency retireOrderFrequency(OrderFrequency orderFrequency, String reason) { return dao.saveOrderFrequency(orderFrequency); } /** * @see org.openmrs.api.OrderService#unretireOrderFrequency(org.openmrs.OrderFrequency) */ @Override public OrderFrequency unretireOrderFrequency(OrderFrequency orderFrequency) { return Context.getOrderService().saveOrderFrequency(orderFrequency); } /** * @see org.openmrs.api.OrderService#purgeOrderFrequency(org.openmrs.OrderFrequency) */ @Override public void purgeOrderFrequency(OrderFrequency orderFrequency) { if (dao.isOrderFrequencyInUse(orderFrequency)) { throw new CannotDeleteObjectInUseException("Order.frequency.cannot.delete", (Object[]) null); } dao.purgeOrderFrequency(orderFrequency); } /** * @see org.openmrs.api.OrderService#getOrderFrequencyByConcept(org.openmrs.Concept) */ @Override @Transactional(readOnly = true) public OrderFrequency getOrderFrequencyByConcept(Concept concept) { return dao.getOrderFrequencyByConcept(concept); } /** * @see GlobalPropertyListener#supportsPropertyName(String) */ @Override public boolean supportsPropertyName(String propertyName) { return OpenmrsConstants.GP_ORDER_NUMBER_GENERATOR_BEAN_ID.equals(propertyName); } /** * @see GlobalPropertyListener#globalPropertyChanged(org.openmrs.GlobalProperty) */ @Override public void globalPropertyChanged(GlobalProperty newValue) { setOrderNumberGenerator(null); } /** * @see GlobalPropertyListener#globalPropertyDeleted(String) */ @Override public void globalPropertyDeleted(String propertyName) { setOrderNumberGenerator(null); } /** * Helper method to deter instance methods from setting static fields */ private static void setOrderNumberGenerator(OrderNumberGenerator orderNumberGenerator) { OrderServiceImpl.orderNumberGenerator = orderNumberGenerator; } /** * @see org.openmrs.api.OrderService#getOrderType(Integer) */ @Override @Transactional(readOnly = true) public OrderType getOrderType(Integer orderTypeId) { return dao.getOrderType(orderTypeId); } /** * @see org.openmrs.api.OrderService#getOrderTypeByUuid(String) */ @Override @Transactional(readOnly = true) public OrderType getOrderTypeByUuid(String uuid) { return dao.getOrderTypeByUuid(uuid); } /** * @see org.openmrs.api.OrderService#getOrderTypes(boolean) */ @Override @Transactional(readOnly = true) public List<OrderType> getOrderTypes(boolean includeRetired) { return dao.getOrderTypes(includeRetired); } /** * @see org.openmrs.api.OrderService#saveOrderType(org.openmrs.OrderType) */ @Override public OrderType saveOrderType(OrderType orderType) { return dao.saveOrderType(orderType); } /** * @see org.openmrs.api.OrderService#purgeOrderType(org.openmrs.OrderType) */ @Override public void purgeOrderType(OrderType orderType) { if (dao.isOrderTypeInUse(orderType)) { throw new CannotDeleteObjectInUseException("Order.type.cannot.delete", (Object[]) null); } dao.purgeOrderType(orderType); } /** * @see org.openmrs.api.OrderService#retireOrderType(org.openmrs.OrderType, String) */ @Override public OrderType retireOrderType(OrderType orderType, String reason) { return saveOrderType(orderType); } /** * @see org.openmrs.api.OrderService#unretireOrderType(org.openmrs.OrderType) */ @Override public OrderType unretireOrderType(OrderType orderType) { return saveOrderType(orderType); } /** * @see org.openmrs.api.OrderService#getSubtypes(org.openmrs.OrderType, boolean) */ @Override @Transactional(readOnly = true) public List<OrderType> getSubtypes(OrderType orderType, boolean includeRetired) { List<OrderType> allSubtypes = new ArrayList<OrderType>(); List<OrderType> immediateAncestors = dao.getOrderSubtypes(orderType, includeRetired); while (!immediateAncestors.isEmpty()) { List<OrderType> ancestorsAtNextLevel = new ArrayList<OrderType>(); for (OrderType type : immediateAncestors) { allSubtypes.add(type); ancestorsAtNextLevel.addAll(dao.getOrderSubtypes(type, includeRetired)); } immediateAncestors = ancestorsAtNextLevel; } return allSubtypes; } /** * @see org.openmrs.api.OrderService#getOrderTypeByConceptClass(org.openmrs.ConceptClass) */ @Override @Transactional(readOnly = true) public OrderType getOrderTypeByConceptClass(ConceptClass conceptClass) { return dao.getOrderTypeByConceptClass(conceptClass); } /** * @see org.openmrs.api.OrderService#getOrderTypeByConcept(org.openmrs.Concept) */ @Override @Transactional(readOnly = true) public OrderType getOrderTypeByConcept(Concept concept) { return Context.getOrderService().getOrderTypeByConceptClass(concept.getConceptClass()); } /** * @see org.openmrs.api.OrderService#getDrugRoutes() */ @Override @Transactional(readOnly = true) public List<Concept> getDrugRoutes() { return getSetMembersOfConceptSetFromGP(OpenmrsConstants.GP_DRUG_ROUTES_CONCEPT_UUID); } @Override @Transactional(readOnly = true) public List<Concept> getDrugDosingUnits() { return getSetMembersOfConceptSetFromGP(OpenmrsConstants.GP_DRUG_DOSING_UNITS_CONCEPT_UUID); } @Override @Transactional(readOnly = true) public List<Concept> getDrugDispensingUnits() { List<Concept> dispensingUnits = new ArrayList<Concept>(); dispensingUnits.addAll(getSetMembersOfConceptSetFromGP(OpenmrsConstants.GP_DRUG_DISPENSING_UNITS_CONCEPT_UUID)); for (Concept concept : getDrugDosingUnits()) { if (!dispensingUnits.contains(concept)) { dispensingUnits.add(concept); } } return dispensingUnits; } @Override @Transactional(readOnly = true) public List<Concept> getDurationUnits() { return getSetMembersOfConceptSetFromGP(OpenmrsConstants.GP_DURATION_UNITS_CONCEPT_UUID); } /** * @see org.openmrs.api.OrderService#getTestSpecimenSources() */ @Override public List<Concept> getTestSpecimenSources() { return getSetMembersOfConceptSetFromGP(OpenmrsConstants.GP_TEST_SPECIMEN_SOURCES_CONCEPT_UUID); } @Override public Concept getNonCodedDrugConcept() { String conceptUuid = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GP_DRUG_ORDER_DRUG_OTHER); if (StringUtils.hasText(conceptUuid)) { return Context.getConceptService().getConceptByUuid(conceptUuid); } return null; } @Override @Transactional(readOnly = true) public OrderGroup getOrderGroupByUuid(String uuid) throws APIException { return dao.getOrderGroupByUuid(uuid); } @Override @Transactional(readOnly = true) public OrderGroup getOrderGroup(Integer orderGroupId) throws APIException { return dao.getOrderGroupById(orderGroupId); } private List<Concept> getSetMembersOfConceptSetFromGP(String globalProperty) { String conceptUuid = Context.getAdministrationService().getGlobalProperty(globalProperty); Concept concept = Context.getConceptService().getConceptByUuid(conceptUuid); if (concept != null && concept.getSet()) { return concept.getSetMembers(); } return Collections.emptyList(); } }