From 81d604f3d4c8a9b1b61bc30cf1d4829aaa0fac26 Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Mon, 18 Nov 2024 15:50:47 -0500 Subject: [PATCH] Fix transfers and form currencies (#1477) --- app/controllers/account/trades_controller.rb | 5 ++++- app/controllers/account/transfers_controller.rb | 3 +-- app/controllers/account/valuations_controller.rb | 5 ++++- app/models/account/entry_builder.rb | 1 + app/models/account/transaction_builder.rb | 5 +++-- app/models/account/transfer.rb | 14 +++++++++++--- app/views/account/trades/_form.html.erb | 5 +++-- app/views/account/transfers/_form.html.erb | 6 +++--- app/views/account/valuations/_form.html.erb | 2 +- app/views/shared/_money_field.html.erb | 6 ++++-- test/controllers/account/trades_controller_test.rb | 8 ++++++-- 11 files changed, 41 insertions(+), 19 deletions(-) diff --git a/app/controllers/account/trades_controller.rb b/app/controllers/account/trades_controller.rb index e0897c9f..f57c3089 100644 --- a/app/controllers/account/trades_controller.rb +++ b/app/controllers/account/trades_controller.rb @@ -5,7 +5,10 @@ class Account::TradesController < ApplicationController before_action :set_entry, only: :update def new - @entry = @account.entries.account_trades.new(entryable_attributes: {}) + @entry = @account.entries.account_trades.new( + currency: @account.currency, + entryable_attributes: {} + ) end def index diff --git a/app/controllers/account/transfers_controller.rb b/app/controllers/account/transfers_controller.rb index cdcca52d..0aaac5c2 100644 --- a/app/controllers/account/transfers_controller.rb +++ b/app/controllers/account/transfers_controller.rb @@ -16,8 +16,7 @@ class Account::TransfersController < ApplicationController @transfer = Account::Transfer.build_from_accounts from_account, to_account, \ date: transfer_params[:date], - amount: transfer_params[:amount].to_d, - currency: transfer_params[:currency] + amount: transfer_params[:amount].to_d if @transfer.save @transfer.entries.each(&:sync_account_later) diff --git a/app/controllers/account/valuations_controller.rb b/app/controllers/account/valuations_controller.rb index 577dfbf4..35b83b90 100644 --- a/app/controllers/account/valuations_controller.rb +++ b/app/controllers/account/valuations_controller.rb @@ -4,7 +4,10 @@ class Account::ValuationsController < ApplicationController before_action :set_account def new - @entry = @account.entries.account_valuations.new(entryable_attributes: {}) + @entry = @account.entries.account_valuations.new( + currency: @account.currency, + entryable_attributes: {} + ) end def create diff --git a/app/models/account/entry_builder.rb b/app/models/account/entry_builder.rb index 0cfa3189..189acfdd 100644 --- a/app/models/account/entry_builder.rb +++ b/app/models/account/entry_builder.rb @@ -40,6 +40,7 @@ class Account::EntryBuilder date: date, amount: amount, account: account, + currency: currency, transfer_account_id: transfer_account_id end end diff --git a/app/models/account/transaction_builder.rb b/app/models/account/transaction_builder.rb index b6968443..6c87d6a4 100644 --- a/app/models/account/transaction_builder.rb +++ b/app/models/account/transaction_builder.rb @@ -3,7 +3,7 @@ class Account::TransactionBuilder TYPES = %w[income expense interest transfer_in transfer_out].freeze - attr_accessor :type, :amount, :date, :account, :transfer_account_id + attr_accessor :type, :amount, :date, :account, :currency, :transfer_account_id validates :type, :amount, :date, presence: true validates :type, inclusion: { in: TYPES } @@ -45,8 +45,9 @@ class Account::TransactionBuilder def build_entry(account_id, amount, marked_as_transfer: false) Account::Entry.new \ account_id: account_id, + name: marked_as_transfer ? (amount < 0 ? "Deposit" : "Withdrawal") : "Interest", amount: amount, - currency: account.currency, + currency: currency, date: date, marked_as_transfer: marked_as_transfer, entryable: Account::Transaction.new diff --git a/app/models/account/transfer.rb b/app/models/account/transfer.rb index ec464003..b919c4b7 100644 --- a/app/models/account/transfer.rb +++ b/app/models/account/transfer.rb @@ -49,7 +49,7 @@ class Account::Transfer < ApplicationRecord end class << self - def build_from_accounts(from_account, to_account, date:, amount:, currency:) + def build_from_accounts(from_account, to_account, date:, amount:) outflow = from_account.entries.build \ amount: amount.abs, currency: from_account.currency, @@ -58,9 +58,17 @@ class Account::Transfer < ApplicationRecord marked_as_transfer: true, entryable: Account::Transaction.new + # Attempt to convert the amount to the to_account's currency. If the conversion fails, + # use the original amount. + converted_amount = begin + Money.new(amount.abs, from_account.currency).exchange_to(to_account.currency) + rescue Money::ConversionError + Money.new(amount.abs, from_account.currency) + end + inflow = to_account.entries.build \ - amount: amount.abs * -1, - currency: from_account.currency, + amount: converted_amount.amount * -1, + currency: converted_amount.currency.iso_code, date: date, name: "Transfer from #{from_account.name}", marked_as_transfer: true, diff --git a/app/views/account/trades/_form.html.erb b/app/views/account/trades/_form.html.erb index 41180a20..a3330927 100644 --- a/app/views/account/trades/_form.html.erb +++ b/app/views/account/trades/_form.html.erb @@ -1,6 +1,7 @@ <%# locals: (entry:) %> <%= styled_form_with data: { turbo_frame: "_top", controller: "trade-form" }, + model: entry, scope: :account_entry, url: account_trades_path(entry.account) do |form| %>
@@ -15,7 +16,7 @@ <%= form.date_field :date, label: true, value: Date.today %>
- <%= form.money_field :price, label: t(".price"), disable_currency: true %> + <%= form.money_field :price, label: t(".price"), currency_value_override: "USD", disable_currency: true %>
diff --git a/app/views/account/transfers/_form.html.erb b/app/views/account/transfers/_form.html.erb index 0ce7566a..2d9e5741 100644 --- a/app/views/account/transfers/_form.html.erb +++ b/app/views/account/transfers/_form.html.erb @@ -1,4 +1,4 @@ -<%= styled_form_with model: transfer, class: "space-y-4", data: { turbo_frame: "_top" } do |f| %> +<%= styled_form_with model: transfer, class: "space-y-4", data: { turbo_frame: "_top", controller: "transfer-form" } do |f| %> <% if transfer.errors.present? %>
<%= lucide_icon "circle-alert", class: "w-5 h-5" %> @@ -28,8 +28,8 @@
<%= f.collection_select :from_account_id, Current.family.accounts.manual.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".from") }, required: true %> <%= f.collection_select :to_account_id, Current.family.accounts.manual.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".to") }, required: true %> - <%= f.money_field :amount, label: t(".amount"), required: true, hide_currency: true %> - <%= f.date_field :date, value: transfer.date, label: t(".date"), required: true, max: Date.current %> + <%= f.number_field :amount, label: t(".amount"), required: true, min: 0, placeholder: "100", step: 0.00000001 %> + <%= f.date_field :date, value: transfer.date || Date.today, label: t(".date"), required: true, max: Date.current %>
diff --git a/app/views/account/valuations/_form.html.erb b/app/views/account/valuations/_form.html.erb index a84d2ae5..0ceef3c3 100644 --- a/app/views/account/valuations/_form.html.erb +++ b/app/views/account/valuations/_form.html.erb @@ -6,7 +6,7 @@ data: { turbo: false } do |form| %>
<%= form.date_field :date, label: true, required: true, value: Date.today, min: Account::Entry.min_supported_date, max: Date.today %> - <%= form.money_field :amount, label: t(".amount"), required: true, default_currency: Current.family.currency %> + <%= form.money_field :amount, label: t(".amount"), required: true %>
<%= form.submit t(".submit") %> diff --git a/app/views/shared/_money_field.html.erb b/app/views/shared/_money_field.html.erb index 0bbbc0a7..4a3f8001 100644 --- a/app/views/shared/_money_field.html.erb +++ b/app/views/shared/_money_field.html.erb @@ -1,6 +1,8 @@ <%# locals: (form:, amount_method:, currency_method:, **options) %> -<% currency_value = if form.object && form.object.respond_to?(currency_method) +<% currency_value = if options[:currency_value_override].present? + options[:currency_value_override] + elsif form.object && form.object.respond_to?(currency_method) form.object.public_send(currency_method) end currency = Money::Currency.new(currency_value || options[:default_currency] || "USD") %> @@ -44,7 +46,7 @@
<%= form.select currency_method, currencies_for_select.map(&:iso_code), - { inline: true }, + { inline: true, selected: currency_value }, { class: "w-fit pr-5 disabled:text-gray-400 form-field__input", disabled: options[:disable_currency], diff --git a/test/controllers/account/trades_controller_test.rb b/test/controllers/account/trades_controller_test.rb index 43f20720..eaa4f4c5 100644 --- a/test/controllers/account/trades_controller_test.rb +++ b/test/controllers/account/trades_controller_test.rb @@ -27,6 +27,7 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest type: "transfer_in", date: Date.current, amount: 10, + currency: "USD", transfer_account_id: from_account.id } } @@ -46,6 +47,7 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest type: "transfer_out", date: Date.current, amount: 10, + currency: "USD", transfer_account_id: to_account.id } } @@ -62,7 +64,8 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest account_entry: { type: "transfer_out", date: Date.current, - amount: 10 + amount: 10, + currency: "USD" } } end @@ -80,7 +83,8 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest account_entry: { type: "interest", date: Date.current, - amount: 10 + amount: 10, + currency: "USD" } } end