Class: Delivery

Inherits:
ApplicationRecord show all
Includes:
Memery, Models::Auditable, Models::LegacyRateRequest, Models::Notable, Models::Packable, Models::Payable, Models::Profitable, Models::ShipMeasurable
Defined in:
app/models/delivery.rb

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)

rubocop:disable Metrics/ClassLength -- 2265 lines; pre-existing god object, tracked
for decomposition (see the god-object-decomposition skill). Disabled at the class
level so reviews do not repeatedly flag acknowledged debt whenever Delivery is
touched.

Defined Under Namespace

Classes: ApplySelectedShippingCost, CalculateShippingOptions, GenerateLabels, GroupWwwShippingCosts, InvoicingHandler, PrintLabels, RetrieveShippingCosts, SetProperShippingCost

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 Models::Schedulable

Models::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::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 Models::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.



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

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.



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

def force_shipping_cost_update
  @force_shipping_cost_update
end

#ltl_exclusion_reasonsObject

Returns the value of attribute ltl_exclusion_reasons.



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

def ltl_exclusion_reasons
  @ltl_exclusion_reasons
end

#master_tracking_numberObject (readonly)

The number warehouse staff type when a label was bought outside
Heatwave. Checked against the delivery's carrier the same way each
shipment's own number is. @see TrackingNumberFormatValidator

Validations:

  • Tracking_number_format ({ carrier_method: :reported_carrier })


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

validates :master_tracking_number, tracking_number_format: { carrier_method: :reported_carrier }

#override_carrierObject

Returns the value of attribute override_carrier.



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

def override_carrier
  @override_carrier
end

#override_future_release_dateObject

Returns the value of attribute override_future_release_date.



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

def override_future_release_date
  @override_future_release_date
end

#payment_idsObject

Returns the value of attribute payment_ids.



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

def payment_ids
  @payment_ids
end

#skip_tracking_number_validationObject

Returns the value of attribute skip_tracking_number_validation.



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

def skip_tracking_number_validation
  @skip_tracking_number_validation
end

Class Method Details

.activeActiveRecord::Relation<Delivery>

A relation of Deliveries that are active. Active Record Scope

Returns:

See Also:



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

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:



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

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

.auto_ship_confirm(logger: nil) ⇒ Object

Auto ship confirm.

Parameters:

  • logger (Object) (defaults to: nil)

    the logger



3987
3988
3989
3990
3991
3992
3993
3994
3995
# File 'app/models/delivery.rb', line 3987

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:



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

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:



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

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:



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

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:



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

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:



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

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

.dropshipActiveRecord::Relation<Delivery>

A relation of Deliveries that are dropship. Active Record Scope

Returns:

See Also:



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

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:



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

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:



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

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:



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

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

.generate_super_pick_slip_pdf(deliveries, split_kits = false) ⇒ Object

Generate super pick slip pdf.

Parameters:

  • deliveries (Object)

    the deliveries

  • split_kits (Object) (defaults to: false)

    the split kits



1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
# File 'app/models/delivery.rb', line 1485

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

Invoice shipped deliveries.

Parameters:

  • _logger (Object) (defaults to: Rails.logger)

    the logger



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

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:



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

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:



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

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') }

.locked_ids_among(delivery_ids) ⇒ Set<Integer>

Which of the given delivery ids are locked (invoiced). Line items on a
locked delivery are financially frozen — the deferred
locked_delivery_line_items_guard DB trigger rejects any write — so
order-wide recalcs (discount reset, re-pricing) use this to skip them.

Parameters:

  • delivery_ids (Array<Integer>, ActiveRecord::Relation, nil)

Returns:

  • (Set<Integer>)


417
418
419
420
421
# File 'app/models/delivery.rb', line 417

def self.locked_ids_among(delivery_ids)
  return Set.new if delivery_ids.blank?

  where(id: delivery_ids, locked: true).ids.to_set
end

.non_pickupsActiveRecord::Relation<Delivery>

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

Returns:

See Also:



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

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:



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

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:



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

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

.not_ltl_freightActiveRecord::Relation<Delivery>

A relation of Deliveries that are not ltl freight. Active Record Scope

Returns:

See Also:



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

scope :not_ltl_freight, -> { where(ltl_freight: [false, nil], ltl_freight_guaranteed: [false, nil]) }

.pending_manifest_completionActiveRecord::Relation<Delivery>

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

Returns:

See Also:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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.



995
996
997
998
999
1000
1001
1002
1003
1004
# File 'app/models/delivery.rb', line 995

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:



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

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.



1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
# File 'app/models/delivery.rb', line 1010

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:



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

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:



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

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:



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

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

.states_for_selectObject

States for select.



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

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:



399
400
401
402
# File 'app/models/delivery.rb', line 399

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:



436
437
438
439
440
# File 'app/models/delivery.rb', line 436

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:



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

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:



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

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:



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

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

Instance Method Details

#activitiesActiveRecord::Relation<Activity>

Returns the associated activities.

Returns:

  • (ActiveRecord::Relation<Activity>)

    the associated activities



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

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

#actual_shipping_cost_exceeds_threshold?Boolean

Returns whether the record actual shipping cost exceeds threshold.

Returns:

  • (Boolean)

    whether the record actual shipping cost exceeds threshold



4092
4093
4094
4095
4096
4097
# File 'app/models/delivery.rb', line 4092

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)


4078
4079
4080
4081
4082
# File 'app/models/delivery.rb', line 4078

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 whether the record actual shipping cost within threshold.

Returns:

  • (Boolean)

    whether the record actual shipping cost within threshold



4085
4086
4087
4088
4089
# File 'app/models/delivery.rb', line 4085

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)


4104
4105
4106
4107
4108
4109
4110
4111
4112
# File 'app/models/delivery.rb', line 4104

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 whether the record actual weight discrepancy exceeds threshold.

Returns:

  • (Boolean)

    whether the record actual weight discrepancy exceeds threshold



4116
4117
4118
4119
4120
4121
4122
4123
# File 'app/models/delivery.rb', line 4116

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:



2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
# File 'app/models/delivery.rb', line 2656

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:



2637
2638
2639
2640
2641
2642
2643
2644
# File 'app/models/delivery.rb', line 2637

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)


3689
3690
3691
3692
3693
3694
3695
# File 'app/models/delivery.rb', line 3689

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:



895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
# File 'app/models/delivery.rb', line 895

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_completed_shipments_reported_tracking?Boolean

True once every completed shipment can report a tracking number for the EDI
ship confirm. Shipment#display_tracking_number resolves the delivery-level
ltl_pro_number for ShipEngine LTL (whose per-pallet tracking_number is
blank) and the shipment's own tracking_number otherwise. This is the gate
for publishing the confirm — LTL carriers that assign the PRO asynchronously
read blank here until the PRO lands.

Returns:

  • (Boolean)


2831
2832
2833
2834
# File 'app/models/delivery.rb', line 2831

def all_completed_shipments_reported_tracking?
  completed = shipments.completed.to_a
  completed.any? && completed.all? { |s| s.display_tracking_number.present? }
end

#all_dropship_items_fulfilled?Boolean

Returns whether the record all dropship items fulfilled.

Returns:

  • (Boolean)

    whether the record all dropship items fulfilled



2539
2540
2541
# File 'app/models/delivery.rb', line 2539

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:



3427
3428
3429
# File 'app/models/delivery.rb', line 3427

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:



