Add Transaction Properties #15
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -32,7 +32,7 @@ public class JdbcDataSourceFactory {
 | 
			
		|||
     * the profile has a newer schema version, we'll exit and prompt the user
 | 
			
		||||
     * 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 {
 | 
			
		||||
        final boolean dbExists = Files.exists(getDatabaseFile(profileName));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,19 @@ import java.util.HashMap;
 | 
			
		|||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Utility class for defining and using all known 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() {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
package com.andrewlalis.perfin.data.util;
 | 
			
		||||
 | 
			
		||||
import com.andrewlalis.perfin.model.Profile;
 | 
			
		||||
import javafx.stage.FileChooser;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
| 
						 | 
				
			
			@ -103,4 +104,14 @@ public class FileUtil {
 | 
			
		|||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@ import java.util.List;
 | 
			
		|||
import java.util.Properties;
 | 
			
		||||
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
 | 
			
		||||
 * 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"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        return name != null &&
 | 
			
		||||
                name.matches("\\w+") &&
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -8,14 +8,6 @@ CREATE TABLE account (
 | 
			
		|||
    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 (
 | 
			
		||||
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
 | 
			
		||||
    uploaded_at TIMESTAMP NOT NULL,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +16,45 @@ CREATE TABLE attachment (
 | 
			
		|||
    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 (
 | 
			
		||||
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
 | 
			
		||||
    timestamp TIMESTAMP NOT NULL,
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +83,34 @@ CREATE TABLE transaction_attachment (
 | 
			
		|||
            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 (
 | 
			
		||||
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
 | 
			
		||||
    timestamp TIMESTAMP NOT NULL,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue