# frozen_string_literal: true module Visits # Service to handle merging multiple visits into one from the visits drawer class MergeService attr_reader :visits, :errors, :base_visit def initialize(visits) @visits = visits @base_visit = visits.first @errors = [] end # Merges multiple visits into one # @return [Visit, nil] The merged visit or nil if merge failed def call return add_error('At least 2 visits must be selected for merging') if visits.length < 2 merge_visits end private def add_error(message) @errors << message nil end def merge_visits Visit.transaction do update_base_visit(base_visit) reassign_points(base_visit, visits) visits.drop(1).each(&:destroy!) base_visit end rescue ActiveRecord::RecordInvalid => e Rails.logger.error("Failed to merge visits: #{e.message}") add_error(e.record.errors.full_messages.join(', ')) nil end def prepare_base_visit earliest_start = visits.min_by(&:started_at).started_at latest_end = visits.max_by(&:ended_at).ended_at total_duration = ((latest_end - earliest_start) / 60).round combined_name = visits.map(&:name).join(', ') { earliest_start:, latest_end:, total_duration:, combined_name: } end def update_base_visit(base_visit) base_visit_data = prepare_base_visit base_visit.update!( started_at: base_visit_data[:earliest_start], ended_at: base_visit_data[:latest_end], duration: base_visit_data[:total_duration], name: base_visit_data[:combined_name], status: 'confirmed' ) end def reassign_points(base_visit, visits) visits[1..].each do |visit| visit.points.update_all(visit_id: base_visit.id) # rubocop:disable Rails/SkipsModelValidations end end end end