mirror of
https://github.com/we-promise/sure.git
synced 2026-06-01 08:49:01 +00:00
Localize investment activity labels and improve transaction processing
- Replaced hardcoded activity labels with `I18n` translations for better localization. - Updated `transactions` views to display localized labels dynamically. - Fixed `InvestmentActivityDetector` to enhance dividend detection. - Refined `Account::ProviderImportAdapter` to prevent unnecessary updates and ensure transactional consistency. - Improved error handling and feedback in rake tasks for invalid arguments.
This commit is contained in:
@@ -119,11 +119,12 @@ class Account::ProviderImportAdapter
|
||||
# Set investment activity label if provided and not already set
|
||||
if investment_activity_label.present? && entry.entryable.is_a?(Transaction)
|
||||
if entry.transaction.investment_activity_label.blank?
|
||||
entry.transaction.update!(investment_activity_label: investment_activity_label)
|
||||
entry.transaction.assign_attributes(investment_activity_label: investment_activity_label)
|
||||
end
|
||||
end
|
||||
|
||||
entry.save!
|
||||
entry.transaction.save! if entry.transaction.changed?
|
||||
|
||||
# AFTER save: For NEW posted transactions, check for fuzzy matches to SUGGEST (not auto-claim)
|
||||
# This handles tip adjustments where auto-matching is too risky
|
||||
|
||||
@@ -119,8 +119,9 @@ class InvestmentActivityDetector
|
||||
end
|
||||
|
||||
# Check for dividend patterns
|
||||
if description == "CASH" || description.include?("DIVIDEND") ||
|
||||
description.include?("DISTRIBUTION")
|
||||
# "CASH" alone typically indicates dividend payout in brokerage feeds (only for inflows)
|
||||
if description.include?("DIVIDEND") || description.include?("DISTRIBUTION") ||
|
||||
(description == "CASH" && amount < 0)
|
||||
return "Dividend"
|
||||
end
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<%# Investment activity label badge %>
|
||||
<% if transaction.investment_activity_label.present? %>
|
||||
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-alpha-black-50 text-secondary" title="<%= t("transactions.transaction.activity_type_tooltip") %>">
|
||||
<%= transaction.investment_activity_label %>
|
||||
<%= t("transactions.activity_labels.#{transaction.investment_activity_label.parameterize(separator: '_')}") %>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
|
||||
<%= ef.select :investment_activity_label,
|
||||
options_for_select(
|
||||
[["—", nil]] + Transaction::ACTIVITY_LABELS.map { |l| [l, l] },
|
||||
[["—", nil]] + Transaction::ACTIVITY_LABELS.map { |l| [t("transactions.activity_labels.#{l.parameterize(separator: '_')}"), l] },
|
||||
@entry.entryable.investment_activity_label
|
||||
),
|
||||
{ label: false },
|
||||
@@ -260,8 +260,8 @@
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<div class="flex cursor-pointer items-center gap-4 justify-between">
|
||||
<div class="text-sm space-y-1">
|
||||
<h4 class="text-primary">One-time <%= @entry.amount.negative? ? "Income" : "Expense" %></h4>
|
||||
<p class="text-secondary">One-time transactions will be excluded from certain budgeting calculations and reports to help you see what's really important.</p>
|
||||
<h4 class="text-primary"><%= t(".one_time_title", type: @entry.amount.negative? ? t("transactions.form.income") : t("transactions.form.expense")) %></h4>
|
||||
<p class="text-secondary"><%= t(".one_time_description") %></p>
|
||||
</div>
|
||||
|
||||
<%= ef.toggle :kind, {
|
||||
|
||||
@@ -38,6 +38,22 @@ en:
|
||||
exclude_from_cashflow_description_investment: Hide from income/expense reports and Sankey chart. Use for internal investment activity like fund swaps, reinvestments, or money market sweeps.
|
||||
activity_type: Activity Type
|
||||
activity_type_description: Type of investment activity (Buy, Sell, Dividend, etc.). Auto-detected or set manually.
|
||||
one_time_title: One-time %{type}
|
||||
one_time_description: One-time transactions will be excluded from certain budgeting calculations and reports to help you see what's really important.
|
||||
activity_labels:
|
||||
buy: Buy
|
||||
sell: Sell
|
||||
sweep_in: Sweep In
|
||||
sweep_out: Sweep Out
|
||||
dividend: Dividend
|
||||
reinvestment: Reinvestment
|
||||
interest: Interest
|
||||
fee: Fee
|
||||
transfer: Transfer
|
||||
contribution: Contribution
|
||||
withdrawal: Withdrawal
|
||||
exchange: Exchange
|
||||
other: Other
|
||||
mark_recurring: Mark as Recurring
|
||||
mark_recurring_subtitle: Track this as a recurring transaction. Amount variance is automatically calculated from past 6 months of similar transactions.
|
||||
mark_recurring_title: Recurring Transaction
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace :sure do
|
||||
# Find transactions (optionally include already-labeled if force=true)
|
||||
entries = account.entries
|
||||
.joins("INNER JOIN transactions ON transactions.id = entries.entryable_id AND entries.entryable_type = 'Transaction'")
|
||||
.includes(:entryable)
|
||||
|
||||
unless force
|
||||
entries = entries.where("transactions.investment_activity_label IS NULL OR transactions.investment_activity_label = ''")
|
||||
@@ -160,8 +161,11 @@ namespace :sure do
|
||||
true
|
||||
elsif %w[1 true yes y].include?(dry_raw)
|
||||
true
|
||||
else
|
||||
elsif %w[0 false no n].include?(dry_raw)
|
||||
false
|
||||
else
|
||||
puts({ ok: false, error: "invalid_argument", message: "dry_run must be one of: true/yes/1 or false/no/0" }.to_json)
|
||||
exit 1
|
||||
end
|
||||
|
||||
account = Account.find(account_id)
|
||||
|
||||
Reference in New Issue
Block a user