/// <summary>
        /// Desassociates a Google Drive account
        /// </summary>
        /// <returns></returns>
        public ActionResult DesassociateGoogleDrive()
        {
            var dbGoogleAccountInfo = this.db.GoogleUserAccountInfo.FirstOrDefault();
            if (dbGoogleAccountInfo != null)
                this.db.GoogleUserAccountInfo.DeleteObject(dbGoogleAccountInfo);

            var dbNotification = new Notification
                {
                    CreatedOn = this.GetUtcNow(),
                    PracticeId = this.DbPractice.Id,
                    UserToId = this.DbUser.Id,
                    Type = NotificationConstants.GOOGLE_DRIVE_DESASSOCIATED_NOTIFICATION_TYPE
                };

            this.db.Notifications.AddObject(dbNotification);

            // mark all patients as not backed up
            foreach (var patient in this.db.Patients)
                patient.IsBackedUp = false;

            this.db.SaveChanges();

            return this.RedirectToAction("Index");
        }
 /// <summary>
 /// Deprecated Method for adding a new object to the Notifications EntitySet. Consider using the .Add method of the associated ObjectSet&lt;T&gt; property instead.
 /// </summary>
 public void AddToNotifications(Notification notification)
 {
     base.AddObject("Notifications", notification);
 }
