From 31f63cb617bec0598e4c11c2a9aff213be3b402c Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Fri, 19 Mar 2021 22:40:42 +0100
Subject: [PATCH] Fixed #590 - added new attribute "accountState" for accounts

---
 .../budgetmaster/accounts/Account.java        | 34 ++++++++++++++-----
 .../accounts/AccountController.java           |  3 ++
 .../budgetmaster/accounts/AccountService.java | 34 +++++++++++++------
 .../budgetmaster/accounts/AccountState.java   |  8 +++++
 .../templates/TemplateService.java            |  3 +-
 .../transactions/TransactionController.java   |  3 +-
 .../transactions/TransactionService.java      |  3 +-
 .../resources/templates/accounts/accounts.ftl |  2 +-
 .../templates/accounts/newAccount.ftl         | 12 ++++++-
 .../transactions/transactionsMacros.ftl       |  2 +-
 10 files changed, 79 insertions(+), 25 deletions(-)
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/accounts/AccountState.java

diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java
index 92f09e6af..c1647ca76 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java
@@ -29,8 +29,13 @@ public class Account
 
 	private Boolean isSelected = false;
 	private Boolean isDefault = false;
+
+	@Deprecated
 	private Boolean isReadOnly = false;
 
+	@Expose
+	private AccountState accountState;
+
 	@ManyToOne
 	@Expose
 	private Image icon;
@@ -44,7 +49,7 @@ public class Account
 		this.type = type;
 		this.isSelected = false;
 		this.isDefault = false;
-		this.isReadOnly = false;
+		this.accountState = AccountState.FULL_ACCESS;
 		this.icon = icon;
 	}
 
@@ -107,16 +112,28 @@ public class Account
 		isDefault = aDefault;
 	}
 
+	@Deprecated
 	public Boolean isReadOnly()
 	{
 		return isReadOnly;
 	}
 
+	@Deprecated
 	public void setReadOnly(Boolean readOnly)
 	{
 		isReadOnly = readOnly;
 	}
 
+	public AccountState getAccountState()
+	{
+		return accountState;
+	}
+
+	public void setAccountState(AccountState accountState)
+	{
+		this.accountState = accountState;
+	}
+
 	public AccountType getType()
 	{
 		return type;
@@ -146,7 +163,7 @@ public class Account
 				", referringTransactions=" + referringTransactions +
 				", isSelected=" + isSelected +
 				", isDefault=" + isDefault +
-				", isReadOnly=" + isReadOnly +
+				", accountState=" + accountState +
 				", type=" + type +
 				", icon=" + icon +
 				'}';
@@ -158,18 +175,17 @@ public class Account
 		if(this == o) return true;
 		if(o == null || getClass() != o.getClass()) return false;
 		Account account = (Account) o;
-		return isSelected == account.isSelected &&
-				isDefault == account.isDefault &&
-				isReadOnly == account.isReadOnly &&
-				Objects.equals(ID, account.ID) &&
+		return Objects.equals(ID, account.ID) &&
 				Objects.equals(name, account.name) &&
-				type == account.type &&
-				Objects.equals(icon, account.icon);
+				Objects.equals(isSelected, account.isSelected) &&
+				Objects.equals(isDefault, account.isDefault) &&
+				accountState == account.accountState &&
+				Objects.equals(icon, account.icon) && type == account.type;
 	}
 
 	@Override
 	public int hashCode()
 	{
-		return Objects.hash(ID, name, isSelected, isDefault, isReadOnly, type, icon);
+		return Objects.hash(ID, name, isSelected, isDefault, accountState, icon, type);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
index 38b3e8dd1..4cb602a66 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java
@@ -133,6 +133,7 @@ public class AccountController extends BaseController
 		model.addAttribute("account", emptyAccount);
 		model.addAttribute("settings", settingsService.getSettings());
 		model.addAttribute("availableImages", imageService.getRepository().findAll());
+		model.addAttribute("availableAccountStates", AccountState.values());
 		return "accounts/newAccount";
 	}
 
@@ -148,6 +149,7 @@ public class AccountController extends BaseController
 		model.addAttribute("account", accountOptional.get());
 		model.addAttribute("settings", settingsService.getSettings());
 		model.addAttribute("availableImages", imageService.getRepository().findAll());
