Source code
class Maintenance::MergeOrphanTreenodesTask < MaintenanceTasks::Task
include ArrayHelper
csv_collection
attribute :emails, :string
validates :emails, presence: true, fcm_email_format: true
REPORT_COLUMNS = %w[
orphan_treenode_id
mbo_id
mbo_profile_id
mid_back_office
code
target_treenode_id
status
errors
].freeze
HEADERS = {
orphan_treenode_id: 'orphan treenode id (without company guid)',
mbo_id: 'mbo_id',
mbo_profile_id: 'mbo_profile_id',
mid_back_office: 'mid_back_office',
country_code: 'code',
target_treenode_id: 'treenode to merge to (with Company GUID)'
}.freeze
after_start :prepare_csv_path
after_complete :send_report
after_error :send_report
def process(row)
result = process_row(row)
status = result.blank? ? 'SUCCESS' : 'FAILED'
append_report(row, status: status, errors: result)
rescue StandardError => e
append_report(row, status: 'FAILED', errors: e.message)
raise
end
def csv_path
@csv_path ||= Rails.root.join('tmp', "merge_orphan_treenodes_#{Time.now.to_i}.csv").to_s
end
private
def process_row(row)
source_tree_node, target_tree_node = fetch_tree_nodes(row)
if invalid_tree_nodes?(source_tree_node, target_tree_node)
return 'Source or target tree node not found, or same tree node'
end
source_mbo_profile = find_source_mbo_profile(row, source_tree_node)
return 'Source mbo_profile not found in orphan tree node' unless source_mbo_profile
move_profile_to_target(
source_tree_node: source_tree_node,
target_tree_node: target_tree_node,
mbo_profile: source_mbo_profile
)
''
end
def prepare_csv_path
@csv_path = Rails.root.join('tmp', "merge_orphan_treenodes_#{Time.now.to_i}.csv").to_s
File.write(csv_path, REPORT_COLUMNS.to_csv)
end
def send_report
return unless csv_path && File.exist?(csv_path)
CsvReportMailer.send_report(
recipients: emails_array(emails),
file_path: csv_path,
report_sender: 'Merge Orphan Treenodes'
).deliver_now
ensure
File.delete(csv_path) if csv_path && File.exist?(csv_path)
end
def append_report(row, status:, errors:)
File.open(csv_path, 'a') do |file|
file.puts(
[
row_value(row, :orphan_treenode_id),
row_value(row, :mbo_id),
row_value(row, :mbo_profile_id),
row_value(row, :mid_back_office),
row_value(row, :country_code),
row_value(row, :target_treenode_id),
status,
errors
].to_csv
)
end
end
def fetch_tree_nodes(row)
source_tree_node = TreeNode.find_by(id: row_value(row, :orphan_treenode_id))
target_tree_node = TreeNode.find_by(id: row_value(row, :target_treenode_id))
[source_tree_node, target_tree_node]
end
def invalid_tree_nodes?(source_tree_node, target_tree_node)
source_tree_node.blank? || target_tree_node.blank? || source_tree_node.id == target_tree_node.id
end
def row_value(row, key)
row.field(HEADERS.fetch(key)).to_s.strip
end
def find_source_mbo_profile(row, source_tree_node)
source_tree_node.mbo_profiles.find_by(id: row_value(row, :mbo_profile_id))
end
def move_profile_to_target(source_tree_node:, target_tree_node:, mbo_profile:)
join = TreeNodeToMboProfile.find_by(tree_node_id: source_tree_node.id, mbo_profile_id: mbo_profile.id)
return unless join
existing_target_join = TreeNodeToMboProfile.find_by(
tree_node_id: target_tree_node.id,
mbo_profile_id: mbo_profile.id
)
if existing_target_join
join.destroy!
else
join.update!(tree_node_id: target_tree_node.id, primary: false)
end
ensure_single_primary_mbo_profile(target_tree_node)
end
def ensure_single_primary_mbo_profile(tree_node)
primary_joins = tree_node.tree_node_to_mbo_profiles.reload.where(primary: true).order(:created_at)
all_joins = tree_node.tree_node_to_mbo_profiles.order(:created_at)
if primary_joins.empty? && all_joins.any?
all_joins.first.update!(primary: true)
elsif primary_joins.many?
primary_joins.offset(1).find_each { |item| item.update!(primary: false) }
end
end
end