Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve original transaction names when enriching #1556

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/controllers/concerns/entryable_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def prepared_entry_params

def entry_params
params.require(:account_entry).permit(
:account_id, :name, :date, :amount, :currency, :excluded, :notes, :nature,
:account_id, :name, :enriched_name, :date, :amount, :currency, :excluded, :notes, :nature,
entryable_attributes: self.class.permitted_entryable_attributes
)
end
Expand Down
1 change: 1 addition & 0 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def update_balance!(balance)
else
entries.create! \
date: Date.current,
name: "Balance update",
amount: balance,
currency: currency,
entryable: Account::Valuation.new
Expand Down
2 changes: 1 addition & 1 deletion app/models/account/data_enricher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def enrich_transactions
category.save! if category.present?
entry.update!(
enriched_at: Time.current,
name: entry.enriched_at.nil? && info.name ? info.name : entry.name,
enriched_name: info.name,
entryable_attributes: entryable_attributes
)
end
Expand Down
14 changes: 5 additions & 9 deletions app/models/account/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Account::Entry < ApplicationRecord
delegated_type :entryable, types: Account::Entryable::TYPES, dependent: :destroy
accepts_nested_attributes_for :entryable

validates :date, :amount, :currency, presence: true
validates :date, :name, :amount, :currency, presence: true
validates :date, uniqueness: { scope: [ :account_id, :entryable_type ] }, if: -> { account_valuation? }
validates :date, comparison: { greater_than: -> { min_supported_date } }

Expand Down Expand Up @@ -47,14 +47,6 @@ def sync_account_later
account.sync_later(start_date: sync_start_date)
end

def inflow?
amount <= 0 && account_transaction?
end

def outflow?
amount > 0 && account_transaction?
end

def entryable_name_short
entryable_type.demodulize.underscore
end
Expand All @@ -63,6 +55,10 @@ def balance_trend(entries, balances)
Account::BalanceTrendCalculator.new(self, entries, balances).trend
end

def display_name
enriched_name.presence || name
end

class << self
# arbitrary cutoff date to avoid expensive sync operations
def min_supported_date
Expand Down
3 changes: 2 additions & 1 deletion app/models/account/syncer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def run
update_account_info(balances, holdings) unless account.plaid_account_id.present?
convert_records_to_family_currency(balances, holdings) unless account.currency == account.family.currency

if account.family.data_enrichment_enabled?
# Enrich if user opted in or if we're syncing transactions from a Plaid account
if account.family.data_enrichment_enabled? || account.plaid_account_id.present?
account.enrich_data_later
else
Rails.logger.info("Data enrichment is disabled, skipping enrichment for account #{account.id}")
Expand Down
5 changes: 0 additions & 5 deletions app/models/account/trade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ def buy?
qty > 0
end

def name
prefix = sell? ? "Sell " : "Buy "
prefix + "#{qty.abs} shares of #{security.ticker}"
end

def unrealized_gain_loss
return nil if sell?
current_price = security.current_price
Expand Down
4 changes: 4 additions & 0 deletions app/models/account/trade_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ def buildable
end

def build_trade
prefix = type == "sell" ? "Sell " : "Buy "
trade_name = prefix + "#{qty.to_i.abs} shares of #{security.ticker}"