+		model.addAttribute("availableAccountStates", AccountState.values());
 		return "accounts/newAccount";
 	}
 
@@ -172,6 +174,7 @@ public class AccountController extends BaseController
 			model.addAttribute("account", account);
 			model.addAttribute("settings", settingsService.getSettings());
 			model.addAttribute("availableImages", imageService.getRepository().findAll());
+			model.addAttribute("availableAccountStates", AccountState.values());
 			return "accounts/newAccount";
 		}
 		else
diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountService.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountService.java
index a2c710af8..cd7624f8a 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountService.java
@@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -105,15 +106,7 @@ public class AccountService implements Resetable
 			LOGGER.debug("Created default account");
 		}
 
-		// handle null values for new field "isReadOnly"
-		for(Account account : accountRepository.findAll())
-		{
-			if(account.isReadOnly() == null)
-			{
-				account.setReadOnly(false);
-			}
-			accountRepository.save(account);
-		}
+		updateMissingAttributes();
 
 		Account defaultAccount = accountRepository.findByIsDefault(true);
 		if(defaultAccount == null)
@@ -124,6 +117,27 @@ public class AccountService implements Resetable
 		setAsDefaultAccount(accountRepository.findByIsDefault(true).getID());
 	}
 
+	private void updateMissingAttributes()
+	{
+		// handle null values for new field "accountState"
+		for(Account account : accountRepository.findAll())
+		{
+			if(account.getAccountState() == null)
+			{
+				if(account.isReadOnly() == null || !account.isReadOnly())
+				{
+					account.setAccountState(AccountState.FULL_ACCESS);
+				}
+				else
+				{
+					account.setAccountState(AccountState.READ_ONLY);
+				}
+				LOGGER.debug(MessageFormat.format("Updated account {0}: Set missing attribute \"accountState\" to {1}", account.getName(), account.getAccountState()));
+			}
+			accountRepository.save(account);
+		}
+	}
+
 	private void deselectAllAccounts()
 	{
 		List<Account> accounts = accountRepository.findAll();
@@ -165,7 +179,7 @@ public class AccountService implements Resetable
 		}
 
 		Account accountToSelect = accountToSelectOptional.get();
-		if(accountToSelect.isReadOnly())
+		if(accountToSelect.getAccountState() != AccountState.FULL_ACCESS)
 		{
 			return;
 		}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountState.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountState.java
new file mode 100644
index 000000000..217345583
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountState.java
@@ -0,0 +1,8 @@
+package de.deadlocker8.budgetmaster.accounts;
+
+public enum AccountState
+{
+	FULL_ACCESS,
+	READ_ONLY,
+	HIDDEN
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java b/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
index 3c41b17d9..7850ed482 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateService.java
@@ -4,6 +4,7 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import de.deadlocker8.budgetmaster.accounts.Account;
 import de.deadlocker8.budgetmaster.accounts.AccountService;
+import de.deadlocker8.budgetmaster.accounts.AccountState;
 import de.deadlocker8.budgetmaster.categories.CategoryService;
 import de.deadlocker8.budgetmaster.categories.CategoryType;
 import de.deadlocker8.budgetmaster.services.Resetable;
@@ -86,7 +87,7 @@ public class TemplateService implements Resetable
 		}
 
 		final Account account = template.getAccount();
-		if(account != null && account.isReadOnly())
+		if(account != null && account.getAccountState() != AccountState.FULL_ACCESS)
 		{
 			template.setAccount(accountService.getRepository().findByIsDefault(true));
 		}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
index 6b0875f95..f406b675b 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionController.java
@@ -2,6 +2,7 @@ package de.deadlocker8.budgetmaster.transactions;
 
 import de.deadlocker8.budgetmaster.accounts.Account;
 import de.deadlocker8.budgetmaster.accounts.AccountService;
+import de.deadlocker8.budgetmaster.accounts.AccountState;
 import de.deadlocker8.budgetmaster.accounts.AccountType;
 import de.deadlocker8.budgetmaster.categories.CategoryService;
 import de.deadlocker8.budgetmaster.categories.CategoryType;
