/// <summary> /// Updates GroupLocationHistorical for any group locations in groups that have data group history enabled /// </summary> /// <param name="context">The context.</param> public void UpdateGroupLocationHistorical(IJobExecutionContext context) { var rockContext = new RockContext(); var groupLocationHistoricalService = new GroupLocationHistoricalService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var groupLocationsWithHistoryEnabledQuery = groupLocationService.Queryable().Where(a => a.Group.GroupType.EnableGroupHistory == true).AsNoTracking(); var groupLocationsHistoricalCurrentQuery = groupLocationHistoricalService.Queryable().Where(a => a.CurrentRowIndicator == true).AsNoTracking(); // Mark GroupLocationHistorical Rows as History ( CurrentRowIndicator = false, etc ) if any of the tracked field values change var groupLocationHistoricalNoLongerCurrentQuery = groupLocationsHistoricalCurrentQuery.Join( groupLocationsWithHistoryEnabledQuery, glh => glh.GroupLocationId, gl => gl.Id, (glh, gl) => new { GroupLocation = gl, GroupLocationHistorical = glh }) .Where(a => a.GroupLocation.GroupId != a.GroupLocationHistorical.GroupId || (a.GroupLocation.GroupLocationTypeValueId != a.GroupLocation.GroupLocationTypeValueId) || (a.GroupLocation.GroupLocationTypeValueId.HasValue && a.GroupLocation.GroupLocationTypeValue.Value != a.GroupLocationHistorical.GroupLocationTypeName) || a.GroupLocation.LocationId != a.GroupLocationHistorical.LocationId || a.GroupLocation.Location.ModifiedDateTime != a.GroupLocationHistorical.LocationModifiedDateTime || (a.GroupLocation.Schedules.Select(s => new { ScheduleId = s.Id, s.ModifiedDateTime }).Except(a.GroupLocationHistorical.GroupLocationHistoricalSchedules.Select(hs => new { hs.ScheduleId, ModifiedDateTime = hs.ScheduleModifiedDateTime }))).Any() ); var effectiveExpireDateTime = RockDateTime.Now; int groupLocationsLoggedToHistory = 0; int groupLocationsSaveToHistoryCurrent = 0; if (groupLocationHistoricalNoLongerCurrentQuery.Any()) { var groupLocationHistoricalNoLongerCurrent = groupLocationHistoricalNoLongerCurrentQuery.Select(a => a.GroupLocationHistorical).AsNoTracking(); groupLocationsLoggedToHistory = rockContext.BulkUpdate(groupLocationHistoricalNoLongerCurrent, glh => new GroupLocationHistorical { CurrentRowIndicator = false, ExpireDateTime = effectiveExpireDateTime }); } // Insert Group Locations (that have a group with GroupType.EnableGroupHistory) that don't have a CurrentRowIndicator row yet ( or don't have a CurrentRowIndicator because it was stamped with CurrentRowIndicator=false ) var groupLocationsToAddToHistoricalCurrentsQuery = groupLocationsWithHistoryEnabledQuery.Where(gl => !groupLocationsHistoricalCurrentQuery.Any(glh => glh.GroupLocationId == gl.Id)); if (groupLocationsToAddToHistoricalCurrentsQuery.Any()) { List <GroupLocationHistorical> groupLocationHistoricalCurrentsToInsert = groupLocationsToAddToHistoricalCurrentsQuery .Include(a => a.GroupLocationTypeValue) .Include(a => a.Location).ToList() .Select(gl => GroupLocationHistorical.CreateCurrentRowFromGroupLocation(gl, effectiveExpireDateTime)).ToList(); groupLocationsSaveToHistoryCurrent = groupLocationHistoricalCurrentsToInsert.Count(); // get the current max GroupLocatiionHistorical.Id to help narrow down which ones were inserted int groupLocationHistoricalStartId = groupLocationHistoricalService.Queryable().Max(a => ( int? )a.Id) ?? 0; rockContext.BulkInsert(groupLocationHistoricalCurrentsToInsert); // since we used BulkInsert, we'll need to go back and get the Ids and the associated GroupLocation's Schedules for the GroupLocationHistorical records that we just inserted var insertedGroupLocationHistoricalIdsWithSchedules = groupLocationHistoricalService.Queryable() .Where(a => a.Id > groupLocationHistoricalStartId && a.GroupLocation.Schedules.Any()).ToList() .Select(a => new { GroupLocationHistoricalId = a.Id, a.GroupLocation.Schedules }); List <GroupLocationHistoricalSchedule> groupLocationHistoricalScheduleCurrentsToInsert = new List <GroupLocationHistoricalSchedule>(); foreach (var insertedGroupLocationHistoricalIdWithSchedules in insertedGroupLocationHistoricalIdsWithSchedules) { foreach (Schedule schedule in insertedGroupLocationHistoricalIdWithSchedules.Schedules) { groupLocationHistoricalScheduleCurrentsToInsert.Add(new GroupLocationHistoricalSchedule { GroupLocationHistoricalId = insertedGroupLocationHistoricalIdWithSchedules.GroupLocationHistoricalId, ScheduleId = schedule.Id, ScheduleName = schedule.ToString(), ScheduleModifiedDateTime = schedule.ModifiedDateTime }); } } if (groupLocationHistoricalScheduleCurrentsToInsert.Any()) { rockContext.BulkInsert(groupLocationHistoricalScheduleCurrentsToInsert); } } if (groupLocationsLoggedToHistory > 0) { _jobStatusMessages.Add($"Logged {groupLocationsLoggedToHistory} {"group location history snapshot".PluralizeIf( groupLocationsLoggedToHistory != 0 )}"); } if (groupLocationsSaveToHistoryCurrent > 0) { int newGroupLocationsAddedToHistory = groupLocationsSaveToHistoryCurrent - groupLocationsLoggedToHistory; if (newGroupLocationsAddedToHistory > 0) { _jobStatusMessages.Add($"Added {newGroupLocationsAddedToHistory} new {"group location history snapshot".PluralizeIf( newGroupLocationsAddedToHistory != 0 )}"); } } }
/// <summary> /// Handles the Click event of the lbSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void lbSave_Click(object sender, EventArgs e) { var rockContext = new RockContext(); var personService = new PersonService(rockContext); Person business = null; if (int.Parse(hfBusinessId.Value) != 0) { business = personService.Get(int.Parse(hfBusinessId.Value)); } if (business == null) { business = new Person(); personService.Add(business); tbBusinessName.Text = tbBusinessName.Text.FixCase(); } // Business Name business.LastName = tbBusinessName.Text; // Phone Number var businessPhoneTypeId = new DefinedValueService(rockContext).GetByGuid(new Guid(Rock.SystemGuid.DefinedValue.PERSON_PHONE_TYPE_WORK)).Id; var phoneNumber = business.PhoneNumbers.FirstOrDefault(n => n.NumberTypeValueId == businessPhoneTypeId); if (!string.IsNullOrWhiteSpace(PhoneNumber.CleanNumber(pnbPhone.Number))) { if (phoneNumber == null) { phoneNumber = new PhoneNumber { NumberTypeValueId = businessPhoneTypeId }; business.PhoneNumbers.Add(phoneNumber); } phoneNumber.CountryCode = PhoneNumber.CleanNumber(pnbPhone.CountryCode); phoneNumber.Number = PhoneNumber.CleanNumber(pnbPhone.Number); phoneNumber.IsMessagingEnabled = cbSms.Checked; phoneNumber.IsUnlisted = cbUnlisted.Checked; } else { if (phoneNumber != null) { business.PhoneNumbers.Remove(phoneNumber); new PhoneNumberService(rockContext).Delete(phoneNumber); } } // Record Type - this is always "business". it will never change. business.RecordTypeValueId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.PERSON_RECORD_TYPE_BUSINESS.AsGuid()).Id; // Record Status business.RecordStatusValueId = dvpRecordStatus.SelectedValueAsInt();; // Record Status Reason int?newRecordStatusReasonId = null; if (business.RecordStatusValueId.HasValue && business.RecordStatusValueId.Value == DefinedValueCache.Get(new Guid(Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_INACTIVE)).Id) { newRecordStatusReasonId = dvpReason.SelectedValueAsInt(); } business.RecordStatusReasonValueId = newRecordStatusReasonId; // Email business.IsEmailActive = true; business.Email = tbEmail.Text.Trim(); business.EmailPreference = rblEmailPreference.SelectedValue.ConvertToEnum <EmailPreference>(); avcEditAttributes.GetEditValues(business); if (!business.IsValid) { // Controls will render the error messages return; } rockContext.WrapTransaction(() => { rockContext.SaveChanges(); // Add/Update Family Group var familyGroupType = GroupTypeCache.GetFamilyGroupType(); int adultRoleId = familyGroupType.Roles .Where(r => r.Guid.Equals(Rock.SystemGuid.GroupRole.GROUPROLE_FAMILY_MEMBER_ADULT.AsGuid())) .Select(r => r.Id) .FirstOrDefault(); var adultFamilyMember = UpdateGroupMember(business.Id, familyGroupType, business.LastName + " Business", ddlCampus.SelectedValueAsInt(), adultRoleId, rockContext); business.GivingGroup = adultFamilyMember.Group; // Add/Update Known Relationship Group Type var knownRelationshipGroupType = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_KNOWN_RELATIONSHIPS.AsGuid()); int knownRelationshipOwnerRoleId = knownRelationshipGroupType.Roles .Where(r => r.Guid.Equals(Rock.SystemGuid.GroupRole.GROUPROLE_KNOWN_RELATIONSHIPS_OWNER.AsGuid())) .Select(r => r.Id) .FirstOrDefault(); var knownRelationshipOwner = UpdateGroupMember(business.Id, knownRelationshipGroupType, "Known Relationship", null, knownRelationshipOwnerRoleId, rockContext); // Add/Update Implied Relationship Group Type var impliedRelationshipGroupType = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_PEER_NETWORK.AsGuid()); int impliedRelationshipOwnerRoleId = impliedRelationshipGroupType.Roles .Where(r => r.Guid.Equals(Rock.SystemGuid.GroupRole.GROUPROLE_PEER_NETWORK_OWNER.AsGuid())) .Select(r => r.Id) .FirstOrDefault(); var impliedRelationshipOwner = UpdateGroupMember(business.Id, impliedRelationshipGroupType, "Implied Relationship", null, impliedRelationshipOwnerRoleId, rockContext); rockContext.SaveChanges(); // Location int workLocationTypeId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.GROUP_LOCATION_TYPE_WORK).Id; var groupLocationService = new GroupLocationService(rockContext); var workLocation = groupLocationService.Queryable("Location") .Where(gl => gl.GroupId == adultFamilyMember.Group.Id && gl.GroupLocationTypeValueId == workLocationTypeId) .FirstOrDefault(); if (string.IsNullOrWhiteSpace(acAddress.Street1)) { if (workLocation != null) { if (cbSaveFormerAddressAsPreviousAddress.Checked) { GroupLocationHistorical.CreateCurrentRowFromGroupLocation(workLocation, RockDateTime.Now); } groupLocationService.Delete(workLocation); } } else { var newLocation = new LocationService(rockContext).Get(acAddress.Street1, acAddress.Street2, acAddress.City, acAddress.State, acAddress.PostalCode, acAddress.Country); if (workLocation == null) { workLocation = new GroupLocation(); groupLocationService.Add(workLocation); workLocation.GroupId = adultFamilyMember.Group.Id; workLocation.GroupLocationTypeValueId = workLocationTypeId; } else { // Save this to history if the box is checked and the new info is different than the current one. if (cbSaveFormerAddressAsPreviousAddress.Checked && newLocation.Id != workLocation.Location.Id) { new GroupLocationHistoricalService(rockContext).Add(GroupLocationHistorical.CreateCurrentRowFromGroupLocation(workLocation, RockDateTime.Now)); } } workLocation.Location = newLocation; workLocation.IsMailingLocation = true; } rockContext.SaveChanges(); hfBusinessId.Value = business.Id.ToString(); }); /* Ethan Drotning 2022-01-11 * Need save the PersonSearchKeys outside of the transaction since the DB might not have READ_COMMITTED_SNAPSHOT enabled. */ // PersonSearchKey var personSearchKeyService = new PersonSearchKeyService(rockContext); var validSearchTypes = GetValidSearchKeyTypes(); var databaseSearchKeys = personSearchKeyService.Queryable().Where(a => a.PersonAlias.PersonId == business.Id && validSearchTypes.Contains(a.SearchTypeValue.Guid)).ToList(); foreach (var deletedSearchKey in databaseSearchKeys.Where(a => !PersonSearchKeysState.Any(p => p.Guid == a.Guid))) { personSearchKeyService.Delete(deletedSearchKey); } foreach (var personSearchKey in PersonSearchKeysState.Where(a => !databaseSearchKeys.Any(d => d.Guid == a.Guid))) { personSearchKey.PersonAliasId = business.PrimaryAliasId.Value; personSearchKeyService.Add(personSearchKey); } rockContext.SaveChanges(); business.SaveAttributeValues(); var queryParams = new Dictionary <string, string> { { "BusinessId", hfBusinessId.Value } }; NavigateToCurrentPage(queryParams); }