Class: Delivery

Overview

== Schema Information

Table name: deliveries
Database name: primary

id :integer not null, primary key
actual_shipping_cost :decimal(, )
bill_shipping_to_customer :boolean
carrier_bol :string
carrier_responses :jsonb
cod_collection_type :string(255)
do_not_recalculate :boolean
do_not_reserve_stock :boolean default(FALSE)
flag_failed_return_label :boolean default(FALSE), not null
freight_load_number :string
freight_order_number :string
future_release_date :date
incorrectly_packaged_ups_canada_order :boolean
incorrectly_packaged_ups_canada_order_fixed :boolean
is_newly_created_from_delivery_quote :boolean default(FALSE), not null
jde_shipping_stop_code :string(255)
label_instructions :string(255)
line_total :decimal(10, 2)
locked :boolean default(FALSE), not null
ltl_freight :boolean
ltl_freight_guaranteed :boolean
ltl_pro_number :string
manual_release_only :boolean
master_tracking_number :string(255)
md5_hash_override :string
old_shipping_cost :decimal(8, 2)
packaged_items_md5_hash :string(255)
pickup_confirmation_number :string
quoted_shipping_cost :decimal(8, 2)
saturday_delivery :boolean
ship_labeled_at :datetime
shipment_instructions :text
shipped_date :datetime
shipping_cost :decimal(, )
signature_confirmation :boolean
state :string(255)
suggested_packaging_text :text
tax_total :decimal(8, 2)
total :decimal(10, 2)
created_at :datetime
updated_at :datetime
destination_address_id :integer
order_id :integer
origin_address_id :integer
prepack_requester_id :integer
quote_id :integer
selected_shipping_cost_id :integer
shipengine_label_id :string
shipping_account_number_id :integer
shipping_option_id :integer
supplier_id :integer

Indexes

index_deliveries_on_carrier_bol (carrier_bol)
index_deliveries_on_destination_address_id (destination_address_id)
index_deliveries_on_order_id_and_id (order_id,id)
index_deliveries_on_order_id_and_state (order_id,state)
index_deliveries_on_origin_address_id (origin_address_id)
index_deliveries_on_quote_id (quote_id)
index_deliveries_on_shipped_date (shipped_date) USING brin
index_deliveries_on_shipping_account_number_id (shipping_account_number_id)
index_deliveries_on_shipping_option_id (shipping_option_id)
index_deliveries_on_state_and_id (state,id)
index_deliveries_on_supplier_id (supplier_id)

Foreign Keys

deliveries_destination_address_id (destination_address_id => addresses.id) ON DELETE => cascade
deliveries_order_id_fk (order_id => orders.id) ON DELETE => cascade
deliveries_origin_address_id (origin_address_id => addresses.id) ON DELETE => cascade
deliveries_quote_id_fk (quote_id => quotes.id) ON DELETE => cascade
deliveries_shipping_option_id_fk (shipping_option_id => shipping_options.id)

Defined Under Namespace

Classes: InvoicingHandler

Constant Summary collapse

SHIPPING_STATES =

States the delivery passes through during warehouse handling and carrier handoff.

%i[at_warehouse picking pending_pickup_confirm pending_ship_labels pending_carrier_confirm pending_ship_confirm shipped].freeze
ANY_EMPLOYEE_CANCELABLE_STATES =

States any sales-rep / CRM employee may cancel from (no warehouse work in progress).

%i[quoting awaiting_po_fulfillment at_warehouse future_release service_ready_to_fulfill return_labels_complete].freeze
WAREHOUSE_CANCELABLE_STATES =

States only warehouse staff can cancel from (mid-pick / mid-pack).

%i[pre_pack picking pending_pickup_confirm pending_ship_labels].freeze
WAREHOUSE_STATES =

All warehouse-side states — used to gate "is this delivery in the warehouse's hands?".

%i[at_warehouse future_release pre_pack picking pending_ship_labels pending_carrier_confirm pending_ship_confirm pending_pickup_confirm pending_manifest_completion].freeze
CANCELABLE_STATES =

Combined cancelable-states set (employee + warehouse).

(ANY_EMPLOYEE_CANCELABLE_STATES + WAREHOUSE_CANCELABLE_STATES).uniq
CHECK_COLD_LEAD_NOTE =

Free-typed note marker used by call-recording transcribers when a customer
call meets the "cold lead" criteria.

'Check notes, contains COLD LEAD.'
SHIP_LABEL_HOLD_PERCENT_THRESHOLD =

Percentage by which actual shipping cost may exceed estimate before the
delivery is auto-held for review.

25.0
SHIP_LABEL_HOLD_DOLLAR_THRESHOLD =

Minimum absolute-dollar overage (in tandem with the percent threshold)
required before auto-holding for review.

25.0
SHIP_LABEL_HOLD_WEIGHT_PERCENT_THRESHOLD =

Percent variance between actual and estimated package weight before holding.

15.0
SHIP_LABEL_HOLD_WEIGHT_THRESHOLD =

Absolute lb variance threshold paired with the percent threshold above.

7.5
CARRIERS_REQUIRING_MANIFEST_COMPLETION =

Carriers that require a manifest-completion handoff after pickup
(Speedee). The state machine routes these through pending_manifest_completion.

['SpeedeeDelivery'].freeze
CROSS_BORDER_BROKER_INSTRUCTIONS =

Default broker instructions printed on cross-border BOL/CI documents.

'Broker: Willson International, Email: service@willsonintl.com'
CROSS_BORDER_COUNTRY_SPECIFIC_BROKER_TEXT =

Per-destination-country broker office address & phone, appended after
CROSS_BORDER_BROKER_INSTRUCTIONS on customs paperwork.

{
  US: 'Address: 160 Wales Avenue, Suite 100, Tonawanda, NY, 14150, USA, Tel: 800-315-1918',
  CA: 'Address: 2345 Argentia Road, Suite 201, Mississauga, ON, L5N 8K4, CAN, Tel: 905-643-9054'
}
CARRIERS_TO_SEND_COMMERCIAL_INVOICES =

Carriers our system emails commercial-invoice copies to as soon as the
ship-confirm fires — keyed by delivery.carrier (the reported_carrier
string) since each entry maps a single carrier name to a customs team.
Each entry: { name:, customs_email: }.

RlCarriers is the legacy XML-API adapter (Shipping::RlCarriers);
ShipengineRlCarriers is the modern ShipEngine LTL adapter against
the same R+L Carriers freight network and the same customs team. We
list both because delivery.carrier carries the adapter name, not
the underlying freight carrier — and we want the auto-email to fire
regardless of which adapter generated the label. R+L is in the
async-PRO group per ShipEngine's supported-carriers table, so for
the ShipEngine adapter the email fires via the
ltl_pro_number_changed? && invoiced? controller path (not at
invoicing time), once the warehouse enters the PRO at pickup.

[
  {
    name: 'RlCarriers',
    customs_email: 'transbordersolutiongroup@rlcarriers.com'
  },
  {
    name: 'ShipengineRlCarriers',
    customs_email: 'transbordersolutiongroup@rlcarriers.com'
  },
  {
    name: 'Freightquote'
  }
]
CARRIERS_NAMES_TO_SEND_COMMERCIAL_INVOICES =

Just the carrier names from CARRIERS_TO_SEND_COMMERCIAL_INVOICES for
quick include? checks.

CARRIERS_TO_SEND_COMMERCIAL_INVOICES.map{|carr| carr[:name]}
FREIGHTQUOTE_CARRIERS_TO_SEND_COMMERCIAL_INVOICES =

Freightquote sub-carriers that need a separate CI email — keyed by SCAC
because Freightquote rates expose the actual carrier as a SCAC code rather
than a name.

[
  {
    key: "polaris",
    name: "Polaris Transport Carriers Inc.",
    carrierCode: "T408447",
    scac:"POLT",
    customs_email: 'customs@polaristransport.com'
  }
]
FREIGHTQUOTE_CARRIER_SCACS_TO_SEND_COMMERCIAL_INVOICES =
FREIGHTQUOTE_CARRIERS_TO_SEND_COMMERCIAL_INVOICES.map{|fcarr| fcarr[:scac]}
SUBQUERY_ORDER_STORE_ID =

SUBQUERY_ORDER_STORE_ID = %{
EXISTS(SELECT 1
FROM orders o
INNER JOIN parties cu ON cu.id = o.customer_id
INNER JOIN catalogs cat on cat.id = cu.catalog_id
WHERE o.id = deliveries.order_id
AND cat.store_id = :store_id)
}

%{
  EXISTS(SELECT 1
         FROM orders o
         INNER JOIN parties cu ON cu.id = o.customer_id
         INNER JOIN catalogs cat on cat.id = cu.catalog_id
         WHERE o.id = deliveries.order_id
         AND o.order_type <> 'ST'
         AND cat.store_id = :store_id
         UNION ALL
         SELECT 1
         FROM orders o
         WHERE o.id = deliveries.order_id
         AND o.order_type = 'ST'
         AND o.from_store_id = :store_id)
}
SUBQUERY_QUOTE_STORE_ID =

SUBQUERY_ORDER_STORE_ID = %{
EXISTS(SELECT 1
FROM orders o
INNER JOIN parties cu ON cu.id = o.customer_id
INNER JOIN catalogs cat on cat.id = cu.catalog_id
WHERE o.id = deliveries.order_id
AND o.order_type <> 'ST'
AND cat.store_id = :store_id
UNION ALL
SELECT 1
FROM orders o
WHERE o.id = deliveries.order_id
AND o.order_type = 'ST'
AND o.from_store_id = :store_id
AND (o.from_store_id NOT IN (3,5) and o.to_store_id is not null)
UNION ALL
SELECT 1
FROM orders o
WHERE o.id = deliveries.order_id
AND o.order_type = 'ST'
AND o.to_store_id = :store_id
AND (o.from_store_id IN (3,5) and o.to_store_id is not null))
} # Here we want non STs to use customer catalog store for warehouse dashboard, otherwise non-FBA inbound STs use the from store. FBA inbound STs use the to store so that they can deal with the inbound shipments

%{
  EXISTS(SELECT 1
         FROM quotes quo
         INNER JOIN opportunities opp ON opp.id = quo.opportunity_id
         INNER JOIN parties cu ON cu.id = opp.customer_id
         INNER JOIN catalogs cat on cat.id = cu.catalog_id
         WHERE quo.id = deliveries.quote_id
          AND cat.store_id = :store_id)
}
FEDEX_GROUND_SHIPPING_OPTION_IDS =

FedEx Ground, Ground Home Delivery or International Ground, US and Canada

[139, 135, 146, 152, 171]
FALLBACK_MIN_OVERRIDE_COST_WWW =

Public-website fallback shipping cost: minimum dollar amount used
when the carrier rate-shop fails entirely.

20.0
FALLBACK_PER_LB_OVERRIDE_COST_WWW =

Per-pound add-on for the public-website fallback shipping cost.

5.0
FALLBACK_MAX_OVERRIDE_COST_FRACTION_WWW =

Cap (as a fraction of subtotal) on the public-website fallback
shipping cost so a tiny order doesn't get a shipping bill larger
than the goods.

20.0
FALLBACK_OVERRIDE_COST_CRM =

CRM-side fallback shipping cost (deliberately higher than the WWW
fallback so CRM users notice and re-rate-shop manually rather than
silently quoting an unrealistic number).

500.0
GATEWAY_PAYMENT_TYPES =

Guard: verify the order still has valid payment coverage before the
delivery reaches pending_ship_confirm (ready to ship).

Only applies to orders with gateway-backed payments (Credit Card, PayPal,
Amazon Pay). PO, Store Credit, Check, Cash, Wire, etc. are terms-based
or manually processed and don't need gateway verification.

Payment is validated when the order is released to the warehouse, and
periodically by PaymentCheckerWorker, but authorizations can expire or
be voided externally between release and the warehouse finishing labels.
Catching it here — before the delivery is ready for carrier pickup —
gives the team time to resolve the payment while the order is still in
the warehouse, rather than blocking at the shipped transition when
goods have already left.

[Payment::CREDIT_CARD, Payment::PAYPAL, Payment::PAYPAL_INVOICE, Payment::AMAZON_PAY].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Attributes included from Models::Profitable

#min_profit_markup

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has one collapse

Has many collapse

Methods included from Models::Payable

#payments

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::LegacyRateRequest

#last_shipping_rate_request_result, #last_shipping_rate_request_result=

Methods included from Models::ShipMeasurable

#cartons_total, #crates_total, #pallets_total, #ship_freight_class_from_shipments, #ship_volume_from_shipments, #ship_volume_from_shipments_in_cubic_feet, #ship_weight_from_shipments, #shipment_set, #shipments_for_measure

Methods included from Models::Profitable

#default_sales_markup, #profit_margins_met?, #profitable_line_items, #profitable_status, #profitable_total_discounted, #profitable_total_estimated_cost, #profitable_total_estimated_line_cost, #profitable_total_profit, #profitable_total_profit_margin, #profitable_total_profit_markup, #track_profit?, #validate_min_profit_markup?

Methods included from Models::Notable

#quick_note

Methods included from Models::Md5Hashable

#md5_hash_items_from_packable

Methods included from Models::Packable

#calculate_actual_insured_value, #carrier, #carrier_fedex?, #delivery_description, #domestic?, #has_supported_carrier?, #is_goods_shipping?, #is_onsite_service_only?, #is_remote_service_only?, #is_service_only?, #is_warehouse_ca_pickup?, #is_warehouse_pickup?, #is_warehouse_us_pickup?, #is_zero_charge_dropship?, #must_be_insured?, #must_be_signature_confirmation?, #search_deliveries_for_equivalent_packaging, #ship_weight, #ships_from_text, #subtotal, #subtotal_cogs, #subtotal_for_commercial_invoice, #subtotal_for_insured_value, #subtotal_for_ltl_threshold, #subtotal_msrp

Methods included from Models::Payable

#balance, #funded_by_cod?, #total_payments_authorized

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

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#do_not_validate_line_itemsObject

Returns the value of attribute do_not_validate_line_items.



92
93
94
# File 'app/models/delivery.rb', line 92

def do_not_validate_line_items
  @do_not_validate_line_items
end

#force_shipping_cost_updateObject

Returns the value of attribute force_shipping_cost_update.



92
93
94
# File 'app/models/delivery.rb', line 92

def force_shipping_cost_update
  @force_shipping_cost_update
end

#ltl_exclusion_reasonsObject

Returns the value of attribute ltl_exclusion_reasons.



92
93
94
# File 'app/models/delivery.rb', line 92

def ltl_exclusion_reasons
  @ltl_exclusion_reasons
end

#override_carrierObject

Returns the value of attribute override_carrier.



92
93
94
# File 'app/models/delivery.rb', line 92

def override_carrier
  @override_carrier
end

#override_future_release_dateObject

Returns the value of attribute override_future_release_date.



92
93
94
# File 'app/models/delivery.rb', line 92

def override_future_release_date
  @override_future_release_date
end

#payment_idsObject

Returns the value of attribute payment_ids.



92
93
94
# File 'app/models/delivery.rb', line 92

def payment_ids
  @payment_ids
end

Class Method Details

.activeActiveRecord::Relation<Delivery>

A relation of Deliveries that are active. Active Record Scope

Returns:

See Also:



356
# File 'app/models/delivery.rb', line 356

scope :active, -> { where.not(state: 'cancelled') }

.all_at_warehouseActiveRecord::Relation<Delivery>

A relation of Deliveries that are all at warehouse. Active Record Scope

Returns:

See Also:



345
# File 'app/models/delivery.rb', line 345

scope :all_at_warehouse, -> { where(state: WAREHOUSE_STATES) }

.auto_ship_confirm(logger: nil) ⇒ Object



4530
4531
4532
4533
4534
4535
4536
4537
4538
# File 'app/models/delivery.rb', line 4530

def self.auto_ship_confirm(logger: nil)
  logger ||= Rails.logger
  logger.info("#{Time.current}: Beginning auto_ship_confirm")
  deliveries = Delivery.where(state: %w[pending_ship_confirm])
  logger.info("#{Time.current}: Deliveries found: #{deliveries.size}")
  deliveries.each do |d|
    DeliveryShipConfirmWorker.perform_async(delivery_id: d.id)
  end
end

.awaiting_po_fulfillmentActiveRecord::Relation<Delivery>

A relation of Deliveries that are awaiting po fulfillment. Active Record Scope

Returns:

See Also:



362
# File 'app/models/delivery.rb', line 362

scope :awaiting_po_fulfillment, -> { where(state: 'awaiting_po_fulfillment') }

.by_order_store_idActiveRecord::Relation<Delivery>

A relation of Deliveries that are by order store id. Active Record Scope

Returns:

See Also:



342
# File 'app/models/delivery.rb', line 342

scope :by_order_store_id, ->(store_id) { where(SUBQUERY_ORDER_STORE_ID, store_id:) }

.by_quote_store_idActiveRecord::Relation<Delivery>

A relation of Deliveries that are by quote store id. Active Record Scope

Returns:

See Also:



343
# File 'app/models/delivery.rb', line 343

scope :by_quote_store_id, ->(store_id) { where(SUBQUERY_QUOTE_STORE_ID, store_id:) }

.by_store_idActiveRecord::Relation<Delivery>

A relation of Deliveries that are by store id. Active Record Scope

Returns:

See Also:



341
# File 'app/models/delivery.rb', line 341

scope :by_store_id, ->(store_id) { by_order_store_id(store_id).or(by_quote_store_id(store_id)) }

.cancelableActiveRecord::Relation<Delivery>

A relation of Deliveries that are cancelable. Active Record Scope

Returns:

See Also:



357
# File 'app/models/delivery.rb', line 357

scope :cancelable, -> { where(state: CANCELABLE_STATES) }

.dropshipActiveRecord::Relation<Delivery>

A relation of Deliveries that are dropship. Active Record Scope

Returns:

See Also:



361
# File 'app/models/delivery.rb', line 361

scope :dropship, -> { where(state: %w[awaiting_po_fulfillment processing_po_fulfillment]) }

.fedex_expressActiveRecord::Relation<Delivery>

A relation of Deliveries that are fedex express. Active Record Scope

Returns:

See Also:



376
# File 'app/models/delivery.rb', line 376

scope :fedex_express, -> { joins(:shipments).where(shipments: { carrier: 'FedEx' }).joins(:selected_shipping_cost).where.not(shipping_costs: { shipping_option_id: FEDEX_GROUND_SHIPPING_OPTION_IDS }) }

.fedex_groundActiveRecord::Relation<Delivery>

A relation of Deliveries that are fedex ground. Active Record Scope

Returns:

See Also:



375
# File 'app/models/delivery.rb', line 375

scope :fedex_ground, -> { joins(:shipments).where(shipments: { carrier: 'FedEx' }).joins(:selected_shipping_cost).where(shipping_costs: { shipping_option_id: FEDEX_GROUND_SHIPPING_OPTION_IDS }) }

.for_future_releaseActiveRecord::Relation<Delivery>

A relation of Deliveries that are for future release. Active Record Scope

Returns:

See Also:



367
# File 'app/models/delivery.rb', line 367

scope :for_future_release, -> { where(state: 'future_release') }

.generate_super_pick_slip_pdf(deliveries, split_kits = false) ⇒ Object



1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
# File 'app/models/delivery.rb', line 1341

def self.generate_super_pick_slip_pdf(deliveries, split_kits = false)
  upload = nil
  files_to_combine = []
  msg_arr = []
  t = deliveries.length
  p = 0
  deliveries.each do |d|
    pdf = (begin
      d.get_or_generate_pick_slip_pdf(split_kits)
    rescue StandardError
      nil
    end)
    if pdf
      d.picking
      files_to_combine << pdf
      p += 1
    else
      msg_arr << d.name.to_s
    end
  end
  unless files_to_combine.empty?
    file_name = "super_pick_slip_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf".downcase
    output_file_path = Upload.temp_location(file_name)
    super_pick_slip_path = PdfTools.combine(files_to_combine, output_file_path:, orientation: :portrait)
    upload = Upload.uploadify(super_pick_slip_path, 'super_pick_slip_pdf')
  end
  msg = "Processed #{p} of #{t} deliveries. "
  msg += "Pick slip PDF could not generate for #{msg_arr.join(', ')}." unless msg_arr.empty?
  [upload, msg]
end

.invoice_shipped_deliveries(_logger = Rails.logger) ⇒ Object



4520
4521
4522
4523
4524
4525
4526
4527
4528
# File 'app/models/delivery.rb', line 4520

def self.invoice_shipped_deliveries(_logger = Rails.logger)
  Rails.logger.info("#{Time.current}: Beginning capture_cc_payments")
  deliveries = Delivery.where(state: 'shipped')
  Rails.logger.info("#{Time.current}: Deliveries found: #{deliveries.length}")

  deliveries.each do |delivery|
    DeliveryInvoicingWorker.perform_in(15.seconds, delivery.id)
  end
end

.invoicedActiveRecord::Relation<Delivery>

A relation of Deliveries that are invoiced. Active Record Scope

Returns:

See Also:



354
# File 'app/models/delivery.rb', line 354

scope :invoiced, -> { where(state: 'invoiced') }

.limit_to_fbaActiveRecord::Relation<Delivery>

A relation of Deliveries that are limit to fba. Active Record Scope

Returns:

See Also:



366
# File 'app/models/delivery.rb', line 366

scope :limit_to_fba, ->(fba_only) { fba_only ? joins(order: { customer: :billing_address }).where('parties.id = ? OR addresses.party_id = ?', CustomerConstants::AMAZON_COM_ID, CustomerConstants::AMAZON_COM_ID) : where('1=1') }

.non_pickupsActiveRecord::Relation<Delivery>

A relation of Deliveries that are non pickups. Active Record Scope

Returns:

See Also:



365
# File 'app/models/delivery.rb', line 365

scope :non_pickups, -> { where.not(destination_address_id: WAREHOUSE_ADDRESS_IDS) }

.non_quotingActiveRecord::Relation<Delivery>

A relation of Deliveries that are non quoting. Active Record Scope

Returns:

See Also:



352
# File 'app/models/delivery.rb', line 352

scope :non_quoting, -> { where.not(state: 'quoting') }

.not_cancelableActiveRecord::Relation<Delivery>

A relation of Deliveries that are not cancelable. Active Record Scope

Returns:

See Also:



358
# File 'app/models/delivery.rb', line 358

scope :not_cancelable, -> { where.not(state: CANCELABLE_STATES) }

.pending_manifest_completionActiveRecord::Relation<Delivery>

A relation of Deliveries that are pending manifest completion. Active Record Scope

Returns:

See Also:



377
# File 'app/models/delivery.rb', line 377

scope :pending_manifest_completion, -> { where(state: 'pending_manifest_completion') }

.pending_ship_confirmActiveRecord::Relation<Delivery>

A relation of Deliveries that are pending ship confirm. Active Record Scope

Returns:

See Also:



368
# File 'app/models/delivery.rb', line 368

scope :pending_ship_confirm, -> { where(state: 'pending_ship_confirm') }

.pickupsActiveRecord::Relation<Delivery>

A relation of Deliveries that are pickups. Active Record Scope

Returns:

See Also:



364
# File 'app/models/delivery.rb', line 364

scope :pickups, -> { where.not(order_id: nil).where(destination_address_id: WAREHOUSE_ADDRESS_IDS) }

.processing_po_fulfillmentActiveRecord::Relation<Delivery>

A relation of Deliveries that are processing po fulfillment. Active Record Scope

Returns:

See Also:



363
# File 'app/models/delivery.rb', line 363

scope :processing_po_fulfillment, -> { where(state: 'processing_po_fulfillment') }

.quotingActiveRecord::Relation<Delivery>

A relation of Deliveries that are quoting. Active Record Scope

Returns:

See Also:



350
# File 'app/models/delivery.rb', line 350

scope :quoting, -> { where(state: 'quoting') }

.quoting_or_pre_packActiveRecord::Relation<Delivery>

A relation of Deliveries that are quoting or pre pack. Active Record Scope

Returns:

See Also:



351
# File 'app/models/delivery.rb', line 351

scope :quoting_or_pre_pack, -> { where(state: %w[quoting pre_pack]) }

.release_deliveries_past_release_dateObject

Cron entry — releases every future_release delivery past its scheduled
release date (excluding manual_release_only ones), then publishes
Events::DeliveryAutomaticallyReleased or Events::DeliveryAutomaticReleaseFailed
per delivery so the async handler re-queries by id at email-send time.
Replaces direct InternalMailer.…deliver_later(d) calls whose Delivery
GlobalID arg was vulnerable to the AppSignal #4958 destroy race.



895
896
897
898
899
900
901
902
903
904
# File 'app/models/delivery.rb', line 895

def self.release_deliveries_past_release_date
  Delivery.where("state = 'future_release' and future_release_date <= ? and manual_release_only is not true", Date.current).find_each do |d|
    d.release
    delivery_id = d.id
    event = d.at_warehouse? ? Events::DeliveryAutomaticallyReleased.new(data: { delivery_id: }) : Events::DeliveryAutomaticReleaseFailed.new(data: { delivery_id: })
    Rails.configuration.event_store.publish(event, stream_name: "Delivery-#{delivery_id}")
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

.sales_ordersActiveRecord::Relation<Delivery>

A relation of Deliveries that are sales orders. Active Record Scope

Returns:

See Also:



359
# File 'app/models/delivery.rb', line 359

scope :sales_orders, -> { active.joins(:order).merge(Order.sales_orders) }

.send_manual_release_due_notificationObject

Cron entry — for every manual_release_only future-release delivery whose
scheduled release date has arrived, publishes
Events::DeliveryManualReleaseDue so the async handler re-queries by id.
Same AppSignal #4958 race-removal as release_deliveries_past_release_date.



910
911
912
913
914
915
916
917
918
919
920
# File 'app/models/delivery.rb', line 910