3358
3359
3360
# File 'app/models/delivery.rb', line 3358

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

#all_lines_allocated_to_shipments?Boolean

Returns whether the record all lines allocated to shipments.

Returns:

  • (Boolean)

    whether the record all lines allocated to shipments



1078
1079
1080
# File 'app/models/delivery.rb', line 1078

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 whether the record all lines allocated to shipments and shipments have weight.

Returns:

  • (Boolean)

    whether the record all lines allocated to shipments and shipments have weight



1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
# File 'app/models/delivery.rb', line 1083

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_lines_labeled_by_completed_shipments?Boolean

True when every shippable line item is already fully covered, by quantity,
by an active label_complete shipment — i.e. the delivery's items already
have carrier labels, so buying more would just duplicate them. Idempotency
guard for #generate_labels (defect G / SO728077, where a re-pack cycle
bought 4 carrier labels for the same 2 items). Recovery is the existing
"Void Shipments" action.

Mirrors #line_allocation_status_hash but scopes coverage to bought labels
rather than packing-stage shipments.

Returns:

  • (Boolean)


1147
1148
1149
1150
1151
1152
# File 'app/models/delivery.rb', line 1147

def all_lines_labeled_by_completed_shipments?
  return false if shipments.label_complete.none?

  labeled = 
  line_items_eligible_for_packing.all? { |li| labeled[li.id].to_i >= li.quantity.abs }
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



4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
# File 'app/models/delivery.rb', line 4266

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) ⇒ void

This method returns an undefined value.

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.

Parameters:

  • kind (String)

    the kind of log entry (e.g. the carrier event name)

  • fields (Hash)

    extra entry fields, forwarded to #build_shipping_api_log_entry

Options Hash (**fields):

  • status (String)

    entry status, defaults to 'success'

  • status_message (String, nil)

    message describing the status

  • request (Object, nil)

    the carrier request payload

  • response (Object, nil)

    the carrier response payload

  • response_status (Integer, nil)

    HTTP status of the response

  • response_headers (Hash, nil)

    HTTP headers of the response



3296
3297
3298
3299
3300
3301
3302
# File 'app/models/delivery.rb', line 3296

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.

Parameters:

  • kind (Object)

    the kind

  • shipping_result (Object)

    the shipping result



3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
# File 'app/models/delivery.rb', line 3245

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)


1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
# File 'app/models/delivery.rb', line 1744

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
  # NOTE: free-online-shipping removal lives in
  # apply_shipping_match_for_economy_shipping so every caller (including the
  # CRM re-apply action) clears FS before crediting the match (defect C).
  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 — logic in ApplySelectedShippingCost (god-object
decomposition). Ensures consistency whether the selection is auto-computed
or explicitly chosen elsewhere.

Parameters:

  • shipping_cost_entry (Object)

    the shipping cost entry

  • previous_selected_id (Integer) (defaults to: nil)

    the previous selected id

  • persist (Object) (defaults to: false)

    the persist



2461
2462
2463
2464
# File 'app/models/delivery.rb', line 2461

def apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false)
  Delivery::ApplySelectedShippingCost.new(self, shipping_cost_entry,
                                          previous_selected_id: previous_selected_id, persist: persist).process
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.



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
# File 'app/models/delivery.rb', line 1764

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

  # Free-online-shipping and the match must never both credit shipping
  # (SO728077 defect C). Clear every live FS discount before crediting the
  # match — all of them, not `.first`, and even zero-amount rows (the next
  # recalculation would re-credit those) — but never the blacklisted ones
  # (DeleteDiscount's un-blacklist branch would revive them).
  order.discounts.free_online_shipping.non_blacklisted.to_a.each do |fs_discount|
    Coupon::DeleteDiscount.new.perform(fs_discount, { skip_lock_check: true })
  end

  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 = economy_shipping_match_amount(order)
  return if amount.nil? # snapshot missing, no drift, or would-be surcharge — see helper

  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
  return if shipping_li.nil? # the discount recalcs above can rebuild shipping lines; nothing left to credit against

  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 whether the record apply warehouse fee.

Returns:

  • (Boolean)

    whether the record apply warehouse fee



2412
2413
2414
2415
# File 'app/models/delivery.rb', line 2412

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)


2949
2950
2951
2952
2953
2954
2955
2956
# File 'app/models/delivery.rb', line 2949

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)


1065
1066
1067
# File 'app/models/delivery.rb', line 1065

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:



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

delegate :billing_entity, to: :resource

#bol_pdfUpload?

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

Returns:



3365
3366
3367
# File 'app/models/delivery.rb', line 3365

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:



2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
# File 'app/models/delivery.rb', line 2694

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:



2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
# File 'app/models/delivery.rb', line 2602

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)


943
944
945
# File 'app/models/delivery.rb', line 943

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)


3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
# File 'app/models/delivery.rb', line 3781

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)


3772
3773
3774
# File 'app/models/delivery.rb', line 3772

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. Extracted to CalculateShippingOptions (god-object
decomposition) — this stays as the public API for internal callers/tests.

Parameters:

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

    caller-provided seed hash (mutated and returned)

Options Hash (options):

  • ship_date (Date, nil)

    date the shipment is expected to ship

Returns:

  • (Hash)

    options hash with merged shipping context



1705
1706
1707
# File 'app/models/delivery.rb', line 1705

def calculate_shipping_options(options = {})
  Delivery::CalculateShippingOptions.new(self, options).process
end

#can_be_deleted?Boolean

Returns whether the record can be deleted.

Returns:

  • (Boolean)

    whether the record can be deleted



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

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

#can_print_carton_labels?Boolean

Returns whether the record can print carton labels.

Returns:

  • (Boolean)

    whether the record can print carton labels



863
864
865
# File 'app/models/delivery.rb', line 863

def can_print_carton_labels?
  shipments.packed_or_measured.present?
end

#can_update_tracking_info?Boolean

Returns whether the record can update tracking info.

Returns:

  • (Boolean)

    whether the record can update tracking info



858
859
860
# File 'app/models/delivery.rb', line 858

def can_update_tracking_info?
  shipments.completed.present?
end

#can_void_rma_delivery?Boolean

Returns whether the record can void rma delivery.

Returns:

  • (Boolean)

    whether the record can void rma delivery



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

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)


2312
2313
2314
2315
2316
2317
2318
2319
# File 'app/models/delivery.rb', line 2312

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



1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
# File 'app/models/delivery.rb', line 1183

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)


4457
4458
4459
# File 'app/models/delivery.rb', line 4457

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

Parameters:

  • current_user (Object) (defaults to: nil)

    the current user

Returns:

  • (Boolean)


2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
# File 'app/models/delivery.rb', line 2496

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)


2980
2981
2982
# File 'app/models/delivery.rb', line 2980

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)


2935
2936
2937
2938
2939
2940
2941
2942
# File 'app/models/delivery.rb', line 2935

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)


3568
3569
3570
# File 'app/models/delivery.rb', line 3568

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



1073
1074
1075
# File 'app/models/delivery.rb', line 1073

def carrier_options_for_select
  Shipment.carrier_options_for_select(self)
end

#catalogObject

Alias for Customer#catalog

Returns:

  • (Object)

    Customer#catalog

See Also:



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

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:



2027
2028
2029
2030
2031
# File 'app/models/delivery.rb', line 2027

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)


1990
1991
1992
1993
1994
# File 'app/models/delivery.rb', line 1990

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

#clear_shipping_costs_safelyObject

