mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-22 22:59:39 +02:00
Handle case sensitive values when creating securities
This commit is contained in:
parent
867318cbc1
commit
d8e058d7c6
4 changed files with 49 additions and 8 deletions
|
@ -106,8 +106,8 @@ class PlaidInvestmentSync
|
||||||
|
|
||||||
# Find any matching security
|
# Find any matching security
|
||||||
security = Security.find_or_create_by!(
|
security = Security.find_or_create_by!(
|
||||||
ticker: plaid_security.ticker_symbol,
|
ticker: plaid_security.ticker_symbol&.upcase,
|
||||||
exchange_operating_mic: operating_mic
|
exchange_operating_mic: operating_mic&.upcase
|
||||||
)
|
)
|
||||||
|
|
||||||
[ security, plaid_security ]
|
[ security, plaid_security ]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class Security < ApplicationRecord
|
class Security < ApplicationRecord
|
||||||
include Provided
|
include Provided
|
||||||
|
|
||||||
before_save :upcase_ticker
|
before_validation :upcase_symbols
|
||||||
|
|
||||||
has_many :trades, dependent: :nullify, class_name: "Trade"
|
has_many :trades, dependent: :nullify, class_name: "Trade"
|
||||||
has_many :prices, dependent: :destroy
|
has_many :prices, dependent: :destroy
|
||||||
|
@ -29,8 +29,8 @@ class Security < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
def upcase_symbols
|
||||||
def upcase_ticker
|
|
||||||
self.ticker = ticker.upcase
|
self.ticker = ticker.upcase
|
||||||
|
self.exchange_operating_mic = exchange_operating_mic.upcase if exchange_operating_mic.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -90,7 +90,7 @@ class TradeImport < Import
|
||||||
return internal_security if internal_security.present?
|
return internal_security if internal_security.present?
|
||||||
|
|
||||||
# If security prices provider isn't properly configured or available, create with nil exchange_operating_mic
|
# If security prices provider isn't properly configured or available, create with nil exchange_operating_mic
|
||||||
return Security.find_or_create_by!(ticker: ticker, exchange_operating_mic: nil) unless Security.provider.present?
|
return Security.find_or_create_by!(ticker: ticker&.upcase, exchange_operating_mic: nil) unless Security.provider.present?
|
||||||
|
|
||||||
# Cache provider responses so that when we're looping through rows and importing,
|
# Cache provider responses so that when we're looping through rows and importing,
|
||||||
# we only hit our provider for the unique combinations of ticker / exchange_operating_mic
|
# we only hit our provider for the unique combinations of ticker / exchange_operating_mic
|
||||||
|
@ -104,9 +104,9 @@ class TradeImport < Import
|
||||||
).first
|
).first
|
||||||
end
|
end
|
||||||
|
|
||||||
return Security.find_or_create_by!(ticker: ticker, exchange_operating_mic: nil) if provider_security.nil?
|
return Security.find_or_create_by!(ticker: ticker&.upcase, exchange_operating_mic: nil) if provider_security.nil?
|
||||||
|
|
||||||
Security.find_or_create_by!(ticker: provider_security[:ticker], exchange_operating_mic: provider_security[:exchange_operating_mic]) do |security|
|
Security.find_or_create_by!(ticker: provider_security[:ticker]&.upcase, exchange_operating_mic: provider_security[:exchange_operating_mic]&.upcase) do |security|
|
||||||
security.name = provider_security[:name]
|
security.name = provider_security[:name]
|
||||||
security.country_code = provider_security[:country_code]
|
security.country_code = provider_security[:country_code]
|
||||||
security.logo_url = provider_security[:logo_url]
|
security.logo_url = provider_security[:logo_url]
|
||||||
|
|
41
test/models/security_test.rb
Normal file
41
test/models/security_test.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class SecurityTest < ActiveSupport::TestCase
|
||||||
|
# Below has 3 example scenarios:
|
||||||
|
# 1. Original ticker
|
||||||
|
# 2. Duplicate ticker on a different exchange (different market price)
|
||||||
|
# 3. "Offline" version of the same ticker (for users not connected to a provider)
|
||||||
|
test "can have duplicate tickers if exchange is different" do
|
||||||
|
original = Security.create!(ticker: "TEST", exchange_operating_mic: "XNAS")
|
||||||
|
duplicate = Security.create!(ticker: "TEST", exchange_operating_mic: "CBOE")
|
||||||
|
offline = Security.create!(ticker: "TEST", exchange_operating_mic: nil)
|
||||||
|
|
||||||
|
assert original.valid?
|
||||||
|
assert duplicate.valid?
|
||||||
|
assert offline.valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cannot have duplicate tickers if exchange is the same" do
|
||||||
|
original = Security.create!(ticker: "TEST", exchange_operating_mic: "XNAS")
|
||||||
|
duplicate = Security.new(ticker: "TEST", exchange_operating_mic: "XNAS")
|
||||||
|
|
||||||
|
assert_not duplicate.valid?
|
||||||
|
assert_equal [ "has already been taken" ], duplicate.errors[:ticker]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cannot have duplicate tickers if exchange is nil" do
|
||||||
|
original = Security.create!(ticker: "TEST", exchange_operating_mic: nil)
|
||||||
|
duplicate = Security.new(ticker: "TEST", exchange_operating_mic: nil)
|
||||||
|
|
||||||
|
assert_not duplicate.valid?
|
||||||
|
assert_equal [ "has already been taken" ], duplicate.errors[:ticker]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "casing is ignored when checking for duplicates" do
|
||||||
|
original = Security.create!(ticker: "TEST", exchange_operating_mic: "XNAS")
|
||||||
|
duplicate = Security.new(ticker: "tEst", exchange_operating_mic: "xNaS")
|
||||||
|
|
||||||
|
assert_not duplicate.valid?
|
||||||
|
assert_equal [ "has already been taken" ], duplicate.errors[:ticker]
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue