private RouteResultData RouteUser(int currentUserId, SchedulerEvent theEvent, QNomyDataService dataService, int delegateId, List <int> servicesInStage, bool callAfterRoute, int?routeId)
        {
            RouteResultData      result          = new RouteResultData();
            List <RouteListItem> availableRoutes = Service.GetAvailableRoutes(theEvent.ServiceId);
            int?transferedFromServiceId          = dataService.GetTranseredFromServiceId(theEvent.ProcessId);

            var  routesToStage  = new List <RouteListItem>();
            var  returnToSender = new List <RouteListItem>();
            bool skipAddRoute   = false;

            foreach (var rout in availableRoutes)
            {
                skipAddRoute = routeId.HasValue && routeId.Value > 0 && routeId.Value != rout.Id;
                if (!skipAddRoute && transferedFromServiceId.HasValue && rout.TargetType == Route.RouteTargetType.ReturnToSender && servicesInStage.Contains(transferedFromServiceId.Value))
                {
                    routesToStage.Add(rout);
                    continue;
                }

                if (!skipAddRoute && servicesInStage.Contains(rout.TargetServiceId))
                {
                    routesToStage.Add(rout);
                    continue;
                }
            }


            if (routesToStage.Count == 0)
            {
                throw new DataException("Route to this stage does not exists.");
            }

            if (routesToStage.Count > 1)
            {
                result.Selection         = new SelectOptionData();
                result.Selection.Options = new List <KeyValuePair <int, string> >();
                foreach (var item in routesToStage)
                {
                    result.Selection.Options.Add(new KeyValuePair <int, string>(item.Id, item.Name));
                }

                return(result);
            }

            var route = routesToStage[0];

            // Route operation.
            ProcessRouteResults routeResult = Process.Route(theEvent.ProcessId, currentUserId, delegateId, route.Id, route.TargetServiceId, 0, 0, "", false, 0);

            if (routeResult.Status != ProcessRouteResults.ProcessRouteResultsStatus.Success)
            {
                throw new DataException(routeResult.Status.ToString());
            }

            if (routeResult.NewServiceId > 0)
            {
                theEvent.ServiceId = routeResult.NewServiceId;
            }

            if (routeResult.NewProcessId > 0)
            {
                theEvent.ProcessId = routeResult.NewProcessId;
            }

            if (callAfterRoute && routeResult.NewEntityStatus == (int)EntityType.EntityStatus.Waiting)
            {
                try
                {
                    CallUser(currentUserId, theEvent, delegateId);
                }
                catch (DataException)
                {
                }
            }
            result.IsRouted = true;
            return(result);
        }
        internal AppointmentChangedResult AppointmentChanged(int currentUserId, int currentUnitId, int previousStageId, int nextStageId, SchedulerEvent theEvent, int?routeId)
        {
            AppointmentChangedResult result = new AppointmentChangedResult();
            var dataService = new QNomyDataService();


            CustomizeData customizeData = GetCustomizeData(currentUnitId);
            // qnomy
            var calendarStages = customizeData.Stages.ToList <CalendarStage>();

            //var allCalendarStageServices = customizeData.GetClendarStageServices();
            var             prevCalendarStageType = GetStageTypeById(previousStageId, calendarStages);
            var             nextCalendarStageType = GetStageTypeById(nextStageId, calendarStages);
            int             previousServiceId     = theEvent.ServiceId;
            RouteResultData routeResult           = null;

            int delegateId = 0;

            if ((nextCalendarStageType == CalendarStageType.InService) || (prevCalendarStageType != nextCalendarStageType))
            {
                // calendar stage type - is old stageType.
                if (prevCalendarStageType == CalendarStageType.Completed)
                {
                    throw new DataException("Already Completed, cannot change");
                }

                switch (nextCalendarStageType)
                {
                case CalendarStageType.Completed:
                    ProcessPromoteResults processPromoteResults = Process.Promote(theEvent.ProcessId, currentUserId, delegateId, Process.ProcessPromoteAction.Complete, false);
                    break;

                case CalendarStageType.Expected:
                    throw new NotSupportedException("Cannot back to expected status");

                case CalendarStageType.InService:
                    var nextStage = customizeData.Stages.Find(x => x.Id == nextStageId);
                    if (nextStage == null)
                    {
                        throw new DataException("stage is not valid");
                    }
                    var servicesInStage = GetServicesIds(nextStage.Services);

                    if (servicesInStage.Count == 0)
                    {
                        throw new DataException("Cannot find any service for this stage");
                    }

                    if (prevCalendarStageType != CalendarStageType.InService)
                    {
                        if (servicesInStage.Contains(theEvent.ServiceId))
                        {
                            CallUser(currentUserId, theEvent, delegateId);
                        }
                        else
                        {
                            routeResult = RouteUser(currentUserId, theEvent, dataService, delegateId, servicesInStage, true, routeId);
                        }
                    }
                    else
                    {
                        routeResult = RouteUser(currentUserId, theEvent, dataService, delegateId, servicesInStage, true, routeId);
                    }

                    break;

                case CalendarStageType.None:
                    throw new InvalidOperationException("Invalid stage is not defined");

                case CalendarStageType.Waiting:
                    if (prevCalendarStageType == CalendarStageType.Expected)
                    {
                        ProcessEnqueueAppointmentResults enqueResult = Process.EnqueueAppointment(theEvent.ProcessId, forceEarly: true, forceLate: true);
                        var enqueValidStatuses = new List <ProcessEnqueueAppointmentResults.ProcessEnqueueAppointmentResultsStatus>();
                        enqueValidStatuses.Add(ProcessEnqueueAppointmentResults.ProcessEnqueueAppointmentResultsStatus.Success);
                        enqueValidStatuses.Add(ProcessEnqueueAppointmentResults.ProcessEnqueueAppointmentResultsStatus.Late);
                        if (!enqueValidStatuses.Contains(enqueResult.Status))
                        {
                            throw DataException.GetDataException(enqueResult.Status);
                        }
                        break;
                    }

                    if (prevCalendarStageType == CalendarStageType.WaitingCustomerAction)
                    {
                        ProcessStopWaitingForCustomerActionResults stopWaitResult = Process.StopWaitingForCustomerAction(theEvent.ProcessId, currentUserId, delegateId);

                        break;
                    }
                    if (prevCalendarStageType == CalendarStageType.Waiting)
                    {
                        break;
                    }
                    if (prevCalendarStageType == CalendarStageType.InService)
                    {
                        ProcessRequeueInServiceResults requeResult = Process.RequeueInService(theEvent.ProcessId, currentUnitId, delegateId);
                        break;
                    }
                    throw new DataException("Waiting stage does not accessible from this stage");

                case CalendarStageType.WaitingCustomerAction:
                    ProcessWaitForCustomerActionResults waitResult = Process.WaitForCustomerAction(theEvent.ProcessId, currentUserId, 0);
                    break;

                default:
                    throw new InvalidOperationException("Stage is not supported");
                }
            }

            if (routeResult != null && !routeResult.IsRouted)
            {
                result.RouteData = routeResult;
                return(result);
            }


            AdjustStageType(theEvent, calendarStages, customizeData);

            result.EventData = theEvent;
            return(result);
        }