Files
eminuxandCursor c4e5f289cf
CI / scan_ruby (push) Failing after 11m20s
CI / scan_js (push) Successful in 10m35s
CI / lint (push) Has been cancelled
Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 23:01:48 +02:00

67 lines
2.1 KiB
Ruby

class SalesGoal < ApplicationRecord
include Auditable
belongs_to :project, optional: true
validates :name, :metric, :target_value, :start_date, :end_date, presence: true
validates :metric, inclusion: { in: Catalog::GOAL_METRICS.keys }
validates :target_value, numericality: { greater_than: 0 }
validate :end_date_after_start_date
scope :active, -> { where(active: true) }
scope :for_project, ->(project) {
return none if project.nil?
where(project_id: project.id)
}
scope :current, ->(project = nil) {
today = Time.zone.today
scope = active.where("start_date <= ? AND end_date >= ?", today, today)
scope = scope.where(project_id: project.id) if project
scope
}
def metric_label
Catalog.label_for(Catalog::GOAL_METRICS, metric)
end
def current_value
opps = project ? Opportunity.for_project(project) : Opportunity.all
activities = if project
Activity.joins(:organization)
.joins("INNER JOIN organization_projects ON organization_projects.organization_id = activities.organization_id")
.where(organization_projects: { project_id: project.id })
else
Activity.all
end
case metric
when "customers_acquired"
opps.won.where(won_at: start_date.beginning_of_day..end_date.end_of_day).count
when "won_value"
opps.won.where(won_at: start_date.beginning_of_day..end_date.end_of_day).sum(:estimated_value).to_f
when "trials"
activities.where(activity_type: "trial_started", happened_at: start_date.beginning_of_day..end_date.end_of_day).count
when "first_uses"
activities.where(activity_type: "first_use", happened_at: start_date.beginning_of_day..end_date.end_of_day).count
else
0
end
end
def progress_percentage
return 0 if target_value.to_f.zero?
[[(current_value.to_f / target_value.to_f * 100).round, 0].max, 100].min
end
private
def end_date_after_start_date
return if start_date.blank? || end_date.blank?
return if end_date >= start_date
errors.add(:end_date, "deve essere successiva alla data di inizio")
end
end