--- /dev/null
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 59,
+ "identityHash": "a56d86c03528ece865d81fd8171c819f",
+ "entities": [
+ {
+ "tableName": "templates",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `regular_expression` TEXT NOT NULL, `test_text` TEXT, `transaction_description` TEXT, `transaction_description_match_group` INTEGER, `transaction_comment` TEXT, `transaction_comment_match_group` INTEGER, `date_year` INTEGER, `date_year_match_group` INTEGER, `date_month` INTEGER, `date_month_match_group` INTEGER, `date_day` INTEGER, `date_day_match_group` INTEGER, `is_fallback` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "regularExpression",
+ "columnName": "regular_expression",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "testText",
+ "columnName": "test_text",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transactionDescription",
+ "columnName": "transaction_description",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transactionDescriptionMatchGroup",
+ "columnName": "transaction_description_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transactionComment",
+ "columnName": "transaction_comment",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transactionCommentMatchGroup",
+ "columnName": "transaction_comment_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateYear",
+ "columnName": "date_year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateYearMatchGroup",
+ "columnName": "date_year_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateMonth",
+ "columnName": "date_month",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateMonthMatchGroup",
+ "columnName": "date_month_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateDay",
+ "columnName": "date_day",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateDayMatchGroup",
+ "columnName": "date_day_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isFallback",
+ "columnName": "is_fallback",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "template_accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `template_id` INTEGER NOT NULL, `acc` TEXT, `position` INTEGER NOT NULL, `acc_match_group` INTEGER, `currency` INTEGER, `currency_match_group` INTEGER, `amount` REAL, `amount_match_group` INTEGER, `comment` TEXT, `comment_match_group` INTEGER, `negate_amount` INTEGER, FOREIGN KEY(`template_id`) REFERENCES `templates`(`id`) ON UPDATE RESTRICT ON DELETE CASCADE , FOREIGN KEY(`currency`) REFERENCES `currencies`(`id`) ON UPDATE RESTRICT ON DELETE RESTRICT )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "templateId",
+ "columnName": "template_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountName",
+ "columnName": "acc",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountNameMatchGroup",
+ "columnName": "acc_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "currency",
+ "columnName": "currency",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "currencyMatchGroup",
+ "columnName": "currency_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "amount",
+ "columnName": "amount",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "amountMatchGroup",
+ "columnName": "amount_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "accountComment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "accountCommentMatchGroup",
+ "columnName": "comment_match_group",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "negateAmount",
+ "columnName": "negate_amount",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "fk_template_accounts_template",
+ "unique": false,
+ "columnNames": [
+ "template_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_template_accounts_template` ON `${TABLE_NAME}` (`template_id`)"
+ },
+ {
+ "name": "fk_template_accounts_currency",
+ "unique": false,
+ "columnNames": [
+ "currency"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_template_accounts_currency` ON `${TABLE_NAME}` (`currency`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "templates",
+ "onDelete": "CASCADE",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "template_id"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ },
+ {
+ "table": "currencies",
+ "onDelete": "RESTRICT",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "currency"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "currencies",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `position` TEXT NOT NULL, `has_gap` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasGap",
+ "columnName": "has_gap",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_id` INTEGER NOT NULL, `level` INTEGER NOT NULL, `name` TEXT NOT NULL, `name_upper` TEXT NOT NULL, `parent_name` TEXT, `expanded` INTEGER NOT NULL DEFAULT 1, `amounts_expanded` INTEGER NOT NULL DEFAULT 0, `generation` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`profile_id`) REFERENCES `profiles`(`id`) ON UPDATE RESTRICT ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "profileId",
+ "columnName": "profile_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "level",
+ "columnName": "level",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nameUpper",
+ "columnName": "name_upper",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentName",
+ "columnName": "parent_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "expanded",
+ "columnName": "expanded",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "amountsExpanded",
+ "columnName": "amounts_expanded",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "generation",
+ "columnName": "generation",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "un_account_name",
+ "unique": true,
+ "columnNames": [
+ "profile_id",
+ "name"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `un_account_name` ON `${TABLE_NAME}` (`profile_id`, `name`)"
+ },
+ {
+ "name": "fk_account_profile",
+ "unique": false,
+ "columnNames": [
+ "profile_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_account_profile` ON `${TABLE_NAME}` (`profile_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "profiles",
+ "onDelete": "CASCADE",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "profile_id"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "profiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `use_authentication` INTEGER NOT NULL, `auth_user` TEXT, `auth_password` TEXT, `order_no` INTEGER NOT NULL, `permit_posting` INTEGER NOT NULL, `theme` INTEGER NOT NULL DEFAULT -1, `preferred_accounts_filter` TEXT, `future_dates` INTEGER NOT NULL, `api_version` INTEGER NOT NULL, `show_commodity_by_default` INTEGER NOT NULL, `default_commodity` TEXT, `show_comments_by_default` INTEGER NOT NULL DEFAULT 1, `detected_version_pre_1_19` INTEGER NOT NULL, `detected_version_major` INTEGER NOT NULL, `detected_version_minor` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "useAuthentication",
+ "columnName": "use_authentication",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "authUser",
+ "columnName": "auth_user",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "authPassword",
+ "columnName": "auth_password",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "orderNo",
+ "columnName": "order_no",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "permitPosting",
+ "columnName": "permit_posting",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "theme",
+ "columnName": "theme",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "preferredAccountsFilter",
+ "columnName": "preferred_accounts_filter",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "futureDates",
+ "columnName": "future_dates",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "apiVersion",
+ "columnName": "api_version",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showCommodityByDefault",
+ "columnName": "show_commodity_by_default",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "defaultCommodity",
+ "columnName": "default_commodity",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "showCommentsByDefault",
+ "columnName": "show_comments_by_default",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "detectedVersionPre_1_19",
+ "columnName": "detected_version_pre_1_19",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "detectedVersionMajor",
+ "columnName": "detected_version_major",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "detectedVersionMinor",
+ "columnName": "detected_version_minor",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "options",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profile` INTEGER NOT NULL, `name` TEXT NOT NULL, `value` TEXT, PRIMARY KEY(`profile`, `name`))",
+ "fields": [
+ {
+ "fieldPath": "profile",
+ "columnName": "profile",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profile",
+ "name"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "account_values",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account_id` INTEGER NOT NULL, `currency` TEXT NOT NULL DEFAULT '', `value` REAL NOT NULL, `generation` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`account_id`) REFERENCES `accounts`(`id`) ON UPDATE RESTRICT ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "account_id",
+ "columnName": "account_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "currency",
+ "columnName": "currency",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "generation",
+ "columnName": "generation",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "un_account_values",
+ "unique": true,
+ "columnNames": [
+ "account_id",
+ "currency"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `un_account_values` ON `${TABLE_NAME}` (`account_id`, `currency`)"
+ },
+ {
+ "name": "fk_account_value_acc",
+ "unique": false,
+ "columnNames": [
+ "account_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_account_value_acc` ON `${TABLE_NAME}` (`account_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "accounts",
+ "onDelete": "CASCADE",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "account_id"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "description_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT NOT NULL COLLATE NOCASE, `description_upper` TEXT NOT NULL, `generation` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`description`))",
+ "fields": [
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "descriptionUpper",
+ "columnName": "description_upper",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "generation",
+ "columnName": "generation",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "description"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "transactions",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_id` INTEGER NOT NULL, `data_hash` TEXT NOT NULL, `year` INTEGER NOT NULL, `month` INTEGER NOT NULL, `day` INTEGER NOT NULL, `description` TEXT NOT NULL COLLATE NOCASE, `comment` TEXT, `generation` INTEGER NOT NULL, FOREIGN KEY(`profile_id`) REFERENCES `profiles`(`id`) ON UPDATE RESTRICT ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "profileId",
+ "columnName": "profile_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dataHash",
+ "columnName": "data_hash",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "month",
+ "columnName": "month",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "day",
+ "columnName": "day",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "generation",
+ "columnName": "generation",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "un_transactions_data_hash",
+ "unique": true,
+ "columnNames": [
+ "profile_id",
+ "data_hash"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `un_transactions_data_hash` ON `${TABLE_NAME}` (`profile_id`, `data_hash`)"
+ },
+ {
+ "name": "idx_transaction_description",
+ "unique": false,
+ "columnNames": [
+ "description"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `idx_transaction_description` ON `${TABLE_NAME}` (`description`)"
+ },
+ {
+ "name": "fk_transaction_profile",
+ "unique": false,
+ "columnNames": [
+ "profile_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_transaction_profile` ON `${TABLE_NAME}` (`profile_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "profiles",
+ "onDelete": "CASCADE",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "profile_id"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "transaction_accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `transaction_id` INTEGER NOT NULL, `order_no` INTEGER NOT NULL, `account_name` TEXT NOT NULL, `currency` TEXT NOT NULL DEFAULT '', `amount` REAL NOT NULL, `comment` TEXT, `generation` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`transaction_id`) REFERENCES `transactions`(`id`) ON UPDATE RESTRICT ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "transactionId",
+ "columnName": "transaction_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "orderNo",
+ "columnName": "order_no",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountName",
+ "columnName": "account_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "currency",
+ "columnName": "currency",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "amount",
+ "columnName": "amount",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "generation",
+ "columnName": "generation",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "fk_tran_acc_trans",
+ "unique": false,
+ "columnNames": [
+ "transaction_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `fk_tran_acc_trans` ON `${TABLE_NAME}` (`transaction_id`)"
+ },
+ {
+ "name": "un_transaction_accounts",
+ "unique": true,
+ "columnNames": [
+ "transaction_id",
+ "order_no"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `un_transaction_accounts` ON `${TABLE_NAME}` (`transaction_id`, `order_no`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "transactions",
+ "onDelete": "CASCADE",
+ "onUpdate": "RESTRICT",
+ "columns": [
+ "transaction_id"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a56d86c03528ece865d81fd8171c819f')"
+ ]
+ }
+}
\ No newline at end of file
/*
- * Copyright © 2020 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
protected String doInBackground(MainModel[] model) {
final MobileLedgerProfile profile = Data.getProfile();
- String profile_uuid = profile.getUuid();
+ long profile_id = profile.getId();
Data.backgroundTaskStarted();
try {
String sql;
if (accFilter == null) {
sql = "SELECT id, year, month, day FROM transactions WHERE profile=? ORDER BY " +
"year desc, month desc, day desc, id desc";
- params = new String[]{profile_uuid};
+ params = new String[]{String.valueOf(profile_id)};
}
else {
"and ta.account_name LIKE ?||'%' AND ta" +
".amount <> 0 ORDER BY tr.year desc, tr.month desc, tr.day desc, tr.id " +
"desc";
- params = new String[]{profile_uuid, accFilter};
+ params = new String[]{String.valueOf(profile_id), accFilter};
}
debug("UTT", sql);
@Dao
public abstract class AccountDAO extends BaseDAO<Account> {
+ static public List<String> unbox(List<AccountNameContainer> list) {
+ ArrayList<String> result = new ArrayList<>(list.size());
+ for (AccountNameContainer item : list) {
+ result.add(item.name);
+ }
+
+ return result;
+ }
@Insert
- public abstract void insertSync(Account item);
+ public abstract long insertSync(Account item);
@Update
public abstract void updateSync(Account item);
@Query("SELECT * FROM accounts")
public abstract LiveData<List<Account>> getAll();
- @Query("SELECT * FROM accounts WHERE profile = :profileUUID AND name = :accountName")
- public abstract LiveData<Account> getByName(@NonNull String profileUUID,
- @NonNull String accountName);
-
-// not useful for now
+ // not useful for now
// @Transaction
// @Query("SELECT * FROM patterns")
// List<PatternWithAccounts> getPatternsWithAccounts();
+ @Query("SELECT * FROM accounts WHERE profile_id = :profileId AND name = :accountName")
+ public abstract LiveData<Account> getByName(long profileId, @NonNull String accountName);
- static public List<String> unbox(List<AccountNameContainer> list) {
- ArrayList<String> result = new ArrayList<>(list.size());
- for (AccountNameContainer item : list) {
- result.add(item.name);
- }
-
- return result;
- }
@Query("SELECT name, CASE WHEN name_upper LIKE :term||'%%' THEN 1 " +
" WHEN name_upper LIKE '%%:'||:term||'%%' THEN 2 " +
" WHEN name_upper LIKE '%% '||:term||'%%' THEN 3 " +
" ELSE 9 END AS ordering " + "FROM accounts " +
- "WHERE profile=:profileUUID AND name_upper LIKE '%%'||:term||'%%' " +
+ "WHERE profile_id=:profileId AND name_upper LIKE '%%'||:term||'%%' " +
"ORDER BY ordering, name_upper, rowid ")
- public abstract LiveData<List<AccountNameContainer>> lookupInProfileByName(
- @NonNull String profileUUID, @NonNull String term);
+ public abstract LiveData<List<AccountNameContainer>> lookupInProfileByName(long profileId,
+ @NonNull
+ String term);
@Query("SELECT name, CASE WHEN name_upper LIKE :term||'%%' THEN 1 " +
" WHEN name_upper LIKE '%%:'||:term||'%%' THEN 2 " +
" WHEN name_upper LIKE '%% '||:term||'%%' THEN 3 " +
" ELSE 9 END AS ordering " + "FROM accounts " +
- "WHERE profile=:profileUUID AND name_upper LIKE '%%'||:term||'%%' " +
+ "WHERE profile_id=:profileId AND name_upper LIKE '%%'||:term||'%%' " +
"ORDER BY ordering, name_upper, rowid ")
- public abstract List<AccountNameContainer> lookupInProfileByNameSync(
- @NonNull String profileUUID, @NonNull String term);
+ public abstract List<AccountNameContainer> lookupInProfileByNameSync(long profileId,
+ @NonNull String term);
@Query("SELECT DISTINCT name, CASE WHEN name_upper LIKE :term||'%%' THEN 1 " +
" WHEN name_upper LIKE '%%:'||:term||'%%' THEN 2 " +
import androidx.annotation.Nullable;
abstract class BaseDAO<T> {
- abstract void insertSync(T item);
- public void insert(T item, @Nullable Runnable onDone) {
+ abstract long insertSync(T item);
+ public void insert(T item, @Nullable OnInsertedReceiver receiver) {
AsyncTask.execute(() -> {
- insertSync(item);
- if (onDone != null)
- new Handler(Looper.getMainLooper()).post(onDone);
+ long id = insertSync(item);
+ if (receiver != null)
+ new Handler(Looper.getMainLooper()).post(() -> receiver.onInsert(id));
});
}
new Handler(Looper.getMainLooper()).post(onDone);
});
}
-
-
+ interface OnInsertedReceiver {
+ void onInsert(long id);
+ }
}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.dao;
+
+import androidx.room.Dao;
+
+import net.ktnx.mobileledger.db.Profile;
+
+@Dao
+abstract class ProfileDAO extends BaseDAO<Profile> {
+ @Override
+ abstract long insertSync(Profile item);
+
+ @Override
+ abstract void updateSync(Profile item);
+
+ @Override
+ abstract void deleteSync(Profile item);
+}
return result;
}
@Insert
- public abstract void insertSync(Transaction item);
+ public abstract long insertSync(Transaction item);
@Update
public abstract void updateSync(Transaction item);
// @Transaction
// @Query("SELECT * FROM patterns")
// List<PatternWithAccounts> getPatternsWithAccounts();
- @Query("SELECT * FROM transactions WHERE profile = :profileUUID AND id = :id")
- public abstract LiveData<Transaction> getById(@NonNull String profileUUID, long id);
+ @Query("SELECT * FROM transactions WHERE id = :id")
+ public abstract LiveData<Transaction> getById(long id);
@Query("SELECT DISTINCT description, CASE WHEN description_upper LIKE :term||'%%' THEN 1 " +
" WHEN description_upper LIKE '%%:'||:term||'%%' THEN 2 " +
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
+import androidx.room.ForeignKey;
+import androidx.room.Index;
+import androidx.room.PrimaryKey;
-@Entity(tableName = "accounts", primaryKeys = {"profile", "name"})
+@Entity(tableName = "accounts",
+ indices = {@Index(name = "un_account_name", unique = true, value = {"profile_id", "name"}),
+ @Index(name = "fk_account_profile", value = "profile_id")
+ }, foreignKeys = {
+ @ForeignKey(entity = Profile.class, parentColumns = "id", childColumns = "profile_id",
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT)
+})
public class Account {
@ColumnInfo
- int level;
+ @PrimaryKey(autoGenerate = true)
+ long id;
+ @ColumnInfo(name = "profile_id")
+ long profileId;
@ColumnInfo
- @NonNull
- private String profile;
+ int level;
@ColumnInfo
@NonNull
private String name;
private boolean amountsExpanded = false;
@ColumnInfo(defaultValue = "0")
private int generation;
- @NonNull
- public String getProfile() {
- return profile;
+ public long getId() {
+ return id;
+ }
+ public void setId(long id) {
+ this.id = id;
+ }
+ public long getProfileId() {
+ return profileId;
}
- public void setProfile(@NonNull String profile) {
- this.profile = profile;
+ public void setProfileId(long profileId) {
+ this.profileId = profileId;
}
@NonNull
public String getName() {
private final AccountFilter filter = new AccountFilter();
private final AccountDAO dao = DB.get()
.getAccountDAO();
- private String profileUUID;
+ private long profileId;
public AccountAutocompleteAdapter(Context context) {
super(context, android.R.layout.simple_dropdown_item_1line, new ArrayList<>());
}
public AccountAutocompleteAdapter(Context context, @NonNull MobileLedgerProfile profile) {
this(context);
- profileUUID = profile.getUuid();
+ profileId = profile.getId();
}
- public void setProfileUUID(String profileUUID) {
- this.profileUUID = profileUUID;
+ public void setProfileId(long profileId) {
+ this.profileId = profileId;
}
@NonNull
@Override
}
Logger.debug("acc", String.format("Looking for account '%s'", constraint));
- final List<String> matches = AccountDAO.unbox(
- (profileUUID == null) ? dao.lookupByNameSync(String.valueOf(constraint)
- .toUpperCase())
- : dao.lookupInProfileByNameSync(profileUUID,
- String.valueOf(constraint)
- .toUpperCase()));
+ final List<String> matches = AccountDAO.unbox((profileId == 0) ? dao.lookupByNameSync(
+ String.valueOf(constraint)
+ .toUpperCase()) : dao.lookupInProfileByNameSync(profileId,
+ String.valueOf(constraint)
+ .toUpperCase()));
results.values = matches;
results.count = matches.size();
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
+import androidx.room.ForeignKey;
+import androidx.room.Index;
+import androidx.room.PrimaryKey;
-/*
-create table account_values(profile varchar not null, account varchar not null, currency varchar
-not null default '', value decimal not null, generation integer default 0 );
-create unique index un_account_values on account_values(profile,account,currency);
- */
-@Entity(tableName = "account_values", primaryKeys = {"profile", "account", "currency"})
+
+@Entity(tableName = "account_values", indices = {
+ @Index(name = "un_account_values", unique = true, value = {"account_id", "currency"}),
+ @Index(name = "fk_account_value_acc", value = "account_id")
+}, foreignKeys = {
+ @ForeignKey(entity = Account.class, parentColumns = "id", childColumns = "account_id",
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT)
+})
public class AccountValue {
@ColumnInfo
- @NonNull
- private String profile;
+ @PrimaryKey(autoGenerate = true)
+ long id;
@ColumnInfo
- @NonNull
- private String account;
+ private long account_id;
@NonNull
@ColumnInfo(defaultValue = "")
private String currency = "";
private float value;
@ColumnInfo(defaultValue = "0")
private int generation = 0;
- @NonNull
- public String getProfile() {
- return profile;
+ public long getId() {
+ return id;
}
- public void setProfile(@NonNull String profile) {
- this.profile = profile;
+ public void setId(long id) {
+ this.id = id;
}
- @NonNull
- public String getAccount() {
- return account;
+ public long getAccount_id() {
+ return account_id;
}
- public void setAccount(@NonNull String account) {
- this.account = account;
+ public void setAccount_id(long account_id) {
+ this.account_id = account_id;
}
@NonNull
public String getCurrency() {
Transaction.class, TransactionAccount.class
})
abstract public class DB extends RoomDatabase {
- public static final int REVISION = 58;
+ public static final int REVISION = 59;
public static final String DB_NAME = "MoLe.db";
private static DB instance;
public static DB get() {
import androidx.room.ColumnInfo;
import androidx.room.Entity;
-@Entity(tableName = "options", primaryKeys = {"profile", "name"})
+@Entity(tableName = "options", primaryKeys = {"profile_id", "name"})
public class Option {
- @NonNull
- @ColumnInfo
- private String profile = "invalid";
+ @ColumnInfo(name = "profile_id")
+ private long profileId;
@NonNull
@ColumnInfo
private String name = "";
@ColumnInfo
private String value;
- @NonNull
- public String getProfile() {
- return profile;
+ public long getProfileId() {
+ return profileId;
}
- public void setProfile(@NonNull String profile) {
- this.profile = profile;
+ public void setProfile(long profileId) {
+ this.profileId = profileId;
}
@NonNull
public String getName() {
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
+import androidx.room.PrimaryKey;
-/*
- create table profiles(uuid varchar not null primary key, name not null, url not null,
- use_authentication boolean not null, auth_user varchar, auth_password varchar, order_no
- integer, permit_posting boolean default 0, theme integer default -1, preferred_accounts_filter
- varchar, future_dates integer, api_version integer, show_commodity_by_default boolean default
- 0, default_commodity varchar, show_comments_by_default boolean default 1,
- detected_version_pre_1_19 boolean, detected_version_major integer, detected_version_minor
- integer);
-*/
-@Entity(tableName = "profiles", primaryKeys = {"uuid"})
+@Entity(tableName = "profiles")
public class Profile {
- @NonNull
@ColumnInfo
- private String uuid = "invalid";
+ @PrimaryKey(autoGenerate = true)
+ private long id;
@NonNull
@ColumnInfo
private String name = "";
private int detectedVersionMajor;
@ColumnInfo(name = "detected_version_minor")
private int detectedVersionMinor;
- @NonNull
- public String getUuid() {
- return uuid;
+ public long getId() {
+ return id;
}
- public void setUuid(@NonNull String uuid) {
- this.uuid = uuid;
+ public void setId(long id) {
+ this.id = id;
}
@NonNull
public String getName() {
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
+import androidx.room.ForeignKey;
import androidx.room.Index;
+import androidx.room.PrimaryKey;
+
+import org.jetbrains.annotations.NotNull;
/*
create table transactions(profile varchar not null, id integer not null, data_hash varchar not
create unique index un_transactions_data_hash on transactions(profile,data_hash);
create index idx_transaction_description on transactions(description);
*/
-@Entity(tableName = "transactions", primaryKeys = {"profile", "id"}, indices = {
- @Index(name = "un_transactions_data_hash", unique = true, value = {"profile", "data_hash"}),
- @Index(name = "idx_transaction_description", value = "description")
+@Entity(tableName = "transactions", foreignKeys = {
+ @ForeignKey(entity = Profile.class, parentColumns = "id", childColumns = "profile_id",
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT)
+}, indices = {@Index(name = "un_transactions_data_hash", unique = true,
+ value = {"profile_id", "data_hash"}),
+ @Index(name = "idx_transaction_description", value = "description"),
+ @Index(name = "fk_transaction_profile", value = "profile_id")
})
public class Transaction {
@ColumnInfo
- @NonNull
- private String profile;
- @ColumnInfo
- private int id;
+ @PrimaryKey(autoGenerate = true)
+ long id;
+ @ColumnInfo(name = "profile_id")
+ private long profileId;
@ColumnInfo(name = "data_hash")
@NonNull
private String dataHash;
private String comment;
@ColumnInfo
private int generation = 0;
- public String getProfile() {
- return profile;
+ public long getProfileId() {
+ return profileId;
}
- public void setProfile(String profile) {
- this.profile = profile;
+ public void setProfileId(long profileId) {
+ this.profileId = profileId;
}
- public int getId() {
+ public long getId() {
return id;
}
- public void setId(int id) {
+ public void setId(long id) {
this.id = id;
}
public String getDataHash() {
return dataHash;
}
- public void setDataHash(String dataHash) {
+ public void setDataHash(@NotNull String dataHash) {
this.dataHash = dataHash;
}
public int getYear() {
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
+import androidx.room.PrimaryKey;
-@Entity(tableName = "transaction_accounts", primaryKeys = {"profile", "transaction_id", "order_no"},
- foreignKeys = {@ForeignKey(entity = Transaction.class, parentColumns = {"profile", "id"},
- childColumns = {"profile", "transaction_id"},
- onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT),
- @ForeignKey(entity = Account.class, parentColumns = {"profile", "name"},
- childColumns = {"profile", "account_name"},
- onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT)
- }, indices = {@Index(name = "fk_tran_acc_prof_acc", value = {"profile", "account_name"})})
+@Entity(tableName = "transaction_accounts", foreignKeys = {
+ @ForeignKey(entity = Transaction.class, parentColumns = {"id"},
+ childColumns = {"transaction_id"}, onDelete = ForeignKey.CASCADE,
+ onUpdate = ForeignKey.RESTRICT),
+}, indices = {@Index(name = "fk_tran_acc_trans", value = {"transaction_id"}),
+ @Index(name = "un_transaction_accounts", unique = true,
+ value = {"transaction_id", "order_no"})
+})
public class TransactionAccount {
@ColumnInfo
- @NonNull
- private String profile;
+ @PrimaryKey(autoGenerate = true)
+ private long id;
@ColumnInfo(name = "transaction_id")
private int transactionId;
@ColumnInfo(name = "order_no")
private String comment;
@ColumnInfo(defaultValue = "0")
private int generation = 0;
- @NonNull
- public String getProfile() {
- return profile;
+ public long getId() {
+ return id;
}
- public void setProfile(@NonNull String profile) {
- this.profile = profile;
+ public void setId(long id) {
+ this.id = id;
}
+ @NonNull
public int getTransactionId() {
return transactionId;
}
backgroundTasksRunning.postValue(cnt > 0);
}
public static void setCurrentProfile(@NonNull MobileLedgerProfile newProfile) {
- MLDB.setOption(MLDB.OPT_PROFILE_UUID, newProfile.getUuid());
+ MLDB.setLongOption(MLDB.OPT_PROFILE_ID, newProfile.getId());
profile.setValue(newProfile);
}
public static void postCurrentProfile(@NonNull MobileLedgerProfile newProfile) {
- MLDB.setOption(MLDB.OPT_PROFILE_UUID, newProfile.getUuid());
+ MLDB.setLongOption(MLDB.OPT_PROFILE_ID, newProfile.getId());
profile.postValue(newProfile);
}
public static int getProfileIndex(MobileLedgerProfile profile) {
}
}
@SuppressWarnings("WeakerAccess")
- public static int getProfileIndex(String profileUUID) {
+ public static int getProfileIndex(long profileId) {
try (LockHolder ignored = profilesLocker.lockForReading()) {
List<MobileLedgerProfile> prList = profiles.getValue();
if (prList == null)
throw new AssertionError();
for (int i = 0; i < prList.size(); i++) {
MobileLedgerProfile p = prList.get(i);
- if (p.getUuid()
- .equals(profileUUID))
+ if (p.getId() == profileId)
return i;
}
}
}
public static int retrieveCurrentThemeIdFromDb() {
- String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null);
- if (profileUUID == null)
+ long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0);
+ if (profileId == 0)
return -1;
SQLiteDatabase db = App.getDatabase();
try (Cursor c = db.rawQuery("SELECT theme from profiles where uuid=?",
- new String[]{profileUUID}))
+ new String[]{String.valueOf(profileId)}))
{
if (c.moveToNext())
return c.getInt(0);
return -1;
}
@Nullable
- public static MobileLedgerProfile getProfile(String profileUUID) {
+ public static MobileLedgerProfile getProfile(long profileId) {
MobileLedgerProfile profile;
try (LockHolder readLock = profilesLocker.lockForReading()) {
List<MobileLedgerProfile> prList = profiles.getValue();
if ((prList == null) || prList.isEmpty()) {
readLock.close();
try (LockHolder ignored = profilesLocker.lockForWriting()) {
- profile = MobileLedgerProfile.loadAllFromDB(profileUUID);
+ profile = MobileLedgerProfile.loadAllFromDB(profileId);
}
}
else {
- int i = getProfileIndex(profileUUID);
+ int i = getProfileIndex(profileId);
if (i == -1)
i = 0;
profile = prList.get(i);
if (currentProfile != null)
return currentProfile;
- String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null);
- MobileLedgerProfile startupProfile = getProfile(profileUUID);
+ long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0);
+ MobileLedgerProfile startupProfile = getProfile(profileId);
if (startupProfile != null)
setCurrentProfile(startupProfile);
Logger.debug("profile", "initProfile() returning " + startupProfile);
/*
- * Copyright © 2020 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
return res;
return Float.compare(o1.getAmount(), o2.getAmount());
};
- private final String profile;
+ private final long profile;
private final Integer id;
+ private final List<LedgerTransactionAccount> accounts;
private SimpleDate date;
private String description;
private String comment;
- private final List<LedgerTransactionAccount> accounts;
private String dataHash;
private boolean dataLoaded;
public LedgerTransaction(Integer id, String dateString, String description)
}
public LedgerTransaction(Integer id, SimpleDate date, String description,
MobileLedgerProfile profile) {
- this.profile = profile.getUuid();
+ this.profile = profile.getId();
this.id = id;
this.date = date;
this.description = description;
public LedgerTransaction(int id) {
this(id, (SimpleDate) null, null);
}
- public LedgerTransaction(int id, String profileUUID) {
- this.profile = profileUUID;
+ public LedgerTransaction(int id, long profileId) {
+ this.profile = profileId;
this.id = id;
this.date = null;
this.description = null;
try (Cursor cTr = db.rawQuery(
"SELECT year, month, day, description, comment from transactions WHERE profile=? " +
- "AND id=?", new String[]{profile, String.valueOf(id)}))
+ "AND id=?", new String[]{String.valueOf(profile), String.valueOf(id)}))
{
if (cTr.moveToFirst()) {
date = new SimpleDate(cTr.getInt(0), cTr.getInt(1), cTr.getInt(2));
try (Cursor cAcc = db.rawQuery(
"SELECT account_name, amount, currency, comment FROM " +
"transaction_accounts WHERE profile=? AND transaction_id = ?",
- new String[]{profile, String.valueOf(id)}))
+ new String[]{String.valueOf(profile), String.valueOf(id)}))
{
while (cAcc.moveToNext()) {
// debug("transactions",
public final class MobileLedgerProfile {
// N.B. when adding new fields, update the copy-constructor below
- private final String uuid;
+ private final long id;
private String name;
private boolean permitPosting;
private boolean showCommentsByDefault;
private HledgerVersion detectedVersion;
// N.B. when adding new fields, update the copy-constructor below
transient private AccountAndTransactionListSaver accountAndTransactionListSaver;
- public MobileLedgerProfile(String uuid) {
- this.uuid = uuid;
+ public MobileLedgerProfile(long id) {
+ this.id = id;
}
public MobileLedgerProfile(MobileLedgerProfile origin) {
- uuid = origin.uuid;
+ id = origin.id;
name = origin.name;
permitPosting = origin.permitPosting;
showCommentsByDefault = origin.showCommentsByDefault;
}
// loads all profiles into Data.profiles
// returns the profile with the given UUID
- public static MobileLedgerProfile loadAllFromDB(@Nullable String currentProfileUUID) {
+ public static MobileLedgerProfile loadAllFromDB(long currentProfileId) {
MobileLedgerProfile result = null;
ArrayList<MobileLedgerProfile> list = new ArrayList<>();
SQLiteDatabase db = App.getDatabase();
- try (Cursor cursor = db.rawQuery("SELECT uuid, name, url, use_authentication, auth_user, " +
+ try (Cursor cursor = db.rawQuery("SELECT id, name, url, use_authentication, auth_user, " +
"auth_password, permit_posting, theme, order_no, " +
"preferred_accounts_filter, future_dates, api_version, " +
"show_commodity_by_default, default_commodity, " +
"profiles order by order_no", null))
{
while (cursor.moveToNext()) {
- MobileLedgerProfile item = new MobileLedgerProfile(cursor.getString(0));
+ MobileLedgerProfile item = new MobileLedgerProfile(cursor.getLong(0));
item.setName(cursor.getString(1));
item.setUrl(cursor.getString(2));
item.setAuthEnabled(cursor.getInt(3) == 1);
}
}
list.add(item);
- if (item.getUuid()
- .equals(currentProfileUUID))
+ if (item.getId() == currentProfileId)
result = item;
}
}
int orderNo = 0;
for (MobileLedgerProfile p : Objects.requireNonNull(Data.profiles.getValue())) {
db.execSQL("update profiles set order_no=? where uuid=?",
- new Object[]{orderNo, p.getUuid()});
+ new Object[]{orderNo, p.getId()});
p.orderNo = orderNo;
orderNo++;
}
return false;
MobileLedgerProfile p = (MobileLedgerProfile) obj;
- if (!uuid.equals(p.uuid))
+ if (id != p.id)
return false;
if (!name.equals(p.name))
return false;
public void setPostingPermitted(boolean permitPosting) {
this.permitPosting = permitPosting;
}
- public String getUuid() {
- return uuid;
+ public long getId() {
+ return id;
}
public String getName() {
return name;
// "url=%s, permit_posting=%s, authEnabled=%s, " +
// "themeHue=%d", uuid, name, url,
// permitPosting ? "TRUE" : "FALSE", authEnabled ? "TRUE" : "FALSE", themeHue));
- db.execSQL("REPLACE INTO profiles(uuid, name, permit_posting, url, " +
+ db.execSQL("REPLACE INTO profiles(id, name, permit_posting, url, " +
"use_authentication, auth_user, auth_password, theme, order_no, " +
"preferred_accounts_filter, future_dates, api_version, " +
"show_commodity_by_default, default_commodity, show_comments_by_default," +
"detected_version_pre_1_19, detected_version_major, " +
"detected_version_minor) " +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
- new Object[]{uuid, name, permitPosting, url, authEnabled,
+ new Object[]{id, name, permitPosting, url, authEnabled,
authEnabled ? authUserName : null,
authEnabled ? authPassword : null, themeHue, orderNo,
preferredAccountsFilter, futureDates.toInt(), apiVersion.toInt(),
params.add(acc.isExpanded() ? 1 : 0);
}
sql += " where profile=? and name=?";
- params.add(uuid);
+ params.add(id);
params.add(acc.getName());
db.execSQL(sql, params.toArray());
db.execSQL("insert into accounts(profile, name, name_upper, parent_name, level, " +
"expanded, generation) select ?,?,?,?,?,0,? where (select changes() = 0)",
- new Object[]{uuid, acc.getName(), acc.getName().toUpperCase(), acc.getParentName(),
+ new Object[]{id, acc.getName(), acc.getName().toUpperCase(), acc.getParentName(),
acc.getLevel(), generation
});
// debug("accounts", String.format("Stored account '%s' in DB [%s]", acc.getName(), uuid));
db.execSQL("replace into account_values(profile, account, " +
"currency, value, generation) values(?, ?, ?, ?, ?);",
- new Object[]{uuid, name, Misc.emptyIsNull(currency), amount, generation});
+ new Object[]{id, name, Misc.emptyIsNull(currency), amount, generation});
}
public void storeTransaction(SQLiteDatabase db, int generation, LedgerTransaction tr) {
tr.fillDataHash();
db.execSQL("UPDATE transactions SET year=?, month=?, day=?, description=?, comment=?, " +
"data_hash=?, generation=? WHERE profile=? AND id=?",
new Object[]{d.year, d.month, d.day, tr.getDescription(), tr.getComment(),
- tr.getDataHash(), generation, uuid, tr.getId()
+ tr.getDataHash(), generation, id, tr.getId()
});
db.execSQL("INSERT INTO transactions(profile, id, year, month, day, description, " +
"comment, data_hash, generation) " +
"select ?,?,?,?,?,?,?,?,? WHERE (select changes() = 0)",
- new Object[]{uuid, tr.getId(), tr.getDate().year, tr.getDate().month,
+ new Object[]{id, tr.getId(), tr.getDate().year, tr.getDate().month,
tr.getDate().day, tr.getDescription(), tr.getComment(),
tr.getDataHash(), generation
});
"WHERE profile=? AND transaction_id=? AND order_no=?",
new Object[]{item.getAccountName(), item.getAmount(),
Misc.nullIsEmpty(item.getCurrency()), item.getComment(),
- generation, uuid, tr.getId(), accountOrderNo
+ generation, id, tr.getId(), accountOrderNo
});
db.execSQL("INSERT INTO transaction_accounts(profile, transaction_id, " +
"order_no, account_name, amount, currency, comment, generation) " +
"select ?, ?, ?, ?, ?, ?, ?, ? WHERE (select changes() = 0)",
- new Object[]{uuid, tr.getId(), accountOrderNo, item.getAccountName(),
+ new Object[]{id, tr.getId(), accountOrderNo, item.getAccountName(),
item.getAmount(), Misc.nullIsEmpty(item.getCurrency()),
item.getComment(), generation
});
public String getOption(String name, String default_value) {
SQLiteDatabase db = App.getDatabase();
try (Cursor cursor = db.rawQuery("select value from options where profile = ? and name=?",
- new String[]{uuid, name}))
+ new String[]{String.valueOf(id), name}))
{
if (cursor.moveToFirst()) {
String result = cursor.getString(0);
public void setOption(String name, String value) {
debug("profile", String.format("setting option %s=%s", name, value));
DbOpQueue.add("insert or replace into options(profile, name, value) values(?, ?, ?);",
- new String[]{uuid, name, value});
+ new String[]{String.valueOf(id), name, value});
}
public void setLongOption(String name, long value) {
setOption(name, String.valueOf(value));
}
public void removeFromDB() {
SQLiteDatabase db = App.getDatabase();
- debug("db", String.format("removing profile %s from DB", uuid));
+ debug("db", String.format(Locale.ROOT, "removing profile %d from DB", id));
db.beginTransactionNonExclusive();
try {
- Object[] uuid_param = new Object[]{uuid};
- db.execSQL("delete from transaction_accounts where profile=?", uuid_param);
- db.execSQL("delete from transactions where profile=?", uuid_param);
- db.execSQL("delete from account_values where profile=?", uuid_param);
- db.execSQL("delete from accounts where profile=?", uuid_param);
- db.execSQL("delete from options where profile=?", uuid_param);
- db.execSQL("delete from profiles where uuid=?", uuid_param);
+ Object[] id_param = new Object[]{id};
+ db.execSQL("delete from transaction_accounts where profile=?", id_param);
+ db.execSQL("delete from transactions where profile=?", id_param);
+ db.execSQL("delete from account_values where profile=?", id_param);
+ db.execSQL("delete from accounts where profile=?", id_param);
+ db.execSQL("delete from options where profile=?", id_param);
+ db.execSQL("delete from profiles where uuid=?", id_param);
db.setTransactionSuccessful();
}
finally {
}
}
public LedgerTransaction loadTransaction(int transactionId) {
- LedgerTransaction tr = new LedgerTransaction(transactionId, this.uuid);
+ LedgerTransaction tr = new LedgerTransaction(transactionId, this.id);
tr.loadData(App.getDatabase());
return tr;
public int getNextTransactionsGeneration(SQLiteDatabase db) {
int generation = 1;
try (Cursor c = db.rawQuery("SELECT generation FROM transactions WHERE profile=? LIMIT 1",
- new String[]{uuid}))
+ new String[]{String.valueOf(id)}))
{
if (c.moveToFirst()) {
generation = c.getInt(0) + 1;
private int getNextAccountsGeneration(SQLiteDatabase db) {
int generation = 1;
try (Cursor c = db.rawQuery("SELECT generation FROM accounts WHERE profile=? LIMIT 1",
- new String[]{uuid}))
+ new String[]{String.valueOf(id)}))
{
if (c.moveToFirst()) {
generation = c.getInt(0) + 1;
private void deleteNotPresentAccounts(SQLiteDatabase db, int generation) {
Logger.debug("db/benchmark", "Deleting obsolete accounts");
db.execSQL("DELETE FROM account_values WHERE profile=? AND generation <> ?",
- new Object[]{uuid, generation});
+ new Object[]{id, generation});
db.execSQL("DELETE FROM accounts WHERE profile=? AND generation <> ?",
- new Object[]{uuid, generation});
+ new Object[]{id, generation});
Logger.debug("db/benchmark", "Done deleting obsolete accounts");
}
private void deleteNotPresentTransactions(SQLiteDatabase db, int generation) {
Logger.debug("db/benchmark", "Deleting obsolete transactions");
db.execSQL("DELETE FROM transaction_accounts WHERE profile=? AND generation <> ?",
- new Object[]{uuid, generation});
+ new Object[]{id, generation});
db.execSQL("DELETE FROM transactions WHERE profile=? AND generation <> ?",
- new Object[]{uuid, generation});
+ new Object[]{id, generation});
Logger.debug("db/benchmark", "Done deleting obsolete transactions");
}
public void wipeAllData() {
SQLiteDatabase db = App.getDatabase();
db.beginTransaction();
try {
- String[] pUuid = new String[]{uuid};
+ String[] pUuid = new String[]{String.valueOf(id)};
db.execSQL("delete from options where profile=?", pUuid);
db.execSQL("delete from accounts where profile=?", pUuid);
db.execSQL("delete from account_values where profile=?", pUuid);
/*
- * Copyright © 2020 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
@Override
public void run() {
Logger.debug("async-acc", "AccountListLoader::run() entered");
- String profileUUID = profile.getUuid();
+ long profileId = profile.getId();
ArrayList<LedgerAccount> list = new ArrayList<>();
HashMap<String, LedgerAccount> map = new HashMap<>();
SQLiteDatabase db = App.getDatabase();
Logger.debug("async-acc", "AccountListLoader::run() connected to DB");
- try (Cursor cursor = db.rawQuery(sql, new String[]{profileUUID})) {
+ try (Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(profileId)})) {
Logger.debug("async-acc", "AccountListLoader::run() executed query");
while (cursor.moveToNext()) {
if (isInterrupted())
final String accName = cursor.getString(0);
// debug("accounts",
// String.format("Read account '%s' from DB [%s]", accName,
-// profileUUID));
+// profileId));
String parentName = LedgerAccount.extractParentName(accName);
LedgerAccount parent;
if (parentName != null) {
try (Cursor c2 = db.rawQuery(
"SELECT value, currency FROM account_values WHERE profile = ?" + " " +
- "AND account = ?", new String[]{profileUUID, accName}))
+ "AND account = ?", new String[]{String.valueOf(profileId), accName}))
{
while (c2.moveToNext()) {
acc.addAmount(c2.getFloat(0), c2.getString(1));
model.updateDisplayedAccounts();
DbOpQueue.add("update accounts set expanded=? where name=? and profile=?",
- new Object[]{mAccount.isExpanded(), mAccount.getName(), profile.getUuid()
+ new Object[]{mAccount.isExpanded(), mAccount.getName(), profile.getId()
});
}
return;
DbOpQueue.add("update accounts set amounts_expanded=? where name=? and profile=?",
- new Object[]{mAccount.amountsExpanded(), mAccount.getName(), profile.getUuid()
+ new Object[]{mAccount.amountsExpanded(), mAccount.getName(), profile.getId()
});
}
continue;
final ShortcutInfo.Builder builder =
- new ShortcutInfo.Builder(this, "new_transaction_" + p.getUuid());
+ new ShortcutInfo.Builder(this, "new_transaction_" + p.getId());
ShortcutInfo si = builder.setShortLabel(p.getName())
.setIcon(Icon.createWithResource(this,
R.drawable.thick_plus_icon))
.setIntent(new Intent(Intent.ACTION_VIEW, null, this,
- NewTransactionActivity.class).putExtra("profile_uuid",
- p.getUuid()))
+ NewTransactionActivity.class).putExtra("profile_id",
+ p.getId()))
.setRank(i)
.build();
shortcuts.add(si);
/*
- * Copyright © 2020 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
private static class DatabaseInitTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
- MobileLedgerProfile.loadAllFromDB(null);
+ MobileLedgerProfile.loadAllFromDB(0);
- String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null);
- MobileLedgerProfile startupProfile = Data.getProfile(profileUUID);
+ long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0);
+ MobileLedgerProfile startupProfile = Data.getProfile(profileId);
if (startupProfile != null)
Data.postCurrentProfile(startupProfile);
return null;
}
@Override
protected void initProfile() {
- String profileUUID = getIntent().getStringExtra("profile_uuid");
+ long profileId = getIntent().getLongExtra("profile_id", 0);
- if (profileUUID != null) {
- mProfile = Data.getProfile(profileUUID);
+ if (profileId != 0) {
+ mProfile = Data.getProfile(profileId);
if (mProfile == null)
finish();
Data.setCurrentProfile(mProfile);
}
@Override
public boolean onRow(@NonNull Cursor cursor) {
- final String profileUUID = cursor.getString(0);
+ final long profileId = cursor.getLong(0);
final int transactionId = cursor.getInt(1);
- runOnUiThread(() -> model.loadTransactionIntoModel(profileUUID, transactionId));
+ runOnUiThread(() -> model.loadTransactionIntoModel(profileId, transactionId));
return false; // limit 1, by the way
}
@Override
}
@Override
public boolean onRow(@NonNull Cursor cursor) {
- final String profileUUID = cursor.getString(0);
+ final long profileId = cursor.getLong(0);
final int transactionId = cursor.getInt(1);
- runOnUiThread(() -> model.loadTransactionIntoModel(profileUUID,
+ runOnUiThread(() -> model.loadTransactionIntoModel(profileId,
transactionId));
return false;
}
return tr;
}
- void loadTransactionIntoModel(String profileUUID, int transactionId) {
+ void loadTransactionIntoModel(long profileId, int transactionId) {
List<Item> newList = new ArrayList<>();
Item.resetIdDispenser();
LedgerTransaction tr;
- MobileLedgerProfile profile = Data.getProfile(profileUUID);
+ MobileLedgerProfile profile = Data.getProfile(profileId);
if (profile == null)
throw new RuntimeException(String.format(
"Unable to find profile %s, which is supposed to contain transaction %d",
- profileUUID, transactionId));
+ profileId, transactionId));
tr = profile.loadTransaction(transactionId);
TransactionHead head = new TransactionHead(tr.getDescription());
index));
debug("profiles", String.format(Locale.ENGLISH, "Editing profile %s (%s); hue=%d",
- profile.getName(), profile.getUuid(), profile.getThemeHue()));
+ profile.getName(), profile.getId(), profile.getThemeHue()));
}
}
import java.net.URL;
import java.util.ArrayList;
import java.util.Objects;
-import java.util.UUID;
import static net.ktnx.mobileledger.utils.Logger.debug;
builder.setTitle(mProfile.getName());
builder.setMessage(R.string.remove_profile_dialog_message);
builder.setPositiveButton(R.string.Remove, (dialog, which) -> {
- debug("profiles", String.format("[fragment] removing profile %s", mProfile.getUuid()));
+ debug("profiles", String.format("[fragment] removing profile %s", mProfile.getId()));
mProfile.removeFromDB();
ArrayList<MobileLedgerProfile> oldList = Data.profiles.getValue();
if (oldList == null)
// debug("profiles", String.format("Selected item is %d", mProfile.getThemeHue()));
final MobileLedgerProfile currentProfile = Data.getProfile();
- if (mProfile.getUuid()
- .equals(currentProfile.getUuid()))
- {
+ if (mProfile.getId() == currentProfile.getId()) {
Data.setCurrentProfile(mProfile);
}
viewAdapter.notifyItemChanged(pos);
}
else {
- mProfile = new MobileLedgerProfile(String.valueOf(UUID.randomUUID()));
+ mProfile = new MobileLedgerProfile(0);
model.updateProfile(mProfile);
mProfile.storeInDB();
final ArrayList<MobileLedgerProfile> newList = new ArrayList<>(profiles);
public static final String DESCRIPTION_HISTORY_TABLE = "description_history";
public static final String OPT_LAST_SCRAPE = "last_scrape";
@NonNls
- public static final String OPT_PROFILE_UUID = "profile_uuid";
+ public static final String OPT_PROFILE_ID = "profile_id";
private static final String NO_PROFILE = "-";
@SuppressWarnings("unused")
static public int getIntOption(String name, int default_value) {
--- /dev/null
+-- Copyright © 2021 Damyan Ivanov.
+-- This file is part of MoLe.
+-- MoLe is free software: you can distribute it and/or modify it
+-- under the term of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your opinion), any later version.
+--
+-- MoLe is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License terms for details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+
+-- migrate from revision 58 to revision 59
+
+CREATE TABLE profiles_new (
+id INTEGER NOT NULL PRIMARY KEY,
+uuid text,
+name TEXT NOT NULL,
+url TEXT NOT NULL,
+use_authentication INTEGER NOT NULL,
+auth_user TEXT,
+auth_password TEXT,
+order_no INTEGER NOT NULL,
+permit_posting INTEGER NOT NULL,
+theme INTEGER NOT NULL DEFAULT -1,
+preferred_accounts_filter TEXT,
+future_dates INTEGER NOT NULL,
+api_version INTEGER NOT NULL,
+show_commodity_by_default INTEGER NOT NULL,
+default_commodity TEXT,
+show_comments_by_default INTEGER NOT NULL DEFAULT 1,
+detected_version_pre_1_19 INTEGER NOT NULL,
+detected_version_major INTEGER NOT NULL,
+detected_version_minor INTEGER NOT NULL);
+
+insert into profiles_new(
+ uuid, name, url, use_authentication, auth_user, auth_password,
+ order_no, permit_posting, theme, preferred_accounts_filter, future_dates, api_version,
+ show_commodity_by_default, default_commodity, show_comments_by_default, detected_version_pre_1_19,
+ detected_version_major, detected_version_minor)
+select uuid, name, url, use_authentication, auth_user, auth_password,
+ order_no, permit_posting, theme, preferred_accounts_filter, future_dates, api_version,
+ show_commodity_by_default, default_commodity, show_comments_by_default, detected_version_pre_1_19,
+ detected_version_major, detected_version_minor
+from profiles;
+
+create table accounts_new(
+id integer primary key not null,
+profile_id integer not null references profiles_new(id) on delete cascade on update restrict,
+level INTEGER NOT NULL,
+name TEXT NOT NULL,
+name_upper TEXT NOT NULL,
+parent_name TEXT,
+expanded INTEGER NOT NULL DEFAULT 1,
+amounts_expanded INTEGER NOT NULL DEFAULT 0,
+generation INTEGER NOT NULL DEFAULT 0);
+
+insert into accounts_new(profile_id, level, name, name_upper, parent_name, expanded, amounts_expanded, generation)
+select p.id, a.level, a.name, a.name_upper, a.parent_name, a.expanded, a.amounts_expanded, a.generation
+from profiles_new p
+join accounts a on a.profile=p.uuid;
+
+drop table accounts;
+alter table accounts_new rename to accounts;
+
+drop table profiles;
+alter table profiles_new rename to profiles;
\ No newline at end of file