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>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
require "csv"
|
||||
|
||||
class CsvImport::Organizations
|
||||
STANDARD_HEADERS = %w[
|
||||
organization_name organization_type sport country region province city website
|
||||
organization_email contact_first_name contact_last_name contact_role contact_email
|
||||
contact_phone lead_source notes
|
||||
].freeze
|
||||
|
||||
Result = Struct.new(:imported, :skipped, :errors, keyword_init: true)
|
||||
|
||||
def initialize(file:, user:, mapping: nil, project: nil)
|
||||
@file = file
|
||||
@user = user
|
||||
@mapping = mapping
|
||||
@project = project || Current.project
|
||||
end
|
||||
|
||||
def preview(limit: 10)
|
||||
rows = []
|
||||
CSV.foreach(@file.path, headers: true, encoding: "bom|utf-8") do |row|
|
||||
rows << row.to_h
|
||||
break if rows.size >= limit
|
||||
end
|
||||
{ headers: rows.first&.keys || [], rows: rows }
|
||||
end
|
||||
|
||||
def import!
|
||||
imported = 0
|
||||
skipped = 0
|
||||
errors = []
|
||||
|
||||
CSV.foreach(@file.path, headers: true, encoding: "bom|utf-8").with_index(2) do |row, line|
|
||||
Current.user = @user
|
||||
attrs = mapped_attrs(row)
|
||||
|
||||
if duplicate?(attrs)
|
||||
skipped += 1
|
||||
next
|
||||
end
|
||||
|
||||
Organization.transaction do
|
||||
org = Organization.new(
|
||||
name: attrs[:organization_name],
|
||||
organization_type: normalize_type(attrs[:organization_type]),
|
||||
sport: attrs[:sport],
|
||||
country: attrs[:country].presence || "Italia",
|
||||
region: attrs[:region],
|
||||
province: attrs[:province],
|
||||
city: attrs[:city],
|
||||
website: normalize_website(attrs[:website]),
|
||||
email: attrs[:organization_email],
|
||||
lead_source: normalize_lead_source(attrs[:lead_source]),
|
||||
notes: attrs[:notes],
|
||||
status: "prospect",
|
||||
assigned_user: @user
|
||||
)
|
||||
org.projects = [@project].compact
|
||||
org.save!
|
||||
|
||||
if attrs[:contact_first_name].present? || attrs[:contact_last_name].present?
|
||||
org.contacts.create!(
|
||||
first_name: attrs[:contact_first_name].presence || "N/D",
|
||||
last_name: attrs[:contact_last_name].presence || "N/D",
|
||||
role: attrs[:contact_role],
|
||||
email: attrs[:contact_email],
|
||||
phone: attrs[:contact_phone],
|
||||
primary_contact: true
|
||||
)
|
||||
end
|
||||
end
|
||||
imported += 1
|
||||
rescue StandardError => e
|
||||
errors << { line: line, message: e.message, row: row.to_h }
|
||||
end
|
||||
|
||||
Result.new(imported: imported, skipped: skipped, errors: errors)
|
||||
ensure
|
||||
Current.user = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mapped_attrs(row)
|
||||
source = row.to_h.transform_keys { |k| k.to_s.strip }
|
||||
STANDARD_HEADERS.index_with do |header|
|
||||
key = @mapping&.dig(header) || header
|
||||
source[key].to_s.strip.presence
|
||||
end.symbolize_keys
|
||||
end
|
||||
|
||||
def duplicate?(attrs)
|
||||
name = attrs[:organization_name].to_s
|
||||
email = attrs[:organization_email].to_s.downcase
|
||||
website = normalize_website(attrs[:website])
|
||||
|
||||
return true if name.present? && Organization.where("LOWER(name) = ?", name.downcase).exists?
|
||||
return true if email.present? && Organization.where("LOWER(email) = ?", email).exists?
|
||||
return true if website.present? && Organization.where("LOWER(website) = ?", website.downcase).exists?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def normalize_website(value)
|
||||
return if value.blank?
|
||||
|
||||
value.to_s.strip.downcase.sub(%r{\Ahttps?://}, "").sub(%r{/\z}, "")
|
||||
end
|
||||
|
||||
def normalize_type(value)
|
||||
return "altro" if value.blank?
|
||||
|
||||
key = value.to_s.downcase.gsub(/\s+/, "_")
|
||||
return key if Catalog::ORGANIZATION_TYPES.key?(key)
|
||||
|
||||
Catalog::ORGANIZATION_TYPES.find { |_k, label| label.downcase == value.to_s.downcase }&.first || "altro"
|
||||
end
|
||||
|
||||
def normalize_lead_source(value)
|
||||
return if value.blank?
|
||||
|
||||
key = value.to_s.downcase.gsub(/\s+/, "_")
|
||||
return key if Catalog::LEAD_SOURCES.key?(key)
|
||||
|
||||
Catalog::LEAD_SOURCES.find { |_k, label| label.downcase == value.to_s.downcase }&.first || "other"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user