mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 06:21:23 +00:00
fix: Handle empty compound conditions on rules index (#965)
* fix: Handle empty compound conditions on rules index * fix: avoid contradictory rule condition summary on /rules * refactor: move rules condition display logic from view to model * fix: localize rule title fallback and preload conditions in rules index
This commit is contained in:
@@ -11,7 +11,7 @@ class RulesController < ApplicationController
|
||||
@sort_by = "name" unless allowed_columns.include?(@sort_by)
|
||||
@direction = "asc" unless [ "asc", "desc" ].include?(@direction)
|
||||
|
||||
@rules = Current.family.rules.order(@sort_by => @direction)
|
||||
@rules = Current.family.rules.includes(conditions: :sub_conditions).order(@sort_by => @direction)
|
||||
|
||||
# Fetch recent rule runs with pagination
|
||||
recent_runs_scope = RuleRun
|
||||
|
||||
@@ -86,14 +86,23 @@ class Rule < ApplicationRecord
|
||||
end
|
||||
|
||||
def primary_condition_title
|
||||
return "No conditions" if conditions.none?
|
||||
condition = displayed_condition
|
||||
return I18n.t("rules.no_condition") if condition.blank?
|
||||
|
||||
first_condition = conditions.first
|
||||
if first_condition.compound? && first_condition.sub_conditions.any?
|
||||
first_sub_condition = first_condition.sub_conditions.first
|
||||
"If #{first_sub_condition.filter.label.downcase} #{first_sub_condition.operator} #{first_sub_condition.value_display}"
|
||||
else
|
||||
"If #{first_condition.filter.label.downcase} #{first_condition.operator} #{first_condition.value_display}"
|
||||
"If #{condition.filter.label.downcase} #{condition.operator} #{condition.value_display}"
|
||||
end
|
||||
|
||||
def displayed_condition
|
||||
displayable_conditions.first
|
||||
end
|
||||
|
||||
def additional_displayable_conditions_count
|
||||
[ displayable_conditions.size - 1, 0 ].max
|
||||
end
|
||||
|
||||
def displayable_conditions
|
||||
conditions.filter_map do |condition|
|
||||
condition.compound? ? condition.sub_conditions.first : condition
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -6,20 +6,22 @@
|
||||
<h3 class="font-medium text-md"><%= rule.name %></h3>
|
||||
<% end %>
|
||||
<% if rule.conditions.any? %>
|
||||
<% displayed_condition = rule.displayed_condition %>
|
||||
<% additional_condition_count = rule.additional_displayable_conditions_count %>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<div class="flex items-center gap-1 text-secondary w-16 shrink-0">
|
||||
<span class="font-mono text-xs">IF</span>
|
||||
</div>
|
||||
<p class="flex items-center flex-wrap gap-1.5 m-0">
|
||||
<span class="px-2 py-1 border border-secondary rounded-full">
|
||||
<% if rule.conditions.first.compound? %>
|
||||
<%= rule.conditions.first.sub_conditions.first.filter.label %> <%= rule.conditions.first.sub_conditions.first.operator %> <%= rule.conditions.first.sub_conditions.first.value_display %>
|
||||
<% if displayed_condition.present? %>
|
||||
<%= displayed_condition.filter.label %> <%= displayed_condition.operator %> <%= displayed_condition.value_display %>
|
||||
<% else %>
|
||||
<%= rule.conditions.first.filter.label %> <%= rule.conditions.first.operator %> <%= rule.conditions.first.value_display %>
|
||||
<%= t("rules.no_condition") %>
|
||||
<% end %>
|
||||
</span>
|
||||
<% if rule.conditions.count > 1 %>
|
||||
and <%= rule.conditions.count - 1 %> more <%= rule.conditions.count - 1 == 1 ? "condition" : "conditions" %>
|
||||
<% if additional_condition_count.positive? %>
|
||||
and <%= additional_condition_count %> more <%= additional_condition_count == 1 ? "condition" : "conditions" %>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ ca:
|
||||
success: Totes les regles s'han posat a cua per a execució
|
||||
view_usage: Veure l'historial d'ús
|
||||
no_action: Sense acció
|
||||
no_condition: Sense condició
|
||||
recent_runs:
|
||||
columns:
|
||||
date_time: Data/Hora
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
de:
|
||||
rules:
|
||||
no_action: Keine Aktion
|
||||
no_condition: Keine Bedingung
|
||||
recent_runs:
|
||||
title: Letzte Ausführungen
|
||||
description: Zeige die Ausführungsgeschichte deiner Regeln einschließlich Erfolgs-/Fehlerstatus und Transaktionsanzahlen.
|
||||
@@ -22,4 +23,3 @@ de:
|
||||
pending: Ausstehend
|
||||
success: Erfolgreich
|
||||
failed: Fehlgeschlagen
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
en:
|
||||
rules:
|
||||
no_action: No Action
|
||||
no_condition: No condition
|
||||
actions:
|
||||
value_placeholder: Enter a value
|
||||
apply_all:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
es:
|
||||
rules:
|
||||
no_action: Sin acción
|
||||
no_condition: Sin condición
|
||||
recent_runs:
|
||||
title: Ejecuciones Recientes
|
||||
description: Ver el historial de ejecución de tus reglas incluyendo el estado de éxito/fallo y los conteos de transacciones.
|
||||
@@ -22,4 +23,3 @@ es:
|
||||
pending: Pendiente
|
||||
success: Éxito
|
||||
failed: Fallido
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
fr:
|
||||
rules:
|
||||
no_action: Aucune action
|
||||
no_condition: Aucune condition
|
||||
actions:
|
||||
value_placeholder: Entrez une valeur
|
||||
apply_all:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
nb:
|
||||
rules:
|
||||
no_action: Ingen handling
|
||||
no_condition: Ingen betingelse
|
||||
recent_runs:
|
||||
title: Siste Kjøringer
|
||||
description: Se kjøringsloggen for reglene dine inkludert suksess/feil-status og transaksjonsantall.
|
||||
@@ -22,4 +23,3 @@ nb:
|
||||
pending: Ventende
|
||||
success: Vellykket
|
||||
failed: Mislyktes
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
nl:
|
||||
rules:
|
||||
no_action: Geen actie
|
||||
no_condition: Geen voorwaarde
|
||||
actions:
|
||||
value_placeholder: Voer een waarde in
|
||||
apply_all:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
ro:
|
||||
rules:
|
||||
no_action: Nicio acțiune
|
||||
no_condition: Nicio condiție
|
||||
recent_runs:
|
||||
title: Rulări Recente
|
||||
description: Vezi istoricul de execuție al regulilor tale incluzând statusul de succes/eșec și numărul de tranzacții.
|
||||
@@ -22,4 +23,3 @@ ro:
|
||||
pending: În Așteptare
|
||||
success: Succes
|
||||
failed: Eșuat
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
tr:
|
||||
rules:
|
||||
no_action: İşlem yok
|
||||
no_condition: Koşul yok
|
||||
recent_runs:
|
||||
title: Son Çalıştırmalar
|
||||
description: Başarı/başarısızlık durumu ve işlem sayıları dahil olmak üzere kurallarınızın yürütme geçmişini görüntüleyin.
|
||||
@@ -22,4 +23,3 @@ tr:
|
||||
pending: Beklemede
|
||||
success: Başarılı
|
||||
failed: Başarısız
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
zh-CN:
|
||||
rules:
|
||||
no_action: 无操作
|
||||
no_condition: 无条件
|
||||
recent_runs:
|
||||
columns:
|
||||
date_time: 日期/时间
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
zh-TW:
|
||||
rules:
|
||||
no_action: 無動作
|
||||
no_condition: 無條件
|
||||
recent_runs:
|
||||
title: 最近執行紀錄
|
||||
description: 查看規則的執行歷史,包括成功/失敗狀態以及交易處理筆數。
|
||||
|
||||
@@ -180,6 +180,36 @@ class RulesControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_redirected_to rules_url
|
||||
end
|
||||
|
||||
test "index renders when rule has empty compound condition" do
|
||||
malformed_rule = @user.family.rules.build(resource_type: "transaction")
|
||||
malformed_rule.conditions.build(condition_type: "compound", operator: "and")
|
||||
malformed_rule.actions.build(action_type: "exclude_transaction")
|
||||
malformed_rule.save!
|
||||
|
||||
get rules_url
|
||||
|
||||
assert_response :success
|
||||
assert_includes response.body, I18n.t("rules.no_condition")
|
||||
end
|
||||
|
||||
test "index uses next valid condition when first compound condition is empty" do
|
||||
rule = @user.family.rules.build(resource_type: "transaction")
|
||||
rule.conditions.build(condition_type: "compound", operator: "and")
|
||||
rule.conditions.build(condition_type: "transaction_name", operator: "like", value: "edge-case-name")
|
||||
rule.actions.build(action_type: "exclude_transaction")
|
||||
rule.save!
|
||||
|
||||
get rules_url
|
||||
|
||||
assert_response :success
|
||||
|
||||
assert_select "##{ActionView::RecordIdentifier.dom_id(rule)}" do
|
||||
assert_select "span", text: /edge-case-name/
|
||||
assert_select "span", text: /#{Regexp.escape(I18n.t("rules.no_condition"))}/, count: 0
|
||||
assert_select "p", text: /and 1 more condition/, count: 0
|
||||
end
|
||||
end
|
||||
|
||||
test "should get confirm_all" do
|
||||
get confirm_all_rules_url
|
||||
assert_response :success
|
||||
|
||||
@@ -138,6 +138,40 @@ class RuleTest < ActiveSupport::TestCase
|
||||
assert_equal [ "Compound conditions cannot be nested" ], rule.errors.full_messages
|
||||
end
|
||||
|
||||
test "displayed_condition falls back to next valid condition when first compound condition is empty" do
|
||||
rule = Rule.new(
|
||||
family: @family,
|
||||
resource_type: "transaction",
|
||||
actions: [ Rule::Action.new(action_type: "exclude_transaction") ],
|
||||
conditions: [
|
||||
Rule::Condition.new(condition_type: "compound", operator: "and"),
|
||||
Rule::Condition.new(condition_type: "transaction_name", operator: "like", value: "starbucks")
|
||||
]
|
||||
)
|
||||
|
||||
displayed_condition = rule.displayed_condition
|
||||
|
||||
assert_not_nil displayed_condition
|
||||
assert_equal "transaction_name", displayed_condition.condition_type
|
||||
assert_equal "like", displayed_condition.operator
|
||||
assert_equal "starbucks", displayed_condition.value
|
||||
end
|
||||
|
||||
test "additional_displayable_conditions_count ignores empty compound conditions" do
|
||||
rule = Rule.new(
|
||||
family: @family,
|
||||
resource_type: "transaction",
|
||||
actions: [ Rule::Action.new(action_type: "exclude_transaction") ],
|
||||
conditions: [
|
||||
Rule::Condition.new(condition_type: "compound", operator: "and"),
|
||||
Rule::Condition.new(condition_type: "transaction_name", operator: "like", value: "first"),
|
||||
Rule::Condition.new(condition_type: "transaction_amount", operator: ">", value: 100)
|
||||
]
|
||||
)
|
||||
|
||||
assert_equal 1, rule.additional_displayable_conditions_count
|
||||
end
|
||||
|
||||
test "rule matching on transaction details" do
|
||||
# Create PayPal transaction with underlying merchant in details
|
||||
paypal_entry = create_transaction(
|
||||
|
||||
Reference in New Issue
Block a user