/// <summary>
        /// Insert a new User Account, or adds the user with this business account to the business's role
        /// </summary>
        /// <param name="userAccount">The new user account to create</param>
        /// <param name="roleId">The role</param>
        public void Post(Guid roleId, UserAccount userAccount)
        {
            var currentUserAccount = CoreEntitiesContainer.CurrentUserAccount();

            //check for admin abilities
            var businessAccount = CoreEntitiesContainer.Owner(roleId, new[] { RoleType.Administrator }).Include(ba => ba.OwnedRoles).FirstOrDefault();
            if (businessAccount == null)
                throw Request.NotAuthorized();

            //find the role in the BusinessAccount that matches the name of the one passed in
            var role = CoreEntitiesContainer.Roles.FirstOrDefault(r => r.OwnerBusinessAccountId == businessAccount.Id && r.Name == userAccount.Role);
            if (role == null)
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, "This role does not exist on the business account. Please contact customer support"));

            //Check to be sure that the time zone passed is valid
            var engine = EntityRules.SetupTimeZoneRules();
            var errors = EntityRules.ValidateAndReturnErrors(engine, userAccount.TimeZone);
            if (errors != null)
                Request.BadRequest(errors);

            var user = GetUser(userAccount.EmailAddress);

            var newUser = user == null;

            //if the user does not exist, create a new one
            if (newUser)
            {
                user = new Core.Models.CoreEntities.UserAccount
                {
                    Id = Guid.NewGuid(),
                    EmailAddress = userAccount.EmailAddress.Trim(),
                    FirstName = userAccount.FirstName,
                    LastName = userAccount.LastName,
                    TimeZone = "Eastern Standard Time",
                    CreatedDate = DateTime.UtcNow,
                    LastModifyingUserId = CoreEntitiesContainer.CurrentUserAccount().Id,
                    //PasswordHash and PasswordSalt are not nullable 
                    //set temporary values (even though they will not be used)
                    PasswordHash = EmailPasswordTools.GeneratePassword(),
                    PasswordSalt = EncryptionTools.GenerateSalt()
                };
            }
            //If the user does exist, track the changes
            else
            {
                user.LastModified = DateTime.UtcNow;
                user.LastModifyingUserId = currentUserAccount.Id;
            }

            //add the user to the role
            user.RoleMembership.Add(role);

            //setup the employee link
            SetupEmployee(userAccount.EmployeeId, user, businessAccount, currentUserAccount.Id);

            SaveWithRetry();

            if (newUser)
            {
                //Send new user email
                var sender = CoreEntitiesContainer.CurrentUserAccount().DisplayName;
                var recipient = user.FirstName;
                var subject = "Your FoundOPS invite from " + sender;
                var body = "Hi " + recipient + ", \r\n\r\n" +
                            sender + " has created a user account for you in FoundOPS. \r\n\r\n" +
                            "FoundOPS is an easy to use tool that helps field services teams communicate and provide the best possible service to their clients. \r\n\r\n" +
                            "Click here to accept the invite: \r\n " + @"{0}" + "\r\n\r\n" +
                            "If you have any difficulty accepting the invitation, email us at [email protected]. This invitation expires in 7 days. \r\n\r\n\r\n" +
                            "The FoundOPS Team";

                CoreEntitiesMembershipProvider.ResetAccount(user.EmailAddress, subject, body, TimeSpan.FromDays(7));
            }

            //TODO consider sending an email for existing users (they were given access to...)
        }
        /// <summary>
        /// If roleId is set, changing properties on behalf of a business account. Can only change role and employee link.
        /// If roleId is null, changing properties on behalf of the current user. Can only change user properties: FirstName, LastName, Email Address, TimeZone, Password
        /// </summary>
        /// <param name="userAccount">The UserAccount</param>
        /// <param name="roleId">(Optional) The role of the business to change</param>
        /// <param name="newPass">(Optional) For resetting the password: the users new password</param>
        /// <param name="oldPass">(Optional) For resetting the password: the users old password</param>
        public void Put(UserAccount userAccount, Guid? roleId = null, string newPass = null, string oldPass = null)
        {
            var currentUserAccount = CoreEntitiesContainer.CurrentUserAccount();
            if (currentUserAccount == null)
                throw Request.NotAuthorized();

            //changing properties on behalf of a business account
            if (roleId.HasValue)
            {
                //used for changing roles
                var businessAccount = CoreEntitiesContainer.Owner(roleId.Value, new[] { RoleType.Administrator }).FirstOrDefault();
                if (businessAccount == null)
                    throw Request.NotAuthorized();

                //Check to be sure that the time zone passed is valid
                var engine = EntityRules.SetupTimeZoneRules();
                var errors = EntityRules.ValidateAndReturnErrors(engine, userAccount.TimeZone);
                if (errors != null)
                    Request.BadRequest(errors);

                var user = CoreEntitiesContainer.Parties.OfType<Core.Models.CoreEntities.UserAccount>()
                    .Include(u => u.RoleMembership).Include(u => u.LinkedEmployees).First(ua => ua.Id == userAccount.Id);

                //Get the users current role for this business account
                var userRole = CoreEntitiesContainer.Roles.FirstOrDefault(r => r.OwnerBusinessAccountId == businessAccount.Id
                    && r.MemberParties.Any(p => p.Id == user.Id));

                //If a new role has been selected for the user, remove all old ones and assign the new one
                if (userRole != null && userRole.Name != userAccount.Role)
                {
                    user.RoleMembership.Remove(userRole);

                    //Find the new role to be added for the user
                    var newRole = CoreEntitiesContainer.Roles.FirstOrDefault(r => r.OwnerBusinessAccountId == businessAccount.Id && r.Name == userAccount.Role);

                    //Add the new role for the user
                    if (newRole != null)
                        user.RoleMembership.Add(newRole);
                }

                SetupEmployee(userAccount.EmployeeId, user, businessAccount, currentUserAccount.Id);

                //TODO CR these lines are common w below. Merge
                //TODO CR Make extension method on ITrackable to do this
                user.LastModified = DateTime.UtcNow;
                user.LastModifyingUserId = currentUserAccount.Id;

                SaveWithRetry();
            }
            //changing properties on behalf of the current user
            else
            {
                //if the user is not editing itself, and if it does not have admin access to the business account
                //throw not authorized
                if (userAccount != null && currentUserAccount.Id != userAccount.Id)
                    throw Request.NotAuthorized();

                if (userAccount != null) //can be null if just changing the password
                {
                    //if the email address changed, check it does not conflict
                    if (userAccount.EmailAddress != currentUserAccount.EmailAddress && GetUser(userAccount.EmailAddress) != null)
                        throw ApiExceptions.Create(Request.CreateResponse(HttpStatusCode.Conflict, "This email address already exists"));

                    //update properties
                    currentUserAccount.FirstName = userAccount.FirstName;
                    currentUserAccount.LastName = userAccount.LastName;
                    currentUserAccount.EmailAddress = userAccount.EmailAddress;

                    //check the time zone is acceptable
                    try
                    {
                        TimeZoneInfo.FindSystemTimeZoneById(userAccount.TimeZone.Id);
                    }
                    catch (Exception)
                    {
                        throw Request.BadRequest("TimeZone not acceptable");
                    }

                    currentUserAccount.TimeZone = userAccount.TimeZone.Id;
                }

                //try to change the password
                if (newPass != null && oldPass != null)
                {
                    if (!CoreEntitiesMembershipProvider.ChangePassword(currentUserAccount.EmailAddress, oldPass, newPass))
                        throw Request.BadRequest("The password was incorrect, or the new password is not acceptable");
                }

                //TODO CR Make extension method on ITrackable to do this
                currentUserAccount.LastModified = DateTime.UtcNow;
                currentUserAccount.LastModifyingUserId = currentUserAccount.Id;

                SaveWithRetry();
            }
        }