Class: MailActivity

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/mail_activity.rb

Overview

== Schema Information

Table name: mail_activities
Database name: primary

id :integer not null, primary key
actual_shipping_cost :decimal(, )
postage_rates :hstore is an Array
service_code :string
state :string
activity_id :integer
address_id :integer
mailing_id :integer

Indexes

index_mail_activities_on_activity_id (activity_id)
index_mail_activities_on_address_id (address_id)
index_mail_activities_on_mailing_id (mailing_id)
index_mail_activities_on_state (state)

Foreign Keys

fk_rails_... (activity_id => activities.id) ON DELETE => cascade
fk_rails_... (address_id => addresses.id)
fk_rails_... (mailing_id => mailings.id)

Constant Summary

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

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::EventPublishable

#publish_event

Class Method Details

.completeActiveRecord::Relation<MailActivity>

A relation of MailActivities that are complete. Active Record Scope

Returns:

See Also:



50
# File 'app/models/mail_activity.rb', line 50

scope :complete, -> { where("mail_activities.state = 'complete'") }

.openActiveRecord::Relation<MailActivity>

A relation of MailActivities that are open. Active Record Scope

Returns:

See Also:



46
# File 'app/models/mail_activity.rb', line 46

scope :open, -> { where("mail_activities.state = 'open'") }

.packagedActiveRecord::Relation<MailActivity>

A relation of MailActivities that are packaged. Active Record Scope

Returns:

See Also:



47
# File 'app/models/mail_activity.rb', line 47

scope :packaged, -> { where("mail_activities.state = 'packaged'") }

.postage_labeledActiveRecord::Relation<MailActivity>

A relation of MailActivities that are postage labeled. Active Record Scope

Returns:

See Also:



49
# File 'app/models/mail_activity.rb', line 49

scope :postage_labeled, -> { where("mail_activities.state = 'postage_labeled'") }

.postage_ratedActiveRecord::Relation<MailActivity>

A relation of MailActivities that are postage rated. Active Record Scope

Returns:

See Also:



48
# File 'app/models/mail_activity.rb', line 48

scope :postage_rated, -> { where("mail_activities.state = 'postage_rated'") }

Instance Method Details

#activityActivity

Returns:

See Also:

Validations:



31
# File 'app/models/mail_activity.rb', line 31

belongs_to :activity, optional: true

#addressObject



302
303
304
# File 'app/models/mail_activity.rb', line 302

def address
  activity.party.main_address
end

#all_labels_pdfObject



286
287
288
# File 'app/models/mail_activity.rb', line 286

def all_labels_pdf
  uploads.find_by(category: 'all_labels_pdf')
end

#calculate_postage_ratesObject



172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'app/models/mail_activity.rb', line 172

def calculate_postage_rates
  res = WyShipping.find_mail_activity_shipping_rates(self)
  rate_hashes = []
  rate_hashes = (res[:request_response][:rates] || []).sort_by { |r| r[:total_charges].to_f } if res[:request_response]
  if res[:status_code] == :ok and rate_hashes.any?
    self.postage_rates = rate_hashes # this will stringify the keys in the hash array
    self.service_code = rate_hashes.first[:service_code] if rate_hashes.any?
    # @mail_activity.service_code = "MediaMail" if rate_hashes.detect{|rh| rh[:service_code] == "MediaMail"}
    postage_rate_mail_activity!
    reload
  end
  res
end

#carrierObject Also known as: reported_carrier



167
168
169
# File 'app/models/mail_activity.rb', line 167

def carrier
  selected_carrier
end

#company_nameObject

Sanitized name for mailing



92
93
94
95
96
97
98
# File 'app/models/mail_activity.rb', line 92

def company_name
  cn = activity&.party&.full_name unless activity&.party&.is_person?
  cn ||= address&.party&.full_name unless address&.party&.is_person?
  return unless cn

  cn.to_s.gsub(/[^0-9a-z\s]/i, '')[0..34]
end

#default_carrierObject

The default postal carrier for the store's country (used when no rate has been selected yet)



147
148
149
# File 'app/models/mail_activity.rb', line 147

def default_carrier
  store&.country_iso3 == 'CAN' ? 'Canadapost' : 'USPS'
end

#formatted_address(separator: '<br>') ⇒ Object



296
297
298
299
300
# File 'app/models/mail_activity.rb', line 296

def formatted_address(separator: '<br>')
  return unless a = address

  a.full_address(true, separator, person_name)
end

#generate_all_labels_pdfObject



248
249
250
251
252
253
254
255
256
257
258
# File 'app/models/mail_activity.rb', line 248

