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