Safely clears shipping_costs by first nullifying shipping_cost_id on line_items
to prevent FK violation when the line_items are later saved with stale references.
The FK constraint is ON DELETE CASCADE, but delete_all bypasses Rails callbacks
and the in-memory line_items collection retains stale shipping_cost_id values.



2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
# File 'app/models/delivery.rb', line 2093

def clear_shipping_costs_safely
  # Nullify shipping_cost_id on in-memory line_items to prevent FK violation during save
  line_items.select(&:is_shipping?).each do |li|
    li.shipping_cost_id = nil if li.shipping_cost_id.present?
  end
  # Also update any persisted line_items in the database
  line_items.where.not(shipping_cost_id: nil).update_all(shipping_cost_id: nil)
  # Now safe to delete shipping_costs
  shipping_costs.delete_all
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.



3723
3724
3725
# File 'app/models/delivery.rb', line 3723

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.



3731
3732
3733
# File 'app/models/delivery.rb', line 3731

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.



4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
# File 'app/models/delivery.rb', line 4415

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 whether the record completed regular delivery.

Returns:

  • (Boolean)

    whether the record completed regular delivery



1363
1364
1365
# File 'app/models/delivery.rb', line 1363

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.



4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
# File 'app/models/delivery.rb', line 4028

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:



3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
# File 'app/models/delivery.rb', line 3798

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



3951
3952
3953
3954
3955
3956
3957
# File 'app/models/delivery.rb', line 3951

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



4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
# File 'app/models/delivery.rb', line 4333

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).



3939
3940
3941
3942
# File 'app/models/delivery.rb', line 3939

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)


3817
3818
3819
# File 'app/models/delivery.rb', line 3817

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)


3824
3825
3826
# File 'app/models/delivery.rb', line 3824

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:



1451
1452
1453
1454
1455
1456
# File 'app/models/delivery.rb', line 1451

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:



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

delegate :customer, :primary_party, to: :resource_or_rma_for_delivery

#declaration_232_pdfUpload?

Most recent generated Section 232 declaration PDF Upload.

Returns:



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

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

#default_container_typeString

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

Returns:

  • (String)


4402
4403
4404
4405
4406
# File 'app/models/delivery.rb', line 4402

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.



4195
4196
4197
# File 'app/models/delivery.rb', line 4195

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



1110
1111
1112
# File 'app/models/delivery.rb', line 1110

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 the destination address this record belongs to.

Returns:

  • (Address, nil)

    the destination address this record belongs to

Validations (unless => #skip_destination_address_validation? ):



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

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

#discountsActiveRecord::Relation<Discount>

Returns the associated discounts.

Returns:

  • (ActiveRecord::Relation<Discount>)

    the associated discounts



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

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.



2198
2199
2200
# File 'app/models/delivery.rb', line 2198

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

#do_not_ship_insure_via_carrier?Boolean

Returns whether the record do not ship insure via carrier.

Returns:

  • (Boolean)

    whether the record do not ship insure via carrier



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

def do_not_ship_insure_via_carrier?
  res = false
  # RMA returns are never carrier/ShipEngine ship-insured — coverage, if any,
  # is via the separate Shipsurance return path (ReturnShippingInsurance).
  # Carriers reject insurance on return labels outright (AppSignal #4508).
  # Short-circuits first so a return never falls through to the carrier
  # declared-value fallback below.
  res = true if is_rma_return?
  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 whether the record do not validate line items.

Returns:

  • (Boolean)

    whether the record do not validate line items



2959
2960
2961
# File 'app/models/delivery.rb', line 2959

def do_not_validate_line_items?
  do_not_validate_line_items
end

#does_not_require_shipping_labeling?Boolean

Returns whether the record does not require shipping labeling.

Returns:

  • (Boolean)

    whether the record does not require shipping labeling



4505
4506
4507
4508
# File 'app/models/delivery.rb', line 4505

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 the associated drop ship purchase orders.

Returns:

  • (ActiveRecord::Relation<PurchaseOrder>)

    the associated drop ship purchase orders



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

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

#economy_shipping_match_amount(order) ⇒ BigDecimal, ...

Signed credit for the economy shipping match, or nil when the match must
not apply. A "match" only ever credits the customer the gap between the
live carrier rate and the economy rate they locked in at checkout, so a
valid amount is always <= 0 (checkout snapshot - live cost).

Guards the two ways this corrupted an order in SO728077:

  • Missing snapshot (defect B): never fabricate the checkout price from
    the live rate. A nil shipping_cost_at_time_of_checkout makes the match
    inapplicable, not a $0 (or self-referential) match.
  • Positive amount (defect A): a snapshot above the live rate would
    charge the customer. Skip and alert rather than surcharge.

Parameters:

Returns:

  • (BigDecimal, Float, nil)

    a non-positive credit, or nil if inapplicable



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
# File 'app/models/delivery.rb', line 1814

def economy_shipping_match_amount(order)
  snapshot = order.shipping_cost_at_time_of_checkout
  if snapshot.nil?
    ErrorReporting.warning('Economy shipping match skipped: shipping_cost_at_time_of_checkout is nil',
                           order_id: order.id)
    return nil
  end

  # Compare against the RAW shipping-line price, not the `shipping_cost`
  # column: that column is the post-discount net, so any live shipping
  # credit (FS, or a previously applied match's own line discount) pollutes
  # it — re-applying the match then computed "no drift" against its own
  # credit, destroyed the old match, and silently stripped the customer's
  # locked-in economy rate (SO728077 defect C follow-on).
  live_cost = order.undiscounted_shipping_total

  amount = snapshot - live_cost
  return nil unless amount.abs > 0.0 # no drift between checkout and live rate

  if amount.positive? # snapshot exceeds live rate: matching would surcharge the customer
    ErrorReporting.warning('Economy shipping match would surcharge the customer; skipping',
                           order_id: order.id,
                           custom_data: { checkout_snapshot: snapshot.to_f,
                                          live_cost: live_cost.to_f,
                                          would_be_amount: amount.to_f })
    return nil
  end

  amount
end

#edi_ship_confirm_already_sent?Boolean

True when a ship confirm carrying the delivery's current tracking number
has already been recorded for this order. Guards the invoiced catch-up so
a confirm isn't sent twice for the same shipment — e.g. an LTL delivery
whose PRO was already confirmed at pending_ship_confirm, or a re-invoice.

The check is tracking-number-aware, not "ever sent," so a re-send stays
possible: after labels are voided and re-generated a fresh PRO is booked, so
the prior confirm's tracking won't match the new one and the catch-up
re-sends. (Manual re-sends via the EDI-log UI or console call the sender
directly and bypass this guard entirely.)

order_acknowledge is Amazon Seller Central's ship-confirm category — the
only EDI channel shipping LTL today — whose payload nests the tracking
number at packageDetail.trackingNumber.

Returns:

  • (Boolean)


2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
# File 'app/models/delivery.rb', line 2852

def edi_ship_confirm_already_sent?
  return false unless order

  tracking_numbers = shipments.completed.filter_map(&:display_tracking_number).uniq
  return false if tracking_numbers.empty?

  order.edi_communication_logs.where(category: :order_acknowledge).any? do |ecl|
    tracking_numbers.include?(ecl.data_as_hash&.dig('packageDetail', 'trackingNumber'))
  end
end

#electronic_ship_ci_pdfUpload?

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

Returns:



3414
3415
3416
# File 'app/models/delivery.rb', line 3414

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



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

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)


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

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 whether the record european shipment.

