コード例 #1
0
ファイル: AppointmentL.cs プロジェクト: royedwards/DRDNet
        ///<summary></summary>
        public static List <DateTime> GetSearchResults(long aptNum, DateTime afterDate, List <long> providerNums, int resultCount, TimeSpan beforeTime, TimeSpan afterTime)
        {
            if (beforeTime == TimeSpan.FromSeconds(0))           //if they didn't set a before time, set it to a large timespan so that we can use the same logic for checking appointment times.
            {
                beforeTime = TimeSpan.FromHours(25);             //bigger than any time of day.
            }
            SearchBehaviorCriteria SearchType   = (SearchBehaviorCriteria)PrefC.GetInt(PrefName.AppointmentSearchBehavior);
            List <DateTime>        retVal       = new List <DateTime>();
            DateTime           dayEvaluating    = afterDate.AddDays(1);
            Appointment        appointmentToAdd = Appointments.GetOneApt(aptNum);
            List <DateTime>    potentialProvAppointmentTime;
            List <DateTime>    potentialOpAppointmentTime;
            List <Operatory>   opsListAll                          = Operatories.GetDeepCopy();                 //all operatory Numbers
            List <Schedule>    scheduleListAll                     = Schedules.GetTwoYearPeriod(dayEvaluating); // Schedules for the given day.
            List <Appointment> appointmentListAll                  = Appointments.GetForPeriodList(dayEvaluating, dayEvaluating.AddYears(2));
            List <ScheduleOp>  schedOpListAll                      = ScheduleOps.GetForSchedList(scheduleListAll);
            List <ApptSearchProviderSchedule>  provScheds          = new List <ApptSearchProviderSchedule>();  //Provider Bar, ProviderSched Bar, Date and Provider
            List <ApptSearchOperatorySchedule> operatrorySchedules = new List <ApptSearchOperatorySchedule>(); //filtered based on SearchType
            List <long> operatoryNums = new List <long>();                                                     //more usefull than a list of operatories.

            for (int i = 0; i < opsListAll.Count; i++)
            {
                operatoryNums.Add(opsListAll[i].OperatoryNum);
            }
            while (retVal.Count < resultCount && dayEvaluating < afterDate.AddYears(2))
            {
                potentialOpAppointmentTime = new List <DateTime>();               //clear or create
                //Providers-------------------------------------------------------------------------------------------------------------------------------------
                potentialProvAppointmentTime = new List <DateTime>();             //clear or create
                provScheds = Appointments.GetApptSearchProviderScheduleForProvidersAndDate(providerNums, dayEvaluating, scheduleListAll, appointmentListAll);
                for (int i = 0; i < provScheds.Count; i++)
                {
                    for (int j = 0; j < 288; j++)               //search every 5 minute increment per day
                    {
                        if (j + appointmentToAdd.Pattern.Length > 288)
                        {
                            break;
                        }
                        if (potentialProvAppointmentTime.Contains(dayEvaluating.AddMinutes(j * 5)))
                        {
                            continue;
                        }
                        bool addDateTime = true;
                        for (int k = 0; k < appointmentToAdd.Pattern.Length; k++)
                        {
                            if ((provScheds[i].ProvBar[j + k] == false && appointmentToAdd.Pattern[k] == 'X') || provScheds[i].ProvSchedule[j + k] == false)
                            {
                                addDateTime = false;
                                break;
                            }
                        }
                        if (addDateTime)
                        {
                            potentialProvAppointmentTime.Add(dayEvaluating.AddMinutes(j * 5));
                        }
                    }
                }
                if (SearchType == SearchBehaviorCriteria.ProviderTimeOperatory)               //Handle Operatories here----------------------------------------------------------------------------
                {
                    operatrorySchedules        = GetAllForDate(dayEvaluating, scheduleListAll, appointmentListAll, schedOpListAll, operatoryNums, providerNums);
                    potentialOpAppointmentTime = new List <DateTime>(); //create or clear
                    //for(int j=0;j<operatrorySchedules.Count;j++) {//for each operatory
                    for (int i = 0; i < 288; i++)                       //search every 5 minute increment per day
                    {
                        if (i + appointmentToAdd.Pattern.Length > 288)  //skip if appointment would span across midnight
                        {
                            break;
                        }
                        for (int j = 0; j < operatrorySchedules.Count; j++)                   //for each operatory
                        //if(potentialOpAppointmentTime.Contains(dayEvaluating.AddMinutes(i*5))) {//skip if we already have this dateTime
                        //  break;
                        //}
                        {
                            bool addDateTime = true;
                            for (int k = 0; k < appointmentToAdd.Pattern.Length; k++)                       //check appointment against operatories
                            {
                                if (operatrorySchedules[j].OperatorySched[i + k] == false)
                                {
                                    addDateTime = false;
                                    break;
                                }
                            }
                            if (!addDateTime)
                            {
                                continue;
                            }
                            if (addDateTime)                            // && SearchType==SearchBehaviorCriteria.ProviderTimeOperatory) {//check appointment against providers available for the given operatory
                            {
                                bool provAvail = false;
                                for (int k = 0; k < providerNums.Count; k++)
                                {
                                    if (!operatrorySchedules[j].ProviderNums.Contains(providerNums[k]))
                                    {
                                        continue;
                                    }
                                    provAvail = true;
                                    for (int m = 0; m < appointmentToAdd.Pattern.Length; m++)
                                    {
                                        if ((provScheds[k].ProvBar[i + m] == false && appointmentToAdd.Pattern[m] == 'X') || provScheds[k].ProvSchedule[i + m] == false)                               //if provider bar time slot
                                        {
                                            provAvail = false;
                                            break;
                                        }
                                    }
                                    if (provAvail)                                     //found a provider with an available operatory
                                    {
                                        break;
                                    }
                                }
                                if (provAvail && addDateTime)                                 //operatory and provider are available
                                {
                                    potentialOpAppointmentTime.Add(dayEvaluating.AddMinutes(i * 5));
                                }
                            }
                            else                              //not using SearchBehaviorCriteria.ProviderTimeOperatory
                            {
                                if (addDateTime)
                                {
                                    potentialOpAppointmentTime.Add(dayEvaluating.AddMinutes(i * 5));
                                }
                            }
                        }
                    }
                }
                //At this point the potentialOpAppointmentTime is already filtered and only contains appointment times that match both provider time and operatory time.
                switch (SearchType)
                {
                case SearchBehaviorCriteria.ProviderTime:
                    //Add based on provider bars
                    for (int i = 0; i < potentialProvAppointmentTime.Count; i++)
                    {
                        if (potentialProvAppointmentTime[i].TimeOfDay > beforeTime || potentialProvAppointmentTime[i].TimeOfDay < afterTime)
                        {
                            continue;
                        }
                        retVal.Add(potentialProvAppointmentTime[i]); //add one for this day
                        break;                                       //stop looking through potential times for today.
                    }
                    break;

                case SearchBehaviorCriteria.ProviderTimeOperatory:
                    //add based on provider bar and operatory bar
                    for (int i = 0; i < potentialOpAppointmentTime.Count; i++)
                    {
                        if (potentialOpAppointmentTime[i].TimeOfDay > beforeTime || potentialOpAppointmentTime[i].TimeOfDay < afterTime)
                        {
                            continue;
                        }
                        retVal.Add(potentialOpAppointmentTime[i]); //add one for this day
                        break;                                     //stop looking through potential times for today.
                    }
                    break;
                }
                dayEvaluating = dayEvaluating.AddDays(1);
            }
            return(retVal);
        }