def self.send_manual_release_due_notification
  Delivery.where("state = 'future_release' and future_release_date <= ? and manual_release_only is true", Date.current).find_each do |d|
    delivery_id = d.id
    Rails.configuration.event_store.publish(
      Events::DeliveryManualReleaseDue.new(data: { delivery_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

.ship_labeled_beforeActiveRecord::Relation<Delivery>

A relation of Deliveries that are ship labeled before. Active Record Scope

Returns:

See Also:



378
# File 'app/models/delivery.rb', line 378

scope :ship_labeled_before, ->(time) { where(Delivery[:ship_labeled_at].lteq(time)) }

.shippedActiveRecord::Relation<Delivery>

A relation of Deliveries that are shipped. Active Record Scope

Returns:

See Also:



353
# File 'app/models/delivery.rb', line 353

scope :shipped, -> { where(state: %w[shipped pending_ship_confirm pending_pickup_confirm]) }

.shippingActiveRecord::Relation<Delivery>

A relation of Deliveries that are shipping. Active Record Scope

Returns:

See Also:



360
# File 'app/models/delivery.rb', line 360

scope :shipping, -> { where(state: SHIPPING_STATES) }

.states_for_selectObject



4516
4517
4518
# File 'app/models/delivery.rb', line 4516

def self.states_for_select
  state_machine.states.sort_by(&:human_name).map { |s| [s.human_name, s.value] }
end

.with_active_parentActiveRecord::Relation<Delivery>

A relation of Deliveries that are with active parent. Active Record Scope

Returns:

See Also:



346
347
348
349
# File 'app/models/delivery.rb', line 346

scope :with_active_parent, -> {
  where(order_id: Order.where.not(state: :cancelled).select(:id))
    .or(where(order_id: nil, quote_id: Quote.where.not(state: :cancelled).select(:id)))
}

.with_amz_bs_carrierActiveRecord::Relation<Delivery>

A relation of Deliveries that are with amz bs carrier. Active Record Scope

Returns:

See Also:



370
371
372
373
374
# File 'app/models/delivery.rb', line 370

scope :with_amz_bs_carrier, ->(carrier) {
  joins(:shipments)
    .where(shipments: { carrier: 'AmazonSeller' })
    .where("shipments.amz_metadata->>'amz_carrier' = ?", carrier)
}

.with_associationsActiveRecord::Relation<Delivery>

A relation of Deliveries that are with associations. Active Record Scope

Returns:

See Also:



344
# File 'app/models/delivery.rb', line 344

scope :with_associations, -> { includes(:shipments, :origin_address, :destination_address, { quote: [{ opportunity: [{ customer: [:buying_group, { catalog: :store }] }] }] }, order: [{ customer: [:buying_group, { catalog: :store }] }]) }

.with_line_itemsActiveRecord::Relation<Delivery>

A relation of Deliveries that are with line items. Active Record Scope

Returns:

See Also:



355
# File 'app/models/delivery.rb', line 355

scope :with_line_items, -> { includes(line_items: { catalog_item: { store_item: :item } }) }

.with_shipment_carrierActiveRecord::Relation<Delivery>

A relation of Deliveries that are with shipment carrier. Active Record Scope

Returns:

See Also:



369
# File 'app/models/delivery.rb', line 369

scope :with_shipment_carrier, ->(carrier) { joins(:shipments).where(shipments: { carrier: }) }

Instance Method Details

#activitiesActiveRecord::Relation<Activity>

Returns:

See Also:



122
# File 'app/models/delivery.rb', line 122

has_many :activities, as: :resource, dependent: :nullify

#actual_shipping_cost_exceeds_threshold?Boolean

Returns:

  • (Boolean)


4633
4634
4635
4636
4637
4638
# File 'app/models/delivery.rb', line 4633

def actual_shipping_cost_exceeds_threshold?
  estimate_shipping_cost = line_items.shipping_only.to_a.sum(&:price).to_f
  (exceeds = (actual_shipping_cost_to_show > (1.0 + (Delivery::SHIP_LABEL_HOLD_PERCENT_THRESHOLD / 100).round(2)) * estimate_shipping_cost)) && ((actual_shipping_cost_to_show - estimate_shipping_cost) > Delivery::SHIP_LABEL_HOLD_DOLLAR_THRESHOLD) && !order.is_rma_return?
  # logger.debug "Delivery, ID: #{self.id}, actual_shipping_cost_exceeds_threshold?: #{exceeds}, actual_shipping_cost_to_show: #{actual_shipping_cost_to_show}, estimate_shipping_cost: #{estimate_shipping_cost}, self.order.is_rma_return?: #{self.order.is_rma_return?}"
  exceeds
end

#actual_shipping_cost_to_showFloat

The actual shipping cost we report on screens and emails — zero
when the customer pays the carrier on their own account, otherwise
the carrier's billed amount rounded to cents.

Returns:

  • (Float)


4621
4622
4623
4624
4625
# File 'app/models/delivery.rb', line 4621

def actual_shipping_cost_to_show
  actual_shipping_cost_to_use = 0.0
  actual_shipping_cost_to_use = self.actual_shipping_cost.to_f unless third_party_billed?
  actual_shipping_cost_to_use.round(2)
end

#actual_shipping_cost_within_threshold?Boolean

Returns:

  • (Boolean)


4627
4628
4629
4630
4631
# File 'app/models/delivery.rb', line 4627

def actual_shipping_cost_within_threshold?
  estimate_shipping_cost = line_items.shipping_only.to_a.sum(&:price).to_f.round(2)
  (actual_shipping_cost_to_show <= estimate_shipping_cost) && !order.is_rma_return?
  # puts "actual_shipping_cost_within_threshold?: #{within}, actual_shipping_cost_to_show: #{actual_shipping_cost_to_show}, estimate_shipping_cost: #{estimate_shipping_cost}, self.order.is_rma_return?: #{self.order.is_rma_return?}"
end

#actual_weightBigDecimal Also known as: actual_shipment_weight

Sum of measured weights across the delivery's top-level Shipments,
preferring carrier-measured rows over packed estimates. Used for
weight-discrepancy thresholds and ship-label hold flags.

Returns:

  • (BigDecimal)


4645
4646
4647
4648
4649
4650
4651
4652
4653
# File 'app/models/delivery.rb', line 4645

def actual_weight
  # only if you have a weight do we care
  shipments_for_weight = shipments.where.not(weight: nil)
  # We only use top level shipments, ie shipments that are not contained in other shipments/pallets
  shipments_for_weight = shipments_for_weight.top_level
  # Measured shipments at an advanced stage take precedence over the packed one
  shipments_for_weight = shipments_for_weight.measured.presence || shipments_for_weight.packed
  shipments_for_weight.sum(:weight).round(1)
end

#actual_weight_discrepancy_exceeds_threshold?Boolean

Returns:

  • (Boolean)


4656
4657
4658
4659
4660
4661
4662
4663
# File 'app/models/delivery.rb', line 4656

def actual_weight_discrepancy_exceeds_threshold?
  expected_weight = ship_weight + estimated_tare_weight
  wt_diff_exceeds_percent_thresh = (actual_weight - expected_weight).abs > (Delivery::SHIP_LABEL_HOLD_WEIGHT_PERCENT_THRESHOLD / 100).round(2) * expected_weight
  wt_diff_exceeds_lbs_threshold = (actual_weight - expected_weight).abs > Delivery::SHIP_LABEL_HOLD_WEIGHT_THRESHOLD
  exceeds = wt_diff_exceeds_percent_thresh && wt_diff_exceeds_lbs_threshold && !(order && order.is_rma_return?)
  logger.debug "Delivery, ID: #{id}, actual_weight_discrepancy_exceeds_threshold?: #{exceeds}, wt_diff_exceeds_percent_thresh: #{wt_diff_exceeds_percent_thresh}, wt_diff_exceeds_lbs_threshold: #{wt_diff_exceeds_lbs_threshold}, actual_weight: #{actual_weight}, expected_weight: #{expected_weight} (ship_weight: #{ship_weight} + tare: #{estimated_tare_weight}), self.order.is_rma_return?: #{order&.is_rma_return?}"
  exceeds
end

#add_or_update_purchase_order_item(po, existing_po_items, item, li, unit_cost) ⇒ void

This method returns an undefined value.

If a previously unlinked PurchaseOrderItem matches the new
line item's item/quantity, reattaches it; otherwise builds a fresh
PO item via #build_new_purchase_order_item.

Parameters:



3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
# File 'app/models/delivery.rb', line 3232

def add_or_update_purchase_order_item(po, existing_po_items, item, li, unit_cost)
  quantity = li.quantity
  existing_item = existing_po_items.find { |poi| matches?(poi, item, quantity) }

  if existing_item
    existing_item.update(line_item: li)
  else
    po.purchase_order_items << build_new_purchase_order_item(item, li, quantity, unit_cost)
  end
end

#add_purchase_order_items(po, item, grouped_line_items, existing_po_items) ⇒ void

This method returns an undefined value.

Adds PurchaseOrderItems to a PurchaseOrder for one item across
one or more LineItems, using the supplier's tier price for the
combined quantity. Re-uses any matching unlinked existing PO item.

Parameters:



3213
3214
3215
3216
3217
3218
3219
3220
# File 'app/models/delivery.rb', line 3213

def add_purchase_order_items(po, item, grouped_line_items, existing_po_items)
  total_qty = grouped_line_items.sum(&:quantity)
  unit_cost = item.supplier_item.get_price_for_qty(total_qty)

  grouped_line_items.each do |li|
    add_or_update_purchase_order_item(po, existing_po_items, item, li, unit_cost)
  end
end

#adjusted_actual_shipping_costBigDecimal

Shipping cost we actually bill the customer — zero when the SAN's
owner is configured for third-party billing (the customer pays the
carrier directly), otherwise the actual carrier-charged amount.

Gates on the SAN OWNER's bill_shipping_to_customer? rather than
bare SAN presence so the customer invoice and the carrier label
(see WyShipping.classify_third_party_billing) read the same
predicate. Bare SAN presence used to zero the invoice while the
carrier was still billed P/P on WarmlyYours's account — silent
double leak (e.g. Ferguson Aurora #1983 SAN 3E049R).

Returns:

  • (BigDecimal)


4245
4246
4247
4248
4249
4250
4251
# File 'app/models/delivery.rb', line 4245

def adjusted_actual_shipping_cost
  if third_party_billed?
    BigDecimal(0)
  else
    actual_shipping_cost
  end
end

#all_activitiesActiveRecord::Relation<Activity>

Activities recorded against this delivery plus those on its parent
Order and Quote, so the delivery's UI activity feed shows the full
conversation around the shipment, not just delivery-scoped notes.

Returns:



800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'app/models/delivery.rb', line 800

def all_activities
  # Start with the initial condition for Delivery
  query = Activity.where(
    Activity.arel_table[:resource_type].eq('Delivery')
      .and(Activity.arel_table[:resource_id].eq(id))
  )

  # Add the Order condition if order_id is present
  if order_id.present?
    order_condition = Activity.where(
      Activity.arel_table[:resource_type].eq('Order')
        .and(Activity.arel_table[:resource_id].eq(order_id))
    )
    query = query.or(order_condition)
  end

  # Add the Quote condition if quote_id is present
  if quote_id.present?
    quote_condition = Activity.where(
      Activity.arel_table[:resource_type].eq('Quote')
        .and(Activity.arel_table[:resource_id].eq(quote_id))
    )
    query = query.or(quote_condition)
  end
  query
end

#all_dropship_items_fulfilled?Boolean

Returns:

  • (Boolean)


3117
3118
3119
# File 'app/models/delivery.rb', line 3117

def all_dropship_items_fulfilled?
  line_items.none? { |li| li.dropship? && (li.purchase_order_item.nil? || !li.purchase_order_item.fully_receipted?) }
end

#all_intl_forms_pdfUpload?

Most recent bundled international-forms Upload (CI + BOL + USMCA
certificates).

Returns:



4029
4030
4031
# File 'app/models/delivery.rb', line 4029

def all_intl_forms_pdf
  uploads.order(:id).reverse_order.find_by(category: 'all_intl_forms_pdf')
end

#all_labels_pdfUpload?

Most recent combined-labels PDF Upload attached to the delivery.

Returns:



3994
3995
3996
# File 'app/models/delivery.rb', line 3994

def all_labels_pdf
  uploads.order(:id).reverse_order.find_by(category: 'all_labels_pdf')
end

#all_lines_allocated_to_shipments?Boolean

Returns:

  • (Boolean)


977
978
979
# File 'app/models/delivery.rb', line 977

def all_lines_allocated_to_shipments?
  line_allocation_status_hash.values.all?(&:zero?)
end

#all_lines_allocated_to_shipments_and_shipments_have_weight?Boolean

Returns:

  • (Boolean)


981
982
983
984
985
986
987
988
989
990
991
992
993
# File 'app/models/delivery.rb', line 981

def all_lines_allocated_to_shipments_and_shipments_have_weight?
  res = true
  unless all_lines_allocated_to_shipments?
    errors.add(:base, 'All items must be allocated to shipments.')
    res = false
  end
  # CB: Disabled for now, causing a lot of frictions with warehouse.  Ramie when back will revisit.
  # unless shipments.all?{|s| s.weight > s.compute_tare_weight && s.entered_and_computed_weights_are_close?}
  #   errors.add(:base, "All shipments must have a weight close the computed item weight plus the tare/packaging weight. Shipments weights: #{shipments.map{|s| s.weight.to_f.to_s + 'lbs'}.join(', ')}, computed weights: #{shipments.map{|s| (s.compute_tare_weight.to_f + s.compute_shipment_weight.to_f).to_s + 'lbs'}.join(', ')}")
  #   res = false
  # end
  res
end

#all_shipments_weights_match_expectedHash{Symbol => Object}

Cross-checks entered weights against computed weights for pallets
and item-bearing shipments. Items with sustained mismatches are
flagged for weight review (review_product_weight_flag).

Returns:

  • (Hash{Symbol => Object})

    :status, optional :error_message/:warning_message



4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
# File 'app/models/delivery.rb', line 4805

def all_shipments_weights_match_expected
  # here, because we can't really trust item weight data, we want to return valid false only for pallets without items but with child containers whose weights mismatch
  # otherwise we flag items on shipments whose entered weights do not match computed weights from items for review/re-weighing
  res = {}
  res[:status] = true
  if (pallets = shipments.pallets).any? && (problematic_pallets = pallets.select { |p| p.shipment_contents.blank? && !p.entered_and_computed_weights_are_close? }).any?
    res[:status] = false
    err_msgs = []
    problematic_pallets.each do |s|
      err_msgs << "#{s.container_type.to_s.titleize} #{s.reference_number} has entered weight of #{s.weight} LBS and computed weight of #{s.compute_shipment_weight} LBS from its cartons"
    end
    res[:error_message] = err_msgs.join('. ')
  end
  if (problematic_shipments_with_contents = shipments.select { |s| s.shipment_contents.present? && !s.entered_and_computed_weights_are_close? }).any?
    items_to_review = []
    problematic_shipments_with_contents.each do |s|
      s.shipment_contents.each do |sc|
        if sc.line_item.item.condition_new? && sc.line_item.item.base_weight > Shipment::MIN_WEIGHT_DELTA_LBS && !sc.line_item.item.product_weight_flag_audited? # only new items not refurbished. that weight more than the minimum threshold, and don't redo item weights that have already been corrected
          items_to_review << sc.line_item.item
        end
      end
    end
    items_to_review.uniq!
    items_to_review.each do |item|
      item.update_column(:review_product_weight_flag, true)
    end
    res[:warning_message] = "The weights for the following items SKUs may need to be reviewed/re-measured: #{items_to_review.map(&:sku).join(', ')}" if items_to_review.any?
  end
  res
end

#append_shipping_api_log_entry!(kind:, **fields) ⇒ Object

Append a single ad-hoc entry to shipping_api_log. Used for polling-style
carrier calls (e.g. Freightquote events) that don't flow through
append_to_shipping_api_log!'s label/void wrappers. Persists immediately
so polling traces survive crashes mid-loop.



3941
3942
3943
3944
3945
3946
3947
# File 'app/models/delivery.rb', line 3941

def append_shipping_api_log_entry!(kind:, **fields)
  entry = build_shipping_api_log_entry(kind: kind, **fields)
  new_log = Array(shipping_api_log) + [entry]
  update_column(:shipping_api_log, new_log)
rescue StandardError => e
  Rails.logger.error("[Delivery##{id}] append_shipping_api_log_entry! failed: #{e.class}: #{e.message}")
end

#append_to_shipping_api_log!(kind:, shipping_result:) ⇒ Object

Append one or more entries to shipping_api_log per WyShipping label/void
call. Captures request/response payloads (Hash for JSON-native carriers,
String for legacy XML carriers), HTTP status code, and response headers so
we don't have to dig through rotated prod logs after the fact. Persists
immediately via update_column to survive crashes/rollbacks in the
surrounding flow. When the carrier's label flow includes an inline re-rate
call (Freightquote re-rates at label time to lock in a fresh quoteId), a
companion kind: 'rate' entry is appended automatically.



3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
# File 'app/models/delivery.rb', line 3900

def append_to_shipping_api_log!(kind:, shipping_result:)
  payload = shipping_result.is_a?(Hash) ? shipping_result.with_indifferent_access : {}
  shipment = payload[:shipment].is_a?(Hash) ? payload[:shipment].with_indifferent_access : payload

  request_key  = kind == 'void' ? :void_request_xml  : :ship_request_xml
  response_key = kind == 'void' ? :void_response_xml : :ship_reply_xml

  entries = []
  entries << build_shipping_api_log_entry(
    kind: kind,
    status: payload[:status_code] == :error ? 'error' : 'success',
    status_message: payload[:status_message],
    request: shipment[request_key] || payload[request_key],
    response: shipment[response_key] || payload[response_key],
    response_status: shipment[:response_status] || payload[:response_status],
    response_headers: shipment[:response_headers] || payload[:response_headers]
  )

  rate_request  = shipment[:rate_request_xml] || payload[:rate_request_xml]
  rate_response = shipment[:rate_reply_xml]   || payload[:rate_reply_xml]
  if rate_request.present? || rate_response.present?
    entries << build_shipping_api_log_entry(
      kind: 'rate',
      status: 'success',
      request: rate_request,
      response: rate_response,
      response_status: shipment[:rate_response_status]  || payload[:rate_response_status],
      response_headers: shipment[:rate_response_headers] || payload[:rate_response_headers]
    )
  end

  new_log = Array(shipping_api_log) + entries
  update_column(:shipping_api_log, new_log)
rescue StandardError => e
  Rails.logger.error("[Delivery##{id}] append_to_shipping_api_log! failed: #{e.class}: #{e.message}")
end

#apply_cheapest_economy_shipping_methodBoolean

For "ships economy" deliveries currently sitting on the override
placeholder, refreshes carrier rates and switches the selection to the
cheapest ground option. Removes the free-online-shipping coupon when
present and re-applies the economy shipping match coupon so the
customer still pays the original economy rate.

Returns:

  • (Boolean)


2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
# File 'app/models/delivery.rb', line 2008

def apply_cheapest_economy_shipping_method
  return true unless selected_shipping_cost&.is_override? # only applies to override ships economy

  retrieve_shipping_costs # get new shipping costs post-pack
  self.selected_shipping_cost = sorted_shipping_costs_www_hash[:ground]&.first # choose cheapest ground
  save
  if order&.customer_qualifies_for_free_online_shipping? # if order had free shipping online coupon, remove it now
    free_online_shipping_discount = order.discounts.free_online_shipping.first
    Coupon::DeleteDiscount.new.perform(free_online_shipping_discount, { skip_lock_check: true })
  end
  order&.reload&.reset_discount(reset_item_pricing: false) # reset the discount
  apply_shipping_match_for_economy_shipping
  true
end

#apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false) ⇒ Object

Centralized application of the selected shipping cost to delivery and its shipping line
Ensures consistency whether selection is auto-computed or explicitly chosen elsewhere



2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
# File 'app/models/delivery.rb', line 2986

def apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false)
  unless shipping_cost_entry.shipping_option.item
    raise "Cannot create shipping line item as item is missing from shipping_option ID: #{begin
      shipping_cost_entry.shipping_option_id
    rescue StandardError
      nil
    end}"
  end

  shipping_line = line_items.detect(&:is_shipping?)
  shipping_line ||= line_items.build(resource:, tax_class: 'shp')
  shipping_line.quantity = is_rma_return? ? -1 : 1
  shipping_line.item = shipping_cost_entry.shipping_option.item
  shipping_line.price = shipping_cost_entry.calculated_cost
  # Clear old line_discounts when shipping changes - reset_discount will recalculate them
  if previous_selected_id && previous_selected_id != shipping_cost_entry.id
    if persist
      shipping_line.line_discounts.destroy_all
    else
      shipping_line.line_discounts.each(&:mark_for_destruction)
    end
  end
  shipping_line.discounted_price = shipping_cost_entry.calculated_cost
  shipping_line.shipping_cost = shipping_cost_entry
  self.selected_shipping_cost_id = shipping_cost_entry.id
  shipping_line.description = retrieve_shipping_description_for_shipping_cost(shipping_cost_entry)
  self.shipping_cost = shipping_cost_entry.calculated_cost
  carrier = shipping_cost_entry&.shipping_option&.carrier
  shipments.each do |s|
    Rails.logger.debug { "apply_selected_shipping_cost!, before: carrier: #{carrier}, s.carrier: #{s.carrier}" }
    s.update(carrier:)
    Rails.logger.debug { "apply_selected_shipping_cost!, after: carrier: #{carrier}, s.carrier: #{s.carrier}" }
  end
  self.do_not_recalculate = true

  if persist
    # Persist delivery columns without invoking callbacks
    update_columns(
      shipping_option_id: shipping_cost_entry.shipping_option_id,
      shipping_cost: shipping_cost_entry.calculated_cost,
      selected_shipping_cost_id: shipping_cost_entry.id,
      updated_at: Time.current
    )
    # Persist shipping line
    shipping_line.save!
    # Remove any extra shipping lines now that the current one is saved
    extra_shipping_lines = line_items.select(&:is_shipping?) - [shipping_line]
    extra_shipping_lines.each(&:destroy)
    # Ensure itemizable knows about the shipping line
    resource.sync_shipping_line if resource.respond_to?(:sync_shipping_line)
  else
    extra_shipping_lines = line_items.select(&:is_shipping?) - [shipping_line]
    extra_shipping_lines.each(&:destroy)
  end
  resource.do_not_set_totals = nil if resource.present?
  if resource && !resource.editing_locked?
    resource.force_total_reset = true
  end
  Rails.logger.debug { "apply_selected_shipping_cost!, after: shipping_line: #{shipping_line.inspect}" }
end

#apply_shipping_match_for_economy_shippingvoid

This method returns an undefined value.

Adjusts the parent Order's discounts so the customer keeps paying
the originally quoted economy shipping rate after the warehouse swaps
in a real carrier. Difference between checkout-time and current
shipping cost is applied as an economy_shipping_match_crm Discount.



2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
# File 'app/models/delivery.rb', line 2029

def apply_shipping_match_for_economy_shipping
  order.reload
  economy_shipping_match_crm_coupon = Coupon.find_by_code(Coupon::ECONOMY_SHIPPING_MATCH_CRM_COUPON_CODE)
  return unless economy_shipping_match_crm_coupon.present? && order.line_items.shipping_only.present? # only proceed if we have the shipping line, coupon

  if order.discounts.with_economy_shipping_match_crm.present? # if order had economy_shipping_match_crm_coupon, remove it now
    economy_shipping_match_crm_discount = order.discounts.with_economy_shipping_match_crm.first
    Coupon::DeleteDiscount.new.perform(economy_shipping_match_crm_discount, { skip_lock_check: true })
    order.reload.reset_discount(reset_item_pricing: false) # reset the discount
  end
  amount = (order.shipping_cost_at_time_of_checkout || order.shipping_cost) - order.shipping_cost # order.shipping_cost_at_time_of_checkout should NEVER be nil, but hey it happens, very rarely, for no explicable reason, the fix is to set it to order.shipping_cost
  return unless amount.abs > 0.0 # only proceed if we have an order balance

  discount = Discount.new(itemizable: order, coupon_id: economy_shipping_match_crm_coupon.id, effective_date: Date.current)

  shipping_li = order.line_items.shipping_only.first
  discount.line_discounts.build(coupon_id: economy_shipping_match_crm_coupon.id, amount:, line_item_id: shipping_li.id)

  if discount.line_discounts.any?
    discount.amount = discount.user_amount = discount.line_discounts.to_a.sum(&:amount)
    discount.save!
  end
  order.reload.calculate_tax_for_all_lines
end

#apply_warehouse_fee?Boolean

Returns:

  • (Boolean)


2602
2603
2604
2605
# File 'app/models/delivery.rb', line 2602

def apply_warehouse_fee?
  # only apply warehouse pickup to first warehouse pickup delivery, so total pickup fee is applied only once per ship_quotable
  is_warehouse_pickup? && (resource.deliveries.reload.first == self)
end

#at_least_one_shipmentBoolean

Validation guard: every shipping delivery (except service-only,
warehouse pickup, and European fulfillment) must have at least one
completed Shipment before we let it leave the warehouse.

Returns:

  • (Boolean)


3440
3441
3442
3443
3444
3445
3446
3447
# File 'app/models/delivery.rb', line 3440

def at_least_one_shipment
  ret = true
  if shipments.completed.empty? && !is_service_only? && !is_warehouse_pickup? && !european_shipment?
    ret = false
    errors.add(:base, 'at least one box/package must be defined')
  end
  ret
end

#authoritative_packing_for_shipment_contents?Boolean

True when a Packing row exists for this delivery with origin from_delivery
(DeliveryMd5Extractor) or from_manual_entry (pre-pack). Shipment#unpack keeps
shipment_contents in that case so recalculate-shipping does not wipe allocations.
from_shipment is intentionally excluded (unused in production; legacy enum value).

Returns:

  • (Boolean)


965
966
967
# File 'app/models/delivery.rb', line 965

def authoritative_packing_for_shipment_contents?
  Packing.where(delivery_id: id, origin: %i[from_delivery from_manual_entry]).exists?
end

#billing_entityObject

Alias for Resource#billing_entity

Returns:

  • (Object)

    Resource#billing_entity

See Also:



175
# File 'app/models/delivery.rb', line 175

delegate :billing_entity, to: :resource

#bol_pdfUpload?

Latest bill-of-lading Upload attached to the delivery.

Returns:



4001
4002
4003
# File 'app/models/delivery.rb', line 4001

def bol_pdf
  uploads.ship_bol_pdfs.first
end

#build_new_purchase_order_item(item, li, quantity, unit_cost) ⇒ PurchaseOrderItem

Builds an unsaved PurchaseOrderItem for a dropship line item with
supplier SKU/description, weights, costs, and auto-receive flag from
the linked SupplierItem.

Parameters:

  • item (Item)
  • li (LineItem)
  • quantity (Integer)
  • unit_cost (BigDecimal)

Returns:



3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
# File 'app/models/delivery.rb', line 3258

def build_new_purchase_order_item(item, li, quantity, unit_cost)
  PurchaseOrderItem.new(
    item:,
    sku: item.sku,
    description: item.name,
    quantity:,
    unit_weight: item.base_weight,
    total_weight: item.base_weight * quantity,
    unit_cost:,
    total_cost: unit_cost * quantity,
    uom: 'EA',
    unit_quantity: quantity,
    line_item: li,
    auto_receive: item.supplier_item.auto_receive,
    supplier_sku: item.supplier_item.supplier_sku,
    supplier_description: item.supplier_item.supplier_description
  )
end

#build_purchase_order(supplier) ⇒ PurchaseOrder

Builds a new awaiting-transmission dropship PurchaseOrder for the
given supplier on the catalog's company/store, ready to receive
purchase order items.

Parameters:

Returns:



3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
# File 'app/models/delivery.rb', line 3178

def build_purchase_order(supplier)
  PurchaseOrder.new(
    po_type: 'purchase',
    company: catalog.company,
    store: catalog.store,
    supplier:,
    terms: supplier.terms,
    state: 'awaiting_transmission',
    carrier: supplier,
    order_date: Date.current,
    request_date: Date.current,
    currency: supplier.currency,
    drop_ship: true,
    drop_ship_delivery: self
  )
end

#calculate_all_cogsBigDecimal

Total cost of goods sold for non-shipping LineItems on this delivery,
used by Models::Profitable margin computations and ledger entries.

Returns:

  • (BigDecimal)


848
849
850
# File 'app/models/delivery.rb', line 848

def calculate_all_cogs
  BigDecimal(line_items.non_shipping.sum('unit_cogs * quantity'))
end

#calculate_declared_valueBigDecimal

Aggregate declared/insured value for this delivery: for each goods
LineItem, quantity * unit_value_for_commercial_invoice. Eager
loads supplier-item prices to avoid N+1.

Returns:

  • (BigDecimal)


4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
# File 'app/models/delivery.rb', line 4337

def calculate_declared_value
  # Use the same logic as calculate_actual_insured_value to ensure declared value matches calculated value
  # Eager load item -> supplier_items -> supplier_item_prices to avoid N+1 queries
  # in unit_value_for_commercial_invoice -> unit_supplier_purchase_cost
  insured_value = line_items.goods.without_children
    .includes(item: { supplier_items: :supplier_item_prices }).sum do |li|
    unit_value = li.unit_value_for_commercial_invoice || 0
    li.quantity * unit_value
  end
  insured_value
end

#calculate_grand_totalBigDecimal

Subtotal plus actual shipping cost — the value invoiced to the
customer at ship time (excluding tax and discounts already factored
into subtotal).

Returns:

  • (BigDecimal)


4328
4329
4330
# File 'app/models/delivery.rb', line 4328

def calculate_grand_total
  (actual_shipping_cost || 0.0) + subtotal
end

#calculate_shipping_options(options = {}) ⇒ Hash

Builds the option payload WyShipping.calculate_shipping_from_options
consumes — addresses, country, package dimensions/weights, residential
flag, COD/insurance values, LTL/freight defaults, RMA carrier
restrictions, etc. Pure construction; does not call carriers itself.

Parameters:

  • options (Hash) (defaults to: {})

    caller-provided seed hash

Returns:

  • (Hash)

    options hash with merged shipping context



1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
# File 'app/models/delivery.rb', line 1911

def calculate_shipping_options(options = {})
  # TBD REFACTOR NO ORDER DEPENDENCY FOR RETURN DELIVERY
  options[:resource_shipping_method] = (resource&.try(:edi_shipping_option_name) || resource_shipping_method)
  if resource&.try(:edi_shipping_option_name)
    options[:force_shipping_carrier] = (ShippingOption.active.where(name: resource.try(:edi_shipping_option_name))&.last&.carrier || ShippingOption.where(name: resource.try(:edi_shipping_option_name))&.last&.carrier)
  end
  options[:origin_address] = origin_address
  options[:origin_country_iso3] = origin_address.country.iso3
  if destination_address.present?
    options[:city] = destination_address.city
    options[:state] = destination_address.state_code
    options[:country_iso] = destination_address.country.iso
    options[:postal_code] = destination_address.zip_compact.to_s.upcase.strip.squeeze(' ')
    options[:is_residential] = destination_address.is_residential
    options[:destination_address] = destination_address
  elsif resource.installation_postal_code.present?
    options[:state] = resource.installation_state_code
    options[:country_iso] = resource.installation_country_iso
    options[:postal_code] = resource.installation_postal_code
  end
  # As far as i can tell this is useless when we use a scope to load them
  # options[:line_items] = line_items.select { |li| !li.destroyed? && !li.marked_for_destruction? && !li.is_shipping? && !li.is_service? } # This skips deleted, shipping and service line items
  options[:store] = customer.store
  options[:saturday_delivery] = saturday_delivery
  options[:signature_confirmation] = signature_confirmation.to_b

  options[:myprojects_legacy] = false
  if options[:country_iso] == 'CA' # kludge
    options[:ltl_freight] = is_default_ltl_freight? || ltl_freight.to_b || ltl_freight_guaranteed.to_b
    # puts "!!!!!!!!options[:ltl_freight]: #{options[:ltl_freight]}, self.is_default_ltl_freight?: #{self.is_default_ltl_freight?}, self.ltl_freight: #{self.ltl_freight}, self.ltl_freight_guaranteed: #{self.ltl_freight_guaranteed}"
  else
    options[:ltl_freight] = ltl_freight.to_b
    options[:ltl_freight_guaranteed] = ltl_freight_guaranteed.to_b
  end
  options[:insured_value] = calculate_declared_value
  use_shipments = Shipping::CreateSuggestedShipment.new.process(self)
  options.merge!(shipments_to_packages_hash(use_shipments.select { |s| s.parent_shipment_id.nil? }))
  cod_amount = 0.0
  cod_amount = resource&.total if cod_collection_type && resource
  options[:cod_collection_type] = cod_collection_type
  options[:cod_amount] = cod_amount

  # Limit carriers for RMA returns to carriers assigned for RMAs in the country
  if is_rma_return?
    rma_country = rma_for_return&.customer&.store&.country&.iso
    if rma_country.present?
      rma_sos = ShippingOption.active.for_rmas
                               .where(country: rma_country)
                               .where.not(carrier: nil)
      options[:allowed_carriers] = rma_sos.distinct.pluck(:carrier)
      options[:limit_service_codes] = rma_sos.where.not(service_code: nil).pluck(:service_code)
    end
  end
  # options[:media_mail] = true if self.line_items.goods.all?{|li| li.is_publication?} # apparently only for educational materials, see:https://about.usps.com/notices/not121/not121_tech.htm
  # puts "calculate_shipping_options: #{options.inspect}"
  if resource&.try(:is_www_ship_by_zip) || (resource&.try(:in_shipping_estimate?) && resource&.try(:shipping_address_id).nil?)
    options[:is_www_ship_by_zip] = true
  elsif resource&.try(:is_www)
    options[:www] = true
  end
  options
end

#can_be_deleted?Boolean

Returns:

  • (Boolean)


761
762
763
# File 'app/models/delivery.rb', line 761

def can_be_deleted?
  quoting? || pre_pack? || picking? || at_warehouse? || (order && order.order_type == Order::CREDIT_ORDER)
end

#can_print_carton_labels?Boolean

Returns:

  • (Boolean)


769
770
771
# File 'app/models/delivery.rb', line 769

def can_print_carton_labels?
  shipments.packed_or_measured.present?
end

#can_update_tracking_info?Boolean

Returns:

  • (Boolean)


765
766
767
# File 'app/models/delivery.rb', line 765

def can_update_tracking_info?
  shipments.completed.present?
end

#can_void_rma_delivery?Boolean

Returns:

  • (Boolean)


877
878
879
# File 'app/models/delivery.rb', line 877

def can_void_rma_delivery?
  shipments.label_complete.any? && created_at > 30.days.ago # we implemented Shipengine for RMA carrier (UPS) on 2023-08-31 but there's a 30 day void deadline per: https://www.shipengine.com/docs/labels/voiding/#refund-process
end

#canadian_tire_special_check?Boolean

Canadian tire requires all packages are less than 67 lbs to ship Purolator otherwise Consolidated Fastfrate LTL

Returns:

  • (Boolean)


2511
2512
2513
2514
2515
2516
2517
2518
# File 'app/models/delivery.rb', line 2511

def canadian_tire_special_check?
  customer&.billing_entity&.is_canadian_tire? &&
    (
      shipments.any? { |s| s.weight > CustomerConstants::CANADIAN_TIRE_LTL_FREIGHT_WEIGHT_THRESHOLD } ||
        CustomerConstants::CANADIAN_TIRE_LTL_FREIGHT_REQUIRED_STORE_ADDRESS_IDS.include?(destination_address.id) ||
        CustomerConstants::CANADIAN_TIRE_LTL_FREIGHT_REQUIRED_STORE_NUMBERS.include?(destination_address&.company_name&.split(' #')&.last)
    )
end

#cancelBoolean

Cancels the delivery in a transaction: releases reserved serial numbers
and committed inventory, voids any in-flight marketplace labels (Walmart
SWW, Amazon Buy Shipping), unpacks shipments, and cancels associated
dropship PurchaseOrders and pre-created Rma. No-op (and adds an
error) if the delivery is not in a cancelable state.

Returns:

  • (Boolean)

    true when cancelled, false when not cancelable



1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
# File 'app/models/delivery.rb', line 1051

def cancel
  Delivery.transaction do
    res = false
    if cancelable?
      logger.info 'Before delivery cancel'
      uncommit_reserved_serial_numbers
      rejoin_serial_numbers

      # Void marketplace labels (Walmart SWW, Amazon, etc.) before canceling shipments
      void_marketplace_labels

      shipments.awaiting_label.each(&:pack)
      unless pre_pack?
        uncommit_catalog_items
        shipments.packed.each(&:unpack) # This prevents shipments from changing when any conditions (like changing items) causes shipping to be recalculated, we should allow shipments to be resettable # unless all_lines_allocated_to_shipments?
      end # this is issued so that the qty_available goes back up
      drop_ship_purchase_orders.each(&:cancel_items_and_self)
      precreated_rma.void_all_items_and_self if precreated_rma.present?
      res = true
    else
      errors.add(:base, "Can't cancel delivery #{name}, ID: #{id} in state: #{state}, it is not in a cancelable state.")
    end
    res
  end
end

#cancel_estimated_packagingBoolean

Aborts an in-progress pre-pack and returns the delivery to
quoting. Wraps the state machine event with the same name.

Returns:

  • (Boolean)


4993
4994
4995
# File 'app/models/delivery.rb', line 4993

def cancel_estimated_packaging
  back_to_quoting
end

#cancelable?(current_user = nil) ⇒ Boolean

These methods above are from the model previously known as delivery_quote

Returns:

  • (Boolean)


3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
# File 'app/models/delivery.rb', line 3076

def cancelable?(current_user = nil)
  if current_user
    # lock FBA based on criteria unless an admin
    return false if locked_for_fba? && !current_user.has_role?('admin')

    # here we are role sensitive so only let warehouse reps cancel certain states when role sensitive
    ANY_EMPLOYEE_CANCELABLE_STATES.include?(state.to_sym) || (WAREHOUSE_CANCELABLE_STATES.include?(state.to_sym) && current_user.has_role?('warehouse_rep'))

  else
    # here we are not role sensitive so let any cancelable states through
    CANCELABLE_STATES.include?(state.to_sym)
  end
end

#cannot_ship_empty_delivery?Boolean

Guard method for shipping state transitions.
Returns true if shipping should be BLOCKED (used with :unless in state machine).

Returns:

  • (Boolean)


3470
3471
3472
# File 'app/models/delivery.rb', line 3470

def cannot_ship_empty_delivery?
  !has_shippable_content?
end

#carrier_customs_emailString?

Customs-team email address for the reported carrier when manual
customs handoff is required (Freightquote dispatches by SCAC, others
by carrier name).

Returns:

  • (String, nil)


3426
3427
3428
3429
3430
3431
3432
3433
# File 'app/models/delivery.rb', line 3426

def carrier_customs_email
  return unless should_send_commercial_invoice_to_carrier? # just return nil unless we qualify
  if reported_carrier == 'Freightquote' # Deal with Freightquote special case
    FREIGHTQUOTE_CARRIERS_TO_SEND_COMMERCIAL_INVOICES.detect{|fcarr| fcarr.dig(:scac) == selected_shipping_cost&.rate_data&.dig('scac')}&.dig(:customs_email)
  else
    CARRIERS_TO_SEND_COMMERCIAL_INVOICES.detect{|carr| carr.dig(:name) == reported_carrier}&.dig(:customs_email)
  end
end

#carrier_iconString

Asset path / icon class for the delivery's carrier, used in
warehouse and customer dashboards.

Returns:

  • (String)


4156
4157
4158
# File 'app/models/delivery.rb', line 4156

def carrier_icon
  Shipment.carrier_icon(carrier)
end

#carrier_options_for_selectArray<Array(String, String)>

Carriers eligible for this delivery formatted for a <select> helper —
filtered by origin country, supplier capabilities, and override flags.

Returns:

  • (Array<Array(String, String)>)

    [label, carrier_code] pairs



973
974
975
# File 'app/models/delivery.rb', line 973

def carrier_options_for_select
  Shipment.carrier_options_for_select(self)
end

#catalogObject

Alias for Customer#catalog

Returns:

  • (Object)

    Customer#catalog

See Also:



176
# File 'app/models/delivery.rb', line 176

delegate :catalog, :is_amazon_seller_central?, to: :customer

#chosen_shipping_methodShippingCost?

The ShippingCost the order is currently committed to — explicit
selected_shipping_cost if present, otherwise the cost linked to the
shipping LineItem. Service-only deliveries default to the first
available cost row.

Returns:



2259
2260
2261
2262
2263
# File 'app/models/delivery.rb', line 2259

def chosen_shipping_method
  return shipping_costs.first if is_service_only?

  selected_shipping_cost || shipping_line_item.try(:shipping_cost)
end

#chosen_shipping_method_carrier_costFloat

Carrier-quoted cost for the chosen shipping method, falling back to
the rate_data actual_cost when the row was zeroed out (e.g. customer
third-party billing accounts where we don't bill the rate).

Returns:

  • (Float)


2229
2230
2231
2232
2233
# File 'app/models/delivery.rb', line 2229

def chosen_shipping_method_carrier_cost
  c = chosen_shipping_method&.cost.to_f
  rd = chosen_shipping_method&.rate_data
  (c == 0.0 && rd.present? ? rd['actual_cost'].to_f : c)
end

#commit_catalog_itemsvoid

This method returns an undefined value.

Decrements available inventory for all of this delivery's
LineItems via Item::InventoryCommitter — invoked at ship time
to convert reserved stock into shipped stock.



4279
4280
4281
# File 'app/models/delivery.rb', line 4279

def commit_catalog_items
  Item::InventoryCommitter.crm_commit(line_items)
end

#commit_reserved_serial_numbersvoid

This method returns an undefined value.

Promotes all reserved SerialNumbers on the delivery's line items
to "committed" so they're attached to the shipped units.



4287
4288
4289
# File 'app/models/delivery.rb', line 4287

def commit_reserved_serial_numbers
  line_items.each(&:commit_reserved_serial_numbers)
end

#complete_pickedvoid

This method returns an undefined value.

End-of-pick / end-of-pre-pack handler invoked from the warehouse UI:
transitions pre_pack deliveries through the pre-packed flow with
parent-order notifications, and transitions picking /
pending_ship_labels through the picked event when allocation is
complete.



4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
# File 'app/models/delivery.rb', line 4951

def complete_picked
  if pre_pack?
    pre_packed
    if all_lines_allocated_to_shipments_and_shipments_have_weight? && quoting? # this means it successfully completed pre_pack, all items allocated,we need this all_lines_allocated_to_shipments_and_shipments_have_weight? call to populate the errors
      shipments.suggested.each(&:pack!) # mark shipments as actually packed
      # now handle order and quote flow notifications and state flow
      # The two notification branches publish Events::DeliveryPrePacked via
      # send_delivery_pre_packed_notification; Shipping::DeliveryPrePackPackingHandler
      # subscribes to that event and writes the Packing row with
      # origin: :from_manual_entry, replacing the previous synchronous
      # set_packaged_items_md5_hash(origin: :from_manual_entry) call.
      #
      # The wasn4_or_wat0f branch does not publish DeliveryPrePacked
      # (intentionally — it has its own carrier-assignment notification
      # path), so it keeps the synchronous Packing write to preserve the
      # original "for all pre-packs, add to md5 Packing db" guarantee.
      if order&.pre_pack?
        # order pre_pack flow has its own notifications
        if order&.is_wasn4_or_wat0f?
          order.request_carrier_assignment
          set_packaged_items_md5_hash(origin: :from_manual_entry)
        else
          order.release_from_pre_pack_to_cr_hold
          send_delivery_pre_packed_notification
        end
      else
        quote.ready_to_transmit if quote&.pre_pack?
        send_delivery_pre_packed_notification
      end
    end
  elsif picking? || pending_ship_labels? # || processing_po_fulfillment?) # rb_any_ship_from here we are plan to ship-label the drop-ship delivery
    picked
    if all_lines_allocated_to_shipments_and_shipments_have_weight? && pending_ship_labels? # we need this all_lines_allocated_to_shipments_and_shipments_have_weight? call to populate the errors
      shipments.suggested.each(&:pack!) # mark shipments as actually packed
    end
  end
end

#completed_regular_delivery?Boolean

Returns:

  • (Boolean)


1228
1229
1230
# File 'app/models/delivery.rb', line 1228

def completed_regular_delivery?
  (pending_pickup_confirm? || shipped? || invoiced?) && !is_service_only?
end

#copy_shipments_if_drop_ship_poObject

Copies shipments from associated drop ship purchase orders
to this delivery's shipments. This is done after a drop ship
purchase order is fulfilled to copy the shipment details over.



4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
# File 'app/models/delivery.rb', line 4571

def copy_shipments_if_drop_ship_po
  return unless has_dropship_items?

  commit_catalog_items # this is issued so that the qty_available goes down
  # clear out suggested shipments
  shipments.suggested.destroy_all
  drop_ship_purchase_orders.each do |drop_ship_po|
    drop_ship_po.purchase_order_shipments.each do |pos|
      shipments.create(
        is_legacy: false,
        is_manual: true,
        state: 'manually_complete',
        delivery_id: id,
        order_id: order.id,
        tracking_number: pos.tracking_number,
        carrier: pos.drop_ship_carrier,
        container_type: pos.container_type,
        weight: pos.weight,
        width: pos.width,
        length: pos.length,
        height: pos.height,
        actual_total_charges: pos.actual_shipping_cost
      )
      carrier_bol ||= pos.bill_of_lading if pos.bill_of_lading.present?
      # puts "Delivery#copy_shipments_if_drop_ship_po: delivery: #{self.name}, self.shipments: #{self.shipments.inspect}"
    end
  end
  set_master_tracking_and_actual_shipping_cost_if_needed
end

#countryCountry?

Country the delivery is associated with for currency, tax, and
carrier defaults. RMA returns prefer the address country when
origin/destination match, otherwise the customer's country.

Returns:



4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
# File 'app/models/delivery.rb', line 4354

def country
  if rma_for_return.present?
    # For RMA deliveries, determine country based on origin and destination addresses
    # If both addresses are in the same country, use that country
    # Otherwise, fall back to customer's country
    if origin_address && destination_address && origin_address.country_iso3 == destination_address.country_iso3
      origin_address.country
    else
      rma_for_return.customer.country
    end
  else
    order&.country || resource&.country
  end
end

#create_invoiceInvoice

Builds the Invoice for this shipped delivery via
Invoicing::CreateInvoiceFromDelivery, validating the delivery and
its payments first. Service raises on any failure so callers can
surface the error.

Returns:

Raises:

  • (RuntimeError)

    when the delivery or its payments are invalid



4499
4500
4501
4502
4503
4504
4505
# File 'app/models/delivery.rb', line 4499

def create_invoice
  raise "Delivery ID: #{id} is not valid, errors #{errors_to_s}" unless valid?
  raise "A Payment on Delivery ID: #{id} is not valid" unless payments.all?(&:valid?)

  # Service handles all invoice creation, validation, and raises on any failure
  Invoicing::CreateInvoiceFromDelivery.new.process(self)
end

#create_shipments_from_equivalent_delivery(equivalent_delivery, shipment_state = 'awaiting_label') ⇒ void

This method returns an undefined value.

Clones Shipments and shipment contents from another delivery
(e.g. when re-creating a voided shipment plan) onto this delivery,
mapping items by id and respecting per-line allocation limits.

Parameters:

  • equivalent_delivery (Delivery)
  • shipment_state (String) (defaults to: 'awaiting_label')

    state to filter source shipments by



4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
# File 'app/models/delivery.rb', line 4870

def create_shipments_from_equivalent_delivery(equivalent_delivery, shipment_state = 'awaiting_label')
  Shipment.transaction do
    # here we want to copy the shipments and shipments contents from an equivalent delivery
    item_map = {}
    item_map = Shipping::CreateSuggestedShipment.new.build_item_map_from_line_items(line_items) if equivalent_delivery.shipments.where(state: shipment_state).all? { |s| s.shipment_contents.present? }

    equivalent_delivery.shipments.where(state: shipment_state).find_each do |s|
      suggested_shipment = shipments.create(weight: s.weight,
                                            length: s.length,
                                            width: s.width,
                                            height: s.height,
                                            is_legacy: false,
                                            is_manual: false,
                                            flat_rate_package_type: s.flat_rate_package_type,
                                            container_type: s.container_type,
                                            state: 'suggested')
      if suggested_shipment.errors.present? || !suggested_shipment.persisted?
        logger.error "Could not create suggested shipment: #{suggested_shipment.errors_to_s}"
      elsif item_map.present? && s.shipment_contents.present?
        s.shipment_contents.each do |shipment_content|
          # this returns an array of [line item id, quantities]
          qty_remaining_to_allocate = shipment_content.quantity
          # Sometimes garbage data or mismatch can cause an item to be fully allocated already
          break if item_map[shipment_content.line_item.item.id].nil?

          item_map[shipment_content.line_item.item.id].each do |(line_item_id, line_quantity)|
            allocatable_qty = [qty_remaining_to_allocate, line_quantity].min
            sc = suggested_shipment.shipment_contents.where(line_item_id:).first_or_initialize
            sc.quantity = allocatable_qty
            sc.save
            # Remove or reduce the quantity from this line
            item_map[shipment_content.line_item.item.id][line_item_id] -= allocatable_qty
            # If the line is fully allocated, we remove this entry
            item_map[shipment_content.line_item.item.id].delete(line_item_id) if item_map[shipment_content.line_item.item.id][line_item_id] <= 0
            # and remove the item id if its all allocated
            item_map.delete(shipment_content.line_item.item.id) if item_map[shipment_content.line_item.item.id].empty?
          end
          # If we allocated everything in this box we move on
          break if qty_remaining_to_allocate.zero?
        end
        # map shipment contents
      end
    end
  end
end

#create_st_ledger_entriesvoid

This method returns an undefined value.

Records intra-company GL and item-ledger entries for an in-flight
store-transfer delivery (one warehouse to another inside the same
company).



4487
4488
4489
4490
# File 'app/models/delivery.rb', line 4487

def create_st_ledger_entries
  LedgerTransaction.process_intracompany_st_delivery(self)
  ItemLedgerEntry.process_intracompany_st_delivery(self)
end

#currencyString?

ISO-4217 currency code the delivery transacts in, falling through
order → resource → RMA customer's catalog.

Returns:

  • (String, nil)


4373
4374
4375
# File 'app/models/delivery.rb', line 4373

def currency
  order&.currency || resource&.currency || rma_for_return&.customer&.catalog&.currency
end

#currency_symbolString

Glyph for #currency (e.g. "$", "€") for display formatting.

Returns:

  • (String)


4380
4381
4382
# File 'app/models/delivery.rb', line 4380

def currency_symbol
  Money::Currency.new(currency).symbol
end

#custom_pack_listUpload?

Customer-supplied custom packing slip (Upload) attached to the
parent Order, when present. Memoized; returns nil if the file no
longer exists on disk.

Returns:



1311
1312
1313
1314
1315
1316
# File 'app/models/delivery.rb', line 1311

def custom_pack_list
  cpl = order.uploads.in_category('custom_packing_slip_pdf').first
  return cpl if cpl&.file_exists? # commenting out for now because this returns false though it actually does exist and can be downloaded combined etc.

  nil
end

#customerObject

Alias for Resource_or_rma_for_delivery#customer

Returns:

  • (Object)

    Resource_or_rma_for_delivery#customer

See Also:



174
# File 'app/models/delivery.rb', line 174

delegate :customer, :primary_party, to: :resource_or_rma_for_delivery

#default_container_typeString

Default packaging container for new Shipments on this delivery —
pallet for LTL freight, otherwise carton.

Returns:

  • (String)


4938
4939
4940
4941
4942
# File 'app/models/delivery.rb', line 4938

def default_container_type
  # Shipment.container_types.keys[1] # 'pallet'
  # Shipment.container_types.keys.first # 'carton'
  ships_ltl_freight? ? Shipment.container_types.keys[1] : Shipment.container_types.keys.first
end

#delete_serial_number_reservationsvoid

This method returns an undefined value.

Destroys every ReservedSerialNumber on this delivery's line
items, releasing them for reuse.



4735
4736
4737
# File 'app/models/delivery.rb', line 4735

def delete_serial_number_reservations
  line_items.each { |li| li.reserved_serial_numbers.destroy_all }
end

#delta_weight_factor_remaining_to_allocateFloat

Fraction of the delivery's expected ship weight still represented by
un-allocated LineItems. Used to gate "ready to ship-label" UX so the
warehouse doesn't try to label a partially packed shipment.

Returns:

  • (Float)

    0.0 = fully allocated, 1.0 = nothing allocated



1008
1009
1010
# File 'app/models/delivery.rb', line 1008

def delta_weight_factor_remaining_to_allocate
  (line_allocation_status_hash.sum{|k,v| LineItem.find(k).shipping_weight*v.abs}.to_f/ship_weight)
end

#destination_addressAddress

Returns:

See Also:

Validations (unless => #skip_destination_address_validation? ):



104
# File 'app/models/delivery.rb', line 104

belongs_to :destination_address, class_name: 'Address', validate: true, optional: true

#discountsActiveRecord::Relation<Discount>

Returns:

See Also:



115
# File 'app/models/delivery.rb', line 115

has_many :discounts, through: :line_discounts

#display_carrier_nameObject

When Freightquote is used as a broker, the human-readable carrier is stored
in rate_data rather than on the delivery itself. Fall back to reported_carrier
for all other carriers, or when rate_data has no carrier_name.



2399
2400
2401
# File 'app/models/delivery.rb', line 2399

def display_carrier_name
  chosen_shipping_method&.rate_data&.[]('carrier_name') || reported_carrier
end

#do_not_ship_insure_via_carrier?Boolean

Returns:

  • (Boolean)


2243
2244
2245
2246
2247
2248
2249
2250
2251
# File 'app/models/delivery.rb', line 2243

def do_not_ship_insure_via_carrier?
  res = false
  res = true if Shipping::ShippingInsurance.new.qualifies_for_rating?(self)
  res = true if order&.is_sales_order? && order.is_edi_order? && customer.is_wayfair? # we do not declare value for Wayfair EDI orders
  # we do not declare value for THD orders of any kind per MARIA_C_WASSER@homedepot.com and ALEX_T_LOVELL@homedepot.com 4/30/24
  res = true if order&.is_sales_order? && (order&.is_home_depot_usa? || order&.is_home_depot_can?)
  res = true if ships_ltl_freight? && Shipping::LtlShippingInsurance::LTL_SELF_INSURED
  res
end

#do_not_validate_line_items?Boolean

Returns:

  • (Boolean)


3449
3450
3451
# File 'app/models/delivery.rb', line 3449

def do_not_validate_line_items?
  do_not_validate_line_items
end

#does_not_require_shipping_labeling?Boolean

Returns:

  • (Boolean)


5040
5041
5042
5043
# File 'app/models/delivery.rb', line 5040

def does_not_require_shipping_labeling?
  order&.is_store_transfer? &&
    customer&.id&.in?(CustomerConstants::TRANSFER_TO_WY_CUSTOMER_IDS)
end

#drop_ship_purchase_ordersActiveRecord::Relation<PurchaseOrder>

Returns:

See Also:



118
# File 'app/models/delivery.rb', line 118

has_many :drop_ship_purchase_orders, class_name: 'PurchaseOrder', foreign_key: 'drop_ship_delivery_id'

#electronic_ship_ci_pdfUpload?

Most recent carrier-electronic commercial-invoice Upload (for UPS
paperless invoicing and similar).

Returns:



4017
4018
4019
# File 'app/models/delivery.rb', line 4017

def electronic_ship_ci_pdf
  uploads.order(:id).reverse_order.find_by(category: 'electronic_ship_ci_pdf')
end

#estimated_delivery_dateObject

Trying to 'guess' the delivery date



754
755
756
757
758
759
# File 'app/models/delivery.rb', line 754

def estimated_delivery_date
  carrier_commitment = (selected_shipping_cost || shipping_option)&.days_commitment&.ceil || 7
  ship_date = shipped_date || future_release_date || 0.working.day.from_now
  ship_date += carrier_commitment.days
  ship_date.to_date
end

#estimated_tare_weightFloat

Memoized total tare weight across the delivery's top-level
Shipments, preferring measured shipments, then packed, then any.
Used in weight-discrepancy threshold checks.

Returns:

  • (Float)


4670
4671
4672
4673
4674
4675
4676
# File 'app/models/delivery.rb', line 4670

def estimated_tare_weight
  return @estimated_tare_weight if instance_variable_defined?(:@estimated_tare_weight)

  top_level = shipments.top_level
  shipments_to_check = top_level.measured.presence || top_level.packed.presence || top_level
  @estimated_tare_weight = shipments_to_check.to_a.sum { |s| s.compute_tare_weight.to_f }
end

#european_shipment?Boolean

Returns:

  • (Boolean)


5045
5046
5047
# File 'app/models/delivery.rb', line 5045

def european_shipment?
  customer&.catalog&.store&.country&.eu_country? # This might be too loose but for now it works
end

#existing_shipment_attributes=(shipment_attributes) ⇒ void

This method returns an undefined value.

Nested-attributes setter for editing or removing already-persisted
Shipments — rows missing from the hash are removed via the
association.

Parameters:

  • shipment_attributes (Hash{String => Hash})

    keyed by Shipment id



3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
# File 'app/models/delivery.rb', line 3541

def existing_shipment_attributes=(shipment_attributes)
  shipments.reject(&:new_record?).each do |shipment|
    attributes = shipment_attributes[shipment.id.to_s]
    if attributes
      shipment.attributes = attributes
    else
      shipments.delete(shipment)
    end
  end
end

#existing_shipping_cost_attributes=(shipping_cost_attributes) ⇒ void

This method returns an undefined value.

Nested-attributes setter for editing or removing already-persisted
ShippingCost rows from a delivery form. Rows missing from the hash
are deleted via the association.

Parameters:

  • shipping_cost_attributes (Hash{String => Hash})

    keyed by ShippingCost id



2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
# File 'app/models/delivery.rb', line 2211

def existing_shipping_cost_attributes=(shipping_cost_attributes)
  # puts "existing_shipping_cost_attributes= : shipping_cost_attributes: #{shipping_cost_attributes}"
  shipping_costs.reject(&:new_record?).each do |sc|
    attributes = shipping_cost_attributes[sc.id.to_s]
    if attributes
      sc.attributes = attributes
      sc.save # trying to fix itemizable before save prevention of saving these on order.update
    else
      shipping_costs.delete(sc)
    end
  end
end

#freight_eventsActiveRecord::Relation<FreightEvent>

CHR/Freightquote Navisphere events landed via the webhook pipeline; consumed by
FreightEventStatusSummary for the warehouse-dashboard and delivery-show
status icons, and by MissedFreightPickupSweep / FreightquoteVoidConfirmationWorker.

Ordered newest-first by emitted_at (the CHR webhook-gateway emission
timestamp from payload['time']) because CHR's per-subsystem eventTime
values aren't monotonically ordered across event types — LOAD BOOKED's
eventTime can precede the LOAD CREATED that conceptually came first.
emitted_at is CHR's single monotonic clock; event_time and id are
deterministic tiebreakers for the sub-millisecond races (ORDER CREATED +
ORDER UPDATED arriving in the same packet).

Returns:

See Also:



136
137
# File 'app/models/delivery.rb', line 136

has_many :freight_events, -> { order(emitted_at: :desc, event_time: :desc, id: :desc) },
inverse_of: :delivery, dependent: :nullify

#freightquote_carrier?Boolean

Returns:

  • (Boolean)


2392
2393
2394
# File 'app/models/delivery.rb', line 2392

def freightquote_carrier?
  reported_carrier.to_s.include?('Freightquote')
end

#friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ String

Cached, fully decorated shipping method label (carrier, service,
COD/insurance/account notes) suitable for emails, invoices, and
confirmation pages.

Parameters:

  • show_customer_pays_info (Boolean) (defaults to: false)

    include third-party billing account hints

  • for_www (Boolean) (defaults to: false)

    phrase the account hint for the public site

  • with_delivery_commitment (Boolean) (defaults to: false)

    append the carrier delivery commitment

Returns:

  • (String)


2322
2323
2324
2325
2326
2327
2328
# File 'app/models/delivery.rb', line 2322

def friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false)
  if show_customer_pays_info
    @friendly_shipping_method_with_pay_info ||= retrieve_friendly_shipping_method(show_customer_pays_info, for_www, nil, with_delivery_commitment)
  else
    @friendly_shipping_method ||= retrieve_friendly_shipping_method(show_customer_pays_info, for_www, nil, with_delivery_commitment)
  end
end

#friendly_shipping_method_for_ediString

EDI-flavored variant of #friendly_shipping_method — strips the
FedEx signature notice and other consumer-facing decorations partner
systems don't want.

Returns:

  • (String)


2374
2375
2376
# File 'app/models/delivery.rb', line 2374

def friendly_shipping_method_for_edi
  retrieve_friendly_shipping_method(false, false, nil, false, true)
end

#generate_all_international_forms_pdfBoolean, Array<Upload>

Combines the commercial invoice, BOL, USMCA/FTA item certificates,
and the latest steel/aluminum/copper declaration into a single
all_intl_forms_pdf Upload for international shipments.

Returns:

Raises:

  • (RuntimeError)

    when the combined PDF fails to generate



3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
# File 'app/models/delivery.rb', line 3623

def generate_all_international_forms_pdf
  res = true
  # puts "self.generate_all_international_forms_pdf, delivery #{self.id}: #{self.inspect}"
  all_forms = [ship_ci_pdf, bol_pdf].compact

  # Include USMCA/FTA certificates attached to items on this delivery
  begin
    item_ids = line_items.goods.pluck(:item_id)
    if item_ids.present?
      usmca_cert_uploads = Upload.where(category: 'usmca_certificate')
                                 .joins(:items)
                                 .where(items: { id: item_ids })
                                 .to_a
      steel_decl = Upload.where(category: 'declaration_of_steel_aluminimum_copper').order(id: :desc).first
      usmca_cert_uploads << steel_decl if steel_decl
      all_forms.concat(usmca_cert_uploads) if usmca_cert_uploads.present?
    end
  rescue StandardError => e
    Rails.logger.warn("generate_all_international_forms_pdf: USMCA cert include failed: #{e}")
  end

  # Add Commercial Invoice
  if all_forms.present?
    file_name = "#{name(false, true)}_all_intl_forms_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf".downcase
    all_forms_path = Rails.application.config.x.temp_storage_path.join(file_name)
    # puts "self.generate_all_international_forms_pdf: all_forms_path: #{all_forms_path}, all_forms: #{all_forms.inspect}"
    PdfTools.combine(all_forms, output_file_path: all_forms_path)
    upload = Upload.uploadify(all_forms_path, 'all_intl_forms_pdf')
    raise 'Combo international forms was not generated' unless upload

    res = uploads << upload
  end
  res
end

#generate_all_labels_pdfvoid

This method returns an undefined value.

Combines every per-shipment label PDF, the serial-numbers PDF, any
master-carton labels, and any return-RMA labels into batched
all_labels_pdf Uploads. Batched at 10 to stay within the temp-dir
FD ceiling.

Raises:

  • (RuntimeError)

    when a batch combo PDF fails to generate



3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
# File 'app/models/delivery.rb', line 3579

def generate_all_labels_pdf
  all_shipments = shipments.completed.order(:id).includes(:uploads)
  # Batching since the tmp folder will only reliably hold ~15 tmp files before GC, so use batches of 10
  batch_size = 10
  (all_shipments.size.to_f / batch_size.to_f).ceil.times do |i|
    all_shipments_batch = all_shipments[(i * batch_size)..((batch_size * (i + 1)) - 1)]
    all_labels = []
    all_shipments_batch.each do |s|
      label = s.uploads.detect { |u| u.category == 'ship_label_pdf' }
      next unless label

      all_labels << label
    end
    rmas = []
    rmas << order.rma if order&.is_rma_replacement?
    rmas << precreated_rma if precreated_rma.present?
    rmas.each do |rma|
      rma.credit_orders.each do |co|
        co.shipments.label_complete.order(:id).includes(:uploads).each do |s|
          label = s.uploads.detect { |u| u.category == 'ship_label_pdf' }
          all_labels << label if label
        end
      end
    end
    serial_numbers_pdf = generate_serial_numbers_pdf
    all_labels << serial_numbers_pdf unless serial_numbers_pdf.nil?
    all_labels.concat(order.uploads.in_category('master_carton_label')) if order # include any master carton labels
    file_name = "batch_#{i}_#{name(false, true)}_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("generate_all_labels_pdf", path: all_labels_path, label_count: all_labels&.size)
    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
end

#generate_asynch_labelsObject



3984
3985
3986
3987
3988
3989
# File 'app/models/delivery.rb', line 3984

def generate_asynch_labels
  generate_bol_pdf
  generate_ci_pdf
  generate_all_labels_pdf
  generate_all_international_forms_pdf
end

#generate_barcodeString

Writes a Code 128B barcode of the parent order's reference number to
tmp/ for embedding into pick-slip / packing-slip PDFs.

Returns:

  • (String)

    absolute path to the generated PNG



1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
# File 'app/models/delivery.rb', line 1376

def generate_barcode
  require 'barby'
  require 'barby/barcode/code_128'
  require 'barby/outputter/png_outputter'
  barcode = Barby::Code128B.new(order.reference_number.to_s)
  path = File.join(Rails.application.config.x.temp_storage_path.to_s, "#{name(false, true)}_generated_barcode_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.png")
  File.open(path, 'wb') do |file|
    file.write barcode.to_png(height: 70)
    file.flush
    file.fsync
  end
  path
end

#generate_bol_pdf(ship_bol_tmp_path = nil) ⇒ Array<Upload>

Produces the bill-of-lading PDF for an LTL freight shipment and
attaches it as an Upload. If the carrier supplied its own BOL we use
that file; otherwise we fall back to Shipping::BolGenerator. Two
copies are combined per warehouse policy.

Parameters:

  • ship_bol_tmp_path (String, nil) (defaults to: nil)

    carrier-generated BOL temp file

Returns:

  • (Array<Upload>)

    uploads with the new BOL appended

Raises:

  • (RuntimeError)

    when no upload was produced



1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
# File 'app/models/delivery.rb', line 1398

def generate_bol_pdf(ship_bol_tmp_path = nil)
  Rails.logger.info("generate_bol_pdf: ship_bol_tmp_path: #{ship_bol_tmp_path}")
  # if we have carrier generated BOL, use that
  if ship_bol_tmp_path
    files_to_combine = [ship_bol_tmp_path, ship_bol_tmp_path] # combine two copies into one per JJ @warehouse
    file_name = "#{reference_number}_BOL.pdf"
    ship_bol_pdf_path = Upload.temp_location(file_name)
    Rails.logger.info("generate_bol_pdf: ship_bol_pdf_path: #{ship_bol_pdf_path}")
    duplicate_bol_pdf_path = PdfTools.combine(files_to_combine, output_file_path: ship_bol_pdf_path, orientation: :portrait)
    Rails.logger.info("generate_bol_pdf: duplicate_bol_pdf_path: #{duplicate_bol_pdf_path}")
    upload = Upload.uploadify(duplicate_bol_pdf_path, 'ship_bol_pdf')
  else # otherwise try to generate it using our template from scratch
    res = Shipping::BolGenerator.new.process(self)
    combined_pdf = PdfCombinator.new
    2.times do # combine two copies into one per JJ @warehouse
      combined_pdf << res.pdf
    end
    path = Upload.temp_location(res.file_name)
    File.open(path, 'wb') do |file|
      file.write(combined_pdf.to_pdf)
      file.flush
      file.fsync
    end
    upload = Upload.uploadify(path, 'ship_bol_pdf', nil, res.file_name)
  end
  raise 'BOL PDF was not generated' unless upload

  Rails.logger.debug("generate_bol_pdf complete", upload_id: upload&.id)
  uploads << upload
end

#generate_ci_pdf(ci_tmp_path = nil) ⇒ Array<Upload>

Produces the commercial invoice PDF (in triplicate) required for
international shipments and attaches it as an Upload. Uses the
carrier-supplied electronic CI when available, otherwise renders one
via Pdf::Document::CommercialInvoice.

Parameters:

  • ci_tmp_path (String, nil) (defaults to: nil)

    carrier-generated CI temp file

Returns:

  • (Array<Upload>)

    uploads with the new CI appended

Raises:

  • (RuntimeError)

    when no upload was produced



1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
# File 'app/models/delivery.rb', line 1441

def generate_ci_pdf(ci_tmp_path = nil)
  # if we have carrier generated CI, use that
  # UPSFreight generates a CI in triplicate ie x 3
  if ci_tmp_path
    # generate the pdf from the file path, note that this means it is a UPS **electronic** Commercial Invoice, not to be printed but stored for future reference.
    file_name = "order_#{order.reference_number}_delivery_#{index}_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}_ci.pdf"
    ci_pdf_path = Upload.temp_location(file_name)
    FileUtils.cp(ci_tmp_path, ci_pdf_path)
    # attach the finished CI pdf
    upload = Upload.uploadify(ci_pdf_path, 'electronic_ship_ci_pdf', nil, file_name)
  else # otherwise try to generate it using our template from scratch
    pdf_data = Pdf::Document::CommercialInvoice.new(self).call.pdf

    combined_pdf = PdfCombinator.new
    3.times do
      combined_pdf << pdf_data
    end

    path = Upload.temp_location("#{reference_number}_CI.pdf")
    File.open(path, 'wb') do |file|
      file.write combined_pdf.to_pdf
      file.flush
      file.fsync
    end
    upload = Upload.uploadify(path, 'ship_ci_pdf', nil, "#{reference_number}_CI.pdf")
  end
  raise 'CI PDF was not generated' unless upload

  uploads << upload
end

#generate_combined_pdf(split_kits: false, skip_plans: false) ⇒ Object

this must be here, even if just as a wrapper because the method above calls it!



1297
1298
1299
1300
1301
1302
1303
1304
# File 'app/models/delivery.rb', line 1297

def generate_combined_pdf(split_kits: false, skip_plans: false) # this must be here, even if just as a wrapper because the method above calls it!
  pdf_generator = Pdf::Document::PackingSlip.new(self, { split_kits:, skip_plans: })
  pdf_data = pdf_generator.call

  pdf_data.pages.each { |p| p.orientation :portrait }

  pdf_data
end

#generate_dropship_poObject

Generates dropship purchase orders and purchase order items for any dropship
line items on this delivery that do not already have associated purchase orders.
Groups line items by supplier, and line items for the same item. Calculates total
quantities and costs for each item group. Creates new purchase orders per supplier,
and adds purchase order items for each line item group. Associates the new
purchase order items with the delivery line items. Saves the purchase orders unless
they have no line items. Finally sends a dropship delivery notification email.



3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
# File 'app/models/delivery.rb', line 3136

def generate_dropship_po
  line_items_by_supplier = group_unprocessed_dropship_line_items_by_supplier
  existing_po_items = get_existing_po_items

  line_items_by_supplier.each do |supplier, line_items|
    po = build_purchase_order(supplier)

    group_line_items_by_item(line_items).each do |item, grouped_line_items|
      add_purchase_order_items(po, item, grouped_line_items, existing_po_items)
    end

    save_purchase_order_if_needed(po)
  end

  send_dropship_delivery_notification
end

#generate_labelsObject

res = uploads << upload
end
res
end



3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
# File 'app/models/delivery.rb', line 3676

def generate_labels
  if pending_ship_labels? && (incurs_oversized_penalty? != true)
    require 'wy_shipping'
    shipping_result = WyShipping.ship_delivery(self)
    Rails.logger.info("#{Time.current}: Order ID #{order&.id}, reference number: #{order&.reference_number}, RMA: #{rma_for_return&.rma_number}, labels generated START LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$, status_code: #{shipping_result[:status_code]}")
    append_to_shipping_api_log!(kind: 'label', shipping_result: shipping_result)
    if shipping_result[:status_code] == :error
      Rails.logger.info("#{carrier}, request_xml:")
      if carrier == 'LegacyUPS'
        keys_of_interest = %i[confirm_request_xml confirm_response_xml accept_request_xml accept_response_xml]
        keys_of_interest.each do |key|
          Rails.logger.info("#{key}:")
          Rails.logger.info(shipping_result.dig(:shipment, key))
        end
      else # self.carrier == "FedEx" || self.carrier == "UPSFreight" || self.carrier == "Purolator"
        keys_of_interest = %i[ship_request_xml ship_reply_xml]
        keys_of_interest.each do |key|
          Rails.logger.info("#{key}:")
          Rails.logger.info(shipping_result.dig(:shipment, key))
        end
      end
    end
    send_address_type_issue_notification if shipping_result[:flag_address_type_issue]
    # see if labels generated
    if shipping_result[:status_code] == :error
      { status_code: :error, 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", delivery_id: id)
      self.master_tracking_number = shipping_result[:shipment][:shipment_identification_number]
      # ShipEngine LTL carriers split into auto-PRO (SAIA, FedEx Freight, ABF…)
      # and async-PRO (Roadrunner, R+L Carriers, XPO/Con-way) per ShipEngine's
      # supported-carriers table. Auto-PRO carriers return pro_number in
      # book_pickup → pre-populate ltl_pro_number so the `:shipped` transition
      # guard at delivery.rb:364 doesn't trip. Async-PRO carriers return nil
      # — warehouse staff enters the PRO from the driver's sticker at pickup,
      # same fallback as the FreightQuote e_bol_enabled=false branch in
      # GetFreightquoteLoadNumber.
      if ShippingOption::SHIPENGINE_LTL_CARRIERS.include?(carrier) && shipping_result[:shipment][:shipment_identification_number].present?
        self.ltl_pro_number = shipping_result[:shipment][:shipment_identification_number]
      end
      self.carrier_bol = shipping_result[:shipment][:carrier_bol]
      self.pickup_confirmation_number = shipping_result[:shipment][:pickup_confirmation_number]
      self.shipengine_label_id = shipping_result[:shipment][:shipengine_label_id]
      self.freight_order_number = shipping_result[:shipment][:freight_order_number]
      self.actual_shipping_cost = shipping_result[:shipment][:total_charges]
      self.confirmed_pickup_date = shipping_result[:shipment][:confirmed_pickup_date]
      self.confirmed_pickup_window_start_at = shipping_result[:shipment][:confirmed_pickup_window_start_at]
      self.confirmed_pickup_window_end_at = shipping_result[:shipment][:confirmed_pickup_window_end_at]
      Rails.logger.info("master_tracking_number: #{master_tracking_number}, actual_shipping_cost: #{actual_shipping_cost}")
      # puts "shipping_result[:shipment][:labels].length: #{shipping_result[:shipment][:labels].length}"
      # puts "shipping_result[:shipment][:labels]: #{shipping_result[:shipment][:labels].inspect}"
      save # pure hacking to get this to work
      reload # pure hacking to get this to work
      shipments.awaiting_label.order('shipments.id ASC').each_with_index do |shipment, j|
        # Single-document LTL carriers return one BOL covering every pallet,
        # so every shipment row maps to labels[0]. Everyone else gets a
        # per-package label at labels[j]. Without ShipEngine's LTL family
        # here, the loop would mark only the first pallet label_complete
        # and leave the others stuck in awaiting_label.
        i = (%w[UPSFreight Freightquote RlCarriers Saia YrcFreight] + ShippingOption::SHIPENGINE_LTL_CARRIERS).include?(carrier) ? 0 : j
        Rails.logger.debug { "shipment: #{shipment.inspect}" }
        next unless label = shipping_result.dig(:shipment, :labels, i)

        Rails.logger.debug { "shipping_result[:shipment][:labels][i]: #{label&.inspect}" }
        Rails.logger.debug { "shipping_result[:shipment][:labels][i][:tracking_number]: #{label[:tracking_number]}" }
        Rails.logger.debug { "shipping_result[:shipment][:labels][i][:shipengine_label_id]: #{label[:shipengine_label_id]}" }
        shipment.tracking_number = label[:tracking_number]
        shipment.shipengine_label_id = label[:shipengine_label_id]
        shipment.label_generated!
      end
      Rails.logger.info("#{Time.current}: Order ID #{order&.id}, reference number: #{order&.reference_number}, RMA: #{rma_for_return&.rma_number}, labels generated END LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$")
      # save this delivery
      save

      # second pass generate the labels from the temporary paths
      label_exceptions = []
      bol_exceptions = []
      status_code = :ok
      shipments.label_complete.order(:id).each_with_index do |shipment, j|
        # UPS Freight, Freightquote, ShipEngine LTL, etc. only have a single
        # label / BOL for the whole load — same single-document-LTL set as
        # the state-transition loop above.
        i = (%w[UPSFreight Freightquote RlCarriers Saia YrcFreight] + ShippingOption::SHIPENGINE_LTL_CARRIERS).include?(carrier) ? 0 : j

        old_label_path = (begin
          shipping_result[:shipment][:labels][i][:image].path
        rescue StandardError
          nil
        end)
        # skip when label path is nil
        if old_label_path
          label_path = old_label_path
          if shipment.generate_ship_label_pdf(label_path, carrier)
            # generate letter shipping label pdf for e-mailing rma return if this is an rma return
            if is_rma_return?
              # Per-package paperless QR/barcode. For USPS Label Broker
              # (display_scheme: 'paperless') the QR IS the deliverable —
              # `label_download` is the QR page itself, NOT a separate
              # printable label — so attach the QR and do NOT rotate it
              # into a bogus letter-format "label". When the carrier didn't
              # issue paperless (nil), fall back to the printable
              # letter-format label.
              paperless_png = shipping_result.dig(:shipment, :labels, i, :paperless_png)
              if paperless_png
                shipment.attach_paperless_qr(
                  paperless_png.path,
                  shipping_result.dig(:shipment, :labels, i, :paperless_instructions),
                  shipping_result.dig(:shipment, :labels, i, :paperless_handoff_code)
                )
              elsif %w[UPS FedEx USPS].include?(carrier)
                # Shipengine will have the PNG files for each label so use those for image processing rather than the PDF
                # rotate label 90 left and generate letter ship label
                label_png_path = shipping_result.dig(:shipment, :labels, i, :png).path
                new_label_png_path = "#{label_png_path}_rot.png"
                require 'vips'
                image = Vips::Image.new_from_file(label_png_path)
                image = image.rot(:d270) # Rotate 90 degrees counter-clockwise (same as -90)
                image.write_to_file(new_label_png_path)

                # Generate letter ship label PDF using the converted image
                shipment.generate_letter_ship_label_pdf(new_label_png_path, carrier)
              elsif %w[Canadapost Canpar].include?(carrier)
                # Canadapost and Canpar will have the PDF files for each label so use those for image processing rather than the PNG
                # Generate letter ship label PDF using the converted image
                shipment.generate_letter_ship_label_pdf(nil, carrier)
              end
            end
          else
            label_exceptions << (i + 1)
          end
        end
        if carrier == 'SpeedeeDelivery'
          if shipment.generate_speedee_label_pdf(shipping_result[:shipment][:rate_data])
            shipment.generate_letter_ship_label_pdf(nil, carrier) if is_rma_return?
          else
            label_exceptions << (i + 1)
          end
        end
      end

      # Shipment-level paperless: the carrier issued a single QR/barcode
      # covering the whole MPS (typical for USPS Label Broker when one
      # ShipEngine call generated multiple per-package labels in one
      # response). Attach it to the first return shipment; the PDF
      # renderer emits one QR page up front when it finds this layout.
      if is_rma_return? && (sp = shipping_result.dig(:shipment, :shipment_paperless)) && sp[:png]
        first_return_shipment = shipments.label_complete.order(:id).first
        first_return_shipment&.attach_paperless_qr(
          sp[:png].path,
          sp[:instructions],
          sp[:handoff_code]
        )
      end

      # generate delivery RMA return instructions and labels here

      if ships_ltl_freight? || carrier == 'Freightquote'
        ship_bol_pdf_path = (begin
          shipping_result[:shipment][:labels].first[:bol_image].path
        rescue StandardError
          nil
        end)
        logger.debug("ship_bol_pdf_path: #{ship_bol_pdf_path}")
        unless carrier == 'Freightquote' # this will be done asynchronously when we get the load number
          if generate_bol_pdf(ship_bol_pdf_path)
          else
            bol_exceptions << (i + 1)
          end
        end
      end

      ci_tmp_path = shipping_result.dig(:shipment)&.dig(:ci)&.dig(:image)&.path # Generated by carrier in the case of electronic customs documents
      if ci_tmp_path || is_international? # check if we need to generate commercial invoice
        generate_ci_pdf(ci_tmp_path) unless carrier == 'Freightquote' # for Freightquote, we generate this asynchronously after we get the load number
      end
      if label_exceptions.empty? && bol_exceptions.empty? && status_code == :ok
        begin
          generate_all_labels_pdf unless carrier == 'Freightquote' # this will be done asynchronously when we get the load number
          status_message = "Labels generated for #{shipments.completed.length} packages. Estimated charge: #{currency_symbol}#{shipping_cost}. Actual charge: #{currency_symbol}#{actual_shipping_cost}. #{status_message}"
        rescue StandardError => e
          ErrorReporting.error e
          status_message = "Labels generated but combined PDF failed: #{e.message}. Individual labels are still available."
          Rails.logger.error("generate_all_labels_pdf failed for delivery #{id}: #{e.class} - #{e.message}")
        end
        # International forms backgrounded via worker (not needed for label printing)
        if is_international? && carrier != 'Freightquote'
          DeliveryPostLabelWorker.perform_async(id)
        end
        begin
          ship_labeled # implicit save!
        rescue RuntimeError => e
          return { status_code: :error, status_message: e.message }
        end
        { status_code:, 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 += ", there was an issue with bols #{bol_exceptions.map { |l| l + 1 }.join(', ')}" if bol_exceptions.any?
        msg += ", status code returned:  #{status_code}" unless status_code == :ok
        msg += '.'
        { status_code: :error, status_message: msg }
      end
    end
  else
    error_msg = if incurs_oversized_penalty?
                  "Can't generate labels because the shipping packages exceed oversized limits or include a pallet and would incur big penalties, please review packaging and consider shipping via LTL freight."
                else
                  "Can't generate labels because the delivery is in the state: #{state.humanize}."
                end
    { status_code: :error, status_message: error_msg }
  end
end

#generate_pick_slip_pdf(split_kits = false) ⇒ Upload

Renders and persists a fresh pick-slip Upload for the warehouse,
writing the PDF through Pdf::Document::PackingSlip and Upload.uploadify.

Parameters:

  • split_kits (Boolean) (defaults to: false)

    render each kit component as its own row

Returns:



1287
1288
1289
1290
1291
1292
1293
1294
1295
# File 'app/models/delivery.rb', line 1287

def generate_pick_slip_pdf(split_kits = false)
  cat = split_kits.to_b ? 'split_pick_slip_pdf' : 'pick_slip_pdf'
  combined_pdf = generate_combined_pdf(split_kits:)
  path = File.join(Rails.application.config.x.temp_storage_path.to_s, pick_slip_file_name)
  combined_pdf.save path
  upload = Upload.uploadify(path, cat, self, pick_slip_file_name)
  uploads << upload
  upload
end

#generate_serial_numbers_pdfUpload?

Renders printable serial-number labels for every reserved
SerialNumber on this delivery, attaches the PDF as an Upload, and
marks the printed serials as such.

Returns:

  • (Upload, nil)

    nil when there are no serials to print



1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
# File 'app/models/delivery.rb', line 1328

def generate_serial_numbers_pdf
  return nil if serial_numbers_to_print.empty?

  file_name = serial_numbers_file_name
  pdf       = Pdf::Label::SerialNumber.call(serial_numbers_to_print).pdf
  path      = Upload.temp_location(file_name)
  File.open(path, 'wb') { |f| f.write(pdf); f.flush; f.fsync }
  upload = Upload.uploadify(path, 'serial_numbers_pdf', self, file_name)
  uploads << upload
  SerialNumber.where(id: serial_numbers_to_print.collect(&:id)).update_all(print_state: 'printed')
  upload
end

#get_address_hash_from_address(address) ⇒ Hash{Symbol => Object}

Flattens an Address into the plain hash carrier APIs and the
carrier_responses JSONB column expect.

Parameters:

Returns:

  • (Hash{Symbol => Object})


2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
# File 'app/models/delivery.rb', line 2075

def get_address_hash_from_address(address)
  {
    street1: address.street1,
    street2: address.street2,
    city: address.city,
    state_code: address.state_code,
    country_iso: address.country_iso,
    zip: address.zip,
    is_residential: address.is_residential,
    require_signature_by_default: address.require_signature_by_default,
    has_loading_dock: address.has_loading_dock,
    is_construction_site: address.is_construction_site,
    requires_inside_delivery: address.requires_inside_delivery,
    is_trade_show: address.is_trade_show,
    requires_liftgate: address.requires_liftgate,
    limited_access: address.limited_access,
    requires_appointment: address.requires_appointment,
    timezone_name: address.timezone_name
  }
end

#get_economy_shipping_cost_to_useFloat

Cost we charge when a "ships economy" delivery's override row needs
a price. Locks to Order#shipping_cost_at_time_of_checkout when
available so cycling back through quoting (CR hold, address change,
pre-pack) never bumps the customer-facing override above what they
paid. Falls back to the cheapest WWW ground for orders with no
checkout snapshot (carts, instant quotes, CRM-built orders), and to
#get_fallback_cost_to_use when no rates are available.

Returns:

  • (Float)


5095
5096
5097
5098
5099
5100
# File 'app/models/delivery.rb', line 5095

def get_economy_shipping_cost_to_use
  locked = order&.shipping_cost_at_time_of_checkout
  return locked.to_f if locked.present? && locked.to_f > 0.0

  sorted_shipping_costs_www_hash[:ground]&.first&.cost || get_fallback_cost_to_use
end

#get_existing_po_itemsArray<PurchaseOrderItem>

Existing dropship PurchaseOrderItems on this delivery that have not
yet been linked to a specific LineItem — candidates for re-linking
rather than creating duplicates.

Returns:



3168
3169
3170
# File 'app/models/delivery.rb', line 3168

def get_existing_po_items
  drop_ship_purchase_orders.flat_map(&:purchase_order_items).select { |poi| poi.line_item_id.nil? }
end

#get_fallback_cost_to_useFloat

Sentinel cost used when carrier APIs return no rates so the order
still has something to charge. Public WWW orders use a weight
× per-lb formula clamped between FALLBACK_MIN_OVERRIDE_COST_WWW
and a fraction of declared value; CRM uses the flat
FALLBACK_OVERRIDE_COST_CRM ($500) sentinel.

Returns:

  • (Float)


5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
# File 'app/models/delivery.rb', line 5109

def get_fallback_cost_to_use
  # here we separate out the usual $500 for override and instead come up with a reasonable WWW cost based on weight and/or calculated declared value, and $500 only for CRM override, to avoid the horrible customer experience we saw when all new Canadian customer checkouts were showing $500 shipping costs because of an error in country (fixed here: https://github.com/warmlyyours/heatwave/commit/fffed50980dec5f024cb01d957b2b42792cd1e37)
  if is_www? || ships_economy_package?
    fallback_cost_to_use = ship_weight * FALLBACK_PER_LB_OVERRIDE_COST_WWW
    fallback_cost_to_use = [fallback_cost_to_use, FALLBACK_MAX_OVERRIDE_COST_FRACTION_WWW * calculate_declared_value, FALLBACK_OVERRIDE_COST_CRM].min # here max out at the smallest of per lbs rate or a fraction of the calculated value or, finally, FALLBACK_OVERRIDE_COST_CRM
    fallback_cost_to_use = [fallback_cost_to_use, FALLBACK_MIN_OVERRIDE_COST_WWW].max # here minimum of above or nominal FALLBACK_MIN_OVERRIDE_COST_WWW starting cost
  else
    fallback_cost_to_use = FALLBACK_OVERRIDE_COST_CRM
  end
  fallback_cost_to_use
end

#get_or_generate_pick_slip_pdf(split_kits = false) ⇒ Upload

Returns the existing pick-slip Upload for this delivery or generates
one on the fly when missing or its attachment was lost.

Parameters:

  • split_kits (Boolean) (defaults to: false)

    when true, render kit components as separate rows

Returns:



1221
1222
1223
1224
1225
1226
# File 'app/models/delivery.rb', line 1221

def get_or_generate_pick_slip_pdf(split_kits = false)
  cat = split_kits ? 'split_pick_slip_pdf' : 'pick_slip_pdf'
  pdf = uploads.in_category(cat).first
  pdf = generate_pick_slip_pdf(split_kits) if pdf.nil? || (pdf && pdf.attachment.blank?)
  pdf
end

#group_line_items_by_item(line_items) ⇒ Hash{Item => Array<LineItem>}

Groups a slice of LineItems by Item so we can compute one
purchase-order quantity (and therefore one tier price) per item.

Parameters:

Returns:



3200
3201
3202
# File 'app/models/delivery.rb', line 3200

def group_line_items_by_item(line_items)
  line_items.group_by(&:item)
end

#group_unprocessed_dropship_line_items_by_supplierHash{Supplier => Array<LineItem>}

Dropship LineItems that still need a PurchaseOrder (no PO item
yet, or the existing one was cancelled), grouped by supplier so each
supplier gets its own PO.

Returns:



3158
3159
3160
3161
# File 'app/models/delivery.rb', line 3158

def group_unprocessed_dropship_line_items_by_supplier
  # here we want the dropship line items that have yet to be processed, i.e. no linked dropship PO or the dropship PO item is cancelled, all group by supplier
  line_items.dropship.includes(:item).select { |li| li.purchase_order_item.nil? || li.purchase_order_item&.cancelled? }.group_by { |li| li.item.supplier_item.supplier }
end

#has_custom_products?Boolean

Returns:

  • (Boolean)


1429
1430
1431
# File 'app/models/delivery.rb', line 1429

def has_custom_products?
  line_items.joins(:item).where(items: { product_category_id: ProductCategory.custom_product_ids }).exists?
end

#has_custom_shipping_labels?Boolean

Returns:

  • (Boolean)


773
774
775
# File 'app/models/delivery.rb', line 773

def has_custom_shipping_labels?
  order.custom_shipping_labels.present?
end

#has_destination_postal_codeBoolean

Validation helper for instant-quote deliveries: requires either a
destination Address or an installation postal code on the parent
quote so rate shopping has somewhere to ship to.

Returns:

  • (Boolean)


3070
3071
3072
# File 'app/models/delivery.rb', line 3070

def has_destination_postal_code
  destination_address || resource.installation_postal_code.present?
end

#has_dropship_items?Boolean

Returns:

  • (Boolean)


3121
3122
3123
# File 'app/models/delivery.rb', line 3121

def has_dropship_items?
  line_items.joins(:item).where(items: { dropship: true }).present?
end

#has_future_release_date?Boolean

Returns:

  • (Boolean)


852
853
854
# File 'app/models/delivery.rb', line 852

def has_future_release_date?
  future_release_date && (future_release_date > Date.current)
end

#has_kits?Boolean

Returns:

  • (Boolean)


869
870
871
# File 'app/models/delivery.rb', line 869

def has_kits?
  line_items_with_counters.any? { |li| li.children_count.to_i.positive? }
end

#has_kits_or_serial_numbers?Boolean

Returns:

  • (Boolean)


873
874
875
# File 'app/models/delivery.rb', line 873

def has_kits_or_serial_numbers?
  has_serial_numbers? || has_kits?
end

#has_not_changed_but_has_shipping_lines?Boolean

Returns:

  • (Boolean)


2607
2608
2609
# File 'app/models/delivery.rb', line 2607

def has_not_changed_but_has_shipping_lines?
  relevant_changes.empty? && line_items.select(&:is_shipping?).any?
end

#has_ready_to_print_amazon_fba_items?Boolean

Returns:

  • (Boolean)


4765
4766
4767
# File 'app/models/delivery.rb', line 4765

def has_ready_to_print_amazon_fba_items?
  line_items.goods.any? { |li| li.item.ready_to_print_amazon_fba_labels? }
end

#has_serial_numbers?Boolean

Returns:

  • (Boolean)


865
866
867
# File 'app/models/delivery.rb', line 865

def has_serial_numbers?
  line_items_with_counters.any? { |li| li.reserved_serial_numbers_count.positive? || li.serial_numbers_count.positive? }
end

#has_shippable_content?Boolean

Check if this delivery has shippable content (non-shipping line items).
A delivery should not be shipped if it only contains shipping line items
and no actual goods or services. This prevents empty/shipping-only deliveries
from being invoiced, which creates €0.00 invoices.

Returns true if:

  • The delivery has at least one non-shipping line item (goods or services), OR
  • The delivery is a service-only delivery (handled via service_ready_to_fulfill), OR
  • The delivery is an RMA return (special handling)

Returns:

  • (Boolean)


3462
3463
3464
3465
3466
# File 'app/models/delivery.rb', line 3462

def has_shippable_content?
  return true if is_rma_return?

  line_items.non_shipping.exists?
end

#has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line) ⇒ Boolean

Returns:

  • (Boolean)


2611
2612
2613
# File 'app/models/delivery.rb', line 2611

def has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line)
  shipping_line && shipping_cost_ids.any? && shipping_cost_ids.index(shipping_line.shipping_cost_id).nil?
end

#has_unfulfilled_dropship_items?Boolean

Returns:

  • (Boolean)


3113
3114
3115
# File 'app/models/delivery.rb', line 3113

def has_unfulfilled_dropship_items?
  line_items.any? { |li| li.dropship? && (li.purchase_order_item.nil? || !li.purchase_order_item.fully_receipted?) }
end

#has_unprocessed_dropship_items?Boolean

Returns:

  • (Boolean)


3125
3126
3127
# File 'app/models/delivery.rb', line 3125

def has_unprocessed_dropship_items?
  line_items.any? { |li| li.dropship? && (li.purchase_order_item.nil? || li.purchase_order_item&.cancelled?) }
end

#has_valid_shipments?Boolean

Returns:

  • (Boolean)


4160
4161
4162
# File 'app/models/delivery.rb', line 4160

def has_valid_shipments?
  shipments.packed_or_measured.present? && shipments.packed_or_measured.all?(&:has_dimensions?)
end

#incurs_oversized_penalty?Boolean

Returns:

  • (Boolean)


4836
4837
4838
# File 'app/models/delivery.rb', line 4836

def incurs_oversized_penalty?
  (ships_ltl_freight? != true) && (is_warehouse_pickup? != true) && shipments.packed_or_measured.any?(&:incurs_oversized_penalty?)
end

#indexInteger

Zero-based position among the parent Order/Quote's active
deliveries — used as the "Delivery N" suffix on names and filenames.

Returns:

  • (Integer)


1501
1502
1503
# File 'app/models/delivery.rb', line 1501

def index
  resource&.deliveries&.active&.map(&:id)&.index(id) || 0
end

#individual_auto_ship_confirm(logger: nil) ⇒ Object

Transitions a delivery to shipped state if ready.

Invoice queuing is handled automatically by ToShippedHandler in the
after_transition callback.

Parameters:

  • logger (Logger) (defaults to: nil)

    Optional logger (defaults to Rails.logger)



4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
# File 'app/models/delivery.rb', line 4546

def individual_auto_ship_confirm(logger: nil)
  logger ||= Rails.logger
  ErrorReporting.scoped({ delivery_id: id }) do
    ready = !(is_warehouse_pickup? || shipments.completed.empty?)

    return unless ready

    logger.info "#{Time.current}: Processing delivery id: #{id}, ref/name: #{name}"
    begin
      shipped!
      # NOTE: Do NOT queue DeliveryInvoicingWorker here!
      # The after_transition callback in the state machine already queues it
      # via ToShippedHandler.queue_invoicing_worker. Queueing it here causes
      # race conditions where two workers try to create the same invoice.
    rescue StandardError => e
      error_message = "#{Time.current}: Exception!!! Processing delivery id: #{id}, ref/name: #{name}: #{e}"
      logger.error error_message
      logger.error e
    end
  end
end

#instant_quote?Boolean

True when the parent opportunity originated in the web quote-builder
("Instant Quote") flow. Drives the "no full shipping address yet,
just need a zip to estimate" UX exception — the
destination_address / has_destination_postal_code rules above bypass
the usual presence check for these deliveries.

Returns:

  • (Boolean)


3054
3055
3056
3057
# File 'app/models/delivery.rb', line 3054

def instant_quote?
  resource.respond_to?(:opportunity) && resource.opportunity &&
    resource.opportunity.opportunity_reception_type == 'IQ'
end

#instantiate_shipping_insuranceShipping::LtlShippingInsurance, Shipping::PackageShippingInsurance

Returns the appropriate Shipping::*ShippingInsurance strategy
object for the delivery's mode (LTL freight vs package).



5125
5126
5127
5128
5129
5130
5131
# File 'app/models/delivery.rb', line 5125

def instantiate_shipping_insurance
  if ships_ltl_freight?
    Shipping::LtlShippingInsurance.new
  else
    Shipping::PackageShippingInsurance.new
  end
end

#insured_shipmentsActiveRecord::Relation<Shipment>

Subset of label-complete Shipments that opted into third-party shipping
insurance — used by claim filing and billing reports.

Returns:



885
886
887
# File 'app/models/delivery.rb', line 885

def insured_shipments
  shipments.label_complete.where(is_ship_insured: true)
end

#insured_valueBigDecimal?

Carrier-declared value the chosen rate was quoted with — null for
carriers we don't insure through (Wayfair, Home Depot EDI, etc).

Returns:

  • (BigDecimal, nil)


2239
2240
2241
# File 'app/models/delivery.rb', line 2239

def insured_value
  chosen_shipping_method&.insured_value
end

#invoicesActiveRecord::Relation<Invoice>

Returns:

  • (ActiveRecord::Relation<Invoice>)

See Also:



119
# File 'app/models/delivery.rb', line 119

has_many :invoices

#is_amazon_seller_central?Object

Alias for Customer#is_amazon_seller_central?

Returns:

  • (Object)

    Customer#is_amazon_seller_central?

See Also:



176
# File 'app/models/delivery.rb', line 176

delegate :catalog, :is_amazon_seller_central?, to: :customer

#is_amazon_seller_central_veeqo?Boolean

Returns:

  • (Boolean)


2520
2521
2522
# File 'app/models/delivery.rb', line 2520

def is_amazon_seller_central_veeqo?
  is_amazon_seller_central? && override_shipping_method?
end

#is_cross_border?Boolean

Returns:

  • (Boolean)


4451
4452
4453
# File 'app/models/delivery.rb', line 4451

def is_cross_border?
  origin_address && destination_address && (origin_address.country_iso3 != destination_address.country_iso3) && %w[USA CAN].include?(origin_address.country_iso3) && %w[USA CAN].include?(destination_address.country_iso3)
end

#is_default_ltl_freight?Boolean

Returns:

  • (Boolean)


2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
# File 'app/models/delivery.rb', line 2524

def is_default_ltl_freight?
  # country_iso3 = "USA"
  # address = self.shipping_address or self.customer.first_address
  # country_iso3 = address.country_iso3 if address
  if order&.is_store_transfer? && order.shipping_method&.index('freight') && order.shipping_method.index('ltl')
    true
  elsif subtotal_for_ltl_threshold > LTL_FREIGHT_DOLLAR_THRESHOLD
    true
  elsif ship_weight > LTL_FREIGHT_WEIGHT_THRESHOLD
    true
  elsif line_items.parents_only.any? { |li| li.item.ships_via_freight? }
    true
  elsif canadian_tire_special_check?
    true
  elsif customer&.billing_entity&.is_build_com? && order&.shipping_method&.index('freight')
    true
  else
    false
  end

  # for now we only have USA freight implemented but allow PurolatorFreight as an unsupported carrier
end

#is_domestic?Boolean

Returns:

  • (Boolean)


4443
4444
4445
# File 'app/models/delivery.rb', line 4443

def is_domestic?
  origin_address && destination_address && origin_address.country_iso3 == destination_address.country_iso3
end

#is_international?Boolean

Returns:

  • (Boolean)


4447
4448
4449
# File 'app/models/delivery.rb', line 4447

def is_international?
  !is_domestic?
end

#is_international_and_ups_or_fedex?Boolean

Returns:

  • (Boolean)


4455
4456
4457
# File 'app/models/delivery.rb', line 4455

def is_international_and_ups_or_fedex?
  is_international? && %w[UPS FedEx].include?(carrier)
end

#is_part_of_manifest?Boolean

Returns:

  • (Boolean)


4164
4165
4166
# File 'app/models/delivery.rb', line 4164

def is_part_of_manifest?
  shipments.any?(&:manifest) || shipments.any?(&:speedee_manifest_shipment)
end

#is_rma_returnBoolean Also known as: is_rma_return?

Whether this delivery is the return half of an Rma (precreated by
CRM agents to send return labels to customers).

Returns:

  • (Boolean)


2490
2491
2492
# File 'app/models/delivery.rb', line 2490

def is_rma_return
  rma_for_return.present?
end

#is_smart_service?Boolean

Returns:

  • (Boolean)


2506
2507
2508
# File 'app/models/delivery.rb', line 2506

def is_smart_service?
  line_items.non_shipping.all?(&:is_smart_service?)
end

#is_sww_shipping_cost?(sc = nil) ⇒ Boolean

Check if the shipping cost is a Ship with Walmart rate

Returns:

  • (Boolean)


2441
2442
2443
2444
2445
2446
# File 'app/models/delivery.rb', line 2441

def is_sww_shipping_cost?(sc = nil)
  sc ||= selected_shipping_cost
  return false unless sc&.rate_data.is_a?(Hash)

  sc.rate_data['sww_carrier_id'].present? || sc.rate_data[:sww_carrier_id].present?
end

#is_www?Boolean

Returns:

  • (Boolean)


5065
5066
5067
# File 'app/models/delivery.rb', line 5065

def is_www?
  resource&.try(:cart?) || resource&.try(:is_www) # unfortunately we do not have a rock-solid single source of truth method to determine what is a WWW vs CRM delivery
end

#item_ledger_entriesActiveRecord::Relation<ItemLedgerEntry>

Returns:

See Also:



124
# File 'app/models/delivery.rb', line 124

has_many :item_ledger_entries

#ledger_transactionsActiveRecord::Relation<LedgerTransaction>

Returns:

See Also:



123
# File 'app/models/delivery.rb', line 123

has_many :ledger_transactions

#line_allocation_status_hashHash{Integer => Integer}

Map of LineItem id → unit quantity not yet allocated to a Shipment.
Negative values mean over-allocated. Memoized — used by allocation
validators, packing UI badges, and shipping cost recalculation.

Returns:

  • (Hash{Integer => Integer})


1025
1026
1027
1028
1029
1030
1031
# File 'app/models/delivery.rb', line 1025

def line_allocation_status_hash
  line_items_eligible_for_packing.each_with_object({}) do |li, hsh|
    allocated = shipments_for_packing.joins(:shipment_contents).where(shipment_contents: { line_item_id: li.id }).sum(:quantity)
    remaining_to_allocate = li.quantity.abs - allocated
    hsh[li.id] = remaining_to_allocate
  end
end

#line_discountsActiveRecord::Relation<LineDiscount>

Returns:

See Also:



113
# File 'app/models/delivery.rb', line 113

has_many :line_discounts, through: :line_items

#line_itemsActiveRecord::Relation<LineItem>

Returns:

See Also:



112
# File 'app/models/delivery.rb', line 112

has_many :line_items, extend: LineItemExtension, autosave: true, dependent: :nullify

#line_items_eligible_for_packingActiveRecord::Relation<LineItem>

LineItems the warehouse can put into a Shipment — eager-loads
Item (and any kit parent's item) and skips destroyed/shipping/service
rows. Sorted by SKU for stable pick-slip ordering.

Returns:



949
950
951
# File 'app/models/delivery.rb', line 949

def line_items_eligible_for_packing
  line_items.eager_load(:item).includes(parent: :item).order(Item[:sku]).active_lines_for_packaging
end

#line_items_requiring_serial_numberArray<LineItem>

Live (non-destroyed) LineItems whose Items require serial
numbers — used by the warehouse to know which lines still need
serials assigned before shipping.

Returns:



4319
4320
4321
# File 'app/models/delivery.rb', line 4319

def line_items_requiring_serial_number
  line_items.reject(&:marked_for_destruction?).select(&:require_serial_number?)
end

#line_items_with_countersActiveRecord::Relation<LineItem>

LineItem relation eager-loaded with reserved/shipped serial-number
counts so the warehouse UI can render allocation badges without N+1
queries.

Returns:



861
862
863
# File 'app/models/delivery.rb', line 861

def line_items_with_counters
  line_items.with_reserved_serial_numbers_count.with_serial_numbers_count
end

#lines_overallocated?Boolean

def delta_volume_factor_remaining_to_allocate
(line_allocation_status_hash.sum{|k,v| LineItem.find(k).shipping_volume*v.abs}.to_f/ship_volume_from_shipments)
end

Returns:

  • (Boolean)


1016
1017
1018
# File 'app/models/delivery.rb', line 1016

def lines_overallocated?
  line_allocation_status_hash.values.any?(&:negative?)
end

This method returns an undefined value.

Records the shipped SerialNumbers on each LineItem, attaching
carrier scan data to the historical line.



4719
4720
4721
# File 'app/models/delivery.rb', line 4719

def link_serial_numbers_to_line_items
  line_items.each(&:link_serial_numbers)
end

#linked_return_deliveriesArray<Delivery>

Return-shipment Delivery records spawned by an attached Rma (RMA
replacement orders + precreated RMAs), so warehouse UIs can show
which return labels belong with this outbound delivery.

Returns:



3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
# File 'app/models/delivery.rb', line 3557

def linked_return_deliveries
  return_deliveries = []
  rmas = []
  rmas << order.rma if order&.is_rma_replacement?
  rmas << precreated_rma if precreated_rma.present?
  rmas.each do |rma|
    rma.credit_orders.each do |co|
      co.deliveries.each do |d|
        return_deliveries << d
      end
    end
  end
  return_deliveries
end

#locked_for_fba?Boolean

Returns:

  • (Boolean)


2583
2584
2585
# File 'app/models/delivery.rb', line 2583

def locked_for_fba?
  order&.is_fba? && order&.shipment_reference_number =~ CustomerConstants::AMAZON_FBA_ID_REGEX && shipments.any? && shipments.all?(&:locked_for_fba?)
end

#ltl_freight_has_changed?Boolean

Returns:

  • (Boolean)


2624
2625
2626
2627
# File 'app/models/delivery.rb', line 2624

def ltl_freight_has_changed?
  rc = relevant_changes
  rc.keys.include?('ltl_freight') && (rc['ltl_freight'] == [nil, true] || rc['ltl_freight'] == [false, true] || rc['ltl_freight'] == [true, false] || rc['ltl_freight'] == [true, nil])
end

#manual_pickup_contactObject



2571
2572
2573
# File 'app/models/delivery.rb', line 2571

def manual_pickup_contact
  effective_shipping_option&.manual_pickup_contact
end

#mark_multi_shipments_manifested(manifest, shipment) ⇒ Integer

Tags every other label-complete Shipment on this delivery with
the given manifest id so they ride along with the carrier manifest.

Parameters:

  • manifest (Manifest)
  • shipment (Shipment)

    the shipment that triggered manifesting

Returns:

  • (Integer)

    rows updated



4753
4754
4755
# File 'app/models/delivery.rb', line 4753

def mark_multi_shipments_manifested(manifest, shipment)
  shipments.label_complete.where.not('shipments.id' => shipment.id).update_all(manifest_id: manifest.id)
end

#matches?(existing_item, item, quantity) ⇒ Boolean

Returns:

  • (Boolean)


3243
3244
3245
3246
3247
# File 'app/models/delivery.rb', line 3243

def matches?(existing_item, item, quantity)
  existing_item.line_item.nil? &&
    existing_item.item == item &&
    existing_item.unit_quantity == quantity
end

#messaging_logsActiveRecord::Relation<MessagingLog>

Returns:

See Also:



114
# File 'app/models/delivery.rb', line 114

has_many :messaging_logs, dependent: :destroy, as: :resource

#name(short = false, filename = false) ⇒ String

Human-friendly delivery name (e.g. "SO Order #SO12345 Delivery 2")
for UI titles and filenames. The two flags strip the prefix/parent and
turn the result into a filesystem-safe slug.

Parameters:

  • short (Boolean) (defaults to: false)

    omit the Order/Quote #ref clause

  • filename (Boolean) (defaults to: false)

    sanitize for filesystem use

Returns:

  • (String)


1512
1513
1514
1515
1516
1517
1518
1519
1520
# File 'app/models/delivery.rb', line 1512

def name(short = false, filename = false)
  ot = +''
  ot = order.order_type.to_s.upcase if order && [Order::SALES_ORDER, Order::MARKETING_ORDER, Order::TECH_ORDER].exclude?(ot)
  root_name = "#{ot} "
  root_name = "#{ot} #{resource_or_rma_for_delivery.class.name} ##{resource_or_rma_for_delivery.reference_number} " unless short
  final_name = "#{root_name}Delivery #{index.to_i + 1}"
  final_name = final_name.tr(' ', '_').gsub(/[^0-9a-z_]/i, '').underscore if filename
  final_name
end

#new_shipment_attributes=(shipment_attributes) ⇒ void

This method returns an undefined value.

Nested-attributes setter that builds new Shipments from a list of
hashes (used by the manual-shipment form on the warehouse UI).

Parameters:

  • shipment_attributes (Array<Hash>)


3529
3530
3531
3532
3533
# File 'app/models/delivery.rb', line 3529

def new_shipment_attributes=(shipment_attributes)
  shipment_attributes.each do |attributes|
    shipments.build(attributes)
  end
end

#no_empty_shipmentsvoid

This method returns an undefined value.

State-validation helper: rejects pending-label deliveries that have
any packed-or-awaiting-labels Shipment with no contents and no
child shipments.



4784
4785
4786
4787
4788
# File 'app/models/delivery.rb', line 4784

def no_empty_shipments
  return unless shipments.packed_or_awaiting_labels.any? { |shp| shp.shipment_contents.empty? && shp.child_shipments.empty? }

  errors.add(:base, 'Empty containers are present')
end

#notify_storevoid

This method returns an undefined value.

Publishes Events::DeliveryArrivedAtWarehouse from
after_all_transactions_commit so the async
DeliveryArrivedAtWarehouseNotificationHandler re-queries the delivery by
id and emails the store's operations contacts. The async re-query
tolerates a destroy between the at_warehouse transition and the email
send (e.g. an order cancelled within the original 1-minute mailer delay
cascading dependent: :destroy on its deliveries) — replacing
ActiveJob::DeserializationError (AppSignal #4958) with a clean no-op.



932
933
934
935
936
937
938
939
940
941
942
# File 'app/models/delivery.rb', line 932

def notify_store
  delivery_id = id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::DeliveryArrivedAtWarehouse.new(data: { delivery_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

#open_activities_counterInteger

Count of unresolved activities across delivery, parent order, and parent
quote — drives the bell-icon badge on delivery summary screens.

Returns:

  • (Integer)


831
832
833
# File 'app/models/delivery.rb', line 831

def open_activities_counter
  all_activities.open_activities.count
end

#orderOrder

Returns:

See Also:



98
# File 'app/models/delivery.rb', line 98

belongs_to :order, inverse_of: :deliveries, optional: true

#origin_addressAddress

Returns:

See Also:

Validations:



103
# File 'app/models/delivery.rb', line 103

belongs_to :origin_address, class_name: 'Address', validate: true, optional: true

#override_shipping_method?Boolean

Returns:

  • (Boolean)


2378
2379
2380
# File 'app/models/delivery.rb', line 2378

def override_shipping_method?
  chosen_shipping_method&.is_override?
end

#packable_active_linesActiveRecord::Relation<LineItem>

All non-destroyed LineItems eligible for packaging, eager-loaded
for the packing UI. Includes kit children.

Returns:



4172
4173
4174
# File 'app/models/delivery.rb', line 4172

def packable_active_lines
  line_items.includes(:item, :catalog_item, parent: :catalog_item).active_lines_for_packaging
end

#packable_active_parent_lines_only(skip_spare_parts = false) ⇒ Array<LineItem>

Only the goods-bearing parent LineItems eligible for packaging
(used when callers want to count packageable units without
double-counting kit components).

Parameters:

  • skip_spare_parts (Boolean) (defaults to: false)

    omit lines flagged as spare parts

Returns:



4182
4183
4184
4185
4186
# File 'app/models/delivery.rb', line 4182

def packable_active_parent_lines_only(skip_spare_parts=false)
  lines = line_items.active_parent_lines.select(&:is_goods?)
  lines = lines.reject(&:is_spare_parts?) if skip_spare_parts
  lines
end

#packable_item_hashHash{Item => Integer}

Aggregated {item => quantity} hash for all packable lines,
collapsing duplicates (same Item on multiple lines).

Returns:

  • (Hash{Item => Integer})


4192
4193
4194
# File 'app/models/delivery.rb', line 4192

def packable_item_hash
  packable_active_lines.each_with_object({}) { |li, hsh| hsh[li.item] = (hsh[li.item].nil? ? li.quantity : hsh[li.item] + li.quantity) }
end

#packable_parent_lines_item_hash(skip_spare_parts = false) ⇒ Hash{Item => Integer}

Parent-only variant of #packable_item_hash — does not include kit
component items.

Parameters:

  • skip_spare_parts (Boolean) (defaults to: false)

Returns:

  • (Hash{Item => Integer})


4201
4202
4203
# File 'app/models/delivery.rb', line 4201

def packable_parent_lines_item_hash(skip_spare_parts=false)
  packable_active_parent_lines_only(skip_spare_parts).each_with_object({}) { |li, hsh| hsh[li.item] = (hsh[li.item].nil? ? li.quantity : hsh[li.item] + li.quantity) }
end

#packageable?Boolean

Are there shipments that can have content specified?

Returns:

  • (Boolean)


2482
2483
2484
# File 'app/models/delivery.rb', line 2482

def packageable?
  shipments.packageable.present?
end

#pallet_weight_matchingBoolean

State-validation helper that surfaces pallet/carton weight
mismatches detected by #all_shipments_weights_match_expected.

Returns:

  • (Boolean)


4794
4795
4796
4797
4798
# File 'app/models/delivery.rb', line 4794

def pallet_weight_matching
  res = all_shipments_weights_match_expected
  errors.add(:base, "Weights for shipments don't add up to what is expected. #{res[:error_message]}") if res[:status] != true
  res[:status]
end

#paperless_return?Boolean

True when this delivery is an RMA return AND the chosen shipping option
is on the known-paperless-supporting list (ShippingOption.paperless_eligible).
Drives Shipping::ShipengineBase#get_label_options_hash to set
display_scheme: 'label_and_paperless' so the customer's emailed
PDF carries both a printable label and a QR/barcode. Only USPS
is seeded eligible; other carriers stay false until we have direct
evidence they support paperless.

Returns:

  • (Boolean)


2502
2503
2504
# File 'app/models/delivery.rb', line 2502

def paperless_return?
  is_rma_return? && rma_for_return&.shipping_option&.paperless_eligible?
end

#pick_slip_file_name(with_extension = true) ⇒ String

Filename for the warehouse pick slip PDF, dated to the current minute
so concurrent regenerations don't clobber each other in tmp/.

Parameters:

  • with_extension (Boolean) (defaults to: true)

    include the trailing .pdf

Returns:

  • (String)


1109
1110
1111
1112
1113
1114
1115
1116
# File 'app/models/delivery.rb', line 1109

def pick_slip_file_name(with_extension = true)
  s = []
  s << name(false, true)
  s << '_generated_pick_slip'
  s << Time.current.strftime('%m_%d_%Y_%I_%M%p')
  s << '.pdf' if with_extension
  s.join
end

#pick_slip_line_items(split_kits: false, sort_method: :location) ⇒ Object

Generates a line hash for the pick/pack slip pdf



1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
# File 'app/models/delivery.rb', line 1119

def pick_slip_line_items(split_kits: false, sort_method: :location)
  # Start with our items
  lines = line_items.non_shipping.parents_only.joins(:item)
                    .includes(:reserved_serial_numbers)
                    .includes(:direct_store_item, catalog_item: { store_items: :storage_locations })
                    .includes(item: :supplier_items)
                    .includes(:children)
  # Start grouping
  lines_hash = lines.each_with_object({}) do |li, hsh|
    li_hsh = hsh[li.sku] || {}
    li_hsh[:quantity] ||= 0
    li_hsh[:quantity] += li.quantity
    li_hsh[:name] ||= li.name
    li_hsh[:is_kit] = li.is_kit? if li_hsh[:is_kit].nil?
    li_hsh[:supplier_skus] ||= li.item.supplier_items.select(&:active).map { |si| si.supplier_sku.presence }.compact
    # Only need to add locations once since they're the same at the item level
    li_hsh[:locations] ||= li.store_item.storage_locations.map(&:reference_number)
    li_hsh[:oj_wifi_notes] = true if li.item.requires_distributor_id_code?
    li_hsh[:serial_numbers] ||= []
    li_hsh[:serial_numbers] += li.reserved_serial_numbers.map { |rsn| { serial_number: rsn.serial_number.number, quantity: rsn.qty } }
    li_hsh[:quantity_on_hand] ||= li.store_item.qty_on_hand
    li_hsh[:quantity_available] ||= li.store_item.qty_available
    li_hsh[:third_party_part_number] ||= li.catalog_item&.third_party_part_number
    li_hsh[:packed_shipments] ||= {}
    packed_shipment_contents = li.shipment_contents.joins(:shipment).where(shipments: { state: 'packed' })
    packed_shipment_contents.each do |sc|
      li_hsh[:packed_shipments][sc.shipment_id] ||= 0
      li_hsh[:packed_shipments][sc.shipment_id] += sc.quantity
    end
    # If we don't split kits but some components have locations or serial numbers, we will
    # aggregate them at the parent level
    if !split_kits && li.children.present?
      li_hsh[:locations] |= li.children.flat_map { |lic| lic.store_item.storage_locations.map(&:reference_number) }
      li_hsh[:serial_numbers] += li.children.flat_map { |lic| lic.reserved_serial_numbers.map { |rsn| { serial_number: rsn.serial_number.number, quantity: rsn.qty } } }
    end

    if (split_kits && li.children.present?) || li.children.any? { |lc| lc.reserved_serial_numbers.present? }
      li_hsh[:children] ||= {}
      li.children.each do |lic|
        lic_hsh = li_hsh[:children][lic.sku] || {}
        lic_hsh[:quantity] ||= 0
        lic_hsh[:quantity] += lic.quantity
        lic_hsh[:supplier_skus] = lic.item.supplier_items.select(&:active).map { |si| si.supplier_sku.presence }.compact
        lic_hsh[:name] = lic.name
        lic_hsh[:locations] ||= lic.store_item.storage_locations.map(&:reference_number)
        lic_hsh[:serial_numbers] ||= []
        lic_hsh[:serial_numbers] += lic.reserved_serial_numbers.map { |rsn| { serial_number: rsn.serial_number.number, quantity: rsn.qty } }
        lic_hsh[:quantity_on_hand] ||= lic.store_item.qty_on_hand
        lic_hsh[:quantity_available] ||= lic.store_item.qty_available
        lic_hsh[:packed_shipments] ||= {}
        packed_shipment_contents = lic.shipment_contents.joins(:shipment).where(shipments: { state: 'packed' })
        packed_shipment_contents.each do |sc|
          lic_hsh[:packed_shipments][sc.shipment_id] ||= 0
          lic_hsh[:packed_shipments][sc.shipment_id] += sc.quantity
        end

        li_hsh[:oj_wifi_notes] = true if lic.item.requires_distributor_id_code?
        li_hsh[:children][lic.sku] = lic_hsh
      end
    end

    hsh[li.sku] = li_hsh
  end

  # Resort the hash by location so that items are picked in location order
  case sort_method
  when :location
    Hash[lines_hash.sort_by { |sku, line_props| line_props[:locations]&.first || sku }]
  when :quantity_desc
    Hash[lines_hash.sort_by { |_sku, line_props| -line_props[:quantity] }]
  else
    Hash[lines_hash.sort_by { |sku, _line_props| sku }]
  end
end

#pickup_alert_visible?Boolean

True when a confirmed pickup is on the books and the delivery hasn't yet
shipped/invoiced/cancelled — used by the order and delivery screens to
surface a persistent banner showing the pickup window.

Returns:

  • (Boolean)


3970
3971
3972
# File 'app/models/delivery.rb', line 3970

def pickup_alert_visible?
  confirmed_pickup_date.present? && %w[shipped invoiced cancelled].exclude?(state)
end

#po_numberObject

Alias for Order#po_number

Returns:

  • (Object)

    Order#po_number

See Also:



178
# File 'app/models/delivery.rb', line 178

delegate :po_number, to: :order, allow_nil: true

#precreated_rmaRma

Returns:

See Also:



108
# File 'app/models/delivery.rb', line 108

has_one :precreated_rma, class_name: 'Rma', foreign_key: 'precreate_from_delivery_id', dependent: :nullify

#preferred_shipping_optionObject

These methods below are from the model previously known as delivery_quote



1524
1525
1526
1527
1528
1529
1530
1531
# File 'app/models/delivery.rb', line 1524

def preferred_shipping_option
  # choose based on resource's shipping_method first
  preferred_shipping_option = nil
  preferred_shipping_option = ShippingOption.active.for_country_iso(resource.country&.iso).where(name: resource_shipping_method).first || ShippingOption.for_country_iso(resource.country&.iso).where(name: resource_shipping_method).first if resource && resource_shipping_method.present?
  # # if not use customer's preferred_shipping_method
  preferred_shipping_option ||= ShippingOption.active.where(name: customer.preferred_shipping_method).first || ShippingOption.where(name: customer.preferred_shipping_method).first
  preferred_shipping_option
end

#prepack_requesterParty

Returns:

See Also:



138
# File 'app/models/delivery.rb', line 138

belongs_to :prepack_requester, class_name: 'Party', optional: true

#preset_jobsActiveRecord::Relation<PresetJob>

Returns:

See Also:



120
# File 'app/models/delivery.rb', line 120

has_many :preset_jobs, inverse_of: :order

#primary_partyObject

Alias for Resource_or_rma_for_delivery#primary_party

Returns:

  • (Object)

    Resource_or_rma_for_delivery#primary_party

See Also:



174
# File 'app/models/delivery.rb', line 174

delegate :customer, :primary_party, to: :resource_or_rma_for_delivery

Returns:

  • (Boolean)


725
726
727
728
729
730
731
732
733
734
735
736
737
738
# File 'app/models/delivery.rb', line 725

def print_container_label?
  cr = customer.customer_record
  # If specified that we always print (e.g. Amazon) then true
  if has_custom_shipping_labels?
    false
  elsif cr&.always_container_label?
    true
  # If never, or we have a custom pack list, or we have a homeowner, then false
  elsif cr&.never_container_label? || custom_pack_list || customer.is_homeowner?
    false
  else # Trade, Dealers, etc. true by default
    true
  end
end

#publish_delivery_label_complete_eventvoid

This method returns an undefined value.

Publishes Events::DeliveryLabelComplete so
Shipping::DeliveryLabelCompleteHandler can re-run the Packing-write
asynchronously once labels are purchased and shipment_contents exist.
Captures per-box packdim_contents that weren't yet known at
picking → pending_ship_labels time (notably Amazon Buy Shipping).



3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
# File 'app/models/delivery.rb', line 3351

def publish_delivery_label_complete_event
  delivery_id = id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::DeliveryLabelComplete.new(data: { delivery_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

#publish_delivery_ready_for_labeling_eventvoid

This method returns an undefined value.

Publishes Events::DeliveryReadyForLabeling so
Shipping::DeliveryReadyForLabelingHandler can refresh the
from_delivery Packing record asynchronously. Replaces the synchronous
set_packaged_items_md5_hash call in the
picking → pending_ship_labels after_transition block — the
DeliveryMd5Extractor write is bookkeeping, not transactional state,
so deferring it speeds up the warehouse UI's "ready to label" click.



3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
# File 'app/models/delivery.rb', line 3332

def publish_delivery_ready_for_labeling_event
  delivery_id = id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::DeliveryReadyForLabeling.new(data: { delivery_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

#quantities_remaining_to_allocateInteger

Total unit count still needing to be put into a Shipment across all
packable line items — drives the packing progress indicator.

Returns:

  • (Integer)


999
1000
1001
# File 'app/models/delivery.rb', line 999

def quantities_remaining_to_allocate
  line_allocation_status_hash.values.sum
end

#quoteQuote

Returns:

See Also:



97
# File 'app/models/delivery.rb', line 97

belongs_to :quote, inverse_of: :deliveries, optional: true

#ready_to_choose_ships_economy_carrier?Boolean

Returns:

  • (Boolean)


5082
5083
5084
# File 'app/models/delivery.rb', line 5082

def ready_to_choose_ships_economy_carrier?
  ships_economy_package? && pending_ship_labels?
end

#ready_to_print_amazon_fba_line_itemsArray<LineItem>

Goods LineItems whose Items are ready for Amazon FBA box-label
printing — used by the FBA inbound flow.

Returns:



4761
4762
4763
# File 'app/models/delivery.rb', line 4761

def ready_to_print_amazon_fba_line_items
  line_items.goods.select { |li| li.item.ready_to_print_amazon_fba_labels? }
end

#ready_to_ship!void

This method returns an undefined value.

Promotes a quoting delivery into the warehouse pipeline: routes to
awaiting_po_fulfillment when dropship items are present, otherwise to
at_warehouse. Wrapped in an advisory lock to prevent concurrent
transitions on the same delivery from racing.



1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
# File 'app/models/delivery.rb', line 1478

def ready_to_ship!
  return unless quoting?

  # Use advisory lock to prevent deadlocks from concurrent state transitions
  lock_key = "delivery|#{id}|ready_to_ship"
  self.class.with_advisory_lock(lock_key, timeout_seconds: 10) do
    reload # Refresh state after acquiring lock
    return unless quoting? # Re-check state after reload

    dropship = has_unprocessed_dropship_items?
    dropship = false if is_warehouse_pickup? || order.single_origin
    if dropship
      awaiting_po_fulfillment!
    else
      at_warehouse!
    end
  end
end

#reference_numberString Also known as: to_s

Stable customer/warehouse-facing identifier (e.g. DE12345) used on
labels, packing slips, and Slack notifications.

Returns:

  • (String)


1038
1039
1040
# File 'app/models/delivery.rb', line 1038

def reference_number
  "DE#{id}"
end

#reference_number_for_labelString

Short identifier embedded in the carrier's "reference number" label
field — the parent Order's reference, the RMA number for returns,
or the delivery reference as a fallback.

Returns:

  • (String)


4406
4407
4408
4409
4410
4411
4412
# File 'app/models/delivery.rb', line 4406

def reference_number_for_label
  if order.present?
    "ORD: #{order.reference_number}"
  else
    rma_for_return.present? ? "RMA: #{rma_for_return.rma_number}" : reference_number
  end
end

#rejoin_serial_numbersvoid

This method returns an undefined value.

Re-merges previously split serial-number LineItems back into a
single multi-quantity row, used when cancelling a delivery so we
don't leave one-unit rows behind.



4711
4712
4713
# File 'app/models/delivery.rb', line 4711

def rejoin_serial_numbers
  line_items.select(&:require_reservation?).each(&:rejoin_serial_numbers)
end

#relevant_changesHashWithIndifferentAccess

Subset of changes that materially affect rate shopping or carrier
selection — excludes audit-only / display-only fields like packaging
text, master tracking, BOL, and release-date metadata.

Returns:

  • (HashWithIndifferentAccess)


2620
2621
2622
# File 'app/models/delivery.rb', line 2620

def relevant_changes
  changes.except('suggested_packaging_text', 'master_tracking_number', 'ltl_pro_number', 'actual_shipping_cost', 'future_release_date', 'manual_release_only', 'do_not_reserve_stock', 'shipment_instructions', 'carrier_bol')
end

after_save hook that points all Payment rows from the
payment_ids accessor at this delivery — used when payments are
captured during a delivery edit before the delivery is persisted.

Returns:

  • (Integer)

    rows updated



4512
4513
4514
# File 'app/models/delivery.rb', line 4512

def relink_payments
  Payment.where(id: payment_ids).update_all(delivery_id: id)
end

#remap_legacy_shipping_options_if_anyvoid

This method returns an undefined value.

Migrates any LEGACY_-prefixed ShippingOption service codes on this
delivery and its ShippingCost rows to the current code, persisting
the change. One-shot data migration helper called on touch.



4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
# File 'app/models/delivery.rb', line 4683

def remap_legacy_shipping_options_if_any
  shipping_costs.select { |sc| sc.shipping_option&.service_code.to_s.starts_with?('LEGACY_') }.each do |sc|
    updated_serv_code = sc.shipping_option.service_code.split('LEGACY_').last
    sc.update({ shipping_option_id: ShippingOption.active.where(service_code: updated_serv_code,
                                                                country: sc.shipping_option.country).first&.id || ShippingOption.where(service_code: updated_serv_code,
                                                                                                                                       country: sc.shipping_option.country).first&.id })
  end
  return unless (serv_code = shipping_option&.service_code).to_s.starts_with?('LEGACY_')

  updated_serv_code = serv_code.split('LEGACY_').last
  self.shipping_option_id = (ShippingOption.active.where(service_code: updated_serv_code, country: shipping_option.country).first&.id || ShippingOption.where(service_code: updated_serv_code, country: shipping_option.country).first&.id)
  save
end

#reported_carrierString?

Carrier name to surface to customers and EDI partners — prefers the
value set on the delivery (carrier), then falls back to the first
completed shipment's carrier or the override description for override
selections (where the warehouse manually chose a carrier).

Returns:

  • (String, nil)


2388
2389
2390
# File 'app/models/delivery.rb', line 2388

def reported_carrier
  (carrier || (override_shipping_method? && (shipments&.completed&.top_level&.first&.carrier || chosen_shipping_method&.description_override))).presence
end

#reported_master_tracking_numberString?

Tracking-style identifier reported externally — falls through master
tracking number, LTL PRO number, carrier BOL, and finally the first
completed shipment's tracking number (for override selections).

Returns:

  • (String, nil)


2408
2409
2410
# File 'app/models/delivery.rb', line 2408

def reported_master_tracking_number
  (master_tracking_number || (ltl_pro_number || carrier_bol || (override_shipping_method? && shipments&.completed&.top_level&.first&.tracking_number))).presence
end

#requires_manifest_completion?Boolean

Returns:

  • (Boolean)


4930
4931
4932
# File 'app/models/delivery.rb', line 4930

def requires_manifest_completion?
  CARRIERS_REQUIRING_MANIFEST_COMPLETION.include?(carrier)
end

#requires_manual_pickup?Boolean

Returns:

  • (Boolean)


2567
2568
2569
# File 'app/models/delivery.rb', line 2567

def requires_manual_pickup?
  effective_shipping_option&.requires_manual_pickup? || false
end

#reserved_serial_numbersActiveRecord::Relation<ReservedSerialNumber>

Returns:

See Also:



121
# File 'app/models/delivery.rb', line 121

has_many :reserved_serial_numbers, through: :line_items

#reset_early_label_flag_on_orderObject

Reset purchase_label_early flag on the order so next ship-label goes through normal flow



4128
4129
4130
4131
4132
4133
4134
# File 'app/models/delivery.rb', line 4128

def reset_early_label_flag_on_order
  return unless resource.is_a?(Order)
  return unless resource.purchase_label_early?

  Rails.logger.info("[Delivery] Resetting purchase_label_early flag on order #{resource.reference_number}")
  resource.update!(purchase_label_early: false)
end

#reset_shipping_costvoid

This method returns an undefined value.

Clears all ShippingCost rows safely (nullifying line-item references
first to avoid FK violations) and zeros out the cached shipping_cost
on the delivery.



2270
2271
2272
2273
# File 'app/models/delivery.rb', line 2270

def reset_shipping_cost
  clear_shipping_costs_safely
  self.shipping_cost = 0 if respond_to? :shipping_cost # REFACTOR
end

#reset_ships_economy_if_unselectedBoolean

after_save callback that clears the order's ships_economy flag
when the warehouse swaps in a real shipping method that isn't a
ground/economy match — so future deliveries don't keep treating the
order as economy.

Returns:

  • (Boolean)


5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
# File 'app/models/delivery.rb', line 5011

def reset_ships_economy_if_unselected
  Rails.logger.debug do
    "reset_ships_economy_if_unselected, ships_economy?: #{ships_economy?}, saved_change_to_selected_shipping_cost_id: #{saved_change_to_selected_shipping_cost_id}, selected_shipping_cost.present?: #{selected_shipping_cost.present?}"
  end
  # this is designed to reset the whole ships economy flag when a ships economy order's selected shipping method is changed to use another shipping method, and not HW or the warehouse choosing an equivalent economy shipping/ground
  return unless ships_economy?
  return unless saved_change_to_selected_shipping_cost_id? && selected_shipping_cost.present?

  reset_ships_economy = true
  # don't touch if ships_economy (not LTL) AND pending labels and HW or warehouse is choosing an equivalent economy shipping/ground method OR economy shipping override is set
  Rails.logger.debug { "reset_ships_economy_if_unselected, ships_economy_package?: #{ships_economy_package?}" }
  Rails.logger.debug { "reset_ships_economy_if_unselected, ready_to_choose_ships_economy_carrier?: #{ready_to_choose_ships_economy_carrier?}" }
  Rails.logger.debug { "reset_ships_economy_if_unselected, selected_shipping_cost&.name: #{selected_shipping_cost&.name}" }
  Rails.logger.debug do
    "reset_ships_economy_if_unselected, sorted_ground_shipping_costs(skip_override=true).map{|sc| sc.name}.include?(selected_shipping_cost&.name): #{sorted_ground_shipping_costs(true).map(&:name).include?(selected_shipping_cost&.name)}"
  end
  Rails.logger.debug { "reset_ships_economy_if_unselected, selected_shipping_cost&.is_override?: #{selected_shipping_cost&.is_override?}" }
  if ships_economy_package? && ((ready_to_choose_ships_economy_carrier? && sorted_ground_shipping_costs(true).map(&:name).include?(selected_shipping_cost&.name)) || selected_shipping_cost&.is_override? || rma_for_return.present?)
    # don't touch if ships_economy (not LTL) and we have the economy shipping override set
    reset_ships_economy = false
  end
  Rails.logger.debug { "reset_ships_economy_if_unselected, reset_ships_economy: #{reset_ships_economy}" }
  if reset_ships_economy
    order.ships_economy = false
    order.save
  end
  true
end

#resourceOrder, ...

Parent record this delivery belongs to: either the Order (sales/store-transfer
workflow) or the Quote (pre-sale rate-shopping workflow). RMA returns use
#resource_or_rma_for_delivery since they are not tied to an order/quote.

Returns:



782
783
784
# File 'app/models/delivery.rb', line 782

def resource
  order || quote
end

#resource_or_rma_for_deliveryOrder, ...

Falls back to Rma for return deliveries when neither Order nor Quote
owns the delivery — used for customer/billing/party delegations so RMA
returns work in the same code paths as regular deliveries.

Returns:



791
792
793
# File 'app/models/delivery.rb', line 791

def resource_or_rma_for_delivery
  resource || rma_for_return
end

#resource_presentBoolean

Validation helper enforcing that every delivery has a parent Order or
Quote (RMA returns are exempt because they fall back via
#resource_or_rma_for_delivery).

Returns:

  • (Boolean)


840
841
842
# File 'app/models/delivery.rb', line 840

def resource_present
  resource.present?
end

#resource_shipping_methodString?

Shipping method preference inherited from the parent Order or
Quote; RMA returns hard-code to "ground". Drives
#preferred_shipping_option lookup.

Returns:

  • (String, nil)


5138
5139
5140
5141
5142
5143
5144
5145
# File 'app/models/delivery.rb', line 5138

def resource_shipping_method
  if resource.present?
    resource.shipping_method
  elsif is_rma_return? && rma_for_return.present?
    # For RMA returns, always use 'ground' as the shipping method
    'ground'
  end
end

#retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, sc = nil, with_delivery_commitment = false, for_edi = false) ⇒ Object

rescue "n/a"



2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
# File 'app/models/delivery.rb', line 2330

def retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, sc = nil, with_delivery_commitment = false, for_edi = false) # rescue "n/a"
  shipping_method_name = +''
  mps = +''
  method_cod = +''
  customer_pays = +''
  ret = +''
  notes = +''
  if is_service_only?
    shipping_method_name = 'Service'
  else
    sc ||= chosen_shipping_method
    if sc
      shipping_method_name = simple_shipping_description_for_shipping_cost(sc)
      shipping_method_name += " (#{sc.delivery_commitment})" if with_delivery_commitment
      num_shipments = (begin
        shipments.label_complete.length
      rescue StandardError
        0
      end)
      mps = ", #{num_shipments} containers" if num_shipments > 1
      method_cod = ' (inc. COD charge)' if sc.cod
      # Surface the customer's account number whenever a SAN is attached
      # (= we bill that account third-party). Same predicate as the
      # label and the invoice, so the displayed account always matches
      # what is actually billed.
      if sc.third_party_billed? && show_customer_pays_info
        customer_pays = " (cust. acct.: #{sc..})" unless for_www
        customer_pays = " (using your linked #{carrier} account: #{sc..})" if for_www
      end
      if for_edi == false && sc&.shipping_option&.carrier == 'FedEx' && sc&.insured_value.to_f >= 500.0 && !signature_confirmation # flag this unless we already have signature_confirmation set
        notes += ' (FedEx automatically requires Direct Signature for all declared value shipments of $500 or more)'
      end
    elsif shipping_line_item
      shipping_method_name = shipping_line_item.name
    end
  end
  "#{shipping_method_name}#{mps}#{method_cod}#{customer_pays}#{ret}#{notes}".strip
end

#retrieve_shipping_costs(rate_ship_date: nil) ⇒ Object

Retrieve shipping costs from carriers

Parameters:

  • rate_ship_date (Date, nil) (defaults to: nil)

    Optional date to use for rate calculation (user can override)



1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
# File 'app/models/delivery.rb', line 1544

def retrieve_shipping_costs(rate_ship_date: nil)
  # Use advisory lock to prevent concurrent processing and infinite loops
  lock_key = "delivery|#{id}|retrieve_shipping_costs"
  result = self.class.with_advisory_lock_result(lock_key, timeout_seconds: 0) do

  # Store the rate_ship_date for use later in this method
  @rate_ship_date_override = rate_ship_date

  # puts "Delivery#retrieve_shipping_costs: self.resource.shipping_address_id: #{self.resource.shipping_address_id}"
  # puts "Delivery#retrieve_shipping_costs: self.destination_address.id: #{self.destination_address.id}, self.destination_address_id: #{self.destination_address_id}"
  return_code = :no_options
  return_message = +''
  packages_message = +''
  shipping_options = []

  is_store_transfer_delivery = order.try(:is_store_transfer?)
  shipping_option
  if is_service_only? || is_warehouse_pickup?
    so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first
    self.shipping_option_id = so.id
    old_override_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option == so }
    cost_to_use = 0.0
    if old_override_shipping_cost
      cost_to_use = old_override_shipping_cost.cost || 0.0
    end # need something here
    cost_to_use = 0.0 if is_warehouse_pickup?
    cost_to_use = WAREHOUSE_PICKUP_FEE if apply_warehouse_fee? # we apply warehouse pickup fee only to first delivery
    clear_shipping_costs_safely
    shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use)
    shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst)
    shipping_costs << shpcst
    raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil?

    return_code = :ok
  elsif is_zero_charge_dropship?
    Rails.logger.debug 'is_zero_charge_dropship? true'
    so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first
    self.shipping_option_id = so.id
    shipping_costs.detect { |sc| sc.shipping_option == so }
    cost_to_use = 0.0
    clear_shipping_costs_safely
    shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use)
    shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst)
    shipping_costs << shpcst
    raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil?
    return_code = :ok
  # elsif is_rma_return?
  #   # TBD REFACTOR NO ORDER DEPENDENCY FOR RETURN DELIVERY
  #   # here we want just UPS ground shipping option with cost of 0.00
  #   if self.shipping_option_id.present?
  #     so = self.shipping_option
  #   else
  #     country_iso = resource&.store&.country&.iso || rma_for_return&.customer&.store&.country&.iso
  #     ups_ground = 'ground'
  #     ups_ground = 'standard' if country_iso == 'CA'
  #     so = ShippingOption.active.where(name: ups_ground, country: country_iso).first || ShippingOption.where(name: ups_ground, country: country_iso).first
  #     self.shipping_option_id = so.id
  #   end
  #   cost_to_use = 0.0
  #   shipping_costs.delete_all
  #   shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use)
  #   shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst)
  #   shipping_costs << shpcst
  #   raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil?

  #   return_code = :ok
  elsif customer && (destination_address || resource.installation_postal_code)
    # test if we want to default to ltl freight, here the ltl_freight.nil? is important because it means the user did not explcitly set it to false, so it's true no need to set it true, or it's false which means it was manually and explicitly set to false so we 'stick' with the selected shipping option even if the weight increases past the default LTL threshold
    Rails.logger.debug { "retrieve_shipping_costs: ltl_freight: #{ltl_freight}, ltl_freight_guaranteed: #{ltl_freight_guaranteed}, order&.cart?: #{order&.cart?}, is_default_ltl_freight?: #{is_default_ltl_freight?}" }
    if (ltl_freight.nil? || order&.cart?) && !ltl_freight_guaranteed && is_default_ltl_freight? # only do this for when no shipping method has been chosen, i.e. no shipping costs present or none matching or is cart and freight option not already set
      self.ltl_freight = true
    end
    if (ltl_freight || ltl_freight_guaranteed) && order&.cart? && !is_default_ltl_freight? # also reset ltl freight for www carts and when freight is no longer relevant
      self.ltl_freight = false
      self.ltl_freight_guaranteed = false
    end
    # Heavyweight method call below.
    if ltl_freight_changed?
      # Modality switch (parcel ↔ LTL): drop suggested/packed shipments so
      # CreateSuggestedShipment rebuilds them against the new modality.
      # The freight-mode Packing record (kept per service_type) survives
      # and is rehydrated on the way back.  Without this, pallet dims leak
      # into parcel mode (and vice versa) because CreateSuggestedShipment
      # only deletes suggested shipments with blank container_code and
      # returns any remaining non-voided shipments as-is.
      shipments.where(state: %w[suggested packed]).destroy_all
      # we reset selected shipping options because this is like a new shipping calculation and we want sensible defaults, not override
      update_column(:selected_shipping_cost_id, nil)
      update_column(:shipping_option_id, nil)
    end
    options = calculate_shipping_options(www: order.try(:is_www))
    packages_arr = []
    options[:shipping_weights].each_with_index do |weight, i|
      packages_arr << {
        length: options[:shipping_dimensions][i][0].to_f,
        width: options[:shipping_dimensions][i][1].to_f,
        height: options[:shipping_dimensions][i][2].to_f,
        weight: weight.to_f,
        flat_rate_package_type: options[:flat_rate_package_types][i],
        container_type: options[:container_types][i]
      }
    end
    carrier_responses_hash = {
      date: Time.current.to_s,
      packages: packages_arr,
      origin_address: get_address_hash_from_address(options[:origin_address]),
      destination_address: (if options[:destination_address].present?
                              get_address_hash_from_address(options[:destination_address])
                            else
                              {
                                postal_code: options[:postal_code],
                                state_code: options[:state],
                                country_iso: options[:country_iso]
                              }
                            end
                           ),
      rates: []
    }
    options[:delivery] = self

    # Calculate effective ship date for rate requests
    # This is when we plan to ship - user can override, otherwise defaults to:
    # - Today if a working day and before the noon same-day cutoff
    # - Next valid business day otherwise (skips weekends + company holidays)
    # Note: requested_ship_before is the retailer's deadline (informational only, not used for rate calculation)
    company = order&.company || resource&.company || rma_for_return&.company
    raise "Cannot calculate shipping - no company associated with order/resource/rma" unless company

    # Use user-provided rate_ship_date if valid, otherwise calculate default.
    # Orders use the noon-cutoff `next_warehouse_ship_date` so carrier-quoted
    # arrival dates match the customer-facing "Ships ___" banner; non-order
    # paths (RMAs, store transfers) fall back to the warehouse-hours-aware
    # `next_valid_ship_date`.
    ship_date = @rate_ship_date_override if @rate_ship_date_override && @rate_ship_date_override >= Date.current
    ship_date ||= order&.next_warehouse_ship_date
    ship_date ||= company.next_valid_ship_date

    # For LTL freight, ship date must be at least next business day (pickup scheduling required)
    if ltl_freight || ltl_freight_guaranteed
      next_business_day = company.next_business_day(Date.current + 1.day)
      if ship_date < next_business_day
        ship_date = next_business_day
        Rails.logger.debug { "retrieve_shipping_costs: LTL requires pickup scheduling, adjusted ship_date to #{ship_date}" }
      end
    end
    options[:ship_date] = ship_date
    Rails.logger.debug { "retrieve_shipping_costs: using ship_date=#{ship_date} (override=#{@rate_ship_date_override.present?})" }

    # Add Walmart "Ship with Walmart" info for Walmart marketplace orders
    # This enables discounted shipping rate comparison from Walmart's carrier partnerships
    if order&.edi_orchestrator_partner&.start_with?('walmart_seller')
      options[:walmart_order_info] = {
        partner: order.edi_orchestrator_partner.to_sym,
        po_number: order.edi_po_number,
        deliver_by_date: order.requested_deliver_by,
        ship_by_date: order.requested_ship_before || Date.current
      }
    end

    # Add Amazon Buy Shipping info for Amazon marketplace orders
    # This enables Amazon's negotiated rates with A-to-Z Buy Shipping protections
    if order&.edi_orchestrator_partner&.start_with?('amazon_seller')
      options[:amazon_order_info] = {
        partner: order.edi_orchestrator_partner.to_sym,
        order_id: order.edi_po_number,
        deliver_by_date: order.requested_deliver_by,
        ship_by_date: ship_date
      }
    end

    # Pass catalog for carrier exclusion filtering in WyShipping
    options[:catalog] = catalog if order&.customer&.catalog_id.present?

    shipping_options = WyShipping.calculate_shipping_from_options(options)
    Rails.logger.debug { "Delivery id: #{id}, retrieve_shipping_costs, shipping_options: #{shipping_options.inspect}" }

    if shipping_options.any?
      return_message << shipping_options.first[:status_description]
      self.ltl_exclusion_reasons = Array(shipping_options.first[:ltl_exclusion_reasons])
      if shipping_options.first[:status_code] == '1'
        packages_message += shipping_options.first[:packages].inspect.to_s # [0..149]
        # return_message += "..." if return_message.length > 149
        so_override = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.find_by(name: 'override', country: resource.store.country.iso)
        old_override_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id }
        cost_override = nil
        cost_override = old_override_shipping_cost.cost if old_override_shipping_cost
        cost_override = get_economy_shipping_cost_to_use if ships_economy_package?
        cost_override = get_fallback_cost_to_use if ships_economy_ltl?
        # Capture intent before deleting: if the ONLY existing cost is the auto-set override
        # (i.e. a previous failed request left no real options), we can release the override
        # lock when valid rates come back. If non-override costs already existed alongside the
        # override, the user deliberately chose override and we must respect that choice.
        override_was_only_option = shipping_option&.is_override? && shipping_costs.skip_override.empty?
        shipping_costs.skip_override.delete_all
        # Preload all ShippingOption records needed for this batch to avoid N+1 queries.
        # Many option names exist for multiple countries (US, CA, CN, etc.).
        # Scope to the store's origin country first so index_by always picks the
        # correct variant; fall back to any country if no origin-country record exists.
        all_option_names = shipping_options.last.map { |opt, val| (val[:option_name] || opt).to_s }.uniq
        origin_country_iso = is_rma_return? ? rma_for_return&.customer&.store&.country&.iso : resource.store.country.iso
        so_by_name = ShippingOption.where(name: all_option_names, country: origin_country_iso).index_by(&:name)
        # Collect all new shipping cost records for batch insert (avoids 12+ individual transactions)
        now = Time.current
        batch_attrs = []
        shipping_options.last.each do |option, value|
          san_id = nil
          if bill_shipping_to_customer && origin_address.supports_third_party_shipping
            san = customer.(value[:carrier],
                                                   (begin
                                                     resource.billing_address
                                                   rescue StandardError
                                                     nil
                                                   end),
                                                   destination_address)
            san_id = san.id if san
          end
          option_name = (value[:option_name] || option).to_s
          so = so_by_name[option_name] || ShippingOption.find_by(name: option_name, country: origin_country_iso) || ShippingOption.find_by(name: option_name)
          cod = true if cod_collection_type.present?
          # handle origin address supports_third_party_shipping varies from supplier to supplier
          if is_store_transfer_delivery || so.is_third_party_only?
            cost_to_use = 0.0
            transportation_charges = 0.0
            service_options_charges = 0.0
          else
            cost_to_use = value[:cost]
            transportation_charges = value[:transportation_charges]
            service_options_charges = value[:service_options_charges]
            insured_value = value[:insured_value]
          end
          batch_attrs << {
            delivery_id: id, shipping_option_id: so.id, cost: cost_to_use,
            transportation_charges: transportation_charges || 0.0, service_options_charges: service_options_charges || 0.0,
            saturday_delivery: saturday_delivery || false, signature_confirmation: signature_confirmation || false,
            cod: cod || false, shipping_account_number_id: san_id, insured_value: insured_value,
            description_override: (so.is_freightquote? ? "Freightquote #{value[:service_code]}" : nil),
            rate_data: (value[:rate_data] || {}).merge({ actual_cost: value[:cost].to_f.round(2) }),
            created_at: now, updated_at: now
          }
          # Use .to_f before rounding — BigDecimal serializes as a String in JSONB, causing
          # sort/comparison failures when the column is read back (same pattern as line 1502).
          rate_entry = value.merge({
            cost: cost_to_use.to_f.round(2),
            transportation_charges: value[:transportation_charges].to_f.round(2),
            service_options_charges: value[:service_options_charges].to_f.round(2)
          })
          # Prefix marketplace rates so they're distinguishable from direct carrier rates
          if value[:carrier] == 'WalmartSeller'
            rate_entry[:service_name] = "Ship with Walmart: #{rate_entry[:service_name]}"
          elsif value[:carrier] == 'AmazonSeller'
            ptp_carrier = value.dig(:rate_data, :amz_carrier_id) || value.dig(:rate_data, 'amz_carrier_id')
            if ptp_carrier.present? && ptp_carrier != 'AMZN_US'
              rate_entry[:service_name] = "AMZ #{rate_entry[:service_name]}"
            else
              rate_entry[:service_name] = "Amazon Shipping: #{rate_entry[:service_name]}"
            end
          end
          carrier_responses_hash[:rates] << rate_entry
        end
        fk_error = false
        if batch_attrs.any?
          begin
            ShippingCost.insert_all(batch_attrs)
          rescue ActiveRecord::InvalidForeignKey => e
            Rails.logger.warn "Delivery#retrieve_shipping_costs: FK violation for delivery #{id}#{e.message}"
            fk_error = true
          end
        end
        unless fk_error
          shipping_costs.reload

          return_code = :ok if shipping_costs.any?

          # Release the override lock when valid rates come back AND the override was auto-set
          # (the only cost before this run was the placeholder override from a prior failed request).
          # If the user had already picked override while other options existed, non-override costs
          # would have been present before the delete above — override_was_only_option stays false
          # and we leave the intentional selection untouched.
          # EDI orders where the label is purchased externally always keep override regardless.
          if return_code == :ok && override_was_only_option && order&.edi_shipping_option_name != 'override'
            self.shipping_option_id = nil
          end

          unless shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id }
            if is_store_transfer_delivery
              cost_override = 0.0
            else
              cost_override ||= (shipping_costs.detect { |sc| sc.shipping_option_id == shipping_option_id } || sorted_shipping_costs.first).try(:cost) || get_fallback_cost_to_use # need something here
            end
            shpcst = ShippingCost.new(shipping_option: so_override, cost: cost_override)
            shpcst.description_override = 'Shipping override, please confirm'
            shipping_costs << shpcst
          end

          sc_override = shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id }
          cost_override = get_economy_shipping_cost_to_use if ships_economy_package?
          cost_override = get_fallback_cost_to_use if ships_economy_ltl?
          sc_override.update(cost: cost_override) if sc_override
        end

      else
        # nothing comes back, invalid preference or option set like signature confirmation or saturday delivery, in this case just set override of $500 and let rep sort it out: we need some shipping option/cost. If europe, override is 0 euro
        so = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.where(name: 'override', country: resource.store.country.iso).first
        self.shipping_option_id = so.id
        clear_shipping_costs_safely
        cost_to_use = if is_rma_return?
                        rma_for_return&.customer&.store&.country&.eu_country? ? 0 : get_fallback_cost_to_use
                      else
                        resource.store.country.eu_country? ? 0 : get_fallback_cost_to_use
                      end
        shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use)
        shpcst.description_override = 'Shipping override, please confirm'
        shipping_costs << shpcst
        return_code = :no_options
        return_message << "No valid shipping options could be found for #{name}. Setting override of $#{get_fallback_cost_to_use}."
      end

    else # shipping service unavailable
      # shipping services unavailable, in this case just set override of $500 and let rep sort it out: we need some shipping option/cost. If europe, override is 0 euro
      so = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.where(name: 'override', country: resource.store.country.iso).first
      self.shipping_option_id = so.id
      clear_shipping_costs_safely
      cost_to_use = resource.store.country.eu_country? ? 0 : get_fallback_cost_to_use
      shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use)
      shpcst.description_override = 'Shipping override, please confirm'
      shipping_costs << shpcst
      return_code = :no_response
      return_message = "There was no response from carrier web services for #{name}. Setting override of $#{get_fallback_cost_to_use}, please try again or request assistance."
    end
  end
  result = { code: return_code, message: return_message, packages: packages_message, created_at: Time.current.to_fs(:db) }
  # Persist the human/debug message via messaging_logs (see Models::LegacyRateRequest)
  begin
    messaging_logs.create!(category: 'shipping_rate_request', message: result)
  rescue StandardError
    # non-fatal; continue
  end

  # Assign carrier_responses in-memory
  self.carrier_responses = carrier_responses_hash || {}
  # Avoid triggering before_save callback work if nothing changed materially beyond carrier_responses

    if is_rma_return? && changes.except('carrier_responses').empty?
      update_columns(carrier_responses: self.carrier_responses)
    else
      save
    end
    resource.shipping_issue_alerted = false if resource.present?
    result
  end

  # If lock was not acquired, return already processing message
  unless result.lock_was_acquired?
    return { code: :already_processing, message: 'Shipping costs are already being retrieved', packages: '' }
  end

  # Return the result from the block
  result.result
end

#retrieve_shipping_description_for_line_item(shipping_line) ⇒ String?

Convenience for invoice rendering: pulls the shipping-cost description
from a shipping LineItem.

Parameters:

Returns:

  • (String, nil)


2453
2454
2455
# File 'app/models/delivery.rb', line 2453

def retrieve_shipping_description_for_line_item(shipping_line)
  retrieve_shipping_description_for_shipping_cost(shipping_line&.shipping_cost)
end

#retrieve_shipping_description_for_shipping_cost(sc = nil) ⇒ String?

Decorated description for a ShippingCost including COD note,
third-party billing account hint, and the FedEx ≥ $500 signature
warning. Override rows return their plain description.

Parameters:

Returns:

  • (String, nil)


2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
# File 'app/models/delivery.rb', line 2463

def retrieve_shipping_description_for_shipping_cost(sc = nil)
  sc ||= selected_shipping_cost
  description = simple_shipping_description_for_shipping_cost(sc)
  # nil description means line item name will default to linked item shipping option name, otherwise it's an override
  unless sc&.is_override?
    # only override if there is a COD, customer shipping account, or special services - shipping cost description includes special services descriptions too, - shipping option name does not
    sc&.description
    notes = +''
    notes << ' (inc. COD charge)' if sc&.cod
    notes << " (cust. acct.: #{sc&.&.})" if sc&.third_party_billed?
    if sc&.shipping_option&.carrier == 'FedEx' && sc&.insured_value.to_f >= 500.0 && !signature_confirmation # flag this unless we already have signature_confirmation set
      notes << ' (FedEx automatically requires Direct Signature for all declared value shipments of $500 or more)'
    end
    description = "#{description}#{notes}"
  end
  description
end

#revert_to_override_economy_shipping_method(autosave = true) ⇒ void

This method returns an undefined value.

Resets a "ships economy" delivery back to the override shipping option
at the economy fallback cost — used when the previously selected real
carrier becomes invalid (e.g. order returns to quoting after items
change).

Parameters:

  • autosave (Boolean) (defaults to: true)

    persist the changes immediately



2061
2062
2063
2064
2065
2066
2067
2068
# File 'app/models/delivery.rb', line 2061

def revert_to_override_economy_shipping_method(autosave = true)
  so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first
  self.shipping_option_id = so.id
  self.selected_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option_id == shipping_option_id } || shipping_costs.new(shipping_option: so, cost: get_economy_shipping_cost_to_use)
  selected_shipping_cost.cost = get_economy_shipping_cost_to_use
  save if autosave
  order&.reload&.reset_discount(reset_item_pricing: false)
end

#rma_for_returnRma

Returns:

See Also:



109
# File 'app/models/delivery.rb', line 109

has_one :rma_for_return, class_name: 'Rma', foreign_key: 'return_delivery_id', dependent: :nullify

#same_day_pickup?Boolean

Whether the carrier-confirmed pickup is today, in the sender's timezone.
The send happens at sender-local midnight boundaries, so compare dates in
that zone rather than UTC.

Returns:

  • (Boolean)


3977
3978
3979
3980
3981
3982
# File 'app/models/delivery.rb', line 3977

def same_day_pickup?
  return false unless confirmed_pickup_date.present?

  tz = origin_address&.timezone_name.presence || 'America/Chicago'
  confirmed_pickup_date == Time.current.in_time_zone(tz).to_date
end

#save_purchase_order_if_needed(po) ⇒ void

This method returns an undefined value.

Persists a freshly built PurchaseOrder only if at least one PO
item was added (so empty supplier groups don't create empty POs).

Parameters:



3282
3283
3284
# File 'app/models/delivery.rb', line 3282

def save_purchase_order_if_needed(po)
  po.save! unless po.purchase_order_items.empty?
end

#schedule_pickup_if_necessaryvoid

This method returns an undefined value.

Queues the FedEx Freight pickup-scheduling worker (US or CA variant)
when this delivery has been ship-labeled through Heatwave. No-op for
non-FedEx-Freight carriers.



5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
# File 'app/models/delivery.rb', line 5054

def schedule_pickup_if_necessary
  # Only schedule pickups for FedExFreight (US and CA).
  return unless ship_labeled_via_heatwave? && pending_ship_confirm?

  if origin_address&.country&.iso == 'US' && carrier.index('FedEx') && carrier.index('Freight')
    FedExFreightUsSchedulePickupWorker.new.perform
  elsif origin_address&.country&.iso == 'CA' && carrier.index('FedEx') && carrier.index('Freight')
    FedExFreightCaSchedulePickupWorker.new.perform
  end
end

#schedule_request_estimated_packagingString

after_save callback that re-queues the pre-pack worker when state
drift between an order and its delivery puts them out of sync.

Returns:

  • (String)

    Sidekiq jid



5001
5002
5003
# File 'app/models/delivery.rb', line 5001

def schedule_request_estimated_packaging
  DeliveryRequestPrePackWorker.perform_in(5.seconds, id)
end

#selected_shipping_costShippingCost



101
# File 'app/models/delivery.rb', line 101

belongs_to :selected_shipping_cost, class_name: 'ShippingCost', optional: true

#send_address_type_issue_notificationvoid

This method returns an undefined value.

Notifies the team when a carrier reports an address-type issue
(residential vs commercial) on this delivery's destination so it can
be reclassified.



3291
3292
3293
# File 'app/models/delivery.rb', line 3291

def send_address_type_issue_notification
  DeliveryMailer.address_type_issue_notification(self).deliver
end

#send_canada_post_manual_void_email(tracking_numbers) ⇒ void

This method returns an undefined value.

Emails Canada Post asking them to manually void labels we couldn't
void via API (Canada Post has no programmatic void endpoint).

Parameters:

  • tracking_numbers (Array<String>)


3388
3389
3390
# File 'app/models/delivery.rb', line 3388

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

#send_commercial_invoice_to_carriervoid

This method returns an undefined value.

Forwards the commercial invoice to the carrier's customs email when
the carrier requires manual customs handoff (R+L Carriers,
Freightquote / Polaris). No-op when carrier is e-CI-capable.



3406
3407
3408
# File 'app/models/delivery.rb', line 3406

def send_commercial_invoice_to_carrier
  DeliveryMailer.commercial_invoice_to_carrier(self).deliver if should_send_commercial_invoice_to_carrier?
end

#send_delivery_pre_pack_cancelled_notification(cancelled_by: nil) ⇒ void

This method returns an undefined value.

Publishes Events::DeliveryPrePackCancelled so
DeliveryPrePackCancelledNotificationHandler can re-query this delivery
by id and email the warehouse / requester, naming the cancelling user.
Same-transaction-destroy safe (see send_delivery_pre_packed_notification).

Parameters:

  • cancelled_by (Party, nil) (defaults to: nil)


3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
# File 'app/models/delivery.rb', line 3370

def send_delivery_pre_pack_cancelled_notification(cancelled_by: nil)
  delivery_id = id
  cancelled_by_id = cancelled_by&.id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::DeliveryPrePackCancelled.new(data: { delivery_id:, cancelled_by_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

#send_delivery_pre_packed_notificationvoid

This method returns an undefined value.

Publishes Events::DeliveryPrePacked so
DeliveryPrePackedNotificationHandler can re-query this delivery by id
and email the team. The async re-query tolerates a same-transaction
destroy (purge_empty_quoting_deliveries) that would otherwise leave
the mailer's GlobalID arg pointing at a phantom row (AppSignal #4958).
The handler also clears suggested_packaging_text.



3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
# File 'app/models/delivery.rb', line 3311

def send_delivery_pre_packed_notification
  delivery_id = id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::DeliveryPrePacked.new(data: { delivery_id: }),
      stream_name: "Delivery-#{delivery_id}"
    )
  rescue StandardError => e
    ErrorReporting.error(e)
  end
end

#send_dropship_delivery_notificationvoid

This method returns an undefined value.

Sends the internal "new dropship delivery" notification announcing
that supplier purchase orders have been generated.



3299
3300
3301
# File 'app/models/delivery.rb', line 3299

def send_dropship_delivery_notification
  DeliveryMailer.dropship_delivery_notification(self).deliver
end

#send_purolator_manual_void_email(tracking_numbers) ⇒ void

This method returns an undefined value.

Emails Purolator asking them to manually void labels we couldn't
void via API.

Parameters:

  • tracking_numbers (Array<String>)


3397
3398
3399
# File 'app/models/delivery.rb', line 3397

def send_purolator_manual_void_email(tracking_numbers)
  Mailer.purolator_manual_void_email(self, tracking_numbers).deliver
end

#serial_numbers_file_nameString

Filename for the bundled serial-number labels PDF, dated to the current
minute to avoid tmp/ collisions on regeneration.

Returns:

  • (String)


1198
1199
1200
# File 'app/models/delivery.rb', line 1198

def serial_numbers_file_name
  "#{name(false, true)}_generated_serial_numbers_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf"
end

#serial_numbers_to_printArray<SerialNumber>

Reserved serial numbers for this delivery, including the original/swapped
numbers when an item was re-serialized. De-duplicated so each number
prints once on the labels PDF.

Returns:



1207
1208
1209
1210
1211
1212
1213
1214
# File 'app/models/delivery.rb', line 1207

def serial_numbers_to_print
  serial_numbers = []
  reserved_serial_numbers.each do |rsn|
    serial_numbers << rsn.serial_number
    serial_numbers << rsn.original_serial_number if rsn.original_serial_number.present?
  end
  serial_numbers.uniq
end

#set_cogsvoid

This method returns an undefined value.

Stamps unit and total cost of goods sold onto every LineItem on
this delivery. Pulls per-store COGS for store transfers, the
catalog/store item COGS for normal orders, and the carrier-actual
shipping cost for the shipping line.



3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
# File 'app/models/delivery.rb', line 3096

def set_cogs
  is_st = order && order.order_type == Order::STORE_TRANSFER
  line_items.non_shipping.each do |li|
    cogs = if li.parent_id.present?
             0.0
           elsif is_st
             li.item.store_item_for(order.from_store_id, 'AVAILABLE').unit_cogs
           else
             li.catalog_item.store_item.unit_cogs
           end
    li.update(unit_cogs: cogs, total_cogs: cogs * li.quantity)
  end
  line_items.shipping_only.each do |li|
    li.update(unit_cogs: actual_shipping_cost, total_cogs: actual_shipping_cost)
  end
end

#set_master_tracking_and_actual_shipping_cost_if_neededvoid

This method returns an undefined value.

Backfills the delivery's master_tracking_number, ltl_pro_number,
and actual_shipping_cost from the first completed Shipment when
they're missing — typically after dropship PO shipments are copied
over.



4607
4608
4609
4610
4611
4612
4613
4614
# File 'app/models/delivery.rb', line 4607

def set_master_tracking_and_actual_shipping_cost_if_needed
  return unless s = shipments.completed.first

  self.master_tracking_number ||= s.tracking_number
  self.ltl_pro_number ||= s.tracking_number if ships_ltl_freight?
  self.actual_shipping_cost ||= s.actual_total_charges
  save if master_tracking_number_changed? || ltl_pro_number_changed? || actual_shipping_cost_changed?
end

#set_override_shipping(autosave = true) ⇒ void

This method returns an undefined value.

Forces this delivery onto the override ShippingOption at $0.00 —
used by service-only / store-transfer flows where shipping isn't
billed separately.

Parameters:

  • autosave (Boolean) (defaults to: true)

    persist immediately



4922
4923
4924
4925
4926
4927
4928
# File 'app/models/delivery.rb', line 4922

def set_override_shipping(autosave = true)
  so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first
  self.shipping_option_id = so.id
  self.selected_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option_id == shipping_option_id } || shipping_costs.new(shipping_option: so, cost: 0.0)
  selected_shipping_cost.cost = 0.0
  save if autosave
end

#set_packaged_items_md5_hash(options = {}) ⇒ void

This method returns an undefined value.

Records the packed-items signature for this delivery via
Shipping::DeliveryMd5Extractor so future shipping recalculations
know not to wipe authoritative packing.

Parameters:

  • options (Hash) (defaults to: {})


4211
4212
4213
# File 'app/models/delivery.rb', line 4211

def set_packaged_items_md5_hash(options = {})
  Shipping::DeliveryMd5Extractor.new(options).process(self)
end

#set_proper_shipping_costBoolean

before_save driver that picks the right ShippingCost for the
delivery and syncs it onto the delivery (shipping_option,
selected_shipping_cost, shipping_cost) and the shipping
LineItem. Honours user-intentional overrides, EDI shipping options,
delivery deadlines, LTL switches, and falls back through preferred
service / carrier / cheapest. The bulk of carrier-selection policy
lives here.

Returns:

  • (Boolean)


2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
# File 'app/models/delivery.rb', line 2638

def set_proper_shipping_cost
  shipping_lines = line_items.select(&:is_shipping?)
  shipping_line = shipping_lines.pop
  Rails.logger.debug { "set_proper_shipping_cost, shipping_line: #{shipping_line ? shipping_line.id : 'none'}" }
  skip = false
  Rails.logger.debug { "set_proper_shipping_cost, self.changes: #{changes}" }
  Rails.logger.debug { "set_proper_shipping_cost, self.relevant_changes: #{relevant_changes}" }
  Rails.logger.debug { "set_proper_shipping_cost, self.has_not_changed_but_has_shipping_lines?: #{has_not_changed_but_has_shipping_lines?}" }
  Rails.logger.debug { "set_proper_shipping_cost, shipping_costs.select{|sc| sc.changes.any?}.empty?: #{shipping_costs.select { |sc| sc.changes.any? }.empty?}" }
  if has_not_changed_but_has_shipping_lines? && shipping_costs.select { |sc| sc.changes.any? }.empty?
    skip = true
  end # in the before_save context only set_proper_shipping_cost if we have not changed but have shipping lines
  Rails.logger.debug { "set_proper_shipping_cost, self.has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line): #{has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line)}" }
  if has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line)
    skip = false
  end # this tests to see if we do have shipping cost ids but none of them match the shipping line's shipping cost id: this means we are out of synch and the shipping line must be updated
  Rails.logger.debug { "set_proper_shipping_cost, resource&.do_not_detect_shipping: #{resource&.do_not_detect_shipping}" }
  skip = true if resource&.do_not_detect_shipping
  Rails.logger.debug { "set_proper_shipping_cost, self.shipping_costs.present?: #{shipping_costs.present?}" }
  skip = true if shipping_costs.blank?
  Rails.logger.debug { "set_proper_shipping_cost, skip: #{skip}" }
  Rails.logger.debug { "set_proper_shipping_cost, force_shipping_cost_update: #{force_shipping_cost_update}" }
  # Extra guard: if no trigger fields changed, a valid selection exists, and no shipping_costs mutated, skip heavy work
  if is_rma_return? && !skip
    trigger_keys = %w[destination_address_id shipping_option_id ltl_freight ltl_freight_guaranteed signature_confirmation saturday_delivery selected_shipping_cost_id]
    if (relevant_changes.keys & trigger_keys).empty? && selected_shipping_cost_id.present? && shipping_costs.select { |sc| sc.changes.any? }.empty? && !has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line)
      skip = true
    end
  end
  return true if skip && force_shipping_cost_update != true

  # try first with selected_shipping_cost_id
  Rails.logger.debug { "set_proper_shipping_cost, selected_shipping_cost_id: #{selected_shipping_cost_id}" }
  Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option.id: #{shipping_option&.id}" }
  Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option.is_override?: #{shipping_option&.is_override?}" }
  Rails.logger.debug { "set_proper_shipping_cost, self.selected_shipping_cost&.shipping_option&.is_override?: #{selected_shipping_cost&.shipping_option&.is_override?}" }
  if selected_shipping_cost.present? && selected_shipping_cost&.shipping_option&.is_override?
    reset_override = false
    reset_override = true if relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed?
    Rails.logger.debug { "set_proper_shipping_cost, (relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed?)): #{relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed?}" }
    # If the override is intentionally selected, never reset it regardless of address/LTL changes.
    # Check both: 1) delivery.shipping_option is override, 2) order's EDI shipping option is 'override'
    override_is_intentional = self.shipping_option&.is_override? || order&.edi_shipping_option_name == 'override'
    reset_override = false if override_is_intentional
    if reset_override
      Rails.logger.debug 'set_proper_shipping_cost, reset_override!!!'
      # go for cheapest if address changed or it switched from package to ltl freight or vice versa
      self.selected_shipping_cost_id = nil
    end
  end
  # Skip beta and item-less options from auto-selection — beta options require an
  # explicit admin choice; item-less options crash apply_selected_shipping_cost!
  # (AppSignal #5031, #5157). Falls back to the next viable option.
  sorted_costs = sorted_shipping_costs(uniq_by_shipping_option_id: true, filter_by_ltl_freight: ships_ltl_freight?)
                   .compact
                   .reject { |sc| sc.shipping_option&.is_beta? }
                   .select { |sc| sc.shipping_option&.item.present? }
  # If the current selection is a beta option, preserve it as an explicit admin choice —
  # beta options are intentionally excluded from sorted_costs (the auto-select pool) but
  # remain valid when a human picked them. Without this, the detect below returns nil and
  # the system treats the manual beta selection as "auto-matched", replacing it.
  selected_cost = shipping_costs.detect { |sc| sc.id == selected_shipping_cost_id }
  # Across a rate rebuild (retrieve_shipping_costs deletes + reinserts non-override
  # ShippingCost rows), selected_shipping_cost_id orphans to a deleted row. Non-beta,
  # non-freight selections recover via the `sc.shipping_option == self.shipping_option`
  # match below, but beta and freight options are filtered out of sorted_costs and can't
  # take that path — so fall back to matching by the delivery's still-set
  # shipping_option_id when the delivery is currently pointing at a beta or freight option.
  # Bugs surfaced on ST726065 (ShipEngine LTL beta dropped on every item edit) and
  # SO727121 (Sameday Worldwide Threshold dropped on release).
  if selected_cost.nil? && (shipping_option&.is_beta? || shipping_option&.is_freight)
    selected_cost = shipping_costs.detect { |sc| sc.shipping_option_id == shipping_option_id }
  end
  # Honor a deliberate selection that sorted_costs would otherwise drop, so an admin/EDI
  # pick survives a re-save (e.g. order release). sorted_costs excludes beta options
  # (rejected above) AND — because it is filtered by `filter_by_ltl_freight:
  # ships_ltl_freight?` — any freight option when the delivery's ltl_freight flag is unset.
  # In both cases the detect below misses the pick, shipping_cost_was_auto_matched flips
  # true, and the carrier / customer-default fallbacks replace it. That is the SO727121
  # swap: the lone is_freight rate (Sameday Worldwide Threshold) on a delivery with
  # ltl_freight unset was overwritten by the customer default (Purolator Ground).
  explicit_freight_pick = selected_cost.present? && !selected_cost.is_override? &&
                          selected_cost.shipping_option&.is_freight && !ships_ltl_freight?
  if selected_cost&.shipping_option&.is_beta? || explicit_freight_pick
    shipping_cost_entry = selected_cost
    shipping_cost_was_auto_matched = false
  else
    shipping_cost_entry = sorted_costs.detect { |sc| sc.id == selected_shipping_cost_id } # need to do this because the selected_shipping_cost might not yet be save e.g. setting override and override cost in one step
    shipping_cost_was_auto_matched = shipping_cost_entry.nil?
  end

  # Retrieves based on the resource or customer default if not set already
  # IMPORTANT: Don't override manually selected shipping options (especially overrides) with EDI preferred option
  # Preserve override selections even when shipping_option is temporarily nil during transitions
  if shipping_option.blank?
    # If there's already a selected shipping cost (especially an override), use its shipping option
    if selected_shipping_cost.present? && selected_shipping_cost.shipping_option.present?
      self.shipping_option = selected_shipping_cost.shipping_option
    # Only fall back to preferred_shipping_option if no selection exists
    else
      self.shipping_option = preferred_shipping_option
    end
  # If shipping_option is already set (including overrides), preserve it - don't change to preferred_shipping_option
  # This ensures manually selected overrides are preserved during order release from CR hold
  end
  Rails.logger.debug { "set_proper_shipping_cost, sorted_costs: #{sorted_costs.map { |sc| [sc.shipping_option.name, sc.id] }.inspect}" }

  # If switching to LTL, prefer the absolute cheapest freight option BEFORE any heuristic matches
  # This avoids picking an expensive carrier/service-level just because it matches previous preferences
  if ships_ltl_freight? && shipping_cost_entry.nil?
    freight_candidates = shipping_costs.select { |sp| sp.shipping_option.is_freight && !sp.hide? && !sp.shipping_option.is_beta? && sp.shipping_option.item.present? }
    freight_candidates_sorted = freight_candidates.sort_by { |a| a.cost.to_f.round(2) }
    Rails.logger.debug do
      "set_proper_shipping_cost, freight candidates by cost: #{freight_candidates_sorted.map { |sc| [sc.id, sc.shipping_option&.description, sc.cost.to_f.round(2)] }.inspect}"
    end
    cheapest_freight = freight_candidates_sorted.first
    if cheapest_freight
      Rails.logger.debug do
        "set_proper_shipping_cost, preselect cheapest freight: id=#{cheapest_freight.id}, #{cheapest_freight.shipping_option&.description}, cost=#{cheapest_freight.cost.to_f.round(2)}"
      end
      shipping_cost_entry = cheapest_freight
    end
  end
  # We have a selected shipping option, we try to match it or come close to it
  if shipping_cost_entry.nil? && self.shipping_option && !self.shipping_option.is_override?
    Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option: #{self.shipping_option.inspect}" }
    # First try to match the selected shipping option if we have one
    shipping_cost_entry = sorted_costs.detect { |sc| sc.shipping_option == self.shipping_option }
    Rails.logger.debug do
      "set_proper_shipping_cost, after match by shipping_option: shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
    # If Nothing matched based on the shipping option, try to be smart and find a similar service level and carrier option
    shipping_cost_entry ||= sorted_costs.detect { |sc| (sc.shipping_option.carrier == self.shipping_option.carrier) && (sc.shipping_option.service_level == self.shipping_option.service_level) } if self.shipping_option.service_level.present?
    Rails.logger.debug do
      "set_proper_shipping_cost, after match by shipping_option service_level and/or carrier: shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
    # Still nothing? so try to find the closest shipping_option based on the same service level, different carrier
    shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.service_level == self.shipping_option.service_level } if self.shipping_option.service_level.present?
    Rails.logger.debug do
      "set_proper_shipping_cost, after match by shipping_option.service_level: shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
    # Nothing again? ok try somethign with the same carrier
    shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.carrier == self.shipping_option.carrier } if self.shipping_option.carrier.present?
    Rails.logger.debug do
      "set_proper_shipping_cost, after match by shipping_option.carrier: shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
  end
  if self.shipping_option.present? && self.shipping_option.is_override?
    Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option: #{self.shipping_option.inspect}" }
    shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option == self.shipping_option }
  end
  if ltl_freight_has_changed?
    override_shipping_cost_entry ||= sorted_costs.detect(&:is_override?)
    override_shipping_cost_entry.cost = get_fallback_cost_to_use if relevant_changes['ltl_freight'].last == true
    override_shipping_cost_entry.cost = get_economy_shipping_cost_to_use if relevant_changes['ltl_freight'].last == false && ships_economy
    Rails.logger.debug { "set_proper_shipping_cost, LTL changes: #{relevant_changes['ltl_freight']}, shipping_cost_entry.cost: #{shipping_cost_entry&.cost}" }
  end
  # If LTL freight is active and nothing matched yet, as an additional guard prefer the cheapest freight option
  if ships_ltl_freight?
    shipping_cost_entry ||= sorted_shipping_costs_www_hash[:freight]&.reject { |sc| sc.shipping_option&.is_beta? }&.first
  end

  # If there's a delivery deadline, check if current selection meets it - if not, find best option
  if order&.requested_deliver_by.present?
    deliver_by = order.requested_deliver_by
    current_est_date = shipping_cost_entry&.carrier_estimated_delivery_date
    current_is_late = current_est_date.present? && current_est_date > deliver_by
    current_is_override = shipping_cost_entry&.is_override?
    current_has_no_date = shipping_cost_entry.present? && current_est_date.blank?

    # IMPORTANT: Do NOT auto-replace selections when:
    # 1. The delivery's shipping_option is explicitly set to override (user chose it)
    # 2. The order's EDI shipping option is 'override' (EDI orders like Amazon non-Buy-Shipping)
    # 3. The delivery's shipping_option is a Ship with Walmart (SWW) option (Walmart orders)
    # 4. The delivery already has an AmazonSeller option selected (user chose a specific AMZBS rate)
    user_selected_override = self.shipping_option&.is_override?
    edi_requires_override = order&.edi_shipping_option_name == 'override'
    user_selected_sww = self.shipping_option&.carrier == 'WalmartSeller'
    edi_requires_sww = order&.edi_shipping_option_name == 'sww' || order&.edi_shipping_option_name&.start_with?('sww_')
    user_selected_amz_bs = self.shipping_option&.carrier == 'AmazonSeller'
    edi_requires_amz_bs = order&.edi_shipping_option_name == 'amzbs' || order&.edi_shipping_option_name&.start_with?('amzbs_')
    selection_is_intentional = user_selected_override || edi_requires_override || user_selected_sww || edi_requires_sww || user_selected_amz_bs

    # For AMZBS orders where no prior selection existed (auto-matched by
    # carrier heuristic), optimize to the cheapest on-time AMZBS rate.
    # This handles initial import where the carrier-match picks a suboptimal
    # rate (e.g., Amazon Shipping Ground at $22.93 when FedEx HD at $18.61
    # is available and on-time). Skipped when the user already selected a
    # specific AMZBS rate (selection_is_intentional) — a rate refresh
    # orphans the old selected_shipping_cost_id making
    # shipping_cost_was_auto_matched true even though the user chose it.
    if edi_requires_amz_bs && shipping_cost_entry.present? && shipping_cost_was_auto_matched && !selection_is_intentional
      amzbs_costs = sorted_costs.select(&:is_amzbs?)
      amzbs_on_time = amzbs_costs.select { |sc|
        est = sc.carrier_estimated_delivery_date
        est.present? && est <= deliver_by
      }.sort_by { |sc| sc.cost.to_f }
      cheapest_amzbs = amzbs_on_time.first
      if cheapest_amzbs && cheapest_amzbs.cost.to_f < shipping_cost_entry.cost.to_f
        shipping_cost_entry = cheapest_amzbs
        Rails.logger.debug { "set_proper_shipping_cost, AMZBS auto-optimized to cheapest on-time: #{cheapest_amzbs.shipping_option&.name} at $#{cheapest_amzbs.cost}" }
      end
    end

    # Only find a better option if:
    # - No selection exists AND selection is NOT intentional, OR
    # - Current selection is late (but NOT if selection is intentional), OR
    # - Current selection is an unintentional override, OR
    # - Current selection has no delivery date (but NOT if selection is intentional)
    # IMPORTANT: If selection is intentional (override, SWW, or AMZBS), we should
    # NEVER auto-select a different option even if shipping_cost_entry is nil
    # (happens during refresh_deliveries_quoting before the ShippingCost is recreated)
    should_find_better_option = (shipping_cost_entry.nil? && !selection_is_intentional) ||
                                (current_is_late && !selection_is_intentional) ||
                                (current_is_override && !selection_is_intentional) ||
                                (current_has_no_date && !selection_is_intentional)
    if should_find_better_option
      # Filter to non-override options for this selection
      real_options = sorted_costs.reject(&:is_override?)
      # AMZBS orders must use Amazon Buy Shipping rates — Heatwave rates are
      # irrelevant because the label is purchased from Amazon's API.
      real_options = real_options.select(&:is_amzbs?) if edi_requires_amz_bs

      on_time_candidates = real_options.select do |sc|
        est_date = sc.carrier_estimated_delivery_date
        est_date.present? && est_date <= deliver_by
      end.sort_by { |sc| sc.cost.to_f }

      best_option = nil

      if on_time_candidates.any?
        # If customer has a third-party billing account, prefer options matching that carrier
        if customer&.&.any?
          preferred_carriers = customer..map(&:carrier).uniq
          best_option = on_time_candidates.detect { |sc| preferred_carriers.include?(sc.shipping_option.carrier) }
        end
        # Otherwise just pick the cheapest on-time option
        best_option ||= on_time_candidates.first
      else
        # No on-time options - pick the cheapest non-override option
        best_option = real_options.sort_by { |sc| sc.cost.to_f }.first
      end

      if best_option
        shipping_cost_entry = best_option
        Rails.logger.debug do
          "set_proper_shipping_cost, delivery deadline selection: deliver_by=#{deliver_by}, on_time=#{on_time_candidates.any?}, selected=#{shipping_cost_entry&.shipping_option&.description}"
        end
      end
    end
  end

  if resource.try(:cart?)
    # For carts, fall back default is the cheapest
    shipping_cost_entry ||= sorted_shipping_costs_www_hash[:economy]&.first
    Rails.logger.debug do
      "set_proper_shipping_cost, after fall back default to cheapest for cart: shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
  elsif has_dropship_items? && !order&.single_origin
    # here we want to use the cheapest option for one of the carriers on the supplier
    shipping_cost_entry ||= sorted_costs.select { |sc| origin_address.supported_carriers.present? ? origin_address.supported_carriers.include?(sc.shipping_option.carrier) : true }.first
    Rails.logger.debug do
      "set_proper_shipping_cost, dropship delivery, after fall back default to cheapest supplier carrier option: #{customer.default_shipping_option_name}, shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
  else
    # Fall back default is the customer's default/preferred
    shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.name == customer.default_shipping_option_name }
    Rails.logger.debug do
      "set_proper_shipping_cost, after fall back default to customer.default_shipping_option_name: #{customer.default_shipping_option_name}, shipping_cost_entry.shipping_option: #{begin
        shipping_cost_entry.shipping_option.inspect
      rescue StandardError
        nil
      end}"
    end
  end

  if ships_economy_package?
    Rails.logger.debug { "set_proper_shipping_cost, ships_economy_package?: #{ships_economy_package?}" }
    # use the override when shipping economy and reaching this point
    shipping_cost_entry ||= sorted_costs.detect(&:is_override?)
  elsif ships_ltl_freight?
    # For LTL freight, always prefer cheapest non-override freight option
    non_override_freight = sorted_costs.reject(&:is_override?).sort_by { |sc| sc.cost.to_f }
    if shipping_cost_entry.nil? || shipping_cost_entry.is_override?
      shipping_cost_entry = non_override_freight.first if non_override_freight.any?
    end
    Rails.logger.debug { "set_proper_shipping_cost, ships_ltl_freight, selected: #{shipping_cost_entry&.shipping_option&.description}" }
  else
    Rails.logger.debug { "set_proper_shipping_cost, !ships_economy_package?: #{!ships_economy_package?}" }
    # Fall back default is the first non postal option
    shipping_cost_entry ||= sorted_costs.detect { |sc| !sc.shipping_option.is_postal? }
  end
  Rails.logger.debug do
    "set_proper_shipping_cost, after fall back default: shipping_cost_entry.shipping_option: #{begin
      shipping_cost_entry.shipping_option.inspect
    rescue StandardError
      nil
    end}"
  end
  # Store back the shipping option since it could be different or changed
  # IMPORTANT: Do NOT overwrite shipping_option if it's intentionally set to override
  # This preserves user's explicit override selection even when shipping costs are being refreshed
  current_override_intentional = self.shipping_option&.is_override? || order&.edi_shipping_option_name == 'override'
  if shipping_cost_entry && !current_override_intentional
    self.shipping_option = shipping_cost_entry.shipping_option
  elsif shipping_cost_entry && current_override_intentional && shipping_cost_entry.is_override?
    # Only update if the new entry is also override (to sync override cost with override option)
    self.shipping_option = shipping_cost_entry.shipping_option
  end
  Rails.logger.debug do
    "set_proper_shipping_cost, after all matches: shipping_option: #{begin
      shipping_option.inspect
    rescue StandardError
      nil
    end}"
  end
  return unless shipping_cost_entry

  apply_selected_shipping_cost!(shipping_cost_entry)
  true