Returns:

  • (Boolean)

    whether the record european shipment



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

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



3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
# File 'app/models/delivery.rb', line 3058

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



1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
# File 'app/models/delivery.rb', line 1972

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:



172
173
# File 'app/models/delivery.rb', line 172

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

#freightquote_carrier?Boolean

Returns whether the record freightquote carrier.

Returns:

  • (Boolean)

    whether the record freightquote carrier



2191
2192
2193
# File 'app/models/delivery.rb', line 2191

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)


2112
2113
2114
2115
2116
2117
2118
# File 'app/models/delivery.rb', line 2112

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)


2170
2171
2172
# File 'app/models/delivery.rb', line 2170

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



3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
# File 'app/models/delivery.rb', line 3155

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

  # Dynamic per-delivery Section 232 declaration (replaces the stale global
  # `declaration_of_steel_aluminimum_copper` upload that stapled ST711716's
  # hand-filled form onto every international delivery until 2026-07 and got
  # ST728704 held at the border). Rescued so a 232 failure still ships the
  # rest of the packet — the broker can request the declaration separately.
  begin
    # Gate the attachment on the same predicate as generation — a delivery
    # that once qualified (and has an old declaration upload) but no longer
    # does must not re-attach the stale form. The declaration is normally
    # generated at the order's awaiting_deliveries transition (line items are
    # immutable past that point without a CR HOLD); this is only a backfill
    # for deliveries that slipped through without one.
    if requires_232_declaration?
      generate_declaration_232_forms if declaration_232_pdf.nil?
      all_forms << declaration_232_pdf if declaration_232_pdf
    end
  rescue StandardError => e
    Rails.logger.warn("generate_all_international_forms_pdf: 232 declaration 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_pdfObject

Generate all labels pdf.



3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
# File 'app/models/delivery.rb', line 3108

def generate_all_labels_pdf
  all_shipments = shipments.completed.order(:id).includes(:uploads)
  # Computed once (single BOL per delivery); folded into the first batch only.
  ltl_bol = combined_labels_ltl_bol
  # 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_labels << ltl_bol if i.zero? && ltl_bol
    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

Generate asynch labels.



3348
3349
3350
3351
3352
3353
# File 'app/models/delivery.rb', line 3348

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



1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
# File 'app/models/delivery.rb', line 1520

def generate_barcode
  require 'zint'
  barcode = Zint::Barcode.new(value: order.reference_number.to_s, symbology: Zint::Constants::Symbologies::BARCODE_CODE128)
  barcode.height = 70
  barcode.show_hrt = 0 # barby rendered bars only; keep it that way
  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_memory_file(extension: '.png')
    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



1542
1543
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
# File 'app/models/delivery.rb', line 1542

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



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
# File 'app/models/delivery.rb', line 1586

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

Generate combined pdf.

Parameters:

  • split_kits (Object) (defaults to: false)

    the split kits

  • skip_plans (Object) (defaults to: false)

    the skip plans



1437
1438
1439
1440
1441
1442
1443
1444
# File 'app/models/delivery.rb', line 1437

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_declaration_232_formsBoolean

Generates the Section 232 declaration pair — the broker's macro-enabled
xlsm (for electronic submission) and a printable PDF twin (folded into the
combined international forms packet) — and attaches both as uploads.
No-op for deliveries that don't require a declaration.

Returns:

  • (Boolean)

    whether the forms were generated



3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
# File 'app/models/delivery.rb', line 3382

def generate_declaration_232_forms
  return false unless requires_232_declaration?

  base = "#{name(false, true)}_232_declaration_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}".downcase
  uploads << Upload.uploadify_from_data(file_name: "#{base}.xlsm",
                                        data: Declaration232::XlsmWriter.new(self).call,
                                        category: 'declaration_232_xlsm')
  uploads << Upload.uploadify_from_data(file_name: "#{base}.pdf",
                                        data: Pdf::Document::Declaration232.new(self).call.pdf,
                                        category: 'declaration_232_pdf')
  true
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.



2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
# File 'app/models/delivery.rb', line 2560

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_labelsHash{Symbol => Object}

Buys carrier labels for this delivery — advisory-locked guard-then-buy
flow lives in GenerateLabels (god-object decomposition).

Returns:

  • (Hash{Symbol => Object})

    :status_code / :status_message



3230
3231
3232
# File 'app/models/delivery.rb', line 3230

def generate_labels
  Delivery::GenerateLabels.new(self).process
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:



1424
1425
1426
1427
1428
1429
1430
1431
1432
# File 'app/models/delivery.rb', line 1424

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



1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
# File 'app/models/delivery.rb', line 1469

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})


1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
# File 'app/models/delivery.rb', line 1866

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)


4565
4566
4567
4568
4569
4570
# File 'app/models/delivery.rb', line 4565

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:



2592
2593
2594
# File 'app/models/delivery.rb', line 2592

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)


4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
# File 'app/models/delivery.rb', line 4579

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:



1355
1356
1357
1358
1359
1360
# File 'app/models/delivery.rb', line 1355

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:



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

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:



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

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 whether the record has custom products.

Returns:

  • (Boolean)

    whether the record has custom products



1574
1575
1576
# File 'app/models/delivery.rb', line 1574

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 whether the record has custom shipping labels.

Returns:

  • (Boolean)

    whether the record has custom shipping labels



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

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)


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

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

#has_dropship_items?Boolean

Returns whether the record has dropship items.

Returns:

  • (Boolean)

    whether the record has dropship items



2544
2545
2546
# File 'app/models/delivery.rb', line 2544

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

#has_future_release_date?Boolean

Returns whether the record has future release date.

Returns:

  • (Boolean)

    whether the record has future release date



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

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

#has_kits?Boolean

Returns whether the record has kits.

Returns:

  • (Boolean)

    whether the record has kits



967
968
969
# File 'app/models/delivery.rb', line 967

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

#has_kits_or_serial_numbers?Boolean

Returns whether the record has kits or serial numbers.

Returns:

  • (Boolean)

    whether the record has kits or serial numbers



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

def has_kits_or_serial_numbers?
  has_serial_numbers? || has_kits?
end

#has_not_changed_but_has_shipping_lines?Boolean

Returns whether the record has not changed but has shipping lines.

Returns:

  • (Boolean)

    whether the record has not changed but has shipping lines



2418
2419
2420
# File 'app/models/delivery.rb', line 2418

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 whether the record has ready to print amazon fba items.

Returns:

  • (Boolean)

    whether the record has ready to print amazon fba items



4226
4227
4228
# File 'app/models/delivery.rb', line 4226

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 whether the record has serial numbers.

Returns:

  • (Boolean)

    whether the record has serial numbers



962
963
964
# File 'app/models/delivery.rb', line 962

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)


2972
2973
2974
2975
2976
# File 'app/models/delivery.rb', line 2972

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 whether the record has shipping line linked to shipping cost that doesnt exist in delivery.

Parameters:

  • shipping_line (Object)

    the shipping line

Returns:

  • (Boolean)

    whether the record has shipping line linked to shipping cost that doesnt exist in delivery



2424
2425
2426
# File 'app/models/delivery.rb', line 2424

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 whether the record has unfulfilled dropship items.

Returns:

  • (Boolean)

    whether the record has unfulfilled dropship items



2534
2535
2536
# File 'app/models/delivery.rb', line 2534

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 whether the record has unprocessed dropship items.

