Class: Activity
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Activity
- Includes:
- ActionView::Helpers::TextHelper, Models::Auditable, Models::Embeddable, PgSearch::Model
- Defined in:
- app/models/activity.rb
Overview
== Schema Information
Table name: activities
Database name: primary
id :integer not null, primary key
completion_datetime :datetime
description :string(255)
lock_target_datetime :boolean default(FALSE)
notes :text
original_target_datetime :datetime
resource_type :string(255)
start_datetime :datetime
target_datetime :datetime
uploads_count :integer default(0)
created_at :datetime
updated_at :datetime
activity_result_type_id :integer
activity_type_id :integer
assigned_resource_id :integer
call_record_id :integer
campaign_id :integer
closed_by_id :integer
communication_id :integer
creator_id :integer
customer_id :integer
opportunity_id :integer
original_assigned_resource_id :integer
parent_activity_id :integer
party_id :integer
resource_id :integer
updater_id :integer
visit_id :integer
Indexes
by_act_type_w (activity_type_id) WHERE (activity_result_type_id IS NULL)
by_type_and_assi_resrc_w (activity_type_id,assigned_resource_id) WHERE (activity_result_type_id IS NULL)
index_activities_on_activity_result_type_id (activity_result_type_id)
index_activities_on_activity_type_id (activity_type_id)
index_activities_on_assigned_resource_id (assigned_resource_id)
index_activities_on_call_record_id (call_record_id)
index_activities_on_campaign_id (campaign_id)
index_activities_on_closed_by_id (closed_by_id)
index_activities_on_communication_id (communication_id)
index_activities_on_creator_id (creator_id)
index_activities_on_customer_id (customer_id)
index_activities_on_opportunity_id (opportunity_id)
index_activities_on_party_id (party_id)
index_activities_on_resource_type_and_resource_id (resource_type,resource_id) WHERE ((resource_type IS NOT NULL) AND (resource_id IS NOT NULL))
index_activities_on_target_datetime (target_datetime) USING brin
index_activities_on_updater_id (updater_id)
index_activities_on_visit_id (visit_id)
index_activities_resource_id (resource_id)
Foreign Keys
activities_activity_result_type_id_fkey (activity_result_type_id => activity_result_types.id)
activities_activity_type_id_fkey (activity_type_id => activity_types.id)
activities_communication_id_fk (communication_id => communications.id) ON DELETE => nullify
activities_creator_id_fk (creator_id => parties.id) ON DELETE => nullify
activities_party_id_fk (party_id => parties.id) ON DELETE => cascade
activities_updater_id_fk (updater_id => parties.id) ON DELETE => nullify
fk_rails_... (call_record_id => call_records.id) ON DELETE => nullify
fk_rails_... (customer_id => parties.id)
fk_rails_... (opportunity_id => opportunities.id) ON DELETE => nullify ON UPDATE => cascade
fk_rails_... (visit_id => visits.id)
Defined Under Namespace
Classes: AssignmentNotificationHandler, AutoChainCloser, ChainRunner, ChainRunnerHandler, CompactNotes, EmailNotificationHandler, ExpiredActivityCloser, OpenActivitiesStamper, Prioritizer, ResourceList, SalesActivityHandler, SynchronousEffectsHandler, TypeRulesCreatedHandler, TypeRulesUpdatedHandler, VoicemailReadHandler
Constant Summary collapse
- NOTE_RESULT_ID =
Note result id.
2- NATURAL_ORDER =
Natural order.
'activities.activity_result_type_id IS NULL DESC, COALESCE(activities.completion_datetime,activities.target_datetime) DESC'- RESOURCE_TYPES =
Recognised resource types.
%w[Delivery Opportunity Order SupportCase Party SpiffEnrollment Receipt CreditMemo SupportCaseParticipant StatementOfAccount Rma Invoice RoomConfiguration ExportedCatalogItemPacket LocatorRecord CreditApplication CampaignDelivery PurchaseOrder Payment Quote SchedulerBooking].freeze
- FINAL_SERVICES =
Final services.
[ActivityTypeConstants::ONSITESERVICE, ActivityTypeConstants::SSI_INSTALL, ActivityTypeConstants::SGS_ONSITESUPPORT, ActivityTypeConstants::SGS_REMOTESUPPORT, ActivityTypeConstants::SSFIX_ONSITE_SERVICE].freeze
- INITIAL_SERVICES =
Initial services.
[ActivityTypeConstants::SERVICECONFIRM, ActivityTypeConstants::SSI_PREPLAN_MEET, ActivityTypeConstants::SGS_ONSITE_PREPLAN_MEET, ActivityTypeConstants::SGS_REMOTE_PREPLAN_MEET, ActivityTypeConstants::SSFIX_SERVICE_CONFIRM].freeze
- MIN_EMBEDDABLE_NOTES_LENGTH =
Minimum embeddable notes length.
50
Constants included from Models::Embeddable
Models::Embeddable::MAX_CONTENT_LENGTH
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Models::Schedulable
Models::Schedulable::SIMPLE_FORM_OPTIONS
Belongs to collapse
-
#activity_result_type ⇒ ActivityResultType?
The result that closed the activity.
-
#activity_type ⇒ ActivityType?
The type of this activity (nil for notes).
-
#assigned_resource ⇒ Employee?
The employee the activity is assigned to.
-
#call_record ⇒ CallRecord?
The call that spawned this activity.
-
#campaign ⇒ Campaign?
The campaign this activity belongs to.
-
#closed_by ⇒ Party?
Who closed the activity.
-
#communication ⇒ Communication?
The communication that spawned this activity.
-
#customer ⇒ Customer?
The party's or resource's customer (or supplier).
-
#opportunity ⇒ Opportunity?
The linked opportunity.
-
#original_assigned_resource ⇒ Employee?
The employee originally assigned.
-
#parent_activity ⇒ Activity?
The activity this one was chained from.
-
#party ⇒ Party?
The contact/customer the activity is for.
-
#resource ⇒ ApplicationRecord?
The polymorphic resource this activity concerns.
-
#resource_opportunity ⇒ Opportunity?
Typed resource shortcut for eager loading.
-
#resource_order ⇒ Order?
Typed resource shortcut for eager loading.
-
#resource_quote ⇒ Quote?
Typed resource shortcut for eager loading.
-
#resource_room_configuration ⇒ RoomConfiguration?
Typed resource shortcut for eager loading.
-
#resource_scheduler_booking ⇒ SchedulerBooking?
Typed resource shortcut for eager loading.
-
#visit ⇒ Visit?
The website visit that spawned this activity.
Has many collapse
-
#child_activities ⇒ ActiveRecord::Associations::CollectionProxy<Activity>
Activities chained from this one.
-
#mail_activities ⇒ ActiveRecord::Associations::CollectionProxy<MailActivity>
Inbound/outbound mail links.
-
#triggered_communications ⇒ ActiveRecord::Associations::CollectionProxy<Communication>
Communications this activity triggered.
-
#uploads ⇒ ActiveRecord::Associations::CollectionProxy<Upload>
Files attached to this activity.
Has and belongs to many collapse
-
#parties ⇒ ActiveRecord::Associations::CollectionProxy<Party>
Additional linked parties.
Instance Attribute Summary collapse
-
#assignment_resource_ids ⇒ Object
Returns the value of attribute assignment_resource_ids.
-
#chained_activity_result ⇒ Object
Returns the value of attribute chained_activity_result.
-
#fallback_employee_id ⇒ Object
Returns the value of attribute fallback_employee_id.
-
#halt_chain ⇒ Object
Returns the value of attribute halt_chain.
-
#linked_party_ids ⇒ Object
Returns the value of attribute linked_party_ids.
-
#mileage ⇒ Object
Returns the value of attribute mileage.
-
#new_target_date ⇒ Object
Returns the value of attribute new_target_date.
-
#new_target_time ⇒ Object
Returns the value of attribute new_target_time.
-
#service_date ⇒ Object
Returns the value of attribute service_date.
-
#service_duration ⇒ Object
Returns the value of attribute service_duration.
-
#service_time ⇒ Object
Returns the value of attribute service_time.
-
#skip_callbacks ⇒ Object
Returns the value of attribute skip_callbacks.
-
#skip_check_for_open_sales_activity ⇒ Object
Returns the value of attribute skip_check_for_open_sales_activity.
-
#skip_check_for_valid_party ⇒ Object
Returns the value of attribute skip_check_for_valid_party.
-
#support_case_closed_reason ⇒ Object
Returns the value of attribute support_case_closed_reason.
-
#support_case_event ⇒ Object
Returns the value of attribute support_case_event.
-
#travel_time ⇒ Object
Returns the value of attribute travel_time.
-
#updated_activities ⇒ Object
Returns the value of attribute updated_activities.
Belongs to collapse
-
#resource_support_case ⇒ SupportCase
Type-specific resource associations for eager loading (polymorphic can't be preloaded).
Methods included from Models::Auditable
Has many collapse
-
#historical_open_activities ⇒ ActiveRecord::Relation<HistoricalOpenActivity>
Track each time the activity was left open at end of day.
Methods included from Models::Embeddable
Delegated Instance Attributes collapse
-
#cancelled? ⇒ Object
Alias for Activity_result_type#cancelled?.
-
#complete? ⇒ Object
Alias for Activity_result_type#complete?.
-
#is_email? ⇒ Object
Alias for Activity_type#is_email?.
-
#result_code ⇒ Object
(also: #activity_result_code)
Alias for Activity_result_type#result_code.
Class Method Summary collapse
-
.all_activity_filters ⇒ Array<String>
Resource types filterable in the activity list.
-
.completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are completed.
-
.completion_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are completion order.
-
.embeddable_content_types ⇒ Array<Symbol>
-- Embeddable interface ---------------------------------------------------.
-
.final_service_completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are final service completed.
-
.for_customer_and_contacts ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are for customer and contacts.
-
.heatwave_assistant_id ⇒ Integer?
The id of the Heatwave assistant employee (bot account).
-
.high_priority ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are high priority.
-
.inbound_emails ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are inbound emails.
-
.initial_service_completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are initial service completed.
-
.iqoppfu ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are iqoppfu.
-
.leads ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are leads.
-
.mcp_searchable? ⇒ Boolean
False — activities are excluded from MCP search.
-
.meeting ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are meeting.
-
.natural_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are natural order.
-
.non_cob_time(datetime) ⇒ String?
Formatted time when it falls outside close-of-business rounding.
-
.non_notes ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are non notes.
-
.not_tagged_with ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are not tagged with.
-
.notes_only ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are notes only.
-
.open_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are open activities.
-
.overdue_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are overdue activities.
-
.position_of_activity(activity_id, relation) ⇒ Integer?
1-based position of the activity within the relation.
-
.preloaded ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are preloaded.
-
.quofu ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are quofu.
-
.ransackable_scopes(_auth_object = nil) ⇒ Array<Symbol>
Scopes ransack may call.
-
.recency_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are recency order.
-
.resource_types ⇒ Array<String>
Recognized resource_type values.
-
.sales_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are sales activities.
-
.tagged_with ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are tagged with.
-
.training ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are training.
-
.visible_by_default ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are visible by default.
-
.with_activity_uploads_count ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are with activity uploads count.
-
.with_customer_as_c ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are with customer as c.
Instance Method Summary collapse
-
#activity_type_for_select ⇒ Array
Activity type options scoped to the party.
-
#age_in_days ⇒ Integer
Days from the effective origin date to completion (or today).
-
#auto_assign(allocate_on = nil, current_user = nil, options = {}) ⇒ Employee?
Auto-assigns this activity to the best available resource.
-
#auto_assign_and_schedule(ideal_target_date = nil, options = {}) ⇒ Boolean
Assigns and schedules this activity on the best available date.
-
#auto_close_if_applicable(auto_save: true) ⇒ Symbol
Our activity chain might have special rules, such as an auto close result when the due date is reached, useful for triggering drip email chains.
-
#best_resource(current_user = nil, options = {}) ⇒ Integer?
current_user: a value to use as the currently logged in user.
-
#best_resource_employee(current_user = nil, options = {}) ⇒ Employee?
Returns the best available employee resource for this activity.
-
#campaign_options_for_select ⇒ Array<Array(String, Integer)>
Campaign name/id pairs for the customer.
-
#cancel(options = {}) ⇒ Boolean
Marks this activity cancelled.
-
#closed? ⇒ Boolean
True for notes or activities with a result.
-
#compact_notes ⇒ String?
Notes (or description) with bracketed stamps stripped.
-
#complete(options = {}) ⇒ Boolean
Marks this activity complete.
-
#contact_options_for_select ⇒ Array
Contact/organization options from the address book builder.
-
#content_for_embedding(_content_type = :primary) ⇒ String?
Builds the text embedded for semantic search.
-
#default_resource_for_select ⇒ Array
Single [display, identifier] option for the current resource.
-
#description ⇒ String?
First 500 chars of notes, else the type's description.
-
#display_date ⇒ String
Effective date formatted for CRM display.
-
#display_type ⇒ String
The activity type's task type, or "Note".
-
#effective_date ⇒ Time
Completion, else target, else creation timestamp.
-
#effective_origin_date ⇒ Date
Original target date, falling back to target or creation date.
-
#embedding_content_changed? ⇒ Boolean
True when notes changed (embedding needs refresh).
-
#has_time? ⇒ Boolean
True when the effective date carries a non-midnight time.
-
#is_note? ⇒ Boolean
True when the activity is a plain note (no type).
-
#is_onsite_service? ⇒ Boolean
True for on-site service activity types.
-
#is_service_confirm? ⇒ Boolean
True for service-confirm activity types.
-
#is_service_confirm_completed? ⇒ Boolean?
True when a service-confirm type completed with CMP.
-
#is_smartservice_related? ⇒ Boolean
True when the type is any SmartService activity type.
-
#lock ⇒ Boolean
Locks the target datetime, skipping callbacks.
-
#new_note ⇒ String
Always blank; pairs with #new_note= for form binding.
-
#new_note=(n) ⇒ void
Appends a timestamped note to the notes field.
-
#open? ⇒ Boolean
True when no result type has been set.
-
#overdue? ⇒ Boolean
True when open and past its target datetime.
-
#party_name ⇒ String?
The party's full name.
-
#preloaded ⇒ ActiveRecord::Relation
Eager-loads all associations required to render activity cards without N+1 queries.
-
#primary_address_id ⇒ Integer?
The customer's mailing/shipping/billing address id.
-
#resource_combo=(val) ⇒ void
Backwards-compat: cached browser forms may still submit the old "ClassName|id" format.
-
#resource_gid ⇒ String?
GlobalID URI for the polymorphic resource.
-
#resource_gid=(gid_string) ⇒ void
Sets the polymorphic resource from a GlobalID string.
-
#result_options_for_select ⇒ Array?
Result options for the activity's type.
-
#return_to ⇒ Object?
Record to redirect back to (resource, else party).
-
#sales_activity? ⇒ Boolean?
Whether the activity type is a sales activity.
-
#sender_for_email_templates ⇒ Employee?
Assigned resource, falling back to updater then creator.
-
#sender_party ⇒ Party
For emails, who is the sender?.
-
#set_assigned_rep ⇒ Boolean, ...
Defaults assigned/original resource for open activities before save.
-
#set_campaign ⇒ void
Subscribes the customer to, and assigns, the applicable campaign.
-
#set_linked_parties ⇒ void
Reconciles the parties join table with party_id, linked_party_ids, and resource-reported ids, dropping ids that no longer exist.
-
#target_date ⇒ String?
Target date formatted for CRM display.
-
#target_datetime_outside_business_hours? ⇒ Boolean
True when no target datetime or it falls outside working hours.
-
#target_time ⇒ String?
Target time formatted "03:30 pm".
-
#to_s ⇒ String
(also: #name)
"date rep type" summary line.
-
#unlock ⇒ Boolean
Unlocks the target datetime, skipping callbacks.
Methods included from Models::Embeddable
#embeddable_locales, #embedding_content_hash, #embedding_current?, #embedding_eligible?, embedding_partition_class, #embedding_stale?, #embedding_type_name, #embedding_vector, #find_content_embedding, #find_similar, #generate_all_embeddings!, #generate_chunked_embeddings!, #generate_embedding!, #has_embedding?, #locale_for_embedding, #needs_chunking?, regenerate_all_embeddings, semantic_search, with_shape_lock
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation
Methods included from Models::Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#activity_result_type ⇒ ActivityResultType?
Returns the result that closed the activity.
119 |
# File 'app/models/activity.rb', line 119 belongs_to :activity_result_type, inverse_of: :activities, optional: true |
#activity_type ⇒ ActivityType?
Returns the type of this activity (nil for notes).
81 |
# File 'app/models/activity.rb', line 81 belongs_to :activity_type, inverse_of: :activities, optional: true |
#assigned_resource ⇒ Employee?
Returns the employee the activity is assigned to.
93 |
# File 'app/models/activity.rb', line 93 belongs_to :assigned_resource, class_name: 'Employee', optional: true |
#assignment_resource_ids ⇒ Object
Returns the value of attribute assignment_resource_ids.
319 320 321 |
# File 'app/models/activity.rb', line 319 def assignment_resource_ids @assignment_resource_ids end |
#call_record ⇒ CallRecord?
Returns the call that spawned this activity.
134 |
# File 'app/models/activity.rb', line 134 belongs_to :call_record, optional: true |
#campaign ⇒ Campaign?
Returns the campaign this activity belongs to.
125 |
# File 'app/models/activity.rb', line 125 belongs_to :campaign, optional: true |
#chained_activity_result ⇒ Object
Returns the value of attribute chained_activity_result.
319 320 321 |
# File 'app/models/activity.rb', line 319 def chained_activity_result @chained_activity_result end |
#child_activities ⇒ ActiveRecord::Associations::CollectionProxy<Activity>
Returns activities chained from this one.
141 |
# File 'app/models/activity.rb', line 141 has_many :child_activities, class_name: 'Activity', inverse_of: :parent_activity |
#closed_by ⇒ Party?
Returns who closed the activity.
78 |
# File 'app/models/activity.rb', line 78 belongs_to :closed_by, class_name: 'Party', optional: true |
#communication ⇒ Communication?
Returns the communication that spawned this activity.
122 |
# File 'app/models/activity.rb', line 122 belongs_to :communication, optional: true |
#customer ⇒ Customer?
Returns the party's or resource's customer (or supplier).
87 |
# File 'app/models/activity.rb', line 87 belongs_to :customer, optional: true, inverse_of: :related_activities |
#fallback_employee_id ⇒ Object
Returns the value of attribute fallback_employee_id.
319 320 321 |
# File 'app/models/activity.rb', line 319 def fallback_employee_id @fallback_employee_id end |
#halt_chain ⇒ Object
Returns the value of attribute halt_chain.
319 320 321 |
# File 'app/models/activity.rb', line 319 def halt_chain @halt_chain end |
#linked_party_ids ⇒ Object
Returns the value of attribute linked_party_ids.
319 320 321 |
# File 'app/models/activity.rb', line 319 def linked_party_ids @linked_party_ids end |
#mail_activities ⇒ ActiveRecord::Associations::CollectionProxy<MailActivity>
Returns inbound/outbound mail links.
150 |
# File 'app/models/activity.rb', line 150 has_many :mail_activities |
#mileage ⇒ Object
Returns the value of attribute mileage.
319 320 321 |
# File 'app/models/activity.rb', line 319 def mileage @mileage end |
#new_target_date ⇒ Object
Returns the value of attribute new_target_date.
319 320 321 |
# File 'app/models/activity.rb', line 319 def new_target_date @new_target_date end |
#new_target_time ⇒ Object
Returns the value of attribute new_target_time.
319 320 321 |
# File 'app/models/activity.rb', line 319 def new_target_time @new_target_time end |
#opportunity ⇒ Opportunity?
Returns the linked opportunity.
128 |
# File 'app/models/activity.rb', line 128 belongs_to :opportunity, inverse_of: :linked_activities, optional: true |
#original_assigned_resource ⇒ Employee?
Returns the employee originally assigned.
96 |
# File 'app/models/activity.rb', line 96 belongs_to :original_assigned_resource, class_name: 'Employee', optional: true |
#parent_activity ⇒ Activity?
Returns the activity this one was chained from.
90 |
# File 'app/models/activity.rb', line 90 belongs_to :parent_activity, class_name: 'Activity', inverse_of: :child_activities, optional: true |
#parties ⇒ ActiveRecord::Associations::CollectionProxy<Party>
Returns additional linked parties.
155 |
# File 'app/models/activity.rb', line 155 has_and_belongs_to_many :parties |
#party ⇒ Party?
Returns the contact/customer the activity is for.
84 |
# File 'app/models/activity.rb', line 84 belongs_to :party, optional: true |
#resource ⇒ ApplicationRecord?
Returns the polymorphic resource this activity concerns.
99 |
# File 'app/models/activity.rb', line 99 belongs_to :resource, polymorphic: true, optional: true |
#resource_opportunity ⇒ Opportunity?
Returns typed resource shortcut for eager loading.
104 |
# File 'app/models/activity.rb', line 104 belongs_to :resource_opportunity, class_name: 'Opportunity', foreign_key: :resource_id, optional: true |
#resource_order ⇒ Order?
Returns typed resource shortcut for eager loading.
107 |
# File 'app/models/activity.rb', line 107 belongs_to :resource_order, class_name: 'Order', foreign_key: :resource_id, optional: true |
#resource_quote ⇒ Quote?
Returns typed resource shortcut for eager loading.
110 |
# File 'app/models/activity.rb', line 110 belongs_to :resource_quote, class_name: 'Quote', foreign_key: :resource_id, optional: true |
#resource_room_configuration ⇒ RoomConfiguration?
Returns typed resource shortcut for eager loading.
113 |
# File 'app/models/activity.rb', line 113 belongs_to :resource_room_configuration, class_name: 'RoomConfiguration', foreign_key: :resource_id, optional: true |
#resource_scheduler_booking ⇒ SchedulerBooking?
Returns typed resource shortcut for eager loading.
116 |
# File 'app/models/activity.rb', line 116 belongs_to :resource_scheduler_booking, class_name: 'SchedulerBooking', foreign_key: :resource_id, optional: true |
#service_date ⇒ Object
Returns the value of attribute service_date.
319 320 321 |
# File 'app/models/activity.rb', line 319 def service_date @service_date end |
#service_duration ⇒ Object
Returns the value of attribute service_duration.
319 320 321 |
# File 'app/models/activity.rb', line 319 def service_duration @service_duration end |
#service_time ⇒ Object
Returns the value of attribute service_time.
319 320 321 |
# File 'app/models/activity.rb', line 319 def service_time @service_time end |
#skip_callbacks ⇒ Object
Returns the value of attribute skip_callbacks.
319 320 321 |
# File 'app/models/activity.rb', line 319 def skip_callbacks @skip_callbacks end |
#skip_check_for_open_sales_activity ⇒ Object
Returns the value of attribute skip_check_for_open_sales_activity.
319 320 321 |
# File 'app/models/activity.rb', line 319 def skip_check_for_open_sales_activity @skip_check_for_open_sales_activity end |
#skip_check_for_valid_party ⇒ Object
Returns the value of attribute skip_check_for_valid_party.
319 320 321 |
# File 'app/models/activity.rb', line 319 def skip_check_for_valid_party @skip_check_for_valid_party end |
#support_case_closed_reason ⇒ Object
Returns the value of attribute support_case_closed_reason.
319 320 321 |
# File 'app/models/activity.rb', line 319 def support_case_closed_reason @support_case_closed_reason end |
#support_case_event ⇒ Object
Returns the value of attribute support_case_event.
319 320 321 |
# File 'app/models/activity.rb', line 319 def support_case_event @support_case_event end |
#travel_time ⇒ Object
Returns the value of attribute travel_time.
319 320 321 |
# File 'app/models/activity.rb', line 319 def travel_time @travel_time end |
#triggered_communications ⇒ ActiveRecord::Associations::CollectionProxy<Communication>
Returns communications this activity triggered.
144 145 146 147 |
# File 'app/models/activity.rb', line 144 has_many :triggered_communications, class_name: 'Communication', foreign_key: :triggered_by_activity_id, inverse_of: :triggered_activity |
#updated_activities ⇒ Object
Returns the value of attribute updated_activities.
319 320 321 |
# File 'app/models/activity.rb', line 319 def updated_activities @updated_activities end |
#uploads ⇒ ActiveRecord::Associations::CollectionProxy<Upload>
Returns files attached to this activity.
138 |
# File 'app/models/activity.rb', line 138 has_many :uploads, as: :resource, dependent: :destroy |
#visit ⇒ Visit?
Returns the website visit that spawned this activity.
131 |
# File 'app/models/activity.rb', line 131 belongs_to :visit, optional: true |
Class Method Details
.all_activity_filters ⇒ Array<String>
Returns resource types filterable in the activity list.
627 628 629 |
# File 'app/models/activity.rb', line 627 def self.all_activity_filters %w[Party Contact Opportunity] end |
.completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are completed. Active Record Scope
174 |
# File 'app/models/activity.rb', line 174 scope :completed, -> { joins(:activity_result_type).merge(ActivityResultType.completed) } |
.completion_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are completion order. Active Record Scope
179 |
# File 'app/models/activity.rb', line 179 scope :completion_order, -> { order('activities.completion_datetime desc') } |
.embeddable_content_types ⇒ Array<Symbol>
-- Embeddable interface ---------------------------------------------------
696 697 698 |
# File 'app/models/activity.rb', line 696 def self. [:primary] end |
.final_service_completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are final service completed. Active Record Scope
242 |
# File 'app/models/activity.rb', line 242 scope :final_service_completed, -> { where(activity_type_id: FINAL_SERVICES, activity_result_type_id: ActivityResultTypeConstants::CMP) } |
.for_customer_and_contacts ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are for customer and contacts. Active Record Scope
220 |
# File 'app/models/activity.rb', line 220 scope :for_customer_and_contacts, ->(customer) { where(party_id: customer.self_and_contacts_party_ids_arr) } |
.heatwave_assistant_id ⇒ Integer?
Returns the id of the Heatwave assistant employee (bot account).
765 766 767 |
# File 'app/models/activity.rb', line 765 def self.heatwave_assistant_id Employee.joins(:contact_points).merge(ContactPoint.emails).where(ContactPoint[:detail].eq('hwav@warmlyyours.com')).pick(:id) end |
.high_priority ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are high priority. Active Record Scope
177 |
# File 'app/models/activity.rb', line 177 scope :high_priority, -> { joins(:activity_type).where(activity_types: { priority: 1 }) } |
.inbound_emails ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are inbound emails. Active Record Scope
240 |
# File 'app/models/activity.rb', line 240 scope :inbound_emails, -> { where(activity_type_id: ActivityTypeConstants::EMAILIN_TYPES) } |
.initial_service_completed ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are initial service completed. Active Record Scope
241 |
# File 'app/models/activity.rb', line 241 scope :initial_service_completed, -> { where(activity_type_id: INITIAL_SERVICES, activity_result_type_id: ActivityResultTypeConstants::CMP) } |
.iqoppfu ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are iqoppfu. Active Record Scope
172 |
# File 'app/models/activity.rb', line 172 scope :iqoppfu, -> { where(activity_type_id: ActivityTypeConstants::IQOPPFUS_IDS) } |
.leads ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are leads. Active Record Scope
173 |
# File 'app/models/activity.rb', line 173 scope :leads, -> { where(activity_type_id: ActivityTypeConstants::LEAD_FORM) } |
.mcp_searchable? ⇒ Boolean
Returns false — activities are excluded from MCP search.
701 702 703 |
# File 'app/models/activity.rb', line 701 def self.mcp_searchable? false end |
.meeting ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are meeting. Active Record Scope
187 |
# File 'app/models/activity.rb', line 187 scope :meeting, -> { joins(:activity_type).merge(ActivityType.meeting) } |
.natural_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are natural order. Active Record Scope
178 |
# File 'app/models/activity.rb', line 178 scope :natural_order, -> { order(Arel.sql(NATURAL_ORDER)) } |
.non_cob_time(datetime) ⇒ String?
Returns formatted time when it falls outside close-of-business rounding.
739 740 741 742 743 744 745 746 |
# File 'app/models/activity.rb', line 739 def self.non_cob_time(datetime) return unless datetime closing_time = WorkingHours.advance_to_closing_time(datetime) return unless datetime != closing_time && (datetime.min != 0) datetime.strftime('%I:%M %p') end |
.non_notes ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are non notes. Active Record Scope
170 |
# File 'app/models/activity.rb', line 170 scope :non_notes, -> { where.not(activity_type_id: nil) } |
.not_tagged_with ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are not tagged with. Active Record Scope
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'app/models/activity.rb', line 222 scope :not_tagged_with, ->(*) { = [].flatten.filter_map(&:presence).uniq.map(&:downcase) return all if .empty? # Exclude activities whose activity_type has ANY of the specified tags # Activity types without the tags OR activities without an activity type are included tagged_activity_type_ids = ActivityType.joins(:tag_records) .where(tags: { name: }) .distinct .ids if tagged_activity_type_ids.any? where('activities.activity_type_id IS NULL OR activities.activity_type_id NOT IN (?)', tagged_activity_type_ids) else all end } |
.notes_only ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are notes only. Active Record Scope
169 |
# File 'app/models/activity.rb', line 169 scope :notes_only, -> { where(activity_type_id: nil) } |
.open_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are open activities. Active Record Scope
175 |
# File 'app/models/activity.rb', line 175 scope :open_activities, -> { where(activity_result_type_id: nil).non_notes } |
.overdue_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are overdue activities. Active Record Scope
176 |
# File 'app/models/activity.rb', line 176 scope :overdue_activities, -> { open_activities.where(Activity[:target_datetime].lteq(Time.current)) } |
.position_of_activity(activity_id, relation) ⇒ Integer?
Returns 1-based position of the activity within the relation.
365 366 367 |
# File 'app/models/activity.rb', line 365 def self.position_of_activity(activity_id, relation) relation.order(relation.values[:order] || :id).ids.index(activity_id).try(:+, 1) end |
.preloaded ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are preloaded. Active Record Scope
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'app/models/activity.rb', line 191 scope :preloaded, -> { includes( :party, :activity_result_type, :campaign, :call_record, :creator, :updater, :uploads, { activity_type: :tag_records }, { assigned_resource: :profile_image }, { closed_by: :profile_image }, { communication: :uploads }, # parent_activity (with its activity_type) is rendered in the metadata # footer of the selected activity's form (_activity_form.html.erb). { parent_activity: :activity_type }, # Polymorphic resource — Rails groups by resource_type and runs one # query per type. Covers SupportCase / Opportunity / Order / Quote / # RoomConfiguration / SchedulerBooking *and* Invoice / Delivery / Rma / # CreditApplication / PurchaseOrder / CreditMemo / LocatorRecord / # SpiffEnrollment which were previously N+1. :resource ) } |
.quofu ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are quofu. Active Record Scope
171 |
# File 'app/models/activity.rb', line 171 scope :quofu, -> { where(activity_type_id: ActivityTypeConstants::QUOFUS_IDS) } |
.ransackable_scopes(_auth_object = nil) ⇒ Array<Symbol>
Returns scopes ransack may call.
353 354 355 |
# File 'app/models/activity.rb', line 353 def self.ransackable_scopes(_auth_object = nil) %i[tagged_with not_tagged_with] end |
.recency_order ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are recency order. Active Record Scope
184 185 |
# File 'app/models/activity.rb', line 184 scope :recency_order, -> { order(Arel.sql('COALESCE(activities.completion_datetime, activities.target_datetime) DESC NULLS LAST')) } |
.resource_types ⇒ Array<String>
Returns recognized resource_type values.
358 359 360 |
# File 'app/models/activity.rb', line 358 def self.resource_types RESOURCE_TYPES end |
.sales_activities ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are sales activities. Active Record Scope
188 |
# File 'app/models/activity.rb', line 188 scope :sales_activities, -> { joins(:activity_type).merge(ActivityType.sales_activities) } |
.tagged_with ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are tagged with. Active Record Scope
221 |
# File 'app/models/activity.rb', line 221 scope :tagged_with, ->(*) { joins(:activity_type).merge(ActivityType.tagged_with()) } |
.training ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are training. Active Record Scope
186 |
# File 'app/models/activity.rb', line 186 scope :training, -> { joins(:activity_type).merge(ActivityType.training) } |
.visible_by_default ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are visible by default. Active Record Scope
239 |
# File 'app/models/activity.rb', line 239 scope :visible_by_default, -> { not_tagged_with(['drip']) } |
.with_activity_uploads_count ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are with activity uploads count. Active Record Scope
219 |
# File 'app/models/activity.rb', line 219 scope :with_activity_uploads_count, -> { select("activities.*, (select count(u.id) from uploads u where u.resource_type = 'Activity' and u.resource_id = activities.id) as uploads_count") } |
.with_customer_as_c ⇒ ActiveRecord::Relation<Activity>
A relation of Activities that are with customer as c. Active Record Scope
215 216 217 218 |
# File 'app/models/activity.rb', line 215 scope :with_customer_as_c, -> { joins('inner join parties p on p.id = activities.party_id') .joins("left join parties c on ((p.type = 'Contact' and p.customer_id = c.id) or (p.type = 'Customer' and p.id = c.id))") } |
Instance Method Details
#activity_type_for_select ⇒ Array
Returns activity type options scoped to the party.
424 425 426 |
# File 'app/models/activity.rb', line 424 def activity_type_for_select ActivityType.(party:, include_activity_type_id: activity_type_id) end |
#age_in_days ⇒ Integer
Returns days from the effective origin date to completion (or today).
370 371 372 373 |
# File 'app/models/activity.rb', line 370 def age_in_days end_date = closed? ? completion_datetime.to_date : Date.current (end_date - effective_origin_date).to_i end |
#auto_assign(allocate_on = nil, current_user = nil, options = {}) ⇒ Employee?
Auto-assigns this activity to the best available resource.
553 554 555 556 557 558 559 |
# File 'app/models/activity.rb', line 553 def auto_assign(allocate_on = nil, current_user = nil, = {}) self.target_datetime ||= created_at # Set a default if we don't have one for an odd reason self.original_target_datetime ||= target_datetime self.target_datetime = allocate_on if allocate_on && !lock_target_datetime # if allocate on is passed and our activity is not time locked we use it self.original_assigned_resource ||= assigned_resource self.assigned_resource = best_resource_employee(current_user, ) end |
#auto_assign_and_schedule(ideal_target_date = nil, options = {}) ⇒ Boolean
Assigns and schedules this activity on the best available date.
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
# File 'app/models/activity.rb', line 517 def auto_assign_and_schedule(ideal_target_date = nil, = {}) # What is the best target date to schedule this activity? logger = [:logger] || Rails.logger original_scheduled_date = ideal_target_date || target_datetime allocate_on = original_scheduled_date return false unless allocate_on begin logger.debug "Processing activity #{id} with a target date of #{allocate_on}." # Pass the date processed to the calling block so special pre-loading can occur in case of batch reassignments # This is necessary so that as we advance the days we scoop them in our deck yield(allocate_on) if allocate_on.present? && block_given? auto_assign(allocate_on, ) # Provides a resource for that day success = false # Sensible default if assigned_resource.present? # Was a resource available and assigned on that day? save # Then save the record success = true # And mark the operation as a success allocate_on = nil # To break the loop, set allocate_on to nil elsif allocate_on > (original_scheduled_date + 1.month) # Try for one month, if we can't succeed at this point break we'll end up in a never ending loop allocate_on = nil # Breaks the loop logger.error ' * Could not find a suitable resource for activity after 30 days' else # No assigned resource and not past the 1 month limit, so move up one day and try again allocate_on += 1.day end logger.debug " ** assigned resource is #{assigned_resource.try(:full_name)} allocate on is now #{allocate_on}" end while allocate_on success end |
#auto_close_if_applicable(auto_save: true) ⇒ Symbol
Our activity chain might have special rules, such as an auto close result when the due date is reached, useful for triggering
drip email chains
565 566 567 568 569 570 571 572 573 574 |
# File 'app/models/activity.rb', line 565 def auto_close_if_applicable(auto_save: true) return :not_applicable if is_note? || closed? return :not_applicable unless (art = activity_type.auto_close_result_chain) return :not_overdue unless overdue? && art.due_date? self.activity_result_type = art.activity_result_type save! if auto_save :overdue_auto_close end |
#best_resource(current_user = nil, options = {}) ⇒ Integer?
current_user: a value to use as the currently logged in user
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
# File 'app/models/activity.rb', line 582 def best_resource(current_user = nil, = {}) return nil if is_note? current_user = nil unless current_user.is_a?(Employee) # Sensible default when operating in batch mode. current_user ||= self.original_assigned_resource current_user ||= creator if creator.is_a?(Employee) current_user ||= updater if updater.is_a?(Employee) at = activity_type if resource.instance_of?(SupportCase) && customer.nil? resource.assigned_to_id || at.activity_type_assignment_queues.first.try(:fallback_employee_id) || current_user elsif customer at.determine_assigned_resource_id(customer, current_user.try(:id), self.target_datetime, resource, ) end end |
#best_resource_employee(current_user = nil, options = {}) ⇒ Employee?
Returns the best available employee resource for this activity.
604 605 606 607 608 609 |
# File 'app/models/activity.rb', line 604 def best_resource_employee(current_user = nil, = {}) rep_id = best_resource(current_user, ) return Employee.find(rep_id) if rep_id nil end |
#campaign_options_for_select ⇒ Array<Array(String, Integer)>
Returns campaign name/id pairs for the customer.
453 454 455 456 457 458 459 460 |
# File 'app/models/activity.rb', line 453 def = [] if (customer = party.try(:customer) || resource.try(:customer)) = Campaign.joins(audiences: :audience_members).where(audience_members: { customer_id: customer.id }).pluck(:name, :id) << [campaign.name, campaign.id] if campaign end .compact.uniq end |
#cancel(options = {}) ⇒ Boolean
Marks this activity cancelled.
683 684 685 686 |
# File 'app/models/activity.rb', line 683 def cancel( = {}) opts = { activity_result_type_id: ActivityResultTypeConstants::CANCEL, completion_datetime: Time.current }.merge() update(opts) end |
#cancelled? ⇒ Object
Alias for Activity_result_type#cancelled?
259 |
# File 'app/models/activity.rb', line 259 delegate :complete?, :cancelled?, :result_code, to: :activity_result_type, allow_nil: true |
#closed? ⇒ Boolean
Returns true for notes or activities with a result.
501 502 503 |
# File 'app/models/activity.rb', line 501 def closed? activity_type_id.nil? || activity_result_type_id.present? end |
#compact_notes ⇒ String?
Returns notes (or description) with bracketed stamps stripped.
689 690 691 692 |
# File 'app/models/activity.rb', line 689 def compact_notes d = notes || description d.gsub(/\[.*\]/, '').strip end |
#complete(options = {}) ⇒ Boolean
Marks this activity complete.
672 673 674 675 |
# File 'app/models/activity.rb', line 672 def complete( = {}) opts = { activity_result_type_id: ActivityResultTypeConstants::CMP, completion_datetime: Time.current }.merge() update(opts) end |
#complete? ⇒ Object
Alias for Activity_result_type#complete?
259 |
# File 'app/models/activity.rb', line 259 delegate :complete?, :cancelled?, :result_code, to: :activity_result_type, allow_nil: true |
#contact_options_for_select ⇒ Array
Returns contact/organization options from the address book builder.
439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'app/models/activity.rb', line 439 def # Memoize these values to ensure the guard check and the builder call use the same values. # This prevents edge cases where association state changes between the check and the call # (e.g., during failed updates where party_id is cleared but not saved). p = party r = resource return [] unless p || r skip_persons = activity_type&.party_must_be_company? skip_organizations = activity_type&.party_must_be_contact? ContactPoint::AddressBookBuilder.(resource: r, party: p, skip_persons:, skip_organizations:) end |
#content_for_embedding(_content_type = :primary) ⇒ String?
Builds the text embedded for semantic search.
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
# File 'app/models/activity.rb', line 711 def (_content_type = :primary) return nil if notes.blank? || notes.length < MIN_EMBEDDABLE_NOTES_LENGTH parts = [] parts << "Activity: #{activity_type&.task_type || 'Note'}" parts << "Tags: #{activity_type..join(', ')}" if activity_type&.&.any? parts << "Status: #{result_code || 'Open'}" parts << "Priority: #{activity_type.priority}" if activity_type&.priority.present? parts << "Party: #{party&.full_name}" if party parts << "Customer: #{customer&.full_name}" if customer && customer != party parts << "Assigned to: #{assigned_resource&.full_name}" if assigned_resource parts << "Date: #{effective_date.strftime('%B %d, %Y')}" if effective_date parts << "Due: #{target_datetime.strftime('%B %d, %Y')}" if target_datetime && open? parts << "Overdue" if overdue? parts << "Completed: #{completion_datetime.strftime('%B %d, %Y')}" if completion_datetime parts << "Resource: #{resource_type} ##{resource_id}" if resource_type.present? parts << "Campaign: #{campaign&.name}" if campaign parts << compact_notes parts.compact.join("\n\n") end |
#default_resource_for_select ⇒ Array
Returns single [display, identifier] option for the current resource.
804 805 806 807 808 809 |
# File 'app/models/activity.rb', line 804 def default_resource_for_select return [] unless resource formatter = Activity::ResourceList.build_formatter(resource) [[formatter.display, formatter.identifier]] end |
#description ⇒ String?
Returns first 500 chars of notes, else the type's description.
812 813 814 |
# File 'app/models/activity.rb', line 812 def description notes.try(:first, 500) || activity_type.try(:description) end |
#display_date ⇒ String
Returns effective date formatted for CRM display.
632 633 634 |
# File 'app/models/activity.rb', line 632 def display_date effective_date.to_s(has_time? ? :crm_default : :crm_date_only) end |
#display_type ⇒ String
Returns the activity type's task type, or "Note".
506 507 508 |
# File 'app/models/activity.rb', line 506 def display_type activity_type&.task_type || 'Note' end |
#effective_date ⇒ Time
Returns completion, else target, else creation timestamp.
642 643 644 |
# File 'app/models/activity.rb', line 642 def effective_date completion_datetime || self.target_datetime || created_at end |
#effective_origin_date ⇒ Date
Returns original target date, falling back to target or creation date.
397 398 399 |
# File 'app/models/activity.rb', line 397 def effective_origin_date (original_target_datetime || target_datetime || created_at).to_date end |
#embedding_content_changed? ⇒ Boolean
Returns true when notes changed (embedding needs refresh).
733 734 735 |
# File 'app/models/activity.rb', line 733 def saved_change_to_notes? end |
#has_time? ⇒ Boolean
Returns true when the effective date carries a non-midnight time.
637 638 639 |
# File 'app/models/activity.rb', line 637 def has_time? effective_date != effective_date.beginning_of_day end |
#historical_open_activities ⇒ ActiveRecord::Relation<HistoricalOpenActivity>
Track each time the activity was left open at end of day
151 |
# File 'app/models/activity.rb', line 151 has_many :historical_open_activities |
#is_email? ⇒ Object
Alias for Activity_type#is_email?
258 |
# File 'app/models/activity.rb', line 258 delegate :is_email?, to: :activity_type, allow_nil: true |
#is_note? ⇒ Boolean
Returns true when the activity is a plain note (no type).
647 648 649 |
# File 'app/models/activity.rb', line 647 def is_note? activity_type_id.nil? end |
#is_onsite_service? ⇒ Boolean
Returns true for on-site service activity types.
657 658 659 |
# File 'app/models/activity.rb', line 657 def is_onsite_service? [ActivityTypeConstants::ONSITESERVICE, ActivityTypeConstants::SSI_INSTALL, ActivityTypeConstants::SSFIX_ONSITE_SERVICE, ActivityTypeConstants::SGS_ONSITESUPPORT].include?(activity_type_id) end |
#is_service_confirm? ⇒ Boolean
Returns true for service-confirm activity types.
652 653 654 |
# File 'app/models/activity.rb', line 652 def is_service_confirm? [ActivityTypeConstants::SERVICECONFIRM, ActivityTypeConstants::SSI_PREPLAN_MEET, ActivityTypeConstants::SSFIX_SERVICE_CONFIRM].include?(activity_type_id) end |
#is_service_confirm_completed? ⇒ Boolean?
Returns true when a service-confirm type completed with CMP.
923 924 925 926 927 928 929 |
# File 'app/models/activity.rb', line 923 def is_service_confirm_completed? true if activity_type_id.in?([ActivityTypeConstants::SERVICECONFIRM, ActivityTypeConstants::SSI_PREPLAN_MEET, ActivityTypeConstants::SGS_ONSITE_PREPLAN_MEET, ActivityTypeConstants::SGS_REMOTE_PREPLAN_MEET, ActivityTypeConstants::SSFIX_SERVICE_CONFIRM]) && (activity_result_type_id == ActivityResultTypeConstants::CMP) end |
#is_smartservice_related? ⇒ Boolean
Returns true when the type is any SmartService activity type.
909 910 911 912 913 914 915 916 917 918 919 920 |
# File 'app/models/activity.rb', line 909 def activity_type_id.in?([ActivityTypeConstants::SERVICECONFIRM, ActivityTypeConstants::ONSITESERVICE, ActivityTypeConstants::SSI_PREPLAN_MEET, ActivityTypeConstants::SSI_INSTALL, ActivityTypeConstants::SGS_ONSITE_PREPLAN_MEET, ActivityTypeConstants::SGS_REMOTE_PREPLAN_MEET, ActivityTypeConstants::SGS_ONSITESUPPORT, ActivityTypeConstants::SGS_REMOTESUPPORT, ActivityTypeConstants::SSFIX_ONSITE_SERVICE, ActivityTypeConstants::SSFIX_SERVICE_CONFIRM]) end |
#lock ⇒ Boolean
Locks the target datetime, skipping callbacks.
377 378 379 380 381 |
# File 'app/models/activity.rb', line 377 def lock self.skip_callbacks = true self.lock_target_datetime = true save end |
#new_note ⇒ String
Returns always blank; pairs with #new_note= for form binding.
489 490 491 |
# File 'app/models/activity.rb', line 489 def new_note '' end |
#new_note=(n) ⇒ void
This method returns an undefined value.
Appends a timestamped note to the notes field.
478 479 480 481 482 483 484 485 486 |
# File 'app/models/activity.rb', line 478 def new_note=(n) return if n.blank? date_stamp = [] date_stamp << Time.current.to_fs(:crm_default) date_stamp << CurrentScope.user&.show_name_4 if CurrentScope.user.respond_to?(:show_name_4) self.notes = "[#{date_stamp.compact.join(' ')}]\n#{n}\n#{notes}" self.description = n&.first(80) end |
#open? ⇒ Boolean
Returns true when no result type has been set.
662 663 664 |
# File 'app/models/activity.rb', line 662 def open? activity_result_type.nil? end |
#overdue? ⇒ Boolean
Returns true when open and past its target datetime.
463 464 465 466 467 468 |
# File 'app/models/activity.rb', line 463 def overdue? return false if closed? return false if target_datetime.blank? (target_datetime < Time.current) end |
#party_name ⇒ String?
Returns the party's full name.
496 497 498 |
# File 'app/models/activity.rb', line 496 def party_name party&.full_name end |
#preloaded ⇒ ActiveRecord::Relation
Eager-loads all associations required to render activity cards without N+1 queries.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'app/models/activity.rb', line 191 scope :preloaded, -> { includes( :party, :activity_result_type, :campaign, :call_record, :creator, :updater, :uploads, { activity_type: :tag_records }, { assigned_resource: :profile_image }, { closed_by: :profile_image }, { communication: :uploads }, # parent_activity (with its activity_type) is rendered in the metadata # footer of the selected activity's form (_activity_form.html.erb). { parent_activity: :activity_type }, # Polymorphic resource — Rails groups by resource_type and runs one # query per type. Covers SupportCase / Opportunity / Order / Quote / # RoomConfiguration / SchedulerBooking *and* Invoice / Delivery / Rma / # CreditApplication / PurchaseOrder / CreditMemo / LocatorRecord / # SpiffEnrollment which were previously N+1. :resource ) } |
#primary_address_id ⇒ Integer?
Returns the customer's mailing/shipping/billing address id.
622 623 624 |
# File 'app/models/activity.rb', line 622 def primary_address_id customer.try(:mailing_address_id) || customer.try(:shipping_address_id) || customer.try(:billing_address_id) end |
#resource_combo=(val) ⇒ void
This method returns an undefined value.
Backwards-compat: cached browser forms may still submit the old
"ClassName|id" format. Silently convert to GlobalID so
Activity.new(params[:activity]) doesn't blow up.
794 795 796 797 798 799 800 801 |
# File 'app/models/activity.rb', line 794 def resource_combo=(val) if val.present? class_name, rid = val.split("|", 2) self.resource_gid = URI::GID.build(app: GlobalID.app, model_name: class_name, model_id: rid.to_s).to_s else self.resource_gid = nil end end |
#resource_gid ⇒ String?
Returns GlobalID URI for the polymorphic resource.
770 771 772 773 774 |
# File 'app/models/activity.rb', line 770 def resource_gid return unless resource_type.present? && resource_id.present? URI::GID.build(app: GlobalID.app, model_name: resource_type, model_id: resource_id.to_s).to_s end |
#resource_gid=(gid_string) ⇒ void
This method returns an undefined value.
Sets the polymorphic resource from a GlobalID string.
779 780 781 782 783 784 785 786 787 |
# File 'app/models/activity.rb', line 779 def resource_gid=(gid_string) if gid_string.present? && (gid = GlobalID.parse(gid_string)) self.resource_type = gid.model_name self.resource_id = gid.model_id else self.resource_type = nil self.resource_id = nil end end |
#resource_support_case ⇒ SupportCase
Type-specific resource associations for eager loading (polymorphic can't be preloaded)
101 |
# File 'app/models/activity.rb', line 101 belongs_to :resource_support_case, class_name: 'SupportCase', foreign_key: :resource_id, optional: true |
#result_code ⇒ Object Also known as: activity_result_code
Alias for Activity_result_type#result_code
259 |
# File 'app/models/activity.rb', line 259 delegate :complete?, :cancelled?, :result_code, to: :activity_result_type, allow_nil: true |
#result_options_for_select ⇒ Array?
Returns result options for the activity's type.
417 418 419 420 421 |
# File 'app/models/activity.rb', line 417 def return unless activity_type activity_type. end |
#return_to ⇒ Object?
Returns record to redirect back to (resource, else party).
434 435 436 |
# File 'app/models/activity.rb', line 434 def return_to resource || party end |
#sales_activity? ⇒ Boolean?
Returns whether the activity type is a sales activity.
392 393 394 |
# File 'app/models/activity.rb', line 392 def sales_activity? activity_type.try(:sales_activity) end |
#sender_for_email_templates ⇒ Employee?
Returns assigned resource, falling back to updater then creator.
471 472 473 |
# File 'app/models/activity.rb', line 471 def sender_for_email_templates assigned_resource || updater || creator end |
#sender_party ⇒ Party
For emails, who is the sender?
750 751 752 753 754 755 756 757 758 759 760 761 762 |
# File 'app/models/activity.rb', line 750 def sender_party primary_rep = party&.customer&.primary_sales_rep sp = primary_rep if activity_type.sales_rep_as_sender sp ||= activity_type.sender_party sp ||= assigned_resource unless assigned_resource_id == self.class.heatwave_assistant_id sp ||= primary_rep = party.try(:primary_sales_rep) sp ||= creator if creator.is_a?(Employee) sp = nil if sp&.id == self.class.heatwave_assistant_id # just in case we never never allow the bot sp ||= Employee.find(105) # 'Julia Billen' raise StandardError, "Unable to determine sender_party for activity id #{id}" unless sp sp end |
#set_assigned_rep ⇒ Boolean, ...
Defaults assigned/original resource for open activities before save.
883 884 885 886 887 888 |
# File 'app/models/activity.rb', line 883 def set_assigned_rep return true if closed? self.assigned_resource ||= best_resource_employee self.original_assigned_resource ||= self.assigned_resource end |
#set_campaign ⇒ void
This method returns an undefined value.
Subscribes the customer to, and assigns, the applicable campaign.
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
# File 'app/models/activity.rb', line 892 def set_campaign use_campaign = campaign || activity_type&.campaign return unless use_campaign # customer can be determined? return unless (cust = customer) # add customer to campaign res = use_campaign.add_customer cust return unless res.subscribed? # assign this activity to that campaign self.campaign = use_campaign end |
#set_linked_parties ⇒ void
This method returns an undefined value.
Reconciles the parties join table with party_id, linked_party_ids, and
resource-reported ids, dropping ids that no longer exist.
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 |
# File 'app/models/activity.rb', line 819 def set_linked_parties pids = [party_id] # Initially we will use the linked party if linked_party_ids.present? # Array of additional party linked to us if linked_party_ids.is_a?(Array) pids += linked_party_ids elsif linked_party_ids.is_a?(String) pids += linked_party_ids.split(',') end end pids += resource.link_party_ids if resource.respond_to?(:link_party_ids) # Pull those from our resources pids = pids.compact.uniq.map(&:to_i) # Load up customer of contacts (compact to remove nil customer_ids) pids += Contact.where(id: pids).pluck(Arel.sql('distinct customer_id')).compact pids.uniq! # Drop party IDs that no longer exist (e.g. customer merge destroyed the party while a # resource still reports the old id via #link_party_ids — AppSignal #3373 / FK on activities_parties). valid_pids = Party.where(id: pids).ids if valid_pids.size < pids.size logger.warn( "Activity#set_linked_parties skipping missing party_ids=#{(pids - valid_pids).join(', ')} " \ "activity_id=#{persisted? ? id : 'new'}" ) end pids = valid_pids return if pids.blank? # For new records, use standard assignment - no race condition possible yet unless persisted? logger.info "Activity (new) linked party ids will become #{pids.join(', ')}" self.party_ids = pids return end # For existing records, only add party_ids that don't already exist to avoid # race conditions when multiple workers process the same record concurrently existing_pids = party_ids.to_a new_pids = pids - existing_pids return if new_pids.empty? logger.info "Activity #{id} linked party ids adding #{new_pids.join(', ')} (existing: #{existing_pids.join(', ')})" # Use upsert to avoid RecordNotUnique errors from concurrent workers # trying to add the same party-activity associations new_pids.each do |pid| self.class.lease_connection.execute( self.class.sanitize_sql_array([ 'INSERT INTO activities_parties (activity_id, party_id) VALUES (?, ?) ON CONFLICT (party_id, activity_id) DO NOTHING', id, pid ]) ) end # Reload the association to reflect the new state parties.reset rescue ActiveRecord::RecordNotUnique # Race condition - another process already created this record # This is expected when multiple workers process the same invoicing concurrently logger.info "Activity #{id} set_linked_parties encountered RecordNotUnique - records already exist" parties.reset end |
#target_date ⇒ String?
Returns target date formatted for CRM display.
402 403 404 |
# File 'app/models/activity.rb', line 402 def target_date target_datetime.nil? ? nil : target_datetime.to_datetime.to_date.to_fs(:crm_default) end |
#target_datetime_outside_business_hours? ⇒ Boolean
Returns true when no target datetime or it falls outside working hours.
932 933 934 935 936 937 938 |
# File 'app/models/activity.rb', line 932 def target_datetime_outside_business_hours? # The lack of a target datetime will imply we are outside business hours return true unless target_datetime # Because the closing time is exclusive, we need to subtract 1 second to do this check !(target_datetime - 1.second).in_working_hours? end |
#target_time ⇒ String?
Returns target time formatted "03:30 pm".
407 408 409 410 411 412 413 414 |
# File 'app/models/activity.rb', line 407 def target_time return unless target_datetime # The following code causes time zone jumps based on the user # rounded = Time.at((target_datetime.to_time.to_i / 900.0).round * 900) # rounded_dt = target_datetime.is_a?(DateTime) ? rounded.to_datetime : rounded target_datetime.strftime('%I:%M %P') end |
#to_s ⇒ String Also known as: name
Returns "date rep type" summary line.
612 613 614 615 616 617 618 |
# File 'app/models/activity.rb', line 612 def to_s [ target_datetime.try(:to_s).try(:crm_date_only), assigned_resource.try(:name), activity_type.try(:task_type) ].compact.join(' ') end |
#unlock ⇒ Boolean
Unlocks the target datetime, skipping callbacks.
385 386 387 388 389 |
# File 'app/models/activity.rb', line 385 def unlock self.skip_callbacks = true self.lock_target_datetime = false save end |