end

#set_shipped_datevoid

This method returns an undefined value.

Stamps the first ship event onto the delivery; idempotent so a
re-shipment doesn't move the date.



4270
4271
4272
# File 'app/models/delivery.rb', line 4270

def set_shipped_date
  update_attribute(:shipped_date, Time.current) if shipped_date.blank?
end

#ship_ci_pdfUpload?

Most recent commercial-invoice Upload attached to the delivery
(printable triplicate variant).

Returns:



4009
4010
4011
# File 'app/models/delivery.rb', line 4009

def ship_ci_pdf
  uploads.order(:id).reverse_order.find_by(category: 'ship_ci_pdf')
end

#ship_from_attributesHash?

Address attributes used as the "ship from" block on labels and
commercial invoices — driven by the delivery's origin warehouse for
orders, or the RMA's ship-from for returns.

Returns:

  • (Hash, nil)


4397
4398
4399
# File 'app/models/delivery.rb', line 4397

def ship_from_attributes
  order&.ship_from_attributes(self) || rma_for_return&.ship_from_attributes
end

#ship_labeled_via_heatwave?Boolean

Returns:

  • (Boolean)


740
741
742
# File 'app/models/delivery.rb', line 740

def ship_labeled_via_heatwave?
  supported_shipping_carrier? && shipments.completed.any? && shipments.completed.all? { |s| s.state == 'label_complete' }
