06/05 Optimize app: implement Plaid
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
Migration: add Plaid tables.
|
||||
|
||||
Run once after deploying the Plaid integration:
|
||||
python scripts/add_plaid_tables.py
|
||||
|
||||
Creates:
|
||||
plaid_items — one row per connected bank (Item in Plaid terminology)
|
||||
plaid_accounts — one row per Plaid account, mapped to a PFM account
|
||||
plaid_sync_previews — temporary storage for transaction sync previews
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from app.extensions import db
|
||||
|
||||
|
||||
TABLES = [
|
||||
# plaid_items
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `plaid_items` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`item_id` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`access_token` TEXT NOT NULL,
|
||||
`institution_id` VARCHAR(50) DEFAULT NULL,
|
||||
`institution_name` VARCHAR(100) DEFAULT NULL,
|
||||
`cursor` VARCHAR(500) DEFAULT NULL,
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`last_synced_at` DATETIME DEFAULT NULL,
|
||||
INDEX `ix_plaid_items_item_id` (`item_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""",
|
||||
|
||||
# plaid_accounts
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `plaid_accounts` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`item_id` INT NOT NULL,
|
||||
`plaid_account_id` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`pfm_account_id` INT DEFAULT NULL,
|
||||
`account_name` VARCHAR(100) DEFAULT NULL,
|
||||
`account_type` VARCHAR(50) DEFAULT NULL,
|
||||
`account_subtype` VARCHAR(50) DEFAULT NULL,
|
||||
`mask` VARCHAR(10) DEFAULT NULL,
|
||||
`last_sync_date` DATE DEFAULT NULL,
|
||||
`cc_due_date` DATE DEFAULT NULL,
|
||||
`cc_minimum_payment` DECIMAL(12,2) DEFAULT NULL,
|
||||
`cc_last_statement_balance` DECIMAL(12,2) DEFAULT NULL,
|
||||
`cc_is_overdue` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`cc_updated_at` DATETIME DEFAULT NULL,
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `plaid_items`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`pfm_account_id`) REFERENCES `accounts`(`id`) ON DELETE SET NULL,
|
||||
INDEX `ix_plaid_accounts_plaid_account_id` (`plaid_account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""",
|
||||
|
||||
# plaid_sync_previews
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `plaid_sync_previews` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`item_id` INT NOT NULL UNIQUE,
|
||||
`data_json` TEXT NOT NULL,
|
||||
`next_cursor` VARCHAR(500) DEFAULT NULL,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `plaid_items`(`id`) ON DELETE CASCADE,
|
||||
INDEX `ix_plaid_sync_previews_item_id` (`item_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""",
|
||||
]
|
||||
|
||||
|
||||
def run():
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
conn = db.engine.raw_connection()
|
||||
cur = conn.cursor()
|
||||
for ddl in TABLES:
|
||||
name = ddl.strip().split('`')[1]
|
||||
cur.execute(ddl)
|
||||
print(f' ✓ {name}')
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
print('Done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
Reference in New Issue
Block a user