From 1dd98e8239d74a4aa1a131625dcb811e099a59a2 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sat, 7 Nov 2020 13:08:42 +0100
Subject: [PATCH] Fixed #542 - new transaction: allow date input without using
 the graphical datepicker

---
 .../de/deadlocker8/budgetmaster/Main.java     |  1 +
 .../deadlocker8/budgetmaster/ProgramArgs.java |  5 ++
 .../resources/languages/base_de.properties    |  2 +
 .../resources/languages/base_en.properties    |  1 +
 src/main/resources/static/js/transactions.js  | 67 ++++++++++++++++++-
 .../transactions/newTransactionMacros.ftl     |  5 +-
 6 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/src/main/java/de/deadlocker8/budgetmaster/Main.java b/src/main/java/de/deadlocker8/budgetmaster/Main.java
index 65a80695a..77401e212 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/Main.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/Main.java
@@ -68,6 +68,7 @@ public class Main extends SpringBootServletInitializer implements ApplicationRun
 		Localization.load();
 
 		ProgramArgs.setArgs(Arrays.asList(args));
+		LOGGER.debug(MessageFormat.format("Starting with ProgramArgs: {0}", ProgramArgs.getArgs()));
 
 		Path applicationSupportFolder = getApplicationSupportFolder();
 		PathUtils.createDirectoriesIfNotExists(applicationSupportFolder);
diff --git a/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java b/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java
index f36195409..b15699d79 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java
@@ -60,4 +60,9 @@ public class ProgramArgs
 	{
 		return RunMode.currentRunMode.equals(RunMode.TEST);
 	}
+
+	public static boolean isUseSimpleDatepickerForTransactions()
+	{
+		return ProgramArgs.getArgs().contains("--useSimpleDatepicker");
+	}
 }
\ No newline at end of file
diff --git a/src/main/resources/languages/base_de.properties b/src/main/resources/languages/base_de.properties
index 71c0361ed..3215447b3 100644
--- a/src/main/resources/languages/base_de.properties
+++ b/src/main/resources/languages/base_de.properties
@@ -143,6 +143,8 @@ warning.settings.password.confirmation.wrong=Passwort und Passwort Wiederholung
 warning.empty.chart.name=Bitte gib einen Namen ein.
 warning.empty.chart.script=Bitte gib ein Script ein.
 warning.duplicate.template.name=Es existiert bereits eine Vorlage mit diesem Namen.
+warning.transaction.date=Das angegebene Datum entspricht nicht dem erlaubten Format. Erwartetes Format: DD.MM.YY oder DD.MM.YYYY.
+
 
 # UI
 menu.home=Startseite
diff --git a/src/main/resources/languages/base_en.properties b/src/main/resources/languages/base_en.properties
index 83b9f94cd..15c64f114 100644
--- a/src/main/resources/languages/base_en.properties
+++ b/src/main/resources/languages/base_en.properties
@@ -143,6 +143,7 @@ warning.settings.password.confirmation.wrong=Password and password confirmation
 warning.empty.chart.name=Please insert a name.
 warning.empty.chart.script=Please insert a script.
 warning.duplicate.template.name=A template with this name is already existing.
+warning.transaction.date=The specified date does not correspond to the allowed format. Expected format: DD.MM.YY or DD.MM.YYYY.
 
 # UI
 menu.home=Home
diff --git a/src/main/resources/static/js/transactions.js b/src/main/resources/static/js/transactions.js
index 7869d0530..501b4a40a 100644
--- a/src/main/resources/static/js/transactions.js
+++ b/src/main/resources/static/js/transactions.js
@@ -26,6 +26,24 @@ $(document).ready(function()
         $("#transaction-description").characterCounter();
     }
 