end

#ship_labeled_via_heatwave_or_manual_and_ship_insuring?Boolean

Returns:

  • (Boolean)


744
745
746
# File 'app/models/delivery.rb', line 744

def ship_labeled_via_heatwave_or_manual_and_ship_insuring?
  ship_labeled_via_heatwave? || is_amazon_seller_central_veeqo?
end

#ship_natively_keySymbol?

Account-number lookup key used to pick credentials when shipping on
the customer's own carrier account ("ship natively"); nil when we
ship on Heatwave's accounts.

Returns:

  • (Symbol, nil)


4258
4259
4260
4261
4262
4263
4264
# File 'app/models/delivery.rb', line 4258

def ship_natively_key
  key = nil
  if chosen_shipping_method&. && chosen_shipping_method..ship_natively? && chosen_shipping_method...present?
    key = chosen_shipping_method...to_sym
  end
  key
end

#ship_to_attributesHash?

Address attributes used as the "ship to" block on labels and
commercial invoices, sourced from the parent Order or Rma.

Returns:

  • (Hash, nil)


4388
4389
4390
# File 'app/models/delivery.rb', line 4388

def ship_to_attributes
  order&.ship_to_attributes || rma_for_return&.ship_to_attributes
end

#shipment_contents_editable?(current_user = nil) ⇒ Boolean

