From a3c11fd434c707495790487c893f730d9ee0b16a Mon Sep 17 00:00:00 2001 From: Alex Gonzalez Date: Wed, 3 Apr 2024 16:00:35 -0700 Subject: [PATCH] Fix bug with going back in subflows (#196) * Fix bug with going back in subflows Co-authored-by: Ana Medrano --- .../ClearIncompleteIncomeIterations.java | 35 ++++++++++++ ...learIncompleteIterationsFromHousehold.java | 42 +++++++++++++++ .../app/utils/IncomeCalculator.java | 6 +-- .../app/utils/SubmissionUtilities.java | 9 ++-- src/main/resources/flows-config.yaml | 2 + .../ClearIncompleteIncomeIterationsTest.java | 46 ++++++++++++++++ ...IncompleteIterationsFromHouseholdTest.java | 53 +++++++++++++++++++ .../app/utils/IncomeCalculatorTest.java | 37 +++++++++++++ .../app/utils/SubmissionUtilityTest.java | 31 +++++++++++ 9 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterations.java create mode 100644 src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHousehold.java create mode 100644 src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterationsTest.java create mode 100644 src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHouseholdTest.java create mode 100644 src/test/java/org/mdbenefits/app/utils/IncomeCalculatorTest.java diff --git a/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterations.java b/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterations.java new file mode 100644 index 00000000..d61f60b9 --- /dev/null +++ b/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterations.java @@ -0,0 +1,35 @@ +package org.mdbenefits.app.submission.actions; + + +import formflow.library.config.submission.Action; +import formflow.library.data.Submission; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class ClearIncompleteIncomeIterations implements Action { + + /** + * This action will clear out the incomplete iterations from the income subflow. These can be created when the user goes back + * to the beginning of the subflow before completing an iteration. + * + * @param submission submission object the action is associated with, not null + * @param id the id of the current iteration being submitted when this action is run, not null + */ + @Override + public void run(Submission submission, String id) { + ArrayList> incomeSubflow = (ArrayList>) submission.getInputData().get("income"); + if (incomeSubflow != null) { + List> filteredIncomeSubflow = incomeSubflow.stream() + .filter(iteration -> id.equals(iteration.get("uuid")) || + Boolean.TRUE.equals(iteration.get(Submission.ITERATION_IS_COMPLETE_KEY))) + .collect(Collectors.toList()); + submission.getInputData().put("income", filteredIncomeSubflow); + } + } +} diff --git a/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHousehold.java b/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHousehold.java new file mode 100644 index 00000000..5d480cd8 --- /dev/null +++ b/src/main/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHousehold.java @@ -0,0 +1,42 @@ +package org.mdbenefits.app.submission.actions; + +import formflow.library.config.submission.Action; +import formflow.library.data.Submission; +import formflow.library.data.SubmissionRepository; +import formflow.library.data.SubmissionRepositoryService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class ClearIncompleteIterationsFromHousehold implements Action { + + SubmissionRepositoryService submissionRepositoryService; + + public ClearIncompleteIterationsFromHousehold(SubmissionRepositoryService submissionRepositoryService) { + this.submissionRepositoryService = submissionRepositoryService; + } + + /** + * This action will clear out the incomplete iterations from the household subflow. These can be created when the user goes back + * to the beginning of the subflow before completing an iteration. + * + * @param submission submission object the action is associated with, not null + */ + @Override + public void run(Submission submission) { + ArrayList> household = (ArrayList>) submission.getInputData().get("household"); + if (household != null) { + List> filteredHouseholdSubflow = household.stream() + .filter(iteration -> Boolean.TRUE.equals(iteration.get(Submission.ITERATION_IS_COMPLETE_KEY))) + .collect(Collectors.toList()); + submission.getInputData().put("household", filteredHouseholdSubflow); + submissionRepositoryService.save(submission); + } + } +} diff --git a/src/main/java/org/mdbenefits/app/utils/IncomeCalculator.java b/src/main/java/org/mdbenefits/app/utils/IncomeCalculator.java index cfc56e77..95bb2112 100644 --- a/src/main/java/org/mdbenefits/app/utils/IncomeCalculator.java +++ b/src/main/java/org/mdbenefits/app/utils/IncomeCalculator.java @@ -21,11 +21,11 @@ public Double totalFutureEarnedIncome() { // if submission.getInputData(). var jobs = (List>) submission.getInputData() .getOrDefault("income", new ArrayList>()); - var total = jobs.stream() + var completedJobs = jobs.stream().filter(job -> job.get(Submission.ITERATION_IS_COMPLETE_KEY).equals(true)).toList(); + + return completedJobs.stream() .map(IncomeCalculator::futureIncomeForJob) .reduce(0.0d, Double::sum); - - return total; } public static double futureIncomeForJob(Map job) throws NumberFormatException { diff --git a/src/main/java/org/mdbenefits/app/utils/SubmissionUtilities.java b/src/main/java/org/mdbenefits/app/utils/SubmissionUtilities.java index c5dd0e11..6416a895 100644 --- a/src/main/java/org/mdbenefits/app/utils/SubmissionUtilities.java +++ b/src/main/java/org/mdbenefits/app/utils/SubmissionUtilities.java @@ -7,6 +7,7 @@ import java.time.OffsetDateTime; import java.util.*; +import static formflow.library.data.Submission.ITERATION_IS_COMPLETE_KEY; import static formflow.library.inputs.FieldNameMarkers.DYNAMIC_FIELD_MARKER; import static java.util.Collections.emptyList; @@ -183,9 +184,11 @@ public static ArrayList> getHouseholdIncomeReviewItems(S .getOrDefault("lastName", ""); var notYetShownNames = getHouseholdMemberNames(submission); ArrayList> items = new ArrayList<>(); + List> incomeSubflowIterations = (List>) submission.getInputData() + .getOrDefault("income", new ArrayList>()); - for (var job : (List>) submission.getInputData() - .getOrDefault("income", new ArrayList>())) { + for (var job : incomeSubflowIterations.stream().filter(job -> + job.get(ITERATION_IS_COMPLETE_KEY).equals(true)).toList()) { var item = new HashMap(); var name = job.get("householdMemberJobAdd").equals("you") ? applicantFullName : job.get("householdMemberJobAdd"); item.put("name", name); @@ -266,7 +269,7 @@ public static boolean isNoneOfAboveSelection(@Nullable Object value) { * Uses the "birthDay", "birthMonth", and "birthYear" fields from the input data to create an "MM/DD/YYYY" formatted string. * * @param inputData input data map to pull the dates from. - * @return + * @return formatted birthdate string in format of "MM/DD/YYYY" */ public static String getFormattedBirthdate(Map inputData) { Integer month = Integer.valueOf((String) inputData.getOrDefault("birthMonth", "0")); diff --git a/src/main/resources/flows-config.yaml b/src/main/resources/flows-config.yaml index 0b450b10..fa5aeeee 100644 --- a/src/main/resources/flows-config.yaml +++ b/src/main/resources/flows-config.yaml @@ -127,6 +127,7 @@ flow: ## END applicant information householdList: + beforeDisplayAction: ClearIncompleteIterationsFromHousehold nextScreens: - name: householdSeasonalFarmWorker householdInfo: @@ -175,6 +176,7 @@ flow: nextScreens: - name: householdIncomeWho householdIncomeWho: + beforeSaveAction: ClearIncompleteIncomeIterations subflow: income nextScreens: - name: householdEmployerName diff --git a/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterationsTest.java b/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterationsTest.java new file mode 100644 index 00000000..d669f342 --- /dev/null +++ b/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIncomeIterationsTest.java @@ -0,0 +1,46 @@ +package org.mdbenefits.app.submission.actions; + +import static org.assertj.core.api.Assertions.assertThat; + +import formflow.library.data.Submission; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ClearIncompleteIncomeIterationsTest { + + @Test + void shouldClearIncompleteIterationsFromIncomeSubflow() { + ClearIncompleteIncomeIterations clearIncompleteIncomeIterations = new ClearIncompleteIncomeIterations(); + Submission submission = new Submission(); + var income = new ArrayList>(); + var job1 = new HashMap(); + job1.put(Submission.ITERATION_IS_COMPLETE_KEY, true); + job1.put("uuid", "complete iteration"); + job1.put("employerName", "ACME Inc"); + job1.put("payPeriod", "It varies"); + job1.put("payPeriodAmount", 400.0); + var job2 = new HashMap(); + job2.put(Submission.ITERATION_IS_COMPLETE_KEY, false); + job2.put("uuid", "Current Iteration"); + job2.put("employerName", "Monsters Inc"); + job2.put("payPeriodAmount", 200.0); + var job3 = new HashMap(); + job3.put(Submission.ITERATION_IS_COMPLETE_KEY, false); + job3.put("uuid", "Incomplete Iteration"); + job3.put("employerName", "Disney Inc"); + income.add(job1); + income.add(job2); + income.add(job3); + HashMap inputData = new HashMap<>(); + inputData.put("income", income); + submission.setInputData(inputData); + clearIncompleteIncomeIterations.run(submission, "Current Iteration"); + ArrayList> incomeSubflow = (ArrayList>) submission.getInputData().get("income"); + assertThat(incomeSubflow).size().isEqualTo(2); + assertThat(incomeSubflow).doesNotContain(job3); + assertThat(incomeSubflow).isEqualTo(List.of(job1, job2)); + } +} \ No newline at end of file diff --git a/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHouseholdTest.java b/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHouseholdTest.java new file mode 100644 index 00000000..68ef9555 --- /dev/null +++ b/src/test/java/org/mdbenefits/app/submission/actions/ClearIncompleteIterationsFromHouseholdTest.java @@ -0,0 +1,53 @@ +package org.mdbenefits.app.submission.actions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import formflow.library.data.Submission; +import formflow.library.data.SubmissionRepositoryService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.context.ActiveProfiles; + +@ActiveProfiles("test") +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class ClearIncompleteIterationsFromHouseholdTest { + + @Autowired + SubmissionRepositoryService submissionRepositoryService; + + @Test + void shouldClearIncompleteIterationsFromHouseholdSubflow() { + ClearIncompleteIterationsFromHousehold clearIncompleteIterationsFromHousehold = new ClearIncompleteIterationsFromHousehold(submissionRepositoryService); + Submission submission = new Submission().builder() + .id(UUID.randomUUID()) + .flow("flow") + .build(); + var household = new ArrayList>(); + var householdMember1 = new HashMap(); + householdMember1.put(Submission.ITERATION_IS_COMPLETE_KEY, true); + var householdMember2 = new HashMap(); + householdMember2.put(Submission.ITERATION_IS_COMPLETE_KEY, true); + var householdMember3 = new HashMap(); + householdMember3.put(Submission.ITERATION_IS_COMPLETE_KEY, false); + household.add(householdMember1); + household.add(householdMember2); + household.add(householdMember3); + HashMap inputData = new HashMap<>(); + inputData.put("household", household); + submission.setInputData(inputData); + clearIncompleteIterationsFromHousehold.run(submission); + ArrayList> incomeSubflow = (ArrayList>) submission.getInputData().get("household"); + assertThat(incomeSubflow).size().isEqualTo(2); + assertThat(incomeSubflow).doesNotContain(householdMember3); + assertThat(incomeSubflow).isEqualTo(List.of(householdMember1, householdMember2)); + } +} \ No newline at end of file diff --git a/src/test/java/org/mdbenefits/app/utils/IncomeCalculatorTest.java b/src/test/java/org/mdbenefits/app/utils/IncomeCalculatorTest.java new file mode 100644 index 00000000..2aea31c0 --- /dev/null +++ b/src/test/java/org/mdbenefits/app/utils/IncomeCalculatorTest.java @@ -0,0 +1,37 @@ +package org.mdbenefits.app.utils; + +import static org.junit.jupiter.api.Assertions.*; + +import formflow.library.data.Submission; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class IncomeCalculatorTest { + + @Test + void totalFutureEarnedIncomeShouldFilterIncompleteSubflowIterations() { + // given + var submission = new Submission(); + var income = new ArrayList>(); + var job1 = new HashMap(); + job1.put(Submission.ITERATION_IS_COMPLETE_KEY, true); + job1.put("employerName", "ACME Inc"); + job1.put("payPeriod", "It varies"); + job1.put("payPeriodAmount", 400.0); + var job2 = new HashMap(); + job2.put(Submission.ITERATION_IS_COMPLETE_KEY, false); + job2.put("employerName", "Monsters Inc"); + job2.put("payPeriodAmount", 200.0); + income.add(job1); + income.add(job2); + submission.setInputData(Map.of("income", income)); + var calculator = new IncomeCalculator(submission); + + var total = calculator.totalFutureEarnedIncome(); + + // Amount will exclude amount from incomplete iteration + assertEquals(400.0, total); + } +} \ No newline at end of file diff --git a/src/test/java/org/mdbenefits/app/utils/SubmissionUtilityTest.java b/src/test/java/org/mdbenefits/app/utils/SubmissionUtilityTest.java index 417db51a..4ddb2d71 100644 --- a/src/test/java/org/mdbenefits/app/utils/SubmissionUtilityTest.java +++ b/src/test/java/org/mdbenefits/app/utils/SubmissionUtilityTest.java @@ -2,7 +2,11 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import formflow.library.data.Submission; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -22,4 +26,31 @@ public void shouldBeCorrectlyFormatted(String month, String day, String year, St assertThat(SubmissionUtilities.getFormattedBirthdate(inputData)).isEqualTo(fullDate); } + @Test + public void getHouseholdIncomeReviewItemsShouldIgnoreIncompleteSubflowIterations() { + Submission submission = new Submission(); + HashMap job1 = new HashMap<>(); + job1.put(Submission.ITERATION_IS_COMPLETE_KEY, true); + job1.put("householdMemberJobAdd", "you"); + job1.put("employerName", "ACME Inc"); + job1.put("payPeriod", "It varies"); + job1.put("payPeriodAmount", 400.0); + + HashMap job2 = new HashMap<>(); + job2.put(Submission.ITERATION_IS_COMPLETE_KEY, false); + job2.put("employerName", "Monsters Inc"); + job2.put("payPeriodAmount", 200.0); + + ArrayList> income = new ArrayList<>(); + income.add(job1); + income.add(job2); + + HashMap inputData = new HashMap<>(); + inputData.put("income", income); + submission.setInputData(inputData); + + ArrayList> householdIncomeReviewItems = SubmissionUtilities.getHouseholdIncomeReviewItems(submission); + assertThat(householdIncomeReviewItems.stream().noneMatch(item -> item.getOrDefault("jobName", "").equals("Monsters Inc"))).isTrue(); + assertThat(householdIncomeReviewItems.stream().noneMatch(item -> item.getOrDefault("income", "").equals("$200"))).isTrue(); + } }