Returns:

  • (Boolean)

    whether the record has unprocessed dropship items



2549
2550
2551
# File 'app/models/delivery.rb', line 2549

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 whether the record has valid shipments.

Returns:

  • (Boolean)

    whether the record has valid shipments



3573
3574
3575
# File 'app/models/delivery.rb', line 3573

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

#incurs_oversized_penalty?Boolean

Returns whether the record incurs oversized penalty.

Returns:

  • (Boolean)

    whether the record incurs oversized penalty



4298
4299
4300
# File 'app/models/delivery.rb', line 4298

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)


1646
1647
1648
# File 'app/models/delivery.rb', line 1646

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)



4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
# File 'app/models/delivery.rb', line 4003

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)


2473
2474
2475
2476
# File 'app/models/delivery.rb', line 2473

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).



4595
4596
4597
4598
4599
4600
4601
# File 'app/models/delivery.rb', line 4595

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:



985
986
987
# File 'app/models/delivery.rb', line 985

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)


2000
2001
2002
# File 'app/models/delivery.rb', line 2000

def insured_value
  chosen_shipping_method&.insured_value
end

#invoicesActiveRecord::Relation<Invoice>

Returns the associated invoices.

Returns:

  • (ActiveRecord::Relation<Invoice>)

    the associated invoices



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

has_many :invoices

#is_amazon_seller_central?Object

Alias for Customer#is_amazon_seller_central?

Returns:

  • (Object)

    Customer#is_amazon_seller_central?

See Also:



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

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

#is_amazon_seller_central_veeqo?Boolean

Returns whether the record is amazon seller central veeqo.

Returns:

  • (Boolean)

    whether the record is amazon seller central veeqo



2322
2323
2324
# File 'app/models/delivery.rb', line 2322

def is_amazon_seller_central_veeqo?
  is_amazon_seller_central? && override_shipping_method?
end

#is_cross_border?Boolean

Returns whether the record is cross border.

Returns:

  • (Boolean)

    whether the record is cross border



3902
3903
3904
# File 'app/models/delivery.rb', line 3902

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 whether the record is default ltl freight.

Returns:

  • (Boolean)

    whether the record is default ltl freight



2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
# File 'app/models/delivery.rb', line 2327

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 whether the record is domestic.

Returns:

  • (Boolean)

    whether the record is domestic



3892
3893
3894
# File 'app/models/delivery.rb', line 3892

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

#is_international?Boolean

Returns whether the record is international.

Returns:

  • (Boolean)

    whether the record is international



3897
3898
3899
# File 'app/models/delivery.rb', line 3897

def is_international?
  !is_domestic?
end

#is_international_and_ups_or_fedex?Boolean

Returns whether the record is international and ups or fedex.

Returns:

  • (Boolean)

    whether the record is international and ups or fedex



3907
3908
3909
# File 'app/models/delivery.rb', line 3907

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

#is_part_of_manifest?Boolean

Returns whether the record is part of manifest.

Returns:

  • (Boolean)

    whether the record is part of manifest



3578
3579
3580
# File 'app/models/delivery.rb', line 3578

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

#is_part_of_transmitted_manifest?Boolean

Whether this delivery sits on a manifest that was actually handed to the
carrier and therefore can't be unwound.

Only the legacy Manifest qualifies. A SpeedeeManifest is never
transmitted — the CSV/FTP handoff is gone for good, and the sheet exists
solely so the driver's paper matches Spee-Dee's dashboard and the parcels on
the truck. Voiding after close is therefore safe, provided the shipment also
comes off the sheet. See SpeedeeManifest.drop_voided_shipments! and
SpeedeeManifest#cancel_manifest.

Returns:

  • (Boolean)


3593
3594
3595
# File 'app/models/delivery.rb', line 3593

def is_part_of_transmitted_manifest?
  shipments.any?(&:manifest)
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)


2290
2291
2292
# File 'app/models/delivery.rb', line 2290

def is_rma_return
  rma_for_return.present?
end

#is_smart_service?Boolean

Returns whether the record is smart service.

Returns:

  • (Boolean)

    whether the record is smart service



2307
2308
2309
# File 'app/models/delivery.rb', line 2307

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

Parameters:

  • sc (Object) (defaults to: nil)

    the sc

Returns:

  • (Boolean)


2241
2242
2243
2244
2245
2246
# File 'app/models/delivery.rb', line 2241

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 whether the record is www.

Returns:

  • (Boolean)

    whether the record is www



4532
4533
4534
# File 'app/models/delivery.rb', line 4532

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 the associated item ledger entries.

Returns:

  • (ActiveRecord::Relation<ItemLedgerEntry>)

    the associated item ledger entries



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

has_many :item_ledger_entries

#labeled_quantities_by_line_itemHash{Integer => Numeric}

Quantity of each line item already covered by active label_complete
shipments, keyed by line_item_id.

Returns:

  • (Hash{Integer => Numeric})


1158
1159
1160
1161
1162
1163
1164
# File 'app/models/delivery.rb', line 1158

def 
  shipments.label_complete
           .reorder(nil) # drop Shipment's default created_at order — illegal under GROUP BY
           .joins(:shipment_contents)
           .group('shipment_contents.line_item_id')
           .sum('shipment_contents.quantity')
end

#ledger_transactionsActiveRecord::Relation<LedgerTransaction>

Returns the associated ledger transactions.

Returns:



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

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})


1127
1128
1129
1130
1131
1132
1133
# File 'app/models/delivery.rb', line 1127

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 the associated line discounts.

Returns:

  • (ActiveRecord::Relation<LineDiscount>)

    the associated line discounts



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

has_many :line_discounts, through: :line_items

#line_itemsActiveRecord::Relation<LineItem>

Returns the associated line items.

Returns:

  • (ActiveRecord::Relation<LineItem>)

    the associated line items



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

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:



1049
1050
1051
# File 'app/models/delivery.rb', line 1049

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:



3763
3764
3765
# File 'app/models/delivery.rb', line 3763

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:



957
958
959
# File 'app/models/delivery.rb', line 957

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)


1118
1119
1120
# File 'app/models/delivery.rb', line 1118

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.



4179
4180
4181
# File 'app/models/delivery.rb', line 4179

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:



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

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 whether the record locked for fba.

Returns:

  • (Boolean)

    whether the record locked for fba



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

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 whether the record ltl freight has changed.

Returns:

  • (Boolean)

    whether the record ltl freight has changed



2438
2439
2440
2441
# File 'app/models/delivery.rb', line 2438

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

Manual pickup contact.



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

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



4213
4214
4215
# File 'app/models/delivery.rb', line 4213

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

Whether an unlinked existing PurchaseOrderItem can be reattached to a new
dropship line item instead of building a fresh one. A cancelled PO item
is never reusable: LineItem#cancel_linked_po cancels + unlinks it, so it
lands here unlinked, and reattaching it (leaving it cancelled) would strand
the line with a dead PO item and transmit nothing to the supplier — the
SO728077 dropship failure (defect E). Skip it so a live PO item is built.

Parameters:

Returns:

  • (Boolean)


2678
2679
2680
2681
2682
2683
# File 'app/models/delivery.rb', line 2678

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

#md5_hash_items_from_packableString

MD5 signature of the packed items, annotated when the shipping-relevant
subset diverges from the full set.

Returns:

  • (String)