def generate_all_labels_pdf
  all_labels = shipments.completed.map(&:ship_label_pdf)
  file_name = "#{id}_all_labels_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf".downcase
  all_labels_path = Rails.application.config.x.temp_storage_path.join(file_name)
  Rails.logger.debug { "self.generate_all_labels_pdf: all_labels_path: #{all_labels_path}, all_labels: #{all_labels.inspect}" }
  PdfTools.combine(all_labels, output_file_path: all_labels_path)
  upload = Upload.uploadify(all_labels_path, 'all_labels_pdf')
  raise 'Combo label was not generated' unless upload

  uploads << upload
end

#generate_labelsObject



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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/mail_activity.rb', line 186

def generate_labels
  shipping_result = WyShipping.ship_mail_activity(self)
  Rails.logger.info("#{Time.current}: MailActivity ID #{id}, labels generated START LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$, status_code: #{shipping_result[:status_code]}")
  if shipping_result[:status_code] == :error
    Rails.logger.info("#{carrier}, request/response:")
    Rails.logger.info("#{begin
      shipping_result[:shipment][:ship_reply_xml]
    rescue StandardError
      nil
    end}
")
    { status_code: shipping_result[:status_code], status_message: shipping_result[:status_message] }
  else
    # only save if labels successfully generated
    # first pass get tracking numbers and charges
    Rails.logger.debug("Shipping result received", mail_activity_id: id)
    self.actual_shipping_cost = shipping_result[:shipment][:total_charges]
    save # pure hacking to get this to work
    reload # pure hacking to get this to work
    shipments.awaiting_label.each_with_index do |shipment, i|
      ship_result_label = shipping_result.dig(:shipment,:labels,i)

      Rails.logger.debug "shipment: #{shipment.inspect}"
      Rails.logger.debug "shipping_result[:shipment][:labels][i]: #{ship_result_label.inspect}"
      Rails.logger.debug "shipping_result[:shipment][:labels][i][:tracking_number]: #{ship_result_label[:tracking_number] || 'not there'}"

      shipment.tracking_number = ship_result_label[:tracking_number]
      shipment.shipengine_label_id = ship_result_label[:shipengine_label_id]
      shipment.label_generated!
    end
    Rails.logger.info("#{Time.current}: MailActivity ID #{id}, labels generated END LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$")
    # save this mail_activity
    save
    # second pass generate the labels from the temporary paths
    label_exceptions = []
    status_code = :ok
    shipments.label_complete.each_with_index do |shipment, i|
      ship_result_label = shipping_result.dig(:shipment,:labels,i)
      old_label_path = ship_result_label[:image]&.path
      # skip when label path is nil
      if old_label_path
        if shipment.generate_ship_label_pdf(old_label_path, carrier)
        else
          label_exceptions << (i + 1)
        end
      end
    end
    if label_exceptions.empty? && status_code == :ok
      generate_all_labels_pdf
      status_message = "Labels generated for #{shipments.completed.length} packages. Actual charge: #{store.catalogs.first.currency_symbol}#{actual_shipping_cost}. #{status_message}"
      save
      { status_code: status_code, status_message: status_message }
    else
      msg = String.new("Can't generate labels")
      msg << ", there was an issue with labels #{label_exceptions.map { |l| l + 1 }.join(', ')}" if label_exceptions.any?
      msg << ", status code returned:  #{status_code}" unless status_code == :ok
      msg << '.'
      { status_code: :error, status_message: msg }
    end
  end
end

#mailingMailing

Returns:

See Also:

Validations:



32
# File 'app/models/mail_activity.rb', line 32

belongs_to :mailing, optional: true

#manually_complete?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'app/models/mail_activity.rb', line 134

def manually_complete?
  complete? and !ready_to_print_labels?
end

#nameObject



100
101
102
# File 'app/models/mail_activity.rb', line 100

def name
  "Mail Activity #{activity.activity_type.task_type}, ID: #{activity.id}, to: #{address.full_address(include_recipient = true, sep = ', ')}"
end

#ok_to_delete?Boolean

Returns:

  • (Boolean)


290
291
292
293
294
# File 'app/models/mail_activity.rb', line 290

def ok_to_delete?
  ok = true
  ok = false unless open? and shipments.all? { |s| s.ok_to_delete_with_mail_activity? }
  ok
end

#person_nameObject

Sanitized name for mailing



83
84
85
86
87
88
89
# File 'app/models/mail_activity.rb', line 83

def person_name
  pn = activity.party.full_name if activity.party.is_person?
  pn ||= address.party.full_name if address.party&.is_person?
  return unless pn

  pn.to_s.gsub(/[^0-9a-z\s]/i, '')[0..34]
