Class: MailActivity
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- MailActivity
- 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
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Belongs to collapse
Methods included from Models::Auditable
Has many collapse
-
#shipments ⇒ ActiveRecord::Relation<Shipment>
belongs_to :address, optional: true.
- #uploads ⇒ ActiveRecord::Relation<Upload>
Class Method Summary collapse
-
.complete ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are complete.
-
.open ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are open.
-
.packaged ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are packaged.
-
.postage_labeled ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are postage labeled.
-
.postage_rated ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are postage rated.
Instance Method Summary collapse
- #address ⇒ Object
- #all_labels_pdf ⇒ Object
- #calculate_postage_rates ⇒ Object
- #carrier ⇒ Object (also: #reported_carrier)
-
#company_name ⇒ Object
Sanitized name for mailing.
-
#default_carrier ⇒ Object
The default postal carrier for the store's country (used when no rate has been selected yet).
- #formatted_address(separator: '<br>') ⇒ Object
- #generate_all_labels_pdf ⇒ Object
- #generate_labels ⇒ Object
- #manually_complete? ⇒ Boolean
- #name ⇒ Object
- #ok_to_delete? ⇒ Boolean
-
#person_name ⇒ Object
Sanitized name for mailing.
- #ready_to_complete? ⇒ Boolean
- #ready_to_label? ⇒ Boolean
- #ready_to_manually_complete? ⇒ Boolean
- #ready_to_print_labels? ⇒ Boolean
- #ready_to_print_postage_label? ⇒ Boolean
- #ready_to_rate? ⇒ Boolean
- #ready_to_reset? ⇒ Boolean
-
#selected_carrier ⇒ Object
Resolve the carrier from a composite service_code like "UPS::03" or "FedEx::FEDEX_GROUND".
-
#selected_service_code ⇒ Object
Extract the raw carrier service code from a composite service_code.
- #send_canada_post_manual_void_email(tracking_numbers) ⇒ Object
- #store ⇒ Object
- #store_country_iso3 ⇒ Object
- #void_shipments ⇒ Object
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 Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Class Method Details
.complete ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are complete. Active Record Scope
51 |
# File 'app/models/mail_activity.rb', line 51 scope :complete, -> { where("mail_activities.state = 'complete'") } |
.open ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are open. Active Record Scope
47 |
# File 'app/models/mail_activity.rb', line 47 scope :open, -> { where("mail_activities.state = 'open'") } |
.packaged ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are packaged. Active Record Scope
48 |
# File 'app/models/mail_activity.rb', line 48 scope :packaged, -> { where("mail_activities.state = 'packaged'") } |
.postage_labeled ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are postage labeled. Active Record Scope
50 |
# File 'app/models/mail_activity.rb', line 50 scope :postage_labeled, -> { where("mail_activities.state = 'postage_labeled'") } |
.postage_rated ⇒ ActiveRecord::Relation<MailActivity>
A relation of MailActivities that are postage rated. Active Record Scope
49 |
# File 'app/models/mail_activity.rb', line 49 scope :postage_rated, -> { where("mail_activities.state = 'postage_rated'") } |
Instance Method Details
#activity ⇒ Activity
Validations:
32 |
# File 'app/models/mail_activity.rb', line 32 belongs_to :activity, optional: true |
#address ⇒ Object
311 312 313 |
# File 'app/models/mail_activity.rb', line 311 def address activity.party.main_address end |
#all_labels_pdf ⇒ Object
295 296 297 |
# File 'app/models/mail_activity.rb', line 295 def all_labels_pdf uploads.find_by(category: 'all_labels_pdf') end |
#calculate_postage_rates ⇒ Object
173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'app/models/mail_activity.rb', line 173 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) && 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 |
#carrier ⇒ Object Also known as: reported_carrier
168 169 170 |
# File 'app/models/mail_activity.rb', line 168 def carrier selected_carrier end |
#company_name ⇒ Object
Sanitized name for mailing
93 94 95 96 97 98 99 |
# File 'app/models/mail_activity.rb', line 93 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_carrier ⇒ Object
The default postal carrier for the store's country (used when no rate has been selected yet)
148 149 150 |
# File 'app/models/mail_activity.rb', line 148 def default_carrier store&.country_iso3 == 'CAN' ? 'Canadapost' : 'USPS' end |
#formatted_address(separator: '<br>') ⇒ Object
305 306 307 308 309 |
# File 'app/models/mail_activity.rb', line 305 def formatted_address(separator: '<br>') return unless (a = address) a.full_address(true, separator, person_name) end |
#generate_all_labels_pdf ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 |
# File 'app/models/mail_activity.rb', line 256 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_labels ⇒ Object
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 247 248 249 250 251 252 253 254 |
# File 'app/models/mail_activity.rb', line 187 def generate_labels # Guard the ShipEngine call: a MailActivity with no shipments awaiting a # label sends an empty `shipment.packages` payload, which ShipEngine # rejects with a `ShipEngineRb::Exceptions::ValidationError` that then # round-trips through `WyShipping.ship_mail_activity` -> `ErrorReporting.error` # to AppSignal (#5307). Surface a user-friendly status instead. if shipments.awaiting_label.empty? Rails.logger.warn("MailActivity #{id}: generate_labels called with no shipments awaiting label; skipping ShipEngine call") return { status_code: :error, status_message: 'No shipments are awaiting a label for this mail activity.' } end 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 next unless old_label_path label_exceptions << (i + 1) unless shipment.generate_ship_label_pdf(old_label_path, carrier) end if label_exceptions.empty? && status_code == :ok generate_all_labels_pdf = "Labels generated for #{shipments.completed.length} packages. Actual charge: #{store.catalogs.first.currency_symbol}#{actual_shipping_cost}. #{}" save { status_code: status_code, status_message: } else msg = +"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 |
#mailing ⇒ Mailing
Validations:
33 |
# File 'app/models/mail_activity.rb', line 33 belongs_to :mailing, optional: true |
#manually_complete? ⇒ Boolean
135 136 137 |
# File 'app/models/mail_activity.rb', line 135 def manually_complete? complete? and !ready_to_print_labels? end |
#name ⇒ Object
101 102 103 |
# File 'app/models/mail_activity.rb', line 101 def name "Mail Activity #{activity.activity_type.task_type}, ID: #{activity.id}, to: #{address.full_address(true, ', ')}" end |
#ok_to_delete? ⇒ Boolean
299 300 301 302 303 |
# File 'app/models/mail_activity.rb', line 299 def ok_to_delete? ok = true ok = false unless open? && shipments.all?(&:ok_to_delete_with_mail_activity?) ok end |
#person_name ⇒ Object
Sanitized name for mailing
84 85 86 87 88 89 90 |
# File 'app/models/mail_activity.rb', line 84 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
123 124 125 |
# File 'app/models/mail_activity.rb', line 123 def ready_to_complete? shipments.any?(&:label_complete?) end |
#ready_to_label? ⇒ Boolean
119 120 121 |
# File 'app/models/mail_activity.rb', line 119 def ready_to_label? postage_rates&.any? and shipments.any?(&:awaiting_label?) end |
#ready_to_manually_complete? ⇒ Boolean
127 128 129 |
# File 'app/models/mail_activity.rb', line 127 def ready_to_manually_complete? shipments.none? { |s| s.label_complete? or s.awaiting_label? } end |
#ready_to_print_labels? ⇒ Boolean
131 132 133 |
# File 'app/models/mail_activity.rb', line 131 def ready_to_print_labels? all_labels_pdf.present? and shipments.label_complete.any? end |
#ready_to_print_postage_label? ⇒ Boolean
105 106 107 108 109 |
# File 'app/models/mail_activity.rb', line 105 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
115 116 117 |
# File 'app/models/mail_activity.rb', line 115 def ready_to_rate? shipments.any?(&:awaiting_label?) end |
#ready_to_reset? ⇒ Boolean
111 112 113 |
# File 'app/models/mail_activity.rb', line 111 def ready_to_reset? shipments.all?(&:ok_to_delete_with_mail_activity?) end |
#selected_carrier ⇒ Object
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.
154 155 156 157 158 |
# File 'app/models/mail_activity.rb', line 154 def selected_carrier return default_carrier unless service_code&.include?('::') service_code.split('::').first end |
#selected_service_code ⇒ Object
Extract the raw carrier service code from a composite service_code.
"UPS::03" → "03", "USPS::Priority" → "Priority", "Priority" → "Priority"
162 163 164 165 166 |
# File 'app/models/mail_activity.rb', line 162 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
291 292 293 |
# File 'app/models/mail_activity.rb', line 291 def send_canada_post_manual_void_email(tracking_numbers) Mailer.canada_post_manual_void_email_for_mail_activity(self, tracking_numbers).deliver end |
#shipments ⇒ ActiveRecord::Relation<Shipment>
belongs_to :address, optional: true
35 |
# File 'app/models/mail_activity.rb', line 35 has_many :shipments, dependent: :destroy |
#store ⇒ Object
139 140 141 |
# File 'app/models/mail_activity.rb', line 139 def store @store ||= mailing.store end |
#store_country_iso3 ⇒ Object
143 144 145 |
# File 'app/models/mail_activity.rb', line 143 def store_country_iso3 store&.mailing_address&.country_iso3 end |
#uploads ⇒ ActiveRecord::Relation<Upload>
36 |
# File 'app/models/mail_activity.rb', line 36 has_many :uploads, as: :resource, dependent: :destroy |
#void_shipments ⇒ Object
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'app/models/mail_activity.rb', line 268 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 |