3629
3630
3631
3632
3633
3634
3635
3636
# File 'app/models/delivery.rb', line 3629

def md5_hash_items_from_packable
  md5_res = Shipping::Md5HashItem.process(packable_item_hash)
  if md5_res.md5 == md5_res.relevant_md5
    md5_res.md5
  else
    "#{md5_res.md5} (relevant items md5: #{md5_res.relevant_md5})"
  end
end

#messaging_logsActiveRecord::Relation<MessagingLog>

Returns the associated messaging logs.

Returns:

  • (ActiveRecord::Relation<MessagingLog>)

    the associated messaging logs



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

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)


1657
1658
1659
1660
1661
1662
1663
1664
1665
# File 'app/models/delivery.rb', line 1657

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>)


3046
3047
3048
3049
3050
# File 'app/models/delivery.rb', line 3046

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.



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

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.



1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
# File 'app/models/delivery.rb', line 1032

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)


926
927
928
# File 'app/models/delivery.rb', line 926

def open_activities_counter
  all_activities.open_activities.count
end

#orderOrder?

Returns the order this record belongs to.

Returns:

  • (Order, nil)

    the order this record belongs to



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

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

#origin_addressAddress?

Returns the origin address this record belongs to.

Returns:

  • (Address, nil)

    the origin address this record belongs to

Validations:



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

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

#override_shipping_method?Boolean

Returns whether the record override shipping method.

Returns:

  • (Boolean)

    whether the record override shipping method



2175
2176
2177
# File 'app/models/delivery.rb', line 2175

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:



3601
3602
3603
# File 'app/models/delivery.rb', line 3601

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:



3611
3612
3613
3614
3615
# File 'app/models/delivery.rb', line 3611

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})


3621
3622
3623
# File 'app/models/delivery.rb', line 3621

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})


3643
3644
3645
# File 'app/models/delivery.rb', line 3643

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)


2282
2283
2284
# File 'app/models/delivery.rb', line 2282

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)


4255
4256
4257
4258
4259
# File 'app/models/delivery.rb', line 4255

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)


2302
2303
2304
# File 'app/models/delivery.rb', line 2302

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)


1241
1242
1243
1244
1245
1246
1247
1248
# File 'app/models/delivery.rb', line 1241

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

Parameters:

  • split_kits (Object) (defaults to: false)

    the split kits

  • sort_method (Object) (defaults to: :location)

    the sort method



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
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
# File 'app/models/delivery.rb', line 1253

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)


3333
3334
3335
# File 'app/models/delivery.rb', line 3333

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:



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

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

#precreated_rmaRma?

Returns the associated precreated rma.

Returns:

  • (Rma, nil)

    the associated precreated rma



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

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



1669
1670
1671
1672
1673
1674
1675
1676
# File 'app/models/delivery.rb', line 1669

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 the prepack requester this record belongs to.

Returns:

  • (Party, nil)

    the prepack requester this record belongs to



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

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

#preset_jobsActiveRecord::Relation<PresetJob>

Returns the associated preset jobs.

Returns:

  • (ActiveRecord::Relation<PresetJob>)

    the associated preset jobs



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

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:



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

delegate :customer, :primary_party, to: :resource_or_rma_for_delivery

Returns whether the record print container label.

Returns:

  • (Boolean)

    whether the record print container label



813
814
815
816
817
818
819
820
821
822
823
824
825
826
# File 'app/models/delivery.rb', line 813

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).



2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
# File 'app/models/delivery.rb', line 2787

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.



2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
# File 'app/models/delivery.rb', line 2768

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

#publish_order_acknowledged_eventvoid

This method returns an undefined value.

Publishes Events::OrderAcknowledged, which drives the EDI ship
confirmation (Edi::OrderEventProcessor → the partner's
confirm_message_processor.acknowledge_order). Guarded to EDI orders that
expect a ship-confirm push: no-op for non-EDI, early-label, and Amazon Buy
Shipping deliveries (Buy Shipping's purchaseShipment already confirms).

Callers gate on all_completed_shipments_reported_tracking? so the confirm
never carries a blank trackingNumber: fired at pending_ship_confirm when
tracking is already present (parcel, synchronously-assigned LTL PROs) and at
invoiced as a catch-up for LTL PROs assigned asynchronously.



2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
# File 'app/models/delivery.rb', line 2811

def publish_order_acknowledged_event
  return unless order&.edi_transaction_id.present? && !order.has_early_purchased_label? && carrier != 'AmazonSeller'

  order_id = order.id
  ActiveRecord.after_all_transactions_commit do
    Rails.configuration.event_store.publish(
      Events::OrderAcknowledged.new(data: { order_id: }),
      stream_name: "Order-#{order_id}"
    )
  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)


1101
1102
1103
# File 'app/models/delivery.rb', line 1101

def quantities_remaining_to_allocate
  line_allocation_status_hash.values.sum
end

#quoteQuote?

Returns the quote this record belongs to.

Returns:

  • (Quote, nil)

    the quote this record belongs to



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

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

#ready_to_choose_ships_economy_carrier?Boolean

Returns whether the record ready to choose ships economy carrier.

Returns:

  • (Boolean)

    whether the record ready to choose ships economy carrier



4552
4553
4554
# File 'app/models/delivery.rb', line 4552

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:



4221
4222
4223
# File 'app/models/delivery.rb', line 4221

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.



1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
# File 'app/models/delivery.rb', line 1623

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)


1170
1171
1172
# File 'app/models/delivery.rb', line 1170

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)


3850
3851
3852
3853
3854
3855
3856
# File 'app/models/delivery.rb', line 3850

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.



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

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)


2433
2434
2435
# File 'app/models/delivery.rb', line 2433

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



3964
3965
3966
# File 'app/models/delivery.rb', line 3964

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.



4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
# File 'app/models/delivery.rb', line 4143

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)


2185
2186
2187
# File 'app/models/delivery.rb', line 2185

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)


2207
2208
2209
# File 'app/models/delivery.rb', line 2207

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_232_declaration?Boolean

Whether this delivery needs a Section 232 steel/aluminum/copper
declaration: US customs requires it for goods entering the USA, so only
CAN→USA cross-border deliveries qualify.

Returns:

  • (Boolean)


3372
3373
3374
# File 'app/models/delivery.rb', line 3372

def requires_232_declaration?
  is_cross_border? && destination_address&.country_iso3 == 'USA'
end

#requires_manifest_completion?Boolean

Returns whether the record requires manifest completion.

Returns:

  • (Boolean)

    whether the record requires manifest completion



4394
4395
4396
# File 'app/models/delivery.rb', line 4394

def requires_manifest_completion?
  CARRIERS_REQUIRING_MANIFEST_COMPLETION.include?(carrier)
end

#requires_manual_pickup?Boolean

Returns whether the record requires manual pickup.

Returns:

  • (Boolean)

    whether the record requires manual pickup



2372
2373
2374
# File 'app/models/delivery.rb', line 2372

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

#reserved_serial_numbersActiveRecord::Relation<ReservedSerialNumber>

Returns the associated reserved serial numbers.

Returns:



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

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



3535
3536
3537
3538
3539
3540
3541
# File 'app/models/delivery.rb', line 3535

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.



2038
2039
2040
2041
# File 'app/models/delivery.rb', line 2038

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)


4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
# File 'app/models/delivery.rb', line 4475

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:



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

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:



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

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)


935
936
937
# File 'app/models/delivery.rb', line 935

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)


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

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

