diff --git a/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountDb.java b/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountDb.java index f181a3f8..b5415d07 100644 --- a/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountDb.java +++ b/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountDb.java @@ -1,10 +1,11 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.accounting; import de.srsoftware.umbrella.core.model.Account; import de.srsoftware.umbrella.core.model.Transaction; - import java.util.Collection; import java.util.List; +import java.util.Set; public interface AccountDb { Collection listAccounts(long userId); @@ -16,4 +17,6 @@ public interface AccountDb { Account save(Account account); Transaction save(Transaction transaction); + + Set searchField(long userId, String field, String key); } diff --git a/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountingModule.java b/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountingModule.java index b0b115eb..21c5a350 100644 --- a/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountingModule.java +++ b/accounting/src/main/java/de/srsoftware/umbrella/accounting/AccountingModule.java @@ -1,5 +1,15 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.accounting; +import static de.srsoftware.tools.Optionals.nullIfEmpty; +import static de.srsoftware.umbrella.accounting.Constants.CONFIG_DATABASE; +import static de.srsoftware.umbrella.core.ConnectionProvider.connect; +import static de.srsoftware.umbrella.core.ModuleRegistry.userService; +import static de.srsoftware.umbrella.core.Util.mapValues; +import static de.srsoftware.umbrella.core.constants.Path.*; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.invalidField; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingField; + import com.sun.net.httpserver.HttpExchange; import de.srsoftware.configuration.Configuration; import de.srsoftware.tools.Path; @@ -11,26 +21,14 @@ import de.srsoftware.umbrella.core.constants.Field; import de.srsoftware.umbrella.core.constants.Text; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; -import org.json.JSONObject; - -import static de.srsoftware.umbrella.core.constants.Path.SOURCES; -import static de.srsoftware.umbrella.core.constants.Path.DESTINATIONS; - import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Optional; - -import static de.srsoftware.tools.Optionals.nullIfEmpty; -import static de.srsoftware.umbrella.accounting.Constants.CONFIG_DATABASE; -import static de.srsoftware.umbrella.core.ConnectionProvider.connect; -import static de.srsoftware.umbrella.core.ModuleRegistry.userService; -import static de.srsoftware.umbrella.core.Util.mapValues; -import static de.srsoftware.umbrella.core.constants.Path.JSON; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.invalidField; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingField; +import org.json.JSONObject; public class AccountingModule extends BaseHandler implements AccountingService { private final AccountDb accountDb; @@ -68,14 +66,14 @@ public class AccountingModule extends BaseHandler implements AccountingService { public boolean doPost(Path path, HttpExchange ex) throws IOException { addCors(ex); try { - Optional token = SessionToken.from(ex).map(Token::of); - var user = userService().loadUser(token); + var user = userService().refreshSession(ex); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { case null -> postEntry(user.get(),ex); case SOURCES -> postSearchSources(user.get(),ex); case DESTINATIONS -> postSearchDestinations(user.get(),ex); + case PURPOSES -> postSearchPurposes(user.get(),ex); default -> super.doPost(path,ex); }; } catch (UmbrellaException e){ @@ -114,6 +112,15 @@ public class AccountingModule extends BaseHandler implements AccountingService { + private static boolean noNumbers(String s){ + try { + Long.parseLong(s); + return false; + } catch (NumberFormatException e) { + return true; + } + } + private boolean postEntry(UmbrellaUser user, HttpExchange ex) throws IOException { var json = json(ex); if (!json.has(Field.ACCOUNT)) throw missingField(Field.ACCOUNT); @@ -176,16 +183,26 @@ public class AccountingModule extends BaseHandler implements AccountingService { } public boolean postSearchDestinations(UmbrellaUser user, HttpExchange ex) throws IOException { - var key = body(ex); - var users = userService().search(key); - // TODO: search known transactions for possible destinations - return sendContent(ex,mapValues(users)); + return sendContent(ex,searchOptions(user, Field.DESTINATION, body(ex))); } - public boolean postSearchSources(UmbrellaUser user, HttpExchange ex) throws IOException { + public boolean postSearchPurposes(UmbrellaUser user, HttpExchange ex) throws IOException { var key = body(ex); + var purposes = accountDb.searchField(user.id(),Field.DESCRIPTION,key); + return sendContent(ex,purposes); + } + + + public boolean postSearchSources(UmbrellaUser user, HttpExchange ex) throws IOException { + return sendContent(ex,searchOptions(user, Field.SOURCE, body(ex))); + } + + public ArrayList> searchOptions(UmbrellaUser user, String field, String key){ var users = userService().search(key); - // TODO: search known transactions for possible sources - return sendContent(ex,mapValues(users)); + var destinations = accountDb.searchField(user.id(), field, key); + var optionList = new ArrayList>(); + users.values().stream().map(UmbrellaUser::toMap).forEach(optionList::add); + destinations.stream().filter(AccountingModule::noNumbers).map(s -> Map.of(Field.DISPLAY,s)).forEach(optionList::add); + return optionList; } } diff --git a/accounting/src/main/java/de/srsoftware/umbrella/accounting/Constants.java b/accounting/src/main/java/de/srsoftware/umbrella/accounting/Constants.java index 9031166c..1bc7de26 100644 --- a/accounting/src/main/java/de/srsoftware/umbrella/accounting/Constants.java +++ b/accounting/src/main/java/de/srsoftware/umbrella/accounting/Constants.java @@ -1,3 +1,4 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.accounting; public class Constants { diff --git a/accounting/src/main/java/de/srsoftware/umbrella/accounting/SqliteDb.java b/accounting/src/main/java/de/srsoftware/umbrella/accounting/SqliteDb.java index 61d7fdc2..f94be490 100644 --- a/accounting/src/main/java/de/srsoftware/umbrella/accounting/SqliteDb.java +++ b/accounting/src/main/java/de/srsoftware/umbrella/accounting/SqliteDb.java @@ -1,5 +1,15 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.accounting; +import static de.srsoftware.tools.NotImplemented.notImplemented; +import static de.srsoftware.tools.jdbc.Condition.*; +import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; +import static de.srsoftware.tools.jdbc.Query.select; +import static de.srsoftware.umbrella.accounting.Constants.TABLE_ACCOUNTS; +import static de.srsoftware.umbrella.accounting.Constants.TABLE_TRANSACTIONS; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; +import static java.text.MessageFormat.format; + import de.srsoftware.tools.jdbc.Condition; import de.srsoftware.tools.jdbc.Query; import de.srsoftware.umbrella.core.BaseDb; @@ -7,7 +17,6 @@ import de.srsoftware.umbrella.core.constants.Field; import de.srsoftware.umbrella.core.constants.Text; import de.srsoftware.umbrella.core.model.Account; import de.srsoftware.umbrella.core.model.Transaction; - import java.sql.Connection; import java.sql.SQLException; import java.time.ZoneOffset; @@ -15,15 +24,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import static de.srsoftware.tools.NotImplemented.notImplemented; -import static de.srsoftware.tools.jdbc.Condition.equal; -import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; -import static de.srsoftware.tools.jdbc.Query.select; -import static de.srsoftware.umbrella.accounting.Constants.TABLE_ACCOUNTS; -import static de.srsoftware.umbrella.accounting.Constants.TABLE_TRANSACTIONS; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; -import static java.text.MessageFormat.format; - public class SqliteDb extends BaseDb implements AccountDb { public SqliteDb(Connection connection) { super(connection); @@ -154,4 +154,19 @@ public class SqliteDb extends BaseDb implements AccountDb { throw failedToStoreObject(transaction); } } + + @Override + public HashSet searchField(long userId, String field , String key) { + var accounts = listAccounts(userId); + var accountIds = accounts.stream().map(Account::id).toArray(); + var destinations = new HashSet(); + try { + var rs = Query.select("DISTINCT "+field).from(TABLE_TRANSACTIONS).where(Field.ACCOUNT,in(accountIds)).where(field,like("%"+key+"%")).exec(db); + while (rs.next()) destinations.add(rs.getString(1)); + rs.close(); + return destinations; + } catch (SQLException e) { + throw failedToReadFromTable(field,TABLE_TRANSACTIONS).causedBy(e); + } + } } diff --git a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java index edd2e11b..23d87a8a 100644 --- a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java +++ b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java @@ -15,7 +15,6 @@ import de.srsoftware.umbrella.company.CompanyModule; import de.srsoftware.umbrella.contact.ContactModule; import de.srsoftware.umbrella.core.SettingsService; import de.srsoftware.umbrella.core.Util; -import de.srsoftware.umbrella.core.api.AccountingService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.DocumentApi; import de.srsoftware.umbrella.files.FileModule; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/AccountingService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/AccountingService.java index fbac2aca..4cbc8c58 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/AccountingService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/AccountingService.java @@ -1,3 +1,4 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.api; public interface AccountingService { diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/StockService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/StockService.java index f96bf9ab..3407a88f 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/StockService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/StockService.java @@ -4,7 +4,6 @@ package de.srsoftware.umbrella.core.api; import de.srsoftware.umbrella.core.model.DbLocation; import de.srsoftware.umbrella.core.model.Item; - import java.util.Collection; public interface StockService { diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java index 399c84c2..394ec2ba 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java @@ -39,6 +39,7 @@ public class Path { public static final String PROJECT = "project"; public static final String PROPERTIES = "properties"; public static final String PROPERTY = "property"; + public static final String PURPOSES = "purposes"; public static final String READ = "read"; public static final String REDIRECT = "redirect"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Account.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Account.java index 4a520312..d2610d60 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Account.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Account.java @@ -1,14 +1,14 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; +import static de.srsoftware.umbrella.core.ModuleRegistry.userService; + import de.srsoftware.tools.Mappable; import de.srsoftware.umbrella.core.constants.Field; - import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; -import static de.srsoftware.umbrella.core.ModuleRegistry.userService; - public record Account(long id, String name, String currency, long ownerId) implements Mappable { public static Account of(ResultSet rs) throws SQLException { var id = rs.getLong(Field.ID); diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/IdOrString.java b/core/src/main/java/de/srsoftware/umbrella/core/model/IdOrString.java index 463e7318..75b68a21 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/IdOrString.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/IdOrString.java @@ -1,8 +1,8 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; import de.srsoftware.tools.Mappable; import de.srsoftware.umbrella.core.constants.Field; - import java.util.HashMap; import java.util.Map; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Transaction.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Transaction.java index f8d700bf..49524300 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Transaction.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Transaction.java @@ -1,13 +1,12 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; import de.srsoftware.tools.Mappable; import de.srsoftware.umbrella.core.constants.Field; - import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; import java.time.ZoneOffset; -import java.util.HashMap; import java.util.Map; public record Transaction(long accountId, LocalDateTime date, IdOrString source, IdOrString destination, double amount, String purpose) implements Mappable { diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java index ba1b1fd1..f72adc98 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java @@ -5,7 +5,6 @@ package de.srsoftware.umbrella.core.model; import static de.srsoftware.umbrella.core.constants.Field.*; import de.srsoftware.tools.Mappable; -import de.srsoftware.umbrella.core.api.NamedThing; import de.srsoftware.umbrella.core.api.Owner; import de.srsoftware.umbrella.core.constants.Module; import java.util.HashMap; diff --git a/frontend/src/Components/Autocomplete.svelte b/frontend/src/Components/Autocomplete.svelte index 76bd89ca..37c0f8ef 100644 --- a/frontend/src/Components/Autocomplete.svelte +++ b/frontend/src/Components/Autocomplete.svelte @@ -4,6 +4,7 @@ let { + id = null, autofocus = false, getCandidates = dummyGetCandidates, onCommit = dummyOnCommit, @@ -108,7 +109,7 @@ - + {#if candidates && candidates.length > 0} + {t('source')} - + {t('destination')} @@ -110,10 +128,10 @@ {t('purpose')} - + - + \ No newline at end of file