Allinea i numeri della dashboard tra griglia, funnel e liste di attenzione.

Il funnel mostra stage attuale e cumulato, i KPI escludono Da contattare e le liste usano il totale reale invece del tetto a 20.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-02 23:23:20 +02:00
co-authored by Cursor
parent 936930e691
commit 8f73105dfe
7 changed files with 271 additions and 85 deletions
+98
View File
@@ -1,16 +1,114 @@
require "test_helper"
class DashboardMetricsTest < ActiveSupport::TestCase
setup do
@project = projects(:matchlivetv)
Current.user = users(:marco)
Current.project = @project
@opps = []
end
teardown do
Current.user = nil
Current.project = nil
end
test "computes stage counts and pipeline value" do
metrics = Dashboard::Metrics.new
assert metrics.stage_counts.key?("interested")
assert metrics.stage_counts.key?("to_contact")
assert_kind_of Numeric, metrics.open_pipeline_value
assert_kind_of Array, metrics.funnel_steps
end
test "funnel shows current snapshot and reached-or-beyond without skipping stages" do
add_opp("to_contact")
2.times { add_opp("contacted") }
add_opp("replied")
add_opp("interested")
2.times { add_opp("proposal", estimated_value: 100) }
add_opp("lost", lost_reason: "price")
steps = metrics.funnel_steps.index_by { |step| step[:stage] }
assert_equal Dashboard::Metrics::FUNNEL_STAGES, metrics.funnel_steps.map { |step| step[:stage] }
assert_equal 2, steps["contacted"][:current]
assert_equal 6, steps["contacted"][:reached]
assert_equal 0, steps["first_use"][:current]
assert_equal 2, steps["first_use"][:reached]
assert_equal 2, steps["proposal"][:current]
assert_equal 2, steps["proposal"][:reached]
assert_equal 0, steps["won"][:current]
refute steps.key?("lost")
contacted_step = steps["replied"]
assert_equal "4 su 6 hanno superato Contattato", contacted_step[:conversion_label]
assert_equal 67, contacted_step[:rate]
end
test "next action KPI excludes da contattare and attention remainder uses real total" do
2.times { add_opp("to_contact", send_status: "to_send") }
10.times { add_opp("contacted") }
interested = add_opp("interested")
Task.create!(
title: "Richiama",
organization: interested.organization,
opportunity: interested,
assigned_user: users(:marco),
due_at: 1.day.from_now,
priority: "normal",
task_type: "call",
status: "pending"
)
assert_equal 10, metrics.opportunities_without_next_action
preview = metrics.attention_items[:opportunities_without_next_action]
assert_equal 10, preview[:total]
assert_equal 8, preview[:items].size
end
test "demo and first use conversions are blank when those stages were never used" do
add_opp("proposal")
assert_nil metrics.demo_to_won_conversion
assert_nil metrics.first_use_to_won_conversion
end
test "demo conversion is zero when someone is in demo and nobody won" do
add_opp("demo_trial")
assert_equal 0, metrics.demo_to_won_conversion
end
test "goal progress" do
goal = sales_goals(:september)
assert_equal 0, goal.current_value
assert_equal 0, goal.progress_percentage
end
private
def metrics
Dashboard::Metrics.new(scope: Opportunity.where(id: @opps.map(&:id)), project: @project)
end
def add_opp(stage, **attrs)
org = Organization.create!(
name: "Soc #{stage} #{SecureRandom.hex(3)}",
status: "prospect",
organization_type: "societa_sportiva"
)
opp = Opportunity.create!(
{
organization: org,
project: @project,
name: "Deal #{stage}",
pipeline_stage: stage
}.merge(attrs)
)
@opps << opp
opp
end
end