Repository: rotki/rotki

Issue:
Refactor manual balances identifiers
## Abstract

Currently we use the label of manually tracked balances as identifiers. This makes this field non mutable. 

## Motivation

It makes sense that, if we display the label as data for the balance a user, this field could be modified

## Specification

A new id random id should be generated and work as identifier, leaving the label field free to be modified.


Discussion:

Resolving patch:
diff --git a/rotkehlchen/db/schema.py b/rotkehlchen/db/schema.py
--- a/rotkehlchen/db/schema.py
+++ b/rotkehlchen/db/schema.py
@@ -317,8 +317,9 @@
 
 DB_CREATE_MANUALLY_TRACKED_BALANCES = """
 CREATE TABLE IF NOT EXISTS manually_tracked_balances (
+    id INTEGER PRIMARY KEY,
     asset TEXT NOT NULL,
-    label TEXT NOT NULL PRIMARY KEY,
+    label TEXT NOT NULL,
     amount TEXT,
     location CHAR(1) NOT NULL DEFAULT('A') REFERENCES location(location),
     category CHAR(1) NOT NULL DEFAULT('A') REFERENCES balance_category(category),
diff --git a/rotkehlchen/db/upgrades/v31_v32.py b/rotkehlchen/db/upgrades/v31_v32.py
--- a/rotkehlchen/db/upgrades/v31_v32.py
+++ b/rotkehlchen/db/upgrades/v31_v32.py
@@ -86,6 +86,29 @@ def _add_new_tables(cursor: 'Cursor') -> None:
 );""")  # noqa: E501
 
 
+def _refactor_manual_balance_id(cursor: 'Cursor') -> None:
+    cursor.execute("""
+    CREATE TABLE manually_tracked_balances_copy (
+        id INTEGER PRIMARY KEY,
+        asset TEXT NOT NULL,
+        label TEXT NOT NULL,
+        amount TEXT,
+        location CHAR(1) NOT NULL DEFAULT('A') REFERENCES location(location),
+        category CHAR(1) NOT NULL DEFAULT('A') REFERENCES balance_category(category),
+        FOREIGN KEY(asset) REFERENCES assets(identifier) ON UPDATE CASCADE
+    );""")
+    cursor.execute("""
+    INSERT INTO manually_tracked_balances_copy(asset, label, amount, location, category)
+    SELECT asset, label, amount, location, category
+    FROM manually_tracked_balances;
+    """)
+    cursor.execute('DROP TABLE manually_tracked_balances;')
+    cursor.execute(
+        'ALTER TABLE manually_tracked_balances_copy RENAME TO '
+        'manually_tracked_balances;',
+    )
+
+
 def upgrade_v31_to_v32(db: 'DBHandler') -> None:
     """Upgrades the DB from v31 to v32
     - use new identifiers for the history_events table. The id will be generated by sqlite
@@ -101,4 +124,5 @@ def upgrade_v31_to_v32(db: 'DBHandler') -> None:
     _upgrade_history_events(cursor)
     _remove_gitcoin(cursor)
     _add_new_tables(cursor)
+    _refactor_manual_balance_id(cursor)
     db.conn.commit()


Test patch:
diff --git a/rotkehlchen/tests/data/v31_rotkehlchen.db b/rotkehlchen/tests/data/v31_rotkehlchen.db
Binary files a/rotkehlchen/tests/data/v31_rotkehlchen.db and b/rotkehlchen/tests/data/v31_rotkehlchen.db differ
diff --git a/rotkehlchen/tests/db/test_db_upgrades.py b/rotkehlchen/tests/db/test_db_upgrades.py
--- a/rotkehlchen/tests/db/test_db_upgrades.py
+++ b/rotkehlchen/tests/db/test_db_upgrades.py
@@ -2456,6 +2456,11 @@ def test_upgrade_db_31_to_32(user_data_dir):  # pylint: disable=unused-argument
             'SELECT COUNT(*) FROM sqlite_master WHERE type="table" AND name=?', (name,),
         ).fetchone()[0] == 1
 
+    manual_balance_before = cursor.execute(
+        'SELECT asset, label, amount, location, category FROM '
+        'manually_tracked_balances;',
+    ).fetchall()
+
     db_v31.logout()
     # Execute upgrade
     db = _init_db_with_target_version(
@@ -2485,6 +2490,22 @@ def test_upgrade_db_31_to_32(user_data_dir):  # pylint: disable=unused-argument
             'SELECT COUNT(*) FROM sqlite_master WHERE type="table" AND name=?', (name,),
         ).fetchone()[0] == 0
 
+    manual_balance_after = cursor.execute(
+        'SELECT asset, label, amount, location, category FROM '
+        'manually_tracked_balances;',
+    ).fetchall()
+
+    manual_balance_right = [
+        ('1CR', 'LABEL1', '34.5', 'A', 'B'),
+        ('2GIVE', 'LABEL2', '0.3', 'B', 'B'),
+        ('1CR', 'LABEL3', '3', 'A', 'A'),
+    ]
+    assert manual_balance_right == manual_balance_before == manual_balance_after
+
+    manual_balance_ids = cursor.execute('SELECT id FROM manually_tracked_balances;').fetchall()
+
+    assert [1, 2, 3] == list(map(lambda x: x[0], manual_balance_ids))
+
 
 def test_latest_upgrade_adds_remove_tables(user_data_dir):
     """