Returns:

  • (Boolean)


3510
3511
3512
3513
3514
# File 'app/models/delivery.rb', line 3510

def shipment_contents_editable?(current_user = nil)
  return false if locked_for_fba? && !current_user&.has_role?('admin')

  picking? || pending_ship_labels? || pre_pack? # rb_any_ship_from || processing_po_fulfillment?
end

#shipment_event_tracking_numbersArray<String>

Tracking numbers whose ShipmentEvent scans belong to this delivery: the
per-package shipment tracking numbers PLUS the LTL freight PRO, which lives
on the delivery (not its pallet shipments) for ShipEngine LTL. Single
source for the Tracking Events tab's nav counter and content query so the
two can't drift — parcel scans key off shipment tracking_numbers, ShipEngine
LTL scans off ltl_pro_number.

Returns:

  • (Array<String>)


2559
2560
2561
2562
2563
2564
2565
# File 'app/models/delivery.rb', line 2559

def shipment_event_tracking_numbers
  # Use the loaded association in-memory when the caller preloaded shipments
  # (list views) so this doesn't fire a pluck query per delivery; fall back to
  # pluck for the single-delivery (show-page) path where nothing's preloaded.
  numbers = shipments.loaded? ? shipments.map(&:tracking_number) : shipments.pluck(:tracking_number)
  (numbers + [ltl_pro_number]).compact_blank.uniq
