Summary
Stopping an order — via OrderService.discontinueOrder(...), or by saving a DISCONTINUE-action order, or auto-expiry — updates the original order's dateStopped / autoExpireDate, but fires no *ServiceEvent for that original order. Event-driven consumers therefore never learn that the order was stopped: the order continues to appear active. (The new discontinuation order itself is evented when created through saveOrder; the gap is specifically the state change on the order being stopped.)
Environment
Root cause
OrderServiceImpl.discontinueOrder(...) stops the previous order via a private stopOrder(...) → saveOrderInternal(...) path, and saveOrder(...) does the same when it processes a DISCONTINUE order. Bytecode (2.9.0-SNAPSHOT):
OrderServiceImpl.discontinueOrder(Order, Concept, Date, Provider, Encounter):
invokespecial stopOrder:(Lorg/openmrs/Order;Ljava/util/Date;Z)V // private — stops the original
invokespecial saveOrderInternal:(...)Lorg/openmrs/Order; // private — persists, no event
Because the stop is applied through the private saveOrderInternal (not the public, proxied saveOrder), the #6084 OpenmrsServiceEventAdvice never intercepts it → no SaveServiceEvent for the stopped order. discontinueOrder itself also does not match the advice's method-name pattern (save*/void*/unvoid*/retire*/unretire*/purge*).
Reproduction
- Create a test/drug order T (it fires
SaveServiceEvent → consumer indexes it as active).
- Discontinue it (create a
DISCONTINUE order whose previousOrder is T, or call discontinueOrder).
- Observe in core:
T.dateStopped is now set. But no event fired for T.
Verified on the standalone: after discontinuing, the original order's dateStopped was populated in core, yet a consumer indexing dateStopped still showed the order with no stop date (i.e. active). The discontinuation order was indexed (it goes through saveOrder); only the original's state change was missed.
Impact
Any event-driven consumer that reflects order state (active-vs-stopped, dateStopped, autoExpireDate) goes stale on discontinue/auto-expire — e.g. a query for "active medications/orders" would still return a discontinued order. This is the same root pattern as #6194: a service operation mutates an indexed record without emitting a corresponding event.
Suggested fix
When an order is stopped, fire a SaveServiceEvent for the stopped order — e.g. route the stop through the public saveOrder (so the advice fires) or publish the event explicitly from stopOrder. Optionally also have the advice recognise discontinueOrder.
Related
Summary
Stopping an order — via
OrderService.discontinueOrder(...), or by saving aDISCONTINUE-action order, or auto-expiry — updates the original order'sdateStopped/autoExpireDate, but fires no*ServiceEventfor that original order. Event-driven consumers therefore never learn that the order was stopped: the order continues to appear active. (The new discontinuation order itself is evented when created throughsaveOrder; the gap is specifically the state change on the order being stopped.)Environment
*ServiceEvents; the issue is in core.Root cause
OrderServiceImpl.discontinueOrder(...)stops the previous order via a privatestopOrder(...)→saveOrderInternal(...)path, andsaveOrder(...)does the same when it processes aDISCONTINUEorder. Bytecode (2.9.0-SNAPSHOT):Because the stop is applied through the private
saveOrderInternal(not the public, proxiedsaveOrder), the #6084OpenmrsServiceEventAdvicenever intercepts it → noSaveServiceEventfor the stopped order.discontinueOrderitself also does not match the advice's method-name pattern (save*/void*/unvoid*/retire*/unretire*/purge*).Reproduction
SaveServiceEvent→ consumer indexes it as active).DISCONTINUEorder whosepreviousOrderis T, or calldiscontinueOrder).T.dateStoppedis now set. But no event fired for T.Verified on the standalone: after discontinuing, the original order's
dateStoppedwas populated in core, yet a consumer indexingdateStoppedstill showed the order with no stop date (i.e. active). The discontinuation order was indexed (it goes throughsaveOrder); only the original's state change was missed.Impact
Any event-driven consumer that reflects order state (active-vs-stopped,
dateStopped,autoExpireDate) goes stale on discontinue/auto-expire — e.g. a query for "active medications/orders" would still return a discontinued order. This is the same root pattern as #6194: a service operation mutates an indexed record without emitting a corresponding event.Suggested fix
When an order is stopped, fire a
SaveServiceEventfor the stopped order — e.g. route the stop through the publicsaveOrder(so the advice fires) or publish the event explicitly fromstopOrder. Optionally also have the advice recognisediscontinueOrder.Related
mergePatientsdoes not reassign Conditions/Allergies/MedicationDispenses (same root pattern).