From 9f25418bf83b38e34faff6fcf4f9250ef23ff9d9 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 9 Aug 2020 16:17:23 +0200
Subject: [PATCH] #516 - create modal for new type selection

---
 .../transactions/TransactionController.java   |  65 +++++++-
 .../transactions/TransactionType.java         |  45 ++++++
 src/main/resources/languages/_de.properties   |   2 +
 src/main/resources/languages/_en.properties   |   2 +
 src/main/resources/static/js/templates.js     | 122 --------------
 .../resources/static/js/transactionActions.js | 150 ++++++++++++++++++
 .../transactions/changeTypeModal.ftl          |  38 +++++
 .../transactions/newTransactionMacros.ftl     |   6 +-
 .../transactions/newTransactionNormal.ftl     |   3 +
 .../transactions/newTransactionRepeating.ftl  |   3 +
 .../transactions/newTransactionTransfer.ftl   |   3 +
 11 files changed, 313 insertions(+), 126 deletions(-)
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionType.java
 create mode 100644 src/main/resources/static/js/transactionActions.js
 create mode 100644 src/main/resources/templates/transactions/changeTypeModal.ftl

diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
index acb0b578b..6d96e1090 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
@@ -27,6 +27,7 @@ import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.text.MessageFormat;
 import java.util.List;
 import java.util.Optional;
 
@@ -214,7 +215,7 @@ public class TransactionController extends BaseController
 	public String editTransaction(Model model, @CookieValue("currentDate") String cookieDate, @PathVariable("ID") Integer ID)
 	{
 		Optional<Transaction> transactionOptional = transactionService.getRepository().findById(ID);
-		if(!transactionOptional.isPresent())
+		if(transactionOptional.isEmpty())
 		{
 			throw new ResourceNotFoundException();
 		}
@@ -257,4 +258,66 @@ public class TransactionController extends BaseController
 		model.addAttribute("highlightID", ID);
 		return "transactions/transactions";
 	}
