Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • deadlocker8/BudgetMaster
1 result
Select Git revision
Loading items
Show changes
Commits on Source (28)
Showing
with 4154 additions and 122 deletions
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>BudgetMaster</artifactId> <artifactId>BudgetMaster</artifactId>
<groupId>de.deadlocker8</groupId> <groupId>de.deadlocker8</groupId>
<version>2.17.2</version> <version>2.18.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -26,23 +26,23 @@ ...@@ -26,23 +26,23 @@
<properties> <properties>
<jlibs.version>3.2.0</jlibs.version> <jlibs.version>3.2.0</jlibs.version>
<versionizer.version>3.0.1</versionizer.version> <versionizer.version>3.0.1</versionizer.version>
<webjars-locator.version>0.52</webjars-locator.version> <webjars-locator.version>1.1.0</webjars-locator.version>
<jquery.version>3.7.1</jquery.version> <jquery.version>3.7.1</jquery.version>
<materializecss.version>1.0.0</materializecss.version> <materializecss.version>1.0.0</materializecss.version>
<fontawesome.version>6.5.2</fontawesome.version> <fontawesome.version>6.7.2</fontawesome.version>
<sortablejs.version>1.15.3</sortablejs.version> <sortablejs.version>1.15.6</sortablejs.version>
<mousetrap.version>1.6.5</mousetrap.version> <mousetrap.version>1.6.5</mousetrap.version>
<codemirror.version>5.62.2</codemirror.version> <codemirror.version>5.62.2</codemirror.version>
<selenium.version>4.26.0</selenium.version> <selenium.version>4.31.0</selenium.version>
<jgit.version>7.0.0.202409031743-r</jgit.version> <jgit.version>7.2.0.202503040940-r</jgit.version>
<natorder.version>1.1.3</natorder.version> <natorder.version>1.1.3</natorder.version>
<itextpdf.version>5.5.13.4</itextpdf.version> <itextpdf.version>5.5.13.4</itextpdf.version>
<vanilla-picker.version>2.12.3</vanilla-picker.version> <vanilla-picker.version>2.12.3</vanilla-picker.version>
<jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
<opencsv.version>5.9</opencsv.version> <opencsv.version>5.10</opencsv.version>
<datatables.version>2.1.0</datatables.version> <datatables.version>2.1.8</datatables.version>
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version> <jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
<junit-jupiter-engine.version>5.11.3</junit-jupiter-engine.version> <junit-jupiter-engine.version>5.12.1</junit-jupiter-engine.version>
<project.outputDirectory>${project.build.directory}/../build/${project.version}</project.outputDirectory> <project.outputDirectory>${project.build.directory}/../build/${project.version}</project.outputDirectory>
<project.artifactName>${project.artifactId}-v${project.version}</project.artifactName> <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName>
...@@ -149,7 +149,7 @@ ...@@ -149,7 +149,7 @@
<!--Webjars--> <!--Webjars-->
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId> <artifactId>webjars-locator-lite</artifactId>
<version>${webjars-locator.version}</version> <version>${webjars-locator.version}</version>
</dependency> </dependency>
<dependency> <dependency>
...@@ -192,7 +192,11 @@ ...@@ -192,7 +192,11 @@
<artifactId>datatables</artifactId> <artifactId>datatables</artifactId>
<version>${datatables.version}</version> <version>${datatables.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- selenium --> <!-- selenium -->
<dependency> <dependency>
......
...@@ -11,16 +11,16 @@ import de.deadlocker8.budgetmaster.utils.notification.Notification; ...@@ -11,16 +11,16 @@ import de.deadlocker8.budgetmaster.utils.notification.Notification;
import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder; import de.deadlocker8.budgetmaster.utils.notification.NotificationLinkBuilder;
import de.deadlocker8.budgetmaster.utils.notification.NotificationType; import de.deadlocker8.budgetmaster.utils.notification.NotificationType;
import de.thecodelabs.utils.util.Localization; import de.thecodelabs.utils.util.Localization;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
...@@ -43,6 +43,7 @@ public class AccountController extends BaseController ...@@ -43,6 +43,7 @@ public class AccountController extends BaseController
public static final String ERROR = "error"; public static final String ERROR = "error";
public static final String NOTIFICATIONS = "notifications"; public static final String NOTIFICATIONS = "notifications";
public static final String TODAY = "today"; public static final String TODAY = "today";
public static final String FILTER_CONFIGURATION = "accountsFilterConfiguration";
} }
private static class ReturnValues private static class ReturnValues
...@@ -115,22 +116,24 @@ public class AccountController extends BaseController ...@@ -115,22 +116,24 @@ public class AccountController extends BaseController
} }
@GetMapping @GetMapping
public String accounts(Model model) public String accounts(Model model, HttpServletRequest request)
{ {
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getAllEntitiesAsc()); final AccountsFilterConfiguration accountsFilterConfiguration = getAccountsFilterConfiguration(request);
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getFilteredEntitiesAsc(accountsFilterConfiguration));
return ReturnValues.SHOW_ALL; return ReturnValues.SHOW_ALL;
} }
@GetMapping("/{ID}/requestDelete") @GetMapping("/{ID}/requestDelete")
public String requestDeleteAccount(Model model, @PathVariable("ID") Integer ID) public String requestDeleteAccount(Model model, @PathVariable("ID") Integer ID, HttpServletRequest request)
{ {
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getAllEntitiesAsc()); final AccountsFilterConfiguration accountsFilterConfiguration = getAccountsFilterConfiguration(request);
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getFilteredEntitiesAsc(accountsFilterConfiguration));
model.addAttribute(ModelAttributes.ENTITY_TO_DELETE, accountService.getRepository().getReferenceById(ID)); model.addAttribute(ModelAttributes.ENTITY_TO_DELETE, accountService.getRepository().getReferenceById(ID));
return ReturnValues.DELETE_ENTITY; return ReturnValues.DELETE_ENTITY;
} }
@GetMapping("/{ID}/delete") @GetMapping("/{ID}/delete")
public String deleteAccountAndReferringTransactions(WebRequest request, Model model, @PathVariable("ID") Integer ID) public String deleteAccountAndReferringTransactions(HttpServletRequest servletRequest, WebRequest request, Model model, @PathVariable("ID") Integer ID)
{ {
// at least one account is required (to delete a sole account another one has to be created first) // at least one account is required (to delete a sole account another one has to be created first)
final Account accountToDelete = accountService.getRepository().getReferenceById(ID); final Account accountToDelete = accountService.getRepository().getReferenceById(ID);
...@@ -141,7 +144,8 @@ public class AccountController extends BaseController ...@@ -141,7 +144,8 @@ public class AccountController extends BaseController
return ReturnValues.REDIRECT_SHOW_ALL; return ReturnValues.REDIRECT_SHOW_ALL;
} }
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getAllEntitiesAsc()); final AccountsFilterConfiguration accountsFilterConfiguration = getAccountsFilterConfiguration(servletRequest);
model.addAttribute(ModelAttributes.ALL_ENTITIES, accountService.getFilteredEntitiesAsc(accountsFilterConfiguration));
model.addAttribute(ModelAttributes.CURRENT_ACCOUNT, accountToDelete); model.addAttribute(ModelAttributes.CURRENT_ACCOUNT, accountToDelete);
model.addAttribute(ModelAttributes.ACCOUNT_NOT_DELETABLE, true); model.addAttribute(ModelAttributes.ACCOUNT_NOT_DELETABLE, true);
return ReturnValues.SHOW_ALL; return ReturnValues.SHOW_ALL;
...@@ -272,4 +276,23 @@ public class AccountController extends BaseController ...@@ -272,4 +276,23 @@ public class AccountController extends BaseController
settingsService.updateLastAccountEndDateReminderDate(); settingsService.updateLastAccountEndDateReminderDate();
return "redirect:" + request.getHeader("Referer"); return "redirect:" + request.getHeader("Referer");
} }
@PostMapping(value = "/applyFilter")
public String applyFilter(WebRequest request,
@ModelAttribute("AccountsFilter") AccountsFilterConfiguration accountsFilterConfiguration)
{
request.setAttribute(ModelAttributes.FILTER_CONFIGURATION, accountsFilterConfiguration, RequestAttributes.SCOPE_SESSION);
return ReturnValues.REDIRECT_SHOW_ALL;
}
private AccountsFilterConfiguration getAccountsFilterConfiguration(HttpServletRequest request)
{
Object sessionAccountsFilterConfiguration = request.getSession().getAttribute(ModelAttributes.FILTER_CONFIGURATION);
if(sessionAccountsFilterConfiguration == null)
{
request.getSession().setAttribute(ModelAttributes.FILTER_CONFIGURATION, AccountsFilterConfiguration.DEFAULT);
return AccountsFilterConfiguration.DEFAULT;
}
return (AccountsFilterConfiguration) sessionAccountsFilterConfiguration;
}
} }
\ No newline at end of file
...@@ -79,6 +79,17 @@ public class AccountService implements Resettable, AccessAllEntities<Account>, A ...@@ -79,6 +79,17 @@ public class AccountService implements Resettable, AccessAllEntities<Account>, A
return accounts; return accounts;
} }
public List<Account> getFilteredEntitiesAsc(AccountsFilterConfiguration accountsFilterConfiguration)
{
return accountRepository.findAllByTypeOrderByNameAsc(AccountType.CUSTOM).stream()
.filter(a -> accountsFilterConfiguration.getIncludedStates().contains(a.getAccountState()))
.filter(a -> (accountsFilterConfiguration.isIncludeWithEndDate() && a.getEndDate() != null) || (accountsFilterConfiguration.isIncludeWithoutEndDate() && a.getEndDate() == null))
.filter(a -> (!accountsFilterConfiguration.hasName() || (accountsFilterConfiguration.hasName() && a.getName().toLowerCase().contains(accountsFilterConfiguration.getName().toLowerCase()))))
.filter(a -> (!accountsFilterConfiguration.hasDescription() || (accountsFilterConfiguration.hasDescription() && a.getDescription().toLowerCase().contains(accountsFilterConfiguration.getDescription().toLowerCase()))))
.sorted((a1, a2) -> new NaturalOrderComparator().compare(a1.getName(), a2.getName()))
.toList();
}
@Override @Override
public Optional<Account> findById(Integer ID) public Optional<Account> findById(Integer ID)
{ {
......
package de.deadlocker8.budgetmaster.accounts;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class AccountsFilterConfiguration
{
private boolean includeFullAccess;
private boolean includeReadOnly;
private boolean includeHidden;
private boolean includeWithEndDate;
private boolean includeWithoutEndDate;
private String name;
private String description;
public static final AccountsFilterConfiguration DEFAULT = new AccountsFilterConfiguration(true, true, false, true, true, "", "");
public List<AccountState> getIncludedStates()
{
final List<AccountState> includedStates = new ArrayList<>();
if(includeFullAccess)
{
includedStates.add(AccountState.FULL_ACCESS);
}
if(includeReadOnly)
{
includedStates.add(AccountState.READ_ONLY);
}
if(includeHidden)
{
includedStates.add(AccountState.HIDDEN);
}
return includedStates;
}
public boolean hasName()
{
return name != null && !name.isEmpty();
}
public boolean hasDescription()
{
return description != null && !description.isEmpty();
}
}
\ No newline at end of file
...@@ -119,7 +119,7 @@ public class ChartController extends BaseController ...@@ -119,7 +119,7 @@ public class ChartController extends BaseController
throw new ResourceNotFoundException(); throw new ResourceNotFoundException();
} }
List<Transaction> transactions = transactionService.getTransactionsForAccount(helpers.getCurrentAccount(), chartSettings.getStartDate(), chartSettings.getEndDate(), chartSettings.getFilterConfiguration()); List<Transaction> transactions = transactionService.getTransactionsForAccount(helpers.getCurrentAccount(), chartSettings.getStartDate(), chartSettings.getEndDate(), chartSettings.getFilterConfiguration(), false);
List<Transaction> convertedTransactions = convertTransferAmounts(transactions); List<Transaction> convertedTransactions = convertTransferAmounts(transactions);
String transactionJson = GSON.toJson(convertedTransactions); String transactionJson = GSON.toJson(convertedTransactions);
...@@ -298,7 +298,7 @@ public class ChartController extends BaseController ...@@ -298,7 +298,7 @@ public class ChartController extends BaseController
} }
} }
final List<Transaction> transactions = transactionService.getTransactionsForAccount(helpers.getCurrentAccount(), chartSettings.getStartDate(), chartSettings.getEndDate(), chartSettings.getFilterConfiguration()); final List<Transaction> transactions = transactionService.getTransactionsForAccount(helpers.getCurrentAccount(), chartSettings.getStartDate(), chartSettings.getEndDate(), chartSettings.getFilterConfiguration(), false);
final List<Transaction> convertedTransactions = convertTransferAmounts(transactions); final List<Transaction> convertedTransactions = convertTransferAmounts(transactions);
model.addAttribute(ModelAttributes.MATCHING_TRANSACTIONS, convertedTransactions); model.addAttribute(ModelAttributes.MATCHING_TRANSACTIONS, convertedTransactions);
model.addAttribute(ModelAttributes.MATCHING_TRANSACTIONS_TITLE, title); model.addAttribute(ModelAttributes.MATCHING_TRANSACTIONS_TITLE, title);
......
...@@ -47,11 +47,9 @@ public class AboutController extends BaseController ...@@ -47,11 +47,9 @@ public class AboutController extends BaseController
public String whatsNewModal(Model model) public String whatsNewModal(Model model)
{ {
final List<NewsEntry> newsEntries = new ArrayList<>(); final List<NewsEntry> newsEntries = new ArrayList<>();
newsEntries.add(NewsEntry.createWithLocalizationKey("accountEndDate")); newsEntries.add(NewsEntry.createWithLocalizationKey("accountFilter"));
newsEntries.add(NewsEntry.createWithLocalizationKey("accountDescription")); newsEntries.add(NewsEntry.createWithLocalizationKey("java21"));
newsEntries.add(NewsEntry.createWithLocalizationKey("transactionNameSuggestionsSort")); newsEntries.add(NewsEntry.createWithLocalizationKey("bugfixAllAccountsSum"));
newsEntries.add(NewsEntry.createWithLocalizationKey("bugfixCsvImport"));
newsEntries.add(NewsEntry.createWithLocalizationKey("bugfixEndDateReminder"));
model.addAttribute(ModelAttributes.NEWS_ENTRIES, newsEntries); model.addAttribute(ModelAttributes.NEWS_ENTRIES, newsEntries);
return ReturnValues.WHATS_NEW; return ReturnValues.WHATS_NEW;
......
...@@ -194,7 +194,7 @@ public class HelpersService ...@@ -194,7 +194,7 @@ public class HelpersService
private int getBudgetForAccount(Account account) private int getBudgetForAccount(Account account)
{ {
final LocalDate endDate = DateHelper.getCurrentDate(); final LocalDate endDate = DateHelper.getCurrentDate();
List<Transaction> transactions = transactionService.getTransactionsForAccountUntilDate(account, endDate, FilterConfiguration.DEFAULT); List<Transaction> transactions = transactionService.getTransactionsForAccountUntilDate(account, endDate, FilterConfiguration.DEFAULT, account.getType().equals(AccountType.ALL));
int sum = 0; int sum = 0;
for(Transaction transaction : transactions) for(Transaction transaction : transactions)
......
...@@ -96,16 +96,16 @@ public class TransactionService implements Resettable ...@@ -96,16 +96,16 @@ public class TransactionService implements Resettable
final LocalDate startDate = LocalDate.of(year, month, 1); final LocalDate startDate = LocalDate.of(year, month, 1);
final LocalDate endDate = LocalDate.of(year, month, 1).with(lastDayOfMonth()); final LocalDate endDate = LocalDate.of(year, month, 1).with(lastDayOfMonth());
return getTransactionsForAccount(account, startDate, endDate, filterConfiguration); return getTransactionsForAccount(account, startDate, endDate, filterConfiguration, false);
} }
public List<Transaction> getTransactionsForAccountUntilDate(Account account, LocalDate date, FilterConfiguration filterConfiguration) public List<Transaction> getTransactionsForAccountUntilDate(Account account, LocalDate date, FilterConfiguration filterConfiguration, boolean includeHidden)
{ {
LocalDate startDate = LocalDate.of(1900, 1, 1); LocalDate startDate = LocalDate.of(1900, 1, 1);
return getTransactionsForAccount(account, startDate, date, filterConfiguration); return getTransactionsForAccount(account, startDate, date, filterConfiguration, includeHidden);
} }
public List<Transaction> getTransactionsForAccount(Account account, LocalDate startDate, LocalDate endDate, FilterConfiguration filterConfiguration) public List<Transaction> getTransactionsForAccount(Account account, LocalDate startDate, LocalDate endDate, FilterConfiguration filterConfiguration, boolean includeHidden)
{ {
if(filterConfiguration == null) if(filterConfiguration == null)
{ {
...@@ -114,11 +114,11 @@ public class TransactionService implements Resettable ...@@ -114,11 +114,11 @@ public class TransactionService implements Resettable
if(account.getType().equals(AccountType.ALL)) if(account.getType().equals(AccountType.ALL))
{ {
Specification<Transaction> spec = TransactionSpecifications.withDynamicQuery(startDate, endDate, null, filterConfiguration.isIncludeIncome(), filterConfiguration.isIncludeExpenditure(), false, filterConfiguration.isIncludeRepeatingAndNotRepeating(), filterConfiguration.getIncludedCategoryIDs(), filterConfiguration.getIncludedTagIDs(), filterConfiguration.getName()); Specification<Transaction> spec = TransactionSpecifications.withDynamicQuery(startDate, endDate, null, filterConfiguration.isIncludeIncome(), filterConfiguration.isIncludeExpenditure(), false, filterConfiguration.isIncludeRepeatingAndNotRepeating(), filterConfiguration.getIncludedCategoryIDs(), filterConfiguration.getIncludedTagIDs(), filterConfiguration.getName(), includeHidden);
return transactionRepository.findAll(spec); return transactionRepository.findAll(spec);
} }
Specification<Transaction> spec = TransactionSpecifications.withDynamicQuery(startDate, endDate, account, filterConfiguration.isIncludeIncome(), filterConfiguration.isIncludeExpenditure(), filterConfiguration.isIncludeTransfer(), filterConfiguration.isIncludeRepeatingAndNotRepeating(), filterConfiguration.getIncludedCategoryIDs(), filterConfiguration.getIncludedTagIDs(), filterConfiguration.getName()); Specification<Transaction> spec = TransactionSpecifications.withDynamicQuery(startDate, endDate, account, filterConfiguration.isIncludeIncome(), filterConfiguration.isIncludeExpenditure(), filterConfiguration.isIncludeTransfer(), filterConfiguration.isIncludeRepeatingAndNotRepeating(), filterConfiguration.getIncludedCategoryIDs(), filterConfiguration.getIncludedTagIDs(), filterConfiguration.getName(), includeHidden);
return transactionRepository.findAll(spec); return transactionRepository.findAll(spec);
} }
......
...@@ -27,7 +27,8 @@ public class TransactionSpecifications ...@@ -27,7 +27,8 @@ public class TransactionSpecifications
final Boolean isRepeating, final Boolean isRepeating,
final List<Integer> categoryIDs, final List<Integer> categoryIDs,
final List<Integer> tagIDs, final List<Integer> tagIDs,
final String name) final String name,
final boolean isIncludeHidden)
{ {
return (transaction, query, builder) -> { return (transaction, query, builder) -> {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();
...@@ -126,7 +127,12 @@ public class TransactionSpecifications ...@@ -126,7 +127,12 @@ public class TransactionSpecifications
Predicate generalPredicates = builder.and(dateConstraint, predicatesCombined); Predicate generalPredicates = builder.and(dateConstraint, predicatesCombined);
if(account == null) if(account == null)
{ {
Predicate accountPredicate = transaction.get(Transaction_.account).get("accountState").in(List.of(AccountState.FULL_ACCESS, AccountState.READ_ONLY)); final List<AccountState> accountStates = new ArrayList<>(List.of(AccountState.FULL_ACCESS, AccountState.READ_ONLY));
if(isIncludeHidden)
{
accountStates.add(AccountState.HIDDEN);
}
Predicate accountPredicate = transaction.get(Transaction_.account).get("accountState").in(accountStates);
generalPredicates = builder.and(generalPredicates, accountPredicate); generalPredicates = builder.and(generalPredicates, accountPredicate);
} }
else else
......
locale=de locale=de
# DEFAULT # DEFAULT
credits=Verwendete Schriftarten: Roboto<br>Verwendete Bibliotheken:<br>spring-boot-starter-parent 3.3.5<br>spring-boot-devtools 3.3.5<br>spring-boot-starter-web 3.3.5<br>spring-boot-starter-test 3.3.5<br>spring-boot-starter-security 3.3.5<br>spring-boot-starter-freemarker 3.3.5<br>spring-boot-starter-validation 3.3.5<br>spring-boot-starter-data-jpa 3.3.5<br>hibernate-jpamodelgen 6.1.7.Final<br>jakarta.xml.bind-api 4.0.2<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.7.1<br>materialize 1.0.0<br>fontawesome 6.5.2<br>Google Material Icons<br>Vanilla-picker 2.12.3<br>SortableJS 1.15.3<br>jlibs 3.2.0<br>itextpdf 5.5.13.4<br>mousetrap 1.6.5<br>plotly 2.35.2<br>momentjs 2.30.1<br>codemirror 5.62.2<br>webjars-locator 0.52<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 7.0.0.202409031743-r<br>opencsv 5.9<br>datatables 2.1.0<br> credits=Verwendete Schriftarten: Roboto<br>Verwendete Bibliotheken:<br>spring-boot-starter-parent 3.3.5<br>spring-boot-devtools 3.3.5<br>spring-boot-starter-web 3.3.5<br>spring-boot-starter-test 3.3.5<br>spring-boot-starter-security 3.3.5<br>spring-boot-starter-freemarker 3.3.5<br>spring-boot-starter-validation 3.3.5<br>spring-boot-starter-data-jpa 3.3.5<br>hibernate-jpamodelgen 6.1.7.Final<br>jakarta.xml.bind-api 4.0.2<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.7.1<br>materialize 1.0.0<br>fontawesome 6.7.2<br>Google Material Icons<br>Vanilla-picker 2.12.3<br>SortableJS 1.15.6<br>jlibs 3.2.0<br>itextpdf 5.5.13.4<br>mousetrap 1.6.5<br>plotly 2.35.2<br>momentjs 2.30.1<br>codemirror 5.62.2<br>webjars-locator 0.52<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 7.2.0.202503040940-r<br>opencsv 5.10<br>datatables 2.1.8<br>
folder=Deadlocker/BudgetMaster folder=Deadlocker/BudgetMaster
roadmap.url=https://roadmaps.thecodelabs.de/roadmap/1 roadmap.url=https://roadmaps.thecodelabs.de/roadmap/1
github.url=https://github.com/deadlocker8/BudgetMaster github.url=https://github.com/deadlocker8/BudgetMaster
...@@ -371,6 +371,8 @@ account.all=Alle Konten ...@@ -371,6 +371,8 @@ account.all=Alle Konten
account.tooltip.default=Als Standardkonto festlegen account.tooltip.default=Als Standardkonto festlegen
account.tooltip.endDate.soon=Das Enddatum für dieses Konto wird in {0} Tagen erreicht! account.tooltip.endDate.soon=Das Enddatum für dieses Konto wird in {0} Tagen erreicht!
account.tooltip.endDate.done=Das Enddatum für dieses Konto wurde vor {0} Tagen erreicht! account.tooltip.endDate.done=Das Enddatum für dieses Konto wurde vor {0} Tagen erreicht!
account.label.endDate.with=mit
account.label.endDate.without=ohne
account.state.full.access=Vollzugriff account.state.full.access=Vollzugriff
account.state.read.only=Lesezugriff account.state.read.only=Lesezugriff
...@@ -624,22 +626,22 @@ chart.matching.transactions.title.category=Buchungen in der Kategorie "{0}" ...@@ -624,22 +626,22 @@ chart.matching.transactions.title.category=Buchungen in der Kategorie "{0}"
charts.default.accountSumPerDay=Kontostand pro Tag charts.default.accountSumPerDay=Kontostand pro Tag
charts.default.accountSumPerDay.localization='{"axisY": "Summe in "'} charts.default.accountSumPerDay.localization='{"axisY": "Summe in "'}
charts.default.categories=Eingaben/Ausgaben pro Kategorie charts.default.categories=Einnahmen/Ausgaben pro Kategorie
charts.default.incomesAndExpendituresPerMonthBar=Eingaben/Ausgaben charts.default.incomesAndExpendituresPerMonthBar=Einnahmen/Ausgaben
charts.default.incomesAndExpendituresPerMonthBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} charts.default.incomesAndExpendituresPerMonthBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'}
charts.default.incomesAndExpendituresPerMonthLine=Eingaben/Ausgaben charts.default.incomesAndExpendituresPerMonthLine=Einnahmen/Ausgaben
charts.default.incomesAndExpendituresPerMonthLine.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} charts.default.incomesAndExpendituresPerMonthLine.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'}
charts.default.incomesAndExpendituresByCategoryBar=Eingaben/Ausgaben pro Kategorie charts.default.incomesAndExpendituresByCategoryBar=Einnahmen/Ausgaben pro Kategorie
charts.default.incomesAndExpendituresByCategoryBar.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.incomesAndExpendituresByCategoryBar.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'}
charts.default.incomesAndExpendituresByCategoryPie=Eingaben/Ausgaben pro Kategorie charts.default.incomesAndExpendituresByCategoryPie=Einnahmen/Ausgaben pro Kategorie
charts.default.incomesAndExpendituresByCategoryPie.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.incomesAndExpendituresByCategoryPie.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'}
charts.default.incomesAndExpendituresPerMonthByCategories=Eingaben/Ausgaben pro Kategorie charts.default.incomesAndExpendituresPerMonthByCategories=Einnahmen/Ausgaben pro Kategorie
charts.default.incomesAndExpendituresPerMonthByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.incomesAndExpendituresPerMonthByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'}
charts.default.restPerMonth=Rest charts.default.restPerMonth=Rest
charts.default.restPerMonth.localization='{"label1": "Rest in "'} charts.default.restPerMonth.localization='{"label1": "Rest in "'}
charts.default.incomesAndExpendituresPerYearBar=Eingaben/Ausgaben charts.default.incomesAndExpendituresPerYearBar=Einnahmen/Ausgaben
charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'}
charts.default.incomesAndExpendituresPerYearByCategories=Eingaben/Ausgaben pro Kategorie charts.default.incomesAndExpendituresPerYearByCategories=Einnahmen/Ausgaben pro Kategorie
charts.default.incomesAndExpendituresPerYearByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.incomesAndExpendituresPerYearByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'}
charts.default.averageTransactionAmountPerCategory=Durchschnittlicher Transaktionsbetrag pro Kategorie charts.default.averageTransactionAmountPerCategory=Durchschnittlicher Transaktionsbetrag pro Kategorie
charts.default.averageTransactionAmountPerCategory.localization='{"label1": "Durchschnittlicher Transaktionsbetrag in"'} charts.default.averageTransactionAmountPerCategory.localization='{"label1": "Durchschnittlicher Transaktionsbetrag in"'}
......
locale=en locale=en
# DEFAULT # DEFAULT
credits=Fonts used: Roboto<br>Libraries used:<br>spring-boot-starter-parent 3.3.5<br>spring-boot-devtools 3.3.5<br>spring-boot-starter-web 3.3.5<br>spring-boot-starter-test 3.3.5<br>spring-boot-starter-security 3.3.5<br>pring-boot-starter-freemarker 3.3.5<br>spring-boot-starter-validation 3.3.5<br>spring-boot-starter-data-jpa 3.3.5<br>hibernate-jpamodelgen 6.1.7.Final<br>jakarta.xml.bind-api 4.0.2<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.7.1<br>materialize 1.0.0<br>fontawesome 6.5.2<br>Google Material Icons<br>Vanilla-picker 2.12.3<br>SortableJS 1.15.3<br>jlibs 3.2.0<br>itextpdf 5.5.13.4<br>mousetrap 1.6.5<br>plotly 2.35.2<br>momentjs 2.30.1<br>codemirror 5.62.2<br>webjars-locator 0.52<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 7.0.0.202409031743-r<br>opencsv 5.9<br>datatables 2.1.0<br> credits=Fonts used: Roboto<br>Libraries used:<br>spring-boot-starter-parent 3.3.5<br>spring-boot-devtools 3.3.5<br>spring-boot-starter-web 3.3.5<br>spring-boot-starter-test 3.3.5<br>spring-boot-starter-security 3.3.5<br>pring-boot-starter-freemarker 3.3.5<br>spring-boot-starter-validation 3.3.5<br>spring-boot-starter-data-jpa 3.3.5<br>hibernate-jpamodelgen 6.1.7.Final<br>jakarta.xml.bind-api 4.0.2<br>maven-surefire-plugin 2.22.2<br>launch4j-maven-plugin 1.7.25<br>jquery 3.7.1<br>materialize 1.0.0<br>fontawesome 6.7.2<br>Google Material Icons<br>Vanilla-picker 2.12.3<br>SortableJS 1.15.6<br>jlibs 3.2.0<br>itextpdf 5.5.13.4<br>mousetrap 1.6.5<br>plotly 2.35.2<br>momentjs 2.30.1<br>codemirror 5.62.2<br>webjars-locator 0.52<br>libUtils 3.2.7<br>libStorage 3.2.3<br>natorder 1.1.3<br>jgit 7.2.0.202503040940-r<br>opencsv 5.10<br>datatables 2.1.8<br>
folder=Deadlocker/BudgetMaster folder=Deadlocker/BudgetMaster
roadmap.url=https://roadmaps.thecodelabs.de/roadmap/2 roadmap.url=https://roadmaps.thecodelabs.de/roadmap/2
github.url=https://github.com/deadlocker8/BudgetMaster github.url=https://github.com/deadlocker8/BudgetMaster
...@@ -370,6 +370,8 @@ account.all=All Accounts ...@@ -370,6 +370,8 @@ account.all=All Accounts
account.tooltip.default=Set as default account account.tooltip.default=Set as default account
account.tooltip.endDate.soon=The end date for this account will be reached in {0} days! account.tooltip.endDate.soon=The end date for this account will be reached in {0} days!
account.tooltip.endDate.done=The end date for this account was reached {0} days ago! account.tooltip.endDate.done=The end date for this account was reached {0} days ago!
account.label.endDate.with=with
account.label.endDate.without=without
account.state.full.access=Full access account.state.full.access=Full access
account.state.read.only=Read-only account.state.read.only=Read-only
......
...@@ -2,17 +2,11 @@ news.further.information=Weitere Informationen ...@@ -2,17 +2,11 @@ news.further.information=Weitere Informationen
news.all.releases=Alle veröffentlichten und geplanten Versionen: news.all.releases=Alle veröffentlichten und geplanten Versionen:
news.detailed=Ausführliches Changelog (nur auf Englisch): news.detailed=Ausführliches Changelog (nur auf Englisch):
news.accountEndDate.headline=Konten: neues optionales Feld "Enddatum" news.accountFilter.headline=Konten filtern
news.accountEndDate.description=Konten können jetzt mit einem Enddatum versehen werden. Kurz vor Erreichen des Enddatums wird nach dem Login eine Warnung angezeigt. news.accountFilter.description=Auf der Kontoübersichtsseite können die aufgelisteten Konten nun gefiltert werden.
news.accountDescription.headline=Konten: neues optionales Feld "Beschreibung" news.java21.headline=Java 21
news.accountDescription.description=Konten können jetzt mit einer Beschreibung versehen werden. news.java21.description=Update auf Java 21.
news.transactionNameSuggestionsSort.headline=Einstellungsoption für die Sortierung der Buchungsnamensvorschläge news.bugfixAllAccountsSum.headline=Bugfix: Transaktionen aus versteckten Konten
news.transactionNameSuggestionsSort.description=Neue Einstellungsoption hinzugefügt, um festzulegen, wie die Vorschläge für Transaktionsnamen sortiert werden (alphabetisch oder nach Häufigkeit der Verwendung). news.bugfixAllAccountsSum.description=Transaktionen aus versteckten Konten werden nun korrekt in der Summe aller Konten berücksichtigt.
\ No newline at end of file
news.bugfixCsvImport.headline=Bugfix: CSV Import
news.bugfixCsvImport.description=Fehler behoben, der verhinderte, dass beim csv-Import die richtigen Spalten ausgewählt wurden, wenn die csv-Datei weniger Zeilen als Spalten hat.
news.bugfixEndDateReminder.headline=Bugfix: Enddatumserinnerung
news.bugfixEndDateReminder.description=Fehler behoben, der dazu führte, dass die Enddatumserinnerung für Konten auch angezeigt wurde, wenn keine Konten ihr Enddatum bald erreichen.
\ No newline at end of file
...@@ -2,17 +2,11 @@ news.further.information=Further information ...@@ -2,17 +2,11 @@ news.further.information=Further information
news.all.releases=All published and planned releases: news.all.releases=All published and planned releases:
news.detailed=Detailed changelog (english only): news.detailed=Detailed changelog (english only):
news.accountEndDate.headline=Accounts: new optional field "end date" news.accountFilter.headline=Account Filter
news.accountEndDate.description=Accounts can now have an end date. A warning is displayed after login if the end date of an account will soon be reached. news.accountFilter.description=The account overview page now allows to filter the listed accounts.
news.accountDescription.headline=Accounts: new optional field "description" news.java21.headline=Java 21
news.accountDescription.description=Accounts can now be given a description. news.java21.description=Update to Java 21.
news.transactionNameSuggestionsSort.headline=Settings option for transaction name suggestion ordering news.bugfixAllAccountsSum.headline=Bugfix: Transactions from hidden accounts
news.transactionNameSuggestionsSort.description=Add new settings option to define how transaction name suggestions are ordered (alphabetically or by frequency of use). news.bugfixAllAccountsSum.description=Transactions from hidden accounts are now correctly included in all accounts sum.
news.bugfixCsvImport.headline=Bugfix: CSV import
news.bugfixCsvImport.description=Fixed a bug that prevented to select the correct columns during csv import if the csv has fewer rows than columns.
news.bugfixEndDateReminder.headline=Bugfix: End date reminder
news.bugfixEndDateReminder.description=Fixed a bug that caused the end date reminder for accounts to be displayed even if no accounts are about to reach their end date.
\ No newline at end of file
...@@ -31,3 +31,11 @@ ...@@ -31,3 +31,11 @@
border: 3px solid var(--color-text); border: 3px solid var(--color-text);
line-height: 32px !important; line-height: 32px !important;
} }
.account-container th {
vertical-align: top;
}
.account-container th.vertical-align-middle {
vertical-align: middle;
}
\ No newline at end of file
...@@ -24,10 +24,72 @@ ...@@ -24,10 +24,72 @@
<div class="center-align"><@header.buttonLink url='/accounts/newAccount' icon='add' localizationKey='title.account.new' id='button-new-account'/></div> <div class="center-align"><@header.buttonLink url='/accounts/newAccount' icon='add' localizationKey='title.account.new' id='button-new-account'/></div>
<br> <br>
<div class="container account-container"> <div class="container account-container">
<form name="AccountsFilter" action="<@s.url '/accounts/applyFilter'/>" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<table class="bordered"> <table class="bordered">
<thead class="hide-on-med-and-down">
<tr>
<th></th>
<th>
<div>${locale.getString("account.new.label.state")}</div>
<div>
<label>
<input type="checkbox" name="includeFullAccess" <#if accountsFilterConfiguration?? && accountsFilterConfiguration.includeFullAccess>checked</#if>/>
<span><i class="fas fa-edit placeholder-icon-right"></i>${locale.getString("account.state.full.access")}</span>
</label>
</div>
<div>
<label>
<input type="checkbox" name="includeReadOnly" <#if accountsFilterConfiguration?? && accountsFilterConfiguration.includeReadOnly>checked</#if>/>
<span><i class="fas fa-lock placeholder-icon-right"></i>${locale.getString("account.state.read.only")}</span>
</label>
</div>
<div>
<label>
<input type="checkbox" name="includeHidden" <#if accountsFilterConfiguration?? && accountsFilterConfiguration.includeHidden>checked</#if>/>
<span id="includeHidden"><i class="far fa-eye-slash placeholder-icon-right"></i>${locale.getString("account.state.hidden")}</span>
</label>
</div>
</th>
<th>
<div>${locale.getString("account.new.label.endDate")}</div>
<div>
<label>
<input type="checkbox" name="includeWithEndDate" <#if accountsFilterConfiguration?? && accountsFilterConfiguration.includeWithEndDate>checked</#if>/>
<span><i class="fas fa-bell placeholder-icon-right"></i>${locale.getString("account.label.endDate.with")}</span>
</label>
</div>
<div>
<label>
<input type="checkbox" name="includeWithoutEndDate" <#if accountsFilterConfiguration?? && accountsFilterConfiguration.includeWithoutEndDate>checked</#if>/>
<span><i class="fas fa-bell-slash placeholder-icon-right"></i>${locale.getString("account.label.endDate.without")}</span>
</label>
</div>
</th>
<th>
<div>${locale.getString("account.new.label.name")}</div>
<div class="input-field">
<input type="text" name="name" value="<#if accountsFilterConfiguration??>${accountsFilterConfiguration.name}</#if>"/>
</div>
</th>
<th>
<div>${locale.getString("transaction.new.label.description")}</div>
<div class="input-field">
<input type="text" name="description" value="<#if accountsFilterConfiguration??>${accountsFilterConfiguration.description}</#if>"/>
</div>
</th>
<th class="vertical-align-middle">
<button class="btn waves-effect waves-light background-green" type="submit" id="accounts-filter-button">
<i class="fas fa-filter left"></i>${locale.getString("filter.apply")}
</button>
</th>
</tr>
</thead>
<#list accounts as account> <#list accounts as account>
<#if (account.getType().name() == "CUSTOM")> <#if (account.getType().name() == "CUSTOM")>
<tr class="account-overview-row"> <tr class="account-overview-row">
<td><@customSelectMacros.accountIcon account account.getName() "text-blue"/></td>
<td> <td>
<#if account.getAccountState().name() == "READ_ONLY"> <#if account.getAccountState().name() == "READ_ONLY">
<div class="placeholder-icon placeholder-icon-right"></div> <div class="placeholder-icon placeholder-icon-right"></div>
...@@ -39,14 +101,14 @@ ...@@ -39,14 +101,14 @@
<a href="<@s.url '/accounts/${account.getID()?c}/setAsDefault'/>" class="btn-flat no-padding text-default tooltipped" data-position="left" data-tooltip="${locale.getString("account.tooltip.default")}"><i class="material-icons left"><#if account.isDefault()>star<#else>star_border</#if></i></a> <a href="<@s.url '/accounts/${account.getID()?c}/setAsDefault'/>" class="btn-flat no-padding text-default tooltipped" data-position="left" data-tooltip="${locale.getString("account.tooltip.default")}"><i class="material-icons left"><#if account.isDefault()>star<#else>star_border</#if></i></a>
<i class="fas fa-edit placeholder-icon-right"></i> <i class="fas fa-edit placeholder-icon-right"></i>
</#if> </#if>
</td>
<td>
<#if account.getEndDate()??> <#if account.getEndDate()??>
<i class="fas fa-bell"></i> <i class="fas fa-bell placeholder-icon-right"></i> ${dateService.getDateStringNormal(account.getEndDate())}
<#else> <#else>
<div class="placeholder-icon"></div> <div class="placeholder-icon"></div>
</#if> </#if>
</td> </td>
<td><@customSelectMacros.accountIcon account account.getName() "text-blue"/></td>
<td>${account.getName()}</td> <td>${account.getName()}</td>
<td> <td>
<div class="truncate account-description"> <div class="truncate account-description">
...@@ -61,6 +123,7 @@ ...@@ -61,6 +123,7 @@
</#if> </#if>
</#list> </#list>
</table> </table>
</form>
<#if accounts?size == 0> <#if accounts?size == 0>
<div class="headline center-align">${locale.getString("placeholder")}</div> <div class="headline center-align">${locale.getString("placeholder")}</div>
</#if> </#if>
......
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
<!-- Scripts--> <!-- Scripts-->
<#import "../helpers/scripts.ftl" as scripts> <#import "../helpers/scripts.ftl" as scripts>
<@scripts.scripts/> <@scripts.scripts/>
<script src="<@s.url '/webjars/vanilla-picker/2.12.3/dist/vanilla-picker.min.js'/>"></script> <script src="<@s.url '/webjars/vanilla-picker/dist/vanilla-picker.min.js'/>"></script>
<script src="<@s.url '/js/accounts.js'/>"></script> <script src="<@s.url '/js/accounts.js'/>"></script>
<script src="<@s.url '/js/iconSelect.js'/>"></script> <script src="<@s.url '/js/iconSelect.js'/>"></script>
<script src="<@s.url '/js/fontColorPicker.js'/>"></script> <script src="<@s.url '/js/fontColorPicker.js'/>"></script>
......
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
<!-- Scripts--> <!-- Scripts-->
<#import "../helpers/scripts.ftl" as scripts> <#import "../helpers/scripts.ftl" as scripts>
<@scripts.scripts/> <@scripts.scripts/>
<script src="<@s.url '/webjars/vanilla-picker/2.12.3/dist/vanilla-picker.min.js'/>"></script> <script src="<@s.url '/webjars/vanilla-picker/dist/vanilla-picker.min.js'/>"></script>
<script src="<@s.url '/js/categories.js'/>"></script> <script src="<@s.url '/js/categories.js'/>"></script>
<script src="<@s.url '/js/iconSelect.js'/>"></script> <script src="<@s.url '/js/iconSelect.js'/>"></script>
<script src="<@s.url '/js/fontColorPicker.js'/>"></script> <script src="<@s.url '/js/fontColorPicker.js'/>"></script>
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<@header.header "BudgetMaster - ${title}"/> <@header.header "BudgetMaster - ${title}"/>
<#import "/spring.ftl" as s> <#import "/spring.ftl" as s>
<link rel="stylesheet" href="<@s.url "/webjars/codemirror/5.62.2/lib/codemirror.css"/>"> <link rel="stylesheet" href="<@s.url "/webjars/codemirror/lib/codemirror.css"/>">
<@header.style "charts"/> <@header.style "charts"/>
</head> </head>
<@header.body> <@header.body>
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<!-- Scripts--> <!-- Scripts-->
<#import "../helpers/scripts.ftl" as scripts> <#import "../helpers/scripts.ftl" as scripts>
<@scripts.scripts/> <@scripts.scripts/>
<script src="<@s.url '/webjars/codemirror/5.62.2/lib/codemirror.js'/>"></script> <script src="<@s.url '/webjars/codemirror/lib/codemirror.js'/>"></script>
<script src="<@s.url '/webjars/codemirror/5.62.2/mode/javascript/javascript.js'/>"></script> <script src="<@s.url '/webjars/codemirror/mode/javascript/javascript.js'/>"></script>
<script src="<@s.url '/js/charts.js'/>"></script> <script src="<@s.url '/js/charts.js'/>"></script>
</@header.body> </@header.body>
</html> </html>
\ No newline at end of file
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
<#import "/spring.ftl" as s> <#import "/spring.ftl" as s>
<title>${title}</title> <title>${title}</title>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="stylesheet" href="<@s.url '/webjars/font-awesome/6.5.2/css/all.min.css'/>"> <link rel="stylesheet" href="<@s.url '/webjars/font-awesome/css/all.min.css'/>">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="<@s.url "/webjars/materializecss/1.0.0/css/materialize.min.css"/>"> <link rel="stylesheet" href="<@s.url "/webjars/materializecss/css/materialize.min.css"/>">
<@style "colors"/> <@style "colors"/>
<@style "style"/> <@style "style"/>
<@style "navbar"/> <@style "navbar"/>
......