/// <summary> /// Resume a pending export: Checks if the export is complete. If the export is complete, then download the export, process the addresses and sent a notification. /// </summary> /// <param name="sparkDataConfig">The spark data configuration.</param> public void PendingExport(SparkDataConfig sparkDataConfig = null) { if (sparkDataConfig == null) { sparkDataConfig = GetSettings(); } SparkDataApi.SparkDataApi sparkDataApi = new SparkDataApi.SparkDataApi(); var credentials = sparkDataApi.NcoaGetCredentials(sparkDataConfig.SparkDataApiKey); var ncoaApi = new NcoaApi(credentials); if (!ncoaApi.IsReportExportCreated(sparkDataConfig.NcoaSettings.FileName)) { return; } List <NcoaReturnRecord> ncoaReturnRecords; ncoaApi.DownloadExport(sparkDataConfig.NcoaSettings.CurrentReportExportKey, out ncoaReturnRecords); var ncoaHistoryList = ncoaReturnRecords.Select(r => r.ToNcoaHistory()).ToList(); FilterDuplicateLocations(ncoaHistoryList); // Making sure that the database is empty to avoid adding duplicate data. using (var rockContext = new RockContext()) { NcoaHistoryService ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.DeleteRange(ncoaHistoryService.Queryable()); rockContext.SaveChanges(); } if (ncoaReturnRecords != null && ncoaReturnRecords.Count != 0) { using (var rockContext = new RockContext()) { var ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.AddRange(ncoaHistoryList); rockContext.SaveChanges(); } } ProcessNcoaResults(sparkDataConfig); sparkDataApi.NcoaCompleteReport(sparkDataConfig.SparkDataApiKey, sparkDataConfig.NcoaSettings.FileName, sparkDataConfig.NcoaSettings.CurrentReportExportKey); //Notify group SentNotification(sparkDataConfig, "finished"); sparkDataConfig.NcoaSettings.LastRunDate = RockDateTime.Now; sparkDataConfig.NcoaSettings.CurrentReportStatus = "Complete"; sparkDataConfig.NcoaSettings.FileName = null; SaveSettings(sparkDataConfig); }
/// <summary> /// Job to get a National Change of Address (NCOA) report for all active people's addresses. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { Exception exception = null; // Get the job setting(s) JobDataMap dataMap = context.JobDetail.JobDataMap; SparkDataConfig sparkDataConfig = Ncoa.GetSettings(); if (!sparkDataConfig.NcoaSettings.IsEnabled || !sparkDataConfig.NcoaSettings.IsValid()) { return; } try { Guid?sparkDataApiKeyGuid = sparkDataConfig.SparkDataApiKey.AsGuidOrNull(); if (sparkDataApiKeyGuid == null) { exception = new Exception($"Spark Data Api Key '{sparkDataConfig.SparkDataApiKey.ToStringSafe()}' is empty or invalid. The Spark Data Api Key can be configured in System Settings > Spark Data Settings."); return; } switch (sparkDataConfig.NcoaSettings.CurrentReportStatus) { case "": case null: if (sparkDataConfig.NcoaSettings.RecurringEnabled) { StatusStart(sparkDataConfig); } break; case "Start": StatusStart(sparkDataConfig); break; case "Failed": StatusFailed(sparkDataConfig); break; case "Pending: Report": StatusPendingReport(sparkDataConfig); break; case "Pending: Export": StatusPendingExport(sparkDataConfig); break; case "Complete": StatusComplete(sparkDataConfig); break; } } catch (Exception ex) { exception = ex; } finally { if (exception != null) { context.Result = $"NCOA Job failed: {exception.Message}"; sparkDataConfig.NcoaSettings.CurrentReportStatus = "Failed"; sparkDataConfig.Messages.Add($"NOCA job failed: {RockDateTime.Now.ToString()} - {exception.Message}"); Ncoa.SaveSettings(sparkDataConfig); try { var ncoa = new Ncoa(); ncoa.SentNotification(sparkDataConfig, "failed"); } catch { } if (sparkDataConfig.SparkDataApiKey.IsNotNullOrWhiteSpace() && sparkDataConfig.NcoaSettings.FileName.IsNotNullOrWhiteSpace()) { SparkDataApi sparkDataApi = new SparkDataApi(); } Exception ex = new AggregateException("NCOA job failed.", exception); HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw ex; } else { string msg; if (sparkDataConfig.NcoaSettings.CurrentReportStatus == "Complete") { using (RockContext rockContext = new RockContext()) { NcoaHistoryService ncoaHistoryService = new NcoaHistoryService(rockContext); msg = $"NCOA request processed, {ncoaHistoryService.Count()} {(ncoaHistoryService.Count() == 1 ? "address" : "addresses")} processed, {ncoaHistoryService.MovedCount()} {(ncoaHistoryService.MovedCount() > 1 ? "were" : "was")} marked as 'moved'"; } } else { msg = $"Job complete. NCOA status: {sparkDataConfig.NcoaSettings.CurrentReportStatus}"; } context.Result = msg; sparkDataConfig.Messages.Add($"{msg}: {RockDateTime.Now.ToString()}"); Ncoa.SaveSettings(sparkDataConfig); } } }
/// <summary> /// Shows the view. /// </summary> protected void ShowView() { var rockContext = new RockContext(); int resultCount = Int32.Parse(GetAttributeValue("ResultCount")); int pageNumber = 0; if (!String.IsNullOrEmpty(PageParameter("page"))) { pageNumber = Int32.Parse(PageParameter("page")); } var skipCount = pageNumber * resultCount; var query = new NcoaHistoryService(rockContext).Queryable(); var processed = gfNcoaFilter.GetUserPreference("Processed").ConvertToEnumOrNull <Processed>(); if (processed.HasValue) { if (processed.Value != Processed.All && processed.Value != Processed.ManualUpdateRequiredOrNotProcessed) { query = query.Where(i => i.Processed == processed); } else if (processed.Value == Processed.ManualUpdateRequiredOrNotProcessed) { query = query.Where(i => i.Processed == Processed.ManualUpdateRequired || i.Processed == Processed.NotProcessed); } } var moveDateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(gfNcoaFilter.GetUserPreference("Move Date")); if (moveDateRange.Start.HasValue) { query = query.Where(e => e.MoveDate.HasValue && e.MoveDate.Value >= moveDateRange.Start.Value); } if (moveDateRange.End.HasValue) { query = query.Where(e => e.MoveDate.HasValue && e.MoveDate.Value < moveDateRange.End.Value); } var ncoaDateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(gfNcoaFilter.GetUserPreference("NCOA Processed Date")); if (ncoaDateRange.Start.HasValue) { query = query.Where(e => e.NcoaRunDateTime >= ncoaDateRange.Start.Value); } if (ncoaDateRange.End.HasValue) { query = query.Where(e => e.NcoaRunDateTime < ncoaDateRange.End.Value); } var moveType = gfNcoaFilter.GetUserPreference("Move Type").ConvertToEnumOrNull <MoveType>(); if (moveType.HasValue) { query = query.Where(i => i.MoveType == moveType); } var addressStatus = gfNcoaFilter.GetUserPreference("Address Status").ConvertToEnumOrNull <AddressStatus>(); if (addressStatus.HasValue) { query = query.Where(i => i.AddressStatus == addressStatus); } var addressInvalidReason = gfNcoaFilter.GetUserPreference("Address Invalid Reason").ConvertToEnumOrNull <AddressInvalidReason>(); if (addressInvalidReason.HasValue) { query = query.Where(i => i.AddressInvalidReason == addressInvalidReason); } decimal?moveDistance = gfNcoaFilter.GetUserPreference("Move Distance").AsDecimalOrNull(); if (moveDistance.HasValue) { query = query.Where(i => i.MoveDistance <= moveDistance.Value); } string lastName = gfNcoaFilter.GetUserPreference("Last Name"); if (!string.IsNullOrWhiteSpace(lastName)) { var personAliasQuery = new PersonAliasService(rockContext) .Queryable() .Where(p => p.Person != null && p.Person.LastName.Contains(lastName)) .Select(p => p.Id); query = query.Where(i => personAliasQuery.Contains(i.PersonAliasId)); } var campusId = gfNcoaFilter.GetUserPreference("Campus").AsIntegerOrNull(); if (campusId.HasValue) { var familyGroupType = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid()); var personAliasQuery = new PersonAliasService(rockContext).Queryable().AsNoTracking(); var campusQuery = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(m => m.Group.GroupTypeId == familyGroupType.Id && m.Group.CampusId.HasValue && m.Group.CampusId.Value == campusId.Value) .Select(m => m.PersonId) .Join(personAliasQuery, m => m, p => p.PersonId, (m, p) => p.Id); query = query.Where(i => campusQuery.Contains(i.PersonAliasId)); } var filteredRecords = query.ToList(); lTotal.Text = string.Format("Records: {0}", filteredRecords.Count()); #region Grouping rows var ncoaRows = filteredRecords .Where(a => a.MoveType != MoveType.Individual) .GroupBy(a => new { a.FamilyId, a.MoveType, a.MoveDate }) .Select(a => new NcoaRow { Id = a.Select(b => b.Id).Max(), FamilyMemberPersonAliasIds = a.Select(b => b.PersonAliasId).ToList() }).ToList(); var ncoaIndividualRows = filteredRecords .Where(a => a.MoveType == MoveType.Individual) .Select(a => new NcoaRow { Id = a.Id, IndividualPersonAliasId = a.PersonAliasId }).ToList(); ncoaRows.AddRange(ncoaIndividualRows); #endregion var pagedNcoaRows = ncoaRows.OrderBy(a => a.Id).Skip(skipCount).Take(resultCount + 1).ToList(); var familyMemberPersonAliasIds = pagedNcoaRows.SelectMany(r => r.FamilyMemberPersonAliasIds).ToList(); var individualPersonAliasIds = pagedNcoaRows.Select(r => r.IndividualPersonAliasId).ToList(); var people = new PersonAliasService(rockContext) .Queryable().AsNoTracking() .Where(p => familyMemberPersonAliasIds.Contains(p.Id) || individualPersonAliasIds.Contains(p.Id)) .Select(p => new { PersonAliasId = p.Id, Person = p.Person }) .ToList(); foreach (var ncoaRow in pagedNcoaRows) { ncoaRow.FamilyMembers = people .Where(p => ncoaRow.FamilyMemberPersonAliasIds.Contains(p.PersonAliasId)) .Select(p => p.Person) .ToList(); ncoaRow.Individual = people .Where(p => p.PersonAliasId == ncoaRow.IndividualPersonAliasId) .Select(p => p.Person) .FirstOrDefault(); var ncoaHistoryRecord = filteredRecords.Single(a => a.Id == ncoaRow.Id); ncoaRow.OriginalAddress = FormattedAddress(ncoaHistoryRecord.OriginalStreet1, ncoaHistoryRecord.OriginalStreet2, ncoaHistoryRecord.OriginalCity, ncoaHistoryRecord.OriginalState, ncoaHistoryRecord.OriginalPostalCode) .ConvertCrLfToHtmlBr(); ncoaRow.Status = ncoaHistoryRecord.Processed == Processed.Complete ? "Processed" : "Not Processed"; ncoaRow.StatusCssClass = ncoaHistoryRecord.Processed == Processed.Complete ? "label-success" : "label-default"; ncoaRow.ShowButton = false; var family = new GroupService(rockContext).Get(ncoaHistoryRecord.FamilyId); var person = ncoaRow.Individual ?? ncoaRow.FamilyMembers.First(); if (family == null) { family = person.GetFamily(rockContext); } var personService = new PersonService(rockContext); ncoaRow.FamilyName = family.Name; ncoaRow.HeadOftheHousehold = personService.GetHeadOfHousehold(person, family); if (ncoaHistoryRecord.MoveType != MoveType.Individual) { ncoaRow.FamilyMembers = personService.GetFamilyMembers(family, person.Id, true).Select(a => a.Person).ToList(); } else { ncoaRow.FamilyMembers = personService.GetFamilyMembers(family, person.Id, false).Select(a => a.Person).ToList(); } if (ncoaHistoryRecord.AddressStatus == AddressStatus.Invalid) { ncoaRow.TagLine = "Invalid Address"; ncoaRow.TagLineCssClass = "label-warning"; if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandName = "MarkAddressAsPrevious"; ncoaRow.CommandText = "Mark Address As Previous"; ncoaRow.ShowButton = true; } } if (ncoaHistoryRecord.NcoaType == NcoaType.Month48Move) { ncoaRow.TagLine = "48 Month Move"; ncoaRow.TagLineCssClass = "label-info"; if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandName = "MarkAddressAsPrevious"; ncoaRow.CommandText = "Mark Address As Previous"; ncoaRow.ShowButton = true; } } if (ncoaHistoryRecord.NcoaType == NcoaType.Move) { ncoaRow.TagLine = ncoaHistoryRecord.MoveType.ConvertToString(); ncoaRow.TagLineCssClass = "label-success"; ncoaRow.MoveDate = ncoaHistoryRecord.MoveDate; ncoaRow.MoveDistance = ncoaHistoryRecord.MoveDistance; ncoaRow.NewAddress = FormattedAddress(ncoaHistoryRecord.UpdatedStreet1, ncoaHistoryRecord.UpdatedStreet2, ncoaHistoryRecord.UpdatedCity, ncoaHistoryRecord.UpdatedState, ncoaHistoryRecord.UpdatedPostalCode) .ConvertCrLfToHtmlBr(); if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandText = "Mark Processed"; ncoaRow.CommandName = "MarkProcessed"; ncoaRow.ShowButton = true; } } } rptNcoaResultsFamily.DataSource = pagedNcoaRows.Take(resultCount).GroupBy(n => n.FamilyName); rptNcoaResultsFamily.DataBind(); if (pagedNcoaRows.Count() > resultCount) { hlNext.Visible = hlNext.Enabled = true; Dictionary <string, string> queryStringNext = new Dictionary <string, string>(); queryStringNext.Add("page", (pageNumber + 1).ToString()); var pageReferenceNext = new Rock.Web.PageReference(CurrentPageReference.PageId, CurrentPageReference.RouteId, queryStringNext); hlNext.NavigateUrl = pageReferenceNext.BuildUrl(); } else { hlNext.Visible = hlNext.Enabled = false; } // build prev button if (pageNumber == 0) { hlPrev.Visible = hlPrev.Enabled = false; } else { hlPrev.Visible = hlPrev.Enabled = true; Dictionary <string, string> queryStringPrev = new Dictionary <string, string>(); queryStringPrev.Add("page", (pageNumber - 1).ToString()); var pageReferencePrev = new Rock.Web.PageReference(CurrentPageReference.PageId, CurrentPageReference.RouteId, queryStringPrev); hlPrev.NavigateUrl = pageReferencePrev.BuildUrl(); } }
/// <summary> /// Handles the ItemCommand event of the rptNcoaResults control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RepeaterCommandEventArgs"/> instance containing the event data.</param> protected void rptNcoaResults_ItemCommand(object sender, RepeaterCommandEventArgs e) { var ncoaHistoryId = e.CommandArgument.ToStringSafe().AsIntegerOrNull(); if (!ncoaHistoryId.HasValue) { return; } if (e.CommandName == "MarkAddressAsPrevious") { using (var rockContext = new RockContext()) { var ncoaHistory = new NcoaHistoryService(rockContext).Get(ncoaHistoryId.Value); if (ncoaHistory != null) { var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var changes = new History.HistoryChangeList(); Ncoa ncoa = new Ncoa(); var previousValue = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.GROUP_LOCATION_TYPE_PREVIOUS.AsGuid()); int? previousValueId = previousValue == null ? ( int? )null : previousValue.Id; var previousGroupLocation = ncoa.MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, changes); if (previousGroupLocation != null) { ncoaHistory.Processed = Processed.Complete; // If there were any changes, write to history if (changes.Any()) { var family = groupService.Get(ncoaHistory.FamilyId); if (family != null) { foreach (var fm in family.Members) { HistoryService.SaveChanges( rockContext, typeof(Person), Rock.SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), fm.PersonId, changes, family.Name, typeof(Group), family.Id, false); } } } } } rockContext.SaveChanges(); } } else if (e.CommandName == "MarkProcessed") { using (RockContext rockContext = new RockContext()) { var ncoa = (new NcoaHistoryService(rockContext)).Get(ncoaHistoryId.Value); ncoa.Processed = Processed.Complete; rockContext.SaveChanges(); } } NcoaResults_BlockUpdated(null, null); }
/// <summary> /// Processes the NCOA results: Mark all individual move addresses as previous, add the new address as current; and processed. /// If minMoveDistance is specified, mark the individual as inactive if the individual moved further than the specified distance. /// </summary> /// <param name="inactiveReason">The inactive reason.</param> /// <param name="minMoveDistance">The minimum move distance.</param> /// <param name="homeValueId">The home value identifier.</param> /// <param name="previousValueId">The previous value identifier.</param> private void ProcessNcoaResultsIndividualMove(DefinedValueCache inactiveReason, decimal?minMoveDistance, int?homeValueId, int?previousValueId) { List <int> ncoaIds = null; // Process 'Move' NCOA Types (For the remaining Individual move types that weren't updated with the family move) using (var rockContext = new RockContext()) { ncoaIds = new NcoaHistoryService(rockContext) .Queryable().AsNoTracking() .Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Individual) .Select(n => n.Id) .ToList(); } foreach (int id in ncoaIds) { using (var rockContext = new RockContext()) { // Get the NCOA record and make sure it still hasn't been processed var ncoaHistory = new NcoaHistoryService(rockContext).Get(id); if (ncoaHistory != null && ncoaHistory.Processed == Processed.NotProcessed) { var ncoaHistoryService = new NcoaHistoryService(rockContext); var groupMemberService = new GroupMemberService(rockContext); var personAliasService = new PersonAliasService(rockContext); var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var locationService = new LocationService(rockContext); var personService = new PersonService(rockContext); var changes = new History.HistoryChangeList(); // Default the status to requiring a manual update (we might change this though) ncoaHistory.Processed = Processed.ManualUpdateRequired; // Find the existing family var family = groupService.Get(ncoaHistory.FamilyId); // If there's only one person in the family if (family.Members.Count == 1) { // And that person is the same as the move record's person then we can process it. var personAlias = personAliasService.Get(ncoaHistory.PersonAliasId); var familyMember = family.Members.First(); if (personAlias != null && familyMember.PersonId == personAlias.PersonId) { // If were able to mark their existing address as previous and add a new updated Home address, // then set the status to complete (otherwise leave it as needing a manual update). var previousGroupLocation = MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, changes); if (previousGroupLocation != null) { if (AddNewHomeLocation(ncoaHistory, locationService, groupLocationService, homeValueId, changes, previousGroupLocation.IsMailingLocation, previousGroupLocation.IsMappedLocation)) { ncoaHistory.Processed = Processed.Complete; // Look for any other moves for the same person to same address, and set their process to complete also foreach (var ncoaIndividual in ncoaHistoryService .Queryable().Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Individual && n.PersonAliasId == ncoaHistory.PersonAliasId && n.Id != ncoaHistory.Id && n.UpdatedStreet1 == ncoaHistory.UpdatedStreet1)) { ncoaIndividual.Processed = Processed.Complete; } // If there were any changes, write to history and check to see if person should be inactivated if (changes.Any()) { if (ncoaHistory.MoveDistance.HasValue && minMoveDistance.HasValue && ncoaHistory.MoveDistance.Value >= minMoveDistance.Value) { History.HistoryChangeList personChanges; personService.InactivatePerson(familyMember.Person, inactiveReason, $"Received a Change of Address (NCOA) notice that was for more than {minMoveDistance} miles away.", out personChanges); if (personChanges.Any()) { HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES.AsGuid(), familyMember.PersonId, personChanges, false); } } HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), familyMember.PersonId, changes, family.Name, typeof(Group), family.Id, false); } } } } } rockContext.SaveChanges(); } } } }
/// <summary> /// Processes the NCOA results: Mark all family move addresses as previous, add the new address as current; and processed. /// If minMoveDistance is specified, mark the family as inactive if the family moved further than the specified distance. /// </summary> /// <param name="inactiveReason">The inactive reason.</param> /// <param name="minMoveDistance">The minimum move distance.</param> /// <param name="homeValueId">The home value identifier.</param> /// <param name="previousValueId">The previous value identifier.</param> private void ProcessNcoaResultsFamilyMove(DefinedValueCache inactiveReason, decimal?minMoveDistance, int?homeValueId, int?previousValueId) { List <int> ncoaIds = null; // Process 'Move' NCOA Types (The 'Family' move types will be processed first) using (var rockContext = new RockContext()) { ncoaIds = new NcoaHistoryService(rockContext) .Queryable().AsNoTracking() .Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Family) .Select(n => n.Id) .ToList(); } foreach (int id in ncoaIds) { using (var rockContext = new RockContext()) { // Get the NCOA record and make sure it still hasn't been processed var ncoaHistory = new NcoaHistoryService(rockContext).Get(id); if (ncoaHistory != null && ncoaHistory.Processed == Processed.NotProcessed) { var ncoaHistoryService = new NcoaHistoryService(rockContext); var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var locationService = new LocationService(rockContext); var personService = new PersonService(rockContext); var familyChanges = new History.HistoryChangeList(); ncoaHistory.Processed = Processed.ManualUpdateRequired; // If we're able to mark the existing address as previous and successfully create a new home address.. var previousGroupLocation = MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, familyChanges); if (previousGroupLocation != null) { if (AddNewHomeLocation(ncoaHistory, locationService, groupLocationService, homeValueId, familyChanges, previousGroupLocation.IsMailingLocation, previousGroupLocation.IsMappedLocation)) { // set the status to 'Complete' ncoaHistory.Processed = Processed.Complete; // Look for any other moves for the same family and to same address, and set their status to complete as well foreach (var ncoaIndividual in ncoaHistoryService .Queryable().Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.FamilyId == ncoaHistory.FamilyId && n.Id != ncoaHistory.Id && n.UpdatedStreet1 == ncoaHistory.UpdatedStreet1)) { ncoaIndividual.Processed = Processed.Complete; } // If there were any changes, write to history and check to see if person should be inactivated if (familyChanges.Any()) { var family = groupService.Get(ncoaHistory.FamilyId); if (family != null) { foreach (var fm in family.Members) { if (ncoaHistory.MoveDistance.HasValue && minMoveDistance.HasValue && ncoaHistory.MoveDistance.Value >= minMoveDistance.Value) { History.HistoryChangeList personChanges; personService.InactivatePerson(fm.Person, inactiveReason, $"Received a Change of Address (NCOA) notice that was for more than {minMoveDistance} miles away.", out personChanges); if (personChanges.Any()) { HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES.AsGuid(), fm.PersonId, personChanges, false); } } HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), fm.PersonId, familyChanges, family.Name, typeof(Group), family.Id, false); } } } } } rockContext.SaveChanges(); } } } }
/// <summary> /// Processes the NCOA results: Mark all 48 month move addresses as previous and processed if enabled, otherwise mark as manual update required. /// </summary> /// <param name="mark48MonthAsPrevious">if a 48 month move should be marked as previous, set to <c>true</c>.</param> /// <param name="previousValueId">The previous value identifier.</param> private void ProcessNcoaResults48MonthMove(bool mark48MonthAsPrevious, int?previousValueId) { List <int> ncoaIds = null; // Process the '48 Month Move' NCOA Types using (var rockContext = new RockContext()) { ncoaIds = new NcoaHistoryService(rockContext) .Queryable().AsNoTracking() .Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Month48Move) .Select(n => n.Id) .ToList(); } foreach (int id in ncoaIds) { using (var rockContext = new RockContext()) { var ncoaHistory = new NcoaHistoryService(rockContext).Get(id); if (ncoaHistory != null) { var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var changes = new History.HistoryChangeList(); // If configured to mark these as previous, and we're able to mark it as previous set the status to 'Complete' // otherwise set it to require a manual update if (mark48MonthAsPrevious && MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, changes) != null) { ncoaHistory.Processed = Processed.Complete; // If there were any changes, write to history if (changes.Any()) { var family = groupService.Get(ncoaHistory.FamilyId); if (family != null) { foreach (var fm in family.Members) { HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), fm.PersonId, changes, family.Name, typeof(Group), family.Id, false); } } } } else { ncoaHistory.Processed = Processed.ManualUpdateRequired; } rockContext.SaveChanges(); } } } }
/// <summary> /// Starts the NCOA request: Check if there is a valid credit card on the Spark Data server. If there is a valid credit card, then get the addresses, initialize a report on NCOA, upload the addresses to NCOA and delete the previous NcoaHistory data. /// </summary> /// <param name="sparkDataConfig">The spark data configuration.</param> public void Start(SparkDataConfig sparkDataConfig) { if (sparkDataConfig == null) { sparkDataConfig = GetSettings(); } SparkDataApi.SparkDataApi sparkDataApi = new SparkDataApi.SparkDataApi(); var accountStatus = sparkDataApi.CheckAccount(sparkDataConfig.SparkDataApiKey); switch (accountStatus) { case SparkDataApi.SparkDataApi.AccountStatus.AccountNoName: throw new UnauthorizedAccessException("Account does not have a name."); case SparkDataApi.SparkDataApi.AccountStatus.AccountNotFound: throw new UnauthorizedAccessException("Account not found."); case SparkDataApi.SparkDataApi.AccountStatus.Disabled: throw new UnauthorizedAccessException("Account is disabled."); case SparkDataApi.SparkDataApi.AccountStatus.EnabledCardExpired: throw new UnauthorizedAccessException("Credit card on Spark server expired."); case SparkDataApi.SparkDataApi.AccountStatus.EnabledNoCard: throw new UnauthorizedAccessException("No credit card found on Spark server."); case SparkDataApi.SparkDataApi.AccountStatus.InvalidSparkDataKey: throw new UnauthorizedAccessException("Invalid Spark Data Key."); } var addresses = GetAddresses(sparkDataConfig.NcoaSettings.PersonDataViewId); if (addresses.Count == 0) { sparkDataConfig.NcoaSettings.LastRunDate = RockDateTime.Now; sparkDataConfig.NcoaSettings.CurrentReportStatus = "Complete"; SaveSettings(sparkDataConfig); return; } GroupNameTransactionKey groupNameTransactionKey = null; if (sparkDataConfig.NcoaSettings.FileName.IsNotNullOrWhiteSpace()) { groupNameTransactionKey = sparkDataApi.NcoaRetryReport(sparkDataConfig.SparkDataApiKey, sparkDataConfig.NcoaSettings.FileName); } if (groupNameTransactionKey == null) { groupNameTransactionKey = sparkDataApi.NcoaInitiateReport(sparkDataConfig.SparkDataApiKey, addresses.Count, sparkDataConfig.NcoaSettings.PersonFullName); } if (groupNameTransactionKey == null) { if (sparkDataConfig.NcoaSettings.CurrentReportStatus == "Failed") { // To avoid trying to charging customer over and over again... sparkDataConfig.NcoaSettings.IsEnabled = false; } throw new Exception("Init NCOA: Failed to initialize request."); } sparkDataConfig.NcoaSettings.FileName = groupNameTransactionKey.TransactionKey; var credentials = sparkDataApi.NcoaGetCredentials(sparkDataConfig.SparkDataApiKey); var ncoaApi = new NcoaApi(credentials); string id; ncoaApi.CreateFile(sparkDataConfig.NcoaSettings.FileName, groupNameTransactionKey.GroupName, out id); sparkDataConfig.NcoaSettings.CurrentReportKey = id; ncoaApi.UploadAddresses(addresses, sparkDataConfig.NcoaSettings.CurrentReportKey); sparkDataConfig.NcoaSettings.CurrentUploadCount = addresses.Count; ncoaApi.CreateReport(sparkDataConfig.NcoaSettings.CurrentReportKey); // Delete previous NcoaHistory entries. This prevent an user thinking the previous run's data is the current run's data. using (RockContext rockContext = new RockContext()) { NcoaHistoryService ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.DeleteRange(ncoaHistoryService.Queryable()); rockContext.SaveChanges(); } sparkDataConfig.NcoaSettings.CurrentReportStatus = "Pending: Report"; SaveSettings(sparkDataConfig); }