+
+	@GetMapping("/transactions/{ID}/changeTypeModal")
+	public String changeTypeModal(Model model, @PathVariable("ID") Integer ID)
+	{
+		final Optional<Transaction> transactionOptional = transactionService.getRepository().findById(ID);
+		if(transactionOptional.isEmpty())
+		{
+			throw new ResourceNotFoundException();
+		}
+
+		model.addAttribute("transaction", transactionOptional.get());
+		return "transactions/changeTypeModal";
+	}
+
+	@GetMapping("/transactions/{ID}/changeType")
+	public String changeTypeModal(Model model, @PathVariable("ID") Integer ID,
+								  @CookieValue("currentDate") String cookieDate,
+								  @RequestParam(value = "newType") int newType)
+	{
+		final Optional<Transaction> transactionOptional = transactionService.getRepository().findById(ID);
+		if(transactionOptional.isEmpty())
+		{
+			throw new ResourceNotFoundException();
+		}
+
+		final Optional<TransactionType> transactionTypeOptional = TransactionType.getByID(newType);
+		if(transactionTypeOptional.isEmpty())
+		{
+			throw new IllegalArgumentException();
+		}
+
+		Transaction transaction = transactionOptional.get();
+		// select first transaction in order to provide correct start date for repeating transactions
+		if(transaction.getRepeatingOption() != null)
+		{
+			transaction = transaction.getRepeatingOption().getReferringTransactions().get(0);
+		}
+
+		final TransactionType newTransactionType = transactionTypeOptional.get();
+		LOGGER.debug(MessageFormat.format("Changing transaction type to {0} for transaction with ID {1}", newTransactionType, transaction.getID()));
+
+
+		String redirectUrl = "";
+		switch(newTransactionType)
+		{
+			case NORMAL:
+				transaction.setTransferAccount(null);
+				redirectUrl = "transactions/newTransactionNormal";
+				break;
+			case REPEATING:
+				redirectUrl = "transactions/newTransactionRepeating";
+				break;
+			case TRANSFER:
+				redirectUrl = "transactions/newTransactionTransfer";
+				break;
+		}
+
+		DateTime date = dateService.getDateTimeFromCookie(cookieDate);
+		transactionService.prepareModelNewOrEdit(model, true, date, transaction, accountService.getAllAccountsAsc());
+
+		return redirectUrl;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionType.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionType.java
new file mode 100644
index 000000000..2847e8543
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionType.java
@@ -0,0 +1,45 @@
+package de.deadlocker8.budgetmaster.transactions;
+
+import java.util.Optional;
+
+public enum TransactionType
+{
+	NORMAL(1),
+	REPEATING(2),
+	TRANSFER(3);
+
+	private int typeID;
+
+	TransactionType(int typeID)
+	{
+		this.typeID = typeID;
+	}
+
+	public int getTypeID()
+	{
+		return typeID;
+	}
+
+	public static Optional<TransactionType> getByID(int typeID)
+	{
+		switch(typeID)
+		{
+			case 1:
+				return Optional.of(NORMAL);
+			case 2:
+				return Optional.of(REPEATING);
+			case 3:
+				return Optional.of(TRANSFER);
+			default:
+				return Optional.empty();
+		}
+	}
+
+	@Override
+	public String toString()
+	{
+		return "TransactionType{" +
+				"typeID=" + typeID +
+				'}';
+	}
+}
diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties
index ad554dae3..859ce427d 100644
--- a/src/main/resources/languages/_de.properties
+++ b/src/main/resources/languages/_de.properties
@@ -68,6 +68,8 @@ placeholder.advice=Füge {0} hinzu
 save.as.template=Vorlage erzeugen
 save.as.template.errorsInForm=Vorlage konnte nicht erstellt werden, da Fehler im Formular existieren!
 transaction.change.type=Buchungstyp ändern
+transaction.change.type.warning=Hinweis: Nicht gespeicherte Änderungen gehen verloren!
+transaction.change.type.new=Neuer Buchungstyp
 
 # WEEK DAYS
 monday=Montag
diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties
index d0fcf399d..707179352 100644
--- a/src/main/resources/languages/_en.properties
+++ b/src/main/resources/languages/_en.properties
@@ -68,6 +68,8 @@ placeholder.advice=Get started by adding {0}
 save.as.template=Create template
 save.as.template.errorsInForm=Template could not be created because errors exist in the form!
 transaction.change.type=Change type
+transaction.change.type.warning=Note: Unsaved changes will be lost!
+transaction.change.type.new=New type
 
 # WEEK DAYS
 monday=Monday
diff --git a/src/main/resources/static/js/templates.js b/src/main/resources/static/js/templates.js
index 2935786e9..eae1407d3 100644
--- a/src/main/resources/static/js/templates.js
+++ b/src/main/resources/static/js/templates.js
@@ -5,24 +5,6 @@ $(document).ready(function()
         $('#modalConfirmDelete').modal('open');
     }
 
-    if($('#buttonSaveAsTemplate').length)
-    {
-        M.FloatingActionButton.init(document.querySelectorAll('#transaction-actions-button'), {});
-
-        $('.transaction-action').click(function()
-        {
-            let actionType = $(this).attr('data-action-type');
-            if(actionType === 'saveAsTemplate')
-            {
-                openSaveAsTemplateModal(this);
-            }
-            else if(actionType === 'changeType')
-            {
-                changeTransactionType(this);
-            }
-        });
-    }
-
     M.Collapsible.init(document.querySelector('.collapsible.expandable'), {
         accordion: false
     });
@@ -70,84 +52,6 @@ function handleIncludeAccountCheckbox(checkboxID, selectID)
     });
 }
 
-function createAndOpenModal(data)
-{
-    let modalID = '#modalCreateFromTransaction';
-
-    $('#saveAsTemplateModalContainer').html(data);
-    $(modalID).modal();
-    $(modalID).modal('open');
-    let templateNameInput = document.getElementById('template-name');
-    templateNameInput.focus();
-    $(templateNameInput).on('keypress', function(e)
-    {
-        let code = e.keyCode || e.which;
-        if(code === 13)
-        {
-            saveAsTemplate();
-        }
-    });
-
-    $('#buttonCreateTemplate').click(function()
-    {
-        saveAsTemplate();
-    });
-}
-
-function saveAsTemplate()
-{
-    // validate template name
-    let templateName = document.getElementById('template-name').value;
-    let isValid = validateTemplateName(templateName);
-    if(!isValid)
-    {
-        return
-    }
-
-    let form = document.getElementsByName('NewTransaction')[0];
-    form.appendChild(createAdditionalHiddenInput('templateName', templateName));
-    form.appendChild(createAdditionalHiddenInput('includeCategory', document.getElementById('include-category').checked));
-    form.appendChild(createAdditionalHiddenInput('includeAccount', document.getElementById('include-account').checked));
-
-    // replace form target url
-    form.action = $('#buttonCreateTemplate').attr('data-url');
-    form.submit();
-}
-
-function validateTemplateName(templateName)
-{
-    if(templateName.length === 0)
-    {
-        addTooltip('template-name', templateNameEmptyValidationMessage);
-        return false;
-    }
-    else
-    {
-        removeTooltip('template-name');
-    }
-
-    if(existingTemplateNames.includes(templateName))
-    {
-        addTooltip('template-name', templateNameDuplicateValidationMessage);
-        return false;
-    }
-    else
-    {
-        removeTooltip('template-name');
-    }
-
-    return true;
-}
-
-function createAdditionalHiddenInput(name, value)
-{
-    let newInput = document.createElement('input');
-    newInput.setAttribute('type', 'hidden');
-    newInput.setAttribute('name', name);
-    newInput.setAttribute('value', value);
-    return newInput;
-}
-
 function searchTemplates(searchText)
 {
     searchText = searchText.trim();
@@ -192,29 +96,3 @@ function searchTemplates(searchText)
         collapsible.classList.remove('hidden');
     }
 }
-
-function openSaveAsTemplateModal(item)
-{
-    // check if transaction form is valid
-    let isValidForm = validateForm(true);
-    if(!isValidForm)
-    {
-        $('#modalCreateFromTransaction').modal('close');
-        M.toast({html: createTemplateWithErrorInForm});
-        return;
-    }
-
-    $.ajax({
-        type: 'GET',
-        url: $(item).attr('data-url'),
-        data: {},
-        success: function(data)
-        {
-            createAndOpenModal(data)
-        }
-    });
-}
-
-function changeTransactionType(item)
-{
-}
\ No newline at end of file
diff --git a/src/main/resources/static/js/transactionActions.js b/src/main/resources/static/js/transactionActions.js
new file mode 100644
index 000000000..31218d7ed
--- /dev/null
+++ b/src/main/resources/static/js/transactionActions.js
@@ -0,0 +1,150 @@
+$(document).ready(function()
+{
+    M.FloatingActionButton.init(document.querySelectorAll('#transaction-actions-button'), {});
+
+    $('.transaction-action').click(function()
+    {
+        let actionType = $(this).attr('data-action-type');
+        if(actionType === 'saveAsTemplate')
+        {
+            openSaveAsTemplateModal(this);
+        }
+        else if(actionType === 'changeType')
+        {
+            openChangeTransactionTypeModal(this);
+        }
+    });
+});
+
+function openSaveAsTemplateModal(item)
+{
+    // check if transaction form is valid
+    let isValidForm = validateForm(true);
+    if(!isValidForm)
+    {
+        $('#modalCreateFromTransaction').modal('close');
+        M.toast({html: createTemplateWithErrorInForm});
+        return;
+    }
+
+    $.ajax({
+        type: 'GET',
+        url: $(item).attr('data-url'),
+        data: {},
+        success: function(data)
+        {
+            createAndOpenModal(data)
+        }
+    });
+}
+
+function createAndOpenModal(data)
+{
+    let modalID = '#modalCreateFromTransaction';
+
+    $('#saveAsTemplateModalContainer').html(data);
+    $(modalID).modal();
+    $(modalID).modal('open');
+    let templateNameInput = document.getElementById('template-name');
+    templateNameInput.focus();
+    $(templateNameInput).on('keypress', function(e)
+    {
+        let code = e.keyCode || e.which;
+        if(code === 13)
+        {
+            saveAsTemplate();
+        }
+    });
+
+    $('#buttonCreateTemplate').click(function()
+    {
+        saveAsTemplate();
+    });
+}
+
+function saveAsTemplate()
+{
+    // validate template name
+    let templateName = document.getElementById('template-name').value;
+    let isValid = validateTemplateName(templateName);
+    if(!isValid)
+    {
+        return
+    }
+
+    let form = document.getElementsByName('NewTransaction')[0];
+    form.appendChild(createAdditionalHiddenInput('templateName', templateName));
+    form.appendChild(createAdditionalHiddenInput('includeCategory', document.getElementById('include-category').checked));
+    form.appendChild(createAdditionalHiddenInput('includeAccount', document.getElementById('include-account').checked));
+
+    // replace form target url
+    form.action = $('#buttonCreateTemplate').attr('data-url');
+    form.submit();
+}
+
+function validateTemplateName(templateName)
+{
+    if(templateName.length === 0)
+    {
+        addTooltip('template-name', templateNameEmptyValidationMessage);
+        return false;
+    }
+    else
+    {
+        removeTooltip('template-name');
+    }
+
+    if(existingTemplateNames.includes(templateName))
+    {
+        addTooltip('template-name', templateNameDuplicateValidationMessage);
+        return false;
+    }
+    else
+    {
+        removeTooltip('template-name');
+    }
+
+    return true;
+}
+
+function createAdditionalHiddenInput(name, value)
+{
+    let newInput = document.createElement('input');
+    newInput.setAttribute('type', 'hidden');
+    newInput.setAttribute('name', name);
+    newInput.setAttribute('value', value);
+    return newInput;
+}
+
+function openChangeTransactionTypeModal(item)
+{
+    console.log($(item).attr('data-url'));
+    $.ajax({
+        type: 'GET',
+        url: $(item).attr('data-url'),
+        data: {},
+        success: function(data)
+        {
+            createAndOpenModalSelectNewType(data)
+        }
+    });
+}
+
+function createAndOpenModalSelectNewType(data)
+{
+    let modalID = '#modalChangeTransactionType';
+
+    $('#changeTransactionTypeModalContainer').html(data);
+    $(modalID).modal();
+    $(modalID).modal('open');
+    $('#newTypeSelect').formSelect();
+
+    $('#buttonChangeTransactionType').click(function()
+    {
+        let newType = document.getElementById('newTypeSelect').value;
+        document.getElementById('inputNewType').setAttribute('value', newType);
+
+        let form = document.getElementById('formChangeTransactionType');
+        form.submit();
+    });
+}
\ No newline at end of file
diff --git a/src/main/resources/templates/transactions/changeTypeModal.ftl b/src/main/resources/templates/transactions/changeTypeModal.ftl
new file mode 100644
index 000000000..7afe21bae
--- /dev/null
+++ b/src/main/resources/templates/transactions/changeTypeModal.ftl
@@ -0,0 +1,38 @@
+<#global locale = static["de.thecodelabs.utils.util.Localization"]>
+<#import "/spring.ftl" as s>
+
+<div id="modalChangeTransactionType" class="modal background-color">
+    <div class="modal-content">
+        <h4>${locale.getString("transaction.change.type")}</h4>
+
+        <div class="row">
+            <div class="sol s12">
+                ${locale.getString("transaction.change.type.warning")}
+            </div>
+        </div>
+        <div class="row">
+            <div class="input-field col s12">
+                <select id="newTypeSelect">
+                    <#if transaction.isRepeating() || transaction.isTransfer()>
+                        <option value="1">${locale.getString("title.transaction.new.normal")}</option>
+                    </#if>
+                    <#if !transaction.isRepeating()>
+                        <option value="2">${locale.getString("title.transaction.new.repeating")}</option>
+                    </#if>
+                    <#if !transaction.isTransfer()>
+                        <option value="3">${locale.getString("title.transaction.new.transfer")}</option>
+                    </#if>
+                </select>
+                <label for="newTypeSelect">${locale.getString("transaction.change.type.new")}</label>
+            </div>
+        </div>
+    </div>
+    <div class="modal-footer background-color">
+        <a class="modal-action modal-close waves-effect waves-light red btn-flat white-text">${locale.getString("cancel")}</a>
+        <a id="buttonChangeTransactionType" class="modal-action waves-effect waves-light green btn-flat white-text">${locale.getString("ok")}</a>
+    </div>
+
+    <form id="formChangeTransactionType" class="hidden" action="<@s.url '/transactions/${transaction.getID()?c}/changeType'/>">
+        <input type="hidden" name="newType" id="inputNewType">
+    </form>
+</div>
\ No newline at end of file
diff --git a/src/main/resources/templates/transactions/newTransactionMacros.ftl b/src/main/resources/templates/transactions/newTransactionMacros.ftl
index 5ceadf987..1d4226d61 100644
--- a/src/main/resources/templates/transactions/newTransactionMacros.ftl
+++ b/src/main/resources/templates/transactions/newTransactionMacros.ftl
@@ -377,13 +377,13 @@
 
 <#macro buttonTransactionActions>
     <div class="fixed-action-btn" id="transaction-actions-button">