コード例 #2
0
ファイル: TimeSlots.cs プロジェクト: WaltWatt/OpenDental
        ///<summary>Gets open time slots based on the parameters passed in.
        ///Open time slots are found by looping through the passed in operatories and finding openings that can hold the entire appointment.
        ///Make sure that timePattern is always passed in utilizing 5 minute increments (no conversion will be applied to the pattern passed in).
        ///Providers passed in will be the only providers considered when looking for available time slots.
        ///Passing in a null clinic will only consider operatories with clinics set to 0 (unassigned).
        ///The timeslots on and between the Start and End dates passed in will be considered and potentially returned as available.
        ///Optionally set defNumApptType if looking for time slots for New Pat Appt which will apply the DefNum to all time slots found.
        ///Throws exceptions.</summary>
        public static List <TimeSlot> GetTimeSlotsForRange(DateTime dateStart, DateTime dateEnd, string timePattern, List <long> listProvNums
                                                           , List <Operatory> listOperatories, List <Schedule> listSchedules, Clinic clinic, long defNumApptType = 0, Logger.IWriteLine log = null)
        {
            //No need to check RemotingRole; no call to db.
            //Order the operatories passed in by their ItemOrder just in case they were passed in all jumbled up.
            List <long> listOpNums = listOperatories.OrderBy(x => x.ItemOrder).Select(x => x.OperatoryNum).Distinct().ToList();

            //Remove all schedules that fall outside of the date range passed in.  Only consider the date right now, the time portion is handled later.
            listSchedules.RemoveAll(x => !x.SchedDate.Date.Between(dateStart.Date, dateEnd.Date));
            List <Schedule> listProviderSchedules = listSchedules.FindAll(x => x.BlockoutType == 0);
            List <Schedule> listBlockoutSchedules = listSchedules.FindAll(x => x.BlockoutType > 0);
            //Get every single appointment for all operatories within our start and end dates for double booking and overlapping consideration.
            List <Appointment> listApptsForOps = Appointments.GetAppointmentsForOpsByPeriod(Operatories.GetDeepCopy(true).Select(x => x.OperatoryNum).ToList()
                                                                                            , dateStart, dateEnd, log, listSchedules.GroupBy(x => x.ProvNum).Select(x => x.Key).ToList());

            log?.WriteLine("listProviderSchedules:\r\n\t" + string.Join(",\r\n\t",
                                                                        listProviderSchedules.Select(x => x.ScheduleNum + " - " + x.SchedDate.ToShortDateString() + " " + x.StartTime)), LogLevel.Verbose);
            log?.WriteLine("listBlockoutSchedules:\r\n\t" + string.Join(",\r\n\t",
                                                                        listBlockoutSchedules.Select(x => x.ScheduleNum + " - " + x.SchedDate.ToShortDateString() + " " + x.StartTime)), LogLevel.Verbose);
            log?.WriteLine("listApptsForOps:\r\n\t"
                           + string.Join(",\r\n\t", listApptsForOps.Select(x => x.AptNum + " - " + x.AptDateTime + " OpNum: " + x.Op)), LogLevel.Verbose);
            //We need to be conscious of double booking possibilities.  Go get provider schedule information for the date range passed in.
            Dictionary <DateTime, List <ApptSearchProviderSchedule> > dictProvSchedules = Appointments.GetApptSearchProviderScheduleForProvidersAndDate(
                listProvNums, dateStart, dateEnd, listProviderSchedules, listApptsForOps);
            //Split up the operatory specific provider schedules from the dynamic ones because each will have different operatory logic.
            List <Schedule>   listProviderSchedulesWithOp = listProviderSchedules.FindAll(x => x.Ops.Intersect(listOpNums).ToList().Count > 0);
            List <ScheduleOp> listScheduleOps             = ScheduleOps.GetForSchedList(listProviderSchedules);
            //Now we need to get the dynamic schedules (not assigned to a specific operatory).
            List <Schedule> listProviderDynamicSchedules = listProviderSchedules.FindAll(x => !listScheduleOps.Exists(y => y.ScheduleNum == x.ScheduleNum));
            //Now that we have found all possible valid schedules, find all the unique time slots from them.
            List <Schedule> listProviderSchedulesAll = new List <Schedule>(listProviderSchedulesWithOp);

            listProviderSchedulesAll.AddRange(listProviderDynamicSchedules);
            listProviderSchedulesAll = listProviderSchedulesAll.OrderBy(x => x.SchedDate).ToList();
            List <TimeSlot> listAvailableTimeSlots = new List <TimeSlot>();
            List <DateTime> listUniqueDays         = new List <DateTime>();
            int             timeIncrement          = PrefC.GetInt(PrefName.AppointmentTimeIncrement);

            //Loop through all schedules five minutes at a time to find time slots large enough that have no appointments and no blockouts within them.
            foreach (Schedule schedule in listProviderSchedulesAll)
            {
                DateTime dateSched = schedule.SchedDate;
                //Straight up ignore schedules in the past.  This should not be possible but this is just in case.
                if (dateSched.Date < DateTime.Today)
                {
                    continue;
                }
                if (!listUniqueDays.Contains(dateSched))
                {
                    listUniqueDays.Add(dateSched);
                }
                TimeSpan timeSchedStart = schedule.StartTime;
                TimeSpan timeSchedStop  = schedule.StopTime;
                //Now, make sure that the start time is set to a starting time that makes sense with the appointment time increment preference.
                int minsOver = (timeSchedStart.Minutes) % timeIncrement;
                if (minsOver > 0)
                {
                    int minsToAdd = timeIncrement - minsOver;
                    timeSchedStart = timeSchedStart.Add(new TimeSpan(0, minsToAdd, 0));
                }
                //Double check that we haven't pushed the start time past the stop time.
                if (timeSchedStart >= timeSchedStop)
                {
                    continue;
                }
                //Figure out all possible operatories for this particular schedule.
                List <Operatory> listOpsForSchedule = new List <Operatory>();
                if (schedule.Ops.Count > 0)
                {
                    listOpsForSchedule = listOperatories.FindAll(x => schedule.Ops.Exists(y => y == x.OperatoryNum));
                }
                else                  //Dynamic schedule.  Figure out what operatories this provider is part of that are associated to the corresponding eService.
                                      //Get all of the valid operatories that this provider is associated with.
                {
                    listOpsForSchedule = listOperatories.FindAll(x => x.ProvDentist == schedule.ProvNum || x.ProvHygienist == schedule.ProvNum);
                }
                if (PrefC.HasClinicsEnabled)
                {
                    //Skip this schedule entry if the operatory's clinic does not match the patient's clinic.
                    if (clinic == null)
                    {
                        //If a clinic was not passed in, ONLY consider unassigned operatories
                        listOpsForSchedule = listOpsForSchedule.FindAll(x => x.ClinicNum == 0);
                    }
                    else
                    {
                        //If a valid clinic was passed in, make sure the operatory has a matching clinic.
                        listOpsForSchedule = listOpsForSchedule.FindAll(x => x.ClinicNum == clinic.ClinicNum);
                    }
                }
                if (listOpsForSchedule.Count == 0)
                {
                    continue;                    //No valid operatories for this schedule.
                }
                log?.WriteLine("schedule: " + schedule.ScheduleNum + "\tlistOpsForSchedule:\r\n\t"
                               + string.Join(",\r\n\t", listOpsForSchedule.Select(x => x.OperatoryNum + " - " + x.Abbrev)), LogLevel.Verbose);
                //The list of operatories has been filtered above so we need to find ALL available time slots for this schedule in all operatories.
                foreach (Operatory op in listOpsForSchedule)
                {
                    AddTimeSlotsFromSchedule(listAvailableTimeSlots, schedule, op.OperatoryNum, timeSchedStart, timeSchedStop
                                             , listBlockoutSchedules, dictProvSchedules, listApptsForOps, timePattern, defNumApptType);
                }
            }
            //Remove any time slots that start before right now (just in case the consuming method is looking for slots for today).
            listAvailableTimeSlots.RemoveAll(x => x.DateTimeStart.Date == DateTime.Now.Date && x.DateTimeStart.TimeOfDay < DateTime.Now.TimeOfDay);
            //Order the entire list of available time slots so that they are displayed to the user in sequential order.
            //We need to do this because we loop through each provider's schedule one at a time and add openings as we find them.
            //Then order by operatory.ItemOrder in order to preserve old behavior (filling up the schedule via operatories from the left to the right).
            return(listAvailableTimeSlots.OrderBy(x => x.DateTimeStart)
                   //listOpNums was ordered by ItemOrder at the top of this method so we can trust that it is in the correct order.
                   .ThenBy(x => listOpNums.IndexOf(x.OperatoryNum))
                   .ToList());
        }