Class: Customer::FillSalesActivity
- Inherits:
-
BaseService
- Object
- BaseService
- Customer::FillSalesActivity
- Defined in:
- app/services/customer/fill_sales_activity.rb
Defined Under Namespace
Classes: CustomerResult, Result
Instance Attribute Summary collapse
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
-
#stats ⇒ Object
readonly
Returns the value of attribute stats.
Instance Method Summary collapse
- #customer_has_open_toucbhase_activity?(customer) ⇒ Boolean
- #customers_core_query ⇒ Object
- #customers_drip_query ⇒ Object
- #customers_touchbase_query ⇒ Object
- #fill_drip_for_customer(customer) ⇒ Object
- #fill_touchbase_for_customer(customer) ⇒ Object
-
#initialize(options = {}) ⇒ FillSalesActivity
constructor
A new instance of FillSalesActivity.
-
#process(employee_id: nil, customer_ids: nil, skip_verify: false) ⇒ Object
fill sales activity.
- #process_customers_for_activities(activity_method, customer_ids: nil, employee_id: nil) ⇒ Object
- #reset_stats ⇒ Object
Methods inherited from BaseService
#log_debug, #log_error, #log_info, #log_warning, #logger, #options, #tagged_logger
Constructor Details
#initialize(options = {}) ⇒ FillSalesActivity
Returns a new instance of FillSalesActivity.
13 14 15 16 17 18 19 |
# File 'app/services/customer/fill_sales_activity.rb', line 13 def initialize( = {}) @touchbase_activity_type = ActivityType.find_by(task_type: 'TOUCHBASE') @drip_activity_type = ActivityType.find_by(task_type: 'PRO_REACT_DRIP_LAUNCH') @trial_run = [:trial_run].to_b @limit = [:limit] super end |
Instance Attribute Details
#limit ⇒ Object (readonly)
Returns the value of attribute limit.
11 12 13 |
# File 'app/services/customer/fill_sales_activity.rb', line 11 def limit @limit end |
#stats ⇒ Object (readonly)
Returns the value of attribute stats.
11 12 13 |
# File 'app/services/customer/fill_sales_activity.rb', line 11 def stats @stats end |
Instance Method Details
#customer_has_open_toucbhase_activity?(customer) ⇒ Boolean
102 103 104 |
# File 'app/services/customer/fill_sales_activity.rb', line 102 def customer_has_open_toucbhase_activity?(customer) Activity.open_activities.where(customer_id: customer.id, activity_type_id: @touchbase_activity_type.id).exists? end |
#customers_core_query ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'app/services/customer/fill_sales_activity.rb', line 31 def customers_core_query Customer.joins(:customer_record) .active .organizations .where(catalog_id: Catalog.main_catalog_ids) .where(report_grouping: nil) .where(open_sales_activity: false) .where(%{ lifetime_revenue > :revenue }, revenue: 5000) .where(%{ last_completed_sales_activity_date < :activity_date AND ( (last_opportunity_date IS NULL AND last_order_date IS NULL) OR GREATEST(last_opportunity_date,last_order_date) < :opp_order_date ) }, activity_date: 90.days.ago, opp_order_date: 90.days.ago) end |
#customers_drip_query ⇒ Object
59 60 61 62 63 |
# File 'app/services/customer/fill_sales_activity.rb', line 59 def customers_drip_query customers = customers_core_query customers = customers.where(state: %w[lead prospect customer]) @drip_activity_type.customer_filter.chain_query(customers) end |
#customers_touchbase_query ⇒ Object
48 49 50 51 52 53 54 55 56 57 |
# File 'app/services/customer/fill_sales_activity.rb', line 48 def customers_touchbase_query customers = customers_core_query customers = customers.watched .joins(:primary_sales_rep) .where.not(primary_sales_rep: { inactive: true }) .where(state: %w[prospect customer]) .preload(:primary_sales_rep) # Combine with activity type filter query @touchbase_activity_type.customer_filter.chain_query(customers) end |
#fill_drip_for_customer(customer) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'app/services/customer/fill_sales_activity.rb', line 164 def fill_drip_for_customer(customer) logger.tagged "#{customer.full_name} - #{customer.reference_number}" do party_ids_with_sales_activity = [] target_activity_type = @drip_activity_type.task_type target_datetime = 1.day.from_now target_datetime = WorkingHours.advance_to_closing_time(target_datetime) new_note = "Auto generated #{target_activity_type.to_s.upcase} on customer #{customer}, scheduling activity for #{target_datetime.to_date.to_fs(:compact)}" # Get all the contact and possible parties parties = [customer, *customer.contacts.active] stats[:contacts_total] += parties.size valid_parties = Party.distinct.where(id: parties.map(&:id)).joins(:contact_points).merge(ContactPoint.emails).where.not('exists(select 1 from email_preferences ep where ep.email = contact_points.detail and disable_announcements = true)') # Those who already had this drip scheduled don't get it anymore valid_parties = valid_parties.where.not('exists(select 1 from activities where activities.party_id = parties.id and activities.activity_type_id = ?)', @drip_activity_type.id) if (excluded_parties = (parties.to_a - valid_parties.to_a)).present? logger.warn "Parties #{excluded_parties.map(&:id).join(', ')} were excluded from drip because they had no email or their email were unsubscribed from announcements emails or they already had this drip" stats[:contacts_unsubscribed] += excluded_parties.size end # remove customer when we are have duplicate emails, we have a method in drip campaign let's use it valid_parties = Campaign::AssignDripCampaigns.compact_valid_parties(valid_parties, logger:) # Now with what's left we can create activities activities_created = [] = [] valid_parties.each do |party| new_act = party.activities.new activity_type: @drip_activity_type, assigned_resource_id: nil, target_datetime:, new_note:, skip_check_for_open_sales_activity: true, skip_check_for_valid_party: true # We already did this earlier if @trial_run || new_act.save msg = "Activity scheduled: #{new_act&.id || 'trial run'} on #{party.full_name} due on #{new_act.target_datetime}" logger.info msg << msg activities_created << new_act # Mark the customer has having a sales activity party_ids_with_sales_activity << party.id stats[:activities_created] += 1 else msg = "CANNOT create new activity #{new_act.errors..inspect}" logger.error msg << msg stats[:activities_failed] += 1 end end Party.where(id: party_ids_with_sales_activity).update_all(open_sales_activity: true) if party_ids_with_sales_activity.present? return CustomerResult.new(success: activities_created.present?, activity_created: activities_created, message: .join("\n")) end end |
#fill_touchbase_for_customer(customer) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'app/services/customer/fill_sales_activity.rb', line 106 def fill_touchbase_for_customer(customer) return CustomerResult.new(success: false, message: "No primary rep on customer #{customer}") unless customer.primary_sales_rep return CustomerResult.new(success: false, message: "No ACTIVE primary rep on customer #{customer}") if customer.primary_sales_rep.inactive? return CustomerResult.new(success: false, message: "Sales activity already present on customer #{customer}") if customer.open_sales_activity return CustomerResult.new(success: false, message: "Customer has open touchbase activity already") if customer_has_open_toucbhase_activity?(customer) logger.tagged "#{customer.full_name} - #{customer.reference_number}" do last_sales_or_quote_date = [customer.customer_record.last_opportunity_date, customer.customer_record.last_order_date].compact.max last_activity_date = customer.customer_record.last_completed_sales_activity_date assigned_resource_id = customer.primary_sales_rep_id target_activity_type = @touchbase_activity_type.task_type time_offset = 60.days if last_activity_date time_offset ||= 1.day target_datetime = [(last_activity_date + time_offset), 1.day.from_now].max # prevents scheduling in the past else target_datetime = 1.day.from_now end logger.info "Evaluating #{target_activity_type} Activity on #{target_datetime}" # make sure we don't schedule anything while on vacation or a day off target_datetime = customer.primary_sales_rep.next_working_day(target_datetime, 0) return CustomerResult.new(success: false, message: "Could not determine next working day for primary sale rep id #{customer.primary_sales_rep_id} starting on #{next_date}") unless target_datetime new_note = "Auto generated #{target_activity_type.to_s.upcase} on account, last sales activity was detected at #{last_activity_date&.to_fs(:compact)} and last sales/quote event at #{last_sales_or_quote_date&.to_fs(:compact) || 'n/a'}, performed check on #{Time.current.to_fs(:compact)} and scheduling activity for #{target_datetime.to_date.to_fs(:compact)}" target_datetime = WorkingHours.advance_to_closing_time(target_datetime) return CustomerResult.new(success: true, message: "Activity Scheduled for #{target_datetime}") if @trial_run # If customer has no sales activity present, we create the desired type new_act = customer.activities.create activity_type: @touchbase_activity_type, assigned_resource_id:, target_datetime:, new_note:, skip_check_for_open_sales_activity: true # No need to run the activity's intrisic check we do this automatically later return CustomerResult.new(success: false, message: "Sales activity already present on customer #{customer}") unless new_act if new_act.errors.present? msg = "CANNOT create new activity #{new_act.errors..inspect}" logger.error msg return CustomerResult.new(success: false, message: msg) else logger.info "Activity scheduled: #{new_act.id} due on #{new_act.target_datetime}" # We mark the account has having a sales activity now customer.update_column(:open_sales_activity, true) stats[:activities_created] += 1 return CustomerResult.new(success: true, activity_created: new_act, message: "Customer #{customer} for #{customer.primary_sales_rep} : #{new_note}") end end end |
#process(employee_id: nil, customer_ids: nil, skip_verify: false) ⇒ Object
fill sales activity
22 23 24 25 26 27 28 29 |
# File 'app/services/customer/fill_sales_activity.rb', line 22 def process(employee_id: nil, customer_ids: nil, skip_verify: false) logger.debug("Filling sales activities", employee_id: employee_id, customer_count: customer_ids&.size) results = {} reset_stats results[:touchbase] = process_customers_for_activities(:touchbase, customer_ids:, employee_id:) results[:drip] = process_customers_for_activities(:drip, customer_ids:, employee_id:) results end |
#process_customers_for_activities(activity_method, customer_ids: nil, employee_id: nil) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'app/services/customer/fill_sales_activity.rb', line 65 def process_customers_for_activities(activity_method, customer_ids: nil, employee_id: nil) case activity_method when :touchbase customers = customers_touchbase_query fill_method = :fill_touchbase_for_customer when :drip customers = customers_drip_query fill_method = :fill_drip_for_customer else raise 'Invalid activity_method, must be :touchbase or :drip' end customers = customers.where(id: customer_ids) if customer_ids.present? customers = customers.where(primary_sales_rep_id: employee_id) if employee_id.present? customers = customers.limit(limit) if limit.present? customers_count = customers.size stats[:customers_total] += customers_count logger.info "Found #{customers_count} to process for #{activity_method}" activities_created = 0 counter = 1 = [] customers.find_each do |c| logger.tagged "#{counter}/#{customers_count}" do res = send(fill_method, c) activities_created += 1 if res.success << res. counter += 1 end end logger.info "Created #{activities_created} #{activity_method}" Result.new(success: true, customers_processed: customers_count, activities_created:, messages: .compact) end |
#reset_stats ⇒ Object
223 224 225 226 227 228 229 230 231 232 |
# File 'app/services/customer/fill_sales_activity.rb', line 223 def reset_stats @stats = { customers_total: 0, contacts_total: 0, contacts_unsubscribed: 0, contacts_with_existing_activity: 0, activities_failed: 0, activities_created: 0 } end |