mirror of
https://github.com/we-promise/sure.git
synced 2026-08-06 00:52:16 +00:00
* Add support for trading212 integration in investments sync * Respect Trading 212 history rate limits * Replace hex colors with design tokens * Changed withdrawal to withdraw * Address PR review feedback for Trading 212 integration - Replace all Rails.logger calls with DebugLogEntry.capture across syncer, importer, processor, provider, controller, and unlinking - Fix destroy action to surface unlink errors via DebugLogEntry - Replace hardcoded 'GBP' currency fallback with Current.family.currency - Replace hardcoded English status strings in Syncer with i18n - Add config/initializers/trading212.rb with DEBUG_RAW ENV toggle - Add comprehensive test coverage: item, account, data_helpers, holdings_processor, activities_processor, importer, syncer, and controller tests - Rewrite syncer and controller tests to match actual interfaces * Fix tests and bugs found during local test execution - Fix standard_ticker: empty string caused nil.upcase via [].first - Fix parse_date: DateTime < Date, so when Date caught DateTime first - Fix ActivitiesProcessor/HoldingsProcessor: missing instruments_map in DataHelpers caused NameError when processing dividends - Fix test: security fixture ticker collision with test data - Fix test: sync_status_summary i18n matching in assertions - Fix test: trading212_provider ConfigurationError test path - Fix test: controller invalid params needs Turbo-Frame header - Fix test: syncer test uses stubs instead of strict expects 116 tests, 257 assertions, 0 failures, 0 errors * Keep raw provider response bodies out of exception messages. * Fix Secrets leak into page HTML, prevents the API key/secret from appearing in the HTML source while keeping the "leave blank to keep existing" UX. * Address review findings: env gate, sync test, uniqueness test, destroy flow - Gate TRADING212_DEBUG_RAW behind Rails.env.local? so staging/production cannot accidentally enable raw payload logging - Assert SyncJob enqueue in sync controller test, not just redirect - Fix cross-item uniqueness test to actually use two different items - Remove rescue in destroy so unlink failures stop the flow (matching Brex/Akahu pattern) instead of proceeding to destroy_later silently * Add Trading212 tables to db/schema.rb for CI test database CI runs db:test:prepare which loads db/schema.rb, not migrations. Without these table definitions, fixture loading fails with PG::UndefinedTable: relation "trading212_accounts" does not exist. * Removed lint complaint * Register Trading212 in ProviderConnectionStatus::PROVIDERS Fixes CI failure: test_provider_registry_covers_syncable_family_provider_item_associations * Fixed bad merge --------- Signed-off-by: jdcdp <47483528+jdcdp@users.noreply.github.com> Co-authored-by: jdcdp <jdcdp@cdm4.net>
81 lines
2.5 KiB
Ruby
81 lines
2.5 KiB
Ruby
module Trading212Account::DataHelpers
|
|
extend ActiveSupport::Concern
|
|
|
|
private
|
|
|
|
def instruments_map
|
|
@trading212_account&.instruments_map || {}
|
|
end
|
|
|
|
def parse_decimal(value)
|
|
return nil if value.nil?
|
|
|
|
normalized = value.is_a?(String) ? value.strip : value.to_s
|
|
return nil if normalized.blank?
|
|
|
|
BigDecimal(normalized)
|
|
rescue ArgumentError
|
|
nil
|
|
end
|
|
|
|
def parse_date(value)
|
|
return nil if value.blank?
|
|
|
|
case value
|
|
when DateTime, Time, ActiveSupport::TimeWithZone
|
|
value.to_date
|
|
when Date
|
|
value
|
|
else
|
|
Time.zone.parse(value.to_s)&.to_date || Date.parse(value.to_s)
|
|
end
|
|
rescue ArgumentError, TypeError
|
|
nil
|
|
end
|
|
|
|
# T212 tickers look like "AAPL_US_EQ". Derive a standard ticker by taking
|
|
# the first segment, which is the instrument symbol on its primary exchange.
|
|
def standard_ticker(t212_ticker)
|
|
t212_ticker.to_s.split("_").first&.upcase || ""
|
|
end
|
|
|
|
def resolve_security_for_ticker(t212_ticker)
|
|
instrument = instruments_map[t212_ticker]
|
|
|
|
if instrument
|
|
resolve_security_from_instrument(instrument, t212_ticker)
|
|
else
|
|
ticker = standard_ticker(t212_ticker)
|
|
Security.find_by(ticker: ticker) || Security.create!(ticker: ticker, name: ticker)
|
|
end
|
|
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
ticker = standard_ticker(t212_ticker)
|
|
Security.find_by(ticker: ticker)
|
|
end
|
|
|
|
def resolve_security_from_instrument(instrument, t212_ticker)
|
|
ticker = standard_ticker(t212_ticker)
|
|
name = instrument["shortName"].presence || ticker
|
|
|
|
Security.find_by(ticker: ticker) ||
|
|
Security.create!(ticker: ticker, name: name)
|
|
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
Security.find_by(ticker: ticker)
|
|
end
|
|
|
|
# Resolve (or create) a Security when we already have isin/ticker/name in hand,
|
|
# e.g. from the nested instrument object in positions and orders responses.
|
|
# Note: the securities table has no isin column; isin is accepted but unused.
|
|
def resolve_security_direct(isin, ticker, name)
|
|
Security.find_by(ticker: ticker) ||
|
|
Security.create!(ticker: ticker, name: name)
|
|
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
Security.find_by(ticker: ticker)
|
|
end
|
|
|
|
def instrument_currency(t212_ticker)
|
|
instrument = instruments_map[t212_ticker]
|
|
instrument&.dig("currencyCode").presence || currency
|
|
end
|
|
end
|