@@ -249,7 +250,7 @@ public class TransactionController extends BaseController
 
 		Transaction transaction = transactionOptional.get();
 
-		if(transaction.getAccount().isReadOnly())
+		if(transaction.getAccount().getAccountState() != AccountState.FULL_ACCESS)
 		{
 			return "redirect:/transactions";
 		}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java
index 52d401fbc..6724a4689 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java
@@ -3,6 +3,7 @@ package de.deadlocker8.budgetmaster.transactions;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import de.deadlocker8.budgetmaster.accounts.Account;
+import de.deadlocker8.budgetmaster.accounts.AccountState;
 import de.deadlocker8.budgetmaster.accounts.AccountType;
 import de.deadlocker8.budgetmaster.categories.CategoryService;
 import de.deadlocker8.budgetmaster.categories.CategoryType;
@@ -193,7 +194,7 @@ public class TransactionService implements Resetable
 					return false;
 				}
 
-				return !transaction.getAccount().isReadOnly();
+				return transaction.getAccount().getAccountState() == AccountState.FULL_ACCESS;
 			}
 		}
 		return false;
diff --git a/src/main/resources/templates/accounts/accounts.ftl b/src/main/resources/templates/accounts/accounts.ftl
index 0f7d695ab..930563cb4 100644
--- a/src/main/resources/templates/accounts/accounts.ftl
+++ b/src/main/resources/templates/accounts/accounts.ftl
@@ -27,7 +27,7 @@
                             <#if (account.getType().name() == "CUSTOM")>
                                 <tr class="account-overview-row">
                                     <td>
-                                        <#if account.isReadOnly()>
+                                        <#if account.getAccountState().name() == "READ_ONLY">
                                             <#assign toolTipText = locale.getString("account.tooltip.readonly.activate")/>
                                             <#assign lockIcon = '<i class="fas fa-lock"></i>'/>
                                             <div class="placeholder-icon"></div>
diff --git a/src/main/resources/templates/accounts/newAccount.ftl b/src/main/resources/templates/accounts/newAccount.ftl
index 6e2eb0c3b..15503f670 100644
--- a/src/main/resources/templates/accounts/newAccount.ftl
+++ b/src/main/resources/templates/accounts/newAccount.ftl
@@ -34,7 +34,6 @@
                         <input type="hidden" name="ID" value="<#if account.getID()??>${account.getID()?c}</#if>">
                         <input type="hidden" name="isSelected" value="<#if account.isSelected()??>${account.isSelected()?c}</#if>">
                         <input type="hidden" name="isDefault" value="<#if account.isDefault()??>${account.isDefault()?c}</#if>">
-                        <input type="hidden" name="isReadOnly" value="<#if account.isReadOnly()??>${account.isReadOnly()?c}</#if>">
 
                         <#-- name -->
                         <div class="row">
@@ -63,6 +62,17 @@
                             </div>
                         </div>
 
+                        <#-- state -->
+                        <div class="row">
+                            <div class="input-field col s12 m12 l8 offset-l2">
+                                <select name="accountState">
+                                    <#list availableAccountStates as state>
+                                        <option value="${state}">${state}</option>
+                                    </#list>
+                                </select>
+                            </div>
+                        </div>
+
                         <br>
 
                         <#-- buttons -->
diff --git a/src/main/resources/templates/transactions/transactionsMacros.ftl b/src/main/resources/templates/transactions/transactionsMacros.ftl
index 4cd4bd449..24c9c5f5b 100644
--- a/src/main/resources/templates/transactions/transactionsMacros.ftl
+++ b/src/main/resources/templates/transactions/transactionsMacros.ftl
@@ -61,7 +61,7 @@
 </#macro>
 
 <#macro transactionAccountIcon transaction>
-    <#if helpers.getCurrentAccount().getType() == "ALL" && transaction.getAccount()??>
+    <#if helpers.getCurrentAccount().getType().name() == "ALL" && transaction.getAccount()??>
         <#import "../helpers/customSelectMacros.ftl" as customSelectMacros>
         <div class="col s2 l1 xl1 tooltipped no-padding" data-position="bottom" data-tooltip="${transaction.getAccount().getName()}">
             <div class="hide-on-med-and-down">
-- 
GitLab