-        <a id="buttonSaveAsTemplate" class="btn-floating btn-large waves-effect waves-light budgetmaster-blue">
+        <a class="btn-floating btn-large waves-effect waves-light budgetmaster-blue">
             <i class="material-icons left">settings</i>${locale.getString("save")}
         </a>
         <ul>
             <li>
-                <a class="btn-floating btn transaction-action mobile-fab-tip no-wrap" data-action-type="changeType" data-url="<@s.url '/transactions/changeType'/>">${locale.getString("transaction.change.type")}</a>
-                <a class="btn-floating btn transaction-action budgetmaster-baby-blue" data-action-type="changeType" data-url="<@s.url '/transactions/changeType'/>"><i class="material-icons">shuffle</i></a>
+                <a class="btn-floating btn transaction-action mobile-fab-tip no-wrap" data-action-type="changeType" data-url="<@s.url '/transactions/${transaction.getID()?c}/changeTypeModal'/>">${locale.getString("transaction.change.type")}</a>
+                <a class="btn-floating btn transaction-action budgetmaster-baby-blue" data-action-type="changeType" data-url="<@s.url '/transactions/${transaction.getID()?c}/changeTypeModal'/>"><i class="material-icons">shuffle</i></a>
             </li>
             <li>
                 <a class="btn-floating btn transaction-action mobile-fab-tip no-wrap" data-action-type="saveAsTemplate" data-url="<@s.url '/templates/fromTransactionModal'/>">${locale.getString("save.as.template")}</a>
