2025-04-01 20:17:57 -04:00
|
|
|
class Rule::Condition < ApplicationRecord
|
2025-04-02 11:36:38 -04:00
|
|
|
UnsupportedOperatorError = Class.new(StandardError)
|
|
|
|
UnsupportedConditionTypeError = Class.new(StandardError)
|
2025-04-01 20:17:57 -04:00
|
|
|
|
2025-04-02 11:36:38 -04:00
|
|
|
OPERATORS = [ "and", "or", "like", ">", ">=", "<", "<=", "=" ]
|
|
|
|
|
|
|
|
belongs_to :rule, optional: -> { where.not(parent_id: nil) }
|
2025-04-01 20:17:57 -04:00
|
|
|
belongs_to :parent, class_name: "Rule::Condition", optional: true
|
2025-04-02 11:36:38 -04:00
|
|
|
has_many :sub_conditions, class_name: "Rule::Condition", foreign_key: :parent_id, dependent: :destroy
|
2025-04-01 20:17:57 -04:00
|
|
|
|
|
|
|
validates :operator, inclusion: { in: OPERATORS }, allow_nil: true
|
2025-04-02 11:36:38 -04:00
|
|
|
validates :condition_type, presence: true
|
|
|
|
|
|
|
|
def apply(resource_scope)
|
|
|
|
filtered_scope = resource_scope
|
|
|
|
|
|
|
|
case condition_type
|
|
|
|
when "compound"
|
2025-04-02 12:47:07 -04:00
|
|
|
filtered_scope = build_compound_scope(filtered_scope)
|
2025-04-02 11:36:38 -04:00
|
|
|
when "transaction_name"
|
2025-04-02 12:47:07 -04:00
|
|
|
filtered_scope = filtered_scope.where("account_entries.name #{Arel.sql(sanitize_operator(operator))} ?", value)
|
2025-04-02 11:36:38 -04:00
|
|
|
when "transaction_amount"
|
2025-04-02 12:47:07 -04:00
|
|
|
filtered_scope = filtered_scope.where("account_entries.amount #{Arel.sql(sanitize_operator(operator))} ?", value.to_d)
|
2025-04-02 11:36:38 -04:00
|
|
|
when "transaction_merchant"
|
|
|
|
filtered_scope = filtered_scope.left_joins(:merchant).where(merchant: { name: value })
|
|
|
|
else
|
|
|
|
raise UnsupportedConditionTypeError, "Unsupported condition type: #{condition_type}"
|
|
|
|
end
|
|
|
|
|
|
|
|
filtered_scope
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def sanitize_operator(operator)
|
|
|
|
raise UnsupportedOperatorError, "Unsupported operator: #{operator}" unless OPERATORS.include?(operator)
|
|
|
|
operator
|
|
|
|
end
|
2025-04-02 12:47:07 -04:00
|
|
|
|
|
|
|
def build_compound_scope(filtered_scope)
|
|
|
|
if operator == "or"
|
|
|
|
combined_scope = nil
|
|
|
|
|
|
|
|
sub_conditions.each do |sub_condition|
|
|
|
|
sub_scope = sub_condition.apply(filtered_scope)
|
|
|
|
|
|
|
|
if combined_scope.nil?
|
|
|
|
combined_scope = sub_scope
|
|
|
|
else
|
|
|
|
combined_scope = combined_scope.or(sub_scope)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
filtered_scope = combined_scope || filtered_scope
|
|
|
|
else
|
|
|
|
sub_conditions.each do |sub_condition|
|
|
|
|
filtered_scope = sub_condition.apply(filtered_scope)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
filtered_scope
|
|
|
|
end
|
2025-04-01 20:17:57 -04:00
|
|
|
end
|