end

#ready_to_complete?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'app/models/mail_activity.rb', line 122

def ready_to_complete?
  shipments.any? { |s| s.label_complete? }
end

#ready_to_label?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'app/models/mail_activity.rb', line 118

def ready_to_label?
  postage_rates and postage_rates.any? and shipments.any? { |s| s.awaiting_label? }
end

#ready_to_manually_complete?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'app/models/mail_activity.rb', line 126

def ready_to_manually_complete?
  !shipments.any? { |s| (s.label_complete? or s.awaiting_label?) }
end

#ready_to_print_labels?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'app/models/mail_activity.rb', line 130

def ready_to_print_labels?
  all_labels_pdf.present? and shipments.label_complete.any?
end

#ready_to_print_postage_label?Boolean

Returns:

  • (Boolean)


104
105
106
107
108
# File 'app/models/mail_activity.rb', line 104

def ready_to_print_postage_label?
  (postage_labeled? or complete?) and
    all_labels_pdf.present? and
    shipments.label_complete.any?
end

#ready_to_rate?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'app/models/mail_activity.rb', line 114

def ready_to_rate?
  shipments.any? { |s| s.awaiting_label? }
end

#ready_to_reset?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'app/models/mail_activity.rb', line 110

def ready_to_reset?
  shipments.all? { |s| s.ok_to_delete_with_mail_activity? }
end

#selected_carrierObject

Resolve the carrier from a composite service_code like "UPS::03" or "FedEx::FEDEX_GROUND".
Falls back to the default postal carrier for legacy service codes without a prefix.



153
154
155
156
157
# File 'app/models/mail_activity.rb', line 153

def selected_carrier
  return default_carrier unless service_code&.include?('::')

  service_code.split('::').first
end

#selected_service_codeObject

Extract the raw carrier service code from a composite service_code.
"UPS::03" → "03", "USPS::Priority" → "Priority", "Priority" → "Priority"



161
162
163
164
165
# File 'app/models/mail_activity.rb', line 161

def selected_service_code
  return service_code unless service_code&.include?('::')

  service_code.split('::').last
end

#send_canada_post_manual_void_email(tracking_numbers) ⇒ Object



282
283
284
# File 'app/models/mail_activity.rb', line 282

def send_canada_post_manual_void_email(tracking_numbers)
  Mailer.canada_post_manual_void_email_for_mail_activity(self, tracking_numbers).deliver
end

#shipmentsActiveRecord::Relation<Shipment>

belongs_to :address, optional: true

Returns:

See Also:



34
# File 'app/models/mail_activity.rb', line 34

has_many :shipments, dependent: :destroy

#storeObject



138
139
140
# File 'app/models/mail_activity.rb', line 138

def store
  @store ||= mailing.store
end

#store_country_iso3Object



142
143
144
# File 'app/models/mail_activity.rb', line 142

def store_country_iso3
  store&.mailing_address&.country_iso3
end

#uploadsActiveRecord::Relation<Upload>

Returns:

  • (ActiveRecord::Relation<Upload>)

See Also:



35
# File 'app/models/mail_activity.rb', line 35

has_many :uploads, as: :resource, dependent: :destroy

#void_shipmentsObject



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'app/models/mail_activity.rb', line 260

def void_shipments
  if shipments.label_complete.any?
    tracking_numbers = shipments.label_complete.pluck(:tracking_number)
    shipping_result = {}
    shipping_result[:status_code] = :ok
    shipping_result = WyShipping.void_mail_activity(self)
    # going to go merrily along but send admin notification if this didn't properly void
    if shipping_result[:status_code] != :ok
      Mailer.admin_notification("mail_activity#void_shipments for mail_activity #{id} returned error shipping_result[:status_code]: #{shipping_result[:status_code]}", "mail_activity#void_shipments for mail_activity #{id} returned error shipping_result[:status_code]: #{shipping_result[:status_code]}, shipping_result[:status_message]: #{shipping_result[:status_message]}, shipment tracking numbers: #{tracking_numbers.join(', ')}").deliver_now
      send_canada_post_manual_void_email(tracking_numbers) if carrier == 'Canadapost'
    end
    shipments.label_complete.each(&:label_voided!)
    self.postage_rates = []
    self.service_code = nil
    reset_mail_activity!
    { status_code: shipping_result[:status_code],
      status_message: "Please ensure you manually void manual shipments. Shipments voided. #{shipping_result[:status_message]}" }
  else
    { status_code: :error, status_message: "Can't void postage label(s) because the mail activity has no postage label(s)." }
  end
end