Exemplo n.º 3
0
        public void PatientArrived(int appointmentId, string time)
        {
            var appointment = this.db.Appointments.FirstOrDefault(p => p.Id == appointmentId);
            if (appointment == null)
                return;

            appointment.Status = (int)TypeAppointmentStatus.Accomplished;

            Debug.Assert(appointment.PatientId != null, "appointment.PatientId != null");
            var notificationData = new PatientArrivedNotificationData()
                {
                    PatientId = appointment.PatientId.Value,
                    PatientName = appointment.Patient.Person.FullName,
                    Time = time,
                    PracticeIdentifier = appointment.Practice.UrlIdentifier,
                    DoctorIdentifier = appointment.Doctor.UrlIdentifier
                };

            var notificationDataString = new JavaScriptSerializer().Serialize(notificationData);

            var newNotification = new Notification()
                {
                    CreatedOn = DateTime.UtcNow,
                    IsClosed = false,
                    UserToId = appointment.DoctorId,
                    Type = NotificationConstants.PATIENT_ARRIVED_NOTIFICATION_TYPE,
                    PracticeId = appointment.PracticeId,
                    Data = notificationDataString
                };

            this.db.Notifications.AddObject(newNotification);

            this.db.SaveChanges();

            BroadcastDbNotification(newNotification, notificationData);
        }
 /// <summary>
 /// Create a new Notification object.
 /// </summary>
 /// <param name="id">Initial value of the Id property.</param>
 /// <param name="createdOn">Initial value of the CreatedOn property.</param>
 /// <param name="practiceId">Initial value of the PracticeId property.</param>
 /// <param name="isClosed">Initial value of the IsClosed property.</param>
 /// <param name="type">Initial value of the Type property.</param>
 /// <param name="userToId">Initial value of the UserToId property.</param>
 public static Notification CreateNotification(global::System.Int32 id, global::System.DateTime createdOn, global::System.Int32 practiceId, global::System.Boolean isClosed, global::System.String type, global::System.Int32 userToId)
 {
     Notification notification = new Notification();
     notification.Id = id;
     notification.CreatedOn = createdOn;
     notification.PracticeId = practiceId;
     notification.IsClosed = isClosed;
     notification.Type = type;
     notification.UserToId = userToId;
     return notification;
 }
        public ActionResult CreateAccount(CreateAccountViewModel registrationData)
        {
            // If the user being edited is a medic, then we must check the fields that are required for medics.
            if (!registrationData.IsDoctor)
            {
                // Removing validation error of medic properties, because this user is not a medic.
                this.ModelState.ClearPropertyErrors(() => registrationData.MedicCRM);
                this.ModelState.ClearPropertyErrors(() => registrationData.MedicalEntityId);
                this.ModelState.ClearPropertyErrors(() => registrationData.MedicalSpecialtyId);
                this.ModelState.ClearPropertyErrors(() => registrationData.MedicalSpecialtyName);
                this.ModelState.ClearPropertyErrors(() => registrationData.MedicalEntityJurisdiction);
            }

            if (this.ModelState.Remove(e => e.ErrorMessage.Contains("requerido")))
                this.ModelState.AddModelError("MultipleItems", "É necessário preencher todos os campos.");

            // Normalizing name properties.
            if (!string.IsNullOrEmpty(registrationData.PracticeName))
                registrationData.PracticeName = Regex.Replace(registrationData.PracticeName, @"\s+", " ").Trim();

            if (!string.IsNullOrEmpty(registrationData.FullName))
                registrationData.FullName = Regex.Replace(registrationData.FullName, @"\s+", " ").Trim();

            var urlPracticeId = StringHelper.GenerateUrlIdentifier(registrationData.PracticeName);

            var subscription = StringHelper.FirstNonEmpty(registrationData.Subscription, "trial");
            if (!validSubscriptions.Contains(subscription) || registrationData.AsTrial == true)
                subscription = "trial";

            // Note: Url identifier for the name of the user, don't need any verification.
            // The name of the user must be unique inside a practice, not the entire database.

            var alreadyLoggedUser = this.User as AuthenticatedPrincipal;

            Practice practiceToReuse = null;
            if (alreadyLoggedUser != null)
            {
                practiceToReuse = this.db.Practices
                    .SingleOrDefault(p => p.UrlIdentifier == alreadyLoggedUser.Profile.PracticeIdentifier
                        && p.AccountContract.IsPartialBillingInfo);
            }

            var alreadyExistingPractice = this.db.Practices.SingleOrDefault(p => p.UrlIdentifier == urlPracticeId);
            if (alreadyExistingPractice != null)
            {
                if (alreadyExistingPractice.AccountContract != null && alreadyExistingPractice.AccountContract.IsPartialBillingInfo)
                    practiceToReuse = practiceToReuse ?? alreadyExistingPractice;
                else
                {
                    // killing practice that already exists if it expires, and freeing the name for a new practice
                    if (alreadyExistingPractice.AccountExpiryDate < this.GetUtcNow())
                    {
                        alreadyExistingPractice.AccountDisabled = true;
                        alreadyExistingPractice.UrlIdentifier += " !expired"; // change this, so that a new practice with this name can be created.
                        this.db.SaveChanges();
                    }
                    else
                    {
                        this.ModelState.AddModelError(
                            () => registrationData.PracticeName,
                            "Nome de consultório já está em uso.");
                    }
                }
            }

            var utcNow = this.GetUtcNow();

            // Creating the new user.
            User user;
            if (practiceToReuse == null)
            {
                var result = SecurityManager.CreateUser(out user, registrationData, this.db.Users, utcNow, null);

                if (result == CreateUserResult.InvalidUserNameOrPassword)
                {
                    // Note: nothing to do because user-name and password fields are already validated.
                }

                if (result == CreateUserResult.UserNameAlreadyInUse)
                {
                    this.ModelState.AddModelError(
                        () => registrationData.UserName,
                        // Todo: this message is also used in the App -> UsersController.
                        "O nome de usuário não pode ser registrado pois já está em uso. "
                        + "Note que nomes de usuário diferenciados por acentos, "
                        + "maiúsculas/minúsculas ou por '.', '-' ou '_' não são permitidos."
                        + "(Não é possível cadastrar 'MiguelAngelo' e 'miguel.angelo' no mesmo consultório.");
                }
            }
            else
            {
                user = this.db.Users.SingleOrDefault(u => u.Id == alreadyLoggedUser.Profile.Id && u.PracticeId == practiceToReuse.Id);
                SecurityManager.UpdateUser(user, registrationData, this.db.Users, utcNow);
            }

            if (user != null)
            {
                string timeZoneId = null;
                if (registrationData.PracticeProvince != null)
                    timeZoneId = TimeZoneDataAttribute.GetAttributeFromEnumValue((TypeEstadoBrasileiro)registrationData.PracticeProvince.Value).Id;

                user.Practice = practiceToReuse ?? new Practice();
                user.Practice.Name = registrationData.PracticeName;
                user.Practice.UrlIdentifier = urlPracticeId;
                user.Practice.CreatedOn = utcNow;
                user.Practice.WindowsTimeZoneId = timeZoneId;
                user.Practice.Province = registrationData.PracticeProvince;
                user.Practice.PhoneMain = registrationData.PracticePhone;

                // Setting the BirthDate of the user as a person.
                user.Person.DateOfBirth = PracticeController.ConvertToUtcDateTime(user.Practice, registrationData.DateOfBirth ?? new DateTime());

                user.IsOwner = true;

                if (user.Administrator == null)
                    user.Administrator = new Administrator { };

                bool isNewDoctor = false;
                // when the user is a doctor, we need to fill the properties of the doctor
                if (registrationData.IsDoctor)
                {
                    // if user is already a doctor, we just edit the properties
                    // otherwise we create a new doctor instance
                    if (user.Doctor == null)
                    {
                        user.Doctor = new Doctor();
                        isNewDoctor = true;
                    }

                    user.Doctor.CRM = registrationData.MedicCRM;

                    if (registrationData.MedicalSpecialtyId != null)
                    {
                        var ms = this.db.SYS_MedicalSpecialty
                                     .Single(ms1 => ms1.Id == registrationData.MedicalSpecialtyId);
                        user.Doctor.MedicalSpecialtyCode = ms.Code;
                        user.Doctor.MedicalSpecialtyName = ms.Name;
                    }

                    if (registrationData.MedicalEntityId != null)
                    {
                        var me = this.db.SYS_MedicalEntity
                                     .Single(me1 => me1.Id == registrationData.MedicalEntityId);
                        user.Doctor.MedicalEntityCode = me.Code;
                        user.Doctor.MedicalEntityName = me.Name;
                    }

                    user.Doctor.MedicalEntityJurisdiction = ((TypeEstadoBrasileiro)registrationData.MedicalEntityJurisdiction).ToString();

                    // Creating an unique UrlIdentifier for this doctor.
                    // This is the first doctor, so there will be no conflicts.
                    var urlId = UsersController.GetUniqueDoctorUrlId(this.db.Doctors, registrationData.FullName, null);
                    if (urlId == null)
                    {
                        this.ModelState.AddModelError(
                            () => registrationData.FullName,
                            // Todo: this message is also used in the UserController.
                            "Quantidade máxima de homônimos excedida.");
                    }

                    user.Doctor.UrlIdentifier = urlId;
                }
                else
                {
                    // todo: create a program that clears all orphaned Doctor objects
                    user.Doctor = null;
                }

                if (practiceToReuse == null)
                    this.db.Users.AddObject(user);

                if (this.ModelState.IsValid)
                {
                    MailMessage emailMessageToUser = null;
                    if (subscription == "trial")
                    {
                        // Creating confirmation email, with a token.
                        emailMessageToUser = this.EmailMessageToUser(user, utcNow, subscription == "trial");

                        // Sending e-mail to tell us the good news.
                        this.SendAccountCreatedSelfEmail(registrationData, user);
                    }

                    // If the ModelState is still valid, then save objects to the database,
                    // and send confirmation email message to the user.
                    using (emailMessageToUser)
                    {
                        // Saving changes to the DB.
                        this.db.SaveChanges();

                        if (subscription == "trial")
                        {
                            // Creating a new trial account contract.
                            var contract = user.Practice.AccountContract ?? new AccountContract();
                            contract.Practice = user.Practice;

                            contract.ContractTypeId = (int)ContractTypes.TrialContract;
                            contract.IsTrial = true;
                            contract.IssuanceDate = utcNow;
                            contract.StartDate = utcNow;
                            contract.EndDate = null; // indeterminated
                            contract.CustomText = null;

                            contract.DoctorsLimit = null;
                            contract.PatientsLimit = 50; // fixed limit for trial account

                            // no billings
                            contract.BillingAmount = null;
                            contract.BillingDueDay = null;
                            contract.BillingPaymentMethod = null;
                            contract.BillingPeriodCount = null;
                            contract.BillingPeriodSize = null;
                            contract.BillingPeriodType = null;
                            contract.BillingDiscountAmount = null;

                            user.Practice.AccountExpiryDate = utcNow.AddHours(Constants.MAX_HOURS_TO_VERIFY_TRIAL_ACCOUNT);
                            user.Practice.AccountContract = contract;

                            if (practiceToReuse == null)
                                this.db.AccountContracts.AddObject(contract);
                        }
                        else
                        {
                            // Creating a new account contract, getting info from the subscription string.
                            var dicData = new Dictionary<string, dynamic>(StringComparer.InvariantCultureIgnoreCase)
                                {
                                    { "1M", new { Price = Bus.Pro.PRICE_MONTH, PeriodSize = 1 } },
                                    { "3M", new { Price = Bus.Pro.PRICE_QUARTER, PeriodSize = 3 } },
                                    { "6M", new { Price = Bus.Pro.PRICE_SEMESTER, PeriodSize = 6 } },
                                    { "12M", new { Price = Bus.Pro.PRICE_YEAR, PeriodSize = 12 } }
                                };

                            var data = dicData[subscription];

                            var contract = user.Practice.AccountContract ?? new AccountContract();
                            contract.Practice = user.Practice;

                            contract.ContractTypeId = (int)ContractTypes.ProfessionalContract;
                            contract.IsTrial = false;
                            contract.IssuanceDate = utcNow;
                            contract.StartDate = null; // inderterminated (will be defined when user pays)
                            contract.EndDate = null; // indeterminated
                            contract.CustomText = null;

                            contract.DoctorsLimit = null; // unknown at this moment (will be defined after user fills payment info)
                            contract.PatientsLimit = null; // fixed limit for trial account

                            // billings data can be redefined when the user fills payment info
                            // for now these are the default values
                            contract.IsPartialBillingInfo = true; // indicates that the billing info for this contract must be defined by the user
                            contract.BillingAmount = Bus.Pro.PRICE_MONTH * (int)data.PeriodSize;
                            contract.BillingDueDay = null; // payment method has no default (will be defined in the payment-info step)
                            contract.BillingPaymentMethod = null; // payment method has no default (will be defined in the payment-info step)
                            contract.BillingPeriodCount = null;
                            contract.BillingPeriodSize = data.PeriodSize;
                            contract.BillingPeriodType = "M";
                            contract.BillingDiscountAmount = (Bus.Pro.PRICE_MONTH * (int)data.PeriodSize) - data.Price;

                            user.Practice.AccountExpiryDate = utcNow + Constants.MaxTimeToVerifyProfessionalAccount;
                            user.Practice.AccountContract = contract;

                            if (practiceToReuse == null)
                                this.db.AccountContracts.AddObject(contract);
                        }

                        this.db.SaveChanges();

                        // if the new user is a doctor, create some other useful things
                        // like some medical-certificates and a default health-insurance
                        if (isNewDoctor)
                            BusHelper.FillNewDoctorUtilityBelt(user.Doctor);

                        if (practiceToReuse == null)
                        {
                            // adding message to the user so that he/she completes his/her profile informations
                            // todo: add complete profile notification
                            // If practice is being reused then notification was already sent.
                            var notificationData = new CompletePracticeInfoNotificationData();
                            var notificationDataString = new JavaScriptSerializer().Serialize(notificationData);
                            var dbNotification = new Notification
                                {
                                    CreatedOn = this.GetUtcNow(),
                                    PracticeId = user.PracticeId,
                                    Data = notificationDataString,
                                    UserToId = user.Id,
                                    Type = NotificationConstants.COMPLETE_INFO_NOTIFICATION_TYPE,
                                };
                            this.db.Notifications.AddObject(dbNotification);
                            NotificationsHub.BroadcastDbNotification(dbNotification, notificationData);
                        }

                        if (practiceToReuse == null)
                        {
                            // If practice is being reused then these values were already set.
                            user.Practice.Owner = user;
                            user.Person.PracticeId = user.PracticeId;
                            user.Administrator.PracticeId = user.PracticeId;
                            if (user.Doctor != null)
                                user.Doctor.PracticeId = user.PracticeId;
                        }

                        this.db.SaveChanges();

                        // Sending the confirmation e-mail to the new user.
                        // This must be synchronous.
                        // If practice is being reused then an email was already sent.
                        if (practiceToReuse == null && emailMessageToUser != null)
                            this.TrySendEmail(emailMessageToUser);

                        // Log the user in.
                        var loginModel = new LoginViewModel
                        {
                            Password = registrationData.Password,
                            PracticeIdentifier = user.Practice.UrlIdentifier,
                            RememberMe = false,
                            UserNameOrEmail = registrationData.UserName,
                        };

                        if (!SecurityManager.Login(this.HttpContext.Response.Cookies, loginModel, this.db.Users, out user, this.GetUtcNow()))
                        {
                            throw new Exception("Login cannot fail.");
                        }

                        if (subscription == "trial")
                            return this.RedirectToAction("CreateAccountCompleted", new { practice = user.Practice.UrlIdentifier });
                        else
                            return this.RedirectToAction("SetAccountPaymentInfo", new { practice = user.Practice.UrlIdentifier });
                    }
                }
            }

            this.ViewBag.MedicalSpecialtyOptions =
                this.db.SYS_MedicalSpecialty
                .ToList()
                .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name })
                .ToList();

            this.ViewBag.MedicalEntityOptions =
                this.db.SYS_MedicalEntity
                .ToList()
                .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name })
                .ToList();

            return this.View(registrationData);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Generates notifications for new Medical Appointments
        /// </summary>
        private static void GenerateNotificationsForNewGenericAppointments(
            [NotNull] CerebelloEntities db, DateTime referenceTime, [NotNull] ICollection<Tuple<Notification, object>> notificationsToBeDispatched)
        {
            if (db == null) throw new ArgumentNullException("db");
            if (notificationsToBeDispatched == null) throw new ArgumentNullException("notificationsToBeDispatched");

            // check for appointments that have to be notified
            var timeOffset = referenceTime.AddMinutes(30);
            var unnotifiedAppointments =
                db.Appointments.Where(
                    a => !a.Notified && a.Type == (int)TypeAppointment.GenericAppointment && a.Start >= referenceTime && a.Start < timeOffset).ToList();
            foreach (var appointment in unnotifiedAppointments)
            {
                Debug.Assert(appointment.PatientId != null, "appointment.PatientId != null");

                var genericAppointmentData = new GenericAppointmentNotificationData()
                {
                    Text = appointment.Description,
                    Time = DateTimeHelper.GetFormattedTime(
                        PracticeController.ConvertToLocalDateTime(appointment.Practice, appointment.Start))
                };

                var genericAppointmentDataString = new JavaScriptSerializer().Serialize(genericAppointmentData);

                // notify the doctor
                var newNotification = new Notification()
                {
                    CreatedOn = referenceTime,
                    IsClosed = false,
                    UserToId = appointment.DoctorId,
                    Type = NotificationConstants.GENERIC_APPOINTMENT_NOTIFICATION_TYPE,
                    PracticeId = appointment.PracticeId,
                    Data = genericAppointmentDataString
                };

                appointment.Doctor.Users.First().Notifications.Add(newNotification);
                notificationsToBeDispatched.Add(new Tuple<Notification, object>(newNotification, genericAppointmentData));

                appointment.Notified = true;
            }
            db.SaveChanges();
        }