end

#shipmentsActiveRecord::Relation<Shipment>

Returns:

See Also:



116
# File 'app/models/delivery.rb', line 116

has_many :shipments, -> { order(:created_at) }, autosave: true, dependent: :destroy

#shipments_for_packingActiveRecord::Relation<Shipment>

Shipments currently visible to the packing UI — those still being
built (suggested), already packed, or awaiting carrier labels.

Returns:



957
958
959
# File 'app/models/delivery.rb', line 957

def shipments_for_packing
  shipments.suggested_packed_or_awaiting_labels.order(:created_at)
end

#shipments_to_packages_hash(use_shipments = nil) ⇒ Object

Bridge method from Shipments to package hash model used by WyShipping



1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
# File 'app/models/delivery.rb', line 1975

def shipments_to_packages_hash(use_shipments = nil)
  # Packed packages are used first, top level, ie not cartons packed on pallets, etc... if none present, we look at suggested
  use_shipments ||= shipments.top_level.where(state: 'packed').presence
  use_shipments ||= shipments.top_level.where(state: 'suggested')
  shipping_weights = []
  shipping_dimensions = []
  flat_rate_package_types = []
  container_types = []
  package_values = []
  use_shipments.each do |shp|
    # Convert BigDecimal to float to prevent JSON serialization as strings in carrier_responses jsonb
    shipping_weights << shp.weight.to_f
    shipping_dimensions << [shp.length.to_f, shp.width.to_f, shp.height.to_f]
    flat_rate_package_types << shp.flat_rate_package_type
    container_types << shp.container_type
    package_values << shp.compute_shipment_declared_value
  end
  {
    shipping_weights:,
    shipping_dimensions:,
    flat_rate_package_types:,
    container_types:,
    package_values:
  }