diff --git a/src/main/resources/templates/transactions/newTransactionNormal.ftl b/src/main/resources/templates/transactions/newTransactionNormal.ftl
index 2c9e4542e..1664b697d 100644
--- a/src/main/resources/templates/transactions/newTransactionNormal.ftl
+++ b/src/main/resources/templates/transactions/newTransactionNormal.ftl
@@ -65,6 +65,8 @@
                     </form>
 
                     <div id="saveAsTemplateModalContainer"></div>
+
+                    <div id="changeTransactionTypeModalContainer"></div>
                 </div>
             </div>
         </main>
@@ -85,6 +87,7 @@
         <script src="<@s.url '/js/libs/spectrum.js'/>"></script>
         <script src="<@s.url '/js/helpers.js'/>"></script>
         <script src="<@s.url '/js/transactions.js'/>"></script>
+        <script src="<@s.url '/js/transactionActions.js'/>"></script>
         <script src="<@s.url '/js/categorySelect.js'/>"></script>
         <script src="<@s.url '/js/templates.js'/>"></script>
     </body>
diff --git a/src/main/resources/templates/transactions/newTransactionRepeating.ftl b/src/main/resources/templates/transactions/newTransactionRepeating.ftl
index b5219f410..06fb6526d 100644
--- a/src/main/resources/templates/transactions/newTransactionRepeating.ftl
+++ b/src/main/resources/templates/transactions/newTransactionRepeating.ftl
@@ -62,6 +62,8 @@
                         <#-- buttons -->
                         <@newTransactionMacros.buttons '/transactions'/>
                     </form>
+
+                    <div id="changeTransactionTypeModalContainer"></div>
                 </div>
             </div>
         </main>
@@ -76,6 +78,7 @@
         <script src="<@s.url '/js/libs/spectrum.js'/>"></script>
         <script src="<@s.url '/js/helpers.js'/>"></script>
         <script src="<@s.url '/js/transactions.js'/>"></script>
+        <script src="<@s.url '/js/transactionActions.js'/>"></script>
         <script src="<@s.url '/js/categorySelect.js'/>"></script>
     </body>
 </html>
diff --git a/src/main/resources/templates/transactions/newTransactionTransfer.ftl b/src/main/resources/templates/transactions/newTransactionTransfer.ftl
index f579be86b..5ac2c8bc1 100644
--- a/src/main/resources/templates/transactions/newTransactionTransfer.ftl
+++ b/src/main/resources/templates/transactions/newTransactionTransfer.ftl
@@ -70,6 +70,8 @@
                     </form>
 
                     <div id="saveAsTemplateModalContainer"></div>
+
+                    <div id="changeTransactionTypeModalContainer"></div>
                 </div>
             </div>
         </main>
@@ -90,6 +92,7 @@
         <script src="<@s.url '/js/libs/spectrum.js'/>"></script>
         <script src="<@s.url '/js/helpers.js'/>"></script>
         <script src="<@s.url '/js/transactions.js'/>"></script>
+        <script src="<@s.url '/js/transactionActions.js'/>"></script>
         <script src="<@s.url '/js/categorySelect.js'/>"></script>
         <script src="<@s.url '/js/templates.js'/>"></script>
     </body>
-- 
GitLab