]> git.ktnx.net Git - mobile-ledger.git/commitdiff
migrate to surrogate IDs for all database objects
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 18 Mar 2021 17:09:34 +0000 (19:09 +0200)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Wed, 24 Mar 2021 19:46:24 +0000 (19:46 +0000)
along with foreign keys etc

27 files changed:
app/schemas/net.ktnx.mobileledger.db.DB/59.json [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/async/UpdateTransactionsTask.java
app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java
app/src/main/java/net/ktnx/mobileledger/dao/BaseDAO.java
app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java
app/src/main/java/net/ktnx/mobileledger/db/Account.java
app/src/main/java/net/ktnx/mobileledger/db/AccountAutocompleteAdapter.java
app/src/main/java/net/ktnx/mobileledger/db/AccountValue.java
app/src/main/java/net/ktnx/mobileledger/db/DB.java
app/src/main/java/net/ktnx/mobileledger/db/Option.java
app/src/main/java/net/ktnx/mobileledger/db/Profile.java
app/src/main/java/net/ktnx/mobileledger/db/Transaction.java
app/src/main/java/net/ktnx/mobileledger/db/TransactionAccount.java
app/src/main/java/net/ktnx/mobileledger/model/Data.java
app/src/main/java/net/ktnx/mobileledger/model/LedgerTransaction.java
app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java
app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java
app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfileDetailActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfileDetailFragment.java
app/src/main/java/net/ktnx/mobileledger/utils/MLDB.java
app/src/main/res/raw/db_59.sql [new file with mode: 0644]

diff --git a/app/schemas/net.ktnx.mobileledger.db.DB/59.json b/app/schemas/net.ktnx.mobileledger.db.DB/59.json
new file mode 100644 (file)
index 0000000..3ebd83f
--- /dev/null
@@ -0,0 +1,849 @@
+{
+  "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
index 43de74025ad83ca7dc8dd511080dcc60d2d3c55c..b6e9c3edad505f5d786a30388c0eaa26e4ede6fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -34,7 +34,7 @@ public class UpdateTransactionsTask extends AsyncTask<MainModel, Void, String> {
     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;
@@ -45,7 +45,7 @@ public class UpdateTransactionsTask extends AsyncTask<MainModel, Void, String> {
             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 {
@@ -55,7 +55,7 @@ public class UpdateTransactionsTask extends AsyncTask<MainModel, Void, String> {
                       "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);
index 31aafbf200fdf9dc6a796c55666ced9816e33ee5..bb8d4ec2333d9e34a647d83c28ded648cdfd1762 100644 (file)
@@ -33,8 +33,16 @@ import java.util.List;
 
 @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);
@@ -45,40 +53,31 @@ public abstract class AccountDAO extends BaseDAO<Account> {
     @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 " +
index 0bcb3e488d7fe25b7e15b91f9949af795860d0dc..b68ea5157a76abd8ff84898dfb7285fa76085083 100644 (file)
@@ -24,12 +24,12 @@ import android.os.Looper;
 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));
         });
     }
 
@@ -49,6 +49,7 @@ abstract class BaseDAO<T> {
                 new Handler(Looper.getMainLooper()).post(onDone);
         });
     }
-
-
+    interface OnInsertedReceiver {
+        void onInsert(long id);
+    }
 }
diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java
new file mode 100644 (file)
index 0000000..ac51ecc
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+}
index 0bee771368900def7e73e8b111ce7e76243e9f81..81c5df6744b7424f4a4b00bf7df661d29e33239d 100644 (file)
@@ -42,7 +42,7 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
         return result;
     }
     @Insert
-    public abstract void insertSync(Transaction item);
+    public abstract long insertSync(Transaction item);
 
     @Update
     public abstract void updateSync(Transaction item);
@@ -57,8 +57,8 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
 //    @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 " +
index d0eb1ae08829c23838a2b58b3f88949b960f4dee..59c164b0b8e561423bed8bb7bda37743abaaec3c 100644 (file)
@@ -20,14 +20,25 @@ package net.ktnx.mobileledger.db;
 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;