account.entries.new(
name: trade_name,
date: date,
amount: signed_amount,
currency: currency,
Expand Down
4 changes: 0 additions & 4 deletions app/models/account/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ def searchable_keys
end
end

def name
entry.name || (entry.amount.positive? ? "Expense" : "Income")
end

def eod_balance
entry.amount_money
end
Expand Down
4 changes: 2 additions & 2 deletions app/models/account/transfer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def to_account
end

def inflow_transaction
entries.find { |e| e.inflow? }
entries.find { |e| e.amount.negative? }
end

def outflow_transaction
entries.find { |e| e.outflow? }
entries.find { |e| e.amount.positive? }
end

def update_entries!(params)
Expand Down
4 changes: 0 additions & 4 deletions app/models/account/valuation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@ def requires_search?(_params)
false
end
end

def name
"Balance update"
end
end
1 change: 1 addition & 0 deletions app/models/demo/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ def create_valuation!(account, date, amount)
date: date,
amount: amount,
currency: "USD",
name: "Balance update",
entryable: Account::Valuation.new
end

Expand Down
6 changes: 3 additions & 3 deletions app/views/account/trades/_trade.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
<div class="max-w-full">
<%= tag.div class: ["flex items-center gap-2"] do %>
<div class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gray-600/5 text-gray-600">
<%= trade.name.first.upcase %>
<%= entry.display_name.first.upcase %>
</div>

<div class="truncate">
<% if entry.new_record? %>
<%= content_tag :p, trade.name %>
<%= content_tag :p, entry.display_name %>
<% else %>
<%= link_to trade.name,
<%= link_to entry.display_name,
account_entry_path(entry),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>
Expand Down
14 changes: 7 additions & 7 deletions app/views/account/transactions/_transaction.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@

<div class="max-w-full">
<%= content_tag :div, class: ["flex items-center gap-2"] do %>
<% if entry.account_transaction.merchant&.icon_url %>
<%= image_tag entry.account_transaction.merchant.icon_url, class: "w-6 h-6 rounded-full" %>
<% if transaction.merchant&.icon_url %>
<%= image_tag transaction.merchant.icon_url, class: "w-6 h-6 rounded-full" %>
<% else %>
<%= render "shared/circle_logo", name: transaction.name, size: "sm" %>
<%= render "shared/circle_logo", name: entry.display_name, size: "sm" %>
<% end %>

<div class="truncate">
<% if entry.new_record? %>
<%= content_tag :p, transaction.name %>
<%= content_tag :p, entry.display_name %>
<% else %>
<%= link_to transaction.name,
<%= link_to entry.display_name,
entry.transfer.present? ? account_transfer_path(entry.transfer) : account_entry_path(entry),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>
Expand All @@ -41,7 +41,7 @@
<% end %>

<div class="col-span-2">
<%= render "account/transfers/account_logos", transfer: entry.transfer, outflow: entry.outflow? %>
<%= render "account/transfers/account_logos", transfer: entry.transfer, outflow: entry.amount.positive? %>
</div>
<% else %>
<div class="flex items-center gap-1 col-span-2">
Expand All @@ -65,7 +65,7 @@
<div class="col-span-2 ml-auto">
<%= content_tag :p,
format_money(-entry.amount_money),
class: ["text-green-600": entry.inflow?] %>
class: ["text-green-600": entry.amount.negative?] %>
</div>

<% if balance_trend %>
Expand Down
3 changes: 2 additions & 1 deletion app/views/account/transactions/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
url: account_transaction_path(@entry),
class: "space-y-2",
data: { controller: "auto-submit-form" } do |f| %>
<%= f.text_field :name,

<%= f.text_field @entry.enriched_at.present? ? :enriched_name : :name,
label: t(".name_label"),
"data-auto-submit-form-target": "auto" %>

Expand Down
1 change: 1 addition & 0 deletions app/views/account/valuations/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<% end %>

<div class="space-y-3">
<%= form.hidden_field :name, value: "Balance update" %>
<%= form.date_field :date, label: true, required: true, value: Date.current, min: Account::Entry.min_supported_date, max: Date.current %>
<%= form.money_field :amount, label: t(".amount"), required: true %>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/views/account/valuations/_valuation.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

<div class="truncate text-gray-900">
<% if entry.new_record? %>
<%= content_tag :p, entry.entryable.name %>
<%= content_tag :p, entry.display_name %>
<% else %>
<%= link_to entry.entryable.name,
<%= link_to entry.display_name,
account_entry_path(entry),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>
Expand Down
48 changes: 24 additions & 24 deletions config/brakeman.ignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "5bfdb129316655dc4e02f3a599156660414a6562212a5f61057d376f6888f078",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/concerns/entryable_resource.rb",
"line": 122,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.require(:account_entry).permit(:account_id, :name, :enriched_name, :date, :amount, :currency, :excluded, :notes, :nature, :entryable_attributes => self.class.permitted_entryable_attributes)",
"render_path": null,
"location": {
"type": "method",
"class": "EntryableResource",
"method": "entry_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
Expand Down Expand Up @@ -80,29 +103,6 @@
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "f158202dcc66f2273ddea5e5296bad7146a50ca6667f49c77372b5b234542334",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/concerns/entryable_resource.rb",
"line": 122,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.require(:account_entry).permit(:account_id, :name, :date, :amount, :currency, :excluded, :notes, :nature, :entryable_attributes => self.class.permitted_entryable_attributes)",
"render_path": null,
"location": {
"type": "method",
"class": "EntryableResource",
"method": "entry_params"
},
"user_input": ":account_id",
"confidence": "High",
"cwe_id": [
915
],
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
Expand Down Expand Up @@ -138,6 +138,6 @@
"note": ""
}
],
"updated": "2024-11-27 15:33:53 -0500",
"updated": "2024-12-18 17:46:13 -0500",
"brakeman_version": "6.2.2"
}
37 changes: 37 additions & 0 deletions db/migrate/20241218132503_add_enriched_name_field.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class AddEnrichedNameField < ActiveRecord::Migration[7.2]
def change
add_column :account_entries, :enriched_name, :string

reversible do |dir|
dir.up do
execute <<-SQL
UPDATE account_entries ae
SET name = CASE ae.entryable_type
WHEN 'Account::Trade' THEN
CASE
WHEN EXISTS (
SELECT 1 FROM account_trades t
WHERE t.id = ae.entryable_id AND t.qty < 0
) THEN 'Sell trade'
ELSE 'Buy trade'
END
WHEN 'Account::Transaction' THEN
CASE
WHEN ae.amount > 0 THEN 'Expense'
ELSE 'Income'
END
WHEN 'Account::Valuation' THEN 'Balance update'
ELSE 'Unknown entry'
END
WHERE name IS NULL
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only updating "bad data" that currently exists

SQL

change_column_null :account_entries, :name, false
end

dir.down do
change_column_null :account_entries, :name, true
end
end
end
end
5 changes: 3 additions & 2 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/interfaces/accountable_resource_interface_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ module AccountableResourceInterfaceTest
end

test "updates account balance by editing existing valuation for today" do
@account.entries.create! date: Date.current, amount: 6000, currency: "USD", entryable: Account::Valuation.new
@account.entries.create! date: Date.current, amount: 6000, currency: "USD", name: "Balance update", entryable: Account::Valuation.new

assert_no_difference [ "Account::Entry.count", "Account::Valuation.count" ] do
patch account_url(@account), params: {
Expand Down
6 changes: 0 additions & 6 deletions test/models/account/entry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,4 @@ class Account::EntryTest < ActiveSupport::TestCase

assert_equal Money.new(-200), family.entries.income_total("USD")
end

# See: https://github.com/maybe-finance/maybe/wiki/vision#signage-of-money
test "transactions with negative amounts are inflows, positive amounts are outflows to an account" do
assert create_transaction(amount: -10).inflow?
assert create_transaction(amount: 10).outflow?
end
end
Loading
Loading