mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 07:24:55 +00:00
Rename raw_investments_payload to raw_holdings_payload for Plaid accounts (#760)
* refactor: rename `raw_investments_payload` to `raw_holdings_payload` - Update references and models to use consistent naming. - Adjust migrations, tests, and encryption setup accordingly. * fix: improve safety when accessing raw_holdings_payload keys - Use `dig` with safe navigation to prevent potential nil errors. - Add support for decryption from the old column name `raw_investments_payload`. - Adjust related methods and calculations for consistency. --------- Co-authored-by: luckyPipewrench <luckypipewrench@proton.me>
This commit is contained in:
@@ -5,7 +5,8 @@ class PlaidAccount < ApplicationRecord
|
||||
if encryption_ready?
|
||||
encrypts :raw_payload
|
||||
encrypts :raw_transactions_payload
|
||||
encrypts :raw_investments_payload
|
||||
# Support reading data encrypted under the old column name after rename
|
||||
encrypts :raw_holdings_payload, previous: { attribute: :raw_investments_payload }
|
||||
encrypts :raw_liabilities_payload
|
||||
end
|
||||
|
||||
@@ -48,9 +49,9 @@ class PlaidAccount < ApplicationRecord
|
||||
save!
|
||||
end
|
||||
|
||||
def upsert_plaid_investments_snapshot!(investments_snapshot)
|
||||
def upsert_plaid_holdings_snapshot!(holdings_snapshot)
|
||||
assign_attributes(
|
||||
raw_investments_payload: investments_snapshot
|
||||
raw_holdings_payload: holdings_snapshot
|
||||
)
|
||||
|
||||
save!
|
||||
|
||||
@@ -23,7 +23,7 @@ class PlaidAccount::Importer
|
||||
end
|
||||
|
||||
def import_investments
|
||||
plaid_account.upsert_plaid_investments_snapshot!(account_snapshot.investments_data)
|
||||
plaid_account.upsert_plaid_holdings_snapshot!(account_snapshot.investments_data)
|
||||
end
|
||||
|
||||
def import_liabilities
|
||||
|
||||
@@ -44,7 +44,7 @@ class PlaidAccount::Investments::BalanceCalculator
|
||||
attr_reader :plaid_account, :security_resolver
|
||||
|
||||
def holdings
|
||||
plaid_account.raw_investments_payload["holdings"] || []
|
||||
plaid_account.raw_holdings_payload&.dig("holdings") || []
|
||||
end
|
||||
|
||||
def calculate_investment_brokerage_cash
|
||||
|
||||
@@ -51,7 +51,7 @@ class PlaidAccount::Investments::HoldingsProcessor
|
||||
end
|
||||
|
||||
def holdings
|
||||
plaid_account.raw_investments_payload&.[]("holdings") || []
|
||||
plaid_account.raw_holdings_payload&.[]("holdings") || []
|
||||
end
|
||||
|
||||
def parse_decimal(value)
|
||||
|
||||
@@ -43,7 +43,7 @@ class PlaidAccount::Investments::SecurityResolver
|
||||
Response = Struct.new(:security, :cash_equivalent?, :brokerage_cash?, keyword_init: true)
|
||||
|
||||
def securities
|
||||
plaid_account.raw_investments_payload["securities"] || []
|
||||
plaid_account.raw_holdings_payload&.dig("securities") || []
|
||||
end
|
||||
|
||||
# Tries to find security, or returns the "proxy security" (common with options contracts that have underlying securities)
|
||||
|
||||
@@ -98,7 +98,7 @@ class PlaidAccount::Investments::TransactionsProcessor
|
||||
end
|
||||
|
||||
def transactions
|
||||
plaid_account.raw_investments_payload["transactions"] || []
|
||||
plaid_account.raw_holdings_payload&.dig("transactions") || []
|
||||
end
|
||||
|
||||
# Plaid unfortunately returns incorrect signage on some `quantity` values. They claim all "sell" transactions
|
||||
|
||||
@@ -61,7 +61,7 @@ class PlaidItem::Syncer
|
||||
|
||||
def count_holdings(plaid_accounts)
|
||||
plaid_accounts.sum do |pa|
|
||||
Array(pa.raw_investments_payload).size
|
||||
pa.raw_holdings_payload&.dig("holdings")&.size || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class RenameRawInvestmentsPayloadToRawHoldingsPayload < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
rename_column :plaid_accounts, :raw_investments_payload, :raw_holdings_payload
|
||||
end
|
||||
end
|
||||
4
db/schema.rb
generated
4
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_01_23_000000) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_01_23_214127) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pgcrypto"
|
||||
enable_extension "plpgsql"
|
||||
@@ -943,7 +943,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_01_23_000000) do
|
||||
t.datetime "updated_at", null: false
|
||||
t.jsonb "raw_payload", default: {}
|
||||
t.jsonb "raw_transactions_payload", default: {}
|
||||
t.jsonb "raw_investments_payload", default: {}
|
||||
t.jsonb "raw_holdings_payload", default: {}
|
||||
t.jsonb "raw_liabilities_payload", default: {}
|
||||
t.index ["plaid_id"], name: "index_plaid_accounts_on_plaid_id", unique: true
|
||||
t.index ["plaid_item_id"], name: "index_plaid_accounts_on_plaid_item_id"
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace :security do
|
||||
results[:enable_banking_items] = backfill_model(EnableBankingItem, %i[client_certificate session_id raw_payload raw_institution_payload], batch_size, dry_run)
|
||||
|
||||
# Provider accounts
|
||||
results[:plaid_accounts] = backfill_model(PlaidAccount, %i[raw_payload raw_transactions_payload raw_investments_payload raw_liabilities_payload], batch_size, dry_run)
|
||||
results[:plaid_accounts] = backfill_model(PlaidAccount, %i[raw_payload raw_transactions_payload raw_holdings_payload raw_liabilities_payload], batch_size, dry_run)
|
||||
results[:simplefin_accounts] = backfill_model(SimplefinAccount, %i[raw_payload raw_transactions_payload raw_holdings_payload], batch_size, dry_run)
|
||||
results[:lunchflow_accounts] = backfill_model(LunchflowAccount, %i[raw_payload raw_transactions_payload], batch_size, dry_run)
|
||||
results[:enable_banking_accounts] = backfill_model(EnableBankingAccount, %i[raw_payload raw_transactions_payload], batch_size, dry_run)
|
||||
|
||||
@@ -38,7 +38,7 @@ class PlaidAccount::ImporterTest < ActiveSupport::TestCase
|
||||
|
||||
@plaid_account.expects(:upsert_plaid_snapshot!).with(account_data)
|
||||
@plaid_account.expects(:upsert_plaid_transactions_snapshot!).with(transactions_data)
|
||||
@plaid_account.expects(:upsert_plaid_investments_snapshot!).with(investments_data)
|
||||
@plaid_account.expects(:upsert_plaid_holdings_snapshot!).with(investments_data)
|
||||
@plaid_account.expects(:upsert_plaid_liabilities_snapshot!).with(liabilities_data)
|
||||
|
||||
PlaidAccount::Importer.new(@plaid_account, account_snapshot: @mock_account_snapshot).import
|
||||
|
||||
@@ -67,7 +67,7 @@ class PlaidAccount::Investments::BalanceCalculatorTest < ActiveSupport::TestCase
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments)
|
||||
|
||||
security_resolver = PlaidAccount::Investments::SecurityResolver.new(@plaid_account)
|
||||
balance_calculator = PlaidAccount::Investments::BalanceCalculator.new(@plaid_account, security_resolver: security_resolver)
|
||||
|
||||
@@ -27,7 +27,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: [] # not relevant for test
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve)
|
||||
.with(plaid_security_id: "123")
|
||||
@@ -125,7 +125,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: []
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
# Mock security resolver for all three securities
|
||||
@security_resolver.expects(:resolve)
|
||||
@@ -175,7 +175,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: []
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
# First security fails to resolve
|
||||
@security_resolver.expects(:resolve)
|
||||
@@ -213,7 +213,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: []
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve)
|
||||
.with(plaid_security_id: "string_values")
|
||||
@@ -264,7 +264,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: []
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve)
|
||||
.with(plaid_security_id: "missing_quantity")
|
||||
@@ -310,7 +310,7 @@ class PlaidAccount::Investments::HoldingsProcessorTest < ActiveSupport::TestCase
|
||||
transactions: []
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve)
|
||||
.with(plaid_security_id: "no_currency")
|
||||
|
||||
@@ -11,7 +11,7 @@ class PlaidAccount::Investments::SecurityResolverTest < ActiveSupport::TestCase
|
||||
missing_id = "missing_security_id"
|
||||
|
||||
# Ensure there are *no* securities that reference the missing ID
|
||||
@plaid_account.update!(raw_investments_payload: {
|
||||
@plaid_account.update!(raw_holdings_payload: {
|
||||
securities: [
|
||||
{
|
||||
"security_id" => "some_other_id",
|
||||
@@ -35,7 +35,7 @@ class PlaidAccount::Investments::SecurityResolverTest < ActiveSupport::TestCase
|
||||
test "identifies brokerage cash plaid securities" do
|
||||
brokerage_cash_id = "brokerage_cash_security_id"
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: {
|
||||
@plaid_account.update!(raw_holdings_payload: {
|
||||
securities: [
|
||||
{
|
||||
"security_id" => brokerage_cash_id,
|
||||
@@ -58,7 +58,7 @@ class PlaidAccount::Investments::SecurityResolverTest < ActiveSupport::TestCase
|
||||
test "identifies cash equivalent plaid securities" do
|
||||
mmf_security_id = "money_market_security_id"
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: {
|
||||
@plaid_account.update!(raw_holdings_payload: {
|
||||
securities: [
|
||||
{
|
||||
"security_id" => mmf_security_id,
|
||||
@@ -87,7 +87,7 @@ class PlaidAccount::Investments::SecurityResolverTest < ActiveSupport::TestCase
|
||||
test "resolves normal plaid securities" do
|
||||
security_id = "regular_security_id"
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: {
|
||||
@plaid_account.update!(raw_holdings_payload: {
|
||||
securities: [
|
||||
{
|
||||
"security_id" => security_id,
|
||||
|
||||
@@ -23,7 +23,7 @@ class PlaidAccount::Investments::TransactionsProcessorTest < ActiveSupport::Test
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.stubs(:resolve).returns(OpenStruct.new(
|
||||
security: securities(:aapl)
|
||||
@@ -58,7 +58,7 @@ class PlaidAccount::Investments::TransactionsProcessorTest < ActiveSupport::Test
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve).never # Cash transactions don't have a security
|
||||
|
||||
@@ -91,7 +91,7 @@ class PlaidAccount::Investments::TransactionsProcessorTest < ActiveSupport::Test
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve).never # Cash transactions don't have a security
|
||||
|
||||
@@ -127,7 +127,7 @@ class PlaidAccount::Investments::TransactionsProcessorTest < ActiveSupport::Test
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve).returns(OpenStruct.new(
|
||||
security: securities(:aapl)
|
||||
@@ -163,7 +163,7 @@ class PlaidAccount::Investments::TransactionsProcessorTest < ActiveSupport::Test
|
||||
]
|
||||
}
|
||||
|
||||
@plaid_account.update!(raw_investments_payload: test_investments_payload)
|
||||
@plaid_account.update!(raw_holdings_payload: test_investments_payload)
|
||||
|
||||
@security_resolver.expects(:resolve).never
|
||||
|
||||
|
||||
Reference in New Issue
Block a user