@@ -42,12 +53,17 @@ public class Account {
     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() {
index 6cf1dc8c3c16808c7b9f310c1b04229d4d5fc6de..42bf4c6b50f4e9579fcf4e894d7f97be85ecc564 100644 (file)
@@ -34,16 +34,16 @@ public class AccountAutocompleteAdapter extends ArrayAdapter<String> {
     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
@@ -73,12 +73,11 @@ public class AccountAutocompleteAdapter extends ArrayAdapter<String> {
             }
 
             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();
 
index f41b0e73ddce51c641cbd258dafa7bd4b81a7910..0b39c4756be500b3a6ea655aa13981b4fed10217 100644 (file)
@@ -20,20 +20,24 @@ package net.ktnx.mobileledger.db;
 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 = "";
@@ -41,19 +45,17 @@ public class AccountValue {
     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() {
index 32e8b216a47dac5d0f73a10b8395a4015db4e585..7dcb0897746b29ecb5696716b51c9924f647b58b 100644 (file)
@@ -50,7 +50,7 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
                       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() {
index 2b8f6a3cff72a8db60789aa6bedead02a26906a7..180295b36e2517b8add676c09b39c27fb85b3597 100644 (file)
@@ -21,22 +21,20 @@ import androidx.annotation.NonNull;
 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() {
index 1ed8a21b1d1666498c88850e4a31f83de4c8d0a0..d23303807053b73c7e32847457d9782b771c7fd7 100644 (file)
@@ -20,21 +20,13 @@ package net.ktnx.mobileledger.db;
 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 = "";
@@ -71,12 +63,11 @@ public class Profile {
     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() {
index f6808f9406c6f4c292ef939512a1205ba3175fee..d67cedf53d6df0002205cbd170a886f971ace772 100644 (file)
@@ -20,7 +20,11 @@ package net.ktnx.mobileledger.db;
 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
@@ -29,16 +33,20 @@ collate NOCASE not null, comment varchar, generation integer default 0, primary
 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;
@@ -55,22 +63,22 @@ public class Transaction {
     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() {
index 6d51fec9d44e5cb1c20b8f89fb6f5832b96b01e2..730a866a1daf2d2da48cefc491ab6ed3137b487e 100644 (file)
@@ -22,19 +22,20 @@ import androidx.room.ColumnInfo;
 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")
@@ -51,13 +52,13 @@ public class TransactionAccount {
     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;
     }
index c0f4cebaf81c7491185fd6cc7fbd2657727904e7..3520711bff846952c7c2c6bc61b73cdb706fb730 100644 (file)
@@ -94,11 +94,11 @@ public final class Data {
         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) {
@@ -116,15 +116,14 @@ public final class Data {
         }
     }
     @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;
             }
 
@@ -132,13 +131,13 @@ public final class Data {
         }
     }
     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);
@@ -147,18 +146,18 @@ public final class Data {
         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);
@@ -218,8 +217,8 @@ public final class Data {
         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);
index e566ca8f06fb8f5373ee15036f3c868f4895d086..2eba45e9cd4ea1e58d243b794c59d3547b2aaa81 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -52,12 +52,12 @@ public class LedgerTransaction {
             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)
@@ -66,7 +66,7 @@ public class LedgerTransaction {
     }
     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;
@@ -83,8 +83,8 @@ public class LedgerTransaction {
     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;
@@ -170,7 +170,7 @@ public class LedgerTransaction {
 
         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));
@@ -182,7 +182,7 @@ public class LedgerTransaction {
                 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",
index b8085765eb069048f78cfe5621e93ef8e5887268..fddacf67d44e794bc84efcd97d2cefba22d3a255 100644 (file)
@@ -51,7 +51,7 @@ import static net.ktnx.mobileledger.utils.Logger.debug;
 
 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;
@@ -71,11 +71,11 @@ public final class MobileLedgerProfile {
     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;
@@ -97,11 +97,11 @@ public final class MobileLedgerProfile {
     }
     // 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, " +
@@ -110,7 +110,7 @@ public final class MobileLedgerProfile {
                                          "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);
@@ -141,8 +141,7 @@ public final class MobileLedgerProfile {
                     }
                 }
                 list.add(item);
-                if (item.getUuid()
-                        .equals(currentProfileUUID))
+                if (item.getId() == currentProfileId)
                     result = item;
             }
         }
@@ -156,7 +155,7 @@ public final class MobileLedgerProfile {
             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++;
             }
@@ -194,7 +193,7 @@ public final class MobileLedgerProfile {
             return false;
 
         MobileLedgerProfile p = (MobileLedgerProfile) obj;
-        if (!uuid.equals(p.uuid))
+        if (id != p.id)
             return false;
         if (!name.equals(p.name))
             return false;
@@ -281,8 +280,8 @@ public final class MobileLedgerProfile {
     public void setPostingPermitted(boolean permitPosting) {
         this.permitPosting = permitPosting;
     }
-    public String getUuid() {
-        return uuid;
+    public long getId() {
+        return id;
     }
     public String getName() {
         return name;
@@ -334,14 +333,14 @@ public final class MobileLedgerProfile {
 //                                            "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(),
@@ -368,13 +367,13 @@ public final class MobileLedgerProfile {
             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));
@@ -401,7 +400,7 @@ public final class MobileLedgerProfile {
 
         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();
@@ -410,12 +409,12 @@ public final class MobileLedgerProfile {
         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
                 });
@@ -427,12 +426,12 @@ public final class MobileLedgerProfile {
                        "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
                     });
@@ -444,7 +443,7 @@ public final class MobileLedgerProfile {
     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);
@@ -489,23 +488,23 @@ public final class MobileLedgerProfile {
     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 {
@@ -513,7 +512,7 @@ public final class MobileLedgerProfile {
         }
     }
     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;
@@ -532,7 +531,7 @@ public final class MobileLedgerProfile {
     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;
@@ -543,7 +542,7 @@ public final class MobileLedgerProfile {
     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;
@@ -554,24 +553,24 @@ public final class MobileLedgerProfile {
     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);
index 44a6f303be75fac7d193b16cee33400af49d5e9b..813e3a00b3ab54a49b480ccad5f9b5584d077cd5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -286,7 +286,7 @@ public class MainModel extends ViewModel {
         @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<>();
 
@@ -296,7 +296,7 @@ public class MainModel extends ViewModel {
 
             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())
@@ -305,7 +305,7 @@ public class MainModel extends ViewModel {
                     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) {
@@ -326,7 +326,7 @@ public class MainModel extends ViewModel {
 
                     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));
index b8e4c303b588a1f9514763fde3e04f317d6133fb..20b72915b9224158cfb11d07d0caa3c5c71b4afa 100644 (file)
@@ -169,7 +169,7 @@ public class AccountSummaryAdapter
             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()
                     });
 
         }
@@ -192,7 +192,7 @@ public class AccountSummaryAdapter
                 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()
                     });
 
         }
index c4d5c24a9f38016ac413373fa8e3eb5c74827874..25efe111e255aa4b8f82fdcaad5be83af6285ac0 100644 (file)
@@ -347,13 +347,13 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                 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);
index 4205c80b5775084c898792bdca8c3115479b0bee..2a238adc61333e826c5c5340034e26c311a11f39 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -100,10 +100,10 @@ public class SplashActivity extends CrashReportingActivity {
     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;
index ce97d985d9dc8eb5593b29a5eaa46993e4bd6ed5..469e773e95470a55570a210dcfc423404b9fd3b9 100644 (file)
@@ -115,10 +115,10 @@ public class NewTransactionActivity extends ProfileThemedActivity
     }
     @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);
@@ -378,9 +378,9 @@ public class NewTransactionActivity extends ProfileThemedActivity
             }
             @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
@@ -409,9 +409,9 @@ public class NewTransactionActivity extends ProfileThemedActivity
                             }
                             @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;
                             }
index 7333561c5b52f129a77fe0eefb03de50a2d2c002..641a9b7b8686271f78e8b65f444755dcb77ed456 100644 (file)
@@ -461,15 +461,15 @@ public class NewTransactionModel extends ViewModel {
 
         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 8afdefc10a40b2616d489a8715710cc72ab35f17..d860a678649bc37833f3cf2f98461b863d2c39b8 100644 (file)
@@ -65,7 +65,7 @@ public class ProfileDetailActivity extends CrashReportingActivity {
                                     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()));
             }
         }
 
index 13a8b55a030e1a3d863e84b5506614f6ef774741..f5b48aef3e22c5dc4d0ec52f9ef8670f6bbdd485 100644 (file)
@@ -62,7 +62,6 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Objects;
-import java.util.UUID;
 
 import static net.ktnx.mobileledger.utils.Logger.debug;
 
@@ -111,7 +110,7 @@ public class ProfileDetailFragment extends Fragment {
         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)
@@ -431,9 +430,7 @@ public class ProfileDetailFragment extends Fragment {
 //                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);
             }
 
@@ -442,7 +439,7 @@ public class ProfileDetailFragment extends Fragment {
                 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);
index e11044de2e3085c8cea55e942b3d85608e6e3cf0..2a9dca89a626ac78a504597194b0e8de3ec80b60 100644 (file)
@@ -35,7 +35,7 @@ public final class MLDB {
     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) {
diff --git a/app/src/main/res/raw/db_59.sql b/app/src/main/res/raw/db_59.sql
new file mode 100644 (file)
index 0000000..67c9beb
--- /dev/null
@@ -0,0 +1,70 @@
+-- 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