Files
sure/db/schema.rb
T
Brandon 686205c0ff feat(bills): schema and domain core for the bills subsystem (#3201)
* feat(bills): schema and domain core for the bills subsystem

First of three chunks carved out of #3083. This one carries the schema and the
domain layer: no bills pages, no calendar feed, no assistant tools. Nothing here
is reachable from the UI yet, so it changes no user-visible behavior on its own.

Schema, in a single migration with a full down:

- recurrence_rules, recurring_occurrences, recurring_allocations,
  recurring_price_changes and recurring_match_rejections
- bill columns on recurring_transactions (bill_type, payment_url, autopay,
  notes, anchor and end conditions, weekend adjustment, dedup scope)
- the four data backfills, in their original order

Domain layer:

- Schedule, the pure date PORO every cadence resolves through, and
  FrequencyPreset for the labels
- OccurrenceGenerator, Matcher, Allocator, PriceChangeDetector, Classifier,
  DeclaredBill, HistoryBackfiller and PaycheckPlanner
- Pipeline, tying detection to generation, plus the nightly job and rake task

Existing detection code changed in three places, each a bug this schema exposes:

- Cleaner used a flat two-month staleness threshold, which silently retired
  every quarterly and annual series
- SubscriptionAuditGenerator used a flat 45-day overdue threshold, meaningless
  at both ends of the frequency range
- CashFlowWarningGenerator read one projected entry per series, which only
  equalled the monthly amount because every series was monthly; weekly bills
  were under-counted fourfold in its 30-day projection

The JSON API travels with the model rather than the UI, because the status enum
widens here. The API accepts only active and inactive on write; suggested,
paused and ended are lifecycle states owned by detection, so the documented
enum stays truthful.

Uniqueness keys gain dedup_scope alongside amount, never instead of it: a
series that is not price-forked carries a blank scope, so amount is what keeps
two different prices apart.

Suite 7,550 runs, 0 failures. Rubocop and brakeman clean. Eager loading
verified, and the migration reverses and re-applies. Includes the first review round: orphan repair matches income and refuses coincidental twins, session imports persist occurrence mappings across chunks, semimonthly anchors canonicalize, classifier keywords match whole words, and the down refuses rather than failing when price-forked rows exist.

* Address second review round

Bound the cross-currency default allocation by the entry leftover and the
occurrence remainder, matching the same-currency path. Let keyword stems
carry a suffix again after the word-boundary fix silenced them. Skip an
incoherent recurrence rule row instead of rolling back the whole import.
Check rollback collisions per restored index so a refusal cannot land
after the bills tables are dropped. Replay the closed_at test through a
real second import. Preload the orphan repair associations and move the
allocator errors to locale keys.

* Match index NULL semantics in the rollback collision checks

GROUP BY treats NULLs as equal but the restored unique indexes do not:
account_id is nullable and indexed, so two accountless rows can never
collide under any of them. Excluding NULL accounts keeps the guard from
refusing a rollback PostgreSQL can perform. Verified live both ways:
accountless duplicates roll back, a real collision still refuses.

* Address maintainer review

Scope the payable debt-destination subquery to the row and its family
instead of scanning every account in the installation. Batch the cash
flow generator remaining-amount sums into one grouped query, matching
the two sibling sites. Enforce both window bounds in the after_count
branch so a future-anchored plan cannot leak past the requested end
date. Skip the explicit regeneration when the day column change will
fire the model callback anyway. Add the missing locale entry for the
allocation currency validation.
2026-08-31 23:41:38 +02:00

2701 lines
134 KiB
Ruby
Generated

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2026_08_26_090000) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
enable_extension "pgcrypto"
# Custom types defined in this database.
# Note that some types may not work with other database engines. Be careful if changing database.
create_enum "account_status", ["ok", "syncing", "error"]
create_enum "goal_pledge_kind", ["transfer", "manual_save"]
create_enum "goal_pledge_status", ["open", "matched", "cancelled", "expired"]
create_table "account_providers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.datetime "created_at", null: false
t.uuid "provider_id", null: false
t.string "provider_type", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "provider_type"], name: "index_account_providers_on_account_and_provider_type", unique: true
t.index ["provider_type", "provider_id"], name: "index_account_providers_on_provider_type_and_provider_id", unique: true
end
create_table "account_shares", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.datetime "created_at", null: false
t.boolean "include_in_finances", default: true, null: false
t.string "permission", default: "read_only", null: false
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["account_id", "user_id"], name: "index_account_shares_on_account_id_and_user_id", unique: true
t.index ["account_id"], name: "index_account_shares_on_account_id"
t.index ["user_id", "include_in_finances"], name: "index_account_shares_on_user_id_and_include_in_finances"
t.index ["user_id"], name: "index_account_shares_on_user_id"
t.check_constraint "permission::text = ANY (ARRAY['full_control'::character varying::text, 'read_write'::character varying::text, 'read_only'::character varying::text])", name: "chk_account_shares_permission"
end
create_table "account_statements", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id"
t.string "account_last4_hint", limit: 4
t.string "account_name_hint", limit: 200
t.bigint "byte_size", null: false
t.string "checksum", limit: 64, null: false
t.decimal "closing_balance", precision: 19, scale: 4
t.string "content_sha256"
t.string "content_type", limit: 100, null: false
t.datetime "created_at", null: false
t.string "currency", limit: 3
t.uuid "family_id", null: false
t.string "filename", limit: 255, null: false
t.string "institution_name_hint", limit: 200
t.decimal "match_confidence", precision: 5, scale: 4
t.decimal "opening_balance", precision: 19, scale: 4
t.decimal "parser_confidence", precision: 5, scale: 4
t.date "period_end_on"
t.date "period_start_on"
t.string "review_status", default: "unmatched", null: false
t.jsonb "sanitized_parser_output", default: {}, null: false
t.string "source", default: "manual_upload", null: false
t.uuid "suggested_account_id"
t.datetime "updated_at", null: false
t.string "upload_status", default: "stored", null: false
t.index ["account_id", "period_start_on", "period_end_on"], name: "index_account_statements_on_account_period"
t.index ["account_id"], name: "index_account_statements_on_account_id"
t.index ["family_id", "checksum"], name: "index_account_statements_on_family_checksum"
t.index ["family_id", "content_sha256"], name: "index_account_statements_on_family_content_sha256", unique: true, where: "(content_sha256 IS NOT NULL)"
t.index ["family_id", "review_status"], name: "index_account_statements_on_family_review_status"
t.index ["family_id"], name: "index_account_statements_on_family_id"
t.index ["suggested_account_id", "review_status"], name: "index_account_statements_on_suggested_account_review"
t.index ["suggested_account_id"], name: "index_account_statements_on_suggested_account_id"
t.check_constraint "account_last4_hint IS NULL OR char_length(account_last4_hint::text) <= 4", name: "chk_account_statements_account_last4_hint_length"
t.check_constraint "account_name_hint IS NULL OR char_length(account_name_hint::text) <= 200", name: "chk_account_statements_account_name_hint_length"
t.check_constraint "byte_size <= 26214400", name: "chk_account_statements_byte_size_max"
t.check_constraint "byte_size > 0", name: "chk_account_statements_byte_size_positive"
t.check_constraint "char_length(checksum::text) <= 64", name: "chk_account_statements_checksum_length"
t.check_constraint "char_length(content_type::text) <= 100", name: "chk_account_statements_content_type_length"
t.check_constraint "char_length(filename::text) <= 255", name: "chk_account_statements_filename_length"
t.check_constraint "content_sha256 IS NULL OR content_sha256::text ~ '^[0-9a-f]{64}$'::text", name: "chk_account_statements_content_sha256"
t.check_constraint "currency IS NULL OR char_length(currency::text) <= 3", name: "chk_account_statements_currency_length"
t.check_constraint "institution_name_hint IS NULL OR char_length(institution_name_hint::text) <= 200", name: "chk_account_statements_institution_hint_length"
t.check_constraint "match_confidence IS NULL OR match_confidence >= 0::numeric AND match_confidence <= 1::numeric", name: "chk_account_statements_match_confidence"
t.check_constraint "parser_confidence IS NULL OR parser_confidence >= 0::numeric AND parser_confidence <= 1::numeric", name: "chk_account_statements_parser_confidence"
t.check_constraint "period_start_on IS NULL OR period_end_on IS NULL OR period_start_on <= period_end_on", name: "chk_account_statements_period_order"
t.check_constraint "review_status::text = ANY (ARRAY['unmatched'::character varying::text, 'linked'::character varying::text, 'rejected'::character varying::text])", name: "chk_account_statements_review_status"
t.check_constraint "source::text = 'manual_upload'::text", name: "chk_account_statements_source"
t.check_constraint "upload_status::text = ANY (ARRAY['stored'::character varying::text, 'failed'::character varying::text])", name: "chk_account_statements_upload_status"
end
create_table "accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.integer "account_providers_count", default: 0, null: false
t.uuid "accountable_id"
t.string "accountable_type"
t.decimal "balance", precision: 19, scale: 4
t.decimal "cash_balance", precision: 19, scale: 4, default: "0.0"
t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY (ARRAY[('Loan'::character varying)::text, ('CreditCard'::character varying)::text, ('OtherLiability'::character varying)::text])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true
t.datetime "created_at", null: false
t.string "currency"
t.datetime "disabled_at"
t.boolean "enable_category_matcher", default: true, null: false
t.boolean "exclude_from_reports", default: false, null: false
t.uuid "family_id", null: false
t.uuid "import_id"
t.string "institution_domain"
t.string "institution_name"
t.jsonb "locked_attributes", default: {}
t.string "name"
t.text "notes"
t.uuid "owner_id"
t.uuid "plaid_account_id"
t.uuid "simplefin_account_id"
t.string "status", default: "active"
t.string "subtype"
t.datetime "updated_at", null: false
t.index ["accountable_id", "accountable_type"], name: "index_accounts_on_accountable_id_and_accountable_type"
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
t.index ["currency"], name: "index_accounts_on_currency"
t.index ["family_id", "accountable_type"], name: "index_accounts_on_family_id_and_accountable_type"
t.index ["family_id", "exclude_from_reports"], name: "index_accounts_on_family_id_and_exclude_from_reports"
t.index ["family_id", "id"], name: "index_accounts_on_family_id_and_id"
t.index ["family_id", "status", "accountable_type"], name: "index_accounts_on_family_id_status_accountable_type"
t.index ["family_id", "status"], name: "index_accounts_on_family_id_and_status"
t.index ["family_id"], name: "index_accounts_on_family_id"
t.index ["import_id"], name: "index_accounts_on_import_id"
t.index ["owner_id"], name: "index_accounts_on_owner_id"
t.index ["plaid_account_id"], name: "index_accounts_on_plaid_account_id"
t.index ["simplefin_account_id"], name: "index_accounts_on_simplefin_account_id"
t.index ["status"], name: "index_accounts_on_status"
end
create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "blob_id", null: false
t.datetime "created_at", null: false
t.string "name", null: false
t.uuid "record_id", null: false
t.string "record_type", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.bigint "byte_size", null: false
t.string "checksum"
t.string "content_type"
t.datetime "created_at", null: false
t.string "filename", null: false
t.string "key", null: false
t.text "metadata"
t.string "service_name", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "addresses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "addressable_id"
t.string "addressable_type"
t.string "country"
t.string "county"
t.datetime "created_at", null: false
t.string "line1"
t.string "line2"
t.string "locality"
t.string "postal_code"
t.string "region"
t.datetime "updated_at", null: false
t.index ["addressable_type", "addressable_id"], name: "index_addresses_on_addressable"
end
create_table "akahu_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.uuid "akahu_item_id", null: false
t.decimal "available_balance", precision: 19, scale: 4
t.decimal "balance_limit", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.string "formatted_account"
t.jsonb "institution_metadata"
t.string "name"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_akahu_accounts_on_account_id"
t.index ["akahu_item_id", "account_id"], name: "index_akahu_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["akahu_item_id"], name: "index_akahu_accounts_on_akahu_item_id"
end
create_table "akahu_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "app_token"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.text "user_token"
t.index ["family_id"], name: "index_akahu_items_on_family_id"
t.index ["status"], name: "index_akahu_items_on_status"
end
create_table "api_keys", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "display_key", null: false
t.datetime "expires_at"
t.datetime "last_used_at"
t.string "name"
t.datetime "revoked_at"
t.json "scopes"
t.string "source", default: "web"
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["display_key"], name: "index_api_keys_on_display_key", unique: true
t.index ["revoked_at"], name: "index_api_keys_on_revoked_at"
t.index ["user_id", "source"], name: "index_api_keys_on_user_id_and_source"
t.index ["user_id"], name: "index_api_keys_on_user_id"
end
create_table "archived_exports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "download_token_digest", null: false
t.string "email", null: false
t.datetime "expires_at", null: false
t.string "family_name"
t.datetime "updated_at", null: false
t.index ["download_token_digest"], name: "index_archived_exports_on_download_token_digest", unique: true
t.index ["expires_at"], name: "index_archived_exports_on_expires_at"
end
create_table "balances", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.decimal "balance", precision: 19, scale: 4, null: false
t.decimal "cash_adjustments", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "cash_balance", precision: 19, scale: 4, default: "0.0"
t.decimal "cash_inflows", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "cash_outflows", precision: 19, scale: 4, default: "0.0", null: false
t.datetime "created_at", null: false
t.string "currency", default: "USD", null: false
t.date "date", null: false
t.virtual "end_balance", type: :decimal, precision: 19, scale: 4, as: "(((start_cash_balance + ((cash_inflows - cash_outflows) * (flows_factor)::numeric)) + cash_adjustments) + (((start_non_cash_balance + ((non_cash_inflows - non_cash_outflows) * (flows_factor)::numeric)) + net_market_flows) + non_cash_adjustments))", stored: true
t.virtual "end_cash_balance", type: :decimal, precision: 19, scale: 4, as: "((start_cash_balance + ((cash_inflows - cash_outflows) * (flows_factor)::numeric)) + cash_adjustments)", stored: true
t.virtual "end_non_cash_balance", type: :decimal, precision: 19, scale: 4, as: "(((start_non_cash_balance + ((non_cash_inflows - non_cash_outflows) * (flows_factor)::numeric)) + net_market_flows) + non_cash_adjustments)", stored: true
t.integer "flows_factor", default: 1, null: false
t.decimal "net_market_flows", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "non_cash_adjustments", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "non_cash_inflows", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "non_cash_outflows", precision: 19, scale: 4, default: "0.0", null: false
t.virtual "start_balance", type: :decimal, precision: 19, scale: 4, as: "(start_cash_balance + start_non_cash_balance)", stored: true
t.decimal "start_cash_balance", precision: 19, scale: 4, default: "0.0", null: false
t.decimal "start_non_cash_balance", precision: 19, scale: 4, default: "0.0", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "date", "currency"], name: "index_account_balances_on_account_id_date_currency_unique", unique: true
t.index ["account_id", "date"], name: "index_balances_on_account_id_and_date", order: { date: :desc }
t.index ["account_id"], name: "index_balances_on_account_id"
end
create_table "binance_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_type"
t.uuid "binance_item_id", null: false
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "extra", default: {}, null: false
t.jsonb "institution_metadata"
t.string "name"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["account_type"], name: "index_binance_accounts_on_account_type"
t.index ["binance_item_id", "account_type"], name: "index_binance_accounts_on_item_and_type", unique: true
t.index ["binance_item_id"], name: "index_binance_accounts_on_binance_item_id"
end
create_table "binance_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_key"
t.text "api_secret"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_binance_items_on_family_id"
t.index ["status"], name: "index_binance_items_on_status"
end
create_table "brex_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id", null: false
t.string "account_kind", default: "cash", null: false
t.decimal "account_limit", precision: 19, scale: 4
t.string "account_status"
t.string "account_type"
t.decimal "available_balance", precision: 19, scale: 4
t.uuid "brex_item_id", null: false
t.datetime "created_at", null: false
t.string "currency", default: "USD", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.string "name"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["brex_item_id", "account_id"], name: "index_brex_accounts_on_item_and_account_id", unique: true
t.index ["brex_item_id"], name: "index_brex_accounts_on_brex_item_id"
end
create_table "brex_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "base_url"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name", null: false
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.text "token", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_brex_items_on_family_id"
t.index ["status"], name: "index_brex_items_on_status"
end
create_table "budget_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "budget_id", null: false
t.decimal "budgeted_spending", precision: 19, scale: 4, null: false
t.uuid "category_id", null: false
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "rolled_over_amount", precision: 19, scale: 4, default: "0.0", null: false
t.boolean "rollover_enabled", default: false, null: false
t.datetime "updated_at", null: false
t.index ["budget_id", "category_id"], name: "index_budget_categories_on_budget_id_and_category_id", unique: true
t.index ["budget_id"], name: "index_budget_categories_on_budget_id"
t.index ["category_id"], name: "index_budget_categories_on_category_id"
t.check_constraint "rolled_over_amount >= 0::numeric", name: "chk_budget_categories_rolled_over_amount_non_negative"
end
create_table "budget_shares", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "owner_id", null: false
t.string "permission", default: "read_only", null: false
t.datetime "updated_at", null: false
t.uuid "viewer_id", null: false
t.index ["owner_id", "viewer_id"], name: "index_budget_shares_on_owner_id_and_viewer_id", unique: true
t.index ["owner_id"], name: "index_budget_shares_on_owner_id"
t.index ["viewer_id"], name: "index_budget_shares_on_viewer_id"
end
create_table "budgets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "budgeted_spending", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency", null: false
t.date "end_date", null: false
t.decimal "expected_income", precision: 19, scale: 4
t.uuid "family_id", null: false
t.date "start_date", null: false
t.datetime "updated_at", null: false
t.uuid "user_id"
t.index ["family_id", "start_date", "end_date", "user_id"], name: "index_budgets_personal_unique", unique: true, where: "(user_id IS NOT NULL)"
t.index ["family_id", "start_date", "end_date"], name: "index_budgets_shared_unique", unique: true, where: "(user_id IS NULL)"
t.index ["family_id"], name: "index_budgets_on_family_id"
t.index ["user_id"], name: "index_budgets_on_user_id"
end
create_table "categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "classification_unused", default: "expense", null: false
t.string "color", default: "#6172F3", null: false
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.datetime "last_used_at"
t.string "lucide_icon", default: "shapes", null: false
t.string "name", null: false
t.uuid "parent_id"
t.datetime "updated_at", null: false
t.index ["family_id", "last_used_at"], name: "index_categories_on_family_id_and_last_used_at"
t.index ["family_id", "name"], name: "index_categories_on_family_id_and_name", unique: true
t.index ["family_id"], name: "index_categories_on_family_id"
end
create_table "chats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "error"
t.string "instructions"
t.string "latest_assistant_response_id"
t.string "title", null: false
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["user_id"], name: "index_chats_on_user_id"
end
create_table "coinbase_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.uuid "coinbase_item_id", null: false
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.string "name"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_coinbase_accounts_on_account_id"
t.index ["coinbase_item_id", "account_id"], name: "index_coinbase_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["coinbase_item_id"], name: "index_coinbase_accounts_on_coinbase_item_id"
end
create_table "coinbase_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_key"
t.text "api_secret"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_coinbase_items_on_family_id"
t.index ["status"], name: "index_coinbase_items_on_status"
end
create_table "coinstats_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.uuid "coinstats_item_id", null: false
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.string "name"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.string "wallet_address"
t.index ["coinstats_item_id", "account_id", "wallet_address"], name: "index_coinstats_accounts_on_item_account_and_wallet", unique: true
t.index ["coinstats_item_id"], name: "index_coinstats_accounts_on_coinstats_item_id"
end
create_table "coinstats_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "api_key", null: false
t.datetime "created_at", null: false
t.string "exchange_connection_id"
t.string "exchange_portfolio_id"
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["exchange_connection_id"], name: "index_coinstats_items_on_exchange_connection_id"
t.index ["family_id", "exchange_portfolio_id"], name: "index_coinstats_items_on_family_id_and_exchange_portfolio_id", unique: true, where: "(exchange_portfolio_id IS NOT NULL)"
t.index ["family_id"], name: "index_coinstats_items_on_family_id"
t.index ["status"], name: "index_coinstats_items_on_status"
end
create_table "credit_cards", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "annual_fee", precision: 10, scale: 2
t.decimal "apr", precision: 10, scale: 2
t.decimal "available_credit", precision: 10, scale: 2
t.datetime "created_at", null: false
t.date "expiration_date"
t.jsonb "locked_attributes", default: {}
t.decimal "minimum_payment", precision: 10, scale: 2
t.string "subtype"
t.datetime "updated_at", null: false
end
create_table "cryptos", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.string "tax_treatment", default: "taxable", null: false
t.datetime "updated_at", null: false
end
create_table "data_enrichments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "attribute_name"
t.datetime "created_at", null: false
t.uuid "enrichable_id", null: false
t.string "enrichable_type", null: false
t.jsonb "metadata"
t.string "source"
t.datetime "updated_at", null: false
t.jsonb "value"
t.index ["enrichable_id", "enrichable_type", "source", "attribute_name"], name: "idx_on_enrichable_id_enrichable_type_source_attribu_5be5f63e08", unique: true
t.index ["enrichable_type", "enrichable_id"], name: "index_data_enrichments_on_enrichable"
end
create_table "debug_log_entries", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id"
t.uuid "account_provider_id"
t.string "category", null: false
t.datetime "created_at", null: false
t.uuid "family_id"
t.string "level", null: false
t.text "message", null: false
t.jsonb "metadata", default: {}, null: false
t.string "provider_key"
t.string "source", null: false
t.datetime "updated_at", null: false
t.uuid "user_id"
t.index ["account_id"], name: "index_debug_log_entries_on_account_id"
t.index ["account_provider_id"], name: "index_debug_log_entries_on_account_provider_id"
t.index ["category", "created_at"], name: "index_debug_log_entries_on_category_and_created_at"
t.index ["category"], name: "index_debug_log_entries_on_category"
t.index ["created_at"], name: "index_debug_log_entries_on_created_at"
t.index ["family_id"], name: "index_debug_log_entries_on_family_id"
t.index ["level"], name: "index_debug_log_entries_on_level"
t.index ["provider_key", "created_at"], name: "index_debug_log_entries_on_provider_key_and_created_at"
t.index ["provider_key"], name: "index_debug_log_entries_on_provider_key"
t.index ["source"], name: "index_debug_log_entries_on_source"
t.index ["user_id"], name: "index_debug_log_entries_on_user_id"
t.check_constraint "level::text = ANY (ARRAY['debug'::character varying::text, 'info'::character varying::text, 'warn'::character varying::text, 'error'::character varying::text])", name: "chk_debug_log_entries_level"
end
create_table "depositories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.datetime "updated_at", null: false
end
create_table "enable_banking_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.datetime "created_at", null: false
t.decimal "credit_limit", precision: 19, scale: 4
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.uuid "enable_banking_item_id", null: false
t.string "iban"
t.jsonb "identification_hashes", default: []
t.jsonb "institution_metadata"
t.string "name"
t.string "product"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.boolean "treat_balance_as_available_credit", default: false, null: false
t.string "uid"
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_enable_banking_accounts_on_account_id"
t.index ["enable_banking_item_id"], name: "index_enable_banking_accounts_on_enable_banking_item_id"
t.index ["identification_hashes"], name: "index_enable_banking_accounts_on_identification_hashes", using: :gin
end
create_table "enable_banking_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "application_id"
t.string "aspsp_auth_approach"
t.string "aspsp_id"
t.integer "aspsp_maximum_consent_validity"
t.string "aspsp_name"
t.jsonb "aspsp_psu_types", default: []
t.jsonb "aspsp_required_psu_headers", default: []
t.string "authorization_id"
t.text "client_certificate"
t.string "country_code"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "last_psu_ip"
t.string "name"
t.boolean "pending_account_setup", default: false
t.string "psu_type"
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.datetime "session_expires_at"
t.string "session_id"
t.string "status", default: "good"
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_enable_banking_items_on_family_id"
t.index ["status"], name: "index_enable_banking_items_on_status"
end
create_table "entries", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.decimal "amount", precision: 19, scale: 4, null: false
t.datetime "created_at", null: false
t.string "currency"
t.date "date"
t.uuid "entryable_id"
t.string "entryable_type"
t.boolean "excluded", default: false
t.string "external_id"
t.uuid "import_id"
t.boolean "import_locked", default: false, null: false
t.jsonb "locked_attributes", default: {}
t.string "name", null: false
t.text "notes"
t.uuid "parent_entry_id"
t.string "plaid_id"
t.datetime "reconciled_at"
t.uuid "reconciled_by_statement_id"
t.string "source"
t.datetime "updated_at", null: false
t.boolean "user_modified", default: false, null: false
t.index "lower((name)::text)", name: "index_entries_on_lower_name"
t.index ["account_id", "date", "entryable_id"], name: "index_entries_on_investment_totals_lookup", where: "(((entryable_type)::text = 'Trade'::text) AND (excluded = false))"
t.index ["account_id", "date"], name: "index_entries_on_account_id_and_date"
t.index ["account_id", "reconciled_at"], name: "index_entries_on_account_and_reconciled_at", where: "(reconciled_at IS NOT NULL)"
t.index ["account_id", "source", "external_id"], name: "index_entries_on_account_source_and_external_id", unique: true, where: "((external_id IS NOT NULL) AND (source IS NOT NULL))"
t.index ["account_id"], name: "index_entries_on_account_id"
t.index ["currency", "amount", "date", "account_id"], name: "index_entries_on_transfer_match_lookup", where: "(((entryable_type)::text = 'Transaction'::text) AND (excluded = false))"
t.index ["date"], name: "index_entries_on_date"
t.index ["entryable_type"], name: "index_entries_on_entryable_type"
t.index ["import_id"], name: "index_entries_on_import_id"
t.index ["import_locked"], name: "index_entries_on_import_locked_true", where: "(import_locked = true)"
t.index ["parent_entry_id"], name: "index_entries_on_parent_entry_id"
t.index ["reconciled_by_statement_id"], name: "index_entries_on_reconciled_by_statement", where: "(reconciled_by_statement_id IS NOT NULL)"
t.index ["user_modified"], name: "index_entries_on_user_modified_true", where: "(user_modified = true)"
t.check_constraint "reconciled_by_statement_id IS NULL OR reconciled_at IS NOT NULL", name: "chk_entries_reconciled_at_present_when_statement_set"
end
create_table "eval_datasets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.boolean "active", default: true
t.datetime "created_at", null: false
t.string "description"
t.string "eval_type", null: false
t.jsonb "metadata", default: {}
t.string "name", null: false
t.integer "sample_count", default: 0
t.datetime "updated_at", null: false
t.string "version", default: "1.0", null: false
t.index ["eval_type", "active"], name: "index_eval_datasets_on_eval_type_and_active"
t.index ["name"], name: "index_eval_datasets_on_name", unique: true
end
create_table "eval_results", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.jsonb "actual_output", null: false
t.boolean "alternative_match", default: false
t.integer "completion_tokens"
t.boolean "correct", null: false
t.decimal "cost", precision: 10, scale: 6
t.datetime "created_at", null: false
t.uuid "eval_run_id", null: false
t.uuid "eval_sample_id", null: false
t.boolean "exact_match", default: false
t.float "fuzzy_score"
t.boolean "hierarchical_match", default: false
t.integer "latency_ms"
t.jsonb "metadata", default: {}
t.boolean "null_expected", default: false
t.boolean "null_returned", default: false
t.integer "prompt_tokens"
t.datetime "updated_at", null: false
t.index ["eval_run_id", "correct"], name: "index_eval_results_on_eval_run_id_and_correct"
t.index ["eval_run_id"], name: "index_eval_results_on_eval_run_id"
t.index ["eval_sample_id"], name: "index_eval_results_on_eval_sample_id"
end
create_table "eval_runs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "completed_at"
t.datetime "created_at", null: false
t.text "error_message"
t.uuid "eval_dataset_id", null: false
t.jsonb "metrics", default: {}
t.string "model", null: false
t.string "name"
t.string "provider", null: false
t.jsonb "provider_config", default: {}
t.datetime "started_at"
t.string "status", default: "pending", null: false
t.integer "total_completion_tokens", default: 0
t.decimal "total_cost", precision: 10, scale: 6, default: "0.0"
t.integer "total_prompt_tokens", default: 0
t.datetime "updated_at", null: false
t.index ["eval_dataset_id", "model"], name: "index_eval_runs_on_eval_dataset_id_and_model"
t.index ["eval_dataset_id"], name: "index_eval_runs_on_eval_dataset_id"
t.index ["provider", "model"], name: "index_eval_runs_on_provider_and_model"
t.index ["status"], name: "index_eval_runs_on_status"
end
create_table "eval_samples", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.jsonb "context_data", default: {}
t.datetime "created_at", null: false
t.string "difficulty", default: "medium"
t.uuid "eval_dataset_id", null: false
t.jsonb "expected_output", null: false
t.jsonb "input_data", null: false
t.jsonb "metadata", default: {}
t.string "tags", default: [], array: true
t.datetime "updated_at", null: false
t.index ["eval_dataset_id", "difficulty"], name: "index_eval_samples_on_eval_dataset_id_and_difficulty"
t.index ["eval_dataset_id"], name: "index_eval_samples_on_eval_dataset_id"
t.index ["tags"], name: "index_eval_samples_on_tags", using: :gin
end
create_table "exchange_rate_pairs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.date "first_provider_rate_on"
t.string "from_currency", null: false
t.string "provider_name"
t.string "to_currency", null: false
t.datetime "updated_at", null: false
t.index ["from_currency", "to_currency"], name: "index_exchange_rate_pairs_on_pair_unique", unique: true
end
create_table "exchange_rates", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.date "date", null: false
t.string "from_currency", null: false
t.decimal "rate", null: false
t.string "to_currency", null: false
t.datetime "updated_at", null: false
t.index ["from_currency", "to_currency", "date"], name: "index_exchange_rates_on_base_converted_date_unique", unique: true
t.index ["from_currency"], name: "index_exchange_rates_on_from_currency"
t.index ["to_currency"], name: "index_exchange_rates_on_to_currency"
end
create_table "families", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "assistant_type", default: "builtin", null: false
t.boolean "auto_sync_on_login", default: true, null: false
t.string "bills_feed_token"
t.string "country", default: "US"
t.datetime "created_at", null: false
t.string "currency", default: "USD"
t.boolean "data_enrichment_enabled", default: false
t.string "date_format", default: "%m-%d-%Y"
t.string "default_account_sharing", default: "shared", null: false
t.boolean "early_access", default: false
t.string "enabled_currencies", array: true
t.boolean "household_budget_enabled", default: true, null: false
t.datetime "last_sync_all_attempted_at"
t.datetime "latest_sync_activity_at", default: -> { "CURRENT_TIMESTAMP" }
t.datetime "latest_sync_completed_at", default: -> { "CURRENT_TIMESTAMP" }
t.string "locale", default: "en"
t.string "moniker", default: "Family", null: false
t.integer "month_start_day", default: 1, null: false
t.string "name"
t.boolean "personal_budgets", default: false, null: false
t.boolean "recurring_transactions_disabled", default: false, null: false
t.string "stripe_customer_id"
t.string "timezone"
t.datetime "updated_at", null: false
t.string "vector_store_id"
t.index ["bills_feed_token"], name: "index_families_on_bills_feed_token", unique: true
t.check_constraint "default_account_sharing::text = ANY (ARRAY['shared'::character varying::text, 'private'::character varying::text])", name: "chk_families_default_account_sharing"
t.check_constraint "month_start_day >= 1 AND month_start_day <= 28", name: "month_start_day_range"
end
create_table "family_documents", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "content_type"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.integer "file_size"
t.string "filename", null: false
t.jsonb "metadata", default: {}
t.string "provider_file_id"
t.string "status", default: "pending", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_family_documents_on_family_id"
t.index ["provider_file_id"], name: "index_family_documents_on_provider_file_id"
t.index ["status"], name: "index_family_documents_on_status"
end
create_table "family_exports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "status", default: "pending", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_family_exports_on_family_id"
end
create_table "family_merchant_associations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.uuid "merchant_id", null: false
t.datetime "unlinked_at"
t.datetime "updated_at", null: false
t.index ["family_id", "merchant_id"], name: "idx_on_family_id_merchant_id_23e883e08f", unique: true
t.index ["family_id"], name: "index_family_merchant_associations_on_family_id"
t.index ["merchant_id"], name: "index_family_merchant_associations_on_merchant_id"
end
create_table "goal_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.decimal "allocated_amount", precision: 19, scale: 4
t.datetime "created_at", null: false
t.uuid "goal_id", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_goal_accounts_on_account_id"
t.index ["goal_id", "account_id"], name: "index_savings_goal_accounts_on_goal_and_account", unique: true
t.index ["goal_id"], name: "index_goal_accounts_on_goal_id"
t.check_constraint "allocated_amount IS NULL OR allocated_amount >= 0::numeric", name: "chk_goal_accounts_allocation_non_negative"
end
create_table "goal_pledges", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.decimal "amount", precision: 19, scale: 4, null: false
t.datetime "created_at", null: false
t.string "currency", null: false
t.datetime "expires_at", null: false
t.uuid "goal_id", null: false
t.enum "kind", null: false, enum_type: "goal_pledge_kind"
t.uuid "matched_transaction_id"
t.enum "status", default: "open", null: false, enum_type: "goal_pledge_status"
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_goal_pledges_on_account_id"
t.index ["goal_id", "status"], name: "index_goal_pledges_on_goal_id_and_status"
t.index ["goal_id"], name: "index_goal_pledges_on_goal_id"
t.index ["matched_transaction_id"], name: "index_goal_pledges_on_matched_transaction_id", unique: true, where: "(matched_transaction_id IS NOT NULL)"
t.index ["status", "expires_at"], name: "index_goal_pledges_open_by_expiry", where: "(status = 'open'::goal_pledge_status)"
t.check_constraint "amount > 0::numeric", name: "chk_goal_pledges_amount_positive"
end
create_table "goals", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "color"
t.decimal "completed_amount", precision: 19, scale: 4
t.datetime "completed_at"
t.decimal "consumed_amount", precision: 19, scale: 4, default: "0.0", null: false
t.datetime "created_at", null: false
t.string "currency", null: false
t.uuid "family_id", null: false
t.string "icon"
t.string "kind", default: "one_off", null: false
t.string "name", null: false
t.text "notes"
t.string "progress_basis", default: "balance", null: false
t.string "state", default: "active", null: false
t.decimal "target_amount", precision: 19, scale: 4, null: false
t.date "target_date"
t.string "target_mode", default: "fixed", null: false
t.integer "target_months"
t.datetime "updated_at", null: false
t.index ["family_id", "state"], name: "index_goals_on_family_id_and_state"
t.index ["family_id"], name: "index_goals_on_family_id"
t.check_constraint "char_length(name::text) <= 255", name: "chk_savings_goals_name_length"
t.check_constraint "consumed_amount >= 0::numeric", name: "chk_goals_consumed_amount_non_negative"
t.check_constraint "kind::text = ANY (ARRAY['one_off'::character varying::text, 'maintained'::character varying::text])", name: "chk_goals_kind_enum"
t.check_constraint "progress_basis::text = ANY (ARRAY['balance'::character varying::text, 'contributions'::character varying::text])", name: "chk_goals_progress_basis_enum"
t.check_constraint "state::text = ANY (ARRAY['active'::character varying::text, 'paused'::character varying::text, 'completed'::character varying::text, 'archived'::character varying::text])", name: "chk_savings_goals_state_enum"
t.check_constraint "target_amount > 0::numeric", name: "chk_savings_goals_target_amount_positive"
t.check_constraint "target_mode::text = ANY (ARRAY['fixed'::character varying::text, 'months_of_expenses'::character varying::text])", name: "chk_goals_target_mode_enum"
end
create_table "holdings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.uuid "account_provider_id"
t.decimal "amount", precision: 19, scale: 4, null: false
t.decimal "cost_basis", precision: 19, scale: 4
t.boolean "cost_basis_locked", default: false, null: false
t.string "cost_basis_source"
t.datetime "created_at", null: false
t.string "currency", null: false
t.date "date", null: false
t.string "external_id"
t.decimal "price", precision: 19, scale: 4, null: false
t.uuid "provider_security_id"
t.decimal "qty", precision: 24, scale: 8, null: false
t.uuid "security_id", null: false
t.boolean "security_locked", default: false, null: false
t.datetime "updated_at", null: false
t.index ["account_id", "external_id"], name: "idx_holdings_on_account_id_external_id_unique", unique: true, where: "(external_id IS NOT NULL)"
t.index ["account_id", "security_id", "date", "currency"], name: "idx_on_account_id_security_id_date_currency_5323e39f8b", unique: true
t.index ["account_id"], name: "index_holdings_on_account_id"
t.index ["account_provider_id"], name: "index_holdings_on_account_provider_id"
t.index ["provider_security_id"], name: "index_holdings_on_provider_security_id"
t.index ["security_id"], name: "index_holdings_on_security_id"
end
create_table "ibkr_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "cash_balance", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.string "ibkr_account_id"
t.uuid "ibkr_item_id", null: false
t.jsonb "institution_metadata"
t.datetime "last_activities_sync"
t.datetime "last_holdings_sync"
t.string "name"
t.jsonb "raw_activities_payload", default: {}
t.jsonb "raw_cash_report_payload", default: []
t.jsonb "raw_equity_summary_payload", default: [], null: false
t.jsonb "raw_holdings_payload", default: []
t.date "report_date"
t.datetime "updated_at", null: false
t.index ["ibkr_item_id", "ibkr_account_id"], name: "index_ibkr_accounts_on_item_and_ibkr_account_id", unique: true, where: "(ibkr_account_id IS NOT NULL)"
t.index ["ibkr_item_id"], name: "index_ibkr_accounts_on_ibkr_item_id"
end
create_table "ibkr_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.string "query_id"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.string "token"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_ibkr_items_on_family_id"
t.index ["status"], name: "index_ibkr_items_on_status"
end
create_table "impersonation_session_logs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "action"
t.string "controller"
t.datetime "created_at", null: false
t.uuid "impersonation_session_id", null: false
t.string "ip_address"
t.string "method"
t.text "path"
t.datetime "updated_at", null: false
t.text "user_agent"
t.index ["impersonation_session_id"], name: "index_impersonation_session_logs_on_impersonation_session_id"
end
create_table "impersonation_sessions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "impersonated_id", null: false
t.uuid "impersonator_id", null: false
t.string "status", default: "pending", null: false
t.datetime "updated_at", null: false
t.index ["impersonated_id"], name: "index_impersonation_sessions_on_impersonated_id"
t.index ["impersonator_id"], name: "index_impersonation_sessions_on_impersonator_id"
end
create_table "import_mappings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.boolean "create_when_empty", default: true
t.datetime "created_at", null: false
t.uuid "import_id", null: false
t.string "key"
t.uuid "mappable_id"
t.string "mappable_type"
t.string "type", null: false
t.datetime "updated_at", null: false
t.string "value"
t.index ["import_id"], name: "index_import_mappings_on_import_id"
t.index ["mappable_type", "mappable_id"], name: "index_import_mappings_on_mappable"
end
create_table "import_rows", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account"
t.text "actions"
t.boolean "active"
t.string "amount"
t.string "category"
t.string "category_classification"
t.string "category_color"
t.string "category_icon"
t.string "category_parent"
t.text "conditions"
t.datetime "created_at", null: false
t.string "currency"
t.string "date"
t.string "effective_date"
t.string "entity_type"
t.string "exchange_operating_mic"
t.uuid "import_id", null: false
t.string "merchant_color"
t.string "merchant_website"
t.string "name"
t.text "notes"
t.string "price"
t.string "qty"
t.string "resource_type"
t.integer "source_row_number", null: false
t.string "tags"
t.string "ticker"
t.datetime "updated_at", null: false
t.index ["import_id", "source_row_number"], name: "index_import_rows_on_import_id_and_source_row_number", unique: true
t.index ["import_id"], name: "index_import_rows_on_import_id"
t.check_constraint "source_row_number > 0", name: "chk_import_rows_source_row_number_positive"
end
create_table "import_sessions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "client_session_id", limit: 255
t.datetime "created_at", null: false
t.jsonb "error_details", default: {}, null: false
t.integer "expected_chunks"
t.uuid "family_id", null: false
t.string "import_type", default: "SureImport", null: false
t.string "status", default: "pending", null: false
t.jsonb "summary", default: {}, null: false
t.datetime "updated_at", null: false
t.index ["family_id", "client_session_id"], name: "idx_import_sessions_on_family_client_session", unique: true, where: "(client_session_id IS NOT NULL)"
t.index ["family_id", "status"], name: "index_import_sessions_on_family_id_and_status"
t.index ["family_id"], name: "index_import_sessions_on_family_id"
t.index ["id", "family_id"], name: "idx_import_sessions_on_id_family", unique: true
t.check_constraint "client_session_id IS NULL OR btrim(client_session_id::text) <> ''::text", name: "chk_import_sessions_client_session_id_present"
t.check_constraint "expected_chunks IS NULL OR expected_chunks > 0", name: "chk_import_sessions_expected_chunks_positive"
t.check_constraint "import_type::text = 'SureImport'::text", name: "chk_import_sessions_import_type"
t.check_constraint "jsonb_typeof(error_details) = 'object'::text", name: "chk_import_sessions_error_details_object"
t.check_constraint "jsonb_typeof(summary) = 'object'::text", name: "chk_import_sessions_summary_object"
t.check_constraint "status::text = ANY (ARRAY['pending'::character varying::text, 'importing'::character varying::text, 'complete'::character varying::text, 'failed'::character varying::text])", name: "chk_import_sessions_status"
end
create_table "import_source_mappings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.uuid "import_session_id", null: false
t.string "source_id", limit: 255, null: false
t.string "source_type", limit: 64, null: false
t.uuid "target_id", null: false
t.string "target_type", null: false
t.datetime "updated_at", null: false
t.index ["family_id", "source_type", "source_id"], name: "idx_import_source_mappings_on_family_source"
t.index ["family_id"], name: "index_import_source_mappings_on_family_id"
t.index ["import_session_id", "source_type", "source_id"], name: "index_import_source_mappings_on_session_type_and_source", unique: true
t.index ["import_session_id"], name: "index_import_source_mappings_on_import_session_id"
t.index ["target_type", "target_id"], name: "idx_import_source_mappings_on_target"
t.check_constraint "btrim(source_id::text) <> ''::text", name: "chk_import_source_mappings_source_id_present"
t.check_constraint "btrim(source_type::text) <> ''::text", name: "chk_import_source_mappings_source_type_present"
t.check_constraint "btrim(target_type::text) <> ''::text", name: "chk_import_source_mappings_target_type_present"
t.check_constraint "source_type::text = ANY (ARRAY['Account'::character varying, 'Category'::character varying, 'Tag'::character varying, 'Merchant'::character varying, 'RecurringTransaction'::character varying, 'RecurringOccurrence'::character varying, 'Transaction'::character varying, 'Budget'::character varying, 'Security'::character varying, 'Rule'::character varying]::text[])", name: "chk_import_source_mappings_source_type"
t.check_constraint "target_type::text = ANY (ARRAY['Account'::character varying, 'Category'::character varying, 'Tag'::character varying, 'Merchant'::character varying, 'RecurringTransaction'::character varying, 'RecurringOccurrence'::character varying, 'Transaction'::character varying, 'Budget'::character varying, 'Security'::character varying, 'Rule'::character varying]::text[])", name: "chk_import_source_mappings_target_type"
end
create_table "imports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_col_label"
t.uuid "account_id"
t.uuid "account_statement_id"
t.text "ai_summary"
t.string "amount_col_label"
t.string "amount_type_identifier_value"
t.string "amount_type_inflow_value"
t.string "amount_type_strategy", default: "signed_amount"
t.string "category_col_label"
t.string "checksum", limit: 64
t.string "client_chunk_id", limit: 255
t.string "col_sep", default: ","
t.jsonb "column_mappings"
t.datetime "created_at", null: false
t.string "currency_col_label"
t.string "date_col_label"
t.string "date_format", default: "%m/%d/%Y"
t.string "document_type"
t.string "entity_type_col_label"
t.string "error"
t.jsonb "error_details", default: {}, null: false
t.string "exchange_operating_mic_col_label"
t.jsonb "expected_record_counts", default: {}, null: false
t.jsonb "extracted_data"
t.uuid "family_id", null: false
t.uuid "import_session_id"
t.string "name_col_label"
t.string "normalized_csv_str"
t.string "notes_col_label"
t.string "number_format"
t.string "price_col_label"
t.string "qty_col_label"
t.string "raw_file_str"
t.jsonb "readback_verification", default: {}, null: false
t.integer "rows_count", default: 0, null: false
t.integer "rows_to_skip", default: 0, null: false
t.integer "sequence"
t.string "signage_convention", default: "inflows_positive"
t.string "status"
t.jsonb "summary", default: {}, null: false
t.string "tags_col_label"
t.string "ticker_col_label"
t.string "type", null: false
t.datetime "updated_at", null: false
t.index ["account_statement_id"], name: "index_imports_on_account_statement_id"
t.index ["family_id"], name: "index_imports_on_family_id"
t.index ["import_session_id", "client_chunk_id"], name: "idx_imports_on_session_client_chunk", unique: true, where: "((import_session_id IS NOT NULL) AND (client_chunk_id IS NOT NULL))"
t.index ["import_session_id", "sequence"], name: "idx_imports_on_session_sequence", unique: true, where: "((import_session_id IS NOT NULL) AND (sequence IS NOT NULL))"
t.index ["import_session_id"], name: "index_imports_on_import_session_id"
t.check_constraint "checksum IS NULL OR length(checksum::text) = 64", name: "chk_imports_checksum_sha256_length"
t.check_constraint "client_chunk_id IS NULL OR btrim(client_chunk_id::text) <> ''::text", name: "chk_imports_client_chunk_id_present"
t.check_constraint "import_session_id IS NULL OR checksum IS NOT NULL", name: "chk_imports_session_checksum_present"
t.check_constraint "import_session_id IS NULL OR sequence IS NOT NULL", name: "chk_imports_session_sequence_present"
t.check_constraint "jsonb_typeof(error_details) = 'object'::text", name: "chk_imports_error_details_object"
t.check_constraint "jsonb_typeof(summary) = 'object'::text", name: "chk_imports_summary_object"
t.check_constraint "sequence IS NULL OR sequence > 0", name: "chk_imports_session_sequence_positive"
end
create_table "indexa_capital_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_number"
t.string "account_status"
t.string "account_type"
t.boolean "activities_fetch_pending", default: false
t.decimal "cash_balance", precision: 19, scale: 4, default: "0.0"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.string "indexa_capital_account_id"
t.string "indexa_capital_authorization_id"
t.uuid "indexa_capital_item_id", null: false
t.jsonb "institution_metadata"
t.datetime "last_activities_sync"
t.datetime "last_holdings_sync"
t.string "name"
t.string "provider"
t.jsonb "raw_activities_payload", default: []
t.jsonb "raw_holdings_payload", default: []
t.jsonb "raw_payload"
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["indexa_capital_authorization_id"], name: "idx_on_indexa_capital_authorization_id_58db208d52"
t.index ["indexa_capital_item_id", "indexa_capital_account_id"], name: "index_indexa_capital_accounts_on_item_and_account_id", unique: true, where: "(indexa_capital_account_id IS NOT NULL)"
t.index ["indexa_capital_item_id"], name: "index_indexa_capital_accounts_on_indexa_capital_item_id"
end
create_table "indexa_capital_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_token"
t.datetime "created_at", null: false
t.string "document"
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.text "password"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.string "username"
t.index ["family_id"], name: "index_indexa_capital_items_on_family_id"
t.index ["status"], name: "index_indexa_capital_items_on_status"
end
create_table "insights", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "body", null: false
t.datetime "created_at", null: false
t.string "currency", default: "USD", null: false
t.string "dedup_key", null: false
t.datetime "dismissed_at"
t.jsonb "facts", default: {}, null: false
t.uuid "family_id", null: false
t.datetime "generated_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
t.string "insight_type", null: false
t.jsonb "metadata", default: {}, null: false
t.date "period_end"
t.date "period_start"
t.string "priority", default: "medium", null: false
t.datetime "read_at"
t.string "status", default: "active", null: false
t.string "title", null: false
t.datetime "updated_at", null: false
t.index ["family_id", "dedup_key"], name: "index_insights_on_family_id_and_dedup_key", unique: true
t.index ["family_id", "generated_at"], name: "index_insights_on_family_id_and_generated_at"
t.index ["family_id", "status"], name: "index_insights_on_family_id_and_status"
t.check_constraint "priority::text = ANY (ARRAY['high'::character varying::text, 'medium'::character varying::text, 'low'::character varying::text])", name: "chk_insights_priority"
t.check_constraint "status::text = ANY (ARRAY['active'::character varying::text, 'read'::character varying::text, 'dismissed'::character varying::text, 'expired'::character varying::text])", name: "chk_insights_status"
end
create_table "investments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.datetime "updated_at", null: false
end
create_table "invitations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "accepted_at"
t.datetime "created_at", null: false
t.string "email"
t.datetime "expires_at"
t.uuid "family_id", null: false
t.uuid "inviter_id", null: false
t.string "role"
t.string "token"
t.string "token_digest"
t.datetime "updated_at", null: false
t.index ["email", "family_id"], name: "index_invitations_on_email_and_family_id_pending", unique: true, where: "(accepted_at IS NULL)"
t.index ["email"], name: "index_invitations_on_email"
t.index ["family_id"], name: "index_invitations_on_family_id"
t.index ["inviter_id"], name: "index_invitations_on_inviter_id"
t.index ["token"], name: "index_invitations_on_token", unique: true
t.index ["token_digest"], name: "index_invitations_on_token_digest", unique: true, where: "(token_digest IS NOT NULL)"
end
create_table "invite_codes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "token", null: false
t.string "token_digest"
t.datetime "updated_at", null: false
t.index ["token"], name: "index_invite_codes_on_token", unique: true
t.index ["token_digest"], name: "index_invite_codes_on_token_digest", unique: true, where: "(token_digest IS NOT NULL)"
end
create_table "kraken_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id", null: false
t.string "account_type"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "extra", default: {}, null: false
t.jsonb "institution_metadata"
t.uuid "kraken_item_id", null: false
t.string "name"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["account_type"], name: "index_kraken_accounts_on_account_type"
t.index ["kraken_item_id", "account_id"], name: "index_kraken_accounts_on_item_and_account_id", unique: true
t.index ["kraken_item_id"], name: "index_kraken_accounts_on_kraken_item_id"
end
create_table "kraken_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_key"
t.text "api_secret"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_name"
t.string "institution_url"
t.bigint "last_nonce", default: 0, null: false
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_kraken_items_on_family_id"
t.index ["status"], name: "index_kraken_items_on_status"
end
create_table "llm_usages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.integer "cache_creation_tokens"
t.integer "cache_read_tokens"
t.integer "completion_tokens", default: 0, null: false
t.datetime "created_at", null: false
t.decimal "estimated_cost", precision: 10, scale: 6
t.uuid "family_id", null: false
t.jsonb "metadata", default: {}
t.string "model", null: false
t.string "operation", null: false
t.integer "prompt_tokens", default: 0, null: false
t.string "provider", null: false
t.integer "total_tokens", default: 0, null: false
t.datetime "updated_at", null: false
t.index ["family_id", "created_at"], name: "index_llm_usages_on_family_id_and_created_at"
t.index ["family_id", "operation"], name: "index_llm_usages_on_family_id_and_operation"
t.index ["family_id"], name: "index_llm_usages_on_family_id"
t.check_constraint "cache_creation_tokens IS NULL OR cache_creation_tokens >= 0", name: "chk_llm_usages_cache_creation_tokens_non_negative"
t.check_constraint "cache_read_tokens IS NULL OR cache_read_tokens >= 0", name: "chk_llm_usages_cache_read_tokens_non_negative"
end
create_table "loans", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.decimal "initial_balance", precision: 19, scale: 4
t.decimal "interest_rate", precision: 10, scale: 3
t.jsonb "locked_attributes", default: {}
t.string "rate_type"
t.string "subtype"
t.integer "term_months"
t.datetime "updated_at", null: false
end
create_table "lunchflow_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.boolean "holdings_supported", default: true, null: false
t.jsonb "institution_metadata"
t.uuid "lunchflow_item_id", null: false
t.string "name"
t.string "provider"
t.jsonb "raw_holdings_payload"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_lunchflow_accounts_on_account_id"
t.index ["lunchflow_item_id", "account_id"], name: "index_lunchflow_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["lunchflow_item_id"], name: "index_lunchflow_accounts_on_lunchflow_item_id"
end
create_table "lunchflow_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_key"
t.string "base_url"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_lunchflow_items_on_family_id"
t.index ["status"], name: "index_lunchflow_items_on_status"
end
create_table "merchants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "color"
t.datetime "created_at", null: false
t.uuid "family_id"
t.string "logo_url"
t.string "name", null: false
t.string "provider_merchant_id"
t.string "source"
t.string "type", null: false
t.datetime "updated_at", null: false
t.string "website_url"
t.index ["family_id", "name"], name: "index_merchants_on_family_id_and_name", unique: true, where: "((type)::text = 'FamilyMerchant'::text)"
t.index ["family_id"], name: "index_merchants_on_family_id"
t.index ["provider_merchant_id", "source"], name: "index_merchants_on_provider_merchant_id_and_source", unique: true, where: "((provider_merchant_id IS NOT NULL) AND ((type)::text = 'ProviderMerchant'::text))"
t.index ["source", "name"], name: "index_merchants_on_source_and_name", unique: true, where: "((type)::text = 'ProviderMerchant'::text)"
t.index ["type"], name: "index_merchants_on_type"
end
create_table "mercury_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id", null: false
t.string "account_status"
t.string "account_type"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.uuid "mercury_item_id", null: false
t.string "name"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "updated_at", null: false
t.index ["mercury_item_id", "account_id"], name: "index_mercury_accounts_on_item_and_account_id", unique: true
t.index ["mercury_item_id"], name: "index_mercury_accounts_on_mercury_item_id"
end
create_table "mercury_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "base_url"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.text "token"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_mercury_items_on_family_id"
t.index ["status"], name: "index_mercury_items_on_status"
end
create_table "messages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "ai_model"
t.uuid "chat_id", null: false
t.text "content"
t.datetime "created_at", null: false
t.boolean "debug", default: false
t.string "provider_id"
t.boolean "reasoning", default: false
t.string "status", default: "complete", null: false
t.string "type", null: false
t.datetime "updated_at", null: false
t.index ["chat_id"], name: "index_messages_on_chat_id"
end
create_table "mobile_devices", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "app_version"
t.datetime "created_at", null: false
t.string "device_id"
t.string "device_name"
t.string "device_type"
t.datetime "last_seen_at"
t.string "os_version"
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["user_id", "device_id"], name: "index_mobile_devices_on_user_id_and_device_id", unique: true
t.index ["user_id"], name: "index_mobile_devices_on_user_id"
end
create_table "notification_deliveries", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "rule_id", null: false
t.uuid "transaction_id", null: false
t.datetime "updated_at", null: false
t.index ["rule_id", "transaction_id"], name: "index_notification_deliveries_on_rule_and_transaction", unique: true
t.index ["rule_id"], name: "index_notification_deliveries_on_rule_id"
t.index ["transaction_id"], name: "index_notification_deliveries_on_transaction_id"
end
create_table "oauth_access_grants", force: :cascade do |t|
t.bigint "application_id", null: false
t.datetime "created_at", null: false
t.integer "expires_in", null: false
t.text "redirect_uri", null: false
t.string "resource_owner_id", null: false
t.datetime "revoked_at"
t.string "scopes", default: "", null: false
t.string "token", null: false
t.index ["application_id"], name: "index_oauth_access_grants_on_application_id"
t.index ["resource_owner_id"], name: "index_oauth_access_grants_on_resource_owner_id"
t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true
end
create_table "oauth_access_tokens", force: :cascade do |t|
t.bigint "application_id", null: false
t.datetime "created_at", null: false
t.integer "expires_in"
t.uuid "mobile_device_id"
t.string "previous_refresh_token", default: "", null: false
t.string "refresh_token"
t.string "resource_owner_id"
t.datetime "revoked_at"
t.string "scopes"
t.string "token", null: false
t.index ["application_id"], name: "index_oauth_access_tokens_on_application_id"
t.index ["mobile_device_id"], name: "index_oauth_access_tokens_on_mobile_device_id"
t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true
t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id"
t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true
end
create_table "oauth_applications", force: :cascade do |t|
t.boolean "confidential", default: true, null: false
t.datetime "created_at", null: false
t.string "name", null: false
t.uuid "owner_id"
t.string "owner_type"
t.text "redirect_uri", null: false
t.string "scopes", default: "", null: false
t.string "secret", null: false
t.string "uid", null: false
t.datetime "updated_at", null: false
t.index ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type"
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true
end
create_table "oidc_identities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "info", default: {}
t.string "issuer"
t.datetime "last_authenticated_at"
t.string "provider", null: false
t.string "uid", null: false
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["issuer"], name: "index_oidc_identities_on_issuer"
t.index ["provider", "uid"], name: "index_oidc_identities_on_provider_and_uid", unique: true
t.index ["user_id"], name: "index_oidc_identities_on_user_id"
end
create_table "onchain_wallet_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "asset_kind", null: false
t.string "chain", null: false
t.string "content_hash"
t.string "contract_address"
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.integer "decimals", default: 0, null: false
t.jsonb "extra", default: {}, null: false
t.string "name"
t.uuid "onchain_wallet_item_id", null: false
t.decimal "quantity", precision: 32, scale: 18, default: "0.0", null: false
t.jsonb "raw_movements_payload"
t.jsonb "raw_payload"
t.string "symbol", null: false
t.datetime "updated_at", null: false
t.string "wallet_address", null: false
t.index ["onchain_wallet_item_id", "chain", "wallet_address", "contract_address"], name: "index_onchain_wallet_accounts_unique_erc20", unique: true, where: "((asset_kind)::text = 'erc20'::text)"
t.index ["onchain_wallet_item_id", "chain", "wallet_address", "contract_address"], name: "index_onchain_wallet_accounts_unique_spl", unique: true, where: "((asset_kind)::text = 'spl'::text)"
t.index ["onchain_wallet_item_id", "chain", "wallet_address"], name: "index_onchain_wallet_accounts_on_item_and_address"
t.index ["onchain_wallet_item_id", "chain", "wallet_address"], name: "index_onchain_wallet_accounts_unique_native", unique: true, where: "((asset_kind)::text = 'native'::text)"
t.index ["onchain_wallet_item_id"], name: "index_onchain_wallet_accounts_on_onchain_wallet_item_id"
t.check_constraint "asset_kind::text = 'native'::text OR contract_address IS NOT NULL", name: "chk_onchain_wallet_accounts_token_has_contract"
t.check_constraint "asset_kind::text = ANY (ARRAY['native'::character varying::text, 'erc20'::character varying::text, 'spl'::character varying::text])", name: "chk_onchain_wallet_accounts_known_asset_kind"
end
create_table "onchain_wallet_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.text "etherscan_api_key"
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_onchain_wallet_items_on_family_id"
t.index ["status"], name: "index_onchain_wallet_items_on_status"
end
create_table "other_assets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.datetime "updated_at", null: false
end
create_table "other_liabilities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.datetime "updated_at", null: false
end
create_table "plaid_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "available_balance", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.string "mask"
t.string "name", null: false
t.string "plaid_id", null: false
t.uuid "plaid_item_id", null: false
t.string "plaid_subtype"
t.string "plaid_type", null: false
t.jsonb "raw_holdings_payload", default: {}
t.jsonb "raw_liabilities_payload", default: {}
t.jsonb "raw_payload", default: {}
t.jsonb "raw_transactions_payload", default: {}
t.datetime "updated_at", null: false
t.index ["plaid_item_id", "plaid_id"], name: "index_plaid_accounts_on_item_and_plaid_id", unique: true
t.index ["plaid_item_id"], name: "index_plaid_accounts_on_plaid_item_id"
end
create_table "plaid_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "access_token"
t.string "available_products", default: [], array: true
t.string "billed_products", default: [], array: true
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_id"
t.string "institution_url"
t.string "name"
t.string "next_cursor"
t.string "plaid_id", null: false
t.string "plaid_region", default: "us", null: false
t.jsonb "raw_institution_payload", default: {}
t.jsonb "raw_payload", default: {}
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_plaid_items_on_family_id"
t.index ["plaid_id"], name: "index_plaid_items_on_plaid_id", unique: true
end
create_table "properties", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "area_unit"
t.integer "area_value"
t.date "avm_last_synced_on"
t.string "avm_provider"
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "subtype"
t.datetime "updated_at", null: false
t.integer "year_built"
t.index ["avm_last_synced_on"], name: "index_properties_on_avm_provider_sync", order: "NULLS FIRST", where: "(avm_provider IS NOT NULL)"
t.check_constraint "avm_provider IS NULL OR (avm_provider::text = ANY (ARRAY['rentcast'::character varying::text, 'realie'::character varying::text]))", name: "properties_avm_provider_check"
end
create_table "provider_request_counts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.integer "count", default: 0, null: false
t.datetime "created_at", null: false
t.string "period", null: false
t.string "provider_key", null: false
t.datetime "updated_at", null: false
t.index ["provider_key", "period"], name: "index_provider_request_counts_on_provider_key_and_period", unique: true
end
create_table "push_subscriptions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "environment", null: false
t.datetime "last_registered_at", null: false
t.string "platform", default: "ios", null: false
t.string "token", null: false
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index "lower((token)::text)", name: "index_push_subscriptions_on_lower_token", unique: true
t.index ["last_registered_at"], name: "index_push_subscriptions_on_last_registered_at"
t.index ["user_id"], name: "index_push_subscriptions_on_user_id"
t.check_constraint "environment::text = ANY (ARRAY['sandbox'::character varying::text, 'production'::character varying::text])", name: "chk_push_subscriptions_environment"
t.check_constraint "platform::text = 'ios'::text", name: "chk_push_subscriptions_platform"
end
create_table "questrade_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_number"
t.string "account_status"
t.string "account_type"
t.boolean "activities_fetch_pending", default: false
t.decimal "cash_balance", precision: 19, scale: 4, default: "0.0"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.datetime "last_activities_sync"
t.datetime "last_holdings_sync"
t.string "name"
t.string "provider"
t.string "questrade_account_id"
t.string "questrade_authorization_id"
t.uuid "questrade_item_id", null: false
t.jsonb "raw_activities_payload", default: []
t.jsonb "raw_balances_payload"
t.jsonb "raw_holdings_payload", default: []
t.jsonb "raw_payload"
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["questrade_authorization_id"], name: "index_questrade_accounts_on_questrade_authorization_id"
t.index ["questrade_item_id", "questrade_account_id"], name: "idx_on_questrade_item_id_questrade_account_id_c3e6274342", unique: true
t.index ["questrade_item_id"], name: "index_questrade_accounts_on_questrade_item_id"
end
create_table "questrade_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "api_server"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.text "refresh_token"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_questrade_items_on_family_id"
t.index ["status"], name: "index_questrade_items_on_status"
end
create_table "recurrence_rules", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "day_of_month"
t.string "frequency", null: false
t.integer "interval", default: 1, null: false
t.integer "month_of_year"
t.integer "position", default: 0, null: false
t.uuid "recurring_transaction_id", null: false
t.datetime "updated_at", null: false
t.integer "weekday"
t.integer "weekday_ordinal"
t.index ["recurring_transaction_id", "position"], name: "idx_on_recurring_transaction_id_position_62f0900d95", unique: true
t.check_constraint "NOT (day_of_month IS NOT NULL AND weekday IS NOT NULL)", name: "chk_recurrence_rules_single_day_spec"
t.check_constraint "\"interval\" > 0", name: "chk_recurrence_rules_interval_positive"
t.check_constraint "day_of_month IS NULL OR day_of_month >= '-1'::integer AND day_of_month <= 31 AND day_of_month <> 0", name: "chk_recurrence_rules_day_of_month_range"
t.check_constraint "frequency::text = ANY (ARRAY['weekly'::character varying, 'monthly'::character varying, 'yearly'::character varying]::text[])", name: "chk_recurrence_rules_frequency"
t.check_constraint "month_of_year IS NULL OR month_of_year >= 1 AND month_of_year <= 12", name: "chk_recurrence_rules_month_of_year_range"
t.check_constraint "weekday IS NULL OR weekday >= 0 AND weekday <= 6", name: "chk_recurrence_rules_weekday_range"
t.check_constraint "weekday_ordinal IS NULL OR weekday IS NOT NULL", name: "chk_recurrence_rules_ordinal_requires_weekday"
t.check_constraint "weekday_ordinal IS NULL OR weekday_ordinal >= '-1'::integer AND weekday_ordinal <= 5 AND weekday_ordinal <> 0", name: "chk_recurrence_rules_weekday_ordinal_range"
end
create_table "recurring_allocations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "allocated_amount", precision: 19, scale: 4, null: false
t.datetime "created_at", null: false
t.string "currency", null: false
t.uuid "entry_id"
t.decimal "match_confidence", precision: 5, scale: 4
t.jsonb "match_signals", default: {}, null: false
t.date "paid_on"
t.uuid "recurring_occurrence_id", null: false
t.string "source", null: false
t.decimal "source_amount", precision: 19, scale: 4
t.string "source_currency"
t.string "state", default: "confirmed", null: false
t.datetime "updated_at", null: false
t.index ["entry_id"], name: "index_recurring_allocations_on_entry_id"
t.index ["recurring_occurrence_id", "entry_id"], name: "idx_recurring_allocations_entry_once", unique: true, where: "(entry_id IS NOT NULL)"
t.index ["recurring_occurrence_id"], name: "index_recurring_allocations_on_recurring_occurrence_id"
t.check_constraint "allocated_amount > 0::numeric", name: "chk_recurring_allocations_amount_positive"
t.check_constraint "source::text = ANY (ARRAY['auto_matched'::character varying, 'user_confirmed'::character varying, 'user_created'::character varying]::text[])", name: "chk_recurring_allocations_source"
t.check_constraint "state::text = ANY (ARRAY['suggested'::character varying, 'confirmed'::character varying]::text[])", name: "chk_recurring_allocations_state"
end
create_table "recurring_match_rejections", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "entry_id", null: false
t.uuid "recurring_transaction_id", null: false
t.datetime "updated_at", null: false
t.index ["entry_id"], name: "index_recurring_match_rejections_on_entry_id"
t.index ["recurring_transaction_id", "entry_id"], name: "idx_recurring_match_rejections_pair", unique: true
end
create_table "recurring_occurrences", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "closed_at"
t.string "closed_source"
t.datetime "created_at", null: false
t.string "currency", null: false
t.date "due_on", null: false
t.decimal "expected_amount", precision: 19, scale: 4
t.uuid "family_id", null: false
t.text "notes"
t.date "original_due_on", null: false
t.uuid "recurring_transaction_id", null: false
t.date "snoozed_until"
t.string "status", default: "scheduled", null: false
t.datetime "updated_at", null: false
t.index ["family_id", "due_on"], name: "index_recurring_occurrences_on_family_id_and_due_on"
t.index ["family_id", "status", "due_on"], name: "index_recurring_occurrences_on_family_id_and_status_and_due_on"
t.index ["id", "currency"], name: "idx_recurring_occurrences_id_currency", unique: true
t.index ["recurring_transaction_id", "original_due_on"], name: "idx_recurring_occurrences_identity", unique: true
t.check_constraint "(status::text = 'scheduled'::text) = (closed_at IS NULL)", name: "chk_recurring_occurrences_closed_state"
t.check_constraint "closed_source IS NULL OR (closed_source::text = ANY (ARRAY['auto'::character varying, 'user'::character varying]::text[]))", name: "chk_recurring_occurrences_closed_source"
t.check_constraint "status::text = ANY (ARRAY['scheduled'::character varying, 'paid'::character varying, 'skipped'::character varying, 'missed'::character varying]::text[])", name: "chk_recurring_occurrences_status"
end
create_table "recurring_price_changes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "currency", null: false
t.date "effective_on", null: false
t.uuid "entry_id"
t.decimal "new_amount", precision: 19, scale: 4, null: false
t.decimal "previous_amount", precision: 19, scale: 4, null: false
t.uuid "recurring_transaction_id", null: false
t.string "source", null: false
t.datetime "updated_at", null: false
t.index ["entry_id"], name: "index_recurring_price_changes_on_entry_id"
t.index ["recurring_transaction_id", "effective_on"], name: "idx_recurring_price_changes_identity", unique: true
end
create_table "recurring_transactions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id"
t.decimal "amount", precision: 19, scale: 4, null: false
t.string "amount_strategy", default: "fixed", null: false
t.decimal "amount_tolerance_pct", precision: 5, scale: 2, default: "7.5", null: false
t.date "anchor_date"
t.boolean "autopay", default: false, null: false
t.string "bill_type", default: "bill", null: false
t.date "cancelled_on"
t.uuid "category_id"
t.datetime "created_at", null: false
t.string "currency", null: false
t.string "dedup_scope", default: "", null: false
t.uuid "destination_account_id"
t.integer "end_after_count"
t.string "end_mode", default: "never", null: false
t.date "end_on"
t.decimal "expected_amount_avg", precision: 19, scale: 4
t.decimal "expected_amount_max", precision: 19, scale: 4
t.decimal "expected_amount_min", precision: 19, scale: 4
t.integer "expected_day_of_month", null: false
t.uuid "family_id", null: false
t.string "holiday_calendar"
t.date "last_occurrence_date", null: false
t.boolean "manual", default: false, null: false
t.integer "match_days_early", default: 2, null: false
t.integer "match_days_late", default: 7, null: false
t.jsonb "matcher_hints", default: {}, null: false
t.uuid "merchant_id"
t.string "name"
t.date "next_expected_date", null: false
t.text "notes"
t.integer "notify_days_before"
t.integer "occurrence_count", default: 0, null: false
t.integer "overdue_grace_days"
t.string "payment_url"
t.date "renews_on"
t.uuid "replaced_by_id"
t.string "status", default: "active", null: false
t.date "trial_ends_on"
t.integer "upcoming_window_days"
t.datetime "updated_at", null: false
t.string "weekend_adjust", default: "none", null: false
t.index ["account_id"], name: "index_recurring_transactions_on_account_id"
t.index ["category_id"], name: "index_recurring_transactions_on_category_id"
t.index ["destination_account_id"], name: "index_recurring_transactions_on_destination_account_id"
t.index ["family_id", "account_id", "destination_account_id", "merchant_id", "amount", "currency", "dedup_scope"], name: "idx_recurring_txns_pair_merchant", unique: true, where: "((destination_account_id IS NOT NULL) AND (merchant_id IS NOT NULL))"
t.index ["family_id", "account_id", "destination_account_id", "name", "amount", "currency", "dedup_scope"], name: "idx_recurring_txns_pair_name", unique: true, where: "((destination_account_id IS NOT NULL) AND (name IS NOT NULL) AND (merchant_id IS NULL))"
t.index ["family_id", "account_id", "merchant_id", "amount", "currency", "dedup_scope"], name: "idx_recurring_txns_acct_merchant", unique: true, where: "((merchant_id IS NOT NULL) AND (destination_account_id IS NULL))"
t.index ["family_id", "account_id", "name", "amount", "currency", "dedup_scope"], name: "idx_recurring_txns_acct_name", unique: true, where: "((name IS NOT NULL) AND (merchant_id IS NULL) AND (destination_account_id IS NULL))"
t.index ["family_id", "status"], name: "index_recurring_transactions_on_family_id_and_status"
t.index ["family_id"], name: "index_recurring_transactions_on_family_id"
t.index ["merchant_id"], name: "index_recurring_transactions_on_merchant_id"
t.index ["next_expected_date"], name: "index_recurring_transactions_on_next_expected_date"
t.check_constraint "destination_account_id IS NULL OR account_id IS NOT NULL", name: "chk_recurring_txns_transfer_requires_source"
t.check_constraint "destination_account_id IS NULL OR destination_account_id <> account_id", name: "chk_recurring_txns_transfer_distinct_accounts"
end
create_table "redbark_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_number"
t.string "account_status"
t.string "account_type"
t.string "connection_id"
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.boolean "ignored", default: false, null: false
t.jsonb "institution_metadata"
t.string "name", null: false
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.string "redbark_account_id", null: false
t.uuid "redbark_item_id", null: false
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["redbark_item_id", "redbark_account_id"], name: "index_redbark_accounts_on_item_and_account_id", unique: true
t.index ["redbark_item_id"], name: "index_redbark_accounts_on_redbark_item_id"
end
create_table "redbark_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "api_key", null: false
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name", null: false
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_redbark_items_on_family_id"
t.index ["status"], name: "index_redbark_items_on_status"
end
create_table "rejected_transfers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "inflow_transaction_id", null: false
t.uuid "outflow_transaction_id", null: false
t.datetime "updated_at", null: false
t.index ["inflow_transaction_id", "outflow_transaction_id"], name: "idx_on_inflow_transaction_id_outflow_transaction_id_412f8e7e26", unique: true
t.index ["inflow_transaction_id"], name: "index_rejected_transfers_on_inflow_transaction_id"
t.index ["outflow_transaction_id"], name: "index_rejected_transfers_on_outflow_transaction_id"
end
create_table "rule_actions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "action_type", null: false
t.datetime "created_at", null: false
t.uuid "rule_id", null: false
t.datetime "updated_at", null: false
t.string "value"
t.index ["rule_id"], name: "index_rule_actions_on_rule_id"
end
create_table "rule_conditions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "condition_type", null: false
t.datetime "created_at", null: false
t.string "operator", null: false
t.uuid "parent_id"
t.uuid "rule_id"
t.datetime "updated_at", null: false
t.string "value"
t.index ["parent_id"], name: "index_rule_conditions_on_parent_id"
t.index ["rule_id"], name: "index_rule_conditions_on_rule_id"
end
create_table "rule_runs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.text "error_message"
t.datetime "executed_at", null: false
t.string "execution_type", null: false
t.integer "pending_jobs_count", default: 0, null: false
t.uuid "rule_id", null: false
t.string "rule_name"
t.string "status", null: false
t.integer "transactions_modified", default: 0, null: false
t.integer "transactions_processed", default: 0, null: false
t.integer "transactions_queued", default: 0, null: false
t.datetime "updated_at", null: false
t.index ["executed_at"], name: "index_rule_runs_on_executed_at"
t.index ["rule_id", "executed_at"], name: "index_rule_runs_on_rule_id_and_executed_at"
t.index ["rule_id"], name: "index_rule_runs_on_rule_id"
end
create_table "rules", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.boolean "active", default: false, null: false
t.datetime "created_at", null: false
t.date "effective_date"
t.uuid "family_id", null: false
t.string "name"
t.string "resource_type", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_rules_on_family_id"
end
create_table "securities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "country_code"
t.datetime "created_at", null: false
t.string "exchange_acronym"
t.string "exchange_mic"
t.string "exchange_operating_mic"
t.datetime "failed_fetch_at"
t.integer "failed_fetch_count", default: 0, null: false
t.date "first_provider_price_on"
t.string "kind", default: "standard", null: false
t.datetime "last_health_check_at"
t.string "logo_url"
t.string "name"
t.boolean "offline", default: false, null: false
t.string "offline_reason"
t.string "price_provider"
t.string "ticker", null: false
t.datetime "updated_at", null: false
t.string "website_url"
t.index "upper((ticker)::text), COALESCE(upper((exchange_operating_mic)::text), ''::text)", name: "index_securities_on_ticker_and_exchange_operating_mic_unique", unique: true
t.index ["country_code"], name: "index_securities_on_country_code"
t.index ["exchange_operating_mic"], name: "index_securities_on_exchange_operating_mic"
t.index ["kind"], name: "index_securities_on_kind"
t.index ["price_provider", "offline_reason"], name: "index_securities_on_price_provider_and_offline_reason"
t.index ["price_provider"], name: "index_securities_on_price_provider"
t.check_constraint "kind::text = ANY (ARRAY['standard'::character varying::text, 'cash'::character varying::text])", name: "chk_securities_kind"
end
create_table "security_prices", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "currency", default: "USD", null: false
t.date "date", null: false
t.decimal "price", precision: 19, scale: 4, null: false
t.boolean "provisional", default: false, null: false
t.uuid "security_id"
t.datetime "updated_at", null: false
t.index ["security_id", "date", "currency"], name: "index_security_prices_on_security_id_and_date_and_currency", unique: true
t.index ["security_id"], name: "index_security_prices_on_security_id"
end
create_table "sessions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "active_impersonator_session_id"
t.datetime "created_at", null: false
t.jsonb "data", default: {}
t.string "ip_address"
t.string "ip_address_digest"
t.jsonb "prev_transaction_page_params", default: {}
t.datetime "subscribed_at"
t.datetime "updated_at", null: false
t.string "user_agent"
t.uuid "user_id", null: false
t.index ["active_impersonator_session_id"], name: "index_sessions_on_active_impersonator_session_id"
t.index ["ip_address_digest"], name: "index_sessions_on_ip_address_digest"
t.index ["user_id"], name: "index_sessions_on_user_id"
end
create_table "settings", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "value"
t.string "var", null: false
t.index ["var"], name: "index_settings_on_var", unique: true
end
create_table "simplefin_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_subtype"
t.string "account_type"
t.decimal "available_balance", precision: 19, scale: 4
t.datetime "balance_date"
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "extra"
t.string "name"
t.jsonb "org_data"
t.jsonb "raw_holdings_payload"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.uuid "simplefin_item_id", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_simplefin_accounts_on_account_id"
t.index ["simplefin_item_id", "account_id"], name: "idx_unique_sfa_per_item_and_upstream", unique: true, where: "(account_id IS NOT NULL)"
t.index ["simplefin_item_id"], name: "index_simplefin_accounts_on_simplefin_item_id"
end
create_table "simplefin_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "access_url"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_simplefin_items_on_family_id"
t.index ["institution_domain"], name: "index_simplefin_items_on_institution_domain"
t.index ["institution_id"], name: "index_simplefin_items_on_institution_id"
t.index ["institution_name"], name: "index_simplefin_items_on_institution_name"
t.index ["status"], name: "index_simplefin_items_on_status"
end
create_table "snaptrade_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_number"
t.string "account_status"
t.string "account_type"
t.boolean "activities_fetch_pending", default: false
t.string "brokerage_name"
t.decimal "cash_balance", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.jsonb "institution_metadata"
t.datetime "last_activities_sync"
t.datetime "last_holdings_sync"
t.string "name"
t.string "provider"
t.jsonb "raw_activities_payload", default: []
t.jsonb "raw_balances_payload", default: []
t.jsonb "raw_holdings_payload", default: []
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.string "snaptrade_account_id"
t.string "snaptrade_authorization_id"
t.uuid "snaptrade_item_id", null: false
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["snaptrade_item_id", "snaptrade_account_id"], name: "index_snaptrade_accounts_on_item_and_snaptrade_account_id", unique: true, where: "(snaptrade_account_id IS NOT NULL)"
t.index ["snaptrade_item_id"], name: "index_snaptrade_accounts_on_snaptrade_item_id"
end
create_table "snaptrade_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "client_id"
t.string "consumer_key"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.datetime "last_synced_at"
t.string "name"
t.text "oauth_access_token"
t.text "oauth_refresh_token"
t.string "oauth_scope"
t.datetime "oauth_token_expires_at"
t.string "oauth_token_type"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "snaptrade_user_id"
t.string "snaptrade_user_secret"
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_snaptrade_items_on_family_id"
t.index ["status"], name: "index_snaptrade_items_on_status"
end
create_table "sophtron_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id", null: false
t.string "account_number_mask"
t.string "account_status"
t.string "account_sub_type"
t.string "account_type"
t.decimal "available_balance", precision: 19, scale: 4
t.decimal "balance", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency"
t.string "customer_id"
t.jsonb "institution_metadata"
t.datetime "last_updated"
t.boolean "manual_sync", default: false, null: false
t.string "member_id"
t.string "name", null: false
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.uuid "sophtron_item_id", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_sophtron_accounts_on_account_id"
t.index ["sophtron_item_id", "account_id"], name: "idx_unique_sophtron_accounts_per_item", unique: true
t.index ["sophtron_item_id"], name: "index_sophtron_accounts_on_sophtron_item_id"
end
create_table "sophtron_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "access_key", null: false
t.string "base_url"
t.datetime "created_at", null: false
t.string "current_job_id"
t.uuid "current_job_sophtron_account_id"
t.string "customer_id"
t.string "customer_name"
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "job_status"
t.text "last_connection_error"
t.boolean "manual_sync", default: false, null: false
t.string "name"
t.boolean "pending_account_setup", default: false
t.jsonb "raw_customer_payload"
t.jsonb "raw_institution_payload"
t.jsonb "raw_job_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false
t.string "status", default: "good"
t.datetime "sync_start_date"
t.datetime "updated_at", null: false
t.string "user_id", null: false
t.string "user_institution_id"
t.index ["current_job_sophtron_account_id"], name: "index_sophtron_items_on_current_job_sophtron_account_id"
t.index ["customer_id"], name: "index_sophtron_items_on_customer_id"
t.index ["family_id"], name: "index_sophtron_items_on_family_id"
t.index ["status"], name: "index_sophtron_items_on_status"
t.index ["user_institution_id"], name: "index_sophtron_items_on_user_institution_id"
end
create_table "sso_audit_logs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "event_type", null: false
t.string "ip_address"
t.jsonb "metadata", default: {}, null: false
t.string "provider"
t.datetime "updated_at", null: false
t.string "user_agent"
t.uuid "user_id"
t.index ["created_at"], name: "index_sso_audit_logs_on_created_at"
t.index ["event_type"], name: "index_sso_audit_logs_on_event_type"
t.index ["user_id", "created_at"], name: "index_sso_audit_logs_on_user_id_and_created_at"
t.index ["user_id"], name: "index_sso_audit_logs_on_user_id"
end
create_table "sso_identity_blocks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.text "identity_label", null: false
t.string "provider", null: false
t.string "uid_digest", null: false
t.datetime "updated_at", null: false
t.index ["provider", "uid_digest"], name: "index_sso_identity_blocks_on_provider_and_uid_digest", unique: true
end
create_table "sso_providers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "client_id"
t.string "client_secret"
t.datetime "created_at", null: false
t.boolean "enabled", default: true, null: false
t.string "icon"
t.string "issuer"
t.string "label", null: false
t.string "name", null: false
t.string "redirect_uri"
t.jsonb "settings", default: {}, null: false
t.string "strategy", null: false
t.datetime "updated_at", null: false
t.index ["enabled"], name: "index_sso_providers_on_enabled"
t.index ["name"], name: "index_sso_providers_on_name", unique: true
end
create_table "subscriptions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "amount", precision: 19, scale: 4
t.boolean "cancel_at_period_end", default: false, null: false
t.datetime "created_at", null: false
t.string "currency"
t.datetime "current_period_ends_at"
t.uuid "family_id", null: false
t.string "interval"
t.string "status", null: false
t.string "stripe_id"
t.datetime "trial_ends_at"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_subscriptions_on_family_id", unique: true
end
create_table "syncs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "cancel_requested_at"
t.datetime "completed_at"
t.datetime "created_at", null: false
t.jsonb "data"
t.string "error"
t.datetime "failed_at"
t.uuid "parent_id"
t.datetime "pending_at"
t.string "status", default: "pending"
t.text "sync_stats"
t.uuid "syncable_id", null: false
t.string "syncable_type", null: false
t.datetime "syncing_at"
t.datetime "updated_at", null: false
t.date "window_end_date"
t.date "window_start_date"
t.index ["parent_id"], name: "index_syncs_on_parent_id"
t.index ["status"], name: "index_syncs_on_status"
t.index ["syncable_type", "syncable_id", "created_at", "id"], name: "index_syncs_on_syncable_and_created_at_and_id", order: { created_at: :desc, id: :desc }
t.index ["syncable_type", "syncable_id"], name: "index_syncs_on_syncable"
end
create_table "taggings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "tag_id", null: false
t.uuid "taggable_id"
t.string "taggable_type"
t.datetime "updated_at", null: false
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable"
end
create_table "tags", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "color", default: "#e99537", null: false
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "name"
t.datetime "updated_at", null: false
t.index ["family_id", "name"], name: "index_tags_on_family_id_and_name", unique: true
t.index ["family_id"], name: "index_tags_on_family_id"
end
create_table "tool_calls", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "function_arguments"
t.string "function_name"
t.jsonb "function_result"
t.uuid "message_id", null: false
t.string "provider_call_id"
t.string "provider_id", null: false
t.string "type", null: false
t.datetime "updated_at", null: false
t.index ["message_id"], name: "index_tool_calls_on_message_id"
end
create_table "trades", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "currency"
t.jsonb "extra", default: {}, null: false
t.decimal "fee", precision: 19, scale: 4, default: "0.0", null: false
t.string "investment_activity_label"
t.jsonb "locked_attributes", default: {}
t.decimal "price", precision: 19, scale: 10
t.decimal "qty", precision: 24, scale: 8
t.uuid "security_id", null: false
t.datetime "updated_at", null: false
t.index ["extra"], name: "index_trades_on_extra", using: :gin
t.index ["investment_activity_label"], name: "index_trades_on_investment_activity_label"
t.index ["security_id"], name: "index_trades_on_security_id"
end
create_table "trading212_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_type"
t.decimal "cash_balance", precision: 19, scale: 4
t.datetime "created_at", null: false
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.datetime "last_orders_sync"
t.datetime "last_positions_sync"
t.string "name"
t.jsonb "raw_dividends_payload", default: [], null: false
t.jsonb "raw_orders_payload", default: [], null: false
t.jsonb "raw_positions_payload", default: [], null: false
t.jsonb "raw_transactions_payload", default: [], null: false
t.string "trading212_account_id"
t.uuid "trading212_item_id", null: false
t.datetime "updated_at", null: false
t.index ["trading212_item_id", "trading212_account_id"], name: "index_trading212_accounts_on_item_and_account_id", unique: true, where: "(trading212_account_id IS NOT NULL)"
t.index ["trading212_item_id"], name: "index_trading212_accounts_on_trading212_item_id"
end
create_table "trading212_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "api_key"
t.string "api_secret"
t.datetime "created_at", null: false
t.string "currency"
t.string "environment", default: "live", null: false
t.uuid "family_id", null: false
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_instruments_payload", default: [], null: false
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_trading212_items_on_family_id"
t.index ["status"], name: "index_trading212_items_on_status"
end
create_table "transactions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "category_id"
t.datetime "created_at", null: false
t.string "external_id"
t.jsonb "extra", default: {}, null: false
t.string "investment_activity_label"
t.string "kind", default: "standard", null: false
t.jsonb "locked_attributes", default: {}
t.uuid "merchant_id"
t.uuid "transfer_id"
t.datetime "updated_at", null: false
t.index "(((extra -> 'goal'::text) ->> 'pledge_id'::text))", name: "ix_transactions_extra_goal_pledge_id", unique: true, where: "(((extra -> 'goal'::text) ->> 'pledge_id'::text) IS NOT NULL)"
t.index ["category_id"], name: "index_transactions_on_category_id"
t.index ["external_id"], name: "index_transactions_on_external_id"
t.index ["extra"], name: "index_transactions_on_extra", using: :gin
t.index ["investment_activity_label"], name: "index_transactions_on_investment_activity_label"
t.index ["kind"], name: "index_transactions_on_kind"
t.index ["merchant_id"], name: "index_transactions_on_merchant_id"
t.index ["transfer_id"], name: "index_transactions_on_transfer_id"
end
create_table "transfers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.decimal "amount", precision: 19, scale: 4, default: "0.0", null: false
t.datetime "created_at", null: false
t.uuid "inflow_transaction_id", null: false
t.text "notes"
t.uuid "outflow_transaction_id", null: false
t.string "status", default: "pending", null: false
t.datetime "updated_at", null: false
t.index ["inflow_transaction_id", "outflow_transaction_id"], name: "idx_on_inflow_transaction_id_outflow_transaction_id_8cd07a28bd", unique: true
t.index ["inflow_transaction_id"], name: "index_transfers_on_inflow_transaction_id"
t.index ["outflow_transaction_id"], name: "index_transfers_on_outflow_transaction_id"
t.index ["status"], name: "index_transfers_on_status"
t.check_constraint "amount >= 0::numeric", name: "check_transfer_amount_non_negative"
end
create_table "up_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "account_id"
t.string "account_status"
t.string "account_type"
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.boolean "ignored", default: false, null: false
t.jsonb "institution_metadata"
t.string "name", null: false
t.string "ownership_type"
t.string "provider"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.date "sync_start_date"
t.uuid "up_item_id", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_up_accounts_on_account_id"
t.index ["up_item_id", "account_id"], name: "index_up_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["up_item_id"], name: "index_up_accounts_on_up_item_id"
end
create_table "up_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "access_token"
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.string "institution_color"
t.string "institution_domain"
t.string "institution_id"
t.string "institution_name"
t.string "institution_url"
t.string "name"
t.boolean "pending_account_setup", default: false, null: false
t.jsonb "raw_institution_payload"
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.date "sync_start_date"
t.datetime "updated_at", null: false
t.index ["family_id"], name: "index_up_items_on_family_id"
t.index ["status"], name: "index_up_items_on_status"
end
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.boolean "active", default: true, null: false
t.boolean "ai_enabled", default: false, null: false
t.datetime "created_at", null: false
t.uuid "default_account_id"
t.string "default_account_order", default: "name_asc"
t.string "default_period", default: "last_30_days", null: false
t.string "email"
t.uuid "family_id", null: false
t.string "first_name"
t.text "goals", default: [], array: true
t.datetime "last_login_at"
t.string "last_name"
t.uuid "last_viewed_chat_id"
t.string "locale"
t.datetime "onboarded_at"
t.string "otp_backup_codes", default: [], array: true
t.boolean "otp_required", default: false, null: false
t.string "otp_secret"
t.string "password_digest"
t.jsonb "preferences", default: {}, null: false
t.string "role", default: "member", null: false
t.datetime "rule_prompt_dismissed_at"
t.boolean "rule_prompts_disabled", default: false
t.integer "sessions_count", default: 0, null: false
t.datetime "set_onboarding_goals_at"
t.datetime "set_onboarding_preferences_at"
t.boolean "show_ai_sidebar", default: true
t.boolean "show_sidebar", default: true
t.string "theme", default: "system"
t.string "ui_layout"
t.string "unconfirmed_email"
t.datetime "updated_at", null: false
t.string "webauthn_id"
t.index ["default_account_id"], name: "index_users_on_default_account_id"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["family_id"], name: "index_users_on_family_id"
t.index ["last_viewed_chat_id"], name: "index_users_on_last_viewed_chat_id"
t.index ["locale"], name: "index_users_on_locale"
t.index ["otp_secret"], name: "index_users_on_otp_secret", unique: true, where: "(otp_secret IS NOT NULL)"
t.index ["preferences"], name: "index_users_on_preferences", using: :gin
t.index ["webauthn_id"], name: "index_users_on_webauthn_id", unique: true, where: "(webauthn_id IS NOT NULL)"
end
create_table "valuations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "kind", default: "reconciliation", null: false
t.jsonb "locked_attributes", default: {}
t.datetime "updated_at", null: false
end
create_table "vehicles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.jsonb "locked_attributes", default: {}
t.string "make"
t.string "mileage_unit"
t.integer "mileage_value"
t.string "model"
t.string "subtype"
t.datetime "updated_at", null: false
t.integer "year"
end
create_table "webauthn_credentials", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "credential_id", null: false
t.datetime "last_used_at"
t.string "nickname", null: false
t.text "public_key", null: false
t.bigint "sign_count", default: 0, null: false
t.string "transports", default: [], null: false, array: true
t.datetime "updated_at", null: false
t.uuid "user_id", null: false
t.index ["credential_id"], name: "index_webauthn_credentials_on_credential_id", unique: true
t.index ["user_id"], name: "index_webauthn_credentials_on_user_id"
t.check_constraint "sign_count >= 0", name: "chk_webauthn_credentials_sign_count_non_negative"
end
create_table "wise_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "balance_id", null: false
t.datetime "created_at", null: false
t.string "currency", null: false
t.decimal "current_balance", precision: 19, scale: 4
t.string "name"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.decimal "reserved_balance", precision: 19, scale: 4
t.datetime "updated_at", null: false
t.uuid "wise_item_id", null: false
t.index ["wise_item_id", "balance_id"], name: "index_wise_accounts_on_wise_item_id_and_balance_id", unique: true
t.index ["wise_item_id"], name: "index_wise_accounts_on_wise_item_id"
end
create_table "wise_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "family_id", null: false
t.boolean "import_all_history", default: false, null: false
t.string "name", null: false
t.boolean "pending_account_setup", default: false, null: false
t.string "profile_id", null: false
t.string "profile_type", null: false
t.jsonb "raw_payload"
t.boolean "scheduled_for_deletion", default: false, null: false
t.string "status", default: "good", null: false
t.datetime "sync_start_date"
t.text "token", null: false
t.datetime "updated_at", null: false
t.index ["family_id", "profile_id"], name: "index_wise_items_on_family_id_and_profile_id", unique: true
t.index ["family_id"], name: "index_wise_items_on_family_id"
t.index ["status"], name: "index_wise_items_on_status"
end
add_foreign_key "account_providers", "accounts", on_delete: :cascade
add_foreign_key "account_shares", "accounts"
add_foreign_key "account_shares", "users"
add_foreign_key "account_statements", "accounts", column: "suggested_account_id", on_delete: :nullify
add_foreign_key "account_statements", "accounts", on_delete: :nullify
add_foreign_key "account_statements", "families", on_delete: :cascade
add_foreign_key "accounts", "families"
add_foreign_key "accounts", "imports"
add_foreign_key "accounts", "plaid_accounts"
add_foreign_key "accounts", "simplefin_accounts"
add_foreign_key "accounts", "users", column: "owner_id", on_delete: :nullify
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "akahu_accounts", "akahu_items"
add_foreign_key "akahu_items", "families"
add_foreign_key "api_keys", "users"
add_foreign_key "balances", "accounts", on_delete: :cascade
add_foreign_key "binance_accounts", "binance_items"
add_foreign_key "binance_items", "families"
add_foreign_key "brex_accounts", "brex_items"
add_foreign_key "brex_items", "families"
add_foreign_key "budget_categories", "budgets", on_delete: :cascade
add_foreign_key "budget_categories", "categories"
add_foreign_key "budget_shares", "users", column: "owner_id"
add_foreign_key "budget_shares", "users", column: "viewer_id"
add_foreign_key "budgets", "families"
add_foreign_key "budgets", "users", on_delete: :cascade
add_foreign_key "categories", "families"
add_foreign_key "chats", "users"
add_foreign_key "coinbase_accounts", "coinbase_items"
add_foreign_key "coinbase_items", "families"
add_foreign_key "coinstats_accounts", "coinstats_items"
add_foreign_key "coinstats_items", "families"
add_foreign_key "debug_log_entries", "account_providers", on_delete: :nullify
add_foreign_key "debug_log_entries", "accounts", on_delete: :nullify
add_foreign_key "debug_log_entries", "families", on_delete: :nullify
add_foreign_key "debug_log_entries", "users", on_delete: :nullify
add_foreign_key "enable_banking_accounts", "enable_banking_items"
add_foreign_key "enable_banking_items", "families"
add_foreign_key "entries", "account_statements", column: "reconciled_by_statement_id", on_delete: :nullify
add_foreign_key "entries", "accounts", on_delete: :cascade
add_foreign_key "entries", "entries", column: "parent_entry_id", on_delete: :cascade
add_foreign_key "entries", "imports"
add_foreign_key "eval_results", "eval_runs"
add_foreign_key "eval_results", "eval_samples"
add_foreign_key "eval_runs", "eval_datasets"
add_foreign_key "eval_samples", "eval_datasets"
add_foreign_key "family_documents", "families"
add_foreign_key "family_exports", "families"
add_foreign_key "family_merchant_associations", "families"
add_foreign_key "family_merchant_associations", "merchants"
add_foreign_key "goal_accounts", "accounts", on_delete: :restrict
add_foreign_key "goal_accounts", "goals", on_delete: :cascade
add_foreign_key "goal_pledges", "accounts", on_delete: :restrict
add_foreign_key "goal_pledges", "goals", on_delete: :cascade
add_foreign_key "goal_pledges", "transactions", column: "matched_transaction_id", on_delete: :nullify
add_foreign_key "goals", "families", on_delete: :cascade
add_foreign_key "holdings", "account_providers"
add_foreign_key "holdings", "accounts", on_delete: :cascade
add_foreign_key "holdings", "securities"
add_foreign_key "holdings", "securities", column: "provider_security_id"
add_foreign_key "ibkr_accounts", "ibkr_items"
add_foreign_key "ibkr_items", "families"
add_foreign_key "impersonation_session_logs", "impersonation_sessions"
add_foreign_key "impersonation_sessions", "users", column: "impersonated_id"
add_foreign_key "impersonation_sessions", "users", column: "impersonator_id"
add_foreign_key "import_rows", "imports"
add_foreign_key "import_sessions", "families"
add_foreign_key "import_source_mappings", "families"
add_foreign_key "import_source_mappings", "import_sessions", column: ["import_session_id", "family_id"], primary_key: ["id", "family_id"], name: "fk_import_source_mappings_session_family", on_delete: :cascade
add_foreign_key "imports", "account_statements", on_delete: :nullify
add_foreign_key "imports", "families"
add_foreign_key "imports", "import_sessions", column: ["import_session_id", "family_id"], primary_key: ["id", "family_id"], name: "fk_imports_session_family", on_delete: :cascade
add_foreign_key "indexa_capital_accounts", "indexa_capital_items"
add_foreign_key "indexa_capital_items", "families"
add_foreign_key "insights", "families"
add_foreign_key "invitations", "families"
add_foreign_key "invitations", "users", column: "inviter_id"
add_foreign_key "kraken_accounts", "kraken_items"
add_foreign_key "kraken_items", "families"
add_foreign_key "llm_usages", "families"
add_foreign_key "lunchflow_accounts", "lunchflow_items"
add_foreign_key "lunchflow_items", "families"
add_foreign_key "merchants", "families"
add_foreign_key "mercury_accounts", "mercury_items"
add_foreign_key "mercury_items", "families"
add_foreign_key "messages", "chats"
add_foreign_key "mobile_devices", "users"
add_foreign_key "notification_deliveries", "rules", on_delete: :cascade
add_foreign_key "notification_deliveries", "transactions", on_delete: :cascade
add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id"
add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
add_foreign_key "oidc_identities", "users"
add_foreign_key "onchain_wallet_accounts", "onchain_wallet_items"
add_foreign_key "onchain_wallet_items", "families"
add_foreign_key "plaid_accounts", "plaid_items"
add_foreign_key "plaid_items", "families"
add_foreign_key "push_subscriptions", "users"
add_foreign_key "questrade_accounts", "questrade_items"
add_foreign_key "questrade_items", "families"
add_foreign_key "recurrence_rules", "recurring_transactions", on_delete: :cascade
add_foreign_key "recurring_allocations", "entries", on_delete: :nullify
add_foreign_key "recurring_allocations", "recurring_occurrences", column: ["recurring_occurrence_id", "currency"], primary_key: ["id", "currency"], name: "fk_recurring_allocations_currency_matches_occurrence", on_delete: :cascade
add_foreign_key "recurring_allocations", "recurring_occurrences", on_delete: :cascade
add_foreign_key "recurring_match_rejections", "entries", on_delete: :cascade
add_foreign_key "recurring_match_rejections", "recurring_transactions", on_delete: :cascade
add_foreign_key "recurring_occurrences", "families", on_delete: :cascade
add_foreign_key "recurring_occurrences", "recurring_transactions", on_delete: :cascade
add_foreign_key "recurring_price_changes", "entries", on_delete: :nullify
add_foreign_key "recurring_price_changes", "recurring_transactions", on_delete: :cascade
add_foreign_key "recurring_transactions", "accounts", column: "destination_account_id", on_delete: :cascade
add_foreign_key "recurring_transactions", "accounts", on_delete: :cascade
add_foreign_key "recurring_transactions", "categories", on_delete: :nullify
add_foreign_key "recurring_transactions", "families"
add_foreign_key "recurring_transactions", "merchants"
add_foreign_key "recurring_transactions", "recurring_transactions", column: "replaced_by_id", on_delete: :nullify
add_foreign_key "redbark_accounts", "redbark_items"
add_foreign_key "redbark_items", "families"
add_foreign_key "rejected_transfers", "transactions", column: "inflow_transaction_id", on_delete: :cascade
add_foreign_key "rejected_transfers", "transactions", column: "outflow_transaction_id", on_delete: :cascade
add_foreign_key "rule_actions", "rules"
add_foreign_key "rule_conditions", "rule_conditions", column: "parent_id"
add_foreign_key "rule_conditions", "rules"
add_foreign_key "rule_runs", "rules"
add_foreign_key "rules", "families"
add_foreign_key "security_prices", "securities"
add_foreign_key "sessions", "impersonation_sessions", column: "active_impersonator_session_id"
add_foreign_key "sessions", "users"
add_foreign_key "simplefin_accounts", "simplefin_items"
add_foreign_key "simplefin_items", "families"
add_foreign_key "snaptrade_accounts", "snaptrade_items"
add_foreign_key "snaptrade_items", "families"
add_foreign_key "sophtron_accounts", "sophtron_items"
add_foreign_key "sophtron_items", "families"
add_foreign_key "sso_audit_logs", "users"
add_foreign_key "subscriptions", "families"
add_foreign_key "syncs", "syncs", column: "parent_id"
add_foreign_key "taggings", "tags"
add_foreign_key "tags", "families"
add_foreign_key "tool_calls", "messages"
add_foreign_key "trades", "securities"
add_foreign_key "trading212_accounts", "trading212_items"
add_foreign_key "trading212_items", "families"
add_foreign_key "transactions", "categories", on_delete: :nullify
add_foreign_key "transactions", "merchants"
add_foreign_key "transactions", "transfers"
add_foreign_key "transfers", "transactions", column: "inflow_transaction_id", on_delete: :cascade
add_foreign_key "transfers", "transactions", column: "outflow_transaction_id", on_delete: :cascade
add_foreign_key "up_accounts", "up_items"
add_foreign_key "up_items", "families"
add_foreign_key "users", "accounts", column: "default_account_id", on_delete: :nullify
add_foreign_key "users", "chats", column: "last_viewed_chat_id"
add_foreign_key "users", "families"
add_foreign_key "webauthn_credentials", "users"
add_foreign_key "wise_accounts", "wise_items", on_delete: :cascade
add_foreign_key "wise_items", "families"
end