From b2d644d1707e57b00b00452f8f481d94873e8a79 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 16 Oct 2022 12:11:24 +0200
Subject: [PATCH] #721 - do not force specification of start and end date
 together

---
 .../TransactionSearchSpecifications.java      | 32 +++++++++++++++----
 .../src/main/resources/static/js/search.js    |  6 ++--
 .../TransactionSearchSpecificationsTest.java  | 22 +++++++++++++
 3 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java
index b4907d6c2..8f15cc4b8 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java
@@ -7,12 +7,12 @@ import de.deadlocker8.budgetmaster.tags.Tag;
 import de.deadlocker8.budgetmaster.tags.Tag_;
 import org.springframework.data.jpa.domain.Specification;
 
-import javax.persistence.criteria.Join;
-import javax.persistence.criteria.JoinType;
-import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.*;
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 
 public class TransactionSearchSpecifications
 {
@@ -92,10 +92,10 @@ public class TransactionSearchSpecifications
 
 			final Predicate allPredicates = builder.and(accountStatePredicate, predicatesCombined);
 
-			if(search.getStartDate() != null && search.getEndDate() != null)
+			final Optional<Predicate> datePredicateOptional = getDatePredicate(builder, transaction, search);
+			if(datePredicateOptional.isPresent())
 			{
-				final Predicate dateConstraint = builder.between(transaction.get(Transaction_.date), search.getStartDate(), search.getEndDate());
-				return builder.and(dateConstraint, allPredicates);
+				return builder.and(datePredicateOptional.get(), allPredicates);
 			}
 
 			return allPredicates;
@@ -115,4 +115,24 @@ public class TransactionSearchSpecifications
 
 		return allowedAccountStates;
 	}
+
+	private static Optional<Predicate> getDatePredicate(CriteriaBuilder builder, Root<Transaction> transaction, Search search)
+	{
+		if(search.getStartDate() != null && search.getEndDate() != null)
+		{
+			return Optional.of(builder.between(transaction.get(Transaction_.date), search.getStartDate(), search.getEndDate()));
+		}
+
+		if(search.getStartDate() != null)
+		{
+			return Optional.of(builder.between(transaction.get(Transaction_.date), search.getStartDate(), LocalDate.of(2100, 1, 1)));
+		}
+
+		if(search.getEndDate() != null)
+		{
+			return Optional.of(builder.between(transaction.get(Transaction_.date), LocalDate.of(2000, 1, 1), search.getEndDate()));
+		}
+
+		return Optional.empty();
+	}
 }
diff --git a/BudgetMasterServer/src/main/resources/static/js/search.js b/BudgetMasterServer/src/main/resources/static/js/search.js
index 5f0d6f57b..317ebea96 100644
--- a/BudgetMasterServer/src/main/resources/static/js/search.js
+++ b/BudgetMasterServer/src/main/resources/static/js/search.js
@@ -66,18 +66,18 @@ $(document).ready(function()
 
 function createSearchDatePickerEnd(minDate, selectedDate)
 {
+    let shouldSetDefaultDate = endDate !== null;
     if(selectedDate < minDate)
     {
+        shouldSetDefaultDate = selectedDate !== null;
         selectedDate = minDate;
     }
 
-
-
     return M.Datepicker.init(document.getElementById('search-datepicker-end'), {
         yearRange: 50,
         firstDay: 1,
         showClearBtn: false,
-        setDefaultDate: true,
+        setDefaultDate: shouldSetDefaultDate,
         minDate: minDate,
         defaultDate: selectedDate,
         autoClose: true,
diff --git a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java
index 2315d8ae2..62045b38e 100644
--- a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java
+++ b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java
@@ -346,4 +346,26 @@ class TransactionSearchSpecificationsTest
 		assertThat(results).hasSize(4)
 				.contains(transaction2, transferTransaction, repeatingTransaction, transactionFromHiddenAccount);
 	}
+
+	@Test
+	void getMatches_OnlyStartDateDefined()
+	{
+		Search search = new Search("", true, true, true, true, true, 0, LocalDate.of(2018, 1, 1), null);
+		Specification spec = TransactionSearchSpecifications.withDynamicQuery(search);
+
+		List<Transaction> results = transactionRepository.findAll(spec);
+		assertThat(results).hasSize(5)
+				.contains(transaction2, transferTransaction, repeatingTransaction, transactionFromHiddenAccount, transactionWithMultipleTags);
+	}
+
+	@Test
+	void getMatches_OnlyEndDateDefined()
+	{
+		Search search = new Search("", true, true, true, true, true, 0, null, LocalDate.of(2019, 1, 1));
+		Specification spec = TransactionSearchSpecifications.withDynamicQuery(search);
+
+		List<Transaction> results = transactionRepository.findAll(spec);
+		assertThat(results).hasSize(5)
+				.contains(transaction1, transaction2, transferTransaction, repeatingTransaction, transactionFromHiddenAccount);
+	}
 }
\ No newline at end of file
-- 
GitLab