Exemplo n.º 7
0
        /// <summary>
        /// Generates notifications for new Medical Appointments
        /// </summary>
        private static void GenerateNotificationsForNewMedicalAppointments(
            [NotNull] CerebelloEntities db, DateTime referenceTime, [NotNull] ICollection<Tuple<Notification, object>> notificationsToBeDispatched)
        {
            if (db == null) throw new ArgumentNullException("db");
            if (notificationsToBeDispatched == null) throw new ArgumentNullException("notificationsToBeDispatched");

            // check for appointments that have to be notified
            var timeOffset = referenceTime.AddMinutes(10);
            var unnotifiedAppointments =
                db.Appointments.Where(
                    a => !a.Notified && a.Type == (int)TypeAppointment.MedicalAppointment && a.Start >= referenceTime && a.Start < timeOffset).ToList();
            foreach (var appointment in unnotifiedAppointments)
            {
                Debug.Assert(appointment.PatientId != null, "appointment.PatientId != null");

                var medicalAppointmentData = new MedicalAppointmentNotificationData
                {
                    PatientId = appointment.PatientId.Value,
                    PatientName = appointment.Patient.Person.FullName,
                    DoctorName = appointment.Patient.Doctor.Users.First().Person.FullName,
                    DoctorId = appointment.Patient.DoctorId,
                    AppointmentId = appointment.Id,
                    Time = DateTimeHelper.GetFormattedTime(
                        PracticeController.ConvertToLocalDateTime(appointment.Practice, appointment.Start)),
                    PracticeIdentifier = appointment.Practice.UrlIdentifier,
                    DoctorIdentifier = appointment.Doctor.UrlIdentifier
                };

                var medicalAppointmentDataString = new JavaScriptSerializer().Serialize(medicalAppointmentData);

                // for each secretary, I need to create a new notification
                foreach (var user in appointment.Practice.Users.Where(user => user.Secretary != null))
                {
                    var newNotification = new Notification()
                    {
                        CreatedOn = referenceTime,
                        IsClosed = false,
                        UserToId = user.Id,
                        Type = NotificationConstants.MEDICAL_APPOINTMENT_NOTIFICATION_TYPE,
                        PracticeId = appointment.PracticeId,
                        Data = medicalAppointmentDataString
                    };

                    user.Notifications.Add(newNotification);
                    notificationsToBeDispatched.Add(new Tuple<Notification, object>(newNotification, medicalAppointmentData));
                }

                appointment.Notified = true;
            }
            db.SaveChanges();
        }