end

#shipments_voidable?Boolean

Returns:

  • (Boolean)


3516
3517
3518
# File 'app/models/delivery.rb', line 3516

def shipments_voidable?
  SHIPPING_STATES.include?(state.to_sym)
end

#shipping?Boolean

Returns:

  • (Boolean)


3520
3521
3522
# File 'app/models/delivery.rb', line 3520

def shipping?
  %i[pending_ship_confirm shipped].include?(state.to_sym)
end

#shipping_account_numberShippingAccountNumber



99
# File 'app/models/delivery.rb', line 99

belongs_to :shipping_account_number, optional: true

#shipping_costsActiveRecord::Relation<ShippingCost>

dependent destroy handled by trigger

Returns:

See Also:



111
# File 'app/models/delivery.rb', line 111

has_many :shipping_costs, -> { order(:cost) }, autosave: true

#shipping_line_itemLineItem?

Single shipping LineItem for the delivery (the row that bills the
carrier cost). Each delivery has at most one — extra ones are
cleaned up by #apply_selected_shipping_cost!.

Returns:



1538
1539
1540
# File 'app/models/delivery.rb', line 1538

def shipping_line_item
  line_items.shipping_only.first
end

#shipping_method_friendlyString

Compact human-readable shipping method label for delivery summary
widgets — collapses overrides, warehouse pickups, and service-only
deliveries to descriptive text rather than the raw shipping option name.

