diff --git a/app/models/transfer.rb b/app/models/transfer.rb index 7d5c44947..3b60c1cfb 100644 --- a/app/models/transfer.rb +++ b/app/models/transfer.rb @@ -11,6 +11,7 @@ class Transfer < ApplicationRecord validate :transfer_has_opposite_amounts_or_fees validate :transfer_within_date_range validate :transfer_has_same_family + validate :fees_must_be_non_negative class << self def kind_for_account(account) @@ -148,14 +149,19 @@ class Transfer < ApplicationRecord inflow_amount = inflow_entry.amount outflow_amount = outflow_entry.amount + errors.add(:base, :opposite_amounts) unless inflow_amount.negative? && outflow_amount.positive? + if inflow_entry.currency == outflow_entry.currency total_fee = source_fee_amount.to_d + destination_fee_amount.to_d errors.add(:base, :opposite_amounts) if inflow_amount + outflow_amount != total_fee - else - errors.add(:base, :opposite_amounts) unless inflow_amount.negative? && outflow_amount.positive? end end + def fees_must_be_non_negative + errors.add(:source_fee_amount, :greater_than_or_equal_to, count: 0) if source_fee_amount.to_d.negative? + errors.add(:destination_fee_amount, :greater_than_or_equal_to, count: 0) if destination_fee_amount.to_d.negative? + end + def transfer_within_date_range return unless inflow_transaction&.entry && outflow_transaction&.entry diff --git a/app/views/transfers/show.html.erb b/app/views/transfers/show.html.erb index 6b4a82afd..5b713b54a 100644 --- a/app/views/transfers/show.html.erb +++ b/app/views/transfers/show.html.erb @@ -35,13 +35,13 @@
<%= t(".amount") %>
-
<%= format_money @transfer.outflow_transaction.entry.amount_money * -1 %>
+
<%= format_money @transfer.outflow_transaction.entry.amount_money * -1 %>
<% if @transfer.has_source_fee? %>
<%= t(".source_fee") %>
-
+
-<%= format_money Money.new(@transfer.source_fee_amount, @transfer.from_account.currency) %>
@@ -50,7 +50,7 @@ <% if @transfer.has_destination_fee? %>
<%= t(".destination_fee") %>
-
+
-<%= format_money Money.new(@transfer.destination_fee_amount, @transfer.to_account.currency) %>
@@ -71,7 +71,7 @@
<%= t(".amount") %>
-
+<%= format_money @transfer.inflow_transaction.entry.amount_money * -1 %>
+
+<%= format_money @transfer.inflow_transaction.entry.amount_money * -1 %>
@@ -115,7 +115,7 @@ <%= button_to t(".delete"), transfer_path(@transfer), method: :delete, - class: "rounded-lg px-3 py-2 whitespace-nowrap text-red-500 text-sm + class: "rounded-lg px-3 py-2 whitespace-nowrap text-destructive text-sm font-medium border border-secondary", data: { turbo_confirm: true, turbo_frame: "_top" } %> diff --git a/test/models/transfer_test.rb b/test/models/transfer_test.rb index f59293096..f2abc4df9 100644 --- a/test/models/transfer_test.rb +++ b/test/models/transfer_test.rb @@ -203,4 +203,47 @@ class TransferTest < ActiveSupport::TestCase transfer.update!(source_fee_amount: 3, destination_fee_amount: 2) assert_equal 5, transfer.total_fee end + + test "destination fee larger than amount inverts inflow sign and fails validation" do + outflow_entry = create_transaction(date: Date.current, account: accounts(:depository), amount: 100) + inflow_entry = create_transaction(date: Date.current, account: accounts(:credit_card), amount: 50) + + transfer = Transfer.new( + inflow_transaction: inflow_entry.transaction, + outflow_transaction: outflow_entry.transaction, + destination_fee_amount: 150 + ) + + # inflow amount (50) is positive, which means destination is also outflowing + assert transfer.invalid? + assert_equal "Must have opposite amounts", transfer.errors.full_messages.first + end + + test "negative source fee is rejected" do + outflow_entry = create_transaction(date: Date.current, account: accounts(:depository), amount: 500) + inflow_entry = create_transaction(date: Date.current, account: accounts(:credit_card), amount: -500) + + transfer = Transfer.new( + inflow_transaction: inflow_entry.transaction, + outflow_transaction: outflow_entry.transaction, + source_fee_amount: -5 + ) + + assert transfer.invalid? + assert_equal [ "Source fee amount must be greater than or equal to 0" ], transfer.errors.full_messages + end + + test "negative destination fee is rejected" do + outflow_entry = create_transaction(date: Date.current, account: accounts(:depository), amount: 500) + inflow_entry = create_transaction(date: Date.current, account: accounts(:credit_card), amount: -500) + + transfer = Transfer.new( + inflow_transaction: inflow_entry.transaction, + outflow_transaction: outflow_entry.transaction, + destination_fee_amount: -5 + ) + + assert transfer.invalid? + assert_equal [ "Destination fee amount must be greater than or equal to 0" ], transfer.errors.full_messages + end end