Retrieve friendly shipping method.

Parameters:

  • show_customer_pays_info (Object) (defaults to: false)

    the show customer pays info

  • for_www (Object) (defaults to: false)

    the for www

  • sc (Object) (defaults to: nil)

    the sc

  • with_delivery_commitment (Object) (defaults to: false)

    the with delivery commitment

  • for_edi (Object) (defaults to: false)

    the for edi



2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
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
2163
# File 'app/models/delivery.rb', line 2126

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) ⇒ Hash

Retrieve shipping costs from carriers and persist them as ShippingCost
rows. Extracted to RetrieveShippingCosts (god-object
decomposition) — this stays as the public API for the ~35 callers.

Parameters:

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

    optional user-chosen ship date for rates

Returns:

  • (Hash)

    { code:, message:, packages:, created_at: } — or
    { code: :already_processing, ... } when another request holds the lock



1694
1695
1696
# File 'app/models/delivery.rb', line 1694

def retrieve_shipping_costs(rate_ship_date: nil)
  Delivery::RetrieveShippingCosts.new(self, rate_ship_date: rate_ship_date).process
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)


2253
2254
2255
# File 'app/models/delivery.rb', line 2253

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)


2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
# File 'app/models/delivery.rb', line 2263

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



1852
1853
1854
1855
1856
1857
1858
1859
# File 'app/models/delivery.rb', line 1852

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 the associated rma for return.

Returns:

  • (Rma, nil)

    the associated rma for return



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

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)


3340
3341
3342
3343
3344
3345
# File 'app/models/delivery.rb', line 3340

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:



2718
2719
2720
# File 'app/models/delivery.rb', line 2718

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.



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

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



4465
4466
4467
# File 'app/models/delivery.rb', line 4465

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

#selected_shipping_costShippingCost?

Returns the selected shipping cost this record belongs to.

Returns:

  • (ShippingCost, nil)

    the selected shipping cost this record belongs to



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

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.



2727
2728
2729
# File 'app/models/delivery.rb', line 2727

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>)


2888
2889
2890
# File 'app/models/delivery.rb', line 2888

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.



2906
2907
2908
# File 'app/models/delivery.rb', line 2906

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)


2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
# File 'app/models/delivery.rb', line 2870

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.



2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
# File 'app/models/delivery.rb', line 2747

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.



2735
2736
2737
# File 'app/models/delivery.rb', line 2735

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>)


2897
2898
2899
# File 'app/models/delivery.rb', line 2897

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)


1332
1333
1334
# File 'app/models/delivery.rb', line 1332

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:



1341
1342
1343
1344
1345
1346
1347
1348
# File 'app/models/delivery.rb', line 1341

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.



2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
# File 'app/models/delivery.rb', line 2516

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.



4064
4065
4066
4067
4068
4069
4070
4071
# File 'app/models/delivery.rb', line 4064

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



4385
4386
4387
4388
4389
4390
4391
# File 'app/models/delivery.rb', line 4385

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: {})

    extraction options

Options Hash (options):

  • ignore_timestamp (Boolean)

    ignore the recorded timestamp when comparing hashes

  • origin (String, nil)

    origin recorded with the hash



3655
3656
3657
# File 'app/models/delivery.rb', line 3655

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 and the shipping LineItem.
The carrier-selection policy lives in SetProperShippingCost
(god-object decomposition) — this delegator keeps the callback wiring
and public API on the model.

Returns:

  • (Boolean, nil)


2450
2451
2452
# File 'app/models/delivery.rb', line 2450

def set_proper_shipping_cost
  Delivery::SetProperShippingCost.new(self).process
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.



3714
3715
3716
# File 'app/models/delivery.rb', line 3714

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:



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

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)


3841
3842
3843
# File 'app/models/delivery.rb', line 3841

def ship_from_attributes
  order&.ship_from_attributes(self) || rma_for_return&.ship_from_attributes
end

#ship_labeled_via_heatwave?Boolean

Returns whether the record ship labeled via heatwave.

Returns:

  • (Boolean)

    whether the record ship labeled via heatwave



829
830
831
# File 'app/models/delivery.rb', line 829

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 whether the record ship labeled via heatwave or manual and ship insuring.

Returns:

  • (Boolean)

    whether the record ship labeled via heatwave or manual and ship insuring



834
835
836
# File 'app/models/delivery.rb', line 834

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)


3702
3703
3704
3705
3706
3707
3708
# File 'app/models/delivery.rb', line 3702

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)


3832
3833
3834
# File 'app/models/delivery.rb', line 3832

def ship_to_attributes
  order&.ship_to_attributes || rma_for_return&.ship_to_attributes
end

#shipment_contents_editable?(current_user = nil) ⇒ Boolean

Returns whether the record shipment contents editable.

Parameters:

  • current_user (Object) (defaults to: nil)

    the current user

Returns:

  • (Boolean)

    whether the record shipment contents editable



3025
3026
3027
3028
3029
# File 'app/models/delivery.rb', line 3025

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>)


2363
2364
2365
2366
2367
2368
2369
# File 'app/models/delivery.rb', line 2363

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 the associated shipments.

Returns:

  • (ActiveRecord::Relation<Shipment>)

    the associated shipments



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

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:



1057
1058
1059
# File 'app/models/delivery.rb', line 1057

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

Parameters:

  • use_shipments (Object) (defaults to: nil)

    the use shipments



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
# File 'app/models/delivery.rb', line 1711

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 whether the record shipments voidable.

Returns:

  • (Boolean)

    whether the record shipments voidable



3032
3033
3034
# File 'app/models/delivery.rb', line 3032

def shipments_voidable?
  SHIPPING_STATES.include?(state.to_sym)
end

#shipping?Boolean

Returns whether the record shipping.

Returns:

  • (Boolean)

    whether the record shipping



3037
3038
3039
# File 'app/models/delivery.rb', line 3037

def shipping?
  %i[pending_ship_confirm shipped].include?(state.to_sym)
end

#shipping_account_numberShippingAccountNumber?

Returns the shipping account number this record belongs to.

Returns:



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

belongs_to :shipping_account_number, optional: true

#shipping_costsActiveRecord::Relation<ShippingCost>

dependent destroy handled by trigger

Returns:

See Also:



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

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:



1683
1684
1685
# File 'app/models/delivery.rb', line 1683

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)


1372
1373
1374
1375
1376
1377
1378
1379
# File 'app/models/delivery.rb', line 1372

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)>)


2402
2403
2404
2405
2406
2407
2408
2409
# File 'app/models/delivery.rb', line 2402

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?

Returns the shipping option this record belongs to.

Returns:

  • (ShippingOption, nil)

    the shipping option this record belongs to



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

belongs_to :shipping_option, optional: true

#shipping_option_matches?(so_name) ⇒ Boolean

Returns whether the record shipping option matches.

Parameters:

  • so_name (Object)

    the so name

Returns:

  • (Boolean)

    whether the record shipping option matches



1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
# File 'app/models/delivery.rb', line 1383

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)


4536
4537
4538
# File 'app/models/delivery.rb', line 4536

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 whether the record ships economy ltl.

Returns:

  • (Boolean)

    whether the record ships economy ltl



4547
4548
4549
# File 'app/models/delivery.rb', line 4547

def ships_economy_ltl?
  ships_economy && ships_ltl_freight?
end

#ships_economy_package?Boolean

Returns whether the record ships economy package.

Returns:

  • (Boolean)

    whether the record ships economy package



