Add Transaction Properties #15

Merged
andrewlalis merged 18 commits from transaction-properties into main 2024-02-04 04:31:04 +00:00
6 changed files with 164 additions and 20 deletions
Showing only changes of commit b783234794 - Show all commits

View File

@ -32,7 +32,7 @@ public class JdbcDataSourceFactory {
* the profile has a newer schema version, we'll exit and prompt the user * the profile has a newer schema version, we'll exit and prompt the user
* to update their app. * to update their app.
*/ */
public static final int SCHEMA_VERSION = 1; public static final int SCHEMA_VERSION = 2;
public DataSource getDataSource(String profileName) throws ProfileLoadException { public DataSource getDataSource(String profileName) throws ProfileLoadException {
final boolean dbExists = Files.exists(getDatabaseFile(profileName)); final boolean dbExists = Files.exists(getDatabaseFile(profileName));

View File

@ -4,10 +4,19 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* Utility class for defining and using all known migrations.
*/
public class Migrations { public class Migrations {
/**
* Gets a list of migrations, as a map with the key being the version to
* migrate from. For example, a migration that takes us from version 42 to
* 43 would exist in the map with key 42.
* @return The map of all migrations.
*/
public static Map<Integer, Migration> getMigrations() { public static Map<Integer, Migration> getMigrations() {
final Map<Integer, Migration> migrations = new HashMap<>(); final Map<Integer, Migration> migrations = new HashMap<>();
migrations.put(1, new PlainSQLMigration("/sql/migration/M1_AddBalanceRecordDeleted.sql")); migrations.put(1, new PlainSQLMigration("/sql/migration/M001_AddTransactionProperties.sql"));
return migrations; return migrations;
} }

View File

@ -1,5 +1,6 @@
package com.andrewlalis.perfin.data.util; package com.andrewlalis.perfin.data.util;
import com.andrewlalis.perfin.model.Profile;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -103,4 +104,14 @@ public class FileUtil {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public static void copyResourceFile(String resource, Path dest) throws IOException {
try (
var in = Profile.class.getResourceAsStream(resource);
var out = Files.newOutputStream(dest)
) {
if (in == null) throw new IOException("Could not load resource " + resource);
in.transferTo(out);
}
}
} }

View File

@ -17,6 +17,8 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.function.Consumer; import java.util.function.Consumer;
import static com.andrewlalis.perfin.data.util.FileUtil.copyResourceFile;
/** /**
* A profile is essentially a complete set of data that the application can * A profile is essentially a complete set of data that the application can
* operate on, sort of like a save file or user account. The profile contains * operate on, sort of like a save file or user account. The profile contains
@ -153,16 +155,6 @@ public class Profile {
copyResourceFile("/text/contentDirReadme.txt", getContentDir(name).resolve("README.txt")); copyResourceFile("/text/contentDirReadme.txt", getContentDir(name).resolve("README.txt"));
} }
private static void copyResourceFile(String resource, Path dest) throws IOException {
try (
var in = Profile.class.getResourceAsStream(resource);
var out = Files.newOutputStream(dest)
) {
if (in == null) throw new IOException("Could not load resource " + resource);
in.transferTo(out);
}
}
public static boolean validateName(String name) { public static boolean validateName(String name) {
return name != null && return name != null &&
name.matches("\\w+") && name.matches("\\w+") &&

View File

@ -0,0 +1,73 @@
/*
Migration to add additional properties to transactions as per this GitHub issue:
https://github.com/andrewlalis/perfin/issues/10
Adds the following:
- An optional "vendor" field and associated vendor entity.
- An optional "category" field and associated category entity.
- An optional set of "tags" that are user-defined strings.
- An optional set of "line items" that comprise some subtotal of the transaction
and can be used to specify that X amount of the total was spent on some
specific item.
- An optional address of the purchase.
*/
CREATE TABLE transaction_vendor (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL UNIQUE,
description VARCHAR(255)
);
CREATE TABLE transaction_category (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
parent_id BIGINT DEFAULT NULL,
name VARCHAR(63) NOT NULL UNIQUE,
color VARCHAR(6) NOT NULL DEFAULT 'FFFFFF',
CONSTRAINT fk_transaction_category_parent
FOREIGN KEY (parent_id) REFERENCES transaction_category(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE transaction_tag (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(63) NOT NULL UNIQUE
);
CREATE TABLE transaction_tag_join (
transaction_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL,
PRIMARY KEY (transaction_id, tag_id),
CONSTRAINT fk_transaction_tag_join_transaction
FOREIGN KEY (transaction_id) REFERENCES transaction(id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_transaction_tag_join_tag
FOREIGN KEY (tag_id) REFERENCES transaction_tag(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE transaction_line_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
transaction_id BIGINT NOT NULL,
value_per_item NUMERIC(12, 4) NOT NULL,
quantity INT NOT NULL DEFAULT 1,
description VARCHAR(255) NOT NULL,
CONSTRAINT fk_transaction_line_item_transaction
FOREIGN KEY (transaction_id) REFERENCES transaction(id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT ck_transaction_line_item_quantity_positive
CHECK quantity > 0
);
ALTER TABLE transaction
ADD COLUMN vendor_id BIGINT DEFAULT NULL AFTER description;
ALTER TABLE transaction
ADD COLUMN category_id BIGINT DEFAULT NULL AFTER vendor_id;
ALTER TABLE transaction
ADD CONSTRAINT fk_transaction_vendor
FOREIGN KEY (vendor_id) REFERENCES transaction_vendor(id)
ON UPDATE CASCADE ON DELETE SET NULL;
ALTER TABLE transaction
ADD CONSTRAINT fk_transaction_category
FOREIGN KEY (category_id) REFERENCES transaction_category(id)
ON UPDATE CASCADE ON DELETE SET NULL;

View File

@ -8,14 +8,6 @@ CREATE TABLE account (
currency VARCHAR(3) NOT NULL currency VARCHAR(3) NOT NULL
); );
CREATE TABLE transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
timestamp TIMESTAMP NOT NULL,
amount NUMERIC(12, 4) NOT NULL,
currency VARCHAR(3) NOT NULL,
description VARCHAR(255) NULL
);
CREATE TABLE attachment ( CREATE TABLE attachment (
id BIGINT PRIMARY KEY AUTO_INCREMENT, id BIGINT PRIMARY KEY AUTO_INCREMENT,
uploaded_at TIMESTAMP NOT NULL, uploaded_at TIMESTAMP NOT NULL,
@ -24,6 +16,45 @@ CREATE TABLE attachment (
content_type VARCHAR(255) NOT NULL content_type VARCHAR(255) NOT NULL
); );
/* TRANSACTION ENTITIES */
CREATE TABLE transaction_vendor (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL UNIQUE,
description VARCHAR(255)
);
CREATE TABLE transaction_category (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
parent_id BIGINT DEFAULT NULL,
name VARCHAR(63) NOT NULL UNIQUE,
color VARCHAR(6) NOT NULL DEFAULT 'FFFFFF',
CONSTRAINT fk_transaction_category_parent
FOREIGN KEY (parent_id) REFERENCES transaction_category(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE transaction_tag (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(63) NOT NULL UNIQUE
);
CREATE TABLE transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
timestamp TIMESTAMP NOT NULL,
amount NUMERIC(12, 4) NOT NULL,
currency VARCHAR(3) NOT NULL,
description VARCHAR(255) NULL,
vendor_id BIGINT DEFAULT NULL,
category_id BIGINT DEFAULT NULL,
CONSTRAINT fk_transaction_vendor
FOREIGN KEY (vendor_id) REFERENCES transaction_vendor(id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_transaction_category
FOREIGN KEY (category_id) REFERENCES transaction_category(id)
ON UPDATE CASCADE ON DELETE SET NULL
);
CREATE TABLE account_entry ( CREATE TABLE account_entry (
id BIGINT PRIMARY KEY AUTO_INCREMENT, id BIGINT PRIMARY KEY AUTO_INCREMENT,
timestamp TIMESTAMP NOT NULL, timestamp TIMESTAMP NOT NULL,
@ -52,6 +83,34 @@ CREATE TABLE transaction_attachment (
ON UPDATE CASCADE ON DELETE CASCADE ON UPDATE CASCADE ON DELETE CASCADE
); );
CREATE TABLE transaction_tag_join (
transaction_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL,
PRIMARY KEY (transaction_id, tag_id),
CONSTRAINT fk_transaction_tag_join_transaction
FOREIGN KEY (transaction_id) REFERENCES transaction(id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_transaction_tag_join_tag
FOREIGN KEY (tag_id) REFERENCES transaction_tag(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE transaction_line_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
transaction_id BIGINT NOT NULL,
value_per_item NUMERIC(12, 4) NOT NULL,
quantity INT NOT NULL DEFAULT 1,
idx INT NOT NULL DEFAULT 0,
description VARCHAR(255) NOT NULL,
CONSTRAINT fk_transaction_line_item_transaction
FOREIGN KEY (transaction_id) REFERENCES transaction(id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT ck_transaction_line_item_quantity_positive
CHECK quantity > 0
);
/* BALANCE RECORD ENTITIES */
CREATE TABLE balance_record ( CREATE TABLE balance_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT, id BIGINT PRIMARY KEY AUTO_INCREMENT,
timestamp TIMESTAMP NOT NULL, timestamp TIMESTAMP NOT NULL,