+    if($(".datepicker-simple".length) && $("#transaction-repeating-end-date-input").length)
+    {
+        let pickerEndDate = document.getElementById('transaction-repeating-end-date-input');
+
+        // select corresponding radio button
+        let endDate = document.getElementById("repeating-end-date");
+
+        pickerEndDate.addEventListener('input', function()
+        {
+            endDate.checked = true;
+        });
+
+        pickerEndDate.addEventListener('focus', function()
+        {
+            endDate.checked = true;
+        });
+    }
+
     if($(".datepicker").length)
     {
         let pickerStartDate = M.Datepicker.init(document.getElementById('transaction-datepicker'), {
@@ -254,8 +272,10 @@ let transactionRepeatingEndAfterXTimesInputID = "#transaction-repeating-end-afte
 
 AMOUNT_REGEX = new RegExp("^-?\\d+(,\\d+)?(\\.\\d+)?$");
 ALLOWED_CHARACTERS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", "."];
+DATE_REGEX_SHORT = new RegExp("^(\\d{2}.\\d{2}.)(\\d{2})$");
+DATE_REGEX_LONG = new RegExp("^\\d{2}.\\d{2}.\\d{4}$");
 
-function validateAmount(text, allowEmpty=false)
+function validateAmount(text, allowEmpty = false)
 {
     let id = "transaction-amount";
 
@@ -281,7 +301,36 @@ function validateAmount(text, allowEmpty=false)
     }
 }
 
-function validateForm(allowEmptyAmount=false)
+function validateDate(inputId)
+{
+    let dateInput = document.getElementById(inputId);
+    dateInput.value = dateInput.value.trim();
+    let date = dateInput.value;
+
+    if(date.match(DATE_REGEX_LONG) != null)
+    {
+        removeTooltip(inputId);
+        return true;
+    }
+
+    let match = date.match(DATE_REGEX_SHORT);
+    if(match != null)
+    {
+        let dayAndMonth = match[1];
+        let year = match[2];
+
+        dateInput.value = dayAndMonth + '20' + year;
+        removeTooltip(inputId);
+        return true;
+    }
+    else
+    {
+        addTooltip(inputId, dateValidationMessage);
+        return false;
+    }
+}
+
+function validateForm(allowEmptyAmount = false)
 {
     // amount
     let isValidAmount = validateAmount($('#transaction-amount').val(), allowEmptyAmount);
@@ -290,6 +339,13 @@ function validateForm(allowEmptyAmount=false)
         return false;
     }
 
+    // start date
+    let isValidDate = validateDate('transaction-datepicker');
+    if(!isValidDate)
+    {
+        return false;
+    }
+
     // description
     let description = document.getElementById('transaction-description').value;
     if(description.length > 250)
@@ -342,6 +398,13 @@ function validateForm(allowEmptyAmount=false)
 
         if(endDate.checked)
         {
+            // start date
+            let isValidDate = validateDate('transaction-repeating-end-date-input');
+            if(!isValidDate)
+            {
+                return false;
+            }
+
             endInput.value = $("#transaction-repeating-end-date-input").val();
         }
     }
diff --git a/src/main/resources/templates/transactions/newTransactionMacros.ftl b/src/main/resources/templates/transactions/newTransactionMacros.ftl
index cb14bad35..1f2e7efac 100644
--- a/src/main/resources/templates/transactions/newTransactionMacros.ftl
+++ b/src/main/resources/templates/transactions/newTransactionMacros.ftl
@@ -81,6 +81,7 @@
     <script>
         amountValidationMessage = "${locale.getString("warning.transaction.amount")}";
         numberValidationMessage = "${locale.getString("warning.empty.number")}";
+        dateValidationMessage = "${locale.getString("warning.transaction.date")}";
     </script>
 </#macro>
 
@@ -136,7 +137,7 @@
                 <#assign startDate = dateService.getLongDateString(currentDate)/>
             </#if>
 
-            <input id="transaction-datepicker" type="text" class="datepicker" name="date" value="${startDate}">
+            <input id="transaction-datepicker" type="text" class="datepicker<#if programArgs.isUseSimpleDatepickerForTransactions()>-simple</#if>" name="date" value="${startDate}">
             <label class="input-label" for="transaction-datepicker">${locale.getString("transaction.new.label.date")}</label>
         </div>
     </div>
@@ -329,7 +330,7 @@
                     <td class="cell">${locale.getString("repeating.end.date")}</td>
                     <td class="cell input-cell">
                         <div class="input-field no-margin">
-                            <input class="datepicker no-margin input-min-width" id="transaction-repeating-end-date-input" type="text" value="${endDate}">
+                            <input class="datepicker<#if programArgs.isUseSimpleDatepickerForTransactions()>-simple</#if> no-margin input-min-width" id="transaction-repeating-end-date-input" type="text" value="${endDate}">
                             <label for="transaction-repeating-end-date-input"></label>
                         </div>
                     </td>
-- 
GitLab