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:
@@ -0,0 +1,26 @@
|
||||
require "test_helper"
|
||||
|
||||
class DashboardControllerTest < ActionDispatch::IntegrationTest
|
||||
test "dashboard shows snapshot cards and dual funnel counts" do
|
||||
login_as users(:admin)
|
||||
get project_root_path(project_code: "matchlivetv")
|
||||
|
||||
assert_response :success
|
||||
assert_match(/Da contattare/, response.body)
|
||||
assert_match(/Conteggio per stage attuale/, response.body)
|
||||
assert_match(/da qui in poi/, response.body)
|
||||
assert_match(/In trattativa senza next action/, response.body)
|
||||
assert_match(/Senza attività in timeline/, response.body)
|
||||
assert_no_match(/↓ \d+%/, response.body)
|
||||
end
|
||||
|
||||
test "reports funnel uses current and reached columns" do
|
||||
login_as users(:admin)
|
||||
get reports_path(project_code: "matchlivetv")
|
||||
|
||||
assert_response :success
|
||||
assert_match(/In questo stage/, response.body)
|
||||
assert_match(/Da qui in poi/, response.body)
|
||||
assert_no_match(/dal passo precedente/, response.body)
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user