preparing storage of tags
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -25,10 +25,7 @@ import de.srsoftware.umbrella.core.model.*;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -180,10 +177,13 @@ public class AccountingModule extends BaseHandler implements AccountingService {
|
|||||||
accountId = newAccount.id();
|
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);
|
return sendContent(ex,newAccount != null ? newAccount : transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ public class Constants {
|
|||||||
public static final String CONFIG_DATABASE = "umbrella.modules.accounting.database";
|
public static final String CONFIG_DATABASE = "umbrella.modules.accounting.database";
|
||||||
|
|
||||||
public static final String TABLE_ACCOUNTS = "accounts";
|
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";
|
public static final String TABLE_TRANSACTIONS = "transactions";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ package de.srsoftware.umbrella.accounting;
|
|||||||
|
|
||||||
import static de.srsoftware.tools.NotImplemented.notImplemented;
|
import static de.srsoftware.tools.NotImplemented.notImplemented;
|
||||||
import static de.srsoftware.tools.jdbc.Condition.*;
|
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.SelectQuery.ALL;
|
||||||
import static de.srsoftware.tools.jdbc.Query.select;
|
import static de.srsoftware.umbrella.accounting.Constants.*;
|
||||||
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 de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||||
import static java.text.MessageFormat.format;
|
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.BaseDb;
|
||||||
import de.srsoftware.umbrella.core.constants.Field;
|
import de.srsoftware.umbrella.core.constants.Field;
|
||||||
import de.srsoftware.umbrella.core.constants.Text;
|
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.Account;
|
||||||
import de.srsoftware.umbrella.core.model.Transaction;
|
import de.srsoftware.umbrella.core.model.Transaction;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
case 0:
|
case 0:
|
||||||
createAccountsTable();
|
createAccountsTable();
|
||||||
createTransactionsTable();
|
createTransactionsTable();
|
||||||
|
createTagsTable();
|
||||||
|
createTagsTransactionsTable();
|
||||||
}
|
}
|
||||||
return setCurrentVersion(1);
|
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() {
|
private void createTransactionsTable() {
|
||||||
var sql = """
|
var sql = """
|
||||||
CREATE TABLE IF NOT EXISTS {0} (
|
CREATE TABLE IF NOT EXISTS {0} (
|
||||||
|
{7} INTEGER PRIMARY KEY,
|
||||||
{1} LONG NOT NULL,
|
{1} LONG NOT NULL,
|
||||||
{2} LONG NOT NULL,
|
{2} LONG NOT NULL,
|
||||||
{3} VARCHAR(255) NOT NULL,
|
{3} VARCHAR(255) NOT NULL,
|
||||||
@@ -70,7 +109,7 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
{6} TEXT
|
{6} TEXT
|
||||||
);""";
|
);""";
|
||||||
try {
|
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);
|
var stmt = db.prepareStatement(sql);
|
||||||
stmt.execute();
|
stmt.execute();
|
||||||
stmt.close();
|
stmt.close();
|
||||||
@@ -144,12 +183,48 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transaction save(Transaction transaction) {
|
public Transaction save(Transaction transaction) {
|
||||||
|
if (transaction.id() == 0) {
|
||||||
try {
|
try {
|
||||||
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
|
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
|
||||||
Query.replaceInto(TABLE_TRANSACTIONS,Field.ACCOUNT,Field.TIMESTAMP,Field.SOURCE,Field.DESTINATION, Field.AMOUNT,Field.DESCRIPTION)
|
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())
|
.values(transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose())
|
||||||
.execute(db).close();
|
.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;
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTags(Transaction transaction) {
|
||||||
|
var remaining = new HashSet<String>(transaction.tags());
|
||||||
|
var existingTags = new HashMap<String,Long>();
|
||||||
|
try {
|
||||||
|
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){
|
} catch (SQLException e){
|
||||||
throw failedToStoreObject(transaction);
|
throw failedToStoreObject(transaction);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ public class Field {
|
|||||||
public static final String TAG = "tag";
|
public static final String TAG = "tag";
|
||||||
public static final String TAGS = "tags";
|
public static final String TAGS = "tags";
|
||||||
public static final String TAG_COLORS = "tag_colors";
|
public static final String TAG_COLORS = "tag_colors";
|
||||||
|
public static final String TAG_ID = "tag_id";
|
||||||
public static final String TASK = "task";
|
public static final String TASK = "task";
|
||||||
public static final String TASK_IDS = "task_ids";
|
public static final String TASK_IDS = "task_ids";
|
||||||
public static final String TASKS = "tasks";
|
public static final String TASKS = "tasks";
|
||||||
@@ -175,6 +176,7 @@ public class Field {
|
|||||||
public static final String TO = "to";
|
public static final String TO = "to";
|
||||||
public static final String TOKEN = "token";
|
public static final String TOKEN = "token";
|
||||||
public static final String TOTAL_PRIO = "total_prio";
|
public static final String TOTAL_PRIO = "total_prio";
|
||||||
|
public static final String TRANSACTION_ID = "transaction_id";
|
||||||
public static final String TRANSACTIONS = "transactions";
|
public static final String TRANSACTIONS = "transactions";
|
||||||
public static final String TYPE = "type";
|
public static final String TYPE = "type";
|
||||||
public static final String TYPE_ID = "type_id";
|
public static final String TYPE_ID = "type_id";
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public record Transaction(long accountId, LocalDateTime date, IdOrString source, IdOrString destination, double amount, String purpose) implements Mappable {
|
public record Transaction(long id, long accountId, LocalDateTime date, IdOrString source, IdOrString destination, double amount, String purpose, Set<String> tags) implements Mappable {
|
||||||
|
|
||||||
|
|
||||||
public static Transaction of(ResultSet rs) throws SQLException {
|
public static Transaction of(ResultSet rs) throws SQLException {
|
||||||
@@ -20,12 +22,14 @@ public record Transaction(long accountId, LocalDateTime date, IdOrString source,
|
|||||||
var destination = IdOrString.of(rs.getString(Field.DESTINATION));
|
var destination = IdOrString.of(rs.getString(Field.DESTINATION));
|
||||||
var amount = rs.getDouble(Field.AMOUNT);
|
var amount = rs.getDouble(Field.AMOUNT);
|
||||||
var purpose = rs.getString(Field.DESCRIPTION);
|
var purpose = rs.getString(Field.DESCRIPTION);
|
||||||
return new Transaction(accountId,date,source,destination,amount,purpose);
|
var id = rs.getLong(Field.ID);
|
||||||
|
return new Transaction(id, accountId, date, source, destination, amount, purpose, new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> toMap() {
|
public Map<String, Object> toMap() {
|
||||||
return Map.of(
|
return Map.of(
|
||||||
|
Field.ID, id,
|
||||||
Field.ACCOUNT, accountId,
|
Field.ACCOUNT, accountId,
|
||||||
Field.DATE, date.toLocalDate(),
|
Field.DATE, date.toLocalDate(),
|
||||||
Field.SOURCE, source.toMap(),
|
Field.SOURCE, source.toMap(),
|
||||||
@@ -34,4 +38,8 @@ public record Transaction(long accountId, LocalDateTime date, IdOrString source,
|
|||||||
Field.PURPOSE, purpose
|
Field.PURPOSE, purpose
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Transaction withId(long id) {
|
||||||
|
return new Transaction(id, accountId, date, source, destination, amount, purpose, new HashSet<>(tags));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user