Returns:

  • (String)


1237
1238
1239
1240
1241
1242
1243
1244
# File 'app/models/delivery.rb', line 1237

def shipping_method_friendly
  return 'Service' if is_service_only?
  return 'Unknown' unless so = shipping_option
  return 'Pickup' if destination_address&.is_warehouse
  return line_items.shipping_only.map(&:shipping_cost).compact.first&.description if so.is_override?

  so.description
end

#shipping_methods_for_select(verbose = false, skip_override = false) ⇒ Array<Array(String, Integer)>

<select> payload of formatted carrier rate options, currency-aware
and with optional commitment text and account hints.

Parameters:

  • verbose (Boolean) (defaults to: false)

    include carrier delivery-commitment string

  • skip_override (Boolean) (defaults to: false)

    omit the override placeholder row

Returns:

  • (Array<Array(String, Integer)>)


2593
2594
2595
2596
2597
2598
2599
2600
# File 'app/models/delivery.rb', line 2593

def shipping_methods_for_select(verbose = false, skip_override = false)
  sorted_shipping_costs(skip_override).map do |sc|
    [
      "#{ActionController::Base.helpers.number_to_currency(sc.cost.round(2),
                                                           unit: currency_symbol)}: #{sc.shipping_option.description} #{sc.third_party_billed? ? " (using your linked account: #{sc..})" : ''} #{verbose ? "(#{sc.shipping_option.delivery_commitment})" : ''} ", sc.shipping_option.id
    ]
  end
end

#shipping_optionShippingOption



100
# File 'app/models/delivery.rb', line 100

belongs_to :shipping_option, optional: true

#shipping_option_matches?(so_name) ⇒ Boolean

Returns:

  • (Boolean)


1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
# File 'app/models/delivery.rb', line 1246

def shipping_option_matches?(so_name)
  return false if shipping_option.blank?

  # If our shipping option match straight we can return true right here
  return true if shipping_option.name == so_name

  # For Walmart orders, any Ship with Walmart (SWW) option is valid
  # 'sww' or 'override' as edi_shipping_option_name indicates any WalmartSeller option is acceptable
  if order&.edi_orchestrator_partner&.start_with?('walmart_seller') && shipping_option.carrier == 'WalmartSeller'
    return true
  end

  # 'sww' or any 'sww_*' pattern allows any SWW shipping option
  # This handles both the generic 'sww' marker and specific options like 'sww_fedex_smartpost'
  return true if (so_name == 'sww' || so_name&.start_with?('sww_')) && shipping_option.name.start_with?('sww_')

  # For Amazon Buy Shipping orders, any AmazonSeller shipping option is valid
  if order&.edi_orchestrator_partner&.start_with?('amazon_seller') && shipping_option.carrier == 'AmazonSeller'
    return true
  end

  # 'amzbs' or any 'amzbs_*' pattern allows any Amazon Buy Shipping option
  return true if (so_name == 'amzbs' || so_name&.start_with?('amzbs_')) && shipping_option.name.start_with?('amzbs_')

  # deal with non-exact matching ie fedex ground vs fedex ground residential
  if so_name.match?('fedex_ground')
    shipping_option.name.match?('fedex_ground')
  elsif customer.is_wayfair? && so_name.match?('nextdayair') # deal with Wayfair's option to use UPS second day air when UPS next day air or next day air saver is not available, see: https://partners.wayfair.com/help/2/article/323
    shipping_option.name.match?('secondayair')
  elsif customer.is_wayfair? && so_name.match?('fedex_standard_overnight') # deal with Wayfair's option to use FedEx two day when FedEx standard overnight is not available, see: https://partners.wayfair.com/help/2/article/323
    shipping_option.name.match?('fedex_twoday')
  else
    false
  end
end

#ships_economy?Boolean Also known as: ships_economy

don't know why, but need to do it this way, can't use delegate

Returns:

  • (Boolean)


5069
5070
5071
# File 'app/models/delivery.rb', line 5069

def ships_economy? # don't know why, but need to do it this way, can't use delegate
  resource&.ships_economy? || false
end

#ships_economy_ltl?Boolean

Returns:

  • (Boolean)


5078
5079
5080
# File 'app/models/delivery.rb', line 5078

def ships_economy_ltl?
  ships_economy && ships_ltl_freight?
end

#ships_economy_package?Boolean

Returns:

  • (Boolean)


5074
5075
5076
# File 'app/models/delivery.rb', line 5074

def ships_economy_package?
  ships_economy && !ships_ltl_freight?
end

#ships_ltl_freight?Boolean

Returns:

  • (Boolean)


2547
2548
2549
# File 'app/models/delivery.rb', line 2547

def ships_ltl_freight?
  !is_warehouse_pickup? && (ltl_freight.present? || ltl_freight_guaranteed.present?)
end

#should_have_electronic_commercial_invoice?Boolean

Returns:

  • (Boolean)


4021
4022
4023
# File 'app/models/delivery.rb', line 4021

def should_have_electronic_commercial_invoice?
  is_international? && (carrier == 'UPS' || carrier == 'FedEx') && shipments_voidable? # only return true on international deliveries using UPS when in shipping states, i.e. not quoting invoiced, etc
end

#should_print_heating_element_labels?Boolean

Returns:

  • (Boolean)


748
749
750
751
# File 'app/models/delivery.rb', line 748

def should_print_heating_element_labels?
  line_items.any? { |li| li.item.controllable? } &&
    line_items.any? { |li| li.item.is_thermostat? || li.item.is_towel_warmer_hardwired_control? || li.item.is_power? }
end

#should_send_commercial_invoice_to_carrier?Boolean

Returns:

  • (Boolean)


3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
# File 'app/models/delivery.rb', line 3410

def should_send_commercial_invoice_to_carrier?

  carrier_qualifies = false
  if reported_carrier == 'Freightquote'
   carrier_qualifies = true if FREIGHTQUOTE_CARRIER_SCACS_TO_SEND_COMMERCIAL_INVOICES.include?(selected_shipping_cost&.rate_data&.dig('scac')) # eBol functionality does not work for Polaris, so just send it using load number which hopefully works && ltl_pro_number != master_tracking_number) # We ensure that the Freqightquote carrier SCAC is included and also that ltl_pro_number is not the same as master_tracking_number, which is a sign that the warehouse did not update the ltl_pro_number from the carrier. For Freightquote this is populated via the events API, so we let the automated GetFreightquoteLoadNumber job send the commercial_invoice, when the pro number is populated
  else
    carrier_qualifies = true if CARRIERS_NAMES_TO_SEND_COMMERCIAL_INVOICES.include?(reported_carrier) # Otherwise we just trust the ltl_pro_number as entered by the warehouse or Freightquote events API
  end
  is_international? && ship_ci_pdf&.attachment_name.present? && carrier_qualifies && ltl_pro_number.present? # we must have an international delivery, with a CI attached, with a qualifying carrier and an LTL Pro number
end

#should_ship_ltl_freight?Boolean

Returns:

  • (Boolean)


2579
2580
2581
# File 'app/models/delivery.rb', line 2579

def should_ship_ltl_freight?
  is_default_ltl_freight? || ltl_freight.present? || ltl_freight_guaranteed.present?
end

#show_packaging_on_pick_slip?Boolean

Returns:

  • (Boolean)


1319
1320
1321
# File 'app/models/delivery.rb', line 1319

def show_packaging_on_pick_slip?
  destination_address&.is_amazon?
end

#simple_shipping_description_for_shipping_cost(sc = nil) ⇒ String?

Plain-text label describing the ShippingCost (no COD/insurance
badges). Recognises override variants — service, warehouse pickup,
zero-charge dropship, economy — and Ship-with-Walmart rates whose
descriptions are pulled from rate_data.

Parameters:

Returns:

  • (String, nil)


2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
# File 'app/models/delivery.rb', line 2419

def simple_shipping_description_for_shipping_cost(sc = nil)
  sc ||= selected_shipping_cost
  # nil description means line item name will default to linked item shipping option name, otherwise it's an override
  if sc&.is_override? && is_service_only?
    'Service'
  elsif sc&.is_override? && is_warehouse_pickup?
    'Warehouse Pickup'
  elsif sc&.is_override? && is_zero_charge_dropship?
    'Zero charge dropship'
  elsif sc&.is_override? && ships_economy_package?
    'Economy Shipping (up to 7-9 business days)'
  elsif is_sww_shipping_cost?(sc)
    # Ship with Walmart rate - use the shipping option description which is already formatted as "FedEx Ground Economy"
    # The sww_service_name already includes the carrier name, so we don't need to add it again
    sww_description = sc.shipping_option&.description || sc.rate_data&.dig('sww_service_name') || sc.rate_data&.dig(:sww_service_name) || 'Unknown Service'
    "Ship with Walmart: #{sww_description}".strip
  else
    sc&.description
  end
end

#skip_destination_address_validation?Boolean

Skip destination_address validation for instant quotes and shopping carts
Carts don't have a shipping address until checkout

Returns:

  • (Boolean)


3061
3062
3063
# File 'app/models/delivery.rb', line 3061

def skip_destination_address_validation?
  instant_quote? || resource.try(:cart?)
end

#sorted_ground_shipping_costs(skip_override = false) ⇒ Array<ShippingCost>

Ground-tier ShippingCost options sorted cheapest-first, with the
override row pushed to the bottom. Service level is taken from the
underlying shipping_option.days_commitment so dynamic carrier ETAs
don't bleed into categorization.

Parameters:

  • skip_override (Boolean) (defaults to: false)

    omit the override placeholder entirely

Returns:



2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
# File 'app/models/delivery.rb', line 2103

def sorted_ground_shipping_costs(skip_override = false)
  res = shipping_costs
  # res = shipping_costs.skip_3rd_party # we ignore skip_3rd_party
  res = res.skip_override if skip_override
  # sort by cost then days committment, override options last if any
  # Use shipping_option.days_commitment for service level categorization (ground vs expedited vs rush)
  # NOT sc.days_commitment which now includes dynamic carrier estimates that vary by shipment
  res.select { |sc| sc.shipping_option.days_commitment >= 3.5 || sc.shipping_option.carrier == 'SpeedeeDelivery' || sc.is_override? }.sort_by do |sc|
    sk1 = (sc.shipping_option.name == 'override' ? 9999 : 1)
    sk2 = begin
      (sc.rate_data['actual_cost'] || sc.cost).to_f.round(2)
    rescue StandardError
      9999.0
    end
    sk3 = begin
      (-1.0 * sc.days_commitment.to_f)
    rescue StandardError
      9999.0
    end
    [sk1, sk2, sk3]
  end
end

#sorted_shipping_costs(skip_override: false, uniq_by_shipping_option_id: false, filter_by_ltl_freight: nil) ⇒ Array<ShippingCost>

Full ShippingCost list for the delivery sorted cheapest-first with
override last. Optionally filters by LTL/package context and
de-duplicates rows that share a shipping_option_id (favoring the
latest insert so before-save churn doesn't pick a soon-to-be-deleted
row).

Parameters:

  • skip_override (Boolean) (defaults to: false)
  • uniq_by_shipping_option_id (Boolean) (defaults to: false)
  • filter_by_ltl_freight (Boolean, nil) (defaults to: nil)

    true=freight only, false=package only, nil=all

Returns:



2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
# File 'app/models/delivery.rb', line 2136

def sorted_shipping_costs(skip_override: false, uniq_by_shipping_option_id: false, filter_by_ltl_freight: nil)
  res = shipping_costs.includes(:shipping_option)
  # res = shipping_costs.skip_3rd_party # we ignore skip_3rd_party
  res = res.skip_override if skip_override
  # keep default behavior for nil, otherwise look for matching is_freightflag on the linked shipping option
  unless filter_by_ltl_freight.nil?
    res = res.select{|sc| sc.shipping_option.is_freight == filter_by_ltl_freight || sc.is_override?} # always include override since the scope above will handle skipping it
  end
  if uniq_by_shipping_option_id
    res = res.sort_by(&:id).reverse.uniq(&:shipping_option_id).reverse # here we favor the higher IDs since this can be called in a before)save context where the lower IDs will get deleted
  end
  # sort by cost then days committment, override options last if any
  res.sort_by do |sc|
    sk1 = (sc.shipping_option.name == 'override' ? 9999 : 1)
    sk2 = begin
      (sc.rate_data['actual_cost'] || sc.cost).to_f.round(2)
    rescue StandardError
      9999.0
    end
    sk3 = begin
      (-1.0 * sc.days_commitment.to_f)
    rescue StandardError
      9999.0
    end
    [sk1, sk2, sk3]
  end
end

#sorted_shipping_costs_www_hash(sort_by_price: true) ⇒ Hash{Symbol => Array<ShippingCost>}

Service-level grouped ShippingCost buckets used by the WWW shipping
picker: :economy (override), :ground, :faster (expedited+rush),
:freight. PO-box destinations are restricted to postal carriers.

Parameters:

  • sort_by_price (Boolean) (defaults to: true)

    when true, sort each bucket by cost; otherwise honor catalog carrier order

Returns:



2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
# File 'app/models/delivery.rb', line 2170

def sorted_shipping_costs_www_hash(sort_by_price: true)
  res = shipping_costs.skip_3rd_party.includes(:shipping_option)

  carriers = WWW_SHIPPING_CARRIERS[origin_address.country_iso3.to_sym]
  WWW_FREIGHT_CARRIERS[origin_address.country_iso3.to_sym]
  if destination_address.present? && destination_address.po_box?
    carriers = PO_BOX_CARRIERS
  end # If it's a PO box, then use postal carriers

  # ECONOMY SERVICE
  result_economy = shipping_costs.override_only

  # GROUND SERVICE
  # Use shipping_option.days_commitment for service level categorization (ground vs expedited vs rush)
  # NOT sc.days_commitment which now includes dynamic carrier estimates that vary by shipment
  gs = res.select { |sc| sc.shipping_option.days_commitment >= 3.5 || sc.shipping_option.carrier == 'SpeedeeDelivery' }
  result_ground = gs.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] }
  result_ground = result_ground.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price

  # EXPEDITED SERVICE
  es = res.select { |sc| sc.shipping_option.days_commitment >= 2 && sc.shipping_option.days_commitment < 3.5 && sc.shipping_option.carrier != 'SpeedeeDelivery' }
  result_expedited = es.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] }
  result_expedited = result_expedited.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price

  # RUSH SERVICE
  rs = res.select { |sc| sc.shipping_option.days_commitment < 2 && sc.shipping_option.carrier != 'SpeedeeDelivery' }
  result_rush = rs.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] }
  result_rush = result_rush.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price

  # FREIGHT SERVICE # filter these using our new is_freight column and choose the cheapest
  result_freight = [res.select { |sp| sp.shipping_option.is_freight && !sp.hide? }.min_by { |a| a.cost.to_f.round(2) }]

  { economy: result_economy.uniq, ground: result_ground.compact.uniq, faster: (result_expedited + result_rush).compact.uniq, freight: result_freight.compact.uniq }
end

#split_serial_numbersvoid

This method returns an undefined value.

Splits each reservation-requiring LineItem into per-serial-number
rows so each unit can carry its own serial. Inverse of
#rejoin_serial_numbers.



4702
4703
4704
# File 'app/models/delivery.rb', line 4702

def split_serial_numbers
  line_items.select(&:require_reservation?).each(&:split_serial_numbers)
end

#state_listArray<Symbol>

Ordered list of states this delivery actually moves through, used
to render the progress stepper in the UI. Varies by delivery flavor:
warehouse pickup, dropship, RMA replacement, service-only, or
standard shipping.

Returns:

  • (Array<Symbol>)


4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
# File 'app/models/delivery.rb', line 4465

def state_list
  if order && (order.order_type == Order::CREDIT_ORDER)
    %i[pending_ship_labels return_labels_complete]
  elsif is_warehouse_pickup?
    %i[at_warehouse picking pending_pickup_confirm shipped invoiced]
  elsif has_dropship_items? && !order.single_origin
    %i[awaiting_po_fulfillment processing_po_fulfillment shipped invoiced]
  elsif order && (order.is_rma_replacement? || order.precreate_rma?)
    %i[at_warehouse picking pending_ship_labels pending_ship_confirm shipped invoiced]
  elsif is_service_only?
    %i[service_ready_to_fulfill shipped invoiced cancelled]
  else
    %i[at_warehouse picking pending_ship_labels pending_ship_confirm shipped invoiced]
  end
end

#storeObject

Alias for Resource#store

Returns:

  • (Object)

    Resource#store

See Also:



177
# File 'app/models/delivery.rb', line 177

delegate :store, to: :resource

#supplierSupplier

Returns:

See Also:



102
# File 'app/models/delivery.rb', line 102

belongs_to :supplier, optional: true

#supported_shipping_carrier?Boolean

Returns:

  • (Boolean)


4414
4415
4416
4417
4418
4419
4420
4421
4422
# File 'app/models/delivery.rb', line 4414

def supported_shipping_carrier?
  return false if override_shipping_method?

  # Standard carriers (FedEx, UPS, USPS, etc.)
  return true if (SUPPORTED_SHIPPING_CARRIERS[country&.iso3&.to_sym] || []).include?(carrier)

  # Marketplace carriers (WalmartSeller, etc.) - label purchase via marketplace API
  Edi::MarketplaceLabelPurchaser.marketplace_carrier?(carrier)
end

#third_party_billed?Boolean

Single source of truth, at the delivery layer, for "is the carrier
billed to the customer's own (attached) account?" — i.e. a
ShippingAccountNumber is attached to the chosen shipping method.
Selecting a SAN (auto-default OR manually from the rate-shop dropdown,
which is allowed even when the owner's bill_shipping_to_customer is
off) IS the instruction to bill it; the attached SAN carries the
account number. The carrier label
(WyShipping.classify_third_party_billing), the customer invoice
(#adjusted_actual_shipping_cost, ShippingCost#calculated_cost),
and the on-screen account labels all gate on this one predicate so
they cannot drift apart (the drift is what billed WarmlyYours's
account while zeroing the customer invoice — SO723955, SO725940).

Returns:

  • (Boolean)


4229
4230
4231
# File 'app/models/delivery.rb', line 4229

def third_party_billed?
  chosen_shipping_method&.third_party_billed? || false
end

Public tracking URL for the delivery, resolving marketplace carriers
(Amazon, Walmart) to the underlying first-mile carrier when available.

Returns:

  • (String, nil)


4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
# File 'app/models/delivery.rb', line 4140

def tracking_link
  resolved_carrier = case carrier
                      when 'AmazonSeller'
                        shipments.detect { |s| s.amz_carrier.present? }&.amz_carrier || carrier
                      when 'WalmartSeller'
                        shipments.detect { |s| s.sww_carrier.present? }&.sww_carrier || carrier
                      else
                        carrier
                      end
  Shipment.tracking_link(resolved_carrier, master_tracking_number)
end

#uncommit_catalog_itemsvoid

This method returns an undefined value.

Reverses #commit_catalog_items when a delivery is cancelled,
returning quantity-available back to inventory.



4295
4296
4297
# File 'app/models/delivery.rb', line 4295

def uncommit_catalog_items
  Item::InventoryCommitter.crm_uncommit(line_items)
end

#uncommit_reserved_serial_numbersvoid

This method returns an undefined value.

Reverses #commit_reserved_serial_numbers so the serials become
available for another delivery.



4303
4304
4305
# File 'app/models/delivery.rb', line 4303

def uncommit_reserved_serial_numbers
  line_items.each(&:uncommit_reserved_serial_numbers)
end

This method returns an undefined value.

Reverses #link_serial_numbers_to_line_items (e.g. when an invoiced
delivery is reverted) so the serials become available again.



4727
4728
4729
# File 'app/models/delivery.rb', line 4727

def unlink_serial_numbers_to_line_items
  line_items.each(&:unlink_serial_numbers)
end

#update_line_items_qty_shippedObject

Updates qty_shipped to match quantity for all line items in this delivery.
Uses update_all for atomicity and to prevent deadlocks when multiple
processes ship deliveries concurrently (see AppSignal #1981).



4310
4311
4312
# File 'app/models/delivery.rb', line 4310

def update_line_items_qty_shipped
  line_items.update_all('qty_shipped = quantity')
end

#update_serial_numbers_shipped_countvoid

This method returns an undefined value.

Bumps the per-SerialNumber shipped count after a successful ship
event so analytics and warranty tracking stay current.



4743
4744
4745
# File 'app/models/delivery.rb', line 4743

def update_serial_numbers_shipped_count
  line_items.each(&:update_serial_numbers_shipped_count)
end

#uploadsActiveRecord::Relation<Upload>

Returns:

  • (ActiveRecord::Relation<Upload>)

See Also:



117
# File 'app/models/delivery.rb', line 117

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

#valid_for_generating_return_labels?Boolean

Returns:

  • (Boolean)


4436
4437
4438
4439
4440
4441
# File 'app/models/delivery.rb', line 4436

def valid_for_generating_return_labels?
  return true if pending_ship_labels? && supported_shipping_carrier? && is_domestic? && shipments.packed_or_awaiting_labels.any?

  errors.add(:base, 'needs be in state pending ship labels, shipping domestically, and with a supported carrier and shipments ready to label')
  false
end

#valid_for_generating_ship_labels?Boolean

Returns:

  • (Boolean)


4424
4425
4426
4427
4428
4429
4430
# File 'app/models/delivery.rb', line 4424

def valid_for_generating_ship_labels?
  return false unless pending_ship_labels? && supported_shipping_carrier?
  return true if is_domestic?
  return true if shipping_option.supported_for_st && order&.is_store_transfer?

  false
end

#valid_for_voiding_ship_labels?Boolean

Returns:

  • (Boolean)


4432
4433
4434
# File 'app/models/delivery.rb', line 4432

def valid_for_voiding_ship_labels?
  pending_ship_confirm? && shipments.label_complete.any? && !is_part_of_manifest?
end

#validate_all_contents_allocatedvoid

This method returns an undefined value.

State-validation helper for pending_ship_labels: every LineItem
must be fully assigned to a Shipment before labels can be cut.



4773
4774
4775
4776
4777
# File 'app/models/delivery.rb', line 4773

def validate_all_contents_allocated
  return if all_lines_allocated_to_shipments?

  errors.add(:base, 'Not all lines are allocated properly to containers')
end

#verify_payment_coverage!void

This method returns an undefined value.

Re-checks gateway payment authorization right before pickup-confirm,
raising if authorizations have expired or been voided externally.
Skipped for non-gateway payment types (PO, Check, Wire, Store Credit).

Raises:

  • (RuntimeError)

    when the order's available funds are insufficient



3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
# File 'app/models/delivery.rb', line 3496

def verify_payment_coverage!
  return unless order
  return unless order.payments.any? { |p| p.category.in?(GATEWAY_PAYMENT_TYPES) }

  order.check_payments_status
  order.reload if order.persisted?

  unless order.all_funds_available?
    raise "Cannot ship delivery #{id}: order #{order.id} does not have sufficient " \
          "authorized or captured funds (balance: $#{'%.2f' % order.balance}). " \
          "Resolve payment before shipping."
  end
end

#versions_for_audit_trail(_params = {}) ⇒ ActiveRecord::Relation<RecordVersion>

RecordVersion rows for the audit trail tab — covers the delivery
itself plus any line-item versions whose reference_data scopes them
to this delivery (under either Order or Quote ownership).

Returns:



4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
# File 'app/models/delivery.rb', line 4845

def versions_for_audit_trail(_params = {})
  query_sql = %q{
                (
                  item_type = 'LineItem'
                    AND reference_data @> '{"resource_type": "Order"}'
                    AND reference_data @> :delivery_id_json
                )
                OR (
                  item_type = 'LineItem'
                    AND reference_data @> '{"resource_type": "Quote"}'
                    AND reference_data @> :delivery_id_json
                )
                OR
                (item_type = 'Delivery' AND item_id = :id)
              }
  RecordVersion.where(query_sql, id:, delivery_id_json: { delivery_id: id }.to_json)
end

#void_early_label_on_orderBoolean

Void early-purchased label on the order if one exists and hasn't been transferred to a shipment yet
Only called when there are no completed shipments (label not yet transferred)

Returns:

  • (Boolean)

    true if an early label was voided, false otherwise



4101
4102
4103
4104
4105
4106
4107
4108
4109
# File 'app/models/delivery.rb', line 4101

def void_early_label_on_order
  return false unless resource.is_a?(Order)
  return false unless resource.respond_to?(:has_early_purchased_label?)
  return false unless resource.has_early_purchased_label?

  Rails.logger.info("[Delivery] Voiding early label on order #{resource.reference_number} (not yet transferred to shipment)")
  resource.void_early_label!(reason: 'Shipments voided on delivery')
  true
end

#void_early_label_on_order_if_existsObject

Void the early-purchased label on the order if one exists (regardless of
whether it was transferred to a shipment). Also resets purchase_label_early.
Called from void_shipments when completed shipments exist — the carrier void
already happened via WyShipping.void_delivery, this just cleans up the
early label metadata so the order behaves like a regular order.



4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
# File 'app/models/delivery.rb', line 4116

def void_early_label_on_order_if_exists
  return unless resource.is_a?(Order)

  if resource.has_early_purchased_label?
    Rails.logger.info("[Delivery] Voiding early label metadata on order #{resource.reference_number} (shipments voided)")
    resource.void_early_label!(reason: 'Shipments voided on delivery', reset_flag: true)
  elsif resource.purchase_label_early?
    reset_early_label_flag_on_order
  end
end

#void_marketplace_labelsObject

Void marketplace labels (Walmart SWW, Amazon, etc.) for all shipments with labels



1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
# File 'app/models/delivery.rb', line 1078

def void_marketplace_labels
  shipments.label_complete.each do |shipment|
    next unless shipment.tracking_number.present?

    # Check if this is a marketplace label
    purchaser_class = Edi::MarketplaceLabelPurchaser.for_delivery(self)
    next unless purchaser_class

    begin
      purchaser = purchaser_class.new(shipment)
      if purchaser.respond_to?(:void_label)
        result = purchaser.void_label
        if result[:success]
          logger.info("[Delivery] Voided marketplace label for shipment #{shipment.id}")
        else
          logger.warn("[Delivery] Failed to void marketplace label for shipment #{shipment.id}: #{result[:error]}")
          # Continue anyway - the label might already be voided or the API might be unavailable
        end
      end
    rescue StandardError => e
      logger.error("[Delivery] Error voiding marketplace label for shipment #{shipment.id}: #{e.message}")
      # Continue anyway - don't block the cancel operation
    end
  end
end

#void_shipmentsHash{Symbol => Object}

Voids all completed Shipments on the delivery: walks the
carrier-void path through WyShipping, falls back to manual void
emails for Canadapost/Purolator, clears master tracking/BOL/cost,
transitions back to pending_ship_labels and cleans up any
early-purchased label on the parent Order.

Returns:

  • (Hash{Symbol => Object})

    status_code/status_message



4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
# File 'app/models/delivery.rb', line 4040

def void_shipments
  # puts "!!void_shipments"
  if shipments.completed.any? && !is_part_of_manifest?
    shipments.manually_complete.each(&:manually_voided!)
    shipping_result = {}
    shipping_result[:status_code] = :ok
    if shipments.label_complete.any?
      tracking_numbers = shipments.label_complete.pluck(:tracking_number)
      shipping_result = WyShipping.void_delivery(self)
      append_to_shipping_api_log!(kind: 'void', shipping_result: shipping_result)
      # going to go merrily along but send admin notification if this didn't properly void
      # puts "shipping_result: #{shipping_result}"
      if shipping_result[:status_code] != :ok
        msg = %(
          delivery#void_shipments for delivery #{id} returned error shipping_result[:status_code]: #{shipping_result[:status_code]}
        )

        ErrorReporting.error(msg,
                      delivery_id: id,
                      shipping_result:,
                      tracking_numbers: shipments.label_complete.map(&:tracking_number))
        Rails.logger.error msg

        send_canada_post_manual_void_email(tracking_numbers) if carrier == 'Canadapost'
        send_purolator_manual_void_email(tracking_numbers) if carrier == 'Purolator'
      elsif carrier == 'Freightquote' && freight_order_number.present?
        # CHR's DELETE returns an async requestId rather than a
        # synchronous cancel confirmation. Schedule a follow-up that
        # alerts ops if no LOAD CANCELLED / ORDER CANCELED event has
        # arrived within 1 hour. Non-blocking — same notify-and-move-on
        # pattern as the Canadapost / Purolator manual void emails
        # above.
        FreightquoteVoidConfirmationWorker.perform_in(
          1.hour, id, freight_order_number, Time.current.iso8601
        )
      end
      shipments.label_complete.each(&:label_voided!)
      # Clear carrier-assigned fields so the re-label form starts clean
      update_columns(master_tracking_number: nil, carrier_bol: nil, actual_shipping_cost: nil, shipengine_label_id: nil,
                     confirmed_pickup_date: nil, confirmed_pickup_window_start_at: nil, confirmed_pickup_window_end_at: nil)
      reload.cancel_shipments! unless pending_ship_labels?
    end

    # Void the early-purchased label metadata on the order (if any) so the
    # picker no longer shows the early label banner and the order behaves
    # like a regular order going forward. Also resets purchase_label_early.
    void_early_label_on_order_if_exists

    { status_code: shipping_result[:status_code], status_message: "Please ensure you manually void manual shipments. Shipments voided. #{shipping_result[:status_message]}" }
  elsif void_early_label_on_order
    # No completed shipments yet, but there's an early label that hasn't been transferred - void it
    { status_code: :ok, status_message: 'Early-purchased shipping label has been voided.' }
  else
    { status_code: :error, status_message: "Can't void shipments because the delivery has no completed shipments OR delivery has been added to a manifest." }
  end
end