Class: Certification
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Certification
- Includes:
- Models::Auditable, Models::LiquidMethods
- Defined in:
- app/models/certification.rb
Overview
== Schema Information
Table name: certifications
Database name: primary
id :bigint not null, primary key
attachment_name :string
attachment_uid :string
certified_by :integer
certified_on :datetime
expiration_reminder_10_days_sent :boolean default(FALSE), not null
expiration_reminder_10_days_sent_on :datetime
expiration_reminder_30_days_sent :boolean default(FALSE), not null
expiration_reminder_30_days_sent_on :datetime
expired_reminder_sent :boolean default(FALSE), not null
expired_reminder_sent_on :datetime
expires_at :datetime
reference :string
state :string
title :string
welcome_meeting :boolean default(FALSE), not null
welcome_meeting_date :datetime
workshop_attended :boolean
workshop_attended_date :datetime
created_at :datetime not null
updated_at :datetime not null
course_enrollment_id :integer
party_id :integer
Indexes
idx_party_id (party_id)
An installer's certification credential, issued after passing a course exam.
Lifecycle: pending → requested → active → expired (or cancelled from
pending/requested when staff reject the request). Activation is gated by
#can_be_activated?: active liability insurance, a welcome meeting, workshop
attendance, and a signed installer agreement. On activation the certification
receives a CER#### reference number, a one-year expiry, and the customer a
congratulations email; expiry reminders are handled by
CertificationCheckWorker.
Defined Under Namespace
Classes: InsuranceEscalation
Constant Summary
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Models::Schedulable
Models::Schedulable::SIMPLE_FORM_OPTIONS
Belongs to collapse
-
#contact ⇒ Party
Same-FK alias of #party used by contact-centric call sites.
-
#course_enrollment ⇒ CourseEnrollment
The enrollment this certification was earned through.
-
#customer ⇒ Party
Same-FK alias of #party used by customer-centric call sites.
-
#party ⇒ Party
The certified installer (STI Party: Customer/Contact/Employee).
Methods included from Models::Auditable
Has one collapse
-
#agreement ⇒ Agreement
The installer agreement that must be signed before activation.
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are active.
-
.expired ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are expired.
-
.requested ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are requested.
-
.title ⇒ String?
Class-level title fallback used by legacy call sites.
Instance Method Summary collapse
-
#activation_blockers ⇒ Array<String>
Human-readable list of unmet activation requirements, for surfacing to staff when an activation attempt is refused.
-
#agreement_ok? ⇒ Boolean
Whether the installer agreement exists and has been signed.
-
#can_be_activated? ⇒ Boolean
Whether every activation requirement is met (state-machine guard for the
activateevent). -
#certificate_link ⇒ String
Customer-portal URL of this certification (exposed to Liquid templates).
-
#create_agreement ⇒ void
Builds the installer agreement and sends it out for e-signature (after_create callback).
-
#create_welcome_meeting_activity ⇒ Activity
Creates the welcome-meeting task for staff (fired on
request). -
#create_workshop_activity ⇒ Activity
Creates the workshop-scheduling task for staff (fired on
request). -
#liability_ok? ⇒ Boolean
Whether the installer has liability insurance effective right now.
-
#possible_events ⇒ Array<Symbol>
State-machine events currently available from this record's state.
-
#possible_events_for_select ⇒ Array<Array(String, Symbol)>
#possible_events shaped for a select input.
-
#send_admin_alert ⇒ void
Alerts staff that a new certification was requested (fired on the
requesttransition). -
#send_congratulations_email ⇒ Communication?
Emails the installer their NEW_INSTALLER congratulations (fired on activation).
-
#send_expiration_reminder ⇒ Boolean
Notifies staff and the installer that the certification expired, and stamps the sent flag (fired on the active → expired transition).
-
#send_expiration_reminder_10 ⇒ Boolean
Emails the 10-days-to-expiry reminder and stamps the sent flag.
-
#send_expiration_reminder_30 ⇒ Boolean
Emails the 30-days-to-expiry reminder and stamps the sent flag.
-
#set_party ⇒ void
Derives the certified party from the enrollment (before_create callback).
-
#set_reference_number ⇒ Boolean
Assigns the public
CER####certificate number (fired on activation). -
#set_title ⇒ void
Defaults the title to the course name (before_create callback).
-
#welcome_meeting_ok? ⇒ Boolean
Whether the onboarding welcome meeting has taken place.
-
#workshop_attended_ok? ⇒ Boolean
Whether the installer attended at least one workshop.
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, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Models::Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Class Method Details
.active ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are active. Active Record Scope
73 |
# File 'app/models/certification.rb', line 73 scope :active, -> { where(state: 'active') } |
.expired ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are expired. Active Record Scope
74 |
# File 'app/models/certification.rb', line 74 scope :expired, -> { where(state: 'expired') } |
.requested ⇒ ActiveRecord::Relation<Certification>
A relation of Certifications that are requested. Active Record Scope
75 |
# File 'app/models/certification.rb', line 75 scope :requested, -> { where(state: 'requested') } |
.title ⇒ String?
Class-level title fallback used by legacy call sites.
280 281 282 |
# File 'app/models/certification.rb', line 280 def self.title title || course_enrollment.course&.name end |
Instance Method Details
#activation_blockers ⇒ Array<String>
Human-readable list of unmet activation requirements, for surfacing to
staff when an activation attempt is refused.
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'app/models/certification.rb', line 146 def activation_blockers blockers = [] # There needs to be at least 1 liability insurance active at this moment blockers << 'active proof of liability insurance' unless liability_ok? # An online welcome meeting needs to happen before being activated blockers << 'welcome meeting' unless welcome_meeting_ok? # Pro needs to attend at least one workshop blockers << 'workshop attendance' unless workshop_attended_ok? # The agreement needs to be signed and uploaded blockers << 'signed installer agreement' unless agreement_ok? blockers end |
#agreement ⇒ Agreement
The installer agreement that must be signed before activation.
69 |
# File 'app/models/certification.rb', line 69 has_one :agreement, as: :resource |
#agreement_ok? ⇒ Boolean
Whether the installer agreement exists and has been signed.
183 184 185 |
# File 'app/models/certification.rb', line 183 def agreement_ok? agreement.present? && agreement.signed? end |
#can_be_activated? ⇒ Boolean
Whether every activation requirement is met (state-machine guard for
the activate event).
138 139 140 |
# File 'app/models/certification.rb', line 138 def can_be_activated? activation_blockers.empty? end |
#certificate_link ⇒ String
Customer-portal URL of this certification (exposed to Liquid templates).
287 288 289 |
# File 'app/models/certification.rb', line 287 def certificate_link "#{WEB_URL}/my_account/certifications/#{id}" end |
#contact ⇒ Party
Same-FK alias of #party used by contact-centric call sites.
Deliberately declared without inverse_of: — contact is a same-FK alias
of customer/party (all three resolve party_id to the STI-typed Party).
The base Party class has no :certifications association — only the
subclasses (Contact/Customer/Employee) do — so inverse_of: :certifications
raised "Could not find the inverse association for contact (:certifications
in Party)" whenever #contact was touched (AppSignal #6034). The sibling
:customer/:party work fine without it.
66 |
# File 'app/models/certification.rb', line 66 belongs_to :contact, class_name: 'Party', foreign_key: 'party_id', optional: true |
#course_enrollment ⇒ CourseEnrollment
The enrollment this certification was earned through.
52 |
# File 'app/models/certification.rb', line 52 belongs_to :course_enrollment, optional: true |
#create_agreement ⇒ void
This method returns an undefined value.
Builds the installer agreement and sends it out for e-signature
(after_create callback).
212 213 214 215 216 217 218 |
# File 'app/models/certification.rb', line 212 def create_agreement agreement = build_agreement(sign_method: 'esignatures', template: 'installer_agreement') agreement.agreement_participants.new( party_id: party_id ) agreement.send_to_esignatures if agreement.save end |
#create_welcome_meeting_activity ⇒ Activity
Creates the welcome-meeting task for staff (fired on request).
319 320 321 322 323 324 325 |
# File 'app/models/certification.rb', line 319 def create_welcome_meeting_activity Activity.create(lock_target_datetime: true, activity_type_id: ActivityType.find_by(task_type: 'INSTALL_CERTIFICATION_PM')&.id, party: party, resource: self, notes: "Welcome meeting for new certification request. Please arrange meeting.") end |
#create_workshop_activity ⇒ Activity
Creates the workshop-scheduling task for staff (fired on request).
308 309 310 311 312 313 314 |
# File 'app/models/certification.rb', line 308 def create_workshop_activity Activity.create(lock_target_datetime: true, activity_type_id: ActivityType.find_by(task_type: 'INSTALL_CERTIFICATION_PWS')&.id, party: party, resource: self, notes: "New certification requires workshop to attend. Please schedule and verify this customer attends.") end |
#customer ⇒ Party
Same-FK alias of #party used by customer-centric call sites.
56 |
# File 'app/models/certification.rb', line 56 belongs_to :customer, class_name: 'Party', foreign_key: 'party_id', optional: true |
#liability_ok? ⇒ Boolean
Whether the installer has liability insurance effective right now.
162 163 164 |
# File 'app/models/certification.rb', line 162 def liability_ok? party.customer.liability_insurances.active.any? && customer.liability_insurances.effective_on(Time.current).present? end |
#party ⇒ Party
The certified installer (STI Party: Customer/Contact/Employee).
54 |
# File 'app/models/certification.rb', line 54 belongs_to :party, optional: true |
#possible_events ⇒ Array<Symbol>
State-machine events currently available from this record's state.
294 295 296 |
# File 'app/models/certification.rb', line 294 def possible_events state_transitions.map(&:event).sort end |
#possible_events_for_select ⇒ Array<Array(String, Symbol)>
#possible_events shaped for a select input.
301 302 303 |
# File 'app/models/certification.rb', line 301 def possible_events_for_select possible_events.map { |evt| [evt.to_s.titleize, evt] } end |
#send_admin_alert ⇒ void
This method returns an undefined value.
Alerts staff that a new certification was requested (fired on the
request transition).
273 274 275 |
# File 'app/models/certification.rb', line 273 def send_admin_alert TrainingMailer.new_certification_request(self).deliver_later! end |
#send_congratulations_email ⇒ Communication?
Emails the installer their NEW_INSTALLER congratulations (fired on activation).
223 224 225 226 227 228 229 |
# File 'app/models/certification.rb', line 223 def send_congratulations_email customer = party CommunicationBuilder.new(resource: self, recipient_party: customer, use_best_email: true, template_system_code: 'NEW_INSTALLER').create end |
#send_expiration_reminder ⇒ Boolean
Notifies staff and the installer that the certification expired, and
stamps the sent flag (fired on the active → expired transition).
259 260 261 262 263 264 265 266 267 |
# File 'app/models/certification.rb', line 259 def send_expiration_reminder TrainingMailer.certification_expired(self).deliver_later! customer = party CommunicationBuilder.new(resource: self, recipient_party: customer, use_best_email: true, template_system_code: 'CERT_EXPIRED').create update(expired_reminder_sent: true, expired_reminder_sent_on: Time.current) end |
#send_expiration_reminder_10 ⇒ Boolean
Emails the 10-days-to-expiry reminder and stamps the sent flag.
246 247 248 249 250 251 252 253 |
# File 'app/models/certification.rb', line 246 def send_expiration_reminder_10 customer = party CommunicationBuilder.new(resource: self, recipient_party: customer, use_best_email: true, template_system_code: 'CERT_EXPIRES_10').create update(expiration_reminder_10_days_sent: true, expiration_reminder_10_days_sent_on: Time.current) end |
#send_expiration_reminder_30 ⇒ Boolean
Emails the 30-days-to-expiry reminder and stamps the sent flag.
234 235 236 237 238 239 240 241 |
# File 'app/models/certification.rb', line 234 def send_expiration_reminder_30 customer = party CommunicationBuilder.new(resource: self, recipient_party: customer, use_best_email: true, template_system_code: 'CERT_EXPIRES_30').create update(expiration_reminder_30_days_sent: true, expiration_reminder_30_days_sent_on: Time.current) end |
#set_party ⇒ void
This method returns an undefined value.
Derives the certified party from the enrollment (before_create callback).
204 205 206 |
# File 'app/models/certification.rb', line 204 def set_party self.party_id = course_enrollment.party.id if course_enrollment.party.present? && party_id.nil? end |
#set_reference_number ⇒ Boolean
Assigns the public CER#### certificate number (fired on activation).
197 198 199 |
# File 'app/models/certification.rb', line 197 def set_reference_number update(reference: "CER#{id.to_s.rjust(4, '0')}") end |
#set_title ⇒ void
This method returns an undefined value.
Defaults the title to the course name (before_create callback).
190 191 192 |
# File 'app/models/certification.rb', line 190 def set_title self.title = course_enrollment&.course&.name if title.blank? end |
#welcome_meeting_ok? ⇒ Boolean
Whether the onboarding welcome meeting has taken place.
169 170 171 |
# File 'app/models/certification.rb', line 169 def welcome_meeting_ok? welcome_meeting.to_b end |
#workshop_attended_ok? ⇒ Boolean
Whether the installer attended at least one workshop.
176 177 178 |
# File 'app/models/certification.rb', line 176 def workshop_attended_ok? workshop_attended.to_b end |