/// <summary> /// Factory method to create a new order. /// </summary> public static Order NewOrder(OrderCreationArgs args, IProcedureNumberBroker procedureNumberBroker, IDicomUidBroker dicomUidBroker) { // validate required members are set Platform.CheckMemberIsSet(args.Patient, "Patient"); Platform.CheckMemberIsSet(args.Visit, "Visit"); Platform.CheckMemberIsSet(args.AccessionNumber, "AccessionNumber"); Platform.CheckMemberIsSet(args.DiagnosticService, "DiagnosticService"); Platform.CheckMemberIsSet(args.ReasonForStudy, "ReasonForStudy"); Platform.CheckMemberIsSet(args.OrderingFacility, "OrderingFacility"); Platform.CheckMemberIsSet(args.OrderingPractitioner, "OrderingPractitioner"); // create the order var order = new Order { Patient = args.Patient, Visit = args.Visit, AccessionNumber = args.AccessionNumber, DiagnosticService = args.DiagnosticService, ReasonForStudy = args.ReasonForStudy, OrderingFacility = args.OrderingFacility, OrderingPractitioner = args.OrderingPractitioner, Priority = args.Priority, SchedulingRequestTime = args.SchedulingRequestTime, EnteredTime = args.EnteredTime, EnteredBy = args.EnteredBy, EnteredComment = args.EnteredComment }; if (args.Procedures == null || args.Procedures.Count == 0) { // create procedures according to the diagnostic service plan args.Procedures = CollectionUtils.Map <ProcedureType, Procedure>( args.DiagnosticService.ProcedureTypes, type => new Procedure(type, procedureNumberBroker.GetNext(), dicomUidBroker.GetNewUid()) { PerformingFacility = args.PerformingFacility ?? args.OrderingFacility }); } // associate all procedures with the order foreach (var procedure in args.Procedures) { order.AddProcedure(procedure); } // add recipients if (args.ResultRecipients != null) { foreach (var recipient in args.ResultRecipients) { order.ResultRecipients.Add(recipient); } } var recipientsContainsOrderingPractitioner = CollectionUtils.Contains( order.ResultRecipients, r => r.PractitionerContactPoint.Practitioner.Equals(args.OrderingPractitioner)); // if the result recipients collection does not contain the ordering practitioner, add it by force if (!recipientsContainsOrderingPractitioner) { var orderingPractitionerContactPoint = // use the contact point associated to the ordering facility's information authority CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => args.OrderingFacility.InformationAuthority.Equals(cp.InformationAuthority) && cp.Deactivated == false) // or, use the default contact point ?? CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => cp.IsDefaultContactPoint) // or, if no default, use first available active CP (should never happen) ?? CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => !cp.Deactivated) // or, if no active CPs, use first in the collection (should never happen) ?? CollectionUtils.FirstElement(args.OrderingPractitioner.ContactPoints); if (orderingPractitionerContactPoint != null) { order.ResultRecipients.Add(new ResultRecipient(orderingPractitionerContactPoint, ResultCommunicationMode.ANY)); } } return(order); }
/// <summary> /// Un-merges this order from its merge destination, returning a new order with the specified accession #, /// and marking this order as Replaced by the new order. /// </summary> /// <param name="cancelInfo"></param> /// <param name="newAccessionNumber"></param> /// <returns></returns> public virtual UnmergeResult Unmerge(OrderCancelInfo cancelInfo, string newAccessionNumber) { string failureReason; if (!CanUnmerge(cancelInfo, out failureReason)) { throw new WorkflowException(failureReason); } var destOrder = _mergeInfo.MergeDestinationOrder; // create replacement order var newOrder = new Order( _patient, _visit, null, // do not copy placer-number newAccessionNumber, // assign new acc # _diagnosticService, _enteredTime, _enteredBy, _enteredComment, _schedulingRequestTime, null, // will be set by call to UpdateScheduling() null, null, _orderingPractitioner, _orderingFacility, new HashedSet <Procedure>(), // will be added later new HashedSet <Procedure>(), // ghosts CollectionUtils.Map(_resultRecipients, (ResultRecipient rr) => (ResultRecipient)rr.Clone()), new List <OrderAttachment>(), _reasonForStudy, _priority, (int)_priority, OrderStatus.SC, null, null, new HashedSet <Order>(), ExtendedPropertyUtils.Copy(_extendedProperties) ); // reclaim order notes var notes = OrderNote.GetNotesForOrder(this); var reclaimNotes = CollectionUtils.Map( CollectionUtils.Select(notes, n => n.GhostOf != null), (OrderNote n) => n.GhostOf.Downcast <OrderNote>()); foreach (var note in reclaimNotes) { note.Order = newOrder; } // reclaim attachments var reclaimAttachments = CollectionUtils.Map(_attachments, (OrderAttachment a) => CollectionUtils.SelectFirst(destOrder.Attachments, b => Equals(a.Document.GhostOf, b.Document))); foreach (var attachment in reclaimAttachments) { destOrder.Attachments.Remove(attachment); newOrder.Attachments.Add(attachment); } // reclaim procedures // need to create new ghost copies on the dest order, so that HL7 can cancel them var reclaimProcedures = CollectionUtils.Map(_ghostProcedures, (Procedure p) => p.GhostOf); var ghostProcedures = CollectionUtils.Map(reclaimProcedures, (Procedure p) => p.CreateGhostCopy()); foreach (var procedure in reclaimProcedures) { newOrder.AddProcedure(procedure); } destOrder.Procedures.AddAll(ghostProcedures); // note: procedure Indexes are already set correctly // update scheduling/status information newOrder.UpdateScheduling(); newOrder.UpdateStatus(); // any orders that were merged into this order must be redirected to the new order, // in order to support recursive unmerge foreach (var sourceOrder in _mergeSourceOrders) { sourceOrder.MergeInfo.MergeDestinationOrder = newOrder; newOrder.MergeSourceOrders.Add(sourceOrder); } _mergeSourceOrders.Clear(); // change status of this order to RP, and set cancel info _cancelInfo = (OrderCancelInfo)cancelInfo.Clone(); _cancelInfo.ReplacementOrder = newOrder; // clear merge info on this order, since it is no longer considered merged _mergeInfo = null; // set status of this order to RP, and set end time manually SetStatus(OrderStatus.RP); _endTime = Platform.Time; return(new UnmergeResult { ReplacementOrder = newOrder, GhostProcedures = ghostProcedures }); }
/// <summary> /// Factory method to create a new order. /// </summary> public static Order NewOrder(OrderCreationArgs args, IProcedureNumberBroker procedureNumberBroker, IDicomUidBroker dicomUidBroker) { // validate required members are set Platform.CheckMemberIsSet(args.Patient, "Patient"); Platform.CheckMemberIsSet(args.Visit, "Visit"); Platform.CheckMemberIsSet(args.AccessionNumber, "AccessionNumber"); Platform.CheckMemberIsSet(args.DiagnosticService, "DiagnosticService"); Platform.CheckMemberIsSet(args.ReasonForStudy, "ReasonForStudy"); Platform.CheckMemberIsSet(args.OrderingFacility, "OrderingFacility"); Platform.CheckMemberIsSet(args.OrderingPractitioner, "OrderingPractitioner"); // create the order var order = new Order { Patient = args.Patient, Visit = args.Visit, AccessionNumber = args.AccessionNumber, DiagnosticService = args.DiagnosticService, ReasonForStudy = args.ReasonForStudy, OrderingFacility = args.OrderingFacility, OrderingPractitioner = args.OrderingPractitioner, Priority = args.Priority, SchedulingRequestTime = args.SchedulingRequestTime, EnteredTime = args.EnteredTime, EnteredBy = args.EnteredBy, EnteredComment = args.EnteredComment }; if (args.Procedures == null || args.Procedures.Count == 0) { // create procedures according to the diagnostic service plan args.Procedures = CollectionUtils.Map<ProcedureType, Procedure>( args.DiagnosticService.ProcedureTypes, type => new Procedure(type, procedureNumberBroker.GetNext(), dicomUidBroker.GetNewUid()) { PerformingFacility = args.PerformingFacility ?? args.OrderingFacility }); } // associate all procedures with the order foreach (var procedure in args.Procedures) { order.AddProcedure(procedure); } // add recipients if (args.ResultRecipients != null) { foreach (var recipient in args.ResultRecipients) { order.ResultRecipients.Add(recipient); } } var recipientsContainsOrderingPractitioner = CollectionUtils.Contains( order.ResultRecipients, r => r.PractitionerContactPoint.Practitioner.Equals(args.OrderingPractitioner)); // if the result recipients collection does not contain the ordering practitioner, add it by force if (!recipientsContainsOrderingPractitioner) { var orderingPractitionerContactPoint = // use the contact point associated to the ordering facility's information authority CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => args.OrderingFacility.InformationAuthority.Equals(cp.InformationAuthority) && cp.Deactivated == false) // or, use the default contact point ?? CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => cp.IsDefaultContactPoint) // or, if no default, use first available active CP (should never happen) ?? CollectionUtils.SelectFirst(args.OrderingPractitioner.ContactPoints, cp => !cp.Deactivated) // or, if no active CPs, use first in the collection (should never happen) ?? CollectionUtils.FirstElement(args.OrderingPractitioner.ContactPoints); if (orderingPractitionerContactPoint != null) { order.ResultRecipients.Add(new ResultRecipient(orderingPractitionerContactPoint, ResultCommunicationMode.ANY)); } } return order; }
private void UpdateProceduresHelper(Order order, IEnumerable<ProcedureRequisition> procedureReqs, ModifyOrderRequest request) { // do not update the procedures if the order is completed if (order.IsTerminated) return; var assembler = new OrderEntryAssembler(); // if any procedure is in downtime recovery mode, assume the entire order is a "downtime order" var isDowntime = CollectionUtils.Contains(order.Procedures, p => p.DowntimeRecoveryMode); // separate the list into additions and updates var existingReqs = new List<ProcedureRequisition>(); var addedReqs = new List<ProcedureRequisition>(); foreach (var req in procedureReqs) { if (CollectionUtils.Contains(order.Procedures, x => req.ProcedureNumber == x.Number)) { existingReqs.Add(req); } else { addedReqs.Add(req); } } // process the additions first, so that we don't accidentally cancel an order (if all its procedures are cancelled momentarily) var procedureNumberBroker = PersistenceContext.GetBroker<IProcedureNumberBroker>(); var dicomUidBroker = PersistenceContext.GetBroker<IDicomUidBroker>(); foreach (var req in addedReqs) { var requestedType = this.PersistenceContext.Load<ProcedureType>(req.ProcedureType.ProcedureTypeRef); // create a new procedure for this requisition var procedure = new Procedure(requestedType, procedureNumberBroker.GetNext(), dicomUidBroker.GetNewUid()) { DowntimeRecoveryMode = isDowntime }; order.AddProcedure(procedure); // note: need to lock the new procedure now, prior to creating the procedure steps // otherwise may get exceptions saying the Procedure is a transient object this.PersistenceContext.Lock(procedure, DirtyState.New); // create the procedure steps procedure.CreateProcedureSteps(); // apply the requisition information to the actual procedure assembler.UpdateProcedureFromRequisition(procedure, req, this.CurrentUserStaff, this.PersistenceContext); LogicalHL7Event.ProcedureCreated.EnqueueEvents(procedure); } // process updates foreach (var req in existingReqs) { var requestedType = this.PersistenceContext.Load<ProcedureType>(req.ProcedureType.ProcedureTypeRef); var procedure = CollectionUtils.SelectFirst(order.Procedures, x => req.ProcedureNumber == x.Number); // validate that the type has not changed if (!procedure.Type.Equals(requestedType)) throw new RequestValidationException("Order modification must not modify the type of a requested procedure."); // If the procedure is already terminated, just move on to the next one since procedures cannot be "un-terminated". if (procedure.IsTerminated) continue; // apply the requisition information to the actual procedure assembler.UpdateProcedureFromRequisition(procedure, req, this.CurrentUserStaff, this.PersistenceContext); (req.Cancelled ? LogicalHL7Event.ProcedureCancelled : LogicalHL7Event.ProcedureModified).EnqueueEvents(procedure); } }
/// <summary> /// Un-merges this order from its merge destination, returning a new order with the specified accession #, /// and marking this order as Replaced by the new order. /// </summary> /// <param name="cancelInfo"></param> /// <param name="newAccessionNumber"></param> /// <returns></returns> public virtual UnmergeResult Unmerge(OrderCancelInfo cancelInfo, string newAccessionNumber) { string failureReason; if (!CanUnmerge(cancelInfo, out failureReason)) throw new WorkflowException(failureReason); var destOrder = _mergeInfo.MergeDestinationOrder; // create replacement order var newOrder = new Order( _patient, _visit, null, // do not copy placer-number newAccessionNumber, // assign new acc # _diagnosticService, _enteredTime, _enteredBy, _enteredComment, _schedulingRequestTime, null, // will be set by call to UpdateScheduling() null, null, _orderingPractitioner, _orderingFacility, new HashedSet<Procedure>(), // will be added later new HashedSet<Procedure>(), // ghosts CollectionUtils.Map(_resultRecipients, (ResultRecipient rr) => (ResultRecipient)rr.Clone()), new List<OrderAttachment>(), _reasonForStudy, _priority, (int)_priority, OrderStatus.SC, null, null, new HashedSet<Order>(), ExtendedPropertyUtils.Copy(_extendedProperties) ); // reclaim order notes var notes = OrderNote.GetNotesForOrder(this); var reclaimNotes = CollectionUtils.Map( CollectionUtils.Select(notes, n => n.GhostOf != null), (OrderNote n) => n.GhostOf.Downcast<OrderNote>()); foreach (var note in reclaimNotes) { note.Order = newOrder; } // reclaim attachments var reclaimAttachments = CollectionUtils.Map(_attachments, (OrderAttachment a) => CollectionUtils.SelectFirst(destOrder.Attachments, b => Equals(a.Document.GhostOf, b.Document))); foreach (var attachment in reclaimAttachments) { destOrder.Attachments.Remove(attachment); newOrder.Attachments.Add(attachment); } // reclaim procedures // need to create new ghost copies on the dest order, so that HL7 can cancel them var reclaimProcedures = CollectionUtils.Map(_ghostProcedures, (Procedure p) => p.GhostOf); var ghostProcedures = CollectionUtils.Map(reclaimProcedures, (Procedure p) => p.CreateGhostCopy()); foreach (var procedure in reclaimProcedures) { newOrder.AddProcedure(procedure); } destOrder.Procedures.AddAll(ghostProcedures); // note: procedure Indexes are already set correctly // update scheduling/status information newOrder.UpdateScheduling(); newOrder.UpdateStatus(); // any orders that were merged into this order must be redirected to the new order, // in order to support recursive unmerge foreach (var sourceOrder in _mergeSourceOrders) { sourceOrder.MergeInfo.MergeDestinationOrder = newOrder; newOrder.MergeSourceOrders.Add(sourceOrder); } _mergeSourceOrders.Clear(); // change status of this order to RP, and set cancel info _cancelInfo = (OrderCancelInfo)cancelInfo.Clone(); _cancelInfo.ReplacementOrder = newOrder; // clear merge info on this order, since it is no longer considered merged _mergeInfo = null; // set status of this order to RP, and set end time manually SetStatus(OrderStatus.RP); _endTime = Platform.Time; return new UnmergeResult {ReplacementOrder = newOrder, GhostProcedures = ghostProcedures}; }