4542
4543
4544
# File 'app/models/delivery.rb', line 4542

def ships_economy_package?
  ships_economy && !ships_ltl_freight?
end

#ships_ltl_freight?Boolean

Returns whether the record ships ltl freight.

Returns:

  • (Boolean)

    whether the record ships ltl freight



2351
2352
2353
# File 'app/models/delivery.rb', line 2351

def ships_ltl_freight?
  !is_warehouse_pickup? && (ltl_freight.present? || ltl_freight_guaranteed.present?)
end

#should_have_electronic_commercial_invoice?Boolean

Returns whether the record should have electronic commercial invoice.

Returns:

  • (Boolean)

    whether the record should have electronic commercial invoice



3419
3420
3421
# File 'app/models/delivery.rb', line 3419

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 whether the record should print heating element labels.

Returns:

  • (Boolean)

    whether the record should print heating element labels



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

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

Whether to auto-email the customs commercial invoice to the LTL carrier.
The commented-out ltl_pro_number != master_tracking_number guard below is
disabled ON PURPOSE: Polaris runs eBOL off, so the real carrier PRO never
arrives via CHR's events API and ltl_pro_number falls back to the CHR
tracking number. We deliberately send anyway — the load number on the BOL is
the cross-reference. Do NOT re-enable the guard; it would suppress the CI
forever. Full rationale + workaround:
doc/development/freightquote_events_pipeline.md (Customs commercial-invoice
email — the async-PRO / Polaris eBOL gap).

Returns:

  • (Boolean)


2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
# File 'app/models/delivery.rb', line 2919

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 whether the record should ship ltl freight.

Returns:

  • (Boolean)

    whether the record should ship ltl freight



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

def should_ship_ltl_freight?
  is_default_ltl_freight? || ltl_freight.present? || ltl_freight_guaranteed.present?
end

#show_packaging_on_pick_slip?Boolean

Returns whether the record show packaging on pick slip.

Returns:

  • (Boolean)

    whether the record show packaging on pick slip



1460
1461
1462
# File 'app/models/delivery.rb', line 1460

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)


2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
# File 'app/models/delivery.rb', line 2218

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)


2480
2481
2482
# File 'app/models/delivery.rb', line 2480

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:



1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
# File 'app/models/delivery.rb', line 1894

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:



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
# File 'app/models/delivery.rb', line 1927

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. Extracted to GroupWwwShippingCosts (god-object
decomposition).

Parameters:

  • sort_by_price (Boolean) (defaults to: true)

    when true, sort each bucket by cost; otherwise honor catalog carrier order

Returns:



1962
1963
1964
# File 'app/models/delivery.rb', line 1962

def sorted_shipping_costs_www_hash(sort_by_price: true)
  Delivery::GroupWwwShippingCosts.new(self, sort_by_price: sort_by_price).process
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.



4162
4163
4164
# File 'app/models/delivery.rb', line 4162

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>)


3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
# File 'app/models/delivery.rb', line 3917

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:



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

delegate :store, to: :resource

#supplierSupplier?

Returns the supplier this record belongs to.

Returns:

  • (Supplier, nil)

    the supplier this record belongs to



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

belongs_to :supplier, optional: true

#supported_shipping_carrier?Boolean

Returns whether the record supported shipping carrier.

Returns:

  • (Boolean)

    whether the record supported shipping carrier



3859
3860
3861
3862
3863
3864
3865
3866
3867
# File 'app/models/delivery.rb', line 3859

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)


3673
3674
3675
# File 'app/models/delivery.rb', line 3673

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)


3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
# File 'app/models/delivery.rb', line 3547

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
  # ShipEngine LTL freight tracks by its PRO (ltl_pro_number), not the
  # master_tracking_number — for some carriers the latter is a non-trackable
  # booking number (e.g. R&L returns WB…/WC… at booking while the trackable
  # PRO arrives asynchronously into ltl_pro_number).
  number = shipping_option&.is_shipengine_ltl? ? ltl_pro_number.presence : master_tracking_number
  Shipment.tracking_link(resolved_carrier, 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.



3739
3740
3741
# File 'app/models/delivery.rb', line 3739

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.



3747
3748
3749
# File 'app/models/delivery.rb', line 3747

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.



4187
4188
4189
# File 'app/models/delivery.rb', line 4187

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).



3754
3755
3756
# File 'app/models/delivery.rb', line 3754

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.



4203
4204
4205
# File 'app/models/delivery.rb', line 4203

def update_serial_numbers_shipped_count
  line_items.each(&:update_serial_numbers_shipped_count)
end

#uploadsActiveRecord::Relation<Upload>

Returns the associated uploads.

Returns:

  • (ActiveRecord::Relation<Upload>)

    the associated uploads



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

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

#valid_for_generating_return_labels?Boolean

Returns whether the record valid for generating return labels.

Returns:

  • (Boolean)

    whether the record valid for generating return labels



3884
3885
3886
3887
3888
3889
# File 'app/models/delivery.rb', line 3884

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 whether the record valid for generating ship labels.

Returns:

  • (Boolean)

    whether the record valid for generating ship labels



3870
3871
3872
3873
3874
3875
3876
# File 'app/models/delivery.rb', line 3870

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 whether the record valid for voiding ship labels.

Returns:

  • (Boolean)

    whether the record valid for voiding ship labels



3879
3880
3881
# File 'app/models/delivery.rb', line 3879

def valid_for_voiding_ship_labels?
  pending_ship_confirm? && shipments.label_complete.any? && !is_part_of_transmitted_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.



4234
4235
4236
4237
4238
# File 'app/models/delivery.rb', line 4234

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



3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
# File 'app/models/delivery.rb', line 3006

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?
    detail = order.funds_shortfall_report
    msg = "Cannot ship delivery #{id}: order #{order.id} does not have sufficient " \
          "authorized or captured funds (balance: $#{'%.2f' % order.balance})."
    msg += " Uncovered — #{detail}." if detail.present?
    msg += ' Resolve payment before shipping.'
    raise msg
  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).

Parameters:

  • _params (Object) (defaults to: {})

    the params

Returns:



4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
# File 'app/models/delivery.rb', line 4308

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



3508
3509
3510
3511
3512
3513
3514
3515
3516
# File 'app/models/delivery.rb', line 3508

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.



3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
# File 'app/models/delivery.rb', line 3523

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



1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
# File 'app/models/delivery.rb', line 1210

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



3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
# File 'app/models/delivery.rb', line 3438

def void_shipments
  # puts "!!void_shipments"
  if shipments.completed.any? && !is_part_of_transmitted_manifest?
    # Voiding after the Spee-Dee close cancels the Dispatch Science order and
    # keeps the parcel on the dock, so it must come off the printed sheet too
    # — the manifest's whole job is to match the dashboard and the truck.
    SpeedeeManifest.drop_voided_shipments!(shipments)
    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.
      # freight_order_number is included so a re-label after voiding a Spee-Dee
      # (Dispatch Science) order creates a FRESH order rather than resuming the
      # cancelled one (see Delivery::GenerateLabels Spee-Dee resume guard). The
      # Freightquote void-confirmation worker already received it as an arg
      # above, so nil-ing the column here is safe for that carrier too.
      update_columns(master_tracking_number: nil, carrier_bol: nil, actual_shipping_cost: nil, shipengine_label_id: nil, freight_order_number: 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 carrier-transmitted manifest." }
  end
end