preparing storage of tags

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-04-09 09:09:17 +02:00
parent b4b3173cc7
commit 85efb0ec02
5 changed files with 106 additions and 19 deletions

View File

@@ -25,10 +25,7 @@ import de.srsoftware.umbrella.core.model.*;
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 java.util.*;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -180,10 +177,13 @@ public class AccountingModule extends BaseHandler implements AccountingService {
accountId = newAccount.id();
}
var transaction = accountDb.save(new Transaction(accountId,dateTime,source,destination,amount.doubleValue(),purpose));
var tagList = json.has(Field.TAGS) && json.get(Field.TAGS) instanceof JSONArray t ? t : List.of();
var tags = new HashSet<String>();
tagList.forEach(e -> tags.add(e.toString()));
var transaction = accountDb.save(new Transaction(0,accountId,dateTime,source,destination,amount.doubleValue(),purpose,tags));
var tags = json.has(Field.TAGS) && json.get(Field.TAGS) instanceof JSONArray t ? t : null;
if (tags != null) LOG.log(WARNING, "Tagging transactions not implemented!");
return sendContent(ex,newAccount != null ? newAccount : transaction);
}

View File

@@ -5,5 +5,7 @@ public class Constants {
public static final String CONFIG_DATABASE = "umbrella.modules.accounting.database";
public static final String TABLE_ACCOUNTS = "accounts";
public static final String TABLE_TAGS = "tag";
public static final String TABLE_TAGS_TRANSACTIONS = "tags_transactions";
public static final String TABLE_TRANSACTIONS = "transactions";
}

View File

@@ -3,10 +3,9 @@ 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.*;
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.accounting.Constants.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static java.text.MessageFormat.format;
@@ -15,12 +14,14 @@ import de.srsoftware.tools.jdbc.Query;
import de.srsoftware.umbrella.core.BaseDb;
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.Account;
import de.srsoftware.umbrella.core.model.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -36,6 +37,8 @@ public class SqliteDb extends BaseDb implements AccountDb {
case 0:
createAccountsTable();
createTransactionsTable();
createTagsTable();
createTagsTransactionsTable();
}
return setCurrentVersion(1);
}
@@ -59,9 +62,45 @@ public class SqliteDb extends BaseDb implements AccountDb {
}
}
private void createTagsTable(){
var sql = """
CREATE TABLE IF NOT EXISTS {0} (
{1} INTEGER PRIMARY KEY,
{2} VARCHAR(32) UNIQUE NOT NULL
);
""";
try {
sql = format(sql,TABLE_TAGS,Field.ID,Field.TAG);
var stmt = db.prepareStatement(sql);
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw failedToCreateTable(TABLE_TAGS).causedBy(e);
}
}
private void createTagsTransactionsTable(){
var sql = """
CREATE TABLE IF NOT EXISTS {0} (
{1} LONG NOT NULL,
{2} LONG NOT NULL,
PRIMARY KEY({1}, {2})
);
""";
try {
sql = format(sql,TABLE_TAGS_TRANSACTIONS,Field.TRANSACTION_ID,Field.TAG_ID);
var stmt = db.prepareStatement(sql);
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw failedToCreateTable(TABLE_TAGS).causedBy(e);
}
}
private void createTransactionsTable() {
var sql = """
CREATE TABLE IF NOT EXISTS {0} (
{7} INTEGER PRIMARY KEY,
{1} LONG NOT NULL,
{2} LONG NOT NULL,
{3} VARCHAR(255) NOT NULL,
@@ -70,7 +109,7 @@ public class SqliteDb extends BaseDb implements AccountDb {
{6} TEXT
);""";
try {
sql = format(sql,TABLE_TRANSACTIONS,Field.ACCOUNT,Field.TIMESTAMP,Field.SOURCE,Field.DESTINATION, Field.AMOUNT,Field.DESCRIPTION);
sql = format(sql,TABLE_TRANSACTIONS,Field.ACCOUNT,Field.TIMESTAMP,Field.SOURCE,Field.DESTINATION, Field.AMOUNT,Field.DESCRIPTION, Field.ID);
var stmt = db.prepareStatement(sql);
stmt.execute();
stmt.close();
@@ -144,13 +183,49 @@ public class SqliteDb extends BaseDb implements AccountDb {
@Override
public Transaction save(Transaction transaction) {
if (transaction.id() == 0) {
try {
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
var rs = Query.insertInto(TABLE_TRANSACTIONS, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION)
.values(transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose())
.execute(db).getGeneratedKeys();
if (rs.next()) transaction = transaction.withId(rs.getLong(1));
rs.close();
} catch (SQLException e) {
throw failedToStoreObject(transaction);
}
} else { // TODO : implement update
throw UmbrellaException.failedToStoreObject(transaction);
}
saveTags(transaction);
return transaction;
}
private void saveTags(Transaction transaction) {
var remaining = new HashSet<String>(transaction.tags());
var existingTags = new HashMap<String,Long>();
try {
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
Query.replaceInto(TABLE_TRANSACTIONS,Field.ACCOUNT,Field.TIMESTAMP,Field.SOURCE,Field.DESTINATION, Field.AMOUNT,Field.DESCRIPTION)
.values(transaction.accountId(),timestamp,transaction.source().value(),transaction.destination().value(),transaction.amount(),transaction.purpose())
.execute(db).close();
return transaction;
} catch (SQLException e) {
var rs = select(ALL).from(TABLE_TAGS).where(Field.TAG,in(transaction.tags().toArray())).exec(db);
while (rs.next()) existingTags.put(rs.getString(Field.TAG), rs.getLong(Field.ID));
rs.close();
} catch (SQLException e){
throw failedToLoadMembers(transaction);
}
remaining.removeAll(existingTags.keySet());
for (var tag : remaining) {
try {
var rs = insertInto(TABLE_TAGS, Field.TAG).values(tag).execute(db).getGeneratedKeys();
if (rs.next()) existingTags.put(tag,rs.getLong(1));
rs.close();
} catch (SQLException e){
throw failedToStoreObject(tag);
}
}
try {
var query = replaceInto(TABLE_TAGS_TRANSACTIONS, Field.TRANSACTION_ID, Field.TAG_ID);
for (var tag_id : existingTags.values()) query.values(transaction.id(), tag_id);
query.execute(db).close();
} catch (SQLException e){
throw failedToStoreObject(transaction);
}
}