Exemplo n.º 8
0
        public ActionResult Edit(UserViewModel formModel)
        {
            var isEditingOrCreating = formModel.Id != null ? 'E' : 'C';

            this.ViewBag.IsEditingOrCreating = isEditingOrCreating;

            var utcNow = this.GetUtcNow();

            User user;

            // Normalizing the name of the person.
            if (!string.IsNullOrEmpty(formModel.FullName))
                formModel.FullName = Regex.Replace(formModel.FullName, @"\s+", " ").Trim();

            if (isEditingOrCreating == 'E')
            {
                // Note: User name cannot be edited, and should not be validated.
                this.ModelState.ClearPropertyErrors(() => formModel.UserName);

                user = this.db.Users.First(p => p.Id == formModel.Id);

                // TODO: suggest that r# use the attribute EdmScalarPropertyAttribute(IsNullable=false)
                // as a way to determine if a property can ever receive a null value or not
                // there was a bug in the line inside the following if, that could be detected by r# if it did consider that attribute.
                if (!string.IsNullOrWhiteSpace(formModel.FullName))
                    user.Person.FullName = formModel.FullName;

                user.Person.Gender = (short)formModel.Gender;

                // If there are model errors, we must return original user name to the view.
                formModel.UserName = user.UserName;
            }
            else
            {
                // UserName must not be null nor empty.
                if (string.IsNullOrWhiteSpace(formModel.UserName))
                {
                    this.ModelState.AddModelError(() => formModel.UserName, "Nome de usuário inválido.");
                }

                var loggedUser = this.DbUser;

                // Checking doctors limit of this account.
                if (formModel.IsDoctor)
                {
                    var doctorsCount = this.DbPractice.Users.Count(u => u.DoctorId != null);
                    if (doctorsCount >= this.DbPractice.AccountContract.DoctorsLimit)
                        this.ModelState.AddModelError(
                            "DoctorsLimit",
                            "Essa conta está configurada para suportar até {0} médicos.",
                            this.DbPractice.AccountContract.DoctorsLimit);
                }

                // Looking for another user with the same UserName or Email.
                var conflictingData = this.db.Users
                    .Where(u => u.PracticeId == loggedUser.PracticeId)
                    .Where(u => u.UserName == formModel.UserName || u.Person.Email == formModel.Email)
                    .Select(u => new { u.UserName, u.Person.Email })
                    .ToList();

                // Verifying wich fields are conflicting: Email.
            #warning [Validate] Must validate all emails.
                bool emailConflict = conflictingData.Any(c => c.Email == formModel.Email);

                // For every new user we must create a login, with a common
                // password used the first time the person logs in.
                // The only action allowed with this password,
                // is to change the password.
                var userData = new CreateAccountViewModel
                {
                    UserName = formModel.UserName,
                    Password = Constants.DEFAULT_PASSWORD,
                    ConfirmPassword = Constants.DEFAULT_PASSWORD,
                    EMail = formModel.Email,
                    FullName = formModel.FullName,
                    Gender = (short)formModel.Gender,
                };

                // Creating the new user.
                // The user belongs to the same practice as the logged user.
                var result = SecurityManager.CreateUser(out user, userData, this.db.Users, utcNow, loggedUser.PracticeId);

                if (result == CreateUserResult.UserNameAlreadyInUse)
                {
                    this.ModelState.AddModelError(
                        () => formModel.UserName,
                        // Todo: this message is also used in the AuthenticationController.
                        "O nome de usuário não pode ser registrado pois já está em uso. "
                        + "Note que nomes de usuário diferenciados por acentos, "
                        + "maiúsculas/minúsculas ou por '.', '-' ou '_' não são permitidos."
                        + "(Não é possível cadastrar 'MiguelAngelo' e 'miguel.angelo' no mesmo consultório.");
                }
            }

            #warning Must validade all emails, cannot repeat emails in the same practice.

            if (!formModel.IsDoctor && !formModel.IsAdministrador && !formModel.IsSecretary)
                this.ModelState.AddModelError("", "Usuário tem que ter pelo menos uma função: médico, administrador ou secretária.");

            // If the user being edited is a doctor, then we must check the fields that are required for medics.
            if (!formModel.IsDoctor)
            {
                // Removing validation error of medic properties, because this user is not a medic.
                this.ModelState.ClearPropertyErrors(() => formModel.MedicCRM);
                this.ModelState.ClearPropertyErrors(() => formModel.MedicalEntityId);
                this.ModelState.ClearPropertyErrors(() => formModel.MedicalSpecialtyId);
                this.ModelState.ClearPropertyErrors(() => formModel.MedicalSpecialtyName);
                this.ModelState.ClearPropertyErrors(() => formModel.MedicalEntityJurisdiction);
            }

            if (user != null)
            {
                if (formModel.DateOfBirth.HasValue)
                    user.Person.DateOfBirth = ConvertToUtcDateTime(this.DbPractice, formModel.DateOfBirth.Value);
                user.Person.BirthPlace = formModel.BirthPlace;
                user.Person.CPF = formModel.Cpf;
                user.Person.CreatedOn = this.GetUtcNow();
                user.Person.MaritalStatus = formModel.MaritalStatus;
                user.Person.Profession = formModel.Profissao;
                user.Person.Email = formModel.Email;
                user.Person.EmailGravatarHash = GravatarHelper.GetGravatarHash(formModel.Email);

                // handle address
                if (!user.Person.Addresses.Any())
                    user.Person.Addresses.Add(
                        new Address
                            {
                                PracticeId = this.DbUser.PracticeId,
                                CEP = formModel.Address.CEP,
                                City = formModel.Address.City,
                                Complement = formModel.Address.Complement,
                                Neighborhood = formModel.Address.Neighborhood,
                                StateProvince = formModel.Address.StateProvince,
                                Street = formModel.Address.Street,
                            });

                // when the user is a doctor, we need to fill the properties of the doctor
                if (formModel.IsDoctor)
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                    {
                        // if user is already a doctor, we just edit the properties
                        // otherwise we create a new doctor instance
                        if (user.Doctor == null)
                        {
                            user.Doctor = new Doctor { PracticeId = this.DbUser.PracticeId, };
                            BusHelper.FillNewDoctorUtilityBelt(user.Doctor);
                        }
                    }

                    // Changing the doctor's informations.
                    if (!string.IsNullOrWhiteSpace(formModel.MedicCRM))
                        user.Doctor.CRM = formModel.MedicCRM;

                    if (formModel.MedicalEntityId != null)
                    {
                        var me = this.db.SYS_MedicalEntity.First(me1 => me1.Id == formModel.MedicalEntityId);
                        user.Doctor.MedicalEntityCode = me.Code;
                        user.Doctor.MedicalEntityName = me.Name;
                    }

                    if (formModel.MedicalSpecialtyId != null)
                    {
                        var ms = this.db.SYS_MedicalSpecialty.First(ms1 => ms1.Id == formModel.MedicalSpecialtyId);
                        user.Doctor.MedicalSpecialtyCode = ms.Code;
                        user.Doctor.MedicalSpecialtyName = ms.Name;
                    }

                    if (formModel.MedicalEntityJurisdiction != null)
                        user.Doctor.MedicalEntityJurisdiction =
                            ((TypeEstadoBrasileiro)formModel.MedicalEntityJurisdiction.Value).ToString();

                    // Creating an unique UrlIdentifier for this doctor.
                    // This does not consider UrlIdentifier's used by other kinds of objects.
                    string urlId = GetUniqueDoctorUrlId(this.db.Doctors, formModel.FullName, this.DbPractice.Id);
                    if (urlId == null && !string.IsNullOrWhiteSpace(formModel.FullName))
                    {
                        this.ModelState.AddModelError(
                            () => formModel.FullName,
                            // Todo: this message is also used in the AuthenticationController.
                            string.Format("Quantidade máxima de homônimos excedida para esta conta: {0}.", this.DbPractice.UrlIdentifier));
                    }

                    if (!string.IsNullOrWhiteSpace(urlId))
                        user.Doctor.UrlIdentifier = urlId;
                }
                else
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                    {
                        if (user.Doctor != null)
                            this.db.Doctors.DeleteObject(user.Doctor);

                        // if the user is not a doctor, then we make sure
                        // by assigning the doctor property to null
                        user.Doctor = null;
                    }
                }

                // when the user is an administrator
                if (formModel.IsAdministrador)
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                        if (user.Administrator == null)
                            user.Administrator = new Administrator { PracticeId = this.DbUser.PracticeId, };
                }
                else
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                    {
                        if (user.Administrator != null)
                            this.db.Administrators.DeleteObject(user.Administrator);
                        user.Administrator = null;
                    }
                }

                if (user.IsOwner)
                {
                    if (!formModel.IsAdministrador)
                        this.ModelState.AddModelError(
                            () => formModel.IsAdministrador,
                            "Não é possível remover o papel de administrador do proprietário da conta.");
                }

                // when the user is a secretary
                if (formModel.IsSecretary)
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                        if (user.Secretary == null)
                            user.Secretary = new Secretary { PracticeId = this.DbUser.PracticeId, };
                }
                else
                {
                    // Only administrators can change the role of an user.
                    if (this.DbUser.AdministratorId != null)
                    {
                        if (user.Secretary != null)
                            this.db.Secretaries.DeleteObject(user.Secretary);
                        user.Secretary = null;
                    }
                }
            }

            // If ModelState is still valid, save the objects to the database.
            if (this.ModelState.IsValid)
            {
                if (formModel.Id == null)
                {
                    var notificationData = new NewUserCreatedNotificationData() { UserName = user.UserName };
                    var dbNotification = new Notification()
                        {
                            CreatedOn = this.GetUtcNow(),
                            PracticeId = this.DbPractice.Id,
                            UserToId = this.DbUser.Id,
                            Type = NotificationConstants.NEW_USER_NOTIFICATION_TYPE,
                            Data = new JavaScriptSerializer().Serialize(notificationData)
                        };
                    this.db.Notifications.AddObject(dbNotification);
                    this.db.SaveChanges();
                    NotificationsHub.BroadcastDbNotification(dbNotification, notificationData);
                }

                // Saving all the changes.
                this.db.SaveChanges();

                return this.RedirectToAction("Details", new { id = user.Id });
            }

            this.ViewBag.MedicalSpecialtyOptions =
                this.db.SYS_MedicalSpecialty
                .ToList()
                .Select(ms => new SelectListItem { Value = ms.Id.ToString(), Text = ms.Name })
                .ToList();

            this.ViewBag.MedicalEntityOptions =
                this.db.SYS_MedicalEntity
                .ToList()
                .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name })
                .ToList();

            if (this.DbUser.AdministratorId != null || this.DbUser.IsOwner)
                this.ViewBag.CanEditRole = true;

            // Removes all duplicated messages.
            this.ModelState.RemoveDuplicates();

            return this.View("Edit", formModel);
        }