Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
CI / scan_ruby (push) Failing after 11m20s
CI / scan_js (push) Successful in 10m35s
CI / lint (push) Has been cancelled

Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-17 23:01:48 +02:00
co-authored by Cursor
commit c4e5f289cf
258 changed files with 11293 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
class Organization < ApplicationRecord
include Auditable
belongs_to :assigned_user, class_name: "User", optional: true
has_many :organization_projects, dependent: :destroy
has_many :projects, through: :organization_projects
has_many :contacts, dependent: :destroy
has_many :opportunities, dependent: :destroy
has_many :activities, dependent: :destroy
has_many :tasks, dependent: :destroy
has_many :mailing_recipients, dependent: :destroy
has_one :primary_contact, -> { where(primary_contact: true) }, class_name: "Contact", inverse_of: :organization
validates :name, presence: true
validates :status, inclusion: { in: Catalog::ORGANIZATION_STATUSES.keys }
validates :organization_type, inclusion: { in: Catalog::ORGANIZATION_TYPES.keys }
validates :lead_source, inclusion: { in: Catalog::LEAD_SOURCES.keys }, allow_blank: true
validates :team_gender, inclusion: { in: Catalog::TEAM_GENDERS.keys }, allow_blank: true
validates :streaming_status, inclusion: { in: Catalog::STREAMING_STATUSES.keys }, allow_blank: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true
validate :must_have_at_least_one_project
before_validation :assign_current_project_if_needed, on: :create
scope :prospects, -> { where(status: "prospect") }
scope :customers, -> { where(status: %w[active_customer inactive_customer]) }
scope :active_customers, -> { where(status: "active_customer") }
scope :for_project, ->(project) {
return none if project.nil?
joins(:organization_projects).where(organization_projects: { project_id: project.id }).distinct
}
scope :search, ->(query) {
return all if query.blank?
q = "%#{sanitize_sql_like(query.strip)}%"
left_joins(:contacts).where(
"organizations.name ILIKE :q OR organizations.city ILIKE :q OR organizations.email ILIKE :q OR organizations.phone ILIKE :q OR organizations.website ILIKE :q OR contacts.first_name ILIKE :q OR contacts.last_name ILIKE :q OR contacts.email ILIKE :q OR contacts.phone ILIKE :q OR contacts.mobile ILIKE :q",
q: q
).distinct
}
def status_label
Catalog.label_for(Catalog::ORGANIZATION_STATUSES, status)
end
def organization_type_label
Catalog.label_for(Catalog::ORGANIZATION_TYPES, organization_type)
end
def lead_source_label
Catalog.label_for(Catalog::LEAD_SOURCES, lead_source)
end
def team_gender_label
Catalog.label_for(Catalog::TEAM_GENDERS, team_gender)
end
def streaming_status_label
Catalog.label_for(Catalog::STREAMING_STATUSES, streaming_status)
end
def campaign_opportunity(project = nil)
list = opportunities.to_a
list = list.select { |opp| opp.project_id == project.id } if project
list.max_by(&:updated_at)
end
def primary_pipeline_stage(project = nil)
scope = opportunities
scope = scope.where(project_id: project.id) if project
scope.open_stage.order(updated_at: :desc).first&.pipeline_stage ||
scope.order(updated_at: :desc).first&.pipeline_stage
end
def next_pending_task
tasks.pending.order(:due_at).first
end
def last_activity_at
activities.maximum(:happened_at)
end
def days_since_last_activity
return nil unless last_activity_at
((Time.current - last_activity_at) / 1.day).floor
end
def mark_as_active_customer!
update!(status: "active_customer") if status != "active_customer"
end
def ensure_in_project!(project)
return if project.nil?
return if projects.exists?(project.id)
projects << project
end
private
def assign_current_project_if_needed
return if organization_projects.any? || project_ids.reject(&:blank?).any?
return unless Current.project
organization_projects.build(project: Current.project)
end
def must_have_at_least_one_project
return if organization_projects.any? || project_ids.reject(&:blank?).any?
errors.add(:projects, "seleziona almeno un progetto")
end
end