public JsonResult VerifyTimeAvailability(string date, string start, string end, int? excludeAppointmentId = null)
        {
            if (string.IsNullOrEmpty(date) || string.IsNullOrEmpty(start) || string.IsNullOrEmpty(end))
            {
                return this.Json(new
                {
                    status = DateAndTimeValidationState.Failed.ToString(),
                    text = "Sem informações suficientes",
                }, JsonRequestBehavior.AllowGet);
            }
            else
            {
                var localDateParsed = DateTime.Parse(date).Date;

                if (localDateParsed.Kind != DateTimeKind.Unspecified)
                    throw new ArgumentException("'date' must be expressed in local practice time-zone.", "date");

                AppointmentViewModel viewModel = new AppointmentViewModel
                {
                    LocalDateTime = localDateParsed,
                    Start = start,
                    End = end,
                };

                this.DoDateAndTimeValidation(viewModel, this.GetPracticeLocalNow(), excludeAppointmentId);

                return this.Json(new
                {
                    status = viewModel.DateAndTimeValidationState.ToString(),
                    text = viewModel.TimeValidationMessage,
                }, JsonRequestBehavior.AllowGet);
            }
        }
        public ActionResult Edit(int id)
        {
            var currentUserPracticeId = this.DbUser.PracticeId;

            var appointment = this.db.Appointments
                .Where(a => a.Id == id).FirstOrDefault(a => a.Doctor.Users.FirstOrDefault().PracticeId == currentUserPracticeId);

            if (appointment == null)
            {
                // todo: return new HttpNotFoundResult("A consulta/compromisso não existe.");
                return this.View("NotFound");
            }

            var localNow = this.GetPracticeLocalNow();

            var appointmentLocalStart = ConvertToLocalDateTime(this.DbPractice, appointment.Start);
            var appointmentLocalEnd = ConvertToLocalDateTime(this.DbPractice, appointment.End);

            var viewModel = new AppointmentViewModel()
                {
                    Id = appointment.Id,
                    LocalDateTime = appointmentLocalStart.Date,
                    Start = appointmentLocalStart.ToString("HH:mm"),
                    End = appointmentLocalEnd.ToString("HH:mm"),
                    DoctorId = appointment.DoctorId,
                    LocalDateTimeSpelled = DateTimeHelper.GetDayOfWeekAsString(appointmentLocalStart.Date) + ", "
                        + DateTimeHelper.ConvertToRelative(
                            appointmentLocalStart.Date,
                            localNow,
                            DateTimeHelper.RelativeDateOptions.IncludePrefixes
                                | DateTimeHelper.RelativeDateOptions.IncludeSuffixes
                                | DateTimeHelper.RelativeDateOptions.ReplaceToday
                                | DateTimeHelper.RelativeDateOptions.ReplaceYesterdayAndTomorrow),
                    HealthInsuranceId = appointment.HealthInsuranceId,
                    HealthInsuranceName = this.db.HealthInsurances
                        .Where(hi => hi.Id == appointment.HealthInsuranceId)
                        .Select(hi => hi.Name)
                        .SingleOrDefault(),
                    Status = appointment.Status,
                };

            DoDateAndTimeValidation(viewModel, localNow, id);

            switch ((TypeAppointment)appointment.Type)
            {
                case TypeAppointment.GenericAppointment:
                    viewModel.IsGenericAppointment = true;
                    viewModel.Description = appointment.Description;
                    break;
                case TypeAppointment.MedicalAppointment:
                    viewModel.IsGenericAppointment = false;
                    viewModel.PatientNameLookup = appointment.Patient.Person.FullName;
                    viewModel.PatientId = appointment.PatientId;
                    break;
                default:
                    throw new Exception("Unsupported appointment type.");
            }

            this.ViewBag.IsEditingOrCreating = 'E';

            return this.View("Edit", viewModel);
        }
        public ActionResult Edit(AppointmentViewModel formModel)
        {
            // Custom model validation.
            if (formModel.IsGenericAppointment)
            {
                // This is a generic appointment, so we must clear validation for patient.
                this.ModelState.ClearPropertyErrors(() => formModel.PatientId);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientCode);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientName);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientNameLookup);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientGender);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientFirstAppointment);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientEmail);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientDateOfBirth);
                this.ModelState.ClearPropertyErrors(() => formModel.HealthInsuranceId);
                this.ModelState.ClearPropertyErrors(() => formModel.HealthInsuranceName);
            }
            else if (formModel.PatientFirstAppointment)
            {
                // This is a medical appointment, so we must clear validation for generic appointment.
                this.ModelState.ClearPropertyErrors(() => formModel.Description);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientId);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientNameLookup);
            }
            else
            {
                // This is a medical appointment, so we must clear validation for generic appointment.
                this.ModelState.ClearPropertyErrors(() => formModel.Description);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientCode);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientName);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientGender);
                this.ModelState.ClearPropertyErrors(() => formModel.PatientDateOfBirth);

                if (formModel.PatientId != null)
                {
                    var patient = this.db.Patients.FirstOrDefault(p => p.Id == formModel.PatientId);

                    if (patient == null)
                    {
                        this.ModelState.AddModelError<AppointmentViewModel>(
                            model => model.PatientNameLookup,
                            "O paciente informado não foi encontrado no banco de dados");
                    }
                    else if (patient.Person.FullName != formModel.PatientNameLookup)
                    {
                        this.ModelState.AddModelError<AppointmentViewModel>(
                            model => model.PatientNameLookup,
                            "O paciente informado foi encontrado mas o nome não coincide");
                    }
                }
            }

            // Verify if appoitment hours are consistent
            {
                DoDateAndTimeValidation(formModel, this.GetPracticeLocalNow(), formModel.Id);
            }

            // Verify if we're creating an appointment for the future with a Status set
            if (!string.IsNullOrEmpty(formModel.Start))
            {
                if (formModel.LocalDateTime + DateTimeHelper.GetTimeSpan(formModel.Start) > this.GetPracticeLocalNow())
                    if (formModel.Status != (int)TypeAppointmentStatus.Undefined)
                        ModelState.AddModelError<AppointmentViewModel>(
                            model => model.Status,
                            "Não é permitido determinar o Status para consultas agendadas para o futuro");
            }

            // Verify if the patient code is valid
            if (formModel.PatientFirstAppointment && formModel.PatientCode != null)
            {
                var patientCodeAsInt = default(int);
                int.TryParse(formModel.PatientCode, out patientCodeAsInt);
                if (patientCodeAsInt != default(int) && this.db.Patients.Any(p => p.Code == patientCodeAsInt))
                    this.ModelState.AddModelError<AppointmentViewModel>(
                        model => model.PatientCode, "O código do paciente informado pertence a outro paciente");
            }

            // Saving data if model is valid.
            if (this.ModelState.IsValid)
            {
                // Creating the appointment.
                Appointment appointment = null;

                if (formModel.Id == null)
                {
                    appointment = new Appointment
                        {
                            PracticeId = this.DbUser.PracticeId,
                            CreatedOn = this.UtcNowGetter(),
                            DoctorId = formModel.DoctorId,
                            CreatedById = this.DbUser.Id,
                        };
                    this.db.Appointments.AddObject(appointment);
                }
                else
                {
                    var currentUserPracticeId = this.DbUser.PracticeId;

                    appointment = this.db.Appointments
                        .Where(a => a.Id == formModel.Id).FirstOrDefault(a => a.Doctor.Users.FirstOrDefault().PracticeId == currentUserPracticeId);

                    // If the appointment does not exist, or does not belongs to the current practice,
                    // it should go to a view indicating that.
                    if (appointment == null)
                        return View("NotFound", formModel);
                }

                var appointmentStart = ConvertToUtcDateTime(this.DbPractice,
                    formModel.LocalDateTime + DateTimeHelper.GetTimeSpan(formModel.Start));

                var appointmentEnd = ConvertToUtcDateTime(this.DbPractice,
                    formModel.LocalDateTime + DateTimeHelper.GetTimeSpan(formModel.End));

                if (appointment.Start != appointmentStart)
                    appointment.Notified = false;

                appointment.Start = appointmentStart;
                appointment.End = appointmentEnd;

                appointment.Status = (int)formModel.Status;

                // Setting the appointment type and associated properties.
                // - generic appointment: has description, date and time interval
                // - medical appointment: has patient, date and time interval
                if (formModel.IsGenericAppointment)
                {
                    appointment.Description = formModel.Description;
                    appointment.Type = (int)TypeAppointment.GenericAppointment;
                }
                else
                {
                    appointment.Type = (int)TypeAppointment.MedicalAppointment;
                    appointment.HealthInsuranceId = formModel.HealthInsuranceId;

                    if (formModel.PatientFirstAppointment)
                    {
                        appointment.Patient = new Patient
                            {
                                Person =
                                    new Person
                                        {
                                            FullName = formModel.PatientName,
                                            Gender = (short)formModel.PatientGender,
                                            DateOfBirth =
                                                ConvertToUtcDateTime(this.DbPractice, formModel.PatientDateOfBirth.Value),
                                            PhoneCell = formModel.PatientPhoneCell,
                                            PhoneLand = formModel.PatientPhoneLand,
                                            CreatedOn = this.GetUtcNow(),
                                            PracticeId = this.DbUser.PracticeId,
                                            Email = formModel.PatientEmail,
                                            EmailGravatarHash = GravatarHelper.GetGravatarHash(formModel.PatientEmail)
                                        },
                                Code = int.Parse(formModel.PatientCode),
                                LastUsedHealthInsuranceId = formModel.HealthInsuranceId,
                                Doctor = this.Doctor,
                                PracticeId = this.DbUser.PracticeId,
                            };
                    }
                    else
                    {
                        var patient = this.db.Patients.Single(p => p.Id == formModel.PatientId);
                        patient.LastUsedHealthInsuranceId = formModel.HealthInsuranceId;
                        appointment.PatientId = formModel.PatientId.Value;
                    }
                }

                // Returning a JSON result, indicating what has happened.
                try
                {
                    this.db.SaveChanges();
                    return this.Json((dynamic)new { status = "success" }, JsonRequestBehavior.AllowGet);
                }
                catch (Exception ex)
                {
                    return this.Json((dynamic)new
                    {
                        status = "error",
                        text = "Não foi possível salvar a consulta. Erro inexperado",
                        details = ex.Message
                    }, JsonRequestBehavior.AllowGet);
                }
            }

            this.ViewBag.IsEditingOrCreating = this.RouteData.Values["action"].ToString()
                .ToLowerInvariant() == "edit" ? 'E' : 'C';

            return this.View("Edit", formModel);
        }
        public ActionResult Create(DateTime? date, string start, string end, int? patientId, bool? findNextAvailable)
        {
            // Note: remember that all DataTime parameter that comes from the client, are expressed in local practice time-zone.

            var localNow = this.GetPracticeLocalNow();

            if (date != null)
            {
                // The behavior for 'start' and 'end' parameters are different
                // depending on 'findNextAvailable' param, when 'date' is something:
                // - case false: start must have a valid time value, end is optional.
                if (findNextAvailable == true)
                {
                    // When 'start' is not specified, we set it to the begining of the day.
                    if (string.IsNullOrEmpty(start))
                        start = "00:00";
                }
                else if (string.IsNullOrEmpty(start))
                {
                    // If date has something, then start and end must also be null or empty.
                    this.ModelState.AddModelError<AppointmentViewModel>(
                        m => m.LocalDateTime,
                        "Ocorreu um erro nos parâmetros desta página.");

                    return this.View("Edit", new AppointmentViewModel());
                }
            }
            else
            {
                date = localNow;
                if (string.IsNullOrEmpty(start))
                    start = localNow.ToString("HH:mm");
            }

            var localDateAlone = date.Value.Date;

            //var slots = GetDaySlots(dateOnly, this.Doctor);
            var slotDuration = TimeSpan.FromMinutes(this.Doctor.CFG_Schedule.AppointmentTime);

            // Getting start date and time.
            var localStartTime =
                string.IsNullOrEmpty(start) ?
                localDateAlone :
                localDateAlone + DateTimeHelper.GetTimeSpan(start);

            // todo: just delete code or find a place for it?
            //FindNearestSlotStartTime(ref start, slots, ref startTime);

            // Getting end date and time.
            var localEndTime =
                string.IsNullOrEmpty(end) ?
                localStartTime + slotDuration :
                localDateAlone + DateTimeHelper.GetTimeSpan(end);

            if (localEndTime - localStartTime < slotDuration)
                localEndTime = localStartTime + slotDuration;

            // todo: just delete code or find a place for it?
            //FindNearestSlotEndTime(ref end, slots, ref endTime);

            // Find next available time slot.
            if (findNextAvailable == true)
            {
                var doctor = this.Doctor;

                // Determining the date and time to start scanning for a free time slot.
                var localStartingFrom = localStartTime;

                if (localNow > localStartingFrom)
                    localStartingFrom = localNow;

                // Finding the next available time slot, and setting the startTime and endTime.
                var slot = FindNextFreeTimeInPracticeLocalTime(this.db, doctor, localStartingFrom);
                localStartTime = slot.Item1;
                localEndTime = slot.Item2;
            }

            localDateAlone = localStartTime.Date;
            start = localStartTime.ToString("HH:mm");
            end = localEndTime.ToString("HH:mm");

            // Creating viewmodel.
            var viewModel = new AppointmentViewModel();

            var patient = this.db.Patients
                .Where(p => p.Id == patientId)
                .Select(p => new { p.Code, p.Person.FullName, p.LastUsedHealthInsuranceId })
                .FirstOrDefault();

            if (patient != null)
            {
                viewModel.PatientCode = patient.Code.HasValue ? patient.Code.Value.ToString("D6") : "000000";
                viewModel.PatientNameLookup = patient.FullName;
                viewModel.HealthInsuranceId = patient.LastUsedHealthInsuranceId;
                viewModel.HealthInsuranceName = this.db.HealthInsurances
                    .Where(hi => hi.Id == patient.LastUsedHealthInsuranceId)
                    .Select(hi => hi.Name)
                    .SingleOrDefault();
                viewModel.PatientId = patientId;
            }
            else
            {
                var currentLastCode = this.db.Patients.Max(p => p.Code);
                if (!currentLastCode.HasValue)
                    currentLastCode = 0;
                viewModel.PatientCode = (currentLastCode + 1).Value.ToString("D6");
            }

            viewModel.LocalDateTime = localDateAlone;
            viewModel.Start = start;
            viewModel.End = end;
            viewModel.DoctorId = this.Doctor.Id;
            viewModel.LocalDateTimeSpelled =
                DateTimeHelper.GetDayOfWeekAsString(localDateAlone) + ", "
                + DateTimeHelper.ConvertToRelative(
                    localDateAlone,
                    localNow,
                    DateTimeHelper.RelativeDateOptions.IncludePrefixes
                    | DateTimeHelper.RelativeDateOptions.IncludeSuffixes
                    | DateTimeHelper.RelativeDateOptions.ReplaceToday
                    | DateTimeHelper.RelativeDateOptions.ReplaceYesterdayAndTomorrow);

            this.DoDateAndTimeValidation(viewModel, localNow, null);

            this.ModelState.Clear();

            this.ViewBag.IsEditingOrCreating = 'C';

            return this.View("Edit", viewModel);
        }
 public ActionResult Create(AppointmentViewModel formModel)
 {
     return this.Edit(formModel);
 }
        private void DoDateAndTimeValidation(AppointmentViewModel viewModel, DateTime localNow, int? excludeAppointmentId)
        {
            if (viewModel.LocalDateTime != viewModel.LocalDateTime.Date)
                throw new ArgumentException("viewModel.Date must be the date alone, without time data.");

            var inconsistencyMessages = new ModelStateDictionary();
            if (!string.IsNullOrEmpty(viewModel.Start) && !string.IsNullOrEmpty(viewModel.End))
            {
                var startTimeLocal = viewModel.LocalDateTime + DateTimeHelper.GetTimeSpan(viewModel.Start);
                var endTimeLocal = viewModel.LocalDateTime + DateTimeHelper.GetTimeSpan(viewModel.End);

                var startTimeUtc = ConvertToUtcDateTime(this.DbPractice, startTimeLocal);
                var endTimeUtc = ConvertToUtcDateTime(this.DbPractice, endTimeLocal);

                var isTimeAvailable = IsTimeAvailableUtc(startTimeUtc, endTimeUtc, this.Doctor.Appointments, excludeAppointmentId);
                if (!isTimeAvailable)
                {
                    inconsistencyMessages.AddModelError(
                        () => viewModel.LocalDateTime,
                        "A data e hora já está marcada para outro compromisso.");
                }
            }

            // Setting the error message to display near the date and time configurations.
            var emptyErrors = new ModelErrorCollection();
            var errorsList = new List<ModelError>();
            errorsList.AddRange(this.ModelState.GetPropertyErrors(() => viewModel.LocalDateTime) ?? emptyErrors);
            errorsList.AddRange(this.ModelState.GetPropertyErrors(() => viewModel.Start) ?? emptyErrors);
            errorsList.AddRange(this.ModelState.GetPropertyErrors(() => viewModel.End) ?? emptyErrors);

            // Flag that tells whether the time and date are valid ot not.
            viewModel.DateAndTimeValidationState =
                errorsList.Any() ? DateAndTimeValidationState.Failed :
                !inconsistencyMessages.IsValid ? DateAndTimeValidationState.Warning :
                DateAndTimeValidationState.Passed;

            // Continue filling error list with warnings.
            errorsList.AddRange(inconsistencyMessages.GetPropertyErrors(() => viewModel.LocalDateTime) ?? emptyErrors);
            errorsList.AddRange(inconsistencyMessages.GetPropertyErrors(() => viewModel.Start) ?? emptyErrors);
            errorsList.AddRange(inconsistencyMessages.GetPropertyErrors(() => viewModel.End) ?? emptyErrors);
            if (errorsList.Any())
            {
                viewModel.TimeValidationMessage = errorsList.First().ErrorMessage;
            }
        }
        private Tuple<DateTime, DateTime> FindNextFreeValidTimeInPracticeLocalTime(Doctor doctor, DateTime localNow, DateTime localStartingFrom)
        {
            var localStartingFrom2 = localStartingFrom;

            while (true)
            {
                var slot = FindNextFreeTimeInPracticeLocalTime(this.db, doctor, localStartingFrom2);

                var vm = new AppointmentViewModel
                {
                    LocalDateTime = slot.Item1.Date,
                    Start = slot.Item1.ToString("HH:mm"),
                    End = slot.Item2.ToString("HH:mm"),
                };

                this.DoDateAndTimeValidation(vm, localNow, null);

                if (vm.DateAndTimeValidationState == DateAndTimeValidationState.Passed)
                {
                    return slot;
                }

                localStartingFrom2 = slot.Item2;
            }
        }
        public void Create_SaveAppointmentWhenStatusIsSetForTheFuture_NotAccomplished()
        {
            ScheduleController controller;
            AppointmentViewModel vm;

            try
            {
                // Creating practice, doctor and patient.
                var docAndre = Firestarter.Create_CrmMg_Psiquiatria_DrHouse_Andre(this.db);
                Firestarter.SetupDoctor(docAndre, this.db);
                var patient = Firestarter.CreateFakePatients(docAndre, this.db, 1)[0];
                patient.LastUsedHealthInsuranceId = null;
                this.db.SaveChanges();

                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(docAndre.Users.Single().Practice.WindowsTimeZoneId);
                var localNow = new DateTime(2012, 11, 09, 13, 00, 00, 000);
                var utcNow = TimeZoneInfo.ConvertTimeToUtc(localNow, timeZoneInfo);
                // the next free time from "now"
                var db2 = new CerebelloEntitiesAccessFilterWrapper(this.db);
                db2.SetCurrentUserById(docAndre.Users.Single().Id);

                // the reason for this .AddMinutes(1) is that FindNextFreeTimeInPracticeLocalTime returns now, when now is available
                // but now is not considered future, and I need a date in the future so the Status validation will fail
                var nextFreeTime = ScheduleController.FindNextFreeTimeInPracticeLocalTime(db2, docAndre, localNow.AddMinutes(1));

                // Creating Asp.Net Mvc mocks.
                var mr = new MockRepository(true);
                mr.SetRouteData_ConsultorioDrHouse_GregoryHouse(typeof(ScheduleController), "Create");
                controller = mr.CreateController<ScheduleController>();

                // Mocking 'Now' values.
                controller.UtcNowGetter = () => utcNow;

                // Setting view-model values to create a new appointment.
                vm = new AppointmentViewModel
                {
                    PatientId = patient.Id,
                    PatientNameLookup = patient.Person.FullName,
                    HealthInsuranceId = docAndre.HealthInsurances.First(hi => hi.IsActive).Id,
                    LocalDateTime = nextFreeTime.Item1.Date,
                    DoctorId = docAndre.Id,
                    Start = nextFreeTime.Item1.ToString("HH:mm"),
                    End = nextFreeTime.Item2.ToString("HH:mm"),
                    IsGenericAppointment = false,
                    // this has to generate an error because the appointment is in the future
                    Status = (int)TypeAppointmentStatus.NotAccomplished,
                };

                Mvc3TestHelper.SetModelStateErrors(controller, vm);
            }
            catch (Exception ex)
            {
                InconclusiveInit(ex);
                return;
            }

            var result = controller.Create(vm);

            Assert.IsFalse(controller.ModelState.IsValid);
            Assert.AreEqual(1, controller.ModelState["Status"].Errors.Count);
        }
        public void Create_SaveAppointmentOnLunch_HappyPath()
        {
            ScheduleController controller;
            bool isDbChanged = false;
            AppointmentViewModel vm;

            // Dates that will be used by this test.
            // - utcNow and localNow: used to mock Now values from Utc and User point of view.
            // - start and end: start and end time of the appointments that will be created.
            DateTime utcStart, utcEnd;
            var localNow = new DateTime(2012, 07, 19, 12, 00, 00, 000);

            // Setting Now to be on an thursday, mid day.
            // We know that Dr. House lunch time is from 12:00 until 13:00.
            var start = localNow.Date.AddDays(7).AddHours(12); // 2012-07-19 13:00
            var end = start.AddMinutes(30); // 2012-07-19 13:30

            try
            {
                // Creating practice and doctor.
                var docAndre = Firestarter.Create_CrmMg_Psiquiatria_DrHouse_Andre(this.db);
                Firestarter.SetupDoctor(docAndre, this.db);

                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(docAndre.Users.Single().Practice.WindowsTimeZoneId);
                DateTime utcNow = TimeZoneInfo.ConvertTimeToUtc(localNow, timeZoneInfo);
                utcStart = TimeZoneInfo.ConvertTimeToUtc(start, timeZoneInfo);
                utcEnd = TimeZoneInfo.ConvertTimeToUtc(end, timeZoneInfo);

                // Creating Asp.Net Mvc mocks.
                var mr = new MockRepository(true);
                mr.SetRouteData_ConsultorioDrHouse_GregoryHouse(typeof(ScheduleController), "Create");
                controller = mr.CreateController<ScheduleController>(
                    setupNewDb: db2 => db2.SavingChanges += (s, e) => { isDbChanged = true; });

                controller.UtcNowGetter = () => utcNow;

                // Setting view-model values to create a new appointment.
                // - this view-model must be valid for this test... if some day it becomes invalid,
                //   then it must be made valid again.
                vm = new AppointmentViewModel
                    {
                        Description = "Generic appointment on lunch time.",
                        LocalDateTime = start.Date,
                        DoctorId = docAndre.Id,
                        Start = start.ToString("HH:mm"),
                        End = end.ToString("HH:mm"),
                        IsGenericAppointment = true,
                        HealthInsuranceId = docAndre.HealthInsurances.First(hi => hi.IsActive).Id,
                    };

                Mvc3TestHelper.SetModelStateErrors(controller, vm);

                if (!controller.ModelState.IsValid)
                    throw new Exception("The given view-model must be valid for this test.");
            }
            catch (Exception ex)
            {
                InconclusiveInit(ex);
                return;
            }

            // View new appointment.
            // This must be ok, no exceptions, no validation errors.
            ActionResult actionResult;

            {
                actionResult = controller.Create(vm);
            }

            // Verifying the ActionResult.
            Assert.IsNotNull(actionResult, "The result of the controller method is null.");
            Assert.IsInstanceOfType(actionResult, typeof(JsonResult));
            var jsonResult = (JsonResult)actionResult;
            Assert.AreEqual("success", ((dynamic)jsonResult.Data).status);

            // Verifying the view-model.
            // todo: this should verify a mocked message, but cannot mock messages until languages are implemented.
            Assert.AreEqual(
                "A data e hora marcada está no horário de almoço do médico.",
                vm.TimeValidationMessage);
            Assert.AreEqual(DateAndTimeValidationState.Warning, vm.DateAndTimeValidationState);

            // Verifying the controller.
            Assert.AreEqual(controller.ViewBag.IsEditingOrCreating, null); // when JsonResult there must be no ViewBag
            Assert.IsTrue(controller.ModelState.IsValid, "ModelState is not valid.");

            // Verifying the DB.
            Assert.IsTrue(isDbChanged, "Create actions must change DB.");
            using (var db2 = DbTestBase.CreateNewCerebelloEntities())
            {
                var appointmentsCountAtSameTime = db2.Appointments
                    .Where(a => a.Start == utcStart).Count(a => a.End == utcEnd);

                Assert.AreEqual(1, appointmentsCountAtSameTime);
            }
        }
        public void Create_SaveWithEmptyFormModel()
        {
            ScheduleController controller;
            bool isDbChanged = false;
            AppointmentViewModel vm;

            var localNow = new DateTime(2012, 07, 19, 12, 00, 00, 000);

            // Setting Now to be on an thursday, mid day.
            // We know that Dr. House lunch time is from 12:00 until 13:00.

            try
            {
                // Creating practice and doctor.
                var docAndre = Firestarter.Create_CrmMg_Psiquiatria_DrHouse_Andre(this.db);
                Firestarter.SetupDoctor(docAndre, this.db);

                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(docAndre.Users.First().Practice.WindowsTimeZoneId);

                // Dates that will be used by this test.
                // - utcNow and localNow: used to mock Now values from Utc and User point of view.
                var utcNow = TimeZoneInfo.ConvertTimeToUtc(localNow, timeZoneInfo);

                // Creating Asp.Net Mvc mocks.
                var mr = new MockRepository(true);
                mr.SetRouteData_ConsultorioDrHouse_GregoryHouse(typeof(ScheduleController), "Create");
                controller = mr.CreateController<ScheduleController>(
                    setupNewDb: db2 => db2.SavingChanges += (s, e) => { isDbChanged = true; });

                controller.UtcNowGetter = () => utcNow;

                // Setting view-model values to create a new appointment.
                // - this view-model must be valid for this test... if some day it becomes invalid,
                //   then it must be made valid again.
                vm = new AppointmentViewModel();

                Mvc3TestHelper.SetModelStateErrors(controller, vm);
            }
            catch (Exception ex)
            {
                InconclusiveInit(ex);
                return;
            }

            // View new appointment.
            // This must be ok, no exceptions, no validation errors.
            ActionResult actionResult;

            {
                actionResult = controller.Create(vm);
            }

            // Verifying the ActionResult.
            Assert.IsNotNull(actionResult, "The result of the controller method is null.");

            // Verifying the view-model.
            Assert.AreEqual(DateAndTimeValidationState.Failed, vm.DateAndTimeValidationState);

            // Verifying the controller.
            Assert.AreEqual(controller.ViewBag.IsEditingOrCreating, 'C');
            Assert.IsInstanceOfType(controller.ViewBag.HealthInsuranceSelectItems, typeof(List<SelectListItem>));
            Assert.AreEqual(3, ((List<SelectListItem>)controller.ViewBag.HealthInsuranceSelectItems).Count);
            Assert.IsFalse(controller.ModelState.IsValid, "ModelState should be invalid.");

            // Verifying the DB.
            Assert.IsFalse(isDbChanged, "Create actions must not change DB when there is an error.");
        }
        public void Create_SaveAppointment_HappyPath()
        {
            ScheduleController controller;
            bool isDbChanged = false;
            AppointmentViewModel vm;

            // Dates that will be used by this test.
            // - utcNow and localNow: used to mock Now values from Utc and User point of view.
            // - start and end: start and end time of the appointments that will be created.
            DateTime utcStart, utcEnd;
            var localNow = new DateTime(2012, 11, 09, 13, 00, 00, 000);

            // We know that Dr. House works only after 13:00, so we need to set appointments after that.
            var start = localNow.Date.AddDays(+7).AddHours(13);
            var end = start.AddMinutes(30);

            Patient patient;
            try
            {
                // Creating practice, doctor and patient.
                var docAndre = Firestarter.Create_CrmMg_Psiquiatria_DrHouse_Andre(this.db);
                Firestarter.SetupDoctor(docAndre, this.db);
                patient = Firestarter.CreateFakePatients(docAndre, this.db, 1)[0];
                patient.LastUsedHealthInsuranceId = null;
                this.db.SaveChanges();

                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(docAndre.Users.Single().Practice.WindowsTimeZoneId);
                DateTime utcNow = TimeZoneInfo.ConvertTimeToUtc(localNow, timeZoneInfo);
                utcStart = TimeZoneInfo.ConvertTimeToUtc(start, timeZoneInfo);
                utcEnd = TimeZoneInfo.ConvertTimeToUtc(end, timeZoneInfo);

                // Creating Asp.Net Mvc mocks.
                var mr = new MockRepository(true);
                mr.SetRouteData_ConsultorioDrHouse_GregoryHouse(typeof(ScheduleController), "Create");
                controller = mr.CreateController<ScheduleController>(
                    setupNewDb: db2 => db2.SavingChanges += (s, e) => { isDbChanged = true; });

                // Mocking 'Now' values.
                controller.UtcNowGetter = () => utcNow;

                // Setting view-model values to create a new appointment.
                // - this view-model must be valid for this test... if some day it becomes invalid,
                //   then it must be made valid again.
                vm = new AppointmentViewModel
                {
                    PatientId = patient.Id,
                    PatientNameLookup = patient.Person.FullName,
                    HealthInsuranceId = docAndre.HealthInsurances.First(hi => hi.IsActive).Id,
                    LocalDateTime = start.Date,
                    DoctorId = docAndre.Id,
                    Start = start.ToString("HH:mm"),
                    End = end.ToString("HH:mm"),
                    IsGenericAppointment = false,
                    Status = (int)TypeAppointmentStatus.Undefined,
                };

                Mvc3TestHelper.SetModelStateErrors(controller, vm);
            }
            catch (Exception ex)
            {
                InconclusiveInit(ex);
                return;
            }

            // View new appointment.
            // This must be ok, no exceptions, no validation errors.
            ActionResult actionResult;

            {
                actionResult = controller.Create(vm);
            }

            // Verifying the ActionResult.
            Assert.IsNotNull(actionResult, "The result of the controller method is null.");
            Assert.IsInstanceOfType(actionResult, typeof(JsonResult));
            var jsonResult = (JsonResult)actionResult;
            Assert.AreEqual("success", ((dynamic)jsonResult.Data).status);

            // Verifying the view-model.
            // todo: this should verify a mocked message, but cannot mock messages until languages are implemented.
            Assert.AreEqual(
                null,
                vm.TimeValidationMessage);
            Assert.AreEqual(DateAndTimeValidationState.Passed, vm.DateAndTimeValidationState);

            // Verifying the controller.
            Assert.IsTrue(controller.ModelState.IsValid, "ModelState is not valid, but should be.");

            // Verifying the DB.
            Assert.IsTrue(isDbChanged, "Create actions must change DB.");
            using (var db2 = DbTestBase.CreateNewCerebelloEntities())
            {
                int appointmentsCountAtSameTime = db2.Appointments
                    .Count(a => a.Start == utcStart && a.End == utcEnd);

                Assert.AreEqual(1, appointmentsCountAtSameTime);

                var savedPatient = db2.Patients.Single(p => p.Id == patient.Id);
                Assert.AreEqual(vm.HealthInsuranceId, savedPatient.LastUsedHealthInsuranceId);

                var savedAppointment = savedPatient.Appointments.Single();
                Assert.AreEqual(vm.HealthInsuranceId, savedAppointment.HealthInsuranceId);
            }
        }
        public void Create_SaveAppointmentWhenStatusIsSetForThePast_NotAccomplished()
        {
            ScheduleController controller;
            AppointmentViewModel vm;

            try
            {
                // Creating practice, doctor and patient.
                var docAndre = Firestarter.Create_CrmMg_Psiquiatria_DrHouse_Andre(this.db);
                Firestarter.SetupDoctor(docAndre, this.db);
                var patient = Firestarter.CreateFakePatients(docAndre, this.db, 1)[0];
                patient.LastUsedHealthInsuranceId = null;
                this.db.SaveChanges();

                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(docAndre.Users.Single().Practice.WindowsTimeZoneId);
                var localNow = new DateTime(2012, 11, 09, 13, 00, 00, 000);
                var utcNow = TimeZoneInfo.ConvertTimeToUtc(localNow, timeZoneInfo);
                // finds a free time in the past week.
                var db2 = new CerebelloEntitiesAccessFilterWrapper(this.db);
                db2.SetCurrentUserById(docAndre.Users.Single().Id);
                var freeTimeInPastWeek = ScheduleController.FindNextFreeTimeInPracticeLocalTime(db2, docAndre, localNow.AddDays(-7));

                // Creating Asp.Net Mvc mocks.
                var mr = new MockRepository(true);
                mr.SetRouteData_ConsultorioDrHouse_GregoryHouse(typeof(ScheduleController), "Create");
                controller = mr.CreateController<ScheduleController>();

                // Mocking 'Now' values.
                controller.UtcNowGetter = () => utcNow;

                // Setting view-model values to create a new appointment.
                vm = new AppointmentViewModel
                {
                    PatientId = patient.Id,
                    PatientNameLookup = patient.Person.FullName,
                    HealthInsuranceId = docAndre.HealthInsurances.First(hi => hi.IsActive).Id,
                    LocalDateTime = freeTimeInPastWeek.Item1.Date,
                    DoctorId = docAndre.Id,
                    Start = freeTimeInPastWeek.Item1.ToString("HH:mm"),
                    End = freeTimeInPastWeek.Item2.ToString("HH:mm"),
                    IsGenericAppointment = false,
                    // this should work because it's in the past
                    Status = (int)TypeAppointmentStatus.NotAccomplished,
                };

                Mvc3TestHelper.SetModelStateErrors(controller, vm);
            }
            catch (Exception ex)
            {
                InconclusiveInit(ex);
                return;
            }

            controller.Create(vm);

            Assert.IsTrue(controller.ModelState.IsValid);
        }