diff --git a/app/models/rule/condition_filter.rb b/app/models/rule/condition_filter.rb index 6fa463a50..ad0574386 100644 --- a/app/models/rule/condition_filter.rb +++ b/app/models/rule/condition_filter.rb @@ -72,10 +72,12 @@ class Rule::ConditionFilter "#{field} #{sanitize_operator(operator)}" ) else - sanitized_value = operator == "like" ? "%#{ActiveRecord::Base.sanitize_sql_like(value)}%" : value + normalized_value = normalize_value(value) + normalized_field = normalize_field(field) + sanitized_value = operator == "like" ? "%#{ActiveRecord::Base.sanitize_sql_like(normalized_value)}%" : normalized_value ActiveRecord::Base.sanitize_sql_for_conditions([ - "#{field} #{sanitize_operator(operator)} ?", + "#{normalized_field} #{sanitize_operator(operator)} ?", sanitized_value ]) end @@ -93,4 +95,16 @@ class Rule::ConditionFilter operator end end + + def normalize_value(value) + return value unless type == "text" + + value.to_s.gsub(/\s+/, " ").strip + end + + def normalize_field(field) + return field unless type == "text" + + "BTRIM(REGEXP_REPLACE(#{field}, '[[:space:]]+', ' ', 'g'))" + end end diff --git a/test/models/rule_test.rb b/test/models/rule_test.rb index 731199fab..408cb8cca 100644 --- a/test/models/rule_test.rb +++ b/test/models/rule_test.rb @@ -94,6 +94,30 @@ class RuleTest < ActiveSupport::TestCase assert_not transaction_entry.excluded, "Transaction should not be excluded when attribute is locked" end + test "transaction name rules normalize whitespace in comparisons" do + transaction_entry = create_transaction( + date: Date.current, + account: @account, + name: "Company - Mobile", + amount: 80 + ) + + rule = Rule.create!( + family: @family, + resource_type: "transaction", + effective_date: 1.day.ago.to_date, + conditions: [ Rule::Condition.new(condition_type: "transaction_name", operator: "like", value: "Company - Mobile") ], + actions: [ Rule::Action.new(action_type: "set_transaction_category", value: @groceries_category.id) ] + ) + + assert_equal 1, rule.affected_resource_count + + rule.apply + transaction_entry.reload + + assert_equal @groceries_category, transaction_entry.transaction.category + end + # Artificial limitation put in place to prevent users from creating overly complex rules # Rules should be shallow and wide test "no nested compound conditions" do